Skip to content

Commit

Permalink
Implement partial support for GSSAPI extension GGF
Browse files Browse the repository at this point in the history
GGF provides extended credential and security context inquiry that allows
application to retrieve more information about the client's credentials and
security context. One common use case is to use gss_inquire_sec_context_by_oid
to retrieve the "session" key that is required by the SMB protocol for signing
and encrypting a message. These calls are provided as a part of the raw
interface and are not exposed in the high-level interface.

Thanks to @vm86 for his work on the gss_inquire_sec_context_by_oid.

Draft IETF document for these extensions can be found at
https://tools.ietf.org/html/draft-engert-ggf-gss-extensions-00
  • Loading branch information
jborean93 committed Jan 11, 2018
1 parent beb69c9 commit a50b089
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 0 deletions.
6 changes: 6 additions & 0 deletions gssapi/raw/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,9 @@
from gssapi.raw.ext_rfc6680_comp_oid import * # noqa
except ImportError:
pass

# optional Global Grid Forum support
try:
from gssapi.raw.ext_ggf import * # noqa
except ImportError:
pass
114 changes: 114 additions & 0 deletions gssapi/raw/ext_ggf.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
GSSAPI="BASE" # This ensures that a full module is generated by Cython

from gssapi.raw.cython_types cimport *
from gssapi.raw.ext_buffer_sets cimport *
from gssapi.raw.cython_converters cimport c_get_mech_oid_set
from gssapi.raw.misc import GSSError
from gssapi.raw.oids cimport OID
from gssapi.raw.creds cimport Creds
from gssapi.raw.sec_contexts cimport SecurityContext

cdef extern from "python_gssapi_ext.h":

OM_uint32 gss_inquire_cred_by_oid(OM_uint32 *minor_status,
const gss_cred_id_t cred_handle,
const gss_OID desired_object,
gss_buffer_set_t *data_set) nogil

OM_uint32 gss_inquire_sec_context_by_oid(OM_uint32 *minor_status,
const gss_ctx_id_t context_handle,
const gss_OID desired_object,
gss_buffer_set_t *data_set) nogil


def inquire_cred_by_oid(Creds cred_handle not None, OID mech not None):
"""
inquire_cred_by_oid(cred_handle, mech)
This method inspects a :class:`Creds` object for information
specific to a particular mechanism.
Args:
cred_handle (Creds): the security context to query
mech (OID): the desired mechanism
Returns:
list: A list of zero or more pieces of data corresponding to the
OID set
Raises:
GSS_ERROR
"""

cdef gss_buffer_set_t *data_set_ptr = NULL
cdef gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET
cdef OM_uint32 maj_stat, min_stat

data_set_ptr = &data_set

with nogil:
maj_stat = gss_inquire_cred_by_oid(&min_stat, cred_handle.raw_creds,
&mech.raw_oid, data_set_ptr)

if maj_stat == GSS_S_COMPLETE:
py_tokens = []

if data_set != GSS_C_NO_BUFFER_SET:
for i in range(data_set.count):
token = data_set.elements[i]
py_tokens.append(token.value[:token.length])

gss_release_buffer_set(&min_stat, &data_set)

return py_tokens
else:
raise GSSError(maj_stat, min_stat)


def inquire_sec_context_by_oid(SecurityContext context not None,
OID mech not None):
"""
inquire_sec_context_by_oid(context, mech)
This method inspects a :class:`SecurityContext` object for information
specific to a particular mechanism.
This method can be used with the GSS_KRB5_INQ_SSPI_SESSION_KEY_OID OID to
retrieve the required key that is used to derive the SMB/SAMBA signing and
encryption keys.
Args:
context (SecurityContext): the security context to query
mech (OID): the desired mechanism
Returns:
list: A list of zero or more pieces of data corresponding to the
OID set
Raises:
GSS_ERROR
"""

cdef gss_buffer_set_t *data_set_ptr = NULL
cdef gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET
cdef OM_uint32 maj_stat, min_stat

data_set_ptr = &data_set

with nogil:
maj_stat = gss_inquire_sec_context_by_oid(&min_stat, context.raw_ctx,
&mech.raw_oid, data_set_ptr)

if maj_stat == GSS_S_COMPLETE:
py_tokens = []

if data_set != GSS_C_NO_BUFFER_SET:
for i in range(data_set.count):
token = data_set.elements[i]
py_tokens.append(token.value[:token.length])

gss_release_buffer_set(&min_stat, &data_set)

