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