Coverage Report

Created: 2026-10-17 06:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gss-ntlmssp/src/gss_serialize.c
Line
Count
Source
1
/* Copyright 2013 Simo Sorce <simo@samba.org>, see COPYING for license */
2
3
#include <endian.h>
4
#include <errno.h>
5
#include <stdint.h>
6
#include <stdlib.h>
7
#include <string.h>
8
#include <time.h>
9
10
#include "gssapi_ntlmssp.h"
11
#include "gss_ntlmssp.h"
12
13
/* each integer in the export format is a little endian integer */
14
#pragma pack(push, 1)
15
struct relmem {
16
    uint32_t ptr;
17
    uint32_t len;
18
};
19
20
struct export_attrs {
21
    uint16_t count;
22
    /* for each count there is a pair of name/value buffers
23
     * that we'll pack in a single buffer */
24
    struct relmem buffers;
25
};
26
27
struct export_name {
28
    uint8_t type;
29
    struct relmem dom_or_spn;
30
    struct relmem name;
31
    struct export_attrs attrs;
32
};
33
34
struct export_keys {
35
    struct relmem sign_key;
36
    struct relmem seal_key;
37
    struct relmem rc4_state;
38
    uint32_t seq_num;
39
};
40
41
#define EXPORT_CTX_VER 0x0005
42
struct export_ctx {
43
    uint16_t version;
44
    uint8_t role;
45
    uint8_t stage;
46
    uint8_t sec_req;
47
48
    struct relmem workstation;
49
50
    struct relmem nego_msg;
51
    struct relmem chal_msg;
52
    struct relmem auth_msg;
53
54
    struct export_name source;
55
    struct export_name target;
56
57
    uint8_t server_chal[8];
58
59
    uint32_t gss_flags;
60
    uint32_t neg_flags;
61
62
    struct relmem exported_session_key;
63
    struct export_keys send;
64
    struct export_keys recv;
65
66
    uint8_t int_flags;
67
    uint64_t expration_time;
68
69
    uint8_t data[];
70
};
71
#pragma pack(pop)
72
73
0
#define EXP_CTX_CLIENT 1
74
0
#define EXP_CTX_SERVER 2
75
0
#define EXP_CTX_DOMSRV 3
76
0
#define EXP_CTX_DOMCTR 4
77
0
#define EXP_STG_INIT 1
78
0
#define EXP_STG_NEGO 2
79
0
#define EXP_STG_CHAL 3
80
0
#define EXP_STG_AUTH 4
81
0
#define EXP_STG_DONE 5
82
0
#define EXP_NAME_NONE 0
83
0
#define EXP_NAME_ANON 1
84
0
#define EXP_NAME_USER 2
85
0
#define EXP_NAME_SERV 3
86
87
0
#define INC_EXP_SIZE 0x001000 /* 4K */
88
0
#define MAX_EXP_SIZE 0x100000 /* 1M */
89
90
#define NEW_SIZE(s, n) \
91
0
    ((((s) + (n) + (INC_EXP_SIZE-1)) / INC_EXP_SIZE) * INC_EXP_SIZE)
92
93
struct export_state {
94
    uint8_t *exp_struct;
95
    size_t exp_size;
96
    size_t exp_data;
97
    size_t exp_len;
98
};
99
100
#define RELMEM_PTR(state, rm) \
101
0
    ((state)->exp_struct + (state)->exp_data + (rm)->ptr)
102
103
#define RELMEM_ZERO(rm) \
104
0
    memset((rm), 0, sizeof(struct relmem))
105
106
static int export_data_allocate(struct export_state *state,
107
                                size_t length, struct relmem *rm)
108
0
{
109
0
    size_t new_size;
110
0
    void *tmp;
111
112
0
    if (length > MAX_EXP_SIZE) {
113
0
        return E2BIG;
114
0
    }
115
116
0
    if (length > state->exp_size - state->exp_len) {
117
0
        new_size = NEW_SIZE(state->exp_len, length);
118
0
        if ((new_size < state->exp_size) || new_size > MAX_EXP_SIZE) {
119
0
            return E2BIG;
120
0
        }
121
0
        tmp = realloc(state->exp_struct, new_size);
122
0
        if (!tmp) {
123
0
            return ENOMEM;
124
0
        }
125
0
        state->exp_struct = tmp;
126
0
        state->exp_size = new_size;
127
0
    }
128
129
0
    rm->ptr = state->exp_len - state->exp_data;
130
0
    rm->len = length;
131
0
    state->exp_len += length;
132
133
0
    return 0;
134
0
}
135
136
static int export_data_buffer(struct export_state *state,
137
                              void *data, size_t length,
138
                              struct relmem *rm)
139
0
{
140
0
    int ret;
141
142
0
    if (length == 0) {
143
0
        RELMEM_ZERO(rm);
144
0
        return 0;
145
0
    }
146
147
0
    ret = export_data_allocate(state, length, rm);
148
0
    if (ret) return ret;
149
150
0
    memcpy(RELMEM_PTR(state, rm), data, length);
151
0
    return 0;
152
0
}
153
154
static int export_attrs(struct export_state *state,
155
                        struct gssntlm_name_attribute *attrs,
156
                        struct export_attrs *exp_attrs)
