Coverage Report

Created: 2025-11-09 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gss-ntlmssp/src/gss_names.c
Line
Count
Source
1
/* Copyright 2013-2022 Simo Sorce <simo@samba.org>, see COPYING for license */
2
3
#define _GNU_SOURCE
4
5
#include <ctype.h>
6
#include <errno.h>
7
#include <limits.h>
8
#include <pwd.h>
9
#include <stdio.h>
10
#include <stdlib.h>
11
#include <string.h>
12
#include <sys/types.h>
13
#include <unistd.h>
14
15
#include <gssapi/gssapi.h>
16
#include <gssapi/gssapi_ext.h>
17
18
#include "gss_ntlmssp.h"
19
20
#ifndef HOST_NAME_MAX
21
#include <sys/param.h>
22
#define HOST_NAME_MAX MAXHOSTNAMELEN
23
#endif
24
25
static uint32_t string_split(uint32_t *minor_status, char sep,
26
                             const char *str, size_t len,
27
                             char **s1, char **s2)
28
29
{
29
29
    uint32_t retmaj;
30
29
    uint32_t retmin;
31
29
    char *r1 = NULL;
32
29
    char *r2 = NULL;
33
29
    const char *p;
34
29
    size_t l;
35
36
29
    p = memchr(str, sep, len);
37
29
    if (!p) return GSSERRS(0, GSS_S_UNAVAILABLE);
38
39
    /* left side */
40
29
    l = p - str;
41
29
    if (s1 && l != 0) {
42
18
        r1 = strndup(str, l);
43
18
        if (!r1) {
44
0
            set_GSSERR(ENOMEM);
45
0
            goto done;
46
0
        }
47
18
    }
48
49
    /* right side */
50
29
    p++;
51
29
    l = len - (p - str);
52
29
    if (s2 && l != 0) {
53
29
        r2 = strndup(p, l);
54
29
        if (!r2) {
55
0
            set_GSSERR(ENOMEM);
56
0
            goto done;
57
0
        }
58
29
    }
59
60
29
    set_GSSERRS(0, GSS_S_COMPLETE);
61
62
29
done:
63
29
    if (retmaj) {
64
0
        free(r1);
65
0
        free(r2);
66
29
    } else {
67
29
        if (s1) *s1 = r1;
68
29
        if (s2) *s2 = r2;
69
29
    }
70
29
    return GSSERR();
71
29
}
72
73
/* Form of names allowed in GSSNTLMSSP now:
74
 *
75
 * Standard Forms:
76
 *  foo
77
 *      USERNAME: foo
78
 *      DOMAIN: <null>
79
 *
80
 *  BAR\foo
81
 *      USERNAME: foo
82
 *      DOMAIN: BAR
83
 *
84
 *  foo@BAR
85
 *      USERNAME: foo
86
 *      DOMAIN: BAR
87
 *
88
 * Enterprise name forms:
89
 *  foo\@bar.example.com
90
 *      USERNAME: foo@bar.example.com
91
 *      DOMAIN: <null>
92
 *
93
 *  foo\@bar.example.com@BAR
94
 *      USERNAME: foo@bar.example.com
95
 *      DOMAIN: BAR
96
 *
97
 *  \foo@bar.example.com
98
 *      USERNAME: foo@bar.example.com
99
 *      DOMAIN: <null>
100
 *
101
 *  BAR\foo@bar.example.com
102
 *      USERNAME: foo@bar.example.com
103
 *      DOMAIN: BAR
104
 *
105
 *  BAR@dom\foo@bar.example.com
106
 *      USERNAME: foo@bar.example.com
107
 *      DOMAIN: BAR@dom
108
 *
109
 * Invalid forms:
110
 *  BAR@dom\@foo..
111
 *  DOM\foo\@bar
112
 *  foo@bar\@baz
113
 */
114
106
#define MAX_NAME_LEN 1024
115
static uint32_t parse_user_name(uint32_t *minor_status,
116
                                const char *str, size_t len,
117
                                char **domain, char **username)
