Skip to content

Commit a50b089

Browse files
committed
Implement partial support for GSSAPI extension GGF
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
1 parent beb69c9 commit a50b089

File tree

4 files changed

+208
-0
lines changed

4 files changed

+208
-0
lines changed

gssapi/raw/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,9 @@
119119
from gssapi.raw.ext_rfc6680_comp_oid import * # noqa
120120
except ImportError:
121121
pass
122+
123+
# optional Global Grid Forum support
124+
try:
125+
from gssapi.raw.ext_ggf import * # noqa
126+
except ImportError:
127+
pass

gssapi/raw/ext_ggf.pyx

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
GSSAPI="BASE" # This ensures that a full module is generated by Cython
2+
3+
from gssapi.raw.cython_types cimport *
4+
from gssapi.raw.ext_buffer_sets cimport *
5+
from gssapi.raw.cython_converters cimport c_get_mech_oid_set
6+
from gssapi.raw.misc import GSSError
7+
from gssapi.raw.oids cimport OID
8+
from gssapi.raw.creds cimport Creds
9+
from gssapi.raw.sec_contexts cimport SecurityContext
10+
11+
cdef extern from "python_gssapi_ext.h":
12+
13+
OM_uint32 gss_inquire_cred_by_oid(OM_uint32 *minor_status,
14+
const gss_cred_id_t cred_handle,
15+
const gss_OID desired_object,
16+
gss_buffer_set_t *data_set) nogil
17+
18+
OM_uint32 gss_inquire_sec_context_by_oid(OM_uint32 *minor_status,
19+
const gss_ctx_id_t context_handle,
20+
const gss_OID desired_object,
21+
gss_buffer_set_t *data_set) nogil
22+
23+
24+
def inquire_cred_by_oid(Creds cred_handle not None, OID mech not None):
25+
"""
26+
inquire_cred_by_oid(cred_handle, mech)
27+
28+
This method inspects a :class:`Creds` object for information
29+
specific to a particular mechanism.
30+
31+
Args:
32+
cred_handle (Creds): the security context to query
33+
mech (OID): the desired mechanism
34+
35+
Returns:
36+
list: A list of zero or more pieces of data corresponding to the
37+
OID set
38+
39+
Raises:
40+
GSS_ERROR
41+
"""
42+
43+
cdef gss_buffer_set_t *data_set_ptr = NULL
44+
cdef gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET
45+
cdef OM_uint32 maj_stat, min_stat
46+
47+
data_set_ptr = &data_set
48+
49+
with nogil:
50+
maj_stat = gss_inquire_cred_by_oid(&min_stat, cred_handle.raw_creds,
51+
&mech.raw_oid, data_set_ptr)
52+
53+
if maj_stat == GSS_S_COMPLETE:
54+
py_tokens = []
55+
56+
if data_set != GSS_C_NO_BUFFER_SET:
57+
for i in range(data_set.count):
58+
token = data_set.elements[i]
59+
py_tokens.append(token.value[:token.length])
60+
61+
gss_release_buffer_set(&min_stat, &data_set)
62+
63+
return py_tokens
64+
else:
65+
raise GSSError(maj_stat, min_stat)
66+
67+
68+
def inquire_sec_context_by_oid(SecurityContext context not None,
69+
OID mech not None):
70+
"""
71+
inquire_sec_context_by_oid(context, mech)
72+
73+
This method inspects a :class:`SecurityContext` object for information
74+
specific to a particular mechanism.
75+
76+
This method can be used with the GSS_KRB5_INQ_SSPI_SESSION_KEY_OID OID to
77+
retrieve the required key that is used to derive the SMB/SAMBA signing and
78+
encryption keys.
79+
80+
Args:
81+
context (SecurityContext): the security context to query
82+
mech (OID): the desired mechanism
83+
84+
Returns:
85+
list: A list of zero or more pieces of data corresponding to the
86+
OID set
87+
88+
Raises:
89+
GSS_ERROR
90+
"""
91+
92+
cdef gss_buffer_set_t *data_set_ptr = NULL
93+
cdef gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET
94+
cdef OM_uint32 maj_stat, min_stat
95+
96+
data_set_ptr = &data_set
97+
98+
with nogil:
99+
maj_stat = gss_inquire_sec_context_by_oid(&min_stat, context.raw_ctx,
100+
&mech.raw_oid, data_set_ptr)
101+
102+
if maj_stat == GSS_S_COMPLETE:
103+
py_tokens = []
104+
105+
if data_set != GSS_C_NO_BUFFER_SET:
106+
for i in range(data_set.count):
107+
token = data_set.elements[i]
108+
py_tokens.append(token.value[:token.length])
109+
110+
gss_release_buffer_set(&min_stat, &data_set)
111+
112+
return py_tokens
113+
else:
114+
raise GSSError(maj_stat, min_stat)

gssapi/tests/test_raw.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,93 @@ def test_sasl_names(self):
760760
cmp_mech.shouldnt_be_none()
761761
cmp_mech.should_be(mech)
762762

