Coverage Report

Created: 2024-06-18 06:03

/src/gss-ntlmssp/src/gss_creds.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright 2013 Simo Sorce <simo@samba.org>, see COPYING for license */
2
3
#include <errno.h>
4
#include <stdio.h>
5
#include <stdlib.h>
6
#include <string.h>
7
8
#include <gssapi/gssapi.h>
9
#include <gssapi/gssapi_ext.h>
10
11
#include "gss_ntlmssp.h"
12
13
static int hex_to_key(const char *hex, struct ntlm_key *key)
14
0
{
15
0
    size_t len = strlen(hex);
16
0
    if (len != 32) return ERR_KEYLEN;
17
18
0
    for (int i = 0; i < 16; i++) {
19
0
        key->data[i] = 0;
20
0
        for (int j = 0; j < 2; j++) {
21
0
            uint8_t c = hex[i*2+j];
22
0
            if (c >= '0' && c <= '9') {
23
0
                c -= '0';
24
0
            } else if (c >= 'A' && c <= 'F') {
25
0
                c -= 'A' - 10;
26
0
            } else if (c >= 'a' && c <= 'f') {
27
0
                c -= 'a' - 10;
28
0
            } else {
29
0
                return ERR_BADARG;
30
0
            }
31
0
            key->data[i] |= (c << ((1 - j) * 4));
32
0
        }
33
0
    }
34
0
    key->length = 16;
35
0
    return 0;
36
0
}
37
38
static char *get_user_file_envvar(void)
39
80
{
40
80
    const char *envvar;
41
42
    /* use the same var used by Heimdal */
43
80
    envvar = getenv("NTLM_USER_FILE");
44
80
    if (envvar == NULL) return NULL;
45
46
0
    return strdup(envvar);
47
80
}
48
49
static int get_user_file_creds(const char *filename,
50
                               struct gssntlm_name *name,
51
                               struct gssntlm_cred *cred)