118
106
{
119
106
    uint32_t retmaj;
120
106
    uint32_t retmin;
121
106
    char *at, *sep;
122
123
106
    if (len > MAX_NAME_LEN) {
124
0
        return GSSERRS(ERR_NAMETOOLONG, GSS_S_BAD_NAME);
125
0
    }
126
127
106
    *username = NULL;
128
106
    *domain = NULL;
129
130
    /* let's check if there are '@' or '\' signs */
131
106
    at = memchr(str, '@', len);
132
106
    sep = memchr(str, '\\', len);
133
134
    /* Check if enterprise name first */
135
106
    if (at && sep) {
136
        /* we may have an enterprise name here */
137
77
        char strbuf[len + 1];
138
77
        char *buf = strbuf;
139
140
        /* copy buf to manipulate it */
141
77
        memcpy(buf, str, len);
142
77
        buf[len] = '\0';
143
144
        /* adjust pointers relative to new buffer */
145
77
        sep = buf + (sep - str);
146
77
        at = buf + (at - str);
147
148
77
        if (sep > at) {
149
            /* domain name contains an '@' sign ... */
150
31
            if (*(sep + 1) == '@') {
151
                /* invalid case of XXX@YYY\@ZZZ*/
152
4
                set_GSSERR(EINVAL);
153
4
                goto done;
154
4
            }
155
46
        } else if (at - sep == 1) {
156
            /* it's just a '\@' escape */
157
            /* no leading domain */
158
17
            sep = NULL;
159
17
        }
160
161
73
        if (sep) {
162
            /* terminate and copy domain, even if empty */
163
            /* NOTE: this is important for the Windbind integration case
164
             * where we need to tell the machinery to *not* add the default
165
             * domain name, it happens when the domain is NULL. */
166
56
            *sep = '\0';
167
56
            *domain = strdup(buf);
168
56
            if (NULL == *domain) {
169
0
                set_GSSERR(ENOMEM);
170
0
                goto done;
171
0
            }
172
            /* point buf at username part */
173
56
            len = len - (sep - buf) - 1;
174
56
            buf = sep + 1;
175
56
        }
176
177
1.28k
        for (at = strchr(buf, '@'); at != NULL; at = strchr(at, '@')) {
178
1.22k
            if (*(at - 1) == '\\') {
179
225
                if (*domain) {
180
                    /* Invalid forms like DOM\foo\@bar or foo@bar\@baz */
181
8
                    free(*domain);
182
8
                    *domain = NULL;
183
8
                    set_GSSERR(EINVAL);
184
8
                    goto done;
185
8
                }
186
                /* remove escape, moving all including terminating '\0' */
187
217
                memmove(at - 1, at, len - (at - buf) + 1);
188
998
            } else if (!*domain) {
189
                /* an '@' without escape and no previous
190
                 * domain was split out.
191
                 * the rest of the string is the domain */
192
7
                *at = '\0';
193
7
                *domain = strdup(at + 1);
194
7
                if (NULL == *domain) {
195
0
                    set_GSSERR(ENOMEM);
196
0
                    goto done;
197
0
                }
198
                /* note we continue the loop to check if any invalid
199
                 * \@ escapes is found in the domain part */
200
7
            }
201
1.21k
            at += 1;
202
1.21k
        }
203
204
65
        *username = strdup(buf);
205
65
        if (NULL == *username) {
206
0
            set_GSSERR(ENOMEM);
207
0
            goto done;
208
0
        }
209
210
        /* we got an enterprise name, return */
211
65
        set_GSSERRS(0, GSS_S_COMPLETE);
212
65
        goto done;
213
65
    }
214
215
    /* Check if in classic DOMAIN\User windows format */
216
29
    if (sep) {
217
29
        retmaj = string_split(&retmin, '\\', str, len, domain, username);
218
29
        goto done;
219
29
    }
220
221
    /* else accept a user@domain format too */
222
0
    if (at) {
223
0
        retmaj = string_split(&retmin, '@', str, len, username, domain);
224
0
        goto done;
225
0
    }
226
227
    /* finally, take string as simple user name */
228
0
    *username = strndup(str, len);
229
0
    if (NULL == *username) {
230
0
        set_GSSERR(ENOMEM);
231
0
    }
232
0
    set_GSSERRS(0, GSS_S_COMPLETE);
233
234
106
done:
235
106
    return GSSERR();
236
0
}
237
238
static uint32_t uid_to_name(uint32_t *minor_status, uid_t uid, char **name)
239
0
{
240
0
    uint32_t retmaj;
241
0
    uint32_t retmin;
242
0
    struct passwd *pw;
243
244
0
    pw = getpwuid(uid);
245
0
    if (pw) {
246
0
        return GSSERRS(ERR_NOUSRFOUND, GSS_S_FAILURE);
247
0
    }
248
0
    *name = strdup(pw->pw_name);
249
0
    if (!*name) {
250
0
        set_GSSERR(ENOMEM);
251
0
        goto done;
252
0
    }
253
0
    set_GSSERRS(0, GSS_S_COMPLETE);
254
255
0
done:
256
0
    return GSSERR();
257
0
}
258
259
uint32_t gssntlm_import_name_by_mech(uint32_t *minor_status,
260
                                     gss_const_OID mech_type,
261
                                     gss_buffer_t input_name_buffer,
262
                                     gss_OID input_name_type,
263
                                     gss_name_t *output_name)
264
1.29k
{
265
1.29k
    struct gssntlm_name *name = NULL;
266
1.29k
    uint32_t retmaj;
267
1.29k
    uint32_t retmin;
268
269
    /* TODO: check mech_type == gssntlm_oid */
270
1.29k
    if (mech_type == GSS_C_NO_OID) {
271
0
        return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ);
272
0
    }
273
274
1.29k
    name = calloc(1, sizeof(struct gssntlm_name));
275
1.29k
    if (!name) {
276
0
        set_GSSERR(ENOMEM);
277
0
        goto done;
278
0
    }
279
280
    /* treat null OID like NT_USER_NAME */
281
1.29k
    if (input_name_type == GSS_C_NULL_OID) {
282
0
        input_name_type = GSS_C_NT_USER_NAME;
283
0
    }
