xref: /freebsd/crypto/krb5/src/lib/gssapi/krb5/acquire_cred.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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
gss_krb5int_register_acceptor_identity(OM_uint32 * minor_status,const gss_OID desired_mech,const gss_OID desired_object,gss_buffer_t value)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 {
102     char *new = NULL, *old;
103     int err;
104 
105     err = gss_krb5int_initialize_library();
106     if (err != 0)
107         return GSS_S_FAILURE;
108 
109     if (value->value != NULL) {
110         new = strdup((char *)value->value);
111         if (new == NULL)
112             return GSS_S_FAILURE;
113     }
114 
115     k5_mutex_lock(&gssint_krb5_keytab_lock);
116     old = krb5_gss_keytab;
117     krb5_gss_keytab = new;
118     k5_mutex_unlock(&gssint_krb5_keytab_lock);
119     free(old);
120     return GSS_S_COMPLETE;
121 }
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
check_keytab(krb5_context context,krb5_keytab kt,krb5_gss_name_t name,krb5_principal mprinc)126 check_keytab(krb5_context context, krb5_keytab kt, krb5_gss_name_t name,
127              krb5_principal mprinc)
128 {
129     krb5_error_code code;
130     krb5_keytab_entry ent;
131     char *princname;
132 
133     if (name->service == NULL) {
134         code = krb5_kt_get_entry(context, kt, name->princ, 0, 0, &ent);
135         if (code == 0)
136             krb5_kt_free_entry(context, &ent);
137         return code;
138     }
139 
140     /* If we can't iterate through the keytab, skip this check. */
141     if (kt->ops->start_seq_get == NULL)
142         return 0;
143 
144     /* Scan the keytab for host-based entries matching mprinc. */
145     code = k5_kt_have_match(context, kt, mprinc);
146     if (code == KRB5_KT_NOTFOUND) {
147         if (krb5_unparse_name(context, mprinc, &princname) == 0) {
148             k5_setmsg(context, code, _("No key table entry found matching %s"),
149                       princname);
150             free(princname);
151         }
152     }
153     return code;
154 }
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
acquire_accept_cred(krb5_context context,OM_uint32 * minor_status,krb5_keytab req_keytab,const char * rcname,krb5_gss_cred_id_rec * cred)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 {
165     OM_uint32 major;
166     krb5_error_code code;
167     krb5_keytab kt = NULL;
168     krb5_rcache rc = NULL;
169 
170     assert(cred->keytab == NULL);
171 
172     /* If we have an explicit rcache name, open it. */
173     if (rcname != NULL) {
174         code = k5_rc_resolve(context, rcname, &rc);
175         if (code) {
176             major = GSS_S_FAILURE;
177             goto cleanup;
178         }
179     }
180 
181     if (req_keytab != NULL) {
182         code = krb5_kt_dup(context, req_keytab, &kt);
183     } else {
184         k5_mutex_lock(&gssint_krb5_keytab_lock);
185         if (krb5_gss_keytab != NULL) {
186             code = krb5_kt_resolve(context, krb5_gss_keytab, &kt);
187             k5_mutex_unlock(&gssint_krb5_keytab_lock);
188         } else {
189             k5_mutex_unlock(&gssint_krb5_keytab_lock);
190             code = krb5_kt_default(context, &kt);
191         }
192     }
193     if (code) {
194         major = GSS_S_NO_CRED;
195         goto cleanup;
196     }
197 
198     if (cred->name != NULL) {
199         code = kg_acceptor_princ(context, cred->name, &cred->acceptor_mprinc);
200         if (code) {
201             major = GSS_S_FAILURE;
202             goto cleanup;
203         }
204 
205         /* Make sure we have keys matching the desired name in the keytab. */
206         code = check_keytab(context, kt, cred->name, cred->acceptor_mprinc);
207         if (code) {
208             if (code == KRB5_KT_NOTFOUND) {
209                 k5_change_error_message_code(context, code, KG_KEYTAB_NOMATCH);
210                 code = KG_KEYTAB_NOMATCH;
211             }
212             major = GSS_S_NO_CRED;
213             goto cleanup;
214         }
215 
216         if (rc == NULL) {
217             /* Open the replay cache for this principal. */
218             code = krb5_get_server_rcache(context, &cred->name->princ->data[0],
219                                           &rc);
220             if (code) {
221                 major = GSS_S_FAILURE;
222                 goto cleanup;
223             }
224         }
225     } else {
226         /* Make sure we have a keytab with keys in it. */
227         code = krb5_kt_have_content(context, kt);
228         if (code) {
229             major = GSS_S_NO_CRED;
230             goto cleanup;
231         }
232     }
233 
234     cred->keytab = kt;
235     kt = NULL;
236     cred->rcache = rc;
237     rc = NULL;
238     major = GSS_S_COMPLETE;
239 
240 cleanup:
241     if (kt != NULL)
242         krb5_kt_close(context, kt);
243     if (rc != NULL)
244         k5_rc_close(context, rc);
245     *minor_status = code;
246     return major;
247 }
248 #endif /* LEAN_CLIENT */
249 
250 #ifdef USE_LEASH
251 static krb5_error_code
get_ccache_leash(krb5_context context,krb5_principal desired_princ,krb5_ccache * ccache_out)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
scan_cc_config(krb5_context context,krb5_gss_cred_id_rec * cred,krb5_const_principal config_princ,const krb5_data * value)293 scan_cc_config(krb5_context context, krb5_gss_cred_id_rec *cred,
294                krb5_const_principal config_princ, const krb5_data *value)
295 {
296     krb5_error_code code;
297     krb5_data data0 = empty_data();
298 
299     if (config_princ->length != 2)
300         return 0;
301     if (data_eq_string(config_princ->data[1], KRB5_CC_CONF_PROXY_IMPERSONATOR)
302         && cred->impersonator == NULL) {
303         code = krb5int_copy_data_contents_add0(context, value, &data0);
304         if (code)
305             return code;
306         code = krb5_parse_name(context, data0.data, &cred->impersonator);
307         krb5_free_data_contents(context, &data0);
308         if (code)
309             return code;
310     } else if (data_eq_string(config_princ->data[1], KRB5_CC_CONF_REFRESH_TIME)
311                && cred->refresh_time == 0) {
312         code = krb5int_copy_data_contents_add0(context, value, &data0);
313         if (code)
314             return code;
315         cred->refresh_time = atol(data0.data);
316         krb5_free_data_contents(context, &data0);
317     }
318     return 0;
319 }
320 
321 /* Return true if it appears that we can non-interactively get initial
322  * tickets for cred. */
323 static krb5_boolean
can_get_initial_creds(krb5_context context,krb5_gss_cred_id_rec * cred)324 can_get_initial_creds(krb5_context context, krb5_gss_cred_id_rec *cred)
325 {
326     krb5_error_code code;
327 
328     if (cred->password != NULL)
329         return TRUE;
330 
331     if (cred->client_keytab == NULL)
332         return FALSE;
333 
334     /* If we don't know the client principal yet, check for any keytab keys. */
335     if (cred->name == NULL)
336         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     code = k5_kt_have_match(context, cred->client_keytab, cred->name->princ);
345     return code == 0;
346 }
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
scan_ccache(krb5_context context,krb5_gss_cred_id_rec * cred,krb5_boolean check_name)351 scan_ccache(krb5_context context, krb5_gss_cred_id_rec *cred,
352             krb5_boolean check_name)
353 {
354     krb5_error_code code;
355     krb5_ccache ccache = cred->ccache;
356     krb5_principal ccache_princ = NULL, tgt_princ = NULL;
357     krb5_data *realm;
358     krb5_cc_cursor cursor;
359     krb5_creds creds;
360     krb5_timestamp endtime;
361     krb5_boolean is_tgt;
362 
363     /* Turn on NOTICKET, as we don't need session keys here. */
364     code = krb5_cc_set_flags(context, ccache, KRB5_TC_NOTICKET);
365     if (code)
366         return code;
367 
368     code = krb5_cc_get_principal(context, ccache, &ccache_princ);
369     if (code != 0)
370         goto cleanup;
371 
372     if (cred->name == NULL) {
373         /* Save the ccache principal as the credential name. */
374         code = kg_init_name(context, ccache_princ, NULL, NULL, NULL,
375                             KG_INIT_NAME_NO_COPY, &cred->name);
376         if (code)
377             goto cleanup;
378         ccache_princ = NULL;
379     } else {
380         /* Check against the desired name if needed. */
381         if (check_name) {
382             if (!k5_sname_compare(context, cred->name->princ, ccache_princ)) {
383                 code = KG_CCACHE_NOMATCH;
384                 goto cleanup;
385             }
386         }
387 
388         /* Replace the credential name principal with the canonical client
389          * principal, retaining acceptor_mprinc if set. */
390         krb5_free_principal(context, cred->name->princ);
391         cred->name->princ = ccache_princ;
392         ccache_princ = NULL;
393     }
394 
395     assert(cred->name->princ != NULL);
396     realm = krb5_princ_realm(context, cred->name->princ);
397     code = krb5_build_principal_ext(context, &tgt_princ,
398                                     realm->length, realm->data,
399                                     KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
400                                     realm->length, realm->data,
401                                     0);
402     if (code)
403         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     code = krb5_cc_start_seq_get(context, ccache, &cursor);
408     if (code) {
409         krb5_free_principal(context, tgt_princ);
410         return code;
411     }
412     while (!(code = krb5_cc_next_cred(context, ccache, &cursor, &creds))) {
413         if (krb5_is_config_principal(context, creds.server)) {
414             code = scan_cc_config(context, cred, creds.server, &creds.ticket);
415             krb5_free_cred_contents(context, &creds);
416             if (code)
417                 break;
418             continue;
419         }
420         is_tgt = krb5_principal_compare(context, tgt_princ, creds.server);
421         endtime = creds.times.endtime;
422         krb5_free_cred_contents(context, &creds);
423         if (is_tgt)
424             cred->have_tgt = TRUE;
425         if (is_tgt || cred->expire == 0)
426             cred->expire = endtime;
427     }
428     krb5_cc_end_seq_get(context, ccache, &cursor);
429     if (code && code != KRB5_CC_END)
430         goto cleanup;
431     code = 0;
432 
433     if (cred->expire == 0 && !can_get_initial_creds(context, cred)) {
434         code = KG_EMPTY_CCACHE;
435         goto cleanup;
436     }
437 
438 cleanup:
439     (void)krb5_cc_set_flags(context, ccache, 0);
440     krb5_free_principal(context, ccache_princ);
441     krb5_free_principal(context, tgt_princ);
442     return code;
443 }
444 
445 /* Find an existing or destination ccache for cred->name. */
446 static krb5_error_code
get_cache_for_name(krb5_context context,krb5_gss_cred_id_rec * cred)447 get_cache_for_name(krb5_context context, krb5_gss_cred_id_rec *cred)
448 {
449     krb5_error_code code;
450     krb5_boolean can_get, have_collection;
451     krb5_ccache defcc = NULL;
452     krb5_principal princ = NULL;
453     const char *cctype;
454 
455     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     can_get = can_get_initial_creds(context, cred);
463 
464     /* Look for an existing cache for the client principal. */
465     code = krb5_cc_cache_match(context, cred->name->princ, &cred->ccache);
466     if (code == 0)
467         return scan_ccache(context, cred, FALSE);
468     if (code != KRB5_CC_NOTFOUND || !can_get)
469         return code;
470     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     code = krb5_cc_default(context, &defcc);
475     if (code)
476         return code;
477     cctype = krb5_cc_get_type(context, defcc);
478     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     if (cred->password != NULL || !have_collection) {
483         if (krb5_cc_get_principal(context, defcc, &princ) == KRB5_FCC_NOFILE) {
484             cred->ccache = defcc;
485             defcc = NULL;
486         }
487         krb5_clear_error_message(context);
488     }
489 
490     /* Otherwise, try to use a new cache in the collection. */
491     if (cred->ccache == NULL) {
492         if (!have_collection) {
493             code = KG_CCACHE_NOMATCH;
494             goto cleanup;
495         }
496         code = krb5_cc_new_unique(context, cctype, NULL, &cred->ccache);
497         if (code)
498             goto cleanup;
499     }
500 
501 cleanup:
502     krb5_free_principal(context, princ);
503     if (defcc != NULL)
504         krb5_cc_close(context, defcc);
505     return code;
506 #endif /* not USE_LEASH */
507 }
508 
509 /* Try to set cred->name using the client keytab. */
510 static krb5_error_code
get_name_from_client_keytab(krb5_context context,krb5_gss_cred_id_rec * cred)511 get_name_from_client_keytab(krb5_context context, krb5_gss_cred_id_rec *cred)
512 {
513     krb5_error_code code;
514     krb5_principal princ;
515 
516     assert(cred->name == NULL);
517 
518     if (cred->client_keytab == NULL)
519         return KRB5_KT_NOTFOUND;
520 
521     code = k5_kt_get_principal(context, cred->client_keytab, &princ);
522     if (code)
523         return code;
524     code = kg_init_name(context, princ, NULL, NULL, NULL, KG_INIT_NAME_NO_COPY,
525                         &cred->name);
526     if (code) {
527         krb5_free_principal(context, princ);
528         return code;
529     }
530     return 0;
531 }
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
set_refresh_time(krb5_context context,krb5_ccache ccache,krb5_timestamp refresh_time)536 set_refresh_time(krb5_context context, krb5_ccache ccache,
537                  krb5_timestamp refresh_time)
538 {
539     char buf[128];
540     krb5_data d;
541 
542     snprintf(buf, sizeof(buf), "%u", (unsigned int)ts2tt(refresh_time));
543     d = string2data(buf);
544     (void)krb5_cc_set_config(context, ccache, NULL, KRB5_CC_CONF_REFRESH_TIME,
545                              &d);
546     krb5_clear_error_message(context);
547 }
548 
549 /* Return true if it's time to refresh cred from the client keytab.  If
550  * returning true, avoid retrying for 30 seconds. */
551 krb5_boolean
kg_cred_time_to_refresh(krb5_context context,krb5_gss_cred_id_rec * cred)552 kg_cred_time_to_refresh(krb5_context context, krb5_gss_cred_id_rec *cred)
553 {
554     krb5_timestamp now, soon;
555 
556     if (krb5_timeofday(context, &now))
557         return FALSE;
558     soon = ts_incr(now, 30);
559     if (cred->refresh_time != 0 && !ts_after(cred->refresh_time, now)) {
560         set_refresh_time(context, cred->ccache, soon);
561         return TRUE;
562     }
563 
564     /* If the creds will expire soon, try to refresh even if they weren't
565      * acquired with a client keytab. */
566     if (ts_after(soon, cred->expire)) {
567         set_refresh_time(context, cred->ccache, soon);
568         return TRUE;
569     }
570 
571     return FALSE;
572 }
573 
574 /* If appropriate, make a note to refresh cred from the client keytab when it
575  * is halfway to expired. */
576 void
kg_cred_set_initial_refresh(krb5_context context,krb5_gss_cred_id_rec * cred,krb5_ticket_times * times)577 kg_cred_set_initial_refresh(krb5_context context, krb5_gss_cred_id_rec *cred,
578                             krb5_ticket_times *times)
579 {
580     krb5_timestamp refresh;
581 
582     /* For now, we only mark keytab-acquired credentials for refresh. */
583     if (cred->password != NULL)
584         return;
585 
586     /* Make a note to refresh these when they are halfway to expired. */
587     refresh = ts_incr(times->starttime,
588                       ts_delta(times->endtime, times->starttime) / 2);
589     set_refresh_time(context, cred->ccache, refresh);
590 }
591 
592 struct verify_params {
593     krb5_principal princ;
594     krb5_keytab keytab;
595 };
596 
597 static krb5_error_code
verify_initial_cred(krb5_context context,krb5_creds * creds,const struct verify_params * verify)598 verify_initial_cred(krb5_context context, krb5_creds *creds,
599                     const struct verify_params *verify)
600 {
601     krb5_verify_init_creds_opt vopts;
602 
603     krb5_verify_init_creds_opt_init(&vopts);
604     krb5_verify_init_creds_opt_set_ap_req_nofail(&vopts, TRUE);
605     return krb5_verify_init_creds(context, creds, verify->princ,
606                                   verify->keytab, NULL, &vopts);
607 }
608 
609 /* Get initial credentials using the supplied password or client keytab. */
610 static krb5_error_code
get_initial_cred(krb5_context context,const struct verify_params * verify,krb5_gss_cred_id_rec * cred)611 get_initial_cred(krb5_context context, const struct verify_params *verify,
612                  krb5_gss_cred_id_rec *cred)
613 {
614     krb5_error_code code;
615     krb5_get_init_creds_opt *opt = NULL;
616     krb5_creds creds;
617 
618     code = krb5_get_init_creds_opt_alloc(context, &opt);
619     if (code)
620         return code;
621     code = krb5_get_init_creds_opt_set_out_ccache(context, opt, cred->ccache);
622     if (code)
623         goto cleanup;
624     if (cred->password != NULL) {
625         code = krb5_get_init_creds_password(context, &creds, cred->name->princ,
626                                             cred->password, NULL, NULL, 0,
627                                             NULL, opt);
628     } else if (cred->client_keytab != NULL) {
629         code = krb5_get_init_creds_keytab(context, &creds, cred->name->princ,
630                                           cred->client_keytab, 0, NULL, opt);
631     } else {
632         code = KRB5_KT_NOTFOUND;
633     }
634     if (code)
635         goto cleanup;
636     if (cred->password != NULL && verify != NULL) {
637         code = verify_initial_cred(context, &creds, verify);
638         if (code)
639             goto cleanup;
640     }
641     kg_cred_set_initial_refresh(context, cred, &creds.times);
642     cred->have_tgt = TRUE;
643     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     krb5_free_principal(context, cred->name->princ);
648     cred->name->princ = creds.client;
649     creds.client = NULL;
650 
651     krb5_free_cred_contents(context, &creds);
652 cleanup:
653     krb5_get_init_creds_opt_free(context, opt);
654     return code;
655 }
656 
657 /* Get initial credentials if we ought to and are able to. */
658 static krb5_error_code
maybe_get_initial_cred(krb5_context context,const struct verify_params * verify,krb5_gss_cred_id_rec * cred)659 maybe_get_initial_cred(krb5_context context,
660                        const struct verify_params *verify,
661                        krb5_gss_cred_id_rec *cred)
662 {
663     krb5_error_code code;
664 
665     /* Don't get creds if we don't know the name or are doing IAKERB. */
666     if (cred->name == NULL || cred->iakerb_mech)
667         return 0;
668 
669     /* Get creds if we have none or if it's time to refresh. */
670     if (cred->expire == 0 || kg_cred_time_to_refresh(context, cred)) {
671         code = get_initial_cred(context, verify, cred);
672         /* If we were trying to refresh and failed, we can keep going. */
673         if (code && cred->expire == 0)
674             return code;
675         krb5_clear_error_message(context);
676     }
677     return 0;
678 }
679 
680 static OM_uint32
acquire_init_cred(krb5_context context,OM_uint32 * minor_status,krb5_ccache req_ccache,gss_buffer_t password,krb5_keytab client_keytab,const struct verify_params * verify,krb5_gss_cred_id_rec * cred)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 {
687     krb5_error_code code;
688     krb5_data pwdata, pwcopy;
689     int caller_ccname = 0;
690 
691     /* Get ccache from caller if available. */
692     if (GSS_ERROR(kg_sync_ccache_name(context, minor_status)))
693         return GSS_S_FAILURE;
694     if (GSS_ERROR(kg_caller_provided_ccache_name(minor_status,
695                                                  &caller_ccname)))
696         return GSS_S_FAILURE;
697 
698     if (password != GSS_C_NO_BUFFER) {
699         pwdata = make_data(password->value, password->length);
700         code = krb5int_copy_data_contents_add0(context, &pwdata, &pwcopy);
701         if (code)
702             goto error;
703         cred->password = pwcopy.data;
704 
705         /* We will fetch the credential into a private memory ccache. */
706         assert(req_ccache == NULL);
707         code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache);
708         if (code)
709             goto error;
710         cred->destroy_ccache = 1;
711     } else if (req_ccache != NULL) {
712         code = krb5_cc_dup(context, req_ccache, &cred->ccache);
713         if (code)
714             goto error;
715     } else if (caller_ccname) {
716         /* Caller's ccache name has been set as the context default. */
717         code = krb5int_cc_default(context, &cred->ccache);
718         if (code)
719             goto error;
720     }
721 
722     if (client_keytab != NULL) {
723         code = krb5_kt_dup(context, client_keytab, &cred->client_keytab);
724     } else {
725         code = krb5_kt_client_default(context, &cred->client_keytab);
726         if (code) {
727             /* Treat resolution failure similarly to a client keytab which
728              * resolves but doesn't exist or has no content. */
729             TRACE_GSS_CLIENT_KEYTAB_FAIL(context, code);
730             krb5_clear_error_message(context);
731             code = 0;
732         }
733     }
734     if (code)
735         goto error;
736 
737     if (cred->ccache != NULL) {
738         /* The caller specified a ccache; check what's in it. */
739         code = scan_ccache(context, cred, TRUE);
740         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             if (cred->name == NULL) {
744                 if (!get_name_from_client_keytab(context, cred))
745                     code = 0;
746             } else if (can_get_initial_creds(context, cred)) {
747                 code = 0;
748             }
749         }
750         if (code)
751             goto error;
752     } else if (cred->name != NULL) {
753         /* The caller specified a name but not a ccache; pick a cache. */
754         code = get_cache_for_name(context, cred);
755         if (code)
756             goto error;
757     }
758 
759 #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     if (cred->name == NULL && !can_get_initial_creds(context, cred)) {
763         code = krb5_cccol_have_content(context);
764         if (code)
765             goto error;
766     }
767 #endif
768 
769     code = maybe_get_initial_cred(context, verify, cred);
770     if (code)
771         goto error;
772 
773     *minor_status = 0;
774     return GSS_S_COMPLETE;
775 
776 error:
777     *minor_status = code;
778     return GSS_S_NO_CRED;
779 }
780 
781 static OM_uint32
acquire_cred_context(krb5_context context,OM_uint32 * minor_status,gss_name_t desired_name,gss_buffer_t password,OM_uint32 time_req,gss_cred_usage_t cred_usage,krb5_ccache ccache,krb5_keytab client_keytab,krb5_keytab keytab,const char * rcname,const struct verify_params * verify,krb5_boolean iakerb,gss_cred_id_t * output_cred_handle,OM_uint32 * time_rec)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 {
791     krb5_gss_cred_id_t cred = NULL;
792     krb5_gss_name_t name = (krb5_gss_name_t)desired_name;
793     OM_uint32 ret;
794     krb5_error_code code = 0;
795 
796     /* make sure all outputs are valid */
797     *output_cred_handle = GSS_C_NO_CREDENTIAL;
798     if (time_rec)
799         *time_rec = 0;
800 
801     /* create the gss cred structure */
802     cred = k5alloc(sizeof(krb5_gss_cred_id_rec), &code);
803     if (cred == NULL)
804         goto krb_error_out;
805 
806     cred->usage = cred_usage;
807     cred->name = NULL;
808     cred->impersonator = NULL;
809     cred->iakerb_mech = iakerb;
810     cred->default_identity = (name == NULL);
811 #ifndef LEAN_CLIENT
812     cred->keytab = NULL;
813 #endif /* LEAN_CLIENT */
814     cred->destroy_ccache = 0;
815     cred->suppress_ci_flags = 0;
816     cred->ccache = NULL;
817 
818     code = k5_mutex_init(&cred->lock);
819     if (code)
820         goto krb_error_out;
821 
822     switch (cred_usage) {
823     case GSS_C_INITIATE:
824     case GSS_C_ACCEPT:
825     case GSS_C_BOTH:
826         break;
827     default:
828         ret = GSS_S_FAILURE;
829         *minor_status = (OM_uint32) G_BAD_USAGE;
830         goto error_out;
831     }
832 
833     if (name != NULL) {
834         code = kg_duplicate_name(context, name, &cred->name);
835         if (code)
836             goto krb_error_out;
837     }
838 
839 #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     if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
845         ret = acquire_accept_cred(context, minor_status, keytab, rcname, cred);
846         if (ret != GSS_S_COMPLETE)
847             goto error_out;
848     }
849 #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     if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
856         ret = acquire_init_cred(context, minor_status, ccache, password,
857                                 client_keytab, verify, cred);
858         if (ret != GSS_S_COMPLETE)
859             goto error_out;
860     }
861 
862     assert(cred->default_identity || cred->name != NULL);
863 
864     /*** at this point, the cred structure has been completely created */
865 
866     if (cred_usage == GSS_C_ACCEPT) {
867         if (time_rec)
868             *time_rec = GSS_C_INDEFINITE;
869     } else {
870         krb5_timestamp now;
871 
872         code = krb5_timeofday(context, &now);
873         if (code != 0)
874             goto krb_error_out;
875 
876         if (time_rec) {
877             /* Resolve cred now to determine the expiration time. */
878             ret = kg_cred_resolve(minor_status, context, (gss_cred_id_t)cred,
879                                   GSS_C_NO_NAME);
880             if (GSS_ERROR(ret))
881                 goto error_out;
882             *time_rec = ts_interval(now, cred->expire);
883             k5_mutex_unlock(&cred->lock);
884         }
885     }
886 
887     *minor_status = 0;
888     *output_cred_handle = (gss_cred_id_t) cred;
889 
890     return GSS_S_COMPLETE;
891 
892 krb_error_out:
893     *minor_status = code;
894     ret = GSS_S_FAILURE;
895 
896 error_out:
897     if (cred != NULL) {
898         if (cred->ccache) {
899             if (cred->destroy_ccache)
900                 krb5_cc_destroy(context, cred->ccache);
901             else
902                 krb5_cc_close(context, cred->ccache);
903         }
904         if (cred->client_keytab)
905             krb5_kt_close(context, cred->client_keytab);
906 #ifndef LEAN_CLIENT
907         if (cred->keytab)
908             krb5_kt_close(context, cred->keytab);
909 #endif /* LEAN_CLIENT */
910         if (cred->rcache)
911             k5_rc_close(context, cred->rcache);
912         if (cred->name)
913             kg_release_name(context, &cred->name);
914         krb5_free_principal(context, cred->impersonator);
915         zapfreestr(cred->password);
916         k5_mutex_destroy(&cred->lock);
917         xfree(cred);
918     }
919     save_error_info(*minor_status, context);
920     return ret;
921 }
922 
923 static OM_uint32
acquire_cred(OM_uint32 * minor_status,gss_name_t desired_name,gss_buffer_t password,OM_uint32 time_req,gss_cred_usage_t cred_usage,krb5_ccache ccache,krb5_keytab keytab,krb5_boolean iakerb,gss_cred_id_t * output_cred_handle,OM_uint32 * time_rec)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 {
930     krb5_context context = NULL;
931     krb5_error_code code = 0;
932     OM_uint32 ret;
933 
934     code = gss_krb5int_initialize_library();
935     if (code) {
936         *minor_status = code;
937         ret = GSS_S_FAILURE;
938         goto out;
939     }
940 
941     code = krb5_gss_init_context(&context);
942     if (code) {
943         *minor_status = code;
944         ret = GSS_S_FAILURE;
945         goto out;
946     }
947 
948     ret = acquire_cred_context(context, minor_status, desired_name, password,
949                                time_req, cred_usage, ccache, NULL, keytab,
950                                NULL, NULL, iakerb, output_cred_handle,
951                                time_rec);
952 
953 out:
954     krb5_free_context(context);
955     return ret;
956 }
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
kg_cred_resolve(OM_uint32 * minor_status,krb5_context context,gss_cred_id_t cred_handle,gss_name_t target_name)965 kg_cred_resolve(OM_uint32 *minor_status, krb5_context context,
966                 gss_cred_id_t cred_handle, gss_name_t target_name)
967 {
968     OM_uint32 maj;
969     krb5_error_code code;
970     krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t)cred_handle;
971     krb5_gss_name_t tname = (krb5_gss_name_t)target_name;
972     krb5_principal client_princ;
973 
974     *minor_status = 0;
975 
976     maj = krb5_gss_validate_cred_1(minor_status, cred_handle, context);
977     if (maj != 0)
978         return maj;
979     k5_mutex_assert_locked(&cred->lock);
980 
981     if (cred->usage == GSS_C_ACCEPT || cred->name != NULL)
982         return GSS_S_COMPLETE;
983     /* acquire_init_cred should have set both name and ccache, or neither. */
984     assert(cred->ccache == NULL);
985 
986     if (tname != NULL) {
987         /* Use the target name to select an existing ccache or a principal. */
988         code = krb5_cc_select(context, tname->princ, &cred->ccache,
989                               &client_princ);
990         if (code && code != KRB5_CC_NOTFOUND)
991             goto kerr;
992         if (client_princ != NULL) {
993             code = kg_init_name(context, client_princ, NULL, NULL, NULL,
994                                 KG_INIT_NAME_NO_COPY, &cred->name);
995             if (code) {
996                 krb5_free_principal(context, client_princ);
997                 goto kerr;
998             }
999         }
1000         if (cred->ccache != NULL) {
1001             code = scan_ccache(context, cred, FALSE);
1002             if (code)
1003                 goto kerr;
1004         }
1005     }
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     if (cred->name == NULL) {
1010         code = krb5int_cc_default(context, &cred->ccache);
1011         if (code)
1012             goto kerr;
1013         code = scan_ccache(context, cred, FALSE);
1014         if (code == KRB5_FCC_NOFILE) {
1015             /* Default ccache doesn't exist; fall through to client keytab. */
1016             krb5_cc_close(context, cred->ccache);
1017             cred->ccache = NULL;
1018         } else if (code) {
1019             goto kerr;
1020         }
1021     }
1022 
1023     /* If that didn't work, try getting a name from the client keytab. */
1024     if (cred->name == NULL) {
1025         code = get_name_from_client_keytab(context, cred);
1026         if (code) {
1027             code = KG_EMPTY_CCACHE;
1028             goto kerr;
1029         }
1030     }
1031 
1032     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         code = get_cache_for_name(context, cred);
1036         if (code)
1037             goto kerr;
1038     }
1039 
1040     /* Resolve name to ccache and possibly get initial credentials. */
1041     code = maybe_get_initial_cred(context, NULL, cred);
1042     if (code)
1043         goto kerr;
1044 
1045     return GSS_S_COMPLETE;
1046 
1047 kerr:
1048     k5_mutex_unlock(&cred->lock);
1049     save_error_info(code, context);
1050     *minor_status = code;
1051     return GSS_S_NO_CRED;
1052 }
1053 
1054 OM_uint32
gss_krb5int_set_cred_rcache(OM_uint32 * minor_status,gss_cred_id_t * cred_handle,const gss_OID desired_oid,const gss_buffer_t value)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 {
1060     krb5_gss_cred_id_t cred;
1061     krb5_error_code code;
1062     krb5_context context;
1063     krb5_rcache rcache;
1064 
1065     assert(value->length == sizeof(rcache));
1066 
1067     if (value->length != sizeof(rcache))
1068         return GSS_S_FAILURE;
1069 
1070     rcache = (krb5_rcache)value->value;
1071 
1072     cred = (krb5_gss_cred_id_t)*cred_handle;
1073 
1074     code = krb5_gss_init_context(&context);
1075     if (code) {
1076         *minor_status = code;
1077         return GSS_S_FAILURE;
1078     }
1079     if (cred->rcache != NULL)
1080         k5_rc_close(context, cred->rcache);
1081 
1082     cred->rcache = rcache;
1083 
1084     krb5_free_context(context);
1085 
1086     *minor_status = 0;
1087     return GSS_S_COMPLETE;
1088 }
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
krb5_gss_acquire_cred(OM_uint32 * minor_status,gss_name_t desired_name,OM_uint32 time_req,gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)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 {
1102     return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage,
1103                         NULL, NULL, FALSE, output_cred_handle, time_rec);
1104 }
1105 
1106 OM_uint32 KRB5_CALLCONV
iakerb_gss_acquire_cred(OM_uint32 * minor_status,gss_name_t desired_name,OM_uint32 time_req,gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)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 {
1113     return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage,
1114                         NULL, NULL, TRUE, output_cred_handle, time_rec);
1115 }
1116 
1117 OM_uint32 KRB5_CALLCONV
krb5_gss_acquire_cred_with_password(OM_uint32 * minor_status,const gss_name_t desired_name,const gss_buffer_t password,OM_uint32 time_req,const gss_OID_set desired_mechs,int cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)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 {
1128     return acquire_cred(minor_status, desired_name, password, time_req,
1129                         cred_usage, NULL, NULL, FALSE, output_cred_handle,
1130                         time_rec);
1131 }
1132 
1133 OM_uint32 KRB5_CALLCONV
iakerb_gss_acquire_cred_with_password(OM_uint32 * minor_status,const gss_name_t desired_name,const gss_buffer_t password,OM_uint32 time_req,const gss_OID_set desired_mechs,int cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)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 {
1144     return acquire_cred(minor_status, desired_name, password, time_req,
1145                         cred_usage, NULL, NULL, TRUE, output_cred_handle,
1146                         time_rec);
1147 }
1148 
1149 OM_uint32
gss_krb5int_import_cred(OM_uint32 * minor_status,gss_cred_id_t * cred_handle,const gss_OID desired_oid,const gss_buffer_t value)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 {
1155     struct krb5_gss_import_cred_req *req;
1156     krb5_gss_name_rec name;
1157     OM_uint32 time_rec;
1158     krb5_error_code code;
1159     gss_cred_usage_t usage;
1160     gss_name_t desired_name = GSS_C_NO_NAME;
1161 
1162     assert(value->length == sizeof(*req));
1163 
1164     if (value->length != sizeof(*req))
1165         return GSS_S_FAILURE;
1166 
1167     req = (struct krb5_gss_import_cred_req *)value->value;
1168 
1169     if (req->id != NULL) {
1170         usage = (req->keytab != NULL) ? GSS_C_BOTH : GSS_C_INITIATE;
1171     } else if (req->keytab != NULL) {
1172         usage = GSS_C_ACCEPT;
1173     } else {
1174         *minor_status = EINVAL;
1175         return GSS_S_FAILURE;
1176     }
1177 
1178     if (req->keytab_principal != NULL) {
1179         memset(&name, 0, sizeof(name));
1180         code = k5_mutex_init(&name.lock);
1181         if (code != 0) {
1182             *minor_status = code;
1183             return GSS_S_FAILURE;
1184         }
1185         name.princ = req->keytab_principal;
1186         desired_name = (gss_name_t)&name;
1187     }
1188 
1189     code = acquire_cred(minor_status, desired_name, NULL, GSS_C_INDEFINITE,
1190                         usage, req->id, req->keytab, FALSE, cred_handle,
1191                         &time_rec);
1192     if (req->keytab_principal != NULL)
1193         k5_mutex_destroy(&name.lock);
1194     return code;
1195 }
1196 
1197 static OM_uint32
acquire_cred_from(OM_uint32 * minor_status,const gss_name_t desired_name,OM_uint32 time_req,const gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_const_key_value_set_t cred_store,krb5_boolean iakerb,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)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 {
1205     krb5_context context = NULL;
1206     krb5_error_code code = 0;
1207     krb5_keytab client_keytab = NULL;
1208     krb5_keytab keytab = NULL;
1209     krb5_ccache ccache = NULL;
1210     krb5_principal verify_princ = NULL;
1211     const char *rcname, *value;
1212     struct verify_params vparams = { NULL };
1213     const struct verify_params *verify = NULL;
1214     gss_buffer_desc pwbuf;
1215     gss_buffer_t password = NULL;
1216     OM_uint32 ret;
1217 
1218     code = gss_krb5int_initialize_library();
1219     if (code) {
1220         *minor_status = code;
1221         ret = GSS_S_FAILURE;
1222         goto out;
1223     }
1224 
1225     code = krb5_gss_init_context(&context);
1226     if (code) {
1227         *minor_status = code;
1228         ret = GSS_S_FAILURE;
1229         goto out;
1230     }
1231 
1232     ret = kg_value_from_cred_store(cred_store, KRB5_CS_CCACHE_URN, &value);
1233     if (GSS_ERROR(ret))
1234         goto out;
1235 
1236     if (value) {
1237         code = krb5_cc_resolve(context, value, &ccache);
1238         if (code != 0) {
1239             *minor_status = code;
1240             ret = GSS_S_NO_CRED;
1241             goto out;
1242         }
1243     }
1244 
1245     ret = kg_value_from_cred_store(cred_store, KRB5_CS_CLI_KEYTAB_URN, &value);
1246     if (GSS_ERROR(ret))
1247         goto out;
1248 
1249     if (value) {
1250         code = krb5_kt_resolve(context, value, &client_keytab);
1251         if (code != 0) {
1252             *minor_status = code;
1253             ret = GSS_S_NO_CRED;
1254             goto out;
1255         }
1256     }
1257 
1258     ret = kg_value_from_cred_store(cred_store, KRB5_CS_KEYTAB_URN, &value);
1259     if (GSS_ERROR(ret))
1260         goto out;
1261 
1262     if (value) {
1263         code = krb5_kt_resolve(context, value, &keytab);
1264         if (code != 0) {
1265             *minor_status = code;
1266             ret = GSS_S_NO_CRED;
1267             goto out;
1268         }
1269     }
1270 
1271     ret = kg_value_from_cred_store(cred_store, KRB5_CS_RCACHE_URN, &rcname);
1272     if (GSS_ERROR(ret))
1273         goto out;
1274 
1275     ret = kg_value_from_cred_store(cred_store, KRB5_CS_PASSWORD_URN, &value);
1276     if (GSS_ERROR(ret))
1277         goto out;
1278 
1279     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         if (desired_name == GSS_C_NO_NAME) {
1283             ret = GSS_S_BAD_NAME;
1284             goto out;
1285         }
1286         if (cred_usage == GSS_C_ACCEPT || desired_name == GSS_C_NO_NAME ||
1287             ccache != NULL || client_keytab != NULL) {
1288             *minor_status = (OM_uint32)G_BAD_USAGE;
1289             ret = GSS_S_FAILURE;
1290             goto out;
1291         }
1292         pwbuf.length = strlen(value);
1293         pwbuf.value = (void *)value;
1294         password = &pwbuf;
1295     }
1296 
1297     ret = kg_value_from_cred_store(cred_store, KRB5_CS_VERIFY_URN, &value);
1298     if (GSS_ERROR(ret))
1299         goto out;
1300     if (value != NULL) {
1301         if (iakerb || password == NULL) {
1302             /* Only valid if acquiring cred with password, and not supported
1303              * with IAKERB. */
1304             *minor_status = G_BAD_USAGE;
1305             ret = GSS_S_FAILURE;
1306             goto out;
1307         }
1308         if (*value != '\0') {
1309             code = krb5_parse_name(context, value, &verify_princ);
1310             if (code != 0) {
1311                 *minor_status = code;
1312                 ret = GSS_S_FAILURE;
1313                 goto out;
1314             }
1315         }
1316         vparams.princ = verify_princ;
1317         vparams.keytab = keytab;
1318         verify = &vparams;
1319     }
1320     ret = acquire_cred_context(context, minor_status, desired_name, password,
1321                                time_req, cred_usage, ccache, client_keytab,
1322                                keytab, rcname, verify, iakerb,
1323                                output_cred_handle, time_rec);
1324 
1325 out:
1326     if (ccache != NULL)
1327         krb5_cc_close(context, ccache);
1328     if (client_keytab != NULL)
1329         krb5_kt_close(context, client_keytab);
1330     if (keytab != NULL)
1331         krb5_kt_close(context, keytab);
1332     krb5_free_principal(context, verify_princ);
1333     krb5_free_context(context);
1334     return ret;
1335 }
1336 
1337 OM_uint32 KRB5_CALLCONV
krb5_gss_acquire_cred_from(OM_uint32 * minor_status,const gss_name_t desired_name,OM_uint32 time_req,const gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_const_key_value_set_t cred_store,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)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 {
1348     return acquire_cred_from(minor_status, desired_name, time_req,
1349                              desired_mechs, cred_usage, cred_store,
1350                              FALSE, output_cred_handle, actual_mechs,
1351                              time_rec);
1352 }
1353 
1354 OM_uint32 KRB5_CALLCONV
iakerb_gss_acquire_cred_from(OM_uint32 * minor_status,const gss_name_t desired_name,OM_uint32 time_req,const gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_const_key_value_set_t cred_store,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)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 {
1365     return acquire_cred_from(minor_status, desired_name, time_req,
1366                              desired_mechs, cred_usage, cred_store,
1367                              TRUE, output_cred_handle, actual_mechs,
1368                              time_rec);
1369 }
1370