Skip to content

Commit d64e1c7

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 d64e1c7

File tree

3 files changed

+188
-1
lines changed

3 files changed

+188
-1
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/ext_ggf.pyx

+125-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ required by the SMB protocol for signing and encrypting a message.
99
1010
Draft IETF document for these extensions can be found at
1111
https://tools.ietf.org/html/draft-engert-ggf-gss-extensions-00
12+
13+
Draft IETF document for the gss_set_cred_option can be found at
14+
https://tools.ietf.org/html/draft-williams-kitten-channel-bound-flag-00
1215
"""
1316
GSSAPI="BASE" # This ensures that a full module is generated by Cython
1417

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

38+
# not in GGF draft but usually lumped together with the others
39+
OM_uint32 gss_set_cred_option(OM_uint32 *minor_status,
40+
gss_cred_id_t *cred,
41+
const gss_OID desired_object,
42+
const gss_buffer_t value) nogil
43+
44+
OM_uint32 gss_set_sec_context_option(OM_uint32 *minor_status,
45+
gss_ctx_id_t *context_handle,
46+
const gss_OID desired_object,
47+
const gss_buffer_t value) nogil
48+
3549

3650
def inquire_cred_by_oid(Creds cred_handle not None,
3751
OID desired_aspect not None):
@@ -93,7 +107,7 @@ def inquire_sec_context_by_oid(SecurityContext context not None,
93107
94108
Args:
95109
context (SecurityContext): the Security Context to query
96-
desired_aspect (OID): the desired aspected of the Security Context to
110+
desired_aspect (OID): the desired aspect of the Security Context to
97111
inquire about.
98112
99113
Returns:
@@ -127,3 +141,113 @@ def inquire_sec_context_by_oid(SecurityContext context not None,
127141
return py_tokens
128142
else:
129143
raise GSSError(maj_stat, min_stat)
144+
145+
146+
def set_cred_option(OID desired_aspect not None, Creds creds=None, value=None):
147+
"""
148+
set_cred_option(desired_aspect, creds=None, value=None)
149+
150+
This method is used to set options of a :class:`Creds` object based on
151+
an OID key. The options that can be set depends on the mech the credentials
152+
were created with.
153+
154+
An example of how this can be used would be to set the
155+
GSS_KRB5_CRED_NO_CI_FLAGS_X on a Kerberos credential. The OID string for
156+
this flag is '1.2.752.43.13.29' and it requires no value to be set. This
157+
must be set before the SecurityContext was initialised with the
158+
credentials.
159+
160+
Args:
161+
desired_aspect (OID): the desired aspect of the Credential to set.
162+
cred_handle (Creds): the Credentials to set, or None to create a new
163+
credential.
164+
value (bytes): the value to set on the desired aspect of the Credential
165+
or None to send GSS_C_EMPTY_BUFFER.
166+
167+
Returns:
168+
Creds: The output credential.
169+
170+
Raises:
171+
GSS_ERROR
172+
"""
173+
174+
cdef gss_buffer_desc value_buffer
175+
if value is not None:
176+
value_buffer = gss_buffer_desc(len(value), value)
177+
else:
178+
# GSS_C_EMPTY_BUFFER
179+
value_buffer = gss_buffer_desc(0, NULL)
180+
181+
cdef Creds output_creds = creds
182+
if output_creds is None:
183+
output_creds = Creds()
184+
185+
186+
cdef OM_uint32 maj_stat, min_stat
187+
188+
with nogil:
189+
maj_stat = gss_set_cred_option(&min_stat,
190+
&output_creds.raw_creds,
191+
&desired_aspect.raw_oid,
192+
&value_buffer)
193+
194+
if maj_stat == GSS_S_COMPLETE:
195+
return output_creds
196+
else:
197+
raise GSSError(maj_stat, min_stat)
198+
199+
200+
def set_sec_context_option(OID desired_aspect not None,
201+
SecurityContext context=None,
202+
value=None):
203+
"""
204+
set_sec_context_option(desired_aspect, context=None, value=None)
205+
206+
This method is used to set a value for a specific OID of a
207+
:class:`SecurityContext` object. The OID and value to pass in depends on
208+
the mech the SecurityContext backs.
209+
210+
An example of how this can be used would be to reset the NTLM crypto engine
211+
used in gss-ntlmssp. The OID that controls this value is
212+
'1.3.6.1.4.1.7165.655.1.3' and it takes it a byte value that represents
213+
an int32 where 1 reset's the verifier handle and any other int resets the
214+
sender handle.
215+
216+
Args:
217+
desired_aspect (OID): the desired aspect of the Security Context to set
218+
the value for.
219+
context (SecurityContext): the Security Context to set, or None to
220+
create a new context.
221+
value (bytes): the value to set on the desired aspect of the Security
222+
Context or None to send GSS_C_EMPTY_BUFFER.
223+
224+
Returns:
225+
SecurityContext: The output security context.
226+
227+
Raises:
228+
GSS_ERROR
229+
"""
230+
231+
cdef gss_buffer_desc value_buffer
232+
if value is not None:
233+
value_buffer = gss_buffer_desc(len(value), value)
234+
else:
235+
# GSS_C_EMPTY_BUFFER
236+
value_buffer = gss_buffer_desc(0, NULL)
237+
238+
cdef SecurityContext output_context = context
239+
if output_context is None:
240+
output_context = SecurityContext()
241+
242+
cdef OM_uint32 maj_stat, min_stat
243+
244+
with nogil:
245+
maj_stat = gss_set_sec_context_option(&min_stat,
246+
&output_context.raw_ctx,
247+
&desired_aspect.raw_oid,
248+
&value_buffer)
249+
250+
if maj_stat == GSS_S_COMPLETE:
251+
return output_context
252+
else:
253+
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+
@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
851+
@ktu.krb_minversion_test('1.14',
852+
'GSS_KRB5_CRED_NO_CI_FLAGS_X was added in MIT '
853+
'krb5 1.14')
854+
def test_set_cred_option(self):
855+
name = gb.import_name(SERVICE_PRINCIPAL,
856+
gb.NameType.kerberos_principal)
857+
# GSS_KRB5_CRED_NO_CI_FLAGS_X
858+
no_ci_flags_x = gb.OID.from_int_seq("1.2.752.43.13.29")
859+
orig_cred = gb.acquire_cred(name).creds
860+
861+
# nothing much we can test here apart from it doesn't fail and the
862+
# id of the return cred is the same as the input one
863+
output_cred = gb.set_cred_option(no_ci_flags_x, creds=orig_cred)
864+
id(orig_cred).should_be(id(output_cred))
865+
866+
@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
867+
def test_set_cred_option_should_raise_error(self):
868+
name = gb.import_name(SERVICE_PRINCIPAL,
869+
gb.NameType.kerberos_principal)
870+
orig_cred = gb.acquire_cred(name).creds
871+
872+
# this is a fake OID and shouldn't work at all
873+
invalid_oid = gb.OID.from_int_seq("1.2.3.4.5.6.7.8.9")
874+
gb.set_cred_option.should_raise(gb.GSSError, invalid_oid, orig_cred,
875+
b"\x00")
876+
877+
# TODO: get these tests to detect gss-ntlmssp once it is installed
878+
"""
879+
@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
880+
@ktu.gssapi_extension_test('password', 'Add Credential with Password')
881+
def test_set_sec_context_option(self):
882+
# TODO: skip the test if gss-ntlmssp is not installed, MIT krb5 has not
883+
# implemented any OID options for set_sec_context_option
884+
ntlm_mech = gb.OID.from_int_seq("1.3.6.1.4.1.311.2.2.10")
885+
reset_mech = gb.OID.from_int_seq("1.3.6.1.4.1.7165.655.1.3")
886+
887+
username = gb.import_name(name=b"user",
888+
name_type=gb.NameType.user)
889+
server = gb.import_name(name=b"server",
890+
name_type=gb.NameType.hostbased_service)
891+
cred = gb.acquire_cred_with_password(name=username,
892+
password=b"password",
893+
mechs=[ntlm_mech])
894+
orig_context = gb.init_sec_context(server, creds=cred.creds,
895+
mech=ntlm_mech)[0]
896+
897+
out_context = gb.set_sec_context_option(reset_mech,
898+
context=orig_context,
899+
value=b"\x00" * 4)
900+
id(orig_context).should_be(id(out_context))
901+
"""
902+
903+
@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
904+
def test_set_sec_context_option_fail(self):
905+
# because MIT krb5 doesn't implement any OID's for
906+
# gss_set_sec_context_option, we just need to query any OID and it will
907+
# raise an exception
908+
gb.set_sec_context_option.should_raise(gb.GSSError,
909+
gb.OID.from_int_seq("1.2.3.4"))
910+
850911

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

0 commit comments

Comments
 (0)