1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
|
GSSAPI="BASE" # This ensures that a full module is generated by Cython
import typing
from libc.stdint cimport int32_t, int64_t, uint64_t, uintptr_t, UINT32_MAX
from libc.stdlib cimport calloc, free
from libc.time cimport time_t
from gssapi.raw.creds cimport Creds
from gssapi.raw.cython_converters cimport c_make_oid
from gssapi.raw.cython_types cimport *
from gssapi.raw.sec_contexts cimport SecurityContext
from gssapi.raw import types as gsstypes
from gssapi.raw.named_tuples import CfxKeyData, Rfc1964KeyData
from gssapi.raw.misc import GSSError
cdef extern from "python_gssapi_krb5.h":
# Heimdal on macOS hides these 3 functions behind a private symbol
"""
#ifdef OSX_HAS_GSS_FRAMEWORK
#define gsskrb5_extract_authtime_from_sec_context \
__ApplePrivate_gsskrb5_extract_authtime_from_sec_context
#define gss_krb5_import_cred __ApplePrivate_gss_krb5_import_cred
#define gss_krb5_get_tkt_flags __ApplePrivate_gss_krb5_get_tkt_flags
#endif
"""
cdef struct gss_krb5_lucid_key:
OM_uint32 type
OM_uint32 length
void *data
ctypedef gss_krb5_lucid_key gss_krb5_lucid_key_t
cdef struct gss_krb5_rfc1964_keydata:
OM_uint32 sign_alg
OM_uint32 seal_alg
gss_krb5_lucid_key_t ctx_key
ctypedef gss_krb5_rfc1964_keydata gss_krb5_rfc1964_keydata_t
cdef struct gss_krb5_cfx_keydata:
OM_uint32 have_acceptor_subkey
gss_krb5_lucid_key_t ctx_key
gss_krb5_lucid_key_t acceptor_subkey
ctypedef gss_krb5_cfx_keydata gss_krb5_cfx_keydata_t
cdef struct gss_krb5_lucid_context_v1:
OM_uint32 version
OM_uint32 initiate
OM_uint32 endtime
uint64_t send_seq
uint64_t recv_seq
OM_uint32 protocol
gss_krb5_rfc1964_keydata_t rfc1964_kd
gss_krb5_cfx_keydata_t cfx_kd
ctypedef gss_krb5_lucid_context_v1 gss_krb5_lucid_context_v1_t
gss_OID GSS_KRB5_NT_PRINCIPAL_NAME
int32_t _PY_GSSAPI_KRB5_TIMESTAMP
# The krb5 specific types are defined generically as the type names differ
# across GSSAPI implementations.
OM_uint32 gss_krb5_ccache_name(OM_uint32 *minor_status, const char *name,
const char **out_name) nogil
OM_uint32 gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
OM_uint32 version,
void **kctx) nogil
# The actual authtime size differs across implementations. See individual
# methods for more information.
OM_uint32 gsskrb5_extract_authtime_from_sec_context(
OM_uint32 *minor_status, gss_ctx_id_t context_handle,
void *authtime) nogil
OM_uint32 gsskrb5_extract_authz_data_from_sec_context(
OM_uint32 *minor_status, const gss_ctx_id_t context_handle,
int ad_type, gss_buffer_t ad_data) nogil
OM_uint32 gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status,
void *kctx) nogil
OM_uint32 gss_krb5_import_cred(OM_uint32 *minor_status,
void *id, # krb5_ccache
void *keytab_principal, # krb5_principal
void *keytab, # krb5_keytab
gss_cred_id_t *cred) nogil
# MIT uses a int32_t whereas Heimdal uses uint32_t. Use void * to satisfy
# the compiler.
OM_uint32 gss_krb5_get_tkt_flags(OM_uint32 *minor_status,
gss_ctx_id_t context_handle,
void *ticket_flags) nogil
OM_uint32 gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status,
gss_cred_id_t cred,
OM_uint32 num_ktypes,
int32_t *ktypes) nogil
cdef class Krb5LucidContext:
# defined in pxd
# cdef void *raw_ctx
def __cinit__(Krb5LucidContext self):
self.raw_ctx = NULL
def __dealloc__(Krb5LucidContext self):
cdef OM_uint32 min_stat = 0
if self.raw_ctx:
gss_krb5_free_lucid_sec_context(&min_stat, self.raw_ctx)
self.raw_ctx = NULL
cdef class Krb5LucidContextV1(Krb5LucidContext):
@property
def version(self) -> typing.Optional[int]:
cdef gss_krb5_lucid_context_v1_t *ctx = NULL
if self.raw_ctx:
ctx = <gss_krb5_lucid_context_v1_t *>self.raw_ctx
return ctx.version
@property
def is_initiator(self) -> typing.Optional[bool]:
cdef gss_krb5_lucid_context_v1_t *ctx = NULL
if self.raw_ctx:
ctx = <gss_krb5_lucid_context_v1_t *>self.raw_ctx
return ctx.initiate != 0
@property
def endtime(self) -> typing.Optional[int]:
cdef gss_krb5_lucid_context_v1_t *ctx = NULL
if self.raw_ctx:
ctx = <gss_krb5_lucid_context_v1_t *>self.raw_ctx
return ctx.endtime
@property
def send_seq(self) -> typing.Optional[int]:
cdef gss_krb5_lucid_context_v1_t *ctx = NULL
if self.raw_ctx:
ctx = <gss_krb5_lucid_context_v1_t *>self.raw_ctx
return ctx.send_seq
@property
def recv_seq(self) -> typing.Optional[int]:
cdef gss_krb5_lucid_context_v1_t *ctx = NULL
if self.raw_ctx:
ctx = <gss_krb5_lucid_context_v1_t *>self.raw_ctx
return ctx.recv_seq
@property
def protocol(self) -> typing.Optional[int]:
cdef gss_krb5_lucid_context_v1_t *ctx = NULL
if self.raw_ctx:
ctx = <gss_krb5_lucid_context_v1_t *>self.raw_ctx
return ctx.protocol
@property
def rfc1964_kd(self) -> typing.Optional[Rfc1964KeyData]:
cdef gss_krb5_lucid_context_v1_t *ctx = NULL
if self.raw_ctx != NULL and self.protocol == 0:
ctx = <gss_krb5_lucid_context_v1_t *>self.raw_ctx
kd = ctx.rfc1964_kd
key = <bytes>(<char *>kd.ctx_key.data)[:kd.ctx_key.length]
return Rfc1964KeyData(kd.sign_alg, kd.seal_alg, kd.ctx_key.type,
key)
@property
def cfx_kd(self) -> typing.Optional[CfxKeyData]:
cdef gss_krb5_lucid_context_v1_t *ctx = NULL
if self.raw_ctx != NULL and self.protocol == 1:
ctx = <gss_krb5_lucid_context_v1_t *>self.raw_ctx
kd = ctx.cfx_kd
ctx_type = ctx_key = acceptor_type = acceptor_key = None
ctx_type = kd.ctx_key.type
ctx_key = <bytes>(<char *>kd.ctx_key.data)[:kd.ctx_key.length]
if kd.have_acceptor_subkey != 0:
acceptor_type = kd.acceptor_subkey.type
key = kd.acceptor_subkey
acceptor_key = <bytes>(<char *>key.data)[:key.length]
return CfxKeyData(ctx_type, ctx_key, acceptor_type,
acceptor_key)
# Unfortunately MIT defines it as const - use the cast to silence warnings
gsstypes.NameType.krb5_nt_principal_name = c_make_oid(
<gss_OID>GSS_KRB5_NT_PRINCIPAL_NAME)
def krb5_ccache_name(const unsigned char[:] name):
cdef const char *name_ptr = NULL
if name is not None and len(name):
name_ptr = <const char*>&name[0]
cdef const char *old_name_ptr = NULL
cdef OM_uint32 maj_stat, min_stat
with nogil:
maj_stat = gss_krb5_ccache_name(&min_stat, name_ptr, &old_name_ptr)
if maj_stat == GSS_S_COMPLETE:
out_name = None
if old_name_ptr:
out_name = <bytes>old_name_ptr
return out_name
else:
raise GSSError(maj_stat, min_stat)
def krb5_export_lucid_sec_context(SecurityContext context not None,
OM_uint32 version):
info = {
1: Krb5LucidContextV1,
}.get(version, Krb5LucidContext)()
cdef void **raw_ctx = <void **>&(<Krb5LucidContext>info).raw_ctx
cdef OM_uint32 maj_stat, min_stat
with nogil:
maj_stat = gss_krb5_export_lucid_sec_context(&min_stat,
&context.raw_ctx,
version, raw_ctx)
if maj_stat != GSS_S_COMPLETE:
raise GSSError(maj_stat, min_stat)
return info
def krb5_extract_authtime_from_sec_context(SecurityContext context not None):
# In Heimdal, authtime is time_t which is either a 4 or 8 byte int. By
# passing in a uint64_t reference, there should be enough space for GSSAPI
# to store the data in either situation. Coming back to Python it will be
# handled as a normal int without loosing data.
cdef uint64_t time = 0
cdef OM_uint32 maj_stat, min_stat
with nogil:
maj_stat = gsskrb5_extract_authtime_from_sec_context(&min_stat,
context.raw_ctx,
<void *>&time)
if maj_stat != GSS_S_COMPLETE:
raise GSSError(maj_stat, min_stat)
return time
def krb5_extract_authz_data_from_sec_context(SecurityContext context not None,
ad_type):
# GSS_C_EMPTY_BUFFER
cdef gss_buffer_desc ad_data = gss_buffer_desc(0, NULL)
cdef int ad_type_val = <int>ad_type
cdef OM_uint32 maj_stat, min_stat
with nogil:
maj_stat = gsskrb5_extract_authz_data_from_sec_context(&min_stat,
context.raw_ctx,
ad_type_val,
&ad_data)
if maj_stat != GSS_S_COMPLETE:
raise GSSError(maj_stat, min_stat)
try:
return <bytes>(<char *>ad_data.value)[:ad_data.length]
finally:
gss_release_buffer(&min_stat, &ad_data)
def krb5_import_cred(Creds cred_handle not None, cache=None,
keytab_principal=None, keytab=None):
cdef void *cache_ptr = NULL
if cache is not None and cache:
cache_ptr = <void *>(<uintptr_t>cache)
cdef void *keytab_princ = NULL
if keytab_principal is not None and keytab_principal:
keytab_princ = <void *>(<uintptr_t>keytab_principal)
cdef void *kt = NULL
if keytab is not None and keytab:
kt = <void *>(<uintptr_t>keytab)
if cache_ptr == NULL and kt == NULL:
raise ValueError("Either cache or keytab must be set")
cdef OM_uint32 maj_stat, min_stat
with nogil:
maj_stat = gss_krb5_import_cred(&min_stat, cache_ptr, keytab_princ,
kt, &cred_handle.raw_creds)
if maj_stat != GSS_S_COMPLETE:
raise GSSError(maj_stat, min_stat)
def krb5_get_tkt_flags(SecurityContext context not None):
cdef OM_uint32 maj_stat, min_stat
cdef uint32_t ticket_flags = 0
with nogil:
maj_stat = gss_krb5_get_tkt_flags(&min_stat, context.raw_ctx,
<void *>&ticket_flags)
if maj_stat != GSS_S_COMPLETE:
raise GSSError(maj_stat, min_stat)
return ticket_flags
def krb5_set_allowable_enctypes(Creds cred_handle not None,
ktypes):
cdef OM_uint32 maj_stat, min_stat
# This shouldn't ever happen but it's here to satisfy compiler warnings
cdef size_t ktypes_count = <size_t>len(ktypes)
if ktypes_count > UINT32_MAX:
raise ValueError("ktypes list size too large")
cdef uint32_t count = <uint32_t>ktypes_count
cdef int32_t *enc_types = <int32_t *>calloc(count, sizeof(int32_t))
if not enc_types:
raise MemoryError()
try:
for i, val in enumerate(ktypes):
enc_types[i] = val
with nogil:
maj_stat = gss_krb5_set_allowable_enctypes(&min_stat,
cred_handle.raw_creds,
count,
enc_types)
finally:
free(enc_types)
if maj_stat != GSS_S_COMPLETE:
raise GSSError(maj_stat, min_stat)
|