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

Skip to content

Commit 9ddd1d2

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 99ee548 commit 9ddd1d2

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
@@ -21,39 +21,120 @@ else:
2121
from collections.abc import Sequence
2222

2323

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

137+
cdef extern from "python_gssapi_ext.h":
57138
gss_iov_buffer_t GSS_C_NO_IOV_BUFFER
58139

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

0 commit comments

Comments
 (0)