Skip to content

Commit 90de1d7

Browse files
committed
Introduce Channel Bindings Support
This commit introduces support for channel bindings. The `ChannelBindings` class, found in `raw`, may be used to create a set or channel bindings that may be passed to both the high- and low-level APIs. Additionally, a new `IntEnum` named `AddressType` has been introduced; `AddressType` contains potential values for the initiator and acceptor address-type fields in the `ChannelBindings` class.
1 parent e28d809 commit 90de1d7

File tree

9 files changed

+247
-6
lines changed

9 files changed

+247
-6
lines changed

gssapi/raw/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from gssapi.raw.sec_contexts import * # noqa
77
from gssapi.raw.oids import * # noqa
88
from gssapi.raw.types import * # noqa
9+
from gssapi.raw.chan_bindings import * # noqa
910

1011
# optional S4U support
1112
try:

gssapi/raw/chan_bindings.pxd

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from libc.stdlib cimport malloc, free
2+
3+
from gssapi.raw.cython_types cimport *
4+
5+
cdef class ChannelBindings:
6+
cdef public object initiator_address_type
7+
cdef public bytes initiator_address
8+
9+
cdef public object acceptor_address_type
10+
cdef public bytes acceptor_address
11+
12+
cdef public bytes application_data
13+
14+
cdef gss_channel_bindings_t __cvalue__(ChannelBindings self)

gssapi/raw/chan_bindings.pyx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from libc.stdlib cimport malloc, free
2+
3+
from gssapi.raw.cython_types cimport *
4+
5+
cdef class ChannelBindings:
6+
# defined in pxd file
7+
# cdef public object initiator_address_type
8+
# cdef public bytes initiator_address
9+
10+
# cdef public object acceptor_address_type
11+
# cdef public bytes acceptor_address
12+
13+
# cdef public bytes application_data
14+
15+
def __init__(ChannelBindings self, initiator_address_type=None,
16+
initiator_address=None, acceptor_address_type=None,
17+
acceptor_address=None, application_data=None):
18+
self.initiator_address_type = initiator_address_type
19+
self.initiator_address = initiator_address
20+
21+
self.acceptor_address_type = acceptor_address_type
22+
self.acceptor_address = acceptor_address
23+
24+
self.application_data = application_data
25+
26+
cdef gss_channel_bindings_t __cvalue__(ChannelBindings self) except NULL:
27+
cdef gss_channel_bindings_t res
28+
res = <gss_channel_bindings_t>malloc(sizeof(res[0]))
29+
30+
if self.initiator_address_type is None:
31+
res.initiator_addrtype = GSS_C_AF_NULLADDR
32+
else:
33+
res.initiator_addrtype = self.initiator_address_type
34+
35+
if self.initiator_address is not None:
36+
res.initiator_address.value = self.initiator_address
37+
res.initiator_address.length = len(self.initiator_address)
38+
else:
39+
res.initiator_address.length = 0
40+
41+
if self.acceptor_address_type is None:
42+
res.acceptor_addrtype = GSS_C_AF_NULLADDR
43+
else:
44+
res.acceptor_addrtype = self.acceptor_address_type
45+
46+
if self.acceptor_address is not None:
47+
res.acceptor_address.value = self.acceptor_address
48+
res.acceptor_address.length = len(self.acceptor_address)
49+
else:
50+
res.acceptor_address.length = 0
51+
52+
if self.application_data is not None:
53+
res.application_data.value = self.application_data
54+
res.application_data.length = len(self.application_data)
55+
else:
56+
res.application_data.length = 0
57+
58+
return res

gssapi/raw/cython_types.pxd

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,30 @@ cdef extern from "gssapi.h":
106106
OM_uint32 GSS_C_TRANS_FLAG
107107
OM_uint32 GSS_C_PROT_READY_FLAG
108108

