Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d2726a6

Browse files
committed
Support DCE IOV functions on macOS
The DCE IOV functions on macOS are not exported by any public header on the GSS.Framework. They can still be accessed by dynamically getting the address of the known symbols for each function and invoking that. This allows users of this library on macOS to better interop with Windows SSPI message encryption which typically require the IOV wrapping functions that were previously unavailable.
1 parent dac08a8 commit d2726a6

File tree

5 files changed

+259
-145
lines changed

5 files changed

+259
-145
lines changed

gssapi/raw/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,14 +111,20 @@
111111
except ImportError:
112112
pass
113113

114-
# optional DCE (IOV/AEAD) support
114+
# optional DCE (IOV) support
115115
try:
116116
from gssapi.raw.ext_dce import * # noqa
117117
# optional IOV MIC support (requires DCE support)
118118
from gssapi.raw.ext_iov_mic import * # noqa
119119
except ImportError:
120120
pass
121121

122+
# optional DCE (AEAD) support
123+
try:
124+
from gssapi.raw.ext_dce_aead import * # noqa
125+
except ImportError:
126+
pass
127+
122128
# optional RFC 6680 support
123129
try:
124130
from gssapi.raw.ext_rfc6680 import * # noqa

gssapi/raw/ext_dce.pyx

Lines changed: 113 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -16,39 +16,120 @@ from enum import IntEnum
1616
from gssapi.raw._enum_extensions import ExtendableEnum
1717

1818

