Coverage Report

Created: 2026-09-01 06:18

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
553
0
    if (attrs->count == 0) goto done;
554
555
0
    a = calloc(attrs->count + 1, sizeof(struct gssntlm_name_attribute));
556
0
    if (a == NULL) {
557
0
        set_GSSERR(ENOMEM);
558
0
        goto done;
559
0
    }
560
0
    *imp_attrs = a;
561
562
0
    cursor = RELMEM_PTR(state, &attrs->buffers);
563
564
0
    for (size_t i = 0; i < attrs->count; i++) {
565
0
        struct relmem name;
566
0
        struct relmem value;
567
0
        memcpy(&name, cursor, sizeof(struct relmem));
568
0
        cursor += sizeof(struct relmem);
569
0
        memcpy(&value, cursor, sizeof(struct relmem));
570
0
        cursor += sizeof(struct relmem);
571
0
        retmaj = import_data_buffer(&retmin, state,
572
0
                                    (uint8_t **)&a[i].attr_name,
573
0
                                    NULL, true, &name, true);
574
0
        if (retmaj != GSS_S_COMPLETE) goto done;
575
0
        retmaj = import_data_buffer(&retmin, state,
576
0
                                    (uint8_t **)&a[i].attr_value.value,
577
0
                                    &a[i].attr_value.length,
578
0
                                    true, &value, false);
579
0
        if (retmaj != GSS_S_COMPLETE) goto done;
580
0
    }
581
582
0
    set_GSSERRS(0, GSS_S_COMPLETE);
583
584
0
done:
585
0
    return GSSERR();
586
0
}
587
588
static uint32_t import_name(uint32_t *minor_status,
589
                            struct export_state *state,
590
                            struct export_name *name,
591
                            struct gssntlm_name *imp_name)
592
0
{
593
0
    uint32_t retmaj;
594
0
    uint32_t retmin;
595
0
    uint8_t *dest;
596
597
0
    switch (name->type) {
598
0
    case EXP_NAME_NONE:
599
0
        memset(imp_name, 0, sizeof(struct gssntlm_name));
600
0
        break;
601
602
0
    case EXP_NAME_ANON:
603
0
        memset(imp_name, 0, sizeof(struct gssntlm_name));
604
0
        imp_name->type = GSSNTLM_NAME_ANON;
605
0
        break;
606
607
0
    case EXP_NAME_USER:
608
0
        imp_name->type = GSSNTLM_NAME_USER;
609
0
        dest = NULL;
610
0
        if (name->dom_or_spn.len > 0) {
611
0
            retmaj = import_data_buffer(&retmin, state,
612
0
                                     &dest, NULL, true,
613
0
                                     &name->dom_or_spn, true);
614
0
            if (retmaj != GSS_S_COMPLETE) goto done;
615
0
        }
616
0
        imp_name->data.user.domain = (char *)dest;
617
0
        dest = NULL;
618
0
        if (name->name.len > 0) {
619
0
            retmaj = import_data_buffer(&retmin, state,
620
0
                                     &dest, NULL, true,
621
0
                                     &name->name, true);
622
0
            if (retmaj != GSS_S_COMPLETE) goto done;
623
0
        }
624
0
        imp_name->data.user.name = (char *)dest;
625
0
        break;
626
627
0
    case EXP_NAME_SERV:
628
0
        imp_name->type = GSSNTLM_NAME_SERVER;
629
0
        dest = NULL;
630
0
        if (name->dom_or_spn.len > 0) {
631
0
            retmaj = import_data_buffer(&retmin, state,
632
0
                                     &dest, NULL, true,
633
0
                                     &name->dom_or_spn, true);
634
0
            if (retmaj != GSS_S_COMPLETE) goto done;
635
0
        }
636
0
        imp_name->data.server.spn = (char *)dest;
637
0
        dest = NULL;
638
0
        if (name->name.len > 0) {
639
0
            retmaj = import_data_buffer(&retmin, state,
640
0
                                     &dest, NULL, true,
641
0
                                     &name->name, true);
642
0
            if (retmaj != GSS_S_COMPLETE) goto done;
643
0
        }
644
0
        imp_name->data.server.name = (char *)dest;
645
0
        break;
646
647
0
    default:
648
0
        set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN);
649
0
        break;
650
0
    }