284
285
1.29k
    if (gss_oid_equal(input_name_type, GSS_C_NT_HOSTBASED_SERVICE) ||
286
1.18k
        gss_oid_equal(input_name_type, GSS_C_NT_HOSTBASED_SERVICE_X)) {
287
1.18k
        char *spn = NULL;
288
1.18k
        char *p = NULL;
289
290
1.18k
        name->type = GSSNTLM_NAME_SERVER;
291
292
1.18k
        if (input_name_buffer->length > 0) {
293
0
            spn = strndup(input_name_buffer->value, input_name_buffer->length);
294
0
            if (!spn) {
295
0
                set_GSSERR(ENOMEM);
296
0
                goto done;
297
0
            }
298
0
            p = strchr(spn, '@');
299
0
            if (p && input_name_buffer->length == 1) {
300
0
                free(spn);
301
0
                spn = p = NULL;
302
0
            }
303
0
        }
304
305
1.18k
        if (p) {
306
            /* Windows expects a SPN not a GSS Name */
307
0
            if (p != spn) {
308
0
                *p = '/';
309
0
                name->data.server.spn = spn;
310
0
                spn = NULL;
311
0
            }
312
0
            p += 1;
313
0
            name->data.server.name = strdup(p);
314
0
            if (!name->data.server.name) {
315
0
                free(spn);
316
0
                set_GSSERR(ENOMEM);
317
0
                goto done;
318
0
            }
319
1.18k
        } else {
320
1.18k
            char hostname[HOST_NAME_MAX + 1] = { 0 };
321
1.18k
            size_t l, r;
322
            /* no seprator, assume only service is provided and try to
323
             * source the local host name */
324
1.18k
            retmin = gethostname(hostname, HOST_NAME_MAX);
325
1.18k
            if (retmin) {
326
0
                free(spn);
327
0
                set_GSSERR(retmin);
328
0
                goto done;
329
0
            }
330
1.18k
            hostname[HOST_NAME_MAX] = '\0';
331
1.18k
            if (spn != NULL) {
332
                /* spn = <service> + </> + <hostname> + <\0> */
333
0
                l = strlen(spn) + 1 + strlen(hostname) + 1;
334
0
                name->data.server.spn = malloc(l);
335
0
                if (!name->data.server.spn) {
336
0
                    free(spn);
337
0
                    set_GSSERR(ENOMEM);
338
0
                    goto done;
339
0
                }
340
0
                r = snprintf(name->data.server.spn, l, "%s/%s", spn, hostname);
341
0
                if (r != l - 1) {
342
0
                    free(spn);
343
0
                    set_GSSERR(ENOMEM);
344
0
                    goto done;
345
0
                }
346
0
            }
347
1.18k
            name->data.server.name = strdup(hostname);
348
1.18k
            if (!name->data.server.name) {
349
0
                free(spn);
350
0
                set_GSSERR(ENOMEM);
351
0
                goto done;
352
0
            }
353
1.18k
        }
354
1.18k
        free(spn);
355
1.18k
        set_GSSERRS(0, GSS_S_COMPLETE);
356
357
1.18k
    } else if (gss_oid_equal(input_name_type, GSS_C_NT_USER_NAME)) {
358
359
106
        name->type = GSSNTLM_NAME_USER;
360
106
        retmaj = parse_user_name(&retmin,
361
106
                                 input_name_buffer->value,
362
106
                                 input_name_buffer->length,
363
106
                                 &name->data.user.domain,
364
106
                                 &name->data.user.name);
365
106
    } else if (gss_oid_equal(input_name_type, GSS_C_NT_MACHINE_UID_NAME)) {
366
0
        uid_t uid;
367
368
0
        name->type = GSSNTLM_NAME_USER;
369
0
        name->data.user.domain = NULL;
370
371
0
        uid = *(uid_t *)input_name_buffer->value;
372
0
        retmaj = uid_to_name(&retmin, uid, &name->data.user.name);
373
0
    } else if (gss_oid_equal(input_name_type, GSS_C_NT_STRING_UID_NAME)) {
374
0
        char struid[12] = { 0 };
375
0
        uid_t uid;
376
377
0
        name->type = GSSNTLM_NAME_USER;
378
0
        name->data.user.domain = NULL;
379
380
0
        if (input_name_buffer->length > 12) {
381
0
            set_GSSERR(ERR_BADARG);
382
0
            goto done;
383
0
        }
384
0
        memcpy(struid, input_name_buffer->value, input_name_buffer->length);
385
0
        struid[11] = '\0';
386
0
        errno = 0;
387
0
        uid = strtol(struid, NULL, 10);
388
0
        if (errno) {
389
0
            set_GSSERR(ERR_BADARG);
390
0
            goto done;
391
0
        }
392
0
        retmaj = uid_to_name(&retmin, uid, &name->data.user.name);
393
0
    } else if (gss_oid_equal(input_name_type, GSS_C_NT_ANONYMOUS)) {
394
0
        name->type = GSSNTLM_NAME_ANON;
395
0
        set_GSSERRS(0, GSS_S_COMPLETE);
396
0
    } else if (gss_oid_equal(input_name_type, GSS_C_NT_EXPORT_NAME)) {
397
        /* TODO */
398
0
        set_GSSERRS(ERR_NOTSUPPORTED, GSS_S_BAD_NAMETYPE);
399
0
    } else {
400
0
        set_GSSERRS(ERR_BADARG, GSS_S_BAD_NAMETYPE);
401
0
    }
402
403
1.29k
done:
404
1.29k
    if (retmaj != GSS_S_COMPLETE) {
405
12
        uint32_t tmpmin;
406
12
        gssntlm_release_name(&tmpmin, (gss_name_t *)&name);
407
1.28k
    } else {
408
1.28k
        *output_name = (gss_name_t)name;
409
1.28k
    }
410
1.29k
    return GSSERR();
411
1.29k
}
412
413
uint32_t gssntlm_import_name(uint32_t *minor_status,
414
                             gss_buffer_t input_name_buffer,
415
                             gss_OID input_name_type,
416
                             gss_name_t *output_name)
417
106
{
418
106
    return gssntlm_import_name_by_mech(minor_status,
419
106
                                       discard_const(&gssntlm_oid),
420
106
                                       input_name_buffer,
421
106
                                       input_name_type,
422
106
                                       output_name);
423
106
}
424
425
size_t gssntlm_get_attrs_count(const struct gssntlm_name_attribute *attrs)
426
1.18k
{
427
1.18k
    size_t c;
428
1.18k
    for (c = 0; attrs && attrs[c].attr_name != NULL; c++) ;
429
1.18k
    return c;
430
1.18k
}
431
432
int gssntlm_copy_attrs(const struct gssntlm_name_attribute *src,
433
                       struct gssntlm_name_attribute **dst)
