Skip to content

Commit

Permalink
IOV MIC Extensions
Browse files Browse the repository at this point in the history
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
  • Loading branch information
DirectXMan12 committed Feb 13, 2015
1 parent 66975cb commit c725158
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 10 deletions.
2 changes: 2 additions & 0 deletions gssapi/raw/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,7 @@
# optional DCE (IOV/AEAD) support
try:
from gssapi.raw.ext_dce import * # noqa
# optional IOV MIC support (requires DCE support)
from gssapi.raw.ext_iov_mic import * # noqa
except ImportError:
pass
18 changes: 18 additions & 0 deletions gssapi/raw/ext_dce.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from gssapi.raw.cython_types cimport gss_buffer_desc, OM_uint32

cdef extern from "python_gssapi_ext.h":
ctypedef struct gss_iov_buffer_desc:
OM_uint32 type
gss_buffer_desc buffer
ctypedef gss_iov_buffer_desc* gss_iov_buffer_t

cdef class IOV:
cdef int iov_len
cdef bint c_changed

cdef bint _unprocessed
cdef list _buffs
cdef gss_iov_buffer_desc *_iov

cdef gss_iov_buffer_desc* __cvalue__(IOV self) except NULL
cdef _recreate_python_values(IOV self)
18 changes: 8 additions & 10 deletions gssapi/raw/ext_dce.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ from enum import IntEnum
import six

cdef extern from "python_gssapi_ext.h":
ctypedef struct gss_iov_buffer_desc:
OM_uint32 type
gss_buffer_desc buffer
ctypedef gss_iov_buffer_desc* gss_iov_buffer_t

# NB(directxman12): this wiki page has a different argument order
# than the header file, and uses size_t instead of int
# (this file matches the header file)
Expand Down Expand Up @@ -106,11 +101,14 @@ IOVBuffer = namedtuple('IOVBuffer', ['type', 'allocate', 'value'])


cdef class IOV:
cdef gss_iov_buffer_desc *_iov
cdef int iov_len
cdef bint _unprocessed
cdef bint c_changed
cdef list _buffs
# defined in ext_dce.pxd

# cdef int iov_len
# cdef bint c_changed

# cdef gss_iov_buffer_desc *_iov
# cdef bint _unprocessed
# cdef list _buffs

AUTO_ALLOC_BUFFERS = set([IOVBufferType.header, IOVBufferType.padding,
IOVBufferType.trailer])
Expand Down
145 changes: 145 additions & 0 deletions gssapi/raw/ext_iov_mic.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
GSSAPI="BASE" # This ensures that a full module is generated by Cython

from gssapi.raw.cython_types cimport *
from gssapi.raw.sec_contexts cimport SecurityContext
from gssapi.raw.ext_dce cimport IOV, gss_iov_buffer_desc

from gssapi.raw.misc import GSSError, _EnumExtension
from gssapi.raw import ext_dce

import six

cdef extern from "python_gssapi_ext.h":
OM_uint32 gss_get_mic_iov(OM_uint32 *min_stat, gss_ctx_id_t context_handle,
gss_qop_t qop_req, gss_iov_buffer_desc *iov,
int iov_count) nogil

OM_uint32 gss_get_mic_iov_length(OM_uint32 *min_stat,
gss_ctx_id_t context_handle,
gss_qop_t qop_req,
gss_iov_buffer_desc *iov,
int iov_count) nogil

OM_uint32 gss_verify_mic_iov(OM_uint32 *min_stat,
gss_ctx_id_t context_handle,
gss_qop_t *qop_state,
gss_iov_buffer_desc *iov,
int iov_count) nogil

OM_uint32 GSS_IOV_BUFFER_TYPE_MIC_TOKEN


@six.add_metaclass(_EnumExtension)
class IOVBufferType(object):
__base__ = ext_dce.IOVBufferType
mic_token = GSS_IOV_BUFFER_TYPE_MIC_TOKEN


IOV.AUTO_ALLOC_BUFFERS.add(IOVBufferType.mic_token)


def get_mic_iov(SecurityContext context not None, IOV message not None,
qop=None):
"""
Generate MIC tokens for the given IOV message
This method generates a MIC token for the given IOV message, and places it
in the :attr:`IOVBufferType.mic_token` buffer in the IOV. This method
operates entirely in-place, and returns nothing.
Args:
context (SecurityContext): the current security context
message (list): a list of :class:`IOVBuffer` objects
qop (int): the desired Quality of Protection
(or None for the default QoP)
Raises:
GSSError
"""

cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT

cdef gss_iov_buffer_desc *res_arr = message.__cvalue__()

cdef OM_uint32 maj_stat, min_stat

with nogil:
maj_stat = gss_get_mic_iov(&min_stat, context.raw_ctx, qop_req,
res_arr, message.iov_len)

