/* * do_traversal() * * Find final TGT needed to get CLIENT a ticket for SERVER. Point * OUT_TGT at the desired TGT, which may be an existing cached TGT * (copied into OUT_CC_TGT) or one of the newly obtained TGTs * (collected in OUT_KDC_TGTS). * * Get comfortable; this is somewhat complicated. * * Nomenclature: Cross-realm TGS principal names have the form: * * krbtgt/REMOTE@LOCAL * * krb5_walk_realm_tree() returns a list like: * * krbtgt/R1@R1, krbtgt/R2@R1, krbtgt/R3@R2, ... * * These are prinicpal names, not realm names. We only really use the * remote parts of the TGT principal names. * * The do_traversal loop calls next_closest_tgt() to find the next * closest TGT to the destination realm. next_closest_tgt() updates * NXT_KDC for the following iteration of the do_traversal() loop. * * At the beginning of any given iteration of the do_traversal() loop, * CUR_KDC's remote realm is the remote realm of CUR_TGT->SERVER. The * local realms of CUR_KDC and CUR_TGT->SERVER may not match due to * short-circuit paths provided by intermediate KDCs, e.g., CUR_KDC * might be krbtgt/D@C, while CUR_TGT->SERVER is krbtgt/D@B. * * For example, given KDC_LIST of * * krbtgt/R1@R1, krbtgt/R2@R1, krbtgt/R3@R2, krbtgt/R4@R3, * krbtgt/R5@R4 * * The next_closest_tgt() loop moves NXT_KDC to the left starting from * R5, stopping before it reaches CUR_KDC. When next_closest_tgt() * returns, the do_traversal() loop updates CUR_KDC to be NXT_KDC, and * calls next_closest_tgt() again. * * next_closest_tgt() at start of its loop: * * CUR NXT * | | * V V * +----+----+----+----+----+ * | R1 | R2 | R3 | R4 | R5 | * +----+----+----+----+----+ * * next_closest_tgt() returns after finding a ticket for krbtgt/R3@R1: * * CUR NXT * | | * V V * +----+----+----+----+----+ * | R1 | R2 | R3 | R4 | R5 | * +----+----+----+----+----+ * * do_traversal() updates CUR_KDC: * * NXT * CUR * | * V * +----+----+----+----+----+ * | R1 | R2 | R3 | R4 | R5 | * +----+----+----+----+----+ * * next_closest_tgt() at start of its loop: * * CUR NXT * | | * V V * +----+----+----+----+----+ * | R1 | R2 | R3 | R4 | R5 | * +----+----+----+----+----+ * * etc. * * The algorithm executes in n*(n-1)/2 (the sum of integers from 1 to * n-1) attempts in the worst case, i.e., each KDC only has a * cross-realm ticket for the immediately following KDC in the transit * path. Typically, short-circuit paths will cause execution occur * faster than this worst-case scenario. * * When next_closest_tgt() updates NXT_KDC, it may not perform a * simple increment from CUR_KDC, in part because some KDC may * short-circuit pieces of the transit path. */ 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) { krb5_error_code retval; struct tr_state state, *ts; *out_tgt = NULL; *out_kdc_tgts = NULL; ts = &state; memset(ts, 0, sizeof(*ts)); ts->ctx = ctx; ts->ccache = ccache; init_cc_tgts(ts); retval = init_rtree(ts, client, server); if (retval) goto cleanup; retval = retr_local_tgt(ts, client); if (retval) goto cleanup; for (ts->cur_kdc = ts->kdc_list, ts->nxt_kdc = NULL; ts->cur_kdc != NULL && ts->cur_kdc < ts->lst_kdc; ts->cur_kdc = ts->nxt_kdc, ts->cur_tgt = ts->nxt_tgt) { retval = next_closest_tgt(ts, client); if (retval) goto cleanup; assert(ts->cur_kdc != ts->nxt_kdc); } if (NXT_TGT_IS_CACHED(ts)) { *out_cc_tgt = *ts->cur_cc_tgt; *out_tgt = out_cc_tgt; MARK_CUR_CC_TGT_CLEAN(ts); } else { /* CUR_TGT is somewhere in KDC_TGTS; no need to copy. */ *out_tgt = ts->nxt_tgt; } cleanup: clean_cc_tgts(ts); if (ts->kdc_list != NULL) krb5_free_realm_tree(ctx, ts->kdc_list); if (ts->ntgts == 0) { *out_kdc_tgts = NULL; if (ts->kdc_tgts != NULL) free(ts->kdc_tgts); } else *out_kdc_tgts = ts->kdc_tgts; return retval; }
int main(int argc, char **argv) { krb5_data client, server; char realm_branch_char = '.'; krb5_principal *tree, *p; char *name; krb5_error_code retval; krb5_context context; krb5_init_context(&context); if (argc < 3 || argc > 4) { fprintf(stderr, "Usage: %s client-realm server-realm [sep_char]\n", argv[0]); exit(99); } client.data = argv[1]; client.length = strlen(client.data); server.data = argv[2]; server.length = strlen(server.data); if (argc == 4) realm_branch_char = argv[3][0]; retval = krb5_walk_realm_tree(context, &client, &server, &tree, realm_branch_char); if (retval) { com_err("krb5_walk_realm_tree", retval, " "); exit(1); } for (p = tree; *p; p++) { retval = krb5_unparse_name(context, *p, &name); if (retval) { com_err("krb5_unprase_name", retval, " "); exit(2); } printf("%s\n", name); free(name); } krb5_free_realm_tree(context, tree); krb5_free_context(context); exit(0); }
krb5_error_code krb5_check_transited_list (krb5_context ctx, const krb5_data *trans_in, const krb5_data *crealm, const krb5_data *srealm) { krb5_data trans; struct check_data cdata; krb5_error_code r; const krb5_data *anonymous; trans.length = trans_in->length; trans.data = (char *) trans_in->data; if (trans.length && (trans.data[trans.length-1] == '\0')) trans.length--; Tprintf (("krb5_check_transited_list(trans=\"%.*s\", crealm=\"%.*s\", srealm=\"%.*s\")\n", (int) trans.length, trans.data, (int) crealm->length, crealm->data, (int) srealm->length, srealm->data)); if (trans.length == 0) return 0; anonymous = krb5_anonymous_realm(); if (crealm->length == anonymous->length && (memcmp(crealm->data, anonymous->data, anonymous->length) == 0)) return 0; /* Nothing to check for anonymous */ r = krb5_walk_realm_tree (ctx, crealm, srealm, &cdata.tgs, KRB5_REALM_BRANCH_CHAR); if (r) { Tprintf (("error %ld\n", (long) r)); return r; } #ifdef DEBUG /* avoid compiler warning about 'd' unused */ { int i; Tprintf (("tgs list = {\n")); for (i = 0; cdata.tgs[i]; i++) { char *name; r = krb5_unparse_name (ctx, cdata.tgs[i], &name); Tprintf (("\t'%s'\n", name)); free (name); } Tprintf (("}\n")); } #endif cdata.ctx = ctx; r = foreach_realm (check_realm_in_list, &cdata, crealm, srealm, &trans); krb5_free_realm_tree (ctx, cdata.tgs); return r; }
/* * The request seems to be for a ticket-granting service somewhere else, * but we don't have a ticket for the final TGS. Try to give the requestor * some intermediate realm. */ static krb5_error_code find_alternate_tgs(kdc_realm_t *kdc_active_realm, krb5_principal princ, krb5_db_entry **server_ptr, const char **status) { krb5_error_code retval; krb5_principal *plist = NULL, *pl2; krb5_data tmp; krb5_db_entry *server = NULL; *server_ptr = NULL; assert(is_cross_tgs_principal(princ)); if ((retval = krb5_walk_realm_tree(kdc_context, krb5_princ_realm(kdc_context, princ), krb5_princ_component(kdc_context, princ, 1), &plist, KRB5_REALM_BRANCH_CHAR))) { goto cleanup; } /* move to the end */ for (pl2 = plist; *pl2; pl2++); /* the first entry in this array is for krbtgt/local@local, so we ignore it */ while (--pl2 > plist) { tmp = *krb5_princ_realm(kdc_context, *pl2); krb5_princ_set_realm(kdc_context, *pl2, krb5_princ_realm(kdc_context, princ)); retval = db_get_svc_princ(kdc_context, *pl2, 0, &server, status); krb5_princ_set_realm(kdc_context, *pl2, &tmp); if (retval == KRB5_KDB_NOENTRY) continue; else if (retval) goto cleanup; log_tgs_alt_tgt(kdc_context, server->princ); *server_ptr = server; server = NULL; goto cleanup; } cleanup: if (retval == 0 && *server_ptr == NULL) retval = KRB5_KDB_NOENTRY; if (retval != 0) *status = "UNKNOWN_SERVER"; krb5_free_realm_tree(kdc_context, plist); krb5_db_free_principal(kdc_context, server); return retval; }
/* Initialize the realm path fields for getting a TGT for * ctx->server->realm. */ static krb5_error_code init_realm_path(krb5_context context, krb5_tkt_creds_context ctx) { krb5_error_code code; krb5_principal *tgt_princ_list = NULL; krb5_data *realm_path; size_t nrealms, i; /* Construct a list of TGT principals from client to server. We will throw * this away after grabbing the remote realms from each principal. */ code = krb5_walk_realm_tree(context, &ctx->client->realm, &ctx->server->realm, &tgt_princ_list, KRB5_REALM_BRANCH_CHAR); if (code != 0) return code; /* Count the number of principals and allocate the realm path. */ for (nrealms = 0; tgt_princ_list[nrealms]; nrealms++); assert(nrealms > 1); realm_path = k5alloc((nrealms + 1) * sizeof(*realm_path), &code); if (realm_path == NULL) goto cleanup; /* Steal the remote realm field from each TGT principal. */ for (i = 0; i < nrealms; i++) { assert(tgt_princ_list[i]->length == 2); realm_path[i] = tgt_princ_list[i]->data[1]; tgt_princ_list[i]->data[1].data = NULL; } realm_path[nrealms] = empty_data(); /* Initialize the realm path fields in ctx. */ krb5int_free_data_list(context, ctx->realm_path); ctx->realm_path = realm_path; ctx->last_realm = realm_path + nrealms - 1; ctx->cur_realm = realm_path; ctx->next_realm = ctx->last_realm; realm_path = NULL; cleanup: krb5_free_realm_tree(context, tgt_princ_list); return 0; }
/* * The request seems to be for a ticket-granting service somewhere else, * but we don't have a ticket for the final TGS. Try to give the requestor * some intermediate realm. */ static void find_alternate_tgs(krb5_kdc_req *request, krb5_db_entry *server, krb5_boolean *more, int *nprincs) { krb5_error_code retval; krb5_principal *plist, *pl2; krb5_data tmp; *nprincs = 0; *more = FALSE; /* * Call to krb5_princ_component is normally not safe but is so * here only because find_alternate_tgs() is only called from * somewhere that has already checked the number of components in * the principal. */ if ((retval = krb5_walk_realm_tree(kdc_context, krb5_princ_realm(kdc_context, request->server), krb5_princ_component(kdc_context, request->server, 1), &plist, KRB5_REALM_BRANCH_CHAR))) return; /* move to the end */ for (pl2 = plist; *pl2; pl2++); /* the first entry in this array is for krbtgt/local@local, so we ignore it */ while (--pl2 > plist) { *nprincs = 1; tmp = *krb5_princ_realm(kdc_context, *pl2); krb5_princ_set_realm(kdc_context, *pl2, krb5_princ_realm(kdc_context, tgs_server)); retval = get_principal(kdc_context, *pl2, server, nprincs, more); krb5_princ_set_realm(kdc_context, *pl2, &tmp); if (retval) { *nprincs = 0; *more = FALSE; krb5_free_realm_tree(kdc_context, plist); return; } if (*more) { krb5_db_free_principal(kdc_context, server, *nprincs); continue; } else if (*nprincs == 1) { /* Found it! */ krb5_principal tmpprinc; tmp = *krb5_princ_realm(kdc_context, *pl2); krb5_princ_set_realm(kdc_context, *pl2, krb5_princ_realm(kdc_context, tgs_server)); if ((retval = krb5_copy_principal(kdc_context, *pl2, &tmpprinc))) { krb5_db_free_principal(kdc_context, server, *nprincs); krb5_princ_set_realm(kdc_context, *pl2, &tmp); continue; } krb5_princ_set_realm(kdc_context, *pl2, &tmp); krb5_free_principal(kdc_context, request->server); request->server = tmpprinc; log_tgs_alt_tgt(request->server); krb5_free_realm_tree(kdc_context, plist); return; } krb5_db_free_principal(kdc_context, server, *nprincs); continue; } *nprincs = 0; *more = FALSE; krb5_free_realm_tree(kdc_context, plist); return; }
/* * The request seems to be for a ticket-granting service somewhere else, * but we don't have a ticket for the final TGS. Try to give the requestor * some intermediate realm. */ static krb5_error_code find_alternate_tgs(krb5_kdc_req *request, krb5_db_entry **server_ptr) { krb5_error_code retval; krb5_principal *plist = NULL, *pl2, tmpprinc; krb5_data tmp; krb5_db_entry *server = NULL; *server_ptr = NULL; /* * Call to krb5_princ_component is normally not safe but is so * here only because find_alternate_tgs() is only called from * somewhere that has already checked the number of components in * the principal. */ if ((retval = krb5_walk_realm_tree(kdc_context, krb5_princ_realm(kdc_context, request->server), krb5_princ_component(kdc_context, request->server, 1), &plist, KRB5_REALM_BRANCH_CHAR))) return retval; /* move to the end */ for (pl2 = plist; *pl2; pl2++); /* the first entry in this array is for krbtgt/local@local, so we ignore it */ while (--pl2 > plist) { tmp = *krb5_princ_realm(kdc_context, *pl2); krb5_princ_set_realm(kdc_context, *pl2, krb5_princ_realm(kdc_context, tgs_server)); retval = krb5_db_get_principal(kdc_context, *pl2, 0, &server); krb5_princ_set_realm(kdc_context, *pl2, &tmp); if (retval == KRB5_KDB_NOENTRY) continue; else if (retval) goto cleanup; /* Found it. */ tmp = *krb5_princ_realm(kdc_context, *pl2); krb5_princ_set_realm(kdc_context, *pl2, krb5_princ_realm(kdc_context, tgs_server)); retval = krb5_copy_principal(kdc_context, *pl2, &tmpprinc); if (retval) goto cleanup; krb5_princ_set_realm(kdc_context, *pl2, &tmp); krb5_free_principal(kdc_context, request->server); request->server = tmpprinc; log_tgs_alt_tgt(request->server); *server_ptr = server; server = NULL; goto cleanup; } retval = KRB5_KDB_NOENTRY; cleanup: krb5_free_realm_tree(kdc_context, plist); krb5_db_free_principal(kdc_context, server); return retval; }
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) { krb5_creds **ret_tgts = NULL; int ntgts = 0; krb5_creds tgt, tgtq, *tgtr = NULL; krb5_error_code retval; krb5_principal int_server = NULL; /* Intermediate server for request */ krb5_principal *tgs_list = NULL; krb5_principal *top_server = NULL; krb5_principal *next_server = NULL; unsigned int nservers = 0; krb5_boolean old_use_conf_ktypes = context->use_conf_ktypes; /* in case we never get a TGT, zero the return */ *tgts = NULL; memset((char *)&tgtq, 0, sizeof(tgtq)); memset((char *)&tgt, 0, sizeof(tgt)); /* * we know that the desired credentials aren't in the cache yet. * * To get them, we first need a tgt for the realm of the server. * first, we see if we have such a TGT in cache. if not, then * we ask the kdc to give us one. if that doesn't work, then * we try to get a tgt for a realm that is closest to the target. * once we have that, then we ask that realm if it can give us * tgt for the target. if not, we do the process over with this * new tgt. */ /* * (the ticket may be issued by some other intermediate * realm's KDC; so we use KRB5_TC_MATCH_SRV_NAMEONLY) */ if ((retval = krb5_copy_principal(context, in_cred->client, &tgtq.client))) goto cleanup; /* get target tgt from cache */ if ((retval = krb5_tgtname(context, krb5_princ_realm(context, in_cred->server), krb5_princ_realm(context, in_cred->client), &int_server))) { goto cleanup; } if ((retval = krb5_copy_principal(context, int_server, &tgtq.server))) { goto cleanup; } /* set endtime to now so krb5_cc_retrieve_cred won't return an expired tik */ if ((retval = krb5_timeofday(context, &(tgtq.times.endtime))) != 0) { goto cleanup; } context->use_conf_ktypes = 1; if ((retval = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES | KRB5_TC_MATCH_TIMES, &tgtq, &tgt)) != 0) { if (retval != KRB5_CC_NOTFOUND && retval != KRB5_CC_NOT_KTYPE) { goto cleanup; } /* * Note that we want to request a TGT from our local KDC, even * if we already have a TGT for some intermediate realm. The * reason is that our local KDC may have a shortcut to the * destination realm, and if it does we want to use the * shortcut because it will provide greater security. - bcn */ /* * didn't find it in the cache so it is time to get a local * tgt and walk the realms tree. */ krb5_free_principal(context, int_server); int_server = NULL; if ((retval = krb5_tgtname(context, krb5_princ_realm(context, in_cred->client), krb5_princ_realm(context, in_cred->client), &int_server))) { goto cleanup; } krb5_free_cred_contents(context, &tgtq); memset((char *)&tgtq, 0, sizeof(tgtq)); if ((retval = krb5_copy_principal(context, in_cred->client, &tgtq.client))) goto cleanup; if ((retval = krb5_copy_principal(context, int_server, &tgtq.server))) goto cleanup; if ((retval = krb5_timeofday(context, &(tgtq.times.endtime))) != 0) { goto cleanup; } if ((retval = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES | KRB5_TC_MATCH_TIMES, &tgtq, &tgt)) != 0) { goto cleanup; } /* get a list of realms to consult */ if ((retval = krb5_walk_realm_tree(context, krb5_princ_realm(context,in_cred->client), krb5_princ_realm(context,in_cred->server), &tgs_list, KRB5_REALM_BRANCH_CHAR))) { goto cleanup; } for (nservers = 0; tgs_list[nservers]; nservers++) ; /* allocate storage for TGT pointers. */ if (!(ret_tgts = (krb5_creds **) calloc(nservers+1, sizeof(krb5_creds)))) { retval = ENOMEM; goto cleanup; } *tgts = ret_tgts; /* * step one is to take the current tgt and see if there is a tgt for * krbtgt/realmof(target)@realmof(tgt). if not, try to get one with * the tgt. * * if we don't get a tgt for the target, then try to find a tgt as * close to the target realm as possible. at each step if there isn't * a tgt in the cache we have to try and get one with our latest tgt. * once we have a tgt for a closer realm, we go back to step one. * * once we have a tgt for the target, we go try and get credentials. */ for (top_server = tgs_list; top_server < tgs_list + nservers; top_server = next_server) { /* look in cache for a tgt for the destination */ krb5_free_cred_contents(context, &tgtq); memset(&tgtq, 0, sizeof(tgtq)); if ((retval = krb5_copy_principal(context, tgt.client, &tgtq.client))) goto cleanup; krb5_free_principal(context, int_server); int_server = NULL; if ((retval = krb5_tgtname(context, krb5_princ_realm(context, in_cred->server), krb5_princ_realm(context, *top_server), &int_server))) { goto cleanup; } if ((retval = krb5_copy_principal(context, int_server, &tgtq.server))) goto cleanup; if ((retval = krb5_timeofday(context, &(tgtq.times.endtime))) != 0) { goto cleanup; } if ((retval = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES | KRB5_TC_MATCH_TIMES, &tgtq, &tgt)) != 0) { if (retval != KRB5_CC_NOTFOUND && retval != KRB5_CC_NOT_KTYPE) { goto cleanup; } /* didn't find it in the cache so try and get one */ /* with current tgt. */ if (!krb5_c_valid_enctype(tgt.keyblock.enctype)) { retval = KRB5_PROG_ETYPE_NOSUPP; goto cleanup; } krb5_free_cred_contents(context, &tgtq); memset(&tgtq, 0, sizeof(tgtq)); tgtq.times = tgt.times; if ((retval = krb5_copy_principal(context, tgt.client, &tgtq.client))) goto cleanup; if ((retval = krb5_copy_principal(context, int_server, &tgtq.server))) goto cleanup; tgtq.is_skey = FALSE; tgtq.ticket_flags = tgt.ticket_flags; retval = krb5_get_cred_via_tkt(context, &tgt, FLAGS2OPTS(tgtq.ticket_flags), tgt.addresses, &tgtq, &tgtr); if (retval) { /* * couldn't get one so now loop backwards through the realms * list and try and get a tgt for a realm as close to the * target as possible. the kdc should give us a tgt for the * closest one it knows about, but not all kdc's do this yet. */ for (next_server = tgs_list + nservers - 1; next_server > top_server; next_server--) { krb5_free_cred_contents(context, &tgtq); memset(&tgtq, 0, sizeof(tgtq)); if ((retval = krb5_copy_principal(context, tgt.client, &tgtq.client))) goto cleanup; krb5_free_principal(context, int_server); int_server = NULL; if ((retval = krb5_tgtname(context, krb5_princ_realm(context, *next_server), krb5_princ_realm(context, *top_server), &int_server))) { goto cleanup; } if ((retval = krb5_copy_principal(context, int_server, &tgtq.server))) goto cleanup; if ((retval = krb5_timeofday(context, &(tgtq.times.endtime))) != 0) { goto cleanup; } if ((retval = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES | KRB5_TC_MATCH_TIMES, &tgtq, &tgt)) != 0) { if (retval != KRB5_CC_NOTFOUND) { goto cleanup; } /* not in the cache so try and get one with our current tgt. */ if (!krb5_c_valid_enctype(tgt.keyblock.enctype)) { retval = KRB5_PROG_ETYPE_NOSUPP; goto cleanup; } krb5_free_cred_contents(context, &tgtq); memset(&tgtq, 0, sizeof(tgtq)); tgtq.times = tgt.times; if ((retval = krb5_copy_principal(context, tgt.client, &tgtq.client))) goto cleanup; if ((retval = krb5_copy_principal(context, int_server, &tgtq.server))) goto cleanup; tgtq.is_skey = FALSE; tgtq.ticket_flags = tgt.ticket_flags; retval = krb5_get_cred_via_tkt(context, &tgt, FLAGS2OPTS(tgtq.ticket_flags), tgt.addresses, &tgtq, &tgtr); if (retval) continue; /* save tgt in return array */ if ((retval = krb5_copy_creds(context, tgtr, &ret_tgts[ntgts]))) { goto cleanup; } krb5_free_creds(context, tgtr); tgtr = NULL; tgt = *ret_tgts[ntgts++]; } /* got one as close as possible, now start all over */ break; } if (next_server == top_server) { goto cleanup; } continue; } /* * Got a tgt. If it is for the target realm we can go try for the * credentials. If it is not for the target realm, then make sure it * is in the realms hierarchy and if so, save it and start the loop * over from there. Note that we only need to compare the instance * names since that is the target realm of the tgt. */ for (next_server = top_server; *next_server; next_server++) { krb5_data *realm_1 = krb5_princ_component(context, next_server[0], 1); krb5_data *realm_2 = krb5_princ_component(context, tgtr->server, 1); if (realm_1 != NULL && realm_2 != NULL && realm_1->length == realm_2->length && !memcmp(realm_1->data, realm_2->data, realm_1->length)) { break; } } if (!next_server) { retval = KRB5_KDCREP_MODIFIED; goto cleanup; } if ((retval = krb5_copy_creds(context, tgtr, &ret_tgts[ntgts]))) { goto cleanup; } krb5_free_creds(context, tgtr); tgtr = NULL; tgt = *ret_tgts[ntgts++]; /* we're done if it is the target */ if (!*next_server++) break; } } } /* got/finally have tgt! try for the creds */ if (!krb5_c_valid_enctype(tgt.keyblock.enctype)) { retval = KRB5_PROG_ETYPE_NOSUPP; goto cleanup; } context->use_conf_ktypes = old_use_conf_ktypes; retval = krb5_get_cred_via_tkt(context, &tgt, FLAGS2OPTS(tgt.ticket_flags) | kdcopt | (in_cred->second_ticket.length ? KDC_OPT_ENC_TKT_IN_SKEY : 0), tgt.addresses, in_cred, out_cred); /* cleanup and return */ cleanup: if (tgtr) krb5_free_creds(context, tgtr); if(tgs_list) krb5_free_realm_tree(context, tgs_list); krb5_free_cred_contents(context, &tgtq); if (int_server) krb5_free_principal(context, int_server); if (ntgts == 0) { *tgts = NULL; if (ret_tgts) free(ret_tgts); krb5_free_cred_contents(context, &tgt); } context->use_conf_ktypes = old_use_conf_ktypes; return(retval); }
/* * The request seems to be for a ticket-granting service somewhere else, * but we don't have a ticket for the final TGS. Try to give the requestor * some intermediate realm. */ static void find_alternate_tgs(krb5_kdc_req *request, krb5_db_entry *server, krb5_boolean *more, int *nprincs, const krb5_fulladdr *from, char *cname) { krb5_error_code retval; krb5_principal *plist, *pl2; krb5_data tmp; *nprincs = 0; *more = FALSE; /* * Call to krb5_princ_component is normally not safe but is so * here only because find_alternate_tgs() is only called from * somewhere that has already checked the number of components in * the principal. */ if ((retval = krb5_walk_realm_tree(kdc_context, krb5_princ_realm(kdc_context, request->server), krb5_princ_component(kdc_context, request->server, 1), &plist, KRB5_REALM_BRANCH_CHAR))) return; /* move to the end */ for (pl2 = plist; *pl2; pl2++); /* the first entry in this array is for krbtgt/local@local, so we ignore it */ while (--pl2 > plist) { *nprincs = 1; tmp = *krb5_princ_realm(kdc_context, *pl2); krb5_princ_set_realm(kdc_context, *pl2, krb5_princ_realm(kdc_context, tgs_server)); retval = krb5_db_get_principal(kdc_context, *pl2, server, nprincs, more); krb5_princ_set_realm(kdc_context, *pl2, &tmp); if (retval) { *nprincs = 0; *more = FALSE; krb5_free_realm_tree(kdc_context, plist); return; } if (*more) { krb5_db_free_principal(kdc_context, server, *nprincs); continue; } else if (*nprincs == 1) { /* Found it! */ krb5_principal tmpprinc; char *sname; tmp = *krb5_princ_realm(kdc_context, *pl2); krb5_princ_set_realm(kdc_context, *pl2, krb5_princ_realm(kdc_context, tgs_server)); if ((retval = krb5_copy_principal(kdc_context, *pl2, &tmpprinc))) { krb5_db_free_principal(kdc_context, server, *nprincs); krb5_princ_set_realm(kdc_context, *pl2, &tmp); continue; } krb5_princ_set_realm(kdc_context, *pl2, &tmp); krb5_free_principal(kdc_context, request->server); request->server = tmpprinc; if (krb5_unparse_name(kdc_context, request->server, &sname)) { audit_krb5kdc_tgs_req_alt_tgt( (struct in_addr *)from->address->contents, (in_port_t)from->port, 0, cname, "<unparseable>", 0); krb5_klog_syslog(LOG_INFO, "TGS_REQ: issuing alternate <un-unparseable> TGT"); } else { limit_string(sname); audit_krb5kdc_tgs_req_alt_tgt( (struct in_addr *)from->address->contents, (in_port_t)from->port, 0, cname, sname, 0); krb5_klog_syslog(LOG_INFO, "TGS_REQ: issuing TGT %s", sname); free(sname); } return; } krb5_db_free_principal(kdc_context, server, *nprincs); continue; } *nprincs = 0; *more = FALSE; krb5_free_realm_tree(kdc_context, plist); return; }