Эх сурвалжийг харах

[core] Add kerberos (python-binding) library

bc Wong 13 жил өмнө
parent
commit
fb3c3dee7b

+ 202 - 0
desktop/core/ext-py/kerberos-1.1.1/LICENSE

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.

+ 20 - 0
desktop/core/ext-py/kerberos-1.1.1/PKG-INFO

@@ -0,0 +1,20 @@
+Metadata-Version: 1.0
+Name: kerberos
+Version: 1.1.1
+Summary: Kerberos high-level interface
+Home-page: UNKNOWN
+Author: UNKNOWN
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: 
+        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>.
+        
+        
+Platform: UNKNOWN
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Programming Language :: Python :: 2
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Systems Administration :: Authentication/Directory

+ 69 - 0
desktop/core/ext-py/kerberos-1.1.1/README.txt

@@ -0,0 +1,69 @@
+=========================================================
+PyKerberos Package
+
+Copyright (c) 2006-2008 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.
+
+=========================================================
+
+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.
+
+========
+CONTENTS
+========
+
+    src/               : directory in which C source code resides.
+    setup.py           : Python distutils extension build script.
+    config/            : directory of useful Kerberos config files.
+      edu.mit.Kerberos : example Kerberos .ini file.
+    README.txt         : this file!
+    kerberos.py        : Python api documentation/stub implementation.
+
+=====
+BUILD
+=====
+
+In this directory, run:
+
+    python setup.py build
+
+=======
+TESTING
+=======
+
+You must have a valid Kerberos setup on the test machine and you should ensure that you have valid
+Kerberos tickets for any client authentication being done (run 'klist' on the command line).
+Additionally, for the server: it must have been configured as a valid Kerberos service with the Kerbersos server
+for its realm - this usually requires running kadmin on the server machine to add the principal and generate a keytab
+entry for it (run 'sudo klist -k' to see the currently available keytab entries).
+
+Make sure that PYTHONPATH includes the appropriate build/lib.xxxx directory.
+Then run test.py with suitable command line arguments:
+
+    python test.py -u userid -p password -s service
+    
+    -u : user id for basic authenticate
+    -p : password for basic authenticate
+    -s : service principal for GSSAPI authentication (defaults to 'http@host.example.com')
+
+===========
+Python APIs
+===========
+
+See kerberos.py.

+ 14 - 0
desktop/core/ext-py/kerberos-1.1.1/config/edu.mit.Kerberos

@@ -0,0 +1,14 @@
+[libdefaults]
+    default_realm = EXAMPLE.COM
+    dns_fallback = NO
+    default_tkt_enctypes = des-cbc-crc
+    default_tgs_enctypes = des-cbc-crc
+
+[realms]
+	EXAMPLE.COM = {
+		kdc = kdc.example.com
+	}
+
+[domain_realm]
+	.example.com = EXAMPLE.COM
+	example.com = EXAMPLE.COM

+ 225 - 0
desktop/core/ext-py/kerberos-1.1.1/pysrc/kerberos.py

