Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch requirement to pykerberos #38

Closed
wants to merge 1 commit into from
Closed

Conversation

Daviey
Copy link

@Daviey Daviey commented Sep 18, 2014

Previously requirements.txt declared a requirement on
kerberos==1.1.1. However, pykerberos is more active
upstream. We also remove the version tracking as the
current minimum version on pypi is functional.

Therefore switching to that, which closes #37

Signed-off-by: Dave Walker (Daviey) email@daviey.com

Previously requirements.txt declared a requirement on
kerberos==1.1.1.  However, pykerberos is more active
upstream.  We also remove the version tracking as the
current minimum version on pypi is functional.

Therefore switching to that, which closes requests#37

Signed-off-by: Dave Walker (Daviey) <email@daviey.com>
@Lukasa
Copy link
Member

Lukasa commented Sep 18, 2014

I have no particular objection to this, however I think realistically you only tested with pykerberos 1.1.5, so we should pin to that version.

@Daviey
Copy link
Author

Daviey commented Sep 18, 2014

Personally, I feel that declaring pins purely as a' 'known-good' version is an abuse of requirements.txt. My understanding it is that it should be used to declare known bad compatibilities.

This is especially important if pykerberos needed to do a security release, we'd still be pinned to an older version.

Ie, <1.1.1 is bad.. or API/ABI breaks on version >=1.1.5... However, just as a QA perspective, it feels like abuse.

That said, it is your upstream and not mine... And if you feel strongly about this, i'll change it. :)

@sigmavirus24
Copy link
Contributor

I think what @Lukasa is advocating is a line like: pykerberos==1.1.1,==1.1.5 which believe it or not is valid. I'd rather find out if we can get a real CI system set up so that we can test these more reliably on whichever versions we choose to support.

@Daviey
Copy link
Author

Daviey commented Sep 18, 2014

Sure, that makes sense.. I was disagreeing that this project should presume
that current+1 release of pykerberos may not work, and block automatic
updating to it.

If current+1 comes out and it does not work, then adding !1.16 to the
requirements to blacklist that version makes sense, whilst raising
regression bugs with the pykerberos project (or changing this project if
suitable).

However, I appreciate that this is not my project.. And if you feel that
the workflow you outlined is better (whilst I respectfully disagree ), I
will update the pull request.
On 18 Sep 2014 18:52, "Ian Cordasco" notifications@github.com wrote:

I think what @Lukasa https://github.com/Lukasa is advocating is a line
like: pykerberos==1.1.1,==1.1.5 which believe it or not is valid. I'd
rather find out if we can get a real CI system set up so that we can test
these more reliably on whichever versions we choose to support.


Reply to this email directly or view it on GitHub
#38 (comment)
.

@Lukasa
Copy link
Member

Lukasa commented Sep 19, 2014

I was disagreeing that this project should presume that current+1 release of pykerberos may not work, and block automatic updating to it.

That's an interesting position. I think it is worse to break user code unexpectedly than it is to delay our support for new upstream versions. I suspect you disagree. =)

@tardyp
Copy link

tardyp commented Jan 27, 2015

About pykerberos, there are not much information on pypi about this fork. No maintainer, no source code repository.

Looking at the diff, there are:

  • python 2.6, and python 3.x compatibility fixes
  • some int -> size_t fixes
  • some stack variables initialization
  • support for changing the principal
diff --git a/PKG-INFO b/PKG-INFO
index cd8794a..414de15 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,11 +1,11 @@
-Metadata-Version: 1.0
-Name: kerberos
-Version: 1.1.1
-Summary: Kerberos high-level interface
+Metadata-Version: 1.1
+Name: pykerberos
+Version: 1.1.5
+Summary: High-level interface to Kerberos
 Home-page: UNKNOWN
 Author: UNKNOWN
 Author-email: UNKNOWN
-License: UNKNOWN
+License: ASL 2.0
 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,
@@ -16,5 +16,6 @@ Description:
 Platform: UNKNOWN
 Classifier: License :: OSI Approved :: Apache Software License
 Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Topic :: System :: Systems Administration :: Authentication/Directory