157
0
{
158
0
    size_t count = gssntlm_get_attrs_count(attrs);
159
0
    size_t ptr_array_size = 0;
160
0
    int ret;
161
162
0
    if (count == 0) return 0;
163
0
    if (count > UINT16_MAX) return E2BIG;
164
165
0
    exp_attrs->count = count;
166
167
    /* reserve data space in state->exp_struct for pointers */
168
0
    ptr_array_size = count * 2 * sizeof(struct relmem);
169
0
    ret = export_data_allocate(state, ptr_array_size, &exp_attrs->buffers);
170
0
    if (ret) return ret;
171
172
    /* exp_attrs->buffers may be reallocated as part of data structure
173
     * expansion in export_data_buffer() so we need to recompute the
174
     * buffers pointer after each use of export_data_buffer */
175
0
    for (size_t i = 0; i < count; i++) {
176
0
        struct relmem *buffers;
177
0
        struct relmem buffer;
178
        /* name */
179
0
        ret = export_data_buffer(state, attrs[i].attr_name,
180
0
                                 strlen(attrs[i].attr_name), &buffer);
181
0
        if (ret) return ret;
182
0
        buffers = (struct relmem *)RELMEM_PTR(state, &exp_attrs->buffers);
183
0
        memcpy(&buffers[i * 2], &buffer, sizeof(struct relmem));
184
        /* value */
185
0
        ret = export_data_buffer(state, attrs[i].attr_value.value,
186
0
                                 attrs[i].attr_value.length, &buffer);
187
0
        if (ret) return ret;
188
0
        buffers = (struct relmem *)RELMEM_PTR(state, &exp_attrs->buffers);
189
0
        memcpy(&buffers[i * 2 + 1], &buffer, sizeof(struct relmem));
190
0
    }
191
192
0
    return 0;
193
0
}
194
195
static int export_name(struct export_state *state,
196
                       struct gssntlm_name *name,
197
                       struct export_name *exp_name)
198
0
{
199
0
    int ret;
200
201
0
    memset(exp_name, 0, sizeof(struct export_name));
202
203
0
    switch (name->type) {
204
0
    case GSSNTLM_NAME_NULL:
205
0
        break;
206
0
    case GSSNTLM_NAME_ANON:
207
0
        exp_name->type = EXP_NAME_ANON;
208
0
        break;
209
0
    case GSSNTLM_NAME_USER:
210
0
        exp_name->type = EXP_NAME_USER;
211
0
        if (name->data.user.domain) {
212
0
            ret = export_data_buffer(state, name->data.user.domain,
213
0
                                     strlen(name->data.user.domain),
214
0
                                     &exp_name->dom_or_spn);
215
0
            if (ret) {
216
0
                return ret;
217
0
            }
218
0
        }
219
0
        if (name->data.user.name) {
220
0
            ret = export_data_buffer(state, name->data.user.name,
221
0
                                     strlen(name->data.user.name),
222
0
                                     &exp_name->name);
223
0
            if (ret) {
224
0
                return ret;
225
0
            }
226
0
        }
227
0
        break;
228
0
    case GSSNTLM_NAME_SERVER:
229
0
        exp_name->type = EXP_NAME_SERV;
230
0
        if (name->data.server.spn) {
231
0
            ret = export_data_buffer(state, name->data.server.spn,
232
0
                                     strlen(name->data.server.spn),
233
0
                                     &exp_name->dom_or_spn);
234
0
            if (ret) {
235
0
                return ret;
236
0
            }
237
0
        }
238
0
        if (name->data.server.name) {
239
0
            ret = export_data_buffer(state, name->data.server.name,
240
0
                                     strlen(name->data.server.name),
241
0
                                     &exp_name->name);
242
0
            if (ret) {
243
0
                return ret;
244
0
            }
245
0
        }
246
0
        break;
247
0
    default:
248
0
        return EINVAL;
249
0
    }
250
0
    return export_attrs(state, name->attrs, &exp_name->attrs);
251
0
}
252
253
static int export_keys(struct export_state *state,
254
                       struct ntlm_signseal_handle *keys,
255
                       struct export_keys *exp_keys)
256
0
{
257
0
    uint8_t buf[258*sizeof(uint32_t)];
258
0
    struct ntlm_buffer out = { .data=buf, .length=sizeof(buf) };
259
0
    int ret;
260
261
0
    memset(exp_keys, 0, sizeof(struct export_keys));
262
263
0
    if (keys->sign_key.length > 0) {
264
0
        ret = export_data_buffer(state,
265
0
                                 keys->sign_key.data,
266
0
                                 keys->sign_key.length,
267
0
                                 &exp_keys->sign_key);
268
0
        if (ret) return ret;
269
0
    }
270
271
0
    if (keys->seal_key.length > 0) {
272
0
        ret = export_data_buffer(state,
273
0
                                 keys->seal_key.data,
274
0
                                 keys->seal_key.length,
275
0
                                 &exp_keys->seal_key);
276
0
        if (ret) return ret;
277
0
    }
278
279
0
    if (keys->seal_handle) {
280
0
        ret = RC4_EXPORT(keys->seal_handle, &out);
281
0
        if (ret) return ret;
282
0
        ret = export_data_buffer(state, buf, sizeof(buf),
283
0
                                 &exp_keys->rc4_state);
284
0
        safezero(buf, sizeof(buf));
285
0
        if (ret) return ret;
286
0
    }
287
288
0
    exp_keys->seq_num = htole32(keys->seq_num);
289
290
0
    return 0;
291
0
}
292
293
uint32_t gssntlm_export_sec_context(uint32_t *minor_status,
294
                                    gss_ctx_id_t *context_handle,
295
                                    gss_buffer_t interprocess_token)
296
0
{
297
0
    struct gssntlm_ctx *ctx;
298
0
    struct export_state state = { 0 };
299
0
    struct export_ctx ectx = { 0 };
300
0
    uint64_t expiration;
301
0
    uint32_t retmaj;
302
0
    uint32_t retmin;
303
0
    int ret;
304
305
0
    if (context_handle == NULL) {
306
0
        return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ);
307
0
    }
308
309
0
    if (interprocess_token == NULL) {
310
0
        return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE);
311
0
    }
312
313
0
    ctx = (struct gssntlm_ctx *)*context_handle;
314
0
    if (ctx == NULL) return GSSERRS(ERR_BADARG, GSS_S_NO_CONTEXT);
