File: ext_krb5.pyx

package info (click to toggle)
python-gssapi 1.10.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 876 kB
  • sloc: python: 3,707; sh: 198; makefile: 154; ansic: 60
file content (360 lines) | stat: -rw-r--r-- 12,331 bytes parent folder | download | duplicates (3)
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)