763+
@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
764+
@ktu.gssapi_extension_test('s4u', 'S4U')
765+
@ktu.krb_minversion_test('1.16',
766+
'querying impersonator name of krb5 GSS '
767+
'Credential using the '
768+
'GSS_KRB5_GET_CRED_IMPERSONATOR OID')
769+
def test_inquire_cred_by_oid_impersonator(self):
770+
svc_princ = SERVICE_PRINCIPAL.decode("UTF-8")
771+
self.realm.kinit(svc_princ, flags=['-k', '-f'])
772+
773+
target_name = gb.import_name(TARGET_SERVICE_NAME,
774+
gb.NameType.hostbased_service)
775+
776+
client_token = gb.init_sec_context(target_name).token
777+
778+
# if our acceptor creds have a usage of both, we get
779+
# s4u2proxy delegated credentials
780+
server_creds = gb.acquire_cred(None, usage='both').creds
781+
server_ctx_resp = gb.accept_sec_context(client_token,
782+
acceptor_creds=server_creds)
783+
784+
server_ctx_resp.shouldnt_be_none()
785+
server_ctx_resp.delegated_creds.shouldnt_be_none()
786+
server_ctx_resp.delegated_creds.should_be_a(gb.Creds)
787+
788+
# GSS_KRB5_GET_CRED_IMPERSONATOR
789+
oid = gb.OID.from_int_seq("1.2.840.113554.1.2.2.5.14")
790+
info = gb.inquire_cred_by_oid(server_ctx_resp.delegated_creds, oid)
791+
792+
info.should_be_a(list)
793+
info.shouldnt_be_empty()
794+
info[0].should_be_a(bytes)
795+
info[0].should_be(b"%s@%s" % (SERVICE_PRINCIPAL,
796+
self.realm.realm.encode('utf-8')))
797+
798+
@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
799+
def test_inquire_sec_context_by_oid(self):
800+
target_name = gb.import_name(TARGET_SERVICE_NAME,
801+
gb.NameType.hostbased_service)
802+
ctx_resp1 = gb.init_sec_context(target_name)
803+
804+
server_name = gb.import_name(SERVICE_PRINCIPAL,
805+
gb.NameType.kerberos_principal)
806+
server_creds = gb.acquire_cred(server_name)[0]
807+
server_resp = gb.accept_sec_context(ctx_resp1[3],
808+
acceptor_creds=server_creds)
809+
server_ctx = server_resp[0]
810+
server_tok = server_resp[3]
811+
812+
client_resp2 = gb.init_sec_context(target_name,
813+
context=ctx_resp1[0],
814+
input_token=server_tok)
815+
client_ctx = client_resp2[0]
816+
817+
# GSS_C_INQ_SSPI_SESSION_KEY
818+
session_key_oid = gb.OID.from_int_seq("1.2.840.113554.1.2.2.5.5")
819+
820+
client_key = gb.inquire_sec_context_by_oid(client_ctx, session_key_oid)
821+
server_key = gb.inquire_sec_context_by_oid(server_ctx, session_key_oid)
822+
823+
client_key.should_be_a(list)
824+
client_key.shouldnt_be_empty()
825+
server_key.should_be_a(list)
826+
server_key.shouldnt_be_empty()
827+
client_key.should_have_same_items_as(server_key)
828+
829+
@ktu.gssapi_extension_test('ggf', 'Global Grid Forum')
830+
def test_inquire_sec_context_by_oid_should_raise_error(self):
831+
target_name = gb.import_name(TARGET_SERVICE_NAME,
832+
gb.NameType.hostbased_service)
833+
ctx_resp1 = gb.init_sec_context(target_name)
834+
835+
server_name = gb.import_name(SERVICE_PRINCIPAL,
836+
gb.NameType.kerberos_principal)
837+
server_creds = gb.acquire_cred(server_name)[0]
838+
server_resp = gb.accept_sec_context(ctx_resp1[3],
839+
acceptor_creds=server_creds)
840+
841+
client_resp2 = gb.init_sec_context(target_name,
842+
context=ctx_resp1[0],
843+
input_token=server_resp[3])
844+
client_ctx = client_resp2[0]
845+
846+
invalid_oid = gb.OID.from_int_seq("1.2.3.4.5.6.7.8.9")
847+
gb.inquire_sec_context_by_oid.should_raise(gb.GSSError, client_ctx,
848+
invalid_oid)
849+
763850

764851
class TestIntEnumFlagSet(unittest.TestCase):
765852
def test_create_from_int(self):

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ def gssapi_modules(lst):
264264
extension_file('cred_imp_exp', 'gss_import_cred'),
265265
extension_file('dce', 'gss_wrap_iov'),
266266
extension_file('iov_mic', 'gss_get_mic_iov'),
267+
extension_file('ggf', 'gss_inquire_sec_context_by_oid'),
267268

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

0 commit comments

Comments
 (0)