Coverage Report

Created: 2025-11-03 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/krb5/src/lib/gssapi/krb5/acquire_cred.c
Line
Count
Source
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/*
3
 * Copyright 2000, 2007-2010 by the Massachusetts Institute of Technology.
4
 * All Rights Reserved.
5
 *
6
 * Export of this software from the United States of America may
7
 *   require a specific license from the United States Government.
8
 *   It is the responsibility of any person or organization contemplating
9
 *   export to obtain such a license before exporting.
10
 *
11
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12
 * distribute this software and its documentation for any purpose and
13
 * without fee is hereby granted, provided that the above copyright
14
 * notice appear in all copies and that both that copyright notice and
15
 * this permission notice appear in supporting documentation, and that
16
 * the name of M.I.T. not be used in advertising or publicity pertaining
17
 * to distribution of the software without specific, written prior
18
 * permission.  Furthermore if you modify this software you must label
19
 * your software as modified software and not distribute it in such a
20
 * fashion that it might be confused with the original M.I.T. software.
21
 * M.I.T. makes no representations about the suitability of
22
 * this software for any purpose.  It is provided "as is" without express
23
 * or implied warranty.
24
 */
25
/*
26
 * Copyright 1993 by OpenVision Technologies, Inc.
27
 *
28
 * Permission to use, copy, modify, distribute, and sell this software
29
 * and its documentation for any purpose is hereby granted without fee,
30
 * provided that the above copyright notice appears in all copies and
31
 * that both that copyright notice and this permission notice appear in
32
 * supporting documentation, and that the name of OpenVision not be used
33
 * in advertising or publicity pertaining to distribution of the software
34
 * without specific, written prior permission. OpenVision makes no
35
 * representations about the suitability of this software for any
36
 * purpose.  It is provided "as is" without express or implied warranty.
37
 *
38
 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
39
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
40
 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
41
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
42
 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
43
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
44
 * PERFORMANCE OF THIS SOFTWARE.
45
 */
46
47
/*
48
 * Copyright (C) 1998 by the FundsXpress, INC.
49
 *
50
 * All rights reserved.
51
 *
52
 * Export of this software from the United States of America may require
53
 * a specific license from the United States Government.  It is the
54
 * responsibility of any person or organization contemplating export to
55
 * obtain such a license before exporting.
56
 *
57
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
58
 * distribute this software and its documentation for any purpose and
59
 * without fee is hereby granted, provided that the above copyright
60
 * notice appear in all copies and that both that copyright notice and
61
 * this permission notice appear in supporting documentation, and that
62
 * the name of FundsXpress. not be used in advertising or publicity pertaining
63
 * to distribution of the software without specific, written prior
64
 * permission.  FundsXpress makes no representations about the suitability of
65
 * this software for any purpose.  It is provided "as is" without express
66
 * or implied warranty.
67
 *
68
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
69
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
70
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
71
 */
72
73
#include "k5-int.h"
74
#include "gssapiP_krb5.h"
75
#ifdef HAVE_STRING_H
76
#include <string.h>
77
#else
78
#include <strings.h>
79
#endif
80
81
#ifdef USE_LEASH
82
#ifdef _WIN64
83
#define LEASH_DLL "leashw64.dll"
84
#else
85
#define LEASH_DLL "leashw32.dll"
86
#endif
87
static void (*pLeash_AcquireInitialTicketsIfNeeded)(krb5_context,krb5_principal,char*,int) = NULL;
88
static HANDLE hLeashDLL = INVALID_HANDLE_VALUE;
89
#endif
90
91
#ifndef LEAN_CLIENT
92
k5_mutex_t gssint_krb5_keytab_lock = K5_MUTEX_PARTIAL_INITIALIZER;
93
static char *krb5_gss_keytab = NULL;
94
95
/* Heimdal calls this gsskrb5_register_acceptor_identity. */
96
OM_uint32
97
gss_krb5int_register_acceptor_identity(OM_uint32 *minor_status,
98
                                       const gss_OID desired_mech,
99
                                       const gss_OID desired_object,
100
                                       gss_buffer_t value)
101
0
{
102
0
    char *new = NULL, *old;
103
0
    int err;
104
105
0
    err = gss_krb5int_initialize_library();
106
0
    if (err != 0)
107
0
        return GSS_S_FAILURE;
108
109
0
    if (value->value != NULL) {
110
0
        new = strdup((char *)value->value);
111
0
        if (new == NULL)
112
0
            return GSS_S_FAILURE;
113
0
    }
114
115
0
    k5_mutex_lock(&gssint_krb5_keytab_lock);
116
0
    old = krb5_gss_keytab;
117
0
    krb5_gss_keytab = new;
118
0
    k5_mutex_unlock(&gssint_krb5_keytab_lock);
119
0
    free(old);
120
0
    return GSS_S_COMPLETE;
121
0
}
122
123
/* Try to verify that keytab contains at least one entry for name.  Return 0 if
124
 * it does, KRB5_KT_NOTFOUND if it doesn't, or another error as appropriate. */
125
static krb5_error_code
126
check_keytab(krb5_context context, krb5_keytab kt, krb5_gss_name_t name,
127
             krb5_principal mprinc)
128
0
{
129
0
    krb5_error_code code;
130
0
    krb5_keytab_entry ent;
131
0
    char *princname;
132
133
0
    if (name->service == NULL) {
134
0
        code = krb5_kt_get_entry(context, kt, name->princ, 0, 0, &ent);
135
0
        if (code == 0)
136
0
            krb5_kt_free_entry(context, &ent);
137
0
        return code;
138
0
    }
139
140
    /* If we can't iterate through the keytab, skip this check. */
141
0
    if (kt->ops->start_seq_get == NULL)
142
0
        return 0;
143
144
    /* Scan the keytab for host-based entries matching mprinc. */
145
0
    code = k5_kt_have_match(context, kt, mprinc);
146
0
    if (code == KRB5_KT_NOTFOUND) {
147
0
        if (krb5_unparse_name(context, mprinc, &princname) == 0) {
148
0
            k5_setmsg(context, code, _("No key table entry found matching %s"),
149
0
                      princname);
150
0
            free(princname);
151
0
        }
152
0
    }
153
0
    return code;
154
0
}
155
156
/* get credentials corresponding to a key in the krb5 keytab.
157
   If successful, set the keytab-specific fields in cred
158
*/
159
160
static OM_uint32
161
acquire_accept_cred(krb5_context context, OM_uint32 *minor_status,
162
                    krb5_keytab req_keytab, const char *rcname,
163
                    krb5_gss_cred_id_rec *cred)
