Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support IOV functions on macOS #210

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion gssapi/raw/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,20 @@
except ImportError:
pass

# optional DCE (IOV/AEAD) support
# optional DCE (IOV) support
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should make it more clear (here and below) that DCE != IOV. That is, this is the IOV part of the DCE functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would you suggest, I can do # optional DCE (IOV only) support and the same for the AEAD imports below?

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

# optional DCE (AEAD) support
try:
from gssapi.raw.ext_dce_aead import * # noqa
except ImportError:
pass

# optional RFC 6680 support
try:
from gssapi.raw.ext_rfc6680 import * # noqa
Expand Down
251 changes: 113 additions & 138 deletions gssapi/raw/ext_dce.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,120 @@ from enum import IntEnum
from gssapi.raw._enum_extensions import ExtendableEnum


cdef extern from "python_gssapi_ext.h":
# 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
IF MACOS_FRAMEWORK_PATH:
# GSS.Framework (macOS) does not expose the 4 gss IOV functions in its
# header. Instead dynamically lookup the function address in the GSS
# library file based on the known symbol name.

from posix.dlfcn cimport dlopen, dlerror, dlsym, dlclose, RTLD_NOW, \
RTLD_GLOBAL

cdef:
void *gss = NULL
void *wrap_iov_fn = NULL
void *unwrap_iov_fn = NULL
void *wrap_iov_length_fn = NULL
void *release_iov_buffer_fn = NULL

cdef void * _dlsym(void *handle, name):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How likely are we to need these for other GSSAPI functions on macOS? Wondering if it'd make sense to put this glue in a common file somewhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly I'm not sure, I had a brief check for some other extensions that are not available on macOS, like the aead stuff, but it doesn't seem to be present in the symbols. There's probably other stuff so I can move it out if you would like it.

# dlsym() returns NULL for a symbol that is not found but it could
# also return NULL as a valid symbol address. The correct way to test
# on an error is to:
# 1. call dlerror() to clear any existing errors,
# 2. call dlsym, then
# 3. call dlerror() to check whether that is NULL or not
# https://man7.org/linux/man-pages/man3/dlsym.3.html
dlerror()

hn = dlsym(handle, name)
err_msg = dlerror()
if err_msg != NULL:
raise ImportError(err_msg.decode('utf-8', errors='replace'))

return hn

gss = dlopen(MACOS_FRAMEWORK_PATH.encode(), RTLD_NOW | RTLD_GLOBAL)
if gss == NULL:
raise ImportError(dlerror().decode('utf-8', errors='replace'))

try:
# The symbol names were found when inspecting the GSS library file
# 'nm -gU /path/GSS'. Can confirm these symbols are present in the
# latest (10.15) to at least 10.11. Online docs seem to confirm these
# symbols have been present since iOS 6.0 which is roughly when this
# framework was introduced.
wrap_iov_fn = _dlsym(gss, b'__ApplePrivate_gss_wrap_iov')
unwrap_iov_fn = _dlsym(gss, b'__ApplePrivate_gss_unwrap_iov')
wrap_iov_length_fn = _dlsym(gss, b'__ApplePrivate_gss_wrap_iov_length')
release_iov_buffer_fn = _dlsym(
gss, b'__ApplePrivate_gss_release_iov_buffer')

finally:
dlclose(gss)

cdef 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:

return (<OM_uint32(*)(OM_uint32*, gss_ctx_id_t, int, gss_qop_t, int*,
gss_iov_buffer_desc*, int) nogil>wrap_iov_fn)(
min_stat, ctx_handle, conf_req_flag, qop_req, conf_ret, iov,
iov_count)

cdef 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:

return (<OM_uint32(*)(OM_uint32*, gss_ctx_id_t, int*, gss_qop_t*,
gss_iov_buffer_desc*, int) nogil>unwrap_iov_fn)(
min_stat, ctx_handle, conf_ret, qop_ret, iov, iov_count)

cdef 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:

return (<OM_uint32(*)(OM_uint32*, gss_ctx_id_t, int, gss_qop_t, int*,
gss_iov_buffer_desc*, int) nogil>wrap_iov_length_fn)(
min_stat, ctx_handle, conf_req, qop_req, conf_ret, iov, iov_count)

cdef OM_uint32 gss_release_iov_buffer(OM_uint32 *min_stat,
gss_iov_buffer_desc *iov,
int iov_count) nogil:

return (<OM_uint32(*)(OM_uint32*, gss_iov_buffer_desc*,
int) nogil>release_iov_buffer_fn)(
min_stat, iov, iov_count)

ELSE:
cdef extern from "python_gssapi_ext.h":
# 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


cdef extern from "python_gssapi_ext.h":
gss_iov_buffer_t GSS_C_NO_IOV_BUFFER

OM_uint32 GSS_IOV_BUFFER_TYPE_EMPTY
Expand Down Expand Up @@ -447,109 +528,3 @@ def wrap_iov_length(SecurityContext context not None, IOV message not None,
return <bint>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_aead(context, message, 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 = (<char*>output_buffer.value)[:output_buffer.length]
gss_release_buffer(&min_stat, &output_buffer)
return WrapResult(output_message, <bint>conf_used)
else:
raise GSSError(maj_stat, min_stat)


def unwrap_aead(SecurityContext context not None, bytes message not None,
bytes associated=None):
"""
unwrap_aead(context, message, 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 = (<char*>output_buffer.value)[:output_buffer.length]
gss_release_buffer(&min_stat, &output_buffer)
return UnwrapResult(output_message, <bint>conf_state, qop_state)
else:
raise GSSError(maj_stat, min_stat)
Loading