Skip to content

Commit 0606aaa

Browse files
DirectXMan12frozencemetery
authored andcommitted
IOV MIC Extensions
The DCE extensions introduced the concept of IOV messages, but there was no way to just get a MIC for those messages. The IOV MIC GSSAPI extension add support for getting MICs for IOV messages. Closes #6
1 parent bab5c52 commit 0606aaa

File tree

6 files changed

+223
-10
lines changed

6 files changed

+223
-10
lines changed

gssapi/raw/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,7 @@
4747
# optional DCE (IOV/AEAD) support
4848
try:
4949
from gssapi.raw.ext_dce import * # noqa
50+
# optional IOV MIC support (requires DCE support)
51+
from gssapi.raw.ext_iov_mic import * # noqa
5052
except ImportError:
5153
pass

gssapi/raw/ext_dce.pxd

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from gssapi.raw.cython_types cimport gss_buffer_desc, OM_uint32
2+
3+
cdef extern from "python_gssapi_ext.h":
4+
ctypedef struct gss_iov_buffer_desc:
5+
OM_uint32 type
6+
gss_buffer_desc buffer
7+
ctypedef gss_iov_buffer_desc* gss_iov_buffer_t
8+
9+
cdef class IOV:
10+
cdef int iov_len
11+
cdef bint c_changed
12+
13+
cdef bint _unprocessed
14+
cdef list _buffs
15+
cdef gss_iov_buffer_desc *_iov
16+
17+
cdef gss_iov_buffer_desc* __cvalue__(IOV self) except NULL
18+
cdef _recreate_python_values(IOV self)

gssapi/raw/ext_dce.pyx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@ from enum import IntEnum
1515
import six
1616

1717
cdef extern from "python_gssapi_ext.h":
18-
ctypedef struct gss_iov_buffer_desc:
19-
OM_uint32 type
20-
gss_buffer_desc buffer
21-
ctypedef gss_iov_buffer_desc* gss_iov_buffer_t
22-
2318
# NB(directxman12): this wiki page has a different argument order
2419
# than the header file, and uses size_t instead of int
2520
# (this file matches the header file)
@@ -106,11 +101,14 @@ IOVBuffer = namedtuple('IOVBuffer', ['type', 'allocate', 'value'])
106101

107102

108103
cdef class IOV:
109-
cdef gss_iov_buffer_desc *_iov
110-
cdef int iov_len
111-
cdef bint _unprocessed
112-
cdef bint c_changed
113-
cdef list _buffs
104+
# defined in ext_dce.pxd
105+
106+
# cdef int iov_len
107+
# cdef bint c_changed
108+
109+
# cdef gss_iov_buffer_desc *_iov
110+
# cdef bint _unprocessed
111+
# cdef list _buffs
114112

115113
AUTO_ALLOC_BUFFERS = set([IOVBufferType.header, IOVBufferType.padding,
116114
IOVBufferType.trailer])

gssapi/raw/ext_iov_mic.pyx

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
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.sec_contexts cimport SecurityContext
5+
from gssapi.raw.ext_dce cimport IOV, gss_iov_buffer_desc
6+
7+
from gssapi.raw.misc import GSSError, _EnumExtension
8+
from gssapi.raw import ext_dce
9+
10+
import six
11+
12+
cdef extern from "python_gssapi_ext.h":
13+
OM_uint32 gss_get_mic_iov(OM_uint32 *min_stat, gss_ctx_id_t context_handle,
14+
gss_qop_t qop_req, gss_iov_buffer_desc *iov,
15+
int iov_count) nogil
16+
17+
OM_uint32 gss_get_mic_iov_length(OM_uint32 *min_stat,
18+
gss_ctx_id_t context_handle,
19+
gss_qop_t qop_req,
20+
gss_iov_buffer_desc *iov,
21+
int iov_count) nogil
22+
23+
OM_uint32 gss_verify_mic_iov(OM_uint32 *min_stat,
24+
gss_ctx_id_t context_handle,
25+
gss_qop_t *qop_state,
26+
gss_iov_buffer_desc *iov,
27+
int iov_count) nogil
28+
29+
OM_uint32 GSS_IOV_BUFFER_TYPE_MIC_TOKEN
30+
31+
32+
@six.add_metaclass(_EnumExtension)
33+
class IOVBufferType(object):
34+
__base__ = ext_dce.IOVBufferType
35+
mic_token = GSS_IOV_BUFFER_TYPE_MIC_TOKEN
36+
37+
38+
IOV.AUTO_ALLOC_BUFFERS.add(IOVBufferType.mic_token)
39+
40+
41+
def get_mic_iov(SecurityContext context not None, IOV message not None,
42+
qop=None):
43+
"""
44+
Generate MIC tokens for the given IOV message
45+
46+
This method generates a MIC token for the given IOV message, and places it
47+
in the :attr:`IOVBufferType.mic_token` buffer in the IOV. This method
48+
operates entirely in-place, and returns nothing.
49+
50+
Args:
51+
context (SecurityContext): the current security context
52+
message (list): a list of :class:`IOVBuffer` objects
53+
qop (int): the desired Quality of Protection
54+
(or None for the default QoP)
55+
56+
Raises:
57+
GSSError
58+
"""
59+
60+
cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT
61+
62+
cdef gss_iov_buffer_desc *res_arr = message.__cvalue__()
63+
64+
cdef OM_uint32 maj_stat, min_stat
65+
66+
with nogil:
67+
maj_stat = gss_get_mic_iov(&min_stat, context.raw_ctx, qop_req,
68+
res_arr, message.iov_len)
69+
70+
if maj_stat == GSS_S_COMPLETE:
71+
message.c_changed = True
72+
return
73+
else:
74+
raise GSSError(maj_stat, min_stat)
75+
76+
77+
def get_mic_iov_length(SecurityContext context not None, IOV message not None,
78+
qop=None):
79+
"""
80+
Allocate space for the MIC buffer in the given IOV message
81+
82+
This method allocates space for the MIC token buffer
83+
(:attr:`IOVBufferType.mic_token`) in the given IOV message.
84+
85+
Args:
86+
context (SecurityContext): the current security context
87+
message (list): a list of :class:`IOVBuffer` objects
88+
qop (int): the desired Quality of Protection
89+
(or None for the default QoP)
90+
91+
Raises:
92+
GSSError
93+
"""
94+
95+
cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT
96+
97+
cdef gss_iov_buffer_desc *res_arr = message.__cvalue__()
98+
99+
cdef OM_uint32 maj_stat, min_stat
100+
101+
with nogil:
102+
maj_stat = gss_get_mic_iov_length(&min_stat, context.raw_ctx, qop_req,
103+
res_arr, message.iov_len)
104+
105+
if maj_stat == GSS_S_COMPLETE:
106+
message.c_changed = True
107+
return
108+
else:
109+
raise GSSError(maj_stat, min_stat)
110+
111+
112+
def verify_mic_iov(SecurityContext context not None, IOV message not None,
113+
qop=None):
114+
"""
115+
Verify that the MIC matches the data in the given IOV message
116+
117+
This method verifies that the MIC token in the MIC buffer
118+
(:attr:`IOVBufferType.mic_token`) match the data buffer(s)
119+
in the given IOV method.
120+
121+
Args:
122+
context (SecurityContext): the current security context
123+
message (list): a list of :class:`IOVBuffer` objects
124+
125+
Returns:
126+
int: the QoP used to generate the MIC token
127+
128+
Raises:
129+
GSSError
130+
"""
131+
132+
cdef gss_iov_buffer_desc *res_arr = message.__cvalue__()
133+
134+
cdef gss_qop_t qop_state
135+
136+
cdef OM_uint32 maj_stat, min_stat
137+
138+
with nogil:
139+
maj_stat = gss_verify_mic_iov(&min_stat, context.raw_ctx, &qop_state,
140+
res_arr, message.iov_len)
141+
142+
if maj_stat == GSS_S_COMPLETE:
143+
return qop_state
144+
else:
145+
raise GSSError(maj_stat, min_stat)

