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)126 check_keytab(krb5_context context, krb5_keytab kt, krb5_gss_name_t name)
127 {
128     krb5_error_code code;
129     krb5_keytab_entry ent;
130     krb5_principal accprinc = NULL;
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     /* Get the partial principal for the acceptor name. */
145     code = kg_acceptor_princ(context, name, &accprinc);
146     if (code)
147         return code;
148 
149     /* Scan the keytab for host-based entries matching accprinc. */
150     code = k5_kt_have_match(context, kt, accprinc);
151     if (code == KRB5_KT_NOTFOUND) {
152         if (krb5_unparse_name(context, accprinc, &princname) == 0) {
153             k5_setmsg(context, code, _("No key table entry found matching %s"),
154                       princname);
155             free(princname);
156         }
157     }
158     krb5_free_principal(context, accprinc);
159     return code;
160 }
161 
162 /* get credentials corresponding to a key in the krb5 keytab.
163    If successful, set the keytab-specific fields in cred
164 */
165 
166 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)167 acquire_accept_cred(krb5_context context, OM_uint32 *minor_status,
168                     krb5_keytab req_keytab, const char *rcname,
169                     krb5_gss_cred_id_rec *cred)
170 {
171     OM_uint32 major;
172     krb5_error_code code;
173     krb5_keytab kt = NULL;
174     krb5_rcache rc = NULL;
175 
176     assert(cred->keytab == NULL);
177 
178     /* If we have an explicit rcache name, open it. */
179     if (rcname != NULL) {
180         code = k5_rc_resolve(context, rcname, &rc);
181         if (code) {
182             major = GSS_S_FAILURE;
183             goto cleanup;
184         }
185     }
186 
187     if (req_keytab != NULL) {
188         code = krb5_kt_dup(context, req_keytab, &kt);
189     } else {
190         k5_mutex_lock(&gssint_krb5_keytab_lock);
191         if (krb5_gss_keytab != NULL) {
192             code = krb5_kt_resolve(context, krb5_gss_keytab, &kt);
193             k5_mutex_unlock(&gssint_krb5_keytab_lock);
194         } else {
195             k5_mutex_unlock(&gssint_krb5_keytab_lock);
196             code = krb5_kt_default(context, &kt);
197         }
198     }
199     if (code) {
200         major = GSS_S_NO_CRED;
201         goto cleanup;
202     }
203 
204     if (cred->name != NULL) {
205         /* Make sure we have keys matching the desired name in the keytab. */
206         code = check_keytab(context, kt, cred->name);
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     krb5_keytab_entry entry;
328 
329     if (cred->password != NULL)
330         return TRUE;
331 
332     if (cred->client_keytab == NULL)
333         return FALSE;
334 
335     /* If we don't know the client principal yet, check for any keytab keys. */
336     if (cred->name == NULL)
337         return !krb5_kt_have_content(context, cred->client_keytab);
338 
339     /* Check if we have a keytab key for the client principal. */
340     code = krb5_kt_get_entry(context, cred->client_keytab, cred->name->princ,
341                              0, 0, &entry);
342     if (code) {
343         krb5_clear_error_message(context);
344         return FALSE;
345     }
346     krb5_free_keytab_entry_contents(context, &entry);
347     return TRUE;
348 }
349 
350 /* Scan cred->ccache for name, expiry time, impersonator, refresh time. */
351 static krb5_error_code
scan_ccache(krb5_context context,krb5_gss_cred_id_rec * cred)352 scan_ccache(krb5_context context, krb5_gss_cred_id_rec *cred)
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     /* Credentials cache principal must match the initiator name. */
369     code = krb5_cc_get_principal(context, ccache, &ccache_princ);
370     if (code != 0)
371         goto cleanup;
372     if (cred->name != NULL &&
373         !krb5_principal_compare(context, ccache_princ, cred->name->princ)) {
374         code = KG_CCACHE_NOMATCH;
375         goto cleanup;
376     }
377 
378     /* Save the ccache principal as the credential name if not already set. */
379     if (!cred->name) {
380         code = kg_init_name(context, ccache_princ, NULL, NULL, NULL,
381                             KG_INIT_NAME_NO_COPY, &cred->name);
382         if (code)
383             goto cleanup;
384         ccache_princ = NULL;
385     }
386 
387     assert(cred->name->princ != NULL);
388     realm = krb5_princ_realm(context, cred->name->princ);
389     code = krb5_build_principal_ext(context, &tgt_princ,
390                                     realm->length, realm->data,
391                                     KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
392                                     realm->length, realm->data,
393                                     0);
394     if (code)
395         return code;
396 
397     /* If there's a tgt for the principal's local realm in here, use its expiry
398      * time.  Otherwise use the first key. */
399     code = krb5_cc_start_seq_get(context, ccache, &cursor);
400     if (code) {
401         krb5_free_principal(context, tgt_princ);
402         return code;
403     }
404     while (!(code = krb5_cc_next_cred(context, ccache, &cursor, &creds))) {
405         if (krb5_is_config_principal(context, creds.server)) {
406             code = scan_cc_config(context, cred, creds.server, &creds.ticket);
407             krb5_free_cred_contents(context, &creds);
408             if (code)
409                 break;
410             continue;
411         }
412         is_tgt = krb5_principal_compare(context, tgt_princ, creds.server);
413         endtime = creds.times.endtime;
414         krb5_free_cred_contents(context, &creds);
415         if (is_tgt)
416             cred->have_tgt = TRUE;
417         if (is_tgt || cred->expire == 0)
418             cred->expire = endtime;
419     }
420     krb5_cc_end_seq_get(context, ccache, &cursor);
421     if (code && code != KRB5_CC_END)
422         goto cleanup;
423     code = 0;
424 
425     if (cred->expire == 0 && !can_get_initial_creds(context, cred)) {
426         code = KG_EMPTY_CCACHE;
427         goto cleanup;
428     }
429 
430 cleanup:
431     (void)krb5_cc_set_flags(context, ccache, 0);
432     krb5_free_principal(context, ccache_princ);
433     krb5_free_principal(context, tgt_princ);
434     return code;
435 }
436 
437 /* Find an existing or destination ccache for cred->name. */
438 static krb5_error_code
get_cache_for_name(krb5_context context,krb5_gss_cred_id_rec * cred)439 get_cache_for_name(krb5_context context, krb5_gss_cred_id_rec *cred)
440 {
441     krb5_error_code code;
442     krb5_boolean can_get, have_collection;
443     krb5_ccache defcc = NULL;
444     krb5_principal princ = NULL;
445     const char *cctype;
446 
447     assert(cred->name != NULL && cred->ccache == NULL);
448 #ifdef USE_LEASH
449     code = get_ccache_leash(context, cred->name->princ, &cred->ccache);
450     return code ? code : scan_ccache(context, cred);
451 #else
452     /* Check first whether we can acquire tickets, to avoid overwriting the
453      * extended error message from krb5_cc_cache_match. */
454     can_get = can_get_initial_creds(context, cred);
455 
456     /* Look for an existing cache for the client principal. */
457     code = krb5_cc_cache_match(context, cred->name->princ, &cred->ccache);
458     if (code == 0)
459         return scan_ccache(context, cred);
460     if (code != KRB5_CC_NOTFOUND || !can_get)
461         return code;
462     krb5_clear_error_message(context);
463 
464     /* There is no existing ccache, but we can acquire credentials.  Get the
465      * default ccache to help decide where we should put them. */
466     code = krb5_cc_default(context, &defcc);
467     if (code)
468         return code;
469     cctype = krb5_cc_get_type(context, defcc);
470     have_collection = krb5_cc_support_switch(context, cctype);
471 
472     /* We can use an empty default ccache if we're using a password or if
473      * there's no collection. */
474     if (cred->password != NULL || !have_collection) {
475         if (krb5_cc_get_principal(context, defcc, &princ) == KRB5_FCC_NOFILE) {
476             cred->ccache = defcc;
477             defcc = NULL;
478         }
479         krb5_clear_error_message(context);
480     }
481 
482     /* Otherwise, try to use a new cache in the collection. */
483     if (cred->ccache == NULL) {
484         if (!have_collection) {
485             code = KG_CCACHE_NOMATCH;
486             goto cleanup;
487         }
488         code = krb5_cc_new_unique(context, cctype, NULL, &cred->ccache);
489         if (code)
490             goto cleanup;
491     }
492 
493 cleanup:
494     krb5_free_principal(context, princ);
495     if (defcc != NULL)
496         krb5_cc_close(context, defcc);
497     return code;
498 #endif /* not USE_LEASH */
499 }
500 
501 /* Try to set cred->name using the client keytab. */
502 static krb5_error_code
get_name_from_client_keytab(krb5_context context,krb5_gss_cred_id_rec * cred)503 get_name_from_client_keytab(krb5_context context, krb5_gss_cred_id_rec *cred)
504 {
505     krb5_error_code code;
506     krb5_principal princ;
507 
508     assert(cred->name == NULL);
509 
510     if (cred->client_keytab == NULL)
511         return KRB5_KT_NOTFOUND;
512 
513     code = k5_kt_get_principal(context, cred->client_keytab, &princ);
514     if (code)
515         return code;
516     code = kg_init_name(context, princ, NULL, NULL, NULL, KG_INIT_NAME_NO_COPY,
517                         &cred->name);
518     if (code) {
519         krb5_free_principal(context, princ);
520         return code;
521     }
522     return 0;
523 }
524 
525 /* Make a note in ccache that we should attempt to refresh it from the client
526  * keytab at refresh_time. */
527 static void
set_refresh_time(krb5_context context,krb5_ccache ccache,krb5_timestamp refresh_time)528 set_refresh_time(krb5_context context, krb5_ccache ccache,
529                  krb5_timestamp refresh_time)
530 {
531     char buf[128];
532     krb5_data d;
533 
534     snprintf(buf, sizeof(buf), "%u", (unsigned int)ts2tt(refresh_time));
535     d = string2data(buf);
536     (void)krb5_cc_set_config(context, ccache, NULL, KRB5_CC_CONF_REFRESH_TIME,
537                              &d);
538     krb5_clear_error_message(context);
539 }
540 
541 /* Return true if it's time to refresh cred from the client keytab.  If
542  * returning true, avoid retrying for 30 seconds. */
543 krb5_boolean
kg_cred_time_to_refresh(krb5_context context,krb5_gss_cred_id_rec * cred)544 kg_cred_time_to_refresh(krb5_context context, krb5_gss_cred_id_rec *cred)
545 {
546     krb5_timestamp now, soon;
547 
548     if (krb5_timeofday(context, &now))
549         return FALSE;
550     soon = ts_incr(now, 30);
551     if (cred->refresh_time != 0 && !ts_after(cred->refresh_time, now)) {
552         set_refresh_time(context, cred->ccache, soon);
553         return TRUE;
554     }
555 
556     /* If the creds will expire soon, try to refresh even if they weren't
557      * acquired with a client keytab. */
558     if (ts_after(soon, cred->expire)) {
559         set_refresh_time(context, cred->ccache, soon);
560         return TRUE;
561     }
562 
563     return FALSE;
564 }
565 
566 /* If appropriate, make a note to refresh cred from the client keytab when it
567  * is halfway to expired. */
568 void
kg_cred_set_initial_refresh(krb5_context context,krb5_gss_cred_id_rec * cred,krb5_ticket_times * times)569 kg_cred_set_initial_refresh(krb5_context context, krb5_gss_cred_id_rec *cred,
570                             krb5_ticket_times *times)
571 {
572     krb5_timestamp refresh;
573 
574     /* For now, we only mark keytab-acquired credentials for refresh. */
575     if (cred->password != NULL)
576         return;
577 
578     /* Make a note to refresh these when they are halfway to expired. */
579     refresh = ts_incr(times->starttime,
580                       ts_delta(times->endtime, times->starttime) / 2);
581     set_refresh_time(context, cred->ccache, refresh);
582 }
583 
584 struct verify_params {
585     krb5_principal princ;
586     krb5_keytab keytab;
587 };
588 
589 static krb5_error_code
verify_initial_cred(krb5_context context,krb5_creds * creds,const struct verify_params * verify)590 verify_initial_cred(krb5_context context, krb5_creds *creds,
591                     const struct verify_params *verify)
592 {
593     krb5_verify_init_creds_opt vopts;
594 
595     krb5_verify_init_creds_opt_init(&vopts);
596     krb5_verify_init_creds_opt_set_ap_req_nofail(&vopts, TRUE);
597     return krb5_verify_init_creds(context, creds, verify->princ,
598                                   verify->keytab, NULL, &vopts);
599 }
600 
601 /* Get initial credentials using the supplied password or client keytab. */
602 static krb5_error_code
get_initial_cred(krb5_context context,const struct verify_params * verify,krb5_gss_cred_id_rec * cred)603 get_initial_cred(krb5_context context, const struct verify_params *verify,
604                  krb5_gss_cred_id_rec *cred)
605 {
606     krb5_error_code code;
607     krb5_get_init_creds_opt *opt = NULL;
608     krb5_creds creds;
609 
610     code = krb5_get_init_creds_opt_alloc(context, &opt);
611     if (code)
612         return code;
613     code = krb5_get_init_creds_opt_set_out_ccache(context, opt, cred->ccache);
614     if (code)
615         goto cleanup;
616     if (cred->password != NULL) {
617         code = krb5_get_init_creds_password(context, &creds, cred->name->princ,
618                                             cred->password, NULL, NULL, 0,
619                                             NULL, opt);
620     } else if (cred->client_keytab != NULL) {
621         code = krb5_get_init_creds_keytab(context, &creds, cred->name->princ,
622                                           cred->client_keytab, 0, NULL, opt);
623     } else {
624         code = KRB5_KT_NOTFOUND;
625     }
626     if (code)
627         goto cleanup;
628     if (cred->password != NULL && verify != NULL) {
629         code = verify_initial_cred(context, &creds, verify);
630         if (code)
631             goto cleanup;
632     }
633     kg_cred_set_initial_refresh(context, cred, &creds.times);
634     cred->have_tgt = TRUE;
635     cred->expire = creds.times.endtime;
636     krb5_free_cred_contents(context, &creds);
637 cleanup:
638     krb5_get_init_creds_opt_free(context, opt);
639     return code;
640 }
641 
642 /* Get initial credentials if we ought to and are able to. */
643 static krb5_error_code
maybe_get_initial_cred(krb5_context context,const struct verify_params * verify,krb5_gss_cred_id_rec * cred)644 maybe_get_initial_cred(krb5_context context,
645                        const struct verify_params *verify,
646                        krb5_gss_cred_id_rec *cred)
647 {
648     krb5_error_code code;
649 
650     /* Don't get creds if we don't know the name or are doing IAKERB. */
651     if (cred->name == NULL || cred->iakerb_mech)
652         return 0;
653 
654     /* Get creds if we have none or if it's time to refresh. */
655     if (cred->expire == 0 || kg_cred_time_to_refresh(context, cred)) {
656         code = get_initial_cred(context, verify, cred);
657         /* If we were trying to refresh and failed, we can keep going. */
658         if (code && cred->expire == 0)
659             return code;
660         krb5_clear_error_message(context);
661     }
662     return 0;
663 }
664 
665 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)666 acquire_init_cred(krb5_context context, OM_uint32 *minor_status,
667                   krb5_ccache req_ccache, gss_buffer_t password,
668                   krb5_keytab client_keytab,
669                   const struct verify_params *verify,
670                   krb5_gss_cred_id_rec *cred)
671 {
672     krb5_error_code code;
673     krb5_data pwdata, pwcopy;
674     int caller_ccname = 0;
675 
676     /* Get ccache from caller if available. */
677     if (GSS_ERROR(kg_sync_ccache_name(context, minor_status)))
678         return GSS_S_FAILURE;
679     if (GSS_ERROR(kg_caller_provided_ccache_name(minor_status,
680                                                  &caller_ccname)))
681         return GSS_S_FAILURE;
682 
683     if (password != GSS_C_NO_BUFFER) {
684         pwdata = make_data(password->value, password->length);
685         code = krb5int_copy_data_contents_add0(context, &pwdata, &pwcopy);
686         if (code)
687             goto error;
688         cred->password = pwcopy.data;
689 
690         /* We will fetch the credential into a private memory ccache. */
691         assert(req_ccache == NULL);
692         code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache);
693         if (code)
694             goto error;
695         cred->destroy_ccache = 1;
696     } else if (req_ccache != NULL) {
697         code = krb5_cc_dup(context, req_ccache, &cred->ccache);
698         if (code)
699             goto error;
700     } else if (caller_ccname) {
701         /* Caller's ccache name has been set as the context default. */
702         code = krb5int_cc_default(context, &cred->ccache);
703         if (code)
704             goto error;
705     }
706 
707     if (client_keytab != NULL) {
708         code = krb5_kt_dup(context, client_keytab, &cred->client_keytab);
709     } else {
710         code = krb5_kt_client_default(context, &cred->client_keytab);
711         if (code) {
712             /* Treat resolution failure similarly to a client keytab which
713              * resolves but doesn't exist or has no content. */
714             TRACE_GSS_CLIENT_KEYTAB_FAIL(context, code);
715             krb5_clear_error_message(context);
716             code = 0;
717         }
718     }
719     if (code)
720         goto error;
721 
722     if (cred->ccache != NULL) {
723         /* The caller specified a ccache; check what's in it. */
724         code = scan_ccache(context, cred);
725         if (code == KRB5_FCC_NOFILE) {
726             /* See if we can get initial creds.  If the caller didn't specify
727              * a name, pick one from the client keytab. */
728             if (cred->name == NULL) {
729                 if (!get_name_from_client_keytab(context, cred))
730                     code = 0;
731             } else if (can_get_initial_creds(context, cred)) {
732                 code = 0;
733             }
734         }
735         if (code)
736             goto error;
737     } else if (cred->name != NULL) {
738         /* The caller specified a name but not a ccache; pick a cache. */
739         code = get_cache_for_name(context, cred);
740         if (code)
741             goto error;
742     }
743 
744 #ifndef USE_LEASH
745     /* If we haven't picked a name, make sure we have or can get any creds,
746      * unless we're using Leash and might be able to get them interactively. */
747     if (cred->name == NULL && !can_get_initial_creds(context, cred)) {
748         code = krb5_cccol_have_content(context);
749         if (code)
750             goto error;
751     }
752 #endif
753 
754     code = maybe_get_initial_cred(context, verify, cred);
755     if (code)
756         goto error;
757 
758     *minor_status = 0;
759     return GSS_S_COMPLETE;
760 
761 error:
762     *minor_status = code;
763     return GSS_S_NO_CRED;
764 }
765 
766 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)767 acquire_cred_context(krb5_context context, OM_uint32 *minor_status,
768                      gss_name_t desired_name, gss_buffer_t password,
769                      OM_uint32 time_req, gss_cred_usage_t cred_usage,
770                      krb5_ccache ccache, krb5_keytab client_keytab,
771                      krb5_keytab keytab, const char *rcname,
772                      const struct verify_params *verify,
773                      krb5_boolean iakerb, gss_cred_id_t *output_cred_handle,
774                      OM_uint32 *time_rec)
775 {
776     krb5_gss_cred_id_t cred = NULL;
777     krb5_gss_name_t name = (krb5_gss_name_t)desired_name;
778     OM_uint32 ret;
779     krb5_error_code code = 0;
780 
781     /* make sure all outputs are valid */
782     *output_cred_handle = GSS_C_NO_CREDENTIAL;
783     if (time_rec)
784         *time_rec = 0;
785 
786     /* create the gss cred structure */
787     cred = k5alloc(sizeof(krb5_gss_cred_id_rec), &code);
788     if (cred == NULL)
789         goto krb_error_out;
790 
791     cred->usage = cred_usage;
792     cred->name = NULL;
793     cred->impersonator = NULL;
794     cred->iakerb_mech = iakerb;
795     cred->default_identity = (name == NULL);
796 #ifndef LEAN_CLIENT
797     cred->keytab = NULL;
798 #endif /* LEAN_CLIENT */
799     cred->destroy_ccache = 0;
800     cred->suppress_ci_flags = 0;
801     cred->ccache = NULL;
802 
803     code = k5_mutex_init(&cred->lock);
804     if (code)
805         goto krb_error_out;
806 
807     switch (cred_usage) {
808     case GSS_C_INITIATE:
809     case GSS_C_ACCEPT:
810     case GSS_C_BOTH:
811         break;
812     default:
813         ret = GSS_S_FAILURE;
814         *minor_status = (OM_uint32) G_BAD_USAGE;
815         goto error_out;
816     }
817 
818     if (name != NULL) {
819         code = kg_duplicate_name(context, name, &cred->name);
820         if (code)
821             goto krb_error_out;
822     }
823 
824 #ifndef LEAN_CLIENT
825     /*
826      * If requested, acquire credentials for accepting. This will fill
827      * in cred->name if desired_princ is specified.
828      */
829     if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
830         ret = acquire_accept_cred(context, minor_status, keytab, rcname, cred);
831         if (ret != GSS_S_COMPLETE)
832             goto error_out;
833     }
834 #endif /* LEAN_CLIENT */
835 
836     /*
837      * If requested, acquire credentials for initiation. This will fill
838      * in cred->name if it wasn't set above.
839      */
840     if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
841         ret = acquire_init_cred(context, minor_status, ccache, password,
842                                 client_keytab, verify, cred);
843         if (ret != GSS_S_COMPLETE)
844             goto error_out;
845     }
846 
847     assert(cred->default_identity || cred->name != NULL);
848 
849     /*** at this point, the cred structure has been completely created */
850 
851     if (cred_usage == GSS_C_ACCEPT) {
852         if (time_rec)
853             *time_rec = GSS_C_INDEFINITE;
854     } else {
855         krb5_timestamp now;
856 
857         code = krb5_timeofday(context, &now);
858         if (code != 0)
859             goto krb_error_out;
860 
861         if (time_rec) {
862             /* Resolve cred now to determine the expiration time. */
863             ret = kg_cred_resolve(minor_status, context, (gss_cred_id_t)cred,
864                                   GSS_C_NO_NAME);
865             if (GSS_ERROR(ret))
866                 goto error_out;
867             *time_rec = ts_after(cred->expire, now) ?
868                 ts_delta(cred->expire, now) : 0;
869             k5_mutex_unlock(&cred->lock);
870         }
871     }
872 
873     *minor_status = 0;
874     *output_cred_handle = (gss_cred_id_t) cred;
875 
876     return GSS_S_COMPLETE;
877 
878 krb_error_out:
879     *minor_status = code;
880     ret = GSS_S_FAILURE;
881 
882 error_out:
883     if (cred != NULL) {
884         if (cred->ccache) {
885             if (cred->destroy_ccache)
886                 krb5_cc_destroy(context, cred->ccache);
887             else
888                 krb5_cc_close(context, cred->ccache);
889         }
890         if (cred->client_keytab)
891             krb5_kt_close(context, cred->client_keytab);
892 #ifndef LEAN_CLIENT
893         if (cred->keytab)
894             krb5_kt_close(context, cred->keytab);
895 #endif /* LEAN_CLIENT */
896         if (cred->rcache)
897             k5_rc_close(context, cred->rcache);
898         if (cred->name)
899             kg_release_name(context, &cred->name);
900         krb5_free_principal(context, cred->impersonator);
901         zapfreestr(cred->password);
902         k5_mutex_destroy(&cred->lock);
903         xfree(cred);
904     }
905     save_error_info(*minor_status, context);
906     return ret;
907 }
908 
909 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)910 acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
911              gss_buffer_t password, OM_uint32 time_req,
912              gss_cred_usage_t cred_usage, krb5_ccache ccache,
913              krb5_keytab keytab, krb5_boolean iakerb,
914              gss_cred_id_t *output_cred_handle, OM_uint32 *time_rec)
915 {
916     krb5_context context = NULL;
917     krb5_error_code code = 0;
918     OM_uint32 ret;
919 
920     code = gss_krb5int_initialize_library();
921     if (code) {
922         *minor_status = code;
923         ret = GSS_S_FAILURE;
924         goto out;
925     }
926 
927     code = krb5_gss_init_context(&context);
928     if (code) {
929         *minor_status = code;
930         ret = GSS_S_FAILURE;
931         goto out;
932     }
933 
934     ret = acquire_cred_context(context, minor_status, desired_name, password,
935                                time_req, cred_usage, ccache, NULL, keytab,
936                                NULL, NULL, iakerb, output_cred_handle,
937                                time_rec);
938 
939 out:
940     krb5_free_context(context);
941     return ret;
942 }
943 
944 /*
945  * Resolve the name and ccache for an initiator credential if it has not yet
946  * been done.  If specified, use the target name to pick an appropriate ccache
947  * within the collection.  Validates cred_handle and leaves it locked on
948  * success.
949  */
950 OM_uint32
kg_cred_resolve(OM_uint32 * minor_status,krb5_context context,gss_cred_id_t cred_handle,gss_name_t target_name)951 kg_cred_resolve(OM_uint32 *minor_status, krb5_context context,
952                 gss_cred_id_t cred_handle, gss_name_t target_name)
953 {
954     OM_uint32 maj;
955     krb5_error_code code;
956     krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t)cred_handle;
957     krb5_gss_name_t tname = (krb5_gss_name_t)target_name;
958     krb5_principal client_princ;
959 
960     *minor_status = 0;
961 
962     maj = krb5_gss_validate_cred_1(minor_status, cred_handle, context);
963     if (maj != 0)
964         return maj;
965     k5_mutex_assert_locked(&cred->lock);
966 
967     if (cred->usage == GSS_C_ACCEPT || cred->name != NULL)
968         return GSS_S_COMPLETE;
969     /* acquire_init_cred should have set both name and ccache, or neither. */
970     assert(cred->ccache == NULL);
971 
972     if (tname != NULL) {
973         /* Use the target name to select an existing ccache or a principal. */
974         code = krb5_cc_select(context, tname->princ, &cred->ccache,
975                               &client_princ);
976         if (code && code != KRB5_CC_NOTFOUND)
977             goto kerr;
978         if (client_princ != NULL) {
979             code = kg_init_name(context, client_princ, NULL, NULL, NULL,
980                                 KG_INIT_NAME_NO_COPY, &cred->name);
981             if (code) {
982                 krb5_free_principal(context, client_princ);
983                 goto kerr;
984             }
985         }
986         if (cred->ccache != NULL) {
987             code = scan_ccache(context, cred);
988             if (code)
989                 goto kerr;
990         }
991     }
992 
993     /* If we still haven't picked a client principal, try using an existing
994      * default ccache.  (On Windows, this may acquire initial creds.) */
995     if (cred->name == NULL) {
996         code = krb5int_cc_default(context, &cred->ccache);
997         if (code)
998             goto kerr;
999         code = scan_ccache(context, cred);
1000         if (code == KRB5_FCC_NOFILE) {
1001             /* Default ccache doesn't exist; fall through to client keytab. */
1002             krb5_cc_close(context, cred->ccache);
1003             cred->ccache = NULL;
1004         } else if (code) {
1005             goto kerr;
1006         }
1007     }
1008 
1009     /* If that didn't work, try getting a name from the client keytab. */
1010     if (cred->name == NULL) {
1011         code = get_name_from_client_keytab(context, cred);
1012         if (code) {
1013             code = KG_EMPTY_CCACHE;
1014             goto kerr;
1015         }
1016     }
1017 
1018     if (cred->name != NULL && cred->ccache == NULL) {
1019         /* Pick a cache for the name we chose (from krb5_cc_select or from the
1020          * client keytab). */
1021         code = get_cache_for_name(context, cred);
1022         if (code)
1023             goto kerr;
1024     }
1025 
1026     /* Resolve name to ccache and possibly get initial credentials. */
1027     code = maybe_get_initial_cred(context, NULL, cred);
1028     if (code)
1029         goto kerr;
1030 
1031     return GSS_S_COMPLETE;
1032 
1033 kerr:
1034     k5_mutex_unlock(&cred->lock);
1035     save_error_info(code, context);
1036     *minor_status = code;
1037     return GSS_S_NO_CRED;
1038 }
1039 
1040 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)1041 gss_krb5int_set_cred_rcache(OM_uint32 *minor_status,
1042                             gss_cred_id_t *cred_handle,
1043                             const gss_OID desired_oid,
1044                             const gss_buffer_t value)
1045 {
1046     krb5_gss_cred_id_t cred;
1047     krb5_error_code code;
1048     krb5_context context;
1049     krb5_rcache rcache;
1050 
1051     assert(value->length == sizeof(rcache));
1052 
1053     if (value->length != sizeof(rcache))
1054         return GSS_S_FAILURE;
1055 
1056     rcache = (krb5_rcache)value->value;
1057 
1058     cred = (krb5_gss_cred_id_t)*cred_handle;
1059 
1060     code = krb5_gss_init_context(&context);
1061     if (code) {
1062         *minor_status = code;
1063         return GSS_S_FAILURE;
1064     }
1065     if (cred->rcache != NULL)
1066         k5_rc_close(context, cred->rcache);
1067 
1068     cred->rcache = rcache;
1069 
1070     krb5_free_context(context);
1071 
1072     *minor_status = 0;
1073     return GSS_S_COMPLETE;
1074 }
1075 
1076 /*
1077  * krb5 and IAKERB mech API functions follow.  The mechglue always passes null
1078  * desired_mechs and actual_mechs, so we ignore those parameters.
1079  */
1080 
1081 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)1082 krb5_gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
1083                       OM_uint32 time_req, gss_OID_set desired_mechs,
1084                       gss_cred_usage_t cred_usage,
1085                       gss_cred_id_t *output_cred_handle,
1086                       gss_OID_set *actual_mechs, OM_uint32 *time_rec)
1087 {
1088     return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage,
1089                         NULL, NULL, FALSE, output_cred_handle, time_rec);
1090 }
1091 
1092 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)1093 iakerb_gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
1094                         OM_uint32 time_req, gss_OID_set desired_mechs,
1095                         gss_cred_usage_t cred_usage,
1096                         gss_cred_id_t *output_cred_handle,
1097                         gss_OID_set *actual_mechs, OM_uint32 *time_rec)
1098 {
1099     return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage,
1100                         NULL, NULL, TRUE, output_cred_handle, time_rec);
1101 }
1102 
1103 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)1104 krb5_gss_acquire_cred_with_password(OM_uint32 *minor_status,
1105                                     const gss_name_t desired_name,
1106                                     const gss_buffer_t password,
1107                                     OM_uint32 time_req,
1108                                     const gss_OID_set desired_mechs,
1109                                     int cred_usage,
1110                                     gss_cred_id_t *output_cred_handle,
1111                                     gss_OID_set *actual_mechs,
1112                                     OM_uint32 *time_rec)
1113 {
1114     return acquire_cred(minor_status, desired_name, password, time_req,
1115                         cred_usage, NULL, NULL, FALSE, output_cred_handle,
1116                         time_rec);
1117 }
1118 
1119 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)1120 iakerb_gss_acquire_cred_with_password(OM_uint32 *minor_status,
1121                                       const gss_name_t desired_name,
1122                                       const gss_buffer_t password,
1123                                       OM_uint32 time_req,
1124                                       const gss_OID_set desired_mechs,
1125                                       int cred_usage,
1126                                       gss_cred_id_t *output_cred_handle,
1127                                       gss_OID_set *actual_mechs,
1128                                       OM_uint32 *time_rec)
1129 {
1130     return acquire_cred(minor_status, desired_name, password, time_req,
1131                         cred_usage, NULL, NULL, TRUE, output_cred_handle,
1132                         time_rec);
1133 }
1134 
1135 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)1136 gss_krb5int_import_cred(OM_uint32 *minor_status,
1137                         gss_cred_id_t *cred_handle,
1138                         const gss_OID desired_oid,
1139                         const gss_buffer_t value)
1140 {
1141     struct krb5_gss_import_cred_req *req;
1142     krb5_gss_name_rec name;
1143     OM_uint32 time_rec;
1144     krb5_error_code code;
1145     gss_cred_usage_t usage;
1146     gss_name_t desired_name = GSS_C_NO_NAME;
1147 
1148     assert(value->length == sizeof(*req));
1149 
1150     if (value->length != sizeof(*req))
1151         return GSS_S_FAILURE;
1152 
1153     req = (struct krb5_gss_import_cred_req *)value->value;
1154 
1155     if (req->id != NULL) {
1156         usage = (req->keytab != NULL) ? GSS_C_BOTH : GSS_C_INITIATE;
1157     } else if (req->keytab != NULL) {
1158         usage = GSS_C_ACCEPT;
1159     } else {
1160         *minor_status = EINVAL;
1161         return GSS_S_FAILURE;
1162     }
1163 
1164     if (req->keytab_principal != NULL) {
1165         memset(&name, 0, sizeof(name));
1166         code = k5_mutex_init(&name.lock);
1167         if (code != 0) {
1168             *minor_status = code;
1169             return GSS_S_FAILURE;
1170         }
1171         name.princ = req->keytab_principal;
1172         desired_name = (gss_name_t)&name;
1173     }
1174 
1175     code = acquire_cred(minor_status, desired_name, NULL, GSS_C_INDEFINITE,
1176                         usage, req->id, req->keytab, FALSE, cred_handle,
1177                         &time_rec);
1178     if (req->keytab_principal != NULL)
1179         k5_mutex_destroy(&name.lock);
1180     return code;
1181 }
1182 
1183 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)1184 acquire_cred_from(OM_uint32 *minor_status, const gss_name_t desired_name,
1185                   OM_uint32 time_req, const gss_OID_set desired_mechs,
1186                   gss_cred_usage_t cred_usage,
1187                   gss_const_key_value_set_t cred_store, krb5_boolean iakerb,
1188                   gss_cred_id_t *output_cred_handle,
1189                   gss_OID_set *actual_mechs, OM_uint32 *time_rec)
1190 {
1191     krb5_context context = NULL;
1192     krb5_error_code code = 0;
1193     krb5_keytab client_keytab = NULL;
1194     krb5_keytab keytab = NULL;
1195     krb5_ccache ccache = NULL;
1196     krb5_principal verify_princ = NULL;
1197     const char *rcname, *value;
1198     struct verify_params vparams = { NULL };
1199     const struct verify_params *verify = NULL;
1200     gss_buffer_desc pwbuf;
1201     gss_buffer_t password = NULL;
1202     OM_uint32 ret;
1203 
1204     code = gss_krb5int_initialize_library();
1205     if (code) {
1206         *minor_status = code;
1207         ret = GSS_S_FAILURE;
1208         goto out;
1209     }
1210 
1211     code = krb5_gss_init_context(&context);
1212     if (code) {
1213         *minor_status = code;
1214         ret = GSS_S_FAILURE;
1215         goto out;
1216     }
1217 
1218     ret = kg_value_from_cred_store(cred_store, KRB5_CS_CCACHE_URN, &value);
1219     if (GSS_ERROR(ret))
1220         goto out;
1221 
1222     if (value) {
1223         code = krb5_cc_resolve(context, value, &ccache);
1224         if (code != 0) {
1225             *minor_status = code;
1226             ret = GSS_S_NO_CRED;
1227             goto out;
1228         }
1229     }
1230 
1231     ret = kg_value_from_cred_store(cred_store, KRB5_CS_CLI_KEYTAB_URN, &value);
1232     if (GSS_ERROR(ret))
1233         goto out;
1234 
1235     if (value) {
1236         code = krb5_kt_resolve(context, value, &client_keytab);
1237         if (code != 0) {
1238             *minor_status = code;
1239             ret = GSS_S_NO_CRED;
1240             goto out;
1241         }
1242     }
1243 
1244     ret = kg_value_from_cred_store(cred_store, KRB5_CS_KEYTAB_URN, &value);
1245     if (GSS_ERROR(ret))
1246         goto out;
1247 
1248     if (value) {
1249         code = krb5_kt_resolve(context, value, &keytab);
1250         if (code != 0) {
1251             *minor_status = code;
1252             ret = GSS_S_NO_CRED;
1253             goto out;
1254         }
1255     }
1256 
1257     ret = kg_value_from_cred_store(cred_store, KRB5_CS_RCACHE_URN, &rcname);
1258     if (GSS_ERROR(ret))
1259         goto out;
1260 
1261     ret = kg_value_from_cred_store(cred_store, KRB5_CS_PASSWORD_URN, &value);
1262     if (GSS_ERROR(ret))
1263         goto out;
1264 
1265     if (value) {
1266         /* We must be acquiring an initiator cred with an explicit name.  A
1267          * password is mutually exclusive with a client keytab or ccache. */
1268         if (desired_name == GSS_C_NO_NAME) {
1269             ret = GSS_S_BAD_NAME;
1270             goto out;
1271         }
1272         if (cred_usage == GSS_C_ACCEPT || desired_name == GSS_C_NO_NAME ||
1273             ccache != NULL || client_keytab != NULL) {
1274             *minor_status = (OM_uint32)G_BAD_USAGE;
1275             ret = GSS_S_FAILURE;
1276             goto out;
1277         }
1278         pwbuf.length = strlen(value);
1279         pwbuf.value = (void *)value;
1280         password = &pwbuf;
1281     }
1282 
1283     ret = kg_value_from_cred_store(cred_store, KRB5_CS_VERIFY_URN, &value);
1284     if (GSS_ERROR(ret))
1285         goto out;
1286     if (value != NULL) {
1287         if (iakerb || password == NULL) {
1288             /* Only valid if acquiring cred with password, and not supported
1289              * with IAKERB. */
1290             *minor_status = G_BAD_USAGE;
1291             ret = GSS_S_FAILURE;
1292             goto out;
1293         }
1294         if (*value != '\0') {
1295             code = krb5_parse_name(context, value, &verify_princ);
1296             if (code != 0) {
1297                 *minor_status = code;
1298                 ret = GSS_S_FAILURE;
1299                 goto out;
1300             }
1301         }
1302         vparams.princ = verify_princ;
1303         vparams.keytab = keytab;
1304         verify = &vparams;
1305     }
1306     ret = acquire_cred_context(context, minor_status, desired_name, password,
1307                                time_req, cred_usage, ccache, client_keytab,
1308                                keytab, rcname, verify, iakerb,
1309                                output_cred_handle, time_rec);
1310 
1311 out:
1312     if (ccache != NULL)
1313         krb5_cc_close(context, ccache);
1314     if (client_keytab != NULL)
1315         krb5_kt_close(context, client_keytab);
1316     if (keytab != NULL)
1317         krb5_kt_close(context, keytab);
1318     krb5_free_principal(context, verify_princ);
1319     krb5_free_context(context);
1320     return ret;
1321 }
1322 
1323 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)1324 krb5_gss_acquire_cred_from(OM_uint32 *minor_status,
1325                            const gss_name_t desired_name,
1326                            OM_uint32 time_req,
1327                            const gss_OID_set desired_mechs,
1328                            gss_cred_usage_t cred_usage,
1329                            gss_const_key_value_set_t cred_store,
1330                            gss_cred_id_t *output_cred_handle,
1331                            gss_OID_set *actual_mechs,
1332                            OM_uint32 *time_rec)
1333 {
1334     return acquire_cred_from(minor_status, desired_name, time_req,
1335                              desired_mechs, cred_usage, cred_store,
1336                              FALSE, output_cred_handle, actual_mechs,
1337                              time_rec);
1338 }
1339 
1340 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)1341 iakerb_gss_acquire_cred_from(OM_uint32 *minor_status,
1342                              const gss_name_t desired_name,
1343                              OM_uint32 time_req,
1344                              const gss_OID_set desired_mechs,
1345                              gss_cred_usage_t cred_usage,
1346                              gss_const_key_value_set_t cred_store,
1347                              gss_cred_id_t *output_cred_handle,
1348                              gss_OID_set *actual_mechs,
1349                              OM_uint32 *time_rec)
1350 {
1351     return acquire_cred_from(minor_status, desired_name, time_req,
1352                              desired_mechs, cred_usage, cred_store,
1353                              TRUE, output_cred_handle, actual_mechs,
1354                              time_rec);
1355 }
1356