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