@@ -0,0 +1,225 @@
+##
+# Copyright (c) 2006-2009 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.
+    
+    NB 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.
+    
+    @param user:          a string containing the Kerberos user name. A realm may be
+        included by appending an '@' 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 containging the Kerberos service to check access for.
+        This will be of the form 'sss/xx.yy.zz', where 'sss' is the service identifier
+        (e.g., 'http', 'krbtgt'), and '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., '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 an '@' 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 containging 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 /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, gssflags=GSS_C_MUTUAL_FLAG|GSS_C_SEQUENCE_FLAG):
+    """
+    Initializes a context for GSSAPI client-side authentication with the given service principal.
+    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 'type@fqdn'
+        (e.g. 'imap@mail.apple.com').
+    @param gssflags: optional integer used to set GSS flags.
+        (e.g.  GSS_C_DELEG_FLAG|GSS_C_MUTUAL_FLAG|GSS_C_SEQUENCE_FLAG will allow 
+        for forwarding credentials to the remote host)
+    @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. After this call the context
+    object is invalid and should not be used again.
+
+    @param context: the context object returned from authGSSClientInit.
+    @return: a result code (see above).
+    """
+
+def authGSSClientStep(context, challenge):
+    """
+    Processes a single GSSAPI client-side step using the supplied server data.
+
+    @param context: the context object returned from authGSSClientInit.
+    @param challenge: a string containing the base64-encoded server data (which may be empty
+        for the first step).
+    @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 authGSSClientInit.
+    @return: a string containing the base64-encoded client data to be sent to the server.
+    """
+
+def authGSSClientUserName(context):
+    """
+    Get the user name of the principal authenticated via the now complete GSSAPI client-side operations.
+    This method must only be called after authGSSClientStep returns a complete response code.
+
+    @param context:   the context object returned from 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): 
+    """ 
+    Perform the client side GSSAPI wrap step.  
+    
+    @param data:the result of the authGSSClientResponse after the authGSSClientUnwrap 
+    @param user: the user to authorize 
+    @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 'type@fqdn'
+        (e.g. 'imap@mail.apple.com').
+    @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. After this call the context
+    object is invalid and should not be used again.
+
+    @param context: the context object returned from authGSSServerInit.
+    @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 authGSSServerInit.
+    @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 authGSSServerInit.
+    @return: a string containing the base64-encoded server data to be sent to the client.
+    """
+
+def authGSSServerUserName(context):
+    """
+    Get the user name of the principal trying to authenticate to the server.
+    This method must only be called after authGSSServerStep returns a complete or continue response code.
+
+    @param context: the context object returned from authGSSServerInit.
+    @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 authGSSServerStep returns a complete or continue response code.
+
+    @param context: the context object returned from authGSSServerInit.
+    @return: a string containing the target name.
+    """
+

+ 54 - 0
desktop/core/ext-py/kerberos-1.1.1/setup.py

@@ -0,0 +1,54 @@
+##
+# Copyright (c) 2006-2008 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 distutils.core import setup, Extension
+import sys
+import commands
+
+long_description = """
+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>.
+
+"""
+
+setup (
+    name = "kerberos",
+    version = "1.1.1",
+    description = "Kerberos high-level interface",
+    long_description=long_description,
+    classifiers = [
+        "License :: OSI Approved :: Apache Software License",
+        "Programming Language :: Python :: 2",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+        "Topic :: System :: Systems Administration :: Authentication/Directory"
+        ],
+    ext_modules = [
+        Extension(
+            "kerberos",
+            extra_link_args = commands.getoutput("krb5-config --libs gssapi").split(),
+            extra_compile_args = commands.getoutput("krb5-config --cflags gssapi").split(),
+            sources = [
+                "src/kerberos.c",
+                "src/kerberosbasic.c",
+                "src/kerberosgss.c",
+                "src/kerberospw.c",
+                "src/base64.c"
+            ],
+        ),
+    ],
+)

+ 120 - 0
desktop/core/ext-py/kerberos-1.1.1/src/base64.c

@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2006-2008 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, int vlen)
+{
+    char *result = (char *)malloc((vlen * 4) / 3 + 5);
+    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, int *rlen)
+{
+    *rlen = 0;
+    int c1, c2, c3, c4;
+
+    int vlen = strlen(value);
+    unsigned char *result =(unsigned char *)malloc((vlen * 3) / 4 + 1);
+    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;
+}

+ 18 - 0
desktop/core/ext-py/kerberos-1.1.1/src/base64.h

@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2006-2008 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.
+ **/
+
+char *base64_encode(const unsigned char *value, int vlen);
+unsigned char *base64_decode(const char *value, int *rlen);

+ 465 - 0
desktop/core/ext-py/kerberos-1.1.1/src/kerberos.c

@@ -0,0 +1,465 @@
+/**
+ * Copyright (c) 2006-2009 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"
+
+PyObject *KrbException_class;
+PyObject *BasicAuthException_class;
+PyObject *PwdChangeException_class;
+PyObject *GssException_class;
+
+static PyObject *checkPassword(PyObject *self, PyObject *args)
+{
+    const char *user;
+    const char *pswd;
+    const char *service;
+    const char *default_realm;
+    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, *oldpswd;
+    const char *user;
+    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;
+    const char *hostname;
+    char* result;
+
+    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 PyObject* authGSSClientInit(PyObject* self, PyObject* args, PyObject* keywds)
+{
+    const char *service;
+    gss_client_state *state;
+    PyObject *pystate;
+    static char *kwlist[] = {"service", "gssflags", NULL};
+    long int gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
+    int result = 0;
+
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|l", kwlist, &service, &gss_flags))
+        return NULL;
+
+    state = (gss_client_state *) malloc(sizeof(gss_client_state));
+    pystate = PyCObject_FromVoidPtr(state, NULL);
+
+    result = authenticate_gss_client_init(service, gss_flags, state);
+    if (result == AUTH_GSS_ERROR)
+        return NULL;
+
+    return Py_BuildValue("(iO)", result, pystate);
+}
+
+static PyObject *authGSSClientClean(PyObject *self, PyObject *args)
+{
+    gss_client_state *state;
+    PyObject *pystate;
+    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)
+    {
+        result = authenticate_gss_client_clean(state);
+
+        free(state);
+        PyCObject_SetVoidPtr(pystate, NULL);
+    }
+
+    return Py_BuildValue("i", result);
+}
+
+static PyObject *authGSSClientStep(PyObject *self, PyObject *args)
+{
+    gss_client_state *state;
+    PyObject *pystate;
+    char *challenge;
+    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_step(state, challenge);
+    if (result == AUTH_GSS_ERROR)
+        return NULL;
+
+    return Py_BuildValue("i", result);
+}
+
+static PyObject *authGSSClientResponse(PyObject *self, PyObject *args)
+{
+    gss_client_state *state;
+    PyObject *pystate;
+
+    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;
+    PyObject *pystate;
+
+    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;
+	PyObject *pystate;
+	char *challenge;
+	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;
+	PyObject *pystate;
+	char *challenge, *user = NULL;
+	int result = 0;
+
+	if (!PyArg_ParseTuple(args, "Os|z", &pystate, &challenge, &user))
+		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);
+	if (result == AUTH_GSS_ERROR)
+		return NULL;
+
+	return Py_BuildValue("i", result);
+}
+
+static PyObject *authGSSServerInit(PyObject *self, PyObject *args)
+{
+    const char *service;
+    gss_server_state *state;
+    PyObject *pystate;
+    int result = 0;
+
+    if (!PyArg_ParseTuple(args, "s", &service))
+        return NULL;
+
+    state = (gss_server_state *) malloc(sizeof(gss_server_state));
+    pystate = PyCObject_FromVoidPtr(state, NULL);
+
+    result = authenticate_gss_server_init(service, state);
+    if (result == AUTH_GSS_ERROR)
+        return NULL;
+
+    return Py_BuildValue("(iO)", result, pystate);
+}
+
+static PyObject *authGSSServerClean(PyObject *self, PyObject *args)
+{
+    gss_server_state *state;
+    PyObject *pystate;
+    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)
+    {
+        result = authenticate_gss_server_clean(state);
+
+        free(state);
+        PyCObject_SetVoidPtr(pystate, NULL);
+    }
+
+    return Py_BuildValue("i", result);
+}
+
+static PyObject *authGSSServerStep(PyObject *self, PyObject *args)
+{
+    gss_server_state *state;
+    PyObject *pystate;
+    char *challenge;
+    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 *authGSSServerResponse(PyObject *self, PyObject *args)
+{
+    gss_server_state *state;
+    PyObject *pystate;
+
+    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;
+    PyObject *pystate;
+    
+    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 *authGSSServerTargetName(PyObject *self, PyObject *args)
+{
+    gss_server_state *state;
+    PyObject *pystate;
+    
+    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."},
+    {"authGSSClientClean",  authGSSClientClean, METH_VARARGS,
+     "Terminate client-side GSSAPI operations."},
+    {"authGSSClientStep",  authGSSClientStep, METH_VARARGS,
+     "Do a client-side GSSAPI step."},
+    {"authGSSClientResponse",  authGSSClientResponse, METH_VARARGS,
+     "Get the response from the last client-side GSSAPI step."},
+    {"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."},
+    {"authGSSServerClean",  authGSSServerClean, METH_VARARGS,
+     "Terminate server-side GSSAPI operations."},
+    {"authGSSServerStep",  authGSSServerStep, METH_VARARGS,
+     "Do a server-side GSSAPI step."},
+    {"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."},
+    {"authGSSServerTargetName",  authGSSServerTargetName, METH_VARARGS,
+        "Get the target name from the last server-side GSSAPI step."},
+    {NULL, NULL, 0, NULL}        /* Sentinel */
+};
+
+PyMODINIT_FUNC initkerberos(void)
+{
+    PyObject *m,*d;
+
+    m = Py_InitModule("kerberos", KerberosMethods);
+
+    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));
+
+error:
+    if (PyErr_Occurred())
+        PyErr_SetString(PyExc_ImportError, "kerberos: init failed");
+}

+ 151 - 0
desktop/core/ext-py/kerberos-1.1.1/src/kerberosbasic.c

@@ -0,0 +1,151 @@
+/**
+ * Copyright (c) 2006-2008 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);
+    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));
+}

+ 23 - 0
desktop/core/ext-py/kerberos-1.1.1/src/kerberosbasic.h

@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2006-2008 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);

+ 609 - 0
desktop/core/ext-py/kerberos-1.1.1/src/kerberosgss.c

@@ -0,0 +1,609 @@
+/**
+ * Copyright (c) 2006-2010 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);
+
+extern PyObject *GssException_class;
+extern PyObject *KrbException_class;
+
+char* server_principal_details(const char* service, const char* hostname)
+{
+    char match[1024];
+    int 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);
+            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, long int gss_flags, gss_client_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->server_name = GSS_C_NO_NAME;
+    state->context = GSS_C_NO_CONTEXT;
+    state->gss_flags = gss_flags;
+    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;
+    }
+    
+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->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)
+{
+    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)
+    {
+        int len;
+        input_token.value = base64_decode(challenge, &len);
+        input_token.length = len;
+    }
+    
+    // Do GSSAPI step
+    maj_stat = gss_init_sec_context(&min_stat,
+                                    GSS_C_NO_CREDENTIAL,
+                                    &state->context,
+                                    state->server_name,
+                                    GSS_C_NO_OID,
+                                    (OM_uint32)state->gss_flags,
+                                    0,
+                                    GSS_C_NO_CHANNEL_BINDINGS,
+                                    &input_token,
+                                    NULL,
+                                    &output_token,
+                                    NULL,
+                                    NULL);
+    
+    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);;
+        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
+        {
+            state->username = (char *)malloc(name_token.length + 1);
+            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;
+    
+	// 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)
+	{
+		int len;
+		input_token.value = base64_decode(challenge, &len);
+		input_token.length = len;
+	}
+    
+	// Do GSSAPI step
+	maj_stat = gss_unwrap(&min_stat,
+                          state->context,
+                          &input_token,
+                          &output_token,
+                          NULL,
+                          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);
+		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)
+{
+	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)
+	{
+		int len;
+		input_token.value = base64_decode(challenge, &len);
+		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,
+						0,
+						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);;
+		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_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;
+    
+    // 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
+        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, GSS_C_ACCEPT, &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;
+    }
+    
+    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)
+    {
+        int len;
+        input_token.value = base64_decode(challenge, &len);
+        input_token.length = len;
+    }
+    else
+    {
+        PyErr_SetString(KrbException_class, "No challenge parameter in request from client");
+        ret = AUTH_GSS_ERROR;
+        goto end;
+    }
+    
+    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);
+    
+    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);;
+        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);
+    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);
+        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;
+}
+
+
+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));
+}

+ 60 - 0
desktop/core/ext-py/kerberos-1.1.1/src/kerberosgss.h

@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2006-2009 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;
+    long int         gss_flags;
+    char*            username;
+    char*            response;
+} 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;
+} gss_server_state;
+
+char* server_principal_details(const char* service, const char* hostname);
+
+int authenticate_gss_client_init(const char* service, long int gss_flags, 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);
+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 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);

+ 137 - 0
desktop/core/ext-py/kerberos-1.1.1/src/kerberospw.c

@@ -0,0 +1,137 @@
+/**
+ * Copyright (c) 2008 Guido Guenther <agx@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;
+    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);
+    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;
+        asprintf(&message, "%.*s: %.*s",
+                 (int) result_code_string.length,
+                 (char *) result_code_string.data,
+                 (int) result_string.length,
+                 (char *) result_string.data);
+        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;
+}
+

+ 24 - 0
desktop/core/ext-py/kerberos-1.1.1/src/kerberospw.h

@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2008 Guido Guenther <agx@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);

+ 1 - 0
ext/thirdparty/README.md

@@ -23,6 +23,7 @@ Checked-in third party dependencies
 |Y|eventlet|0.9.14|MIT|http://pypi.python.org/pypi/eventlet/|
 |Y|eventlet|0.9.14|MIT|http://pypi.python.org/pypi/eventlet/|
 |Y|FancyUpload|3.0|MIT|http://github.com/digitarald/digitarald-fancyupload/tarball/3.0|
 |Y|FancyUpload|3.0|MIT|http://github.com/digitarald/digitarald-fancyupload/tarball/3.0|
 |Y|greenlet|0.3.1|MIT|http://pypi.python.org/pypi/greenlet/|
 |Y|greenlet|0.3.1|MIT|http://pypi.python.org/pypi/greenlet/|
+|Y|kerberos|1.1.1|ASL2|http://pypi.python.org/pypi/kerberos|
 |Y|lockfile|0.8|MIT|http://smontanaro.dyndns.org/python/lockfile-0.8.tar.gz|
 |Y|lockfile|0.8|MIT|http://smontanaro.dyndns.org/python/lockfile-0.8.tar.gz|
 |Y|lxml|2.2.2|BSD|http://codespeak.net/lxml/|
 |Y|lxml|2.2.2|BSD|http://codespeak.net/lxml/|
 |Y|Mako|0.3.4|MIT|http://makotemplates.org/|
 |Y|Mako|0.3.4|MIT|http://makotemplates.org/|