315
316
0
    if (ctx->expiration_time && ctx->expiration_time < time(NULL)) {
317
0
        return GSSERRS(ERR_EXPIRED, GSS_S_CONTEXT_EXPIRED);
318
0
    }
319
320
    /* we want to leave space to add the basic context structure in the buffer
321
     * however we want a memory stable structure we can refernce via memory
322
     * pointers while we run export functions for all the "static" context
323
     * data, so we allocate space but we use a stack allocated struct until
324
     * the very end. */
325
0
    state.exp_size = NEW_SIZE(0, sizeof(struct export_ctx));
326
0
    state.exp_struct = malloc(state.exp_size);
327
0
    if (!state.exp_struct) {
328
0
        set_GSSERR(ENOMEM);
329
0
        goto done;
330
0
    }
331
0
    state.exp_data = (uint8_t *)&ectx.data - (uint8_t *)&ectx;
332
0
    state.exp_len = state.exp_data;
333
334
0
    ectx.version = htole16(EXPORT_CTX_VER);
335
336
0
    switch(ctx->role) {
337
0
    case GSSNTLM_CLIENT:
338
0
        ectx.role = EXP_CTX_CLIENT;
339
0
        break;
340
0
    case GSSNTLM_SERVER:
341
0
        ectx.role = EXP_CTX_SERVER;
342
0
        break;
343
0
    case GSSNTLM_DOMAIN_SERVER:
344
0
        ectx.role = EXP_CTX_DOMSRV;
345
0
        break;
346
0
    case GSSNTLM_DOMAIN_CONTROLLER:
347
0
        ectx.role = EXP_CTX_DOMCTR;
348
0
        break;
349
0
    }
350
351
0
    switch(ctx->stage) {
352
0
    case NTLMSSP_STAGE_INIT:
353
0
        ectx.stage = EXP_STG_INIT;
354
0
        break;
355
0
    case NTLMSSP_STAGE_NEGOTIATE:
356
0
        ectx.stage = EXP_STG_NEGO;
357
0
        break;
358
0
    case NTLMSSP_STAGE_CHALLENGE:
359
0
        ectx.stage = EXP_STG_CHAL;
360
0
        break;
361
0
    case NTLMSSP_STAGE_AUTHENTICATE:
362
0
        ectx.stage = EXP_STG_AUTH;
363
0
        break;
364
0
    case NTLMSSP_STAGE_DONE:
365
0
        ectx.stage = EXP_STG_DONE;
366
0
        break;
367
0
    }
368
369
0
    ectx.sec_req = ctx->sec_req;
370
371
0
    if (!ctx->workstation) {
372
0
        RELMEM_ZERO(&ectx.workstation);
373
0
    } else {
374
0
        ret = export_data_buffer(&state, ctx->workstation,
375
0
                                 strlen(ctx->workstation),
376
0
                                 &ectx.workstation);
377
0
        if (ret) {
378
0
            set_GSSERR(ret);
379
0
            goto done;
380
0
        }
381
0
    }
382
383
0
    if (ctx->nego_msg.length > 0) {
384
0
        ret = export_data_buffer(&state,
385
0
                                 ctx->nego_msg.data,
386
0
                                 ctx->nego_msg.length,
387
0
                                 &ectx.nego_msg);
388
0
        if (ret) {
389
0
            set_GSSERR(ret);
390
0
            goto done;
391
0
        }
392
0
    } else {
393
0
        RELMEM_ZERO(&ectx.nego_msg);
394
0
    }
395
396
0
    if (ctx->chal_msg.length > 0) {
397
0
        ret = export_data_buffer(&state,
398
0
                                 ctx->chal_msg.data,
399
0
                                 ctx->chal_msg.length,
400
0
                                 &ectx.chal_msg);
401
0
        if (ret) {
402
0
            set_GSSERR(ret);
403
0
            goto done;
404
0
        }
405
0
    } else {
406
0
        RELMEM_ZERO(&ectx.chal_msg);
407
0
    }
408
409
0
    if (ctx->auth_msg.length > 0) {
410
0
        ret = export_data_buffer(&state,
411
0
                                 ctx->auth_msg.data,
412
0
                                 ctx->auth_msg.length,
413
0
                                 &ectx.auth_msg);
414
0
        if (ret) {
415
0
            set_GSSERR(ret);
416
0
            goto done;
417
0
        }
418
0
    } else {
419
0
        RELMEM_ZERO(&ectx.auth_msg);
420
0
    }
421
422
0
    ret = export_name(&state, &ctx->source_name, &ectx.source);
423
0
    if (ret) {
424
0
        set_GSSERR(ret);
425
0
        goto done;
426
0
    }
427
428
0
    ret = export_name(&state, &ctx->target_name, &ectx.target);
429
0
    if (ret) {
430
0
        set_GSSERR(ret);
431
0
        goto done;
432
0
    }
433
434
0
    memcpy(ectx.server_chal, ctx->server_chal, 8);
435
436
0
    ectx.gss_flags = htole32(ctx->gss_flags);
437
0
    ectx.neg_flags = htole32(ctx->neg_flags);
438
439
0
    ret = export_data_buffer(&state,
440
0
                             ctx->exported_session_key.data,
441
0
                             ctx->exported_session_key.length,
442
0
                             &ectx.exported_session_key);
443
0
    if (ret) {
444
0
        set_GSSERR(ret);
445
0
        goto done;
446
0
    }
447
448
0
    ret = export_keys(&state, &ctx->crypto_state.send, &ectx.send);
449
0
    if (ret) {
450
0
        set_GSSERR(ret);
451
0
        goto done;
452
0
    }
453
454
0
    ret = export_keys(&state, &ctx->crypto_state.recv, &ectx.recv);
455
0
    if (ret) {
456
0
        set_GSSERR(ret);
457
0
        goto done;
458
0
    }