52
0
{
53
0
    struct gssntlm_ctx *ctx;
54
0
    int lm_compat_lvl = -1;
55
0
    char line[1024];
56
0
    char *field1, *field2, *field3, *field4;
57
0
    char *dom, *usr, *pwd, *lm, *nt;
58
0
    char *p;
59
0
    bool found = false;
60
0
    FILE *f;
61
0
    int ret = 0;
62
63
0
    ctx = calloc(1, sizeof(struct gssntlm_ctx));
64
0
    if (!ctx) return ENOMEM;
65
66
0
    lm_compat_lvl = gssntlm_get_lm_compatibility_level();
67
0
    if (!gssntlm_required_security(lm_compat_lvl, ctx)) {
68
0
        ret = ERR_BADLMLVL;
69
0
        goto done;
70
0
    }
71
72
    /* Use the same file format used by Heimdal in hope to achieve
73
     * some compatibility between implementations:
74
     * Each line is one entry like the following:
75
     * DOMAIN:USERNAME:PASSWORD */
76
77
    /* **OR** */
78
79
    /* Use the smbpasswd file format for Samba compatibility.
80
     * Each line is one entry like the following:
81
     * NAME:UID:LM_HASH:NT_HASH:ACCT_FLAGS:TIMESTAMP
82
     * Fields after NT_HASH are ignored
83
     *
84
     * The smbpasswd format is extended to allow domain qualified names.
85
     */
86
87
    /* The second format is distinguished from the first based on
88
     * the number of fields encountered. It is technically possible
89
     * to mix both formats in a single file though it is not
90
     * recommended.
91
     */
92
93
0
    f = fopen(filename, "r");
94
0
    if (!f) {
95
0
        ret = errno;
96
0
        goto done;
97
0
    }
98
99
0
    while(fgets(line, 1024, f)) {
100
0
        p = line;
101
0
        if (*p == '#') continue;
102
0
        field1 = p;
103
0
        p = strchr(field1, ':');
104
0
        if (!p) continue;
105
0
        *p++ = '\0';
106
0
        field2 = p;
107
0
        p = strchr(field2, ':');
108
0
        if (!p) continue;
109
0
        *p++ = '\0';
110
0
        field3 = p;
111
0
        p = strchr(field3, ':');
112
0
        if (!p) {
113
            /* Assume Heimdal file format */
114
0
            p = field3;
115
0
            strsep(&p, "\r\n");
116
117
0
            dom = field1;
118
0
            usr = field2;
119
0
            pwd = field3;
120
0
            lm = NULL;
121
0
            nt = NULL;
122
0
        } else {
123
0
            *p++ = '\0';
124
0
            field4 = p;
125
0
            p = strchr(field4, ':');
126
0
            if (!p) continue;
127
0
            *p++ = '\0';
128
            /* Assume smbpasswd file format */
129
0
            dom = NULL;
130
0
            usr = field1;
131
0
            pwd = NULL;
132
0
            lm = field3;
133
0
            nt = field4;
134
135
            /* check if username is domain qualified */
136
0
            p = strchr(usr, '\\');
137
0
            if (p) {
138
0
                dom = usr;
139
0
                *p++ = '\0';
140
0
                usr = p;
141
0
            }
142
0
        }
143
144
        /* if no name is specified use the first found */
145
0
        if (name == NULL) {
146
0
            found = true;
147
0
            break;
148
0
        }
149
150
0
        if (name->data.user.domain && dom) {
151
0
            if (!ntlm_casecmp(dom, name->data.user.domain)) continue;
152
0
        }
153
0
        if (name->data.user.name) {
154
0
            if (!ntlm_casecmp(usr, name->data.user.name)) continue;
155
0
        }
156
        /* all matched (NULLs in name are wildcards) */
157
0
        found = true;
158
0
        break;
159
0
    }
160
161
0
    fclose(f);
162
163
0
    if (!found) {
164
0
        ret = ENOENT;
165
0
        goto done;
166
0
    }
167
168
0
    cred->type = GSSNTLM_CRED_USER;
169
0
    cred->cred.user.user.type = GSSNTLM_NAME_USER;
170
0
    if (dom) {
171
0
        free(cred->cred.user.user.data.user.domain);
172
0
        cred->cred.user.user.data.user.domain = strdup(dom);
173
0
        if (!cred->cred.user.user.data.user.domain) {
174
0
            ret = ENOMEM;
175
0
            goto done;
176
0
        }
177
0
    }
178
0
    free(cred->cred.user.user.data.user.name);
179
0
    cred->cred.user.user.data.user.name = strdup(usr);
180
0
    if (!cred->cred.user.user.data.user.name) {
181
0
        ret = ENOMEM;
182
0
        goto done;
183
0
    }
184
185
0
    if (pwd) {
186
0
        cred->cred.user.nt_hash.length = 16;
187
188
0
        ret = NTOWFv1(pwd, &cred->cred.user.nt_hash);
189
0
        if (ret) goto done;
190
191
0
        if (gssntlm_sec_lm_ok(ctx)) {
192
0
            cred->cred.user.lm_hash.length = 16;
193
0
            ret = LMOWFv1(pwd, &cred->cred.user.lm_hash);
194
0
            if (ret) goto done;
195
0
        }
196
0
    }
197
198
0
    if (lm && nt) {
199
0
        ret = hex_to_key(nt, &cred->cred.user.nt_hash);
200
0
        if (ret) goto done;
201
202
0
        if (gssntlm_sec_lm_ok(ctx)) {
203
0
            ret = hex_to_key(lm, &cred->cred.user.lm_hash);
204
0
            if (ret) goto done;
205
0
        }
206
0
    }
207
208
0
done:
209
0
    free(ctx);
210
0
    return ret;
211
0
}
212
213
static int get_server_creds(struct gssntlm_name *name,
214
                            struct gssntlm_cred *cred)