19-
cdef extern from "python_gssapi_ext.h":
20-
# NB(directxman12): this wiki page has a different argument order
21-
# than the header file, and uses size_t instead of int
22-
# (this file matches the header file)
23-
OM_uint32 gss_wrap_iov(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle,
24-
int conf_req_flag, gss_qop_t qop_req, int *conf_ret,
25-
gss_iov_buffer_desc *iov, int iov_count) nogil
26-
27-
OM_uint32 gss_unwrap_iov(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle,
28-
int* conf_ret, gss_qop_t *qop_ret,
29-
gss_iov_buffer_desc *iov, int iov_count) nogil
30-
31-
OM_uint32 gss_wrap_iov_length(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle,
32-
int conf_req, gss_qop_t qop_req,
33-
int *conf_ret, gss_iov_buffer_desc *iov,
34-
int iov_count) nogil
35-
36-
OM_uint32 gss_release_iov_buffer(OM_uint32 *min_stat,
37-
gss_iov_buffer_desc *iov,
38-
int iov_count) nogil
39-
40-
OM_uint32 gss_wrap_aead(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle,
41-
int conf_req, gss_qop_t qop_req,
42-
gss_buffer_t input_assoc_buffer,
43-
gss_buffer_t input_payload_buffer, int *conf_ret,
44-
gss_buffer_t output_message_buffer) nogil
45-
46-
OM_uint32 gss_unwrap_aead(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle,
47-
gss_buffer_t input_message_buffer,
48-
gss_buffer_t input_assoc_buffer,
49-
gss_buffer_t output_payload_buffer,
50-
int *conf_ret, gss_qop_t *qop_ret) nogil
19+
IF MACOS_FRAMEWORK_PATH:
20+
# GSS.Framework (macOS) does not expose the 4 gss IOV functions in its
21+
# header. Instead dynamically lookup the function address in the GSS
22+
# library file based on the known symbol name.
23+
24+
from posix.dlfcn cimport dlopen, dlerror, dlsym, dlclose, RTLD_NOW, \
25+
RTLD_GLOBAL
26+
27+
cdef:
28+
void *gss = NULL
29+
void *wrap_iov_fn = NULL
30+
void *unwrap_iov_fn = NULL
31+
void *wrap_iov_length_fn = NULL
32+
void *release_iov_buffer_fn = NULL
33+
34+
cdef void * _dlsym(void *handle, name):
35+
# dlsym() returns NULL for a symbol that is not found but it could
36+
# also return NULL as a valid symbol address. The correct way to test
37+
# on an error is to:
38+
# 1. call dlerror() to clear any existing errors,
39+
# 2. call dlsym, then
40+
# 3. call dlerror() to check whether that is NULL or not
41+
# https://man7.org/linux/man-pages/man3/dlsym.3.html
42+
dlerror()
43+
44+
hn = dlsym(handle, name)
45+
err_msg = dlerror()
46+
if err_msg != NULL:
47+
raise ImportError(err_msg.decode('utf-8', errors='replace'))
48+
49+
return hn
50+
51+
gss = dlopen(MACOS_FRAMEWORK_PATH.encode(), RTLD_NOW | RTLD_GLOBAL)
52+
if gss == NULL:
53+
raise ImportError(dlerror().decode('utf-8', errors='replace'))
54+
55+
try:
56+
# The symbol names were found when inspecting the GSS library file
57+
# 'nm -gU /path/GSS'. Can confirm these symbols are present in the
58+
# latest (10.15) to at least 10.11. Online docs seem to confirm these
59+
# symbols have been present since iOS 6.0 which is roughly when this
60+
# framework was introduced.
61+
wrap_iov_fn = _dlsym(gss, b'__ApplePrivate_gss_wrap_iov')
62+
unwrap_iov_fn = _dlsym(gss, b'__ApplePrivate_gss_unwrap_iov')
63+
wrap_iov_length_fn = _dlsym(gss, b'__ApplePrivate_gss_wrap_iov_length')
64+
release_iov_buffer_fn = _dlsym(
65+
gss, b'__ApplePrivate_gss_release_iov_buffer')
66+
67+
finally:
68+
dlclose(gss)
69+
70+
cdef OM_uint32 gss_wrap_iov(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle,
71+
int conf_req_flag, gss_qop_t qop_req,
72+
int *conf_ret, gss_iov_buffer_desc *iov,
73+
int iov_count) nogil:
74+
75+
return (<OM_uint32(*)(OM_uint32*, gss_ctx_id_t, int, gss_qop_t, int*,
76+
gss_iov_buffer_desc*, int) nogil>wrap_iov_fn)(
77+
min_stat, ctx_handle, conf_req_flag, qop_req, conf_ret, iov,
78+
iov_count)
79+
80+
cdef OM_uint32 gss_unwrap_iov(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle,
81+
int* conf_ret, gss_qop_t *qop_ret,
82+
gss_iov_buffer_desc *iov,
83+
int iov_count) nogil:
84+
85+
return (<OM_uint32(*)(OM_uint32*, gss_ctx_id_t, int*, gss_qop_t*,
86+
gss_iov_buffer_desc*, int) nogil>unwrap_iov_fn)(
87+
min_stat, ctx_handle, conf_ret, qop_ret, iov, iov_count)
88+
89+
cdef OM_uint32 gss_wrap_iov_length(OM_uint32 *min_stat,
90+
gss_ctx_id_t ctx_handle, int conf_req,
91+
gss_qop_t qop_req, int *conf_ret,
92+
gss_iov_buffer_desc *iov,
93+
int iov_count) nogil:
94+
95+
return (<OM_uint32(*)(OM_uint32*, gss_ctx_id_t, int, gss_qop_t, int*,
96+
gss_iov_buffer_desc*, int) nogil>wrap_iov_length_fn)(
97+
min_stat, ctx_handle, conf_req, qop_req, conf_ret, iov, iov_count)
98+
99+
cdef OM_uint32 gss_release_iov_buffer(OM_uint32 *min_stat,
100+
gss_iov_buffer_desc *iov,
101+
int iov_count) nogil:
102+
103+
return (<OM_uint32(*)(OM_uint32*, gss_iov_buffer_desc*,
104+
int) nogil>release_iov_buffer_fn)(
105+
min_stat, iov, iov_count)
106+
107+
ELSE:
108+
cdef extern from "python_gssapi_ext.h":
109+
# NB(directxman12): this wiki page has a different argument order
110+
# than the header file, and uses size_t instead of
111+
# int (this file matches the header file)
112+
OM_uint32 gss_wrap_iov(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle,
113+
int conf_req_flag, gss_qop_t qop_req,
114+
int *conf_ret, gss_iov_buffer_desc *iov,
115+
int iov_count) nogil
116+
117+
OM_uint32 gss_unwrap_iov(OM_uint32 *min_stat, gss_ctx_id_t ctx_handle,
118+
int* conf_ret, gss_qop_t *qop_ret,
119+
gss_iov_buffer_desc *iov, int iov_count) nogil
120+
121+
OM_uint32 gss_wrap_iov_length(OM_uint32 *min_stat,
122+
gss_ctx_id_t ctx_handle, int conf_req,
123+
gss_qop_t qop_req, int *conf_ret,
124+
gss_iov_buffer_desc *iov,
125+
int iov_count) nogil
126+
127+
OM_uint32 gss_release_iov_buffer(OM_uint32 *min_stat,
128+
gss_iov_buffer_desc *iov,
129+
int iov_count) nogil
130+
51131

132+
cdef extern from "python_gssapi_ext.h":
52133
gss_iov_buffer_t GSS_C_NO_IOV_BUFFER
53134