651
652
0
    retmaj = import_attrs(minor_status, state, &name->attrs, &imp_name->attrs);
653
0
    if (retmaj != GSS_S_COMPLETE) goto done;
654
655
0
    set_GSSERRS(0, GSS_S_COMPLETE);
656
657
0
done:
658
0
    return GSSERR();
659
0
}
660
661
static uint32_t import_keys(uint32_t *minor_status,
662
                            struct export_state *state,
663
                            struct export_keys *keys,
664
                            struct ntlm_signseal_handle *imp_keys)
665
0
{
666
0
    struct ntlm_buffer in;
667
0
    uint8_t *dest;
668
0
    uint32_t retmaj;
669
0
    uint32_t retmin;
670
0
    int ret;
671
672
0
    if (keys->sign_key.len > 0) {
673
0
        imp_keys->sign_key.length = 16; /* buf max size */
674
0
        dest = imp_keys->sign_key.data;
675
0
        retmaj = import_data_buffer(&retmin, state,
676
0
                                 &dest, &imp_keys->sign_key.length,
677
0
                                 false, &keys->sign_key, false);
678
0
        if (retmaj != GSS_S_COMPLETE) goto done;
679
0
    } else {
680
0
        memset(&imp_keys->sign_key, 0, sizeof(struct ntlm_key));
681
0
    }
682
683
0
    if (keys->seal_key.len > 0) {
684
0
        imp_keys->seal_key.length = 16; /* buf max size */
685
0
        dest = imp_keys->seal_key.data;
686
0
        retmaj = import_data_buffer(&retmin, state,
687
0
                                 &dest, &imp_keys->seal_key.length,
688
0
                                 false, &keys->seal_key, false);
689
0
        if (retmaj != GSS_S_COMPLETE) goto done;
690
0
    } else {
691
0
        memset(&imp_keys->seal_key, 0, sizeof(struct ntlm_key));
692
0
    }
693
694
0
    if (keys->rc4_state.len > 0) {
695
0
        retmaj = import_data_buffer(&retmin, state,
696
0
                                 &in.data, &in.length, true,
697
0
                                 &keys->rc4_state, false);
698
0
        if (retmaj != GSS_S_COMPLETE) goto done;
699
0
        ret = RC4_IMPORT(&imp_keys->seal_handle, &in);
700
0
        safezero(in.data, in.length);
701
0
        safefree(in.data);
702
0
        if (ret) {
703
0
            set_GSSERR(ret);
704
0
            goto done;
705
0
        }
706
0
    } else {
707
0
        imp_keys->seal_handle = NULL;
708
0
    }
709
710
0
    imp_keys->seq_num = le32toh(keys->seq_num);
711
712
0
    set_GSSERRS(0, GSS_S_COMPLETE);
713
714
0
done:
715
0
    return GSSERR();
716
0
}
717
718
uint32_t gssntlm_import_sec_context(uint32_t *minor_status,
719
                                    gss_buffer_t interprocess_token,
720
                                    gss_ctx_id_t *context_handle)
721
0
{
722
0
    struct gssntlm_ctx *ctx = NULL;
723
0
    struct export_state state;
724
0
    struct export_ctx *ectx;
725
0
    uint8_t *dest;
726
0
    uint64_t time;
727
0
    uint32_t retmaj;
728
0
    uint32_t retmin;
729
730
0
    if (interprocess_token == NULL) {
731
0
        return GSSERRS(0, GSS_S_CALL_INACCESSIBLE_READ);
732
0
    }
733
734
0
    if (interprocess_token->length < sizeof(struct export_ctx)) {
735
0
        return GSSERRS(0, GSS_S_DEFECTIVE_TOKEN);
736
0
    }
737
738
0
    if (context_handle == NULL) {
739
0
        return GSSERRS(0, GSS_S_CALL_INACCESSIBLE_WRITE);
740
0
    }
741
742
0
    ctx = calloc(1, sizeof(struct gssntlm_ctx));
743
0
    if (!ctx) {
744
0
        set_GSSERR(ENOMEM);
745
0
        goto done;
746
0
    }
747
0
    retmin = ntlm_init_ctx(&ctx->ntlm);
748
0
    if (retmin) {
749
0
        set_GSSERR(retmin);
750
0
        goto done;
751
0
    }
752
753
0
    state.exp_struct = interprocess_token->value;
754
0
    state.exp_len = interprocess_token->length;
755
0
    ectx = (struct export_ctx *)state.exp_struct;
756
0
    state.exp_data = (uint8_t *)ectx->data - (uint8_t *)ectx;
757
758
0
    if (ectx->version != le16toh(EXPORT_CTX_VER)) {
759
0
        set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN);
760
0
        goto done;
761
0
    }