434
1.18k
{
435
1.18k
    struct gssntlm_name_attribute *copied_attrs;
436
1.18k
    size_t attrs_count = gssntlm_get_attrs_count(src);
437
438
1.18k
    *dst = NULL;
439
1.18k
    if (attrs_count == 0) {
440
1.18k
        return 0;
441
1.18k
    }
442
443
0
    copied_attrs = calloc(attrs_count + 1, /* +1 for terminator entry */
444
0
                          sizeof(struct gssntlm_name_attribute));
445
0
    if (copied_attrs == NULL) {
446
0
        return ENOMEM;
447
0
    }
448
449
0
    for (size_t i = 0; i < attrs_count; i++) {
450
0
        copied_attrs[i].attr_name = strdup(src[i].attr_name);
451
0
        if (copied_attrs[i].attr_name == NULL) {
452
0
            gssntlm_release_attrs(&copied_attrs);
453
0
            return ENOMEM;
454
0
        }
455
0
        copied_attrs[i].attr_value.length = src[i].attr_value.length;
456
0
        copied_attrs[i].attr_value.value = malloc(src[i].attr_value.length);
457
0
        if (copied_attrs[i].attr_value.value == NULL) {
458
0
            gssntlm_release_attrs(&copied_attrs);
459
0
            return ENOMEM;
460
0
        }
461
0
        memcpy(copied_attrs[i].attr_value.value, src[i].attr_value.value,
462
0
               src[i].attr_value.length);
463
0
    }
464
    /* terminator entry is filled with zeroes by calloc */
465
466
0
    *dst = copied_attrs;
467
0
    return 0;
468
0
}
469
470
struct gssntlm_name_attribute *gssntlm_find_attr(
471
                                        struct gssntlm_name_attribute *attrs,
472
                                        const char *attr_name,
473
                                        size_t attr_name_len)
474
0
{
475
0
    for (size_t i = 0; attrs && (attrs[i].attr_name != NULL); i++) {
476
        /* We store attr_name as a zero-terminated string, so
477
         * it is always zero-terminated */
478
0
        if (attr_name_len == strlen(attrs[i].attr_name) &&
479
0
            strncasecmp(attrs[i].attr_name, attr_name, attr_name_len) == 0) {
480
0
            return &attrs[i];
481
0
        }
482
0
    }
483
0
    return NULL;
484
0
}
485
486
void gssntlm_release_attrs(struct gssntlm_name_attribute **attrs)
487
2.48k
{
488
2.48k
    for (size_t i = 0; *attrs && (*attrs)[i].attr_name != NULL; i++) {
489
0
        free((*attrs)[i].attr_name);
490
0
        free((*attrs)[i].attr_value.value);
491
0
    }
492
2.48k
    safefree(*attrs);
493
2.48k
}
494
495
int gssntlm_copy_name(struct gssntlm_name *src, struct gssntlm_name *dst)
496
1.18k
{
497
1.18k
    char *dom = NULL, *usr = NULL, *spn = NULL, *srv = NULL;
498
1.18k
    int ret;
499
1.18k
    dst->type = src->type;
500
1.18k
    switch (src->type) {
501
0
    case GSSNTLM_NAME_NULL:
502
0
    case GSSNTLM_NAME_ANON:
503
0
        break;
504
0
    case GSSNTLM_NAME_USER:
505
0
        if (src->data.user.domain) {
506
0
            dom = strdup(src->data.user.domain);
507
0
            if (!dom) {
508
0
                ret = ENOMEM;
509
0
                goto done;
510
0
            }
511
0
        }
512
0
        if (src->data.user.name) {
513
0
            usr = strdup(src->data.user.name);
514
0
            if (!usr) {
515
0
                ret = ENOMEM;
516
0
                goto done;
517
0
            }
518
0
        }
519
0
        dst->data.user.domain = dom;
520
0
        dst->data.user.name = usr;
521
0
        break;
522
1.18k
    case GSSNTLM_NAME_SERVER:
523
1.18k
        if (src->data.server.spn) {
524
0
            spn = strdup(src->data.server.spn);
525
0
            if (!spn) {
526
0
                ret = ENOMEM;
527
0
                goto done;
528
0
            }
529
0
        }
530
1.18k
        dst->data.server.spn = spn;
531
1.18k
        if (src->data.server.name) {
532
1.18k
            srv = strdup(src->data.server.name);
533
1.18k
            if (!srv) {
534
0
                ret = ENOMEM;
535
0
                goto done;
536
0
            }
537
1.18k
        }
538
1.18k
        dst->data.server.name = srv;
539
1.18k
        break;
540
1.18k
    }
541
542
1.18k
    ret = gssntlm_copy_attrs(src->attrs, &dst->attrs);
543
1.18k
    if (ret) goto done;
544
545
1.18k
    ret = 0;
546
1.18k
done:
547
1.18k
    if (ret) {
548
0
        safefree(dom);
549
0
        safefree(usr);
550
0
        safefree(spn);
551
0
        safefree(srv);
552
0
    }
553
1.18k
    return ret;
554
1.18k
}
555
556
uint32_t gssntlm_duplicate_name(uint32_t *minor_status,
557
                                const gss_name_t input_name,
558
                                gss_name_t *dest_name)
559
0
{
560
0
    struct gssntlm_name *in;
561
0
    struct gssntlm_name *out;
562
0
    uint32_t retmin;
563
0
    uint32_t retmaj;
564
565
0
    if (input_name == GSS_C_NO_NAME || dest_name == NULL) {
566
0
        return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ);
567
0
    }
568
569
0
    in = (struct gssntlm_name *)input_name;
570
571
0
    if (in->type == GSSNTLM_NAME_NULL) {
572
0
        *dest_name = GSS_C_NO_NAME;
573
0
        return GSSERRS(0, GSS_S_COMPLETE);
574
0
    }
575
576
0
    out = calloc(1, sizeof(struct gssntlm_name));
577
0
    if (!out) {
578
0
        set_GSSERR(ENOMEM);
579
0
        goto done;
580
0
    }
581
582
0
    retmin = gssntlm_copy_name(in, out);
