#!/usr/bin/env python
import subprocess
import platform
import re
import sys
import os
import shutil
import shlex
# Enables the vendored distutils in setuptools over the stdlib one to avoid
# the deprecation warning. Must be done before importing setuptools,
# setuptools also must be imported before distutils.
# https://github.com/pypa/setuptools/blob/main/docs/deprecated/distutils-legacy.rst
os.environ['SETUPTOOLS_USE_DISTUTILS'] = 'local'
from setuptools import setup # noqa: E402
from setuptools.extension import Extension # noqa: E402
from Cython.Build import cythonize # noqa: E402
def get_output(*args, **kwargs):
res = subprocess.check_output(*args, shell=True, **kwargs)
decoded = res.decode('utf-8')
return decoded.strip()
# get the compile and link args
kc = "krb5-config"
autodetect_kc = True
posix = os.name != 'nt'
# Per https://docs.python.org/3/library/platform.html#platform.architecture
# this is the preferred way of determining "64-bitness".
is64bit = sys.maxsize > 2**32
kc_env = 'GSSAPI_KRB5CONFIG'
if kc_env in os.environ:
kc = os.environ[kc_env]
autodetect_kc = False
print(f"Using {kc} from env")
link_args, compile_args = [
shlex.split(os.environ[e], posix=posix) if e in os.environ else None
for e in ['GSSAPI_LINKER_ARGS', 'GSSAPI_COMPILER_ARGS']
]
osx_has_gss_framework = False
if sys.platform == 'darwin':
mac_ver = [int(v) for v in platform.mac_ver()[0].split('.')]
osx_has_gss_framework = (mac_ver >= [10, 7, 0])
winkrb_path = None
if os.name == 'nt':
# Try to find location of MIT kerberos
# First check program files of the appropriate architecture
_pf_path = os.path.join(os.environ['ProgramFiles'], 'MIT', 'Kerberos')
if os.path.exists(_pf_path):
winkrb_path = _pf_path
else:
# Try to detect kinit in PATH
_kinit_path = shutil.which('kinit')
if _kinit_path is None:
print("Failed find MIT kerberos!")
else:
winkrb_path = os.path.dirname(os.path.dirname(_kinit_path))
# Monkey patch distutils if it throws errors getting msvcr.
# For MinGW it won't need it.
from distutils import cygwinccompiler
try:
cygwinccompiler.get_msvcr()
except ValueError:
cygwinccompiler.get_msvcr = lambda *a, **kw: []
if sys.platform.startswith("freebsd") and autodetect_kc:
# FreeBSD does $PATH backward, for our purposes. That is, the package
# manager's version of the software is in /usr/local, which is in PATH
# *after* the version in /usr. We prefer the package manager's version
# because the Heimdal in base is truly ancient, but this can be overridden
# - either in the "normal" fashion by putting something in PATH in front
# of it, or by removing /usr/local from PATH.
bins = []
for b in os.environ["PATH"].split(":"):
p = f"{b}/krb5-config"
if not os.path.exists(p):
continue
bins.append(p)
if len(bins) > 1 and bins[0] == "/usr/bin/krb5-config" and \
"/usr/local/bin/krb5-config" in bins:
kc = "/usr/local/bin/krb5-config"
print(f"Detected: {kc}")
if link_args is None:
if osx_has_gss_framework:
link_args = ['-framework', 'GSS']
elif winkrb_path:
_libs = os.path.join(
winkrb_path, 'lib', 'amd64' if is64bit else 'i386'
)
link_args = (
['-L%s' % _libs]
+ ['-l%s' % os.path.splitext(lib)[0] for lib in os.listdir(_libs)]
)
elif os.environ.get('MINGW_PREFIX'):
link_args = ['-lgss']
else:
link_args = shlex.split(get_output(f"{kc} --libs gssapi"))
if compile_args is None:
if osx_has_gss_framework:
compile_args = ['-DOSX_HAS_GSS_FRAMEWORK']
elif winkrb_path:
compile_args = [
'-I%s' % os.path.join(winkrb_path, 'include'),
]
if is64bit:
compile_args.append('-DMS_WIN64')
elif os.environ.get('MINGW_PREFIX'):
compile_args = ['-fPIC']
else:
compile_args = shlex.split(get_output(f"{kc} --cflags gssapi"))
# add in the extra workarounds for different include structures
if winkrb_path:
prefix = winkrb_path
else:
try:
prefix = get_output(f"{kc} gssapi --prefix")
except Exception:
print("WARNING: couldn't find krb5-config; assuming prefix of %s"
% str(sys.prefix))
prefix = sys.prefix
gssapi_ext_h = os.path.join(prefix, 'include/gssapi/gssapi_ext.h')
if os.path.exists(gssapi_ext_h):
compile_args.append("-DHAS_GSSAPI_EXT_H")
# Create a define to detect msys in the headers
if sys.platform == 'msys':
compile_args.append('-D__MSYS__')
# ensure that any specific directories are listed before any generic system
# directories inserted by setuptools
# Also separate out specified libraries as MSBuild requires different args
_link_args = link_args
library_dirs, libraries, link_args = [], [], []
for arg in _link_args:
if arg.startswith('-L'):
library_dirs.append(arg[2:])
elif arg.startswith('-l'):
libraries.append(arg[2:])
else:
link_args.append(arg)
ENABLE_SUPPORT_DETECTION = \
(os.environ.get('GSSAPI_SUPPORT_DETECT', 'true').lower() == 'true')
if ENABLE_SUPPORT_DETECTION:
import ctypes.util
main_lib = os.environ.get('GSSAPI_MAIN_LIB', None)
main_path = ""
if main_lib is None and osx_has_gss_framework:
main_lib = ctypes.util.find_library('GSS')
if not main_lib:
# https://github.com/pythongssapi/python-gssapi/issues/235
# CPython has a bug on Big Sur where find_library will fail to
# find the library path of shared frameworks. This has been fixed
# in newer versions but we have this fallback in case an older
# version is still in use. This fix is expected to be included in
# 3.8.8 and 3.9.2.
main_lib = '/System/Library/Frameworks/GSS.framework/GSS'
elif os.environ.get('MINGW_PREFIX'):
main_lib = os.environ.get('MINGW_PREFIX')+'/bin/libgss-3.dll'
elif sys.platform == 'msys':
# Plain msys, not running in MINGW_PREFIX. Try to get the lib from one
_main_lib = f'/mingw{64 if is64bit else 32}/bin/libgss-3.dll'
if os.path.exists(_main_lib):
main_lib = _main_lib
os.environ['PATH'] += os.pathsep + os.path.dirname(main_lib)
elif main_lib is None:
for opt in libraries:
if 'gssapi' in opt:
if os.name == 'nt':
main_lib = '%s.dll' % opt
if winkrb_path:
main_path = os.path.join(winkrb_path, 'bin')
else:
main_lib = 'lib%s.so' % opt
for opt in link_args:
# To support Heimdal on Debian, read the linker path.
if opt.startswith('-Wl,/'):
main_path = opt[4:] + "/"
if main_path == "":
for d in library_dirs:
if os.path.exists(os.path.join(d, main_lib)):
main_path = d
break
if main_lib is None:
raise Exception("Could not find main GSSAPI shared library. Please "
"try setting GSSAPI_MAIN_LIB yourself or setting "
"GSSAPI_SUPPORT_DETECT to 'false'")
GSSAPI_LIB = ctypes.CDLL(os.path.join(main_path, main_lib))
def make_extension(name_fmt, module, **kwargs):
"""Helper method to remove the repetition in extension declarations."""
source = name_fmt.replace('.', '/') % module + '.pyx'
if not os.path.exists(source):
raise OSError(source)
return Extension(
name_fmt % module,
extra_link_args=link_args,
extra_compile_args=compile_args,
library_dirs=library_dirs,
libraries=libraries,
sources=[source],
**kwargs
)
# detect support
def main_file(module):
return make_extension('gssapi.raw.%s', module)
ENUM_EXTS = []
def extension_file(module, canary):
if ENABLE_SUPPORT_DETECTION and not hasattr(GSSAPI_LIB, canary):
print('Skipping the %s extension because it '
'is not supported by your GSSAPI implementation...' % module)
return
try:
ENUM_EXTS.append(
make_extension('gssapi.raw._enum_extensions.ext_%s', module,
include_dirs=['gssapi/raw/'])
)
except OSError:
pass
return make_extension('gssapi.raw.ext_%s', module)
def gssapi_modules(lst):
# filter out missing files
res = [mod for mod in lst if mod is not None]
# add in supported mech files
res.extend(
make_extension('gssapi.raw.mech_%s', mech)
for mech in os.environ.get('GSSAPI_MECHS', 'krb5').split(',')
)
# add in any present enum extension files
res.extend(ENUM_EXTS)
return cythonize(res, language_level=2)
long_desc = re.sub(r'\.\. role:: \w+\(code\)\s*\n\s*.+', '',
re.sub(r':(python|bash|code):', '',
re.sub(r'\.\. code-block:: \w+', '::',
open('README.txt').read())))
install_requires = [
'decorator',
]
setup(
name='gssapi',
version='1.8.4',
author='The Python GSSAPI Team',
author_email='jborean93@gmail.com',
packages=['gssapi', 'gssapi.raw', 'gssapi.raw._enum_extensions',
'gssapi.tests'],
package_data={
"gssapi": ["py.typed"],
"gssapi.raw": ["*.pyi"],
},
description='Python GSSAPI Wrapper',
long_description=long_desc,
license='LICENSE.txt',
url="https://github.com/pythongssapi/python-gssapi",
python_requires=">=3.7",
classifiers=[
'Development Status :: 5 - Production/Stable',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Intended Audience :: Developers',
'License :: OSI Approved :: ISC License (ISCL)',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Cython',
'Topic :: Security',
'Topic :: Software Development :: Libraries :: Python Modules'
],
ext_modules=gssapi_modules([
main_file('misc'),
main_file('exceptions'),
main_file('creds'),
main_file('names'),
main_file('sec_contexts'),
main_file('types'),
main_file('message'),
main_file('oids'),
main_file('cython_converters'),
main_file('chan_bindings'),
extension_file('s4u', 'gss_acquire_cred_impersonate_name'),
extension_file('cred_store', 'gss_store_cred_into'),
extension_file('rfc4178', 'gss_set_neg_mechs'),
extension_file('rfc5587', 'gss_indicate_mechs_by_attrs'),
extension_file('rfc5588', 'gss_store_cred'),
extension_file('rfc5801', 'gss_inquire_saslname_for_mech'),
extension_file('cred_imp_exp', 'gss_import_cred'),
extension_file('dce',
'__ApplePrivate_gss_wrap_iov' if osx_has_gss_framework
else 'gss_wrap_iov'),
extension_file('dce_aead', 'gss_wrap_aead'),
extension_file('iov_mic', 'gss_get_mic_iov'),
extension_file('ggf', 'gss_inquire_sec_context_by_oid'),
extension_file('set_cred_opt', 'gss_set_cred_option'),
# see ext_rfc6680_comp_oid for more information on this split
extension_file('rfc6680', 'gss_display_name_ext'),
extension_file('rfc6680_comp_oid', 'GSS_C_NT_COMPOSITE_EXPORT'),
# see ext_password{,_add}.pyx for more information on this split
extension_file('password', 'gss_acquire_cred_with_password'),
extension_file('password_add', 'gss_add_cred_with_password'),
extension_file('krb5', 'gss_krb5_ccache_name'),
]),
keywords=['gssapi', 'security'],
install_requires=install_requires
)