164
41
{
165
41
    OM_uint32 major;
166
41
    krb5_error_code code;
167
41
    krb5_keytab kt = NULL;
168
41
    krb5_rcache rc = NULL;
169
170
41
    assert(cred->keytab == NULL);
171
172
    /* If we have an explicit rcache name, open it. */
173
41
    if (rcname != NULL) {
174
0
        code = k5_rc_resolve(context, rcname, &rc);
175
0
        if (code) {
176
0
            major = GSS_S_FAILURE;
177
0
            goto cleanup;
178
0
        }
179
0
    }
180
181
41
    if (req_keytab != NULL) {
182
0
        code = krb5_kt_dup(context, req_keytab, &kt);
183
41
    } else {
184
41
        k5_mutex_lock(&gssint_krb5_keytab_lock);
185
41
        if (krb5_gss_keytab != NULL) {
186
0
            code = krb5_kt_resolve(context, krb5_gss_keytab, &kt);
187
0
            k5_mutex_unlock(&gssint_krb5_keytab_lock);
188
41
        } else {
189
41
            k5_mutex_unlock(&gssint_krb5_keytab_lock);
190
41
            code = krb5_kt_default(context, &kt);
191
41
        }
192
41
    }
193
41
    if (code) {
194
0
        major = GSS_S_NO_CRED;
195
0
        goto cleanup;
196
0
    }
197
198
41
    if (cred->name != NULL) {
199
0
        code = kg_acceptor_princ(context, cred->name, &cred->acceptor_mprinc);
200
0
        if (code) {
201
0
            major = GSS_S_FAILURE;
202
0
            goto cleanup;
203
0
        }
204
205
        /* Make sure we have keys matching the desired name in the keytab. */
206
0
        code = check_keytab(context, kt, cred->name, cred->acceptor_mprinc);
207
0
        if (code) {
208
0
            if (code == KRB5_KT_NOTFOUND) {
209
0
                k5_change_error_message_code(context, code, KG_KEYTAB_NOMATCH);
210
0
                code = KG_KEYTAB_NOMATCH;
211
0
            }
212
0
            major = GSS_S_NO_CRED;
213
0
            goto cleanup;
214
0
        }
215
216
0
        if (rc == NULL) {
217
            /* Open the replay cache for this principal. */
218
0
            code = krb5_get_server_rcache(context, &cred->name->princ->data[0],
219
0
                                          &rc);
220
0
            if (code) {
221
0
                major = GSS_S_FAILURE;
222
0
                goto cleanup;
223
0
            }
224
0
        }
225
41
    } else {
226
        /* Make sure we have a keytab with keys in it. */
227
41
        code = krb5_kt_have_content(context, kt);
228
41
        if (code) {
229
41
            major = GSS_S_NO_CRED;
230
41
            goto cleanup;
231
41
        }
232
41
    }
233
234
0
    cred->keytab = kt;
235
0
    kt = NULL;
236
0
    cred->rcache = rc;
237
0
    rc = NULL;
238
0
    major = GSS_S_COMPLETE;
239
240
41
cleanup:
241
41
    if (kt != NULL)
242
41
        krb5_kt_close(context, kt);
243
41
    if (rc != NULL)
244
0
        k5_rc_close(context, rc);
245
41
    *minor_status = code;
246
41
    return major;
247
0
}
248
#endif /* LEAN_CLIENT */
249
250
#ifdef USE_LEASH
251
static krb5_error_code
252
get_ccache_leash(krb5_context context, krb5_principal desired_princ,
253
                 krb5_ccache *ccache_out)
254
{
255
    krb5_error_code code;
256
    krb5_ccache ccache;
257
    char ccname[256] = "";
258
259
    *ccache_out = NULL;
260
261
    if (hLeashDLL == INVALID_HANDLE_VALUE) {
262
        hLeashDLL = LoadLibrary(LEASH_DLL);
263
        if (hLeashDLL != INVALID_HANDLE_VALUE) {
264
            (FARPROC) pLeash_AcquireInitialTicketsIfNeeded =
265
                GetProcAddress(hLeashDLL, "not_an_API_Leash_AcquireInitialTicketsIfNeeded");
266
        }
267
    }
268
269
    if (pLeash_AcquireInitialTicketsIfNeeded) {
270
        pLeash_AcquireInitialTicketsIfNeeded(context, desired_princ, ccname,
271
                                             sizeof(ccname));
272
        if (!ccname[0])
273
            return KRB5_CC_NOTFOUND;
274
275
        code = krb5_cc_resolve(context, ccname, &ccache);
276
        if (code)
277
            return code;
278
    } else {
279
        /* leash dll not available, open the default credential cache. */
280
        code = krb5int_cc_default(context, &ccache);
281
        if (code)
282
            return code;
283
    }
284
285
    *ccache_out = ccache;
286
    return 0;
287
}
288
#endif /* USE_LEASH */
289
290
/* Set fields in cred according to a ccache config entry whose key (in
291
 * principal form) is config_princ and whose value is value. */
292
static krb5_error_code
293
scan_cc_config(krb5_context context, krb5_gss_cred_id_rec *cred,
294
               krb5_const_principal config_princ, const krb5_data *value)
295
0
{
296
0
    krb5_error_code code;
297
0
    krb5_data data0 = empty_data();
298
299
0
    if (config_princ->length != 2)
300
0
        return 0;
301
0
    if (data_eq_string(config_princ->data[1], KRB5_CC_CONF_PROXY_IMPERSONATOR)
302
0
        && cred->impersonator == NULL) {
303
0
        code = krb5int_copy_data_contents_add0(context, value, &data0);
304
0
        if (code)
305
0
            return code;
306
0
        code = krb5_parse_name(context, data0.data, &cred->impersonator);
307
0
        krb5_free_data_contents(context, &data0);
308
0
        if (code)
309
0
            return code;
310
0
    } else if (data_eq_string(config_princ->data[1], KRB5_CC_CONF_REFRESH_TIME)
311
0
               && cred->refresh_time == 0) {
312
0
        code = krb5int_copy_data_contents_add0(context, value, &data0);
313
0
        if (code)
314
0
            return code;
315
0
        cred->refresh_time = atol(data0.data);
316
0
        krb5_free_data_contents(context, &data0);
317
0
    }
318
0
    return 0;
319
0
}
320
321
/* Return true if it appears that we can non-interactively get initial
322
 * tickets for cred. */
323
static krb5_boolean
324
can_get_initial_creds(krb5_context context, krb5_gss_cred_id_rec *cred)
325
0
{
326
0
    krb5_error_code code;
327
328
0
    if (cred->password != NULL)
329
0
        return TRUE;
330
331
0
    if (cred->client_keytab == NULL)
332
0
        return FALSE;
333
334
    /* If we don't know the client principal yet, check for any keytab keys. */
335
0
    if (cred->name == NULL)
336
0
        return !krb5_kt_have_content(context, cred->client_keytab);
337
338
    /*
339
     * Check if we have a keytab key for the client principal.  This is a bit
340
     * more permissive than we really want because krb5_kt_have_match()
341
     * supports wildcarding and obeys ignore_acceptor_hostname, but that should
342
     * generally be harmless.
343
     */
344
0
    code = k5_kt_have_match(context, cred->client_keytab, cred->name->princ);
345
0
    return code == 0;
346
0
}
347
348
/* Scan cred->ccache for name, expiry time, impersonator, refresh time.  If
349
 * check_name is true, verify the cache name against the credential name. */
350
static krb5_error_code
351
scan_ccache(krb5_context context, krb5_gss_cred_id_rec *cred,
352
            krb5_boolean check_name)