54135
OM_uint32 GSS_IOV_BUFFER_TYPE_EMPTY
@@ -447,109 +528,3 @@ def wrap_iov_length(SecurityContext context not None, IOV message not None,
447528
return <bint>conf_used
448529
else:
449530
raise GSSError(maj_stat, min_stat)
450-
451-
452-
def wrap_aead(SecurityContext context not None, bytes message not None,
453-
bytes associated=None, confidential=True, qop=None):
454-
"""
455-
wrap_aead(context, message, associated=None, confidential=True, qop=None)
456-
Wrap/Encrypt an AEAD message.
457-
458-
This method takes an input message and associated data,
459-
and outputs and AEAD message.
460-
461-
Args:
462-
context (SecurityContext): the current security context
463-
message (bytes): the message to wrap or encrypt
464-
associated (bytes): associated data to go with the message
465-
confidential (bool): whether or not to encrypt the message (True),
466-
or just wrap it with a MIC (False)
467-
qop (int): the desired Quality of Protection
468-
(or None for the default QoP)
469-
470-
Returns:
471-
WrapResult: the wrapped/encrypted total message, and whether or not
472-
encryption was actually used
473-
474-
Raises:
475-
GSSError
476-
"""
477-
478-
cdef int conf_req = confidential
479-
cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT
480-
cdef gss_buffer_desc message_buffer = gss_buffer_desc(len(message),
481-
message)
482-
483-
cdef gss_buffer_t assoc_buffer_ptr = GSS_C_NO_BUFFER
484-
cdef gss_buffer_desc assoc_buffer
485-
if associated is not None:
486-
assoc_buffer = gss_buffer_desc(len(associated), associated)
487-
assoc_buffer_ptr = &assoc_buffer
488-
489-
cdef int conf_used
490-
# GSS_C_EMPTY_BUFFER
491-
cdef gss_buffer_desc output_buffer = gss_buffer_desc(0, NULL)
492-
493-
cdef OM_uint32 maj_stat, min_stat
494-
495-
with nogil:
496-
maj_stat = gss_wrap_aead(&min_stat, context.raw_ctx, conf_req, qop_req,
497-
assoc_buffer_ptr, &message_buffer,
498-
&conf_used, &output_buffer)
499-
500-
if maj_stat == GSS_S_COMPLETE:
501-
output_message = (<char*>output_buffer.value)[:output_buffer.length]
502-
gss_release_buffer(&min_stat, &output_buffer)
503-
return WrapResult(output_message, <bint>conf_used)
504-
else:
505-
raise GSSError(maj_stat, min_stat)
506-
507-
508-
def unwrap_aead(SecurityContext context not None, bytes message not None,
509-
bytes associated=None):
510-
"""
511-
unwrap_aead(context, message, associated=None)
512-
Unwrap/Decrypt an AEAD message.
513-
514-
This method takes an encrpyted/wrapped AEAD message and some associated
515-
data, and returns an unwrapped/decrypted message.
516-
517-
Args:
518-
context (SecurityContext): the current security context
519-
message (bytes): the AEAD message to unwrap or decrypt
520-
associated (bytes): associated data that goes with the message
521-
522-
Returns:
523-
UnwrapResult: the unwrapped/decrypted message, whether or on
524-
encryption was used, and the QoP used
525-
526-
Raises:
527-
GSSError
528-
"""
529-
530-
cdef gss_buffer_desc input_buffer = gss_buffer_desc(len(message), message)
531-
532-
cdef gss_buffer_t assoc_buffer_ptr = GSS_C_NO_BUFFER
533-
cdef gss_buffer_desc assoc_buffer
534-
if associated is not None:
535-
assoc_buffer = gss_buffer_desc(len(associated), associated)
536-
assoc_buffer_ptr = &assoc_buffer
537-
538-
# GSS_C_EMPTY_BUFFER
539-
cdef gss_buffer_desc output_buffer = gss_buffer_desc(0, NULL)
540-
cdef int conf_state
541-
cdef gss_qop_t qop_state
542-
543-
cdef OM_uint32 maj_stat, min_stat
544-
545-
with nogil:
546-
maj_stat = gss_unwrap_aead(&min_stat, context.raw_ctx, &input_buffer,
547-
assoc_buffer_ptr, &output_buffer,
548-
&conf_state, &qop_state)
549-
550-
if maj_stat == GSS_S_COMPLETE:
551-
output_message = (<char*>output_buffer.value)[:output_buffer.length]
552-
gss_release_buffer(&min_stat, &output_buffer)
553-
return UnwrapResult(output_message, <bint>conf_state, qop_state)
554-
else:
555-
raise GSSError(maj_stat, min_stat)

0 commit comments

Comments
 (0)