You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@impala.apache.org by wz...@apache.org on 2022/05/15 22:27:59 UTC
[impala] 03/03: IMPALA-10745 (part 2): Support Kerberos over HTTP for impala-shell
This is an automated email from the ASF dual-hosted git repository.
wzhou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/impala.git
commit b867f4c4f1794b434c90f7bd702b277a570b6303
Author: wzhou-code <wz...@cloudera.com>
AuthorDate: Wed May 11 13:28:24 2022 -0700
IMPALA-10745 (part 2): Support Kerberos over HTTP for impala-shell
This patch adds kerberos-1.3.1 Python module to shell/ext-py so that
the egg file of Kerberos module is built and added into impala-shell
tarball when running script shell/make_shell_tarball.sh.
Kerberos Python module is distributed under Apache License Version 2.
Its source distribution is available at:
https://pypi.org/project/kerberos/
Testing:
- Passed core run.
- Installed impala-shell from impala-shell tarball on dev box as
standalone package. Verified that impala-shell could be ran without
additional configurations.
- Installed impala-shell from impala-shell tarball on a real cluster
with a full Kerberos setup. Verified that impala-shell could
connect to impala server with options "-k --protocol=hs2-http".
Change-Id: Id34074cbe725ba2cf1407fcf59e00475cd417a6d
Reviewed-on: http://gerrit.cloudera.org:8080/18523
Reviewed-by: Impala Public Jenkins <im...@cloudera.com>
Tested-by: Impala Public Jenkins <im...@cloudera.com>
---
bin/rat_exclude_files.txt | 1 +
shell/ext-py/kerberos-1.3.1/MANIFEST.in | 3 +
shell/ext-py/kerberos-1.3.1/PKG-INFO | 138 +++
shell/ext-py/kerberos-1.3.1/README.md | 119 +++
.../kerberos-1.3.1/kerberos.egg-info/PKG-INFO | 138 +++
.../kerberos-1.3.1/kerberos.egg-info/SOURCES.txt | 18 +
.../kerberos.egg-info/dependency_links.txt | 1 +
.../kerberos-1.3.1/kerberos.egg-info/top_level.txt | 1 +
shell/ext-py/kerberos-1.3.1/pysrc/kerberos.py | 461 +++++++++
shell/ext-py/kerberos-1.3.1/setup.cfg | 4 +
shell/ext-py/kerberos-1.3.1/setup.py | 138 +++
shell/ext-py/kerberos-1.3.1/src/base64.c | 133 +++
shell/ext-py/kerberos-1.3.1/src/base64.h | 20 +
shell/ext-py/kerberos-1.3.1/src/kerberos.c | 935 ++++++++++++++++++
shell/ext-py/kerberos-1.3.1/src/kerberosbasic.c | 171 ++++
shell/ext-py/kerberos-1.3.1/src/kerberosbasic.h | 26 +
shell/ext-py/kerberos-1.3.1/src/kerberosgss.c | 1007 ++++++++++++++++++++
shell/ext-py/kerberos-1.3.1/src/kerberosgss.h | 91 ++
shell/ext-py/kerberos-1.3.1/src/kerberospw.c | 172 ++++
shell/ext-py/kerberos-1.3.1/src/kerberospw.h | 26 +
20 files changed, 3603 insertions(+)
diff --git a/bin/rat_exclude_files.txt b/bin/rat_exclude_files.txt
index db2c6017c..44917e1d8 100644
--- a/bin/rat_exclude_files.txt
+++ b/bin/rat_exclude_files.txt
@@ -46,6 +46,7 @@ tests/comparison/leopard/static/css/bootstrap*
tests/comparison/leopard/static/fonts/glyphicons-halflings*
tests/comparison/leopard/static/js/bootstrap*
shell/ext-py/bitarray-2.3.0/*
+shell/ext-py/kerberos-1.3.1/*
shell/ext-py/prettytable-0.7.2/*
shell/ext-py/sasl-0.2.1/*
shell/ext-py/six-1.14.0/*
diff --git a/shell/ext-py/kerberos-1.3.1/MANIFEST.in b/shell/ext-py/kerberos-1.3.1/MANIFEST.in
new file mode 100644
index 000000000..39d92ed76
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/MANIFEST.in
@@ -0,0 +1,3 @@
+include LICENSE README.md
+recursive-include src *.c *.h
+recursive-include pysrc *.py
diff --git a/shell/ext-py/kerberos-1.3.1/PKG-INFO b/shell/ext-py/kerberos-1.3.1/PKG-INFO
new file mode 100644
index 000000000..2d7d27866
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/PKG-INFO
@@ -0,0 +1,138 @@
+Metadata-Version: 2.1
+Name: kerberos
+Version: 1.3.1
+Summary: Kerberos high-level interface
+Home-page: https://github.com/apple/ccs-pykerberos
+Author: Apple Inc.
+Author-email: calendarserver-dev@lists.macosforge.org
+License: Apache License, Version 2.0
+Description: # PyKerberos Package
+
+ This Python package is a high-level wrapper for Kerberos (GSSAPI)
+ operations. The goal is to avoid having to build a module that wraps
+ the entire Kerberos.framework, and instead offer a limited set of
+ functions that do what is needed for client/server Kerberos
+ authentication based on <http://www.ietf.org/rfc/rfc4559.txt>.
+
+ Much of the C-code here is adapted from Apache's mod_auth_kerb-5.0rc7.
+
+
+ ## Build
+
+ In this directory, run:
+
+ ```
+ python setup.py build
+ ```
+
+ ## Testing
+
+ To run the tests in the tests folder, you must have a valid Kerberos setup on
+ the test machine. You can use the script .travis.sh as quick and easy way to
+ setup a Kerberos KDC and Apache web endpoint that can be used for the tests.
+ Otherwise you can also run the following to run a self contained Docker
+ container
+
+ ```
+ docker run \
+ -v $(pwd):/app \
+ -w /app \
+ -e PYENV=2.7.13 \
+ -e KERBEROS_USERNAME=administrator \
+ -e KERBEROS_PASSWORD=Password01 \
+ -e KERBEROS_REALM=example.com \
+ -e KERBEROS_PORT=80 \
+ ubuntu:16.04 \
+ /bin/bash .travis.sh
+ ```
+
+ The docker command needs to be run in the same directory as this library and
+ you can test it with different Python versions by changing the value of the
+ PYENV environment value set in the command.
+
+ Please have a look at testing_notes.md for more information.
+
+
+ ## IMPORTANT
+
+ The checkPassword method provided by this library is meant only for testing purposes as it does
+ not offer any protection against possible KDC spoofing. That method should not be used in any
+ production code.
+
+
+ ## Channel Bindings
+
+ You can use this library to authenticate with Channel Binding support. Channel
+ Bindings are tags that identify the particular data channel being used with the
+ authentication. You can use Channel bindings to offer more proof of a valid
+ identity. Some services like Microsoft's Extended Protection can enforce
+ Channel Binding support on authorisation and you can use this library to meet
+ those requirements.
+
+ More details on Channel Bindings as set through the GSSAPI can be found here
+ <https://docs.oracle.com/cd/E19455-01/806-3814/overview-52/index.html>. Using
+ TLS as a example this is how you would add Channel Binding support to your
+ authentication mechanism. The following code snippet is based on RFC5929
+ <https://tools.ietf.org/html/rfc5929> using the 'tls-server-endpoint-point'
+ type.
+
+ ```
+ import hashlib
+
+ def get_channel_bindings_application_data(socket):
+ # This is a highly simplified example, there are other use cases
+ # where you might need to use different hash types or get a socket
+ # object somehow.
+ server_certificate = socket.getpeercert(True)
+ certificate_hash = hashlib.sha256(server_certificate).hexdigest().upper()
+ certificate_digest = base64.b16decode(certificate_hash)
+ application_data = b'tls-server-end-point:%s' % certificate_digest
+
+ return application_data
+
+ def main():
+ # Code to setup a socket with the server
+ # A lot of code to setup the handshake and start the auth process
+ socket = getsocketsomehow()
+
+ # Connect to the host and start the auth process
+
+ # Build the channel bindings object
+ application_data = get_channel_bindings_application_data(socket)
+ channel_bindings = kerberos.channelBindings(application_data=application_data)
+
+ # More work to get responses from the server
+
+ result, context = kerberos.authGSSClientInit(kerb_spn, gssflags=gssflags, principal=principal)
+
+ # Pass through the channel_bindings object as created in the kerberos.channelBindings method
+ result = kerberos.authGSSClientStep(context, neg_resp_value, channel_bindings=channel_bindings)
+
+ # Repeat as necessary
+ ```
+
+ ## Python APIs
+
+ See kerberos.py.
+
+
+ ## Copyright and License
+
+ Copyright (c) 2006-2021 Apple Inc. All rights reserved.
+
+ This software is licensed under the Apache License, Version 2.0. The
+ Apache License is a well-established open source license, enabling
+ collaborative open source software development.
+
+ See the "LICENSE" file for the full text of the license terms.
+
+Platform: all
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Systems Administration :: Authentication/Directory
+Description-Content-Type: text/markdown
diff --git a/shell/ext-py/kerberos-1.3.1/README.md b/shell/ext-py/kerberos-1.3.1/README.md
new file mode 100644
index 000000000..ce8f3c5cf
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/README.md
@@ -0,0 +1,119 @@
+# PyKerberos Package
+
+This Python package is a high-level wrapper for Kerberos (GSSAPI)
+operations. The goal is to avoid having to build a module that wraps
+the entire Kerberos.framework, and instead offer a limited set of
+functions that do what is needed for client/server Kerberos
+authentication based on <http://www.ietf.org/rfc/rfc4559.txt>.
+
+Much of the C-code here is adapted from Apache's mod_auth_kerb-5.0rc7.
+
+
+## Build
+
+In this directory, run:
+
+```
+python setup.py build
+```
+
+## Testing
+
+To run the tests in the tests folder, you must have a valid Kerberos setup on
+the test machine. You can use the script .travis.sh as quick and easy way to
+setup a Kerberos KDC and Apache web endpoint that can be used for the tests.
+Otherwise you can also run the following to run a self contained Docker
+container
+
+```
+docker run \
+-v $(pwd):/app \
+-w /app \
+-e PYENV=2.7.13 \
+-e KERBEROS_USERNAME=administrator \
+-e KERBEROS_PASSWORD=Password01 \
+-e KERBEROS_REALM=example.com \
+-e KERBEROS_PORT=80 \
+ubuntu:16.04 \
+/bin/bash .travis.sh
+```
+
+The docker command needs to be run in the same directory as this library and
+you can test it with different Python versions by changing the value of the
+PYENV environment value set in the command.
+
+Please have a look at testing_notes.md for more information.
+
+
+## IMPORTANT
+
+The checkPassword method provided by this library is meant only for testing purposes as it does
+not offer any protection against possible KDC spoofing. That method should not be used in any
+production code.
+
+
+## Channel Bindings
+
+You can use this library to authenticate with Channel Binding support. Channel
+Bindings are tags that identify the particular data channel being used with the
+authentication. You can use Channel bindings to offer more proof of a valid
+identity. Some services like Microsoft's Extended Protection can enforce
+Channel Binding support on authorisation and you can use this library to meet
+those requirements.
+
+More details on Channel Bindings as set through the GSSAPI can be found here
+<https://docs.oracle.com/cd/E19455-01/806-3814/overview-52/index.html>. Using
+TLS as a example this is how you would add Channel Binding support to your
+authentication mechanism. The following code snippet is based on RFC5929
+<https://tools.ietf.org/html/rfc5929> using the 'tls-server-endpoint-point'
+type.
+
+```
+import hashlib
+
+def get_channel_bindings_application_data(socket):
+ # This is a highly simplified example, there are other use cases
+ # where you might need to use different hash types or get a socket
+ # object somehow.
+ server_certificate = socket.getpeercert(True)
+ certificate_hash = hashlib.sha256(server_certificate).hexdigest().upper()
+ certificate_digest = base64.b16decode(certificate_hash)
+ application_data = b'tls-server-end-point:%s' % certificate_digest
+
+ return application_data
+
+def main():
+ # Code to setup a socket with the server
+ # A lot of code to setup the handshake and start the auth process
+ socket = getsocketsomehow()
+
+ # Connect to the host and start the auth process
+
+ # Build the channel bindings object
+ application_data = get_channel_bindings_application_data(socket)
+ channel_bindings = kerberos.channelBindings(application_data=application_data)
+
+ # More work to get responses from the server
+
+ result, context = kerberos.authGSSClientInit(kerb_spn, gssflags=gssflags, principal=principal)
+
+ # Pass through the channel_bindings object as created in the kerberos.channelBindings method
+ result = kerberos.authGSSClientStep(context, neg_resp_value, channel_bindings=channel_bindings)
+
+ # Repeat as necessary
+```
+
+## Python APIs
+
+See kerberos.py.
+
+
+## Copyright and License
+
+Copyright (c) 2006-2021 Apple Inc. All rights reserved.
+
+This software is licensed under the Apache License, Version 2.0. The
+Apache License is a well-established open source license, enabling
+collaborative open source software development.
+
+See the "LICENSE" file for the full text of the license terms.
diff --git a/shell/ext-py/kerberos-1.3.1/kerberos.egg-info/PKG-INFO b/shell/ext-py/kerberos-1.3.1/kerberos.egg-info/PKG-INFO
new file mode 100644
index 000000000..2d7d27866
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/kerberos.egg-info/PKG-INFO
@@ -0,0 +1,138 @@
+Metadata-Version: 2.1
+Name: kerberos
+Version: 1.3.1
+Summary: Kerberos high-level interface
+Home-page: https://github.com/apple/ccs-pykerberos
+Author: Apple Inc.
+Author-email: calendarserver-dev@lists.macosforge.org
+License: Apache License, Version 2.0
+Description: # PyKerberos Package
+
+ This Python package is a high-level wrapper for Kerberos (GSSAPI)
+ operations. The goal is to avoid having to build a module that wraps
+ the entire Kerberos.framework, and instead offer a limited set of
+ functions that do what is needed for client/server Kerberos
+ authentication based on <http://www.ietf.org/rfc/rfc4559.txt>.
+
+ Much of the C-code here is adapted from Apache's mod_auth_kerb-5.0rc7.
+
+
+ ## Build
+
+ In this directory, run:
+
+ ```
+ python setup.py build
+ ```
+
+ ## Testing
+
+ To run the tests in the tests folder, you must have a valid Kerberos setup on
+ the test machine. You can use the script .travis.sh as quick and easy way to
+ setup a Kerberos KDC and Apache web endpoint that can be used for the tests.
+ Otherwise you can also run the following to run a self contained Docker
+ container
+
+ ```
+ docker run \
+ -v $(pwd):/app \
+ -w /app \
+ -e PYENV=2.7.13 \
+ -e KERBEROS_USERNAME=administrator \
+ -e KERBEROS_PASSWORD=Password01 \
+ -e KERBEROS_REALM=example.com \
+ -e KERBEROS_PORT=80 \
+ ubuntu:16.04 \
+ /bin/bash .travis.sh
+ ```
+
+ The docker command needs to be run in the same directory as this library and
+ you can test it with different Python versions by changing the value of the
+ PYENV environment value set in the command.
+
+ Please have a look at testing_notes.md for more information.
+
+
+ ## IMPORTANT
+
+ The checkPassword method provided by this library is meant only for testing purposes as it does
+ not offer any protection against possible KDC spoofing. That method should not be used in any
+ production code.
+
+
+ ## Channel Bindings
+
+ You can use this library to authenticate with Channel Binding support. Channel
+ Bindings are tags that identify the particular data channel being used with the
+ authentication. You can use Channel bindings to offer more proof of a valid
+ identity. Some services like Microsoft's Extended Protection can enforce
+ Channel Binding support on authorisation and you can use this library to meet
+ those requirements.
+
+ More details on Channel Bindings as set through the GSSAPI can be found here
+ <https://docs.oracle.com/cd/E19455-01/806-3814/overview-52/index.html>. Using
+ TLS as a example this is how you would add Channel Binding support to your
+ authentication mechanism. The following code snippet is based on RFC5929
+ <https://tools.ietf.org/html/rfc5929> using the 'tls-server-endpoint-point'
+ type.
+
+ ```
+ import hashlib
+
+ def get_channel_bindings_application_data(socket):
+ # This is a highly simplified example, there are other use cases
+ # where you might need to use different hash types or get a socket
+ # object somehow.
+ server_certificate = socket.getpeercert(True)
+ certificate_hash = hashlib.sha256(server_certificate).hexdigest().upper()
+ certificate_digest = base64.b16decode(certificate_hash)
+ application_data = b'tls-server-end-point:%s' % certificate_digest
+
+ return application_data
+
+ def main():
+ # Code to setup a socket with the server
+ # A lot of code to setup the handshake and start the auth process
+ socket = getsocketsomehow()
+
+ # Connect to the host and start the auth process
+
+ # Build the channel bindings object
+ application_data = get_channel_bindings_application_data(socket)
+ channel_bindings = kerberos.channelBindings(application_data=application_data)
+
+ # More work to get responses from the server
+
+ result, context = kerberos.authGSSClientInit(kerb_spn, gssflags=gssflags, principal=principal)
+
+ # Pass through the channel_bindings object as created in the kerberos.channelBindings method
+ result = kerberos.authGSSClientStep(context, neg_resp_value, channel_bindings=channel_bindings)
+
+ # Repeat as necessary
+ ```
+
+ ## Python APIs
+
+ See kerberos.py.
+
+
+ ## Copyright and License
+
+ Copyright (c) 2006-2021 Apple Inc. All rights reserved.
+
+ This software is licensed under the Apache License, Version 2.0. The
+ Apache License is a well-established open source license, enabling
+ collaborative open source software development.
+
+ See the "LICENSE" file for the full text of the license terms.
+
+Platform: all
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Systems Administration :: Authentication/Directory
+Description-Content-Type: text/markdown
diff --git a/shell/ext-py/kerberos-1.3.1/kerberos.egg-info/SOURCES.txt b/shell/ext-py/kerberos-1.3.1/kerberos.egg-info/SOURCES.txt
new file mode 100644
index 000000000..230d66dce
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/kerberos.egg-info/SOURCES.txt
@@ -0,0 +1,18 @@
+MANIFEST.in
+README.md
+setup.cfg
+setup.py
+kerberos.egg-info/PKG-INFO
+kerberos.egg-info/SOURCES.txt
+kerberos.egg-info/dependency_links.txt
+kerberos.egg-info/top_level.txt
+pysrc/kerberos.py
+src/base64.c
+src/base64.h
+src/kerberos.c
+src/kerberosbasic.c
+src/kerberosbasic.h
+src/kerberosgss.c
+src/kerberosgss.h
+src/kerberospw.c
+src/kerberospw.h
\ No newline at end of file
diff --git a/shell/ext-py/kerberos-1.3.1/kerberos.egg-info/dependency_links.txt b/shell/ext-py/kerberos-1.3.1/kerberos.egg-info/dependency_links.txt
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/kerberos.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/shell/ext-py/kerberos-1.3.1/kerberos.egg-info/top_level.txt b/shell/ext-py/kerberos-1.3.1/kerberos.egg-info/top_level.txt
new file mode 100644
index 000000000..4fb686928
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/kerberos.egg-info/top_level.txt
@@ -0,0 +1 @@
+kerberos
diff --git a/shell/ext-py/kerberos-1.3.1/pysrc/kerberos.py b/shell/ext-py/kerberos-1.3.1/pysrc/kerberos.py
new file mode 100644
index 000000000..d4f53a6bc
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/pysrc/kerberos.py
@@ -0,0 +1,461 @@
+##
+# Copyright (c) 2006-2018 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+"""
+PyKerberos Function Description.
+"""
+
+
+
+class KrbError(Exception):
+ pass
+
+
+
+class BasicAuthError(KrbError):
+ pass
+
+
+
+class GSSError(KrbError):
+ pass
+
+
+
+def checkPassword(user, pswd, service, default_realm):
+ """
+ This function provides a simple way to verify that a user name and password
+ match those normally used for Kerberos authentication.
+ It does this by checking that the supplied user name and password can be
+ used to get a ticket for the supplied service.
+ If the user name does not contain a realm, then the default realm supplied
+ is used.
+
+ For this to work properly the Kerberos must be configured properly on this
+ machine.
+ That will likely mean ensuring that the edu.mit.Kerberos preference file
+ has the correct realms and KDCs listed.
+
+ IMPORTANT: This method is vulnerable to KDC spoofing attacks and it should
+ only used for testing. Do not use this in any production system - your
+ security could be compromised if you do.
+
+ @param user: A string containing the Kerberos user name.
+ A realm may be included by appending an C{"@"} followed by the realm
+ string to the actual user id.
+ If no realm is supplied, then the realm set in the default_realm
+ argument will be used.
+
+ @param pswd: A string containing the password for the user.
+
+ @param service: A string containing the Kerberos service to check access
+ for.
+ This will be of the form C{"sss/xx.yy.zz"}, where C{"sss"} is the
+ service identifier (e.g., C{"http"}, C{"krbtgt"}), and C{"xx.yy.zz"} is
+ the hostname of the server.
+
+ @param default_realm: A string containing the default realm to use if one
+ is not supplied in the user argument.
+ Note that Kerberos realms are normally all uppercase (e.g.,
+ C{"EXAMPLE.COM"}).
+
+ @return: True if authentication succeeds, false otherwise.
+ """
+
+
+
+def changePassword(user, oldpswd, newpswd):
+ """
+ This function allows to change the user password on the KDC.
+
+ @param user: A string containing the Kerberos user name.
+ A realm may be included by appending a C{"@"} followed by the realm
+ string to the actual user id.
+ If no realm is supplied, then the realm set in the default_realm
+ argument will be used.
+
+ @param oldpswd: A string containing the old (current) password for the
+ user.
+
+ @param newpswd: A string containing the new password for the user.
+
+ @return: True if password changing succeeds, false otherwise.
+ """
+
+
+
+def getServerPrincipalDetails(service, hostname):
+ """
+ This function returns the service principal for the server given a service
+ type and hostname.
+ Details are looked up via the C{/etc/keytab} file.
+
+ @param service: A string containing the Kerberos service type for the
+ server.
+
+ @param hostname: A string containing the hostname of the server.
+
+ @return: A string containing the service principal.
+ """
+
+
+
+"""
+GSSAPI Function Result Codes:
+
+ -1 : Error
+ 0 : GSSAPI step continuation (only returned by 'Step' function)
+ 1 : GSSAPI step complete, or function return OK
+
+"""
+
+# Some useful result codes
+AUTH_GSS_CONTINUE = 0
+AUTH_GSS_COMPLETE = 1
+
+# Some useful gss flags
+GSS_C_DELEG_FLAG = 1
+GSS_C_MUTUAL_FLAG = 2
+GSS_C_REPLAY_FLAG = 4
+GSS_C_SEQUENCE_FLAG = 8
+GSS_C_CONF_FLAG = 16
+GSS_C_INTEG_FLAG = 32
+GSS_C_ANON_FLAG = 64
+GSS_C_PROT_READY_FLAG = 128
+GSS_C_TRANS_FLAG = 256
+
+
+
+def authGSSClientInit(service, **kwargs):
+ """
+ Initializes a context for GSSAPI client-side authentication with the given
+ service principal.
+ L{authGSSClientClean} must be called after this function returns an OK
+ result to dispose of the context once all GSSAPI operations are complete.
+
+ @param service: A string containing the service principal in the form
+ C{"type@fqdn"}.
+
+ @param principal: Optional string containing the client principal in the
+ form C{"user@realm"}.
+
+ @param gssflags: Optional integer used to set GSS flags.
+ (e.g. C{GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG|GSS_C_SEQUENCE_FLAG} will
+ allow for forwarding credentials to the remote host)
+
+ @param delegated: Optional server context containing delegated credentials
+
+ @param mech_oid: Optional GGS mech OID
+
+ @return: A tuple of (result, context) where result is the result code (see
+ above) and context is an opaque value that will need to be passed to
+ subsequent functions.
+ """
+
+
+
+def authGSSClientClean(context):
+ """
+ Destroys the context for GSSAPI client-side authentication. This function
+ is provided for compatibility with earlier versions of PyKerberos but does
+ nothing. The context object destroys itself when it is reclaimed.
+
+ @param context: The context object returned from L{authGSSClientInit}.
+
+ @return: A result code (see above).
+ """
+
+
+
+def authGSSClientInquireCred(context):
+ """
+ Get the current user name, if any, without a client-side GSSAPI step.
+ If the principal has already been authenticated via completed client-side
+ GSSAPI steps then the user name of the authenticated principal is kept. The
+ user name will be available via authGSSClientUserName.
+
+ @param context: The context object returned from L{authGSSClientInit}.
+
+ @return: A result code (see above).
+ """
+
+
+
+"""
+Address Types for Channel Bindings
+https://docs.oracle.com/cd/E19455-01/806-3814/6jcugr7dp/index.html#reference-9
+
+"""
+
+GSS_C_AF_UNSPEC = 0
+GSS_C_AF_LOCAL = 1
+GSS_C_AF_INET = 2
+GSS_C_AF_IMPLINK = 3
+GSS_C_AF_PUP = 4
+GSS_C_AF_CHAOS = 5
+GSS_C_AF_NS = 6
+GSS_C_AF_NBS = 7
+GSS_C_AF_ECMA = 8
+GSS_C_AF_DATAKIT = 9
+GSS_C_AF_CCITT = 10
+GSS_C_AF_SNA = 11
+GSS_C_AF_DECnet = 12
+GSS_C_AF_DLI = 13
+GSS_C_AF_LAT = 14
+GSS_C_AF_HYLINK = 15
+GSS_C_AF_APPLETALK = 16
+GSS_C_AF_BSC = 17
+GSS_C_AF_DSS = 18
+GSS_C_AF_OSI = 19
+GSS_C_AF_X25 = 21
+GSS_C_AF_NULLADDR = 255
+
+
+
+def channelBindings(**kwargs):
+ """
+ Builds a gss_channel_bindings_struct which can be used to pass onto
+ L{authGSSClientStep} to bind onto the auth. Details on Channel Bindings
+ can be foud at https://tools.ietf.org/html/rfc5929. More details on the
+ struct can be found at
+ https://docs.oracle.com/cd/E19455-01/806-3814/overview-52/index.html
+
+ @param initiator_addrtype: Optional integer used to set the
+ initiator_addrtype, defaults to GSS_C_AF_UNSPEC if not set
+
+ @param initiator_address: Optional byte string containing the
+ initiator_address
+
+ @param acceptor_addrtype: Optional integer used to set the
+ acceptor_addrtype, defaults to GSS_C_AF_UNSPEC if not set
+
+ @param acceptor_address: Optional byte string containing the
+ acceptor_address
+
+ @param application_data: Optional byte string containing the
+ application_data. An example would be 'tls-server-end-point:{cert-hash}'
+ where {cert-hash} is the hash of the server's certificate
+
+ @return: A tuple of (result, gss_channel_bindings_struct) where result is
+ the result code and gss_channel_bindings_struct is the channel bindings
+ structure that can be passed onto L{authGSSClientStep}
+ """
+
+
+
+def authGSSClientStep(context, challenge, **kwargs):
+ """
+ Processes a single GSSAPI client-side step using the supplied server data.
+
+ @param context: The context object returned from L{authGSSClientInit}.
+
+ @param challenge: A string containing the base64-encoded server data (which
+ may be empty for the first step).
+
+ @param channel_bindings: Optional channel bindings to bind onto the auth
+ request. This struct can be built using :{channelBindings}
+ and if not specified it will pass along GSS_C_NO_CHANNEL_BINDINGS as
+ a default.
+
+ @return: A result code (see above).
+ """
+
+
+
+def authGSSClientResponse(context):
+ """
+ Get the client response from the last successful GSSAPI client-side step.
+
+ @param context: The context object returned from L{authGSSClientInit}.
+
+ @return: A string containing the base64-encoded client data to be sent to
+ the server.
+ """
+
+
+
+def authGSSClientResponseConf(context):
+ """
+ Determine whether confidentiality was enabled in the previously unwrapped
+ buffer.
+
+ @param context: The context object returned from L{authGSSClientInit}.
+
+ @return: C{1} if confidentiality was enabled in the previously unwrapped
+ buffer, C{0} otherwise.
+ """
+
+
+
+def authGSSClientUserName(context):
+ """
+ Get the user name of the principal authenticated via the now complete
+ GSSAPI client-side operations, or the current user name obtained via
+ authGSSClientInquireCred. This method must only be called after
+ authGSSClientStep or authGSSClientInquireCred return a complete response
+ code.
+
+ @param context: The context object returned from L{authGSSClientInit}.
+
+ @return: A string containing the user name.
+ """
+
+
+
+def authGSSClientUnwrap(context, challenge):
+ """
+ Perform the client side GSSAPI unwrap step.
+
+ @param challenge: A string containing the base64-encoded server data.
+
+ @return: A result code (see above)
+ """
+
+
+
+def authGSSClientWrap(context, data, user=None, protect=0):
+ """
+ Perform the client side GSSAPI wrap step.
+
+ @param data: The result of the L{authGSSClientResponse} after the
+ L{authGSSClientUnwrap}.
+
+ @param user: The user to authorize.
+
+ @param protect: If C{0}, then just provide integrity protection.
+ If C{1}, then provide confidentiality as well.
+
+ @return: A result code (see above)
+ """
+
+
+
+def authGSSServerInit(service):
+ """
+ Initializes a context for GSSAPI server-side authentication with the given
+ service principal.
+ authGSSServerClean must be called after this function returns an OK result
+ to dispose of the context once all GSSAPI operations are complete.
+
+ @param service: A string containing the service principal in the form
+ C{"type@fqdn"}. To initialize the context for the purpose of accepting
+ delegated credentials, pass the literal string C{"DELEGATE"}.
+
+ @return: A tuple of (result, context) where result is the result code (see
+ above) and context is an opaque value that will need to be passed to
+ subsequent functions.
+ """
+
+
+
+def authGSSServerClean(context):
+ """
+ Destroys the context for GSSAPI server-side authentication. This function
+ is provided for compatibility with earlier versions of PyKerberos but does
+ nothing. The context object destroys itself when it is reclaimed.
+
+ @param context: The context object returned from L{authGSSClientInit}.
+
+ @return: A result code (see above).
+ """
+
+
+
+def authGSSServerStep(context, challenge):
+ """
+ Processes a single GSSAPI server-side step using the supplied client data.
+
+ @param context: The context object returned from L{authGSSClientInit}.
+
+ @param challenge: A string containing the base64-encoded client data.
+
+ @return: A result code (see above).
+ """
+
+
+
+def authGSSServerResponse(context):
+ """
+ Get the server response from the last successful GSSAPI server-side step.
+
+ @param context: The context object returned from L{authGSSClientInit}.
+
+ @return: A string containing the base64-encoded server data to be sent to
+ the client.
+ """
+
+
+
+def authGSSServerHasDelegated(context):
+ """
+ Checks whether a server context has delegated credentials.
+
+ @param context: The context object returned from L{authGSSClientInit}.
+
+ @return: A bool saying whether delegated credentials are available.
+ """
+
+
+
+def authGSSServerUserName(context):
+ """
+ Get the user name of the principal trying to authenticate to the server.
+ This method must only be called after L{authGSSServerStep} returns a
+ complete or continue response code.
+
+ @param context: The context object returned from L{authGSSClientInit}.
+
+ @return: A string containing the user name.
+ """
+
+
+
+def authGSSServerTargetName(context):
+ """
+ Get the target name if the server did not supply its own credentials.
+ This method must only be called after L{authGSSServerStep} returns a
+ complete or continue response code.
+
+ @param context: The context object returned from L{authGSSClientInit}.
+
+ @return: A string containing the target name.
+ """
+
+
+
+def authGSSServerStoreDelegate(context):
+ """
+ Save the ticket sent to the server in the file C{/tmp/krb5_pyserv_XXXXXX}.
+ This method must only be called after L{authGSSServerStep} returns a
+ complete or continue response code.
+
+ @param context: The context object returned from L{authGSSClientInit}.
+
+ @return: A result code (see above).
+ """
+
+
+
+def authGSSServerCacheName(context):
+ """
+ Get the name of the credential cache created with
+ L{authGSSServerStoreDelegate}.
+ This method must only be called after L{authGSSServerStoreDelegate}.
+
+ @param context: The context object returned from L{authGSSClientInit}.
+
+ @return: A string containing the cache name.
+ """
diff --git a/shell/ext-py/kerberos-1.3.1/setup.cfg b/shell/ext-py/kerberos-1.3.1/setup.cfg
new file mode 100644
index 000000000..8bfd5a12f
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/setup.cfg
@@ -0,0 +1,4 @@
+[egg_info]
+tag_build =
+tag_date = 0
+
diff --git a/shell/ext-py/kerberos-1.3.1/setup.py b/shell/ext-py/kerberos-1.3.1/setup.py
new file mode 100644
index 000000000..8bc0c4f6a
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/setup.py
@@ -0,0 +1,138 @@
+##
+# Copyright (c) 2006-2018 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from os.path import dirname, join as joinpath
+from setuptools import setup, Extension
+from io import open
+
+try:
+ from subprocess import getoutput
+except ImportError:
+ from commands import getoutput
+
+
+#
+# Options
+#
+
+project_name = "kerberos"
+
+version_string = "1.3.1"
+
+description = "Kerberos high-level interface"
+
+with open("README.md", "r", encoding="utf-8") as fh:
+ long_description = fh.read()
+
+long_description_content_type = "text/markdown"
+
+url = "https://github.com/apple/ccs-pykerberos"
+
+classifiers = [
+ "Development Status :: 5 - Production/Stable",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: Apache Software License",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python :: 2",
+ "Programming Language :: Python :: 3",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ "Topic :: System :: Systems Administration :: Authentication/Directory",
+]
+
+author = "Apple Inc."
+
+author_email = "calendarserver-dev@lists.macosforge.org"
+
+license = "Apache License, Version 2.0"
+
+platforms = ["all"]
+
+
+#
+# Entry points
+#
+
+entry_points = {
+ "console_scripts": [],
+}
+
+
+#
+# Dependencies
+#
+
+setup_requirements = []
+
+install_requirements = []
+
+extras_requirements = {}
+
+extra_link_args = getoutput("krb5-config --libs gssapi").split()
+
+extra_compile_args = getoutput("krb5-config --cflags gssapi").split()
+
+
+#
+# Set up Extension modules that need to be built
+#
+
+extensions = [
+ Extension(
+ "kerberos",
+ extra_link_args=extra_link_args,
+ extra_compile_args=extra_compile_args,
+ sources=[
+ "src/base64.c",
+ "src/kerberos.c",
+ "src/kerberosbasic.c",
+ "src/kerberosgss.c",
+ "src/kerberospw.c",
+ ],
+ ),
+]
+
+
+#
+# Run setup
+#
+
+
+def doSetup():
+ setup(
+ name=project_name,
+ version=version_string,
+ description=description,
+ long_description=long_description,
+ long_description_content_type=long_description_content_type,
+ url=url,
+ classifiers=classifiers,
+ author=author,
+ author_email=author_email,
+ license=license,
+ platforms=platforms,
+ ext_modules=extensions,
+ setup_requires=setup_requirements,
+ install_requires=install_requirements,
+ extras_require=extras_requirements,
+ )
+
+
+#
+# Main
+#
+
+if __name__ == "__main__":
+ doSetup()
diff --git a/shell/ext-py/kerberos-1.3.1/src/base64.c b/shell/ext-py/kerberos-1.3.1/src/base64.c
new file mode 100644
index 000000000..4496f92e2
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/src/base64.c
@@ -0,0 +1,133 @@
+/**
+ * Copyright (c) 2006-2018 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+#include "base64.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// base64 tables
+static char basis_64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static signed char index_64[128] =
+{
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+ 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
+ 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+ -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+ 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+};
+#define CHAR64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
+
+// base64_encode : base64 encode
+//
+// value : data to encode
+// vlen : length of data
+// (result) : new char[] - c-str of result
+char *base64_encode(const unsigned char *value, size_t vlen)
+{
+ char *result = (char *)malloc((vlen * 4) / 3 + 5);
+ if (result == NULL)
+ {
+ return NULL;
+ }
+ char *out = result;
+ while (vlen >= 3)
+ {
+ *out++ = basis_64[value[0] >> 2];
+ *out++ = basis_64[((value[0] << 4) & 0x30) | (value[1] >> 4)];
+ *out++ = basis_64[((value[1] << 2) & 0x3C) | (value[2] >> 6)];
+ *out++ = basis_64[value[2] & 0x3F];
+ value += 3;
+ vlen -= 3;
+ }
+ if (vlen > 0)
+ {
+ *out++ = basis_64[value[0] >> 2];
+ unsigned char oval = (value[0] << 4) & 0x30;
+ if (vlen > 1) oval |= value[1] >> 4;
+ *out++ = basis_64[oval];
+ *out++ = (vlen < 2) ? '=' : basis_64[(value[1] << 2) & 0x3C];
+ *out++ = '=';
+ }
+ *out = '\0';
+
+ return result;
+}
+
+// base64_decode : base64 decode
+//
+// value : c-str to decode
+// rlen : length of decoded result
+// (result) : new unsigned char[] - decoded result
+unsigned char *base64_decode(const char *value, size_t *rlen)
+{
+ *rlen = 0;
+ int c1, c2, c3, c4;
+
+ size_t vlen = strlen(value);
+ unsigned char *result =(unsigned char *)malloc((vlen * 3) / 4 + 1);
+ if (result == NULL)
+ {
+ return NULL;
+ }
+ unsigned char *out = result;
+
+ while (1) {
+ if (value[0]==0) {
+ return result;
+ }
+ c1 = value[0];
+ if (CHAR64(c1) == -1) {
+ goto base64_decode_error;;
+ }
+ c2 = value[1];
+ if (CHAR64(c2) == -1) {
+ goto base64_decode_error;;
+ }
+ c3 = value[2];
+ if ((c3 != '=') && (CHAR64(c3) == -1)) {
+ goto base64_decode_error;;
+ }
+ c4 = value[3];
+ if ((c4 != '=') && (CHAR64(c4) == -1)) {
+ goto base64_decode_error;;
+ }
+
+ value += 4;
+ *out++ = (CHAR64(c1) << 2) | (CHAR64(c2) >> 4);
+ *rlen += 1;
+
+ if (c3 != '=') {
+ *out++ = ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2);
+ *rlen += 1;
+
+ if (c4 != '=') {
+ *out++ = ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4);
+ *rlen += 1;
+ }
+ }
+ }
+
+base64_decode_error:
+ *result = 0;
+ *rlen = 0;
+
+ return result;
+}
diff --git a/shell/ext-py/kerberos-1.3.1/src/base64.h b/shell/ext-py/kerberos-1.3.1/src/base64.h
new file mode 100644
index 000000000..4ddff3e33
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/src/base64.h
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2006-2018 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+#include <stddef.h>
+
+char *base64_encode(const unsigned char *value, size_t vlen);
+unsigned char *base64_decode(const char *value, size_t *rlen);
diff --git a/shell/ext-py/kerberos-1.3.1/src/kerberos.c b/shell/ext-py/kerberos-1.3.1/src/kerberos.c
new file mode 100644
index 000000000..1e889dfd5
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/src/kerberos.c
@@ -0,0 +1,935 @@
+/**
+ * Copyright (c) 2006-2018 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+#include <Python.h>
+
+#include "kerberosbasic.h"
+#include "kerberospw.h"
+#include "kerberosgss.h"
+
+
+/*
+ * Support the Python 3 API while maintaining backward compatibility for the
+ * Python 2 API.
+ * Thanks to Lennart Regebro for http://python3porting.com/cextensions.html
+ */
+// Handle basic API changes
+#if PY_MAJOR_VERSION >= 3
+ // Basic renames (function parameters are the same)
+ // No more int objects
+ #define PyInt_FromLong PyLong_FromLong
+#endif
+
+#if PY_VERSION_HEX >= 0x03020000
+ // CObjects to Capsules
+ #define PyCObject_Check PyCapsule_CheckExact
+ #define PyCObject_SetVoidPtr PyCapsule_SetPointer
+
+ // More complex macros (function parameters are not the same)
+ // Note for PyCObject_FromVoidPtr, destr is now the third parameter
+ #define PyCObject_FromVoidPtr(cobj, destr) PyCapsule_New(cobj, NULL, destr)
+ #define PyCObject_AsVoidPtr(pobj) PyCapsule_GetPointer(pobj, NULL)
+#endif
+// Handle differences in module definition syntax and interface
+#if PY_MAJOR_VERSION >= 3
+ #define MOD_ERROR_VAL NULL
+ #define MOD_SUCCESS_VAL(val) val
+ #define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
+ #define MOD_DEF(ob, name, doc, methods) \
+ static struct PyModuleDef moduledef = { \
+ PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \
+ ob = PyModule_Create(&moduledef);
+#else
+ #define MOD_ERROR_VAL
+ #define MOD_SUCCESS_VAL(val)
+ #define MOD_INIT(name) void init##name(void)
+ #define MOD_DEF(ob, name, doc, methods) \
+ ob = Py_InitModule3(name, methods, doc);
+#endif
+
+typedef union { char b[16]; uint64_t ull[2]; } align16;
+typedef union { char b[8]; uint64_t ull; } align8;
+
+static align16 krb5_mech_oid_bytes = { { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02 } };
+gss_OID_desc krb5_mech_oid = { 9, NULL };
+
+static align8 spnego_mech_oid_bytes = { { 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02 } };
+gss_OID_desc spnego_mech_oid = { 6, NULL };
+
+PyObject *KrbException_class;
+PyObject *BasicAuthException_class;
+PyObject *PwdChangeException_class;
+PyObject *GssException_class;
+
+static PyObject *checkPassword(PyObject *self, PyObject *args)
+{
+ const char *user = NULL;
+ const char *pswd = NULL;
+ const char *service = NULL;
+ const char *default_realm = NULL;
+ int result = 0;
+
+ if (! PyArg_ParseTuple(args, "ssss", &user, &pswd, &service, &default_realm)) {
+ return NULL;
+ }
+
+ result = authenticate_user_krb5pwd(user, pswd, service, default_realm);
+
+ if (result) {
+ return Py_INCREF(Py_True), Py_True;
+ } else {
+ return NULL;
+ }
+}
+
+static PyObject *changePassword(PyObject *self, PyObject *args)
+{
+ const char *newpswd = NULL;
+ const char *oldpswd = NULL;
+ const char *user = NULL;
+ int result = 0;
+
+ if (! PyArg_ParseTuple(args, "sss", &user, &oldpswd, &newpswd)) {
+ return NULL;
+ }
+
+ result = change_user_krb5pwd(user, oldpswd, newpswd);
+
+ if (result) {
+ return Py_INCREF(Py_True), Py_True;
+ } else {
+ return NULL;
+ }
+}
+
+static PyObject *getServerPrincipalDetails(PyObject *self, PyObject *args)
+{
+ const char *service = NULL;
+ const char *hostname = NULL;
+ char* result = NULL;
+
+ if (! PyArg_ParseTuple(args, "ss", &service, &hostname)) {
+ return NULL;
+ }
+
+ result = server_principal_details(service, hostname);
+
+ if (result != NULL) {
+ PyObject* pyresult = Py_BuildValue("s", result);
+ free(result);
+ return pyresult;
+ } else {
+ return NULL;
+ }
+}
+
+static void
+#if PY_VERSION_HEX >= 0x03020000
+destroy_gss_client(PyObject *obj) {
+ gss_client_state *state = PyCapsule_GetPointer(obj, NULL);
+#else
+destroy_gss_client(void *obj) {
+ gss_client_state *state = (gss_client_state *)obj;
+#endif
+ if (state) {
+ authenticate_gss_client_clean(state);
+ free(state);
+ }
+}
+
+static PyObject* authGSSClientInit(PyObject* self, PyObject* args, PyObject* keywds)
+{
+ const char *service = NULL;
+ const char *principal = NULL;
+ gss_client_state *state = NULL;
+ PyObject *pystate = NULL;
+ gss_server_state *delegatestate = NULL;
+ PyObject *pydelegatestate = NULL;
+ gss_OID mech_oid = GSS_C_NO_OID;
+ PyObject *pymech_oid = NULL;
+ static char *kwlist[] = {
+ "service", "principal", "gssflags", "delegated", "mech_oid", NULL
+ };
+ long int gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
+ int result = 0;
+
+ if (! PyArg_ParseTupleAndKeywords(
+ args, keywds, "s|zlOO", kwlist,
+ &service, &principal, &gss_flags, &pydelegatestate, &pymech_oid
+ )) {
+ return NULL;
+ }
+
+ state = (gss_client_state *) malloc(sizeof(gss_client_state));
+ if (state == NULL)
+ {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ pystate = PyCObject_FromVoidPtr(state, &destroy_gss_client);
+ if (pystate == NULL) {
+ free(state);
+ return NULL;
+ }
+
+ if (pydelegatestate != NULL && PyCObject_Check(pydelegatestate)) {
+ delegatestate = (gss_server_state*)PyCObject_AsVoidPtr(pydelegatestate);
+ }
+
+ if (pymech_oid != NULL && PyCObject_Check(pymech_oid)) {
+ mech_oid = (gss_OID)PyCObject_AsVoidPtr(pymech_oid);
+ }
+
+ result = authenticate_gss_client_init(
+ service, principal, gss_flags, delegatestate, mech_oid, state
+ );
+
+ if (result == AUTH_GSS_ERROR) {
+ Py_DECREF(pystate);
+ return NULL;
+ }
+
+ return Py_BuildValue("(iN)", result, pystate);
+}
+
+static PyObject *authGSSClientClean(PyObject *self, PyObject *args)
+{
+ return Py_BuildValue("i", AUTH_GSS_COMPLETE);
+}
+
+#if PY_VERSION_HEX >= 0x03020000
+void destruct_channel_bindings(PyObject* o) {
+ struct gss_channel_bindings_struct *channel_bindings = PyCapsule_GetPointer(o, NULL);
+#else
+void destruct_channel_bindings(void* o) {
+ struct gss_channel_bindings_struct *channel_bindings = (struct gss_channel_bindings_struct *)o;
+#endif
+
+ if (channel_bindings != NULL) {
+ if (channel_bindings->initiator_address.value != NULL) {
+ PyMem_Free(channel_bindings->initiator_address.value);
+ }
+
+ if (channel_bindings->acceptor_address.value != NULL) {
+ PyMem_Free(channel_bindings->acceptor_address.value);
+ }
+
+ if (channel_bindings->application_data.value != NULL) {
+ PyMem_Free(channel_bindings->application_data.value);
+ }
+
+ free(channel_bindings);
+ }
+}
+
+static PyObject *channelBindings(PyObject *self, PyObject *args, PyObject* keywds)
+{
+ int initiator_addrtype = GSS_C_AF_UNSPEC;
+ int acceptor_addrtype = GSS_C_AF_UNSPEC;
+
+ const char *encoding = NULL;
+ char *initiator_address = NULL;
+ char *acceptor_address = NULL;
+ char *application_data = NULL;
+ int initiator_length = 0;
+ int acceptor_length = 0;
+ int application_length = 0;
+
+ PyObject *pychan_bindings = NULL;
+ struct gss_channel_bindings_struct *input_chan_bindings;
+ static char *kwlist[] = {"initiator_addrtype", "initiator_address", "acceptor_addrtype",
+ "acceptor_address", "application_data", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iet#iet#et#", kwlist,
+ &initiator_addrtype, &encoding, &initiator_address, &initiator_length,
+ &acceptor_addrtype, &encoding, &acceptor_address, &acceptor_length,
+ &encoding, &application_data, &application_length)) {
+ return NULL;
+ }
+
+ input_chan_bindings = (struct gss_channel_bindings_struct *) malloc(sizeof(struct gss_channel_bindings_struct));
+ pychan_bindings = PyCObject_FromVoidPtr(input_chan_bindings, &destruct_channel_bindings);
+
+ input_chan_bindings->initiator_addrtype = initiator_addrtype;
+ input_chan_bindings->initiator_address.length = initiator_length;
+ input_chan_bindings->initiator_address.value = initiator_address;
+
+ input_chan_bindings->acceptor_addrtype = acceptor_addrtype;
+ input_chan_bindings->acceptor_address.length = acceptor_length;
+ input_chan_bindings->acceptor_address.value = acceptor_address;
+
+ input_chan_bindings->application_data.length = application_length;
+ input_chan_bindings->application_data.value = application_data;
+
+ return Py_BuildValue("N", pychan_bindings);
+}
+
+static PyObject *authGSSClientStep(PyObject *self, PyObject *args, PyObject* keywds)
+{
+ gss_client_state *state = NULL;
+ PyObject *pystate = NULL;
+ char *challenge = NULL;
+ PyObject *pychan_bindings = NULL;
+ struct gss_channel_bindings_struct *channel_bindings;
+ static char *kwlist[] = {"state", "challenge", "channel_bindings", NULL};
+ int result = 0;
+
+ if (! PyArg_ParseTupleAndKeywords(args, keywds, "Os|O", kwlist, &pystate, &challenge, &pychan_bindings)) {
+ return NULL;
+ }
+
+ if (! PyCObject_Check(pystate)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a context object");
+ return NULL;
+ }
+
+ state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+
+ if (state == NULL) {
+ return NULL;
+ }
+
+ if (pychan_bindings == NULL) {
+ channel_bindings = GSS_C_NO_CHANNEL_BINDINGS;
+ } else {
+ if (!PyCObject_Check(pychan_bindings)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a gss_channel_bindings_struct object");
+ return NULL;
+ }
+ channel_bindings = (struct gss_channel_bindings_struct *)PyCObject_AsVoidPtr(pychan_bindings);
+ }
+
+ result = authenticate_gss_client_step(state, challenge, channel_bindings);
+
+ if (result == AUTH_GSS_ERROR) {
+ return NULL;
+ }
+
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *authGSSClientResponseConf(PyObject *self, PyObject *args)
+{
+ gss_client_state *state = NULL;
+ PyObject *pystate = NULL;
+
+ if (! PyArg_ParseTuple(args, "O", &pystate)) {
+ return NULL;
+ }
+
+ if (! PyCObject_Check(pystate)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a context object");
+ return NULL;
+ }
+
+ state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+
+ if (state == NULL) {
+ return NULL;
+ }
+
+ return Py_BuildValue("i", state->responseConf);
+}
+
+static PyObject *authGSSServerHasDelegated(PyObject *self, PyObject *args)
+{
+ gss_server_state *state = NULL;
+ PyObject *pystate = NULL;
+
+ if (! PyArg_ParseTuple(args, "O", &pystate)) {
+ return NULL;
+ }
+
+ if (! PyCObject_Check(pystate)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a context object");
+ return NULL;
+ }
+
+ state = (gss_server_state *)PyCObject_AsVoidPtr(pystate);
+
+ if (state == NULL) {
+ return NULL;
+ }
+
+ return PyBool_FromLong(authenticate_gss_server_has_delegated(state));
+}
+
+static PyObject *authGSSClientResponse(PyObject *self, PyObject *args)
+{
+ gss_client_state *state = NULL;
+ PyObject *pystate = NULL;
+
+ if (! PyArg_ParseTuple(args, "O", &pystate)) {
+ return NULL;
+ }
+
+ if (! PyCObject_Check(pystate)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a context object");
+ return NULL;
+ }
+
+ state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+
+ if (state == NULL) {
+ return NULL;
+ }
+
+ return Py_BuildValue("s", state->response);
+}
+
+static PyObject *authGSSClientUserName(PyObject *self, PyObject *args)
+{
+ gss_client_state *state = NULL;
+ PyObject *pystate = NULL;
+
+ if (! PyArg_ParseTuple(args, "O", &pystate)) {
+ return NULL;
+ }
+
+ if (! PyCObject_Check(pystate)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a context object");
+ return NULL;
+ }
+
+ state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+
+ if (state == NULL) {
+ return NULL;
+ }
+
+ return Py_BuildValue("s", state->username);
+}
+
+static PyObject *authGSSClientUnwrap(PyObject *self, PyObject *args)
+{
+ gss_client_state *state = NULL;
+ PyObject *pystate = NULL;
+ char *challenge = NULL;
+ int result = 0;
+
+ if (! PyArg_ParseTuple(args, "Os", &pystate, &challenge)) {
+ return NULL;
+ }
+
+ if (! PyCObject_Check(pystate)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a context object");
+ return NULL;
+ }
+
+ state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+
+ if (state == NULL) {
+ return NULL;
+ }
+
+ result = authenticate_gss_client_unwrap(state, challenge);
+
+ if (result == AUTH_GSS_ERROR) {
+ return NULL;
+ }
+
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *authGSSClientWrap(PyObject *self, PyObject *args)
+{
+ gss_client_state *state = NULL;
+ PyObject *pystate = NULL;
+ char *challenge = NULL;
+ char *user = NULL;
+ int protect = 0;
+ int result = 0;
+
+ if (! PyArg_ParseTuple(
+ args, "Os|zi", &pystate, &challenge, &user, &protect
+ )) {
+ return NULL;
+ }
+
+ if (! PyCObject_Check(pystate)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a context object");
+ return NULL;
+ }
+
+ state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+
+ if (state == NULL) {
+ return NULL;
+ }
+
+ result = authenticate_gss_client_wrap(state, challenge, user, protect);
+
+ if (result == AUTH_GSS_ERROR) {
+ return NULL;
+ }
+
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *authGSSClientInquireCred(PyObject *self, PyObject *args)
+{
+ gss_client_state *state = NULL;
+ PyObject *pystate = NULL;
+ int result = 0;
+ if (!PyArg_ParseTuple(args, "O", &pystate)) {
+ return NULL;
+ }
+
+ if (!PyCObject_Check(pystate)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a context object");
+ return NULL;
+ }
+
+ state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+ if (state == NULL) {
+ return NULL;
+ }
+
+ result = authenticate_gss_client_inquire_cred(state);
+ if (result == AUTH_GSS_ERROR) {
+ return NULL;
+ }
+
+ return Py_BuildValue("i", result);
+}
+
+static void
+#if PY_VERSION_HEX >= 0x03020000
+destroy_gss_server(PyObject *obj) {
+ gss_server_state *state = PyCapsule_GetPointer(obj, NULL);
+#else
+destroy_gss_server(void *obj) {
+ gss_server_state *state = (gss_server_state *)obj;
+#endif
+ if (state) {
+ authenticate_gss_server_clean(state);
+ free(state);
+ }
+}
+
+static PyObject *authGSSServerInit(PyObject *self, PyObject *args)
+{
+ const char *service = NULL;
+ gss_server_state *state = NULL;
+ PyObject *pystate = NULL;
+ int result = 0;
+
+ if (! PyArg_ParseTuple(args, "s", &service)) {
+ return NULL;
+ }
+
+ state = (gss_server_state *) malloc(sizeof(gss_server_state));
+ if (state == NULL)
+ {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ pystate = PyCObject_FromVoidPtr(state, &destroy_gss_server);
+ if (pystate == NULL) {
+ free(state);
+ return NULL;
+ }
+
+ result = authenticate_gss_server_init(service, state);
+
+ if (result == AUTH_GSS_ERROR) {
+ Py_DECREF(pystate);
+ return NULL;
+ }
+
+ return Py_BuildValue("(iN)", result, pystate);
+}
+
+static PyObject *authGSSServerClean(PyObject *self, PyObject *args)
+{
+ return Py_BuildValue("i", AUTH_GSS_COMPLETE);
+}
+
+static PyObject *authGSSServerStep(PyObject *self, PyObject *args)
+{
+ gss_server_state *state = NULL;
+ PyObject *pystate = NULL;
+ char *challenge = NULL;
+ int result = 0;
+
+ if (! PyArg_ParseTuple(args, "Os", &pystate, &challenge)) {
+ return NULL;
+ }
+
+ if (! PyCObject_Check(pystate)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a context object");
+ return NULL;
+ }
+
+ state = (gss_server_state *)PyCObject_AsVoidPtr(pystate);
+
+ if (state == NULL) {
+ return NULL;
+ }
+
+ result = authenticate_gss_server_step(state, challenge);
+
+ if (result == AUTH_GSS_ERROR) {
+ return NULL;
+ }
+
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *authGSSServerStoreDelegate(PyObject *self, PyObject *args)
+{
+ gss_server_state *state = NULL;
+ PyObject *pystate = NULL;
+ int result = 0;
+
+ if (! PyArg_ParseTuple(args, "O", &pystate)) {
+ return NULL;
+ }
+
+ if (! PyCObject_Check(pystate)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a context object");
+ return NULL;
+ }
+
+ state = (gss_server_state *)PyCObject_AsVoidPtr(pystate);
+
+ if (state == NULL) {
+ return NULL;
+ }
+
+ result = authenticate_gss_server_store_delegate(state);
+
+ if (result == AUTH_GSS_ERROR) {
+ return NULL;
+ }
+
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *authGSSServerResponse(PyObject *self, PyObject *args)
+{
+ gss_server_state *state = NULL;
+ PyObject *pystate = NULL;
+
+ if (! PyArg_ParseTuple(args, "O", &pystate)) {
+ return NULL;
+ }
+
+ if (! PyCObject_Check(pystate)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a context object");
+ return NULL;
+ }
+
+ state = (gss_server_state *)PyCObject_AsVoidPtr(pystate);
+
+ if (state == NULL) {
+ return NULL;
+ }
+
+ return Py_BuildValue("s", state->response);
+}
+
+static PyObject *authGSSServerUserName(PyObject *self, PyObject *args)
+{
+ gss_server_state *state = NULL;
+ PyObject *pystate = NULL;
+
+ if (! PyArg_ParseTuple(args, "O", &pystate)) {
+ return NULL;
+ }
+
+ if (! PyCObject_Check(pystate)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a context object");
+ return NULL;
+ }
+
+ state = (gss_server_state *)PyCObject_AsVoidPtr(pystate);
+
+ if (state == NULL) {
+ return NULL;
+ }
+
+ return Py_BuildValue("s", state->username);
+}
+
+static PyObject *authGSSServerCacheName(PyObject *self, PyObject *args)
+{
+ gss_server_state *state = NULL;
+ PyObject *pystate = NULL;
+
+ if (! PyArg_ParseTuple(args, "O", &pystate)) {
+ return NULL;
+ }
+
+ if (! PyCObject_Check(pystate)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a context object");
+ return NULL;
+ }
+
+ state = (gss_server_state *)PyCObject_AsVoidPtr(pystate);
+
+ if (state == NULL) {
+ return NULL;
+ }
+
+ return Py_BuildValue("s", state->ccname);
+}
+
+static PyObject *authGSSServerTargetName(PyObject *self, PyObject *args)
+{
+ gss_server_state *state = NULL;
+ PyObject *pystate = NULL;
+
+ if (! PyArg_ParseTuple(args, "O", &pystate)) {
+ return NULL;
+ }
+
+ if (! PyCObject_Check(pystate)) {
+ PyErr_SetString(PyExc_TypeError, "Expected a context object");
+ return NULL;
+ }
+
+ state = (gss_server_state *)PyCObject_AsVoidPtr(pystate);
+
+ if (state == NULL) {
+ return NULL;
+ }
+
+ return Py_BuildValue("s", state->targetname);
+}
+
+static PyMethodDef KerberosMethods[] = {
+ {
+ "checkPassword",
+ checkPassword, METH_VARARGS,
+ "Check the supplied user/password against Kerberos KDC."
+ },
+ {
+ "changePassword",
+ changePassword, METH_VARARGS,
+ "Change the user password."
+ },
+ {
+ "getServerPrincipalDetails",
+ getServerPrincipalDetails, METH_VARARGS,
+ "Return the service principal for a given service and hostname."
+ },
+ {
+ "authGSSClientInit",
+ (PyCFunction)authGSSClientInit, METH_VARARGS | METH_KEYWORDS,
+ "Initialize client-side GSSAPI operations."
+ },
+ {
+ "channelBindings",
+ (PyCFunction)channelBindings, METH_VARARGS | METH_KEYWORDS,
+ "Build the Channel Bindings Structure for authGSSClientStep."
+ },
+ {
+ "authGSSClientClean",
+ authGSSClientClean, METH_VARARGS,
+ "Terminate client-side GSSAPI operations."
+ },
+ {
+ "authGSSClientStep",
+ (PyCFunction)authGSSClientStep, METH_VARARGS | METH_KEYWORDS,
+ "Do a client-side GSSAPI step."
+ },
+ {
+ "authGSSClientResponse",
+ authGSSClientResponse, METH_VARARGS,
+ "Get the response from the last client-side GSSAPI step."
+ },
+ {
+ "authGSSClientInquireCred", authGSSClientInquireCred, METH_VARARGS,
+ "Get the current user name, if any, without a client-side GSSAPI step"
+ },
+ {
+ "authGSSClientResponseConf",
+ authGSSClientResponseConf, METH_VARARGS,
+ "return 1 if confidentiality was set in the last unwrapped buffer, 0 otherwise."
+ },
+ {
+ "authGSSClientUserName",
+ authGSSClientUserName, METH_VARARGS,
+ "Get the user name from the last client-side GSSAPI step."
+ },
+ {
+ "authGSSServerInit",
+ authGSSServerInit, METH_VARARGS,
+ "Initialize server-side GSSAPI operations."
+ },
+ {
+ "authGSSClientWrap",
+ authGSSClientWrap, METH_VARARGS,
+ "Do a GSSAPI wrap."
+ },
+ {
+ "authGSSClientUnwrap",
+ authGSSClientUnwrap, METH_VARARGS,
+ "Do a GSSAPI unwrap."
+ },
+ {
+ "authGSSClientInquireCred", authGSSClientInquireCred, METH_VARARGS,
+ "Get the current user name, if any."
+ },
+ {
+ "authGSSServerClean",
+ authGSSServerClean, METH_VARARGS,
+ "Terminate server-side GSSAPI operations."
+ },
+ {
+ "authGSSServerStep",
+ authGSSServerStep, METH_VARARGS,
+ "Do a server-side GSSAPI step."
+ },
+ {
+ "authGSSServerHasDelegated",
+ authGSSServerHasDelegated, METH_VARARGS,
+ "Check whether the client delegated credentials to us."
+ },
+ {
+ "authGSSServerStoreDelegate",
+ authGSSServerStoreDelegate, METH_VARARGS,
+ "Store the delegated Credentials."
+ },
+ {
+ "authGSSServerResponse",
+ authGSSServerResponse, METH_VARARGS,
+ "Get the response from the last server-side GSSAPI step."
+ },
+ {
+ "authGSSServerUserName",
+ authGSSServerUserName, METH_VARARGS,
+ "Get the user name from the last server-side GSSAPI step."
+ },
+ {
+ "authGSSServerCacheName",
+ authGSSServerCacheName, METH_VARARGS,
+ "Get the location of the cache where delegated credentials are stored."
+ },
+ {
+ "authGSSServerTargetName",
+ authGSSServerTargetName, METH_VARARGS,
+ "Get the target name from the last server-side GSSAPI step."
+ },
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+MOD_INIT(kerberos)
+{
+ PyObject *m,*d;
+
+ MOD_DEF(m, "kerberos", NULL, KerberosMethods);
+
+ if (m == NULL) {
+ return MOD_ERROR_VAL;
+ }
+
+ d = PyModule_GetDict(m);
+
+ /* create the base exception class */
+ if (! (KrbException_class = PyErr_NewException(
+ "kerberos.KrbError", NULL, NULL
+ ))) {
+ goto error;
+ }
+
+ PyDict_SetItemString(d, "KrbError", KrbException_class);
+ Py_INCREF(KrbException_class);
+
+ /* ...and the derived exceptions */
+ if (! (BasicAuthException_class = PyErr_NewException(
+ "kerberos.BasicAuthError", KrbException_class, NULL
+ ))) {
+ goto error;
+ }
+
+ Py_INCREF(BasicAuthException_class);
+ PyDict_SetItemString(d, "BasicAuthError", BasicAuthException_class);
+
+ if (! (PwdChangeException_class = PyErr_NewException(
+ "kerberos.PwdChangeError", KrbException_class, NULL
+ ))) {
+ goto error;
+ }
+
+ Py_INCREF(PwdChangeException_class);
+ PyDict_SetItemString(d, "PwdChangeError", PwdChangeException_class);
+
+ if (! (GssException_class = PyErr_NewException(
+ "kerberos.GSSError", KrbException_class, NULL
+ ))) {
+ goto error;
+ }
+
+ Py_INCREF(GssException_class);
+ PyDict_SetItemString(
+ d, "GSSError", GssException_class
+ );
+
+ PyDict_SetItemString(
+ d, "AUTH_GSS_COMPLETE", PyInt_FromLong(AUTH_GSS_COMPLETE)
+ );
+ PyDict_SetItemString(
+ d, "AUTH_GSS_CONTINUE", PyInt_FromLong(AUTH_GSS_CONTINUE)
+ );
+
+ PyDict_SetItemString(
+ d, "GSS_C_DELEG_FLAG", PyInt_FromLong(GSS_C_DELEG_FLAG)
+ );
+ PyDict_SetItemString(
+ d, "GSS_C_MUTUAL_FLAG", PyInt_FromLong(GSS_C_MUTUAL_FLAG)
+ );
+ PyDict_SetItemString(
+ d, "GSS_C_REPLAY_FLAG", PyInt_FromLong(GSS_C_REPLAY_FLAG)
+ );
+ PyDict_SetItemString(
+ d, "GSS_C_SEQUENCE_FLAG", PyInt_FromLong(GSS_C_SEQUENCE_FLAG)
+ );
+ PyDict_SetItemString(
+ d, "GSS_C_CONF_FLAG", PyInt_FromLong(GSS_C_CONF_FLAG)
+ );
+ PyDict_SetItemString(
+ d, "GSS_C_INTEG_FLAG", PyInt_FromLong(GSS_C_INTEG_FLAG)
+ );
+ PyDict_SetItemString(
+ d, "GSS_C_ANON_FLAG", PyInt_FromLong(GSS_C_ANON_FLAG)
+ );
+ PyDict_SetItemString(
+ d, "GSS_C_PROT_READY_FLAG", PyInt_FromLong(GSS_C_PROT_READY_FLAG)
+ );
+ PyDict_SetItemString(
+ d, "GSS_C_TRANS_FLAG", PyInt_FromLong(GSS_C_TRANS_FLAG)
+ );
+ krb5_mech_oid.elements = &krb5_mech_oid_bytes.b;
+
+ PyDict_SetItemString(
+ d, "GSS_MECH_OID_KRB5", PyCObject_FromVoidPtr(&krb5_mech_oid, NULL)
+ );
+
+ spnego_mech_oid.elements = &spnego_mech_oid_bytes.b;
+ PyDict_SetItemString(
+ d, "GSS_MECH_OID_SPNEGO", PyCObject_FromVoidPtr(&spnego_mech_oid, NULL)
+ );
+
+error:
+ if (PyErr_Occurred()) {
+ PyErr_SetString(PyExc_ImportError, "kerberos: init failed");
+ return MOD_ERROR_VAL;
+ }
+
+ return MOD_SUCCESS_VAL(m);
+}
diff --git a/shell/ext-py/kerberos-1.3.1/src/kerberosbasic.c b/shell/ext-py/kerberos-1.3.1/src/kerberosbasic.c
new file mode 100644
index 000000000..08703e156
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/src/kerberosbasic.c
@@ -0,0 +1,171 @@
+/**
+ * Copyright (c) 2006-2018 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+#include <Python.h>
+#include "kerberosbasic.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#undef PRINTFS
+
+extern PyObject *BasicAuthException_class;
+static void set_basicauth_error(krb5_context context, krb5_error_code code);
+
+static krb5_error_code verify_krb5_user(
+ krb5_context context, krb5_principal principal, const char *password,
+ krb5_principal server
+);
+
+int authenticate_user_krb5pwd(
+ const char *user, const char *pswd, const char *service,
+ const char *default_realm
+) {
+ krb5_context kcontext = NULL;
+ krb5_error_code code;
+ krb5_principal client = NULL;
+ krb5_principal server = NULL;
+ int ret = 0;
+ char *name = NULL;
+ char *p = NULL;
+
+ code = krb5_init_context(&kcontext);
+ if (code)
+ {
+ PyErr_SetObject(
+ BasicAuthException_class,
+ Py_BuildValue(
+ "((s:i))", "Cannot initialize Kerberos5 context", code
+ )
+ );
+ return 0;
+ }
+
+ ret = krb5_parse_name (kcontext, service, &server);
+
+ if (ret) {
+ set_basicauth_error(kcontext, ret);
+ ret = 0;
+ goto end;
+ }
+
+ code = krb5_unparse_name(kcontext, server, &name);
+ if (code) {
+ set_basicauth_error(kcontext, code);
+ ret = 0;
+ goto end;
+ }
+#ifdef PRINTFS
+ printf("Using %s as server principal for password verification\n", name);
+#endif
+ free(name);
+ name = NULL;
+
+ name = (char *)malloc(256);
+ if (name == NULL)
+ {
+ PyErr_NoMemory();
+ ret = 0;
+ goto end;
+ }
+ p = strchr(user, '@');
+ if (p == NULL) {
+ snprintf(name, 256, "%s@%s", user, default_realm);
+ } else {
+ snprintf(name, 256, "%s", user);
+ }
+
+ code = krb5_parse_name(kcontext, name, &client);
+ if (code) {
+ set_basicauth_error(kcontext, code);
+ ret = 0;
+ goto end;
+ }
+
+ code = verify_krb5_user(kcontext, client, pswd, server);
+
+ if (code) {
+ ret = 0;
+ goto end;
+ }
+
+ ret = 1;
+
+end:
+#ifdef PRINTFS
+ printf(
+ "kerb_authenticate_user_krb5pwd ret=%d user=%s authtype=%s\n",
+ ret, user, "Basic"
+ );
+#endif
+ if (name) {
+ free(name);
+ }
+ if (client) {
+ krb5_free_principal(kcontext, client);
+ }
+ if (server) {
+ krb5_free_principal(kcontext, server);
+ }
+ krb5_free_context(kcontext);
+
+ return ret;
+}
+
+/* Inspired by krb5_verify_user from Heimdal */
+static krb5_error_code verify_krb5_user(
+ krb5_context context, krb5_principal principal, const char *password,
+ krb5_principal server
+) {
+ krb5_creds creds;
+ krb5_get_init_creds_opt gic_options;
+ krb5_error_code ret;
+ char *name = NULL;
+
+ memset(&creds, 0, sizeof(creds));
+
+ ret = krb5_unparse_name(context, principal, &name);
+ if (ret == 0) {
+#ifdef PRINTFS
+ printf("Trying to get TGT for user %s\n", name);
+#endif
+ free(name);
+ }
+
+ krb5_get_init_creds_opt_init(&gic_options);
+ ret = krb5_get_init_creds_password(
+ context, &creds, principal, (char *)password,
+ NULL, NULL, 0, NULL, &gic_options
+ );
+ if (ret) {
+ set_basicauth_error(context, ret);
+ goto end;
+ }
+
+end:
+ krb5_free_cred_contents(context, &creds);
+
+ return ret;
+}
+
+static void set_basicauth_error(krb5_context context, krb5_error_code code)
+{
+ PyErr_SetObject(
+ BasicAuthException_class,
+ Py_BuildValue("(s:i)", krb5_get_err_text(context, code), code)
+ );
+}
diff --git a/shell/ext-py/kerberos-1.3.1/src/kerberosbasic.h b/shell/ext-py/kerberos-1.3.1/src/kerberosbasic.h
new file mode 100644
index 000000000..c558234e4
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/src/kerberosbasic.h
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2006-2018 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+#define krb5_get_err_text(context,code) error_message(code)
+
+int authenticate_user_krb5pwd(
+ const char *user, const char *pswd, const char *service,
+ const char *default_realm
+);
diff --git a/shell/ext-py/kerberos-1.3.1/src/kerberosgss.c b/shell/ext-py/kerberos-1.3.1/src/kerberosgss.c
new file mode 100644
index 000000000..c82a5e439
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/src/kerberosgss.c
@@ -0,0 +1,1007 @@
+/**
+ * Copyright (c) 2006-2018 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+#include <Python.h>
+#include "kerberosgss.h"
+
+#include "base64.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+static void set_gss_error(OM_uint32 err_maj, OM_uint32 err_min);
+
+int create_krb5_ccache(
+ gss_server_state *state, krb5_context kcontext, krb5_principal princ,
+ krb5_ccache *ccache
+);
+
+extern PyObject *GssException_class;
+extern PyObject *KrbException_class;
+
+char* server_principal_details(const char* service, const char* hostname)
+{
+ char match[1024];
+ size_t match_len = 0;
+ char* result = NULL;
+
+ int code;
+ krb5_context kcontext;
+ krb5_keytab kt = NULL;
+ krb5_kt_cursor cursor = NULL;
+ krb5_keytab_entry entry;
+ char* pname = NULL;
+
+ // Generate the principal prefix we want to match
+ snprintf(match, 1024, "%s/%s@", service, hostname);
+ match_len = strlen(match);
+
+ code = krb5_init_context(&kcontext);
+ if (code) {
+ PyErr_SetObject(
+ KrbException_class,
+ Py_BuildValue(
+ "((s:i))", "Cannot initialize Kerberos5 context", code
+ )
+ );
+ return NULL;
+ }
+
+ if ((code = krb5_kt_default(kcontext, &kt))) {
+ PyErr_SetObject(
+ KrbException_class,
+ Py_BuildValue("((s:i))", "Cannot get default keytab", code)
+ );
+ goto end;
+ }
+
+ if ((code = krb5_kt_start_seq_get(kcontext, kt, &cursor))) {
+ PyErr_SetObject(
+ KrbException_class,
+ Py_BuildValue(
+ "((s:i))", "Cannot get sequence cursor from keytab", code
+ )
+ );
+ goto end;
+ }
+
+ while ((code = krb5_kt_next_entry(kcontext, kt, &entry, &cursor)) == 0) {
+ if ((code = krb5_unparse_name(kcontext, entry.principal, &pname))) {
+ PyErr_SetObject(
+ KrbException_class,
+ Py_BuildValue(
+ "((s:i))", "Cannot parse principal name from keytab", code
+ )
+ );
+ goto end;
+ }
+
+ if (strncmp(pname, match, match_len) == 0) {
+ result = malloc(strlen(pname) + 1);
+ if (result == NULL) {
+ PyErr_NoMemory();
+ goto end;
+ }
+ strcpy(result, pname);
+ krb5_free_unparsed_name(kcontext, pname);
+ krb5_free_keytab_entry_contents(kcontext, &entry);
+ break;
+ }
+
+ krb5_free_unparsed_name(kcontext, pname);
+ krb5_free_keytab_entry_contents(kcontext, &entry);
+ }
+
+ if (result == NULL) {
+ PyErr_SetObject(
+ KrbException_class,
+ Py_BuildValue("((s:i))", "Principal not found in keytab", -1)
+ );
+ }
+
+end:
+ if (cursor) {
+ krb5_kt_end_seq_get(kcontext, kt, &cursor);
+ }
+ if (kt) {
+ krb5_kt_close(kcontext, kt);
+ }
+ krb5_free_context(kcontext);
+
+ return result;
+}
+
+int authenticate_gss_client_init(
+ const char* service, const char* principal, long int gss_flags,
+ gss_server_state* delegatestate, gss_OID mech_oid, gss_client_state* state
+)
+{
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_buffer_desc name_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc principal_token = GSS_C_EMPTY_BUFFER;
+ int ret = AUTH_GSS_COMPLETE;
+
+ state->server_name = GSS_C_NO_NAME;
+ state->mech_oid = mech_oid;
+ state->context = GSS_C_NO_CONTEXT;
+ state->gss_flags = gss_flags;
+ state->client_creds = GSS_C_NO_CREDENTIAL;
+ state->username = NULL;
+ state->response = NULL;
+
+ // Import server name first
+ name_token.length = strlen(service);
+ name_token.value = (char *)service;
+
+ maj_stat = gss_import_name(
+ &min_stat, &name_token, gss_krb5_nt_service_name, &state->server_name
+ );
+
+ if (GSS_ERROR(maj_stat)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ // Use the delegate credentials if they exist
+ if (delegatestate && delegatestate->client_creds != GSS_C_NO_CREDENTIAL) {
+ state->client_creds = delegatestate->client_creds;
+ }
+ // If available use the principal to extract its associated credentials
+ else if (principal && *principal) {
+ gss_name_t name;
+ principal_token.length = strlen(principal);
+ principal_token.value = (char *)principal;
+
+ maj_stat = gss_import_name(
+ &min_stat, &principal_token, GSS_C_NT_USER_NAME, &name
+ );
+ if (GSS_ERROR(maj_stat)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ maj_stat = gss_acquire_cred(
+ &min_stat, name, GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
+ GSS_C_INITIATE, &state->client_creds, NULL, NULL
+ );
+ if (GSS_ERROR(maj_stat)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ maj_stat = gss_release_name(&min_stat, &name);
+ if (GSS_ERROR(maj_stat)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+int authenticate_gss_client_clean(gss_client_state *state)
+{
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ int ret = AUTH_GSS_COMPLETE;
+
+ if (state->context != GSS_C_NO_CONTEXT) {
+ maj_stat = gss_delete_sec_context(
+ &min_stat, &state->context, GSS_C_NO_BUFFER
+ );
+ }
+ if (state->server_name != GSS_C_NO_NAME) {
+ maj_stat = gss_release_name(&min_stat, &state->server_name);
+ }
+ if (
+ state->client_creds != GSS_C_NO_CREDENTIAL &&
+ ! (state->gss_flags & GSS_C_DELEG_FLAG)
+ ) {
+ maj_stat = gss_release_cred(&min_stat, &state->client_creds);
+ }
+ if (state->username != NULL) {
+ free(state->username);
+ state->username = NULL;
+ }
+ if (state->response != NULL) {
+ free(state->response);
+ state->response = NULL;
+ }
+
+ return ret;
+}
+
+int authenticate_gss_client_step(
+ gss_client_state* state, const char* challenge, struct gss_channel_bindings_struct* channel_bindings
+) {
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ int ret = AUTH_GSS_CONTINUE;
+
+ // Always clear out the old response
+ if (state->response != NULL) {
+ free(state->response);
+ state->response = NULL;
+ }
+
+ // If there is a challenge (data from the server) we need to give it to GSS
+ if (challenge && *challenge) {
+ size_t len;
+ input_token.value = base64_decode(challenge, &len);
+ if (input_token.value == NULL)
+ {
+ PyErr_NoMemory();
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ input_token.length = len;
+ }
+
+ // Do GSSAPI step
+ Py_BEGIN_ALLOW_THREADS
+ maj_stat = gss_init_sec_context(
+ &min_stat,
+ state->client_creds,
+ &state->context,
+ state->server_name,
+ state->mech_oid,
+ (OM_uint32)state->gss_flags,
+ 0,
+ channel_bindings,
+ &input_token,
+ NULL,
+ &output_token,
+ NULL,
+ NULL
+ );
+ Py_END_ALLOW_THREADS
+
+ if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ ret = (maj_stat == GSS_S_COMPLETE) ? AUTH_GSS_COMPLETE : AUTH_GSS_CONTINUE;
+ // Grab the client response to send back to the server
+ if (output_token.length) {
+ state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);
+ if (state->response == NULL) {
+ PyErr_NoMemory();
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ maj_stat = gss_release_buffer(&min_stat, &output_token);
+ }
+
+ // Try to get the user name if we have completed all GSS operations
+ if (ret == AUTH_GSS_COMPLETE) {
+ gss_name_t gssuser = GSS_C_NO_NAME;
+ maj_stat = gss_inquire_context(&min_stat, state->context, &gssuser, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (GSS_ERROR(maj_stat)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ gss_buffer_desc name_token;
+ name_token.length = 0;
+ maj_stat = gss_display_name(&min_stat, gssuser, &name_token, NULL);
+ if (GSS_ERROR(maj_stat)) {
+ if (name_token.value) {
+ gss_release_buffer(&min_stat, &name_token);
+ }
+ gss_release_name(&min_stat, &gssuser);
+
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ } else {
+ if (state->username != NULL) {
+ free(state->username);
+ state->username = NULL;
+ }
+ state->username = (char *)malloc(name_token.length + 1);
+ if (state->username == NULL) {
+ PyErr_NoMemory();
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ strncpy(state->username, (char*) name_token.value, name_token.length);
+ state->username[name_token.length] = 0;
+ gss_release_buffer(&min_stat, &name_token);
+ gss_release_name(&min_stat, &gssuser);
+ }
+ }
+
+end:
+ if (output_token.value) {
+ gss_release_buffer(&min_stat, &output_token);
+ }
+ if (input_token.value) {
+ free(input_token.value);
+ }
+ return ret;
+}
+
+int authenticate_gss_client_unwrap(
+ gss_client_state *state, const char *challenge
+) {
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ int ret = AUTH_GSS_CONTINUE;
+ int conf = 0;
+
+ // Always clear out the old response
+ if (state->response != NULL) {
+ free(state->response);
+ state->response = NULL;
+ state->responseConf = 0;
+ }
+
+ // If there is a challenge (data from the server) we need to give it to GSS
+ if (challenge && *challenge) {
+ size_t len;
+ input_token.value = base64_decode(challenge, &len);
+ if (input_token.value == NULL) {
+ PyErr_NoMemory();
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ input_token.length = len;
+ }
+
+ // Do GSSAPI step
+ maj_stat = gss_unwrap(
+ &min_stat,
+ state->context,
+ &input_token,
+ &output_token,
+ &conf,
+ NULL
+ );
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ } else {
+ ret = AUTH_GSS_COMPLETE;
+ }
+
+ // Grab the client response
+ if (output_token.length) {
+ state->response = base64_encode(
+ (const unsigned char *)output_token.value, output_token.length
+ );
+ if (state->response == NULL)
+ {
+ PyErr_NoMemory();
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ state->responseConf = conf;
+ maj_stat = gss_release_buffer(&min_stat, &output_token);
+ }
+
+end:
+ if (output_token.value) {
+ gss_release_buffer(&min_stat, &output_token);
+ }
+ if (input_token.value) {
+ free(input_token.value);
+ }
+ return ret;
+}
+
+int authenticate_gss_client_wrap(
+ gss_client_state* state, const char* challenge, const char* user,
+ int protect
+) {
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ int ret = AUTH_GSS_CONTINUE;
+ char buf[4096], server_conf_flags;
+ unsigned long buf_size;
+
+ // Always clear out the old response
+ if (state->response != NULL) {
+ free(state->response);
+ state->response = NULL;
+ }
+
+ if (challenge && *challenge) {
+ size_t len;
+ input_token.value = base64_decode(challenge, &len);
+ if (input_token.value == NULL)
+ {
+ PyErr_NoMemory();
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ input_token.length = len;
+ }
+
+ if (user) {
+ // get bufsize
+ server_conf_flags = ((char*) input_token.value)[0];
+ ((char*) input_token.value)[0] = 0;
+ buf_size = ntohl(*((long *) input_token.value));
+ free(input_token.value);
+#ifdef PRINTFS
+ printf(
+ "User: %s, %c%c%c\n", user,
+ server_conf_flags & GSS_AUTH_P_NONE ? 'N' : '-',
+ server_conf_flags & GSS_AUTH_P_INTEGRITY ? 'I' : '-',
+ server_conf_flags & GSS_AUTH_P_PRIVACY ? 'P' : '-'
+ );
+ printf("Maximum GSS token size is %ld\n", buf_size);
+#endif
+
+ // agree to terms (hack!)
+ buf_size = htonl(buf_size); // not relevant without integrity/privacy
+ memcpy(buf, &buf_size, 4);
+ buf[0] = GSS_AUTH_P_NONE;
+ // server decides if principal can log in as user
+ strncpy(buf + 4, user, sizeof(buf) - 4);
+ input_token.value = buf;
+ input_token.length = 4 + strlen(user);
+ }
+
+ // Do GSSAPI wrap
+ maj_stat = gss_wrap(
+ &min_stat,
+ state->context,
+ protect,
+ GSS_C_QOP_DEFAULT,
+ &input_token,
+ NULL,
+ &output_token
+ );
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ } else {
+ ret = AUTH_GSS_COMPLETE;
+ }
+ // Grab the client response to send back to the server
+ if (output_token.length) {
+ state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);
+ if (state->response == NULL) {
+ PyErr_NoMemory();
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ maj_stat = gss_release_buffer(&min_stat, &output_token);
+ }
+
+end:
+ if (output_token.value) {
+ gss_release_buffer(&min_stat, &output_token);
+ }
+ return ret;
+}
+
+int authenticate_gss_client_inquire_cred(gss_client_state* state)
+{
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_cred_id_t client_creds = GSS_C_NO_CREDENTIAL;
+ gss_buffer_desc name_token = GSS_C_EMPTY_BUFFER;
+ gss_name_t name = GSS_C_NO_NAME;
+ int ret = AUTH_GSS_COMPLETE;
+
+ // Check whether credentials have already been obtained.
+ if (state->username != NULL) {
+ goto end;
+ }
+
+ // Get credentials
+ maj_stat = gss_acquire_cred(
+ &min_stat, GSS_C_NO_NAME, GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET, GSS_C_INITIATE, &client_creds, NULL, NULL
+ );
+
+ if (GSS_ERROR(maj_stat)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ // Get the name
+ maj_stat = gss_inquire_cred(
+ &min_stat, client_creds, &name, NULL, NULL, NULL
+ );
+
+ if (GSS_ERROR(maj_stat)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ maj_stat = gss_display_name(&min_stat, name, &name_token, NULL);
+
+ if (GSS_ERROR(maj_stat)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ state->username = (char *)malloc(name_token.length + 1);
+ if (state->username == NULL) {
+ PyErr_NoMemory();
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ strncpy(state->username, (char*) name_token.value, name_token.length);
+ state->username[name_token.length] = 0;
+
+end:
+ if (client_creds != GSS_C_NO_CREDENTIAL) {
+ gss_release_cred(&min_stat, &client_creds);
+ }
+ if (name_token.length) {
+ gss_release_buffer(&min_stat, &name_token);
+ }
+ if (name != GSS_C_NO_NAME) {
+ gss_release_name(&min_stat, &name);
+ }
+ return ret;
+}
+
+int authenticate_gss_server_init(const char *service, gss_server_state *state)
+{
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_buffer_desc name_token = GSS_C_EMPTY_BUFFER;
+ int ret = AUTH_GSS_COMPLETE;
+
+ state->context = GSS_C_NO_CONTEXT;
+ state->server_name = GSS_C_NO_NAME;
+ state->client_name = GSS_C_NO_NAME;
+ state->server_creds = GSS_C_NO_CREDENTIAL;
+ state->client_creds = GSS_C_NO_CREDENTIAL;
+ state->username = NULL;
+ state->targetname = NULL;
+ state->response = NULL;
+ state->ccname = NULL;
+ int cred_usage = GSS_C_ACCEPT;
+
+ // Server name may be empty which means we aren't going to create our own creds
+ size_t service_len = strlen(service);
+ if (service_len != 0) {
+ // Import server name first
+ if (strcmp(service, "DELEGATE") == 0) {
+ cred_usage = GSS_C_BOTH;
+ }
+ else {
+ name_token.length = strlen(service);
+ name_token.value = (char *)service;
+
+ maj_stat = gss_import_name(
+ &min_stat, &name_token, GSS_C_NT_HOSTBASED_SERVICE,
+ &state->server_name
+ );
+
+ if (GSS_ERROR(maj_stat)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ }
+
+ // Get credentials
+ maj_stat = gss_acquire_cred(
+ &min_stat, state->server_name, GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
+ cred_usage, &state->server_creds, NULL, NULL
+ );
+
+ if (GSS_ERROR(maj_stat)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+int authenticate_gss_server_clean(gss_server_state *state)
+{
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ int ret = AUTH_GSS_COMPLETE;
+
+ if (state->context != GSS_C_NO_CONTEXT) {
+ maj_stat = gss_delete_sec_context(
+ &min_stat, &state->context, GSS_C_NO_BUFFER
+ );
+ }
+ if (state->server_name != GSS_C_NO_NAME) {
+ maj_stat = gss_release_name(&min_stat, &state->server_name);
+ }
+ if (state->client_name != GSS_C_NO_NAME) {
+ maj_stat = gss_release_name(&min_stat, &state->client_name);
+ }
+ if (state->server_creds != GSS_C_NO_CREDENTIAL) {
+ maj_stat = gss_release_cred(&min_stat, &state->server_creds);
+ }
+ if (state->client_creds != GSS_C_NO_CREDENTIAL) {
+ maj_stat = gss_release_cred(&min_stat, &state->client_creds);
+ }
+ if (state->username != NULL) {
+ free(state->username);
+ state->username = NULL;
+ }
+ if (state->targetname != NULL) {
+ free(state->targetname);
+ state->targetname = NULL;
+ }
+ if (state->response != NULL) {
+ free(state->response);
+ state->response = NULL;
+ }
+ if (state->ccname != NULL) {
+ free(state->ccname);
+ state->ccname = NULL;
+ }
+
+ return ret;
+}
+
+int authenticate_gss_server_step(
+ gss_server_state *state, const char *challenge
+) {
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ int ret = AUTH_GSS_CONTINUE;
+
+ // Always clear out the old response
+ if (state->response != NULL) {
+ free(state->response);
+ state->response = NULL;
+ }
+
+ // If there is a challenge (data from the server) we need to give it to GSS
+ if (challenge && *challenge) {
+ size_t len;
+ input_token.value = base64_decode(challenge, &len);
+ if (input_token.value == NULL)
+ {
+ PyErr_NoMemory();
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ input_token.length = len;
+ } else {
+ PyErr_SetString(
+ KrbException_class, "No challenge parameter in request from client"
+ );
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ maj_stat = gss_accept_sec_context(
+ &min_stat,
+ &state->context,
+ state->server_creds,
+ &input_token,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &state->client_name,
+ NULL,
+ &output_token,
+ NULL,
+ NULL,
+ &state->client_creds
+ );
+ Py_END_ALLOW_THREADS
+
+ if (GSS_ERROR(maj_stat)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ // Grab the server response to send back to the client
+ if (output_token.length) {
+ state->response = base64_encode(
+ (const unsigned char *)output_token.value, output_token.length
+ );
+ if (state->response == NULL)
+ {
+ PyErr_NoMemory();
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ maj_stat = gss_release_buffer(&min_stat, &output_token);
+ }
+
+ // Get the user name
+ maj_stat = gss_display_name(
+ &min_stat, state->client_name, &output_token, NULL
+ );
+ if (GSS_ERROR(maj_stat)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ state->username = (char *)malloc(output_token.length + 1);
+ if (state->username == NULL)
+ {
+ PyErr_NoMemory();
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ strncpy(state->username, (char*) output_token.value, output_token.length);
+ state->username[output_token.length] = 0;
+
+ // Get the target name if no server creds were supplied
+ if (state->server_creds == GSS_C_NO_CREDENTIAL) {
+ gss_name_t target_name = GSS_C_NO_NAME;
+ maj_stat = gss_inquire_context(
+ &min_stat, state->context, NULL, &target_name, NULL, NULL, NULL,
+ NULL, NULL
+ );
+ if (GSS_ERROR(maj_stat)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ maj_stat = gss_display_name(
+ &min_stat, target_name, &output_token, NULL
+ );
+ if (GSS_ERROR(maj_stat)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ state->targetname = (char *)malloc(output_token.length + 1);
+ if (state->targetname == NULL)
+ {
+ PyErr_NoMemory();
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+ strncpy(
+ state->targetname, (char*) output_token.value, output_token.length
+ );
+ state->targetname[output_token.length] = 0;
+ }
+
+ ret = AUTH_GSS_COMPLETE;
+
+end:
+ if (output_token.length) {
+ gss_release_buffer(&min_stat, &output_token);
+ }
+ if (input_token.value) {
+ free(input_token.value);
+ }
+ return ret;
+}
+
+int authenticate_gss_server_has_delegated(gss_server_state *state)
+{
+ return (state->client_creds != GSS_C_NO_CREDENTIAL);
+}
+
+static void set_gss_error(OM_uint32 err_maj, OM_uint32 err_min)
+{
+ OM_uint32 maj_stat, min_stat;
+ OM_uint32 msg_ctx = 0;
+ gss_buffer_desc status_string;
+ char buf_maj[512];
+ char buf_min[512];
+
+ do {
+ maj_stat = gss_display_status(
+ &min_stat,
+ err_maj,
+ GSS_C_GSS_CODE,
+ GSS_C_NO_OID,
+ &msg_ctx,
+ &status_string
+ );
+ if (GSS_ERROR(maj_stat)) {
+ break;
+ }
+ strncpy(buf_maj, (char*) status_string.value, sizeof(buf_maj));
+ gss_release_buffer(&min_stat, &status_string);
+
+ maj_stat = gss_display_status(
+ &min_stat,
+ err_min,
+ GSS_C_MECH_CODE,
+ GSS_C_NULL_OID,
+ &msg_ctx,
+ &status_string
+ );
+ if (! GSS_ERROR(maj_stat)) {
+ strncpy(buf_min, (char*) status_string.value, sizeof(buf_min));
+ gss_release_buffer(&min_stat, &status_string);
+ }
+ } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
+
+ PyErr_SetObject(
+ GssException_class,
+ Py_BuildValue("((s:i)(s:i))", buf_maj, err_maj, buf_min, err_min)
+ );
+}
+
+int authenticate_gss_server_store_delegate(gss_server_state *state)
+{
+ gss_cred_id_t delegated_cred = state->client_creds;
+ char *princ_name = state->username;
+ OM_uint32 maj_stat, min_stat;
+ krb5_principal princ = NULL;
+ krb5_ccache ccache = NULL;
+ krb5_error_code problem;
+ krb5_context context;
+ int ret = 500;
+
+ if (delegated_cred == GSS_C_NO_CREDENTIAL){
+ PyErr_SetObject(
+ KrbException_class,
+ Py_BuildValue("(s)", "Ticket is not delegatable")
+ );
+ return AUTH_GSS_ERROR;
+ }
+
+ problem = krb5_init_context(&context);
+ if (problem) {
+ PyErr_SetObject(
+ KrbException_class,
+ Py_BuildValue("(s)", "Cannot initialize krb5 context")
+ );
+ return AUTH_GSS_ERROR;
+ }
+
+ problem = krb5_parse_name(context, princ_name, &princ);
+ if (problem) {
+ PyErr_SetObject(
+ KrbException_class,
+ Py_BuildValue(
+ "(s:s)", "Cannot parse delegated username",
+ krb5_get_err_text(context, problem)
+ )
+ );
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ problem = create_krb5_ccache(state, context, princ, &ccache);
+ if (problem) {
+ PyErr_SetObject(
+ KrbException_class,
+ Py_BuildValue(
+ "(s:s)", "Error in creating krb5 cache",
+ krb5_get_err_text(context, problem)
+ )
+ );
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ maj_stat = gss_krb5_copy_ccache(&min_stat, delegated_cred, ccache);
+ if (GSS_ERROR(maj_stat)) {
+ set_gss_error(maj_stat, min_stat);
+ ret = AUTH_GSS_ERROR;
+ goto end;
+ }
+
+ krb5_cc_close(context, ccache);
+ ccache = NULL;
+ ret = 0;
+
+end:
+ if (princ) {
+ krb5_free_principal(context, princ);
+ }
+ if (ccache) {
+ krb5_cc_destroy(context, ccache);
+ }
+ krb5_free_context(context);
+
+ return ret;
+}
+
+int create_krb5_ccache(
+ gss_server_state *state, krb5_context kcontext, krb5_principal princ,
+ krb5_ccache *ccache
+) {
+ int fd;
+ char ccname[32];
+ krb5_error_code problem;
+ int ret;
+ krb5_ccache tmp_ccache = NULL;
+
+ snprintf(ccname, sizeof(ccname), "/tmp/krb5cc_pyserv_XXXXXX");
+ fd = mkstemp(ccname);
+ if (fd < 0) {
+ PyErr_SetObject(
+ KrbException_class,
+ Py_BuildValue("(s:s)", "Error in mkstemp", strerror(errno))
+ );
+ ret = 1;
+ goto end;
+ }
+ close(fd);
+
+ problem = krb5_cc_resolve(kcontext, ccname, &tmp_ccache);
+ if (problem) {
+ PyErr_SetObject(
+ KrbException_class,
+ Py_BuildValue(
+ "(s:s)", "Error resolving the credential cache",
+ krb5_get_err_text(kcontext, problem)
+ )
+ );
+ ret = 1;
+ unlink(ccname);
+ goto end;
+ }
+
+ problem = krb5_cc_initialize(kcontext, tmp_ccache, princ);
+ if (problem) {
+ PyErr_SetObject(
+ KrbException_class,
+ Py_BuildValue(
+ "(s:s)", "Error initialising the credential cache",
+ krb5_get_err_text(kcontext, problem)
+ )
+ );
+ ret = 1;
+ goto end;
+ }
+
+ *ccache = tmp_ccache;
+ tmp_ccache = NULL;
+
+ ret = 0;
+
+end:
+ if (tmp_ccache) {
+ krb5_cc_destroy(kcontext, tmp_ccache);
+ }
+
+ state->ccname = (char *)malloc(32*sizeof(char));
+ if (state->ccname == NULL) {
+ PyErr_NoMemory();
+ return 1;
+ }
+ strcpy(state->ccname, ccname);
+
+ return ret;
+}
diff --git a/shell/ext-py/kerberos-1.3.1/src/kerberosgss.h b/shell/ext-py/kerberos-1.3.1/src/kerberosgss.h
new file mode 100644
index 000000000..362040d98
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/src/kerberosgss.h
@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) 2006-2018 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+#define krb5_get_err_text(context,code) error_message(code)
+
+#define AUTH_GSS_ERROR -1
+#define AUTH_GSS_COMPLETE 1
+#define AUTH_GSS_CONTINUE 0
+
+#define GSS_AUTH_P_NONE 1
+#define GSS_AUTH_P_INTEGRITY 2
+#define GSS_AUTH_P_PRIVACY 4
+
+typedef struct {
+ gss_ctx_id_t context;
+ gss_name_t server_name;
+ gss_OID mech_oid;
+ long int gss_flags;
+ gss_cred_id_t client_creds;
+ char* username;
+ char* response;
+ int responseConf;
+} gss_client_state;
+
+typedef struct {
+ gss_ctx_id_t context;
+ gss_name_t server_name;
+ gss_name_t client_name;
+ gss_cred_id_t server_creds;
+ gss_cred_id_t client_creds;
+ char* username;
+ char* targetname;
+ char* response;
+ char* ccname;
+} gss_server_state;
+
+char* server_principal_details(const char* service, const char* hostname);
+
+int authenticate_gss_client_init(
+ const char* service, const char* principal, long int gss_flags,
+ gss_server_state* delegatestate, gss_OID mech_oid, gss_client_state* state
+);
+int authenticate_gss_client_clean(
+ gss_client_state *state
+);
+int authenticate_gss_client_step(
+ gss_client_state *state, const char *challenge, struct gss_channel_bindings_struct *channel_bindings
+);
+int authenticate_gss_client_unwrap(
+ gss_client_state* state, const char* challenge
+);
+int authenticate_gss_client_wrap(
+ gss_client_state* state, const char* challenge, const char* user,
+ int protect
+);
+int authenticate_gss_client_inquire_cred(
+ gss_client_state* state
+);
+
+int authenticate_gss_server_init(
+ const char* service, gss_server_state* state
+);
+int authenticate_gss_server_clean(
+ gss_server_state *state
+);
+int authenticate_gss_server_step(
+ gss_server_state *state, const char *challenge
+);
+int authenticate_gss_server_store_delegate(
+ gss_server_state *state
+);
+int authenticate_gss_server_has_delegated(
+ gss_server_state *state
+);
diff --git a/shell/ext-py/kerberos-1.3.1/src/kerberospw.c b/shell/ext-py/kerberos-1.3.1/src/kerberospw.c
new file mode 100644
index 000000000..e6abc0d8a
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/src/kerberospw.c
@@ -0,0 +1,172 @@
+/**
+ * Copyright (c) 2008 Guido Guenther <ag...@sigxcpu.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+#include <Python.h>
+#include "kerberospw.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#undef PRINTFS
+
+extern PyObject *PwdChangeException_class;
+
+static void set_pwchange_error(krb5_context context, krb5_error_code code)
+{
+ PyErr_SetObject(
+ PwdChangeException_class,
+ Py_BuildValue("(s:i)", krb5_get_err_text(context, code), code)
+ );
+}
+
+/* Inspired by krb5_verify_user from Heimdal */
+static krb5_error_code verify_krb5_user(
+ krb5_context context,
+ krb5_principal principal,
+ const char *password,
+ const char *service,
+ krb5_creds* creds
+) {
+ krb5_get_init_creds_opt gic_options;
+ krb5_error_code code;
+ int ret = 0;
+
+#ifdef PRINTFS
+ {
+ char *name = NULL;
+ code = krb5_unparse_name(context, principal, &name);
+ if (!code) {
+ printf("Trying to get TGT for user %s\n", name);
+ }
+ free(name);
+ }
+#endif
+
+ krb5_get_init_creds_opt_init(&gic_options);
+ krb5_get_init_creds_opt_set_forwardable(&gic_options, 0);
+ krb5_get_init_creds_opt_set_proxiable(&gic_options, 0);
+ krb5_get_init_creds_opt_set_renew_life(&gic_options, 0);
+
+ memset(creds, 0, sizeof(krb5_creds));
+
+ code = krb5_get_init_creds_password(
+ context, creds, principal,
+ (char *)password, NULL, NULL, 0,
+ (char *)service, &gic_options
+ );
+ if (code) {
+ set_pwchange_error(context, code);
+ goto end;
+ }
+ ret = 1; /* success */
+
+end:
+ return ret;
+}
+
+int change_user_krb5pwd(
+ const char *user, const char* oldpswd, const char *newpswd
+) {
+ krb5_context kcontext = NULL;
+ krb5_error_code code;
+ krb5_principal client = NULL;
+ krb5_creds creds;
+ int ret = 0;
+ int bytes = 0;
+ char *name = NULL;
+
+ const char* service = "kadmin/changepw";
+ int result_code;
+ krb5_data result_code_string, result_string;
+
+ code = krb5_init_context(&kcontext);
+ if (code) {
+ PyErr_SetObject(
+ PwdChangeException_class,
+ Py_BuildValue(
+ "((s:i))", "Cannot initialize Kerberos5 context", code
+ )
+ );
+ return 0;
+ }
+
+ name = (char *)malloc(256);
+ if (name == NULL)
+ {
+ PyErr_NoMemory();
+ goto end;
+ }
+ snprintf(name, 256, "%s", user);
+
+ code = krb5_parse_name(kcontext, name, &client);
+ if (code) {
+ set_pwchange_error(kcontext, code);
+ goto end;
+ }
+
+ code = verify_krb5_user(kcontext, client, oldpswd, service, &creds);
+ if (! code) { /* exception set by verify_krb5_user */
+ goto end;
+ }
+
+ code = krb5_change_password(kcontext, &creds, (char*)newpswd,
+ &result_code, &result_code_string, &result_string);
+ if (code) {
+ set_pwchange_error(kcontext, code);
+ goto end;
+ }
+ if (result_code) {
+ char *message = NULL;
+ bytes = asprintf(
+ &message, "%.*s: %.*s",
+ (int) result_code_string.length,
+ (char *) result_code_string.data,
+ (int) result_string.length,
+ (char *) result_string.data
+ );
+ if (bytes == -1)
+ {
+ PyErr_NoMemory();
+ }
+ else
+ {
+ PyErr_SetObject(
+ PwdChangeException_class,
+ Py_BuildValue("((s:i))", message, result_code)
+ );
+ free(message);
+ }
+ goto end;
+ }
+
+ ret = 1; /* success */
+
+end:
+#ifdef PRINTFS
+ printf("%s: ret=%d user=%s\n", __FUNCTION__, ret, name);
+#endif
+
+ if (name) {
+ free(name);
+ }
+ if (client) {
+ krb5_free_principal(kcontext, client);
+ }
+ krb5_free_context(kcontext);
+
+ return ret;
+}
diff --git a/shell/ext-py/kerberos-1.3.1/src/kerberospw.h b/shell/ext-py/kerberos-1.3.1/src/kerberospw.h
new file mode 100644
index 000000000..250f2d534
--- /dev/null
+++ b/shell/ext-py/kerberos-1.3.1/src/kerberospw.h
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2008 Guido Guenther <ag...@sigxcpu.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **/
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+#define krb5_get_err_text(context,code) error_message(code)
+
+int change_user_krb5pwd(
+ const char *user, const char* oldpswd, const char *newpswd
+);