gssapi/tests/test_raw.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,55 @@ def test_basic_aead_wrap_unwrap_bad_assoc_raises_error(self):
10861086
gb.unwrap_aead.should_raise(gb.BadMICError, self.server_ctx,
10871087
wrapped_message, b'some other sig data')
10881088

1089+
@_extension_test('iov_mic', 'IOV MIC')
1090+
def test_get_mic_iov(self):
1091+
init_message = gb.IOV(b'some data',
1092+
(gb.IOVBufferType.sign_only, b'some sig data'),
1093+
gb.IOVBufferType.mic_token, std_layout=False)
1094+
1095+
gb.get_mic_iov(self.client_ctx, init_message)
1096+
1097+
init_message[2].type.should_be(gb.IOVBufferType.mic_token)
1098+
init_message[2].value.shouldnt_be_empty()
1099+
1100+
@_extension_test('iov_mic', 'IOV MIC')
1101+
def test_basic_verify_mic_iov(self):
1102+
init_message = gb.IOV(b'some data',
1103+
(gb.IOVBufferType.sign_only, b'some sig data'),
1104+
gb.IOVBufferType.mic_token, std_layout=False)
1105+
1106+
gb.get_mic_iov(self.client_ctx, init_message)
1107+
1108+
init_message[2].type.should_be(gb.IOVBufferType.mic_token)
1109+
init_message[2].value.shouldnt_be_empty()
1110+
1111+
qop_used = gb.verify_mic_iov(self.server_ctx, init_message)
1112+
1113+
qop_used.should_be_an_integer()
1114+
1115+
@_extension_test('iov_mic', 'IOV MIC')
1116+
def test_verify_mic_iov_bad_mic_raises_error(self):
1117+
init_message = gb.IOV(b'some data',
1118+
(gb.IOVBufferType.sign_only, b'some sig data'),
1119+
(gb.IOVBufferType.mic_token, 'abaava'),
1120+
std_layout=False)
1121+
1122+
# test a bad MIC
1123+
gb.verify_mic_iov.should_raise(gb.GSSError, self.server_ctx,
1124+
init_message)
1125+
1126+
@_extension_test('iov_mic', 'IOV MIC')
1127+
def test_get_mic_iov_length(self):
1128+
init_message = gb.IOV(b'some data',
1129+
(gb.IOVBufferType.sign_only, b'some sig data'),
1130+
gb.IOVBufferType.mic_token, std_layout=False,
1131+
auto_alloc=False)
1132+
1133+
gb.get_mic_iov_length(self.client_ctx, init_message)
1134+
1135+
init_message[2].type.should_be(gb.IOVBufferType.mic_token)
1136+
init_message[2].value.shouldnt_be_empty()
1137+
10891138

10901139
TEST_OIDS = {'SPNEGO': {'bytes': b'\053\006\001\005\005\002',
10911140
'string': '1.3.6.1.5.5.2'},

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ def gssapi_modules(lst):
192192
extension_file('rfc5588', 'gss_store_cred'),
193193
extension_file('cred_imp_exp', 'gss_import_cred'),
194194
extension_file('dce', 'gss_wrap_iov'),
195+
extension_file('iov_mic', 'gss_get_mic_iov'),
195196

196197
# see ext_password{,_add}.pyx for more information on this split
197198
extension_file('password', 'gss_acquire_cred_with_password'),

0 commit comments

Comments
 (0)