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