if maj_stat == GSS_S_COMPLETE:
message.c_changed = True
return
else:
raise GSSError(maj_stat, min_stat)


def get_mic_iov_length(SecurityContext context not None, IOV message not None,
qop=None):
"""
Allocate space for the MIC buffer in the given IOV message
This method allocates space for the MIC token buffer
(:attr:`IOVBufferType.mic_token`) in the given IOV message.
Args:
context (SecurityContext): the current security context
message (list): a list of :class:`IOVBuffer` objects
qop (int): the desired Quality of Protection
(or None for the default QoP)
Raises:
GSSError
"""

cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT

cdef gss_iov_buffer_desc *res_arr = message.__cvalue__()

cdef OM_uint32 maj_stat, min_stat

with nogil:
maj_stat = gss_get_mic_iov_length(&min_stat, context.raw_ctx, qop_req,
res_arr, message.iov_len)

if maj_stat == GSS_S_COMPLETE:
message.c_changed = True
return
else:
raise GSSError(maj_stat, min_stat)


def verify_mic_iov(SecurityContext context not None, IOV message not None,
qop=None):
"""
Verify that the MIC matches the data in the given IOV message
This method verifies that the MIC token in the MIC buffer
(:attr:`IOVBufferType.mic_token`) match the data buffer(s)
in the given IOV method.
Args:
context (SecurityContext): the current security context
message (list): a list of :class:`IOVBuffer` objects
Returns:
int: the QoP used to generate the MIC token
Raises:
GSSError
"""

cdef gss_iov_buffer_desc *res_arr = message.__cvalue__()

cdef gss_qop_t qop_state

cdef OM_uint32 maj_stat, min_stat

with nogil:
maj_stat = gss_verify_mic_iov(&min_stat, context.raw_ctx, &qop_state,
res_arr, message.iov_len)

if maj_stat == GSS_S_COMPLETE:
return qop_state
else:
raise GSSError(maj_stat, min_stat)
49 changes: 49 additions & 0 deletions gssapi/tests/test_raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,55 @@ def test_basic_aead_wrap_unwrap_bad_assoc_raises_error(self):
gb.unwrap_aead.should_raise(gb.BadMICError, self.server_ctx,
wrapped_message, b'some other sig data')

@_extension_test('iov_mic', 'IOV MIC')
def test_get_mic_iov(self):
init_message = gb.IOV(b'some data',
(gb.IOVBufferType.sign_only, b'some sig data'),
gb.IOVBufferType.mic_token, std_layout=False)

gb.get_mic_iov(self.client_ctx, init_message)

init_message[2].type.should_be(gb.IOVBufferType.mic_token)
init_message[2].value.shouldnt_be_empty()

@_extension_test('iov_mic', 'IOV MIC')
def test_basic_verify_mic_iov(self):
init_message = gb.IOV(b'some data',
(gb.IOVBufferType.sign_only, b'some sig data'),
gb.IOVBufferType.mic_token, std_layout=False)

gb.get_mic_iov(self.client_ctx, init_message)

init_message[2].type.should_be(gb.IOVBufferType.mic_token)
init_message[2].value.shouldnt_be_empty()

qop_used = gb.verify_mic_iov(self.server_ctx, init_message)

qop_used.should_be_an_integer()

@_extension_test('iov_mic', 'IOV MIC')
def test_verify_mic_iov_bad_mic_raises_error(self):
init_message = gb.IOV(b'some data',
(gb.IOVBufferType.sign_only, b'some sig data'),
(gb.IOVBufferType.mic_token, 'abaava'),
std_layout=False)

# test a bad MIC
gb.verify_mic_iov.should_raise(gb.GSSError, self.server_ctx,
init_message)

@_extension_test('iov_mic', 'IOV MIC')
def test_get_mic_iov_length(self):
init_message = gb.IOV(b'some data',
(gb.IOVBufferType.sign_only, b'some sig data'),
gb.IOVBufferType.mic_token, std_layout=False,
auto_alloc=False)

gb.get_mic_iov_length(self.client_ctx, init_message)

init_message[2].type.should_be(gb.IOVBufferType.mic_token)
init_message[2].value.shouldnt_be_empty()


TEST_OIDS = {'SPNEGO': {'bytes': b'\053\006\001\005\005\002',
'string': '1.3.6.1.5.5.2'},
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ def gssapi_modules(lst):
extension_file('rfc5588', 'gss_store_cred'),
extension_file('cred_imp_exp', 'gss_import_cred'),
extension_file('dce', 'gss_wrap_iov'),
extension_file('iov_mic', 'gss_get_mic_iov'),

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

0 comments on commit c725158

Please sign in to comment.