583
0
    if (retmin) {
584
0
        set_GSSERR(retmin);
585
0
        goto done;
586
0
    }
587
588
0
    set_GSSERRS(0, GSS_S_COMPLETE);
589
590
0
done:
591
0
    if (retmaj) {
592
0
        safefree(out);
593
0
    }
594
0
    *dest_name = (gss_name_t)out;
595
0
    return GSSERR();
596
0
}
597
598
void gssntlm_int_release_name(struct gssntlm_name *name)
599
7.96k
{
600
7.96k
    if (!name) return;
601
602
3.67k
    switch (name->type) {
603
1.18k
    case GSSNTLM_NAME_NULL:
604
1.18k
        return;
605
0
    case GSSNTLM_NAME_ANON:
606
0
        break;
607
106
    case GSSNTLM_NAME_USER:
608
106
        safefree(name->data.user.domain);
609
106
        safefree(name->data.user.name);
610
106
        break;
611
2.37k
    case GSSNTLM_NAME_SERVER:
612
2.37k
        safefree(name->data.server.spn);
613
2.37k
        safefree(name->data.server.name);
614
2.37k
        break;
615
3.67k
    }
616
2.48k
    gssntlm_release_attrs(&name->attrs);
617
2.48k
    name->type = GSSNTLM_NAME_NULL;
618
2.48k
}
619
620
uint32_t gssntlm_release_name(uint32_t *minor_status,
621
                              gss_name_t *input_name)
622
5.58k
{
623
5.58k
    uint32_t retmaj;
624
5.58k
    uint32_t retmin;
625
626
5.58k
    if (!input_name) {
627
0
        return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ);
628
0
    }
629
630
5.58k
    gssntlm_int_release_name((struct gssntlm_name *)*input_name);
631
632
5.58k
    safefree(*input_name);
633
5.58k
    return GSSERRS(0, GSS_S_COMPLETE);
634
5.58k
}
635
636
uint32_t gssntlm_display_name(uint32_t *minor_status,
637
                              gss_name_t input_name,
638
                              gss_buffer_t output_name_buffer,
639
                              gss_OID *output_name_type)
640
0
{
641
0
    struct gssntlm_name *in;
642
0
    gss_buffer_t out;
643
0
    uint32_t retmaj;
644
0
    uint32_t retmin;
645
0
    int ret;
646
647
0
    if (input_name == GSS_C_NO_NAME || output_name_buffer == NULL) {
648
0
        return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ);
649
0
    }
650
651
0
    in = (struct gssntlm_name *)input_name;
652
0
    out = output_name_buffer;
653
654
0
    switch (in->type) {
655
0
    case GSSNTLM_NAME_NULL:
656
0
        return GSSERRS(ERR_BADARG, GSS_S_BAD_NAME);
657
0
    case GSSNTLM_NAME_ANON:
658
0
        out->value = strdup("NT AUTHORITY\\ANONYMOUS LOGON");
659
0
        if (!out->value) {
660
0
            set_GSSERR(ENOMEM);
661
0
            goto done;
662
0
        }
663
0
        out->length = strlen(out->value) + 1;
664
0
        if (output_name_type) {
665
0
            *output_name_type = GSS_C_NT_ANONYMOUS;
666
0
        }
667
0
        break;
668
0
    case GSSNTLM_NAME_USER:
669
0
        if (in->data.user.domain) {
670
0
            ret = asprintf((char **)&out->value, "%s\\%s",
671
0
                           in->data.user.domain, in->data.user.name);
672
0
            if (ret == -1) {
673
0
                out->value = NULL;
674
0
            }
675
0
        } else {
676
0
            out->value = strdup(in->data.user.name);
677
0
        }
678
0
        if (!out->value) {
679
0
            set_GSSERR(ENOMEM);
680
0
            goto done;
681
0
        }
682
0
        out->length = strlen(out->value) + 1;
683
0
        if (output_name_type) {
684
0
            *output_name_type = GSS_C_NT_USER_NAME;
685
0
        }
686
0
        break;
687
0
    case GSSNTLM_NAME_SERVER:
688
0
        out->value = strdup(in->data.server.spn);
689
0
        if (!out->value) {
690
0
            set_GSSERR(ENOMEM);
691
0
            goto done;
692
0
        }
693
0
        out->length = strlen(out->value) + 1;
694
0
        if (output_name_type) {
695
0
            *output_name_type = GSS_C_NT_HOSTBASED_SERVICE;
696
0
        }
697
0
        break;
698
0
    }
699
700
0
    set_GSSERRS(0, GSS_S_COMPLETE);
701
702
0
done:
703
0
    return GSSERR();
704
0
}
705
706
0
#define PWBUFLEN 1024
707
708
uint32_t gssntlm_localname(uint32_t *minor_status,
709
                     const gss_name_t name,
710
                     gss_const_OID mech_type,
711
                     gss_buffer_t localname)