353
0
{
354
0
    krb5_error_code code;
355
0
    krb5_ccache ccache = cred->ccache;
356
0
    krb5_principal ccache_princ = NULL, tgt_princ = NULL;
357
0
    krb5_data *realm;
358
0
    krb5_cc_cursor cursor;
359
0
    krb5_creds creds;
360
0
    krb5_timestamp endtime;
361
0
    krb5_boolean is_tgt;
362
363
    /* Turn on NOTICKET, as we don't need session keys here. */
364
0
    code = krb5_cc_set_flags(context, ccache, KRB5_TC_NOTICKET);
365
0
    if (code)
366
0
        return code;
367
368
0
    code = krb5_cc_get_principal(context, ccache, &ccache_princ);
369
0
    if (code != 0)
370
0
        goto cleanup;
371
372
0
    if (cred->name == NULL) {
373
        /* Save the ccache principal as the credential name. */
374
0
        code = kg_init_name(context, ccache_princ, NULL, NULL, NULL,
375
0
                            KG_INIT_NAME_NO_COPY, &cred->name);
376
0
        if (code)
377
0
            goto cleanup;
378
0
        ccache_princ = NULL;
379
0
    } else {
380
        /* Check against the desired name if needed. */
381
0
        if (check_name) {
382
0
            if (!k5_sname_compare(context, cred->name->princ, ccache_princ)) {
383
0
                code = KG_CCACHE_NOMATCH;
384
0
                goto cleanup;
385
0
            }
386
0
        }
387
388
        /* Replace the credential name principal with the canonical client
389
         * principal, retaining acceptor_mprinc if set. */
390
0
        krb5_free_principal(context, cred->name->princ);
391
0
        cred->name->princ = ccache_princ;
392
0
        ccache_princ = NULL;
393
0
    }
394
395
0
    assert(cred->name->princ != NULL);
396
0
    realm = krb5_princ_realm(context, cred->name->princ);
397
0
    code = krb5_build_principal_ext(context, &tgt_princ,
398
0
                                    realm->length, realm->data,
399
0
                                    KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
400
0
                                    realm->length, realm->data,
401
0
                                    0);
402
0
    if (code)
403
0
        return code;
404
405
    /* If there's a tgt for the principal's local realm in here, use its expiry
406
     * time.  Otherwise use the first key. */
407
0
    code = krb5_cc_start_seq_get(context, ccache, &cursor);
408
0
    if (code) {
409
0
        krb5_free_principal(context, tgt_princ);
410
0
        return code;
411
0
    }
412
0
    while (!(code = krb5_cc_next_cred(context, ccache, &cursor, &creds))) {
413
0
        if (krb5_is_config_principal(context, creds.server)) {
414
0
            code = scan_cc_config(context, cred, creds.server, &creds.ticket);
415
0
            krb5_free_cred_contents(context, &creds);
416
0
            if (code)
417
0
                break;
418
0
            continue;
419
0
        }
420
0
        is_tgt = krb5_principal_compare(context, tgt_princ, creds.server);
421
0
        endtime = creds.times.endtime;
422
0
        krb5_free_cred_contents(context, &creds);
423
0
        if (is_tgt)
424
0
            cred->have_tgt = TRUE;
425
0
        if (is_tgt || cred->expire == 0)
426
0
            cred->expire = endtime;
427
0
    }
428
0
    krb5_cc_end_seq_get(context, ccache, &cursor);
429
0
    if (code && code != KRB5_CC_END)
430
0
        goto cleanup;
431
0
    code = 0;
432
433
0
    if (cred->expire == 0 && !can_get_initial_creds(context, cred)) {
434
0
        code = KG_EMPTY_CCACHE;
435
0
        goto cleanup;
436
0
    }
437
438
0
cleanup:
439
0
    (void)krb5_cc_set_flags(context, ccache, 0);
440
0
    krb5_free_principal(context, ccache_princ);
441
0
    krb5_free_principal(context, tgt_princ);
442
0
    return code;
443
0
}
444
445
/* Find an existing or destination ccache for cred->name. */
446
static krb5_error_code
447
get_cache_for_name(krb5_context context, krb5_gss_cred_id_rec *cred)
448
0
{
449
0
    krb5_error_code code;
450
0
    krb5_boolean can_get, have_collection;
451
0
    krb5_ccache defcc = NULL;
452
0
    krb5_principal princ = NULL;
453
0
    const char *cctype;
454
455
0
    assert(cred->name != NULL && cred->ccache == NULL);
456
#ifdef USE_LEASH
457
    code = get_ccache_leash(context, cred->name->princ, &cred->ccache);
458
    return code ? code : scan_ccache(context, cred, TRUE);
459
#else
460
    /* Check first whether we can acquire tickets, to avoid overwriting the
461
     * extended error message from krb5_cc_cache_match. */
462
0
    can_get = can_get_initial_creds(context, cred);
463
464
    /* Look for an existing cache for the client principal. */
465
0
    code = krb5_cc_cache_match(context, cred->name->princ, &cred->ccache);
466
0
    if (code == 0)
467
0
        return scan_ccache(context, cred, FALSE);
468
0
    if (code != KRB5_CC_NOTFOUND || !can_get)
469
0
        return code;
470
0
    krb5_clear_error_message(context);
471
472
    /* There is no existing ccache, but we can acquire credentials.  Get the
473
     * default ccache to help decide where we should put them. */
474
0
    code = krb5_cc_default(context, &defcc);
475
0
    if (code)
476
0
        return code;
477
0
    cctype = krb5_cc_get_type(context, defcc);
478
0
    have_collection = krb5_cc_support_switch(context, cctype);
479
480
    /* We can use an empty default ccache if we're using a password or if
481
     * there's no collection. */
482
0
    if (cred->password != NULL || !have_collection) {
483
0
        if (krb5_cc_get_principal(context, defcc, &princ) == KRB5_FCC_NOFILE) {
484
0
            cred->ccache = defcc;
485
0
            defcc = NULL;
486
0
        }
487
0
        krb5_clear_error_message(context);
488
0
    }
489
490
    /* Otherwise, try to use a new cache in the collection. */
491
0
    if (cred->ccache == NULL) {
492
0
        if (!have_collection) {
493
0
            code = KG_CCACHE_NOMATCH;
494
0
            goto cleanup;
495
0
        }
496
0
        code = krb5_cc_new_unique(context, cctype, NULL, &cred->ccache);
497
0
        if (code)
498
0
            goto cleanup;
499
0
    }
500
501
0
cleanup:
502
0
    krb5_free_principal(context, princ);
503
0
    if (defcc != NULL)
504
0
        krb5_cc_close(context, defcc);
505
0
    return code;
506
0
#endif /* not USE_LEASH */
507
0
}
508
509
/* Try to set cred->name using the client keytab. */
510
static krb5_error_code
511
get_name_from_client_keytab(krb5_context context, krb5_gss_cred_id_rec *cred)
512
0
{
513
0
    krb5_error_code code;
514
0
    krb5_principal princ;
515
516
0
    assert(cred->name == NULL);
517
518
0
    if (cred->client_keytab == NULL)
519
0
        return KRB5_KT_NOTFOUND;
520
521
0
    code = k5_kt_get_principal(context, cred->client_keytab, &princ);
522
0
    if (code)
523
0
        return code;
524
0
    code = kg_init_name(context, princ, NULL, NULL, NULL, KG_INIT_NAME_NO_COPY,
525
0
                        &cred->name);
526
0
    if (code) {
527
0
        krb5_free_principal(context, princ);
528
0
        return code;
529
0
    }
530
0
    return 0;
531
0
}
532
533
/* Make a note in ccache that we should attempt to refresh it from the client
534
 * keytab at refresh_time. */