215
0
{
216
0
    gss_name_t gssname = NULL;
217
0
    gss_buffer_desc tmpbuf;
218
0
    uint32_t retmaj;
219
0
    uint32_t retmin;
220
0
    int ret;
221
222
0
    if (name == NULL) {
223
0
        tmpbuf.value = discard_const("");
224
0
        tmpbuf.length = 0;
225
0
        ret = 0;
226
0
        retmaj = gssntlm_import_name_by_mech(&retmin,
227
0
                                             &gssntlm_oid,
228
0
                                             &tmpbuf,
229
0
                                             GSS_C_NT_HOSTBASED_SERVICE,
230
0
                                             &gssname);
231
0
        if (retmaj) return retmin;
232
233
0
        name = (struct gssntlm_name *)gssname;
234
0
    }
235
236
0
    cred->type = GSSNTLM_CRED_SERVER;
237
0
    ret = gssntlm_copy_name(name, &cred->cred.server.name);
238
0
    gssntlm_int_release_name((struct gssntlm_name *)gssname);
239
240
0
    return ret;
241
0
}
242
243
0
#define GENERIC_CS_PASSWORD "password"
244
/* To support in future, RC4 Key is NT hash */
245
#define KRB5_CS_CLI_KEYTAB_URN "client_keytab"
246
#define KRB5_CS_KEYTAB_URN "keytab"
247
248
static int get_creds_from_store(struct gssntlm_name *name,
249
                                struct gssntlm_cred *cred,
250
                                gss_const_key_value_set_t cred_store)