762
763
0
    switch (ectx->role) {
764
0
    case EXP_CTX_CLIENT:
765
0
        ctx->role = GSSNTLM_CLIENT;
766
0
        break;
767
0
    case EXP_CTX_SERVER:
768
0
        ctx->role = GSSNTLM_SERVER;
769
0
        break;
770
0
    case EXP_CTX_DOMSRV:
771
0
        ctx->role = GSSNTLM_DOMAIN_SERVER;
772
0
        break;
773
0
    case EXP_CTX_DOMCTR:
774
0
        ctx->role = GSSNTLM_DOMAIN_CONTROLLER;
775
0
        break;
776
0
    default:
777
0
        set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN);
778
0
        goto done;
779
0
    }
780
781
0
    switch (ectx->stage) {
782
0
    case EXP_STG_INIT:
783
0
        ctx->stage = NTLMSSP_STAGE_INIT;
784
0
        break;
785
0
    case EXP_STG_NEGO:
786
0
        ctx->stage = NTLMSSP_STAGE_NEGOTIATE;
787
0
        break;
788
0
    case EXP_STG_CHAL:
789
0
        ctx->stage = NTLMSSP_STAGE_CHALLENGE;
790
0
        break;
791
0
    case EXP_STG_AUTH:
792
0
        ctx->stage = NTLMSSP_STAGE_AUTHENTICATE;
793
0
        break;
794
0
    case EXP_STG_DONE:
795
0
        ctx->stage = NTLMSSP_STAGE_DONE;
796
0
        break;
797
0
    default:
798
0
        set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN);
799
0
        goto done;
800
0
    }
801
802
0
    ctx->sec_req = ectx->sec_req;
803
804
0
    dest = NULL;
805
0
    if (ectx->workstation.len > 0) {
806
0
        retmaj = import_data_buffer(&retmin, &state, &dest, NULL,
807
0
                                 true, &ectx->workstation, true);
808
0
        if (retmaj != GSS_S_COMPLETE) goto done;
809
0
    }
810
0
    ctx->workstation = (char *)dest;
811
812
0
    if (ectx->nego_msg.len > 0) {
813
0
        retmaj = import_data_buffer(&retmin, &state,
814
0
                                 &ctx->nego_msg.data, &ctx->nego_msg.length,
815
0
                                 true, &ectx->nego_msg, false);
816
0
        if (retmaj != GSS_S_COMPLETE) goto done;
817
0
    } else {
818
0
        ctx->nego_msg.data = NULL;
819
0
        ctx->nego_msg.length = 0;
820
0
    }
821
822
0
    if (ectx->chal_msg.len > 0) {
823
0
        retmaj = import_data_buffer(&retmin, &state,
824
0
                                 &ctx->chal_msg.data, &ctx->chal_msg.length,
825
0
                                 true, &ectx->chal_msg, false);
826
0
        if (retmaj != GSS_S_COMPLETE) goto done;
827
0
    } else {
828
0
        ctx->chal_msg.data = NULL;
829
0
        ctx->chal_msg.length = 0;
830
0
    }
831
832
0
    if (ectx->auth_msg.len > 0) {
833
0
        retmaj = import_data_buffer(&retmin, &state,
834
0
                                 &ctx->auth_msg.data, &ctx->auth_msg.length,
835
0
                                 true, &ectx->auth_msg, false);
836
0
        if (retmaj != GSS_S_COMPLETE) goto done;
837
0
    } else {
838
0
        ctx->auth_msg.data = NULL;
839
0
        ctx->auth_msg.length = 0;
840
0
    }
841
842
0
    retmaj = import_name(&retmin, &state,
843
0
                      &ectx->source, &ctx->source_name);