535
static void
536
set_refresh_time(krb5_context context, krb5_ccache ccache,
537
                 krb5_timestamp refresh_time)
538
0
{
539
0
    char buf[128];
540
0
    krb5_data d;
541
542
0
    snprintf(buf, sizeof(buf), "%u", (unsigned int)ts2tt(refresh_time));
543
0
    d = string2data(buf);
544
0
    (void)krb5_cc_set_config(context, ccache, NULL, KRB5_CC_CONF_REFRESH_TIME,
545
0
                             &d);
546
0
    krb5_clear_error_message(context);
547
0
}
548
549
/* Return true if it's time to refresh cred from the client keytab. */
550
krb5_boolean
551
kg_cred_time_to_refresh(krb5_context context, krb5_gss_cred_id_rec *cred)
552
0
{
553
0
    krb5_timestamp now, soon;
554
555
0
    if (krb5_timeofday(context, &now))
556
0
        return FALSE;
557
0
    soon = ts_incr(now, 30);
558
559
    /* If a refresh time is set and has elapsed, attempt a refresh, and set a
560
     * new refresh time to avoid retrying for 30 seconds. */
561
0
    if (cred->refresh_time != 0 && !ts_after(cred->refresh_time, now)) {
562
0
        set_refresh_time(context, cred->ccache, soon);
563
0
        return TRUE;
564
0
    }
565
566
    /* If the creds will expire soon, attempt a refresh even if they weren't
567
     * acquired with a client keytab. */
568
0
    if (ts_after(soon, cred->expire))
569
0
        return TRUE;
570
571
0
    return FALSE;
572
0
}
573
574
/* If appropriate, make a note to refresh cred from the client keytab when it
575
 * is halfway to expired. */
576
void
577
kg_cred_set_initial_refresh(krb5_context context, krb5_gss_cred_id_rec *cred,
578
                            krb5_ticket_times *times)
579
0
{
580
0
    krb5_timestamp refresh;
581
582
    /* For now, we only mark keytab-acquired credentials for refresh. */
583
0
    if (cred->password != NULL)
584
0
        return;
585
586
    /* Make a note to refresh these when they are halfway to expired. */
587
0
    refresh = ts_incr(times->starttime,
588
0
                      ts_delta(times->endtime, times->starttime) / 2);
589
0
    set_refresh_time(context, cred->ccache, refresh);
590
0
}
591
592
struct verify_params {
593
    krb5_principal princ;
594
    krb5_keytab keytab;
595
};
596
597
static krb5_error_code
598
verify_initial_cred(krb5_context context, krb5_creds *creds,
599
                    const struct verify_params *verify)
600
0
{
601
0
    krb5_verify_init_creds_opt vopts;
602
603
0
    krb5_verify_init_creds_opt_init(&vopts);
604
0
    krb5_verify_init_creds_opt_set_ap_req_nofail(&vopts, TRUE);
605
0
    return krb5_verify_init_creds(context, creds, verify->princ,
606
0
                                  verify->keytab, NULL, &vopts);
607
0
}
608
609
/* Get initial credentials using the supplied password or client keytab. */
610
static krb5_error_code
611
get_initial_cred(krb5_context context, const struct verify_params *verify,
612
                 krb5_gss_cred_id_rec *cred)
613
0
{
614
0
    krb5_error_code code;
615
0
    krb5_get_init_creds_opt *opt = NULL;
616
0
    krb5_creds creds;
617
618
0
    code = krb5_get_init_creds_opt_alloc(context, &opt);
619
0
    if (code)
620
0
        return code;
621
0
    code = krb5_get_init_creds_opt_set_out_ccache(context, opt, cred->ccache);
622
0
    if (code)
623
0
        goto cleanup;
624
0
    if (cred->password != NULL) {
625
0
        code = krb5_get_init_creds_password(context, &creds, cred->name->princ,
626
0
                                            cred->password, NULL, NULL, 0,
627
0
                                            NULL, opt);
628
0
    } else if (cred->client_keytab != NULL) {
629
0
        code = krb5_get_init_creds_keytab(context, &creds, cred->name->princ,
630
0
                                          cred->client_keytab, 0, NULL, opt);
631
0
    } else {
632
0
        code = KRB5_KT_NOTFOUND;
633
0
    }
634
0
    if (code)
635
0
        goto cleanup;
636
0
    if (cred->password != NULL && verify != NULL) {
637
0
        code = verify_initial_cred(context, &creds, verify);
638
0
        if (code)
639
0
            goto cleanup;
640
0
    }
641
0
    kg_cred_set_initial_refresh(context, cred, &creds.times);
642
0
    cred->have_tgt = TRUE;
643
0
    cred->expire = creds.times.endtime;
644
645
    /* Steal the canonical client principal name from creds and save it in the
646
     * credential name, retaining acceptor_mprinc if set. */
647
0
    krb5_free_principal(context, cred->name->princ);
648
0
    cred->name->princ = creds.client;
649
0
    creds.client = NULL;
650
651
0
    krb5_free_cred_contents(context, &creds);
652
0
cleanup:
653
0
    krb5_get_init_creds_opt_free(context, opt);
654
0
    return code;
655
0
}
656
657
/* Get initial credentials if we ought to and are able to. */
658
static krb5_error_code
659
maybe_get_initial_cred(krb5_context context,
660
                       const struct verify_params *verify,
661
                       krb5_gss_cred_id_rec *cred)
662
0
{
663
0
    krb5_error_code code;
664
665
    /* Don't get creds if we don't know the name or are doing IAKERB. */
666
0
    if (cred->name == NULL || cred->iakerb_mech)
667
0
        return 0;
668
669
    /* Get creds if we have none or if it's time to refresh. */
670
0
    if (cred->expire == 0 || kg_cred_time_to_refresh(context, cred)) {
671
0
        code = get_initial_cred(context, verify, cred);
672
        /* If we were trying to refresh and failed, we can keep going. */
673
0
        if (code && cred->expire == 0)
674
0
            return code;
675
0
        krb5_clear_error_message(context);
676
0
    }
677
0
    return 0;
678
0
}
679
680
static OM_uint32
681
acquire_init_cred(krb5_context context, OM_uint32 *minor_status,
682
                  krb5_ccache req_ccache, gss_buffer_t password,
683
                  krb5_keytab client_keytab,
684
                  const struct verify_params *verify,
685
                  krb5_gss_cred_id_rec *cred)