459
460
0
    ectx.int_flags = ctx->int_flags;
461
462
0
    expiration = ctx->expiration_time;
463
0
    ectx.expration_time = htole64(expiration);
464
465
    /* finally copy ectx into the allocated buffer */
466
0
    memcpy(state.exp_struct, &ectx, state.exp_data);
467
468
0
    set_GSSERRS(0, GSS_S_COMPLETE);
469
470
0
done:
471
0
    if (retmaj) {
472
0
        free(state.exp_struct);
473
0
    } else {
474
0
        uint32_t min;
475
0
        interprocess_token->value = state.exp_struct;
476
0
        interprocess_token->length = state.exp_len;
477
478
        /* Invalidate the current context once successfully exported */
479
0
        gssntlm_delete_sec_context(&min, context_handle, NULL);
480
0
    }
481
0
    return GSSERR();
482
0
}
483
484
static uint32_t import_data_buffer(uint32_t *minor_status,
485
                                   struct export_state *state,
486
                                   uint8_t **dest, size_t *len, bool alloc,
487
                                   struct relmem *rm, bool str)
488
0
{
489
0
    uint32_t retmaj;
490
0
    uint32_t retmin;
491
0
    void *ptr;
492
493
0
    if (str && !alloc) {
494
0
        return EINVAL;
495
0
    }
496
497
0
    if (rm->len == 0) {
498
0
        if (alloc) {
499
0
            *dest = NULL;
500
0
        }
501
0
        set_GSSERRS(0, GSS_S_COMPLETE);
502
0
        goto done;
503
0
    }
504
505
0
    if (state->exp_data + rm->ptr + rm->len > state->exp_len) {
506
0
        set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN);
507
0
        goto done;
508
0
    }
509
0
    ptr = RELMEM_PTR(state, rm);
510
0
    if (alloc) {
511
0
        if (str) {
512
0
            *dest = (uint8_t *)strndup((const char *)ptr, rm->len);
513
0
        } else {
514
0
            *dest = malloc(rm->len);
515
0
            if (*dest) {
516
0
                memcpy(*dest, ptr, rm->len);
517
0
            }
518
0
        }
519
0
        if (!*dest) {
520
0
            set_GSSERR(ENOMEM);
521
0
            goto done;
522
0
        }
523
0
    } else {
524
0
        if (!*len) {
525
0
            set_GSSERR(ERR_BADARG);
526
0
            goto done;
527
0
        }
528
0
        if (rm->len > *len) {
529
0
            set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN);
530
0
            goto done;
531
0
        }
532
0
        memcpy(*dest, ptr, rm->len);
533
0
    }
534
0
    set_GSSERRS(0, GSS_S_COMPLETE);
535
536
0
done:
537
0
    if (retmaj == GSS_S_COMPLETE) {
538
0
        if (len) *len = rm->len;
539
0
    }
540
0
    return GSSERR();
541
0
}
542
543
static uint32_t import_attrs(uint32_t *minor_status,
544
                             struct export_state *state,
545
                             struct export_attrs *attrs,
546
                             struct gssntlm_name_attribute **imp_attrs)
547
0
{
548
0
    struct gssntlm_name_attribute *a;
549
0
    uint32_t retmaj = GSS_S_COMPLETE;
550
0
    uint32_t retmin = 0;
551
0
    uint8_t *cursor;
552
0
    size_t array_len;
553
554
0
    if (attrs->count == 0) goto done;
555
556
    /* the pointer array holds two relmem entries per attribute; make sure
557
     * it sits entirely within the exported data before we read from it */
558
0
    array_len = (size_t)attrs->count * 2 * sizeof(struct relmem);
559
0
    if (state->exp_data + attrs->buffers.ptr + array_len > state->exp_len) {
560
0
        set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN);
561
0
        goto done;
562
0
    }
563
564
0
    a = calloc(attrs->count + 1, sizeof(struct gssntlm_name_attribute));
565
0
    if (a == NULL) {
566
0
        set_GSSERR(ENOMEM);
567
0
        goto done;
568
0
    }
569
0
    *imp_attrs = a;
570
571
0
    cursor = RELMEM_PTR(state, &attrs->buffers);
572
573
0
    for (size_t i = 0; i < attrs->count; i++) {
574
0
        struct relmem name;
575
0
        struct relmem value;
576
0
        memcpy(&name, cursor, sizeof(struct relmem));
577
0
        cursor += sizeof(struct relmem);
578
0
        memcpy(&value, cursor, sizeof(struct relmem));
579
0
        cursor += sizeof(struct relmem);
580
0
        retmaj = import_data_buffer(&retmin, state,
581
0
                                    (uint8_t **)&a[i].attr_name,
582
0
                                    NULL, true, &name, true);
583
0
        if (retmaj != GSS_S_COMPLETE) goto done;
584
0
        retmaj = import_data_buffer(&retmin, state,
585
0
                                    (uint8_t **)&a[i].attr_value.value,
586
0
                                    &a[i].attr_value.length,
587
0
                                    true, &value, false);
588
0
        if (retmaj != GSS_S_COMPLETE) goto done;
589
0
    }
590
591
0
    set_GSSERRS(0, GSS_S_COMPLETE);
592
593
0
done:
594
0
    return GSSERR();
595
0
}
596
597
static uint32_t import_name(uint32_t *minor_status,
598
                            struct export_state *state,
599
                            struct export_name *name,
600
                            struct gssntlm_name *imp_name)
