Permalink
Browse files

Added channel binding tokens support

  • Loading branch information...
BOREAN, Jordan BOREAN, Jordan
BOREAN, Jordan authored and BOREAN, Jordan committed Jun 12, 2017
1 parent a9fdc2b commit e639516eec7b974bbd2302d4050c4b5e1ba9281a
Showing with 214 additions and 9 deletions.
  1. +53 −0 README.rst
  2. +68 −1 pysrc/kerberos.py
  3. +1 −1 setup.py
  4. +89 −4 src/kerberos.c
  5. +2 −2 src/kerberosgss.c
  6. +1 −1 src/kerberosgss.h
View
@@ -58,6 +58,59 @@ not offer any protection against possible KDC spoofing. That method should not b
production code.
Channel Bindings
================
You can use this library to authenticate with Channel Binding support. Channel
Bindings are tags that identify the particular data channel being used with the
authentication. You can use Channel bindings to offer more proof of a valid
identity. Some services like Microsoft's Extended Protection can enforce
Channel Binding support on authorisation and you can use this library to meet
those requirements.
More details on Channel Bindings as set through the GSSAPI can be found here
<https://docs.oracle.com/cd/E19455-01/806-3814/overview-52/index.html>. Using
TLS as a example this is how you would add Channel Binding support to your
authentication mechanism. The following code snippet is based on RFC5929
<https://tools.ietf.org/html/rfc5929> using the 'tls-server-endpoint-point'
type.
.. 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
===========
View
@@ -193,7 +193,69 @@ def authGSSClientInquireCred(context):
def authGSSClientStep(context, challenge):
"""
Address Types for Channel Bindings
https://docs.oracle.com/cd/E19455-01/806-3814/6jcugr7dp/index.html#reference-9
"""
GSS_C_AF_UNSPEC = 0
GSS_C_AF_LOCAL = 1
GSS_C_AF_INET = 2
GSS_C_AF_IMPLINK = 3
GSS_C_AF_PUP = 4
GSS_C_AF_CHAOS = 5
GSS_C_AF_NS = 6
GSS_C_AF_NBS = 7
GSS_C_AF_ECMA = 8
GSS_C_AF_DATAKIT = 9
GSS_C_AF_CCITT = 10
GSS_C_AF_SNA = 11
GSS_C_AF_DECnet = 12
GSS_C_AF_DLI = 13
GSS_C_AF_LAT = 14
GSS_C_AF_HYLINK = 15
GSS_C_AF_APPLETALK = 16
GSS_C_AF_BSC = 17
GSS_C_AF_DSS = 18
GSS_C_AF_OSI = 19
GSS_C_AF_X25 = 21
GSS_C_AF_NULLADDR = 255
def channelBindings(**kwargs):
"""
Builds a gss_channel_bindings_struct which can be used to pass onto
L{authGSSClientStep} to bind onto the auth. Details on Channel Bindings
can be foud at https://tools.ietf.org/html/rfc5929. More details on the
struct can be found at
https://docs.oracle.com/cd/E19455-01/806-3814/overview-52/index.html
@param initiator_addrtype: Optional integer used to set the
initiator_addrtype, defaults to GSS_C_AF_UNSPEC if not set
@param initiator_address: Optional byte string containing the
initiator_address
@param acceptor_addrtype: Optional integer used to set the
acceptor_addrtype, defaults to GSS_C_AF_UNSPEC if not set
@param acceptor_address: Optional byte string containing the
acceptor_address
@param application_data: Optional byte string containing the
application_data. An example would be 'tls-server-end-point:{cert-hash}'
where {cert-hash} is the hash of the server's certificate
@return: A tuple of (result, gss_channel_bindings_struct) where result is
the result code and gss_channel_bindings_struct is the channel bindings
structure that can be passed onto L{authGSSClientStep}
"""
def authGSSClientStep(context, challenge, **kwargs):
"""
Processes a single GSSAPI client-side step using the supplied server data.
@@ -202,6 +264,11 @@ def authGSSClientStep(context, challenge):
@param challenge: A string containing the base64-encoded server data (which
may be empty for the first step).
@param channel_bindings: Optional channel bindings to bind onto the auth
request. This struct can be built using :{channelBindings}
and if not specified it will pass along GSS_C_NO_CHANNEL_BINDINGS as
a default.
@return: A result code (see above).
"""
View
@@ -29,7 +29,7 @@
project_name = "kerberos"
version_string = "1.2.6"
version_string = "1.3.0"
description = "Kerberos high-level interface"
View
@@ -211,14 +211,84 @@ static PyObject *authGSSClientClean(PyObject *self, PyObject *args)
return Py_BuildValue("i", result);
}
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 = NULL;
int acceptor_length = NULL;
int application_length = NULL;
PyObject *pychan_bindings = NULL;
struct gss_channel_bindings_struct *input_chan_bindings;
static char *kwlist[] = {"initiator_addrtype", "initiator_address", "acceptor_addrtype",
"acceptor_address", "application_data", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iet#iet#et#", kwlist,
&initiator_addrtype, &encoding, &initiator_address, &initiator_length,
&acceptor_addrtype, &encoding, &acceptor_address, &acceptor_length,
&encoding, &application_data, &application_length)) {
return NULL;
}
input_chan_bindings = (struct gss_channel_bindings_struct *) malloc(sizeof(struct gss_channel_bindings_struct));
pychan_bindings = PyCObject_FromVoidPtr(input_chan_bindings, &destruct_channel_bindings);
input_chan_bindings->initiator_addrtype = initiator_addrtype;
input_chan_bindings->initiator_address.length = initiator_length;
input_chan_bindings->initiator_address.value = initiator_address;
input_chan_bindings->acceptor_addrtype = acceptor_addrtype;
input_chan_bindings->acceptor_address.length = acceptor_length;
input_chan_bindings->acceptor_address.value = acceptor_address;
input_chan_bindings->application_data.length = application_length;
input_chan_bindings->application_data.value = application_data;
return Py_BuildValue("N", pychan_bindings);
}
static PyObject *authGSSClientStep(PyObject *self, PyObject *args, PyObject* keywds)
{
gss_client_state *state = NULL;
PyObject *pystate = NULL;
char *challenge = NULL;
PyObject *pychan_bindings = NULL;
struct gss_channel_bindings_struct *channel_bindings;
static char *kwlist[] = {"state", "challenge", "channel_bindings", NULL};
int result = 0;
if (! PyArg_ParseTuple(args, "Os", &pystate, &challenge)) {
if (! PyArg_ParseTupleAndKeywords(args, keywds, "Os|O", kwlist, &pystate, &challenge, &pychan_bindings)) {
return NULL;
}
@@ -233,7 +303,17 @@ static PyObject *authGSSClientStep(PyObject *self, PyObject *args)
return NULL;
}
result = authenticate_gss_client_step(state, challenge);
if (pychan_bindings == NULL) {
channel_bindings = GSS_C_NO_CHANNEL_BINDINGS;
} else {
if (!PyCObject_Check(pychan_bindings)) {
PyErr_SetString(PyExc_TypeError, "Expected a gss_channel_bindings_struct object");
return NULL;
}
channel_bindings = (struct gss_channel_bindings_struct *)PyCObject_AsVoidPtr(pychan_bindings);
}
result = authenticate_gss_client_step(state, challenge, channel_bindings);
if (result == AUTH_GSS_ERROR) {
return NULL;
@@ -656,14 +736,19 @@ static PyMethodDef KerberosMethods[] = {
(PyCFunction)authGSSClientInit, METH_VARARGS | METH_KEYWORDS,
"Initialize client-side GSSAPI operations."
},
{
"channelBindings",
(PyCFunction)channelBindings, METH_VARARGS | METH_KEYWORDS,
"Build the Channel Bindings Structure for authGSSClientStep."
},
{
"authGSSClientClean",
authGSSClientClean, METH_VARARGS,
"Terminate client-side GSSAPI operations."
},
{
"authGSSClientStep",
authGSSClientStep, METH_VARARGS,
(PyCFunction)authGSSClientStep, METH_VARARGS | METH_KEYWORDS,
"Do a client-side GSSAPI step."
},
{
View
@@ -232,7 +232,7 @@ int authenticate_gss_client_clean(gss_client_state *state)
}
int authenticate_gss_client_step(
gss_client_state* state, const char* challenge
gss_client_state* state, const char* challenge, struct gss_channel_bindings_struct* channel_bindings
) {
OM_uint32 maj_stat;
OM_uint32 min_stat;
@@ -269,7 +269,7 @@ int authenticate_gss_client_step(
state->mech_oid,
(OM_uint32)state->gss_flags,
0,
GSS_C_NO_CHANNEL_BINDINGS,
channel_bindings,
&input_token,
NULL,
&output_token,
View
@@ -61,7 +61,7 @@ int authenticate_gss_client_clean(
gss_client_state *state
);
int authenticate_gss_client_step(
gss_client_state *state, const char *challenge
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

0 comments on commit e639516

Please sign in to comment.