686
0
{
687
0
    krb5_error_code code;
688
0
    krb5_data pwdata, pwcopy;
689
0
    int caller_ccname = 0;
690
691
    /* Get ccache from caller if available. */
692
0
    if (GSS_ERROR(kg_sync_ccache_name(context, minor_status)))
693
0
        return GSS_S_FAILURE;
694
0
    if (GSS_ERROR(kg_caller_provided_ccache_name(minor_status,
695
0
                                                 &caller_ccname)))
696
0
        return GSS_S_FAILURE;
697
698
0
    if (password != GSS_C_NO_BUFFER) {
699
0
        pwdata = make_data(password->value, password->length);
700
0
        code = krb5int_copy_data_contents_add0(context, &pwdata, &pwcopy);
701
0
        if (code)
702
0
            goto error;
703
0
        cred->password = pwcopy.data;
704
705
        /* We will fetch the credential into a private memory ccache. */
706
0
        assert(req_ccache == NULL);
707
0
        code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache);
708
0
        if (code)
709
0
            goto error;
710
0
        cred->destroy_ccache = 1;
711
0
    } else if (req_ccache != NULL) {
712
0
        code = krb5_cc_dup(context, req_ccache, &cred->ccache);
713
0
        if (code)
714
0
            goto error;
715
0
    } else if (caller_ccname) {
716
        /* Caller's ccache name has been set as the context default. */
717
0
        code = krb5int_cc_default(context, &cred->ccache);
718
0
        if (code)
719
0
            goto error;
720
0
    }
721
722
0
    if (client_keytab != NULL) {
723
0
        code = krb5_kt_dup(context, client_keytab, &cred->client_keytab);
724
0
    } else {
725
0
        code = krb5_kt_client_default(context, &cred->client_keytab);
726
0
        if (code) {
727
            /* Treat resolution failure similarly to a client keytab which
728
             * resolves but doesn't exist or has no content. */
729
0
            TRACE_GSS_CLIENT_KEYTAB_FAIL(context, code);
730
0
            krb5_clear_error_message(context);
731
0
            code = 0;
732
0
        }
733
0
    }
734
0
    if (code)
735
0
        goto error;
736
737
0
    if (cred->ccache != NULL) {
738
        /* The caller specified a ccache; check what's in it. */
739
0
        code = scan_ccache(context, cred, TRUE);
740
0
        if (code == KRB5_FCC_NOFILE) {
741
            /* See if we can get initial creds.  If the caller didn't specify
742
             * a name, pick one from the client keytab. */
743
0
            if (cred->name == NULL) {
744
0
                if (!get_name_from_client_keytab(context, cred))
745
0
                    code = 0;
746
0
            } else if (can_get_initial_creds(context, cred)) {
747
0
                code = 0;
748
0
            }
749
0
        }
750
0
        if (code)
751
0
            goto error;
752
0
    } else if (cred->name != NULL) {
753
        /* The caller specified a name but not a ccache; pick a cache. */
754
0
        code = get_cache_for_name(context, cred);
755
0
        if (code)
756
0
            goto error;
757
0
    }
758
759
0
#ifndef USE_LEASH
760
    /* If we haven't picked a name, make sure we have or can get any creds,
761
     * unless we're using Leash and might be able to get them interactively. */
762
0
    if (cred->name == NULL && !can_get_initial_creds(context, cred)) {
763
0
        code = krb5_cccol_have_content(context);
764
0
        if (code)
765
0
            goto error;
766
0
    }
767
0
#endif
768
769
0
    code = maybe_get_initial_cred(context, verify, cred);
770
0
    if (code)
771
0
        goto error;
772
773
0
    *minor_status = 0;
774
0
    return GSS_S_COMPLETE;
775
776
0
error:
777
0
    *minor_status = code;
778
0
    return GSS_S_NO_CRED;
779
0
}
780
781
static OM_uint32
782
acquire_cred_context(krb5_context context, OM_uint32 *minor_status,
783
                     gss_name_t desired_name, gss_buffer_t password,
784
                     OM_uint32 time_req, gss_cred_usage_t cred_usage,
785
                     krb5_ccache ccache, krb5_keytab client_keytab,
786
                     krb5_keytab keytab, const char *rcname,
787
                     const struct verify_params *verify,
788
                     krb5_boolean iakerb, gss_cred_id_t *output_cred_handle,
789
                     OM_uint32 *time_rec)
790
41
{
791
41
    krb5_gss_cred_id_t cred = NULL;
792
41
    krb5_gss_name_t name = (krb5_gss_name_t)desired_name;
793
41
    OM_uint32 ret;
794
41
    krb5_error_code code = 0;
795
796
    /* make sure all outputs are valid */
797
41
    *output_cred_handle = GSS_C_NO_CREDENTIAL;
798
41
    if (time_rec)
799
0
        *time_rec = 0;
800
801
    /* create the gss cred structure */
802
41
    cred = k5alloc(sizeof(krb5_gss_cred_id_rec), &code);
803
41
    if (cred == NULL)
804
0
        goto krb_error_out;
805
806
41
    cred->usage = cred_usage;
807
41
    cred->name = NULL;
808
41
    cred->impersonator = NULL;
809
41
    cred->iakerb_mech = iakerb;
810
41
    cred->default_identity = (name == NULL);
811
41
#ifndef LEAN_CLIENT
812
41
    cred->keytab = NULL;
813
41
#endif /* LEAN_CLIENT */
814
41
    cred->destroy_ccache = 0;
815
41
    cred->suppress_ci_flags = 0;
816
41
    cred->ccache = NULL;
817
818
41
    code = k5_mutex_init(&cred->lock);
819
41
    if (code)
820
0
        goto krb_error_out;
821
822
41
    switch (cred_usage) {
823
0
    case GSS_C_INITIATE:
824
41
    case GSS_C_ACCEPT:
825
41
    case GSS_C_BOTH:
826
41
        break;
827
0
    default:
828
0
        ret = GSS_S_FAILURE;
829
0
        *minor_status = (OM_uint32) G_BAD_USAGE;
830
0
        goto error_out;
831
41
    }
832
833
41
    if (name != NULL) {
834
0
        code = kg_duplicate_name(context, name, &cred->name);
835
0
        if (code)
836
0
            goto krb_error_out;
837
0
    }
838
839
41
#ifndef LEAN_CLIENT
840
    /*
841
     * If requested, acquire credentials for accepting. This will fill
842
     * in cred->name if desired_princ is specified.
843
     */
844
41
    if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
845
41
        ret = acquire_accept_cred(context, minor_status, keytab, rcname, cred);
846
41
        if (ret != GSS_S_COMPLETE)
847
41
            goto error_out;
848
41
    }
849
0
#endif /* LEAN_CLIENT */
850
851
    /*
852
     * If requested, acquire credentials for initiation. This will fill
853
     * in cred->name if it wasn't set above.
854
     */
855
0
    if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
856
0
        ret = acquire_init_cred(context, minor_status, ccache, password,
857
0
                                client_keytab, verify, cred);
858
0
        if (ret != GSS_S_COMPLETE)
859
0
            goto error_out;
860
0
    }
861
862
0
    assert(cred->default_identity || cred->name != NULL);
863
864
    /*** at this point, the cred structure has been completely created */
