Skip to content

Commit

Permalink
Implement remaining GGF extensions
Browse files Browse the repository at this point in the history
This commit implements the remaining GGF extensions that provides extended
support for managing security contexts and credentials. This commit adds the
functions gss_set_sec_context_option and gss_set_cred_option. A common use
case for gss_set_sec_context_option is with NTLM to reset the crypto handles
using the GSS_NTLMSSP_RESET_CRYPTO_OID_LENGTH OID.

Draft IETF document for the gss_set_sec_context_option can be found at
https://tools.ietf.org/html/draft-engert-ggf-gss-extensions-00

Draft IETF document for the gss_set_cred_option can be found at
https://tools.ietf.org/html/draft-williams-kitten-channel-bound-flag-00
  • Loading branch information
jborean93 committed Mar 22, 2018
1 parent 2a2b47b commit d64e1c7
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 1 deletion.
2 changes: 2 additions & 0 deletions README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ In addition to RFC 2743/2744, Python-GSSAPI also has support for:

* `acquire_cred_with_password` and `add_cred_with_password`

* GGF Extensions

The Team
========

Expand Down
126 changes: 125 additions & 1 deletion gssapi/raw/ext_ggf.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ required by the SMB protocol for signing and encrypting a message.
Draft IETF document for these extensions can be found at
https://tools.ietf.org/html/draft-engert-ggf-gss-extensions-00
Draft IETF document for the gss_set_cred_option can be found at
https://tools.ietf.org/html/draft-williams-kitten-channel-bound-flag-00
"""
GSSAPI="BASE" # This ensures that a full module is generated by Cython

Expand All @@ -32,6 +35,17 @@ cdef extern from "python_gssapi_ext.h":
const gss_OID desired_object,
gss_buffer_set_t *data_set) nogil

# not in GGF draft but usually lumped together with the others
OM_uint32 gss_set_cred_option(OM_uint32 *minor_status,
gss_cred_id_t *cred,
const gss_OID desired_object,
const gss_buffer_t value) nogil

OM_uint32 gss_set_sec_context_option(OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
const gss_OID desired_object,
const gss_buffer_t value) nogil


def inquire_cred_by_oid(Creds cred_handle not None,
OID desired_aspect not None):
Expand Down Expand Up @@ -93,7 +107,7 @@ def inquire_sec_context_by_oid(SecurityContext context not None,
Args:
context (SecurityContext): the Security Context to query
desired_aspect (OID): the desired aspected of the Security Context to
desired_aspect (OID): the desired aspect of the Security Context to
inquire about.
Returns:
Expand Down Expand Up @@ -127,3 +141,113 @@ def inquire_sec_context_by_oid(SecurityContext context not None,
return py_tokens
else:
raise GSSError(maj_stat, min_stat)


def set_cred_option(OID desired_aspect not None, Creds creds=None, value=None):
"""
set_cred_option(desired_aspect, creds=None, value=None)
This method is used to set options of a :class:`Creds` object based on
an OID key. The options that can be set depends on the mech the credentials
were created with.
An example of how this can be used would be to set the
GSS_KRB5_CRED_NO_CI_FLAGS_X on a Kerberos credential. The OID string for
this flag is '1.2.752.43.13.29' and it requires no value to be set. This
must be set before the SecurityContext was initialised with the
credentials.
Args:
desired_aspect (OID): the desired aspect of the Credential to set.
cred_handle (Creds): the Credentials to set, or None to create a new
credential.
value (bytes): the value to set on the desired aspect of the Credential
or None to send GSS_C_EMPTY_BUFFER.
Returns:
Creds: The output credential.
Raises:
GSS_ERROR
"""

cdef gss_buffer_desc value_buffer
if value is not None:
value_buffer = gss_buffer_desc(len(value), value)
else:
# GSS_C_EMPTY_BUFFER
value_buffer = gss_buffer_desc(0, NULL)

cdef Creds output_creds = creds
if output_creds is None:
output_creds = Creds()


cdef OM_uint32 maj_stat, min_stat

with nogil:
maj_stat = gss_set_cred_option(&min_stat,
&output_creds.raw_creds,
&desired_aspect.raw_oid,
&value_buffer)

if maj_stat == GSS_S_COMPLETE:
return output_creds
else:
raise GSSError(maj_stat, min_stat)


def set_sec_context_option(OID desired_aspect not None,
SecurityContext context=None,
value=None):
"""
set_sec_context_option(desired_aspect, context=None, value=None)
This method is used to set a value for a specific OID of a
:class:`SecurityContext` object. The OID and value to pass in depends on
the mech the SecurityContext backs.
An example of how this can be used would be to reset the NTLM crypto engine
used in gss-ntlmssp. The OID that controls this value is
'1.3.6.1.4.1.7165.655.1.3' and it takes it a byte value that represents
an int32 where 1 reset's the verifier handle and any other int resets the
sender handle.
Args:
desired_aspect (OID): the desired aspect of the Security Context to set
the value for.
context (SecurityContext): the Security Context to set, or None to
create a new context.
value (bytes): the value to set on the desired aspect of the Security
Context or None to send GSS_C_EMPTY_BUFFER.
Returns:
SecurityContext: The output security context.
Raises:
GSS_ERROR
"""

cdef gss_buffer_desc value_buffer
if value is not None:
value_buffer = gss_buffer_desc(len(value), value)
else:
# GSS_C_EMPTY_BUFFER
value_buffer = gss_buffer_desc(0, NULL)

cdef SecurityContext output_context = context
if output_context is None:
output_context = SecurityContext()

cdef OM_uint32 maj_stat, min_stat

with nogil:
maj_stat = gss_set_sec_context_option(&min_stat,
&output_context.raw_ctx,
&desired_aspect.raw_oid,
&value_buffer)

if maj_stat == GSS_S_COMPLETE:
return output_context
else:
raise GSSError(maj_stat, min_stat)
61 changes: 61 additions & 0 deletions gssapi/tests/test_raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,67 @@ def test_inquire_sec_context_by_oid_should_raise_error(self):
gb.inquire_sec_context_by_oid.should_raise(gb.GSSError, client_ctx,
invalid_oid)

@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
@ktu.krb_minversion_test('1.14',
'GSS_KRB5_CRED_NO_CI_FLAGS_X was added in MIT '
'krb5 1.14')
def test_set_cred_option(self):
name = gb.import_name(SERVICE_PRINCIPAL,
gb.NameType.kerberos_principal)
# GSS_KRB5_CRED_NO_CI_FLAGS_X
no_ci_flags_x = gb.OID.from_int_seq("1.2.752.43.13.29")
orig_cred = gb.acquire_cred(name).creds

# nothing much we can test here apart from it doesn't fail and the
# id of the return cred is the same as the input one
output_cred = gb.set_cred_option(no_ci_flags_x, creds=orig_cred)
id(orig_cred).should_be(id(output_cred))

@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
def test_set_cred_option_should_raise_error(self):
name = gb.import_name(SERVICE_PRINCIPAL,
gb.NameType.kerberos_principal)
orig_cred = gb.acquire_cred(name).creds

# this is a fake OID and shouldn't work at all
invalid_oid = gb.OID.from_int_seq("1.2.3.4.5.6.7.8.9")
gb.set_cred_option.should_raise(gb.GSSError, invalid_oid, orig_cred,
b"\x00")

# TODO: get these tests to detect gss-ntlmssp once it is installed
"""
@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
@ktu.gssapi_extension_test('password', 'Add Credential with Password')
def test_set_sec_context_option(self):
# TODO: skip the test if gss-ntlmssp is not installed, MIT krb5 has not
# implemented any OID options for set_sec_context_option
ntlm_mech = gb.OID.from_int_seq("1.3.6.1.4.1.311.2.2.10")
reset_mech = gb.OID.from_int_seq("1.3.6.1.4.1.7165.655.1.3")
username = gb.import_name(name=b"user",
name_type=gb.NameType.user)
server = gb.import_name(name=b"server",
name_type=gb.NameType.hostbased_service)
cred = gb.acquire_cred_with_password(name=username,
password=b"password",
mechs=[ntlm_mech])
orig_context = gb.init_sec_context(server, creds=cred.creds,
mech=ntlm_mech)[0]
out_context = gb.set_sec_context_option(reset_mech,
context=orig_context,
value=b"\x00" * 4)
id(orig_context).should_be(id(out_context))
"""

@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
def test_set_sec_context_option_fail(self):
# because MIT krb5 doesn't implement any OID's for
# gss_set_sec_context_option, we just need to query any OID and it will
# raise an exception
gb.set_sec_context_option.should_raise(gb.GSSError,
gb.OID.from_int_seq("1.2.3.4"))


class TestIntEnumFlagSet(unittest.TestCase):
def test_create_from_int(self):
Expand Down

0 comments on commit d64e1c7

Please sign in to comment.