601
0
{
602
0
    uint32_t retmaj;
603
0
    uint32_t retmin;
604
0
    uint8_t *dest;
605
606
0
    switch (name->type) {
607
0
    case EXP_NAME_NONE:
608
0
        memset(imp_name, 0, sizeof(struct gssntlm_name));
609
0
        break;
610
611
0
    case EXP_NAME_ANON:
612
0
        memset(imp_name, 0, sizeof(struct gssntlm_name));
613
0
        imp_name->type = GSSNTLM_NAME_ANON;
614
0
        break;
615
616
0
    case EXP_NAME_USER:
617
0
        imp_name->type = GSSNTLM_NAME_USER;
618
0
        dest = NULL;
619
0
        if (name->dom_or_spn.len > 0) {
620
0
            retmaj = import_data_buffer(&retmin, state,
621
0
                                     &dest, NULL, true,
622
0
                                     &name->dom_or_spn, true);
623
0
            if (retmaj != GSS_S_COMPLETE) goto done;
624
0
        }
625
0
        imp_name->data.user.domain = (char *)dest;
626
0
        dest = NULL;
627
0
        if (name->name.len > 0) {
628
0
            retmaj = import_data_buffer(&retmin, state,
629
0
                                     &dest, NULL, true,
630
0
                                     &name->name, true);
631
0
            if (retmaj != GSS_S_COMPLETE) goto done;
632
0
        }
633
0
        imp_name->data.user.name = (char *)dest;
634
0
        break;
635
636
0
    case EXP_NAME_SERV:
637
0
        imp_name->type = GSSNTLM_NAME_SERVER;
638
0
        dest = NULL;
639
0
        if (name->dom_or_spn.len > 0) {
640
0
            retmaj = import_data_buffer(&retmin, state,
641
0
                                     &dest, NULL, true,
642
0
                                     &name->dom_or_spn, true);
643
0
            if (retmaj != GSS_S_COMPLETE) goto done;
644
0
        }
645
0
        imp_name->data.server.spn = (char *)dest;
646
0
        dest = NULL;
647
0
        if (name->name.len > 0) {
648
0
            retmaj = import_data_buffer(&retmin, state,
649
0
                                     &dest, NULL, true,
650
0
                                     &name->name, true);
651
0
            if (retmaj != GSS_S_COMPLETE) goto done;
652
0
        }
653
0
        imp_name->data.server.name = (char *)dest;
654
0
        break;
655
656
0
    default:
657
0
        set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN);
658
0
        break;
659
0
    }
660
661
0
    retmaj = import_attrs(minor_status, state, &name->attrs, &imp_name->attrs);
662
0
    if (retmaj != GSS_S_COMPLETE) goto done;
663
664
0
    set_GSSERRS(0, GSS_S_COMPLETE);
665
666
0
done:
667
0
    return GSSERR();
668
0
}
669
670
static uint32_t import_keys(uint32_t *minor_status,
671
                            struct export_state *state,
672
                            struct export_keys *keys,
673
                            struct ntlm_signseal_handle *imp_keys)
674
0
{
675
0
    struct ntlm_buffer in;
676
0
    uint8_t *dest;
677
0
    uint32_t retmaj;
678
0
    uint32_t retmin;
679
0
    int ret;
680
681
0
    if (keys->sign_key.len > 0) {
682
0
        imp_keys->sign_key.length = 16; /* buf max size */
683
0
        dest = imp_keys->sign_key.data;
684
0
        retmaj = import_data_buffer(&retmin, state,
685
0
                                 &dest, &imp_keys->sign_key.length,
686
0
                                 false, &keys->sign_key, false);
687
0
        if (retmaj != GSS_S_COMPLETE) goto done;
688
0
    } else {
689
0
        memset(&imp_keys->sign_key, 0, sizeof(struct ntlm_key));
690
0
    }
691
692
0
    if (keys->seal_key.len > 0) {
693
0
        imp_keys->seal_key.length = 16; /* buf max size */
694
0
        dest = imp_keys->seal_key.data;
695
0
        retmaj = import_data_buffer(&retmin, state,
696
0
                                 &dest, &imp_keys->seal_key.length,
697
0
                                 false, &keys->seal_key, false);
698
0
        if (retmaj != GSS_S_COMPLETE) goto done;
699
0
    } else {
700
0
        memset(&imp_keys->seal_key, 0, sizeof(struct ntlm_key));
701
0
    }
702
703
0
    if (keys->rc4_state.len > 0) {
704
0
        retmaj = import_data_buffer(&retmin, state,
705
0
                                 &in.data, &in.length, true,
706
0
                                 &keys->rc4_state, false);
707
0
        if (retmaj != GSS_S_COMPLETE) goto done;
708
0
        ret = RC4_IMPORT(&imp_keys->seal_handle, &in);
709
0
        safezero(in.data, in.length);
710
0
        safefree(in.data);
711
0
        if (ret) {
712
0
            set_GSSERR(ret);
713
0
            goto done;
714
0
        }
715
0
    } else {
716
0
        imp_keys->seal_handle = NULL;
717
0
    }
718
719
0
    imp_keys->seq_num = le32toh(keys->seq_num);
720
721
0
    set_GSSERRS(0, GSS_S_COMPLETE);
722
723
0
done:
724
0
    return GSSERR();
725
0
}
726
727
uint32_t gssntlm_import_sec_context(uint32_t *minor_status,
728
                                    gss_buffer_t interprocess_token,
729
                                    gss_ctx_id_t *context_handle)