865
866
0
    if (cred_usage == GSS_C_ACCEPT) {
867
0
        if (time_rec)
868
0
            *time_rec = GSS_C_INDEFINITE;
869
0
    } else {
870
0
        krb5_timestamp now;
871
872
0
        code = krb5_timeofday(context, &now);
873
0
        if (code != 0)
874
0
            goto krb_error_out;
875
876
0
        if (time_rec) {
877
            /* Resolve cred now to determine the expiration time. */
878
0
            ret = kg_cred_resolve(minor_status, context, (gss_cred_id_t)cred,
879
0
                                  GSS_C_NO_NAME);
880
0
            if (GSS_ERROR(ret))
881
0
                goto error_out;
882
0
            *time_rec = ts_interval(now, cred->expire);
883
0
            k5_mutex_unlock(&cred->lock);
884
0
        }
885
0
    }
886
887
0
    *minor_status = 0;
888
0
    *output_cred_handle = (gss_cred_id_t) cred;
889
890
0
    return GSS_S_COMPLETE;
891
892
0
krb_error_out:
893
0
    *minor_status = code;
894
0
    ret = GSS_S_FAILURE;
895
896
41
error_out:
897
41
    if (cred != NULL) {
898
41
        if (cred->ccache) {
899
0
            if (cred->destroy_ccache)
900
0
                krb5_cc_destroy(context, cred->ccache);
901
0
            else
902
0
                krb5_cc_close(context, cred->ccache);
903
0
        }
904
41
        if (cred->client_keytab)
905
0
            krb5_kt_close(context, cred->client_keytab);
906
41
#ifndef LEAN_CLIENT
907
41
        if (cred->keytab)
908
0
            krb5_kt_close(context, cred->keytab);
909
41
#endif /* LEAN_CLIENT */
910
41
        if (cred->rcache)
911
0
            k5_rc_close(context, cred->rcache);
912
41
        if (cred->name)
913
0
            kg_release_name(context, &cred->name);
914
41
        krb5_free_principal(context, cred->impersonator);
915
41
        zapfreestr(cred->password);
916
41
        k5_mutex_destroy(&cred->lock);
917
41
        xfree(cred);
918
41
    }
919
41
    save_error_info(*minor_status, context);
920
41
    return ret;
921
0
}
922
923
static OM_uint32
924
acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
925
             gss_buffer_t password, OM_uint32 time_req,
926
             gss_cred_usage_t cred_usage, krb5_ccache ccache,
927
             krb5_keytab keytab, krb5_boolean iakerb,
928
             gss_cred_id_t *output_cred_handle, OM_uint32 *time_rec)
929
3
{
930
3
    krb5_context context = NULL;
931
3
    krb5_error_code code = 0;
932
3
    OM_uint32 ret;
933
934
3
    code = gss_krb5int_initialize_library();
935
3
    if (code) {
936
0
        *minor_status = code;
937
0
        ret = GSS_S_FAILURE;
938
0
        goto out;
939
0
    }
940
941
3
    code = krb5_gss_init_context(&context);
942
3
    if (code) {
943
0
        *minor_status = code;
944
0
        ret = GSS_S_FAILURE;
945
0
        goto out;
946
0
    }
947
948
3
    ret = acquire_cred_context(context, minor_status, desired_name, password,
949
3
                               time_req, cred_usage, ccache, NULL, keytab,
950
3
                               NULL, NULL, iakerb, output_cred_handle,
951
3
                               time_rec);
952
953
3
out:
954
3
    krb5_free_context(context);
955
3
    return ret;
956
3
}
957
958
/*
959
 * Resolve the name and ccache for an initiator credential if it has not yet
960
 * been done.  If specified, use the target name to pick an appropriate ccache
961
 * within the collection.  Validates cred_handle and leaves it locked on
962
 * success.
963
 */
964
OM_uint32
965
kg_cred_resolve(OM_uint32 *minor_status, krb5_context context,
966
                gss_cred_id_t cred_handle, gss_name_t target_name)
967
0
{
968
0
    OM_uint32 maj;
969
0
    krb5_error_code code;
970
0
    krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t)cred_handle;
971
0
    krb5_gss_name_t tname = (krb5_gss_name_t)target_name;
972
0
    krb5_principal client_princ;
973
974
0
    *minor_status = 0;
975
976
0
    maj = krb5_gss_validate_cred_1(minor_status, cred_handle, context);
977
0
    if (maj != 0)
978
0
        return maj;
979
0
    k5_mutex_assert_locked(&cred->lock);
980
981
0
    if (cred->usage == GSS_C_ACCEPT || cred->name != NULL)
982
0
        return GSS_S_COMPLETE;
983
    /* acquire_init_cred should have set both name and ccache, or neither. */
984
0
    assert(cred->ccache == NULL);
985
986
0
    if (tname != NULL) {
987
        /* Use the target name to select an existing ccache or a principal. */
988
0
        code = krb5_cc_select(context, tname->princ, &cred->ccache,
989
0
                              &client_princ);
990
0
        if (code && code != KRB5_CC_NOTFOUND)
991
0
            goto kerr;
992
0
        if (client_princ != NULL) {
993
0
            code = kg_init_name(context, client_princ, NULL, NULL, NULL,
994
0
                                KG_INIT_NAME_NO_COPY, &cred->name);
995
0
            if (code) {
996
0
                krb5_free_principal(context, client_princ);
997
0
                goto kerr;
998
0
            }
999
0
        }
1000
0
        if (cred->ccache != NULL) {
1001
0
            code = scan_ccache(context, cred, FALSE);
1002
0
            if (code)
1003
0
                goto kerr;
1004
0
        }
1005
0
    }
1006
1007
    /* If we still haven't picked a client principal, try using an existing
1008
     * default ccache.  (On Windows, this may acquire initial creds.) */
1009
0
    if (cred->name == NULL) {
1010
0
        code = krb5int_cc_default(context, &cred->ccache);
1011
0
        if (code)
1012
0
            goto kerr;
1013
0
        code = scan_ccache(context, cred, FALSE);
1014
0
        if (code == KRB5_FCC_NOFILE) {
1015
            /* Default ccache doesn't exist; fall through to client keytab. */
1016
0
            krb5_cc_close(context, cred->ccache);
1017
0
            cred->ccache = NULL;
1018
0
        } else if (code) {
1019
0
            goto kerr;
1020
0
        }
1021
0
    }
1022
1023
    /* If that didn't work, try getting a name from the client keytab. */
1024
0
    if (cred->name == NULL) {
1025
0
        code = get_name_from_client_keytab(context, cred);
1026
0
        if (code) {
1027
0
            code = KG_EMPTY_CCACHE;
1028
0
            goto kerr;
1029
0
        }
1030
0
    }
1031
1032
0
    if (cred->name != NULL && cred->ccache == NULL) {
1033
        /* Pick a cache for the name we chose (from krb5_cc_select or from the
1034
         * client keytab). */
1035
0
        code = get_cache_for_name(context, cred);
1036
0
        if (code)
1037
0
            goto kerr;
1038
0
    }
1039
1040
    /* Resolve name to ccache and possibly get initial credentials. */
1041
0
    code = maybe_get_initial_cred(context, NULL, cred);
1042
0
    if (code)
1043
0
        goto kerr;
1044
1045
0
    return GSS_S_COMPLETE;