251
0
{
252
0
    uint32_t i;
253
0
    int ret;
254
255
0
    if (name) {
256
        /* special case to let server creds carry a keyfile */
257
0
        if (name->type == GSSNTLM_NAME_SERVER) {
258
0
            const char *keyfile = NULL;
259
0
            cred->type = GSSNTLM_CRED_SERVER;
260
0
            ret = gssntlm_copy_name(name, &cred->cred.server.name);
261
0
            if (ret) return ret;
262
0
            for (i = 0; i < cred_store->count; i++) {
263
0
                if (strcmp(cred_store->elements[i].key,
264
0
                            GSS_NTLMSSP_CS_KEYFILE) == 0) {
265
0
                    keyfile = cred_store->elements[i].value;
266
0
                }
267
0
            }
268
0
            if (keyfile) {
269
0
                cred->cred.server.keyfile = strdup(keyfile);
270
0
                if (cred->cred.server.keyfile == NULL) {
271
0
                    return errno;
272
0
                }
273
0
            }
274
0
            return 0;
275
0
        }
276
277
0
        if (name->type != GSSNTLM_NAME_USER) return ENOENT;
278
279
0
        ret = gssntlm_copy_name(name, &cred->cred.user.user);
280
0
        if (ret) return ret;
281
0
    }
282
283
    /* so far only user options can be defined in the cred_store */
284
0
    cred->type = GSSNTLM_CRED_USER;
285
286
0
    for (i = 0; i < cred_store->count; i++) {
287
0
        if (strcmp(cred_store->elements[i].key, GSS_NTLMSSP_CS_DOMAIN) == 0) {
288
0
            free(cred->cred.user.user.data.user.domain);
289
0
            cred->cred.user.user.data.user.domain =
290
0
                                    strdup(cred_store->elements[i].value);
291
0
            if (!cred->cred.user.user.data.user.domain) return ENOMEM;
292
0
        }
293
0
        if (strcmp(cred_store->elements[i].key, GSS_NTLMSSP_CS_NTHASH) == 0) {
294
0
            ret = hex_to_key(cred_store->elements[i].value,
295
0
                             &cred->cred.user.nt_hash);
296
0
            if (ret) return ret;
297
0
        }
298
0
        if ((strcmp(cred_store->elements[i].key, GSS_NTLMSSP_CS_PASSWORD) == 0) ||
299
0
            (strcmp(cred_store->elements[i].key, GENERIC_CS_PASSWORD) == 0)) {
300
0
            cred->cred.user.nt_hash.length = 16;
301
0
            ret = NTOWFv1(cred_store->elements[i].value,
302
0
                          &cred->cred.user.nt_hash);
303
304
0
            if (gssntlm_get_lm_compatibility_level() < 3) {
305
0
                cred->cred.user.lm_hash.length = 16;
306
0
                ret = LMOWFv1(cred_store->elements[i].value,
307
0
                              &cred->cred.user.lm_hash);
308
0
                if (ret) return ret;
309
0
            }
310
311
0
            if (ret) return ret;
312
0
        }
313
0
        if (strcmp(cred_store->elements[i].key, GSS_NTLMSSP_CS_KEYFILE) == 0) {
314
0
            ret = get_user_file_creds(cred_store->elements[i].value,
315
0
                                      name, cred);
316
0
            if (ret) return ret;
317
0
        }
318
0
    }
319
320
    /* now check if a user name was found, if not error out */
321
0
    if (cred->cred.user.user.data.user.name == NULL) return ENOENT;
322
323
0
    return 0;
324
0
}
325
326
static void gssntlm_copy_key(struct ntlm_key *dest, struct ntlm_key *src)
327
0
{
328
0
    memcpy(dest->data, src->data, src->length);
329
0
    dest->length = src->length;
330
0
}
331
332
int gssntlm_copy_creds(struct gssntlm_cred *in, struct gssntlm_cred *out)
333
0
{
334
0
    char *dom = NULL, *usr = NULL, *srv = NULL;
335
0
    int ret = 0;
336
337
0
    out->type = GSSNTLM_CRED_NONE;
338
339
0
    switch (in->type) {
340
0
    case GSSNTLM_CRED_NONE:
341
0
        break;
342
0
    case GSSNTLM_CRED_ANON:
343
0
        out->cred.anon.dummy = 1;
344
0
        break;
345
0
    case GSSNTLM_CRED_USER:
346
0
        ret = gssntlm_copy_name(&in->cred.user.user,
347
0
                                &out->cred.user.user);
348
0
        if (ret) goto done;
349
0
        gssntlm_copy_key(&out->cred.user.nt_hash,
350
0
                         &in->cred.user.nt_hash);
351
0
        gssntlm_copy_key(&out->cred.user.lm_hash,
352
0
                         &in->cred.user.lm_hash);
353
0
        break;
354
0
    case GSSNTLM_CRED_SERVER:
355
0
        ret = gssntlm_copy_name(&in->cred.server.name,
356
0
                                &out->cred.server.name);
357
0
        if (ret) goto done;
358
0
        break;
359
0
    case GSSNTLM_CRED_EXTERNAL:
360
0
        ret = gssntlm_copy_name(&in->cred.external.user,
361
0
                                &out->cred.external.user);
362
0
        if (ret) goto done;
363
0
        break;
364
0
    }
365
0
    out->type = in->type;
366
367
0
done:
368
0
    if (ret) {
369
0
        safefree(dom);
370
0
        safefree(usr);
371
0
        safefree(srv);
372
0
    }
373
0
    return ret;
374
0
}
375
376
void gssntlm_int_release_cred(struct gssntlm_cred *cred)
377
3.33k
{
378
3.33k
    if (!cred) return;
379
380
80
    switch (cred->type) {
381
80
    case GSSNTLM_CRED_NONE:
382
80
        break;
383
0
    case GSSNTLM_CRED_ANON:
384
0
        cred->cred.anon.dummy = 0;
385
0
        break;
386
0
    case GSSNTLM_CRED_USER:
387
0
        gssntlm_int_release_name(&cred->cred.user.user);
388
0
        safezero(cred->cred.user.nt_hash.data, 16);
389
0
        cred->cred.user.nt_hash.length = 0;
390
0
        safezero(cred->cred.user.lm_hash.data, 16);
391
0
        cred->cred.user.lm_hash.length = 0;
392
0
        break;
393
0
    case GSSNTLM_CRED_SERVER:
394
0
        gssntlm_int_release_name(&cred->cred.server.name);
395
0
        safefree(cred->cred.server.keyfile);
396
0
        break;
397
0
    case GSSNTLM_CRED_EXTERNAL:
398
0
        gssntlm_int_release_name(&cred->cred.external.user);
399
0
        break;
400
80
    }
401
80
}
402
403
uint32_t gssntlm_acquire_cred_from(uint32_t *minor_status,
404
                                   void *external_context,
405
                                   gss_name_t desired_name,
406
                                   uint32_t time_req,
407
                                   gss_OID_set desired_mechs,
408
                                   gss_cred_usage_t cred_usage,
409
                                   gss_const_key_value_set_t cred_store,
410
                                   gss_cred_id_t *output_cred_handle,
411
                                   gss_OID_set *actual_mechs,
412
                                   uint32_t *time_rec)
