python-gssapi-1.6.1/0000775000372000037200000000000013523321764011252 5ustar python-gssapi-1.6.1/LICENSE.txt0000664000372000037200000000134513523321513013070 0ustar Copyright (c) 2014, The Python GSSAPI Team Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. python-gssapi-1.6.1/docs/0000775000372000037200000000000013523321513012172 5ustar python-gssapi-1.6.1/docs/Makefile0000664000372000037200000001521713523321513013640 0ustar # Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Python-GSSAPI.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Python-GSSAPI.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Python-GSSAPI" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Python-GSSAPI" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." python-gssapi-1.6.1/docs/source/0000775000372000037200000000000013523321513013472 5ustar python-gssapi-1.6.1/docs/source/gssapi.rst0000664000372000037200000000426413523321513015520 0ustar High-Level API ============== .. py:module:: gssapi The high-level API contains three main classes for interacting with GSSAPI, representing the primary abstractions that GSSAPI provides: :class:`~gssapi.names.Name`, :class:`~gssapi.creds.Credentials`, and :class:`~gssapi.sec_contexts.SecurityContext`. .. note:: Classes in the high-level API inherit from the corresponding classes in the low-level API, and thus may be passed in to low-level API functions. .. warning:: All methods in both the high-level and low-level APIs may throw the generic :class:`GSSError` exception. Main Classes ------------ Names """"" .. automodule:: gssapi.names :members: :undoc-members: Credentials """"""""""" .. automodule:: gssapi.creds :members: :undoc-members: Security Contexts """"""""""""""""" .. automodule:: gssapi.sec_contexts :members: :undoc-members: Enums and Helper Classes ------------------------ The following enumerations from the low-level API are also used with the high-level API. For convenience, they are imported in the high-level API :mod:`gssapi` module: .. autoclass:: gssapi.NameType :members: :undoc-members: :show-inheritance: .. autoclass:: gssapi.MechType :members: :undoc-members: :show-inheritance: .. TODO(directxman12): Sphinx doesn't document enums properly yet, so we need to figure out how to document them. .. autoclass:: gssapi.RequirementFlag :show-inheritance: .. autoclass:: gssapi.AddressType :show-inheritance: Similarly, there are a couple classes from the low-level API that are imported into the high-level API module. These classes are less likely to be used directly by a user, but are returned by several methods: .. autoclass:: gssapi.OID :members: .. autoclass:: gssapi.IntEnumFlagSet :members: :undoc-members: :show-inheritance: Exceptions ---------- The high-level API can raise all of the exceptions that the low-level API can raise in addition to several other high-level-specific exceptions: .. automodule:: gssapi.exceptions :members: :undoc-members: :show-inheritance: :imported-members: Utilities --------- .. autofunction:: gssapi.set_encoding python-gssapi-1.6.1/docs/source/credstore.rst0000664000372000037200000000653213523321513016224 0ustar Common Values for Credentials Store Extensions ============================================== The credentials store extension is an extension introduced by the MIT krb5 library implementation of GSSAPI. It allows for finer control of credentials from within a GSSAPI application. Each mechanism can define keywords to manipulate various aspects of their credentials for storage or retrieval operations. .. note: Only mechanisms that implement keywords can use them, some mechanism may share the same or similar keywords, but their meaning is always local to a specific mechanism. The krb5 mechanism in MIT libraries ----------------------------------- The krb5 mechanism as implemented by MIT libraries supports the credentials store extension with a number of keywords. client_keytab """"""""""""" The `client_keytab` keyword can be used in a credential store when it is used with the :func:`gssapi.raw.ext_cred_store.acquire_cred_from` / :func:`gssapi.raw.ext_cred_store.add_cred_from` functions, to indicate a custom location for a keytab containing client keys. It is not used in the context of calls used to store credentials. The value is a string in the form **type:residual** where **type** can be any keytab storage type understood by the implementation and **residual** is the keytab identifier (usually something like a path). If the string is just a path then the type is defaulted to `FILE`. keytab """""" The `keytab` keyword can be used in a credential store when it is used with the :func:`gssapi.raw.ext_cred_store.acquire_cred_from` / :func:`gssapi.raw.ext_cred_store.add_cred_from` functions, to indicate a custom location for a keytab containing service keys. It is not used in the context of calls used to store credentials. The value is a string in the form **type:residual** where **type** can be any keytab storage type understood by the implementation and **residual** is the keytab identifier (usually something like a path). If the string is just a path then the type is defaulted to `FILE`. ccache """""" The `ccache` keyword can be used to reference a specific credential storage. It can be used both to indicate the source of existing credentials for the :func:`gssapi.raw.ext_cred_store.acquire_cred_from` / :func:`gssapi.raw.ext_cred_store.add_cred_from` functions, as well as the destination storage for the :func:`gssapi.raw.ext_cred_store.store_cred_into` function. The value is a string in the form **type:residual** where type can be any credential cache storage type understood by the implementation and **residual** is the ccache identifier. If the string is just a path then the type is defaulted to `FILE`. Other commonly used types are `DIR`, `KEYRING`, `KCM`. Each type has a different format for the **residual**; refer to the MIT krb5 documentation for more details. rcache """""" The `rcache` keyword can be used to reference a custom replay cache storage. It is used only with the :func:`gssapi.raw.ext_cred_store.acquire_cred_from` / :func:`gssapi.raw.ext_cred_store.add_cred_from` functions for credentials used to accept context establishments, not to initiate contexts. The value is a string in the form **type:residual** where type can be any replay cache storage type understood by the implementation and **residual** is the cache identifier (usually something like a path). If the string is just a path then the type is defaulted to `FILE`. python-gssapi-1.6.1/docs/source/index.rst0000664000372000037200000000242213523321513015333 0ustar .. Python-GSSAPI documentation master file, created by sphinx-quickstart on Tue Jul 2 19:01:09 2013. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Python-GSSAPI: Python bindings for GSSAPI ========================================= Python-GSSAPI provides Python bindings for the GSSAPI C bindings as defined by :rfc:`2744`, as well as several extensions. The package is organized into two parts: a high-level API and a low-level API. The high-level API resides in :mod:`gssapi`, and presents an object-oriented API around GSSAPI. The other part of Python-GSSAPI is the low-level API, which resides in :mod:`gssapi.raw`. The low-level API provides thin wrappers around the corresponding C functions. The high-level API makes use of the low-level API to access underlying GSSAPI functionality. Additionally certain extensions are currently only available from the low-level API. To get started, check out the :doc:`tutorials page ` or jump straight into the :doc:`high-level API documentation `. .. toctree:: :hidden: :maxdepth: 3 gssapi.rst gssapi.raw.rst otherdoc.rst tutorials.rst Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` python-gssapi-1.6.1/docs/source/basic-tutorial.md0000664000372000037200000002035313523321513016741 0ustar A Basic Introduction to GSSAPI ============================== GSSAPI (which stands for "Generic Security Service API") is an standard layer for interfacing with security services. While it supports multiple different mechanisms, it is most commonly used with Kerberos 5 ("krb5" for short). This tutorial will provide a basic introduction to interacting with GSSAPI through Python. *Note*: This file is designed to be runnable using [YALPT](https://github.com/directxman12/yalpt). You can also just read it normally. To start out, we'll import python-gssapi, and save the current FQDN for later: >>> import gssapi, socket >>> FQDN = socket.getfqdn() >>> Note that this assumes you have a KRB5 realm set up, and some relevant functions available in the `REALM` object (see gssapi-console.py in [gssapi_console](https://pypi.python.org/pypi/gssapi_console)), or try `$ run-lit -e gssapi basic-tutorial.md` when you have both gssapi_console and yalpt installed). Any actions performed using the `REALM` object are not part of the GSSAPI library; the `REALM` object simply contains wrappers to krb5 commands generally run separately from the application using GSSAPI. Names and Credentials --------------------- Two important concepts in GSSAPI are *names* and *credentials*. *Names*, as the name suggests, identify different entities, be they users or services. GSSAPI has the concept of different *name types*. These represent different types of names and corresponding syntax for representing names as strings. Suppose we wanted to refer to an HTTP server on the current host. We could refer to it as a *host-based service*, or in the default mechanism form (in this case, for krb5): >>> server_hostbased_name = gssapi.Name('HTTP@' + FQDN, name_type=gssapi.NameType.hostbased_service) >>> server_hostbased_name Name(b'HTTP@sross', ) >>> server_name = gssapi.Name('HTTP/sross@') >>> server_name Name(b'HTTP/sross@', None) >>> These are both effectively the same, but if we *canonicalize* both names with respect to krb5, we'll see that GSSAPI knows they're the same: >>> server_name == server_hostbased_name False >>> server_canon_name = server_name.canonicalize(gssapi.MechType.kerberos) >>> server_hostbased_canon_name = server_hostbased_name.canonicalize(gssapi.MechType.kerberos) >>> server_canon_name == server_hostbased_canon_name True >>> To compare two names of different name types, you should canonicalize them first. *Credentials* represent identification for a user or service. In order to establish secure communication with other entities, a user or service first needs credentials. For the krb5 mechanism, credentials generally represent a handle to the TGT. Credentials may be acquired for a particular name, or the default set of credentials may be acquired. For instance, suppose that we are writing a server, and wish to communicate accept connections as the 'HTTP' service. We would need to acquire credentials as such: >>> REALM.addprinc('HTTP/%s@%s' % (FQDN, REALM.realm)) >>> REALM.extract_keytab('HTTP/%s@%s' % (FQDN, REALM.realm), REALM.keytab) >>> server_creds = gssapi.Credentials(usage='accept', name=server_name) >>> Note that for the krb5 mechanism, in order to acquire credentials with the GSSAPI, the system must already have a way to access those credentials. For users, this generally means that they have already performed a `kinit` (i.e. have cached a TGT), while for services (like above), having a keytab is sufficient. This process is generally performed outside the application using the GSSAPI. Credentials have a *usage*: 'accept' for accepting security contexts, 'initiate' for initiating security contexts, or 'both' for credentials used for both initiating and accepting security contexts. Credentials also have an associated *name*, *lifetime* (which may be `None` for indefinite), and set of *mechanisms* with which the credentials are usable: >>> server_creds.usage 'accept' >>> server_creds.name == server_name True >>> server_creds.lifetime is None True >>> gssapi.MechType.kerberos in server_creds.mechs True >>> gssapi.MechType.kerberos in server_creds.mechs True >>> Each of these settings is setable from the constructor as `usage`, `name`, `lifetime`, and `mechs`. Security Contexts ----------------- *Security contexts* represent active sessions between two different entities. Security contexts are used to verify identities, as well as ensure *integrity* (message signing), *confidentiality* (message encryption), or both for messages exchanged between the two parties. When establishing a security context, the default credentials are used unless otherwise specified. This allows applications to use the user's already acquired credentials: >>> client_ctx = gssapi.SecurityContext(name=server_name, usage='initiate') >>> initial_client_token = client_ctx.step() >>> client_ctx.complete False >>> Just like credentials, security contexts are either initiating contexts, or accepting contexts (they cannot be both). Initiating contexts must specify at least a target name. In this case, we indicate that we wish to establish a context with the HTTP server from above. The http server can then accept that context: >>> server_ctx = gssapi.SecurityContext(creds=server_creds, usage='accept') >>> initial_server_token = server_ctx.step(initial_client_token) >>> As you can see, creating an accepting security context is similar. Here, we specify a set of accepting credentials to use, although this is optional (the defaults will be used if no credentials are specified). Let's finish up the exchange: >>> server_tok = initial_server_token >>> >>> while not (client_ctx.complete and server_ctx.complete): ... client_tok = client_ctx.step(server_tok) ... if not client_tok: ... break ... server_tok = server_ctx.step(client_tok) ... >>> client_ctx.complete and server_ctx.complete True >>> We can now wrap and unwrap messages, using the `wrap` and `unwrap` methods on `SecurityContext`: >>> message = b'some message here' >>> wrapped_message, msg_encrypted = client_ctx.wrap(message, True) >>> message not in wrapped_message True >>> msg_encrypted True >>> server_ctx.unwrap(wrapped_message) UnwrapResult(message=b'some message here', encrypted=True, qop=0) >>> We can use the second parameter to control whether or not we encrypt the messages, or just sign them: >>> signed_message, msg_encrypted = client_ctx.wrap(message, False) >>> msg_encrypted False >>> message in signed_message True >>> server_ctx.unwrap(signed_message) UnwrapResult(message=b'some message here', encrypted=False, qop=0) >>> Manually passing in a second parameter and checking whether or not encryption was used can get tedious, so python-gssapi provides two convenience methods to help with this: `encrypt` and `decrypt`. If the context is set up to use encryption, they will call `wrap` with encryption. If not, they will call `wrap` without encryption. >>> encrypted_message = client_ctx.encrypt(message) >>> encrypted_message != message True >>> server_ctx.decrypt(encrypted_message) b'some message here' >>> Notice that if we try to use `decrypt` a signed message, and exception will be raised, since the context was set up to use encryption (the default): >>> signed_message, _ = client_ctx.wrap(message, False) >>> server_ctx.decrypt(signed_message) Traceback (most recent call last): File "", line 1, in File "", line 2, in decrypt File "/usr/lib/python3.4/site-packages/gssapi/_utils.py", line 167, in check_last_err return func(self, *args, **kwargs) File "/usr/lib/python3.4/site-packages/gssapi/sec_contexts.py", line 295, in decrypt unwrapped_message=res.message) gssapi.exceptions.EncryptionNotUsed: Confidentiality was requested, but not used: The context was established with encryption, but unwrapped message was not encrypted. >>> There you have it: the basics of GSSAPI. You can use the `help` function at the interpreter, or check the [docs](http://pythonhosted.org/gssapi/) for more information. python-gssapi-1.6.1/docs/source/gssapi.raw.rst0000664000372000037200000000616713523321513016314 0ustar Low-Level API ============= .. py:module:: gssapi.raw The low-level API contains a variety of Python functions that map directly to the corresponding C functions. Additionally, it contains several basic wrapper classes that wrap underlying C structs and automatically deallocate them when the Python object itself is deallocated. .. warning:: All methods in both the high-level and low-level APIs may throw the generic GSSError exception. Core RFC 2744 ------------- Names ~~~~~ .. note:: Some functions in the following section will refer to "mechanism names". These are not names of mechanisms. Instead, they are a special form of name specific to a given mechanism. .. automodule:: gssapi.raw.names :members: :undoc-members: Credentials ~~~~~~~~~~~ .. automodule:: gssapi.raw.creds :members: :undoc-members: Security Contexts ~~~~~~~~~~~~~~~~~ .. automodule:: gssapi.raw.sec_contexts :members: :undoc-members: .. automodule:: gssapi.raw.message :members: :undoc-members: Misc ~~~~ .. automodule:: gssapi.raw.oids :members: :undoc-members: .. automodule:: gssapi.raw.misc :members: :undoc-members: .. automodule:: gssapi.raw.types :members: :undoc-members: .. automodule:: gssapi.raw.chan_bindings :members: :undoc-members: Additional RFCs and Extensions ------------------------------ The following is a list of GSSAPI extensions supported by the low-level API. .. note:: While all of these extensions have bindings, they may not be supported by your particularly GSSAPI implementation. In this case, they will not be compiled, and will simply not be available in the :mod:`gssapi.raw` namespace. :rfc:`5588` (GSS-API Extension for Storing Delegated Credentials) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: gssapi.raw.ext_rfc5588 :members: :undoc-members: Credential Store Extensions ~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: gssapi.raw.ext_cred_store :members: :undoc-members: :rfc:`6680` (GSS-API Naming Extensions) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: gssapi.raw.ext_rfc6680 :members: :undoc-members: .. automodule:: gssapi.raw.ext_rfc6680_comp_oid :members: :undoc-members: Credentials Import-Export Extensions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: gssapi.raw.ext_cred_imp_exp :members: :undoc-members: DCE (IOV/AEAD) Extensions ~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: gssapi.raw.ext_dce :members: :undoc-members: IOV MIC Extensions ~~~~~~~~~~~~~~~~~~ .. automodule:: gssapi.raw.ext_iov_mic :members: :undoc-members: Services4User Extensions ~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: gssapi.raw.ext_s4u :members: :undoc-members: Acquiring Credentials With a Password Extensions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: gssapi.raw.ext_password :members: :undoc-members: .. automodule:: gssapi.raw.ext_password_add :members: :undoc-members: Exceptions ---------- .. automodule:: gssapi.raw.exceptions :members: :undoc-members: :show-inheritance: python-gssapi-1.6.1/docs/source/tutorials.rst0000664000372000037200000000023613523321513016253 0ustar Tutorials ========= To get started with using Python-GSSAPI, check out some of the following tutorials: .. toctree:: :maxdepth: 1 basic-tutorial.md python-gssapi-1.6.1/docs/source/otherdoc.rst0000664000372000037200000000045413523321513016036 0ustar Other Documentation =================== This section contain documentation that is not expressed directly in functions documentation, like implementation specific quirks or issues, implementation tips, environment influence on operations and similar. .. toctree:: :maxdepth: 1 credstore.rst python-gssapi-1.6.1/docs/source/conf.py0000664000372000037200000002126013523321513014772 0ustar # -*- coding: utf-8 -*- # # Python-GSSAPI documentation build configuration file, created by # sphinx-quickstart on Tue Jul 2 19:01:09 2013. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) sys.path.insert(0, os.path.abspath('../..')) sys.path.insert(0, os.path.abspath('../custom_extensions')) from custom_recommonmark import AllCodeCommonMarkParser # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', "sphinx.ext.napoleon", 'gssapi_find_missing', 'requires_rfc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # Parsers for different suffixes source_parsers = { '.md': AllCodeCommonMarkParser } # The suffix of source filenames. source_suffix = ['.rst', '.md'] # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Python-GSSAPI' copyright = u'2014, The Python-GSSAPI team' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '1.6.1' # The full version, including alpha/beta/rc tags. release = '1.6.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True add_module_names = False # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Python-GSSAPIdoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'Python-GSSAPI.tex', u'Python-GSSAPI Documentation', u'The Python-GSSAPI team', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'python-gssapi', u'Python-GSSAPI Documentation', [u'The Python-GSSAPI team'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'Python-GSSAPI', u'Python-GSSAPI Documentation', u'The Python-GSSAPI team', 'Python-GSSAPI', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} # which docstring to use for the class # can be 'class', 'init', or 'both' autoclass_content = 'both' # how to order members -- can 'alphabetical', # 'groupwise' (by member type), or 'bysource' autodoc_member_order = 'bysource' python-gssapi-1.6.1/docs/make.bat0000664000372000037200000001510413523321513013600 0ustar @ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source set I18NSPHINXOPTS=%SPHINXOPTS% source if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Python-GSSAPI.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Python-GSSAPI.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end python-gssapi-1.6.1/docs/custom_extensions/0000775000372000037200000000000013523321764015773 5ustar python-gssapi-1.6.1/docs/custom_extensions/gssapi_find_missing.py0000664000372000037200000000327113523321513022357 0ustar from docutils import nodes from sphinx.util.nodes import make_refnode MATCH_RE_RAW = r'\b(([A-Z][A-Za-z0-9]+)+)\b' def setup(app): app.connect('missing-reference', _missing_ref) def _missing_ref(app, env, node, contnode): # skip non-elements if not isinstance(contnode, nodes.Element): return if node.get('refdomain') != 'py': return options = env.domains['py'].find_obj( env, None, None, node.get('reftarget'), node.get('reftype'), 1) if not options: return is_raw = node.get('py:module').startswith('gssapi.raw') if len(options) > 1: raw_opts = [] non_raw_opts = [] for opt in options: full_name, type_info = opt mod_name, _mod_type = type_info if mod_name.startswith('gssapi.raw'): raw_opts.append(opt) else: non_raw_opts.append(opt) if is_raw: if raw_opts: choice = raw_opts[0] elif non_raw_opts: choice = non_raw_opts[0] else: return else: if non_raw_opts: choice = non_raw_opts[0] elif raw_opts: choice = raw_opts[0] else: return else: choice = options[0] choice_name, choice_info = choice choice_mod, choice_type = choice_info if choice_type == 'module': return env.domains['py']._make_module_refnode( app.builder, node.get('refdoc'), choice_name, contnode) else: return make_refnode(app.builder, node.get('refdoc'), choice_mod, choice_name, contnode, choice_name) python-gssapi-1.6.1/docs/custom_extensions/custom_recommonmark.py0000664000372000037200000000047313523321513022425 0ustar from recommonmark.parser import CommonMarkParser from docutils import nodes # treats "verbatim" (code without a language specified) as a code sample class AllCodeCommonMarkParser(CommonMarkParser): def verbatim(self, text): node = nodes.literal_block(text, text) self.current_node.append(node) python-gssapi-1.6.1/docs/custom_extensions/requires_rfc.py0000664000372000037200000000363613523321513021036 0ustar import sys from docutils import nodes from docutils.parsers.rst import roles def setup(app): app.add_role('requires-ext', RequiresExtRole(app)) class RequiresExtRole(object): def __init__(self, app): self.app = app def __call__(self, name, rawtext, text, lineno, inliner, options={}, content=[]): if text.startswith('rfc'): rfc_text = text[3:] rfc_node, rfc_msg = roles.rfc_reference_role( 'rfc', ':rfc:`%s`' % rfc_text, rfc_text, lineno, inliner, options, content) if rfc_msg: # error return (rfc_node, rfc_msg) else: middle_parts = rfc_node + [nodes.Text(" extension", " extension")] else: ext_name = 'gssapi.raw.ext_%s' % text # autodoc has already imported everything try: ext_module = sys.modules[ext_name] except KeyError: ext_title = text + " extension" else: if ext_module.__doc__: ext_title = ext_module.__doc__.splitlines()[0] else: ext_title = text + " extension" ref_nodes, ref_messages = self.app.env.domains['py'].role('mod')( 'mod', rawtext, ext_name, lineno, inliner) if ref_messages: # error return (ref_nodes, ref_messages) title_node = nodes.Text(ext_title, ext_title) ref_nodes[0].clear() ref_nodes[0].append(title_node) middle_parts = ref_nodes begin_text = nodes.Text("requires the ", "requires the ") main_nodes = [begin_text] + middle_parts wrapper_node = nodes.emphasis('', '', *main_nodes) return ([nodes.Text('', ''), wrapper_node, nodes.Text('', '')], []) python-gssapi-1.6.1/README.rst0000777000372000037200000000000013523321513017670 2python-gssapi-1.6.1/README.txtustar python-gssapi-1.6.1/setup.py0000775000372000037200000002646213523321513012771 0ustar #!/usr/bin/env python from __future__ import print_function from setuptools import setup from setuptools import Distribution from setuptools.command.sdist import sdist from setuptools.extension import Extension import subprocess import platform import re import sys import os import shutil import shlex SKIP_CYTHON_FILE = '__dont_use_cython__.txt' if os.path.exists(SKIP_CYTHON_FILE): print("In distributed package, building from C files...", file=sys.stderr) SOURCE_EXT = 'c' else: try: from Cython.Build import cythonize print("Building from Cython files...", file=sys.stderr) SOURCE_EXT = 'pyx' except ImportError: print("Cython not found, building from C files...", file=sys.stderr) SOURCE_EXT = 'c' 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 link_args, compile_args = [ shlex.split(os.environ[e]) 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 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 sys.maxsize > 2 ** 32 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('krb5-config --libs gssapi')) if compile_args is None: if osx_has_gss_framework: compile_args = ['-framework', 'GSS', '-DOSX_HAS_GSS_FRAMEWORK'] elif winkrb_path: compile_args = [ '-I%s' % os.path.join(winkrb_path, 'include'), '-DMS_WIN64' ] elif os.environ.get('MINGW_PREFIX'): compile_args = ['-fPIC'] else: compile_args = shlex.split(get_output('krb5-config --cflags gssapi')) # add in the extra workarounds for different include structures if winkrb_path: prefix = winkrb_path else: try: prefix = get_output('krb5-config 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') 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 = ( '/mingw%d/bin/libgss-3.dll' % (64 if sys.maxsize > 2 ** 32 else 32) ) 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 opt.startswith('gssapi'): 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_lib is None: raise Exception("Could not find main GSSAPI shared library. Please " "try setting GSSAPI_MAIN_LIB yourself or setting " "ENABLE_SUPPORT_DETECTION to 'false'") GSSAPI_LIB = ctypes.CDLL(os.path.join(main_path, main_lib)) # add in the flag that causes us not to compile from Cython when # installing from an sdist class sdist_gssapi(sdist): def run(self): if not self.dry_run: with open(SKIP_CYTHON_FILE, 'w') as flag_file: flag_file.write('COMPILE_FROM_C_ONLY') sdist.run(self) os.remove(SKIP_CYTHON_FILE) DONT_CYTHONIZE_FOR = ('clean',) class GSSAPIDistribution(Distribution, object): def run_command(self, command): self._last_run_command = command Distribution.run_command(self, command) @property def ext_modules(self): if SOURCE_EXT != 'pyx': return getattr(self, '_ext_modules', None) if getattr(self, '_ext_modules', None) is None: return None if getattr(self, '_last_run_command', None) in DONT_CYTHONIZE_FOR: return self._ext_modules if getattr(self, '_cythonized_ext_modules', None) is None: self._cythonized_ext_modules = cythonize( self._ext_modules, language_level=2, ) return self._cythonized_ext_modules @ext_modules.setter def ext_modules(self, mods): self._cythonized_ext_modules = None self._ext_modules = mods @ext_modules.deleter def ext_modules(self): del self._ext_modules del self._cythonized_ext_modules def make_extension(name_fmt, module, **kwargs): """Helper method to remove the repetition in extension declarations.""" source = name_fmt.replace('.', '/') % module + '.' + SOURCE_EXT 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 res 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', 'six >= 1.4.0' ] if sys.version_info < (3, 4): install_requires.append('enum34') setup( name='gssapi', version='1.6.1', author='The Python GSSAPI Team', author_email='sross@redhat.com', packages=['gssapi', 'gssapi.raw', 'gssapi.raw._enum_extensions', 'gssapi.tests'], description='Python GSSAPI Wrapper', long_description=long_desc, license='LICENSE.txt', url="https://github.com/pythongssapi/python-gssapi", classifiers=[ 'Development Status :: 4 - Beta', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', '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' ], distclass=GSSAPIDistribution, cmdclass={'sdist': sdist_gssapi}, 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', 'gss_wrap_iov'), 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'), ]), keywords=['gssapi', 'security'], install_requires=install_requires ) python-gssapi-1.6.1/docs-requirements.txt0000664000372000037200000000010213523321513015445 0ustar Sphinx >= 1.3.1 sphinx-rtd-theme >= 0.2.5b1 recommonmark >= 0.4.0 python-gssapi-1.6.1/README.txt0000664000372000037200000001225613523321513012746 0ustar ============= Python-GSSAPI ============= .. role:: python(code) :language: python .. role:: bash(code) :language: bash .. image:: https://travis-ci.org/pythongssapi/python-gssapi.svg?branch=master :target: https://travis-ci.org/pythongssapi/python-gssapi .. image:: https://badge.fury.io/gh/pythongssapi%2Fpython-gssapi.svg :target: http://badge.fury.io/gh/pythongssapi%2Fpython-gssapi .. image:: https://badge.fury.io/py/gssapi.svg :target: http://badge.fury.io/py/gssapi Python-GSSAPI provides both low-level and high level wrappers around the GSSAPI C libraries. While it focuses on the Kerberos mechanism, it should also be useable with other GSSAPI mechanisms. Documentation for the latest released version (including pre-release versions) can be found at `https://pythongssapi.github.io/python-gssapi/stable `_. Documentation for the latest commit on master can be found at `https://pythongssapi.github.io/python-gssapi/latest `_. Requirements ============ Basic ----- * A working implementation of GSSAPI (such as from MIT Kerberos) which includes header files * a C compiler (such as GCC) * either the `enum34` Python package or Python 3.4+ * the `six` and `decorator` python packages Compiling from Scratch ---------------------- To compile from scratch, you will need Cython >= 0.21.1. For Running the Tests --------------------- * the `nose` package (for tests) * the `shouldbe` package (for tests) * the `k5test` package To install test dependencies using pip: .. code-block:: bash $ pip install -r test-requirements.txt # Optional, for running test suite Installation ============ Easy Way -------- .. code-block:: bash $ pip install gssapi From the Git Repo ----------------- After being sure to install all the requirements, .. code-block:: bash $ git clone https://github.com/pythongssapi/python-gssapi.git $ python setup.py build $ python setup.py install Tests ===== The tests for for Python-GSSAPI live in `gssapi.tests`. In order to run the tests, you must have an MIT Kerberos installation (including the KDC). The tests create a self-contained Kerberos setup, so running the tests will not interfere with any existing Kerberos installations. Structure ========= Python-GSSAPI is composed of two parts: a low-level C-style API which thinly wraps the underlying RFC 2744 methods, and a high-level, Pythonic API (which is itself a wrapper around the low-level API). Examples may be found in the `examples` directory. Low-Level API ------------- The low-level API lives in `gssapi.raw`. The methods contained therein are designed to match closely with the original GSSAPI C methods. All relevant methods and classes may be imported directly from `gssapi.raw`. Extension methods will only be imported if they are present. The low-level API follows the given format: * Names match the RFC 2744 specification, with the :python:`gssapi_` prefix removed * Parameters which use C int constants as enums have :python:`enum.IntEnum` classes defined, and thus may be passed either the enum members or integers * In cases where a specific constant is passed in the C API to represent a default value, :python:`None` should be passed instead * In cases where non-integer constants would be used in the API (i.e. OIDs), enum-like objects have been defined containing named references to values specified in RFC 2744. * Major and minor error codes are returned by raising :python:`gssapi.raw.GSSError`. The major error codes have exceptions defined in in `gssapi.raw.exceptions` to make it easier to catch specific errors or categories of errors. * All other relevant output values are returned via named tuples. High-Level API -------------- The high-level API lives directly under :python:`gssapi`. The classes contained in each file are designed to provide a more Pythonic, Object-Oriented view of GSSAPI. The exceptions from the low-level API, plus several additional exceptions, live in `gssapi.exceptions`. The rest of the classes may be imported directly from `gssapi`. Only classes are exported by `gssapi` -- all functions are methods of classes in the high-level API. Please note that QoP is not supported in the high-level API, since it has been deprecated. Extensions ---------- In addition to RFC 2743/2744, Python-GSSAPI also has support for: * RFC 5587 (Extended GSS Mechanism Inquiry APIs) * RFC 5588 (GSS-API Extension for Storing Delegated Credentials) * (Additional) Credential Store Extension * Services4User * Credentials import-export * RFC 6680 (GSS-API Naming Extensions) * DCE and IOV MIC extensions * `acquire_cred_with_password` and `add_cred_with_password` * GGF Extensions The Team ======== (GitHub usernames in parentheses) * Solly Ross (@directxman12) * Robbie Harwood (@frozencemetery) * Simo Sorce (@simo5) * Hugh Cole-Baker (@sigmaris) Get Involved ============ We welcome new contributions in the form of Issues and Pull Requests on Github. If you would like to join our discussions, you can find us on `Freenode `_ IRC, channel `#python-gssapi `_. python-gssapi-1.6.1/gssapi/0000775000372000037200000000000013523321764012540 5ustar python-gssapi-1.6.1/gssapi/creds.py0000664000372000037200000004030513523321513014204 0ustar from gssapi.raw import creds as rcreds from gssapi.raw import named_tuples as tuples from gssapi._utils import import_gssapi_extension, _encode_dict from gssapi import names rcred_imp_exp = import_gssapi_extension('cred_imp_exp') rcred_s4u = import_gssapi_extension('s4u') rcred_cred_store = import_gssapi_extension('cred_store') rcred_rfc5588 = import_gssapi_extension('rfc5588') class Credentials(rcreds.Creds): """GSSAPI Credentials This class represents a set of GSSAPI credentials which may be used with and/or returned by other GSSAPI methods. It inherits from the low-level GSSAPI :class:`~gssapi.raw.creds.Creds` class, and thus may used with both low-level and high-level API methods. If your implementation of GSSAPI supports the credentials import-export extension, you may pickle and unpickle this object. The constructor either acquires or imports a set of GSSAPI credentials. If the `base` argument is used, an existing :class:`~gssapi.raw.creds.Cred` object from the low-level API is converted into a high-level object. If the `token` argument is used, the credentials are imported using the token, if the credentials import-export extension is supported (:requires-ext:`cred_imp_exp`). Otherwise, the credentials are acquired as per the :meth:`acquire` method. Raises: BadMechanismError BadNameTypeError BadNameError ExpiredCredentialsError MissingCredentialsError """ __slots__ = () def __new__(cls, base=None, token=None, name=None, lifetime=None, mechs=None, usage='both', store=None): # TODO(directxman12): this is missing support for password # (non-RFC method) if base is not None: base_creds = base elif token is not None: if rcred_imp_exp is None: raise NotImplementedError("Your GSSAPI implementation does " "not have support for importing and " "exporting creditials") base_creds = rcred_imp_exp.import_cred(token) else: res = cls.acquire(name, lifetime, mechs, usage, store=store) base_creds = res.creds return super(Credentials, cls).__new__(cls, base_creds) @property def name(self): """Get the name associated with these credentials""" return self.inquire(name=True, lifetime=False, usage=False, mechs=False).name @property def lifetime(self): """Get the remaining lifetime of these credentials""" return self.inquire(name=False, lifetime=True, usage=False, mechs=False).lifetime @property def mechs(self): """Get the mechanisms for these credentials""" return self.inquire(name=False, lifetime=False, usage=False, mechs=True).mechs @property def usage(self): """Get the usage (initiate, accept, or both) of these credentials""" return self.inquire(name=False, lifetime=False, usage=True, mechs=False).usage @classmethod def acquire(cls, name=None, lifetime=None, mechs=None, usage='both', store=None): """Acquire GSSAPI credentials This method acquires credentials. If the `store` argument is used, the credentials will be acquired from the given credential store (if supported). Otherwise, the credentials are acquired from the default store. The credential store information is a dictionary containing mechanisms-specific keys and values pointing to a credential store or stores. Using a non-default store requires support for the credentials store extension. Args: name (Name): the name associated with the credentials, or None for the default name lifetime (int): the desired lifetime of the credentials, or None for indefinite mechs (list): the desired :class:`MechType` OIDs to be used with the credentials, or None for the default set usage (str): the usage for the credentials -- either 'both', 'initiate', or 'accept' store (dict): the credential store information pointing to the credential store from which to acquire the credentials, or None for the default store (:requires-ext:`cred_store`) Returns: AcquireCredResult: the acquired credentials and information about them Raises: BadMechanismError BadNameTypeError BadNameError ExpiredCredentialsError MissingCredentialsError """ if store is None: res = rcreds.acquire_cred(name, lifetime, mechs, usage) else: if rcred_cred_store is None: raise NotImplementedError("Your GSSAPI implementation does " "not have support for manipulating " "credential stores") store = _encode_dict(store) res = rcred_cred_store.acquire_cred_from(store, name, lifetime, mechs, usage) return tuples.AcquireCredResult(cls(base=res.creds), res.mechs, res.lifetime) def store(self, store=None, usage='both', mech=None, overwrite=False, set_default=False): """Store these credentials into the given store This method stores the current credentials into the specified credentials store. If the default store is used, support for :rfc:`5588` is required. Otherwise, support for the credentials store extension is required. :requires-ext:`rfc5588` or :requires-ext:`cred_store` Args: store (dict): the store into which to store the credentials, or None for the default store. usage (str): the usage to store the credentials with -- either 'both', 'initiate', or 'accept' mech (OID): the :class:`MechType` to associate with the stored credentials overwrite (bool): whether or not to overwrite existing credentials stored with the same name, etc set_default (bool): whether or not to set these credentials as the default credentials for the given store. Returns: StoreCredResult: the results of the credential storing operation Raises: GSSError ExpiredCredentialsError MissingCredentialsError OperationUnavailableError DuplicateCredentialsElementError """ if store is None: if rcred_rfc5588 is None: raise NotImplementedError("Your GSSAPI implementation does " "not have support for RFC 5588") return rcred_rfc5588.store_cred(self, usage, mech, overwrite, set_default) else: if rcred_cred_store is None: raise NotImplementedError("Your GSSAPI implementation does " "not have support for manipulating " "credential stores directly") store = _encode_dict(store) return rcred_cred_store.store_cred_into(store, self, usage, mech, overwrite, set_default) def impersonate(self, name=None, lifetime=None, mechs=None, usage='initiate'): """Impersonate a name using the current credentials This method acquires credentials by impersonating another name using the current credentials. :requires-ext:`s4u` Args: name (Name): the name to impersonate lifetime (int): the desired lifetime of the new credentials, or None for indefinite mechs (list): the desired :class:`MechType` OIDs for the new credentials usage (str): the desired usage for the new credentials -- either 'both', 'initiate', or 'accept'. Note that some mechanisms may only support 'initiate'. Returns: Credentials: the new credentials impersonating the given name """ if rcred_s4u is None: raise NotImplementedError("Your GSSAPI implementation does not " "have support for S4U") res = rcred_s4u.acquire_cred_impersonate_name(self, name, lifetime, mechs, usage) return type(self)(base=res.creds) def inquire(self, name=True, lifetime=True, usage=True, mechs=True): """Inspect these credentials for information This method inspects these credentials for information about them. Args: name (bool): get the name associated with the credentials lifetime (bool): get the remaining lifetime for the credentials usage (bool): get the usage for the credentials mechs (bool): get the mechanisms associated with the credentials Returns: InquireCredResult: the information about the credentials, with None used when the corresponding argument was False Raises: MissingCredentialsError InvalidCredentialsError ExpiredCredentialsError """ res = rcreds.inquire_cred(self, name, lifetime, usage, mechs) if res.name is not None: res_name = names.Name(res.name) else: res_name = None return tuples.InquireCredResult(res_name, res.lifetime, res.usage, res.mechs) def inquire_by_mech(self, mech, name=True, init_lifetime=True, accept_lifetime=True, usage=True): """Inspect these credentials for per-mechanism information This method inspects these credentials for per-mechanism information about them. Args: mech (OID): the mechanism for which to retrive the information name (bool): get the name associated with the credentials init_lifetime (bool): get the remaining initiate lifetime for the credentials accept_lifetime (bool): get the remaining accept lifetime for the credentials usage (bool): get the usage for the credentials Returns: InquireCredByMechResult: the information about the credentials, with None used when the corresponding argument was False """ res = rcreds.inquire_cred_by_mech(self, mech, name, init_lifetime, accept_lifetime, usage) if res.name is not None: res_name = names.Name(res.name) else: res_name = None return tuples.InquireCredByMechResult(res_name, res.init_lifetime, res.accept_lifetime, res.usage) def add(self, name, mech, usage='both', init_lifetime=None, accept_lifetime=None, impersonator=None, store=None): """Acquire more credentials to add to the current set This method works like :meth:`acquire`, except that it adds the acquired credentials for a single mechanism to a copy of the current set, instead of creating a new set for multiple mechanisms. Unlike :meth:`acquire`, you cannot pass None desired name or mechanism. If the `impersonator` argument is used, the credentials will impersonate the given name using the impersonator credentials (:requires-ext:`s4u`). If the `store` argument is used, the credentials will be acquired from the given credential store (:requires-ext:`cred_store`). Otherwise, the credentials are acquired from the default store. The credential store information is a dictionary containing mechanisms-specific keys and values pointing to a credential store or stores. Note that the `store` argument is not compatible with the `impersonator` argument. Args: name (Name): the name associated with the credentials mech (OID): the desired :class:`MechType` to be used with the credentials usage (str): the usage for the credentials -- either 'both', 'initiate', or 'accept' init_lifetime (int): the desired initiate lifetime of the credentials, or None for indefinite accept_lifetime (int): the desired accept lifetime of the credentials, or None for indefinite impersonator (Credentials): the credentials to use to impersonate the given name, or None to not acquire normally (:requires-ext:`s4u`) store (dict): the credential store information pointing to the credential store from which to acquire the credentials, or None for the default store (:requires-ext:`cred_store`) Returns: Credentials: the credentials set containing the current credentials and the newly acquired ones. Raises: BadMechanismError BadNameTypeError BadNameError DuplicateCredentialsElementError ExpiredCredentialsError MissingCredentialsError """ if store is not None and impersonator is not None: raise ValueError('You cannot use both the `impersonator` and ' '`store` arguments at the same time') if store is not None: if rcred_cred_store is None: raise NotImplementedError("Your GSSAPI implementation does " "not have support for manipulating " "credential stores") store = _encode_dict(store) res = rcred_cred_store.add_cred_from(store, self, name, mech, usage, init_lifetime, accept_lifetime) elif impersonator is not None: if rcred_s4u is None: raise NotImplementedError("Your GSSAPI implementation does " "not have support for S4U") res = rcred_s4u.add_cred_impersonate_name(self, impersonator, name, mech, usage, init_lifetime, accept_lifetime) else: res = rcreds.add_cred(self, name, mech, usage, init_lifetime, accept_lifetime) return Credentials(res.creds) def export(self): """Export these credentials into a token This method exports the current credentials to a token that can then be imported by passing the `token` argument to the constructor. This is often used to pass credentials between processes. :requires-ext:`cred_imp_exp` Returns: bytes: the exported credentials in token form """ if rcred_imp_exp is None: raise NotImplementedError("Your GSSAPI implementation does not " "have support for importing and " "exporting creditials") return rcred_imp_exp.export_cred(self) # pickle protocol support def __reduce__(self): # the unpickle arguments to new are (base=None, token=self.export()) return (type(self), (None, self.export())) python-gssapi-1.6.1/gssapi/raw/0000775000372000037200000000000013523321764013331 5ustar python-gssapi-1.6.1/gssapi/raw/ext_password.pyx0000664000372000037200000000746213523321513016616 0ustar GSSAPI="BASE" # This ensures that a full module is generated by Cythin # Due to a bug in MIT Kerberos, add_cred_with_password was not properly # exported for some time. In order to work around this, # add_cred_with_password is in its own file. For more information, see: # https://github.com/krb5/krb5/pull/244 from gssapi.raw.cython_types cimport * from gssapi.raw.cython_converters cimport c_get_mech_oid_set from gssapi.raw.cython_converters cimport c_create_oid_set from gssapi.raw.cython_converters cimport c_py_ttl_to_c, c_c_ttl_to_py from gssapi.raw.creds cimport Creds from gssapi.raw.names cimport Name from gssapi.raw.misc import GSSError from gssapi.raw.named_tuples import AcquireCredResult cdef extern from "python_gssapi_ext.h": OM_uint32 gss_acquire_cred_with_password(OM_uint32 *min_stat, const gss_name_t desired_name, const gss_buffer_t password, OM_uint32 ttl, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_creds, gss_OID_set *actual_mechs, OM_uint32 *actual_ttl) nogil def acquire_cred_with_password(Name name not None, password not None, lifetime=None, mechs=None, usage="initiate"): """ acquire_cred_with_password(name, password, lifetime=None, mechs=None, \ usage="initiate") Acquire credentials through provided password. This function is originally from Solaris and is not documented by either MIT or Heimdal. In general, it functions similarly to :func:`acquire_cred`. Args: name (Name): the name to acquire credentials for password (bytes): the password used to acquire credentialss with lifetime (int): the lifetime for the credentials (or None for indefinite) mechs ([MechType]): the desired mechanisms for which the credentials should work (or None for the default set) usage (str): usage type for credentials. Possible values: 'initiate' (default), 'accept', 'both' (failsafe). Returns: AcquireCredResult: the resulting credentials, the actual mechanisms with which they may be used, and their actual lifetime (or None for indefinite or not supported) Raises: GSSError """ cdef gss_buffer_desc password_buffer = gss_buffer_desc(len(password), password) cdef OM_uint32 input_ttl = c_py_ttl_to_c(lifetime) cdef gss_OID_set desired_mechs if mechs is not None: desired_mechs = c_get_mech_oid_set(mechs) else: desired_mechs = GSS_C_NO_OID_SET cdef gss_cred_usage_t c_usage if usage == "initiate": c_usage = GSS_C_INITIATE elif usage == "accept": c_usage = GSS_C_ACCEPT else: c_usage = GSS_C_BOTH cdef gss_cred_id_t creds cdef gss_OID_set actual_mechs cdef OM_uint32 actual_ttl cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_acquire_cred_with_password( &min_stat, name.raw_name, &password_buffer, input_ttl, desired_mechs, c_usage, &creds, &actual_mechs, &actual_ttl) cdef OM_uint32 tmp_min_stat if mechs is not None: gss_release_oid_set(&tmp_min_stat, &desired_mechs) cdef Creds rc = Creds() if maj_stat == GSS_S_COMPLETE: rc.raw_creds = creds return AcquireCredResult(rc, c_create_oid_set(actual_mechs), c_c_ttl_to_py(actual_ttl)) else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/types.pyx0000664000372000037200000002005713523321513015233 0ustar GSSAPI="BASE" # This ensures that a full module is generated by Cython from gssapi.raw.cython_types cimport * from gssapi.raw.cython_converters cimport c_make_oid from gssapi.raw.oids cimport OID from gssapi.raw._enum_extensions import ExtendableEnum from enum import IntEnum import collections import copy import numbers import operator import six if six.PY2: from collections import MutableSet else: from collections.abc import MutableSet class NameType(object): """ GSSAPI Name Types This enum-like object represents GSSAPI name types (to be used with :func:`import_name`, etc) """ # mech-agnostic name types hostbased_service = c_make_oid(GSS_C_NT_HOSTBASED_SERVICE) # NB(directxman12): skip GSS_C_NT_HOSTBASED_SERVICE_X since it's deprecated user = c_make_oid(GSS_C_NT_USER_NAME) anonymous = c_make_oid(GSS_C_NT_ANONYMOUS) machine_uid = c_make_oid(GSS_C_NT_MACHINE_UID_NAME) string_uid = c_make_oid(GSS_C_NT_STRING_UID_NAME) export = c_make_oid(GSS_C_NT_EXPORT_NAME) # mech-specific name types are added automatically on import class RequirementFlag(IntEnum, metaclass=ExtendableEnum): """ GSSAPI Requirement Flags This :class:`~enum.IntEnum` represents flags used with the :class:`SecurityContext`-related methods (e.g. :func:`init_sec_context`) The numbers behind the values correspond directly to their C counterparts. """ delegate_to_peer = GSS_C_DELEG_FLAG mutual_authentication = GSS_C_MUTUAL_FLAG replay_detection = GSS_C_REPLAY_FLAG out_of_sequence_detection = GSS_C_SEQUENCE_FLAG confidentiality = GSS_C_CONF_FLAG integrity = GSS_C_INTEG_FLAG anonymity = GSS_C_ANON_FLAG protection_ready = GSS_C_PROT_READY_FLAG transferable = GSS_C_TRANS_FLAG class AddressType(IntEnum, metaclass=ExtendableEnum): """ GSSAPI Channel Bindings Address Types This :class:`~enum.IntEnum` represents the various address types used with the :class:`ChannelBindings` structure. The numbers behind the values correspond directly to their C counterparts. There is no value for ``GSS_C_AF_UNSPEC``, since this is represented by ``None``. """ # unspecified = GSS_C_AF_UNSPEC # None --> GSS_C_AF_UNSPEC local = GSS_C_AF_LOCAL ip = GSS_C_AF_INET arpanet = GSS_C_AF_IMPLINK # ARPAnet support, heh, heh pup = GSS_C_AF_PUP chaos = GSS_C_AF_CHAOS xerox_ns = GSS_C_AF_NS # and XEROX too? nbs = GSS_C_AF_NBS ecma = GSS_C_AF_ECMA datakit = GSS_C_AF_DATAKIT ccitt = GSS_C_AF_CCITT ibm_sna = GSS_C_AF_SNA decnet = GSS_C_AF_DECnet dli = GSS_C_AF_DLI lat = GSS_C_AF_LAT hyperchannel = GSS_C_AF_HYLINK appletalk = GSS_C_AF_APPLETALK # this list just keeps getting better bisync = GSS_C_AF_BSC dss = GSS_C_AF_DSS osi_tp4 = GSS_C_AF_OSI x25 = GSS_C_AF_X25 null = GSS_C_AF_NULLADDR class MechType(object): """ GSSAPI Mechanism Types This enum-like object contains any mechanism :class:`OID` values registered by imported mechanisms. """ pass # these are added in by the individual mechanism files on import class GenericFlagSet(MutableSet): """A set backed by a 32-bit integer This is a set backed by a 32 bit integer. the members are integers where only one bit is set. The class supports normal set operations, as well as traditional "flag set" operations, such as bitwise AND, OR, and XOR. """ __slots__ = '_val' MAX_VAL = 1 << 31 def __init__(self, flags=None): self._val = 0 if isinstance(flags, GenericFlagSet): self._val = flags._val if isinstance(flags, numbers.Integral): self._val = int(flags) elif flags is not None: for flag in flags: self._val |= flag def __contains__(self, flag): return self._val & flag def __iter__(self): i = 1 while i < self.MAX_VAL: if i & self._val: yield i i <<= 1 def __len__(self): # get the Hamming weight of _val cdef unsigned int size = 0 cdef unsigned int i = 1 while i < self.MAX_VAL: if i & self._val: size += 1 i <<= 1 return size def add(self, flag): self._val |= flag def discard(self, flag): # NB(directxman12): the 0xFFFFFFFF mask is needed to # make Python's invert work properly self._val = self._val & (~flag & 0xFFFFFFFF) def __and__(self, other): if isinstance(other, numbers.Integral): return self._val & other else: return super(GenericFlagSet, self).__and__(other) def __rand__(self, other): return self.__and__(other) def __or__(self, other): if isinstance(other, numbers.Integral): return self._val | other else: return super(GenericFlagSet, self).__or__(other) def __ror__(self, other): return self.__or__(other) def __xor__(self, other): if isinstance(other, numbers.Integral): return self._val ^ other else: return super(GenericFlagSet, self).__xor__(other) def __rxor__(self, other): return self.__xor__(other) def __int__(self): return self._val def __long__(self): return long(self._val) def __eq__(self, other): if isinstance(other, GenericFlagSet): return self._val == other._val else: return False def __ne__(self, other): return not self.__eq__(other) def __repr__(self): bits = "{0:032b}".format(self._val & 0xFFFFFFFF) return "<{name} {bits}>".format(name=type(self).__name__, bits=bits) class IntEnumFlagSet(GenericFlagSet): """A set backed by a 32-bit integer with enum members This class is a :class:`GenericFlagSet` where the returned members are values in an :class:`~enum.IntEnum`. It functions exactly like a `GenericFlagSet`, except that it also supports bitwise operations with the enum values. """ __slots__ = ('_val', '_enum') def __init__(self, enum, flags=None): if not issubclass(enum, IntEnum): raise Exception('"enum" not an Enum') self._enum = enum super(IntEnumFlagSet, self).__init__(flags) def __iter__(self): for i in super(IntEnumFlagSet, self).__iter__(): yield self._enum(i) def __repr__(self): fmt_str = "{name}({enum}, [{vals}])" vals = ', '.join([elem.name for elem in self]) return fmt_str.format(name=type(self).__name__, enum=self._enum.__name__, vals=vals) def __and__(self, other): if isinstance(other, self._enum): return other in self else: res = super(IntEnumFlagSet, self).__and__(other) if isinstance(res, GenericFlagSet): return IntEnumFlagSet(self._enum, res) else: return res def __or__(self, other): if isinstance(other, self._enum): cpy = copy.copy(self) cpy.add(other) return cpy else: res = super(IntEnumFlagSet, self).__or__(other) if isinstance(res, GenericFlagSet): return IntEnumFlagSet(self._enum, res) else: return res def __xor__(self, other): if isinstance(other, self._enum): cpy = copy.copy(self) cpy._val = cpy._val ^ other return cpy else: res = super(IntEnumFlagSet, self).__xor__(other) if isinstance(res, GenericFlagSet): return IntEnumFlagSet(self._enum, res) else: return res def __sub__(self, other): return IntEnumFlagSet(self._enum, super(IntEnumFlagSet, self).__sub__(other)) @classmethod def _from_iterable(cls, it): return GenericFlagSet(it) python-gssapi-1.6.1/gssapi/raw/ext_dce.pxd0000664000372000037200000000077013523321513015455 0ustar from gssapi.raw.cython_types cimport gss_buffer_desc, OM_uint32 cdef extern from "python_gssapi_ext.h": ctypedef struct gss_iov_buffer_desc: OM_uint32 type gss_buffer_desc buffer ctypedef gss_iov_buffer_desc* gss_iov_buffer_t cdef class IOV: cdef int iov_len cdef bint c_changed cdef bint _unprocessed cdef list _buffs cdef gss_iov_buffer_desc *_iov cdef gss_iov_buffer_desc* __cvalue__(IOV self) except NULL cdef _recreate_python_values(IOV self) python-gssapi-1.6.1/gssapi/raw/ext_rfc5588.pyx0000664000372000037200000000637413523321513016061 0ustar from gssapi.raw.cython_types cimport * from gssapi.raw.names cimport Name from gssapi.raw.creds cimport Creds from gssapi.raw.oids cimport OID from gssapi.raw.cython_converters cimport c_create_oid_set GSSAPI="BASE" # This ensures that a full module is generated by Cython from gssapi.raw.cython_converters cimport c_get_mech_oid_set from gssapi.raw.cython_converters cimport c_c_ttl_to_py, c_py_ttl_to_c from gssapi.raw.named_tuples import StoreCredResult from gssapi.raw.misc import GSSError cdef extern from "python_gssapi.h": OM_uint32 gss_store_cred(OM_uint32 *min_stat, gss_cred_id_t input_creds, gss_cred_usage_t cred_usage, gss_OID desired_mech, OM_uint32 overwrite_cred, OM_uint32 default_cred, gss_OID_set *elements_stored, gss_cred_usage_t *actual_usage) nogil def store_cred(Creds creds not None, usage='both', OID mech=None, bint overwrite=False, bint set_default=False): """ store_cred(creds, usage='both', mech=None, overwrite=False, \ set_default=False) Store credentials into the default store. This method stores the given credentials into the default store. They may then be retrieved later using :func:`acquire_cred`. Args: creds (Creds): the credentials to store usage (str): the usage to store the credentials with -- either 'both', 'initiate', or 'accept' mech (OID): the mechansim to associate with the stored credentials overwrite (bool): whether or not to overwrite existing credentials stored with the same name, etc set_default (bool): whether or not to set these credentials as the default credentials for the given store. Returns: StoreCredResult: the results of the credential storing operation Raises: GSSError ExpiredCredentialsError MissingCredentialsError OperationUnavailableError DuplicateCredentialsElementError """ cdef gss_OID desired_mech if mech is not None: desired_mech = &mech.raw_oid else: desired_mech = GSS_C_NO_OID cdef gss_cred_usage_t c_usage if usage == 'initiate': c_usage = GSS_C_INITIATE elif usage == 'accept': c_usage = GSS_C_ACCEPT else: c_usage = GSS_C_BOTH cdef gss_cred_id_t c_creds = creds.raw_creds cdef gss_OID_set actual_mech_types cdef gss_cred_usage_t actual_usage cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_store_cred(&min_stat, c_creds, c_usage, desired_mech, overwrite, set_default, &actual_mech_types, &actual_usage) if maj_stat == GSS_S_COMPLETE: if actual_usage == GSS_C_INITIATE: py_actual_usage = 'initiate' elif actual_usage == GSS_C_ACCEPT: py_actual_usage = 'accept' else: py_actual_usage = 'both' return StoreCredResult(c_create_oid_set(actual_mech_types), py_actual_usage) else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/oids.pxd0000664000372000037200000000047513523321513015002 0ustar from gssapi.raw.cython_types cimport gss_OID_desc cdef class OID: # NB(directxman12): this is a pointer, not a gss_OID_desc cdef gss_OID_desc raw_oid cdef bint _free_on_dealloc cdef int _copy_from(OID self, gss_OID_desc base) except -1 cdef int _from_bytes(OID self, object elements) except -1 python-gssapi-1.6.1/gssapi/raw/ext_password_add.pyx0000664000372000037200000001074113523321513017420 0ustar GSSAPI="BASE" # This ensures that a full module is generated by Cythin # Due to a bug in MIT Kerberos, add_cred_with_password was not properly # exported for some time. In order to work around this, # add_cred_with_password is in its own file. For more information, see: # https://github.com/krb5/krb5/pull/244 from gssapi.raw.cython_types cimport * from gssapi.raw.cython_converters cimport c_get_mech_oid_set from gssapi.raw.cython_converters cimport c_create_oid_set from gssapi.raw.cython_converters cimport c_py_ttl_to_c, c_c_ttl_to_py from gssapi.raw.creds cimport Creds from gssapi.raw.names cimport Name from gssapi.raw.oids cimport OID from gssapi.raw.misc import GSSError from gssapi.raw.named_tuples import AddCredResult cdef extern from "python_gssapi_ext.h": OM_uint32 gss_add_cred_with_password(OM_uint32 *min_stat, const gss_cred_id_t input_cred_handle, const gss_name_t desired_name, const gss_OID desired_mech, const gss_buffer_t password, gss_cred_usage_t cred_usage, OM_uint32 initiator_ttl, OM_uint32 acceptor_ttl, gss_cred_id_t *output_creds, gss_OID_set *actual_mechs, OM_uint32 *actual_init_ttl, OM_uint32 *actual_accept_ttl) nogil def add_cred_with_password(Creds input_cred not None, Name name not None, OID mech not None, password not None, usage="initiate", init_lifetime=None, accept_lifetime=None): """ add_cred_with_password(input_cred, name, mech, password, \ usage='initiate', init_lifetime=None, accept_lifetime=None) Add a credential-element to a credential using provided password. This function is originally from Solaris and is not documented by either MIT or Heimdal. In general, it functions similarly to :func:`add_cred`. Args: input_cred (Creds): the credentials to add to name (Name): the name to acquire credentials for mech (MechType): the desired mechanism. Note that this is both singular and required password (bytes): the password used to acquire credentialss with usage (str): the usage type for the credentials: may be 'initiate', 'accept', or 'both' init_lifetime (int): the lifetime for the credentials to remain valid when using them to initiate security contexts (or None for indefinite) accept_lifetime (int): the lifetime for the credentials to remain valid when using them to accept security contexts (or None for indefinite) Returns: AddCredResult: the actual mechanisms with which the credentials may be used, the actual initiator TTL, and the actual acceptor TTL (the TTLs may be None for indefinite or not supported) Raises: GSSError """ cdef gss_buffer_desc password_buffer = gss_buffer_desc(len(password), password) cdef gss_cred_usage_t c_usage if usage == "initiate": c_usage = GSS_C_INITIATE elif usage == "accept": c_usage = GSS_C_ACCEPT else: c_usage = GSS_C_BOTH cdef OM_uint32 input_initiator_ttl = c_py_ttl_to_c(init_lifetime) cdef OM_uint32 input_acceptor_ttl = c_py_ttl_to_c(accept_lifetime) cdef gss_cred_id_t creds cdef gss_OID_set actual_mechs cdef OM_uint32 actual_initiator_ttl cdef OM_uint32 actual_acceptor_ttl cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_add_cred_with_password( &min_stat, input_cred.raw_creds, name.raw_name, &mech.raw_oid, &password_buffer, c_usage, input_initiator_ttl, input_acceptor_ttl, &creds, &actual_mechs, &actual_initiator_ttl, &actual_acceptor_ttl) cdef Creds rc if maj_stat == GSS_S_COMPLETE: rc = Creds() rc.raw_creds = creds return AddCredResult(rc, c_create_oid_set(actual_mechs), c_c_ttl_to_py(actual_initiator_ttl), c_c_ttl_to_py(actual_acceptor_ttl)) else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/sec_contexts.pyx0000664000372000037200000005626213523321513016577 0ustar GSSAPI="BASE" # This ensures that a full module is generated by Cython from libc.stdlib cimport free from gssapi.raw.cython_types cimport * from gssapi.raw.cython_converters cimport c_py_ttl_to_c, c_c_ttl_to_py from gssapi.raw.creds cimport Creds from gssapi.raw.names cimport Name from gssapi.raw.oids cimport OID from gssapi.raw.chan_bindings cimport ChannelBindings from gssapi.raw.types import MechType, RequirementFlag, IntEnumFlagSet from gssapi.raw.misc import GSSError from gssapi.raw.named_tuples import AcceptSecContextResult from gssapi.raw.named_tuples import InitSecContextResult from gssapi.raw.named_tuples import InquireContextResult cdef extern from "python_gssapi.h": OM_uint32 gss_init_sec_context(OM_uint32 *min_stat, const gss_cred_id_t initiator_creds, gss_ctx_id_t *context, const gss_name_t target_name, const gss_OID mech_type, OM_uint32 flags, OM_uint32 ttl, const gss_channel_bindings_t chan_bdgs, const gss_buffer_t input_token, gss_OID *actual_mech_type, gss_buffer_t output_token, OM_uint32 *actual_flags, OM_uint32 *actual_ttl) nogil OM_uint32 gss_accept_sec_context(OM_uint32 *min_stat, gss_ctx_id_t *context, const gss_cred_id_t acceptor_creds, const gss_buffer_t input_token, const gss_channel_bindings_t chan_bdgs, const gss_name_t *initiator_name, gss_OID *mech_type, gss_buffer_t output_token, OM_uint32 *flags, OM_uint32 *ttl, gss_cred_id_t *delegated_creds) nogil OM_uint32 gss_delete_sec_context(OM_uint32 *min_stat, gss_ctx_id_t *context, gss_buffer_t output_token) nogil OM_uint32 gss_process_context_token(OM_uint32 *min_stat, const gss_ctx_id_t context, const gss_buffer_t token) nogil OM_uint32 gss_context_time(OM_uint32 *min_stat, const gss_ctx_id_t context_handle, OM_uint32 *ttl) nogil OM_uint32 gss_inquire_context(OM_uint32 *min_stat, const gss_ctx_id_t context, gss_name_t *initiator_name, gss_name_t *target_name, OM_uint32 *ttl, gss_OID *mech_type, OM_uint32 *ctx_flags, int *locally_initiated, int *is_open) nogil OM_uint32 gss_export_sec_context(OM_uint32 *min_stat, gss_ctx_id_t *context, gss_buffer_t interprocess_token) nogil OM_uint32 gss_import_sec_context(OM_uint32 *min_stat, const gss_buffer_t interprocess_token, gss_ctx_id_t *context) nogil cdef class SecurityContext: """ A GSSAPI Security Context """ # defined in pxd # cdef gss_ctx_id_t raw_ctx def __cinit__(self, SecurityContext cpy=None): if cpy is not None: self.raw_ctx = cpy.raw_ctx cpy.raw_ctx = GSS_C_NO_CONTEXT else: self.raw_ctx = GSS_C_NO_CONTEXT property _started: """Whether the underlying context is NULL.""" def __get__(self): return self.raw_ctx is not NULL def __dealloc__(self): # basically just deleteSecContext, but we are not # allowed to call methods here cdef OM_uint32 maj_stat, min_stat if self.raw_ctx is not GSS_C_NO_CONTEXT: # local deletion only maj_stat = gss_delete_sec_context(&min_stat, &self.raw_ctx, GSS_C_NO_BUFFER) if maj_stat != GSS_S_COMPLETE: raise GSSError(maj_stat, min_stat) self.raw_ctx = NULL # TODO(directxman12): figure out whether GSS_C_NO_NAME can be passed in here def init_sec_context(Name target_name not None, Creds creds=None, SecurityContext context=None, OID mech=None, flags=None, lifetime=None, ChannelBindings channel_bindings=None, input_token=None): """ init_sec_context(target_name, creds=None, context=None, mech=None, \ flags=None, lifetime=None, channel_bindings=None, input_token=None) Initiate a GSSAPI security context. This method initiates a GSSAPI security context, targeting the given target name. To create a basic context, just provide the target name. Further calls used to update the context should pass in the output context of the last call, as well as the input token received from the acceptor. Warning: This changes the input context! Args: target_name (Name): the target for the security context creds (Creds): the credentials to use to initiate the context, or None to use the default credentials context (SecurityContext): the security context to update, or None to create a new context mech (MechType): the mechanism type for this security context, or None for the default mechanism type flags (list): the flags to request for the security context, or None to use the default set: mutual_authentication and out_of_sequence_detection. This may also be an :class:`IntEnumFlagSet` lifetime (int): the request lifetime of the security context (a value of 0 or None means indefinite) channel_bindings (ChannelBindings): The channel bindings (or None for no channel bindings) input_token (bytes): the token to use to update the security context, or None if you are creating a new context Returns: InitSecContextResult: the output security context, the actual mech type, the actual flags used, the output token to send to the acceptor, the actual lifetime of the context (or None if not supported or indefinite), and whether or not more calls are needed to finish the initiation. Raises: InvalidTokenError InvalidCredentialsError MissingCredentialsError ExpiredCredentialsError BadChannelBindingsError BadMICError ExpiredTokenError DuplicateTokenError MissingContextError BadNameTypeError BadNameError BadMechanismError """ cdef gss_OID mech_oid if mech is not None: mech_oid = &mech.raw_oid else: mech_oid = GSS_C_NO_OID # TODO(directxman12): should we default to this? cdef OM_uint32 req_flags = IntEnumFlagSet(RequirementFlag, flags or [ RequirementFlag.mutual_authentication, RequirementFlag.out_of_sequence_detection]) cdef gss_channel_bindings_t bdng if channel_bindings is not None: bdng = channel_bindings.__cvalue__() else: bdng = GSS_C_NO_CHANNEL_BINDINGS cdef gss_buffer_desc input_token_buffer = gss_buffer_desc(0, NULL) cdef OM_uint32 input_ttl = c_py_ttl_to_c(lifetime) cdef SecurityContext output_context = context if output_context is None: output_context = SecurityContext() cdef gss_cred_id_t act_cred if creds is not None: act_cred = creds.raw_creds else: act_cred = GSS_C_NO_CREDENTIAL if input_token is not None: input_token_buffer.value = input_token input_token_buffer.length = len(input_token) cdef gss_OID actual_mech_type = GSS_C_NO_OID cdef gss_buffer_desc output_token_buffer = gss_buffer_desc(0, NULL) cdef OM_uint32 ret_flags cdef OM_uint32 output_ttl cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_init_sec_context(&min_stat, act_cred, &output_context.raw_ctx, target_name.raw_name, mech_oid, req_flags, input_ttl, bdng, &input_token_buffer, &actual_mech_type, &output_token_buffer, &ret_flags, &output_ttl) output_token = None if output_token_buffer.length: output_token = \ (output_token_buffer.value)[:output_token_buffer.length] cdef OM_uint32 tmp_min_stat gss_release_buffer(&tmp_min_stat, &output_token_buffer) if channel_bindings is not None: free(bdng) cdef OID output_mech_type = OID() if maj_stat == GSS_S_COMPLETE or maj_stat == GSS_S_CONTINUE_NEEDED: if actual_mech_type is not GSS_C_NO_OID: output_mech_type.raw_oid = actual_mech_type[0] return InitSecContextResult(output_context, output_mech_type, IntEnumFlagSet(RequirementFlag, ret_flags), output_token, c_c_ttl_to_py(output_ttl), maj_stat == GSS_S_CONTINUE_NEEDED) else: raise GSSError(maj_stat, min_stat, token=output_token) def accept_sec_context(input_token not None, Creds acceptor_creds=None, SecurityContext context=None, ChannelBindings channel_bindings=None): """ accept_sec_context(input_token, acceptor_creds=None, context=None, \ channel_bindings=None) Accept a GSSAPI security context. This method accepts a GSSAPI security context using a token sent by the initiator, using the given credentials. It can either be used to accept a security context and create a new security context object, or to update an existing security context object. Warning: This changes the input context! Args: input_token (bytes): the token sent by the context initiator acceptor_creds (Creds): the credentials to be used to accept the context (or None to use the default credentials) context (SecurityContext): the security context to update (or None to create a new security context object) channel_bindings (ChannelBindings): The channel bindings (or None for no channel bindings) Returns: AcceptSecContextResult: the resulting security context, the initiator name, the mechanism being used, the output token, the flags in use, the lifetime of the context (or None for indefinite or not supported), the delegated credentials (valid only if the delegate_to_peer flag is set), and whether or not further token exchanges are needed to finalize the security context. Raises: InvalidTokenError InvalidCredentialsError MissingCredentialsError ExpiredCredentialsError BadChannelBindingsError MissingContextError BadMICError ExpiredTokenError DuplicateTokenError BadMechanismError """ cdef gss_channel_bindings_t bdng if channel_bindings is not None: bdng = channel_bindings.__cvalue__() else: bdng = GSS_C_NO_CHANNEL_BINDINGS cdef gss_buffer_desc input_token_buffer = gss_buffer_desc(len(input_token), input_token) cdef SecurityContext output_context = context if output_context is None: output_context = SecurityContext() cdef gss_cred_id_t act_acceptor_cred if acceptor_creds is not None: act_acceptor_cred = acceptor_creds.raw_creds else: act_acceptor_cred = GSS_C_NO_CREDENTIAL cdef gss_name_t initiator_name cdef gss_OID mech_type # GSS_C_EMPTY_BUFFER cdef gss_buffer_desc output_token_buffer = gss_buffer_desc(0, NULL) cdef OM_uint32 ret_flags cdef OM_uint32 output_ttl cdef gss_cred_id_t delegated_cred cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_accept_sec_context(&min_stat, &output_context.raw_ctx, act_acceptor_cred, &input_token_buffer, bdng, &initiator_name, &mech_type, &output_token_buffer, &ret_flags, &output_ttl, &delegated_cred) output_token = None if output_token_buffer.length: output_token = \ (output_token_buffer.value)[:output_token_buffer.length] cdef OM_uint32 tmp_min_stat gss_release_buffer(&tmp_min_stat, &output_token_buffer) if channel_bindings is not None: free(bdng) cdef Name on = Name() cdef Creds oc = None cdef OID py_mech_type if maj_stat == GSS_S_COMPLETE or maj_stat == GSS_S_CONTINUE_NEEDED: if output_ttl == GSS_C_INDEFINITE: output_ttl_py = None else: output_ttl_py = output_ttl on.raw_name = initiator_name if delegated_cred is not NULL: oc = Creds() oc.raw_creds = delegated_cred if mech_type is not NULL: py_mech_type = OID() py_mech_type.raw_oid = mech_type[0] else: py_mech_type = None return AcceptSecContextResult(output_context, on, py_mech_type, output_token, IntEnumFlagSet(RequirementFlag, ret_flags), output_ttl_py, oc, maj_stat == GSS_S_CONTINUE_NEEDED) else: raise GSSError(maj_stat, min_stat, token=output_token) def inquire_context(SecurityContext context not None, initiator_name=True, target_name=True, lifetime=True, mech=True, flags=True, locally_init=True, complete=True): """ inquire_context(context, initiator_name=True, target_name=True, \ lifetime=True, mech=True, flags=True, locally_init=True, complete=True) Get information about a security context. This method obtains information about a security context, including the initiator and target names, as well as the TTL, mech, flags, and its current state (open vs closed). Note: the target name may be ``None`` if it would have been ``GSS_C_NO_NAME`` Args: context (SecurityContext): the context in question Returns: InquireContextResult: the initiator name, the target name, the TTL (can be None for indefinite or not supported), the mech type, the flags, whether or not the context was locally initiated, and whether or not the context is currently fully established Raises: MissingContextError """ cdef gss_name_t output_init_name cdef gss_name_t *init_name_ptr = NULL if initiator_name: init_name_ptr = &output_init_name cdef gss_name_t output_target_name cdef gss_name_t *target_name_ptr = NULL if target_name: target_name_ptr = &output_target_name cdef OM_uint32 ttl cdef OM_uint32 *ttl_ptr = NULL if lifetime: ttl_ptr = &ttl cdef gss_OID output_mech_type cdef gss_OID *mech_type_ptr = NULL if mech: mech_type_ptr = &output_mech_type cdef OM_uint32 output_flags cdef OM_uint32 *flags_ptr = NULL if flags: flags_ptr = &output_flags cdef int output_locally_init cdef int *locally_init_ptr = NULL if locally_init: locally_init_ptr = &output_locally_init cdef int is_complete cdef int *is_complete_ptr = NULL if complete: is_complete_ptr = &is_complete cdef OM_uint32 maj_stat, min_stat maj_stat = gss_inquire_context(&min_stat, context.raw_ctx, init_name_ptr, target_name_ptr, ttl_ptr, mech_type_ptr, flags_ptr, locally_init_ptr, is_complete_ptr) cdef Name sn cdef OID py_mech_type cdef Name tn if maj_stat == GSS_S_COMPLETE: if initiator_name: sn = Name() sn.raw_name = output_init_name else: sn = None if target_name and output_target_name != GSS_C_NO_NAME: tn = Name() tn.raw_name = output_target_name else: tn = None if mech: py_mech_type = OID() py_mech_type.raw_oid = output_mech_type[0] else: py_mech_type = None if lifetime and ttl != GSS_C_INDEFINITE: py_ttl = ttl else: py_ttl = None if flags: py_flags = IntEnumFlagSet(RequirementFlag, output_flags) else: py_flags = None if locally_init: py_locally_init = output_locally_init else: py_locally_init = None if complete: py_complete = is_complete else: py_complete = None return InquireContextResult(sn, tn, py_ttl, py_mech_type, py_flags, py_locally_init, py_complete) else: raise GSSError(maj_stat, min_stat) def context_time(SecurityContext context not None): """ context_time(context) Get the amount of time for which the given context will remain valid. This method determines the amount of time for which the given security context will remain valid. An expired context will give a result of 0. Args: context (SecurityContext): the security context in question Returns: int: the number of seconds for which the context will be valid Raises: ExpiredContextError MissingContextError """ cdef OM_uint32 ttl cdef OM_uint32 maj_stat, min_stat maj_stat = gss_context_time(&min_stat, context.raw_ctx, &ttl) if maj_stat == GSS_S_COMPLETE: return ttl else: raise GSSError(maj_stat, min_stat) def process_context_token(SecurityContext context not None, token): """ process_context_token(context, token) Process a token asynchronously. This method provides a way to process a token, even if the given security context is not expecting one. For example, if the initiator has the initSecContext return that the context is complete, but the acceptor is unable to accept the context, and wishes to send a token to the initiator, letting the initiator know of the error. Warning: This method has been essentially deprecated by :rfc:`2744`. Args: context (SecurityContext): the security context against which to process the token token (bytes): the token to process Raises: InvalidTokenError MissingContextError """ cdef gss_buffer_desc token_buffer = gss_buffer_desc(len(token), token) cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_process_context_token(&min_stat, context.raw_ctx, &token_buffer) if maj_stat != GSS_S_COMPLETE: raise GSSError(maj_stat, min_stat) def import_sec_context(token not None): """ import_sec_context(token) Import a context from another process. This method imports a security context established in another process by reading the specified token which was output by :func:`export_sec_context`. Raises: MissingContextError InvalidTokenError OperationUnavailableError UnauthorizedError """ cdef gss_buffer_desc token_buffer = gss_buffer_desc(len(token), token) cdef gss_ctx_id_t ctx cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_import_sec_context(&min_stat, &token_buffer, &ctx) if maj_stat == GSS_S_COMPLETE: res = SecurityContext() res.raw_ctx = ctx return res else: raise GSSError(maj_stat, min_stat) def export_sec_context(SecurityContext context not None): """ export_sec_context(context) Export a context for use in another process. This method exports a security context, deactivating in the current process and creating a token which can then be imported into another process with :func:`import_sec_context`. Warning: this modifies the input context Args: context (SecurityContext): the context to send to another process Returns: bytes: the output token to be imported Raises: ExpiredContextError MissingContextError OperationUnavailableError """ cdef gss_buffer_desc output_token = gss_buffer_desc(0, NULL) cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_export_sec_context(&min_stat, &context.raw_ctx, &output_token) if maj_stat == GSS_S_COMPLETE: res_token = (output_token.value)[:output_token.length] gss_release_buffer(&min_stat, &output_token) return res_token else: raise GSSError(maj_stat, min_stat) def delete_sec_context(SecurityContext context not None, local_only=True): """ delete_sec_context(context, local_only=True) Delete a GSSAPI security context. This method deletes a GSSAPI security context, returning an output token to send to the other holder of the security context to notify them of the deletion. Note: This method generally should not be used. :class:`SecurityContext` objects will automatically be freed by Python. Args: context (SecurityContext): the security context in question local_only (bool): should we request local deletion (True), or also remote deletion (False), in which case a token is also returned Returns: bytes: the output token (if remote deletion is requested). Generally this is None, but bytes for compatibility. Raises: MissingContextError """ cdef OM_uint32 maj_stat, min_stat # GSS_C_EMPTY_BUFFER cdef gss_buffer_desc output_token = gss_buffer_desc(0, NULL) if not local_only: maj_stat = gss_delete_sec_context(&min_stat, &context.raw_ctx, &output_token) else: maj_stat = gss_delete_sec_context(&min_stat, &context.raw_ctx, GSS_C_NO_BUFFER) if maj_stat == GSS_S_COMPLETE: res = (output_token.value)[:output_token.length] context.raw_ctx = NULL return res else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/ext_rfc5587.pyx0000664000372000037200000001150613523321513016051 0ustar from gssapi.raw.cython_types cimport * from gssapi.raw.oids cimport OID from gssapi.raw.cython_converters cimport c_create_oid_set GSSAPI="BASE" # This ensures that a full module is generated by Cython from gssapi.raw.cython_converters cimport c_get_mech_oid_set from gssapi.raw.named_tuples import InquireAttrsResult, DisplayAttrResult from gssapi.raw.misc import GSSError cdef extern from "python_gssapi_ext.h": OM_uint32 gss_indicate_mechs_by_attrs( OM_uint32 *minor_status, const gss_OID_set desired_mech_attrs, const gss_OID_set except_mech_attrs, const gss_OID_set critical_mech_attrs, gss_OID_set *mechs) nogil OM_uint32 gss_inquire_attrs_for_mech( OM_uint32 *minor_status, const gss_OID mech, gss_OID_set *mech_attrs, gss_OID_set *known_mech_attrs) nogil OM_uint32 gss_display_mech_attr( OM_uint32 *minor_status, const gss_OID mech_attr, gss_buffer_t name, gss_buffer_t short_desc, gss_buffer_t long_desc) nogil def indicate_mechs_by_attrs(desired_mech_attrs=None, except_mech_attrs=None, critical_mech_attrs=None): """ indicate_mechs_by_attrs(desired_mech_attrs=None, except_mech_attrs=None, critical_mech_attrs=None) Get a set of mechanisms that have the specified attributes. Args: desired_mech_attrs ([OID]): Attributes that the output mechs MUST offer except_mech_attrs ([OID]): Attributes that the output mechs MUST NOT offer critical_mech_attrs ([OID]): Attributes that the output mechs MUST understand and offer Returns: [MechType]: a set of mechs which satisfy the given criteria Raises: GSSError """ cdef OM_uint32 maj_stat, min_stat cdef gss_OID_set desired_attrs = GSS_C_NO_OID_SET cdef gss_OID_set except_attrs = GSS_C_NO_OID_SET cdef gss_OID_set critical_attrs = GSS_C_NO_OID_SET cdef gss_OID_set mechs if desired_mech_attrs is not None: desired_attrs = c_get_mech_oid_set(desired_mech_attrs) if except_mech_attrs is not None: except_attrs = c_get_mech_oid_set(except_mech_attrs) if critical_mech_attrs is not None: critical_attrs = c_get_mech_oid_set(critical_mech_attrs) with nogil: maj_stat = gss_indicate_mechs_by_attrs(&min_stat, desired_attrs, except_attrs, critical_attrs, &mechs) if maj_stat == GSS_S_COMPLETE: return c_create_oid_set(mechs) else: raise GSSError(maj_stat, min_stat) def inquire_attrs_for_mech(OID mech): """ inquire_attrs_for_mech(mech) Gets the set of attrs supported and known by a mechanism. Args: mech (MechType): Mechanism to inquire about Returns: InquireAttrsResult: the results of inquiry; a mech's attributes and known attributes Raises: GSSError """ cdef OM_uint32 maj_stat, min_stat cdef gss_OID m = GSS_C_NO_OID cdef gss_OID_set mech_attrs = GSS_C_NO_OID_SET cdef gss_OID_set known_mech_attrs = GSS_C_NO_OID_SET if mech is not None: m = &mech.raw_oid with nogil: maj_stat = gss_inquire_attrs_for_mech(&min_stat, m, &mech_attrs, &known_mech_attrs) if maj_stat == GSS_S_COMPLETE: return InquireAttrsResult(c_create_oid_set(mech_attrs), c_create_oid_set(known_mech_attrs)) else: raise GSSError(maj_stat, min_stat) def display_mech_attr(OID attr): """ display_mech_attrs(attr) Returns information about attributes in human readable form. Args: attr (OID): Mechanism attribute to retrive names and descriptions of Returns: DisplayAttrResult: the results of displaying the attribute; mech name, short description, and long description. Raises: GSSError """ cdef OM_uint32 maj_stat, min_stat cdef gss_OID a = GSS_C_NO_OID cdef gss_buffer_desc name cdef gss_buffer_desc short_desc cdef gss_buffer_desc long_desc if attr is not None: a = &attr.raw_oid with nogil: maj_stat = gss_display_mech_attr(&min_stat, a, &name, &short_desc, &long_desc) if maj_stat == GSS_S_COMPLETE: out_name = (name.value)[:name.length] out_short = (short_desc.value)[:short_desc.length] out_long = (long_desc.value)[:long_desc.length] gss_release_buffer(&min_stat, &name) gss_release_buffer(&min_stat, &short_desc) gss_release_buffer(&min_stat, &long_desc) return DisplayAttrResult(out_name, out_short, out_long) else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/oids.pyx0000664000372000037200000001340413523321513015023 0ustar GSSAPI="BASE" # This ensures that a full module is generated by Cython import six from libc.string cimport memcmp, memcpy from libc.stdlib cimport free, malloc from gssapi.raw.cython_types cimport gss_OID cdef inline bint c_compare_oids(gss_OID a, gss_OID b): return (a.length == b.length and not memcmp(a.elements, b.elements, a.length)) cdef class OID: """ A GSSAPI OID A new OID may be created by passing the `elements` argument to the constructor. The `elements` argument should be a `bytes` consisting of the BER-encoded values in the OID. To retrive the underlying bytes, use the :func:`bytes` function in Python 3 or the :meth:`__bytes__` method directly in Python 2. This object is hashable, and may be compared using equality operators. """ # defined in pxd # cdef gss_OID_desc raw_oid = NULL # cdef bint _free_on_dealloc = NULL def __cinit__(OID self, OID cpy=None, elements=None): """ Note: cpy is named such for historical reasons. To perform a deep copy, specify the elements parameter; this will copy the value of the OID. To perform a shallow copy and take ownership of an existing OID, use the cpy (default) argument. """ if cpy is not None and elements is not None: raise TypeError("Cannot instantiate a OID from both a copy and " " a new set of elements") if cpy is not None: self.raw_oid = cpy.raw_oid # take ownership of this OID (for dynamic cases) self._free_on_dealloc = cpy._free_on_dealloc cpy._free_on_dealloc = False if elements is None: self._free_on_dealloc = False else: self._from_bytes(elements) cdef int _copy_from(OID self, gss_OID_desc base) except -1: self.raw_oid.length = base.length self.raw_oid.elements = malloc(self.raw_oid.length) if self.raw_oid.elements is NULL: raise MemoryError("Could not allocate memory for OID elements!") memcpy(self.raw_oid.elements, base.elements, self.raw_oid.length) self._free_on_dealloc = True return 0 cdef int _from_bytes(OID self, object base) except -1: base_bytes = bytes(base) cdef char* byte_str = base_bytes self.raw_oid.length = len(base_bytes) self.raw_oid.elements = malloc(self.raw_oid.length) if self.raw_oid.elements is NULL: raise MemoryError("Could not allocate memory for OID elements!") self._free_on_dealloc = True memcpy(self.raw_oid.elements, byte_str, self.raw_oid.length) return 0 @classmethod def from_int_seq(cls, integer_sequence): """ from_int_seq(integer_sequence) Create a OID from a sequence of integers. This method creates an OID from a sequence of integers. The sequence can either be in dotted form as a string, or in list form. This method is not for BER-encoded byte strings, which can be passed directly to the OID constructor. Args: integer_sequence: either a list of integers or a string in dotted form Returns: OID: the OID represented by the given integer sequence Raises: ValueError: the sequence is less than two elements long """ if isinstance(integer_sequence, six.string_types): integer_sequence = integer_sequence.split('.') oid_seq = [int(x) for x in integer_sequence] elements = cls._encode_asn1ber(oid_seq) return cls(elements=elements) @staticmethod def _encode_asn1ber(oid_seq): if len(oid_seq) < 2: raise ValueError("Sequence must be 2 or more elements long.") byte_seq = bytearray([oid_seq[0] * 40 + oid_seq[1]]) for element in oid_seq[2:]: element_seq = [element & 0x7f] while element > 127: element >>= 7 element_seq.insert(0, (element & 0x7f) | 0x80) byte_seq.extend(element_seq) return bytes(byte_seq) def __dealloc__(self): # NB(directxman12): MIT Kerberos has gss_release_oid # for this purpose, but it's not in the RFC if self._free_on_dealloc: free(self.raw_oid.elements) def __bytes__(self): return (self.raw_oid.elements)[:self.raw_oid.length] def _decode_asn1ber(self): ber_encoding = self.__bytes__() # NB(directxman12): indexing a byte string yields an int in Python 3, # but yields a substring in Python 2 if six.PY2: ber_encoding = [ord(c) for c in ber_encoding] decoded = [ber_encoding[0] // 40, ber_encoding[0] % 40] pos = 1 value = 0 while pos < len(ber_encoding): byte = ber_encoding[pos] if byte & 0x80: # This is one of the leading bytes value <<= 7 value += ((byte & 0x7f) * 128) else: # This is the last byte of this value value += (byte & 0x7f) decoded.append(value) value = 0 pos += 1 return decoded @property def dotted_form(self): return '.'.join(str(x) for x in self._decode_asn1ber()) def __repr__(self): return "".format(self.dotted_form) def __hash__(self): return hash(self.__bytes__()) def __richcmp__(OID self, OID other, op): if op == 2: # == return c_compare_oids(&self.raw_oid, &other.raw_oid) elif op == 3: # != return not c_compare_oids(&self.raw_oid, &other.raw_oid) else: return NotImplemented python-gssapi-1.6.1/gssapi/raw/ext_rfc5801.pyx0000664000372000037200000000530713523321513016040 0ustar from gssapi.raw.cython_types cimport * from gssapi.raw.oids cimport OID GSSAPI="BASE" # This ensures that a full module is generated by Cython from gssapi.raw.cython_converters cimport c_make_oid from gssapi.raw.named_tuples import InquireSASLNameResult from gssapi.raw.misc import GSSError cdef extern from "python_gssapi_ext.h": OM_uint32 gss_inquire_saslname_for_mech( OM_uint32 *min_stat, const gss_OID desired_mech, gss_buffer_t sasl_mech_name, gss_buffer_t mech_name, gss_buffer_t mech_description) nogil OM_uint32 gss_inquire_mech_for_saslname( OM_uint32 *min_stat, const gss_buffer_t sasl_mech_name, gss_OID *mech_type) nogil def inquire_saslname_for_mech(OID mech not None): """ inquire_saslname_for_mech(mech) Gets information about a specified mech, including the SASL name, the mech name, and the mech description. Args: mech (OID): Mechanism to inquire about Returns: InquireSASLNameResult: the results of inquiry; a mech's SASL name, name, and description. Raises: GSSError: an unknown failure occurred """ cdef OM_uint32 maj_stat, min_stat cdef gss_buffer_desc sasl_mech_name cdef gss_buffer_desc mech_name cdef gss_buffer_desc mech_desc cdef gss_OID m = GSS_C_NO_OID m = &mech.raw_oid with nogil: maj_stat = gss_inquire_saslname_for_mech(&min_stat, m, &sasl_mech_name, &mech_name, &mech_desc) if maj_stat == GSS_S_COMPLETE: out_smn = (sasl_mech_name.value)[:sasl_mech_name.length] out_mn = (mech_name.value)[:mech_name.length] out_md = (mech_desc.value)[:mech_desc.length] gss_release_buffer(&min_stat, &sasl_mech_name) gss_release_buffer(&min_stat, &mech_name) gss_release_buffer(&min_stat, &mech_desc) return InquireSASLNameResult(out_smn, out_mn, out_md) else: raise GSSError(maj_stat, min_stat) def inquire_mech_for_saslname(bytes sasl_name not None): """ inquire_mech_for_saslname(sasl_name) Gets the OID for the mech specified by SASL name. Args: sasl_name (bytes): SASL name of the mechanism Returns: OID: the mechanism with corresponding SASL name. Raises: GSSError: An unknown failure occurred """ cdef OM_uint32 maj_stat, min_stat cdef gss_buffer_desc sn cdef gss_OID m sn.length = len(sasl_name) sn.value = sasl_name with nogil: maj_stat = gss_inquire_mech_for_saslname(&min_stat, &sn, &m) if maj_stat == GSS_S_COMPLETE: return c_make_oid(m) else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/exceptions.pyx0000664000372000037200000001110313523321513016240 0ustar from gssapi.raw.cython_types cimport OM_uint32 from gssapi.raw.misc import GSSError """Specific exceptions for GSSAPI errors""" cdef extern from "python_gssapi.h": # calling errors OM_uint32 GSS_S_CALL_INACCESSIBLE_READ OM_uint32 GSS_S_CALL_INACCESSIBLE_WRITE OM_uint32 GSS_S_CALL_BAD_STRUCTURE # routine errors OM_uint32 GSS_S_BAD_MECH OM_uint32 GSS_S_BAD_NAME OM_uint32 GSS_S_BAD_NAMETYPE OM_uint32 GSS_S_BAD_BINDINGS OM_uint32 GSS_S_BAD_STATUS OM_uint32 GSS_S_BAD_SIG # NB(directxman12): BAD_MIC == BAD_SIG, so skip it OM_uint32 GSS_S_NO_CRED OM_uint32 GSS_S_NO_CONTEXT OM_uint32 GSS_S_DEFECTIVE_TOKEN OM_uint32 GSS_S_DEFECTIVE_CREDENTIAL OM_uint32 GSS_S_CREDENTIALS_EXPIRED OM_uint32 GSS_S_CONTEXT_EXPIRED # OM_uint32 GSS_S_FAILURE OM_uint32 GSS_S_BAD_QOP OM_uint32 GSS_S_UNAUTHORIZED OM_uint32 GSS_S_UNAVAILABLE OM_uint32 GSS_S_DUPLICATE_ELEMENT OM_uint32 GSS_S_NAME_NOT_MN # supplementary bits # OM_uint32 GSS_S_CONTINUE_NEEDED OM_uint32 GSS_S_DUPLICATE_TOKEN OM_uint32 GSS_S_OLD_TOKEN OM_uint32 GSS_S_UNSEQ_TOKEN OM_uint32 GSS_S_GAP_TOKEN # Generic calling code errors class ParameterReadError(GSSError): CALLING_CODE = GSS_S_CALL_INACCESSIBLE_READ class ParameterWriteError(GSSError): CALLING_CODE = GSS_S_CALL_INACCESSIBLE_WRITE class MalformedParameterError(GSSError): CALLING_CODE = GSS_S_CALL_BAD_STRUCTURE # generic routine errors class BadMechanismError(GSSError): ROUTINE_CODE = GSS_S_BAD_MECH class BadNameError(GSSError): ROUTINE_CODE = GSS_S_BAD_NAME class BadNameTypeError(GSSError): ROUTINE_CODE = GSS_S_BAD_NAMETYPE class BadChannelBindingsError(GSSError): ROUTINE_CODE = GSS_S_BAD_BINDINGS class BadStatusError(GSSError): ROUTINE_CODE = GSS_S_BAD_STATUS class BadMICError(GSSError): ROUTINE_CODE = GSS_S_BAD_SIG class MissingCredentialsError(GSSError): ROUTINE_CODE = GSS_S_NO_CRED class MissingContextError(GSSError): ROUTINE_CODE = GSS_S_NO_CONTEXT class InvalidTokenError(GSSError): ROUTINE_CODE = GSS_S_DEFECTIVE_TOKEN class InvalidCredentialsError(GSSError): ROUTINE_CODE = GSS_S_DEFECTIVE_CREDENTIAL class ExpiredCredentialsError(GSSError): ROUTINE_CODE = GSS_S_CREDENTIALS_EXPIRED class ExpiredContextError(GSSError): ROUTINE_CODE = GSS_S_CONTEXT_EXPIRED # NB(directxman12): since GSS_S_FAILURE is generic, # we just use GSSError for it class BadQoPError(GSSError): ROUTINE_CODE = GSS_S_BAD_QOP class UnauthorizedError(GSSError): ROUTINE_CODE = GSS_S_UNAUTHORIZED class OperationUnavailableError(GSSError): ROUTINE_CODE = GSS_S_UNAVAILABLE class DuplicateCredentialsElementError(GSSError): ROUTINE_CODE = GSS_S_DUPLICATE_ELEMENT class MechanismNameRequiredError(GSSError): ROUTINE_CODE = GSS_S_NAME_NOT_MN # specific calling | routine errors class NameReadError(ParameterReadError, BadNameError): # CALLING_CODE = GSS_S_CALL_INACCESSIBLE_READ # ROUTINE_CODE = GSS_S_BAD_NAME pass class NameTypeReadError(ParameterReadError, BadNameTypeError): # CALLING_CODE = GSS_S_CALL_INACCESSIBLE_READ # ROUTINE_CODE = GSS_S_BAD_NAMETYPE pass class TokenReadError(ParameterReadError, InvalidTokenError): # CALLING_CODE = GSS_S_CALL_INACCESSIBLE_READ # ROUTINE_CODE = GSS_S_DEFECTIVE_TOKEN pass class ContextReadError(ParameterReadError, MissingContextError): # CALLING_CODE = GSS_S_CALL_INACCESSIBLE_READ # ROUTINE_CODE = GSS_S_NO_CONTEXT pass class CredentialsReadError(ParameterReadError, MissingCredentialsError): # CALLING_CODE = GSS_S_CALL_INACCESSIBLE_READ # ROUTINE_CODE = GSS_S_NO_CRED pass class ContextWriteError(ParameterWriteError, MissingContextError): # CALLING_CODE = GSS_S_CALL_INACCESSIBLE_WRITE # ROUTINE_CODE = GSS_S_NO_CONTEXT pass class CredentialsWriteError(ParameterWriteError, MissingCredentialsError): # CALLING_CODE = GSS_S_CALL_INACCESSIBLE_WRITE # ROUTINE_CODE = GSS_S_NO_CRED pass # generic supplementary bits errors class SupplementaryError(GSSError): # to make it easy for people to catch all supplementary errors pass class DuplicateTokenError(SupplementaryError): SUPPLEMENTARY_CODE = GSS_S_DUPLICATE_TOKEN class ExpiredTokenError(SupplementaryError): SUPPLEMENTARY_CODE = GSS_S_OLD_TOKEN class TokenOutOfSequenceError(SupplementaryError): pass class TokenTooLateError(TokenOutOfSequenceError): SUPPLEMENTARY_CODE = GSS_S_UNSEQ_TOKEN class TokenTooEarlyError(TokenOutOfSequenceError): SUPPLEMENTARY_CODE = GSS_S_GAP_TOKEN python-gssapi-1.6.1/gssapi/raw/ext_iov_mic.pyx0000664000372000037200000001103713523321513016372 0ustar GSSAPI="BASE" # This ensures that a full module is generated by Cython from gssapi.raw.cython_types cimport * from gssapi.raw.sec_contexts cimport SecurityContext from gssapi.raw.ext_dce cimport IOV, gss_iov_buffer_desc from gssapi.raw.misc import GSSError from gssapi.raw.ext_dce import IOVBufferType cdef extern from "python_gssapi_ext.h": OM_uint32 gss_get_mic_iov(OM_uint32 *min_stat, gss_ctx_id_t context_handle, gss_qop_t qop_req, gss_iov_buffer_desc *iov, int iov_count) nogil OM_uint32 gss_get_mic_iov_length(OM_uint32 *min_stat, gss_ctx_id_t context_handle, gss_qop_t qop_req, gss_iov_buffer_desc *iov, int iov_count) nogil OM_uint32 gss_verify_mic_iov(OM_uint32 *min_stat, gss_ctx_id_t context_handle, gss_qop_t *qop_state, gss_iov_buffer_desc *iov, int iov_count) nogil # more in the enum extension file IOV.AUTO_ALLOC_BUFFERS.add(IOVBufferType.mic_token) def get_mic_iov(SecurityContext context not None, IOV message not None, qop=None): """ get_mic_iov(context, message, qop=None) Generate MIC tokens for the given IOV message. This method generates a MIC token for the given IOV message, and places it in the :attr:`IOVBufferType.mic_token` buffer in the IOV. This method operates entirely in-place, and returns nothing. Warning: This modifies the input :class:`IOV`. Args: context (SecurityContext): the current security context message (IOV): the :class:`IOV` containing the message qop (int): the desired Quality of Protection (or None for the default QoP) Raises: GSSError """ cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT cdef gss_iov_buffer_desc *res_arr = message.__cvalue__() cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_get_mic_iov(&min_stat, context.raw_ctx, qop_req, res_arr, message.iov_len) if maj_stat == GSS_S_COMPLETE: message.c_changed = True return else: raise GSSError(maj_stat, min_stat) def get_mic_iov_length(SecurityContext context not None, IOV message not None, qop=None): """ get_mic_iov_length(context, message, qop=None) Allocate space for the MIC buffer in the given IOV message. This method allocates space for the MIC token buffer (:attr:`IOVBufferType.mic_token`) in the given IOV message. Warning: This modifies the input :class:`IOV`. Args: context (SecurityContext): the current security context message (IOV): the :class:`IOV` containing the message qop (int): the desired Quality of Protection (or None for the default QoP) Raises: GSSError """ cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT cdef gss_iov_buffer_desc *res_arr = message.__cvalue__() cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_get_mic_iov_length(&min_stat, context.raw_ctx, qop_req, res_arr, message.iov_len) if maj_stat == GSS_S_COMPLETE: message.c_changed = True return else: raise GSSError(maj_stat, min_stat) def verify_mic_iov(SecurityContext context not None, IOV message not None, qop=None): """ verify_mic_iov(context, message, qop=None) Verify that the MIC matches the data in the given IOV message. This method verifies that the MIC token in the MIC buffer (:attr:`IOVBufferType.mic_token`) match the data buffer(s) in the given IOV method. Args: context (SecurityContext): the current security context message (IOV): the :class:`IOV` containing the message Returns: int: the QoP used to generate the MIC token Raises: GSSError """ cdef gss_iov_buffer_desc *res_arr = message.__cvalue__() cdef gss_qop_t qop_state cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_verify_mic_iov(&min_stat, context.raw_ctx, &qop_state, res_arr, message.iov_len) if maj_stat == GSS_S_COMPLETE: return qop_state else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/ext_cred_store.pyx0000664000372000037200000003113613523321513017100 0ustar """Credential Store Extension""" GSSAPI="BASE" # This ensures that a full module is generated by Cython from libc.string cimport memcmp, memcpy, memset from libc.stdlib cimport free, malloc, calloc from gssapi.raw.cython_types cimport * from gssapi.raw.names cimport Name from gssapi.raw.creds cimport Creds from gssapi.raw.oids cimport OID from gssapi.raw.cython_converters cimport c_create_oid_set from gssapi.raw.cython_converters cimport c_get_mech_oid_set from gssapi.raw.cython_converters cimport c_c_ttl_to_py, c_py_ttl_to_c from collections import namedtuple from gssapi.raw.named_tuples import AddCredResult, AcquireCredResult from gssapi.raw.named_tuples import StoreCredResult from gssapi.raw.misc import GSSError cdef extern from "python_gssapi_ext.h": ctypedef struct gss_key_value_element_desc: const char *key const char *value ctypedef struct gss_key_value_set_desc: OM_uint32 count gss_key_value_element_desc *elements OM_uint32 gss_acquire_cred_from(OM_uint32 *min_stat, gss_name_t desired_name, OM_uint32 ttl, gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, const gss_key_value_set_desc *cred_store, gss_cred_id_t *output_creds, gss_OID_set *actual_mechs, OM_uint32 *actual_ttl) nogil OM_uint32 gss_add_cred_from(OM_uint32 *min_stat, gss_cred_id_t input_creds, gss_name_t desired_name, gss_OID desired_mech, gss_cred_usage_t cred_usage, OM_uint32 initiator_ttl, OM_uint32 acceptor_ttl, const gss_key_value_set_desc *cred_store, gss_cred_id_t *output_creds, gss_OID_set *actual_mechs, OM_uint32 *actual_initiator_ttl, OM_uint32 *actual_acceptor_ttl) nogil OM_uint32 gss_store_cred_into(OM_uint32 *min_stat, gss_cred_id_t input_creds, gss_cred_usage_t cred_usage, gss_OID desired_mech, OM_uint32 overwrite_cred, OM_uint32 default_cred, const gss_key_value_set_desc *cred_store, gss_OID_set *elements_stored, gss_cred_usage_t *actual_usage) nogil # null value for cred stores gss_key_value_set_desc *GSS_C_NO_CRED_STORE cdef gss_key_value_set_desc* c_create_key_value_set(dict values) except NULL: cdef gss_key_value_set_desc* res = malloc( sizeof(gss_key_value_set_desc)) if res is NULL: raise MemoryError("Could not allocate memory for " "key-value set") res.count = len(values) res.elements = calloc( res.count, sizeof(gss_key_value_element_desc)) if res.elements is NULL: raise MemoryError("Could not allocate memory for " "key-value set elements") for (i, (k, v)) in enumerate(values.items()): res.elements[i].key = k res.elements[i].value = v return res cdef void c_free_key_value_set(gss_key_value_set_desc *kvset): free(kvset.elements) free(kvset) def acquire_cred_from(dict store=None, Name name=None, lifetime=None, mechs=None, usage='both'): """ acquire_cred_from(store=None, name=None, lifetime=None, mechs=None, \ usage='both') Acquire credentials from the given store. This method acquires credentials from the store specified by the given credential store information. The credential store information is a dictionary containing mechanisms-specific keys and values pointing to a credential store or stores. Args: store (dict): the credential store information pointing to the credential store from which to acquire the credentials. See :doc:`credstore` for valid values name (Name): the name associated with the credentials, or None for the default name lifetime (int): the desired lifetime of the credentials, or None for indefinite mechs (list): the desired mechanisms to be used with these credentials, or None for the default set usage (str): the usage for these credentials -- either 'both', 'initiate', or 'accept' Returns: AcquireCredResult: the acquired credentials and information about them Raises: GSSError """ cdef gss_OID_set desired_mechs if mechs is not None: desired_mechs = c_get_mech_oid_set(mechs) else: desired_mechs = GSS_C_NO_OID_SET cdef OM_uint32 input_ttl = c_py_ttl_to_c(lifetime) cdef gss_name_t c_name if name is None: c_name = GSS_C_NO_NAME else: c_name = name.raw_name cdef gss_cred_usage_t c_usage if usage == 'initiate': c_usage = GSS_C_INITIATE elif usage == 'accept': c_usage = GSS_C_ACCEPT else: c_usage = GSS_C_BOTH cdef gss_key_value_set_desc *c_store if store is not None: c_store = c_create_key_value_set(store) else: c_store = GSS_C_NO_CRED_STORE cdef gss_cred_id_t creds cdef gss_OID_set actual_mechs cdef OM_uint32 actual_ttl cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_acquire_cred_from(&min_stat, c_name, input_ttl, desired_mechs, c_usage, c_store, &creds, &actual_mechs, &actual_ttl) cdef OM_uint32 tmp_min_stat if mechs is not None: gss_release_oid_set(&tmp_min_stat, &desired_mechs) if store is not None: c_free_key_value_set(c_store) cdef Creds rc = Creds() if maj_stat == GSS_S_COMPLETE: rc.raw_creds = creds return AcquireCredResult(rc, c_create_oid_set(actual_mechs), c_c_ttl_to_py(actual_ttl)) else: raise GSSError(maj_stat, min_stat) def add_cred_from(dict store, Creds input_creds, Name name not None, OID mech not None, usage='both', init_lifetime=None, accept_lifetime=None): """ add_cred_from(store, input_creds, name, mech, usage='both', \ init_lifetime=None, accept_lifetime=None) Acquire credentials to add to the current set from the given store. This method works like :func:`acquire_cred_from`, except that it adds the acquired credentials for a single mechanism to a copy of the current set, instead of creating a new set for multiple mechanisms. Unlike :func:`acquire_cred`, you cannot pass None for the desired name or mechanism. The credential store information is a dictionary containing mechanisms-specific keys and values pointing to a credential store or stores. Args: store (dict): the store into which to store the credentials, or None for the default store. See :doc:`credstore` for valid values name (Name): the name associated with the credentials mech (OID): the desired mechanism to be used with these credentials usage (str): the usage for these credentials -- either 'both', 'initiate', or 'accept' init_lifetime (int): the desired initiate lifetime of the credentials, or None for indefinite accept_lifetime (int): the desired accept lifetime of the credentials, or None for indefinite Returns: AcquireCredResult: the new credentials set and information about it Raises: GSSError """ cdef OM_uint32 input_initiator_ttl = c_py_ttl_to_c(init_lifetime) cdef OM_uint32 input_acceptor_ttl = c_py_ttl_to_c(accept_lifetime) cdef gss_cred_usage_t c_usage if usage == 'initiate': c_usage = GSS_C_INITIATE elif usage == 'accept': c_usage = GSS_C_ACCEPT else: c_usage = GSS_C_BOTH cdef gss_name_t c_name = name.raw_name cdef gss_OID c_mech = &mech.raw_oid cdef gss_cred_id_t c_input_creds if input_creds is not None: c_input_creds = input_creds.raw_creds else: c_input_creds = GSS_C_NO_CREDENTIAL cdef gss_key_value_set_desc *c_store if store is not None: c_store = c_create_key_value_set(store) else: c_store = GSS_C_NO_CRED_STORE cdef gss_cred_id_t creds cdef gss_OID_set actual_mechs cdef OM_uint32 actual_initiator_ttl cdef OM_uint32 actual_acceptor_ttl cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_add_cred_from(&min_stat, c_input_creds, c_name, c_mech, c_usage, input_initiator_ttl, input_acceptor_ttl, c_store, &creds, &actual_mechs, &actual_initiator_ttl, &actual_acceptor_ttl) if store is not None: c_free_key_value_set(c_store) cdef Creds rc if maj_stat == GSS_S_COMPLETE: rc = Creds() rc.raw_creds = creds return AddCredResult(rc, c_create_oid_set(actual_mechs), c_c_ttl_to_py(actual_initiator_ttl), c_c_ttl_to_py(actual_acceptor_ttl)) else: raise GSSError(maj_stat, min_stat) def store_cred_into(dict store, Creds creds not None, usage='both', OID mech=None, bint overwrite=False, bint set_default=False): """ store_cred_into(store, creds, usage='both', mech=None, overwrite=False, \ set_default=False) Store credentials into the given store. This method stores the given credentials into the store specified by the given store information. They may then be retrieved later using :func:`acquire_cred_from` or :func:`add_cred_from`. The credential store information is a dictionary containing mechanisms-specific keys and values pointing to a credential store or stores. Args: store (dict): the store into which to store the credentials, or None for the default store. See :doc:`credstore` for valid values creds (Creds): the credentials to store usage (str): the usage to store the credentials with -- either 'both', 'initiate', or 'accept' mech (OID): the mechansim to associate with the stored credentials overwrite (bool): whether or not to overwrite existing credentials stored with the same name, etc set_default (bool): whether or not to set these credentials as the default credentials for the given store. Returns: StoreCredResult: the results of the credential storing operation Raises: GSSError """ cdef gss_OID desired_mech if mech is not None: desired_mech = &mech.raw_oid else: desired_mech = GSS_C_NO_OID cdef gss_cred_usage_t c_usage if usage == 'initiate': c_usage = GSS_C_INITIATE elif usage == 'accept': c_usage = GSS_C_ACCEPT else: c_usage = GSS_C_BOTH cdef gss_key_value_set_desc *c_store if store is not None: c_store = c_create_key_value_set(store) else: c_store = GSS_C_NO_CRED_STORE cdef gss_cred_id_t c_creds = creds.raw_creds cdef gss_OID_set actual_mech_types cdef gss_cred_usage_t actual_usage cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_store_cred_into(&min_stat, c_creds, c_usage, desired_mech, overwrite, set_default, c_store, &actual_mech_types, &actual_usage) if store is not None: c_free_key_value_set(c_store) if maj_stat == GSS_S_COMPLETE: if actual_usage == GSS_C_INITIATE: py_actual_usage = 'initiate' elif actual_usage == GSS_C_ACCEPT: py_actual_usage = 'accept' else: py_actual_usage = 'both' return StoreCredResult(c_create_oid_set(actual_mech_types), py_actual_usage) else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/sec_contexts.pxd0000664000372000037200000000021413523321513016534 0ustar from gssapi.raw.cython_types cimport gss_ctx_id_t cdef class SecurityContext: cdef gss_ctx_id_t raw_ctx cdef bint _free_on_dealloc python-gssapi-1.6.1/gssapi/raw/creds.pxd0000664000372000037200000000020713523321513015135 0ustar from gssapi.raw.cython_types cimport gss_cred_id_t cdef class Creds: cdef gss_cred_id_t raw_creds cdef bint _free_on_dealloc python-gssapi-1.6.1/gssapi/raw/__init__.py0000664000372000037200000000662013523321513015436 0ustar """Low-Level GSSAPI Bindings The low-level API presents a series of methods designed to closely mimic the C API presented in RFC 2744 and associated RFCs. In this API, classes are simply thin wrappers around C constructs, and generally lack instance methods. However, classes will automatically free associated memory (so the release_xyz methods are not necessary to call). The core RFC 2744 components are organized into the following submodules: gssapi.raw.names -- Names gssapi.raw.creds -- Credentials gssapi.raw.sec_contexts -- Security Contexts gssapi.raw.message -- Message encryption, decryption, etc gssapi.raw.misc -- Miscellaneous functions gssapi.raw.types -- Miscellaneous types (enums, etc) gssapi.raw.exceptions -- Exceptions Additionally, a number of extensions may be present. All extensions are in modules of the form `gssapi.raw.ext_xyz`. All available functions and classes can be accessed directly from this module (`gssapi.raw`) -- it is unneccessary to directly import submodules. """ import pkgutil import importlib from gssapi.raw import _enum_extensions # NB(directxman12): the enum extensions must be imported BEFORE ANYTHING ELSE! for modinf in pkgutil.iter_modules(_enum_extensions.__path__): name = modinf[1] importlib.import_module('{0}._enum_extensions.{1}'.format(__name__, name)) del pkgutil del importlib from gssapi.raw.creds import * # noqa from gssapi.raw.message import * # noqa from gssapi.raw.misc import * # noqa from gssapi.raw.exceptions import * # noqa from gssapi.raw.names import * # noqa from gssapi.raw.sec_contexts import * # noqa from gssapi.raw.oids import * # noqa from gssapi.raw.types import * # noqa from gssapi.raw.chan_bindings import * # noqa # optional S4U support try: from gssapi.raw.ext_s4u import * # noqa except ImportError: pass # no s4u support in the system's GSSAPI library # optional cred store support try: from gssapi.raw.ext_cred_store import * # noqa except ImportError: pass # optional RFC 4178 support try: from gssapi.raw.ext_rfc4178 import * # noqa except ImportError: pass # optional RFC 5587 support try: from gssapi.raw.ext_rfc5587 import * # noqa except ImportError: pass # optional RFC 5588 support try: from gssapi.raw.ext_rfc5588 import * # noqa except ImportError: pass # optional RFC 5801 support try: from gssapi.raw.ext_rfc5801 import * # noqa except ImportError: pass try: from gssapi.raw.ext_cred_imp_exp import * # noqa except ImportError: pass # optional KRB5 mech support try: import gssapi.raw.mech_krb5 # noqa except ImportError: pass # optional password support try: from gssapi.raw.ext_password import * # noqa from gssapi.raw.ext_password_add import * # noqa except ImportError: pass # optional DCE (IOV/AEAD) support try: from gssapi.raw.ext_dce import * # noqa # optional IOV MIC support (requires DCE support) from gssapi.raw.ext_iov_mic import * # noqa except ImportError: pass # optional RFC 6680 support try: from gssapi.raw.ext_rfc6680 import * # noqa from gssapi.raw.ext_rfc6680_comp_oid import * # noqa except ImportError: pass # optional Global Grid Forum support try: from gssapi.raw.ext_ggf import * # noqa except ImportError: pass # optional set_cred_option support try: from gssapi.raw.ext_set_cred_opt import * # noqa except ImportError: pass python-gssapi-1.6.1/gssapi/raw/python_gssapi_ext.h0000664000372000037200000000035213523321513017241 0ustar #ifdef OSX_HAS_GSS_FRAMEWORK #include #else #if defined(__MINGW32__) && defined(__MSYS__) #include #else #ifdef HAS_GSSAPI_EXT_H #include #else #include #endif #endif #endif python-gssapi-1.6.1/gssapi/raw/named_tuples.py0000664000372000037200000000514113523321513016354 0ustar from collections import namedtuple AcquireCredResult = namedtuple('AcquireCredResult', ['creds', 'mechs', 'lifetime']) InquireCredResult = namedtuple('InquireCredResult', ['name', 'lifetime', 'usage', 'mechs']) InquireCredByMechResult = namedtuple('InquireCredByMechResult', ['name', 'init_lifetime', 'accept_lifetime', 'usage']) AddCredResult = namedtuple('AddCredResult', ['creds', 'mechs', 'init_lifetime', 'accept_lifetime']) DisplayNameResult = namedtuple('DisplayNameResult', ['name', 'name_type']) WrapResult = namedtuple('WrapResult', ['message', 'encrypted']) UnwrapResult = namedtuple('UnwrapResult', ['message', 'encrypted', 'qop']) AcceptSecContextResult = namedtuple('AcceptSecContextResult', ['context', 'initiator_name', 'mech', 'token', 'flags', 'lifetime', 'delegated_creds', 'more_steps']) InitSecContextResult = namedtuple('InitSecContextResult', ['context', 'mech', 'flags', 'token', 'lifetime', 'more_steps']) InquireContextResult = namedtuple('InquireContextResult', ['initiator_name', 'target_name', 'lifetime', 'mech', 'flags', 'locally_init', 'complete']) StoreCredResult = namedtuple('StoreCredResult', ['mechs', 'usage']) IOVUnwrapResult = namedtuple('IOVUnwrapResult', ['encrypted', 'qop']) InquireNameResult = namedtuple('InquireNameResult', ['attrs', 'is_mech_name', 'mech']) GetNameAttributeResult = namedtuple('GetNamedAttributeResult', ['values', 'display_values', 'authenticated', 'complete']) InquireAttrsResult = namedtuple('InquireAttrsResult', ['mech_attrs', 'known_mech_attrs']) DisplayAttrResult = namedtuple('DisplayAttrResult', ['name', 'short_desc', 'long_desc']) InquireSASLNameResult = namedtuple('InquireSASLNameResult', ['sasl_mech_name', 'mech_name', 'mech_description']) python-gssapi-1.6.1/gssapi/raw/misc.pyx0000664000372000037200000002533013523321513015021 0ustar GSSAPI="BASE" # This ensures that a full module is generated by Cython import locale # for decoding error messages from gssapi.raw.cython_types cimport * from gssapi.raw.cython_converters cimport c_create_oid_set from gssapi.raw.names cimport Name from gssapi.raw.oids cimport OID from gssapi.raw.types import MechType cdef extern from "python_gssapi.h": OM_uint32 gss_display_status(OM_uint32 *minor_status, OM_uint32 status_value, int status_type, const gss_OID mech_type, OM_uint32 *message_context, gss_buffer_t status_string) OM_uint32 gss_indicate_mechs(OM_uint32 *minor_status, gss_OID_set *mech_set) OM_uint32 gss_inquire_names_for_mech(OM_uint32 *minor_status, const gss_OID mech_type, gss_OID_set *name_types) OM_uint32 gss_inquire_mechs_for_name(OM_uint32 *minor_status, const gss_name_t input_name, gss_OID_set *mech_types) def indicate_mechs(): """ indicate_mechs() Get the currently supported mechanisms. This method retrieves the currently supported GSSAPI mechanisms. Note that if unknown mechanims are found, those will be skipped. """ cdef gss_OID_set mech_set cdef OM_uint32 maj_stat, min_stat maj_stat = gss_indicate_mechs(&min_stat, &mech_set) if maj_stat == GSS_S_COMPLETE: return c_create_oid_set(mech_set) else: raise GSSError(maj_stat, min_stat) def inquire_names_for_mech(OID mech not None): """ inquire_names_for_mech(mech) Get the name types supported by a mechanism. This method retrives the different name types supported by the given mechanism. Args: mech (OID): the mechanism in question Returns: list: the name type OIDs supported by the given mechanism Raises: GSSError """ cdef gss_OID_set name_types cdef OM_uint32 maj_stat, min_stat maj_stat = gss_inquire_names_for_mech(&min_stat, &mech.raw_oid, &name_types) if maj_stat == GSS_S_COMPLETE: return c_create_oid_set(name_types) else: raise GSSError(maj_stat, min_stat) def inquire_mechs_for_name(Name name not None): """ inquire_mechs_for_name(name) List the mechanisms which can process a name. This method lists the mechanisms which may be able to process the given name. Args: name (Name): the name in question Returns: list: the mechanism OIDs able to process the given name Raises: GSSError """ cdef gss_OID_set mech_types cdef OM_uint32 maj_stat, min_stat maj_stat = gss_inquire_mechs_for_name(&min_stat, name.raw_name, &mech_types) if maj_stat == GSS_S_COMPLETE: return c_create_oid_set(mech_types) else: raise GSSError(maj_stat, min_stat) def _display_status(unsigned int error_code, bint is_major_code, OID mech=None, unsigned int message_context=0): """ Display a string message for a GSSAPI error code. This method displays a message for a corresponding GSSAPI error code. Since some error codes might have multiple messages, a context parameter may be passed to indicate where in the series of messages we currently are (this is the second item in the return value tuple). Additionally, the third item in the return value tuple indicates whether or not more messages are available. Args: error_code (int): The error code in question is_major_code (bool): is this a major code (True) or a minor code (False) mech (MechType): The mechanism type that returned this error code (defaults to None, for the default mechanism) message_context (int): The context for this call -- this is used when multiple messages are available (defaults to 0) Returns: (bytes, int, bool): the message, the new message context, and whether or not to call again for further messages Raises: ValueError """ cdef int status_type cdef gss_OID c_mech_type if is_major_code: status_type = GSS_C_GSS_CODE else: status_type = GSS_C_MECH_CODE if mech is None: c_mech_type = GSS_C_NO_OID else: c_mech_type = &mech.raw_oid cdef OM_uint32 maj_stat cdef OM_uint32 min_stat cdef OM_uint32 msg_ctx_out = message_context cdef gss_buffer_desc msg_buff maj_stat = gss_display_status(&min_stat, error_code, status_type, c_mech_type, &msg_ctx_out, &msg_buff) if maj_stat == GSS_S_COMPLETE: call_again = bool(msg_ctx_out) msg_out = (msg_buff.value)[:msg_buff.length] gss_release_buffer(&min_stat, &msg_buff) return (msg_out, msg_ctx_out, call_again) else: # This hides whatever error gss_display_status is complaining about, # but obviates infinite recursion into stack exhaustion. The # exception raised here is handled by get_all_statuses(), which prints # the code. raise ValueError("gss_display_status call returned failure " "(major {0}, minor {1}).".format(maj_stat, min_stat)) class GSSErrorRegistry(type): __registry = {} def __init__(cls, name, bases, attributes): calling_code = getattr(cls, 'CALLING_CODE', None) routine_code = getattr(cls, 'ROUTINE_CODE', None) supplementary_code = getattr(cls, 'SUPPLEMENTARY_CODE', None) # NB(directxman12): we ignore minor code since it's mech-specific if any([calling_code, routine_code, supplementary_code]): if calling_code not in cls.__registry: cls.__registry[calling_code] = {} call_reg = cls.__registry[calling_code] if routine_code not in call_reg: call_reg[routine_code] = {} routine_reg = call_reg[routine_code] routine_reg[supplementary_code] = cls @staticmethod def __get_registry(code, parent_reg): return parent_reg.get(code, parent_reg.get(None, {})) def __find_error(cls, maj_code): codes = cls._parse_major_code(maj_code) calling_code, routine_code, suppl_code = codes call_reg = cls.__get_registry(calling_code, cls.__registry) routine_reg = cls.__get_registry(routine_code, call_reg) return routine_reg.get(suppl_code, routine_reg.get(None, None)) def __call__(cls, maj_code, min_code, *args, **kwargs): new_cls = cls.__find_error(maj_code) or cls return super(GSSErrorRegistry, new_cls).__call__(maj_code, min_code, *args, **kwargs) # NB(directxman12): this needs to be here (and not in another file) # so that display_status can use it class GSSError(Exception, metaclass=GSSErrorRegistry): """ A GSSAPI Error This Exception represents an error returned from the GSSAPI C bindings. It contains the major and minor status codes returned by the method which caused the error, and can generate human-readable string messages from the error codes """ MESSAGE = u"Major ({maj_stat}): {maj_str}, Minor ({min_stat}): {min_str}" @classmethod def _parse_major_code(cls, maj_code): # major status codes consist of # calling error | routine error | supplementary info # in non-overlapping bits calling_code = GSS_CALLING_ERROR(maj_code) or None routine_code = GSS_ROUTINE_ERROR(maj_code) or None supplementary_code = GSS_SUPPLEMENTARY_INFO(maj_code) or None return (calling_code, routine_code, supplementary_code) def __init__(self, maj_code, min_code, token=None): """ Create a new GSSError. This method creates a new GSSError, retrieves the releated human-readable string messages, and uses the results to construct an exception message Args: maj_code (int): the major code associated with this error min_code (int): the minor code associated with this error token (bytes): an error token associated with the error """ self.maj_code = maj_code self.min_code = min_code self.token = token split_codes = self._parse_major_code(maj_code) self.calling_code = split_codes[0] self.routine_code = split_codes[1] self.supplementary_code = split_codes[2] super(GSSError, self).__init__(self.gen_message()) def get_all_statuses(self, code, is_maj): """ Retrieve all messages for a status code. This method retrieves all human-readable messages available for the given status code. Args: code (int): the status code in question is_maj (bool): whether this is a major status code (True) or minor status code (False) Returns: [bytes]: A list of string messages associated with the given code """ msg_encoding = locale.getlocale(locale.LC_MESSAGES)[1] or 'UTF-8' res = [] try: msg, ctx, cont = _display_status(code, is_maj) res.append(msg.decode(msg_encoding)) except ValueError as e: res.append(u'{0} Decoding code: {1}'.format(e, code)) cont = False while cont: try: msg, ctx, cont = _display_status(code, is_maj, message_context=ctx) res.append(msg.decode(msg_encoding)) except ValueError: res.append(u'{0} Decoding code: {1}'.format(e, code)) cont = False return res def gen_message(self): """ Retrieves all messages for this error's status codes This method retrieves all messages for this error's status codes, and forms them into a string for use as an exception message Returns: bytes: a string for use as this error's message """ maj_statuses = self.get_all_statuses(self.maj_code, True) min_statuses = self.get_all_statuses(self.min_code, False) maj_str = u' -- '.join(maj_statuses) min_str = u' -- '.join(min_statuses) return self.MESSAGE.format(maj_stat=self.maj_code, maj_str=maj_str, min_stat=self.min_code, min_str=min_str) python-gssapi-1.6.1/gssapi/raw/_enum_extensions/0000775000372000037200000000000013523321764016713 5ustar python-gssapi-1.6.1/gssapi/raw/_enum_extensions/ext_iov_mic.pyx0000664000372000037200000000046413523321513021756 0ustar from gssapi.raw.cython_types cimport OM_uint32 from gssapi.raw import _enum_extensions as ext_registry cdef extern from "python_gssapi_ext.h": OM_uint32 GSS_IOV_BUFFER_TYPE_MIC_TOKEN ext_registry.register_value('IOVBufferType', 'mic_token', GSS_IOV_BUFFER_TYPE_MIC_TOKEN) python-gssapi-1.6.1/gssapi/raw/_enum_extensions/__init__.py0000664000372000037200000000151213523321513021013 0ustar from enum import EnumMeta _extra_values = {} def register_value(cl_str, name, value): _extra_values[cl_str] = _extra_values.get(cl_str, {}) _extra_values[cl_str][name] = value class ExtendableEnum(EnumMeta): def __new__(metacl, name, bases, classdict): extra_vals = _extra_values.get(name) if extra_vals is not None: for extra_name, extra_val in list(extra_vals.items()): if extra_name in classdict: raise AttributeError( "Enumeration extensions cannot override existing " "enumeration members") else: classdict[extra_name] = extra_val return super(ExtendableEnum, metacl).__new__(metacl, name, bases, classdict) python-gssapi-1.6.1/gssapi/raw/_enum_extensions/ext_dce.pyx0000664000372000037200000000101313523321513021053 0ustar from gssapi.raw.cython_types cimport OM_uint32 import gssapi.raw._enum_extensions as ext_registry cdef extern from "python_gssapi_ext.h": OM_uint32 GSS_C_DCE_STYLE OM_uint32 GSS_C_IDENTIFY_FLAG OM_uint32 GSS_C_EXTENDED_ERROR_FLAG ext_registry.register_value('RequirementFlag', 'dce_style', GSS_C_DCE_STYLE) ext_registry.register_value('RequirementFlag', 'identify', GSS_C_IDENTIFY_FLAG) ext_registry.register_value('RequirementFlag', 'extended_error', GSS_C_EXTENDED_ERROR_FLAG) python-gssapi-1.6.1/gssapi/raw/cython_converters.pyx0000664000372000037200000000253213523321513017643 0ustar from gssapi.raw.cython_types cimport * from gssapi.raw.oids cimport OID from gssapi.raw.types import MechType, NameType cdef OID c_make_oid(gss_OID oid): """Create an OID from a C OID struct pointer""" cdef OID res = OID() res.raw_oid = oid[0] return res cdef gss_OID_set c_get_mech_oid_set(object mechs): """Convert a list of MechType values into an OID set.""" cdef gss_OID_set res_set cdef OM_uint32 min_stat gss_create_empty_oid_set(&min_stat, &res_set) cdef gss_OID oid for mech in mechs: oid = &(mech).raw_oid gss_add_oid_set_member(&min_stat, oid, &res_set) return res_set cdef object c_create_oid_set(gss_OID_set mech_set, bint free=True): """Convert a GSS OID set struct to a set of OIDs""" if mech_set == GSS_C_NO_OID_SET: # return the empty set if the we get passed the C equivalent # (it could be argued that the C equivalent is closer to None, # but returning None would make the API harder to work with, # without much value) return set() py_set = set() cdef i for i in range(mech_set.count): mech_type = OID() mech_type._copy_from(mech_set.elements[i]) py_set.add(mech_type) cdef OM_uint32 tmp_min_stat if free: gss_release_oid_set(&tmp_min_stat, &mech_set) return py_set python-gssapi-1.6.1/gssapi/raw/python_gssapi.h0000664000372000037200000000023313523321513016357 0ustar #ifdef OSX_HAS_GSS_FRAMEWORK #include #elif defined(__MINGW32__) && defined(__MSYS__) #include #else #include #endif python-gssapi-1.6.1/gssapi/raw/names.pxd0000664000372000037200000000017613523321513015145 0ustar from gssapi.raw.cython_types cimport gss_name_t cdef class Name: cdef gss_name_t raw_name cdef bint _free_on_dealloc python-gssapi-1.6.1/gssapi/raw/message.pyx0000664000372000037200000002161713523321513015516 0ustar GSSAPI="BASE" # This ensures that a full module is generated by Cython from gssapi.raw.cython_types cimport * from gssapi.raw.sec_contexts cimport SecurityContext from gssapi.raw.misc import GSSError from gssapi.raw.named_tuples import WrapResult, UnwrapResult cdef extern from "python_gssapi.h": OM_uint32 gss_get_mic(OM_uint32 *min_stat, const gss_ctx_id_t context, gss_qop_t qop, const gss_buffer_t message, gss_buffer_t output_token) nogil OM_uint32 gss_verify_mic(OM_uint32 *min_stat, const gss_ctx_id_t context, const gss_buffer_t message, const gss_buffer_t token, gss_qop_t *qop) nogil OM_uint32 gss_wrap_size_limit(OM_uint32 *min_stat, const gss_ctx_id_t context, int conf_req, gss_qop_t qop, OM_uint32 max_output_size, OM_uint32 *max_input_size) nogil OM_uint32 gss_wrap(OM_uint32 *min_stat, const gss_ctx_id_t context, int conf_req, gss_qop_t qop, const gss_buffer_t input_message, int *conf_used, gss_buffer_t output_message) nogil OM_uint32 gss_unwrap(OM_uint32 *min_stat, const gss_ctx_id_t context, const gss_buffer_t input_message, gss_buffer_t output_message, int *conf_used, gss_qop_t *qop) nogil def get_mic(SecurityContext context not None, message, qop=None): """ get_mic(context, message, qop=None) Generate a MIC for a message. This method generates a Message Integrity Check token for the given message. This can be separately trasmitted to the other entity, unlike wrap, which bundles the MIC and the message together. Args: context (SecurityContext): the current security context message (bytes): the message for which to generate the MIC qop (int): the requested Quality of Protection (or None to use the default) Returns: bytes: the generated MIC token Raises: ExpiredContextError MissingContextError BadQoPError """ cdef gss_buffer_desc message_buffer = gss_buffer_desc(len(message), message) cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT # GSS_C_EMPYT_BUFFER cdef gss_buffer_desc token_buffer = gss_buffer_desc(0, NULL) cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_get_mic(&min_stat, context.raw_ctx, qop_req, &message_buffer, &token_buffer) if maj_stat == GSS_S_COMPLETE: res = (token_buffer.value)[:token_buffer.length] gss_release_buffer(&min_stat, &token_buffer) return res else: raise GSSError(maj_stat, min_stat) def verify_mic(SecurityContext context not None, message, token): """ verify_mic(context, message, token) Verify that a MIC matches a message. This method verifies that the given MIC matches the given message. If the MIC does not match the given message, an exception will be raised. Args: context (SecurityContext): the current security context message (bytes): the message in question token (bytes): the MIC token in question Returns: int: the QoP used. Raises: InvalidTokenError BadMICError DuplicateTokenError ExpiredTokenError TokenTooLateError TokenTooEarlyError ExpiredContextError MissingContextError """ cdef gss_buffer_desc message_buffer = gss_buffer_desc(len(message), message) cdef gss_buffer_desc token_buffer = gss_buffer_desc(len(token), token) cdef gss_qop_t qop_state cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_verify_mic(&min_stat, context.raw_ctx, &message_buffer, &token_buffer, &qop_state) if maj_stat == GSS_S_COMPLETE: return qop_state else: raise GSSError(maj_stat, min_stat) def wrap_size_limit(SecurityContext context not None, OM_uint32 output_size, confidential=True, qop=None): """ wrap_size_limit(context, output_size, confidential=True, qop=None) Calculate the max message size. This method calculates the unwrapped/unencrypted message size for the given maximum wrapped/encrypted message size. Args: context (SecurityContext): the current security context output_size (int): the maximum desired wrapped/encrypted message size confidential (bool): whether or not confidentiality is being used qop (int): the QoP that will be when you actually call wrap (or None for the default QoP) Returns: int: the maximum unencrypted/unwrapped message size Raises: MissingContextError ExpiredContextError BadQoPError """ cdef int conf_req = confidential cdef OM_uint32 qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT cdef OM_uint32 max_input_size cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_wrap_size_limit(&min_stat, context.raw_ctx, conf_req, qop_req, output_size, &max_input_size) if maj_stat == GSS_S_COMPLETE: return max_input_size else: raise GSSError(maj_stat, min_stat) def wrap(SecurityContext context not None, message, confidential=True, qop=None): """ wrap(context, message, confidential=True, qop=None) Wrap/Encrypt a message. This method wraps or encrypts a message (depending on the value of confidential) with the given Quality of Protection. Args: context (SecurityContext): the current security context message (bytes): the message to wrap or encrypt confidential (bool): whether or not to encrypt the message (True), or just wrap it with a MIC (False) qop (int): the desired Quality of Protection (or None for the default QoP) Returns: WrapResult: the wrapped/encrypted message, and whether or not encryption was actually used Raises: ExpiredContextError MissingContextError BadQoPError """ cdef int conf_req = confidential cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT cdef gss_buffer_desc message_buffer = gss_buffer_desc(len(message), message) cdef int conf_used # GSS_C_EMPTY_BUFFER cdef gss_buffer_desc output_buffer = gss_buffer_desc(0, NULL) cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_wrap(&min_stat, context.raw_ctx, conf_req, qop_req, &message_buffer, &conf_used, &output_buffer) if maj_stat == GSS_S_COMPLETE: output_message = (output_buffer.value)[:output_buffer.length] gss_release_buffer(&min_stat, &output_buffer) return WrapResult(output_message, conf_used) else: raise GSSError(maj_stat, min_stat) def unwrap(SecurityContext context not None, message): """ unwrap(context, message) Unwrap/Decrypt a message. This method unwraps or decrypts a message, depending on whether the sender used confidentiality. Args: context (SecurityContext): the current security context message (bytes): the message to unwrap/decrypt Returns: UnwrapResult: the unwrapped/decrypted message, whether or on encryption was used, and the QoP used Raises: InvalidTokenError BadMICError DuplicateTokenError ExpiredTokenError TokenTooLateError TokenTooEarlyError ExpiredContextError MissingContextError """ cdef gss_buffer_desc input_buffer = gss_buffer_desc(len(message), message) # GSS_C_EMPTY_BUFFER cdef gss_buffer_desc output_buffer = gss_buffer_desc(0, NULL) cdef int conf_state cdef gss_qop_t qop_state cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_unwrap(&min_stat, context.raw_ctx, &input_buffer, &output_buffer, &conf_state, &qop_state) if maj_stat == GSS_S_COMPLETE: output_message = (output_buffer.value)[:output_buffer.length] gss_release_buffer(&min_stat, &output_buffer) return UnwrapResult(output_message, conf_state, qop_state) else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/ext_rfc4178.pyx0000664000372000037200000000272113523321513016043 0ustar GSSAPI="BASE" # This ensures that a full module is generated by Cython from gssapi.raw.cython_types cimport * from gssapi.raw.cython_converters cimport c_get_mech_oid_set from gssapi.raw.creds cimport Creds from gssapi.raw.misc import GSSError cdef extern from "python_gssapi_ext.h": OM_uint32 gss_set_neg_mechs( OM_uint32 *minor_status, gss_cred_id_t cred_handle, const gss_OID_set mech_set) nogil def set_neg_mechs(Creds cred_handle not None, mech_set not None): """ set_neg_mechs(cred_handle not None, mech_set not None) Specify the set of security mechanisms that may be negotiated with the credential identified by cred_handle. If more than one mechanism is specified in mech_set, the order in which those mechanisms are specified implies a relative preference. Args: cred_handle (Creds): credentials to set negotiable mechanisms for mech_set ([MechType]): negotiable mechanisms to be set Returns: None Raises: GSSError """ cdef gss_OID_set negotiable_mechs = c_get_mech_oid_set(mech_set) cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_set_neg_mechs(&min_stat, cred_handle.raw_creds, negotiable_mechs) cdef OM_uint32 tmp_min_stat gss_release_oid_set(&tmp_min_stat, &negotiable_mechs) if maj_stat == GSS_S_COMPLETE: return None else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/names.pyx0000664000372000037200000002244513523321513015175 0ustar GSSAPI="BASE" # this ensures that a full module is generated by Cython from gssapi.raw.cython_types cimport * from gssapi.raw.oids cimport OID from gssapi.raw.misc import GSSError from gssapi.raw.named_tuples import DisplayNameResult cdef extern from "python_gssapi.h": OM_uint32 gss_import_name(OM_uint32 *min_stat, const gss_buffer_t input_buffer, const gss_OID name_type, gss_name_t *output_name) nogil OM_uint32 gss_display_name(OM_uint32 *min_stat, const gss_name_t name, gss_buffer_t output_buffer, gss_OID *output_name_type) nogil OM_uint32 gss_compare_name(OM_uint32 *min_stat, const gss_name_t name1, const gss_name_t name2, int *is_equal) nogil OM_uint32 gss_export_name(OM_uint32 *min_stat, const gss_name_t name, gss_buffer_t output_buffer) nogil OM_uint32 gss_canonicalize_name(OM_uint32 *min_stat, const gss_name_t input_name, const gss_OID mech_type, gss_name_t *output_name) nogil OM_uint32 gss_duplicate_name(OM_uint32 *min_stat, const gss_name_t input_name, gss_name_t *output_name) nogil OM_uint32 gss_release_name(OM_uint32 *min_stat, gss_name_t *name) nogil cdef class Name: """ A GSSAPI Name """ # defined in pxd # cdef gss_name_t raw_name def __cinit__(self, Name cpy=None): if cpy is not None: self.raw_name = cpy.raw_name cpy.raw_name = GSS_C_NO_NAME else: self.raw_name = GSS_C_NO_NAME def __dealloc__(self): # essentially just releaseName(self), but it is unsafe to call # methods cdef OM_uint32 maj_stat, min_stat if self.raw_name is not GSS_C_NO_NAME: maj_stat = gss_release_name(&min_stat, &self.raw_name) if maj_stat != GSS_S_COMPLETE: raise GSSError(maj_stat, min_stat) self.raw_name = NULL def import_name(name not None, OID name_type=None): """ import_name(name, name_type=None) Convert a string and a name type into a GSSAPI name. This method takes a string name and a name type and converts them into a GSSAPI :class:`Name`. Args: name (bytes): the string version of the name name_type (NameType): the type of this name Returns: Name: the GSSAPI version of the name Raises: BadNameTypeError BadNameError BadMechanismError """ cdef gss_OID nt if name_type is None: nt = GSS_C_NO_OID else: nt = &name_type.raw_oid # GSS_C_EMPTY_BUFFER cdef gss_buffer_desc name_buffer = gss_buffer_desc(0, NULL) name_buffer.length = len(name) name_buffer.value = name cdef gss_name_t output_name cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_import_name(&min_stat, &name_buffer, nt, &output_name) cdef Name on = Name() if maj_stat == GSS_S_COMPLETE: on.raw_name = output_name return on else: raise GSSError(maj_stat, min_stat) def display_name(Name name not None, name_type=True): """ display_name(name, name_type=True) Convert a GSSAPI name into its components. This method converts a GSSAPI :class:`Name` back into its text form. If ``name_type`` is True, it also attempts to retrieve the :class:`NameType` of the name (otherwise the returned name type will be ``None``). Args: name (Name): the name in question name_type (bool): whether or not to retrieve the name type Returns: DisplayNameResult: the text part of the name and its type Raises: BadNameError """ # GSS_C_EMPTY_BUFFER cdef gss_buffer_desc output_buffer = gss_buffer_desc(0, NULL) cdef gss_OID output_name_type cdef gss_OID *output_name_type_ptr if name_type: output_name_type_ptr = &output_name_type else: output_name_type_ptr = NULL cdef OM_uint32 maj_stat, min_stat maj_stat = gss_display_name(&min_stat, name.raw_name, &output_buffer, output_name_type_ptr) cdef OID py_name_type if maj_stat == GSS_S_COMPLETE: text = (output_buffer.value)[:output_buffer.length] gss_release_buffer(&min_stat, &output_buffer) if name_type: if output_name_type == GSS_C_NO_OID: # whoops, an implementation was being lazy... py_name_type = None else: py_name_type = OID() py_name_type.raw_oid = output_name_type[0] else: py_name_type = None return DisplayNameResult(text, py_name_type) else: raise GSSError(maj_stat, min_stat) def compare_name(Name name1=None, Name name2=None): """ compare_name(name1, name2) Check two GSSAPI names to see if they are the same. This method compares two GSSAPI names, checking to see if they are equivalent. Args: name1 (Name): the first name to compare name2 (Name): the second name to compare Returns: bool: whether or not the names are equal Raises: BadNameTypeError BadNameError """ # check for either value being None if name1 is None and name2 is None: return True elif name1 is None or name2 is None: return False cdef int is_equal cdef OM_uint32 maj_stat, min_stat maj_stat = gss_compare_name(&min_stat, name1.raw_name, name2.raw_name, &is_equal) if maj_stat == GSS_S_COMPLETE: return is_equal else: raise GSSError(maj_stat, min_stat) def export_name(Name name not None): """ Export a GSSAPI name. This method "produces a canonical contigous string representation of a mechanism name, suitable for direct comparison for use in authorization functions". The input name must be a valid GSSAPI mechanism name, as generated by :func:`canonicalize_name` or :func:`accept_sec_context`. Args: name (Name): the name to export Returns: bytes: the exported name Raises: MechanismNameRequiredError BadNameTypeError BadNameError """ # GSS_C_EMPTY_BUFFER cdef gss_buffer_desc exported_name = gss_buffer_desc(0, NULL) cdef OM_uint32 maj_stat, min_stat maj_stat = gss_export_name(&min_stat, name.raw_name, &exported_name) if maj_stat == GSS_S_COMPLETE: # force conversion to a python string with the specified length # (we use the slice to tell cython that we know the length already) res = (exported_name.value)[:exported_name.length] gss_release_buffer(&min_stat, &exported_name) return res else: raise GSSError(maj_stat, min_stat) def canonicalize_name(Name name not None, OID mech not None): """ canonicalize_name(name, mech) Canonicalize an arbitrary GSSAPI Name into a Mechanism Name This method turns any GSSAPI name into a "mechanism name" -- a full form name specific to a mechanism. Args: name (Name): the name to canonicalize mech (MechType): the mechanism type to use to canonicalize the name Returns: Name: a canonicalized version of the input name Raises: BadMechanismError BadNameTypeError BadNameError """ cdef gss_name_t canonicalized_name cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_canonicalize_name(&min_stat, name.raw_name, &mech.raw_oid, &canonicalized_name) cdef Name cn = Name() if maj_stat == GSS_S_COMPLETE: cn.raw_name = canonicalized_name return cn else: raise GSSError(maj_stat, min_stat) def duplicate_name(Name name not None): """ duplicate_name(name) Duplicate a GSSAPI name. Args: name (Name): the name to duplicate Returns: Name: a duplicate of the input name Raises: BadNameError """ cdef gss_name_t new_name cdef OM_uint32 maj_stat, min_stat maj_stat = gss_duplicate_name(&min_stat, name.raw_name, &new_name) cdef Name on = Name() if maj_stat == GSS_S_COMPLETE: on.raw_name = new_name return on else: raise GSSError(maj_stat, min_stat) def release_name(Name name not None): """ release_name(name) Release a GSSAPI name. This method frees a GSSAPI :class:`Name`. You probably won't have to do this. Warning: This method is deprecated. Names are automatically freed by Python. Args: name (Name): the name in question Raises: BadNameError """ cdef OM_uint32 maj_stat, min_stat maj_stat = gss_release_name(&min_stat, &name.raw_name) if maj_stat != GSS_S_COMPLETE: raise GSSError(maj_stat, min_stat) name.raw_name = NULL python-gssapi-1.6.1/gssapi/raw/cython_types.pxd0000664000372000037200000001006413523321513016567 0ustar from libc.stdint cimport uint32_t cdef extern from "python_gssapi.h": # basic types ctypedef uint32_t OM_uint32 # int type aliases ctypedef int gss_cred_usage_t ctypedef OM_uint32 gss_qop_t # struct types ctypedef struct gss_OID_desc: OM_uint32 length void *elements ctypedef gss_OID_desc* gss_OID ctypedef struct gss_OID_set_desc: size_t count gss_OID elements ctypedef gss_OID_set_desc* gss_OID_set ctypedef struct gss_buffer_desc: size_t length char *value ctypedef gss_buffer_desc* gss_buffer_t cdef struct gss_name_struct: pass ctypedef gss_name_struct* gss_name_t cdef struct gss_cred_id_struct: pass ctypedef gss_cred_id_struct* gss_cred_id_t cdef struct gss_ctx_id_struct: pass ctypedef gss_ctx_id_struct* gss_ctx_id_t ctypedef struct gss_channel_bindings_struct: OM_uint32 initiator_addrtype gss_buffer_desc initiator_address OM_uint32 acceptor_addrtype gss_buffer_desc acceptor_address gss_buffer_desc application_data ctypedef gss_channel_bindings_struct* gss_channel_bindings_t # util methods OM_uint32 gss_release_buffer(OM_uint32 *min_stat, gss_buffer_t buff) OM_uint32 gss_create_empty_oid_set(OM_uint32 *min_stat, gss_OID_set *target_set) OM_uint32 gss_release_oid_set(OM_uint32 *min_stat, gss_OID_set *target_set) OM_uint32 gss_add_oid_set_member(OM_uint32 *min_stat, const gss_OID member, gss_OID_set *target_set) OM_uint32 gss_test_oid_set_member(OM_uint32 *min_stat, const gss_OID member, const gss_OID_set target_set, int *present) # misc int constants # status code types int GSS_C_GSS_CODE int GSS_C_MECH_CODE # status code constants OM_uint32 GSS_S_COMPLETE OM_uint32 GSS_S_CONTINUE_NEEDED OM_uint32 GSS_S_DUPLICATE_TOKEN # cred_usage constants gss_cred_usage_t GSS_C_BOTH gss_cred_usage_t GSS_C_INITIATE gss_cred_usage_t GSS_C_ACCEPT # null/default constants gss_OID GSS_C_NO_OID # NB(sross): because of how Cython creates variables, this is useless # gss_buffer_desc GSS_C_EMPTY_BUFFER gss_name_t GSS_C_NO_NAME OM_uint32 GSS_C_INDEFINITE gss_buffer_t GSS_C_NO_BUFFER gss_OID_set GSS_C_NO_OID_SET gss_channel_bindings_t GSS_C_NO_CHANNEL_BINDINGS gss_qop_t GSS_C_QOP_DEFAULT gss_ctx_id_t GSS_C_NO_CONTEXT gss_cred_id_t GSS_C_NO_CREDENTIAL # OID constants # OID name types gss_OID GSS_C_NT_HOSTBASED_SERVICE gss_OID GSS_C_NT_USER_NAME gss_OID GSS_C_NT_ANONYMOUS gss_OID GSS_C_NT_MACHINE_UID_NAME gss_OID GSS_C_NT_STRING_UID_NAME gss_OID GSS_C_NT_EXPORT_NAME # flag constants OM_uint32 GSS_C_DELEG_FLAG OM_uint32 GSS_C_MUTUAL_FLAG OM_uint32 GSS_C_REPLAY_FLAG OM_uint32 GSS_C_SEQUENCE_FLAG OM_uint32 GSS_C_CONF_FLAG OM_uint32 GSS_C_INTEG_FLAG OM_uint32 GSS_C_ANON_FLAG OM_uint32 GSS_C_TRANS_FLAG OM_uint32 GSS_C_PROT_READY_FLAG # address types OM_uint32 GSS_C_AF_UNSPEC OM_uint32 GSS_C_AF_LOCAL OM_uint32 GSS_C_AF_INET OM_uint32 GSS_C_AF_IMPLINK OM_uint32 GSS_C_AF_PUP OM_uint32 GSS_C_AF_CHAOS OM_uint32 GSS_C_AF_NS OM_uint32 GSS_C_AF_NBS OM_uint32 GSS_C_AF_ECMA OM_uint32 GSS_C_AF_DATAKIT OM_uint32 GSS_C_AF_CCITT OM_uint32 GSS_C_AF_SNA OM_uint32 GSS_C_AF_DECnet OM_uint32 GSS_C_AF_DLI OM_uint32 GSS_C_AF_LAT OM_uint32 GSS_C_AF_HYLINK OM_uint32 GSS_C_AF_APPLETALK OM_uint32 GSS_C_AF_BSC OM_uint32 GSS_C_AF_DSS OM_uint32 GSS_C_AF_OSI OM_uint32 GSS_C_AF_X25 OM_uint32 GSS_C_AF_NULLADDR # error helpers OM_uint32 GSS_CALLING_ERROR(OM_uint32 full_error) OM_uint32 GSS_ROUTINE_ERROR(OM_uint32 full_error) OM_uint32 GSS_SUPPLEMENTARY_INFO(OM_uint32 full_error) python-gssapi-1.6.1/gssapi/raw/ext_cred_imp_exp.pyx0000664000372000037200000000473713523321513017414 0ustar """Credentials Import/Export Extension""" GSSAPI="BASE" # This ensures that a full module is generated by Cython from gssapi.raw.cython_types cimport * from gssapi.raw.cython_converters cimport c_create_oid_set from gssapi.raw.cython_converters cimport c_get_mech_oid_set from gssapi.raw.cython_converters cimport c_py_ttl_to_c, c_c_ttl_to_py from gssapi.raw.creds cimport Creds from gssapi.raw.names cimport Name from gssapi.raw.oids cimport OID from gssapi.raw.misc import GSSError from gssapi.raw.named_tuples import AcquireCredResult, AddCredResult cdef extern from "python_gssapi_ext.h": OM_uint32 gss_export_cred(OM_uint32 *min_stat, gss_cred_id_t cred_handle, gss_buffer_t token) nogil OM_uint32 gss_import_cred(OM_uint32 *min_stat, gss_buffer_t token, gss_cred_id_t *cred_handle) nogil def export_cred(Creds creds not None): """ export_cred(creds) Export GSSAPI credentials. This method exports GSSSAPI credentials into a token which may be transmitted between different processes. Args: creds (Creds): the credentials object to be exported Returns: bytes: the exported token representing the given credentials object Raises: GSSError """ # GSS_C_EMPTY_BUFFER cdef gss_buffer_desc exported_creds = gss_buffer_desc(0, NULL) cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_export_cred(&min_stat, creds.raw_creds, &exported_creds) if maj_stat == GSS_S_COMPLETE: res = (exported_creds.value)[:exported_creds.length] gss_release_buffer(&min_stat, &exported_creds) return res else: raise GSSError(maj_stat, min_stat) def import_cred(token not None): """ import_cred(token) Import GSSAPI credentials from a token. This method imports a credentials object from a token previously exported by :func:`export_cred`. Args: token (bytes): the token to import Returns: Creds: the imported credentials object Raises: GSSError """ cdef gss_buffer_desc token_buffer = gss_buffer_desc(len(token), token) cdef gss_cred_id_t creds cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_import_cred(&min_stat, &token_buffer, &creds) cdef Creds res if maj_stat == GSS_S_COMPLETE: res = Creds() res.raw_creds = creds return res else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/python_gssapi_krb5.h0000664000372000037200000000025013523321513017301 0ustar #ifdef OSX_HAS_GSS_FRAMEWORK #include #elif defined(__MINGW32__) && defined(__MSYS__) #include #else #include #endif python-gssapi-1.6.1/gssapi/raw/ext_buffer_sets.pxd0000664000372000037200000000061513523321513017227 0ustar from gssapi.raw.cython_types cimport * cdef extern from "python_gssapi.h": ctypedef struct gss_buffer_set_desc: size_t count gss_buffer_desc *elements ctypedef gss_buffer_set_desc* gss_buffer_set_t gss_buffer_set_t GSS_C_NO_BUFFER_SET OM_uint32 gss_release_buffer_set(OM_uint32 *min_stat, gss_buffer_set_t *buffer_set) nogil python-gssapi-1.6.1/gssapi/raw/ext_dce.pyx0000664000372000037200000004562313523321513015510 0ustar GSSAPI="BASE" # This ensures that a full module is generated by Cython from libc.stdlib cimport malloc, calloc, free from libc.string cimport memcpy from gssapi.raw.cython_types cimport * from gssapi.raw.sec_contexts cimport SecurityContext from gssapi.raw.misc import GSSError from gssapi.raw import types as gssapi_types from gssapi.raw.named_tuples import IOVUnwrapResult, WrapResult, UnwrapResult from collections import namedtuple from enum import IntEnum import six from gssapi.raw._enum_extensions import ExtendableEnum if six.PY2: from collections import Sequence else: from collections.abc import Sequence cdef extern from "python_gssapi_ext.h": # NB(directxman12): this wiki page has a different argument order # than the header file, and uses size_t instead of int # (this file matches the header file) OM_uint32 gss_wrap_iov(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle, int conf_req_flag, gss_qop_t qop_req, int *conf_ret, gss_iov_buffer_desc *iov, int iov_count) nogil OM_uint32 gss_unwrap_iov(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle, int* conf_ret, gss_qop_t *qop_ret, gss_iov_buffer_desc *iov, int iov_count) nogil OM_uint32 gss_wrap_iov_length(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle, int conf_req, gss_qop_t qop_req, int *conf_ret, gss_iov_buffer_desc *iov, int iov_count) nogil OM_uint32 gss_release_iov_buffer(OM_uint32 *min_stat, gss_iov_buffer_desc *iov, int iov_count) nogil OM_uint32 gss_wrap_aead(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle, int conf_req, gss_qop_t qop_req, gss_buffer_t input_assoc_buffer, gss_buffer_t input_payload_buffer, int *conf_ret, gss_buffer_t output_message_buffer) nogil OM_uint32 gss_unwrap_aead(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle, gss_buffer_t input_message_buffer, gss_buffer_t input_assoc_buffer, gss_buffer_t output_payload_buffer, int *conf_ret, gss_qop_t *qop_ret) nogil gss_iov_buffer_t GSS_C_NO_IOV_BUFFER OM_uint32 GSS_IOV_BUFFER_TYPE_EMPTY OM_uint32 GSS_IOV_BUFFER_TYPE_DATA OM_uint32 GSS_IOV_BUFFER_TYPE_HEADER OM_uint32 GSS_IOV_BUFFER_TYPE_MECH_PARAMS OM_uint32 GSS_IOV_BUFFER_TYPE_TRAILER OM_uint32 GSS_IOV_BUFFER_TYPE_PADDING OM_uint32 GSS_IOV_BUFFER_TYPE_STREAM OM_uint32 GSS_IOV_BUFFER_TYPE_SIGN_ONLY OM_uint32 GSS_IOV_BUFFER_FLAG_MASK OM_uint32 GSS_IOV_BUFFER_FLAG_ALLOCATE OM_uint32 GSS_IOV_BUFFER_FLAG_ALLOCATED # a few more are in the enum extension file class IOVBufferType(IntEnum, metaclass=ExtendableEnum): """ IOV Buffer Types This IntEnum represent GSSAPI IOV buffer types to be used with the IOV methods. The numbers behind the values correspond directly to their C counterparts. """ empty = GSS_IOV_BUFFER_TYPE_EMPTY data = GSS_IOV_BUFFER_TYPE_DATA header = GSS_IOV_BUFFER_TYPE_HEADER mech_params = GSS_IOV_BUFFER_TYPE_MECH_PARAMS trailer = GSS_IOV_BUFFER_TYPE_TRAILER padding = GSS_IOV_BUFFER_TYPE_PADDING stream = GSS_IOV_BUFFER_TYPE_STREAM sign_only = GSS_IOV_BUFFER_TYPE_SIGN_ONLY IOVBuffer = namedtuple('IOVBuffer', ['type', 'allocate', 'value']) cdef class IOV: """A GSSAPI IOV""" # defined in ext_dce.pxd # cdef int iov_len # cdef bint c_changed # cdef gss_iov_buffer_desc *_iov # cdef bint _unprocessed # cdef list _buffs AUTO_ALLOC_BUFFERS = set([IOVBufferType.header, IOVBufferType.padding, IOVBufferType.trailer]) def __init__(IOV self, *args, std_layout=True, auto_alloc=True): self._unprocessed = True self.c_changed = False self._buffs = [] if std_layout: self._buffs.append(IOVBuffer(IOVBufferType.header, auto_alloc, None)) cdef char *val_copy for buff_desc in args: if isinstance(buff_desc, tuple): if len(buff_desc) > 3 or len(buff_desc) < 2: raise ValueError("Buffer description tuples must be " "length 2 or 3") buff_type = buff_desc[0] if len(buff_desc) == 2: if buff_type in self.AUTO_ALLOC_BUFFERS: alloc = buff_desc[1] data = None else: data = buff_desc[1] alloc = False else: (buff_type, alloc, data) = buff_desc self._buffs.append(IOVBuffer(buff_type, alloc, data)) elif isinstance(buff_desc, bytes): # assume type data val = buff_desc self._buffs.append(IOVBuffer(IOVBufferType.data, False, val)) else: alloc = False if buff_desc in self.AUTO_ALLOC_BUFFERS: alloc = auto_alloc self._buffs.append(IOVBuffer(buff_desc, alloc, None)) if std_layout: self._buffs.append(IOVBuffer(IOVBufferType.padding, auto_alloc, None)) self._buffs.append(IOVBuffer(IOVBufferType.trailer, auto_alloc, None)) cdef gss_iov_buffer_desc* __cvalue__(IOV self) except NULL: cdef OM_uint32 tmp_min_stat cdef int i if self._unprocessed: if self._iov is not NULL: gss_release_iov_buffer(&tmp_min_stat, self._iov, self.iov_len) free(self._iov) self.iov_len = len(self._buffs) self._iov = calloc( self.iov_len, sizeof(gss_iov_buffer_desc)) if self._iov is NULL: raise MemoryError("Cannot calloc for IOV buffer array") for i in range(self.iov_len): buff = self._buffs[i] self._iov[i].type = buff.type if buff.allocate: self._iov[i].type |= GSS_IOV_BUFFER_FLAG_ALLOCATE elif buff.allocate is None: self._iov[i].type |= GSS_IOV_BUFFER_FLAG_ALLOCATED if buff.value is None: self._iov[i].buffer.length = 0 self._iov[i].buffer.value = NULL else: self._iov[i].buffer.length = len(buff.value) self._iov[i].buffer.value = malloc( self._iov[i].buffer.length) if self._iov[i].buffer.value is NULL: raise MemoryError("Cannot malloc for buffer value") memcpy(self._iov[i].buffer.value, buff.value, self._iov[i].buffer.length) return self._iov cdef _recreate_python_values(IOV self): cdef i cdef bint val_change = False cdef size_t new_len for i in range(self.iov_len): old_type = self._buffs[i].type if self._iov[i].buffer.value is NULL: if self._iov[i].buffer.length == 0: new_val = None else: new_len = self._iov[i].buffer.length new_val = b'\x00' * new_len else: new_len = self._iov[i].buffer.length new_val = (self._iov[i].buffer.value)[:new_len] alloc = False if self._iov[i].type & GSS_IOV_BUFFER_FLAG_ALLOCATE: alloc = True # NB(directxman12): GSSAPI (at least in MIT krb5) doesn't # unset the allocate flag (because it's an "input flag", # so this needs to come second and be separate if self._iov[i].type & GSS_IOV_BUFFER_FLAG_ALLOCATED: alloc = None self._buffs[i] = IOVBuffer(old_type, alloc, new_val) self.c_changed = False def __getitem__(IOV self, ind): if self.c_changed: self._recreate_python_values() return self._buffs[ind] def __len__(IOV self): if self.c_changed: self._recreate_python_values() return len(self._buffs) def __iter__(IOV self): if self.c_changed: self._recreate_python_values() for val in self._buffs: yield val def __contains__(IOV self, item): if self.c_changed: self._recreate_python_values() return item in self._buffs def __reversed__(IOV self): if self.c_changed: self._recreate_python_values() for val in reversed(self._buffs): yield val def index(IOV self, value): for i, v in enumerate(self): if v == value: return i raise ValueError def count(IOV self, value): return sum(1 for v in self if v == value) def __repr__(IOV self): if self.c_changed: self._recreate_python_values() return "<{module}.{name} {buffs}>".format( module=type(self).__module__, name=type(self).__name__, buffs=repr(self._buffs)) def __str__(IOV self): buff_strs = [] for buff in self: type_val = str(buff.type).split('.')[1].upper() if buff.value is None: auto_alloc = buff.allocate if auto_alloc: buff_strs.append(type_val + "(allocate)") else: buff_strs.append(type_val + "(empty)") else: if buff.allocate is None: alloc_str = ", allocated" else: alloc_str = "" buff_strs.append("{0}({1!r}{2})".format(type_val, buff.value, alloc_str)) return "".format(' | '.join(buff_strs)) def __dealloc__(IOV self): cdef OM_uint32 tmp_min_stat cdef int i if self._iov is not NULL: gss_release_iov_buffer(&tmp_min_stat, self._iov, self.iov_len) for i in range(self.iov_len): if self._iov[i].buffer.value is not NULL: free(self._iov[i].buffer.value) free(self._iov) def wrap_iov(SecurityContext context not None, IOV message not None, confidential=True, qop=None): """ wrap_iov(context, message, confidential=True, qop=None) Wrap/Encrypt an IOV message. This method wraps or encrypts an IOV message. The allocate parameter of the :class:`IOVBuffer` objects in the :class:`IOV` indicates whether or not that particular buffer should be automatically allocated (for use with padding, header, and trailer buffers). Warning: This modifies the input :class:`IOV`. Args: context (SecurityContext): the current security context message (IOV): an :class:`IOV` containing the message confidential (bool): whether or not to encrypt the message (True), or just wrap it with a MIC (False) qop (int): the desired Quality of Protection (or None for the default QoP) Returns: bool: whether or not confidentiality was actually used Raises: GSSError """ cdef int conf_req = confidential cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT cdef int conf_used cdef gss_iov_buffer_desc *res_arr = message.__cvalue__() cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_wrap_iov(&min_stat, context.raw_ctx, conf_req, qop_req, &conf_used, res_arr, message.iov_len) if maj_stat == GSS_S_COMPLETE: message.c_changed = True return conf_used else: raise GSSError(maj_stat, min_stat) def unwrap_iov(SecurityContext context not None, IOV message not None): """ unwrap_iov(context, message) Unwrap/Decrypt an IOV message. This method uwraps or decrypts an IOV message. The allocate parameter of the :class:`IOVBuffer` objects in the :class:`IOV` indicates whether or not that particular buffer should be automatically allocated (for use with padding, header, and trailer buffers). As a special case, you may pass an entire IOV message as a single 'stream'. In this case, pass a buffer type of :attr:`IOVBufferType.stream` followed by a buffer type of :attr:`IOVBufferType.data`. The former should contain the entire IOV message, while the latter should be empty. Warning: This modifies the input :class:`IOV`. Args: context (SecurityContext): the current security context message (IOV): an :class:`IOV` containing the message Returns: IOVUnwrapResult: whether or not confidentiality was used, and the QoP used. Raises: GSSError """ cdef int conf_used cdef gss_qop_t qop_used cdef gss_iov_buffer_desc *res_arr = message.__cvalue__() cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_unwrap_iov(&min_stat, context.raw_ctx, &conf_used, &qop_used, res_arr, message.iov_len) if maj_stat == GSS_S_COMPLETE: message.c_changed = True return IOVUnwrapResult(conf_used, qop_used) else: raise GSSError(maj_stat, min_stat) def wrap_iov_length(SecurityContext context not None, IOV message not None, confidential=True, qop=None): """ wrap_iov_length(context, message, confidential=True, qop=None) Appropriately size padding, trailer, and header IOV buffers. This method sets the length values on the IOV buffers. You should already have data provided for the data (and sign-only) buffer(s) so that padding lengths can be appropriately computed. In Python terms, this will result in an appropriately sized `bytes` object consisting of all zeros. Warning: This modifies the input :class:`IOV`. Args: context (SecurityContext): the current security context message (IOV): an :class:`IOV` containing the message Returns: WrapResult: a list of :class:IOVBuffer` objects, and whether or not encryption was actually used Raises: GSSError """ cdef int conf_req = confidential cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT cdef int conf_used cdef gss_iov_buffer_desc *res_arr = message.__cvalue__() cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_wrap_iov_length(&min_stat, context.raw_ctx, conf_req, qop_req, &conf_used, res_arr, message.iov_len) if maj_stat == GSS_S_COMPLETE: message.c_changed = True return conf_used else: raise GSSError(maj_stat, min_stat) def wrap_aead(SecurityContext context not None, bytes message not None, bytes associated=None, confidential=True, qop=None): """ wrap_aead(context, message, associated=None, confidential=True, qop=None) Wrap/Encrypt an AEAD message. This method takes an input message and associated data, and outputs and AEAD message. Args: context (SecurityContext): the current security context message (bytes): the message to wrap or encrypt associated (bytes): associated data to go with the message confidential (bool): whether or not to encrypt the message (True), or just wrap it with a MIC (False) qop (int): the desired Quality of Protection (or None for the default QoP) Returns: WrapResult: the wrapped/encrypted total message, and whether or not encryption was actually used Raises: GSSError """ cdef int conf_req = confidential cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT cdef gss_buffer_desc message_buffer = gss_buffer_desc(len(message), message) cdef gss_buffer_t assoc_buffer_ptr = GSS_C_NO_BUFFER cdef gss_buffer_desc assoc_buffer if associated is not None: assoc_buffer = gss_buffer_desc(len(associated), associated) assoc_buffer_ptr = &assoc_buffer cdef int conf_used # GSS_C_EMPTY_BUFFER cdef gss_buffer_desc output_buffer = gss_buffer_desc(0, NULL) cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_wrap_aead(&min_stat, context.raw_ctx, conf_req, qop_req, assoc_buffer_ptr, &message_buffer, &conf_used, &output_buffer) if maj_stat == GSS_S_COMPLETE: output_message = (output_buffer.value)[:output_buffer.length] gss_release_buffer(&min_stat, &output_buffer) return WrapResult(output_message, conf_used) else: raise GSSError(maj_stat, min_stat) def unwrap_aead(SecurityContext context not None, bytes message not None, bytes associated=None): """ unwrap_aead(context, message, associated=None) Unwrap/Decrypt an AEAD message. This method takes an encrpyted/wrapped AEAD message and some associated data, and returns an unwrapped/decrypted message. Args: context (SecurityContext): the current security context message (bytes): the AEAD message to unwrap or decrypt associated (bytes): associated data that goes with the message Returns: UnwrapResult: the unwrapped/decrypted message, whether or on encryption was used, and the QoP used Raises: GSSError """ cdef gss_buffer_desc input_buffer = gss_buffer_desc(len(message), message) cdef gss_buffer_t assoc_buffer_ptr = GSS_C_NO_BUFFER cdef gss_buffer_desc assoc_buffer if associated is not None: assoc_buffer = gss_buffer_desc(len(associated), associated) assoc_buffer_ptr = &assoc_buffer # GSS_C_EMPTY_BUFFER cdef gss_buffer_desc output_buffer = gss_buffer_desc(0, NULL) cdef int conf_state cdef gss_qop_t qop_state cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_unwrap_aead(&min_stat, context.raw_ctx, &input_buffer, assoc_buffer_ptr, &output_buffer, &conf_state, &qop_state) if maj_stat == GSS_S_COMPLETE: output_message = (output_buffer.value)[:output_buffer.length] gss_release_buffer(&min_stat, &output_buffer) return UnwrapResult(output_message, conf_state, qop_state) else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/cython_converters.pxd0000664000372000037200000000214313523321513017614 0ustar from libc.string cimport memcmp from gssapi.raw.cython_types cimport gss_OID, gss_OID_set, gss_OID_desc from gssapi.raw.cython_types cimport OM_uint32 from gssapi.raw.cython_types cimport GSS_C_INDEFINITE from gssapi.raw.oids cimport OID from gssapi.raw.types import MechType, NameType cdef gss_OID_set c_get_mech_oid_set(object mechs) cdef inline bint c_compare_oids(gss_OID a, gss_OID b) cdef object c_create_oid_set(gss_OID_set mech_set, bint free=*) cdef OID c_make_oid(gss_OID oid) cdef inline OM_uint32 c_py_ttl_to_c(object ttl) except? 1: """Converts None to GSS_C_INDEFINITE, otherwise returns input.""" if ttl is None: return GSS_C_INDEFINITE else: return ttl cdef inline object c_c_ttl_to_py(OM_uint32 ttl): """Converts GSS_C_INDEFINITE to None, otherwise return input.""" if ttl == GSS_C_INDEFINITE: return None else: return ttl cdef inline bint c_compare_oids(gss_OID a, gss_OID b): """Compare two OIDs to see if they are the same.""" return (a.length == b.length and not memcmp(a.elements, b.elements, a.length)) python-gssapi-1.6.1/gssapi/raw/ext_s4u.pyx0000664000372000037200000002005213523321513015455 0ustar """Service4User Extension""" GSSAPI="BASE" # This ensures that a full module is generated by Cython from gssapi.raw.cython_types cimport * from gssapi.raw.cython_converters cimport c_create_oid_set from gssapi.raw.cython_converters cimport c_get_mech_oid_set from gssapi.raw.cython_converters cimport c_py_ttl_to_c, c_c_ttl_to_py from gssapi.raw.creds cimport Creds from gssapi.raw.names cimport Name from gssapi.raw.oids cimport OID from gssapi.raw.misc import GSSError from gssapi.raw.named_tuples import AcquireCredResult, AddCredResult cdef extern from "python_gssapi_ext.h": OM_uint32 gss_acquire_cred_impersonate_name(OM_uint32 *min_stat, const gss_cred_id_t imp_creds, const gss_name_t name, OM_uint32 ttl, const gss_OID_set mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_creds, gss_OID_set *actual_mechs, OM_uint32 *actual_ttl) nogil OM_uint32 gss_add_cred_impersonate_name(OM_uint32 *min_stat, gss_cred_id_t base_creds, const gss_cred_id_t imp_creds, const gss_name_t name, const gss_OID mech, gss_cred_usage_t cred_usage, OM_uint32 initiator_ttl, OM_uint32 acceptor_ttl, gss_cred_id_t *output_creds, gss_OID_set *actual_mechs, OM_uint32 *actual_init_ttl, OM_uint32 *actual_accept_ttl) nogil def acquire_cred_impersonate_name(Creds impersonator_cred not None, Name name not None, lifetime=None, mechs=None, usage='initiate'): """ acquire_cred_impersonate_name(impersonator_cred, name, lifetime=None, \ mechs=None, usage='initiate') Acquire credentials by impersonating another name. This method is one of the ways to use S4U2Self. It acquires credentials by impersonating another name using a set of proxy credentials. The impersonator credentials must have a usage of 'both' or 'initiate'. Args: impersonator_cred (Cred): the credentials with permissions to impersonate the target name name (Name): the name to impersonate lifetime (int): the lifetime for the credentials (or None for indefinite) mechs ([MechType]): the desired mechanisms for which the credentials should work (or None for the default set) usage (str): the usage type for the credentials: may be 'initiate', 'accept', or 'both' Returns: AcquireCredResult: the resulting credentials, the actual mechanisms with which they may be used, and their actual lifetime (or None for indefinite or not support) Raises: GSSError """ cdef gss_OID_set desired_mechs if mechs is not None: desired_mechs = c_get_mech_oid_set(mechs) else: desired_mechs = GSS_C_NO_OID_SET cdef OM_uint32 input_ttl = c_py_ttl_to_c(lifetime) cdef gss_name_t c_name = name.raw_name cdef gss_cred_usage_t c_usage if usage == 'initiate': c_usage = GSS_C_INITIATE elif usage == 'accept': c_usage = GSS_C_ACCEPT else: c_usage = GSS_C_BOTH cdef gss_cred_id_t creds cdef gss_OID_set actual_mechs cdef OM_uint32 actual_ttl cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_acquire_cred_impersonate_name( &min_stat, impersonator_cred.raw_creds, name.raw_name, input_ttl, desired_mechs, c_usage, &creds, &actual_mechs, &actual_ttl) cdef OM_uint32 tmp_min_stat if mechs is not None: gss_release_oid_set(&tmp_min_stat, &desired_mechs) cdef Creds rc = Creds() if maj_stat == GSS_S_COMPLETE: rc.raw_creds = creds return AcquireCredResult(rc, c_create_oid_set(actual_mechs), c_c_ttl_to_py(actual_ttl)) else: raise GSSError(maj_stat, min_stat) def add_cred_impersonate_name(Creds input_cred, Creds impersonator_cred not None, Name name not None, OID mech not None, usage='initiate', init_lifetime=None, accept_lifetime=None): """ add_cred_impersonate_name(input_cred, impersonator_cred, name, mech, \ usage='initiate', init_lifetime=None, accept_lifetime=None) Add a credentials element to a credential by impersonating another name. This method is one of the ways to use S4U2Self. It adds credentials to the input credentials by impersonating another name using a set of proxy credentials. The impersonator credentials must have a usage of 'both' or 'initiate'. Args: input_cred (Cred): the set of credentials to which to add the new credentials impersonator_cred (Cred): the credentials with permissions to impersonate the target name name (Name): the name to impersonate mech (MechType): the desired mechanism. Note that this is both singular and required, unlike acquireCredImpersonateName usage (str): the usage type for the credentials: may be 'initiate', 'accept', or 'both' init_lifetime (int): the lifetime for the credentials to remain valid when using them to initiate security contexts (or None for indefinite) accept_lifetime (int): the lifetime for the credentials to remain valid when using them to accept security contexts (or None for indefinite) Returns: AddCredResult: the actual mechanisms with which the credentials may be used, the actual initiator TTL, and the actual acceptor TTL (the TTLs may be None for indefinite or not supported) Raises: GSSError """ cdef OM_uint32 input_initiator_ttl = c_py_ttl_to_c(init_lifetime) cdef OM_uint32 input_acceptor_ttl = c_py_ttl_to_c(accept_lifetime) cdef gss_name_t c_name = name.raw_name cdef gss_cred_usage_t c_usage if usage == 'initiate': c_usage = GSS_C_INITIATE elif usage == 'accept': c_usage = GSS_C_ACCEPT else: c_usage = GSS_C_BOTH cdef gss_cred_id_t raw_input_cred if input_cred is not None: raw_input_cred = input_cred.raw_creds else: raw_input_cred = GSS_C_NO_CREDENTIAL cdef gss_cred_id_t creds cdef gss_OID_set actual_mechs cdef OM_uint32 actual_initiator_ttl cdef OM_uint32 actual_acceptor_ttl cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_add_cred_impersonate_name(&min_stat, raw_input_cred, impersonator_cred.raw_creds, name.raw_name, &mech.raw_oid, c_usage, input_initiator_ttl, input_acceptor_ttl, &creds, &actual_mechs, &actual_initiator_ttl, &actual_acceptor_ttl) cdef Creds rc if maj_stat == GSS_S_COMPLETE: rc = Creds() rc.raw_creds = creds return AddCredResult(rc, c_create_oid_set(actual_mechs), c_c_ttl_to_py(actual_initiator_ttl), c_c_ttl_to_py(actual_acceptor_ttl)) else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/mech_krb5.pyx0000664000372000037200000000110513523321513015717 0ustar GSSAPI="BASE" # This ensures that a full module is generated by Cython from gssapi.raw.cython_types cimport * from gssapi.raw.cython_converters cimport c_make_oid from gssapi.raw import types as gsstypes """Kerberos-specific constants Upon import, this module will populate Kerberos-specific constants into NameType and MechType. """ cdef extern from "python_gssapi_krb5.h": gss_OID gss_mech_krb5 gss_OID GSS_KRB5_NT_PRINCIPAL_NAME gsstypes.NameType.kerberos_principal = c_make_oid(GSS_KRB5_NT_PRINCIPAL_NAME) gsstypes.MechType.kerberos = c_make_oid(gss_mech_krb5) python-gssapi-1.6.1/gssapi/raw/ext_rfc6680.pyx0000664000372000037200000002544013523321513016046 0ustar GSSAPI="BASE" # This ensures that a full module is generated by Cython from gssapi.raw.cython_types cimport * from gssapi.raw.ext_buffer_sets cimport * from gssapi.raw.names cimport Name from gssapi.raw.oids cimport OID from gssapi.raw.misc import GSSError from gssapi.raw.named_tuples import InquireNameResult, GetNameAttributeResult cdef extern from "python_gssapi_ext.h": OM_uint32 gss_display_name_ext(OM_uint32 *min_stat, gss_name_t name, gss_OID name_type, gss_buffer_t output_name) nogil OM_uint32 gss_inquire_name(OM_uint32 *min_stat, gss_name_t name, int *name_is_mn, gss_OID *mech_type, gss_buffer_set_t *attrs) nogil OM_uint32 gss_get_name_attribute(OM_uint32 *min_stat, gss_name_t name, gss_buffer_t attr, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) nogil OM_uint32 gss_set_name_attribute(OM_uint32 *min_stat, gss_name_t name, int complete, gss_buffer_t attr, gss_buffer_t value) nogil OM_uint32 gss_delete_name_attribute(OM_uint32 *min_stat, gss_name_t name, gss_buffer_t attr) nogil OM_uint32 gss_export_name_composite(OM_uint32 *min_stat, gss_name_t name, gss_buffer_t exported_name) nogil # GSS_C_NT_COMPOSITE_EXPORT lives in ext_rfc6680_comp_oid.pyx def display_name_ext(Name name not None, OID name_type not None): """ display_name_ext(name, name_type) Display the given Name using the given name type. This method attempts to display the given Name using the syntax of the given name type. If this is not possible, an appropriate error will be raised. Args: name (Name): the name to display name_type (OID): the name type (see NameType) to use to display the given name Returns: bytes: the displayed name Raises: OperationUnavailableError: the given name could not be displayed using the given name type """ # GSS_C_EMPTY_BUFFER cdef gss_buffer_desc output_name = gss_buffer_desc(0, NULL) cdef OM_uint32 maj_stat, min_stat maj_stat = gss_display_name_ext(&min_stat, name.raw_name, &name_type.raw_oid, &output_name) if maj_stat == GSS_S_COMPLETE: name_text = (output_name.value)[:output_name.length] gss_release_buffer(&min_stat, &output_name) return name_text else: raise GSSError(maj_stat, min_stat) def inquire_name(Name name not None, mech_name=True, attrs=True): """ inquire_name(name, mech_name=True, attrs=True) Get information about a Name. This method retrives information about the given name, including the set of attribute names for the given name, as well as whether or not the name is a mechanism name. Additionally, if the given name is a mechanism name, the associated mechansim is returned as well. Args: name (Name): the name about which to inquire mech_name (bool): whether or not to retrieve if this name is a mech_name (and the associate mechanism) attrs (bool): whether or not to retrieve the attribute name list Returns: InquireNameResult: the set of attribute names for the given name, whether or not the name is a Mechanism Name, and potentially the associated mechanism if it is a Mechanism Name Raises: GSSError """ cdef int *name_is_mn_ptr = NULL cdef gss_OID *mn_mech_ptr = NULL cdef gss_buffer_set_t *attr_names_ptr = NULL cdef gss_buffer_set_t attr_names = GSS_C_NO_BUFFER_SET if attrs: attr_names_ptr = &attr_names cdef int name_is_mn = 0 cdef gss_OID mn_mech if mech_name: name_is_mn_ptr = &name_is_mn mn_mech_ptr = &mn_mech cdef OM_uint32 maj_stat, min_stat maj_stat = gss_inquire_name(&min_stat, name.raw_name, name_is_mn_ptr, mn_mech_ptr, attr_names_ptr) cdef int i cdef OID py_mech = None if maj_stat == GSS_S_COMPLETE: py_attr_names = [] if attr_names != GSS_C_NO_BUFFER_SET: for i in range(attr_names.count): attr_name = attr_names.elements[i] py_attr_names.append( (attr_name.value)[:attr_name.length] ) gss_release_buffer_set(&min_stat, &attr_names) if name_is_mn: py_mech = OID() py_mech.raw_oid = mn_mech[0] return InquireNameResult(py_attr_names, name_is_mn, py_mech) else: raise GSSError(maj_stat, min_stat) def set_name_attribute(Name name not None, attr not None, value not None, bint complete=False): """ set_name_attribute(name, attr, value, complete=False) Set the value(s) of a name attribute. This method sets the value(s) of the given attribute on the given name. Note that this functionality more closely matches the pseudo-API presented in RFC 6680, not the C API (which uses multiple calls to add multiple values). However, multiple calls to this method will continue adding values, so :func:`delete_name_attribute` must be used in between calls to "clear" the values. Args: name (Name): the Name on which to set the attribute attr (bytes): the name of the attribute value (list): a list of bytes objects to use as the value(s) complete (bool): whether or not to mark this attribute's value set as being "complete" Raises: OperationUnavailableError: the given attribute name is unknown or could not be set """ cdef gss_buffer_desc attr_buff = gss_buffer_desc(len(attr), attr) cdef gss_buffer_desc val_buff cdef OM_uint32 maj_stat, min_stat cdef size_t value_len = len(value) cdef size_t i for val in value: val_buff = gss_buffer_desc(len(val), val) i += 1 if i == value_len: maj_stat = gss_set_name_attribute(&min_stat, name.raw_name, complete, &attr_buff, &val_buff) else: maj_stat = gss_set_name_attribute(&min_stat, name.raw_name, 0, &attr_buff, &val_buff) if maj_stat != GSS_S_COMPLETE: raise GSSError(maj_stat, min_stat) def get_name_attribute(Name name not None, attr not None, more=None): """ get_name_attribute(name, attr, more=None) Get the value(s) of a name attribute. This method retrieves the value(s) of the given attribute for the given Name. Note that this functionality matches pseudo-API presented in RFC 6680, not the C API (which uses a state variable and multiple calls to retrieve multiple values). Args: name (Name): the Name from which to get the attribute attr (bytes): the name of the attribute Returns: GetNameAttributeResult: the raw version of the value(s), the human-readable version of the value(s), whether or not the attribute was authenticated, and whether or not the attribute's value set was marked as complete Raises: OperationUnavailableError: the given attribute is unknown or unset """ cdef gss_buffer_desc attr_buff = gss_buffer_desc(len(attr), attr) cdef gss_buffer_desc val_buff = gss_buffer_desc(0, NULL) cdef gss_buffer_desc displ_val_buff = gss_buffer_desc(0, NULL) cdef int complete cdef int authenticated cdef int more_val = -1 py_vals = [] py_displ_vals = [] cdef OM_uint32 maj_stat, min_stat while more_val != 0: maj_stat = gss_get_name_attribute(&min_stat, name.raw_name, &attr_buff, &authenticated, &complete, &val_buff, &displ_val_buff, &more_val) if maj_stat == GSS_S_COMPLETE: py_vals.append((val_buff.value)[:val_buff.length]) py_displ_vals.append( (displ_val_buff.value)[:displ_val_buff.length]) gss_release_buffer(&min_stat, &val_buff) gss_release_buffer(&min_stat, &displ_val_buff) else: raise GSSError(maj_stat, min_stat) return GetNameAttributeResult(py_vals, py_displ_vals, authenticated, complete) def delete_name_attribute(Name name not None, attr not None): """ delete_name_attribute(name, attr) Remove an attribute from a name. This method removes an attribute from a Name. This method may be used before :func:`set_name_attribute` clear the values of an attribute before setting a new value (making the latter method work like a 'set' operation instead of an 'add' operation). Note that the removal of certain attributes may not be allowed. Args: name (Name): the name to remove the attribute from attr (bytes): the name of the attribute Raises: OperationUnavailableError UnauthorizedError """ cdef gss_buffer_desc attr_buff = gss_buffer_desc(len(attr), attr) cdef OM_uint32 maj_stat, min_stat maj_stat = gss_delete_name_attribute(&min_stat, name.raw_name, &attr_buff) if maj_stat != GSS_S_COMPLETE: raise GSSError(maj_stat, min_stat) def export_name_composite(Name name not None): """ export_name_composite(name) Export a name, preserving attribute information. This method functions similarly to :func:`export_name`, except that it preserves attribute information. The resulting bytes may be imported using :func:`import_name` with the :attr:`NameType.composite_export` name type. Note: Some versions of MIT Kerberos require you to either canonicalize a name once it has been imported with composite-export name type, or to import using the normal export name type. Args: name (Name): the name to export Returns: bytes: the exported composite name Raises: GSSError """ cdef gss_buffer_desc res = gss_buffer_desc(0, NULL) cdef OM_uint32 maj_stat, min_stat maj_stat = gss_export_name_composite(&min_stat, name.raw_name, &res) if maj_stat == GSS_S_COMPLETE: py_res = (res.value)[:res.length] gss_release_buffer(&min_stat, &res) return py_res else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/ext_set_cred_opt.pyx0000664000372000037200000000541313523321513017420 0ustar """ gss_set_cred_option Provides a way to set options on a credential based on the OID specified. A common use case is to set the GSS_KRB5_CRED_NO_CI_FLAGS_X on a Kerberos credential. This is used for interoperability with Microsoft's SSPI. Note this function is commonly lumped with the GGF extensions but they are not part of the GGF IETF draft so it's separated into it's own file. Closest draft IETF document for the gss_set_cred_option can be found at https://tools.ietf.org/html/draft-williams-kitten-channel-bound-flag-01 """ GSSAPI="BASE" # This ensures that a full module is generated by Cython from gssapi.raw.cython_types cimport * from gssapi.raw.ext_buffer_sets cimport * from gssapi.raw.misc import GSSError from gssapi.raw.oids cimport OID from gssapi.raw.creds cimport Creds cdef extern from "python_gssapi_ext.h": OM_uint32 gss_set_cred_option(OM_uint32 *minor_status, gss_cred_id_t *cred, const gss_OID desired_object, const gss_buffer_t value) nogil def set_cred_option(OID desired_aspect not None, Creds creds=None, value=None): """ set_cred_option(desired_aspect, creds=None, value=None) This method is used to set options of a :class:`Creds` object based on an OID key. The options that can be set depends on the mech the credentials were created with. An example of how this can be used would be to set the GSS_KRB5_CRED_NO_CI_FLAGS_X on a Kerberos credential. The OID string for this flag is '1.2.752.43.13.29' and it requires no value to be set. This must be set before the SecurityContext was initialised with the credentials. Args: desired_aspect (OID): the desired aspect of the Credential to set. cred_handle (Creds): the Credentials to set, or None to create a new credential. value (bytes): the value to set on the desired aspect of the Credential or None to send GSS_C_EMPTY_BUFFER. Returns: Creds: The output credential. Raises: GSSError """ cdef gss_buffer_desc value_buffer if value is not None: value_buffer = gss_buffer_desc(len(value), value) else: # GSS_C_EMPTY_BUFFER value_buffer = gss_buffer_desc(0, NULL) cdef Creds output_creds = creds if output_creds is None: output_creds = Creds() cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_set_cred_option(&min_stat, &output_creds.raw_creds, &desired_aspect.raw_oid, &value_buffer) if maj_stat == GSS_S_COMPLETE: return output_creds else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/chan_bindings.pxd0000664000372000037200000000061413523321513016625 0ustar from libc.stdlib cimport malloc, free from gssapi.raw.cython_types cimport * cdef class ChannelBindings: cdef public object initiator_address_type cdef public bytes initiator_address cdef public object acceptor_address_type cdef public bytes acceptor_address cdef public bytes application_data cdef gss_channel_bindings_t __cvalue__(ChannelBindings self) except NULL python-gssapi-1.6.1/gssapi/raw/chan_bindings.pyx0000664000372000037200000000470013523321513016652 0ustar from libc.stdlib cimport calloc, free from gssapi.raw.cython_types cimport * cdef class ChannelBindings: """GSSAPI Channel Bindings This class represents a set of GSSAPI channel bindings. """ # defined in pxd file # cdef public object initiator_address_type # cdef public bytes initiator_address # cdef public object acceptor_address_type # cdef public bytes acceptor_address # cdef public bytes application_data def __init__(ChannelBindings self, initiator_address_type=None, initiator_address=None, acceptor_address_type=None, acceptor_address=None, application_data=None): """ Args: initiator_address_type (AddressType): the initiator address type initiator_address (bytes): the initiator address acceptor_address_type (AddressType): the acceptor address type acceptor_address (bytes): the acceptor address application_data (bytes): additional application-specific data """ self.initiator_address_type = initiator_address_type self.initiator_address = initiator_address self.acceptor_address_type = acceptor_address_type self.acceptor_address = acceptor_address self.application_data = application_data cdef gss_channel_bindings_t __cvalue__(ChannelBindings self) except NULL: """Get the C struct version of the channel bindings""" cdef gss_channel_bindings_t res res = calloc(1, sizeof(res[0])) # NB(directxman12): an addrtype of 0 as set by calloc is equivalent # to GSS_C_AF_UNSPEC as per RFC 2744 if self.initiator_address_type is not None: res.initiator_addrtype = self.initiator_address_type if self.initiator_address is not None: res.initiator_address.value = self.initiator_address res.initiator_address.length = len(self.initiator_address) if self.acceptor_address_type is not None: res.acceptor_addrtype = self.acceptor_address_type if self.acceptor_address is not None: res.acceptor_address.value = self.acceptor_address res.acceptor_address.length = len(self.acceptor_address) if self.application_data is not None: res.application_data.value = self.application_data res.application_data.length = len(self.application_data) return res python-gssapi-1.6.1/gssapi/raw/creds.pyx0000664000372000037200000003531413523321513015171 0ustar GSSAPI="BASE" # This ensures that a full module is generated by Cython from gssapi.raw.cython_types cimport * from gssapi.raw.cython_converters cimport c_get_mech_oid_set from gssapi.raw.cython_converters cimport c_create_oid_set from gssapi.raw.cython_converters cimport c_py_ttl_to_c, c_c_ttl_to_py from gssapi.raw.names cimport Name from gssapi.raw.oids cimport OID from gssapi.raw.types import MechType, NameType from gssapi.raw.misc import GSSError from gssapi.raw.named_tuples import AcquireCredResult, AddCredResult from gssapi.raw.named_tuples import InquireCredResult, InquireCredByMechResult cdef extern from "python_gssapi.h": OM_uint32 gss_acquire_cred(OM_uint32 *min_stat, const gss_name_t name, OM_uint32 ttl, const gss_OID_set mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *creds, gss_OID_set *actual_mechs, OM_uint32 *actual_ttl) nogil OM_uint32 gss_release_cred(OM_uint32 *min_stat, gss_cred_id_t *creds) nogil OM_uint32 gss_add_cred(OM_uint32 *min_stat, const gss_cred_id_t base_creds, const gss_name_t name, const gss_OID mech, gss_cred_usage_t cred_usage, OM_uint32 initiator_ttl, OM_uint32 acceptor_ttl, gss_cred_id_t *output_creds, gss_OID_set *actual_mechs, OM_uint32 *actual_initiator_ttl, OM_uint32 *actual_acceptor_ttl) nogil # NB(directxman12): this is called frequently, so don't release the GIL OM_uint32 gss_inquire_cred(OM_uint32 *min_stat, const gss_cred_id_t creds, gss_name_t *name, OM_uint32 *ttl, gss_cred_usage_t *cred_usage, gss_OID_set *mechs) nogil OM_uint32 gss_inquire_cred_by_mech(OM_uint32 *min_stat, const gss_cred_id_t cred_handle, const gss_OID mech_type, gss_name_t *name, OM_uint32 *initiator_ttl, OM_uint32 *acceptor_ttl, gss_cred_usage_t *cred_usage) nogil cdef class Creds: """ GSSAPI Credentials """ # defined in pxd # cdef gss_cred_id_t raw_creds def __cinit__(self, Creds cpy=None): if cpy is not None: self.raw_creds = cpy.raw_creds cpy.raw_creds = GSS_C_NO_CREDENTIAL else: self.raw_creds = GSS_C_NO_CREDENTIAL def __dealloc__(self): # essentially just releaseCred(self), but it is unsafe to call # methods cdef OM_uint32 maj_stat, min_stat if self.raw_creds is not GSS_C_NO_CREDENTIAL: maj_stat = gss_release_cred(&min_stat, &self.raw_creds) if maj_stat != GSS_S_COMPLETE: raise GSSError(maj_stat, min_stat) self.raw_creds = NULL def acquire_cred(Name name=None, lifetime=None, mechs=None, usage='both'): """ acquire_cred(name=None, lifetime=None, mechs=None, usage='both') Get GSSAPI credentials for the given name and mechanisms. This method gets GSSAPI credentials corresponding to the given name and mechanims. The desired TTL and usage for the the credential may also be specified. Args: name (Name): the name for which to acquire the credentials (or None for the "no name" functionality) lifetime (int): the lifetime for the credentials (or None for indefinite) mechs ([MechType]): the desired mechanisms for which the credentials should work, or None for the default set usage (str): the usage type for the credentials: may be 'initiate', 'accept', or 'both' Returns: AcquireCredResult: the resulting credentials, the actual mechanisms with which they may be used, and their actual lifetime (or None for indefinite or not supported) Raises: BadMechanismError BadNameTypeError BadNameError ExpiredCredentialsError MissingCredentialsError """ cdef gss_OID_set desired_mechs if mechs is not None: desired_mechs = c_get_mech_oid_set(mechs) else: desired_mechs = GSS_C_NO_OID_SET cdef OM_uint32 input_ttl = c_py_ttl_to_c(lifetime) cdef gss_name_t c_name if name is None: c_name = GSS_C_NO_NAME else: c_name = name.raw_name cdef gss_cred_usage_t c_usage if usage == 'initiate': c_usage = GSS_C_INITIATE elif usage == 'accept': c_usage = GSS_C_ACCEPT else: c_usage = GSS_C_BOTH cdef gss_cred_id_t creds cdef gss_OID_set actual_mechs cdef OM_uint32 actual_ttl cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_acquire_cred(&min_stat, c_name, input_ttl, desired_mechs, c_usage, &creds, &actual_mechs, &actual_ttl) cdef OM_uint32 tmp_min_stat if mechs is not None: gss_release_oid_set(&tmp_min_stat, &desired_mechs) cdef Creds rc = Creds() if maj_stat == GSS_S_COMPLETE: rc.raw_creds = creds return AcquireCredResult(rc, c_create_oid_set(actual_mechs), c_c_ttl_to_py(actual_ttl)) else: raise GSSError(maj_stat, min_stat) def release_cred(Creds creds not None): """ release_cred(creds) Release GSSAPI Credentials. This method releases GSSAPI credentials. Warning: This method is deprecated. Credentials are automatically freed by Python. Args: creds (Creds): the credentials in question Raises: MissingCredentialsError """ cdef OM_uint32 maj_stat, min_stat maj_stat = gss_release_cred(&min_stat, &creds.raw_creds) if maj_stat != GSS_S_COMPLETE: raise GSSError(maj_stat, min_stat) creds.raw_creds = NULL def add_cred(Creds input_cred, Name name not None, OID mech not None, usage='initiate', init_lifetime=None, accept_lifetime=None, mutate_input=False): """ add_cred(input_cred, name, mech, usage='initiate', init_lifetime=None, \ accept_lifetime=None, mutate_input=False) Add a credential element to a credential. This method can be used to either compose two credentials (i.e., original and new credential), or to add a new element to an existing credential. Args: input_cred (Cred): the set of credentials to which to add the new credentials name (Name): name of principal to acquire a credential for mech (MechType): the desired security mechanism (required). usage (str): usage type for credentials. Possible values: 'initiate' (default), 'accept', 'both' (failsafe). init_lifetime (int): lifetime of credentials for use in initiating security contexts (None for indefinite) accept_lifetime (int): lifetime of credentials for use in accepting security contexts (None for indefinite) mutate_input (bool): whether to mutate the input credentials (True) or produce a new set of credentials (False). Defaults to False Returns: AddCredResult: the actual mechanisms with which the credentials may be used, the actual initiator TTL, and the actual acceptor TTL (None for either indefinite or not supported). Note that the credentials may be set to None if mutate_input is set to True. Raises: BadMechanismError BadNameTypeError BadNameError DuplicateCredentialsElementError ExpiredCredentialsError MissingCredentialsError """ cdef gss_cred_usage_t c_usage if usage == 'initiate': c_usage = GSS_C_INITIATE elif usage == 'accept': c_usage = GSS_C_ACCEPT else: # usage == 'both' c_usage = GSS_C_BOTH cdef gss_cred_id_t raw_input_cred if input_cred is not None: raw_input_cred = input_cred.raw_creds else: raw_input_cred = GSS_C_NO_CREDENTIAL cdef OM_uint32 input_initiator_ttl = c_py_ttl_to_c(init_lifetime) cdef OM_uint32 input_acceptor_ttl = c_py_ttl_to_c(accept_lifetime) cdef gss_cred_id_t output_creds cdef gss_cred_id_t *output_creds_ptr = NULL if not mutate_input: output_creds_ptr = &output_creds cdef gss_OID_set actual_mechs cdef OM_uint32 actual_initiator_ttl, actual_acceptor_ttl cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_add_cred(&min_stat, raw_input_cred, name.raw_name, &mech.raw_oid, c_usage, input_initiator_ttl, input_acceptor_ttl, output_creds_ptr, &actual_mechs, &actual_initiator_ttl, &actual_acceptor_ttl) cdef Creds rc if maj_stat == GSS_S_COMPLETE: if mutate_input: rc = None else: rc = Creds() rc.raw_creds = output_creds return AddCredResult(rc, c_create_oid_set(actual_mechs), c_c_ttl_to_py(actual_initiator_ttl), c_c_ttl_to_py(actual_acceptor_ttl)) else: raise GSSError(maj_stat, min_stat) def inquire_cred(Creds creds not None, name=True, lifetime=True, usage=True, mechs=True): """ inquire_cred(creds, name=True, lifetime=True, usage=True, mechs=True) Inspect credentials for information. This method inspects a :class:`Creds` object for information. Args: creds (Creds): the credentials to inspect name (bool): get the Name associated with the credentials lifetime (bool): get the TTL for the credentials usage (bool): get the usage type of the credentials mechs (bool): the mechanims used with the credentials Returns: InquireCredResult: the information about the credentials, with unused fields set to None Raises: MissingCredentialsError InvalidCredentialsError ExpiredCredentialsError """ # TODO(directxman12): add docs cdef gss_name_t res_name cdef gss_name_t *res_name_ptr = NULL if name: res_name_ptr = &res_name cdef OM_uint32 res_ttl cdef OM_uint32 *res_ttl_ptr = NULL if lifetime: res_ttl_ptr = &res_ttl cdef gss_cred_usage_t res_usage cdef gss_cred_usage_t *res_usage_ptr = NULL if usage: res_usage_ptr = &res_usage cdef gss_OID_set res_mechs cdef gss_OID_set *res_mechs_ptr = NULL if mechs: res_mechs_ptr = &res_mechs cdef OM_uint32 maj_stat, min_stat maj_stat = gss_inquire_cred(&min_stat, creds.raw_creds, res_name_ptr, res_ttl_ptr, res_usage_ptr, res_mechs_ptr) cdef Name rn if maj_stat == GSS_S_COMPLETE: if name: rn = Name() rn.raw_name = res_name else: rn = None py_usage = None if usage: if res_usage == GSS_C_INITIATE: py_usage = 'initiate' elif res_usage == GSS_C_ACCEPT: py_usage = 'accept' elif res_usage == GSS_C_BOTH: py_usage = 'both' py_ttl = None if lifetime: py_ttl = c_c_ttl_to_py(res_ttl) py_mechs = None if mechs: py_mechs = c_create_oid_set(res_mechs) return InquireCredResult(rn, py_ttl, py_usage, py_mechs) else: raise GSSError(maj_stat, min_stat) def inquire_cred_by_mech(Creds creds not None, OID mech not None, name=True, init_lifetime=True, accept_lifetime=True, usage=True): """ inquire_cred_by_mech(creds, mech, name=True, init_lifetime=True, \ accept_lifetime=True, usage=True) Inspect credentials for mechanism-specific information. This method inspects a :class:`Creds` object for information specific to a particular mechanism. It functions similarly to :func:`inquire_cred`. Args: creds (Creds): the credentials to inspect mech (OID): the desired mechanism name (bool): get the Name associated with the credentials init_lifetime (bool): get the initiator TTL for the credentials accept_lifetime (bool): get the acceptor TTL for the credentials usage (bool): get the usage type of the credentials Returns: InquireCredByMechResult: the information about the credentials, with unused fields set to None Raises: MissingCredentialsError InvalidCredentialsError """ # TODO(directxman12): add docs cdef gss_name_t res_name cdef gss_name_t *res_name_ptr = NULL if name: res_name_ptr = &res_name cdef OM_uint32 res_initiator_ttl cdef OM_uint32 *res_initiator_ttl_ptr = NULL if init_lifetime: res_initiator_ttl_ptr = &res_initiator_ttl cdef OM_uint32 res_acceptor_ttl cdef OM_uint32 *res_acceptor_ttl_ptr = NULL if accept_lifetime: res_acceptor_ttl_ptr = &res_acceptor_ttl cdef gss_cred_usage_t res_usage cdef gss_cred_usage_t *res_usage_ptr = NULL if usage: res_usage_ptr = &res_usage cdef OM_uint32 maj_stat, min_stat maj_stat = gss_inquire_cred_by_mech(&min_stat, creds.raw_creds, &mech.raw_oid, res_name_ptr, res_initiator_ttl_ptr, res_acceptor_ttl_ptr, res_usage_ptr) cdef Name rn if maj_stat == GSS_S_COMPLETE: if name: rn = Name() rn.raw_name = res_name else: rn = None py_initiator_ttl = None if init_lifetime: py_initiator_ttl = c_c_ttl_to_py(res_initiator_ttl) py_acceptor_ttl = None if accept_lifetime: py_acceptor_ttl = c_c_ttl_to_py(res_acceptor_ttl) py_usage = None if usage: if res_usage == GSS_C_INITIATE: py_usage = 'initiate' elif res_usage == GSS_C_ACCEPT: py_usage = 'accept' elif res_usage == GSS_C_BOTH: py_usage = 'both' return InquireCredByMechResult(rn, py_initiator_ttl, py_acceptor_ttl, py_usage) else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/raw/ext_rfc6680_comp_oid.pyx0000664000372000037200000000125113523321513017711 0ustar GSSAPI="BASE" # This ensures that a full module is generated by Cython from gssapi.raw.cython_types cimport gss_OID from gssapi.raw.cython_converters cimport c_make_oid from gssapi.raw import types as gsstypes # NB(directxman12): this is placed in separate file since the # GSS_C_NT_COMPOSITE_EXPORT constant didn't appear in MIT # krb5 until 1.11. However, due to the way that support was # written for composite tokens, simply using GSS_C_NT_EXPORT_NAME # will work in prior version which contain support for RFC 6680 cdef extern from "python_gssapi_ext.h": gss_OID GSS_C_NT_COMPOSITE_EXPORT gsstypes.NameType.composite_export = c_make_oid(GSS_C_NT_COMPOSITE_EXPORT) python-gssapi-1.6.1/gssapi/raw/ext_ggf.pyx0000664000372000037200000001476213523321513015520 0ustar """ GGF Extensions GGF provides extended credential and security context inquiry that allows application to retrieve more information about the client's credentials and security context. One common use case is to use :meth:`inquire_sec_context_by_oid` to retrieve the "session" key that is required by the SMB protocol for signing and encrypting a message. Draft IETF document for these extensions can be found at https://tools.ietf.org/html/draft-engert-ggf-gss-extensions-00 """ GSSAPI="BASE" # This ensures that a full module is generated by Cython from gssapi.raw.cython_types cimport * from gssapi.raw.ext_buffer_sets cimport * from gssapi.raw.misc import GSSError from gssapi.raw.oids cimport OID from gssapi.raw.creds cimport Creds from gssapi.raw.sec_contexts cimport SecurityContext cdef extern from "python_gssapi_ext.h": OM_uint32 gss_inquire_cred_by_oid(OM_uint32 *minor_status, const gss_cred_id_t cred_handle, const gss_OID desired_object, gss_buffer_set_t *data_set) nogil OM_uint32 gss_inquire_sec_context_by_oid(OM_uint32 *minor_status, const gss_ctx_id_t context_handle, const gss_OID desired_object, gss_buffer_set_t *data_set) nogil OM_uint32 gss_set_sec_context_option(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, const gss_OID desired_object, const gss_buffer_t value) nogil def inquire_cred_by_oid(Creds cred_handle not None, OID desired_aspect not None): """ inquire_cred_by_oid(cred_handle, desired_aspect) This method inspects a :class:`Creds` object for information specific to a particular desired aspect as an OID. Args: cred_handle (Creds): the Credentials to query desired_aspect (OID): the desired aspect of the Credentials to inquire about. Returns: list: A list of zero or more pieces of data (as bytes objects) Raises: GSSError """ cdef gss_buffer_set_t *data_set_ptr = NULL cdef gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET cdef OM_uint32 maj_stat, min_stat data_set_ptr = &data_set with nogil: maj_stat = gss_inquire_cred_by_oid(&min_stat, cred_handle.raw_creds, &desired_aspect.raw_oid, data_set_ptr) if maj_stat == GSS_S_COMPLETE: py_tokens = [] if data_set != GSS_C_NO_BUFFER_SET: for i in range(data_set.count): token = data_set.elements[i] py_tokens.append((token.value)[:token.length]) gss_release_buffer_set(&min_stat, &data_set) return py_tokens else: raise GSSError(maj_stat, min_stat) def inquire_sec_context_by_oid(SecurityContext context not None, OID desired_aspect not None): """ inquire_sec_context_by_oid(context, desired_aspect) This method inspects a :class:`SecurityContext` object for information specific to a particular desired aspect as an OID. This method can be used with the GSS_KRB5_INQ_SSPI_SESSION_KEY_OID OID to retrieve the required key that is used to derive the SMB/SAMBA signing and encryption keys. Args: context (SecurityContext): the Security Context to query desired_aspect (OID): the desired aspect of the Security Context to inquire about. Returns: list: A list of zero or more pieces of data (as bytes objects) Raises: GSSError """ cdef gss_buffer_set_t *data_set_ptr = NULL cdef gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET cdef OM_uint32 maj_stat, min_stat data_set_ptr = &data_set with nogil: maj_stat = gss_inquire_sec_context_by_oid(&min_stat, context.raw_ctx, &desired_aspect.raw_oid, data_set_ptr) if maj_stat == GSS_S_COMPLETE: py_tokens = [] if data_set != GSS_C_NO_BUFFER_SET: for i in range(data_set.count): token = data_set.elements[i] py_tokens.append((token.value)[:token.length]) gss_release_buffer_set(&min_stat, &data_set) return py_tokens else: raise GSSError(maj_stat, min_stat) def set_sec_context_option(OID desired_aspect not None, SecurityContext context=None, value=None): """ set_sec_context_option(desired_aspect, context=None, value=None) This method is used to set a value for a specific OID of a :class:`SecurityContext` object. The OID and value to pass in depends on the mech the SecurityContext backs. An example of how this can be used would be to reset the NTLM crypto engine used in gss-ntlmssp. The OID that controls this value is '1.3.6.1.4.1.7165.655.1.3' and it takes it a byte value that represents an int32 where 1 resets the verifier handle and any other int resets the sender handle. Args: desired_aspect (OID): the desired aspect of the Security Context to set the value for. context (SecurityContext): the Security Context to set, or None to create a new context. value (bytes): the value to set on the desired aspect of the Security Context or None to send GSS_C_EMPTY_BUFFER. Returns: SecurityContext: The output security context. Raises: GSSError """ cdef gss_buffer_desc value_buffer if value is not None: value_buffer = gss_buffer_desc(len(value), value) else: # GSS_C_EMPTY_BUFFER value_buffer = gss_buffer_desc(0, NULL) cdef SecurityContext output_context = context if output_context is None: output_context = SecurityContext() cdef OM_uint32 maj_stat, min_stat with nogil: maj_stat = gss_set_sec_context_option(&min_stat, &output_context.raw_ctx, &desired_aspect.raw_oid, &value_buffer) if maj_stat == GSS_S_COMPLETE: return output_context else: raise GSSError(maj_stat, min_stat) python-gssapi-1.6.1/gssapi/sec_contexts.py0000664000372000037200000004523213523321513015611 0ustar import six from gssapi.raw import sec_contexts as rsec_contexts from gssapi.raw import message as rmessage from gssapi.raw import named_tuples as tuples from gssapi.raw.types import RequirementFlag, IntEnumFlagSet import gssapi.exceptions as excs from gssapi import _utils from gssapi.names import Name from gssapi.creds import Credentials @six.add_metaclass(_utils.CheckLastError) class SecurityContext(rsec_contexts.SecurityContext): """A GSSAPI Security Context This class represents a GSSAPI security context that may be used with and/or returned by other GSSAPI methods. It inherits from the low-level GSSAPI :class:`~gssapi.raw.sec_contexts.SecurityContext` class, and thus may used with both low-level and high-level API methods. This class may be pickled and unpickled (the attached delegated credentials object will not be preserved, however). """ def __new__(cls, base=None, token=None, name=None, creds=None, lifetime=None, flags=None, mech=None, channel_bindings=None, usage=None): if token is not None: base = rsec_contexts.import_sec_context(token) return super(SecurityContext, cls).__new__(cls, base) def __init__(self, base=None, token=None, name=None, creds=None, lifetime=None, flags=None, mech=None, channel_bindings=None, usage=None): """ The constructor creates a new security context, but does not begin the initiate or accept process. If the `base` argument is used, an existing :class:`~gssapi.raw.sec_contexts.SecurityContext` object from the low-level API is converted into a high-level object. If the `token` argument is passed, the security context is imported using the token. Otherwise, a new security context is created. If the `usage` argument is not passed, the constructor will attempt to detect what the appropriate usage is based on either the existing security context (if `base` or `token` are used) or the argument set. For a security context of the `initiate` usage, the `name` argument must be used, and the `creds`, `mech`, `flags`, `lifetime`, and `channel_bindings` arguments may be used as well. For a security context of the `accept` usage, the `creds` and `channel_bindings` arguments may optionally be used. """ # NB(directxman12): _last_err must be set first self._last_err = None # determine the usage ('initiate' vs 'accept') if base is None and token is None: # this will be a new context if usage is not None: if usage not in ('initiate', 'accept'): msg = "Usage must be either 'initiate' or 'accept'" raise excs.UnknownUsageError(msg, obj="security context") self.usage = usage elif creds is not None and creds.usage != 'both': self.usage = creds.usage elif name is not None: # if we pass a name, assume the usage is 'initiate' self.usage = 'initiate' else: # if we don't pass a name, assume the usage is 'accept' self.usage = 'accept' # check for appropriate arguments if self.usage == 'initiate': # takes: creds?, target_name, mech?, flags?, # channel_bindings? if name is None: raise TypeError("You must pass the 'name' argument when " "creating an initiating security context") self._target_name = name self._mech = mech self._desired_flags = IntEnumFlagSet(RequirementFlag, flags) self._desired_lifetime = lifetime else: # takes creds? if (name is not None or flags is not None or mech is not None or lifetime is not None): raise TypeError("You must pass at most the 'creds' " "argument when creating an accepting " "security context") self._channel_bindings = channel_bindings self._creds = creds self._delegated_creds = None else: # we already have a context in progress, just inspect it # NB(directxman12): MIT krb5 refuses to inquire about a context # if it's partially established, so we have to check here try: if self.locally_initiated: self.usage = 'initiate' else: self.usage = 'accept' except excs.MissingContextError: msg = ("Cannot extract usage from a partially completed " "context") raise excs.UnknownUsageError(msg, obj="security context") # This is to work around an MIT krb5 bug (see the `complete` property) self._complete = None # NB(directxman12): DO NOT ADD AN __del__ TO THIS CLASS -- it screws up # the garbage collector if _last_tb is still defined # TODO(directxman12): implement flag properties def get_signature(self, message): """Calculate the signature for a message. This method calculates the signature (called a MIC) for the given message, which may be then used with :meth:`verify_signature` to confirm the validity of the signature. This is useful if you wish to transmit the message signature and message in your own format. Args: message (bytes): the input message Returns: bytes: the message signature Raises: ExpiredContextError MissingContextError BadQoPError """ # TODO(directxman12): check flags? return rmessage.get_mic(self, message) def verify_signature(self, message, mic): """Verify the signature for a message. This method verifies that a signature (generated by :meth:`get_signature` is valid for the given message. If the signature is valid, the method will return. Otherwise, it will raise an error. Args: message (bytes): the message mic (bytes): the signature to verify Raises: BadMICError: the signature was not valid InvalidTokenError DuplicateTokenError ExpiredTokenError TokenTooLateError TokenTooEarlyError ExpiredContextError MissingContextError """ return rmessage.verify_mic(self, message, mic) def wrap(self, message, encrypt): """Wrap a message, optionally with encryption This wraps a message, signing it and optionally encrypting it. Args: message (bytes): the message to wrap encrypt (bool): whether or not to encrypt the message Returns: WrapResult: the wrapped message and details about it (e.g. whether encryption was used succesfully) Raises: ExpiredContextError MissingContextError BadQoPError """ return rmessage.wrap(self, message, encrypt) def unwrap(self, message): """Unwrap a wrapped message. This method unwraps/unencrypts a wrapped message, verifying the signature along the way. Args: message (bytes): the message to unwrap/decrypt Returns: UnwrapResult: the unwrapped message and details about it (e.g. wheter encryption was used) Raises: InvalidTokenError BadMICError DuplicateTokenError ExpiredTokenError TokenTooLateError TokenTooEarlyError ExpiredContextError MissingContextError """ return rmessage.unwrap(self, message) def encrypt(self, message): """Encrypt a message. This method wraps and encrypts a message, similarly to :meth:`wrap`. The difference is that encryption is always used, and the method will raise an exception if this is not possible. Additionally, this method simply returns the encrypted message directly. Args: message (bytes): the message to encrypt Returns: bytes: the encrypted message Raises: EncryptionNotUsed: the encryption could not be used ExpiredContextError MissingContextError BadQoPError """ res = self.wrap(message, encrypt=True) if not res.encrypted: raise excs.EncryptionNotUsed("Wrapped message was not encrypted") return res.message def decrypt(self, message): """Decrypt a message. This method decrypts and unwraps a message, verifying the signature along the way, similarly to :meth:`unwrap`. The difference is that this method will raise an exception if encryption was established by the context and not used, and simply returns the decrypted message directly. Args: message (bytes): the encrypted message Returns: bytes: the decrypted message Raises: EncryptionNotUsed: encryption was expected, but not used InvalidTokenError BadMICError DuplicateTokenError ExpiredTokenError TokenTooLateError TokenTooEarlyError ExpiredContextError MissingContextError """ res = self.unwrap(message) if (not res.encrypted and self.actual_flags & RequirementFlag.confidentiality): raise excs.EncryptionNotUsed("The context was established with " "encryption, but unwrapped message " "was not encrypted", unwrapped_message=res.message) return res.message def get_wrap_size_limit(self, desired_output_size, encrypted=True): """Calculate the maximum message size for a given wrapped message size. This method calculates the maximum input message size for a given maximum wrapped/encrypted message size. Args: desired_output_size (int): the maximum output message size encrypted (bool): whether or not encryption should be taken into account Returns: int: the maximum input message size Raises: MissingContextError ExpiredContextError BadQoPError """ return rmessage.wrap_size_limit(self, desired_output_size, encrypted) def process_token(self, token): """Process an output token asynchronously. This method processes an output token even when the security context was not expecting it. Warning: This method is deprecated. Args: token (bytes): the token to process Raises: InvalidTokenError MissingContextError """ rsec_contexts.process_context_token(self, token) def export(self): """Export a security context. This method exports a security context, allowing it to be passed between processes. Returns: bytes: the exported security context Raises: ExpiredContextError MissingContextError OperationUnavailableError """ return rsec_contexts.export_sec_context(self) _INQUIRE_ARGS = ('initiator_name', 'target_name', 'lifetime', 'mech', 'flags', 'locally_init', 'complete') @_utils.check_last_err def _inquire(self, **kwargs): """Inspect the security context for information This method inspects the security context for information. If no keyword arguments are passed, all available information is returned. Otherwise, only the keyword arguments that are passed and set to `True` are returned. Args: initiator_name (bool): get the initiator name for this context target_name (bool): get the target name for this context lifetime (bool): get the remaining lifetime for this context mech (bool): get the :class:`MechType` used by this context flags (bool): get the flags set on this context locally_init (bool): get whether this context was locally initiated complete (bool): get whether negotiation on this context has been completed Returns: InquireContextResult: the results of the inquiry, with unused fields set to None Raises: MissingContextError """ if not kwargs: default_val = True else: default_val = False for arg in self._INQUIRE_ARGS: kwargs[arg] = kwargs.get(arg, default_val) res = rsec_contexts.inquire_context(self, **kwargs) if (kwargs.get('initiator_name', False) and res.initiator_name is not None): init_name = Name(res.initiator_name) else: init_name = None if (kwargs.get('target_name', False) and res.target_name is not None): target_name = Name(res.target_name) else: target_name = None return tuples.InquireContextResult(init_name, target_name, res.lifetime, res.mech, res.flags, res.locally_init, res.complete) @property def lifetime(self): """The amount of time for which this context remains valid""" return rsec_contexts.context_time(self) @property def delegated_creds(self): """The credentials delegated from the initiator to the acceptor .. warning:: This value will not be preserved across picklings. These should be separately exported and transfered. """ return self._delegated_creds initiator_name = _utils.inquire_property( 'initiator_name', 'The :class:`Name` of the initiator of this context') target_name = _utils.inquire_property( 'target_name', 'The :class:`Name` of the target of this context') mech = _utils.inquire_property( 'mech', 'The mechanism (:class:`MechType`) in use by this context') actual_flags = _utils.inquire_property( 'flags', 'The flags set on this context') locally_initiated = _utils.inquire_property( 'locally_init', 'Whether this context was locally intiated') @property @_utils.check_last_err def complete(self): """Whether negotiation for this context has been completed""" # NB(directxman12): MIT krb5 has a bug where it refuses to # inquire about partially completed contexts, # so we can't just use `self._inquire` generally if self._started: if self._complete is None: try: res = self._inquire(complete=True).complete except excs.MissingContextError: return False else: self._complete = res return self._complete else: return False @_utils.catch_and_return_token def step(self, token=None): """Perform a negotation step. This method performs a negotiation step based on the usage type of this context. If `__DEFER_STEP_ERRORS__` is set to True on the class, this method will return a token, even when exceptions would be thrown. The generated exception will be thrown on the next method call or property lookup on the context. **This is the default behavior.** This method should be used in a while loop, as such: .. code-block:: python input_token = None try: while not ctx.complete: output_token = ctx.step(input_token) input_token = send_and_receive(output_token) except GSSError as e: handle_the_issue() .. tip:: Disabling `__DEFER_STEP_ERRORS__` is rarely necessary. When this method is used in a loop (as above), `__DEFER_STEP_ERRORS__` will ensure that you always send an error token when it's available, keeping the other end of the security context updated with the status of the negotiation. Args: token (bytes): the input token from the other participant's step Returns: bytes: the output token to send to the other participant Raises: InvalidTokenError InvalidCredentialsError MissingCredentialsError ExpiredCredentialsError BadChannelBindingsError BadMICError ExpiredTokenError: (initiate only) DuplicateTokenError MissingContextError BadNameTypeError: (initiate only) BadNameError: (initiate only) BadMechanismError """ if self.usage == 'accept': return self._acceptor_step(token=token) else: return self._initiator_step(token=token) def _acceptor_step(self, token): res = rsec_contexts.accept_sec_context(token, self._creds, self, self._channel_bindings) if res.delegated_creds is not None: self._delegated_creds = Credentials(res.delegated_creds) else: self._delegated_creds = None self._complete = not res.more_steps return res.token def _initiator_step(self, token=None): res = rsec_contexts.init_sec_context(self._target_name, self._creds, self, self._mech, self._desired_flags, self._desired_lifetime, self._channel_bindings, token) self._complete = not res.more_steps return res.token # pickle protocol support def __reduce__(self): # the unpickle arguments to new are (base=None, token=self.export()) return (type(self), (None, self.export())) python-gssapi-1.6.1/gssapi/exceptions.py0000664000372000037200000000221013523321513015256 0ustar from gssapi.raw.exceptions import * # noqa from gssapi.raw.misc import GSSError # noqa """High-Level API Errors This module includes several high-level exceptions, in addition to GSSError and exceptions from :mod:`gssapi.raw.exceptions`. """ # non-GSS exceptions class GeneralError(Exception): """A General High-Level API Error""" MAJOR_MESSAGE = "General error" FMT_STR = "{maj}: {min}." def __init__(self, minor_message, **kwargs): maj_str = self.MAJOR_MESSAGE.format(**kwargs) err_str = self.FMT_STR.format(maj=maj_str, min=minor_message) super(GeneralError, self).__init__(err_str) class UnknownUsageError(GeneralError): """An Error indicating an unknown usage type""" MAJOR_MESSAGE = "Unable to determine {obj} usage" class EncryptionNotUsed(GeneralError): """An Error indicating that encryption was requested, but not used""" MAJOR_MESSAGE = "Confidentiality was requested, but not used" def __init__(self, minor_message, unwrapped_message=None, **kwargs): super(EncryptionNotUsed, self).__init__(minor_message, **kwargs) self.unwrapped_message = unwrapped_message python-gssapi-1.6.1/gssapi/__init__.py0000664000372000037200000000210013523321513014632 0ustar """High-Level GSSAPI Bindings The high-level API contains three main classes, which represent the primary abstractions that GSSAPI provides: Name (see gssapi.names) Credentials (see gssapi.creds) SecurityContext (see gssapi.sec_contexts) Additionally, a number of helper classes shared with the low-level API exist as well: Enums (see gssapi.raw.types) -- NameType, RequirementFlag, AddressType, MechType IntEnumFlagSet (see gssapi.raw.types) OID (see gssapi.raw.oids) Note: Classes in the high-level API inherit from the corresponding classes in the low-level API, and thus may be passed in to low-level API functions. """ from gssapi.raw.types import NameType, RequirementFlag, AddressType # noqa from gssapi.raw.types import MechType, IntEnumFlagSet # noqa from gssapi.raw.oids import OID # noqa from gssapi.creds import Credentials # noqa from gssapi.names import Name # noqa from gssapi.sec_contexts import SecurityContext # noqa from gssapi.mechs import Mechanism # noqa from gssapi._utils import set_encoding # noqa python-gssapi-1.6.1/gssapi/tests/0000775000372000037200000000000013523321764013702 5ustar python-gssapi-1.6.1/gssapi/tests/test_raw.py0000664000372000037200000017274113523321513016110 0ustar import copy import os import socket import unittest import six import should_be.all # noqa import gssapi.raw as gb import gssapi.raw.misc as gbmisc import k5test.unit as ktu import k5test as kt if six.PY2: from collections import Set else: from collections.abc import Set TARGET_SERVICE_NAME = b'host' FQDN = socket.getfqdn().encode('utf-8') SERVICE_PRINCIPAL = TARGET_SERVICE_NAME + b'/' + FQDN class _GSSAPIKerberosTestCase(kt.KerberosTestCase): @classmethod def setUpClass(cls): super(_GSSAPIKerberosTestCase, cls).setUpClass() svc_princ = SERVICE_PRINCIPAL.decode("UTF-8") cls.realm.kinit(svc_princ, flags=['-k']) cls._init_env() cls.USER_PRINC = cls.realm.user_princ.split('@')[0].encode("UTF-8") cls.ADMIN_PRINC = cls.realm.admin_princ.split('@')[0].encode("UTF-8") @classmethod def _init_env(cls): cls._saved_env = copy.deepcopy(os.environ) for k, v in cls.realm.env.items(): os.environ[k] = v @classmethod def _restore_env(cls): for k in copy.deepcopy(os.environ): if k in cls._saved_env: os.environ[k] = cls._saved_env[k] else: del os.environ[k] cls._saved_env = None @classmethod def tearDownClass(cls): super(_GSSAPIKerberosTestCase, cls).tearDownClass() cls._restore_env() class TestBaseUtilities(_GSSAPIKerberosTestCase): def setUp(self): self.realm.kinit(SERVICE_PRINCIPAL.decode("UTF-8"), flags=['-k']) def test_indicate_mechs(self): mechs = gb.indicate_mechs() mechs.shouldnt_be_none() mechs.should_be_a(set) mechs.shouldnt_be_empty() mechs.should_include(gb.MechType.kerberos) def test_import_name(self): imported_name = gb.import_name(TARGET_SERVICE_NAME) imported_name.shouldnt_be_none() imported_name.should_be_a(gb.Name) gb.release_name(imported_name) def test_canonicalize_export_name(self): imported_name = gb.import_name(self.ADMIN_PRINC, gb.NameType.kerberos_principal) canonicalized_name = gb.canonicalize_name(imported_name, gb.MechType.kerberos) canonicalized_name.shouldnt_be_none() canonicalized_name.should_be_a(gb.Name) exported_name = gb.export_name(canonicalized_name) exported_name.shouldnt_be_none() exported_name.should_be_a(bytes) exported_name.shouldnt_be_empty() def test_duplicate_name(self): orig_name = gb.import_name(TARGET_SERVICE_NAME) new_name = gb.duplicate_name(orig_name) new_name.shouldnt_be_none() gb.compare_name(orig_name, new_name).should_be_true() def test_display_name(self): imported_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) displ_resp = gb.display_name(imported_name) displ_resp.shouldnt_be_none() (displayed_name, out_type) = displ_resp displayed_name.shouldnt_be_none() displayed_name.should_be_a(bytes) displayed_name.should_be(TARGET_SERVICE_NAME) out_type.shouldnt_be_none() out_type.should_be(gb.NameType.hostbased_service) # NB(directxman12): we don't test display_name_ext because the krb5 mech # doesn't actually implement it @ktu.gssapi_extension_test('rfc6680', 'RFC 6680') def test_inquire_name_not_mech_name(self): base_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) inquire_res = gb.inquire_name(base_name) inquire_res.shouldnt_be_none() inquire_res.is_mech_name.should_be_false() inquire_res.mech.should_be_none() @ktu.gssapi_extension_test('rfc6680', 'RFC 6680') def test_inquire_name_mech_name(self): base_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) mech_name = gb.canonicalize_name(base_name, gb.MechType.kerberos) inquire_res = gb.inquire_name(mech_name) inquire_res.shouldnt_be_none() inquire_res.is_mech_name.should_be_true() inquire_res.mech.should_be_a(gb.OID) inquire_res.mech.should_be(gb.MechType.kerberos) @ktu.gssapi_extension_test('rfc6680', 'RFC 6680') @ktu.gssapi_extension_test('rfc6680_comp_oid', 'RFC 6680 (COMPOSITE_EXPORT OID)') def test_import_export_name_composite_no_attrs(self): base_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) canon_name = gb.canonicalize_name(base_name, gb.MechType.kerberos) exported_name = gb.export_name_composite(canon_name) exported_name.should_be_a(bytes) imported_name = gb.import_name(exported_name, gb.NameType.composite_export) imported_name.should_be_a(gb.Name) # NB(directxman12): the greet_client plugin only allows for one value @ktu.gssapi_extension_test('rfc6680', 'RFC 6680') @ktu.krb_plugin_test('authdata', 'greet_client') def test_inquire_name_with_attrs(self): base_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) canon_name = gb.canonicalize_name(base_name, gb.MechType.kerberos) gb.set_name_attribute(canon_name, b'urn:greet:greeting', [b'some greeting']) inquire_res = gb.inquire_name(canon_name) inquire_res.shouldnt_be_none() inquire_res.attrs.should_be_a(list) inquire_res.attrs.should_be([b'urn:greet:greeting']) @ktu.gssapi_extension_test('rfc6680', 'RFC 6680') @ktu.krb_plugin_test('authdata', 'greet_client') def test_basic_get_set_delete_name_attributes_no_auth(self): base_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) canon_name = gb.canonicalize_name(base_name, gb.MechType.kerberos) gb.set_name_attribute(canon_name, b'urn:greet:greeting', [b'some other val'], complete=True) get_res = gb.get_name_attribute(canon_name, b'urn:greet:greeting') get_res.shouldnt_be_none() get_res.values.should_be_a(list) get_res.values.should_be([b'some other val']) get_res.display_values.should_be_a(list) get_res.display_values.should_be(get_res.values) get_res.complete.should_be_true() get_res.authenticated.should_be_false() gb.delete_name_attribute(canon_name, b'urn:greet:greeting') # NB(directxman12): the code below currently segfaults due to the way # that krb5 and the krb5 greet plugin is written # gb.get_name_attribute.should_raise( # gb.exceptions.OperationUnavailableError, canon_name, # 'urn:greet:greeting') @ktu.gssapi_extension_test('rfc6680', 'RFC 6680') @ktu.krb_plugin_test('authdata', 'greet_client') def test_import_export_name_composite(self): base_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) canon_name = gb.canonicalize_name(base_name, gb.MechType.kerberos) gb.set_name_attribute(canon_name, b'urn:greet:greeting', [b'some val']) exported_name = gb.export_name_composite(canon_name) exported_name.should_be_a(bytes) # TODO(directxman12): when you just import a token as composite, # appears as this name whose text is all garbled, since it contains # all of the attributes, etc, but doesn't properly have the attributes. # Once it's canonicalized, the attributes reappear. However, if you # just import it as normal export, the attributes appear directly. # It is thus unclear as to what is going on # imported_name_raw = gb.import_name(exported_name, # gb.NameType.composite_export) # imported_name = gb.canonicalize_name(imported_name_r, # gb.MechType.kerberos) imported_name = gb.import_name(exported_name, gb.NameType.export) imported_name.should_be_a(gb.Name) get_res = gb.get_name_attribute(imported_name, b'urn:greet:greeting') get_res.values.should_be([b'some val']) def test_compare_name(self): service_name1 = gb.import_name(TARGET_SERVICE_NAME) service_name2 = gb.import_name(TARGET_SERVICE_NAME) init_name = gb.import_name(self.ADMIN_PRINC, gb.NameType.kerberos_principal) gb.compare_name(service_name1, service_name2).should_be_true() gb.compare_name(service_name2, service_name1).should_be_true() gb.compare_name(service_name1, init_name).should_be_false() gb.release_name(service_name1) gb.release_name(service_name2) gb.release_name(init_name) def test_display_status(self): status_resp = gbmisc._display_status(0, False) status_resp.shouldnt_be_none() (status, ctx, cont) = status_resp status.should_be_a(bytes) status.shouldnt_be_empty() ctx.should_be_an_integer() cont.should_be_a(bool) cont.should_be_false() def test_acquire_creds(self): name = gb.import_name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) cred_resp = gb.acquire_cred(name) cred_resp.shouldnt_be_none() (creds, actual_mechs, ttl) = cred_resp creds.shouldnt_be_none() creds.should_be_a(gb.Creds) actual_mechs.shouldnt_be_empty() actual_mechs.should_include(gb.MechType.kerberos) ttl.should_be_an_integer() gb.release_name(name) gb.release_cred(creds) @ktu.gssapi_extension_test('cred_imp_exp', 'credentials import-export') def test_cred_import_export(self): creds = gb.acquire_cred(None).creds token = gb.export_cred(creds) imported_creds = gb.import_cred(token) inquire_orig = gb.inquire_cred(creds, name=True) inquire_imp = gb.inquire_cred(imported_creds, name=True) gb.compare_name(inquire_orig.name, inquire_imp.name).should_be_true() def test_context_time(self): target_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) ctx_resp = gb.init_sec_context(target_name) client_token1 = ctx_resp[3] client_ctx = ctx_resp[0] server_name = gb.import_name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) server_creds = gb.acquire_cred(server_name)[0] server_resp = gb.accept_sec_context(client_token1, acceptor_creds=server_creds) server_tok = server_resp[3] client_resp2 = gb.init_sec_context(target_name, context=client_ctx, input_token=server_tok) ctx = client_resp2[0] ttl = gb.context_time(ctx) ttl.should_be_an_integer() ttl.should_be_greater_than(0) def test_inquire_context(self): target_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) ctx_resp = gb.init_sec_context(target_name) client_token1 = ctx_resp[3] client_ctx = ctx_resp[0] server_name = gb.import_name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) server_creds = gb.acquire_cred(server_name)[0] server_resp = gb.accept_sec_context(client_token1, acceptor_creds=server_creds) server_tok = server_resp[3] client_resp2 = gb.init_sec_context(target_name, context=client_ctx, input_token=server_tok) ctx = client_resp2[0] inq_resp = gb.inquire_context(ctx) inq_resp.shouldnt_be_none() (src_name, target_name, ttl, mech_type, flags, local_est, is_open) = inq_resp src_name.shouldnt_be_none() src_name.should_be_a(gb.Name) target_name.shouldnt_be_none() target_name.should_be_a(gb.Name) ttl.should_be_an_integer() mech_type.shouldnt_be_none() mech_type.should_be(gb.MechType.kerberos) flags.shouldnt_be_none() flags.should_be_a(Set) flags.shouldnt_be_empty() local_est.should_be_a(bool) local_est.should_be_true() is_open.should_be_a(bool) is_open.should_be_true() # NB(directxman12): We don't test `process_context_token` because # there is no clear non-deprecated way to test it @ktu.gssapi_extension_test('s4u', 'S4U') def test_add_cred_impersonate_name(self): target_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) client_ctx_resp = gb.init_sec_context(target_name) client_token = client_ctx_resp[3] del client_ctx_resp # free all the things (except the token)! server_name = gb.import_name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) server_creds = gb.acquire_cred(server_name, usage='both')[0] server_ctx_resp = gb.accept_sec_context(client_token, acceptor_creds=server_creds) input_creds = gb.Creds() imp_resp = gb.add_cred_impersonate_name(input_creds, server_creds, server_ctx_resp[1], gb.MechType.kerberos) imp_resp.shouldnt_be_none() new_creds, actual_mechs, output_init_ttl, output_accept_ttl = imp_resp actual_mechs.shouldnt_be_empty() actual_mechs.should_include(gb.MechType.kerberos) output_init_ttl.should_be_a(int) output_accept_ttl.should_be_a(int) new_creds.should_be_a(gb.Creds) @ktu.gssapi_extension_test('s4u', 'S4U') def test_acquire_creds_impersonate_name(self): target_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) client_ctx_resp = gb.init_sec_context(target_name) client_token = client_ctx_resp[3] del client_ctx_resp # free all the things (except the token)! server_name = gb.import_name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) server_creds = gb.acquire_cred(server_name, usage='both')[0] server_ctx_resp = gb.accept_sec_context(client_token, acceptor_creds=server_creds) imp_resp = gb.acquire_cred_impersonate_name(server_creds, server_ctx_resp[1]) imp_resp.shouldnt_be_none() imp_creds, actual_mechs, output_ttl = imp_resp imp_creds.shouldnt_be_none() imp_creds.should_be_a(gb.Creds) actual_mechs.shouldnt_be_empty() actual_mechs.should_include(gb.MechType.kerberos) output_ttl.should_be_a(int) # no need to explicitly release any more -- we can just rely on # __dealloc__ (b/c cython) @ktu.gssapi_extension_test('s4u', 'S4U') @ktu.krb_minversion_test('1.11', 'returning delegated S4U2Proxy credentials') def test_always_get_delegated_creds(self): svc_princ = SERVICE_PRINCIPAL.decode("UTF-8") self.realm.kinit(svc_princ, flags=['-k', '-f']) target_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) client_token = gb.init_sec_context(target_name).token # if our acceptor creds have a usage of both, we get # s4u2proxy delegated credentials server_creds = gb.acquire_cred(None, usage='both').creds server_ctx_resp = gb.accept_sec_context(client_token, acceptor_creds=server_creds) server_ctx_resp.shouldnt_be_none() server_ctx_resp.delegated_creds.shouldnt_be_none() server_ctx_resp.delegated_creds.should_be_a(gb.Creds) @ktu.gssapi_extension_test('rfc5588', 'RFC 5588') def test_store_cred_acquire_cred(self): # we need to acquire a forwardable ticket svc_princ = SERVICE_PRINCIPAL.decode("UTF-8") self.realm.kinit(svc_princ, flags=['-k', '-f']) target_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) client_creds = gb.acquire_cred(None, usage='initiate').creds client_ctx_resp = gb.init_sec_context( target_name, creds=client_creds, flags=gb.RequirementFlag.delegate_to_peer) client_token = client_ctx_resp[3] server_creds = gb.acquire_cred(None, usage='accept').creds server_ctx_resp = gb.accept_sec_context(client_token, acceptor_creds=server_creds) deleg_creds = server_ctx_resp.delegated_creds deleg_creds.shouldnt_be_none() store_res = gb.store_cred(deleg_creds, usage='initiate', set_default=True, overwrite=True) store_res.shouldnt_be_none() store_res.usage.should_be('initiate') store_res.mechs.should_include(gb.MechType.kerberos) deleg_name = gb.inquire_cred(deleg_creds).name acq_resp = gb.acquire_cred(deleg_name, usage='initiate') acq_resp.shouldnt_be_none() @ktu.gssapi_extension_test('cred_store', 'credentials store') def test_store_cred_into_acquire_cred(self): CCACHE = 'FILE:{tmpdir}/other_ccache'.format(tmpdir=self.realm.tmpdir) KT = '{tmpdir}/other_keytab'.format(tmpdir=self.realm.tmpdir) store = {b'ccache': CCACHE.encode('UTF-8'), b'keytab': KT.encode('UTF-8')} princ_name = 'service/cs@' + self.realm.realm self.realm.addprinc(princ_name) self.realm.extract_keytab(princ_name, KT) self.realm.kinit(princ_name, None, ['-k', '-t', KT]) initial_creds = gb.acquire_cred(None, usage='initiate').creds # NB(sross): overwrite because the ccache doesn't exist yet store_res = gb.store_cred_into(store, initial_creds, overwrite=True) store_res.mechs.shouldnt_be_none() store_res.usage.should_be('initiate') name = gb.import_name(princ_name.encode('UTF-8')) retrieve_res = gb.acquire_cred_from(store, name) retrieve_res.shouldnt_be_none() retrieve_res.creds.shouldnt_be_none() retrieve_res.creds.should_be_a(gb.Creds) retrieve_res.mechs.shouldnt_be_empty() retrieve_res.mechs.should_include(gb.MechType.kerberos) retrieve_res.lifetime.should_be_an_integer() def test_add_cred(self): target_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) client_ctx_resp = gb.init_sec_context(target_name) client_token = client_ctx_resp[3] del client_ctx_resp # free all the things (except the token)! server_name = gb.import_name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) server_creds = gb.acquire_cred(server_name, usage='both')[0] server_ctx_resp = gb.accept_sec_context(client_token, acceptor_creds=server_creds) input_creds = gb.Creds() imp_resp = gb.add_cred(input_creds, server_ctx_resp[1], gb.MechType.kerberos) imp_resp.shouldnt_be_none() new_creds, actual_mechs, output_init_ttl, output_accept_ttl = imp_resp actual_mechs.shouldnt_be_empty() actual_mechs.should_include(gb.MechType.kerberos) output_init_ttl.should_be_a(int) output_accept_ttl.should_be_a(int) new_creds.should_be_a(gb.Creds) # NB(sross): we skip testing add_cred with mutate for the same reasons # that testing add_cred in the high-level API is skipped def test_inquire_creds(self): name = gb.import_name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) cred = gb.acquire_cred(name).creds inq_resp = gb.inquire_cred(cred) inq_resp.shouldnt_be_none() inq_resp.name.should_be_a(gb.Name) assert gb.compare_name(name, inq_resp.name) inq_resp.lifetime.should_be_an_integer() inq_resp.usage.should_be('both') inq_resp.mechs.shouldnt_be_empty() inq_resp.mechs.should_include(gb.MechType.kerberos) def test_create_oid_from_bytes(self): kerberos_bytes = gb.MechType.kerberos.__bytes__() new_oid = gb.OID(elements=kerberos_bytes) new_oid.should_be(gb.MechType.kerberos) del new_oid # make sure we can dealloc def test_error_dispatch(self): err_code1 = gb.ParameterReadError.CALLING_CODE err_code2 = gb.BadNameError.ROUTINE_CODE err = gb.GSSError(err_code1 | err_code2, 0) err.should_be_a(gb.NameReadError) err.maj_code.should_be(err_code1 | err_code2) def test_inquire_names_for_mech(self): res = gb.inquire_names_for_mech(gb.MechType.kerberos) res.shouldnt_be_none() res.should_include(gb.NameType.kerberos_principal) def test_inquire_mechs_for_name(self): name = gb.import_name(self.USER_PRINC, gb.NameType.kerberos_principal) res = gb.inquire_mechs_for_name(name) res.shouldnt_be_none() res.should_include(gb.MechType.kerberos) @ktu.gssapi_extension_test('password', 'Password') def test_acquire_cred_with_password(self): password = self.realm.password('user') self.realm.kinit(self.realm.user_princ, password=password) name = gb.import_name(b'user', gb.NameType.kerberos_principal) imp_resp = gb.acquire_cred_with_password(name, password.encode('UTF-8')) imp_resp.shouldnt_be_none() imp_creds, actual_mechs, output_ttl = imp_resp imp_creds.shouldnt_be_none() imp_creds.should_be_a(gb.Creds) actual_mechs.shouldnt_be_empty() actual_mechs.should_include(gb.MechType.kerberos) output_ttl.should_be_a(int) @ktu.gssapi_extension_test('password_add', 'Password (add)') def test_add_cred_with_password(self): password = self.realm.password('user') self.realm.kinit(self.realm.user_princ, password=password) name = gb.import_name(b'user', gb.NameType.kerberos_principal) input_creds = gb.Creds() imp_resp = gb.add_cred_with_password(input_creds, name, gb.MechType.kerberos, password.encode('UTF-8')) imp_resp.shouldnt_be_none() new_creds, actual_mechs, output_init_ttl, output_accept_ttl = imp_resp actual_mechs.shouldnt_be_empty() actual_mechs.should_include(gb.MechType.kerberos) output_init_ttl.should_be_a(int) output_accept_ttl.should_be_a(int) new_creds.should_be_a(gb.Creds) @ktu.gssapi_extension_test('rfc5587', 'RFC 5587') def test_rfc5587(self): mechs = gb.indicate_mechs_by_attrs(None, None, None) mechs.should_be_a(set) mechs.shouldnt_be_empty() # We're validating RFC 5587 here: by iterating over all mechanisms, # we can query their attributes and build a mapping of attr->{mechs}. # To test indicate_mechs_by_attrs, we can use this mapping and # ensure that, when the attribute is placed in a slot, we get the # expected result (e.g., attr in have --> mechs are present). attrs_dict = {} known_attrs_dict = {} for mech in mechs: mech.shouldnt_be_none() mech.should_be_a(gb.OID) inquire_out = gb.inquire_attrs_for_mech(mech) mech_attrs = inquire_out.mech_attrs known_mech_attrs = inquire_out.known_mech_attrs mech_attrs.should_be_a(set) known_mech_attrs.should_be_a(set) # Verify that we get data for every available # attribute. Testing the contents of a few known # attributes is done in test_display_mech_attr(). for mech_attr in mech_attrs: mech_attr.shouldnt_be_none() mech_attr.should_be_a(gb.OID) display_out = gb.display_mech_attr(mech_attr) display_out.name.shouldnt_be_none() display_out.short_desc.shouldnt_be_none() display_out.long_desc.shouldnt_be_none() display_out.name.should_be_a(bytes) display_out.short_desc.should_be_a(bytes) display_out.long_desc.should_be_a(bytes) if mech_attr not in attrs_dict: attrs_dict[mech_attr] = set() attrs_dict[mech_attr].add(mech) for mech_attr in known_mech_attrs: mech_attr.shouldnt_be_none() mech_attr.should_be_a(gb.OID) display_out = gb.display_mech_attr(mech_attr) display_out.name.shouldnt_be_none() display_out.short_desc.shouldnt_be_none() display_out.long_desc.shouldnt_be_none() display_out.name.should_be_a(bytes) display_out.short_desc.should_be_a(bytes) display_out.long_desc.should_be_a(bytes) if mech_attr not in known_attrs_dict: known_attrs_dict[mech_attr] = set() known_attrs_dict[mech_attr].add(mech) for attr, expected_mechs in attrs_dict.items(): attrs = set([attr]) mechs = gb.indicate_mechs_by_attrs(attrs, None, None) mechs.shouldnt_be_empty() mechs.should_be(expected_mechs) mechs = gb.indicate_mechs_by_attrs(None, attrs, None) for expected_mech in expected_mechs: mechs.shouldnt_include(expected_mech) for attr, expected_mechs in known_attrs_dict.items(): attrs = set([attr]) mechs = gb.indicate_mechs_by_attrs(None, None, attrs) mechs.shouldnt_be_empty() mechs.should_be(expected_mechs) @ktu.gssapi_extension_test('rfc5587', 'RFC 5587') def test_display_mech_attr(self): test_attrs = [ # oid, name, short_desc, long_desc # Taken from krb5/src/tests/gssapi/t_saslname [gb.OID.from_int_seq("1.3.6.1.5.5.13.24"), b"GSS_C_MA_CBINDINGS", b"channel-bindings", b"Mechanism supports channel bindings."], [gb.OID.from_int_seq("1.3.6.1.5.5.13.1"), b"GSS_C_MA_MECH_CONCRETE", b"concrete-mech", b"Mechanism is neither a pseudo-mechanism nor a composite " b"mechanism."] ] for attr in test_attrs: display_out = gb.display_mech_attr(attr[0]) display_out.name.should_be(attr[1]) display_out.short_desc.should_be(attr[2]) display_out.long_desc.should_be(attr[3]) @ktu.gssapi_extension_test('rfc5801', 'SASL Names') def test_sasl_names(self): mechs = gb.indicate_mechs() for mech in mechs: out = gb.inquire_saslname_for_mech(mech) out_smn = out.sasl_mech_name out_smn.shouldnt_be_none() out_smn.should_be_a(bytes) out_smn.shouldnt_be_empty() out_mn = out.mech_name out_mn.shouldnt_be_none() out_mn.should_be_a(bytes) out_md = out.mech_description out_md.shouldnt_be_none() out_md.should_be_a(bytes) cmp_mech = gb.inquire_mech_for_saslname(out_smn) cmp_mech.shouldnt_be_none() cmp_mech.should_be(mech) @ktu.gssapi_extension_test('rfc4178', 'Negotiation Mechanism') def test_set_neg_mechs(self): all_mechs = gb.indicate_mechs() spnego_mech = gb.OID.from_int_seq("1.3.6.1.5.5.2") krb5_mech = gb.OID.from_int_seq("1.2.840.113554.1.2.2") ntlm_mech = gb.OID.from_int_seq("1.3.6.1.4.1.311.2.2.10") server_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) username = gb.import_name(name=b"user", name_type=gb.NameType.user) krb5_client_creds = gb.acquire_cred( None, usage='initiate', mechs=[krb5_mech, spnego_mech]).creds try: ntlm_client_creds = gb.acquire_cred_with_password( name=username, password=b'password', mechs=[ntlm_mech, spnego_mech]).creds except gb.GSSError: self.skipTest('You do not have the GSSAPI gss-ntlmssp mech ' 'installed') server_creds = gb.acquire_cred(server_name, usage='accept', mechs=all_mechs).creds neg_resp = gb.set_neg_mechs(server_creds, [ntlm_mech]) neg_resp.should_be_none() client_ctx_resp = gb.init_sec_context(server_name, creds=ntlm_client_creds, mech=spnego_mech) client_token = client_ctx_resp.token server_ctx_resp = gb.accept_sec_context(client_token, acceptor_creds=server_creds) server_ctx_resp.shouldnt_be_none() client_ctx_resp = gb.init_sec_context(server_name, creds=krb5_client_creds, mech=spnego_mech) client_token = client_ctx_resp.token gb.accept_sec_context.should_raise(gb.GSSError, client_token, acceptor_creds=server_creds) neg_resp = gb.set_neg_mechs(server_creds, [krb5_mech]) neg_resp.should_be_none() client_ctx_resp = gb.init_sec_context(server_name, creds=krb5_client_creds, mech=spnego_mech) client_token = client_ctx_resp.token server_ctx_resp = gb.accept_sec_context(client_token, acceptor_creds=server_creds) server_ctx_resp.shouldnt_be_none() client_ctx_resp = gb.init_sec_context(server_name, creds=ntlm_client_creds, mech=spnego_mech) client_token = client_ctx_resp.token gb.accept_sec_context.should_raise(gb.GSSError, client_token, acceptor_creds=server_creds) @ktu.gssapi_extension_test('ggf', 'Global Grid Forum') @ktu.gssapi_extension_test('s4u', 'S4U') @ktu.krb_minversion_test('1.16', 'querying impersonator name of krb5 GSS ' 'Credential using the ' 'GSS_KRB5_GET_CRED_IMPERSONATOR OID') def test_inquire_cred_by_oid_impersonator(self): svc_princ = SERVICE_PRINCIPAL.decode("UTF-8") self.realm.kinit(svc_princ, flags=['-k', '-f']) target_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) client_token = gb.init_sec_context(target_name).token # if our acceptor creds have a usage of both, we get # s4u2proxy delegated credentials server_creds = gb.acquire_cred(None, usage='both').creds server_ctx_resp = gb.accept_sec_context(client_token, acceptor_creds=server_creds) server_ctx_resp.shouldnt_be_none() server_ctx_resp.delegated_creds.shouldnt_be_none() server_ctx_resp.delegated_creds.should_be_a(gb.Creds) # GSS_KRB5_GET_CRED_IMPERSONATOR oid = gb.OID.from_int_seq("1.2.840.113554.1.2.2.5.14") info = gb.inquire_cred_by_oid(server_ctx_resp.delegated_creds, oid) info.should_be_a(list) info.shouldnt_be_empty() info[0].should_be_a(bytes) info[0].should_be(b"%s@%s" % (SERVICE_PRINCIPAL, self.realm.realm.encode('utf-8'))) @ktu.gssapi_extension_test('ggf', 'Global Grid Forum') def test_inquire_sec_context_by_oid(self): target_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) ctx_resp1 = gb.init_sec_context(target_name) server_name = gb.import_name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) server_creds = gb.acquire_cred(server_name)[0] server_resp = gb.accept_sec_context(ctx_resp1[3], acceptor_creds=server_creds) server_ctx = server_resp[0] server_tok = server_resp[3] client_resp2 = gb.init_sec_context(target_name, context=ctx_resp1[0], input_token=server_tok) client_ctx = client_resp2[0] # GSS_C_INQ_SSPI_SESSION_KEY session_key_oid = gb.OID.from_int_seq("1.2.840.113554.1.2.2.5.5") client_key = gb.inquire_sec_context_by_oid(client_ctx, session_key_oid) server_key = gb.inquire_sec_context_by_oid(server_ctx, session_key_oid) client_key.should_be_a(list) client_key.shouldnt_be_empty() server_key.should_be_a(list) server_key.shouldnt_be_empty() client_key.should_have_same_items_as(server_key) @ktu.gssapi_extension_test('ggf', 'Global Grid Forum') def test_inquire_sec_context_by_oid_should_raise_error(self): target_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) ctx_resp1 = gb.init_sec_context(target_name) server_name = gb.import_name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) server_creds = gb.acquire_cred(server_name)[0] server_resp = gb.accept_sec_context(ctx_resp1[3], acceptor_creds=server_creds) client_resp2 = gb.init_sec_context(target_name, context=ctx_resp1[0], input_token=server_resp[3]) client_ctx = client_resp2[0] invalid_oid = gb.OID.from_int_seq("1.2.3.4.5.6.7.8.9") gb.inquire_sec_context_by_oid.should_raise(gb.GSSError, client_ctx, invalid_oid) @ktu.gssapi_extension_test('ggf', 'Global Grid Forum') @ktu.gssapi_extension_test('password', 'Add Credential with Password') def test_set_sec_context_option(self): ntlm_mech = gb.OID.from_int_seq("1.3.6.1.4.1.311.2.2.10") username = gb.import_name(name=b"user", name_type=gb.NameType.user) try: cred = gb.acquire_cred_with_password(name=username, password=b"password", mechs=[ntlm_mech]) except gb.GSSError: self.skipTest('You do not have the GSSAPI gss-ntlmssp mech ' 'installed') server = gb.import_name(name=b"server", name_type=gb.NameType.hostbased_service) orig_context = gb.init_sec_context(server, creds=cred.creds, mech=ntlm_mech)[0] # GSS_NTLMSSP_RESET_CRYPTO_OID_STRING reset_mech = gb.OID.from_int_seq("1.3.6.1.4.1.7165.655.1.3") out_context = gb.set_sec_context_option(reset_mech, context=orig_context, value=b"\x00" * 4) out_context.should_be_a(gb.SecurityContext) @ktu.gssapi_extension_test('ggf', 'Global Grid Forum') @ktu.gssapi_extension_test('password', 'Add Credential with Password') def test_set_sec_context_option_fail(self): ntlm_mech = gb.OID.from_int_seq("1.3.6.1.4.1.311.2.2.10") username = gb.import_name(name=b"user", name_type=gb.NameType.user) try: cred = gb.acquire_cred_with_password(name=username, password=b"password", mechs=[ntlm_mech]) except gb.GSSError: self.skipTest('You do not have the GSSAPI gss-ntlmssp mech ' 'installed') server = gb.import_name(name=b"server", name_type=gb.NameType.hostbased_service) context = gb.init_sec_context(server, creds=cred.creds, mech=ntlm_mech)[0] # GSS_NTLMSSP_RESET_CRYPTO_OID_STRING reset_mech = gb.OID.from_int_seq("1.3.6.1.4.1.7165.655.1.3") # will raise a GSSError if no data was passed in gb.set_sec_context_option.should_raise(gb.GSSError, reset_mech, context) @ktu.gssapi_extension_test('set_cred_opt', 'Kitten Set Credential Option') @ktu.krb_minversion_test('1.14', 'GSS_KRB5_CRED_NO_CI_FLAGS_X was added in MIT ' 'krb5 1.14') def test_set_cred_option(self): name = gb.import_name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) # GSS_KRB5_CRED_NO_CI_FLAGS_X no_ci_flags_x = gb.OID.from_int_seq("1.2.752.43.13.29") orig_cred = gb.acquire_cred(name).creds # nothing much we can test here apart from it doesn't fail and the # id of the return cred is the same as the input one output_cred = gb.set_cred_option(no_ci_flags_x, creds=orig_cred) output_cred.should_be_a(gb.Creds) @ktu.gssapi_extension_test('set_cred_opt', 'Kitten Set Credential Option') def test_set_cred_option_should_raise_error(self): name = gb.import_name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) orig_cred = gb.acquire_cred(name).creds # this is a fake OID and shouldn't work at all invalid_oid = gb.OID.from_int_seq("1.2.3.4.5.6.7.8.9") gb.set_cred_option.should_raise(gb.GSSError, invalid_oid, orig_cred, b"\x00") class TestIntEnumFlagSet(unittest.TestCase): def test_create_from_int(self): int_val = (gb.RequirementFlag.integrity | gb.RequirementFlag.confidentiality) fset = gb.IntEnumFlagSet(gb.RequirementFlag, int_val) int(fset).should_be(int_val) def test_create_from_other_set(self): int_val = (gb.RequirementFlag.integrity | gb.RequirementFlag.confidentiality) fset1 = gb.IntEnumFlagSet(gb.RequirementFlag, int_val) fset2 = gb.IntEnumFlagSet(gb.RequirementFlag, fset1) fset1.should_be(fset2) def test_create_from_list(self): lst = [gb.RequirementFlag.integrity, gb.RequirementFlag.confidentiality] fset = gb.IntEnumFlagSet(gb.RequirementFlag, lst) list(fset).should_have_same_items_as(lst) def test_create_empty(self): fset = gb.IntEnumFlagSet(gb.RequirementFlag) fset.should_be_empty() def _create_fset(self): lst = [gb.RequirementFlag.integrity, gb.RequirementFlag.confidentiality] return gb.IntEnumFlagSet(gb.RequirementFlag, lst) def test_contains(self): fset = self._create_fset() fset.should_include(gb.RequirementFlag.integrity) fset.shouldnt_include(gb.RequirementFlag.protection_ready) def test_len(self): self._create_fset().should_have_length(2) def test_add(self): fset = self._create_fset() fset.should_have_length(2) fset.add(gb.RequirementFlag.protection_ready) fset.should_have_length(3) fset.should_include(gb.RequirementFlag.protection_ready) def test_discard(self): fset = self._create_fset() fset.should_have_length(2) fset.discard(gb.RequirementFlag.protection_ready) fset.should_have_length(2) fset.discard(gb.RequirementFlag.integrity) fset.should_have_length(1) fset.shouldnt_include(gb.RequirementFlag.integrity) def test_and_enum(self): fset = self._create_fset() (fset & gb.RequirementFlag.integrity).should_be_true() (fset & gb.RequirementFlag.protection_ready).should_be_false() def test_and_int(self): fset = self._create_fset() int_val = int(gb.RequirementFlag.integrity) (fset & int_val).should_be(int_val) def test_and_set(self): fset1 = self._create_fset() fset2 = self._create_fset() fset3 = self._create_fset() fset1.add(gb.RequirementFlag.protection_ready) fset2.add(gb.RequirementFlag.out_of_sequence_detection) (fset1 & fset2).should_be(fset3) def test_or_enum(self): fset1 = self._create_fset() fset2 = fset1 | gb.RequirementFlag.protection_ready (fset1 < fset2).should_be_true() fset2.should_include(gb.RequirementFlag.protection_ready) def test_or_int(self): fset = self._create_fset() int_val = int(gb.RequirementFlag.integrity) (fset | int_val).should_be(int(fset)) def test_or_set(self): fset1 = self._create_fset() fset2 = self._create_fset() fset3 = self._create_fset() fset1.add(gb.RequirementFlag.protection_ready) fset2.add(gb.RequirementFlag.out_of_sequence_detection) fset3.add(gb.RequirementFlag.protection_ready) fset3.add(gb.RequirementFlag.out_of_sequence_detection) (fset1 | fset2).should_be(fset3) def test_xor_enum(self): fset1 = self._create_fset() fset2 = fset1 ^ gb.RequirementFlag.protection_ready fset3 = fset1 ^ gb.RequirementFlag.integrity fset2.should_have_length(3) fset2.should_include(gb.RequirementFlag.protection_ready) fset3.should_have_length(1) fset3.shouldnt_include(gb.RequirementFlag.integrity) def test_xor_int(self): fset = self._create_fset() (fset ^ int(gb.RequirementFlag.protection_ready)).should_be( int(fset) ^ gb.RequirementFlag.protection_ready) (fset ^ int(gb.RequirementFlag.integrity)).should_be( int(fset) ^ gb.RequirementFlag.integrity) def test_xor_set(self): fset1 = self._create_fset() fset2 = self._create_fset() fset1.add(gb.RequirementFlag.protection_ready) fset2.add(gb.RequirementFlag.out_of_sequence_detection) fset3 = fset1 ^ fset2 fset3.should_have_length(2) fset3.shouldnt_include(gb.RequirementFlag.integrity) fset3.shouldnt_include(gb.RequirementFlag.confidentiality) fset3.should_include(gb.RequirementFlag.protection_ready) fset3.should_include(gb.RequirementFlag.out_of_sequence_detection) class TestInitContext(_GSSAPIKerberosTestCase): def setUp(self): self.target_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) def tearDown(self): gb.release_name(self.target_name) def test_basic_init_default_ctx(self): ctx_resp = gb.init_sec_context(self.target_name) ctx_resp.shouldnt_be_none() (ctx, out_mech_type, out_req_flags, out_token, out_ttl, cont_needed) = ctx_resp ctx.shouldnt_be_none() ctx.should_be_a(gb.SecurityContext) out_mech_type.should_be(gb.MechType.kerberos) out_req_flags.should_be_a(Set) out_req_flags.should_be_at_least_length(2) out_token.shouldnt_be_empty() out_ttl.should_be_greater_than(0) cont_needed.should_be_a(bool) gb.delete_sec_context(ctx) class TestAcceptContext(_GSSAPIKerberosTestCase): def setUp(self): self.target_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) ctx_resp = gb.init_sec_context(self.target_name) self.client_token = ctx_resp[3] self.client_ctx = ctx_resp[0] self.client_ctx.shouldnt_be_none() self.server_name = gb.import_name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) self.server_creds = gb.acquire_cred(self.server_name)[0] self.server_ctx = None def tearDown(self): gb.release_name(self.target_name) gb.release_name(self.server_name) gb.release_cred(self.server_creds) gb.delete_sec_context(self.client_ctx) if self.server_ctx is not None: gb.delete_sec_context(self.server_ctx) def test_basic_accept_context_no_acceptor_creds(self): server_resp = gb.accept_sec_context(self.client_token) server_resp.shouldnt_be_none() (self.server_ctx, name, mech_type, out_token, out_req_flags, out_ttl, delegated_cred, cont_needed) = server_resp self.server_ctx.shouldnt_be_none() self.server_ctx.should_be_a(gb.SecurityContext) name.shouldnt_be_none() name.should_be_a(gb.Name) mech_type.should_be(gb.MechType.kerberos) out_token.shouldnt_be_empty() out_req_flags.should_be_a(Set) out_req_flags.should_be_at_least_length(2) out_ttl.should_be_greater_than(0) if delegated_cred is not None: delegated_cred.should_be_a(gb.Creds) cont_needed.should_be_a(bool) def test_basic_accept_context(self): server_resp = gb.accept_sec_context(self.client_token, acceptor_creds=self.server_creds) server_resp.shouldnt_be_none() (self.server_ctx, name, mech_type, out_token, out_req_flags, out_ttl, delegated_cred, cont_needed) = server_resp self.server_ctx.shouldnt_be_none() self.server_ctx.should_be_a(gb.SecurityContext) name.shouldnt_be_none() name.should_be_a(gb.Name) mech_type.should_be(gb.MechType.kerberos) out_token.shouldnt_be_empty() out_req_flags.should_be_a(Set) out_req_flags.should_be_at_least_length(2) out_ttl.should_be_greater_than(0) if delegated_cred is not None: delegated_cred.should_be_a(gb.Creds) cont_needed.should_be_a(bool) def test_channel_bindings(self): bdgs = gb.ChannelBindings(application_data=b'abcxyz', initiator_address_type=gb.AddressType.ip, initiator_address=b'127.0.0.1', acceptor_address_type=gb.AddressType.ip, acceptor_address=b'127.0.0.1') self.target_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) ctx_resp = gb.init_sec_context(self.target_name, channel_bindings=bdgs) self.client_token = ctx_resp[3] self.client_ctx = ctx_resp[0] self.client_ctx.shouldnt_be_none() self.server_name = gb.import_name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) self.server_creds = gb.acquire_cred(self.server_name)[0] server_resp = gb.accept_sec_context(self.client_token, acceptor_creds=self.server_creds, channel_bindings=bdgs) server_resp.shouldnt_be_none self.server_ctx = server_resp.context def test_bad_channel_binding_raises_error(self): bdgs = gb.ChannelBindings(application_data=b'abcxyz', initiator_address_type=gb.AddressType.ip, initiator_address=b'127.0.0.1', acceptor_address_type=gb.AddressType.ip, acceptor_address=b'127.0.0.1') self.target_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) ctx_resp = gb.init_sec_context(self.target_name, channel_bindings=bdgs) self.client_token = ctx_resp[3] self.client_ctx = ctx_resp[0] self.client_ctx.shouldnt_be_none() self.server_name = gb.import_name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) self.server_creds = gb.acquire_cred(self.server_name)[0] bdgs.acceptor_address = b'127.0.1.0' gb.accept_sec_context.should_raise(gb.GSSError, self.client_token, acceptor_creds=self.server_creds, channel_bindings=bdgs) class TestWrapUnwrap(_GSSAPIKerberosTestCase): def setUp(self): self.target_name = gb.import_name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) ctx_resp = gb.init_sec_context(self.target_name) self.client_token1 = ctx_resp[3] self.client_ctx = ctx_resp[0] self.server_name = gb.import_name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) self.server_creds = gb.acquire_cred(self.server_name)[0] server_resp = gb.accept_sec_context(self.client_token1, acceptor_creds=self.server_creds) self.server_ctx = server_resp[0] self.server_tok = server_resp[3] client_resp2 = gb.init_sec_context(self.target_name, context=self.client_ctx, input_token=self.server_tok) self.client_token2 = client_resp2[3] self.client_ctx = client_resp2[0] def tearDown(self): gb.release_name(self.target_name) gb.release_name(self.server_name) gb.release_cred(self.server_creds) gb.delete_sec_context(self.client_ctx) gb.delete_sec_context(self.server_ctx) def test_import_export_sec_context(self): tok = gb.export_sec_context(self.client_ctx) tok.shouldnt_be_none() tok.should_be_a(bytes) tok.shouldnt_be_empty() imported_ctx = gb.import_sec_context(tok) imported_ctx.shouldnt_be_none() imported_ctx.should_be_a(gb.SecurityContext) self.client_ctx = imported_ctx # ensure that it gets deleted def test_get_mic(self): mic_token = gb.get_mic(self.client_ctx, b"some message") mic_token.shouldnt_be_none() mic_token.should_be_a(bytes) mic_token.shouldnt_be_empty() def test_basic_verify_mic(self): mic_token = gb.get_mic(self.client_ctx, b"some message") qop_used = gb.verify_mic(self.server_ctx, b"some message", mic_token) qop_used.should_be_an_integer() # test a bad MIC gb.verify_mic.should_raise(gb.GSSError, self.server_ctx, b"some other message", b"some invalid mic") def test_wrap_size_limit(self): with_conf = gb.wrap_size_limit(self.client_ctx, 100) without_conf = gb.wrap_size_limit(self.client_ctx, 100, confidential=False) with_conf.should_be_an_integer() without_conf.should_be_an_integer() without_conf.should_be_less_than(100) with_conf.should_be_less_than(100) def test_basic_wrap_unwrap(self): (wrapped_message, conf) = gb.wrap(self.client_ctx, b'test message') conf.should_be_a(bool) conf.should_be_true() wrapped_message.should_be_a(bytes) wrapped_message.shouldnt_be_empty() wrapped_message.should_be_longer_than('test message') (unwrapped_message, conf, qop) = gb.unwrap(self.server_ctx, wrapped_message) conf.should_be_a(bool) conf.should_be_true() qop.should_be_an_integer() qop.should_be_at_least(0) unwrapped_message.should_be_a(bytes) unwrapped_message.shouldnt_be_empty() unwrapped_message.should_be(b'test message') @ktu.gssapi_extension_test('dce', 'DCE (IOV/AEAD)') def test_basic_iov_wrap_unwrap_prealloc(self): init_data = b'some encrypted data' init_other_data = b'some other encrypted data' init_signed_info = b'some sig data' init_message = gb.IOV((gb.IOVBufferType.sign_only, init_signed_info), init_data, init_other_data, auto_alloc=False) init_message[0].allocate.should_be_false() init_message[4].allocate.should_be_false() init_message[5].allocate.should_be_false() conf = gb.wrap_iov_length(self.client_ctx, init_message) conf.should_be_a(bool) conf.should_be_true() init_message[0].should_be_at_least_size(1) init_message[5].should_be_at_least_size(1) conf = gb.wrap_iov(self.client_ctx, init_message) conf.should_be_a(bool) conf.should_be_true() # make sure we didn't strings used init_data.should_be(b'some encrypted data') init_other_data.should_be(b'some other encrypted data') init_signed_info.should_be(b'some sig data') init_message[2].value.shouldnt_be(b'some encrypted data') init_message[3].value.shouldnt_be(b'some other encrypted data') (conf, qop) = gb.unwrap_iov(self.server_ctx, init_message) conf.should_be_a(bool) conf.should_be_true() qop.should_be_a(int) init_message[1].value.should_be(init_signed_info) init_message[2].value.should_be(init_data) init_message[3].value.should_be(init_other_data) @ktu.gssapi_extension_test('dce', 'DCE (IOV/AEAD)') def test_basic_iov_wrap_unwrap_autoalloc(self): init_data = b'some encrypted data' init_other_data = b'some other encrypted data' init_signed_info = b'some sig data' init_message = gb.IOV((gb.IOVBufferType.sign_only, init_signed_info), init_data, init_other_data) conf = gb.wrap_iov(self.client_ctx, init_message) conf.should_be_a(bool) conf.should_be_true() # make sure we didn't strings used init_data.should_be(b'some encrypted data') init_other_data.should_be(b'some other encrypted data') init_signed_info.should_be(b'some sig data') init_message[2].value.shouldnt_be(b'some encrypted data') init_message[3].value.shouldnt_be(b'some other encrypted data') (conf, qop) = gb.unwrap_iov(self.server_ctx, init_message) conf.should_be_a(bool) conf.should_be_true() qop.should_be_a(int) init_message[1].value.should_be(init_signed_info) init_message[2].value.should_be(init_data) init_message[3].value.should_be(init_other_data) @ktu.gssapi_extension_test('dce', 'DCE (IOV/AEAD)') def test_basic_aead_wrap_unwrap(self): assoc_data = b'some sig data' (wrapped_message, conf) = gb.wrap_aead(self.client_ctx, b'test message', assoc_data) conf.should_be_a(bool) conf.should_be_true() wrapped_message.should_be_a(bytes) wrapped_message.shouldnt_be_empty() wrapped_message.should_be_longer_than('test message') (unwrapped_message, conf, qop) = gb.unwrap_aead(self.server_ctx, wrapped_message, assoc_data) conf.should_be_a(bool) conf.should_be_true() qop.should_be_an_integer() qop.should_be_at_least(0) unwrapped_message.should_be_a(bytes) unwrapped_message.shouldnt_be_empty() unwrapped_message.should_be(b'test message') @ktu.gssapi_extension_test('dce', 'DCE (IOV/AEAD)') def test_basic_aead_wrap_unwrap_no_assoc(self): (wrapped_message, conf) = gb.wrap_aead(self.client_ctx, b'test message') conf.should_be_a(bool) conf.should_be_true() wrapped_message.should_be_a(bytes) wrapped_message.shouldnt_be_empty() wrapped_message.should_be_longer_than('test message') (unwrapped_message, conf, qop) = gb.unwrap_aead(self.server_ctx, wrapped_message) conf.should_be_a(bool) conf.should_be_true() qop.should_be_an_integer() qop.should_be_at_least(0) unwrapped_message.should_be_a(bytes) unwrapped_message.shouldnt_be_empty() unwrapped_message.should_be(b'test message') @ktu.gssapi_extension_test('dce', 'DCE (IOV/AEAD)') def test_basic_aead_wrap_unwrap_bad_assoc_raises_error(self): assoc_data = b'some sig data' (wrapped_message, conf) = gb.wrap_aead(self.client_ctx, b'test message', assoc_data) conf.should_be_a(bool) conf.should_be_true() wrapped_message.should_be_a(bytes) wrapped_message.shouldnt_be_empty() wrapped_message.should_be_longer_than('test message') gb.unwrap_aead.should_raise(gb.BadMICError, self.server_ctx, wrapped_message, b'some other sig data') @ktu.gssapi_extension_test('iov_mic', 'IOV MIC') def test_get_mic_iov(self): init_message = gb.IOV(b'some data', (gb.IOVBufferType.sign_only, b'some sig data'), gb.IOVBufferType.mic_token, std_layout=False) gb.get_mic_iov(self.client_ctx, init_message) init_message[2].type.should_be(gb.IOVBufferType.mic_token) init_message[2].value.shouldnt_be_empty() @ktu.gssapi_extension_test('iov_mic', 'IOV MIC') def test_basic_verify_mic_iov(self): init_message = gb.IOV(b'some data', (gb.IOVBufferType.sign_only, b'some sig data'), gb.IOVBufferType.mic_token, std_layout=False) gb.get_mic_iov(self.client_ctx, init_message) init_message[2].type.should_be(gb.IOVBufferType.mic_token) init_message[2].value.shouldnt_be_empty() qop_used = gb.verify_mic_iov(self.server_ctx, init_message) qop_used.should_be_an_integer() @ktu.gssapi_extension_test('iov_mic', 'IOV MIC') def test_verify_mic_iov_bad_mic_raises_error(self): init_message = gb.IOV(b'some data', (gb.IOVBufferType.sign_only, b'some sig data'), (gb.IOVBufferType.mic_token, 'abaava'), std_layout=False) # test a bad MIC gb.verify_mic_iov.should_raise(gb.GSSError, self.server_ctx, init_message) @ktu.gssapi_extension_test('iov_mic', 'IOV MIC') def test_get_mic_iov_length(self): init_message = gb.IOV(b'some data', (gb.IOVBufferType.sign_only, b'some sig data'), gb.IOVBufferType.mic_token, std_layout=False, auto_alloc=False) gb.get_mic_iov_length(self.client_ctx, init_message) init_message[2].type.should_be(gb.IOVBufferType.mic_token) init_message[2].value.shouldnt_be_empty() TEST_OIDS = {'SPNEGO': {'bytes': b'\053\006\001\005\005\002', 'string': '1.3.6.1.5.5.2'}, 'KRB5': {'bytes': b'\052\206\110\206\367\022\001\002\002', 'string': '1.2.840.113554.1.2.2'}, 'KRB5_OLD': {'bytes': b'\053\005\001\005\002', 'string': '1.3.5.1.5.2'}, 'KRB5_WRONG': {'bytes': b'\052\206\110\202\367\022\001\002\002', 'string': '1.2.840.48018.1.2.2'}, 'IAKERB': {'bytes': b'\053\006\001\005\002\005', 'string': '1.3.6.1.5.2.5'}} class TestOIDTransforms(unittest.TestCase): def test_decode_from_bytes(self): for oid in TEST_OIDS.values(): o = gb.OID(elements=oid['bytes']) text = repr(o) text.should_be("".format(oid['string'])) def test_encode_from_string(self): for oid in TEST_OIDS.values(): o = gb.OID.from_int_seq(oid['string']) o.__bytes__().should_be(oid['bytes']) def test_encode_from_int_seq(self): for oid in TEST_OIDS.values(): int_seq = oid['string'].split('.') o = gb.OID.from_int_seq(int_seq) o.__bytes__().should_be(oid['bytes']) def test_comparisons(self): krb5 = gb.OID.from_int_seq(TEST_OIDS['KRB5']['string']) krb5_other = gb.OID.from_int_seq(TEST_OIDS['KRB5']['string']) spnego = gb.OID.from_int_seq(TEST_OIDS['SPNEGO']['string']) (krb5 == krb5_other).should_be(True) (krb5 == spnego).should_be(False) (krb5 != krb5_other).should_be(False) (krb5 != spnego).should_be(True) python-gssapi-1.6.1/gssapi/tests/test_high_level.py0000664000372000037200000010101213523321513017404 0ustar import copy import os import socket import sys import pickle import should_be.all # noqa import six from parameterized import parameterized from gssapi import creds as gsscreds from gssapi import mechs as gssmechs from gssapi import names as gssnames from gssapi import sec_contexts as gssctx from gssapi import raw as gb from gssapi import _utils as gssutils from gssapi import exceptions as excs import k5test.unit as ktu import k5test as kt TARGET_SERVICE_NAME = b'host' FQDN = socket.getfqdn().encode('utf-8') SERVICE_PRINCIPAL = TARGET_SERVICE_NAME + b'/' + FQDN # disable error deferring to catch errors immediately gssctx.SecurityContext.__DEFER_STEP_ERRORS__ = False class _GSSAPIKerberosTestCase(kt.KerberosTestCase): @classmethod def setUpClass(cls): super(_GSSAPIKerberosTestCase, cls).setUpClass() svc_princ = SERVICE_PRINCIPAL.decode("UTF-8") cls.realm.kinit(svc_princ, flags=['-k']) cls._init_env() cls.USER_PRINC = cls.realm.user_princ.split('@')[0].encode("UTF-8") cls.ADMIN_PRINC = cls.realm.admin_princ.split('@')[0].encode("UTF-8") @classmethod def _init_env(cls): cls._saved_env = copy.deepcopy(os.environ) for k, v in cls.realm.env.items(): os.environ[k] = v @classmethod def _restore_env(cls): for k in copy.deepcopy(os.environ): if k in cls._saved_env: os.environ[k] = cls._saved_env[k] else: del os.environ[k] cls._saved_env = None @classmethod def tearDownClass(cls): super(_GSSAPIKerberosTestCase, cls).tearDownClass() cls._restore_env() def _perms_cycle(elem, rest, old_d): if elem is None: name_str = "with_params_" true_keys = [k for (k, v) in old_d.items() if v] if not true_keys: name_str += 'none' else: name_str += '_'.join(true_keys) return [(name_str, old_d)] else: if len(rest) > 0: next_elem = rest.pop() else: next_elem = None res = [] for v in (True, False): new_d = copy.deepcopy(old_d) new_d[elem] = v res.extend(_perms_cycle(next_elem, copy.deepcopy(rest), new_d)) return res def exist_perms(**kwargs): all_elems = list(kwargs.keys()) curr_elems = copy.deepcopy(all_elems) perms = _perms_cycle(curr_elems.pop(), curr_elems, {}) res = [] for name_str, perm in perms: args = dict([(k, v) for (k, v) in kwargs.items() if perm[k]]) res.append((name_str, args)) return parameterized.expand(res) def true_false_perms(*all_elems_tuple): all_elems = list(all_elems_tuple) curr_elems = copy.deepcopy(all_elems) perms = _perms_cycle(curr_elems.pop(), curr_elems, {}) return parameterized.expand(perms) # NB(directxman12): MIT Kerberos completely ignores input TTLs for # credentials. I suspect this is because the TTL # is actually set when kinit is called. # NB(directxman12): the above note used to be wonderfully sarcastic class CredsTestCase(_GSSAPIKerberosTestCase): def setUp(self): super(CredsTestCase, self).setUp() svc_princ = SERVICE_PRINCIPAL.decode("UTF-8") self.realm.kinit(svc_princ, flags=['-k']) self.name = gssnames.Name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) @exist_perms(lifetime=30, mechs=[gb.MechType.kerberos], usage='both') def test_acquire_by_init(self, str_name, kwargs): creds = gsscreds.Credentials(name=self.name, **kwargs) creds.lifetime.should_be_an_integer() del creds @exist_perms(lifetime=30, mechs=[gb.MechType.kerberos], usage='both') def test_acquire_by_method(self, str_name, kwargs): cred_resp = gsscreds.Credentials.acquire(name=self.name, **kwargs) cred_resp.shouldnt_be_none() (creds, actual_mechs, ttl) = cred_resp creds.shouldnt_be_none() creds.should_be_a(gsscreds.Credentials) actual_mechs.shouldnt_be_empty() actual_mechs.should_include(gb.MechType.kerberos) ttl.should_be_an_integer() del creds @ktu.gssapi_extension_test('rfc5588', 'RFC 5588') def test_store_acquire(self): # we need to acquire a forwardable ticket svc_princ = SERVICE_PRINCIPAL.decode("UTF-8") self.realm.kinit(svc_princ, flags=['-k', '-f']) target_name = gssnames.Name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) client_creds = gsscreds.Credentials(usage='initiate') client_ctx = gssctx.SecurityContext( name=target_name, creds=client_creds, flags=gb.RequirementFlag.delegate_to_peer) client_token = client_ctx.step() server_creds = gsscreds.Credentials(usage='accept') server_ctx = gssctx.SecurityContext(creds=server_creds) server_ctx.step(client_token) deleg_creds = server_ctx.delegated_creds deleg_creds.shouldnt_be_none() store_res = deleg_creds.store(usage='initiate', set_default=True, overwrite=True) store_res.usage.should_be('initiate') store_res.mechs.should_include(gb.MechType.kerberos) reacquired_creds = gsscreds.Credentials(name=deleg_creds.name, usage='initiate') reacquired_creds.shouldnt_be_none() @ktu.gssapi_extension_test('cred_store', 'credentials store') def test_store_into_acquire_from(self): CCACHE = 'FILE:{tmpdir}/other_ccache'.format(tmpdir=self.realm.tmpdir) KT = '{tmpdir}/other_keytab'.format(tmpdir=self.realm.tmpdir) store = {'ccache': CCACHE, 'keytab': KT} princ_name = 'service/cs@' + self.realm.realm self.realm.addprinc(princ_name) self.realm.extract_keytab(princ_name, KT) self.realm.kinit(princ_name, None, ['-k', '-t', KT]) initial_creds = gsscreds.Credentials(name=None, usage='initiate') store_res = initial_creds.store(store, overwrite=True) store_res.mechs.shouldnt_be_none() store_res.mechs.shouldnt_be_empty() store_res.usage.should_be('initiate') name = gssnames.Name(princ_name) retrieved_creds = gsscreds.Credentials(name=name, store=store) retrieved_creds.shouldnt_be_none() def test_create_from_other(self): raw_creds = gb.acquire_cred(None, usage='accept').creds high_level_creds = gsscreds.Credentials(raw_creds) high_level_creds.usage.should_be('accept') @true_false_perms('name', 'lifetime', 'usage', 'mechs') def test_inquire(self, str_name, kwargs): creds = gsscreds.Credentials(name=self.name) resp = creds.inquire(**kwargs) if kwargs['name']: resp.name.should_be(self.name) else: resp.name.should_be_none() if kwargs['lifetime']: resp.lifetime.should_be_an_integer() else: resp.lifetime.should_be_none() if kwargs['usage']: resp.usage.should_be('both') else: resp.usage.should_be_none() if kwargs['mechs']: resp.mechs.shouldnt_be_empty() resp.mechs.should_include(gb.MechType.kerberos) else: resp.mechs.should_be_none() @true_false_perms('name', 'init_lifetime', 'accept_lifetime', 'usage') def test_inquire_by_mech(self, str_name, kwargs): creds = gsscreds.Credentials(name=self.name) resp = creds.inquire_by_mech(mech=gb.MechType.kerberos, **kwargs) if kwargs['name']: resp.name.should_be(self.name) else: resp.name.should_be_none() if kwargs['init_lifetime']: resp.init_lifetime.should_be_an_integer() else: resp.init_lifetime.should_be_none() if kwargs['accept_lifetime']: resp.accept_lifetime.should_be_an_integer() else: resp.accept_lifetime.should_be_none() if kwargs['usage']: resp.usage.should_be('both') else: resp.usage.should_be_none() def test_add(self): input_creds = gsscreds.Credentials(gb.Creds()) name = gssnames.Name(SERVICE_PRINCIPAL) new_creds = input_creds.add(name, gb.MechType.kerberos, usage='initiate') new_creds.shouldnt_be_none() new_creds.should_be_a(gsscreds.Credentials) @ktu.gssapi_extension_test('cred_store', 'credentials store') def test_store_into_add_from(self): CCACHE = 'FILE:{tmpdir}/other_ccache'.format(tmpdir=self.realm.tmpdir) KT = '{tmpdir}/other_keytab'.format(tmpdir=self.realm.tmpdir) store = {'ccache': CCACHE, 'keytab': KT} princ_name = 'service/cs@' + self.realm.realm self.realm.addprinc(princ_name) self.realm.extract_keytab(princ_name, KT) self.realm.kinit(princ_name, None, ['-k', '-t', KT]) initial_creds = gsscreds.Credentials(name=None, usage='initiate') store_res = initial_creds.store(store, overwrite=True) store_res.mechs.shouldnt_be_none() store_res.mechs.shouldnt_be_empty() store_res.usage.should_be('initiate') name = gssnames.Name(princ_name) input_creds = gsscreds.Credentials(gb.Creds()) retrieved_creds = input_creds.add(name, gb.MechType.kerberos, store=store) retrieved_creds.shouldnt_be_none() retrieved_creds.should_be_a(gsscreds.Credentials) @ktu.gssapi_extension_test('cred_imp_exp', 'credentials import-export') def test_export(self): creds = gsscreds.Credentials(name=self.name) token = creds.export() token.should_be_a(bytes) @ktu.gssapi_extension_test('cred_imp_exp', 'credentials import-export') def test_import_by_init(self): creds = gsscreds.Credentials(name=self.name) token = creds.export() imported_creds = gsscreds.Credentials(token=token) imported_creds.lifetime.should_be(creds.lifetime) imported_creds.name.should_be(creds.name) @ktu.gssapi_extension_test('cred_imp_exp', 'credentials import-export') def test_pickle_unpickle(self): creds = gsscreds.Credentials(name=self.name) pickled_creds = pickle.dumps(creds) unpickled_creds = pickle.loads(pickled_creds) unpickled_creds.lifetime.should_be(creds.lifetime) unpickled_creds.name.should_be(creds.name) @exist_perms(lifetime=30, mechs=[gb.MechType.kerberos], usage='initiate') @ktu.gssapi_extension_test('s4u', 'S4U') def test_impersonate(self, str_name, kwargs): target_name = gssnames.Name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) # TODO(directxman12): make this use the high-level SecurityContext client_ctx_resp = gb.init_sec_context(target_name) client_token = client_ctx_resp[3] del client_ctx_resp # free everything but the token server_name = self.name server_creds = gsscreds.Credentials(name=server_name, usage='both') server_ctx_resp = gb.accept_sec_context(client_token, acceptor_creds=server_creds) imp_creds = server_creds.impersonate(server_ctx_resp[1], **kwargs) imp_creds.shouldnt_be_none() imp_creds.should_be_a(gsscreds.Credentials) @ktu.gssapi_extension_test('s4u', 'S4U') def test_add_with_impersonate(self): target_name = gssnames.Name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) client_ctx = gssctx.SecurityContext(name=target_name) client_token = client_ctx.step() server_creds = gsscreds.Credentials(usage='both') server_ctx = gssctx.SecurityContext(creds=server_creds, usage='accept') server_ctx.step(client_token) # use empty creds to test here input_creds = gsscreds.Credentials(gb.Creds()) new_creds = input_creds.add(server_ctx.initiator_name, gb.MechType.kerberos, impersonator=server_creds, usage='initiate') new_creds.shouldnt_be(None) new_creds.should_be_a(gsscreds.Credentials) class MechsTestCase(_GSSAPIKerberosTestCase): def test_indicate_mechs(self): mechs = gssmechs.Mechanism.all_mechs() for mech in mechs: s = str(mech) s.shouldnt_be_empty() @ktu.gssapi_extension_test('rfc5801', 'RFC 5801: SASL Names') def test_sasl_properties(self): mechs = gssmechs.Mechanism.all_mechs() for mech in mechs: s = str(mech) s.shouldnt_be_empty() s.should_be_a(str) # Note that some mechanisms don't have SASL names or SASL # descriptions; in this case, GSSAPI returns empty strings. if mech.sasl_name: mech.sasl_name.should_be_a(six.text_type) if mech.description: mech.description.should_be_a(six.text_type) cmp_mech = gssmechs.Mechanism.from_sasl_name(mech.sasl_name) str(cmp_mech).should_be(str(mech)) @ktu.gssapi_extension_test('rfc5587', 'RFC 5587: Mech Inquiry') def test_mech_inquiry(self): mechs = list(gssmechs.Mechanism.all_mechs()) c = len(mechs) g_M_from_attrs = gssmechs.Mechanism.from_attrs for mech in mechs: attrs = mech.attrs known_attrs = mech.known_attrs for attr in attrs: from_desired = g_M_from_attrs(desired_attrs=[attr]) from_except = g_M_from_attrs(except_attrs=[attr]) from_desired = list(from_desired) from_except = list(from_except) (len(from_desired) + len(from_except)).should_be(c) from_desired.should_include(mech) from_except.shouldnt_include(mech) for attr in known_attrs: from_desired = g_M_from_attrs(desired_attrs=[attr]) from_except = g_M_from_attrs(except_attrs=[attr]) from_desired = list(from_desired) from_except = list(from_except) (len(from_desired) + len(from_except)).should_be(c) class NamesTestCase(_GSSAPIKerberosTestCase): def test_create_from_other(self): raw_name = gb.import_name(SERVICE_PRINCIPAL) high_level_name = gssnames.Name(raw_name) bytes(high_level_name).should_be(SERVICE_PRINCIPAL) def test_create_from_name_no_type(self): name = gssnames.Name(SERVICE_PRINCIPAL) name.shouldnt_be_none() def test_create_from_name_and_type(self): name = gssnames.Name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) name.shouldnt_be_none() name.name_type.should_be(gb.NameType.kerberos_principal) def test_create_from_token(self): name1 = gssnames.Name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) exported_name = name1.canonicalize(gb.MechType.kerberos).export() name2 = gssnames.Name(token=exported_name) name2.shouldnt_be_none() name2.name_type.should_be(gb.NameType.kerberos_principal) @ktu.gssapi_extension_test('rfc6680', 'RFC 6680') def test_display_as(self): name = gssnames.Name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) canonical_name = name.canonicalize(gb.MechType.kerberos) # NB(directxman12): krb5 doesn't implement display_name_ext, so just # check to make sure we return the right types and a reasonable value krb_name = canonical_name.display_as( gb.NameType.hostbased_service) princ_str = SERVICE_PRINCIPAL.decode('utf-8') + '@' six.text_type(canonical_name).should_be(princ_str) krb_name.should_be_a(six.text_type) krb_name.should_be(princ_str) @ktu.gssapi_extension_test('rfc6680', 'RFC 6680') def test_create_from_composite_token_no_attrs(self): name1 = gssnames.Name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) exported_name = name1.canonicalize( gb.MechType.kerberos).export(composite=True) name2 = gssnames.Name(token=exported_name, composite=True) name2.shouldnt_be_none() @ktu.gssapi_extension_test('rfc6680', 'RFC 6680') @ktu.krb_plugin_test('authdata', 'greet_client') def test_create_from_composite_token_with_attrs(self): name1 = gssnames.Name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) canon_name = name1.canonicalize(gb.MechType.kerberos) canon_name.attributes['urn:greet:greeting'] = b'some val' exported_name = canon_name.export(composite=True) # TODO(directxman12): when you just import a token as composite, # appears as this name whose text is all garbled, since it contains # all of the attributes, etc, but doesn't properly have the attributes. # Once it's canonicalized, the attributes reappear. However, if you # just import it as normal export, the attributes appear directly. # It is thus unclear as to what is going on # name2_raw = gssnames.Name(token=exported_name, composite=True) # name2 = name2_raw.canonicalize(gb.MechType.kerberos) name2 = gssnames.Name(token=exported_name) name2.shouldnt_be_none() name2.attributes['urn:greet:greeting'].values.should_be( set([b'some val'])) name2.attributes['urn:greet:greeting'].complete.should_be_true() name2.attributes['urn:greet:greeting'].authenticated.should_be_false() def test_to_str(self): name = gssnames.Name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) name_str = str(name) name_str.should_be_a(str) if sys.version_info[0] == 2: target_val = SERVICE_PRINCIPAL else: target_val = SERVICE_PRINCIPAL.decode(gssutils._get_encoding()) name_str.should_be(target_val) def test_to_unicode(self): name = gssnames.Name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) name_str = six.text_type(name) name_str.should_be_a(six.text_type) name_str.should_be(SERVICE_PRINCIPAL.decode(gssutils._get_encoding())) def test_to_bytes(self): name = gssnames.Name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) # NB(directxman12): bytes only calles __bytes__ on Python 3+ name_bytes = name.__bytes__() name_bytes.should_be_a(bytes) name_bytes.should_be(SERVICE_PRINCIPAL) def test_compare(self): name1 = gssnames.Name(SERVICE_PRINCIPAL) name2 = gssnames.Name(SERVICE_PRINCIPAL) name3 = gssnames.Name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) name1.should_be(name2) name1.shouldnt_be(name3) def test_canoncialize_and_export(self): name = gssnames.Name(SERVICE_PRINCIPAL, gb.NameType.kerberos_principal) canonical_name = name.canonicalize(gb.MechType.kerberos) exported_name = canonical_name.export() exported_name.should_be_a(bytes) def test_canonicalize(self): name = gssnames.Name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) canonicalized_name = name.canonicalize(gb.MechType.kerberos) canonicalized_name.should_be_a(gssnames.Name) bytes(canonicalized_name).should_be(SERVICE_PRINCIPAL + b'@') def test_copy(self): name1 = gssnames.Name(SERVICE_PRINCIPAL) name2 = copy.copy(name1) name1.should_be(name2) # NB(directxman12): we don't test display_name_ext because the krb5 mech # doesn't actually implement it @ktu.gssapi_extension_test('rfc6680', 'RFC 6680') def test_is_mech_name(self): name = gssnames.Name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) name.is_mech_name.should_be_false() canon_name = name.canonicalize(gb.MechType.kerberos) canon_name.is_mech_name.should_be_true() canon_name.mech.should_be_a(gb.OID) canon_name.mech.should_be(gb.MechType.kerberos) @ktu.gssapi_extension_test('rfc6680', 'RFC 6680') def test_export_name_composite_no_attrs(self): name = gssnames.Name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) canon_name = name.canonicalize(gb.MechType.kerberos) exported_name = canon_name.export(composite=True) exported_name.should_be_a(bytes) @ktu.gssapi_extension_test('rfc6680', 'RFC 6680') @ktu.krb_plugin_test('authdata', 'greet_client') def test_export_name_composite_with_attrs(self): name = gssnames.Name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) canon_name = name.canonicalize(gb.MechType.kerberos) canon_name.attributes['urn:greet:greeting'] = b'some val' exported_name = canon_name.export(composite=True) exported_name.should_be_a(bytes) @ktu.gssapi_extension_test('rfc6680', 'RFC 6680') @ktu.krb_plugin_test('authdata', 'greet_client') def test_basic_get_set_del_name_attribute_no_auth(self): name = gssnames.Name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) canon_name = name.canonicalize(gb.MechType.kerberos) canon_name.attributes['urn:greet:greeting'] = (b'some val', True) canon_name.attributes['urn:greet:greeting'].values.should_be( set([b'some val'])) canon_name.attributes['urn:greet:greeting'].complete.should_be_true() (canon_name.attributes['urn:greet:greeting'].authenticated .should_be_false()) del canon_name.attributes['urn:greet:greeting'] # NB(directxman12): for some reason, the greet:greeting handler plugin # doesn't properly delete itself -- it just clears the value # If we try to get its value now, we segfault (due to an issue with # greet:greeting's delete). Instead, just try setting the value again # canon_name.attributes.should_be_empty(), which would normally give # an error. canon_name.attributes['urn:greet:greeting'] = b'some other val' class SecurityContextTestCase(_GSSAPIKerberosTestCase): def setUp(self): super(SecurityContextTestCase, self).setUp() gssctx.SecurityContext.__DEFER_STEP_ERRORS__ = False self.client_name = gssnames.Name(self.USER_PRINC) self.client_creds = gsscreds.Credentials(name=None, usage='initiate') self.target_name = gssnames.Name(TARGET_SERVICE_NAME, gb.NameType.hostbased_service) self.server_name = gssnames.Name(SERVICE_PRINCIPAL) self.server_creds = gsscreds.Credentials(name=self.server_name, usage='accept') def _create_client_ctx(self, **kwargs): return gssctx.SecurityContext(name=self.target_name, **kwargs) # NB(directxman12): we skip testing process_context_token, because there is # no concrete, non-deprecated was to obtain an "async" # token def test_create_from_other(self): raw_client_ctx, raw_server_ctx = self._create_completed_contexts() high_level_ctx = gssctx.SecurityContext(raw_client_ctx) high_level_ctx.target_name.should_be(self.target_name) @exist_perms(lifetime=30, flags=[], mech=gb.MechType.kerberos, channel_bindings=None) def test_create_new_init(self, str_name, kwargs): client_ctx = gssctx.SecurityContext(name=self.target_name, creds=self.client_creds, **kwargs) client_ctx.usage.should_be('initiate') client_ctx = self._create_client_ctx(**kwargs) client_ctx.usage.should_be('initiate') def test_create_new_accept(self): server_ctx = gssctx.SecurityContext(creds=self.server_creds) server_ctx.usage.should_be('accept') def test_init_throws_error_on_invalid_args(self): def create_sec_context(): gssctx.SecurityContext(usage='accept', name=self.target_name) create_sec_context.should_raise(TypeError) def _create_completed_contexts(self): client_ctx = self._create_client_ctx(lifetime=400) client_token = client_ctx.step() client_token.should_be_a(bytes) server_ctx = gssctx.SecurityContext(creds=self.server_creds) server_token = server_ctx.step(client_token) server_token.should_be_a(bytes) client_ctx.step(server_token) return (client_ctx, server_ctx) def test_complete_on_partially_completed(self): client_ctx = self._create_client_ctx() client_tok = client_ctx.step() client_ctx.complete.should_be_false() server_ctx = gssctx.SecurityContext(creds=self.server_creds) server_tok = server_ctx.step(client_tok) client_ctx.step(server_tok) client_ctx.complete.should_be_true() server_ctx.complete.should_be_true() def test_initiate_accept_steps(self): client_ctx, server_ctx = self._create_completed_contexts() # KDC may allow for clockskew by increasing acceptor context lifetime server_ctx.lifetime.should_be_at_most(400 + 300) server_ctx.initiator_name.should_be(client_ctx.initiator_name) server_ctx.mech.should_be_a(gb.OID) server_ctx.actual_flags.should_be_a(gb.IntEnumFlagSet) server_ctx.locally_initiated.should_be_false() server_ctx.complete.should_be_true() client_ctx.lifetime.should_be_at_most(400) client_ctx.target_name.should_be(self.target_name) client_ctx.mech.should_be_a(gb.OID) client_ctx.actual_flags.should_be_a(gb.IntEnumFlagSet) client_ctx.locally_initiated.should_be_true() client_ctx.complete.should_be_true() def test_channel_bindings(self): bdgs = gb.ChannelBindings(application_data=b'abcxyz', initiator_address_type=gb.AddressType.ip, initiator_address=b'127.0.0.1', acceptor_address_type=gb.AddressType.ip, acceptor_address=b'127.0.0.1') client_ctx = self._create_client_ctx(lifetime=400, channel_bindings=bdgs) client_token = client_ctx.step() client_token.should_be_a(bytes) server_ctx = gssctx.SecurityContext(creds=self.server_creds, channel_bindings=bdgs) server_token = server_ctx.step(client_token) server_token.should_be_a(bytes) client_ctx.step(server_token) def test_bad_channel_bindings_raises_error(self): bdgs = gb.ChannelBindings(application_data=b'abcxyz', initiator_address_type=gb.AddressType.ip, initiator_address=b'127.0.0.1', acceptor_address_type=gb.AddressType.ip, acceptor_address=b'127.0.0.1') client_ctx = self._create_client_ctx(lifetime=400, channel_bindings=bdgs) client_token = client_ctx.step() client_token.should_be_a(bytes) bdgs.acceptor_address = b'127.0.1.0' server_ctx = gssctx.SecurityContext(creds=self.server_creds, channel_bindings=bdgs) server_ctx.step.should_raise(gb.BadChannelBindingsError, client_token) def test_export_create_from_token(self): client_ctx, server_ctx = self._create_completed_contexts() token = client_ctx.export() token.should_be_a(bytes) imported_ctx = gssctx.SecurityContext(token=token) imported_ctx.usage.should_be('initiate') imported_ctx.target_name.should_be(self.target_name) def test_pickle_unpickle(self): client_ctx, server_ctx = self._create_completed_contexts() pickled_ctx = pickle.dumps(client_ctx) unpickled_ctx = pickle.loads(pickled_ctx) unpickled_ctx.should_be_a(gssctx.SecurityContext) unpickled_ctx.usage.should_be('initiate') unpickled_ctx.target_name.should_be(self.target_name) def test_encrypt_decrypt(self): client_ctx, server_ctx = self._create_completed_contexts() encrypted_msg = client_ctx.encrypt(b'test message') encrypted_msg.should_be_a(bytes) decrypted_msg = server_ctx.decrypt(encrypted_msg) decrypted_msg.should_be_a(bytes) decrypted_msg.should_be(b'test message') def test_encrypt_decrypt_throws_error_on_no_encryption(self): client_ctx, server_ctx = self._create_completed_contexts() wrap_res = client_ctx.wrap(b'test message', False) wrap_res.should_be_a(gb.WrapResult) wrap_res.encrypted.should_be_false() wrap_res.message.should_be_a(bytes) server_ctx.decrypt.should_raise(excs.EncryptionNotUsed, wrap_res.message) def test_wrap_unwrap(self): client_ctx, server_ctx = self._create_completed_contexts() wrap_res = client_ctx.wrap(b'test message', True) wrap_res.should_be_a(gb.WrapResult) wrap_res.encrypted.should_be_true() wrap_res.message.should_be_a(bytes) unwrap_res = server_ctx.unwrap(wrap_res.message) unwrap_res.should_be_a(gb.UnwrapResult) unwrap_res.message.should_be_a(bytes) unwrap_res.message.should_be(b'test message') unwrap_res.encrypted.should_be_true() def test_get_wrap_size_limit(self): client_ctx, server_ctx = self._create_completed_contexts() with_conf = client_ctx.get_wrap_size_limit(100) without_conf = client_ctx.get_wrap_size_limit(100, encrypted=True) with_conf.should_be_an_integer() without_conf.should_be_an_integer() with_conf.should_be_at_most(100) without_conf.should_be_at_most(100) def test_get_signature(self): client_ctx, server_ctx = self._create_completed_contexts() mic_token = client_ctx.get_signature(b'some message') mic_token.should_be_a(bytes) mic_token.shouldnt_be_empty() def test_verify_signature_raise(self): client_ctx, server_ctx = self._create_completed_contexts() mic_token = client_ctx.get_signature(b'some message') server_ctx.verify_signature(b'some message', mic_token) server_ctx.verify_signature.should_raise(gb.GSSError, b'other message', mic_token) @ktu.krb_minversion_test("1.11", "returning tokens") def test_defer_step_error_on_method(self): gssctx.SecurityContext.__DEFER_STEP_ERRORS__ = True bdgs = gb.ChannelBindings(application_data=b'abcxyz') client_ctx = self._create_client_ctx(lifetime=400, channel_bindings=bdgs) client_token = client_ctx.step() client_token.should_be_a(bytes) bdgs.application_data = b'defuvw' server_ctx = gssctx.SecurityContext(creds=self.server_creds, channel_bindings=bdgs) server_ctx.step(client_token).should_be_a(bytes) server_ctx.encrypt.should_raise(gb.BadChannelBindingsError, b'test') @ktu.krb_minversion_test("1.11", "returning tokens") def test_defer_step_error_on_complete_property_access(self): gssctx.SecurityContext.__DEFER_STEP_ERRORS__ = True bdgs = gb.ChannelBindings(application_data=b'abcxyz') client_ctx = self._create_client_ctx(lifetime=400, channel_bindings=bdgs) client_token = client_ctx.step() client_token.should_be_a(bytes) bdgs.application_data = b'defuvw' server_ctx = gssctx.SecurityContext(creds=self.server_creds, channel_bindings=bdgs) server_ctx.step(client_token).should_be_a(bytes) def check_complete(): return server_ctx.complete check_complete.should_raise(gb.BadChannelBindingsError) python-gssapi-1.6.1/gssapi/tests/__init__.py0000664000372000037200000000000013523321513015771 0ustar python-gssapi-1.6.1/gssapi/mechs.py0000664000372000037200000001341513523321513014205 0ustar import six from gssapi.raw import oids as roids from gssapi._utils import import_gssapi_extension from gssapi.raw import misc as rmisc from gssapi import _utils rfc5587 = import_gssapi_extension('rfc5587') rfc5801 = import_gssapi_extension('rfc5801') class Mechanism(roids.OID): """ A GSSAPI Mechanism This class represents a mechanism and centralizes functions dealing with mechanisms and can be used with any calls. It inherits from the low-level GSSAPI :class:`~gssapi.raw.oids.OID` class, and thus can be used with both low-level and high-level API calls. """ def __new__(cls, cpy=None, elements=None): return super(Mechanism, cls).__new__(cls, cpy, elements) @property def name_types(self): """ Get the set of name types supported by this mechanism. """ return rmisc.inquire_names_for_mech(self) @property def _saslname(self): if rfc5801 is None: raise NotImplementedError("Your GSSAPI implementation does not " "have support for RFC 5801") return rfc5801.inquire_saslname_for_mech(self) @property def _attrs(self): if rfc5587 is None: raise NotImplementedError("Your GSSAPI implementation does not " "have support for RFC 5587") return rfc5587.inquire_attrs_for_mech(self) def __str__(self): if issubclass(str, six.text_type): # Python 3 -- we should return unicode return self._bytes_desc().decode(_utils._get_encoding()) else: return self._bytes_desc() def __unicode__(self): return self._bytes_desc().decode(_utils._get_encoding()) def _bytes_desc(self): base = self.dotted_form if rfc5801 is not None and self._saslname and self._saslname.mech_name: base = self._saslname.mech_name if isinstance(base, six.text_type): base = base.encode(_utils._get_encoding()) return base def __repr__(self): """ Get a name representing the mechanism; always safe to call """ base = "" % self.dotted_form if rfc5801 is not None: base = "" % ( self._saslname.mech_name.decode('UTF-8'), self.dotted_form ) return base @property def sasl_name(self): """ Get the SASL name for the mechanism :requires-ext:`rfc5801` """ return self._saslname.sasl_mech_name.decode('UTF-8') @property def description(self): """ Get the description of the mechanism :requires-ext:`rfc5801` """ return self._saslname.mech_description.decode('UTF-8') @property def known_attrs(self): """ Get the known attributes of the mechanism; returns a set of OIDs ([OID]) :requires-ext:`rfc5587` """ return self._attrs.known_mech_attrs @property def attrs(self): """ Get the attributes of the mechanism; returns a set of OIDs ([OID]) :requires-ext:`rfc5587` """ return self._attrs.mech_attrs @classmethod def all_mechs(cls): """ Get a generator of all mechanisms supported by GSSAPI """ return (cls(mech) for mech in rmisc.indicate_mechs()) @classmethod def from_name(cls, name=None): """ Get a generator of mechanisms that may be able to process the name Args: name (Name): a name to inquire about Returns: [Mechanism]: a set of mechanisms which support this name Raises: GSSError """ return (cls(mech) for mech in rmisc.inquire_mechs_for_name(name)) @classmethod def from_sasl_name(cls, name=None): """ Create a Mechanism from its SASL name Args: name (str): SASL name of the desired mechanism Returns: Mechanism: the desired mechanism Raises: GSSError :requires-ext:`rfc5801` """ if rfc5801 is None: raise NotImplementedError("Your GSSAPI implementation does not " "have support for RFC 5801") if isinstance(name, six.text_type): name = name.encode(_utils._get_encoding()) m = rfc5801.inquire_mech_for_saslname(name) return cls(m) @classmethod def from_attrs(cls, desired_attrs=None, except_attrs=None, critical_attrs=None): """ Get a generator of mechanisms supporting the specified attributes. See RFC 5587's :func:`indicate_mechs_by_attrs` for more information. Args: desired_attrs ([OID]): Desired attributes except_attrs ([OID]): Except attributes critical_attrs ([OID]): Critical attributes Returns: [Mechanism]: A set of mechanisms having the desired features. Raises: GSSError :requires-ext:`rfc5587` """ if isinstance(desired_attrs, roids.OID): desired_attrs = set([desired_attrs]) if isinstance(except_attrs, roids.OID): except_attrs = set([except_attrs]) if isinstance(critical_attrs, roids.OID): critical_attrs = set([critical_attrs]) if rfc5587 is None: raise NotImplementedError("Your GSSAPI implementation does not " "have support for RFC 5587") mechs = rfc5587.indicate_mechs_by_attrs(desired_attrs, except_attrs, critical_attrs) return (cls(mech) for mech in mechs) python-gssapi-1.6.1/gssapi/_utils.py0000664000372000037200000001250313523321513014402 0ustar import sys import types import six import decorator as deco from gssapi.raw.misc import GSSError def import_gssapi_extension(name): """Import a GSSAPI extension module This method imports a GSSAPI extension module based on the name of the extension (not including the 'ext_' prefix). If the extension is not available, the method retuns None. Args: name (str): the name of the extension Returns: module: Either the extension module or None """ try: path = 'gssapi.raw.ext_{0}'.format(name) __import__(path) return sys.modules[path] except ImportError: return None def flag_property(flag): def setter(self, val): if val: self.flags.add(flag) else: self.flags.discard(flag) def getter(self): return flag in self.flags return property(getter, setter) def inquire_property(name, doc=None): """Creates a property based on an inquire result This method creates a property that calls the :python:`_inquire` method, and return the value of the requested information. Args: name (str): the name of the 'inquire' result information Returns: property: the created property """ def inquire_property(self): if not self._started: msg = ("Cannot read {0} from a security context whose " "establishment has not yet been started.") raise AttributeError(msg) return getattr(self._inquire(**{name: True}), name) return property(inquire_property, doc=doc) # use UTF-8 as the default encoding, like Python 3 _ENCODING = 'UTF-8' def _get_encoding(): """Gets the current encoding used for strings. This value is used to encode and decode string values like names. Returns: str: the current encoding """ return _ENCODING def set_encoding(enc): """Sets the current encoding used for strings This value is used to encode and decode string values like names. Args: enc: the encoding to use """ global _ENCODING _ENCODING = enc def _encode_dict(d): """Encodes any relevant strings in a dict""" def enc(x): if isinstance(x, six.text_type): return x.encode(_ENCODING) else: return x return dict((enc(k), enc(v)) for k, v in six.iteritems(d)) # in case of Python 3, just use exception chaining @deco.decorator def catch_and_return_token(func, self, *args, **kwargs): """Optionally defer exceptions and return a token instead When `__DEFER_STEP_ERRORS__` is set on the implementing class or instance, methods wrapped with this wrapper will catch and save their :python:`GSSError` exceptions and instead return the result token attached to the exception. The exception can be later retrived through :python:`_last_err` (and :python:`_last_tb` when Python 2 is in use). """ try: return func(self, *args, **kwargs) except GSSError as e: if e.token is not None and self.__DEFER_STEP_ERRORS__: self._last_err = e # skip the "return func" line above in the traceback if six.PY2: self._last_tb = sys.exc_info()[2].tb_next.tb_next else: self._last_err.__traceback__ = e.__traceback__.tb_next return e.token else: raise @deco.decorator def check_last_err(func, self, *args, **kwargs): """Check and raise deferred errors before running the function This method checks :python:`_last_err` before running the wrapped function. If present and not None, the exception will be raised with its original traceback. """ if self._last_err is not None: try: if six.PY2: six.reraise(type(self._last_err), self._last_err, self._last_tb) else: # NB(directxman12): not using six.reraise in Python 3 leads # to cleaner tracebacks, and raise x is valid # syntax in Python 3 (unlike raise x, y, z) raise self._last_err finally: if six.PY2: del self._last_tb # in case of cycles, break glass self._last_err = None else: return func(self, *args, **kwargs) @deco.decorator def check_last_err(func, self, *args, **kwargs): if self._last_err is not None: try: raise self._last_err finally: self._last_err = None else: return func(self, *args, **kwargs) class CheckLastError(type): """Check for a deferred error on all methods This metaclass applies the :python:`check_last_err` decorator to all methods not prefixed by '_'. Additionally, it enabled `__DEFER_STEP_ERRORS__` by default. """ def __new__(cls, name, parents, attrs): attrs['__DEFER_STEP_ERRORS__'] = True for attr_name in attrs: attr = attrs[attr_name] # wrap only methods if not isinstance(attr, types.FunctionType): continue if attr_name[0] != '_': attrs[attr_name] = check_last_err(attr) return super(CheckLastError, cls).__new__(cls, name, parents, attrs) python-gssapi-1.6.1/gssapi/names.py0000664000372000037200000003145713523321513014217 0ustar import six from gssapi.raw import names as rname from gssapi.raw import NameType from gssapi.raw import named_tuples as tuples from gssapi import _utils if six.PY2: from collections import MutableMapping, Iterable else: from collections.abc import MutableMapping, Iterable rname_rfc6680 = _utils.import_gssapi_extension('rfc6680') rname_rfc6680_comp_oid = _utils.import_gssapi_extension('rfc6680_comp_oid') class Name(rname.Name): """A GSSAPI Name This class represents a GSSAPI name which may be used with and/or returned by other GSSAPI methods. It inherits from the low-level GSSAPI :class:`~gssapi.raw.names.Name` class, and thus may used with both low-level and high-level API methods. This class may be pickled and unpickled, as well as copied. The :func:`str` and :func:`bytes` methods may be used to retrieve the text of the name. Note: Name strings will be automatically converted to and from unicode strings as appropriate. If a method is listed as returning a :class:`str` object, it will return a unicode string. The encoding used will be python-gssapi's current encoding, which defaults to UTF-8. """ __slots__ = ('_attr_obj') def __new__(cls, base=None, name_type=None, token=None, composite=False): if token is not None: if composite: if rname_rfc6680 is None: raise NotImplementedError( "Your GSSAPI implementation does not support RFC 6680 " "(the GSSAPI naming extensions)") if rname_rfc6680_comp_oid is not None: base_name = rname.import_name(token, NameType.composite_export) displ_name = rname.display_name(base_name, name_type=True) if displ_name.name_type == NameType.composite_export: # NB(directxman12): there's a bug in MIT krb5 <= 1.13 # where GSS_C_NT_COMPOSITE_EXPORT doesn't trigger # immediate import logic. However, we can just use # the normal GSS_C_NT_EXPORT_NAME in this case. base_name = rname.import_name(token, NameType.export) else: # NB(directxman12): some older versions of MIT krb5 don't # have support for the GSS_C_NT_COMPOSITE_EXPORT, but do # support composite tokens via GSS_C_NT_EXPORT_NAME. base_name = rname.import_name(token, NameType.export) else: base_name = rname.import_name(token, NameType.export) elif isinstance(base, rname.Name): base_name = base else: if isinstance(base, six.text_type): base = base.encode(_utils._get_encoding()) base_name = rname.import_name(base, name_type) return super(Name, cls).__new__(cls, base_name) def __init__(self, base=None, name_type=None, token=None, composite=False): """ The constructor can be used to "import" a name from a human readable representation, or from a token, and can also be used to convert a low-level :class:`gssapi.raw.names.Name` object into a high-level object. If a :class:`~gssapi.raw.names.Name` object from the low-level API is passed as the `base` argument, it will be converted into a high-level object. If the `token` argument is used, the name will be imported using the token. If the token was exported as a composite token, pass `composite=True`. Otherwise, a new name will be created, using the `base` argument as the human-readable string and the `name_type` argument to denote the name type. Raises: BadNameTypeError BadNameError BadMechanismError """ if rname_rfc6680 is not None: self._attr_obj = _NameAttributeMapping(self) else: self._attr_obj = None def __str__(self): if issubclass(str, six.text_type): # Python 3 -- we should return unicode return bytes(self).decode(_utils._get_encoding()) else: # Python 2 -- we should return a string return self.__bytes__() def __unicode__(self): # Python 2 -- someone asked for unicode return self.__bytes__().decode(_utils._get_encoding()) def __bytes__(self): # Python 3 -- someone asked for bytes return rname.display_name(self, name_type=False).name def display_as(self, name_type): """ Display this name as the given name type. This method attempts to display the current :class:`Name` using the syntax of the given :class:`NameType`, if possible. Warning: In MIT krb5 versions below 1.13.3, this method can segfault if the name was not *originally* created with a `name_type` that was not ``None`` (even in cases when a ``name_type`` is later "added", such as via :meth:`canonicalize`). **Do not use this method unless you are sure the above conditions can never happen in your code.** Warning: In addition to the above warning, current versions of MIT krb5 do not actually fully implement this method, and it may return incorrect results in the case of canonicalized names. :requires-ext:`rfc6680` Args: name_type (OID): the :class:`NameType` to use to display the given name Returns: str: the displayed name Raises: OperationUnavailableError """ if rname_rfc6680 is None: raise NotImplementedError("Your GSSAPI implementation does not " "support RFC 6680 (the GSSAPI naming " "extensions)") return rname_rfc6680.display_name_ext(self, name_type).decode( _utils._get_encoding()) @property def name_type(self): """The :class:`NameType` of this name""" return rname.display_name(self, name_type=True).name_type def __eq__(self, other): if not isinstance(other, rname.Name): # maybe something else can compare this # to other classes, but we certainly can't return NotImplemented else: return rname.compare_name(self, other) def __ne__(self, other): return not self.__eq__(other) def __repr__(self): disp_res = rname.display_name(self, name_type=True) return "Name({name}, {name_type})".format(name=disp_res.name, name_type=disp_res.name_type) def export(self, composite=False): """Export this name as a token. This method exports the name into a byte string which can then be imported by using the `token` argument of the constructor. Args: composite (bool): whether or not use to a composite token -- :requires-ext:`rfc6680` Returns: bytes: the exported name in token form Raises: MechanismNameRequiredError BadNameTypeError BadNameError """ if composite: if rname_rfc6680 is None: raise NotImplementedError("Your GSSAPI implementation does " "not support RFC 6680 (the GSSAPI " "naming extensions)") return rname_rfc6680.export_name_composite(self) else: return rname.export_name(self) def canonicalize(self, mech): """Canonicalize a name with respect to a mechanism. This method returns a new :class:`Name` that is canonicalized according to the given mechanism. Args: mech (OID): the :class:`MechType` to use Returns: Name: the canonicalized name Raises: BadMechanismError BadNameTypeError BadNameError """ return type(self)(rname.canonicalize_name(self, mech)) def __copy__(self): return type(self)(rname.duplicate_name(self)) def __deepcopy__(self, memo): return type(self)(rname.duplicate_name(self)) def _inquire(self, **kwargs): """Inspect this name for information. This method inspects the name for information. If no keyword arguments are passed, all available information is returned. Otherwise, only the keyword arguments that are passed and set to `True` are returned. Args: mech_name (bool): get whether this is a mechanism name, and, if so, the associated mechanism attrs (bool): get the attributes names for this name Returns: InquireNameResult: the results of the inquiry, with unused fields set to None Raises: GSSError """ if rname_rfc6680 is None: raise NotImplementedError("Your GSSAPI implementation does not " "support RFC 6680 (the GSSAPI naming " "extensions)") if not kwargs: default_val = True else: default_val = False attrs = kwargs.get('attrs', default_val) mech_name = kwargs.get('mech_name', default_val) return rname_rfc6680.inquire_name(self, mech_name=mech_name, attrs=attrs) @property def is_mech_name(self): """Whether or not this name is a mechanism name (:requires-ext:`rfc6680`) """ return self._inquire(mech_name=True).is_mech_name @property def mech(self): """The mechanism associated with this name (:requires-ext:`rfc6680`) """ return self._inquire(mech_name=True).mech @property def attributes(self): """The attributes of this name (:requires-ext:`rfc6680`) The attributes are presenting in the form of a :class:`~collections.MutableMapping` (a dict-like object). Retrieved values will always be in the form of :class:`frozensets`. When assigning values, if iterables are used, they be considered to be the set of values for the given attribute. If a non-iterable is used, it will be considered a single value, and automatically wrapped in an iterable. Note: String types (includes :class:`bytes`) are not considered to be iterables in this case. """ if self._attr_obj is None: raise NotImplementedError("Your GSSAPI implementation does not " "support RFC 6680 (the GSSAPI naming " "extensions)") return self._attr_obj class _NameAttributeMapping(MutableMapping): """Provides dict-like access to RFC 6680 Name attributes.""" def __init__(self, name): self._name = name def __getitem__(self, key): if isinstance(key, six.text_type): key = key.encode(_utils._get_encoding()) res = rname_rfc6680.get_name_attribute(self._name, key) return tuples.GetNameAttributeResult(frozenset(res.values), frozenset(res.display_values), res.authenticated, res.complete) def __setitem__(self, key, value): if isinstance(key, six.text_type): key = key.encode(_utils._get_encoding()) rname_rfc6680.delete_name_attribute(self._name, key) if isinstance(value, tuples.GetNameAttributeResult): complete = value.complete value = value.values elif isinstance(value, tuple) and len(value) == 2: complete = value[1] value = value[0] else: complete = False if (isinstance(value, (six.string_types, bytes)) or not isinstance(value, Iterable)): # NB(directxman12): this allows us to easily assign a single # value, since that's a common case value = [value] rname_rfc6680.set_name_attribute(self._name, key, value, complete=complete) def __delitem__(self, key): if isinstance(key, six.text_type): key = key.encode(_utils._get_encoding()) rname_rfc6680.delete_name_attribute(self._name, key) def __iter__(self): return iter(self._name._inquire(attrs=True).attrs) def __len__(self): return len(self._name._inquire(attrs=True).attrs) python-gssapi-1.6.1/tox.ini0000664000372000037200000000073413523321513012561 0ustar # Tox (http://tox.testrun.org/) is a tool for running tests # in multiple virtualenvs. This configuration file will run the # test suite on all supported python versions. To use it, "pip install tox" # and then run "tox" from this directory. [tox] envlist = py27,py33,py34,py35,py36,py37 [testenv] whitelist_externals=bash commands = bash -c "source ./.travis/lib-verify.sh && verify::flake8" python setup.py nosetests [] deps = -r{toxinidir}/test-requirements.txt python-gssapi-1.6.1/.gitignore0000664000372000037200000000022313523321513013227 0ustar .coverage *.egg-info *.egg *~ *.pyc /build/ *.swp *.swo *.so .tox dist gssapi/**/*.c docs/build __dont_use_cython__.txt **/__pycache__ .eggs .venv python-gssapi-1.6.1/.travis.yml0000664000372000037200000001052613523321513013357 0ustar sudo: required # not necessary, but less confusing if defined language: python services: - docker # we do everything in docker for non MacOS, MacOS setup is in .travis/build.sh install: skip before_install: skip stages: - verify - test - name: deploy latest docs if: (branch = master OR branch =~ ^infra/$) AND type = push - name: deploy if: tag is PRESENT script: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo sed -i '1i 127.0.0.1 test.box' /etc/hosts; sudo hostname test.box; source ./.travis/lib-util.sh; util::docker-run $DISTRO ./.travis/build.sh; fi - if [[ "$TRAVIS_OS_NAME" != "linux" ]]; then ./.travis/build.sh; fi jobs: include: - &docker_verify stage: verify env: DISTRO=fedora:latest PYTHON="2" script: - source ./.travis/lib-util.sh - util::docker-run $DISTRO ./.travis/verify.sh - <<: *docker_verify env: DISTRO=fedora:latest PYTHON="3" # need to explictly define each builder for test due to different os types - stage: test env: DISTRO=debian:stable PYTHON="2" - stage: test env: DISTRO=debian:stable PYTHON="3" # 3.4, not 3.5 - stage: test env: DISTRO=debian:stable PYTHON="3" KRB5_VER="heimdal" - stage: test env: DISTRO=centos:7 PYTHON="2" # el7 doesn't do python3 modules - stage: test env: DISTRO=fedora:latest PYTHON="3" - stage: test env: DISTRO=fedora:latest PYTHON="2" - &osx_test stage: test env: PYTHON="2" KRB5_VER="heimdal" PYENV="2.7.14" os: osx osx_image: xcode9.2 language: generic # causes issues with pyenv installer when set to python - <<: *osx_test env: PYTHON="3" KRB5_VER="heimdal" PYENV="3.6.3" - &win_test stage: test env: PYTHON="2" PYENV="2.7.16" EXTRA_BUILDEXT="--compiler=mingw32" os: windows language: sh # Windows not supported yet - <<: *win_test env: PYTHON="3" PYENV="3.6.8" - <<: *win_test env: PYTHON="3" PYENV="3.7.3" - stage: deploy latest docs script: skip env: - DISTRO=fedora:latest - PYTHON="3" - secure: L5SpEj5+no20PWwC9Y/XNhAfmUvYiuykwSMa/YyqvUuBjdizzpZcHr7Ego5nMdM1TniTxj4pSTM+GbM0FHCzNmAINSRh9g/D3hheRqlRBacqR0XwC9ZZRvkKvtzwnLh4vYWiauq4AoDeR5U6tkEcay6LjE57iMQcLjcKYBc+Eos= before_deploy: - source ./.travis/lib-util.sh - util::docker-run $DISTRO ./.travis/before-docs-deploy.sh deploy: - provider: script script: .travis/docs-deploy.sh travis_docs_build/html latest pythongssapi/python-gssapi skip_cleanup: true on: all_branches: true - stage: deploy script: skip env: - DISTRO=fedora:latest - PYTHON="3" - secure: L5SpEj5+no20PWwC9Y/XNhAfmUvYiuykwSMa/YyqvUuBjdizzpZcHr7Ego5nMdM1TniTxj4pSTM+GbM0FHCzNmAINSRh9g/D3hheRqlRBacqR0XwC9ZZRvkKvtzwnLh4vYWiauq4AoDeR5U6tkEcay6LjE57iMQcLjcKYBc+Eos= before_deploy: - source ./.travis/lib-util.sh - util::docker-run $DISTRO ./.travis/before-deploy.sh - ls -alR `pwd` deploy: - provider: releases api_key: secure: fAaSSSjd/nUrIpINBjAT590pGF2nGq3I8ee6aGq6IAFpXoa/9eeN5eyOrE4MYucWCwYcH28c7510n35vuZQQor+UZIDo6l0K5M64/NZE1cZ43zOMjw3yHlrsJG+ohPS7YvjqD8GaFlLhF6ZvWvrPmWeijvs8qAT1eL7QoEG0xBk= file_glob: true file: - tag_build/* skip_cleanup: true on: all_branches: true - provider: pypi user: rharwood password: secure: "hN861mjtLeC8IysypC6Pqzlazq29I+c69XGjbUR53izYQ90cz2F+B2azVTl9Su9NbXzdsGnhWZrjY1jtYMPIZE15xDaC8vs61QijFClqmyuKNRVzCt1w/sj21hyLXnYIrkAo4e3bswPF+hRGNwfb+rVrR/dqUwd1wyjZBBYMcQE=" skip_cleanup: true docs_dir: travis_docs_build/html on: all_branches: true # NB(directxman12): this is a hack. Check ./.travis/before-deploy.sh for an explanation. distributions: "check" - provider: script script: .travis/docs-deploy.sh travis_docs_build/html stable pythongssapi/python-gssapi skip_cleanup: true on: all_branches: true - &win_deploy stage: deploy os: windows script: # This is egregious hacks around Travis - ./.travis/before-deploy-windows-wheels.sh - ./.travis/deploy-win.sh env: - PYTHON="2" - PYENV="2.7.16" - EXTRA_BUILDEXT="--compiler=mingw32" language: sh # Travis doesn't support python here - <<: *win_deploy env: - PYTHON="3" - PYENV="3.6.8" - <<: *win_deploy env: - PYTHON="3" - PYENV="3.7.3" python-gssapi-1.6.1/ISSUE_TEMPLATE0000664000372000037200000000042113523321513013345 0ustar ### What went wrong? ### How do we reproduce? *(Remember to use fenced code blocks and consider placing in a gist if large)* ### Component versions (python-gssapi, Kerberos, OS / distro, etc.) *(Please include MIT/Heimdal/etc. and how you installed python-gssapi)* python-gssapi-1.6.1/setup.cfg0000664000372000037200000000016513523321513013065 0ustar [build_sphinx] source-dir=docs/source build-dir=docs/build all_files=1 [upload_sphinx] upload-dir = docs/build/html python-gssapi-1.6.1/MANIFEST.in0000664000372000037200000000020413523321513012774 0ustar include *.txt recursive-include docs *.txt recursive-include gssapi *.pxd recursive-include gssapi *.c recursive-include gssapi *.h python-gssapi-1.6.1/test-requirements.txt0000664000372000037200000000006513523321513015504 0ustar flake8 nose parameterized shouldbe six Cython k5test python-gssapi-1.6.1/.travis/0000775000372000037200000000000013523321513012630 5ustar python-gssapi-1.6.1/.travis/verify.sh0000664000372000037200000000021013523321513014461 0ustar #!/bin/bash -ex # set up dependencies, etc source ./.travis/lib-setup.sh setup::install source ./.travis/lib-verify.sh verify::flake8 python-gssapi-1.6.1/.travis/build.sh0000775000372000037200000000115013523321513014263 0ustar #!/bin/bash -ex # set up dependencies, etc source ./.travis/lib-setup.sh setup::install # always build in-place so that Sphinx can find the modules python setup.py build_ext --inplace $EXTRA_BUILDEXT BUILD_RES=$? if [ x"$KRB5_VER" = "xheimdal" ]; then # heimdal can't run the tests yet, so just exit exit $BUILD_RES fi if [ "$TRAVIS_OS_NAME" == "windows" ]; then # Windows can't run tests yet, so just exit exit $BUILD_RES fi if [ $BUILD_RES -ne 0 ]; then # if the build failed, don't run the tests exit $BUILD_RES fi python setup.py nosetests --verbosity=3 TEST_RES=$? exit $TEST_RES python-gssapi-1.6.1/.travis/before-docs-deploy.sh0000664000372000037200000000055613523321513016654 0ustar #!/bin/bash -ex source ./.travis/lib-setup.sh source ./.travis/lib-deploy.sh # build again since I can't figure out how to get travis to recognize the old # build in the new container. The other alternative (besides actually solving # the issue) is to run the docs build and tarball generation every time. ./.travis/build.sh setup::activate deploy::build-docs python-gssapi-1.6.1/.travis/lib-deploy.sh0000664000372000037200000000067413523321513015233 0ustar deploy::build-docs() { # the first run is for the docs build, so don't clean up pip install -r docs-requirements.txt # install dependencies so that sphinx doesn't have issues # (this actually just installs the whole package in dev mode) pip install -e . # place in a non-standard location so that they don't get cleaned up python setup.py build_sphinx --build-dir travis_docs_build echo "travis_docs_build" } python-gssapi-1.6.1/.travis/before-deploy-windows-wheels.sh0000664000372000037200000000140713523321513020677 0ustar #!/bin/bash -ex # See before-deploy.sh for anything unexplained source ./.travis/lib-setup.sh source ./.travis/lib-deploy.sh ./.travis/build.sh # Sigh, go find paths again PYPATH="/c/Python${PYENV:0:1}${PYENV:2:1}" export PATH="$PYPATH:$PYPATH/Scripts:/c/Program Files/MIT/Kerberos/bin:$PATH" # build the wheel python -m pip install wheel python setup.py bdist_wheel cd dist # Rename and checksum the wheel if [ x"${TRAVIS_TAG#v[0-9]}" = "x${TRAVIS_TAG}" ]; then PYTHON_GSSAPI_VERSION=${TRAVIS_TAG} else PYTHON_GSSAPI_VERSION=${TRAVIS_TAG#v} fi PKG_NAME_VER=$(ls *.whl | sed "s/gssapi-[^-]*-\(.*\)\.whl/python-gssapi-${PYTHON_GSSAPI_VERSION}-\1/") cp *.whl "${PKG_NAME_VER}.whl" sha512sum --binary ./${PKG_NAME_VER}.whl > ./${PKG_NAME_VER}.sha512sum cd .. python-gssapi-1.6.1/.travis/docs-deploy.sh0000775000372000037200000000302113523321513015405 0ustar #!/bin/bash -ex # NB (very important): BE VERY CAREFUL WITH `set -x` FOR THIS FILE. # The GitHub token is sensitive information, and should never # be displayed on in the clear. source_directory=${1?need []} target_directory=${2?need []} target_repo=${3?need []} target_branch=${4:-gh-pages} desc=$(git describe --tags) scratch_dir=$(mktemp -d) set +x # IMPORTANT echo "cloning https://@github.com/${target_repo}.git#${target_branch} in to ${scratch_dir}/docs..." git clone https://${GITHUB_TOKEN}@github.com/${target_repo}.git ${scratch_dir}/docs -b ${target_branch} set -x mkdir -p ${scratch_dir}/docs/${target_directory} cp -r ${source_directory}/. ${scratch_dir}/docs/${target_directory} echo $desc > ${scratch_dir}/docs/${target_directory}/.from pushd $scratch_dir/docs git config user.email "deploy@travis-ci.org" git config user.name "Deployment Bot (from Travis CI)" if [[ $(git status --porcelain | wc -l) -eq 0 ]]; then echo "no docs changes in the latest commit" exit 0 fi git add ${target_directory} git commit -m "Update ${target_directory} docs in based on ${desc}" set +x # IMPORTANT echo "pushing to https://@github.com/${target_repo}.git#${target_branch}" git push --quiet --force-with-lease origin ${target_branch}:${target_branch} set -x popd rm -rf ${scratch_dir} echo "done!" python-gssapi-1.6.1/.travis/before-deploy.sh0000775000372000037200000000304113523321513015721 0ustar #!/bin/bash -ex source ./.travis/lib-setup.sh source ./.travis/lib-deploy.sh # build again since I can't figure out how to get travis to recognize the old # build in the new container. The other alternative (besides actually solving # the issue) is to run the docs build and tarball generation every time. ./.travis/build.sh setup::activate yum -y install tar git # build the docs deploy::build-docs # NB(directxman12): this is a *terrible* hack, but basically, # dpl (the Travis deployer) uses `twine` instead of `setup.py sdist upload`. # like this: # - python setup.py $PYPI_DISTRIBUTIONS # - twine upload -r pypi dist/* # - [some other stuff] # # so if we set $PYPI_DISTRIBUTIONS to something harmless, like `check`, # and then build the dist ourselves (and save it from the cleanup), # dpl will upload that # build the sdist python setup.py sdist mv dist dist_saved # for the tarball upload # clean up git clean -Xdf # restore the saved "dist" directory mv dist_saved dist # make the dir rm -rf ./tag_build || true mkdir ./tag_build # create and checksum the tarball if [ x"${TRAVIS_TAG#v[0-9]}" = "x${TRAVIS_TAG}" ]; then PYTHON_GSSAPI_VERSION=${TRAVIS_TAG} else PYTHON_GSSAPI_VERSION=${TRAVIS_TAG#v} fi PKG_NAME_VER="python-gssapi-${PYTHON_GSSAPI_VERSION}" tar -czvf ./tag_build/${PKG_NAME_VER}.tar.gz --exclude='dist' --exclude='tag_build' --exclude='.git' --exclude='travis_docs_build' --exclude='.git' --transform "s,^\.,${PKG_NAME_VER}," . sha512sum --binary ./tag_build/${PKG_NAME_VER}.tar.gz > ./tag_build/${PKG_NAME_VER}.sha512sum python-gssapi-1.6.1/.travis/deploy-win.sh0000775000372000037200000000127413523321513015262 0ustar #!/bin/bash -e # Temporary hack while issue DPL issue persists # Manually upload wheels via twine for windows # https://github.com/travis-ci/dpl/issues/1009 success="yes" # Sigh, go find paths again PYPATH="/c/Python${PYENV:0:1}${PYENV:2:1}" export PATH="$PYPATH:$PYPATH/Scripts:/c/Program Files/MIT/Kerberos/bin:$PATH" echo 'Running: python -m pip install twine ...' python -m pip install twine echo 'Running: set +x; python -m twine upload...' # Please note this cannot be set -x or passwords will leak! set +x python -m twine upload -u $TWINE_USER -p $TWINE_PASSWORD dist/gssapi* > out.log 2>&1 || true # and restore... set -x egrep -i 'fail|error' out.log && cat out.log && exit 1 exit 0 python-gssapi-1.6.1/.travis/lib-setup.sh0000664000372000037200000000660613523321513015100 0ustar #!/bin/bash setup::python-suffix() { if [ x"$PYTHON" = "x3" ]; then echo "3" else echo "" fi } # We test Debian's cython. el7's cython is too old, and Rawhide's virtualenv # doesn't work right (usrmerge bugs) so we can only test Debian's cython. setup::debian::install() { local IS3=$(setup::python-suffix) export DEBIAN_FRONTEND=noninteractive apt-get update if [ x"$KRB5_VER" = "xheimdal" ]; then apt-get -y install heimdal-dev else apt-get -y install krb5-{user,kdc,admin-server,multidev} libkrb5-dev \ gss-ntlmssp fi apt-get -y install gcc virtualenv python$IS3-{virtualenv,dev} cython$IS3 virtualenv --system-site-packages -p $(which python${PYTHON}) .venv source ./.venv/bin/activate } setup::rh::yuminst() { # yum has no update-only verb yum -y --nogpgcheck install $@ } setup::centos::install() { local IS3=$(setup::python-suffix) # Cython on el7 is too old - downstream patches setup::rh::yuminst python$IS3-{virtualenv,devel} virtualenv -p $(which python$IS3) .venv source ./.venv/bin/activate pip install --upgrade pip # el7 pip doesn't quite work right pip install --install-option='--no-cython-compile' cython } setup::fedora::install() { # path to binary here in case Rawhide changes it setup::rh::yuminst redhat-rpm-config \ /usr/bin/virtualenv python${PYTHON}-{virtualenv,devel} virtualenv -p $(which python${PYTHON}) .venv source ./.venv/bin/activate pip install --install-option='--no-cython-compile' cython } setup::rh::install() { setup::rh::yuminst krb5-{devel,libs,server,workstation} \ which gcc findutils gssntlmssp if [ -f /etc/fedora-release ]; then setup::fedora::install else setup::centos::install fi } setup::macos::install() { # install Python from pyenv so we know what version is being used pyenv install $PYENV pyenv global $PYENV virtualenv -p $(pyenv which python) .venv source ./.venv/bin/activate pip install --install-option='--no-cython-compile' cython } setup::windows::install() { # Install the right Python version and MIT Kerberos choco install python"${PYENV:0:1}" --version $PYENV choco install mitkerberos --install-arguments "'ADDLOCAL=ALL'" || true PYPATH="/c/Python${PYENV:0:1}${PYENV:2:1}" # Update path to include them export PATH="$PYPATH:$PYPATH/Scripts:/c/Program Files/MIT/Kerberos/bin:$PATH" if [ "${PYENV:0:1}" == "2" ]; then choco install vcredist2008 # Skip dotnet dependency: # https://github.com/fredrikaverpil/vcpython27/pull/3 choco install --ignore-dependencies vcpython27 fi python -m pip install --upgrade pip } setup::install() { if [ -f /etc/debian_version ]; then setup::debian::install elif [ -f /etc/redhat-release ]; then setup::rh::install elif [ "$(uname)" == "Darwin" ]; then setup::macos::install elif [ "$TRAVIS_OS_NAME" == "windows" ]; then setup::windows::install else echo "Distro not found!" false fi pip install -r test-requirements.txt } setup::activate() { # remove (and restore) set -x to avoid log-spam the source # script, which we don't care about wastrace=${-//[^x]/} set +x source .venv/bin/activate if [[ -n "$wastrace" ]]; then set -x; fi } python-gssapi-1.6.1/.travis/lib-verify.sh0000664000372000037200000000137113523321513015236 0ustar #!/bin/bash verify::flake8() { flake8 setup.py F8_SETUP=$? flake8 gssapi F8_PY=$? # Cython requires special flags since it is not proper Python # E225: missing whitespace around operator # E226: missing whitespace around arithmetic operator # E227: missing whitespace around bitwise or shift operator # E402: module level import not at top of file (needed for the `GSSAPI="blah" lines) # E901: SyntaxError or IndentationError # E999: Internal AST compilation error (flake8 specific) flake8 gssapi --filename='*.pyx,*.pxd' --ignore=E225,E226,E227,E402,E901,E999 F8_MAIN_CYTHON=$? if [ $F8_SETUP -eq 0 -a $F8_PY -eq 0 -a $F8_MAIN_CYTHON -eq 0 ]; then return 0 else return 1 fi } python-gssapi-1.6.1/.travis/lib-util.sh0000664000372000037200000000043513523321513014707 0ustar #!/bin/bash util::docker-run() { local distro=$1 shift docker run \ -v `pwd`:/tmp/build \ -w /tmp/build \ -e TRAVIS_TAG=$TRAVIS_TAG \ -e PKG_NAME_VER=$PKG_NAME_VER \ -e KRB5_VER=$KRB5_VER \ -e PYTHON=$PYTHON \ $distro \ /bin/bash -ex $@ }