Skip to content

Commit a121831

Browse files
committed
Implement remaining GGF extensions
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
1 parent 2a2b47b commit a121831

File tree

6 files changed

+214
-4
lines changed

6 files changed

+214
-4
lines changed

Diff for: README.txt

+2
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ In addition to RFC 2743/2744, Python-GSSAPI also has support for:
156156

157157
* `acquire_cred_with_password` and `add_cred_with_password`
158158

159+
* GGF Extensions
160+
159161
The Team
160162
========
161163

Diff for: gssapi/raw/__init__.py

+6
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,9 @@
125125
from gssapi.raw.ext_ggf import * # noqa
126126
except ImportError:
127127
pass
128+
129+
# optional set_cred_option support
130+
try:
131+
from gssapi.raw.ext_set_cred_opt import * # noqa
132+
except ImportError:
133+
pass

Diff for: gssapi/raw/ext_ggf.pyx

+64-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ GSSAPI="BASE" # This ensures that a full module is generated by Cython
1414

1515
from gssapi.raw.cython_types cimport *
1616
from gssapi.raw.ext_buffer_sets cimport *
17-
from gssapi.raw.cython_converters cimport c_get_mech_oid_set
1817
from gssapi.raw.misc import GSSError
1918
from gssapi.raw.oids cimport OID
2019
from gssapi.raw.creds cimport Creds
@@ -32,6 +31,11 @@ cdef extern from "python_gssapi_ext.h":
3231
const gss_OID desired_object,
3332
gss_buffer_set_t *data_set) nogil
3433

34+
OM_uint32 gss_set_sec_context_option(OM_uint32 *minor_status,
35+
gss_ctx_id_t *context_handle,
36+
const gss_OID desired_object,
37+
const gss_buffer_t value) nogil
38+
3539