844
0
    if (retmaj != GSS_S_COMPLETE) goto done;
845
846
0
    retmaj = import_name(&retmin, &state,
847
0
                      &ectx->target, &ctx->target_name);
848
0
    if (retmaj != GSS_S_COMPLETE) goto done;
849
850
0
    memcpy(ctx->server_chal, ectx->server_chal, 8);
851
852
0
    ctx->gss_flags = le32toh(ectx->gss_flags);
853
0
    ctx->neg_flags = le32toh(ectx->neg_flags);
854
855
0
    if (ectx->exported_session_key.len > 0) {
856
0
        ctx->exported_session_key.length = 16; /* buf max size */
857
0
        dest = ctx->exported_session_key.data;
858
0
        retmaj = import_data_buffer(&retmin, &state, &dest,
859
0
                                 &ctx->exported_session_key.length,
860
0
                                 false, &ectx->exported_session_key, false);
861
0
        if (retmaj != GSS_S_COMPLETE) goto done;
862
0
    } else {
863
0
        memset(&ctx->exported_session_key, 0, sizeof(struct ntlm_key));
864
0
    }
865
866
0
    retmaj = import_keys(&retmin, &state,
867
0
                      &ectx->send, &ctx->crypto_state.send);
868
0
    if (retmaj != GSS_S_COMPLETE) goto done;
869
870
0
    retmaj = import_keys(&retmin, &state,
871
0
                      &ectx->recv, &ctx->crypto_state.recv);
872
0
    if (retmaj != GSS_S_COMPLETE) goto done;
873
874
    /* We need to restoer also the general crypto status flags */
875
0
    ctx->crypto_state.ext_sec =
876
0
        (ctx->neg_flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY);
877
0
    ctx->crypto_state.datagram =
878
0
        (ctx->neg_flags & NTLMSSP_NEGOTIATE_DATAGRAM);
879
880
0
    ctx->int_flags = ectx->int_flags;
881
882
0
    time = le64toh(ectx->expration_time);
883
0
    ctx->expiration_time = time;
884
885
0
    set_GSSERRS(0, GSS_S_COMPLETE);
886
887
0
done:
888
0
    if (retmaj == GSS_S_COMPLETE) {
889
0
        *context_handle = (gss_ctx_id_t)ctx;
890
0
    } else {
891
0
        uint32_t min;
892
0
        gssntlm_delete_sec_context(&min, (gss_ctx_id_t *)&ctx, NULL);
893
0
    }
894
0
    return GSSERR();
895
0
}
896
897
#define EXPORT_CRED_VER 0x0002
898
899
#pragma pack(push, 1)
900
struct export_cred {
901
    uint16_t version;
902
    uint16_t type;
903
904
    struct export_name name;    /* user or server name */
905
    struct relmem nt_hash;      /* empty for dummy or server */
906
    struct relmem lm_hash;      /* empty for dummy or server */
907
    struct relmem keyfile;
908
    uint8_t ext_cached;
909
910
    uint8_t data[];
911
};
912
#pragma pack(pop)
913
914
0
#define EXP_CRED_NONE 0
915
0
#define EXP_CRED_ANON 1
916
0
#define EXP_CRED_USER 2
917
0
#define EXP_CRED_SERVER 3
918
0
#define EXP_CRED_EXTERNAL 4
919
920
uint32_t gssntlm_export_cred(uint32_t *minor_status,
921
                             gss_cred_id_t cred_handle,
922
                             gss_buffer_t token)
923
0
{
924
0
    struct gssntlm_cred *cred;
925
0
    struct export_state state = { 0 };
926
0
    struct export_cred ecred = { 0 };
927
0
    uint32_t retmaj;
928
0
    uint32_t retmin;
929
0
    int ret;
930
931
0
    if (token == NULL) {
932
0
        return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE);
933
0
    }
934
935
0
    cred = (struct gssntlm_cred *)cred_handle;
936
0
    if (cred_handle == NULL) {
937
0
        return GSSERRS(ERR_NOARG, GSS_S_NO_CRED);
938
0
    }