712
0
{
713
0
    struct gssntlm_name *in;
714
0
    char *uname = NULL;
715
0
    char pwbuf[PWBUFLEN];
716
0
    struct passwd pw, *res;
717
0
    uint32_t retmaj;
718
0
    uint32_t retmin;
719
0
    int ret;
720
721
0
    in = (struct gssntlm_name *)name;
722
0
    if (in->type != GSSNTLM_NAME_USER) {
723
0
        set_GSSERRS(ERR_BADARG, GSS_S_BAD_NAME);
724
0
        goto done;
725
0
    }
726
727
    /* TODO: hook up with winbindd/sssd for name resolution ? */
728
729
0
    if (in->data.user.domain) {
730
0
        ret = asprintf(&uname, "%s\\%s",
731
0
                       in->data.user.domain, in->data.user.name);
732
0
        if (ret == -1) {
733
0
            set_GSSERR(ENOMEM);
734
0
            goto done;
735
0
        }
736
0
        ret = getpwnam_r(uname, &pw, pwbuf, PWBUFLEN, &res);
737
0
        if (ret) {
738
0
            set_GSSERR(ret);
739
0
            goto done;
740
0
        }
741
0
        safefree(uname);
742
0
        if (res) {
743
0
            uname = strdup(res->pw_name);
744
0
        }
745
0
    }
746
0
    if (uname == NULL) {
747
0
        ret = getpwnam_r(in->data.user.name, &pw, pwbuf, PWBUFLEN, &res);
748
0
        if (ret != 0 || res == NULL) {
749
0
            set_GSSERR(ret);
750
0
            goto done;
751
0
        }
752
0
        uname = strdup(res->pw_name);
753
0
    }
754
0
    if (!uname) {
755
0
        set_GSSERR(ENOMEM);
756
0
        goto done;
757
0
    }
758
759
0
    set_GSSERRS(0, GSS_S_COMPLETE);
760
761
0
done:
762
0
    if (retmaj) {
763
0
        safefree(uname);
764
0
    } else {
765
0
        localname->value = uname;
766
0
        localname->length = strlen(uname) + 1;
767
0
    }
768
0
    return GSSERR();
769
0
}
770
771
uint32_t netbios_get_names(void *ctx, char *computer_name,
772
                           char **netbios_host, char **netbios_domain)
773
1.18k
{
774
1.18k
    char *nb_computer_name = NULL;
775
1.18k
    char *nb_domain_name = NULL;
776
1.18k
    char *env_name;
777
1.18k
    uint32_t ret;
778
779
1.18k
    env_name = getenv("NETBIOS_COMPUTER_NAME");
780
1.18k
    if (env_name) {
781
0
        nb_computer_name = strdup(env_name);
782
0
        if (!nb_computer_name) {
783
0
            ret = ENOMEM;
784
0
            goto done;
785
0
        }
786
0
    }
787
788
1.18k
    env_name = getenv("NETBIOS_DOMAIN_NAME");
789
1.18k
    if (env_name) {
790
0
        nb_domain_name = strdup(env_name);
791
0
        if (!nb_domain_name) {
792
0
            ret = ENOMEM;
793
0
            goto done;
794
0
        }
795
0
    }
796
797
1.18k
    if (!nb_computer_name || !nb_domain_name) {
798
        /* fetch only mising ones */
799
1.18k
        ret = external_netbios_get_names(ctx,
800
1.18k
                    nb_computer_name ? NULL : &nb_computer_name,
801
1.18k
                    nb_domain_name ? NULL : &nb_domain_name);
802
1.18k
        if ((ret != 0) &&
803
1.18k
            (ret != ENOENT) &&
804
1.18k
            (ret != ERR_NOTAVAIL)) {
805
0
            goto done;
806
0
        }
807
1.18k
    }
808
809
1.18k
    if (!nb_computer_name) {
810
1.18k
        char *p;
811
1.18k
        p = strchr(computer_name, '.');
812
1.18k
        if (p) {
813
0
            nb_computer_name = strndup(computer_name, p - computer_name);
814
1.18k
        } else {
815
1.18k
            nb_computer_name = strdup(computer_name);
816
1.18k
        }
817
15.4k
        for (p = nb_computer_name; p && *p; p++) {
818
            /* Can only be ASCII, so toupper is safe */
819
14.2k
            *p = toupper(*p);
820
14.2k
        }
821
1.18k
        if (!nb_computer_name) {
822
0
            ret = ENOMEM;
823
0
            goto done;
824
0
        }
825
1.18k
    }
826
827
1.18k
    if (!nb_domain_name) {
828
1.18k
        nb_domain_name = strdup(DEF_NB_DOMAIN);
829
1.18k
        if (!nb_domain_name) {
830
0
            ret = ENOMEM;
831
0
            goto done;
832
0
        }
833
1.18k
    }
834
835
1.18k
    ret = 0;
836
837
1.18k
done:
838
1.18k
    if (ret) {
839
0
        safefree(nb_computer_name);
840
0
        safefree(nb_domain_name);
841
0
    }
842
843
1.18k
    *netbios_domain = nb_domain_name;
844
1.18k
    *netbios_host = nb_computer_name;
845
1.18k
    return ret;
846
1.18k
}
847
848
uint32_t gssntlm_inquire_name(uint32_t *minor_status,
849
                              gss_name_t name,
850
                              int *name_is_MN,
851
                              gss_OID *MN_mech,
852
                              gss_buffer_set_t *attrs)
853
0
{
854
0
    uint32_t retmin = 0;
855
0
    uint32_t retmaj = 0;
856
0
    uint32_t tmpmin;
857
0
    const struct gssntlm_name *in = (const struct gssntlm_name *)name;
858
859
0
    if (!attrs) {
860
0
        return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE);
861
0
    }
862
0
    *attrs = GSS_C_NO_BUFFER_SET;
863
864
0
    if (name == GSS_C_NO_NAME) {
865
0
        return GSSERRS(GSS_S_BAD_NAME, GSS_S_CALL_INACCESSIBLE_READ);
866
0
    }