109+
# address types
110+
OM_uint32 GSS_C_AF_UNSPEC
111+
OM_uint32 GSS_C_AF_LOCAL
112+
OM_uint32 GSS_C_AF_INET
113+
OM_uint32 GSS_C_AF_IMPLINK
114+
OM_uint32 GSS_C_AF_PUP
115+
OM_uint32 GSS_C_AF_CHAOS
116+
OM_uint32 GSS_C_AF_NS
117+
OM_uint32 GSS_C_AF_NBS
118+
OM_uint32 GSS_C_AF_ECMA
119+
OM_uint32 GSS_C_AF_DATAKIT
120+
OM_uint32 GSS_C_AF_CCITT
121+
OM_uint32 GSS_C_AF_SNA
122+
OM_uint32 GSS_C_AF_DECnet
123+
OM_uint32 GSS_C_AF_DLI
124+
OM_uint32 GSS_C_AF_LAT
125+
OM_uint32 GSS_C_AF_HYLINK
126+
OM_uint32 GSS_C_AF_APPLETALK
127+
OM_uint32 GSS_C_AF_BSC
128+
OM_uint32 GSS_C_AF_DSS
129+
OM_uint32 GSS_C_AF_OSI
130+
OM_uint32 GSS_C_AF_X25
131+
OM_uint32 GSS_C_AF_NULLADDR
132+
109133
# error helpers
110134
OM_uint32 GSS_CALLING_ERROR(OM_uint32 full_error)
111135
OM_uint32 GSS_ROUTINE_ERROR(OM_uint32 full_error)

gssapi/raw/sec_contexts.pyx

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
GSSAPI="BASE" # This ensures that a full module is generated by Cython
22

3+
from libc.stdlib cimport free
4+
35
from gssapi.raw.cython_types cimport *
46
from gssapi.raw.cython_converters cimport c_py_ttl_to_c, c_c_ttl_to_py
57
from gssapi.raw.creds cimport Creds
68
from gssapi.raw.names cimport Name
79
from gssapi.raw.oids cimport OID
10+
from gssapi.raw.chan_bindings cimport ChannelBindings
811