3640
def inquire_cred_by_oid(Creds cred_handle not None,
3741
OID desired_aspect not None):
@@ -50,7 +54,7 @@ def inquire_cred_by_oid(Creds cred_handle not None,
5054
list: A list of zero or more pieces of data (as bytes objects)
5155
5256
Raises:
53-
GSS_ERROR
57+
GSSError
5458
"""
5559

5660
cdef gss_buffer_set_t *data_set_ptr = NULL
@@ -93,14 +97,14 @@ def inquire_sec_context_by_oid(SecurityContext context not None,
9397
9498
Args:
9599
context (SecurityContext): the Security Context to query
96-
desired_aspect (OID): the desired aspected of the Security Context to
100+
desired_aspect (OID): the desired aspect of the Security Context to
97101
inquire about.
98102
99103
Returns:
100104
list: A list of zero or more pieces of data (as bytes objects)
101105
102106
Raises:
103-
GSS_ERROR
107+
GSSError
104108
"""
105109

106110
cdef gss_buffer_set_t *data_set_ptr = NULL
@@ -127,3 +131,59 @@ def inquire_sec_context_by_oid(SecurityContext context not None,
127131
return py_tokens
128132
else:
129133
raise GSSError(maj_stat, min_stat)
134+
135+
136+
def set_sec_context_option(OID desired_aspect not None,
137+
SecurityContext context=None,
138+
value=None):
139+
"""
140+
set_sec_context_option(desired_aspect, context=None, value=None)
141+
142+
This method is used to set a value for a specific OID of a
143+
:class:`SecurityContext` object. The OID and value to pass in depends on
144+
the mech the SecurityContext backs.
145+
146+
An example of how this can be used would be to reset the NTLM crypto engine
147+
used in gss-ntlmssp. The OID that controls this value is
148+
'1.3.6.1.4.1.7165.655.1.3' and it takes it a byte value that represents
149+
an int32 where 1 resets the verifier handle and any other int resets the
150+
sender handle.
151+
152+
Args:
153+
desired_aspect (OID): the desired aspect of the Security Context to set
154+
the value for.
155+
context (SecurityContext): the Security Context to set, or None to
156+
create a new context.
157+
value (bytes): the value to set on the desired aspect of the Security
158+
Context or None to send GSS_C_EMPTY_BUFFER.
159+
160+
Returns:
161+
SecurityContext: The output security context.
162+
163+
Raises:
164+
GSSError
165+
"""
166+
167+
cdef gss_buffer_desc value_buffer
168+
if value is not None:
169+
value_buffer = gss_buffer_desc(len(value), value)
170+
else:
171+
# GSS_C_EMPTY_BUFFER
172+
value_buffer = gss_buffer_desc(0, NULL)
173+
174+
cdef SecurityContext output_context = context
175+
if output_context is None:
176+
output_context = SecurityContext()
177+
178+
cdef OM_uint32 maj_stat, min_stat
179+
180+
with nogil:
181+
maj_stat = gss_set_sec_context_option(&min_stat,
182+
&output_context.raw_ctx,
183+
&desired_aspect.raw_oid,
184+
&value_buffer)
185+
186+
if maj_stat == GSS_S_COMPLETE:
187+
return output_context
188+
else:
189+
raise GSSError(maj_stat, min_stat)

Diff for: gssapi/raw/ext_set_cred_opt.pyx

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""
2+
gss_set_cred_option
3+
4+
Provides a way to set options on a credential based on the OID specified. A
5+
common use case is to set the GSS_KRB5_CRED_NO_CI_FLAGS_X on a Kerberos
6+
credential. This is used for interoperability with Microsoft's SSPI.
7+
8+
Note this function is commonly lumped with the GGF extensions but they are not
9+
part of the GGF IETF draft so it's separated into it's own file.
10+
11+
Closest draft IETF document for the gss_set_cred_option can be found at
12+
https://tools.ietf.org/html/draft-williams-kitten-channel-bound-flag-01
13+
"""
14+
GSSAPI="BASE" # This ensures that a full module is generated by Cython
15+
16+
from gssapi.raw.cython_types cimport *
17+
from gssapi.raw.ext_buffer_sets cimport *
18+
from gssapi.raw.misc import GSSError
19+
from gssapi.raw.oids cimport OID
20+
from gssapi.raw.creds cimport Creds
21+
22+
cdef extern from "python_gssapi_ext.h":
23+
24+
OM_uint32 gss_set_cred_option(OM_uint32 *minor_status,
25+
gss_cred_id_t *cred,
26+
const gss_OID desired_object,
27+
const gss_buffer_t value) nogil
28+
29+
30+
def set_cred_option(OID desired_aspect not None, Creds creds=None, value=None):
31+
"""
32+
set_cred_option(desired_aspect, creds=None, value=None)
33+
34+
This method is used to set options of a :class:`Creds` object based on
35+
an OID key. The options that can be set depends on the mech the credentials
36+
were created with.
37+
38+
An example of how this can be used would be to set the
39+
GSS_KRB5_CRED_NO_CI_FLAGS_X on a Kerberos credential. The OID string for
40+
this flag is '1.2.752.43.13.29' and it requires no value to be set. This
41+
must be set before the SecurityContext was initialised with the
42+
credentials.
43+
44+
Args:
45+
desired_aspect (OID): the desired aspect of the Credential to set.
46+
cred_handle (Creds): the Credentials to set, or None to create a new
47+
credential.
48+
value (bytes): the value to set on the desired aspect of the Credential
49+
or None to send GSS_C_EMPTY_BUFFER.
50+
51+
Returns:
52+
Creds: The output credential.
53+
54+
Raises:
55+
GSSError
56+
"""
57+
58+
cdef gss_buffer_desc value_buffer
59+
if value is not None:
60+
value_buffer = gss_buffer_desc(len(value), value)
61+
else:
62+
# GSS_C_EMPTY_BUFFER
63+
value_buffer = gss_buffer_desc(0, NULL)
64+
65+
cdef Creds output_creds = creds
66+
if output_creds is None:
67+
output_creds = Creds()
68+
69+
cdef OM_uint32 maj_stat, min_stat
70+
71+
with nogil:
72+
maj_stat = gss_set_cred_option(&min_stat,
73+
&output_creds.raw_creds,
74+
&desired_aspect.raw_oid,
75+
&value_buffer)
76+
77+
if maj_stat == GSS_S_COMPLETE:
78+
return output_creds
79+
else:
80+
raise GSSError(maj_stat, min_stat)

Diff for: gssapi/tests/test_raw.py

+61
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,67 @@ def test_inquire_sec_context_by_oid_should_raise_error(self):
847847
gb.inquire_sec_context_by_oid.should_raise(gb.GSSError, client_ctx,
848848
invalid_oid)
849849

850+
# TODO: get these tests to detect gss-ntlmssp once it is installed
851+
"""
852+
@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
853+
@ktu.gssapi_extension_test('password', 'Add Credential with Password')
854+
def test_set_sec_context_option(self):
855+
# TODO: skip the test if gss-ntlmssp is not installed, MIT krb5 has not
856+
# implemented any OID options for set_sec_context_option
857+
ntlm_mech = gb.OID.from_int_seq("1.3.6.1.4.1.311.2.2.10")
858+
reset_mech = gb.OID.from_int_seq("1.3.6.1.4.1.7165.655.1.3")
859+
860+
username = gb.import_name(name=b"user",
861+
name_type=gb.NameType.user)
862+
server = gb.import_name(name=b"server",
863+
name_type=gb.NameType.hostbased_service)
864+
cred = gb.acquire_cred_with_password(name=username,
865+
password=b"password",
866+
mechs=[ntlm_mech])
867+
orig_context = gb.init_sec_context(server, creds=cred.creds,
868+
mech=ntlm_mech)[0]
869+
870+
out_context = gb.set_sec_context_option(reset_mech,
871+
context=orig_context,
872+
value=b"\x00" * 4)
873+
out_context.should_be_a(gb.SecurityContext)
874+
"""
875+
876+
@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
877+
def test_set_sec_context_option_fail(self):
878+
# because MIT krb5 doesn't implement any OID's for
879+
# gss_set_sec_context_option, we just need to query any OID and it will
880+
# raise an exception
881+
gb.set_sec_context_option.should_raise(gb.GSSError,
882+
gb.OID.from_int_seq("1.2.3.4"))
883+
884+
@ktu.gssapi_extension_test('set_cred_opt', 'Kitten Set Credential Option')
885+
@ktu.krb_minversion_test('1.14',
886+
'GSS_KRB5_CRED_NO_CI_FLAGS_X was added in MIT '
887+
'krb5 1.14')
888+
def test_set_cred_option(self):
889+
name = gb.import_name(SERVICE_PRINCIPAL,
890+
gb.NameType.kerberos_principal)
891+
# GSS_KRB5_CRED_NO_CI_FLAGS_X
892+
no_ci_flags_x = gb.OID.from_int_seq("1.2.752.43.13.29")
893+
orig_cred = gb.acquire_cred(name).creds
894+
895+
# nothing much we can test here apart from it doesn't fail and the
896+
# id of the return cred is the same as the input one
897+
output_cred = gb.set_cred_option(no_ci_flags_x, creds=orig_cred)
898+
output_cred.should_be_a(gb.Creds)
899+
900+
@ktu.gssapi_extension_test('stc_cred_opt', 'Kitten Set Credential Option')
901+
def test_set_cred_option_should_raise_error(self):
902+
name = gb.import_name(SERVICE_PRINCIPAL,
903+
gb.NameType.kerberos_principal)
904+
orig_cred = gb.acquire_cred(name).creds
905+
906+
# this is a fake OID and shouldn't work at all
907+
invalid_oid = gb.OID.from_int_seq("1.2.3.4.5.6.7.8.9")
908+
gb.set_cred_option.should_raise(gb.GSSError, invalid_oid, orig_cred,
909+
b"\x00")
910+
850911

851912
class TestIntEnumFlagSet(unittest.TestCase):
852913
def test_create_from_int(self):

Diff for: setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ def gssapi_modules(lst):
276276
extension_file('dce', 'gss_wrap_iov'),
277277
extension_file('iov_mic', 'gss_get_mic_iov'),
278278
extension_file('ggf', 'gss_inquire_sec_context_by_oid'),
279+
extension_file('set_cred_opt', 'gss_set_cred_option'),
279280

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

0 commit comments

Comments
 (0)