413
80
{
414
80
    struct gssntlm_cred *cred;
415
80
    struct gssntlm_name *name;
416
80
    uint32_t retmaj;
417
80
    uint32_t retmin;
418
419
80
    name = (struct gssntlm_name *)desired_name;
420
421
80
    cred = calloc(1, sizeof(struct gssntlm_cred));
422
80
    if (!cred) {
423
0
        return GSSERRS(errno, GSS_S_FAILURE);
424
0
    }
425
426
    /* FIXME: should we split the cred union and allow GSS_C_BOTH ?
427
     * It may be possible to specify get server name from env and/or
428
     * user creds from cred store at the same time, etc .. */
429
80
    if (cred_usage == GSS_C_BOTH) {
430
0
        if (name == NULL) {
431
0
            cred_usage = GSS_C_ACCEPT;
432
0
        } else {
433
0
            switch (name->type) {
434
0
            case GSSNTLM_NAME_SERVER:
435
0
                cred_usage = GSS_C_ACCEPT;
436
0
                break;
437
0
            case GSSNTLM_NAME_USER:
438
0
            case GSSNTLM_NAME_ANON:
439
0
                cred_usage = GSS_C_INITIATE;
440
0
                break;
441
0
            default:
442
0
                set_GSSERRS(ERR_BADCRED, GSS_S_CRED_UNAVAIL);
443
0
                goto done;
444
0
            }
445
0
        }
446
0
    }
447
448
80
    if (cred_usage == GSS_C_INITIATE) {
449
80
        if (name != NULL && name->type != GSSNTLM_NAME_USER) {
450
0
            set_GSSERRS(ERR_NOUSRNAME, GSS_S_BAD_NAMETYPE);
451
0
            goto done;
452
0
        }
453
454
80
        if (cred_store != GSS_C_NO_CRED_STORE) {
455
0
            retmin = get_creds_from_store(name, cred, cred_store);
456
80
        } else {
457
80
            char *filename;
458
459
80
            filename = get_user_file_envvar();
460
80
            if (filename) {
461
0
                retmin = get_user_file_creds(filename, name, cred);
462
0
                free(filename);
463
80
            } else {
464
80
                retmin = ENOENT;
465
80
            }
466
80
            if (retmin) {
467
80
                uint32_t ret;
468
80
                ret = external_get_creds(external_context, name, cred);
469
80
                if (ret != ERR_NOTAVAIL) {
470
0
                    retmin = ret;
471
0
                }
472
80
            }
473
80
        }
474
80
        if (retmin) {
475
80
            set_GSSERRS(retmin, GSS_S_CRED_UNAVAIL);
476
80
            goto done;
477
80
        }
478
80
    } else if (cred_usage == GSS_C_ACCEPT) {
479
0
        if (name != NULL && name->type != GSSNTLM_NAME_SERVER) {
480
0
            set_GSSERRS(ERR_NOSRVNAME, GSS_S_BAD_NAMETYPE);
481
0
            goto done;
482
0
        }
483
484
0
        if (cred_store != GSS_C_NO_CRED_STORE) {
485
0
            retmin = get_creds_from_store(name, cred, cred_store);
486
0
        } else {
487
0
            retmin = get_server_creds(name, cred);
488
0
            if (retmin) {
489
0
                set_GSSERR(retmin);
490
0
                goto done;
491
0
            }
492
0
        }
493
0
    } else if (cred_usage == GSS_C_BOTH) {
494
0
        set_GSSERRS(ERR_NOTSUPPORTED, GSS_S_CRED_UNAVAIL);
495
0
        goto done;
496
0
    } else {
497
0
        set_GSSERRS(ERR_BADARG, GSS_S_CRED_UNAVAIL);
498
0
        goto done;
499
0
    }
500
501
0
    set_GSSERRS(0, GSS_S_COMPLETE);
502
503
80
done:
504
80
    if (retmaj) {
505
80
        uint32_t tmpmin;
506
80
        gssntlm_release_cred(&tmpmin, (gss_cred_id_t *)&cred);
507
80
    } else {
508
0
        *output_cred_handle = (gss_cred_id_t)cred;
509
0
        if (time_rec) *time_rec = GSS_C_INDEFINITE;
510
0
    }
511
512
80
    return GSSERR();
513
0
}
514
515
uint32_t gssntlm_acquire_cred(uint32_t *minor_status,
516
                              gss_name_t desired_name,
517
                              uint32_t time_req,
518
                              gss_OID_set desired_mechs,
519
                              gss_cred_usage_t cred_usage,
520
                              gss_cred_id_t *output_cred_handle,
521
                              gss_OID_set *actual_mechs,
522
                              uint32_t *time_rec)
