/* * 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; }
/* * Set up the request given by ctx->tgs_in_creds, using ctx->cur_tgt. KDC * options for the requests are determined by ctx->cur_tgt->ticket_flags and * extra_options. */ static krb5_error_code make_request(krb5_context context, krb5_tkt_creds_context ctx, int extra_options) { krb5_error_code code; krb5_data request = empty_data(); ctx->kdcopt = extra_options | FLAGS2OPTS(ctx->cur_tgt->ticket_flags); /* XXX This check belongs in gc_via_tgt.c or nowhere. */ if (!krb5_c_valid_enctype(ctx->cur_tgt->keyblock.enctype)) return KRB5_PROG_ETYPE_NOSUPP; code = krb5int_make_tgs_request(context, ctx->cur_tgt, ctx->kdcopt, ctx->cur_tgt->addresses, NULL, ctx->tgs_in_creds, NULL, NULL, &request, &ctx->timestamp, &ctx->nonce, &ctx->subkey); if (code != 0) return code; krb5_free_data_contents(context, &ctx->previous_request); ctx->previous_request = request; return set_caller_request(context, ctx); }
/* * 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; }
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_error_code retval, subretval; krb5_principal client, server, supplied_server, out_supplied_server; krb5_creds tgtq, cc_tgt, *tgtptr, *referral_tgts[KRB5_REFERRAL_MAXHOPS]; krb5_creds *otgtptr = NULL; int tgtptr_isoffpath = 0; krb5_boolean old_use_conf_ktypes; char **hrealms; unsigned int referral_count, i; /* * Set up client and server pointers. Make a fresh and modifyable * copy of the in_cred server and save the supplied version. */ client = in_cred->client; if ((retval=krb5_copy_principal(context, in_cred->server, &server))) return retval; /* We need a second copy for the output creds. */ if ((retval = krb5_copy_principal(context, server, &out_supplied_server)) != 0 ) { krb5_free_principal(context, server); return retval; } supplied_server = in_cred->server; in_cred->server=server; DUMP_PRINC("gc_from_kdc initial client", client); DUMP_PRINC("gc_from_kdc initial server", server); memset(&cc_tgt, 0, sizeof(cc_tgt)); memset(&tgtq, 0, sizeof(tgtq)); memset(&referral_tgts, 0, sizeof(referral_tgts)); tgtptr = NULL; *tgts = NULL; *out_cred=NULL; old_use_conf_ktypes = context->use_conf_ktypes; /* Copy client realm to server if no hint. */ if (krb5_is_referral_realm(&server->realm)) { /* Use the client realm. */ DPRINTF(("gc_from_kdc: no server realm supplied, " "using client realm.\n")); krb5_free_data_contents(context, &server->realm); if (!( server->realm.data = (char *)malloc(client->realm.length+1))) return ENOMEM; memcpy(server->realm.data, client->realm.data, client->realm.length); server->realm.length = client->realm.length; server->realm.data[server->realm.length] = 0; } /* * Retreive initial TGT to match the specified server, either for the * local realm in the default (referral) case or for the remote * realm if we're starting someplace non-local. */ retval = tgt_mcred(context, client, server, client, &tgtq); if (retval) goto cleanup; /* Fast path: Is it in the ccache? */ context->use_conf_ktypes = 1; retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS, &tgtq, &cc_tgt); if (!retval) { tgtptr = &cc_tgt; } else if (!HARD_CC_ERR(retval)) { DPRINTF(("gc_from_kdc: starting do_traversal to find " "initial TGT for referral\n")); tgtptr_isoffpath = 0; otgtptr = NULL; retval = do_traversal(context, ccache, client, server, &cc_tgt, &tgtptr, tgts, &tgtptr_isoffpath); } if (retval) { DPRINTF(("gc_from_kdc: failed to find initial TGT for referral\n")); goto cleanup; } DUMP_PRINC("gc_from_kdc: server as requested", supplied_server); /* * Try requesting a service ticket from our local KDC with referrals * turned on. If the first referral succeeds, follow a referral-only * path, otherwise fall back to old-style assumptions. */ /* * Save TGTPTR because we rewrite it in the referral loop, and * we might need to explicitly free it later. */ otgtptr = tgtptr; for (referral_count = 0; referral_count < KRB5_REFERRAL_MAXHOPS; referral_count++) { #if 0 DUMP_PRINC("gc_from_kdc: referral loop: tgt in use", tgtptr->server); DUMP_PRINC("gc_from_kdc: referral loop: request is for", server); #endif retval = krb5_get_cred_via_tkt(context, tgtptr, KDC_OPT_CANONICALIZE | FLAGS2OPTS(tgtptr->ticket_flags) | kdcopt | (in_cred->second_ticket.length ? KDC_OPT_ENC_TKT_IN_SKEY : 0), tgtptr->addresses, in_cred, out_cred); if (retval) { DPRINTF(("gc_from_kdc: referral TGS-REQ request failed: <%s>\n", error_message(retval))); /* If we haven't gone anywhere yet, fail through to the non-referral case. */ if (referral_count==0) { DPRINTF(("gc_from_kdc: initial referral failed; " "punting to fallback.\n")); break; } /* Otherwise, try the same query without canonicalization set, and fail hard if that doesn't work. */ DPRINTF(("gc_from_kdc: referral #%d failed; " "retrying without option.\n", referral_count + 1)); retval = krb5_get_cred_via_tkt(context, tgtptr, FLAGS2OPTS(tgtptr->ticket_flags) | kdcopt | (in_cred->second_ticket.length ? KDC_OPT_ENC_TKT_IN_SKEY : 0), tgtptr->addresses, in_cred, out_cred); /* Whether or not that succeeded, we're done. */ goto cleanup; } /* Referral request succeeded; let's see what it is. */ if (krb5_principal_compare(context, in_cred->server, (*out_cred)->server)) { DPRINTF(("gc_from_kdc: request generated ticket " "for requested server principal\n")); DUMP_PRINC("gc_from_kdc final referred reply", in_cred->server); /* * Check if the return enctype is one that we requested if * needed. */ if (old_use_conf_ktypes || context->tgs_ktype_count == 0) goto cleanup; for (i = 0; i < context->tgs_ktype_count; i++) { if ((*out_cred)->keyblock.enctype == context->tgs_ktypes[i]) { /* Found an allowable etype, so we're done */ goto cleanup; } } /* * We need to try again, but this time use the * tgs_ktypes in the context. At this point we should * have all the tgts to succeed. */ /* Free "wrong" credential */ krb5_free_creds(context, *out_cred); *out_cred = NULL; /* Re-establish tgs etypes */ context->use_conf_ktypes = old_use_conf_ktypes; retval = krb5_get_cred_via_tkt(context, tgtptr, KDC_OPT_CANONICALIZE | FLAGS2OPTS(tgtptr->ticket_flags) | kdcopt | (in_cred->second_ticket.length ? KDC_OPT_ENC_TKT_IN_SKEY : 0), tgtptr->addresses, in_cred, out_cred); goto cleanup; } else if (IS_TGS_PRINC(context, (*out_cred)->server)) { krb5_data *r1, *r2; DPRINTF(("gc_from_kdc: request generated referral tgt\n")); DUMP_PRINC("gc_from_kdc credential received", (*out_cred)->server); if (referral_count == 0) r1 = &tgtptr->server->data[1]; else r1 = &referral_tgts[referral_count-1]->server->data[1]; r2 = &(*out_cred)->server->data[1]; if (data_eq(*r1, *r2)) { DPRINTF(("gc_from_kdc: referred back to " "previous realm; fall back\n")); krb5_free_creds(context, *out_cred); *out_cred = NULL; break; } /* Check for referral routing loop. */ for (i=0;i<referral_count;i++) { #if 0 DUMP_PRINC("gc_from_kdc: loop compare #1", (*out_cred)->server); DUMP_PRINC("gc_from_kdc: loop compare #2", referral_tgts[i]->server); #endif if (krb5_principal_compare(context, (*out_cred)->server, referral_tgts[i]->server)) { DFPRINTF((stderr, "krb5_get_cred_from_kdc_opt: " "referral routing loop - " "got referral back to hop #%d\n", i)); retval=KRB5_KDC_UNREACH; goto cleanup; } } /* Point current tgt pointer at newly-received TGT. */ if (tgtptr == &cc_tgt) krb5_free_cred_contents(context, tgtptr); tgtptr=*out_cred; /* Save pointer to tgt in referral_tgts. */ referral_tgts[referral_count]=*out_cred; *out_cred = NULL; /* Copy krbtgt realm to server principal. */ krb5_free_data_contents(context, &server->realm); retval = krb5int_copy_data_contents(context, &tgtptr->server->data[1], &server->realm); if (retval) return retval; /* * Future work: rewrite server principal per any * supplied padata. */ } else { /* Not a TGT; punt to fallback. */ krb5_free_creds(context, *out_cred); *out_cred = NULL; break; } } DUMP_PRINC("gc_from_kdc client at fallback", client); DUMP_PRINC("gc_from_kdc server at fallback", server); /* * At this point referrals have been tried and have failed. Go * back to the server principal as originally issued and try the * conventional path. */ /* * Referrals have failed. Look up fallback realm if not * originally provided. */ if (krb5_is_referral_realm(&supplied_server->realm)) { if (server->length >= 2) { retval=krb5_get_fallback_host_realm(context, &server->data[1], &hrealms); if (retval) goto cleanup; #if 0 DPRINTF(("gc_from_kdc: using fallback realm of %s\n", hrealms[0])); #endif krb5_free_data_contents(context,&in_cred->server->realm); server->realm.data=hrealms[0]; server->realm.length=strlen(hrealms[0]); free(hrealms); } else { /* * Problem case: Realm tagged for referral but apparently not * in a <type>/<host> format that * krb5_get_fallback_host_realm can deal with. */ DPRINTF(("gc_from_kdc: referral specified " "but no fallback realm avaiable!\n")); return KRB5_ERR_HOST_REALM_UNKNOWN; } } DUMP_PRINC("gc_from_kdc server at fallback after fallback rewrite", server); /* * Get a TGT for the target realm. */ krb5_free_cred_contents(context, &tgtq); retval = tgt_mcred(context, client, server, client, &tgtq); if (retval) goto cleanup; /* Fast path: Is it in the ccache? */ /* Free tgtptr data if reused from above. */ if (tgtptr == &cc_tgt) krb5_free_cred_contents(context, tgtptr); tgtptr = NULL; /* Free saved TGT in OTGTPTR if it was off-path. */ if (tgtptr_isoffpath) krb5_free_creds(context, otgtptr); otgtptr = NULL; /* Free TGTS if previously filled by do_traversal() */ if (*tgts != NULL) { for (i = 0; (*tgts)[i] != NULL; i++) { krb5_free_creds(context, (*tgts)[i]); } free(*tgts); *tgts = NULL; } context->use_conf_ktypes = 1; retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS, &tgtq, &cc_tgt); if (!retval) { tgtptr = &cc_tgt; } else if (!HARD_CC_ERR(retval)) { tgtptr_isoffpath = 0; retval = do_traversal(context, ccache, client, server, &cc_tgt, &tgtptr, tgts, &tgtptr_isoffpath); } if (retval) goto cleanup; otgtptr = tgtptr; /* * Finally have TGT for target realm! Try using it to get creds. */ if (!krb5_c_valid_enctype(tgtptr->keyblock.enctype)) { retval = KRB5_PROG_ETYPE_NOSUPP; goto cleanup; } context->use_conf_ktypes = old_use_conf_ktypes; retval = krb5_get_cred_via_tkt(context, tgtptr, FLAGS2OPTS(tgtptr->ticket_flags) | kdcopt | (in_cred->second_ticket.length ? KDC_OPT_ENC_TKT_IN_SKEY : 0), tgtptr->addresses, in_cred, out_cred); cleanup: krb5_free_cred_contents(context, &tgtq); if (tgtptr == &cc_tgt) krb5_free_cred_contents(context, tgtptr); if (tgtptr_isoffpath) krb5_free_creds(context, otgtptr); context->use_conf_ktypes = old_use_conf_ktypes; /* Drop the original principal back into in_cred so that it's cached in the expected format. */ DUMP_PRINC("gc_from_kdc: final hacked server principal at cleanup", server); krb5_free_principal(context, server); in_cred->server = supplied_server; if (*out_cred && !retval) { /* Success: free server, swap supplied server back in. */ krb5_free_principal (context, (*out_cred)->server); (*out_cred)->server= out_supplied_server; } else { /* * Failure: free out_supplied_server. Don't free out_cred here * since it's either null or a referral TGT that we free below, * and we may need it to return. */ krb5_free_principal (context, out_supplied_server); } DUMP_PRINC("gc_from_kdc: final server after reversion", in_cred->server); /* * Deal with ccache TGT management: If tgts has been set from * initial non-referral TGT discovery, leave it alone. Otherwise, if * referral_tgts[0] exists return it as the only entry in tgts. * (Further referrals are never cached, only the referral from the * local KDC.) This is part of cleanup because useful received TGTs * should be cached even if the main request resulted in failure. */ if (*tgts == NULL) { if (referral_tgts[0]) { #if 0 /* * This should possibly be a check on the candidate return * credential against the cache, in the circumstance where we * don't want to clutter the cache with near-duplicate * credentials on subsequent iterations. For now, it is * disabled. */ subretval=...?; if (subretval) { #endif /* Allocate returnable TGT list. */ if (!(*tgts=calloc(sizeof (krb5_creds *), 2))) return ENOMEM; subretval=krb5_copy_creds(context, referral_tgts[0], &((*tgts)[0])); if(subretval) return subretval; (*tgts)[1]=NULL; DUMP_PRINC("gc_from_kdc: returning referral TGT for ccache", (*tgts)[0]->server); #if 0 } #endif } } /* Free referral TGTs list. */ for (i=0;i<KRB5_REFERRAL_MAXHOPS;i++) { if(referral_tgts[i]) { krb5_free_creds(context, referral_tgts[i]); } } DPRINTF(("gc_from_kdc finishing with %s\n", retval ? error_message(retval) : "no error")); return retval; }
/* * chase_offpath() * * Chase off-path TGT referrals. * * If we are traversing a trusted path (either hierarchically derived * or explicit capath) and get a TGT pointing to a realm off this * path, query the realm referenced by that off-path TGT. Repeat * until we get to the destination realm or encounter an error. * * CUR_TGT is always either pointing into REFTGTS or is an alias for * TS->OFFPATH_TGT. */ static krb5_error_code chase_offpath(struct tr_state *ts, krb5_principal client, krb5_principal server) { krb5_error_code retval; krb5_creds mcred; krb5_creds *cur_tgt, *nxt_tgt, *reftgts[KRB5_REFERRAL_MAXHOPS]; krb5_data *rsrc, *rdst, *r1; int rcount, i; rdst = krb5_princ_realm(ts->ctx, server); cur_tgt = ts->offpath_tgt; for (rcount = 0; rcount < KRB5_REFERRAL_MAXHOPS; rcount++) { nxt_tgt = NULL; memset(&mcred, 0, sizeof(mcred)); rsrc = krb5_princ_component(ts->ctx, cur_tgt->server, 1); retval = krb5_tgtname(ts->ctx, rdst, rsrc, &mcred.server); if (retval) goto cleanup; mcred.client = client; retval = krb5_get_cred_via_tkt(ts->ctx, cur_tgt, FLAGS2OPTS(cur_tgt->ticket_flags), cur_tgt->addresses, &mcred, &nxt_tgt); mcred.client = NULL; krb5_free_principal(ts->ctx, mcred.server); mcred.server = NULL; if (retval) goto cleanup; if (!IS_TGS_PRINC(ts->ctx, nxt_tgt->server)) { retval = KRB5_KDCREP_MODIFIED; goto cleanup; } r1 = krb5_princ_component(ts->ctx, nxt_tgt->server, 1); if (rdst->length == r1->length && !memcmp(rdst->data, r1->data, rdst->length)) { retval = 0; goto cleanup; } retval = offpath_loopchk(ts, nxt_tgt, reftgts, rcount); if (retval) goto cleanup; reftgts[rcount] = nxt_tgt; cur_tgt = nxt_tgt; nxt_tgt = NULL; } /* Max hop count exceeded. */ retval = KRB5_KDCREP_MODIFIED; cleanup: if (mcred.server != NULL) { krb5_free_principal(ts->ctx, mcred.server); } /* * Don't free TS->OFFPATH_TGT if it's in the list of cacheable * TGTs to be returned by do_traversal(). */ if (ts->offpath_tgt != ts->nxt_tgt) { krb5_free_creds(ts->ctx, ts->offpath_tgt); } ts->offpath_tgt = NULL; if (nxt_tgt != NULL) { if (retval) krb5_free_creds(ts->ctx, nxt_tgt); else ts->offpath_tgt = nxt_tgt; } for (i = 0; i < rcount; i++) { krb5_free_creds(ts->ctx, reftgts[i]); } 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); }
int krb_renew_tgts( lList *joblist ) { krb5_error_code rc; static u_long32 next_time = 0; u_long32 now = sge_get_gmt(); lListElem *job; krb5_context context = krb_context(); krb5_timestamp time_now; krb_global_data_t *gsd = krb_gsd(); DENTER(TOP_LAYER, "krb_renew_tgts"); if ((now = sge_get_gmt())<next_time) { DEXIT; return 0; } if ((rc = krb5_timeofday(context, &time_now))) { ERROR((SGE_EVENT, MSG_KRB_KRB5TIMEOFDAYFAILEDX_S , error_message(rc))); DEXIT; return -1; } /* renew job TGT's */ for_each(job, joblist) { krb5_error_code rc; krb5_creds ** tgt_creds = NULL; krb5_data tgtbuf; const char *tgtstr = NULL; tgtbuf.length = 0; /* get TGT out of job entry */ if ((tgtstr = lGetString(job, JB_tgt))) { tgtbuf.data = krb_str2bin(tgtstr, NULL, &tgtbuf.length); if (tgtbuf.length) { /* decrypt the TGT using the daemon key */ if ((rc = krb_decrypt_tgt_creds(&tgtbuf, &tgt_creds))) { ERROR((SGE_EVENT, MSG_KRB_COULDNOTDECRYPTTGTFORJOBXY_DS, sge_u32c(lGetUlong(job, JB_job_number)), error_message(rc))); } if (rc == 0 && tgt_creds) { krb5_creds *tgt = *tgt_creds; /* * If TGT is renewable and TGT expiration time is not past * and is within the SGE renewal threshold and the TGT * renewal period is not past, then renew the TGT */ if (tgt->ticket_flags & KDC_OPT_RENEWABLE && tgt->times.endtime > time_now && tgt->times.renew_till > time_now && tgt->times.endtime < time_now + gsd->tgt_renew_threshold) { krb5_creds *new_creds[2]; krb5_creds creds; memset(new_creds, 0, sizeof(new_creds)); memset(&creds, 0 ,sizeof(creds)); /* renew the TGT */ if (((rc = krb5_copy_principal(context, (*tgt_creds)->server, &creds.server))) || ((rc = krb5_copy_principal(context, (*tgt_creds)->client, &creds.client))) || ((rc = krb5_get_cred_via_tkt(context, tgt, FLAGS2OPTS(tgt->ticket_flags)|KDC_OPT_RENEW, tgt->addresses, &creds, &new_creds[0])))) { ERROR((SGE_EVENT, MSG_KRB_COULDNOTRENEWTGTFORJOBXY_DS, sge_u32c(lGetUlong(job, JB_job_number)), error_message(rc))); } krb5_free_cred_contents(context, &creds); if (rc == 0) { krb5_data outbuf; /* store the new TGT back into the job entry */ outbuf.length = 0; if ((rc = krb_encrypt_tgt_creds(new_creds, &outbuf))) { ERROR((SGE_EVENT, MSG_KRB_COULDNOTECRYPTTGTFORJOBXY_DS, sge_u32c(lGetUlong(job, JB_job_number)), error_message(rc))); } else { lSetString(job, JB_tgt, krb_bin2str(outbuf.data, outbuf.length, NULL)); } /* if we are called by the execd, also store the new TGT in the credentials cache of the user */ if (!strcmp(prognames[EXECD], gsd->progname)) { int retries = MAX_NIS_RETRIES; struct passwd *pw = NULL; while (retries-- && !pw) pw = getpwnam(lGetString(job, JB_owner)); if (pw) { if ((krb_store_forwarded_tgt(pw->pw_uid, lGetUlong(job, JB_job_number), new_creds))) { ERROR((SGE_EVENT, MSG_KRB_COULDNOTSTORERENEWEDTGTFORXJOBY_SD, lGetString(job, JB_owner), sge_u32c(lGetUlong(job, JB_job_number)))); } } else { ERROR((SGE_EVENT, MSG_KRB_COULDNOTGETUSERIDFORXY_SD , lGetString(job, JB_owner), sge_u32c(lGetUlong(job, JB_job_number)))); } } if (outbuf.length) krb5_xfree(outbuf.data); } if (!mconf_get_simulate_jobs()) { job_write_spool_file(job, 0, NULL, SPOOL_DEFAULT);; } if (new_creds[0]) krb5_free_creds(context, new_creds[0]); } } } if (tgtbuf.length) krb5_xfree(tgtbuf.data); if (tgt_creds) krb5_free_tgt_creds(context, tgt_creds); } }