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
init_cc_tgts(struct tr_state * ts)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
shift_cc_tgts(struct tr_state * ts)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
clean_cc_tgts(struct tr_state * ts)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
tr_dbg(struct tr_state * ts,const char * prog)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
tr_dbg_ret(struct tr_state * ts,const char * prog,krb5_error_code ret)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
tr_dbg_rtree(struct tr_state * ts,const char * prog,krb5_principal princ)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
tgt_mcred(krb5_context ctx,krb5_principal client,krb5_principal dst,krb5_principal src,krb5_creds * mcreds)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
init_rtree(struct tr_state * ts,krb5_principal client,krb5_principal server)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
retr_local_tgt(struct tr_state * ts,krb5_principal client)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
try_ccache(struct tr_state * ts,krb5_creds * tgtq)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
find_nxt_kdc(struct tr_state * ts)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
try_kdc(struct tr_state * ts,krb5_creds * tgtq)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
kdc_mcred(struct tr_state * ts,krb5_principal client,krb5_creds * mcreds)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
next_closest_tgt(struct tr_state * ts,krb5_principal client)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
do_traversal(krb5_context ctx,krb5_ccache ccache,krb5_principal client,krb5_principal server,krb5_creds * out_cc_tgt,krb5_creds ** out_tgt,krb5_creds *** out_kdc_tgts)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
krb5_get_cred_from_kdc_opt(krb5_context context,krb5_ccache ccache,krb5_creds * in_cred,krb5_creds ** out_cred,krb5_creds *** tgts,int kdcopt)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
krb5_get_cred_from_kdc(krb5_context context,krb5_ccache ccache,krb5_creds * in_cred,krb5_creds ** out_cred,krb5_creds *** tgts)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
krb5_get_cred_from_kdc_validate(krb5_context context,krb5_ccache ccache,krb5_creds * in_cred,krb5_creds ** out_cred,krb5_creds *** tgts)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
krb5_get_cred_from_kdc_renew(krb5_context context,krb5_ccache ccache,krb5_creds * in_cred,krb5_creds ** out_cred,krb5_creds *** tgts)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