730
0
{
731
0
    struct gssntlm_ctx *ctx = NULL;
732
0
    struct export_state state;
733
0
    struct export_ctx *ectx;
734
0
    uint8_t *dest;
735
0
    uint64_t time;
736
0
    uint32_t retmaj;
737
0
    uint32_t retmin;
738
739
0
    if (interprocess_token == NULL) {
740
0
        return GSSERRS(0, GSS_S_CALL_INACCESSIBLE_READ);
741
0
    }
742
743
0
    if (interprocess_token->length < sizeof(struct export_ctx)) {
744
0
        return GSSERRS(0, GSS_S_DEFECTIVE_TOKEN);
745
0
    }
746
747
0
    if (context_handle == NULL) {
748
0
        return GSSERRS(0, GSS_S_CALL_INACCESSIBLE_WRITE);
749
0
    }
750
751
0
    ctx = calloc(1, sizeof(struct gssntlm_ctx));
752
0
    if (!ctx) {
753
0
        set_GSSERR(ENOMEM);
754
0
        goto done;
755
0
    }
756
0
    retmin = ntlm_init_ctx(&ctx->ntlm);
757
0
    if (retmin) {
758
0
        set_GSSERR(retmin);
759
0
        goto done;
760
0
    }
761
762
0
    state.exp_struct = interprocess_token->value;
763
0
    state.exp_len = interprocess_token->length;
764
0
    ectx = (struct export_ctx *)state.exp_struct;
765
0
    state.exp_data = (uint8_t *)ectx->data - (uint8_t *)ectx;
766
767
0
    if (ectx->version != le16toh(EXPORT_CTX_VER)) {
768
0
        set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN);
769
0
        goto done;
770
0
    }
771
772
0
    switch (ectx->role) {
773
0
    case EXP_CTX_CLIENT:
774
0
        ctx->role = GSSNTLM_CLIENT;
775
0
        break;
776
0
    case EXP_CTX_SERVER:
777
0
        ctx->role = GSSNTLM_SERVER;
778
0
        break;
779
0
    case EXP_CTX_DOMSRV:
780
0
        ctx->role = GSSNTLM_DOMAIN_SERVER;
781
0
        break;
782
0
    case EXP_CTX_DOMCTR:
783
0
        ctx->role = GSSNTLM_DOMAIN_CONTROLLER;
784
0
        break;
785
0
    default:
786
0
        set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN);
787
0
        goto done;
788
0
    }
789
790
0
    switch (ectx->stage) {
791
0
    case EXP_STG_INIT:
792
0
        ctx->stage = NTLMSSP_STAGE_INIT;
793
0
        break;
794
0
    case EXP_STG_NEGO:
795
0
        ctx->stage = NTLMSSP_STAGE_NEGOTIATE;
796
0
        break;
797
0
    case EXP_STG_CHAL:
798
0
        ctx->stage = NTLMSSP_STAGE_CHALLENGE;
799
0
        break;
800
0
    case EXP_STG_AUTH:
801
0
        ctx->stage = NTLMSSP_STAGE_AUTHENTICATE;
802
0
        break;
803
0
    case EXP_STG_DONE:
804
0
        ctx->stage = NTLMSSP_STAGE_DONE;
805
0
        break;
806
0
    default:
807
0
        set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN);
808
0
        goto done;
809
0
    }
810
811
0
    ctx->sec_req = ectx->sec_req;
812
813
0
    dest = NULL;
814
0
    if (ectx->workstation.len > 0) {
815
0
        retmaj = import_data_buffer(&retmin, &state, &dest, NULL,
816
0
                                 true, &ectx->workstation, true);
817
0
        if (retmaj != GSS_S_COMPLETE) goto done;
818
0
    }
819
0
    ctx->workstation = (char *)dest;
820
821
0
    if (ectx->nego_msg.len > 0) {
822
0
        retmaj = import_data_buffer(&retmin, &state,
823
0
                                 &ctx->nego_msg.data, &ctx->nego_msg.length,
824
0
                                 true, &ectx->nego_msg, false);
825
0
        if (retmaj != GSS_S_COMPLETE) goto done;
826
0
    } else {
827
0
        ctx->nego_msg.data = NULL;
828
0
        ctx->nego_msg.length = 0;
829
0
    }
830
831
0
    if (ectx->chal_msg.len > 0) {
832
0
        retmaj = import_data_buffer(&retmin, &state,
833
0
                                 &ctx->chal_msg.data, &ctx->chal_msg.length,
834
0
                                 true, &ectx->chal_msg, false);
835
0
        if (retmaj != GSS_S_COMPLETE) goto done;
836
0
    } else {
837
0
        ctx->chal_msg.data = NULL;
838
0
        ctx->chal_msg.length = 0;
839
0
    }
840
841
0
    if (ectx->auth_msg.len > 0) {
842
0
        retmaj = import_data_buffer(&retmin, &state,
843
0
                                 &ctx->auth_msg.data, &ctx->auth_msg.length,
844
0
                                 true, &ectx->auth_msg, false);
845
0
        if (retmaj != GSS_S_COMPLETE) goto done;
846
0
    } else {
847
0
        ctx->auth_msg.data = NULL;
848
0
        ctx->auth_msg.length = 0;
849
0
    }
850
851
0
    retmaj = import_name(&retmin, &state,
852
0
                      &ectx->source, &ctx->source_name);
853
0
    if (retmaj != GSS_S_COMPLETE) goto done;
854
855
0
    retmaj = import_name(&retmin, &state,
856
0
                      &ectx->target, &ctx->target_name);
857
0
    if (retmaj != GSS_S_COMPLETE) goto done;
858
859
0
    memcpy(ctx->server_chal, ectx->server_chal, 8);
860
861
0
    ctx->gss_flags = le32toh(ectx->gss_flags);
862
0
    ctx->neg_flags = le32toh(ectx->neg_flags);
863
864
0
    if (ectx->exported_session_key.len > 0) {
865
0
        ctx->exported_session_key.length = 16; /* buf max size */
866
0
        dest = ctx->exported_session_key.data;
867
0
        retmaj = import_data_buffer(&retmin, &state, &dest,
868
0
                                 &ctx->exported_session_key.length,
869
0
                                 false, &ectx->exported_session_key, false);
870
0
        if (retmaj != GSS_S_COMPLETE) goto done;
871
0
    } else {
872
0
        memset(&ctx->exported_session_key, 0, sizeof(struct ntlm_key));
873
0
    }
