From 6b2e6a2606b9f3a4ed2fb901b483c61b337ac86a Mon Sep 17 00:00:00 2001 From: Solly Ross Date: Thu, 12 Feb 2015 13:40:05 -0500 Subject: [PATCH] Low-level: Implement DCE Extensions The GSSAPI DCE Extensions include support for wrapping and unwrapping IOV and AEAD messages (this allows for DCE RPC and other SSPI functionality). Part of #6 --- gssapi/raw/__init__.py | 6 + gssapi/raw/ext_dce.pyx | 558 +++++++++++++++++++++++++++++++++++++ gssapi/raw/named_tuples.py | 5 + gssapi/tests/test_raw.py | 142 ++++++++++ setup.py | 3 +- 5 files changed, 713 insertions(+), 1 deletion(-) create mode 100644 gssapi/raw/ext_dce.pyx diff --git a/gssapi/raw/__init__.py b/gssapi/raw/__init__.py index a17b4569..57ed4ef9 100644 --- a/gssapi/raw/__init__.py +++ b/gssapi/raw/__init__.py @@ -43,3 +43,9 @@ from gssapi.raw.ext_password_add import * # noqa except ImportError: pass + +# optional DCE (IOV/AEAD) support +try: + from gssapi.raw.ext_dce import * # noqa +except ImportError: + pass diff --git a/gssapi/raw/ext_dce.pyx b/gssapi/raw/ext_dce.pyx new file mode 100644 index 00000000..e69b10d5 --- /dev/null +++ b/gssapi/raw/ext_dce.pyx @@ -0,0 +1,558 @@ +GSSAPI="BASE" # This ensures that a full module is generated by Cython + +from libc.stdlib cimport malloc, calloc, free +from libc.string cimport memcpy + +from gssapi.raw.cython_types cimport * +from gssapi.raw.sec_contexts cimport SecurityContext + +from gssapi.raw.misc import GSSError, _EnumExtension +from gssapi.raw import types as gssapi_types +from gssapi.raw.named_tuples import IOVUnwrapResult, WrapResult, UnwrapResult +from collections import namedtuple, Sequence +from enum import IntEnum + +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) + OM_uint32 gss_wrap_iov(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle, + int conf_req_flag, gss_qop_t qop_req, int *conf_ret, + gss_iov_buffer_desc *iov, int iov_count) nogil + + OM_uint32 gss_unwrap_iov(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle, + int* conf_ret, gss_qop_t *qop_ret, + gss_iov_buffer_desc *iov, int iov_count) nogil + + OM_uint32 gss_wrap_iov_length(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle, + int conf_req, gss_qop_t qop_req, + int *conf_ret, gss_iov_buffer_desc *iov, + int iov_count) nogil + + OM_uint32 gss_release_iov_buffer(OM_uint32 *min_stat, + gss_iov_buffer_desc *iov, + int iov_count) nogil + + OM_uint32 gss_wrap_aead(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle, + int conf_req, gss_qop_t qop_req, + gss_buffer_t input_assoc_buffer, + gss_buffer_t input_payload_buffer, int *conf_ret, + gss_buffer_t output_message_buffer) nogil + + OM_uint32 gss_unwrap_aead(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle, + gss_buffer_t input_message_buffer, + gss_buffer_t input_assoc_buffer, + gss_buffer_t output_payload_buffer, + int *conf_ret, gss_qop_t *qop_ret) nogil + + gss_iov_buffer_t GSS_C_NO_IOV_BUFFER + + OM_uint32 GSS_IOV_BUFFER_TYPE_EMPTY + OM_uint32 GSS_IOV_BUFFER_TYPE_DATA + OM_uint32 GSS_IOV_BUFFER_TYPE_HEADER + OM_uint32 GSS_IOV_BUFFER_TYPE_MECH_PARAMS + OM_uint32 GSS_IOV_BUFFER_TYPE_TRAILER + OM_uint32 GSS_IOV_BUFFER_TYPE_PADDING + OM_uint32 GSS_IOV_BUFFER_TYPE_STREAM + OM_uint32 GSS_IOV_BUFFER_TYPE_SIGN_ONLY + + OM_uint32 GSS_IOV_BUFFER_FLAG_MASK + OM_uint32 GSS_IOV_BUFFER_FLAG_ALLOCATE + OM_uint32 GSS_IOV_BUFFER_FLAG_ALLOCATED + + +class IOVBufferType(IntEnum): + """ + IOV Buffer Types + + This IntEnum represent GSSAPI IOV buffer + types to be used with the IOV methods. + + The numbers behind the values correspond directly + to their C counterparts. + """ + + empty = GSS_IOV_BUFFER_TYPE_EMPTY + data = GSS_IOV_BUFFER_TYPE_DATA + header = GSS_IOV_BUFFER_TYPE_HEADER + mech_params = GSS_IOV_BUFFER_TYPE_MECH_PARAMS + trailer = GSS_IOV_BUFFER_TYPE_TRAILER + padding = GSS_IOV_BUFFER_TYPE_PADDING + stream = GSS_IOV_BUFFER_TYPE_STREAM + sign_only = GSS_IOV_BUFFER_TYPE_SIGN_ONLY + + + +# TODO(directxman12): figure out how to add GSS_C_DCE_STYLE to RequirementFlags +@six.add_metaclass(_EnumExtension) +class RequirementFlag(object): + __base__ = gssapi_types.RequirementFlag + + dce_style = GSS_C_DCE_STYLE + +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 + + def __init__(IOV self, *args, std_layout=True, auto_alloc=True): + self._unprocessed = True + self.c_changed = False + + self._buffs = [] + + if std_layout: + self._buffs.append(IOVBuffer(IOVBufferType.header, + auto_alloc, None)) + + cdef char *val_copy + for buff_desc in args: + if isinstance(buff_desc, tuple): + buff_type = buff_desc[0] + if buff_type in (IOVBufferType.header, IOVBufferType.trailer, + IOVBufferType.padding): + if len(buff_desc) == 2: + alloc = buff_desc[1] + else: + alloc = auto_alloc + + self._buffs.append(IOVBuffer(buff_type, alloc, None)) + else: + val = buff_desc[1] + self._buffs.append(IOVBuffer(buff_type, False, val)) + elif isinstance(buff_desc, bytes): # assume type data + val = buff_desc + self._buffs.append(IOVBuffer(IOVBufferType.data, False, val)) + else: + alloc = False + if buff_desc in (IOVBufferType.header, IOVBufferType.trailer, + IOVBufferType.padding): + alloc = auto_alloc + + self._buffs.append(IOVBuffer(buff_desc, alloc, None)) + + if std_layout: + self._buffs.append(IOVBuffer(IOVBufferType.padding, auto_alloc, + None)) + self._buffs.append(IOVBuffer(IOVBufferType.trailer, auto_alloc, + None)) + + cdef gss_iov_buffer_desc* __cvalue__(IOV self) except NULL: + cdef OM_uint32 tmp_min_stat + cdef int i + if self._unprocessed: + if self._iov is not NULL: + gss_release_iov_buffer(&tmp_min_stat, self._iov, self._iov_len) + free(self._iov) + + self._iov_len = len(self._buffs) + self._iov = calloc( + self._iov_len, sizeof(gss_iov_buffer_desc)) + if self._iov is NULL: + raise MemoryError("Cannot calloc for IOV buffer array") + + for i in range(self._iov_len): + buff = self._buffs[i] + self._iov[i].type = buff.type + + if buff.allocate: + self._iov[i].type |= GSS_IOV_BUFFER_FLAG_ALLOCATE + elif buff.allocate is None: + self._iov[i].type |= GSS_IOV_BUFFER_FLAG_ALLOCATED + + if buff.value is None: + self._iov[i].buffer.length = 0 + self._iov[i].buffer.value = NULL + else: + self._iov[i].buffer.length = len(buff.value) + self._iov[i].buffer.value = malloc( + self._iov[i].buffer.length) + if self._iov[i].buffer.value is NULL: + raise MemoryError("Cannot malloc for buffer value") + + memcpy(self._iov[i].buffer.value, buff.value, + self._iov[i].buffer.length) + + return self._iov + + cdef _recreate_python_values(IOV self): + cdef i + cdef bint val_change = False + cdef size_t new_len + for i in range(self._iov_len): + old_type = self._buffs[i].type + + if self._iov[i].buffer.value is NULL: + if self._iov[i].buffer.length == 0: + new_val = None + else: + new_len = self._iov[i].buffer.length + new_val = b'\x00' * new_len + else: + new_len = self._iov[i].buffer.length + new_val = self._iov[i].buffer.value[:new_len] + + alloc = False + if self._iov[i].type & GSS_IOV_BUFFER_FLAG_ALLOCATE: + alloc = True + + # NB(directxman12): apparently, GSSAPI doesn't actually + # unset the allocate flag (although it + # probably should), so this needs to + # come second and be separate + if self._iov[i].type & GSS_IOV_BUFFER_FLAG_ALLOCATED: + alloc = None + + self._buffs[i] = IOVBuffer(old_type, alloc, new_val) + + self.c_changed = False + + def __getitem__(IOV self, ind): + if self.c_changed: + self._recreate_python_values() + + return self._buffs[ind] + + def __len__(IOV self): + if self.c_changed: + self._recreate_python_values() + + return len(self._buffs) + + # Sequence methods: __contains__, __iter__, __reversed__, index, and count + def __iter__(IOV self): + if self.c_changed: + self._recreate_python_values() + + for val in self._buffs: + yield val + + def __contains__(IOV self, item): + if self.c_changed: + self._recreate_python_values() + + return item in self._buffs + + def __reversed__(IOV self): + if self.c_changed: + self._recreate_python_values() + + for val in reversed(self._buffs): + yield val + + def index(IOV self, value): + for i, v in enumerate(self): + if v == value: + return i + + raise ValueError + + def count(IOV self, value): + return sum(1 for v in self if v == value) + + def __repr__(IOV self): + if self.c_changed: + self._recreate_python_values() + + return "<{module}.{name} {buffs}>".format( + module=type(self).__module__, name=type(self).__name__, + buffs=repr(self._buffs)) + + def __str__(IOV self): + buff_strs = [] + for buff in self: + type_val = str(buff.type).split('.')[1].upper() + if buff.value is None: + auto_alloc = buff.allocate + if auto_alloc: + buff_strs.append(type_val + "(allocate)") + else: + buff_strs.append(type_val + "(empty)") + else: + if buff.allocate is None: + alloc_str = ", allocated" + else: + alloc_str = "" + buff_strs.append("{0}({1!r}{2})".format(type_val, + buff.value, alloc_str)) + + return "".format(' | '.join(buff_strs)) + + def add_data(IOV self, data, sign_only=False, ind=None): + if sign_only: + buff_type = IOVBufferType.sign_only + else: + buff_type = IOVBufferType.data + + if ind is None: + self._buffs.append(IOVBuffer(buff_type, False, data)) + else: + self._buffs.insert(ind, IOVBuffer(buff_type, False, data)) + + self._unprocessed = True + + def __dealloc__(IOV self): + cdef OM_uint32 tmp_min_stat + cdef int i + if self._iov is not NULL: + gss_release_iov_buffer(&tmp_min_stat, self._iov, self._iov_len) + + for i in range(self._iov_len): + if self._iov[i].buffer.value is not NULL: + free(self._iov[i].buffer.value) + + free(self._iov) + + +def wrap_iov(SecurityContext context not None, IOV message not None, + confidential=True, qop=None): + """ + Wrap/Encrypt an IOV message + + This method wraps or encrypts an IOV message. The allocate + parameter of the :class:`IOVBuffer` indicates whether or + not that particular buffer should be automatically allocated + (for use with padding, header, and trailer buffers). + + Args: + context (SecurityContext): the current security context + message (list): a list of :class:`IOVBuffer` objects + confidential (bool): whether or not to encrypt the message (True), + or just wrap it with a MIC (False) + qop (int): the desired Quality of Protection + (or None for the default QoP) + + Returns: + WrapResult: the wrapped/encrypted message (IOV list), and + whether or not encryption was actually used + + Raises: + GSSError + """ + + cdef int conf_req = confidential + cdef int iov_len = len(message) + cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT + cdef int conf_used + + cdef gss_iov_buffer_desc *res_arr = message.__cvalue__() + + cdef OM_uint32 maj_stat, min_stat + + with nogil: + maj_stat = gss_wrap_iov(&min_stat, context.raw_ctx, conf_req, qop_req, + &conf_used, res_arr, iov_len) + + if maj_stat == GSS_S_COMPLETE: + message.c_changed = True + return conf_used + else: + raise GSSError(maj_stat, min_stat) + + +def unwrap_iov(SecurityContext context not None, IOV message not None): + """ + Unwrap/Decrypt an IOV message + + This method unwraps or decrypts an IOV message. The allocate + parameter of the :class:`IOVBuffer` indicates whether or + not that particular buffer should be automatically allocated + (for use with padding, header, and trailer buffers). + + As a special case, you may pass an entire IOV message + as a single 'stream'. In this case, pass a buffer type + of :attr:`IOVBufferType.stream` followed by a buffer type of + :attr:`IOVBufferType.data`. The former should contain the + entire IOV message, while the latter should be empty. + + Args: + context (SecurityContext): the current security context + message (list): a list of :class:`IOVBuffer` objects + + Returns: + UnwrapResult: the unwrapped/decrypted message, whether or not + encryption was used, and the QoP used + + Raises: + GSSError + """ + + # NB(directxman12): it doesn't really matter if we pass allocate + # on the data buffer in the steam scenario, since + # Cython copies the string anyway. + + cdef int iov_len = len(message) + + cdef int conf_used + cdef gss_qop_t qop_used + cdef gss_iov_buffer_desc *res_arr = message.__cvalue__() + + cdef OM_uint32 maj_stat, min_stat + + with nogil: + maj_stat = gss_unwrap_iov(&min_stat, context.raw_ctx, &conf_used, + &qop_used, res_arr, iov_len) + + if maj_stat == GSS_S_COMPLETE: + message.c_changed = True + return IOVUnwrapResult(conf_used, qop_used) + else: + raise GSSError(maj_stat, min_stat) + + +def wrap_iov_length(SecurityContext context not None, IOV message not None, + confidential=True, qop=None): + """ + Appropriately size padding, trailer, and header IOV buffers + + This method sets the length values on the IOV buffers. You + should already have data provided for the data (and sign-only) + buffer(s) so that padding lengths can be appropriately computed. + + In Python terms, this will result in an appropriately sized + `bytes` object consisting of all zeros. + + Args: + context (SecurityContext): the current security context + message (list): a list of :class:`IOVBuffer` objects + + Returns: + WrapResult: a list of :class:IOVBuffer` objects, and whether or not + encryption was actually used + + Raises: + GSSError + """ + + cdef int conf_req = confidential + cdef int iov_len = len(message) + cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT + cdef int conf_used + + cdef gss_iov_buffer_desc *res_arr = message.__cvalue__() + + cdef OM_uint32 maj_stat, min_stat + + with nogil: + maj_stat = gss_wrap_iov_length(&min_stat, context.raw_ctx, + conf_req, qop_req, + &conf_used, res_arr, iov_len) + + if maj_stat == GSS_S_COMPLETE: + message.c_changed = True + return conf_used + else: + raise GSSError(maj_stat, min_stat) + + +def wrap_aead(SecurityContext context not None, bytes message not None, + bytes associated=None, confidential=True, qop=None): + """ + Wrap/Encrypt an AEAD Message + + This method takes an input message and associated data, + and outputs and AEAD message. + + Args: + context (SecurityContext): the current security context + message (bytes): the message to wrap or encrypt + associated (bytes): associated data to go with the message + confidential (bool): whether or not to encrypt the message (True), + or just wrap it with a MIC (False) + qop (int): the desired Quality of Protection + (or None for the default QoP) + + Returns: + WrapResult: the wrapped/encrypted total message, and whether or not + encryption was actually used + + Raises: + GSSError + """ + + cdef int conf_req = confidential + cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT + cdef gss_buffer_desc message_buffer = gss_buffer_desc(len(message), + message) + + cdef gss_buffer_t assoc_buffer_ptr = GSS_C_NO_BUFFER + cdef gss_buffer_desc assoc_buffer + if associated is not None: + assoc_buffer = gss_buffer_desc(len(associated), associated) + assoc_buffer_ptr = &assoc_buffer + + cdef int conf_used + # GSS_C_EMPTY_BUFFER + cdef gss_buffer_desc output_buffer = gss_buffer_desc(0, NULL) + + cdef OM_uint32 maj_stat, min_stat + + with nogil: + maj_stat = gss_wrap_aead(&min_stat, context.raw_ctx, conf_req, qop_req, + assoc_buffer_ptr, &message_buffer, + &conf_used, &output_buffer) + + if maj_stat == GSS_S_COMPLETE: + output_message = output_buffer.value[:output_buffer.length] + gss_release_buffer(&min_stat, &output_buffer) + return WrapResult(output_message, conf_used) + else: + raise GSSError(maj_stat, min_stat) + + +def unwrap_aead(SecurityContext context not None, bytes message not None, + bytes associated=None): + """ + Unwrap/Decrypt an AEAD Message + + This method takes an encrpyted/wrapped AEAD message and some associated + data, and returns an unwrapped/decrypted message. + + Args: + context (SecurityContext): the current security context + message (bytes): the AEAD message to unwrap or decrypt + associated (bytes): associated data that goes with the message + + Returns: + UnwrapResult: the unwrapped/decrypted message, whether or on + encryption was used, and the QoP used + + Raises: + GSSError + """ + + cdef gss_buffer_desc input_buffer = gss_buffer_desc(len(message), message) + + cdef gss_buffer_t assoc_buffer_ptr = GSS_C_NO_BUFFER + cdef gss_buffer_desc assoc_buffer + if associated is not None: + assoc_buffer = gss_buffer_desc(len(associated), associated) + assoc_buffer_ptr = &assoc_buffer + + # GSS_C_EMPTY_BUFFER + cdef gss_buffer_desc output_buffer = gss_buffer_desc(0, NULL) + cdef int conf_state + cdef gss_qop_t qop_state + + cdef OM_uint32 maj_stat, min_stat + + with nogil: + maj_stat = gss_unwrap_aead(&min_stat, context.raw_ctx, &input_buffer, + assoc_buffer_ptr, &output_buffer, + &conf_state, &qop_state) + + if maj_stat == GSS_S_COMPLETE: + output_message = output_buffer.value[:output_buffer.length] + gss_release_buffer(&min_stat, &output_buffer) + return UnwrapResult(output_message, conf_state, qop_state) + else: + raise GSSError(maj_stat, min_stat) diff --git a/gssapi/raw/named_tuples.py b/gssapi/raw/named_tuples.py index 6b918cee..4f52b103 100644 --- a/gssapi/raw/named_tuples.py +++ b/gssapi/raw/named_tuples.py @@ -53,5 +53,10 @@ 'lifetime', 'mech', 'flags', 'locally_init', 'complete']) + StoreCredResult = namedtuple('StoreCredResult', ['mechs', 'usage']) + + +IOVUnwrapResult = namedtuple('IOVUnwrapResult', + ['encrypted', 'qop']) diff --git a/gssapi/tests/test_raw.py b/gssapi/tests/test_raw.py index 9fd69fc9..0c6899d3 100644 --- a/gssapi/tests/test_raw.py +++ b/gssapi/tests/test_raw.py @@ -944,6 +944,148 @@ def test_basic_wrap_unwrap(self): unwrapped_message.shouldnt_be_empty() unwrapped_message.should_be(b'test message') + @_extension_test('dce', 'DCE (IOV/AEAD)') + def test_basic_iov_wrap_unwrap_prealloc(self): + init_data = b'some encrypted data' + init_other_data = b'some other encrypted data' + init_signed_info = b'some sig data' + init_message = gb.IOV((gb.IOVBufferType.sign_only, init_signed_info), + init_data, init_other_data, auto_alloc=False) + + init_message[0].allocate.should_be_false() + init_message[4].allocate.should_be_false() + init_message[5].allocate.should_be_false() + + conf = gb.wrap_iov_length(self.client_ctx, init_message) + + conf.should_be_a(bool) + conf.should_be_true() + + init_message[0].should_be_at_least_size(1) + init_message[5].should_be_at_least_size(1) + + conf = gb.wrap_iov(self.client_ctx, init_message) + + conf.should_be_a(bool) + conf.should_be_true() + + # make sure we didn't strings used + init_data.should_be(b'some encrypted data') + init_other_data.should_be(b'some other encrypted data') + init_signed_info.should_be(b'some sig data') + + init_message[2].value.shouldnt_be(b'some encrypted data') + init_message[3].value.shouldnt_be(b'some other encrypted data') + + (conf, qop) = gb.unwrap_iov(self.server_ctx, init_message) + + conf.should_be_a(bool) + conf.should_be_true() + + qop.should_be_a(int) + + init_message[1].value.should_be(init_signed_info) + init_message[2].value.should_be(init_data) + init_message[3].value.should_be(init_other_data) + + @_extension_test('dce', 'DCE (IOV/AEAD)') + def test_basic_iov_wrap_unwrap_autoalloc(self): + init_data = b'some encrypted data' + init_other_data = b'some other encrypted data' + init_signed_info = b'some sig data' + init_message = gb.IOV((gb.IOVBufferType.sign_only, init_signed_info), + init_data, init_other_data) + + conf = gb.wrap_iov(self.client_ctx, init_message) + + conf.should_be_a(bool) + conf.should_be_true() + + # make sure we didn't strings used + init_data.should_be(b'some encrypted data') + init_other_data.should_be(b'some other encrypted data') + init_signed_info.should_be(b'some sig data') + + init_message[2].value.shouldnt_be(b'some encrypted data') + init_message[3].value.shouldnt_be(b'some other encrypted data') + + (conf, qop) = gb.unwrap_iov(self.server_ctx, init_message) + + conf.should_be_a(bool) + conf.should_be_true() + + qop.should_be_a(int) + + init_message[1].value.should_be(init_signed_info) + init_message[2].value.should_be(init_data) + init_message[3].value.should_be(init_other_data) + + @_extension_test('dce', 'DCE (IOV/AEAD)') + def test_basic_aead_wrap_unwrap(self): + assoc_data = b'some sig data' + (wrapped_message, conf) = gb.wrap_aead(self.client_ctx, + b'test message', assoc_data) + + conf.should_be_a(bool) + conf.should_be_true() + + wrapped_message.should_be_a(bytes) + wrapped_message.shouldnt_be_empty() + wrapped_message.should_be_longer_than('test message') + + (unwrapped_message, conf, qop) = gb.unwrap_aead(self.server_ctx, + wrapped_message, + assoc_data) + conf.should_be_a(bool) + conf.should_be_true() + + qop.should_be_an_integer() + qop.should_be_at_least(0) + + unwrapped_message.should_be_a(bytes) + unwrapped_message.shouldnt_be_empty() + unwrapped_message.should_be(b'test message') + + @_extension_test('dce', 'DCE (IOV/AEAD)') + def test_basic_aead_wrap_unwrap_no_assoc(self): + (wrapped_message, conf) = gb.wrap_aead(self.client_ctx, + b'test message') + + conf.should_be_a(bool) + conf.should_be_true() + + wrapped_message.should_be_a(bytes) + wrapped_message.shouldnt_be_empty() + wrapped_message.should_be_longer_than('test message') + + (unwrapped_message, conf, qop) = gb.unwrap_aead(self.server_ctx, + wrapped_message) + conf.should_be_a(bool) + conf.should_be_true() + + qop.should_be_an_integer() + qop.should_be_at_least(0) + + unwrapped_message.should_be_a(bytes) + unwrapped_message.shouldnt_be_empty() + unwrapped_message.should_be(b'test message') + + @_extension_test('dce', 'DCE (IOV/AEAD)') + def test_basic_aead_wrap_unwrap_bad_assoc_raises_error(self): + assoc_data = b'some sig data' + (wrapped_message, conf) = gb.wrap_aead(self.client_ctx, + b'test message', assoc_data) + + conf.should_be_a(bool) + conf.should_be_true() + + wrapped_message.should_be_a(bytes) + wrapped_message.shouldnt_be_empty() + wrapped_message.should_be_longer_than('test message') + + gb.unwrap_aead.should_raise(gb.BadMICError, self.server_ctx, + wrapped_message, b'some other sig data') + TEST_OIDS = {'SPNEGO': {'bytes': b'\053\006\001\005\005\002', 'string': '1.3.6.1.5.5.2'}, diff --git a/setup.py b/setup.py index 1cedc79c..24845015 100755 --- a/setup.py +++ b/setup.py @@ -62,7 +62,7 @@ def _get_output(*args, **kwargs): compile_args = get_output('krb5-config --cflags gssapi') link_args = link_args.split() -compile_args = compile_args.split() +compile_args = compile_args.split() + ['-g'] ENABLE_SUPPORT_DETECTION = \ (os.environ.get('GSSAPI_SUPPORT_DETECT', 'true').lower() == 'true') @@ -191,6 +191,7 @@ def gssapi_modules(lst): extension_file('cred_store', 'gss_store_cred_into'), extension_file('rfc5588', 'gss_store_cred'), extension_file('cred_imp_exp', 'gss_import_cred'), + extension_file('dce', 'gss_wrap_iov'), # see ext_password{,_add}.pyx for more information on this split extension_file('password', 'gss_acquire_cred_with_password'),