Skip to content

Commit

Permalink
Implement gss_set_cred_option() and gss_set_sec_context_option()
Browse files Browse the repository at this point in the history
These parts of th GGF extensions provide extended support for managing
security contexts and credentials.  In particular, with NTLM, they can
be used to reset the crypto handles using the
GSS_NTLMSSP_RESET_CRYPTO_OID_LENGTH OID.

Draft IETF document for the gss_set_sec_context_option():
https://tools.ietf.org/html/draft-engert-ggf-gss-extensions-00

Draft IETF document for the gss_set_cred_option():
https://tools.ietf.org/html/draft-ietf-kitten-channel-bound-flag-02

Fixes: #51

[rharwood@redhat.com edited commit message]
  • Loading branch information
jborean93 authored and frozencemetery committed Apr 6, 2018
1 parent 5e58d2e commit 13467fb
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 4 deletions.
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
6 changes: 6 additions & 0 deletions gssapi/raw/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,9 @@
from gssapi.raw.ext_ggf import * # noqa
except ImportError:
pass

# optional set_cred_option support
try:
from gssapi.raw.ext_set_cred_opt import * # noqa
except ImportError:
pass
68 changes: 64 additions & 4 deletions gssapi/raw/ext_ggf.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ 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
Expand All @@ -32,6 +31,11 @@ cdef extern from "python_gssapi_ext.h":
const gss_OID desired_object,
gss_buffer_set_t *data_set) 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 All @@ -50,7 +54,7 @@ def inquire_cred_by_oid(Creds cred_handle not None,
list: A list of zero or more pieces of data (as bytes objects)
Raises:
GSS_ERROR
GSSError
"""

cdef gss_buffer_set_t *data_set_ptr = NULL
Expand Down Expand Up @@ -93,14 +97,14 @@ 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:
list: A list of zero or more pieces of data (as bytes objects)
Raises:
GSS_ERROR
GSSError
"""

cdef gss_buffer_set_t *data_set_ptr = NULL
Expand All @@ -127,3 +131,59 @@ def inquire_sec_context_by_oid(SecurityContext context not None,
return py_tokens
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 resets 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:
GSSError
"""

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)
80 changes: 80 additions & 0 deletions gssapi/raw/ext_set_cred_opt.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""
gss_set_cred_option
Provides a way to set options on a credential based on the OID specified. A
common use case is to set the GSS_KRB5_CRED_NO_CI_FLAGS_X on a Kerberos
credential. This is used for interoperability with Microsoft's SSPI.
Note this function is commonly lumped with the GGF extensions but they are not
part of the GGF IETF draft so it's separated into it's own file.
Closest draft IETF document for the gss_set_cred_option can be found at
https://tools.ietf.org/html/draft-williams-kitten-channel-bound-flag-01
"""
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.misc import GSSError
from gssapi.raw.oids cimport OID
from gssapi.raw.creds cimport Creds

cdef extern from "python_gssapi_ext.h":

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


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:
GSSError
"""

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)
79 changes: 79 additions & 0 deletions gssapi/tests/test_raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,85 @@ 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.gssapi_extension_test('password', 'Add Credential with Password')
def test_set_sec_context_option(self):
ntlm_mech = gb.OID.from_int_seq("1.3.6.1.4.1.311.2.2.10")
username = gb.import_name(name=b"user",
name_type=gb.NameType.user)
try:
cred = gb.acquire_cred_with_password(name=username,
password=b"password",
mechs=[ntlm_mech])
except gb.GSSError:
self.skipTest('You do not have the GSSAPI gss-ntlmssp mech '
'installed')

server = gb.import_name(name=b"server",
name_type=gb.NameType.hostbased_service)
orig_context = gb.init_sec_context(server, creds=cred.creds,
mech=ntlm_mech)[0]

# GSS_NTLMSSP_RESET_CRYPTO_OID_STRING
reset_mech = gb.OID.from_int_seq("1.3.6.1.4.1.7165.655.1.3")
out_context = gb.set_sec_context_option(reset_mech,
context=orig_context,
value=b"\x00" * 4)
out_context.should_be_a(gb.SecurityContext)

@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
@ktu.gssapi_extension_test('password', 'Add Credential with Password')
def test_set_sec_context_option_fail(self):
ntlm_mech = gb.OID.from_int_seq("1.3.6.1.4.1.311.2.2.10")
username = gb.import_name(name=b"user",
name_type=gb.NameType.user)
try:
cred = gb.acquire_cred_with_password(name=username,
password=b"password",
mechs=[ntlm_mech])
except gb.GSSError:
self.skipTest('You do not have the GSSAPI gss-ntlmssp mech '
'installed')

server = gb.import_name(name=b"server",
name_type=gb.NameType.hostbased_service)
context = gb.init_sec_context(server, creds=cred.creds,
mech=ntlm_mech)[0]

# GSS_NTLMSSP_RESET_CRYPTO_OID_STRING
reset_mech = gb.OID.from_int_seq("1.3.6.1.4.1.7165.655.1.3")

# will raise a GSSError if no data was passed in
gb.set_sec_context_option.should_raise(gb.GSSError, reset_mech,
context)

@ktu.gssapi_extension_test('set_cred_opt', 'Kitten Set Credential Option')
@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)
output_cred.should_be_a(gb.Creds)

@ktu.gssapi_extension_test('set_cred_opt', 'Kitten Set Credential Option')
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")


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 @@ -276,6 +276,7 @@ def gssapi_modules(lst):
extension_file('dce', 'gss_wrap_iov'),
extension_file('iov_mic', 'gss_get_mic_iov'),
extension_file('ggf', 'gss_inquire_sec_context_by_oid'),
extension_file('set_cred_opt', 'gss_set_cred_option'),

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

0 comments on commit 13467fb

Please sign in to comment.