874
875
0
    retmaj = import_keys(&retmin, &state,
876
0
                      &ectx->send, &ctx->crypto_state.send);
877
0
    if (retmaj != GSS_S_COMPLETE) goto done;
878
879
0
    retmaj = import_keys(&retmin, &state,
880
0
                      &ectx->recv, &ctx->crypto_state.recv);
881
0
    if (retmaj != GSS_S_COMPLETE) goto done;
882
883
    /* We need to restoer also the general crypto status flags */
884
0
    ctx->crypto_state.ext_sec =
885
0
        (ctx->neg_flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY);
886
0
    ctx->crypto_state.datagram =
887
0
        (ctx->neg_flags & NTLMSSP_NEGOTIATE_DATAGRAM);
888
889
0
    ctx->int_flags = ectx->int_flags;
890
891
0
    time = le64toh(ectx->expration_time);
892
0
    ctx->expiration_time = time;
893
894
0
    set_GSSERRS(0, GSS_S_COMPLETE);
895
896
0
done:
897
0
    if (retmaj == GSS_S_COMPLETE) {
898
0
        *context_handle = (gss_ctx_id_t)ctx;
899
0
    } else {
900
0
        uint32_t min;
901
0
        gssntlm_delete_sec_context(&min, (gss_ctx_id_t *)&ctx, NULL);
902
0
    }
903
0
    return GSSERR();
904
0
}
905
906
#define EXPORT_CRED_VER 0x0002
907
908
#pragma pack(push, 1)
909
struct export_cred {
910
    uint16_t version;
911
    uint16_t type;
912
913
    struct export_name name;    /* user or server name */
914
    struct relmem nt_hash;      /* empty for dummy or server */
915
    struct relmem lm_hash;      /* empty for dummy or server */
916
    struct relmem keyfile;
917
    uint8_t ext_cached;
918
919
    uint8_t data[];
920
};
921
#pragma pack(pop)
922
923
0
#define EXP_CRED_NONE 0
924
0
#define EXP_CRED_ANON 1
925
0
#define EXP_CRED_USER 2
926
0
#define EXP_CRED_SERVER 3
927
0
#define EXP_CRED_EXTERNAL 4
928
929
uint32_t gssntlm_export_cred(uint32_t *minor_status,
930
                             gss_cred_id_t cred_handle,
931
                             gss_buffer_t token)
932
0
{
933
0
    struct gssntlm_cred *cred;
934
0
    struct export_state state = { 0 };
935
0
    struct export_cred ecred = { 0 };
936
0
    uint32_t retmaj;
937
0
    uint32_t retmin;
938
0
    int ret;
939
940
0
    if (token == NULL) {
941
0
        return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE);
942
0
    }
943
944
0
    cred = (struct gssntlm_cred *)cred_handle;
945
0
    if (cred_handle == NULL) {
946
0
        return GSSERRS(ERR_NOARG, GSS_S_NO_CRED);
947
0
    }
948
949
    /* we want to leave space to add the basic creds structure in the buffer
950
     * however we want a memory stable structure we can refernce via memory
951
     * pointers while we run export functions for all the "static" context
952
     * data, so we allocate space but we use a stack allocated struct until
953
     * the very end. */
954
0
    state.exp_size = NEW_SIZE(0, sizeof(struct export_cred));
955
0
    state.exp_struct = calloc(1, state.exp_size);
956
0
    if (!state.exp_struct) {
957
0
        set_GSSERR(ENOMEM);
958
0
        goto done;
959
0
    }
960
0
    state.exp_data = (uint8_t *)&ecred.data - (uint8_t *)&ecred;
961
0
    state.exp_len = state.exp_data;
962
963
0
    ecred.version = htole16(EXPORT_CRED_VER);
964
965
0
    switch (cred->type) {
966
0
    case GSSNTLM_CRED_NONE:
967
0
        ecred.type = EXP_CRED_NONE;
968
0
        break;
969
0
    case GSSNTLM_CRED_ANON:
970
0
        ecred.type = EXP_CRED_ANON;
971
0
        break;
972
0
    case GSSNTLM_CRED_USER:
973
0
        ecred.type = EXP_CRED_USER;
974
975
0
        ret = export_name(&state, &cred->cred.user.user, &ecred.name);
976
0
        if (ret) {
977
0
            set_GSSERR(ret);
978
0
            goto done;
979
0
        }
980
981
0
        ret = export_data_buffer(&state,
982
0
                                 cred->cred.user.nt_hash.data,
983
0
                                 cred->cred.user.nt_hash.length,
984
0
                                 &ecred.nt_hash);
985
0
        if (ret) {
986
0
            set_GSSERR(ret);
987
0
            goto done;
988
0
        }
989
990
0
        ret = export_data_buffer(&state,
991
0
                                 cred->cred.user.lm_hash.data,
992
0
                                 cred->cred.user.lm_hash.length,
993
0
                                 &ecred.lm_hash);
994
0
        if (ret) {
995
0
            set_GSSERR(ret);
996
0
            goto done;
997
0
        }
998
0
        break;
999
0
    case GSSNTLM_CRED_SERVER:
1000
0
        ecred.type = EXP_CRED_SERVER;
1001
1002
0
        ret = export_name(&state, &cred->cred.server.name, &ecred.name);
1003
0
        if (ret) {
1004
0
            set_GSSERR(ret);
1005
0
            goto done;
1006
0
        }
1007
1008
0
        if (cred->cred.server.keyfile) {
1009
0
            ret = export_data_buffer(&state,
1010
0
                                     cred->cred.server.keyfile,
1011
0
                                     strlen(cred->cred.server.keyfile),
1012
0
                                     &ecred.keyfile);
1013
0
            if (ret) {
1014
0
                set_GSSERR(ret);
1015
0
                goto done;
1016
0
            }
1017
0
        }
1018
0
        break;
1019
0
    case GSSNTLM_CRED_EXTERNAL:
1020
0
        ecred.type = EXP_CRED_EXTERNAL;
1021
1022
0
        ret = export_name(&state, &cred->cred.external.user, &ecred.name);
1023
0
        if (ret) {
1024
0
            set_GSSERR(ret);
1025
0
            goto done;
1026
0
        }
1027
0
        if (cred->cred.external.creds_in_cache) {
1028
0
            ecred.ext_cached = 1;
1029
0
        }
1030
0
        break;
1031
1032
0
    }