939
940
    /* we want to leave space to add the basic creds structure in the buffer
941
     * however we want a memory stable structure we can refernce via memory
942
     * pointers while we run export functions for all the "static" context
943
     * data, so we allocate space but we use a stack allocated struct until
944
     * the very end. */
945
0
    state.exp_size = NEW_SIZE(0, sizeof(struct export_cred));
946
0
    state.exp_struct = calloc(1, state.exp_size);
947
0
    if (!state.exp_struct) {
948
0
        set_GSSERR(ENOMEM);
949
0
        goto done;
950
0
    }
951
0
    state.exp_data = (uint8_t *)&ecred.data - (uint8_t *)&ecred;
952
0
    state.exp_len = state.exp_data;
953
954
0
    ecred.version = htole16(EXPORT_CRED_VER);
955
956
0
    switch (cred->type) {
957
0
    case GSSNTLM_CRED_NONE:
958
0
        ecred.type = EXP_CRED_NONE;
959
0
        break;
960
0
    case GSSNTLM_CRED_ANON:
961
0
        ecred.type = EXP_CRED_ANON;
962
0
        break;
963
0
    case GSSNTLM_CRED_USER:
964
0
        ecred.type = EXP_CRED_USER;
965
966
0
        ret = export_name(&state, &cred->cred.user.user, &ecred.name);
967
0
        if (ret) {
968
0
            set_GSSERR(ret);
969
0
            goto done;
970
0
        }
971
972
0
        ret = export_data_buffer(&state,
973
0
                                 cred->cred.user.nt_hash.data,
974
0
                                 cred->cred.user.nt_hash.length,
975
0
                                 &ecred.nt_hash);
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.lm_hash.data,
983
0
                                 cred->cred.user.lm_hash.length,
984
0
                                 &ecred.lm_hash);
985
0
        if (ret) {
986
0
            set_GSSERR(ret);
987
0
            goto done;
988
0
        }
989
0
        break;
990
0
    case GSSNTLM_CRED_SERVER:
991
0
        ecred.type = EXP_CRED_SERVER;
992
993
0
        ret = export_name(&state, &cred->cred.server.name, &ecred.name);
994
0
        if (ret) {
995
0
            set_GSSERR(ret);
996
0
            goto done;
997
0
        }
998
999
0
        if (cred->cred.server.keyfile) {
1000
0
            ret = export_data_buffer(&state,
1001
0
                                     cred->cred.server.keyfile,
1002
0
                                     strlen(cred->cred.server.keyfile),
1003
0
                                     &ecred.keyfile);
1004
0
            if (ret) {
1005
0
                set_GSSERR(ret);
1006
0
                goto done;
1007
0
            }
1008
0
        }
1009
0
        break;
1010
0
    case GSSNTLM_CRED_EXTERNAL:
1011
0
        ecred.type = EXP_CRED_EXTERNAL;
1012
1013
0
        ret = export_name(&state, &cred->cred.external.user, &ecred.name);
1014
0
        if (ret) {
1015
0
            set_GSSERR(ret);
1016
0
            goto done;
1017
0
        }
1018
0
        if (cred->cred.external.creds_in_cache) {
1019
0
            ecred.ext_cached = 1;
1020
0
        }
1021
0
        break;
1022
1023
0
    }
1024
1025
    /* finally copy ecred into the allocated buffer */
1026
0
    memcpy(state.exp_struct, &ecred, state.exp_data);
1027
1028
0
    set_GSSERRS(0, GSS_S_COMPLETE);
1029
1030
0
done:
1031
0
    if (retmaj) {
1032
0
        free(state.exp_struct);
1033
0
    } else {
1034
0
        token->value = state.exp_struct;
1035
0
        token->length = state.exp_len;
1036
0
    }
1037
0
    return GSSERR();
1038
0
}
1039
1040
uint32_t gssntlm_import_cred(uint32_t *minor_status,
1041
                             gss_buffer_t token,
1042
                             gss_cred_id_t *cred_handle)
1043
0
{
1044
0
    struct gssntlm_cred *cred;
1045
0
    struct export_state state = { 0 };
1046
0
    struct export_cred *ecred;
1047
0
    uint32_t retmaj;
1048
0
    uint32_t retmin;
1049
1050
0
    if (token == NULL) {
1051
0
        return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ);
1052
0
    }
