/* * try_kdc() * * Using CUR_TGT, attempt to get desired NXT_TGT. Update NXT_KDC if * successful. */ static krb5_error_code try_kdc(struct tr_state *ts, krb5_creds *tgtq) { krb5_error_code retval; krb5_creds ltgtq; TR_DBG(ts, "try_kdc"); /* This check should probably be in gc_via_tkt. */ if (!krb5_c_valid_enctype(ts->cur_tgt->keyblock.enctype)) return KRB5_PROG_ETYPE_NOSUPP; ltgtq = *tgtq; ltgtq.is_skey = FALSE; ltgtq.ticket_flags = ts->cur_tgt->ticket_flags; retval = krb5_get_cred_via_tkt(ts->ctx, ts->cur_tgt, FLAGS2OPTS(ltgtq.ticket_flags), ts->cur_tgt->addresses, <gtq, &ts->kdc_tgts[ts->ntgts++]); if (retval) { ts->ntgts--; ts->nxt_tgt = ts->cur_tgt; TR_DBG_RET(ts, "try_kdc", retval); return retval; } ts->nxt_tgt = ts->kdc_tgts[ts->ntgts-1]; retval = find_nxt_kdc(ts); TR_DBG_RET(ts, "try_kdc", retval); return retval; }
/* * find_nxt_kdc() * * A NXT_TGT gotten from an intermediate KDC might actually be a * referral. Search KDC_LIST forward starting from CUR_KDC, looking * for the KDC with the same remote realm as NXT_TGT. If we don't * find it, the intermediate KDC is leading us off the transit path. * * Match on CUR_KDC's remote realm, not local realm, because, among * other reasons, we can get a referral to the final realm; e.g., * given * * KDC_LIST == { krbtgt/R1@R1, krbtgt/R2@R1, krbtgt/R3@R2, * krbtgt/R4@R3, NULL } * CUR_TGT->SERVER == krbtgt/R2@R1 * NXT_TGT->SERVER == krbtgt/R4@R2 * * i.e., we got a ticket issued by R2 with remote realm R4, we want to * find krbtgt/R4@R3, not krbtgt/R3@R2, even though we have no TGT * with R3 as its local realm. * * Set up for next iteration of do_traversal() loop by pointing * NXT_KDC to one entry forward of the match. */ static krb5_error_code find_nxt_kdc(struct tr_state *ts) { krb5_data *r1, *r2; krb5_principal *kdcptr; TR_DBG(ts, "find_nxt_kdc"); assert(ts->ntgts > 0); assert(ts->nxt_tgt == ts->kdc_tgts[ts->ntgts-1]); if (krb5_princ_size(ts->ctx, ts->nxt_tgt->server) != 2) return KRB5_KDCREP_MODIFIED; r1 = krb5_princ_component(ts->ctx, ts->nxt_tgt->server, 1); for (kdcptr = ts->cur_kdc + 1; *kdcptr != NULL; kdcptr++) { r2 = krb5_princ_component(ts->ctx, *kdcptr, 1); if (r1 != NULL && r2 != NULL && data_eq(*r1, *r2)) { break; } } if (*kdcptr != NULL) { ts->nxt_kdc = kdcptr; TR_DBG_RET(ts, "find_nxt_kdc", 0); return 0; } r2 = krb5_princ_component(ts->ctx, ts->kdc_list[0], 1); if (r1 != NULL && r2 != NULL && r1->length == r2->length && !memcmp(r1->data, r2->data, r1->length)) { TR_DBG_RET(ts, "find_nxt_kdc: looped back to local", KRB5_KDCREP_MODIFIED); return KRB5_KDCREP_MODIFIED; } /* * Realm is not in our list; we probably got an unexpected realm * referral. */ ts->offpath_tgt = ts->nxt_tgt; if (ts->cur_kdc == ts->kdc_list) { /* * Local KDC referred us off path; trust it for caching * purposes. */ return 0; } /* * Unlink the off-path TGT from KDC_TGTS but don't free it, * because we should return it. */ ts->kdc_tgts[--ts->ntgts] = NULL; ts->nxt_tgt = ts->cur_tgt; TR_DBG_RET(ts, "find_nxt_kdc", 0); return 0; }
/* * try_kdc() * * Using CUR_TGT, attempt to get desired NXT_TGT. Update NXT_KDC if * successful. */ static krb5_error_code try_kdc(struct tr_state *ts, krb5_creds *tgtq) { krb5_error_code retval; krb5_creds ltgtq; krb5_creds *tmp_out_cred; TR_DBG(ts, "try_kdc"); /* This check should probably be in gc_via_tkt. */ if (!krb5_c_valid_enctype(ts->cur_tgt->keyblock.enctype)) return KRB5_PROG_ETYPE_NOSUPP; ltgtq = *tgtq; ltgtq.is_skey = FALSE; ltgtq.ticket_flags = ts->cur_tgt->ticket_flags; /* * Solaris Kerberos: * Store credential in a temporary ticket as we may not * want to add it to ts->kdc_tgts if it is already in * the cache. */ retval = krb5_get_cred_via_tkt(ts->ctx, ts->cur_tgt, FLAGS2OPTS(ltgtq.ticket_flags), ts->cur_tgt->addresses, <gtq, &tmp_out_cred); if (retval) { ts->ntgts--; ts->nxt_tgt = ts->cur_tgt; TR_DBG_RET(ts, "try_kdc", retval); return retval; } /* * Solaris Kerberos: * See if the returned creds are different to the requested creds. * This can happen when the server returns a TGT "closer" to the * desired realm. */ if (!(krb5_principal_compare(ts->ctx, tgtq->server, tmp_out_cred->server))) { /* Not equal, ticket may already be in the cache */ retval = try_ccache(ts, tmp_out_cred); if (!retval) { krb5_free_creds(ts->ctx, tmp_out_cred); retval = find_nxt_kdc(ts); return retval; } } ts->kdc_tgts[ts->ntgts++] = tmp_out_cred; ts->nxt_tgt = ts->kdc_tgts[ts->ntgts-1]; retval = find_nxt_kdc(ts); TR_DBG_RET(ts, "try_kdc", retval); return retval; }
/* * try_ccache() * * Attempt to retrieve desired NXT_TGT from ccache. Point NXT_TGT to * it if successful. */ static krb5_error_code try_ccache(struct tr_state *ts, krb5_creds *tgtq) { krb5_error_code retval; krb5_timestamp saved_endtime; TR_DBG(ts, "try_ccache"); /* * Solaris Kerberos: * Ensure the retrieved cred isn't stale. * Set endtime to now so krb5_cc_retrieve_cred won't return an expired ticket. */ saved_endtime = tgtq->times.endtime; if ((retval = krb5_timeofday(ts->ctx, &(tgtq->times.endtime))) != 0) { tgtq->times.endtime = saved_endtime; return retval; } retval = krb5_cc_retrieve_cred(ts->ctx, ts->ccache, RETR_FLAGS, tgtq, ts->nxt_cc_tgt); if (!retval) { shift_cc_tgts(ts); ts->nxt_tgt = ts->cur_cc_tgt; } /* * Solaris Kerberos: * Ensure that tgtq->times.endtime is reset back to its original value so * that if tgtq is used to request a ticket from the KDC it doesn't request * a ticket with an endtime set to "now". */ tgtq->times.endtime = saved_endtime; TR_DBG_RET(ts, "try_ccache", retval); return retval; }
/* * try_ccache() * * Attempt to retrieve desired NXT_TGT from ccache. Point NXT_TGT to * it if successful. */ static krb5_error_code try_ccache(struct tr_state *ts, krb5_creds *tgtq) { krb5_error_code retval; TR_DBG(ts, "try_ccache"); retval = krb5_cc_retrieve_cred(ts->ctx, ts->ccache, RETR_FLAGS, tgtq, ts->nxt_cc_tgt); if (!retval) { shift_cc_tgts(ts); ts->nxt_tgt = ts->cur_cc_tgt; } TR_DBG_RET(ts, "try_ccache", retval); return retval; }
/* * find_nxt_kdc() * * A NXT_TGT gotten from an intermediate KDC might actually be a * referral. Search KDC_LIST forward starting from CUR_KDC, looking * for the KDC with the same remote realm as NXT_TGT. If we don't * find it, the intermediate KDC is leading us off the transit path. * * Match on CUR_KDC's remote realm, not local realm, because, among * other reasons, we can get a referral to the final realm; e.g., * given * * KDC_LIST == { krbtgt/R1@R1, krbtgt/R2@R1, krbtgt/R3@R2, * krbtgt/R4@R3, NULL } * CUR_TGT->SERVER == krbtgt/R2@R1 * NXT_TGT->SERVER == krbtgt/R4@R2 * * i.e., we got a ticket issued by R2 with remote realm R4, we want to * find krbtgt/R4@R3, not krbtgt/R3@R2, even though we have no TGT * with R3 as its local realm. * * Set up for next iteration of do_traversal() loop by pointing * NXT_KDC to one entry forward of the match. */ static krb5_error_code find_nxt_kdc(struct tr_state *ts) { krb5_data *r1, *r2; krb5_principal *kdcptr; TR_DBG(ts, "find_nxt_kdc"); /* * Solaris Kerberos: * The following assertion is not be true for the case when * ts->nxt points to a cached ticket and not to a freshly * fetched TGT in ts->kdc_tgts. See changes in try_kdc() */ /* assert(ts->nxt_tgt == ts->kdc_tgts[ts->ntgts-1]); */ if (krb5_princ_size(ts->ctx, ts->nxt_tgt->server) != 2) { /* Solaris Kerberos */ char *s_name = NULL; int err = krb5_unparse_name(ts->ctx, ts->nxt_tgt->server, &s_name); if (!err) { krb5_set_error_message(ts->ctx, KRB5_KDCREP_MODIFIED, dgettext(TEXT_DOMAIN, "KDC reply did not match expectations: server '%s' principal size should be 2"), s_name); krb5_free_unparsed_name(ts->ctx, s_name); } else krb5_set_error_message(ts->ctx, KRB5_KDCREP_MODIFIED, dgettext(TEXT_DOMAIN, "KDC reply did not match expectations: server principal size should be 2")); return KRB5_KDCREP_MODIFIED; } r1 = krb5_princ_component(ts->ctx, ts->nxt_tgt->server, 1); for (kdcptr = ts->cur_kdc + 1; *kdcptr != NULL; kdcptr++) { r2 = krb5_princ_component(ts->ctx, *kdcptr, 1); if (r1 != NULL && r2 != NULL && r1->length == r2->length && !memcmp(r1->data, r2->data, r1->length)) { break; } } if (*kdcptr == NULL) { /* * Not found; we probably got an unexpected realm referral. * Don't touch NXT_KDC, thus allowing next_closest_tgt() to * continue looping backwards. */ /* * Solaris Kerberos: * Only free the allocated creds if they are in kdc_tgts. If they * are in cc_tgts no freeing is necessary. */ if (ts->ntgts > 0 && ts->nxt_tgt == ts->kdc_tgts[ts->ntgts-1]) { /* Punt NXT_TGT from KDC_TGTS if bogus. */ krb5_free_creds(ts->ctx, ts->kdc_tgts[--ts->ntgts]); ts->kdc_tgts[ts->ntgts] = NULL; } TR_DBG_RET(ts, "find_nxt_kdc", KRB5_KDCREP_MODIFIED); krb5_set_error_message(ts->ctx, KRB5_KDCREP_MODIFIED, dgettext(TEXT_DOMAIN, "KDC reply did not match expectation: KDC not found. Probably got an unexpected realm referral")); return KRB5_KDCREP_MODIFIED; } ts->nxt_kdc = kdcptr; TR_DBG_RET(ts, "find_nxt_kdc", 0); return 0; }