1046
1047
0
kerr:
1048
0
    k5_mutex_unlock(&cred->lock);
1049
0
    save_error_info(code, context);
1050
0
    *minor_status = code;
1051
0
    return GSS_S_NO_CRED;
1052
0
}
1053
1054
OM_uint32
1055
gss_krb5int_set_cred_rcache(OM_uint32 *minor_status,
1056
                            gss_cred_id_t *cred_handle,
1057
                            const gss_OID desired_oid,
1058
                            const gss_buffer_t value)
1059
0
{
1060
0
    krb5_gss_cred_id_t cred;
1061
0
    krb5_error_code code;
1062
0
    krb5_context context;
1063
0
    krb5_rcache rcache;
1064
1065
0
    assert(value->length == sizeof(rcache));
1066
1067
0
    if (value->length != sizeof(rcache))
1068
0
        return GSS_S_FAILURE;
1069
1070
0
    rcache = (krb5_rcache)value->value;
1071
1072
0
    cred = (krb5_gss_cred_id_t)*cred_handle;
1073
1074
0
    code = krb5_gss_init_context(&context);
1075
0
    if (code) {
1076
0
        *minor_status = code;
1077
0
        return GSS_S_FAILURE;
1078
0
    }
1079
0
    if (cred->rcache != NULL)
1080
0
        k5_rc_close(context, cred->rcache);
1081
1082
0
    cred->rcache = rcache;
1083
1084
0
    krb5_free_context(context);
1085
1086
0
    *minor_status = 0;
1087
0
    return GSS_S_COMPLETE;
1088
0
}
1089
1090
/*
1091
 * krb5 and IAKERB mech API functions follow.  The mechglue always passes null
1092
 * desired_mechs and actual_mechs, so we ignore those parameters.
1093
 */
1094
1095
OM_uint32 KRB5_CALLCONV
1096
krb5_gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
1097
                      OM_uint32 time_req, gss_OID_set desired_mechs,
1098
                      gss_cred_usage_t cred_usage,
1099
                      gss_cred_id_t *output_cred_handle,
1100
                      gss_OID_set *actual_mechs, OM_uint32 *time_rec)
1101
3
{
1102
3
    return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage,
1103
3
                        NULL, NULL, FALSE, output_cred_handle, time_rec);
1104
3
}
1105
1106
OM_uint32 KRB5_CALLCONV
1107
iakerb_gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
1108
                        OM_uint32 time_req, gss_OID_set desired_mechs,
1109
                        gss_cred_usage_t cred_usage,
1110
                        gss_cred_id_t *output_cred_handle,
1111
                        gss_OID_set *actual_mechs, OM_uint32 *time_rec)
1112
0
{
1113
0
    return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage,
1114
0
                        NULL, NULL, TRUE, output_cred_handle, time_rec);
1115
0
}
1116
1117
OM_uint32 KRB5_CALLCONV
1118
krb5_gss_acquire_cred_with_password(OM_uint32 *minor_status,
1119
                                    const gss_name_t desired_name,
1120
                                    const gss_buffer_t password,
1121
                                    OM_uint32 time_req,
1122
                                    const gss_OID_set desired_mechs,
1123
                                    int cred_usage,
1124
                                    gss_cred_id_t *output_cred_handle,
1125
                                    gss_OID_set *actual_mechs,
1126
                                    OM_uint32 *time_rec)
1127
0
{
1128
0
    return acquire_cred(minor_status, desired_name, password, time_req,
1129
0
                        cred_usage, NULL, NULL, FALSE, output_cred_handle,
1130
0
                        time_rec);
1131
0
}
1132
1133
OM_uint32 KRB5_CALLCONV
1134
iakerb_gss_acquire_cred_with_password(OM_uint32 *minor_status,
1135
                                      const gss_name_t desired_name,
1136
                                      const gss_buffer_t password,
1137
                                      OM_uint32 time_req,
1138
                                      const gss_OID_set desired_mechs,
1139
                                      int cred_usage,
1140
                                      gss_cred_id_t *output_cred_handle,
1141
                                      gss_OID_set *actual_mechs,
1142
                                      OM_uint32 *time_rec)
1143
0
{
1144
0
    return acquire_cred(minor_status, desired_name, password, time_req,
1145
0
                        cred_usage, NULL, NULL, TRUE, output_cred_handle,
1146
0
                        time_rec);
1147
0
}
1148
1149
OM_uint32
1150
gss_krb5int_import_cred(OM_uint32 *minor_status,
1151
                        gss_cred_id_t *cred_handle,
1152
                        const gss_OID desired_oid,
1153
                        const gss_buffer_t value)
1154
0
{
1155
0
    struct krb5_gss_import_cred_req *req;
1156
0
    krb5_gss_name_rec name;
1157
0
    OM_uint32 time_rec;
1158
0
    krb5_error_code code;
1159
0
    gss_cred_usage_t usage;
1160
0
    gss_name_t desired_name = GSS_C_NO_NAME;
1161
1162
0
    assert(value->length == sizeof(*req));
1163
1164
0
    if (value->length != sizeof(*req))
1165
0
        return GSS_S_FAILURE;
1166
1167
0
    req = (struct krb5_gss_import_cred_req *)value->value;
1168
1169
0
    if (req->id != NULL) {
1170
0
        usage = (req->keytab != NULL) ? GSS_C_BOTH : GSS_C_INITIATE;
1171
0
    } else if (req->keytab != NULL) {
1172
0
        usage = GSS_C_ACCEPT;
1173
0
    } else {
1174
0
        *minor_status = EINVAL;
1175
0
        return GSS_S_FAILURE;
1176
0
    }
1177
1178
0
    if (req->keytab_principal != NULL) {
1179
0
        memset(&name, 0, sizeof(name));
1180
0
        code = k5_mutex_init(&name.lock);
1181
0
        if (code != 0) {
1182
0
            *minor_status = code;
1183
0
            return GSS_S_FAILURE;
1184
0
        }
1185
0
        name.princ = req->keytab_principal;
1186
0
        desired_name = (gss_name_t)&name;
1187
0
    }
1188
1189
0
    code = acquire_cred(minor_status, desired_name, NULL, GSS_C_INDEFINITE,
1190
0
                        usage, req->id, req->keytab, FALSE, cred_handle,
1191
0
                        &time_rec);
1192
0
    if (req->keytab_principal != NULL)
1193
0
        k5_mutex_destroy(&name.lock);
1194
0
    return code;
1195
0
}
1196
1197
static OM_uint32
1198
acquire_cred_from(OM_uint32 *minor_status, const gss_name_t desired_name,
1199
                  OM_uint32 time_req, const gss_OID_set desired_mechs,
1200
                  gss_cred_usage_t cred_usage,
1201
                  gss_const_key_value_set_t cred_store, krb5_boolean iakerb,
1202
                  gss_cred_id_t *output_cred_handle,
1203
                  gss_OID_set *actual_mechs, OM_uint32 *time_rec)