1053
1054
0
    if (token->length < sizeof(struct export_cred)) {
1055
0
        return GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN);
1056
0
    }
1057
1058
0
    if (cred_handle == NULL) {
1059
0
        return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE);
1060
0
    }
1061
1062
0
    cred = calloc(1, sizeof(struct gssntlm_cred));
1063
0
    if (!cred) {
1064
0
        set_GSSERR(ENOMEM);
1065
0
        goto done;
1066
0
    }
1067
1068
0
    state.exp_struct = token->value;
1069
0
    state.exp_len = token->length;
1070
0
    ecred = (struct export_cred *)state.exp_struct;
1071
0
    state.exp_data = (char *)ecred->data - (char *)ecred;
1072
1073
0
    if (ecred->version != le16toh(EXPORT_CRED_VER)) {
1074
0
        set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN);
1075
0
        goto done;
1076
0
    }
1077
1078
0
    switch (ecred->type) {
1079
0
    case EXP_CRED_NONE:
1080
0
        cred->type = GSSNTLM_CRED_NONE;
1081
0
        break;
1082
0
    case EXP_CRED_ANON:
1083
0
        cred->type = GSSNTLM_CRED_ANON;
1084
0
        break;
1085
0
    case EXP_CRED_USER:
1086
0
        cred->type = GSSNTLM_CRED_USER;
1087
0
        retmaj = import_name(&retmin, &state, &ecred->name,
1088
0
                          &cred->cred.user.user);
1089
0
        if (retmaj != GSS_S_COMPLETE) goto done;
1090
1091
0
        if (ecred->nt_hash.len > 16 || ecred->lm_hash.len > 16) {
1092
0
            set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN);
1093
0
            goto done;
1094
0
        }
1095
1096
0
        retmaj = import_data_buffer(&retmin, &state,
1097
0
                                 (uint8_t **)&cred->cred.user.nt_hash.data,
1098
0
                                 &cred->cred.user.nt_hash.length,
1099
0
                                 false, &ecred->nt_hash, false);
1100
0
        if (retmaj != GSS_S_COMPLETE) goto done;
1101
1102
0
        retmaj = import_data_buffer(&retmin, &state,
1103
0
                                 (uint8_t **)&cred->cred.user.lm_hash.data,
1104
0
                                 &cred->cred.user.lm_hash.length,
1105
0
                                 false, &ecred->lm_hash, false);
1106
0
        if (retmaj != GSS_S_COMPLETE) goto done;
1107
0
        break;
1108
0
    case EXP_CRED_SERVER:
1109
0
        cred->type = GSSNTLM_CRED_SERVER;
1110
0
        retmaj = import_name(&retmin, &state, &ecred->name,
1111
0
                          &cred->cred.server.name);
1112
0
        if (retmaj != GSS_S_COMPLETE) goto done;
1113
0
        if (ecred->keyfile.len > 0) {
1114
0
            retmaj = import_data_buffer(&retmin, &state,
1115
0
                                        (uint8_t **)&cred->cred.server.keyfile,
1116
0
                                        NULL, true, &ecred->keyfile, true);
1117
0
            if (retmaj != GSS_S_COMPLETE) goto done;
1118
0
        }
1119
0
        break;
1120
0
    case EXP_CRED_EXTERNAL:
1121
0
        cred->type = GSSNTLM_CRED_EXTERNAL;
1122
0
        retmaj = import_name(&retmin, &state, &ecred->name,
1123
0
                          &cred->cred.external.user);
1124
0
        if (retmaj != GSS_S_COMPLETE) goto done;
1125
0
        cred->cred.external.creds_in_cache = (ecred->ext_cached == 1);
1126
0
        break;
1127
0
    default:
1128
0
        set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN);
1129
0
        break;
1130
0
    }
1131
1132
0
    set_GSSERRS(0, GSS_S_COMPLETE);
1133
1134
0
done:
1135
0
    if (retmaj == GSS_S_COMPLETE) {
1136
0
        *cred_handle = (gss_cred_id_t)cred;
1137
0
    } else {
1138
0
        uint32_t min;
1139
0
        gssntlm_release_cred(&min, (gss_cred_id_t *)&cred);
1140
0
    }
1141
0
    return GSSERR();
1142
0
}