1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 /* 8 * Copyright (c) 1994,2003,2005 by the Massachusetts Institute of Technology. 9 * Copyright (c) 1994 CyberSAFE Corporation 10 * Copyright (c) 1993 Open Computing Security Group 11 * Copyright (c) 1990,1991 by the Massachusetts Institute of Technology. 12 * All Rights Reserved. 13 * 14 * Export of this software from the United States of America may 15 * require a specific license from the United States Government. 16 * It is the responsibility of any person or organization contemplating 17 * export to obtain such a license before exporting. 18 * 19 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 20 * distribute this software and its documentation for any purpose and 21 * without fee is hereby granted, provided that the above copyright 22 * notice appear in all copies and that both that copyright notice and 23 * this permission notice appear in supporting documentation, and that 24 * the name of M.I.T. not be used in advertising or publicity pertaining 25 * to distribution of the software without specific, written prior 26 * permission. Furthermore if you modify this software you must label 27 * your software as modified software and not distribute it in such a 28 * fashion that it might be confused with the original M.I.T. software. 29 * Neither M.I.T., the Open Computing Security Group, nor 30 * CyberSAFE Corporation make any representations about the suitability of 31 * this software for any purpose. It is provided "as is" without express 32 * or implied warranty. 33 * 34 * krb5_get_cred_from_kdc() and related functions: 35 * 36 * Get credentials from some KDC somewhere, possibly accumulating TGTs 37 * along the way. 38 */ 39 40 #include "k5-int.h" 41 #include <stdio.h> 42 #include "int-proto.h" 43 44 struct tr_state; 45 46 /* 47 * Ring buffer abstraction for TGTs returned from a ccache; avoids 48 * lots of excess copying. 49 */ 50 51 #define NCC_TGTS 2 52 struct cc_tgts { 53 krb5_creds cred[NCC_TGTS]; 54 int dirty[NCC_TGTS]; 55 unsigned int cur, nxt; 56 }; 57 58 /* NOTE: This only checks if NXT_TGT is CUR_CC_TGT. */ 59 #define NXT_TGT_IS_CACHED(ts) \ 60 ((ts)->nxt_tgt == (ts)->cur_cc_tgt) 61 62 #define MARK_CUR_CC_TGT_CLEAN(ts) \ 63 do { \ 64 (ts)->cc_tgts.dirty[(ts)->cc_tgts.cur] = 0; \ 65 } while (0) 66 67 static void init_cc_tgts(struct tr_state *); 68 static void shift_cc_tgts(struct tr_state *); 69 static void clean_cc_tgts(struct tr_state *); 70 71 /* 72 * State struct for do_traversal() and helpers. 73 * 74 * CUR_TGT and NXT_TGT can each point either into CC_TGTS or into 75 * KDC_TGTS. 76 * 77 * CUR_TGT is the "working" TGT, which will be used to obtain new 78 * TGTs. NXT_TGT will be CUR_TGT for the next iteration of the loop. 79 * 80 * Part of the baroqueness of this setup is to deal with annoying 81 * differences between krb5_cc_retrieve_cred() and 82 * krb5_get_cred_via_tkt(); krb5_cc_retrieve_cred() fills in a 83 * caller-allocated krb5_creds, while krb5_get_cred_via_tkt() 84 * allocates a krb5_creds for return. 85 */ 86 struct tr_state { 87 krb5_context ctx; 88 krb5_ccache ccache; 89 krb5_principal *kdc_list; 90 unsigned int nkdcs; 91 krb5_principal *cur_kdc; 92 krb5_principal *nxt_kdc; 93 krb5_principal *lst_kdc; 94 krb5_creds *cur_tgt; 95 krb5_creds *nxt_tgt; 96 krb5_creds **kdc_tgts; 97 struct cc_tgts cc_tgts; 98 krb5_creds *cur_cc_tgt; 99 krb5_creds *nxt_cc_tgt; 100 unsigned int ntgts; 101 }; 102 103 /* 104 * Debug support 105 */ 106 #ifdef DEBUG_GC_FRM_KDC 107 108 #define TR_DBG(ts, prog) tr_dbg(ts, prog) 109 #define TR_DBG_RET(ts, prog, ret) tr_dbg_ret(ts, prog, ret) 110 #define TR_DBG_RTREE(ts, prog, princ) tr_dbg_rtree(ts, prog, princ) 111 112 static void tr_dbg(struct tr_state *, const char *); 113 static void tr_dbg_ret(struct tr_state *, const char *, krb5_error_code); 114 static void tr_dbg_rtree(struct tr_state *, const char *, krb5_principal); 115 116 #else 117 118 #define TR_DBG(ts, prog) 119 #define TR_DBG_RET(ts, prog, ret) 120 #define TR_DBG_RTREE(ts, prog, princ) 121 122 #endif /* !DEBUG_GC_FRM_KDC */ 123 124 #ifdef DEBUG_REFERRALS 125 126 #define DPRINTF(x) printf x 127 #define DFPRINTF(x) fprintf x 128 #define DUMP_PRINC(x, y) krb5int_dbgref_dump_principal((x), (y)) 129 130 #else 131 132 #define DPRINTF(x) 133 #define DFPRINTF(x) 134 #define DUMP_PRINC(x, y) 135 136 #endif 137 138 /* Convert ticket flags to necessary KDC options */ 139 #define FLAGS2OPTS(flags) (flags & KDC_TKT_COMMON_MASK) 140 141 /* 142 * Certain krb5_cc_retrieve_cred() errors are soft errors when looking 143 * for a cross-realm TGT. 144 */ 145 #define HARD_CC_ERR(r) ((r) && (r) != KRB5_CC_NOTFOUND && \ 146 (r) != KRB5_CC_NOT_KTYPE) 147 148 #define IS_TGS_PRINC(c, p) \ 149 ((krb5_princ_size((c), (p)) == 2) && \ 150 (krb5_princ_component((c), (p), 0)->length == \ 151 KRB5_TGS_NAME_SIZE) && \ 152 (!memcmp(krb5_princ_component((c), (p), 0)->data, \ 153 KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE))) 154 155 /* 156 * Flags for ccache lookups of cross-realm TGTs. 157 * 158 * A cross-realm TGT may be issued by some other intermediate realm's 159 * KDC, so we use KRB5_TC_MATCH_SRV_NAMEONLY. 160 */ 161 /* 162 * Solaris Kerberos: 163 * Include KRB5_TC_MATCH_TIMES so as to ensure that the retrieved ticket 164 * isn't stale. 165 */ 166 #define RETR_FLAGS (KRB5_TC_MATCH_SRV_NAMEONLY | \ 167 KRB5_TC_SUPPORTED_KTYPES | \ 168 KRB5_TC_MATCH_TIMES) 169 170 /* 171 * Prototypes of helper functions 172 */ 173 static krb5_error_code tgt_mcred(krb5_context, krb5_principal, 174 krb5_principal, krb5_principal, krb5_creds *); 175 static krb5_error_code retr_local_tgt(struct tr_state *, krb5_principal); 176 static krb5_error_code try_ccache(struct tr_state *, krb5_creds *); 177 static krb5_error_code find_nxt_kdc(struct tr_state *); 178 static krb5_error_code try_kdc(struct tr_state *, krb5_creds *); 179 static krb5_error_code kdc_mcred(struct tr_state *, krb5_principal, 180 krb5_creds *mcreds); 181 static krb5_error_code next_closest_tgt(struct tr_state *, krb5_principal); 182 static krb5_error_code init_rtree(struct tr_state *, 183 krb5_principal, krb5_principal); 184 static krb5_error_code do_traversal(krb5_context ctx, krb5_ccache, 185 krb5_principal client, krb5_principal server, 186 krb5_creds *out_cc_tgt, krb5_creds **out_tgt, 187 krb5_creds ***out_kdc_tgts); 188 static krb5_error_code krb5_get_cred_from_kdc_opt(krb5_context, krb5_ccache, 189 krb5_creds *, krb5_creds **, krb5_creds ***, int); 190 191 /* 192 * init_cc_tgts() 193 * 194 * Initialize indices for cached-TGT ring buffer. Caller must zero 195 * CC_TGTS, CC_TGT_DIRTY arrays prior to calling. 196 */ 197 static void 198 init_cc_tgts(struct tr_state *ts) 199 { 200 201 ts->cc_tgts.cur = 0; 202 ts->cc_tgts.nxt = 1; 203 ts->cur_cc_tgt = &ts->cc_tgts.cred[0]; 204 ts->nxt_cc_tgt = &ts->cc_tgts.cred[1]; 205 } 206 207 /* 208 * shift_cc_tgts() 209 * 210 * Given a fresh assignment to NXT_CC_TGT, mark NXT_CC_TGT as dirty, 211 * and shift indices so old NXT_CC_TGT becomes new CUR_CC_TGT. Clean 212 * the new NXT_CC_TGT. 213 */ 214 static void 215 shift_cc_tgts(struct tr_state *ts) 216 { 217 unsigned int i; 218 struct cc_tgts *rb; 219 220 rb = &ts->cc_tgts; 221 i = rb->cur = rb->nxt; 222 rb->dirty[i] = 1; 223 ts->cur_cc_tgt = ts->nxt_cc_tgt; 224 225 i = (i + 1) % NCC_TGTS; 226 227 rb->nxt = i; 228 ts->nxt_cc_tgt = &rb->cred[i]; 229 if (rb->dirty[i]) { 230 krb5_free_cred_contents(ts->ctx, &rb->cred[i]); 231 rb->dirty[i] = 0; 232 } 233 } 234 235 /* 236 * clean_cc_tgts() 237 * 238 * Free CC_TGTS which were dirty, then mark them clean. 239 */ 240 static void 241 clean_cc_tgts(struct tr_state *ts) 242 { 243 unsigned int i; 244 struct cc_tgts *rb; 245 246 rb = &ts->cc_tgts; 247 for (i = 0; i < NCC_TGTS; i++) { 248 if (rb->dirty[i]) { 249 krb5_free_cred_contents(ts->ctx, &rb->cred[i]); 250 rb->dirty[i] = 0; 251 } 252 } 253 } 254 255 /* 256 * Debug support 257 */ 258 #ifdef DEBUG_GC_FRM_KDC 259 static void 260 tr_dbg(struct tr_state *ts, const char *prog) 261 { 262 krb5_error_code retval; 263 char *cur_tgt_str, *cur_kdc_str, *nxt_kdc_str; 264 265 cur_tgt_str = cur_kdc_str = nxt_kdc_str = NULL; 266 retval = krb5_unparse_name(ts->ctx, ts->cur_tgt->server, &cur_tgt_str); 267 if (retval) goto cleanup; 268 retval = krb5_unparse_name(ts->ctx, *ts->cur_kdc, &cur_kdc_str); 269 if (retval) goto cleanup; 270 retval = krb5_unparse_name(ts->ctx, *ts->nxt_kdc, &nxt_kdc_str); 271 if (retval) goto cleanup; 272 fprintf(stderr, "%s: cur_tgt %s\n", prog, cur_tgt_str); 273 fprintf(stderr, "%s: cur_kdc %s\n", prog, cur_kdc_str); 274 fprintf(stderr, "%s: nxt_kdc %s\n", prog, nxt_kdc_str); 275 cleanup: 276 if (cur_tgt_str) 277 krb5_free_unparsed_name(ts->ctx, cur_tgt_str); 278 if (cur_kdc_str) 279 krb5_free_unparsed_name(ts->ctx, cur_kdc_str); 280 if (nxt_kdc_str) 281 krb5_free_unparsed_name(ts->ctx, nxt_kdc_str); 282 } 283 284 static void 285 tr_dbg_ret(struct tr_state *ts, const char *prog, krb5_error_code ret) 286 { 287 fprintf(stderr, "%s: return %d (%s)\n", prog, (int)ret, 288 error_message(ret)); 289 } 290 291 static void 292 tr_dbg_rtree(struct tr_state *ts, const char *prog, krb5_principal princ) 293 { 294 char *str; 295 296 if (krb5_unparse_name(ts->ctx, princ, &str)) 297 return; 298 fprintf(stderr, "%s: %s\n", prog, str); 299 krb5_free_unparsed_name(ts->ctx, str); 300 } 301 #endif /* DEBUG_GC_FRM_KDC */ 302 303 /* 304 * tgt_mcred() 305 * 306 * Return MCREDS for use as a match criterion. 307 * 308 * Resulting credential has CLIENT as the client principal, and 309 * krbtgt/realm_of(DST)@realm_of(SRC) as the server principal. Zeroes 310 * MCREDS first, does not allocate MCREDS, and cleans MCREDS on 311 * failure. The peculiar ordering of DST and SRC args is for 312 * consistency with krb5_tgtname(). 313 */ 314 static krb5_error_code 315 tgt_mcred(krb5_context ctx, krb5_principal client, 316 krb5_principal dst, krb5_principal src, 317 krb5_creds *mcreds) 318 { 319 krb5_error_code retval; 320 321 retval = 0; 322 memset(mcreds, 0, sizeof(*mcreds)); 323 324 retval = krb5_copy_principal(ctx, client, &mcreds->client); 325 if (retval) 326 goto cleanup; 327 328 retval = krb5_tgtname(ctx, krb5_princ_realm(ctx, dst), 329 krb5_princ_realm(ctx, src), &mcreds->server); 330 if (retval) 331 goto cleanup; 332 333 cleanup: 334 if (retval) 335 krb5_free_cred_contents(ctx, mcreds); 336 337 return retval; 338 } 339 340 /* 341 * init_rtree() 342 * 343 * Populate KDC_LIST with the output of krb5_walk_realm_tree(). 344 */ 345 static krb5_error_code 346 init_rtree(struct tr_state *ts, 347 krb5_principal client, krb5_principal server) 348 { 349 krb5_error_code retval; 350 351 ts->kdc_list = NULL; 352 retval = krb5_walk_realm_tree(ts->ctx, krb5_princ_realm(ts->ctx, client), 353 krb5_princ_realm(ts->ctx, server), 354 &ts->kdc_list, KRB5_REALM_BRANCH_CHAR); 355 if (retval) 356 return retval; 357 358 for (ts->nkdcs = 0; ts->kdc_list[ts->nkdcs]; ts->nkdcs++) { 359 assert(krb5_princ_size(ts->ctx, ts->kdc_list[ts->nkdcs]) == 2); 360 TR_DBG_RTREE(ts, "init_rtree", ts->kdc_list[ts->nkdcs]); 361 } 362 assert(ts->nkdcs > 1); 363 ts->lst_kdc = ts->kdc_list + ts->nkdcs - 1; 364 365 ts->kdc_tgts = calloc(ts->nkdcs + 1, sizeof(krb5_creds)); 366 if (ts->kdc_tgts == NULL) 367 return ENOMEM; 368 369 return 0; 370 } 371 372 /* 373 * retr_local_tgt() 374 * 375 * Prime CUR_TGT with the cached TGT of the client's local realm. 376 */ 377 static krb5_error_code 378 retr_local_tgt(struct tr_state *ts, krb5_principal client) 379 { 380 krb5_error_code retval; 381 krb5_creds tgtq; 382 383 memset(&tgtq, 0, sizeof(tgtq)); 384 retval = tgt_mcred(ts->ctx, client, client, client, &tgtq); 385 if (retval) 386 return retval; 387 388 /* Match realm, unlike other ccache retrievals here. */ 389 retval = krb5_cc_retrieve_cred(ts->ctx, ts->ccache, 390 KRB5_TC_SUPPORTED_KTYPES, 391 &tgtq, ts->nxt_cc_tgt); 392 krb5_free_cred_contents(ts->ctx, &tgtq); 393 if (!retval) { 394 shift_cc_tgts(ts); 395 ts->nxt_tgt = ts->cur_tgt = ts->cur_cc_tgt; 396 } 397 return retval; 398 } 399 400 /* 401 * try_ccache() 402 * 403 * Attempt to retrieve desired NXT_TGT from ccache. Point NXT_TGT to 404 * it if successful. 405 */ 406 static krb5_error_code 407 try_ccache(struct tr_state *ts, krb5_creds *tgtq) 408 { 409 krb5_error_code retval; 410 krb5_timestamp saved_endtime; 411 412 TR_DBG(ts, "try_ccache"); 413 /* 414 * Solaris Kerberos: 415 * Ensure the retrieved cred isn't stale. 416 * Set endtime to now so krb5_cc_retrieve_cred won't return an expired ticket. 417 */ 418 saved_endtime = tgtq->times.endtime; 419 if ((retval = krb5_timeofday(ts->ctx, &(tgtq->times.endtime))) != 0) { 420 tgtq->times.endtime = saved_endtime; 421 return retval; 422 } 423 retval = krb5_cc_retrieve_cred(ts->ctx, ts->ccache, RETR_FLAGS, 424 tgtq, ts->nxt_cc_tgt); 425 if (!retval) { 426 shift_cc_tgts(ts); 427 ts->nxt_tgt = ts->cur_cc_tgt; 428 } 429 /* 430 * Solaris Kerberos: 431 * Ensure that tgtq->times.endtime is reset back to its original value so 432 * that if tgtq is used to request a ticket from the KDC it doesn't request 433 * a ticket with an endtime set to "now". 434 */ 435 tgtq->times.endtime = saved_endtime; 436 TR_DBG_RET(ts, "try_ccache", retval); 437 return retval; 438 } 439 440 /* 441 * find_nxt_kdc() 442 * 443 * A NXT_TGT gotten from an intermediate KDC might actually be a 444 * referral. Search KDC_LIST forward starting from CUR_KDC, looking 445 * for the KDC with the same remote realm as NXT_TGT. If we don't 446 * find it, the intermediate KDC is leading us off the transit path. 447 * 448 * Match on CUR_KDC's remote realm, not local realm, because, among 449 * other reasons, we can get a referral to the final realm; e.g., 450 * given 451 * 452 * KDC_LIST == { krbtgt/R1@R1, krbtgt/R2@R1, krbtgt/R3@R2, 453 * krbtgt/R4@R3, NULL } 454 * CUR_TGT->SERVER == krbtgt/R2@R1 455 * NXT_TGT->SERVER == krbtgt/R4@R2 456 * 457 * i.e., we got a ticket issued by R2 with remote realm R4, we want to 458 * find krbtgt/R4@R3, not krbtgt/R3@R2, even though we have no TGT 459 * with R3 as its local realm. 460 * 461 * Set up for next iteration of do_traversal() loop by pointing 462 * NXT_KDC to one entry forward of the match. 463 */ 464 static krb5_error_code 465 find_nxt_kdc(struct tr_state *ts) 466 { 467 krb5_data *r1, *r2; 468 krb5_principal *kdcptr; 469 470 TR_DBG(ts, "find_nxt_kdc"); 471 /* 472 * Solaris Kerberos: 473 * The following assertion is not be true for the case when 474 * ts->nxt points to a cached ticket and not to a freshly 475 * fetched TGT in ts->kdc_tgts. See changes in try_kdc() 476 */ 477 /* assert(ts->nxt_tgt == ts->kdc_tgts[ts->ntgts-1]); */ 478 if (krb5_princ_size(ts->ctx, ts->nxt_tgt->server) != 2) 479 return KRB5_KDCREP_MODIFIED; 480 481 r1 = krb5_princ_component(ts->ctx, ts->nxt_tgt->server, 1); 482 483 for (kdcptr = ts->cur_kdc + 1; *kdcptr != NULL; kdcptr++) { 484 485 r2 = krb5_princ_component(ts->ctx, *kdcptr, 1); 486 487 if (r1 != NULL && r2 != NULL && 488 r1->length == r2->length && 489 !memcmp(r1->data, r2->data, r1->length)) { 490 break; 491 } 492 } 493 if (*kdcptr == NULL) { 494 /* 495 * Not found; we probably got an unexpected realm referral. 496 * Don't touch NXT_KDC, thus allowing next_closest_tgt() to 497 * continue looping backwards. 498 */ 499 /* 500 * Solaris Kerberos: 501 * Only free the allocated creds if they are in kdc_tgts. If they 502 * are in cc_tgts no freeing is necessary. 503 */ 504 if (ts->ntgts > 0 && ts->nxt_tgt == ts->kdc_tgts[ts->ntgts-1]) { 505 /* Punt NXT_TGT from KDC_TGTS if bogus. */ 506 krb5_free_creds(ts->ctx, ts->kdc_tgts[--ts->ntgts]); 507 ts->kdc_tgts[ts->ntgts] = NULL; 508 } 509 TR_DBG_RET(ts, "find_nxt_kdc", KRB5_KDCREP_MODIFIED); 510 return KRB5_KDCREP_MODIFIED; 511 } 512 ts->nxt_kdc = kdcptr; 513 TR_DBG_RET(ts, "find_nxt_kdc", 0); 514 return 0; 515 } 516 517 /* 518 * try_kdc() 519 * 520 * Using CUR_TGT, attempt to get desired NXT_TGT. Update NXT_KDC if 521 * successful. 522 */ 523 static krb5_error_code 524 try_kdc(struct tr_state *ts, krb5_creds *tgtq) 525 { 526 krb5_error_code retval; 527 krb5_creds ltgtq; 528 krb5_creds *tmp_out_cred; 529 530 TR_DBG(ts, "try_kdc"); 531 /* This check should probably be in gc_via_tkt. */ 532 if (!krb5_c_valid_enctype(ts->cur_tgt->keyblock.enctype)) 533 return KRB5_PROG_ETYPE_NOSUPP; 534 535 ltgtq = *tgtq; 536 ltgtq.is_skey = FALSE; 537 ltgtq.ticket_flags = ts->cur_tgt->ticket_flags; 538 /* 539 * Solaris Kerberos: 540 * Store credential in a temporary ticket as we may not 541 * want to add it to ts->kdc_tgts if it is already in 542 * the cache. 543 */ 544 retval = krb5_get_cred_via_tkt(ts->ctx, ts->cur_tgt, 545 FLAGS2OPTS(ltgtq.ticket_flags), 546 ts->cur_tgt->addresses, 547 <gtq, &tmp_out_cred); 548 if (retval) { 549 ts->ntgts--; 550 ts->nxt_tgt = ts->cur_tgt; 551 TR_DBG_RET(ts, "try_kdc", retval); 552 return retval; 553 } 554 555 /* 556 * Solaris Kerberos: 557 * See if the returned creds are different to the requested creds. 558 * This can happen when the server returns a TGT "closer" to the 559 * desired realm. 560 */ 561 if (!(krb5_principal_compare(ts->ctx, tgtq->server, tmp_out_cred->server))) { 562 /* Not equal, ticket may already be in the cache */ 563 retval = try_ccache(ts, tmp_out_cred); 564 if (!retval) { 565 krb5_free_creds(ts->ctx, tmp_out_cred); 566 retval = find_nxt_kdc(ts); 567 return retval; 568 } 569 } 570 571 ts->kdc_tgts[ts->ntgts++] = tmp_out_cred; 572 ts->nxt_tgt = ts->kdc_tgts[ts->ntgts-1]; 573 retval = find_nxt_kdc(ts); 574 TR_DBG_RET(ts, "try_kdc", retval); 575 return retval; 576 } 577 578 /* 579 * kdc_mcred() 580 * 581 * Return MCREDS for use as a match criterion. 582 * 583 * Resulting credential has CLIENT as the client principal, and 584 * krbtgt/remote_realm(NXT_KDC)@local_realm(CUR_KDC) as the server 585 * principal. Zeroes MCREDS first, does not allocate MCREDS, and 586 * cleans MCREDS on failure. 587 */ 588 static krb5_error_code 589 kdc_mcred(struct tr_state *ts, krb5_principal client, krb5_creds *mcreds) 590 { 591 krb5_error_code retval; 592 krb5_data *rdst, *rsrc; 593 594 retval = 0; 595 memset(mcreds, 0, sizeof(*mcreds)); 596 597 rdst = krb5_princ_component(ts->ctx, *ts->nxt_kdc, 1); 598 rsrc = krb5_princ_component(ts->ctx, *ts->cur_kdc, 1); 599 retval = krb5_copy_principal(ts->ctx, client, &mcreds->client); 600 if (retval) 601 goto cleanup; 602 603 retval = krb5_tgtname(ts->ctx, rdst, rsrc, &mcreds->server); 604 if (retval) 605 goto cleanup; 606 607 cleanup: 608 if (retval) 609 krb5_free_cred_contents(ts->ctx, mcreds); 610 611 return retval; 612 } 613 614 /* 615 * next_closest_tgt() 616 * 617 * Using CUR_TGT, attempt to get the cross-realm TGT having its remote 618 * realm closest to the target principal's. Update NXT_TGT, NXT_KDC 619 * accordingly. 620 */ 621 static krb5_error_code 622 next_closest_tgt(struct tr_state *ts, krb5_principal client) 623 { 624 krb5_error_code retval; 625 krb5_creds tgtq; 626 627 retval = 0; 628 memset(&tgtq, 0, sizeof(tgtq)); 629 630 for (ts->nxt_kdc = ts->lst_kdc; 631 ts->nxt_kdc > ts->cur_kdc; 632 ts->nxt_kdc--) { 633 634 krb5_free_cred_contents(ts->ctx, &tgtq); 635 retval = kdc_mcred(ts, client, &tgtq); 636 if (retval) 637 goto cleanup; 638 /* Don't waste time retrying ccache for direct path. */ 639 if (ts->cur_kdc != ts->kdc_list || ts->nxt_kdc != ts->lst_kdc) { 640 retval = try_ccache(ts, &tgtq); 641 if (!retval) 642 break; 643 if (HARD_CC_ERR(retval)) 644 goto cleanup; 645 } 646 /* Not in the ccache, so talk to a KDC. */ 647 retval = try_kdc(ts, &tgtq); 648 if (!retval) { 649 break; 650 } 651 /* 652 * Because try_kdc() validates referral TGTs, it can return an 653 * error indicating a bogus referral. The loop continues when 654 * it gets a bogus referral, which is arguably the right 655 * thing. (Previous implementation unconditionally failed.) 656 */ 657 } 658 /* 659 * If we have a non-zero retval, we either have a hard error or we 660 * failed to find a closer TGT. 661 */ 662 cleanup: 663 krb5_free_cred_contents(ts->ctx, &tgtq); 664 return retval; 665 } 666 667 /* 668 * do_traversal() 669 * 670 * Find final TGT needed to get CLIENT a ticket for SERVER. Point 671 * OUT_TGT at the desired TGT, which may be an existing cached TGT 672 * (copied into OUT_CC_TGT) or one of the newly obtained TGTs 673 * (collected in OUT_KDC_TGTS). 674 * 675 * Get comfortable; this is somewhat complicated. 676 * 677 * Nomenclature: Cross-realm TGS principal names have the form: 678 * 679 * krbtgt/REMOTE@LOCAL 680 * 681 * krb5_walk_realm_tree() returns a list like: 682 * 683 * krbtgt/R1@R1, krbtgt/R2@R1, krbtgt/R3@R2, ... 684 * 685 * These are prinicpal names, not realm names. We only really use the 686 * remote parts of the TGT principal names. 687 * 688 * The do_traversal loop calls next_closest_tgt() to find the next 689 * closest TGT to the destination realm. next_closest_tgt() updates 690 * NXT_KDC for the following iteration of the do_traversal() loop. 691 * 692 * At the beginning of any given iteration of the do_traversal() loop, 693 * CUR_KDC's remote realm is the remote realm of CUR_TGT->SERVER. The 694 * local realms of CUR_KDC and CUR_TGT->SERVER may not match due to 695 * short-circuit paths provided by intermediate KDCs, e.g., CUR_KDC 696 * might be krbtgt/D@C, while CUR_TGT->SERVER is krbtgt/D@B. 697 * 698 * For example, given KDC_LIST of 699 * 700 * krbtgt/R1@R1, krbtgt/R2@R1, krbtgt/R3@R2, krbtgt/R4@R3, 701 * krbtgt/R5@R4 702 * 703 * The next_closest_tgt() loop moves NXT_KDC to the left starting from 704 * R5, stopping before it reaches CUR_KDC. When next_closest_tgt() 705 * returns, the do_traversal() loop updates CUR_KDC to be NXT_KDC, and 706 * calls next_closest_tgt() again. 707 * 708 * next_closest_tgt() at start of its loop: 709 * 710 * CUR NXT 711 * | | 712 * V V 713 * +----+----+----+----+----+ 714 * | R1 | R2 | R3 | R4 | R5 | 715 * +----+----+----+----+----+ 716 * 717 * next_closest_tgt() returns after finding a ticket for krbtgt/R3@R1: 718 * 719 * CUR NXT 720 * | | 721 * V V 722 * +----+----+----+----+----+ 723 * | R1 | R2 | R3 | R4 | R5 | 724 * +----+----+----+----+----+ 725 * 726 * do_traversal() updates CUR_KDC: 727 * 728 * NXT 729 * CUR 730 * | 731 * V 732 * +----+----+----+----+----+ 733 * | R1 | R2 | R3 | R4 | R5 | 734 * +----+----+----+----+----+ 735 * 736 * next_closest_tgt() at start of its loop: 737 * 738 * CUR NXT 739 * | | 740 * V V 741 * +----+----+----+----+----+ 742 * | R1 | R2 | R3 | R4 | R5 | 743 * +----+----+----+----+----+ 744 * 745 * etc. 746 * 747 * The algorithm executes in n*(n-1)/2 (the sum of integers from 1 to 748 * n-1) attempts in the worst case, i.e., each KDC only has a 749 * cross-realm ticket for the immediately following KDC in the transit 750 * path. Typically, short-circuit paths will cause execution occur 751 * faster than this worst-case scenario. 752 * 753 * When next_closest_tgt() updates NXT_KDC, it may not perform a 754 * simple increment from CUR_KDC, in part because some KDC may 755 * short-circuit pieces of the transit path. 756 */ 757 static krb5_error_code 758 do_traversal(krb5_context ctx, 759 krb5_ccache ccache, 760 krb5_principal client, 761 krb5_principal server, 762 krb5_creds *out_cc_tgt, 763 krb5_creds **out_tgt, 764 krb5_creds ***out_kdc_tgts) 765 { 766 krb5_error_code retval; 767 struct tr_state state, *ts; 768 769 *out_tgt = NULL; 770 *out_kdc_tgts = NULL; 771 ts = &state; 772 memset(ts, 0, sizeof(*ts)); 773 ts->ctx = ctx; 774 ts->ccache = ccache; 775 init_cc_tgts(ts); 776 777 retval = init_rtree(ts, client, server); 778 if (retval) 779 goto cleanup; 780 781 retval = retr_local_tgt(ts, client); 782 if (retval) 783 goto cleanup; 784 785 for (ts->cur_kdc = ts->kdc_list, ts->nxt_kdc = NULL; 786 ts->cur_kdc != NULL && ts->cur_kdc < ts->lst_kdc; 787 ts->cur_kdc = ts->nxt_kdc, ts->cur_tgt = ts->nxt_tgt) { 788 789 retval = next_closest_tgt(ts, client); 790 if (retval) 791 goto cleanup; 792 assert(ts->cur_kdc != ts->nxt_kdc); 793 } 794 795 if (NXT_TGT_IS_CACHED(ts)) { 796 *out_cc_tgt = *ts->cur_cc_tgt; 797 *out_tgt = out_cc_tgt; 798 MARK_CUR_CC_TGT_CLEAN(ts); 799 } else { 800 /* CUR_TGT is somewhere in KDC_TGTS; no need to copy. */ 801 *out_tgt = ts->nxt_tgt; 802 } 803 804 cleanup: 805 clean_cc_tgts(ts); 806 if (ts->kdc_list != NULL) 807 krb5_free_realm_tree(ctx, ts->kdc_list); 808 if (ts->ntgts == 0) { 809 *out_kdc_tgts = NULL; 810 if (ts->kdc_tgts != NULL) 811 free(ts->kdc_tgts); 812 } else 813 *out_kdc_tgts = ts->kdc_tgts; 814 return retval; 815 } 816 817 /* 818 * krb5_get_cred_from_kdc_opt() 819 * krb5_get_cred_from_kdc() 820 * krb5_get_cred_from_kdc_validate() 821 * krb5_get_cred_from_kdc_renew() 822 * 823 * Retrieve credentials for client IN_CRED->CLIENT, server 824 * IN_CRED->SERVER, ticket flags IN_CRED->TICKET_FLAGS, possibly 825 * second_ticket if needed. 826 * 827 * Request credentials from the KDC for the server's realm. Point 828 * TGTS to an allocated array of pointers to krb5_creds, containing 829 * any intermediate credentials obtained in the process of contacting 830 * the server's KDC; if no intermediate credentials were obtained, 831 * TGTS is a null pointer. Return intermediate credentials if 832 * intermediate KDCs provided credentials, even if no useful end 833 * ticket results. 834 * 835 * Caller must free TGTS, regardless of whether this function returns 836 * success. 837 * 838 * This function does NOT cache the intermediate TGTs. 839 * 840 * Do not call this routine if desired credentials are already cached. 841 * 842 * On success, OUT_CRED contains the desired credentials; the caller 843 * must free them. 844 * 845 * Beware memory management issues if you have modifications in mind. 846 * With the addition of referral support, it is now the case that *tgts, 847 * referral_tgts, tgtptr, referral_tgts, and *out_creds all may point to 848 * the same credential at different times. 849 * 850 * Returns errors, system errors. 851 */ 852 853 static krb5_error_code 854 krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache, 855 krb5_creds *in_cred, krb5_creds **out_cred, 856 krb5_creds ***tgts, int kdcopt) 857 { 858 krb5_error_code retval, subretval; 859 krb5_principal client, server, supplied_server, out_supplied_server; 860 krb5_creds tgtq, cc_tgt, *tgtptr, *referral_tgts[KRB5_REFERRAL_MAXHOPS]; 861 krb5_boolean old_use_conf_ktypes; 862 char **hrealms; 863 int referral_count, i; 864 865 /* 866 * Set up client and server pointers. Make a fresh and modifyable 867 * copy of the in_cred server and save the supplied version. 868 */ 869 client = in_cred->client; 870 if ((retval=krb5_copy_principal(context, in_cred->server, &server))) 871 return retval; 872 /* We need a second copy for the output creds. */ 873 if ((retval = krb5_copy_principal(context, server, 874 &out_supplied_server)) != 0 ) { 875 krb5_free_principal(context, server); 876 return retval; 877 } 878 supplied_server = in_cred->server; 879 in_cred->server=server; 880 881 DUMP_PRINC("gc_from_kdc initial client", client); 882 DUMP_PRINC("gc_from_kdc initial server", server); 883 memset(&cc_tgt, 0, sizeof(cc_tgt)); 884 memset(&tgtq, 0, sizeof(tgtq)); 885 memset(&referral_tgts, 0, sizeof(referral_tgts)); 886 887 tgtptr = NULL; 888 *tgts = NULL; 889 *out_cred=NULL; 890 old_use_conf_ktypes = context->use_conf_ktypes; 891 892 /* Copy client realm to server if no hint. */ 893 if (krb5_is_referral_realm(&server->realm)) { 894 /* Use the client realm. */ 895 DPRINTF(("gc_from_kdc: no server realm supplied, " 896 "using client realm.\n")); 897 krb5_free_data_contents(context, &server->realm); 898 if (!( server->realm.data = (char *)malloc(client->realm.length+1))) 899 return ENOMEM; 900 memcpy(server->realm.data, client->realm.data, client->realm.length); 901 server->realm.length = client->realm.length; 902 server->realm.data[server->realm.length] = 0; 903 } 904 /* 905 * Retreive initial TGT to match the specified server, either for the 906 * local realm in the default (referral) case or for the remote 907 * realm if we're starting someplace non-local. 908 */ 909 retval = tgt_mcred(context, client, server, client, &tgtq); 910 if (retval) 911 goto cleanup; 912 913 /* Fast path: Is it in the ccache? */ 914 context->use_conf_ktypes = 1; 915 916 /* 917 * Solaris Kerberos: 918 * Ensure the retrieved cred isn't stale. 919 * Set endtime to now so krb5_cc_retrieve_cred won't return an expired ticket. 920 */ 921 if ((retval = krb5_timeofday(context, &(tgtq.times.endtime))) != 0) { 922 goto cleanup; 923 } 924 retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS, 925 &tgtq, &cc_tgt); 926 if (!retval) { 927 tgtptr = &cc_tgt; 928 } else if (!HARD_CC_ERR(retval)) { 929 DPRINTF(("gc_from_kdc: starting do_traversal to find " 930 "initial TGT for referral\n")); 931 retval = do_traversal(context, ccache, client, server, 932 &cc_tgt, &tgtptr, tgts); 933 } 934 if (retval) { 935 DPRINTF(("gc_from_kdc: failed to find initial TGT for referral\n")); 936 goto cleanup; 937 } 938 939 DUMP_PRINC("gc_from_kdc: server as requested", supplied_server); 940 941 /* 942 * Try requesting a service ticket from our local KDC with referrals 943 * turned on. If the first referral succeeds, follow a referral-only 944 * path, otherwise fall back to old-style assumptions. 945 */ 946 947 for (referral_count = 0; 948 referral_count < KRB5_REFERRAL_MAXHOPS; 949 referral_count++) { 950 #if 0 951 DUMP_PRINC("gc_from_kdc: referral loop: tgt in use", tgtptr->server); 952 DUMP_PRINC("gc_from_kdc: referral loop: request is for", server); 953 #endif 954 retval = krb5_get_cred_via_tkt(context, tgtptr, 955 KDC_OPT_CANONICALIZE | 956 FLAGS2OPTS(tgtptr->ticket_flags) | 957 kdcopt | 958 (in_cred->second_ticket.length ? 959 KDC_OPT_ENC_TKT_IN_SKEY : 0), 960 tgtptr->addresses, in_cred, out_cred); 961 if (retval) { 962 DPRINTF(("gc_from_kdc: referral TGS-REQ request failed: <%s>\n", 963 error_message(retval))); 964 /* If we haven't gone anywhere yet, fail through to the 965 non-referral case. */ 966 if (referral_count==0) { 967 DPRINTF(("gc_from_kdc: initial referral failed; " 968 "punting to fallback.\n")); 969 break; 970 } 971 /* Otherwise, try the same query without canonicalization 972 set, and fail hard if that doesn't work. */ 973 DPRINTF(("gc_from_kdc: referral #%d failed; " 974 "retrying without option.\n", referral_count + 1)); 975 retval = krb5_get_cred_via_tkt(context, tgtptr, 976 FLAGS2OPTS(tgtptr->ticket_flags) | 977 kdcopt | 978 (in_cred->second_ticket.length ? 979 KDC_OPT_ENC_TKT_IN_SKEY : 0), 980 tgtptr->addresses, 981 in_cred, out_cred); 982 /* Whether or not that succeeded, we're done. */ 983 goto cleanup; 984 } 985 /* Referral request succeeded; let's see what it is. */ 986 if (krb5_principal_compare(context, in_cred->server, 987 (*out_cred)->server)) { 988 DPRINTF(("gc_from_kdc: request generated ticket " 989 "for requested server principal\n")); 990 DUMP_PRINC("gc_from_kdc final referred reply", 991 in_cred->server); 992 993 /* 994 * Check if the return enctype is one that we requested if 995 * needed. 996 */ 997 if (old_use_conf_ktypes || context->tgs_ktype_count == 0) 998 goto cleanup; 999 for (i = 0; i < context->tgs_ktype_count; i++) { 1000 if ((*out_cred)->keyblock.enctype == context->tgs_ktypes[i]) { 1001 /* Found an allowable etype, so we're done */ 1002 goto cleanup; 1003 } 1004 } 1005 /* 1006 * We need to try again, but this time use the 1007 * tgs_ktypes in the context. At this point we should 1008 * have all the tgts to succeed. 1009 */ 1010 1011 /* Free "wrong" credential */ 1012 krb5_free_creds(context, *out_cred); 1013 *out_cred = NULL; 1014 /* Re-establish tgs etypes */ 1015 context->use_conf_ktypes = old_use_conf_ktypes; 1016 retval = krb5_get_cred_via_tkt(context, tgtptr, 1017 KDC_OPT_CANONICALIZE | 1018 FLAGS2OPTS(tgtptr->ticket_flags) | 1019 kdcopt | 1020 (in_cred->second_ticket.length ? 1021 KDC_OPT_ENC_TKT_IN_SKEY : 0), 1022 tgtptr->addresses, 1023 in_cred, out_cred); 1024 goto cleanup; 1025 } 1026 else if (IS_TGS_PRINC(context, (*out_cred)->server)) { 1027 krb5_data *r1, *r2; 1028 1029 DPRINTF(("gc_from_kdc: request generated referral tgt\n")); 1030 DUMP_PRINC("gc_from_kdc credential received", 1031 (*out_cred)->server); 1032 1033 if (referral_count == 0) 1034 r1 = &tgtptr->server->data[1]; 1035 else 1036 r1 = &referral_tgts[referral_count-1]->server->data[1]; 1037 1038 r2 = &(*out_cred)->server->data[1]; 1039 if (r1->length == r2->length && 1040 !memcmp(r1->data, r2->data, r1->length)) { 1041 DPRINTF(("gc_from_kdc: referred back to " 1042 "previous realm; fall back\n")); 1043 krb5_free_creds(context, *out_cred); 1044 *out_cred = NULL; 1045 break; 1046 } 1047 /* Check for referral routing loop. */ 1048 for (i=0;i<referral_count;i++) { 1049 #if 0 1050 DUMP_PRINC("gc_from_kdc: loop compare #1", 1051 (*out_cred)->server); 1052 DUMP_PRINC("gc_from_kdc: loop compare #2", 1053 referral_tgts[i]->server); 1054 #endif 1055 if (krb5_principal_compare(context, 1056 (*out_cred)->server, 1057 referral_tgts[i]->server)) { 1058 DFPRINTF((stderr, 1059 "krb5_get_cred_from_kdc_opt: " 1060 "referral routing loop - " 1061 "got referral back to hop #%d\n", i)); 1062 retval=KRB5_KDC_UNREACH; 1063 goto cleanup; 1064 } 1065 } 1066 /* Point current tgt pointer at newly-received TGT. */ 1067 if (tgtptr == &cc_tgt) 1068 krb5_free_cred_contents(context, tgtptr); 1069 tgtptr=*out_cred; 1070 /* Save pointer to tgt in referral_tgts. */ 1071 referral_tgts[referral_count]=*out_cred; 1072 /* Copy krbtgt realm to server principal. */ 1073 krb5_free_data_contents(context, &server->realm); 1074 retval = krb5int_copy_data_contents(context, 1075 &tgtptr->server->data[1], 1076 &server->realm); 1077 if (retval) 1078 return retval; 1079 /* 1080 * Future work: rewrite server principal per any 1081 * supplied padata. 1082 */ 1083 } else { 1084 /* Not a TGT; punt to fallback. */ 1085 krb5_free_creds(context, *out_cred); 1086 *out_cred = NULL; 1087 break; 1088 } 1089 } 1090 1091 DUMP_PRINC("gc_from_kdc client at fallback", client); 1092 DUMP_PRINC("gc_from_kdc server at fallback", server); 1093 1094 /* 1095 * At this point referrals have been tried and have failed. Go 1096 * back to the server principal as originally issued and try the 1097 * conventional path. 1098 */ 1099 1100 /* 1101 * Referrals have failed. Look up fallback realm if not 1102 * originally provided. 1103 */ 1104 if (krb5_is_referral_realm(&supplied_server->realm)) { 1105 if (server->length >= 2) { 1106 retval=krb5_get_fallback_host_realm(context, &server->data[1], 1107 &hrealms); 1108 if (retval) goto cleanup; 1109 #if 0 1110 DPRINTF(("gc_from_kdc: using fallback realm of %s\n", 1111 hrealms[0])); 1112 #endif 1113 krb5_free_data_contents(context,&in_cred->server->realm); 1114 server->realm.data=hrealms[0]; 1115 server->realm.length=strlen(hrealms[0]); 1116 free(hrealms); 1117 } 1118 else { 1119 /* 1120 * Problem case: Realm tagged for referral but apparently not 1121 * in a <type>/<host> format that 1122 * krb5_get_fallback_host_realm can deal with. 1123 */ 1124 DPRINTF(("gc_from_kdc: referral specified " 1125 "but no fallback realm avaiable!\n")); 1126 return KRB5_ERR_HOST_REALM_UNKNOWN; 1127 } 1128 } 1129 1130 DUMP_PRINC("gc_from_kdc server at fallback after fallback rewrite", 1131 server); 1132 1133 /* 1134 * Get a TGT for the target realm. 1135 */ 1136 1137 krb5_free_cred_contents(context, &tgtq); 1138 retval = tgt_mcred(context, client, server, client, &tgtq); 1139 if (retval) 1140 goto cleanup; 1141 1142 /* Fast path: Is it in the ccache? */ 1143 /* Free tgtptr data if reused from above. */ 1144 if (tgtptr == &cc_tgt) 1145 krb5_free_cred_contents(context, tgtptr); 1146 /* Free TGTS if previously filled by do_traversal() */ 1147 if (*tgts != NULL) { 1148 for (i = 0; (*tgts)[i] != NULL; i++) { 1149 krb5_free_creds(context, (*tgts)[i]); 1150 } 1151 free(*tgts); 1152 *tgts = NULL; 1153 } 1154 context->use_conf_ktypes = 1; 1155 /* 1156 * Solaris Kerberos: 1157 * Ensure the retrieved cred isn't stale. 1158 * Set endtime to now so krb5_cc_retrieve_cred won't return an expired ticket. 1159 */ 1160 if ((retval = krb5_timeofday(context, &(tgtq.times.endtime))) != 0) { 1161 goto cleanup; 1162 } 1163 retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS, 1164 &tgtq, &cc_tgt); 1165 if (!retval) { 1166 tgtptr = &cc_tgt; 1167 } else if (!HARD_CC_ERR(retval)) { 1168 retval = do_traversal(context, ccache, client, server, 1169 &cc_tgt, &tgtptr, tgts); 1170 } 1171 if (retval) 1172 goto cleanup; 1173 1174 /* 1175 * Finally have TGT for target realm! Try using it to get creds. 1176 */ 1177 1178 if (!krb5_c_valid_enctype(tgtptr->keyblock.enctype)) { 1179 retval = KRB5_PROG_ETYPE_NOSUPP; 1180 goto cleanup; 1181 } 1182 1183 context->use_conf_ktypes = old_use_conf_ktypes; 1184 retval = krb5_get_cred_via_tkt(context, tgtptr, 1185 FLAGS2OPTS(tgtptr->ticket_flags) | 1186 kdcopt | 1187 (in_cred->second_ticket.length ? 1188 KDC_OPT_ENC_TKT_IN_SKEY : 0), 1189 tgtptr->addresses, in_cred, out_cred); 1190 1191 cleanup: 1192 krb5_free_cred_contents(context, &tgtq); 1193 if (tgtptr == &cc_tgt) 1194 krb5_free_cred_contents(context, tgtptr); 1195 context->use_conf_ktypes = old_use_conf_ktypes; 1196 /* Drop the original principal back into in_cred so that it's cached 1197 in the expected format. */ 1198 DUMP_PRINC("gc_from_kdc: final hacked server principal at cleanup", 1199 server); 1200 krb5_free_principal(context, server); 1201 in_cred->server = supplied_server; 1202 if (*out_cred && !retval) { 1203 /* Success: free server, swap supplied server back in. */ 1204 krb5_free_principal (context, (*out_cred)->server); 1205 (*out_cred)->server= out_supplied_server; 1206 } 1207 else { 1208 /* 1209 * Failure: free out_supplied_server. Don't free out_cred here 1210 * since it's either null or a referral TGT that we free below, 1211 * and we may need it to return. 1212 */ 1213 krb5_free_principal (context, out_supplied_server); 1214 } 1215 DUMP_PRINC("gc_from_kdc: final server after reversion", in_cred->server); 1216 /* 1217 * Deal with ccache TGT management: If tgts has been set from 1218 * initial non-referral TGT discovery, leave it alone. Otherwise, if 1219 * referral_tgts[0] exists return it as the only entry in tgts. 1220 * (Further referrals are never cached, only the referral from the 1221 * local KDC.) This is part of cleanup because useful received TGTs 1222 * should be cached even if the main request resulted in failure. 1223 */ 1224 1225 if (*tgts == NULL) { 1226 if (referral_tgts[0]) { 1227 #if 0 1228 /* 1229 * This should possibly be a check on the candidate return 1230 * credential against the cache, in the circumstance where we 1231 * don't want to clutter the cache with near-duplicate 1232 * credentials on subsequent iterations. For now, it is 1233 * disabled. 1234 */ 1235 subretval=...?; 1236 if (subretval) { 1237 #endif 1238 /* Allocate returnable TGT list. */ 1239 if (!(*tgts=calloc(sizeof (krb5_creds *), 2))) 1240 return ENOMEM; 1241 subretval=krb5_copy_creds(context, referral_tgts[0], &((*tgts)[0])); 1242 if(subretval) 1243 return subretval; 1244 (*tgts)[1]=NULL; 1245 DUMP_PRINC("gc_from_kdc: returning referral TGT for ccache", 1246 (*tgts)[0]->server); 1247 #if 0 1248 } 1249 #endif 1250 } 1251 } 1252 1253 /* Free referral TGTs list. */ 1254 for (i=0;i<KRB5_REFERRAL_MAXHOPS;i++) { 1255 if(referral_tgts[i]) { 1256 krb5_free_creds(context, referral_tgts[i]); 1257 } 1258 } 1259 DPRINTF(("gc_from_kdc finishing with %s\n", 1260 retval ? error_message(retval) : "no error")); 1261 return retval; 1262 } 1263 1264 krb5_error_code 1265 krb5_get_cred_from_kdc(krb5_context context, krb5_ccache ccache, 1266 krb5_creds *in_cred, krb5_creds **out_cred, 1267 krb5_creds ***tgts) 1268 { 1269 return krb5_get_cred_from_kdc_opt(context, ccache, in_cred, out_cred, tgts, 1270 0); 1271 } 1272 1273 krb5_error_code 1274 krb5_get_cred_from_kdc_validate(krb5_context context, krb5_ccache ccache, 1275 krb5_creds *in_cred, krb5_creds **out_cred, 1276 krb5_creds ***tgts) 1277 { 1278 return krb5_get_cred_from_kdc_opt(context, ccache, in_cred, out_cred, tgts, 1279 KDC_OPT_VALIDATE); 1280 } 1281 1282 krb5_error_code 1283 krb5_get_cred_from_kdc_renew(krb5_context context, krb5_ccache ccache, 1284 krb5_creds *in_cred, krb5_creds **out_cred, 1285 krb5_creds ***tgts) 1286 { 1287 return krb5_get_cred_from_kdc_opt(context, ccache, in_cred, out_cred, tgts, 1288 KDC_OPT_RENEW); 1289 } 1290