867
868
0
    for (size_t i = 0; in->attrs && in->attrs[i].attr_name != NULL; i++) {
869
0
        struct gssntlm_name_attribute *attr = &in->attrs[i];
870
0
        size_t attr_name_len = strlen(attr->attr_name);
871
0
        gss_buffer_desc buf;
872
0
        gss_buffer_t attr_value = &attr->attr_value;
873
        /* +1 for '=' separator and +1 for EOL */
874
0
        size_t full_string_len = attr_value->length + attr_name_len + 2;
875
0
        size_t offset = 0;
876
0
        char *attr_string = malloc(full_string_len);
877
0
        if (attr_string == NULL) {
878
0
            set_GSSERR(ENOMEM);
879
0
            goto done;
880
0
        }
881
882
        /* Construct 'attr_name=<attr_value>\0' string */
883
0
        memcpy(attr_string, attr->attr_name, attr_name_len);
884
0
        offset += attr_name_len;
885
886
0
        attr_string[offset++] = '=';
887
888
0
        memcpy(attr_string + offset, attr_value->value, attr_value->length);
889
0
        offset += attr_value->length;
890
891
0
        attr_string[offset] = 0;
892
893
        /* now add a buffer to output set */
894
0
        buf.length = full_string_len;
895
0
        buf.value = attr_string;
896
0
        retmaj = gss_add_buffer_set_member(&retmin, &buf, attrs);
897
0
        free(attr_string);
898
0
        if (retmaj != GSS_S_COMPLETE) goto done;
899
0
    }
900
901
0
done:
902
0
    if (retmaj) {
903
0
        (void)gss_release_buffer_set(&tmpmin, attrs);
904
0
    }
905
0
    return GSSERRS(retmin, retmaj);
906
0
}
907
908
/* RFC6680 - GSSAPI Naming Extensions */
909
uint32_t gssntlm_get_name_attribute(uint32_t *minor_status,
910
                                    gss_name_t name,
911
                                    gss_buffer_t attr,
912
                                    int *authenticated,
913
                                    int *complete,
914
                                    gss_buffer_t value,
915
                                    gss_buffer_t display_value,
916
                                    int *more)
917
0
{
918
0
    uint32_t retmin;
919
0
    uint32_t retmaj;
920
0
    const struct gssntlm_name *in = (const struct gssntlm_name *)name;
921
0
    struct gssntlm_name_attribute *found_attr;
922
923
0
    if (name == GSS_C_NO_NAME) {
924
0
        return GSSERRS(GSS_S_BAD_NAME, GSS_S_CALL_INACCESSIBLE_READ);
925
0
    }
926
0
    if (attr == NULL) {
927
0
        return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ);
928
0
    }
929
930
0
    if (display_value) {
931
0
        display_value->value = NULL;
932
0
        display_value->length = 0;
933
0
    }
934
0
    if (more) { *more = 0; }
935
0
    if (authenticated) { *authenticated = 0; }
936
0
    if (complete) { *complete = 0; }
937
938
0
    found_attr = gssntlm_find_attr(in->attrs, attr->value, attr->length);
939
0
    if (!found_attr) {
940
0
        return GSSERRS(ENOENT, GSS_S_UNAVAILABLE);
941
0
    }
942
943
0
    if (authenticated) { *authenticated = 1; }
944
0
    if (complete) { *complete = 1; }
945
0
    if (value) {
946
0
        gss_buffer_t attr_value = &found_attr->attr_value;
947
0
        value->value = malloc(attr_value->length);
948
0
        if (!value->value) {
949
0
            return GSSERRS(ENOMEM, GSS_S_FAILURE);
950
0
        }
951
0
        memcpy(value->value, attr_value->value, attr_value->length);
952
0
        value->length = attr_value->length;
953
0
    }
954
0
    return GSSERRS(0, GSS_S_COMPLETE);
955
0
}
956
957
/* RFC5801 Extensions */
958
959
0
#define GS2_NTLM_SASL_NAME        "GS2-NTLM"
960
0
#define GS2_NTLM_SASL_NAME_LEN    (sizeof(GS2_NTLM_SASL_NAME) - 1)
961
962
uint32_t gssntlm_inquire_saslname_for_mech(OM_uint32 *minor_status,
963
                                           const gss_OID desired_mech,
964
                                           gss_buffer_t sasl_mech_name,
965
                                           gss_buffer_t mech_name,
966
                                           gss_buffer_t mech_description)
967
0
{
968
0
    if (desired_mech && !gss_oid_equal(desired_mech, &gssntlm_oid)) {
969
0
        *minor_status = ENOENT;
970
0
        return GSS_S_BAD_MECH;
971
0
    }
972
973
0
    sasl_mech_name->value = NULL;
974
0
    mech_name->value = NULL;
975
0
    mech_description->value = NULL;
976
977
0
    *minor_status = ENOMEM;
978
979
0
    sasl_mech_name->value = strdup(GS2_NTLM_SASL_NAME);
980
0
    if (sasl_mech_name->value == NULL) {
981
0
        goto done;
982
0
    }
983
0
    sasl_mech_name->length = strlen(sasl_mech_name->value);
984
985
0
    mech_name->value = strdup("NTLM");
986
0
    if (mech_name->value == NULL) {
987
0
        goto done;
988
0
    }
989
0
    mech_name->length = strlen(mech_name->value);
990
991
0
    mech_description->value = strdup("NTLM Mechanism");
992
0
    if (mech_name->value == NULL) {
993
0
        goto done;
994
0
    }
995
0
    mech_description->length = strlen(mech_description->value);
996
997
0
    *minor_status = 0;
998
999
0
done:
1000
0
    if (*minor_status != 0) {
1001
0
        free(sasl_mech_name->value);
1002
0
        free(mech_name->value);
1003
0
        free(mech_description->value);
1004
0
        return GSS_S_FAILURE;
1005
0
    }
1006
1007
0
    return GSS_S_COMPLETE;
1008
0
}
1009
1010
uint32_t gssntlm_inquire_mech_for_saslname(OM_uint32 *minor_status,
1011
                                           const gss_buffer_t sasl_mech_name,
1012
                                           gss_OID *mech_type)
