Hello community, here is the log from the commit of package python-pykerberos for openSUSE:Factory checked in at 2019-02-05 11:19:06 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pykerberos (Old) and /work/SRC/openSUSE:Factory/.python-pykerberos.new.28833 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pykerberos" Tue Feb 5 11:19:06 2019 rev:5 rq:671252 version:1.2.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pykerberos/python-pykerberos.changes 2018-02-10 17:57:24.833768401 +0100 +++ /work/SRC/openSUSE:Factory/.python-pykerberos.new.28833/python-pykerberos.changes 2019-02-05 11:19:10.432882188 +0100 @@ -1,0 +2,19 @@ +Tue Feb 5 05:19:12 UTC 2019 - Thomas Bechtold <[email protected]> + +- update to 1.2.1: + * add crusty CMake support + * minor change to CBT struct + * get build working on OSX again + * changes based on upstream PR + * add CHANGELOG with entries for 1.2.1.beta1 + * add winrm-style IOV encryption support + * bump repo version to 1.2.1dev + * Removing 3.2 from travis b/c it is no longer fully supported + * Added method to build the CBT structure and modified authGSSClientStep + to pass along this CBT structure if it is set + * obviously, verify shouldn't be const + * 1.2.1 final release + * Adding life support note to README +- Use %license macro + +------------------------------------------------------------------- Old: ---- pykerberos-1.1.14.tar.gz New: ---- pykerberos-1.2.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pykerberos.spec ++++++ --- /var/tmp/diff_new_pack.gBr64H/_old 2019-02-05 11:19:10.940881909 +0100 +++ /var/tmp/diff_new_pack.gBr64H/_new 2019-02-05 11:19:10.940881909 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-pykerberos # -# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -12,13 +12,13 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-pykerberos -Version: 1.1.14 +Version: 1.2.1 Release: 0 Summary: High-level interface to Kerberos License: Apache-2.0 @@ -53,7 +53,8 @@ %files %{python_files} %defattr(-,root,root,-) -%doc README.txt LICENSE +%doc README.txt +%license LICENSE %{python_sitearch}/* %changelog ++++++ pykerberos-1.1.14.tar.gz -> pykerberos-1.2.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykerberos-1.1.14/PKG-INFO new/pykerberos-1.2.1/PKG-INFO --- old/pykerberos-1.1.14/PKG-INFO 2017-01-26 08:32:40.000000000 +0100 +++ new/pykerberos-1.2.1/PKG-INFO 2017-12-20 19:27:02.000000000 +0100 @@ -1,11 +1,12 @@ Metadata-Version: 1.1 Name: pykerberos -Version: 1.1.14 +Version: 1.2.1 Summary: High-level interface to Kerberos Home-page: UNKNOWN Author: UNKNOWN Author-email: UNKNOWN License: ASL 2.0 +Description-Content-Type: 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, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykerberos-1.1.14/README.txt new/pykerberos-1.2.1/README.txt --- old/pykerberos-1.1.14/README.txt 2014-03-25 10:21:31.000000000 +0100 +++ new/pykerberos-1.2.1/README.txt 2017-12-07 02:02:50.000000000 +0100 @@ -1,3 +1,6 @@ +NOTE: this fork of ccs-kerberos is currently on life support mode as Apple has resumed work on upstream. Please try to use https://pypi.python.org/pypi/kerberos instead of this fork if possible. + + ========================================================= PyKerberos Package @@ -62,6 +65,59 @@ -p : password for basic authenticate -s : service principal for GSSAPI authentication (defaults to '[email protected]') +================ +CHANNEL BINDINGS +================ + +You can use this library to authenticate with Channel Binding support. Channel +Bindings are tags that identify the particular data channel being used with the +authentication. You can use Channel bindings to offer more proof of a valid +identity. Some services like Microsoft's Extended Protection can enforce +Channel Binding support on authorisation and you can use this library to meet +those requirements. + +More details on Channel Bindings as set through the GSSAPI can be found here +<https://docs.oracle.com/cd/E19455-01/806-3814/overview-52/index.html>. Using +TLS as a example this is how you would add Channel Binding support to your +authentication mechanism. The following code snippet is based on RFC5929 +<https://tools.ietf.org/html/rfc5929> using the 'tls-server-endpoint-point' +type. + +.. code-block:: python + + import hashlib + + def get_channel_bindings_application_data(socket): + # This is a highly simplified example, there are other use cases + # where you might need to use different hash types or get a socket + # object somehow. + server_certificate = socket.getpeercert(True) + certificate_hash = hashlib.sha256(server_certificate).hexdigest().upper() + certificate_digest = base64.b16decode(certificate_hash) + application_data = b'tls-server-end-point:%s' % certificate_digest + + return application_data + + def main(): + # Code to setup a socket with the server + # A lot of code to setup the handshake and start the auth process + socket = getsocketsomehow() + + # Connect to the host and start the auth process + + # Build the channel bindings object + application_data = get_channel_bindings_application_data(socket) + channel_bindings = kerberos.channelBindings(application_data=application_data) + + # More work to get responses from the server + + result, context = kerberos.authGSSClientInit(kerb_spn, gssflags=gssflags, principal=principal) + + # Pass through the channel_bindings object as created in the kerberos.channelBindings method + result = kerberos.authGSSClientStep(context, neg_resp_value, channel_bindings=channel_bindings) + + # Repeat as necessary + =========== Python APIs =========== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykerberos-1.1.14/pykerberos.egg-info/PKG-INFO new/pykerberos-1.2.1/pykerberos.egg-info/PKG-INFO --- old/pykerberos-1.1.14/pykerberos.egg-info/PKG-INFO 2017-01-26 08:32:40.000000000 +0100 +++ new/pykerberos-1.2.1/pykerberos.egg-info/PKG-INFO 2017-12-20 19:27:02.000000000 +0100 @@ -1,11 +1,12 @@ Metadata-Version: 1.1 Name: pykerberos -Version: 1.1.14 +Version: 1.2.1 Summary: High-level interface to Kerberos Home-page: UNKNOWN Author: UNKNOWN Author-email: UNKNOWN License: ASL 2.0 +Description-Content-Type: 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, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykerberos-1.1.14/pysrc/kerberos.py new/pykerberos-1.2.1/pysrc/kerberos.py --- old/pykerberos-1.1.14/pysrc/kerberos.py 2017-01-26 08:32:32.000000000 +0100 +++ new/pykerberos-1.2.1/pysrc/kerberos.py 2017-12-07 02:02:59.000000000 +0100 @@ -130,13 +130,16 @@ @return: a result code (see above). """ -def authGSSClientStep(context, challenge): +def authGSSClientStep(context, challenge, **kwargs): """ 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). + @param channel_bindings: Optional channel bindings to bind onto the auth request. This + struct can be built using the channelBindings function and it not specified, this process + will pass along GSS_C_NO_CHANNEL_BINDINGS as a default @return: a result code (see above). """ @@ -238,3 +241,74 @@ @param context: the context object returned from authGSSServerInit. @return: a string containing the target name. """ + +""" +Address Types for Channel Bindings +https://docs.oracle.com/cd/E19455-01/806-3814/6jcugr7dp/index.html#reference-9 +""" + +GSS_C_AF_UNSPEC = 0 +GSS_C_AF_LOCAL = 1 +GSS_C_AF_INET = 2 +GSS_C_AF_IMPLINK = 3 +GSS_C_AF_PUP = 4 +GSS_C_AF_CHAOS = 5 +GSS_C_AF_NS = 6 +GSS_C_AF_NBS = 7 +GSS_C_AF_ECMA = 8 +GSS_C_AF_DATAKIT = 9 +GSS_C_AF_CCITT = 10 +GSS_C_AF_SNA = 11 +GSS_C_AF_DECnet = 12 +GSS_C_AF_DLI = 13 +GSS_C_AF_LAT = 14 +GSS_C_AF_HYLINK = 15 +GSS_C_AF_APPLETALK = 16 +GSS_C_AF_BSC = 17 +GSS_C_AF_DSS = 18 +GSS_C_AF_OSI = 19 +GSS_C_AF_X25 = 21 +GSS_C_AF_NULLADDR = 255 + +def channelBindings(**kwargs): + """ + Builds a gss_channel_bindings_struct which can be used to pass onto authGSSClientStep to bind + onto the auth. Details on Channel Bindings can be found at https://tools.ietf.org/html/rfc5929. + More details on the struct can be found at https://docs.oracle.com/cd/E19455-01/806-3814/overview-52/index.html + + @param initiator_addrtype: Optional integer used to set the + initiator_addrtype, defaults to GSS_C_AF_UNSPEC if not set + @param initiator_address: Optional byte string containing the + initiator_address + @param acceptor_addrtype: Optional integer used to set the + acceptor_addrtype, defaults to GSS_C_AF_UNSPEC if not set + @param acceptor_address: Optional byte string containing the + acceptor_address + @param application_data: Optional byte string containing the + application_data. An example would be 'tls-server-end-point:{cert-hash}' + where {cert-hash} is the byte string hash of the server's certificate + @return: The gss_channel_bindings_struct pointer, which is the channel + bindings structure that can be passed onto authGSSClientStep + """ + +def authGSSWinRMEncryptMessage(context, message): + """ + Encrypts a message body with the current Kerberos session key using IOV settings for WinRM + + @param context: The context object returned from L{authGSSClientInit}. + @param message: The plaintext message to be encrypted. + @return: A tuple of (encrypted_data, header) where encrypted_data is the + ciphertext result of the encryption operation, and header is the GSSAPI + header describing the encryption parameters. Both strings contain opaque + binary data. + """ + +def authGSSWinRMDecryptMessage(context, encrypted_data, header): + """ + Decrypts a ciphertext message body with the current Kerberos session key using IOV settings for WinRM + + @param context: The context object returned from L{authGSSClientInit}. + @param encrypted_data: The ciphertext message to be decrypted. + @param header: The GSSAPI message header containing the encryption parameters. + @return: The decrypted message text. + """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykerberos-1.1.14/setup.cfg new/pykerberos-1.2.1/setup.cfg --- old/pykerberos-1.1.14/setup.cfg 2017-01-26 08:32:40.000000000 +0100 +++ new/pykerberos-1.2.1/setup.cfg 2017-12-20 19:27:02.000000000 +0100 @@ -1,5 +1,4 @@ [egg_info] tag_build = tag_date = 0 -tag_svn_revision = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykerberos-1.1.14/setup.py new/pykerberos-1.2.1/setup.py --- old/pykerberos-1.1.14/setup.py 2017-01-26 08:32:32.000000000 +0100 +++ new/pykerberos-1.2.1/setup.py 2017-12-20 19:20:04.000000000 +0100 @@ -58,7 +58,7 @@ setup ( name = "pykerberos", - version = "1.1.14", + version = "1.2.1", description = "High-level interface to Kerberos", long_description=long_description, license="ASL 2.0", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykerberos-1.1.14/src/kerberos.c new/pykerberos-1.2.1/src/kerberos.c --- old/pykerberos-1.1.14/src/kerberos.c 2017-01-26 08:32:32.000000000 +0100 +++ new/pykerberos-1.2.1/src/kerberos.c 2017-12-08 18:24:45.000000000 +0100 @@ -50,7 +50,7 @@ const char *pswd = NULL; const char *service = NULL; const char *default_realm = NULL; - const int verify = 1; + int verify = 1; int result = 0; if (!PyArg_ParseTuple(args, "ssss|b", &user, &pswd, &service, &default_realm, &verify)) { @@ -163,13 +163,82 @@ return Py_BuildValue("i", AUTH_GSS_COMPLETE); } -static PyObject *authGSSClientStep(PyObject *self, PyObject *args) { +#if PY_MAJOR_VERSION >= 3 +void destruct_channel_bindings(PyObject* o) { + struct gss_channel_bindings_struct *channel_bindings = PyCapsule_GetPointer(o, NULL); +#else +void destruct_channel_bindings(void* o) { + struct gss_channel_bindings_struct *channel_bindings = (struct gss_channel_bindings_struct *)o; +#endif + + if (channel_bindings != NULL) { + if (channel_bindings->initiator_address.value != NULL) { + PyMem_Free(channel_bindings->initiator_address.value); + } + + if (channel_bindings->acceptor_address.value != NULL) { + PyMem_Free(channel_bindings->acceptor_address.value); + } + + if (channel_bindings->application_data.value != NULL) { + PyMem_Free(channel_bindings->application_data.value); + } + + free(channel_bindings); + } +} + +static PyObject *channelBindings(PyObject *self, PyObject *args, PyObject* keywds) { + int initiator_addrtype = GSS_C_AF_UNSPEC; + int acceptor_addrtype = GSS_C_AF_UNSPEC; + + const char *encoding = NULL; + char *initiator_address = NULL; + char *acceptor_address = NULL; + char *application_data = NULL; + int initiator_length = 0; + int acceptor_length = 0; + int application_length = 0; + + PyObject *pychan_bindings = NULL; + struct gss_channel_bindings_struct *input_chan_bindings; + static char *kwlist[] = {"initiator_addrtype", "initiator_address", "acceptor_addrtype", + "acceptor_address", "application_data", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iet#iet#et#", kwlist, + &initiator_addrtype, &encoding, &initiator_address, &initiator_length, + &acceptor_addrtype, &encoding, &acceptor_address, &acceptor_length, + &encoding, &application_data, &application_length)) { + return NULL; + } + + input_chan_bindings = (struct gss_channel_bindings_struct *) malloc(sizeof(struct gss_channel_bindings_struct)); + pychan_bindings = PyNew(input_chan_bindings, &destruct_channel_bindings); + + input_chan_bindings->initiator_addrtype = initiator_addrtype; + input_chan_bindings->initiator_address.length = initiator_length; + input_chan_bindings->initiator_address.value = initiator_address; + + input_chan_bindings->acceptor_addrtype = acceptor_addrtype; + input_chan_bindings->acceptor_address.length = acceptor_length; + input_chan_bindings->acceptor_address.value = acceptor_address; + + input_chan_bindings->application_data.length = application_length; + input_chan_bindings->application_data.value = application_data; + + return Py_BuildValue("N", pychan_bindings); +} + +static PyObject *authGSSClientStep(PyObject *self, PyObject *args, PyObject* keywds) { gss_client_state *state; PyObject *pystate; char *challenge = NULL; + PyObject *pychan_bindings = NULL; + struct gss_channel_bindings_struct *channel_bindings; + static char *kwlist[] = {"state", "challenge", "channel_bindings", NULL}; int result = 0; - if (!PyArg_ParseTuple(args, "Os", &pystate, &challenge)) { + if (! PyArg_ParseTupleAndKeywords(args, keywds, "Os|O", kwlist, &pystate, &challenge, &pychan_bindings)) { return NULL; } @@ -183,7 +252,17 @@ return NULL; } - result = authenticate_gss_client_step(state, challenge); + if (pychan_bindings == NULL) { + channel_bindings = GSS_C_NO_CHANNEL_BINDINGS; + } else { + if (!PyCheck(pychan_bindings)) { + PyErr_SetString(PyExc_TypeError, "Expected a gss_channel_bindings_struct object"); + return NULL; + } + channel_bindings = PyGet(pychan_bindings, struct gss_channel_bindings_struct); + } + + result = authenticate_gss_client_step(state, challenge, channel_bindings); if (result == AUTH_GSS_ERROR) { return NULL; } @@ -506,6 +585,137 @@ return Py_BuildValue("s", state->targetname); } +#ifdef GSSAPI_EXT +static PyObject* authGSSWinRMEncryptMessage(PyObject* self, PyObject* args) +{ + char *input = NULL; + char *header = NULL; + int header_len = 0; + char *enc_output = NULL; + int enc_output_len = 0; + PyObject *pystate = NULL; + gss_client_state *state = NULL; + int result = 0; + PyObject *pyresult = NULL; + + // NB: use et so we get a copy of the string (since gss_wrap_iov mutates it), and so we're certain it's always + // a UTF8 byte string + if (! PyArg_ParseTuple(args, "Oet", &pystate, "UTF-8", &input)) { + pyresult = NULL; + goto end; + } + + if (!PyCheck(pystate)) { + PyErr_SetString(PyExc_TypeError, "Expected a context object"); + pyresult = NULL; + goto end; + } + + state = PyGet(pystate, gss_client_state); + if (state == NULL) { + pyresult = NULL; + goto end; + } + + result = encrypt_message(state, input, &header, &header_len, &enc_output, &enc_output_len); + + if (result == AUTH_GSS_ERROR) { + pyresult = NULL; + goto end; + } + +#if PY_MAJOR_VERSION >= 3 + pyresult = Py_BuildValue("y# y#", enc_output, enc_output_len, header, header_len); +#else + pyresult = Py_BuildValue("s# s#", enc_output, enc_output_len, header, header_len); +#endif + +end: + if (input) { + PyMem_Free(input); + } + if (header) { + free(header); + } + if (enc_output) { + free(enc_output); + } + + return pyresult; +} + +static PyObject* authGSSWinRMDecryptMessage(PyObject* self, PyObject* args) +{ + char *header = NULL; + int header_len = 0; + char *enc_data = NULL; + int enc_data_len = 0; + PyObject *pystate = NULL; + PyObject *pyheader = NULL; + PyObject *pyenc_data = NULL; + gss_client_state *state = NULL; + char *dec_output = NULL; + int dec_output_len = 0; + int result = 0; + PyObject *pyresult = 0; + + // NB: since the sig/data strings are arbitrary binary and don't conform to + // a valid encoding, none of the normal string marshaling types will work. We'll + // have to extract the data later. + if (! PyArg_ParseTuple(args, "OOO", &pystate, &pyenc_data, &pyheader)) { + pyresult = NULL; + goto end; + } + + if (!PyCheck(pystate)) { + PyErr_SetString(PyExc_TypeError, "Expected a context object"); + pyresult = NULL; + goto end; + } + + state = PyGet(pystate, gss_client_state); + if (state == NULL) { + pyresult = NULL; + goto end; + } + + // request the length and copy the header and encrypted input data from the Python strings + header_len = (int) PyBytes_Size(pyheader); + header = malloc(header_len); + memcpy(header, PyBytes_AsString(pyheader), header_len); + + enc_data_len = (int) PyBytes_Size(pyenc_data); + enc_data = malloc(enc_data_len); + memcpy(enc_data, PyBytes_AsString(pyenc_data), enc_data_len); + + result = decrypt_message(state, header, header_len, enc_data, enc_data_len, &dec_output, &dec_output_len); + + if (result == AUTH_GSS_ERROR) { + pyresult = NULL; + goto end; + } + +#if PY_MAJOR_VERSION >= 3 + pyresult = Py_BuildValue("y#", dec_output, dec_output_len); +#else + pyresult = Py_BuildValue("s#", dec_output, dec_output_len); +#endif + +end: + if (header) { + free(header); + } + if (enc_data) { + free(enc_data); + } + if (dec_output) { + free(dec_output); + } + + return pyresult; +} +#endif + static PyMethodDef KerberosMethods[] = { {"checkPassword", checkPassword, METH_VARARGS, "Check the supplied user/password against Kerberos KDC."}, @@ -517,7 +727,7 @@ "Initialize client-side GSSAPI operations."}, {"authGSSClientClean", authGSSClientClean, METH_VARARGS, "Terminate client-side GSSAPI operations."}, - {"authGSSClientStep", authGSSClientStep, METH_VARARGS, + {"authGSSClientStep", (PyCFunction)authGSSClientStep, METH_VARARGS | METH_KEYWORDS, "Do a client-side GSSAPI step."}, {"authGSSClientResponse", authGSSClientResponse, METH_VARARGS, "Get the response from the last client-side GSSAPI step."}, @@ -531,11 +741,17 @@ "Do a GSSAPI wrap."}, {"authGSSClientUnwrap", authGSSClientUnwrap, METH_VARARGS, "Do a GSSAPI unwrap."}, + {"channelBindings", (PyCFunction)channelBindings, METH_VARARGS | METH_KEYWORDS, + "Build the Channel Bindings Structure based on the input."}, #ifdef GSSAPI_EXT {"authGSSClientWrapIov", authGSSClientWrapIov, METH_VARARGS, "Do a GSSAPI iov wrap."}, {"authGSSClientUnwrapIov", authGSSClientUnwrapIov, METH_VARARGS, "Do a GSSAPI iov unwrap."}, + {"authGSSWinRMEncryptMessage", authGSSWinRMEncryptMessage, METH_VARARGS, + "Encrypt a message"}, + {"authGSSWinRMDecryptMessage", authGSSWinRMDecryptMessage, METH_VARARGS, + "Decrypt a message"}, #endif {"authGSSServerClean", authGSSServerClean, METH_VARARGS, "Terminate server-side GSSAPI operations."}, @@ -620,6 +836,29 @@ PyDict_SetItemString(d, "GSS_MECH_OID_KRB5", PyNew(&krb5_mech_oid, NULL)); PyDict_SetItemString(d, "GSS_MECH_OID_SPNEGO", PyNew(&spnego_mech_oid, NULL)); + PyDict_SetItemString(d, "GSS_C_AF_UNSPEC", PyInt_FromLong(GSS_C_AF_UNSPEC)); + PyDict_SetItemString(d, "GSS_C_AF_LOCAL", PyInt_FromLong(GSS_C_AF_LOCAL)); + PyDict_SetItemString(d, "GSS_C_AF_INET", PyInt_FromLong(GSS_C_AF_INET)); + PyDict_SetItemString(d, "GSS_C_AF_IMPLINK", PyInt_FromLong(GSS_C_AF_IMPLINK)); + PyDict_SetItemString(d, "GSS_C_AF_PUP", PyInt_FromLong(GSS_C_AF_PUP)); + PyDict_SetItemString(d, "GSS_C_AF_CHAOS", PyInt_FromLong(GSS_C_AF_CHAOS)); + PyDict_SetItemString(d, "GSS_C_AF_NS", PyInt_FromLong(GSS_C_AF_NS)); + PyDict_SetItemString(d, "GSS_C_AF_NBS", PyInt_FromLong(GSS_C_AF_NBS)); + PyDict_SetItemString(d, "GSS_C_AF_ECMA", PyInt_FromLong(GSS_C_AF_ECMA)); + PyDict_SetItemString(d, "GSS_C_AF_DATAKIT", PyInt_FromLong(GSS_C_AF_DATAKIT)); + PyDict_SetItemString(d, "GSS_C_AF_CCITT", PyInt_FromLong(GSS_C_AF_CCITT)); + PyDict_SetItemString(d, "GSS_C_AF_SNA", PyInt_FromLong(GSS_C_AF_SNA)); + PyDict_SetItemString(d, "GSS_C_AF_DECnet", PyInt_FromLong(GSS_C_AF_DECnet)); + PyDict_SetItemString(d, "GSS_C_AF_DLI", PyInt_FromLong(GSS_C_AF_DLI)); + PyDict_SetItemString(d, "GSS_C_AF_LAT", PyInt_FromLong(GSS_C_AF_LAT)); + PyDict_SetItemString(d, "GSS_C_AF_HYLINK", PyInt_FromLong(GSS_C_AF_HYLINK)); + PyDict_SetItemString(d, "GSS_C_AF_APPLETALK", PyInt_FromLong(GSS_C_AF_APPLETALK)); + PyDict_SetItemString(d, "GSS_C_AF_BSC", PyInt_FromLong(GSS_C_AF_BSC)); + PyDict_SetItemString(d, "GSS_C_AF_DSS", PyInt_FromLong(GSS_C_AF_DSS)); + PyDict_SetItemString(d, "GSS_C_AF_OSI", PyInt_FromLong(GSS_C_AF_OSI)); + PyDict_SetItemString(d, "GSS_C_AF_X25", PyInt_FromLong(GSS_C_AF_X25)); + PyDict_SetItemString(d, "GSS_C_AF_NULLADDR", PyInt_FromLong(GSS_C_AF_NULLADDR)); + error: if (PyErr_Occurred()) PyErr_SetString(PyExc_ImportError, "kerberos: init failed"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykerberos-1.1.14/src/kerberosgss.c new/pykerberos-1.2.1/src/kerberosgss.c --- old/pykerberos-1.1.14/src/kerberosgss.c 2017-01-26 08:32:32.000000000 +0100 +++ new/pykerberos-1.2.1/src/kerberosgss.c 2017-12-08 18:24:45.000000000 +0100 @@ -205,7 +205,7 @@ return ret; } -int authenticate_gss_client_step(gss_client_state* state, const char* challenge) +int authenticate_gss_client_step(gss_client_state* state, const char* challenge, struct gss_channel_bindings_struct* channel_bindings) { OM_uint32 maj_stat; OM_uint32 min_stat; @@ -238,7 +238,7 @@ state->mech_oid, (OM_uint32)state->gss_flags, 0, - NULL, //GSS_C_NO_CHANNEL_BINDINGS, + channel_bindings, &input_token, NULL, &output_token, @@ -840,3 +840,85 @@ PyErr_SetObject(GssException_class, Py_BuildValue("((s:i)(s:i))", buf_maj, err_maj, buf_min, err_min)); } + +#ifdef GSSAPI_EXT +int encrypt_message(gss_client_state *state, char *message_input, char **header, int *header_len, char **encrypted_data, int *encrypted_data_len) +{ + OM_uint32 maj_stat; + OM_uint32 min_stat; + int ret; + char *outloc = NULL; + + gss_iov_buffer_desc iov[3]; + iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE; + iov[1].type = GSS_IOV_BUFFER_TYPE_DATA; + // NB: this will modify the message buffer in place, ensure caller has copied the string if necessary + iov[1].buffer.value = message_input; + iov[1].buffer.length = strlen(message_input); + iov[2].type = GSS_IOV_BUFFER_TYPE_PADDING | GSS_IOV_BUFFER_FLAG_ALLOCATE; + + maj_stat = gss_wrap_iov(&min_stat, state->context, 1, GSS_C_QOP_DEFAULT, NULL, iov, 3); + + if (GSS_ERROR(maj_stat)) { + set_gss_error(maj_stat, min_stat); + ret = AUTH_GSS_ERROR; + goto end; + } + + *header_len = iov[0].buffer.length; + *header = malloc(*header_len); + memcpy(*header, iov[0].buffer.value, *header_len); + + // copy encrypted data, concatenate padding buffer if present + *encrypted_data_len = iov[1].buffer.length + iov[2].buffer.length; + *encrypted_data = malloc(*encrypted_data_len); + outloc = *encrypted_data; + outloc = mempcpy(outloc, iov[1].buffer.value, iov[1].buffer.length); + // NB: no-op if no padding is necessary (which seems to always be the case with aes256-cts-hmac-sha1-96) + mempcpy(outloc, iov[2].buffer.value, iov[2].buffer.length); + + ret = 0; +end: + maj_stat = gss_release_iov_buffer(&min_stat, iov, 3); + + return ret; +} + +int decrypt_message(gss_client_state *state, char *header, int header_len, char *data, int data_len, char **decrypted_output, int *decrypted_output_len) +{ + OM_uint32 maj_stat; + OM_uint32 min_stat; + int ret = 0; + int conf_state; + gss_qop_t qop_state; + + // NB: ensure the caller has copied the python input buffer + // so we're not mutating somebody else's string + + gss_iov_buffer_desc iov[3]; + iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; + iov[0].buffer.value = header; + iov[0].buffer.length = header_len; + + iov[1].type = GSS_IOV_BUFFER_TYPE_DATA; + iov[1].buffer.value = data; + iov[1].buffer.length = data_len; + + maj_stat = gss_unwrap_iov(&min_stat, state->context, &conf_state, &qop_state, iov, 2); + + if (GSS_ERROR(maj_stat)) { + set_gss_error(maj_stat, min_stat); + ret = AUTH_GSS_ERROR; + goto end; + } + + *decrypted_output = malloc(iov[1].buffer.length); + *decrypted_output_len = iov[1].buffer.length; + + memcpy(*decrypted_output, iov[1].buffer.value, iov[1].buffer.length); + + ret = 0; +end: + return ret; +} +#endif \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykerberos-1.1.14/src/kerberosgss.h new/pykerberos-1.2.1/src/kerberosgss.h --- old/pykerberos-1.1.14/src/kerberosgss.h 2017-01-26 08:32:32.000000000 +0100 +++ new/pykerberos-1.2.1/src/kerberosgss.h 2017-12-07 02:02:50.000000000 +0100 @@ -56,7 +56,7 @@ int authenticate_gss_client_init(const char* service, const char* principal, long int gss_flags, gss_OID mech_oid, gss_client_state* state); int authenticate_gss_client_clean(gss_client_state *state); -int authenticate_gss_client_step(gss_client_state *state, const char *challenge); +int authenticate_gss_client_step(gss_client_state *state, const char *challenge, struct gss_channel_bindings_struct *channel_bindings); int authenticate_gss_client_unwrap(gss_client_state* state, const char* challenge); int authenticate_gss_client_wrap(gss_client_state* state, const char* challenge, const char* user, int protect); #ifdef GSSAPI_EXT @@ -66,3 +66,5 @@ int authenticate_gss_server_init(const char* service, gss_server_state* state); int authenticate_gss_server_clean(gss_server_state *state); int authenticate_gss_server_step(gss_server_state *state, const char *challenge); +int encrypt_message(gss_client_state *state, char *message_input, char **header, int *header_len, char **encrypted_data, int *encrypted_data_len); +int decrypt_message(gss_client_state *state, char *header, int header_len, char *data, int data_len, char **decrypted_output, int *decrypted_output_len);