1204
38
{
1205
38
    krb5_context context = NULL;
1206
38
    krb5_error_code code = 0;
1207
38
    krb5_keytab client_keytab = NULL;
1208
38
    krb5_keytab keytab = NULL;
1209
38
    krb5_ccache ccache = NULL;
1210
38
    krb5_principal verify_princ = NULL;
1211
38
    const char *rcname, *value;
1212
38
    struct verify_params vparams = { NULL };
1213
38
    const struct verify_params *verify = NULL;
1214
38
    gss_buffer_desc pwbuf;
1215
38
    gss_buffer_t password = NULL;
1216
38
    OM_uint32 ret;
1217
1218
38
    code = gss_krb5int_initialize_library();
1219
38
    if (code) {
1220
0
        *minor_status = code;
1221
0
        ret = GSS_S_FAILURE;
1222
0
        goto out;
1223
0
    }
1224
1225
38
    code = krb5_gss_init_context(&context);
1226
38
    if (code) {
1227
0
        *minor_status = code;
1228
0
        ret = GSS_S_FAILURE;
1229
0
        goto out;
1230
0
    }
1231
1232
38
    ret = kg_value_from_cred_store(cred_store, KRB5_CS_CCACHE_URN, &value);
1233
38
    if (GSS_ERROR(ret))
1234
0
        goto out;
1235
1236
38
    if (value) {
1237
0
        code = krb5_cc_resolve(context, value, &ccache);
1238
0
        if (code != 0) {
1239
0
            *minor_status = code;
1240
0
            ret = GSS_S_NO_CRED;
1241
0
            goto out;
1242
0
        }
1243
0
    }
1244
1245
38
    ret = kg_value_from_cred_store(cred_store, KRB5_CS_CLI_KEYTAB_URN, &value);
1246
38
    if (GSS_ERROR(ret))
1247
0
        goto out;
1248
1249
38
    if (value) {
1250
0
        code = krb5_kt_resolve(context, value, &client_keytab);
1251
0
        if (code != 0) {
1252
0
            *minor_status = code;
1253
0
            ret = GSS_S_NO_CRED;
1254
0
            goto out;
1255
0
        }
1256
0
    }
1257
1258
38
    ret = kg_value_from_cred_store(cred_store, KRB5_CS_KEYTAB_URN, &value);
1259
38
    if (GSS_ERROR(ret))
1260
0
        goto out;
1261
1262
38
    if (value) {
1263
0
        code = krb5_kt_resolve(context, value, &keytab);
1264
0
        if (code != 0) {
1265
0
            *minor_status = code;
1266
0
            ret = GSS_S_NO_CRED;
1267
0
            goto out;
1268
0
        }
1269
0
    }
1270
1271
38
    ret = kg_value_from_cred_store(cred_store, KRB5_CS_RCACHE_URN, &rcname);
1272
38
    if (GSS_ERROR(ret))
1273
0
        goto out;
1274
1275
38
    ret = kg_value_from_cred_store(cred_store, KRB5_CS_PASSWORD_URN, &value);
1276
38
    if (GSS_ERROR(ret))
1277
0
        goto out;
1278
1279
38
    if (value) {
1280
        /* We must be acquiring an initiator cred with an explicit name.  A
1281
         * password is mutually exclusive with a client keytab or ccache. */
1282
0
        if (desired_name == GSS_C_NO_NAME) {
1283
0
            ret = GSS_S_BAD_NAME;
1284
0
            goto out;
1285
0
        }
1286
0
        if (cred_usage == GSS_C_ACCEPT || ccache != NULL ||
1287
0
            client_keytab != NULL) {
1288
0
            *minor_status = (OM_uint32)G_BAD_USAGE;
1289
0
            ret = GSS_S_FAILURE;
1290
0
            goto out;
1291
0
        }
1292
0
        pwbuf.length = strlen(value);
1293
0
        pwbuf.value = (void *)value;
1294
0
        password = &pwbuf;
1295
0
    }
1296
1297
38
    ret = kg_value_from_cred_store(cred_store, KRB5_CS_VERIFY_URN, &value);
1298
38
    if (GSS_ERROR(ret))
1299
0
        goto out;
1300
38
    if (value != NULL) {
1301
0
        if (iakerb || password == NULL) {
1302
            /* Only valid if acquiring cred with password, and not supported
1303
             * with IAKERB. */
1304
0
            *minor_status = G_BAD_USAGE;
1305
0
            ret = GSS_S_FAILURE;
1306
0
            goto out;
1307
0
        }
1308
0
        if (*value != '\0') {
1309
0
            code = krb5_parse_name(context, value, &verify_princ);
1310
0
            if (code != 0) {
1311
0
                *minor_status = code;
1312
0
                ret = GSS_S_FAILURE;
1313
0
                goto out;
1314
0
            }
1315
0
        }
1316
0
        vparams.princ = verify_princ;
1317
0
        vparams.keytab = keytab;
1318
0
        verify = &vparams;
1319
0
    }
1320
38
    ret = acquire_cred_context(context, minor_status, desired_name, password,
1321
38
                               time_req, cred_usage, ccache, client_keytab,
1322
38
                               keytab, rcname, verify, iakerb,
1323
38
                               output_cred_handle, time_rec);
1324
1325
38
out:
1326
38
    if (ccache != NULL)
1327
0
        krb5_cc_close(context, ccache);
1328
38
    if (client_keytab != NULL)
1329
0
        krb5_kt_close(context, client_keytab);
1330
38
    if (keytab != NULL)
1331
0
        krb5_kt_close(context, keytab);
1332
38
    krb5_free_principal(context, verify_princ);
1333
38
    krb5_free_context(context);
1334
38
    return ret;
1335
38
}
1336
1337
OM_uint32 KRB5_CALLCONV
1338
krb5_gss_acquire_cred_from(OM_uint32 *minor_status,
1339
                           const gss_name_t desired_name,
1340
                           OM_uint32 time_req,
1341
                           const gss_OID_set desired_mechs,
1342
                           gss_cred_usage_t cred_usage,
1343
                           gss_const_key_value_set_t cred_store,
1344
                           gss_cred_id_t *output_cred_handle,
1345
                           gss_OID_set *actual_mechs,
1346
                           OM_uint32 *time_rec)
1347
38
{
1348
38
    return acquire_cred_from(minor_status, desired_name, time_req,
1349
38
                             desired_mechs, cred_usage, cred_store,
1350
38
                             FALSE, output_cred_handle, actual_mechs,
1351
38
                             time_rec);
1352
38
}
1353
1354
OM_uint32 KRB5_CALLCONV
1355
iakerb_gss_acquire_cred_from(OM_uint32 *minor_status,
1356
                             const gss_name_t desired_name,
1357
                             OM_uint32 time_req,
1358
                             const gss_OID_set desired_mechs,
1359
                             gss_cred_usage_t cred_usage,
1360
                             gss_const_key_value_set_t cred_store,
1361
                             gss_cred_id_t *output_cred_handle,
1362
                             gss_OID_set *actual_mechs,
1363
                             OM_uint32 *time_rec)
1364
0
{
1365
0
    return acquire_cred_from(minor_status, desired_name, time_req,
1366
0
                             desired_mechs, cred_usage, cred_store,
1367
0
                             TRUE, output_cred_handle, actual_mechs,
1368
0
                             time_rec);
1369
0
}