1013
0
{
1014
0
    if (sasl_mech_name->length == GS2_NTLM_SASL_NAME_LEN &&
1015
0
        memcmp(sasl_mech_name->value,
1016
0
               GS2_NTLM_SASL_NAME, GS2_NTLM_SASL_NAME_LEN) == 0) {
1017
0
        if (mech_type != NULL) {
1018
0
            *mech_type = discard_const(&gssntlm_oid);
1019
0
        }
1020
0
        *minor_status = 0;
1021
0
        return GSS_S_COMPLETE;
1022
0
    }
1023
1024
0
    *minor_status = ENOENT;
1025
0
    return GSS_S_BAD_MECH;
1026
1027
0
}
1028
1029
static uint32_t make_ma_oid_set(uint32_t *minor_status, gss_OID_set *ma_set,
1030
                                int supported)
1031
0
{
1032
0
    gss_const_OID known_mech_attrs[] = {
1033
0
        GSS_C_MA_MECH_CONCRETE,
1034
0
        GSS_C_MA_MECH_PSEUDO,
1035
0
        GSS_C_MA_MECH_COMPOSITE,
1036
0
        GSS_C_MA_MECH_NEGO,
1037
0
        GSS_C_MA_MECH_GLUE,
1038
0
        GSS_C_MA_NOT_MECH,
1039
0
        GSS_C_MA_DEPRECATED,
1040
0
        GSS_C_MA_NOT_DFLT_MECH,
1041
0
        GSS_C_MA_ITOK_FRAMED,
1042
0
        GSS_C_MA_AUTH_INIT,
1043
0
        GSS_C_MA_AUTH_TARG,
1044
0
        GSS_C_MA_AUTH_INIT_INIT,
1045
0
        GSS_C_MA_AUTH_TARG_INIT,
1046
0
        GSS_C_MA_AUTH_INIT_ANON,
1047
0
        GSS_C_MA_AUTH_TARG_ANON,
1048
0
        GSS_C_MA_DELEG_CRED,
1049
0
        GSS_C_MA_INTEG_PROT,
1050
0
        GSS_C_MA_CONF_PROT,
1051
0
        GSS_C_MA_MIC,
1052
0
        GSS_C_MA_WRAP,
1053
0
        GSS_C_MA_PROT_READY,
1054
0
        GSS_C_MA_REPLAY_DET,
1055
0
        GSS_C_MA_OOS_DET,
1056
0
        GSS_C_MA_CBINDINGS,
1057
0
        GSS_C_MA_PFS,
1058
0
        GSS_C_MA_COMPRESS,
1059
0
        GSS_C_MA_CTX_TRANS,
1060
0
        NULL
1061
0
    };
1062
0
    gss_const_OID supported_mech_attrs[] = {
1063
0
        GSS_C_MA_MECH_CONCRETE,
1064
0
        GSS_C_MA_AUTH_INIT,
1065
0
        GSS_C_MA_INTEG_PROT,
1066
0
        GSS_C_MA_CONF_PROT,
1067
0
        GSS_C_MA_MIC,
1068
0
        GSS_C_MA_WRAP,
1069
0
        GSS_C_MA_OOS_DET,
1070
0
        GSS_C_MA_CBINDINGS,
1071
0
        GSS_C_MA_CTX_TRANS,
1072
0
        NULL
1073
0
    };
1074
0
    uint32_t maj = 0;
1075
0
    uint32_t min = 0;
1076
0
    gss_const_OID *array = known_mech_attrs;
1077
1078
0
    if (supported) {
1079
0
        array = supported_mech_attrs;
1080
0
    }
1081
1082
0
    maj = gss_create_empty_oid_set(&min, ma_set);
1083
0
    if (maj != GSS_S_COMPLETE) {
1084
0
        goto done;
1085
0
    }
1086
0
    for (int i = 0; array[i] != NULL; i++) {
1087
0
        maj = gss_add_oid_set_member(&min, discard_const(array[i]), ma_set);
1088
0
        if (maj != GSS_S_COMPLETE) {
1089
0
            goto done;
1090
0
        }
1091
0
    }
1092
1093
0
done:
1094
0
    *minor_status = min;
1095
0
    return maj;
1096
0
}
1097
1098
uint32_t gssntlm_inquire_attrs_for_mech(uint32_t *minor_status,
1099
          gss_const_OID mech_oid,
1100
          gss_OID_set *mech_attrs,
1101
          gss_OID_set *known_mech_attrs)
1102
0
{
1103
0
    gss_OID_set s_ma = GSS_C_NULL_OID_SET;
1104
0
    gss_OID_set k_ma = GSS_C_NULL_OID_SET;
1105
0
    uint32_t maj = GSS_S_COMPLETE;
1106
0
    uint32_t min = 0;
1107
1108
0
    if (mech_oid && !gss_oid_equal(mech_oid, &gssntlm_oid)) {
1109
0
        *minor_status = ENOENT;
1110
0
        return GSS_S_BAD_MECH;
1111
0
    }
1112
1113
0
    if (mech_attrs != NULL) {
1114
0
        maj = make_ma_oid_set(&min, &s_ma, 1);
1115
0
        if (maj != GSS_S_COMPLETE) {
1116
0
            goto done;
1117
0
        }
1118
0
    }
1119
0
    if (known_mech_attrs != NULL) {
1120
0
        maj = make_ma_oid_set(&min, &k_ma, 0);
1121
0
        if (maj != GSS_S_COMPLETE) {
1122
0
            goto done;
1123
0
        }
1124
0
    }
1125
1126
0
done:
1127
0
    if (maj != GSS_S_COMPLETE) {
1128
0
        gss_release_oid_set(&min, &s_ma);
1129
0
        gss_release_oid_set(&min, &k_ma);
1130
0
    }
1131
0
    if (mech_attrs != NULL) {
1132
0
        *mech_attrs = s_ma;
1133
0
    }
1134
0
    if (known_mech_attrs != NULL) {
1135
0
        *known_mech_attrs = k_ma;
1136
0
    }
1137
1138
0
    *minor_status = min;
1139
0
    return maj;
1140
0
}