523
0
{
524
0
    return gssntlm_acquire_cred_from(minor_status, NULL,
525
0
                                     desired_name,
526
0
                                     time_req,
527
0
                                     desired_mechs,
528
0
                                     cred_usage,
529
0
                                     GSS_C_NO_CRED_STORE,
530
0
                                     output_cred_handle,
531
0
                                     actual_mechs,
532
0
                                     time_rec);
533
0
}
534
535
uint32_t gssntlm_release_cred(uint32_t *minor_status,
536
                              gss_cred_id_t *cred_handle)
537
3.33k
{
538
3.33k
    *minor_status = 0;
539
540
3.33k
    if (!cred_handle) return GSS_S_COMPLETE;
541
542
3.33k
    gssntlm_int_release_cred((struct gssntlm_cred *)*cred_handle);
543
3.33k
    safefree(*cred_handle);
544
545
3.33k
    return GSS_S_COMPLETE;
546
3.33k
}
547
548
uint32_t gssntlm_acquire_cred_with_password(uint32_t *minor_status,
549
                                            gss_name_t desired_name,
550
                                            gss_buffer_t password,
551
                                            uint32_t time_req,
552
                                            gss_OID_set desired_mechs,
553
                                            gss_cred_usage_t cred_usage,
554
                                            gss_cred_id_t *output_cred_handle,
555
                                            gss_OID_set *actual_mechs,
556
                                            uint32_t *time_rec)
557
0
{
558
0
    gss_key_value_element_desc element;
559
0
    gss_key_value_set_desc cred_store;
560
561
0
    element.key = GENERIC_CS_PASSWORD;
562
0
    element.value = (const char *)password->value;
563
564
0
    cred_store.count = 1;
565
0
    cred_store.elements = &element;
566
567
0
    return gssntlm_acquire_cred_from(minor_status, NULL,
568
0
                                     desired_name,
569
0
                                     time_req,
570
0
                                     desired_mechs,
571
0
                                     cred_usage,
572
0
                                     &cred_store,
573
0
                                     output_cred_handle,
574
0
                                     actual_mechs,
575
0
                                     time_rec);
576
0
}
577
578
uint32_t gssntlm_inquire_cred(uint32_t *minor_status,
579
                              gss_cred_id_t cred_handle,
580
                              gss_name_t *name,
581
                              uint32_t *lifetime,
582
                              gss_cred_usage_t *cred_usage,
583
                              gss_OID_set *mechanisms)