diff --git a/README.txt b/README.txt
index 3ee6103..f248e03 100644
--- a/README.txt
+++ b/README.txt
@@ -1,7 +1,7 @@
 =========================================================
 PyKerberos Package

-Copyright (c) 2006-2008 Apple Inc. All rights reserved.
+Copyright (c) 2006-2013 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.
diff --git a/pysrc/kerberos.py b/pysrc/kerberos.py
index eb8839f..8c6a712 100644
--- a/pysrc/kerberos.py
+++ b/pysrc/kerberos.py
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2006-2009 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2013 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.
@@ -99,7 +99,7 @@ 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):
+def authGSSClientInit(service, principal=None, 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
@@ -107,6 +107,8 @@ def authGSSClientInit(service, gssflags=GSS_C_MUTUAL_FLAG|GSS_C_SEQUENCE_FLAG):

     @param service: a string containing the service principal in the form 'type@fqdn'
         (e.g. 'imap@mail.apple.com').
+    @param principal: optional string containing the client principal in the form 'user@realm'
+        (e.g. 'jdoe@example.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)
@@ -141,6 +143,14 @@ def authGSSClientResponse(context):
     @return: a string containing the base64-encoded client data to be sent to the server.
     """

+def authGSSClientResponseConf(context):
+    """
+    Returns 1 if confidentiality was enabled in the previously unwrapped buffer.  0 otherwise.
+
+    @param context: the context object returned from authGSSClientInit.
+    @return: an integer representing the confidentiality of the previously unwrapped buffer.
+    """
+
 def authGSSClientUserName(context):
     """
     Get the user name of the principal authenticated via the now complete GSSAPI client-side operations.
diff --git a/setup.py b/setup.py
index 80e1529..edccc54 100644
--- a/setup.py
+++ b/setup.py
@@ -14,9 +14,9 @@
 # limitations under the License.
 ##

-from distutils.core import setup, Extension
+from setuptools import setup, Extension
+import subprocess
 import sys
-import commands

 long_description = """
 This Python package is a high-level wrapper for Kerberos (GSSAPI) operations.
@@ -26,22 +26,48 @@ Kerberos authentication based on <http://www.ietf.org/rfc/rfc4559.txt>.

 """

+# Backport from Python 2.7 in case we're in 2.6.
+def check_output(*popenargs, **kwargs):
+    process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
+    output, unused_err = process.communicate()
+    retcode = process.poll()
+    if retcode:
+        cmd = kwargs.get("args")
+        if cmd is None:
+            cmd = popenargs[0]
+        raise subprocess.CalledProcessError(retcode, cmd, output=output)
+    return output
+
+
+extra_link_args = check_output(
+    ["krb5-config", "--libs", "gssapi"],
+    universal_newlines=True
+).split()
+
+extra_compile_args = check_output(
+    ["krb5-config", "--cflags", "gssapi"],
+    universal_newlines=True
+).split()
+
+
 setup (
-    name = "kerberos",
-    version = "1.1.1",
-    description = "Kerberos high-level interface",
+    name = "pykerberos",
+    version = "1.1.5",
+    description = "High-level interface to Kerberos",
     long_description=long_description,
+    license="ASL 2.0",
     classifiers = [
         "License :: OSI Approved :: Apache Software License",
         "Programming Language :: Python :: 2",
+        "Programming Language :: Python :: 3",
         "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(),
+            extra_link_args = extra_link_args,
+            extra_compile_args = extra_compile_args,
             sources = [
                 "src/kerberos.c",
                 "src/kerberosbasic.c",
diff --git a/src/base64.c b/src/base64.c
index 4232106..877379f 100644
--- a/src/base64.c
+++ b/src/base64.c
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2006-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2006-2013 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.
@@ -40,7 +40,7 @@ static signed char index_64[128] =
 // 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 *base64_encode(const unsigned char *value, size_t vlen)
 {
     char *result = (char *)malloc((vlen * 4) / 3 + 5);
     char *out = result;
@@ -72,12 +72,12 @@ char *base64_encode(const unsigned char *value, int vlen)
 // 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)
+unsigned char *base64_decode(const char *value, size_t *rlen)
 {
     *rlen = 0;
     int c1, c2, c3, c4;

-    int vlen = strlen(value);
+    size_t vlen = strlen(value);
     unsigned char *result =(unsigned char *)malloc((vlen * 3) / 4 + 1);
     unsigned char *out = result;

diff --git a/src/base64.h b/src/base64.h
index f0e1f06..9ea9c6b 100644
--- a/src/base64.h
+++ b/src/base64.h
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2006-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2006-2013 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.
@@ -14,5 +14,6 @@
  * limitations under the License.
  **/

-char *base64_encode(const unsigned char *value, int vlen);
-unsigned char *base64_decode(const char *value, int *rlen);
+#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/src/kerberos.c b/src/kerberos.c
index e0d8938..740d9e1 100644
--- a/src/kerberos.c
+++ b/src/kerberos.c
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2006-2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2006-2013 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.
@@ -20,6 +20,10 @@
 #include "kerberospw.h"
 #include "kerberosgss.h"

+#if PY_MAJOR_VERSION >= 3
+    #define PyInt_FromLong PyLong_FromLong
+#endif
+
 PyObject *KrbException_class;
 PyObject *BasicAuthException_class;
 PyObject *PwdChangeException_class;
@@ -27,10 +31,10 @@ PyObject *GssException_class;

 static PyObject *checkPassword(PyObject *self, PyObject *args)
 {
-    const char *user;
-    const char *pswd;
-    const char *service;
-    const char *default_realm;
+    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))
@@ -46,8 +50,9 @@ static PyObject *checkPassword(PyObject *self, PyObject *args)

 static PyObject *changePassword(PyObject *self, PyObject *args)
 {
-    const char *newpswd, *oldpswd;
-    const char *user;
+    const char *newpswd = NULL;
+    const char *oldpswd = NULL;
+    const char *user = NULL;
     int result = 0;

     if (!PyArg_ParseTuple(args, "sss", &user, &oldpswd, &newpswd))
@@ -63,8 +68,8 @@ static PyObject *changePassword(PyObject *self, PyObject *args)

 static PyObject *getServerPrincipalDetails(PyObject *self, PyObject *args)
 {
-    const char *service;
-    const char *hostname;
+    const char *service = NULL;
+    const char *hostname = NULL;
     char* result;

     if (!PyArg_ParseTuple(args, "ss", &service, &hostname))
@@ -84,20 +89,25 @@ static PyObject *getServerPrincipalDetails(PyObject *self, PyObject *args)

 static PyObject* authGSSClientInit(PyObject* self, PyObject* args, PyObject* keywds)
 {
-    const char *service;
+    const char *service = NULL;
+    const char *principal = NULL;
     gss_client_state *state;
     PyObject *pystate;
-    static char *kwlist[] = {"service", "gssflags", NULL};
+    static char *kwlist[] = {"service", "principal", "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))
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|zl", kwlist, &service, &principal, &gss_flags))
         return NULL;

     state = (gss_client_state *) malloc(sizeof(gss_client_state));
+#if PY_MAJOR_VERSION >= 3
+    pystate = PyCapsule_New(state, NULL, NULL);
+#else
     pystate = PyCObject_FromVoidPtr(state, NULL);
+#endif

-    result = authenticate_gss_client_init(service, gss_flags, state);
+    result = authenticate_gss_client_init(service, principal, gss_flags, state);
     if (result == AUTH_GSS_ERROR)
         return NULL;

@@ -113,18 +123,30 @@ static PyObject *authGSSClientClean(PyObject *self, PyObject *args)
     if (!PyArg_ParseTuple(args, "O", &pystate))
         return NULL;

+#if PY_MAJOR_VERSION >= 3
+    if (!PyCapsule_CheckExact(pystate)) {
+#else
     if (!PyCObject_Check(pystate)) {
+#endif
         PyErr_SetString(PyExc_TypeError, "Expected a context object");
         return NULL;
     }

+#if PY_MAJOR_VERSION >= 3
+    state = PyCapsule_GetPointer(pystate, NULL);
+#else
     state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+#endif
     if (state != NULL)
     {
         result = authenticate_gss_client_clean(state);

         free(state);
+#if PY_MAJOR_VERSION >= 3
+        PyCapsule_SetPointer(pystate, NULL);
+#else
         PyCObject_SetVoidPtr(pystate, NULL);
+#endif
     }

     return Py_BuildValue("i", result);
@@ -134,18 +156,26 @@ static PyObject *authGSSClientStep(PyObject *self, PyObject *args)
 {
     gss_client_state *state;
     PyObject *pystate;
-    char *challenge;
+    char *challenge = NULL;
     int result = 0;

     if (!PyArg_ParseTuple(args, "Os", &pystate, &challenge))
         return NULL;

+#if PY_MAJOR_VERSION >= 3
+    if (!PyCapsule_CheckExact(pystate)) {
+#else
     if (!PyCObject_Check(pystate)) {
+#endif
         PyErr_SetString(PyExc_TypeError, "Expected a context object");
         return NULL;
     }

+#if PY_MAJOR_VERSION >= 3
+    state = PyCapsule_GetPointer(pystate, NULL);
+#else
     state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+#endif
     if (state == NULL)
         return NULL;

@@ -156,6 +186,34 @@ static PyObject *authGSSClientStep(PyObject *self, PyObject *args)
     return Py_BuildValue("i", result);
 }

+static PyObject *authGSSClientResponseConf(PyObject *self, PyObject *args)
+{
+    gss_client_state *state;
+    PyObject *pystate;
+
+    if (!PyArg_ParseTuple(args, "O", &pystate))
+        return NULL;
+
+#if PY_MAJOR_VERSION >= 3
+    if (!PyCapsule_CheckExact(pystate)) {
+#else
+    if (!PyCObject_Check(pystate)) {
+#endif
+        PyErr_SetString(PyExc_TypeError, "Expected a context object");
+        return NULL;
+    }
+
+#if PY_MAJOR_VERSION >= 3
+    state = PyCapsule_GetPointer(pystate, NULL);
+#else
+    state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+#endif
+    if (state == NULL)
+        return NULL;
+
+    return Py_BuildValue("i", state->responseConf);
+}
+
 static PyObject *authGSSClientResponse(PyObject *self, PyObject *args)
 {
     gss_client_state *state;
@@ -164,12 +222,20 @@ static PyObject *authGSSClientResponse(PyObject *self, PyObject *args)
     if (!PyArg_ParseTuple(args, "O", &pystate))
         return NULL;

+#if PY_MAJOR_VERSION >= 3
+    if (!PyCapsule_CheckExact(pystate)) {
+#else
     if (!PyCObject_Check(pystate)) {
+#endif
         PyErr_SetString(PyExc_TypeError, "Expected a context object");
         return NULL;
     }

+#if PY_MAJOR_VERSION >= 3
+    state = PyCapsule_GetPointer(pystate, NULL);
+#else
     state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+#endif
     if (state == NULL)
         return NULL;

@@ -184,12 +250,20 @@ static PyObject *authGSSClientUserName(PyObject *self, PyObject *args)
     if (!PyArg_ParseTuple(args, "O", &pystate))
         return NULL;

+#if PY_MAJOR_VERSION >= 3
+    if (!PyCapsule_CheckExact(pystate)) {
+#else
     if (!PyCObject_Check(pystate)) {
+#endif
         PyErr_SetString(PyExc_TypeError, "Expected a context object");
         return NULL;
     }

+#if PY_MAJOR_VERSION >= 3
+    state = PyCapsule_GetPointer(pystate, NULL);
+#else
     state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+#endif
     if (state == NULL)
         return NULL;

@@ -200,18 +274,26 @@ static PyObject *authGSSClientUnwrap(PyObject *self, PyObject *args)
 {
    gss_client_state *state;
    PyObject *pystate;
-   char *challenge;
+   char *challenge = NULL;
    int result = 0;

    if (!PyArg_ParseTuple(args, "Os", &pystate, &challenge))
        return NULL;

-   if (!PyCObject_Check(pystate)) {
+#if PY_MAJOR_VERSION >= 3
+    if (!PyCapsule_CheckExact(pystate)) {
+#else
+    if (!PyCObject_Check(pystate)) {
+#endif
        PyErr_SetString(PyExc_TypeError, "Expected a context object");
        return NULL;
    }

-   state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+#if PY_MAJOR_VERSION >= 3
+    state = PyCapsule_GetPointer(pystate, NULL);
+#else
+    state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+#endif
    if (state == NULL)
        return NULL;

@@ -226,22 +308,32 @@ static PyObject *authGSSClientWrap(PyObject *self, PyObject *args)
 {
    gss_client_state *state;
    PyObject *pystate;
-   char *challenge, *user = NULL;
+   char *challenge = NULL;
+   char *user = NULL;
+    int protect = 0;
    int result = 0;

-   if (!PyArg_ParseTuple(args, "Os|z", &pystate, &challenge, &user))
+   if (!PyArg_ParseTuple(args, "Os|zi", &pystate, &challenge, &user, &protect))
        return NULL;

-   if (!PyCObject_Check(pystate)) {
+#if PY_MAJOR_VERSION >= 3
+    if (!PyCapsule_CheckExact(pystate)) {
+#else
+    if (!PyCObject_Check(pystate)) {
+#endif
        PyErr_SetString(PyExc_TypeError, "Expected a context object");
        return NULL;
    }

-   state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+#if PY_MAJOR_VERSION >= 3
+    state = PyCapsule_GetPointer(pystate, NULL);
+#else
+    state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+#endif
    if (state == NULL)
        return NULL;

-   result = authenticate_gss_client_wrap(state, challenge, user);
+   result = authenticate_gss_client_wrap(state, challenge, user, protect);
    if (result == AUTH_GSS_ERROR)
        return NULL;

@@ -250,7 +342,7 @@ static PyObject *authGSSClientWrap(PyObject *self, PyObject *args)

 static PyObject *authGSSServerInit(PyObject *self, PyObject *args)
 {
-    const char *service;
+    const char *service = NULL;
     gss_server_state *state;
     PyObject *pystate;
     int result = 0;
@@ -259,7 +351,11 @@ static PyObject *authGSSServerInit(PyObject *self, PyObject *args)
         return NULL;

     state = (gss_server_state *) malloc(sizeof(gss_server_state));
+#if PY_MAJOR_VERSION >= 3
+    pystate = PyCapsule_New(state, NULL, NULL);
+#else
     pystate = PyCObject_FromVoidPtr(state, NULL);
+#endif

     result = authenticate_gss_server_init(service, state);
     if (result == AUTH_GSS_ERROR)
@@ -277,18 +373,30 @@ static PyObject *authGSSServerClean(PyObject *self, PyObject *args)
     if (!PyArg_ParseTuple(args, "O", &pystate))
         return NULL;

+#if PY_MAJOR_VERSION >= 3
+    if (!PyCapsule_CheckExact(pystate)) {
+#else
     if (!PyCObject_Check(pystate)) {
+#endif
         PyErr_SetString(PyExc_TypeError, "Expected a context object");
         return NULL;
     }

-    state = (gss_server_state *)PyCObject_AsVoidPtr(pystate);
+#if PY_MAJOR_VERSION >= 3
+    state = PyCapsule_GetPointer(pystate, NULL);
+#else
+    state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+#endif
     if (state != NULL)
     {
         result = authenticate_gss_server_clean(state);

         free(state);
+#if PY_MAJOR_VERSION >= 3
+        PyCapsule_SetPointer(pystate, NULL);
+#else
         PyCObject_SetVoidPtr(pystate, NULL);
+#endif
     }

     return Py_BuildValue("i", result);
@@ -298,18 +406,26 @@ static PyObject *authGSSServerStep(PyObject *self, PyObject *args)
 {
     gss_server_state *state;
     PyObject *pystate;
-    char *challenge;
+    char *challenge = NULL;
     int result = 0;

     if (!PyArg_ParseTuple(args, "Os", &pystate, &challenge))
         return NULL;

+#if PY_MAJOR_VERSION >= 3
+    if (!PyCapsule_CheckExact(pystate)) {
+#else
     if (!PyCObject_Check(pystate)) {
+#endif
         PyErr_SetString(PyExc_TypeError, "Expected a context object");
         return NULL;
     }

-    state = (gss_server_state *)PyCObject_AsVoidPtr(pystate);
+#if PY_MAJOR_VERSION >= 3
+    state = PyCapsule_GetPointer(pystate, NULL);
+#else
+    state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+#endif
     if (state == NULL)
         return NULL;

@@ -328,12 +444,20 @@ static PyObject *authGSSServerResponse(PyObject *self, PyObject *args)
     if (!PyArg_ParseTuple(args, "O", &pystate))
         return NULL;

+#if PY_MAJOR_VERSION >= 3
+    if (!PyCapsule_CheckExact(pystate)) {
+#else
     if (!PyCObject_Check(pystate)) {
+#endif
         PyErr_SetString(PyExc_TypeError, "Expected a context object");
         return NULL;
     }

-    state = (gss_server_state *)PyCObject_AsVoidPtr(pystate);
+#if PY_MAJOR_VERSION >= 3
+    state = PyCapsule_GetPointer(pystate, NULL);
+#else
+    state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+#endif
     if (state == NULL)
         return NULL;

@@ -348,12 +472,20 @@ static PyObject *authGSSServerUserName(PyObject *self, PyObject *args)
     if (!PyArg_ParseTuple(args, "O", &pystate))
         return NULL;

+#if PY_MAJOR_VERSION >= 3
+    if (!PyCapsule_CheckExact(pystate)) {
+#else
     if (!PyCObject_Check(pystate)) {
+#endif
         PyErr_SetString(PyExc_TypeError, "Expected a context object");
         return NULL;
     }

-    state = (gss_server_state *)PyCObject_AsVoidPtr(pystate);
+#if PY_MAJOR_VERSION >= 3
+    state = PyCapsule_GetPointer(pystate, NULL);
+#else
+    state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+#endif
     if (state == NULL)
         return NULL;

@@ -368,12 +500,20 @@ static PyObject *authGSSServerTargetName(PyObject *self, PyObject *args)
     if (!PyArg_ParseTuple(args, "O", &pystate))
         return NULL;

+#if PY_MAJOR_VERSION >= 3
+    if (!PyCapsule_CheckExact(pystate)) {
+#else
     if (!PyCObject_Check(pystate)) {
+#endif
         PyErr_SetString(PyExc_TypeError, "Expected a context object");
         return NULL;
     }

-    state = (gss_server_state *)PyCObject_AsVoidPtr(pystate);
+#if PY_MAJOR_VERSION >= 3
+    state = PyCapsule_GetPointer(pystate, NULL);
+#else
+    state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
+#endif
     if (state == NULL)
         return NULL;

@@ -395,6 +535,8 @@ static PyMethodDef KerberosMethods[] = {
      "Do a client-side GSSAPI step."},
     {"authGSSClientResponse",  authGSSClientResponse, METH_VARARGS,
      "Get the response from the last 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,
@@ -416,11 +558,36 @@ static PyMethodDef KerberosMethods[] = {
     {NULL, NULL, 0, NULL}        /* Sentinel */
 };

-PyMODINIT_FUNC initkerberos(void)
+#if PY_MAJOR_VERSION >= 3
+    static struct PyModuleDef moduledef = {
+        PyModuleDef_HEAD_INIT,
+        "kerberos",          /* m_name */
+        "High-level interface to kerberos",  /* m_doc */
+        -1,                  /* m_size */
+        KerberosMethods,     /* m_methods */
+        NULL,                /* m_reload */
+        NULL,                /* m_traverse */
+        NULL,                /* m_clear */
+        NULL,                /* m_free */
+    };
+
+#endif
+
+
+
+#if PY_MAJOR_VERSION >= 3
+PyObject* PyInit_kerberos(void)
+#else
+void initkerberos(void)
+#endif
 {
     PyObject *m,*d;

+#if PY_MAJOR_VERSION >= 3
+    m = PyModule_Create(&moduledef);
+#else
     m = Py_InitModule("kerberos", KerberosMethods);
+#endif

     d = PyModule_GetDict(m);

@@ -462,4 +629,7 @@ PyMODINIT_FUNC initkerberos(void)
 error:
     if (PyErr_Occurred())
         PyErr_SetString(PyExc_ImportError, "kerberos: init failed");
+#if PY_MAJOR_VERSION >= 3
+    return m;
+#endif
 }
diff --git a/src/kerberosbasic.c b/src/kerberosbasic.c
index 8b38f11..0c7bdd7 100644
--- a/src/kerberosbasic.c
+++ b/src/kerberosbasic.c
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2006-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2006-2013 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.
diff --git a/src/kerberosbasic.h b/src/kerberosbasic.h
index d3dca95..0a91455 100644
--- a/src/kerberosbasic.h
+++ b/src/kerberosbasic.h
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2006-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2006-2013 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.
diff --git a/src/kerberosgss.c b/src/kerberosgss.c
index 42127fb..158b55c 100644
--- a/src/kerberosgss.c
+++ b/src/kerberosgss.c
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2006-2013 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.
@@ -32,7 +32,7 @@ extern PyObject *KrbException_class;
 char* server_principal_details(const char* service, const char* hostname)
 {
     char match[1024];
-    int match_len = 0;
+    size_t match_len = 0;
     char* result = NULL;

     int code;
@@ -106,16 +106,18 @@ end:
     return result;
 }

-int authenticate_gss_client_init(const char* service, long int gss_flags, gss_client_state* state)
+int authenticate_gss_client_init(const char* service, const char* principal, 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;
+    gss_buffer_desc principal_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->client_creds = GSS_C_NO_CREDENTIAL;
     state->username = NULL;
     state->response = NULL;

@@ -132,6 +134,40 @@ int authenticate_gss_client_init(const char* service, long int gss_flags, gss_cl
         goto end;
     }

+    // Get credential for principal
+    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;
 }
@@ -146,6 +182,8 @@ int authenticate_gss_client_clean(gss_client_state *state)
         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)
+        maj_stat = gss_release_cred(&min_stat, &state->client_creds);
     if (state->username != NULL)
     {
         free(state->username);
@@ -178,14 +216,15 @@ int authenticate_gss_client_step(gss_client_state* state, const char* challenge)
     // If there is a challenge (data from the server) we need to give it to GSS
     if (challenge && *challenge)
     {
-        int len;
+        size_t len;
         input_token.value = base64_decode(challenge, &len);
         input_token.length = len;
     }

     // Do GSSAPI step
+    Py_BEGIN_ALLOW_THREADS
     maj_stat = gss_init_sec_context(&min_stat,
-                                    GSS_C_NO_CREDENTIAL,
+                                    state->client_creds,
                                     &state->context,
                                     state->server_name,
                                     GSS_C_NO_OID,
@@ -197,6 +236,7 @@ int authenticate_gss_client_step(gss_client_state* state, const char* challenge)
                                     &output_token,
                                     NULL,
                                     NULL);
+    Py_END_ALLOW_THREADS

     if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED))
     {
@@ -262,18 +302,20 @@ int authenticate_gss_client_unwrap(gss_client_state *state, const char *challeng
    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)
    {
-       int len;
+       size_t len;
        input_token.value = base64_decode(challenge, &len);
        input_token.length = len;
    }
@@ -283,7 +325,7 @@ int authenticate_gss_client_unwrap(gss_client_state *state, const char *challeng
                           state->context,
                           &input_token,
                           &output_token,
-                          NULL,
+                          &conf,
                           NULL);

    if (maj_stat != GSS_S_COMPLETE)
@@ -299,6 +341,7 @@ int authenticate_gss_client_unwrap(gss_client_state *state, const char *challeng
    if (output_token.length)
    {
        state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);
+        state->responseConf = conf;
        maj_stat = gss_release_buffer(&min_stat, &output_token);
    }
 end:
@@ -309,7 +352,7 @@ end:
    return ret;
 }

-int authenticate_gss_client_wrap(gss_client_state* state, const char* challenge, const char* user)
+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;
@@ -328,7 +371,7 @@ int authenticate_gss_client_wrap(gss_client_state* state, const char* challenge,

    if (challenge && *challenge)
    {
-       int len;
+       size_t len;
        input_token.value = base64_decode(challenge, &len);
        input_token.length = len;
    }
@@ -360,7 +403,7 @@ int authenticate_gss_client_wrap(gss_client_state* state, const char* challenge,
    // Do GSSAPI wrap
    maj_stat = gss_wrap(&min_stat,
                        state->context,
-                       0,
+                       protect,
                        GSS_C_QOP_DEFAULT,
                        &input_token,
                        NULL,
@@ -488,7 +531,7 @@ int authenticate_gss_server_step(gss_server_state *state, const char *challenge)
     // If there is a challenge (data from the server) we need to give it to GSS
     if (challenge && *challenge)
     {
-        int len;
+        size_t len;
         input_token.value = base64_decode(challenge, &len);
         input_token.length = len;
     }
@@ -499,6 +542,7 @@ int authenticate_gss_server_step(gss_server_state *state, const char *challenge)
         goto end;
     }

+    Py_BEGIN_ALLOW_THREADS
     maj_stat = gss_accept_sec_context(&min_stat,
                                       &state->context,
                                       state->server_creds,
@@ -510,6 +554,7 @@ int authenticate_gss_server_step(gss_server_state *state, const char *challenge)
                                       NULL,
                                       NULL,
                                       &state->client_creds);
+    Py_END_ALLOW_THREADS

     if (GSS_ERROR(maj_stat))
     {
diff --git a/src/kerberosgss.h b/src/kerberosgss.h
index 0e034aa..1bcefca 100644
--- a/src/kerberosgss.h
+++ b/src/kerberosgss.h
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2006-2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2006-2013 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.
@@ -32,8 +32,10 @@ typedef struct {
     gss_ctx_id_t     context;
     gss_name_t       server_name;
     long int         gss_flags;
+    gss_cred_id_t    client_creds;
     char*            username;
     char*            response;
+    int              responseConf;
 } gss_client_state;

 typedef struct {
@@ -49,11 +51,11 @@ typedef struct {

 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_init(const char* service, const char* principal, 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_client_wrap(gss_client_state* state, const char* challenge, const char* user, int protect);

 int authenticate_gss_server_init(const char* service, gss_server_state* state);
 int authenticate_gss_server_clean(gss_server_state *state);

@tardyp
Copy link

tardyp commented Jan 27, 2015

There is also https://pypi.python.org/pypi/python-gssapi/, which looks like much better maintained that the Apple one (or at least more openly)
https://pypi.python.org/pypi/python-gssapi/

@mkomitee
Copy link
Collaborator

If anyone's looking for my input, if pykerberos is a maintained fork of the original kerberos library, then it makes sense for us to switch.

As to pinning, if the pykerberos maintainers commit to sever, couldn't we make the requirement >= 1.1.1 and < 2.0.0?

@mkomitee
Copy link
Collaborator

As to pygssapi, I've used it on another project and was going to work on converting requests-kerberos to use it, but just when I went to do so, the pygssapi project started a conversion from C extension (maybe python) to cffi. When that happened, things broke. If things have settled, I'd be open to a switch. -- this was a while ago, so my memory may be fuzzy on the details.

@02strich
Copy link

I am the current maintainer of pykerberos and intend to fully semver

@mkomitee
Copy link
Collaborator

That was a typo. I meant semver as in semantic versioning.

@02strich
Copy link

My bad, copying the typo but meaning the same (updated the post)

@mkomitee
Copy link
Collaborator

I'm currently on vacation, but when I get back and have a chance ill take a look.

@mkomitee
Copy link
Collaborator

FYI, I just verified in #52 that pykerberos works in the base-case (I haven't tested it with delegation).

pypi now includes a link to the issue tracker at https://github.com/02strich/pykerberos, which was updated 4 months ago.

sigmavirus24 added a commit that referenced this pull request Oct 31, 2015
…cy-51

Fix dependency for kerberos-sspi on Windows; else use pykerberos. (Fixes #51, #38)
@sigmavirus24
Copy link
Contributor

Addressed by #56

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Pinned to kerberos==1.1.1
6 participants