1033
1034
    /* finally copy ecred into the allocated buffer */
1035
0
    memcpy(state.exp_struct, &ecred, state.exp_data);
1036
1037
0
    set_GSSERRS(0, GSS_S_COMPLETE);
1038
1039
0
done:
1040
0
    if (retmaj) {
1041
0
        free(state.exp_struct);
1042
0
    } else {
1043
0
        token->value = state.exp_struct;
1044
0
        token->length = state.exp_len;
1045
0
    }
1046
0
    return GSSERR();
1047
0
}
1048
1049
uint32_t gssntlm_import_cred(uint32_t *minor_status,
1050
                             gss_buffer_t token,
1051
                             gss_cred_id_t *cred_handle)
1052
0
{
1053
0
    struct gssntlm_cred *cred;
1054
0
    struct export_state state = { 0 };
1055
0
    struct export_cred *ecred;
1056
0
    uint32_t retmaj;
1057
0
    uint32_t retmin;
1058
1059
0
    if (token == NULL) {
1060
0
        return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ);
1061
0
    }
1062
1063
0
    if (token->length < sizeof(struct export_cred)) {
1064
0
        return GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN);
1065
0
    }
1066
1067
0
    if (cred_handle == NULL) {
1068
0
        return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE);
1069
0
    }
1070
1071
0
    cred = calloc(1, sizeof(struct gssntlm_cred));
1072
0
    if (!cred) {
1073
0
        set_GSSERR(ENOMEM);
1074
0
        goto done;
1075
0
    }
1076
1077
0
    state.exp_struct = token->value;
1078
0
    state.exp_len = token->length;
1079
0
    ecred = (struct export_cred *)state.exp_struct;
1080
0
    state.exp_data = (char *)ecred->data - (char *)ecred;
1081
1082
0
    if (ecred->version != le16toh(EXPORT_CRED_VER)) {
1083
0
        set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN);
1084
0
        goto done;
1085
0
    }
1086
1087
0
    switch (ecred->type) {
1088
0
    case EXP_CRED_NONE:
1089
0
        cred->type = GSSNTLM_CRED_NONE;
1090
0
        break;
1091
0
    case EXP_CRED_ANON:
1092
0
        cred->type = GSSNTLM_CRED_ANON;
1093
0
        break;
1094
0
    case EXP_CRED_USER:
1095
0
        cred->type = GSSNTLM_CRED_USER;
1096
0
        retmaj = import_name(&retmin, &state, &ecred->name,
1097
0
                          &cred->cred.user.user);
1098
0
        if (retmaj != GSS_S_COMPLETE) goto done;
1099
1100
0
        if (ecred->nt_hash.len > 16 || ecred->lm_hash.len > 16) {
1101
0
            set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN);
1102
0
            goto done;
1103
0
        }
1104
1105
0
        retmaj = import_data_buffer(&retmin, &state,
1106
0
                                 (uint8_t **)&cred->cred.user.nt_hash.data,
1107
0
                                 &cred->cred.user.nt_hash.length,
1108
0
                                 false, &ecred->nt_hash, false);
1109
0
        if (retmaj != GSS_S_COMPLETE) goto done;
1110
1111
0
        retmaj = import_data_buffer(&retmin, &state,
1112
0
                                 (uint8_t **)&cred->cred.user.lm_hash.data,
1113
0
                                 &cred->cred.user.lm_hash.length,
1114
0
                                 false, &ecred->lm_hash, false);
1115
0
        if (retmaj != GSS_S_COMPLETE) goto done;
1116
0
        break;
1117
0
    case EXP_CRED_SERVER:
1118
0
        cred->type = GSSNTLM_CRED_SERVER;
1119
0
        retmaj = import_name(&retmin, &state, &ecred->name,
1120
0
                          &cred->cred.server.name);
1121
0
        if (retmaj != GSS_S_COMPLETE) goto done;
1122
0
        if (ecred->keyfile.len > 0) {
1123
0
            retmaj = import_data_buffer(&retmin, &state,
1124
0
                                        (uint8_t **)&cred->cred.server.keyfile,
1125
0
                                        NULL, true, &ecred->keyfile, true);
1126
0
            if (retmaj != GSS_S_COMPLETE) goto done;
1127
0
        }
1128
0
        break;
1129
0
    case EXP_CRED_EXTERNAL:
1130
0
        cred->type = GSSNTLM_CRED_EXTERNAL;
1131
0
        retmaj = import_name(&retmin, &state, &ecred->name,
1132
0
                          &cred->cred.external.user);
1133
0
        if (retmaj != GSS_S_COMPLETE) goto done;
1134
0
        cred->cred.external.creds_in_cache = (ecred->ext_cached == 1);
1135
0
        break;
1136
0
    default:
1137
0
        set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN);
1138
0
        break;
1139
0
    }
1140
1141
0
    set_GSSERRS(0, GSS_S_COMPLETE);
1142
1143
0
done:
1144
0
    if (retmaj == GSS_S_COMPLETE) {
1145
0
        *cred_handle = (gss_cred_id_t)cred;
1146
0
    } else {
1147
0
        uint32_t min;
1148
0
        gssntlm_release_cred(&min, (gss_cred_id_t *)&cred);
1149
0
    }
1150
0
    return GSSERR();
1151
0
}