584
0
{
585
0
    struct gssntlm_cred *cred = (struct gssntlm_cred *)GSS_C_NO_CREDENTIAL;
586
0
    uint32_t retmin, retmaj;
587
0
    uint32_t maj, min;
588
589
0
    if (cred_handle == GSS_C_NO_CREDENTIAL) {
590
0
        maj = gssntlm_acquire_cred_from(&min, NULL,
591
0
                                        NULL, GSS_C_INDEFINITE,
592
0
                                        NULL, GSS_C_INITIATE,
593
0
                                        GSS_C_NO_CRED_STORE,
594
0
                                        (gss_cred_id_t *)&cred,
595
0
                                        NULL, NULL);
596
0
        if (maj) {
597
0
            set_GSSERRS(0, GSS_S_NO_CRED);
598
0
            goto done;
599
0
        }
600
0
    } else {
601
0
        cred = (struct gssntlm_cred *)cred_handle;
602
0
    }
603
604
0
    if (cred->type == GSSNTLM_CRED_NONE) {
605
0
        set_GSSERRS(ERR_BADARG, GSS_S_NO_CRED);
606
0
        goto done;
607
0
    }
608
609
0
    if (name) {
610
0
        switch (cred->type) {
611
0
        case GSSNTLM_CRED_NONE:
612
0
        case GSSNTLM_CRED_ANON:
613
0
            *name = GSS_C_NO_NAME;
614
0
            break;
615
0
        case GSSNTLM_CRED_USER:
616
0
            maj = gssntlm_duplicate_name(&min,
617
0
                                         (gss_name_t)&cred->cred.user.user,
618
0
                                         name);
619
0
            if (maj != GSS_S_COMPLETE) {
620
0
                set_GSSERRS(min, maj);
621
0
                goto done;
622
0
            }
623
0
            break;
624
0
        case GSSNTLM_CRED_SERVER:
625
0
            maj = gssntlm_duplicate_name(&min,
626
0
                                         (gss_name_t)&cred->cred.server.name,
627
0
                                         name);
628
0
            if (maj != GSS_S_COMPLETE) {
629
0
                set_GSSERRS(min, maj);
630
0
                goto done;
631
0
            }
632
0
            break;
633
0
        case GSSNTLM_CRED_EXTERNAL:
634
0
            maj = gssntlm_duplicate_name(&min,
635
0
                                        (gss_name_t)&cred->cred.external.user,
636
0
                                        name);
637
0
            if (maj != GSS_S_COMPLETE) {
638
0
                set_GSSERRS(min, maj);
639
0
                goto done;
640
0
            }
641
0
            break;
642
0
        }
643
0
    }
644
645
0
    if (lifetime) *lifetime = GSS_C_INDEFINITE;
646
0
    if (cred_usage) {
647
0
        if (cred->type == GSSNTLM_CRED_SERVER) {
648
0
            *cred_usage = GSS_C_ACCEPT;
649
0
        } else {
650
0
            *cred_usage = GSS_C_INITIATE;
651
0
        }
652
0
    }
653
654
0
    if (mechanisms) {
655
0
        maj = gss_create_empty_oid_set(&min, mechanisms);
656
0
        if (maj != GSS_S_COMPLETE) {
657
0
            set_GSSERRS(min, maj);
658
0
            gss_release_name(&min, name);
659
0
            goto done;
660
0
        }
661
0
        maj = gss_add_oid_set_member(&min,
662
0
                                     discard_const(&gssntlm_oid),
663
0
                                     mechanisms);
664
0
        if (maj != GSS_S_COMPLETE) {
665
0
            set_GSSERRS(min, maj);
666
0
            gss_release_oid_set(&min, mechanisms);
667
0
            gss_release_name(&min, name);
668
0
            goto done;
669
0
        }
670
0
    }
671
672
0
    set_GSSERRS(0, GSS_S_COMPLETE);
673
674
0
done:
675
0
    if (cred_handle == GSS_C_NO_CREDENTIAL) {
676
0
        gssntlm_release_cred(&min, (gss_cred_id_t *)&cred);
677
0
    }
678
0
    return GSSERR();
679
0
}
680
681
uint32_t gssntlm_inquire_cred_by_mech(uint32_t *minor_status,
682
                                      gss_cred_id_t cred_handle,
683
                                      gss_OID mech_type,
684
                                      gss_name_t *name,
685
                                      uint32_t *initiator_lifetime,
686
                                      uint32_t *acceptor_lifetime,
687
                                      gss_cred_usage_t *cred_usage)