912
from gssapi.raw.types import MechType, RequirementFlag, IntEnumFlagSet
1013
from gssapi.raw.misc import GSSError
@@ -110,7 +113,8 @@ cdef class SecurityContext:
110113
def init_sec_context(Name target_name not None, Creds cred=None,
111114
SecurityContext context=None,
112115
OID mech_type=None,
113-
flags=None, ttl=None, channel_bindings=None,
116+
flags=None, ttl=None,
117+
ChannelBindings channel_bindings=None,
114118
input_token=None):
115119
"""
116120
Initiate a GSSAPI Security Context.
@@ -136,7 +140,8 @@ def init_sec_context(Name target_name not None, Creds cred=None,
136140
out_of_sequence_detection
137141
ttl (int): the request lifetime of the security context (a value of
138142
0 or None means indefinite)
139-
channel_bindings (ChannelBindings): NCI
143+
channel_bindings (ChannelBindings): The channel bindings (or None for
144+
no channel bindings)
140145
input_token (bytes): the token to use to update the security context,
141146
or None if you are creating a new context
142147
@@ -162,7 +167,12 @@ def init_sec_context(Name target_name not None, Creds cred=None,
162167
RequirementFlag.mutual_authentication,
163168
RequirementFlag.out_of_sequence_detection])
164169

165-
cdef gss_channel_bindings_t bdng = GSS_C_NO_CHANNEL_BINDINGS
170+
cdef gss_channel_bindings_t bdng
171+
if channel_bindings is not None:
172+
bdng = channel_bindings.__cvalue__()
173+
else:
174+
bdng = GSS_C_NO_CHANNEL_BINDINGS
175+
166176
# TODO(sross): just import GSS_C_EMPTY_BUFFER == gss_buffer_desc(0, NULL)
167177
cdef gss_buffer_desc input_token_buffer = gss_buffer_desc(0, NULL)
168178

@@ -205,6 +215,9 @@ def init_sec_context(Name target_name not None, Creds cred=None,
205215
output_token = output_token_buffer.value[:output_token_buffer.length]
206216
gss_release_buffer(&min_stat, &output_token_buffer)
207217

218+
if channel_bindings is not None:
219+
free(bdng)
220+
208221
cdef OID output_mech_type = OID()
209222
if maj_stat == GSS_S_COMPLETE or maj_stat == GSS_S_CONTINUE_NEEDED:
210223
output_mech_type.raw_oid = actual_mech_type[0]
@@ -219,7 +232,8 @@ def init_sec_context(Name target_name not None, Creds cred=None,
219232

220233

221234
def accept_sec_context(input_token not None, Creds acceptor_cred=None,
222-
SecurityContext context=None, channel_bindings=None):
235+
SecurityContext context=None,
236+
ChannelBindings channel_bindings=None):
223237
"""
224238
Accept a GSSAPI security context.
225239
@@ -237,7 +251,8 @@ def accept_sec_context(input_token not None, Creds acceptor_cred=None,
237251
(or None to use the default credentials)
238252
context (SecurityContext): the security context to update
239253
(or None to create a new security context object)
240-
channel_bindings: NCI
254+
channel_bindings (ChannelBindings): The channel bindings (or None for
255+
no channel bindings)
241256
242257
Returns:
243258
AcceptSecContextResult: the resulting security context, the initiator
@@ -251,7 +266,12 @@ def accept_sec_context(input_token not None, Creds acceptor_cred=None,
251266
GSSError
252267
"""
253268

254-
cdef gss_channel_bindings_t bdng = GSS_C_NO_CHANNEL_BINDINGS
269+
cdef gss_channel_bindings_t bdng
270+
if channel_bindings is not None:
271+
bdng = channel_bindings.__cvalue__()
272+
else:
273+
bdng = GSS_C_NO_CHANNEL_BINDINGS
274+
255275
cdef gss_buffer_desc input_token_buffer = gss_buffer_desc(len(input_token),
256276
input_token)
257277

@@ -289,6 +309,9 @@ def accept_sec_context(input_token not None, Creds acceptor_cred=None,
289309
output_token = output_token_buffer.value[:output_token_buffer.length]
290310
gss_release_buffer(&min_stat, &output_token_buffer)
291311

312+
if channel_bindings is not None:
313+
free(bdng)
314+
292315
cdef Name on = Name()
293316
cdef Creds oc = Creds()
294317
cdef OID py_mech_type

gssapi/raw/types.pyx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,41 @@ class RequirementFlag(IntEnum):
5858
transferable = GSS_C_TRANS_FLAG
5959

6060

61+
class AddressType(IntEnum):
62+
"""
63+
GSSAPI Channel Bindings Address Types
64+
65+
This IntEnum represents the various address
66+
types used with the channel bindings structure.
67+
68+
The numbers behind the values correspond directly
69+
to their C counterparts.
70+
"""
71+
72+
unspecified = GSS_C_AF_UNSPEC
73+
local = GSS_C_AF_LOCAL
74+
ip = GSS_C_AF_INET
75+
arpanet = GSS_C_AF_IMPLINK # ARPAnet support, heh, heh
76+
pup = GSS_C_AF_PUP
77+
chaos = GSS_C_AF_CHAOS
78+
xerox_ns = GSS_C_AF_NS # and XEROX too?
79+
nbs = GSS_C_AF_NBS
80+
ecma = GSS_C_AF_ECMA
81+
datakit = GSS_C_AF_DATAKIT
82+
ccitt = GSS_C_AF_CCITT
83+
ibm_sna = GSS_C_AF_SNA
84+
decnet = GSS_C_AF_DECnet
85+
dli = GSS_C_AF_DLI
86+
lat = GSS_C_AF_LAT
87+
hyperchannel = GSS_C_AF_HYLINK
88+
appletalk = GSS_C_AF_APPLETALK # this list just keeps getting better
89+
bisync = GSS_C_AF_BSC
90+
dss = GSS_C_AF_DSS
91+
osi_tp4 = GSS_C_AF_OSI
92+
x25 = GSS_C_AF_X25
93+
# None --> GSS_C_AF_NULLADDR
94+
95+
6196
class MechType(object):
6297
"""
6398
GSSAPI Mechanism Types

gssapi/tests/test_base.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,55 @@ def test_basic_accept_context(self):
588588

589589
cont_needed.should_be_a(bool)
590590

591+
def test_channel_bindings(self):
592+
bdgs = gb.ChannelBindings(application_data=b'abcxyz',
593+
initiator_address_type=gb.AddressType.ip,
594+
initiator_address=b'127.0.0.1',
595+
acceptor_address_type=gb.AddressType.ip,
596+
acceptor_address=b'127.0.0.1')
597+
self.target_name = gb.import_name(TARGET_SERVICE_NAME,
598+
gb.NameType.hostbased_service)
599+
ctx_resp = gb.init_sec_context(self.target_name,
600+
channel_bindings=bdgs)
601+
602+
self.client_token = ctx_resp[3]
603+
self.client_ctx = ctx_resp[0]
604+
self.client_ctx.shouldnt_be_none()
605+
606+
self.server_name = gb.import_name(SERVICE_PRINCIPAL,
607+
gb.NameType.kerberos_principal)
608+
self.server_creds = gb.acquire_cred(self.server_name)[0]
609+
610+
server_resp = gb.accept_sec_context(self.client_token,
611+
acceptor_cred=self.server_creds,
612+
channel_bindings=bdgs)
613+
server_resp.shouldnt_be_none
614+
self.server_ctx = server_resp.context
615+
616+
def test_bad_channel_binding_raises_error(self):
617+
bdgs = gb.ChannelBindings(application_data=b'abcxyz',
618+
initiator_address_type=gb.AddressType.ip,
619+
initiator_address=b'127.0.0.1',
620+
acceptor_address_type=gb.AddressType.ip,
621+
acceptor_address=b'127.0.0.1')
622+
self.target_name = gb.import_name(TARGET_SERVICE_NAME,
623+
gb.NameType.hostbased_service)
624+
ctx_resp = gb.init_sec_context(self.target_name,
625+
channel_bindings=bdgs)
626+
627+
self.client_token = ctx_resp[3]
628+
self.client_ctx = ctx_resp[0]
629+
self.client_ctx.shouldnt_be_none()
630+
631+
self.server_name = gb.import_name(SERVICE_PRINCIPAL,
632+
gb.NameType.kerberos_principal)
633+
self.server_creds = gb.acquire_cred(self.server_name)[0]
634+
635+
bdgs.acceptor_address = b'127.0.1.0'
636+
gb.accept_sec_context.should_raise(gb.GSSError, self.client_token,
637+
acceptor_cred=self.server_creds,
638+
channel_bindings=bdgs)
639+
591640

592641
class TestWrapUnwrap(_GSSAPIKerberosTestCase):
593642
def setUp(self):

gssapi/tests/test_high_level.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,42 @@ def test_initiate_accept_steps(self):
458458
client_ctx.locally_initiated.should_be_true()
459459
client_ctx.complete.should_be_true()
460460

461+
def test_channel_bindings(self):
462+
bdgs = gb.ChannelBindings(application_data=b'abcxyz',
463+
initiator_address_type=gb.AddressType.ip,
464+
initiator_address=b'127.0.0.1',
465+
acceptor_address_type=gb.AddressType.ip,
466+
acceptor_address=b'127.0.0.1')
467+
client_ctx = self._create_client_ctx(desired_lifetime=400,
468+
channel_bindings=bdgs)
469+
470+
client_token = client_ctx.step()
471+
client_token.should_be_a(bytes)
472+
473+
server_ctx = gssctx.SecurityContext(creds=self.server_creds,
474+
channel_bindings=bdgs)
475+
server_token = server_ctx.step(client_token)
476+
server_token.should_be_a(bytes)
477+
478+
client_ctx.step(server_token)
479+
480+
def test_bad_channel_bindings_raises_error(self):
481+
bdgs = gb.ChannelBindings(application_data=b'abcxyz',
482+
initiator_address_type=gb.AddressType.ip,
483+
initiator_address=b'127.0.0.1',
484+
acceptor_address_type=gb.AddressType.ip,
485+
acceptor_address=b'127.0.0.1')
486+
client_ctx = self._create_client_ctx(desired_lifetime=400,
487+
channel_bindings=bdgs)
488+
489+
client_token = client_ctx.step()
490+
client_token.should_be_a(bytes)
491+
492+
bdgs.acceptor_address = b'127.0.1.0'
493+
server_ctx = gssctx.SecurityContext(creds=self.server_creds,
494+
channel_bindings=bdgs)
495+
server_ctx.step.should_raise(gb.BadChannelBindingsError, client_token)
496+
461497
def test_export_create_from_token(self):
462498
client_ctx, server_ctx = self._create_completed_contexts()
463499
token = client_ctx.export()

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ def gssapi_modules(lst):
143143
main_file('message'),
144144
main_file('oids'),
145145
main_file('cython_converters'),
146+
main_file('chan_bindings'),
146147
extension_file('s4u', 'gss_acquire_cred_impersonate_name'),
147148
extension_file('cred_store', 'gss_store_cred_into'),
148149
extension_file('rfc5588', 'gss_store_cred'),

0 commit comments

Comments
 (0)