return py_tokens
else:
raise GSSError(maj_stat, min_stat)
87 changes: 87 additions & 0 deletions gssapi/tests/test_raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,93 @@ def test_sasl_names(self):
cmp_mech.shouldnt_be_none()
cmp_mech.should_be(mech)

@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
@ktu.gssapi_extension_test('s4u', 'S4U')
@ktu.krb_minversion_test('1.16',
'querying impersonator name of krb5 GSS '
'Credential using the '
'GSS_KRB5_GET_CRED_IMPERSONATOR OID')
def test_inquire_cred_by_oid_impersonator(self):
svc_princ = SERVICE_PRINCIPAL.decode("UTF-8")
self.realm.kinit(svc_princ, flags=['-k', '-f'])

target_name = gb.import_name(TARGET_SERVICE_NAME,
gb.NameType.hostbased_service)

client_token = gb.init_sec_context(target_name).token

# if our acceptor creds have a usage of both, we get
# s4u2proxy delegated credentials
server_creds = gb.acquire_cred(None, usage='both').creds
server_ctx_resp = gb.accept_sec_context(client_token,
acceptor_creds=server_creds)

server_ctx_resp.shouldnt_be_none()
server_ctx_resp.delegated_creds.shouldnt_be_none()
server_ctx_resp.delegated_creds.should_be_a(gb.Creds)

# GSS_KRB5_GET_CRED_IMPERSONATOR
oid = gb.OID.from_int_seq("1.2.840.113554.1.2.2.5.14")
info = gb.inquire_cred_by_oid(server_ctx_resp.delegated_creds, oid)

info.should_be_a(list)
info.shouldnt_be_empty()
info[0].should_be_a(bytes)
info[0].should_be(b"%s@%s" % (SERVICE_PRINCIPAL,
self.realm.realm.encode('utf-8')))

@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
def test_inquire_sec_context_by_oid(self):
target_name = gb.import_name(TARGET_SERVICE_NAME,
gb.NameType.hostbased_service)
ctx_resp1 = gb.init_sec_context(target_name)

server_name = gb.import_name(SERVICE_PRINCIPAL,
gb.NameType.kerberos_principal)
server_creds = gb.acquire_cred(server_name)[0]
server_resp = gb.accept_sec_context(ctx_resp1[3],
acceptor_creds=server_creds)
server_ctx = server_resp[0]
server_tok = server_resp[3]

client_resp2 = gb.init_sec_context(target_name,
context=ctx_resp1[0],
input_token=server_tok)
client_ctx = client_resp2[0]

# GSS_C_INQ_SSPI_SESSION_KEY
session_key_oid = gb.OID.from_int_seq("1.2.840.113554.1.2.2.5.5")

client_key = gb.inquire_sec_context_by_oid(client_ctx, session_key_oid)
server_key = gb.inquire_sec_context_by_oid(server_ctx, session_key_oid)

client_key.should_be_a(list)
client_key.shouldnt_be_empty()
server_key.should_be_a(list)
server_key.shouldnt_be_empty()
client_key.should_have_same_items_as(server_key)

@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
def test_inquire_sec_context_by_oid_should_raise_error(self):
target_name = gb.import_name(TARGET_SERVICE_NAME,
gb.NameType.hostbased_service)
ctx_resp1 = gb.init_sec_context(target_name)

server_name = gb.import_name(SERVICE_PRINCIPAL,
gb.NameType.kerberos_principal)
server_creds = gb.acquire_cred(server_name)[0]
server_resp = gb.accept_sec_context(ctx_resp1[3],
acceptor_creds=server_creds)

client_resp2 = gb.init_sec_context(target_name,
context=ctx_resp1[0],
input_token=server_resp[3])
client_ctx = client_resp2[0]

invalid_oid = gb.OID.from_int_seq("1.2.3.4.5.6.7.8.9")
gb.inquire_sec_context_by_oid.should_raise(gb.GSSError, client_ctx,
invalid_oid)


class TestIntEnumFlagSet(unittest.TestCase):
def test_create_from_int(self):
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ def gssapi_modules(lst):
extension_file('cred_imp_exp', 'gss_import_cred'),
extension_file('dce', 'gss_wrap_iov'),
extension_file('iov_mic', 'gss_get_mic_iov'),
extension_file('ggf', 'gss_inquire_sec_context_by_oid'),

# see ext_rfc6680_comp_oid for more information on this split
extension_file('rfc6680', 'gss_display_name_ext'),
Expand Down

0 comments on commit a50b089

Please sign in to comment.