688
0
{
689
0
    gss_cred_usage_t usage;
690
0
    uint32_t lifetime;
691
0
    uint32_t retmaj;
692
0
    uint32_t retmin;
693
0
    uint32_t maj, min;
694
695
0
    maj = gssntlm_inquire_cred(&min, cred_handle, name,
696
0
                               &lifetime, &usage, NULL);
697
0
    if (maj != GSS_S_COMPLETE) return GSSERRS(min, maj);
698
699
0
    switch (usage) {
700
0
    case GSS_C_INITIATE:
701
0
        if (initiator_lifetime) *initiator_lifetime = lifetime;
702
0
        if (acceptor_lifetime) *acceptor_lifetime = 0;
703
0
        break;
704
0
    case GSS_C_ACCEPT:
705
0
        if (initiator_lifetime) *initiator_lifetime = 0;
706
0
        if (acceptor_lifetime) *acceptor_lifetime = lifetime;
707
0
        break;
708
0
    case GSS_C_BOTH:
709
0
        if (initiator_lifetime) *initiator_lifetime = lifetime;
710
0
        if (acceptor_lifetime) *acceptor_lifetime = lifetime;
711
0
        break;
712
0
    default:
713
0
        return GSSERRS(ERR_BADARG, GSS_S_FAILURE);
714
0
    }
715
716
0
    if (cred_usage) *cred_usage = usage;
717
0
    return GSSERRS(0, GSS_S_COMPLETE);
718
0
}
719
720
gss_OID_desc gssntlm_neg_flags_oid = {
721
    GSS_NTLMSSP_NEG_FLAGS_OID_LENGTH,
722
    discard_const(GSS_NTLMSSP_NEG_FLAGS_OID_STRING)
723
};
724
725
static uint32_t gssntlm_set_cred_neg_flags(uint32_t *minor_status,
726
                                           struct gssntlm_cred *cred,
727
                                           const gss_buffer_t value)
728
0
{
729
730
0
    if (cred == NULL || value == NULL) {
731
0
        *minor_status = EINVAL;
732
0
        return GSS_S_CALL_INACCESSIBLE_READ;
733
0
    }
734
0
    if (value->length == 0) {
735
        /* special to reset to library defaults */
736
0
        if (cred->type == GSSNTLM_CRED_SERVER) {
737
0
            cred->neg_flags = NTLMSSP_DEFAULT_SERVER_FLAGS;
738
0
        } else {
739
0
            cred->neg_flags = NTLMSSP_DEFAULT_CLIENT_FLAGS;
740
0
        }
741
0
    } else if (value->length == sizeof(uint32_t)) {
742
0
        cred->neg_flags = *(uint32_t *)value->value;
743
0
    } else {
744
0
        *minor_status = EINVAL;
745
0
        return GSS_S_FAILURE;
746
0
    }
747
748
0
    *minor_status = 0;
749
0
    return GSS_S_COMPLETE;
750
0
}
751
752
uint32_t gssntlm_set_cred_option(uint32_t *minor_status,
753
                                 gss_cred_id_t *cred_handle,
754
                                 const gss_OID desired_object,
755
                                 const gss_buffer_t value)
756
0
{
757
0
    struct gssntlm_cred *cred;
758
759
0
    if (minor_status == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE;
760
0
    *minor_status = 0;
761
762
0
    if (cred_handle == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE;
763
0
    cred = (struct gssntlm_cred *)*cred_handle;
764
765
0
    if (desired_object == GSS_C_NO_OID) return GSS_S_CALL_INACCESSIBLE_READ;
766
767
0
    if (gss_oid_equal(desired_object, &gssntlm_neg_flags_oid)) {
768
0
        return gssntlm_set_cred_neg_flags(minor_status, cred, value);
769
0
    }
770
771
0
    *minor_status = EINVAL;
772
0
    return GSS_S_UNAVAILABLE;
773
0
}