From bab09a5b9fc4ec1aebce29696694a70160f7b72c Mon Sep 17 00:00:00 2001 From: Theodore Tso Date: Fri, 10 Jun 1994 22:37:30 +0000 Subject: [PATCH] Fixed inter-realm handling; from OCSG/Cliff Neumann git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@3739 dc483132-0cff-0310-8789-dd5450dbe970 --- src/lib/krb5/krb/gc_frm_kdc.c | 510 ++++++++++++++++++++++------------ src/lib/krb5/krb/gc_via_tgt.c | 11 +- 2 files changed, 347 insertions(+), 174 deletions(-) diff --git a/src/lib/krb5/krb/gc_frm_kdc.c b/src/lib/krb5/krb/gc_frm_kdc.c index c825efa2f..f45ffeb18 100644 --- a/src/lib/krb5/krb/gc_frm_kdc.c +++ b/src/lib/krb5/krb/gc_frm_kdc.c @@ -1,8 +1,8 @@ /* - * $Source$ - * $Author$ - * - * Copyright 1990,1991 by the Massachusetts Institute of Technology. + * Copyright (c) 1994 by the Massachusetts Institute of Technology. + * Copyright (c) 1994 CyberSAFE Corporation + * Copyright (c) 1993 Open Computing Security Group + * Copyright (c) 1990,1991 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may @@ -17,11 +17,12 @@ * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior - * permission. M.I.T. makes no representations about the suitability of + * permission. Neither M.I.T., the Open Computing Security Group, nor + * CyberSAFE Corporation make any representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * - * + * krb5_get_cred_from_kdc() * Get credentials from some KDC somewhere, possibly accumulating tgts * along the way. */ @@ -31,7 +32,7 @@ static char rcsid_gcfkdc_c[] = "$Id$"; #endif /* !lint & !SABER */ - +#include #include #include #include "int-proto.h" @@ -62,186 +63,355 @@ static char rcsid_gcfkdc_c[] = extern krb5_cksumtype krb5_kdc_req_sumtype; -/* helper function: convert flags to necessary KDC options */ -#define flags2options(flags) (flags & KDC_TKT_COMMON_MASK) +/* helper macro: convert flags to necessary KDC options */ + +#define FLAGS2OPTS(flags) (flags & KDC_TKT_COMMON_MASK) +#define TGT_ETYPE \ + krb5_keytype_array[tgt.keyblock.keytype]->system->proto_enctype; -krb5_error_code -krb5_get_cred_from_kdc (ccache, cred, tgts) - krb5_ccache ccache; - krb5_creds *cred; - krb5_creds ***tgts; + +#ifdef __STDC__ +krb5_error_code krb5_get_cred_from_kdc( + krb5_ccache ccache, + krb5_creds *cred, + krb5_creds ***tgts +) +#else +krb5_error_code krb5_get_cred_from_kdc(ccache, cred, tgts) + krb5_ccache ccache; + krb5_creds *cred; + krb5_creds ***tgts; +#endif { - krb5_creds tgt, tgtq; - krb5_creds **ret_tgts = 0; - krb5_principal *tgs_list = 0, *next_server; - krb5_principal final_server; - krb5_error_code retval; - int nservers; - int returning_tgt = 0; - krb5_enctype etype; - - /* in case we never get a TGT, zero the return */ - *tgts = 0; - - memset((char *)&tgtq, 0, sizeof(tgtq)); - memset((char *)&tgt, 0, sizeof(tgt)); + krb5_creds **ret_tgts = NULL; + int ntgts = 0; + + krb5_creds tgt, tgtq; + krb5_enctype etype; + 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; + int nservers = 0; + + /* 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(cred->client, &tgtq.client)) + goto cleanup; + + /* get target tgt from cache */ + if (retval = krb5_tgtname(krb5_princ_realm(cred->server), + krb5_princ_realm(cred->client), + &int_server)) { + goto cleanup; + } + + if (retval = krb5_copy_principal(int_server, &tgtq.server)) { + goto cleanup; + } + + if (retval = krb5_cc_retrieve_cred(ccache, + KRB5_TC_MATCH_SRV_NAMEONLY, + &tgtq, + &tgt)) { + + if (retval != KRB5_CC_NOTFOUND) { + goto cleanup; + } /* - * 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. + * 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(int_server); + int_server = NULL; + if (retval = krb5_tgtname(krb5_princ_realm(cred->client), + krb5_princ_realm(cred->client), + &int_server)) { + goto cleanup; + } + + krb5_free_cred_contents(&tgtq); + memset((char *)&tgtq, 0, sizeof(tgtq)); + if(retval = krb5_copy_principal(cred->client, &tgtq.client)) + goto cleanup; + if(retval = krb5_copy_principal(int_server, &tgtq.server)) + goto cleanup; + + if (retval = krb5_cc_retrieve_cred(ccache, + KRB5_TC_MATCH_SRV_NAMEONLY, + &tgtq, + &tgt)) { + goto cleanup; + } + + /* get a list of realms to consult */ + + if (retval = krb5_walk_realm_tree(krb5_princ_realm(cred->client), + krb5_princ_realm(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; /* - * look for ticket with: - * client == cred->client, - * server == "krbtgt/realmof(cred->server)@realmof(cred->client)" + * 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. * - * (actually, the ticket may be issued by some other intermediate - * realm's KDC; so we use KRB5_TC_MATCH_SRV_NAMEONLY below) + * 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(&tgtq); + memset(&tgtq, 0, sizeof(tgtq)); + if(retval = krb5_copy_principal(tgt.client, &tgtq.client)) + goto cleanup; - if (retval = krb5_copy_principal(cred->client, &tgtq.client)) - goto errout; - if (retval = krb5_tgtname(krb5_princ_realm(cred->server), - krb5_princ_realm(cred->client), &final_server)) - goto errout; - if (retval = krb5_copy_principal(final_server, &tgtq.server)) - goto errout; - - retval = krb5_cc_retrieve_cred (ccache, - KRB5_TC_MATCH_SRV_NAMEONLY, - &tgtq, - &tgt); - - if (retval != 0) { - if (retval != KRB5_CC_NOTFOUND) - goto errout; - /* don't have the right TGT in the cred cache. Time to iterate - across realms to get the right TGT. */ - - /* get a list of realms to consult */ - if (retval = krb5_walk_realm_tree(krb5_princ_realm(cred->client), - krb5_princ_realm(cred->server), - &tgs_list, KRB5_REALM_BRANCH_CHAR)) - goto errout; - - /* walk the list BACKWARDS until we find a cached - TGT, then move forward obtaining TGTs until we get the last - TGT needed */ - for (next_server = tgs_list; *next_server;) - next_server++; - nservers = next_server - tgs_list; - next_server--; - - /* next_server now points to the last TGT */ - for (; next_server >= tgs_list; next_server--) { - krb5_free_principal(tgtq.server); - if (retval = krb5_copy_principal(*next_server, &tgt.server)) - goto errout; - retval = krb5_cc_retrieve_cred (ccache, - KRB5_TC_MATCH_SRV_NAMEONLY, - &tgtq, - &tgt); - if (retval) { - if (retval != KRB5_CC_NOTFOUND) - goto errout; - continue; - } - next_server++; - break; /* found one! */ - } - if (next_server < tgs_list) { - /* didn't find any */ - retval = KRB5_NO_TKT_IN_RLM; - goto errout; + krb5_free_principal(int_server); + int_server = NULL; + if (retval = krb5_tgtname(krb5_princ_realm(cred->server), + krb5_princ_realm(*top_server), + &int_server)) { + goto cleanup; + } + + if(retval = krb5_copy_principal(int_server, &tgtq.server)) + goto cleanup; + + if (retval = krb5_cc_retrieve_cred(ccache, + KRB5_TC_MATCH_SRV_NAMEONLY, + &tgtq, + &tgt)) { + + if (retval != KRB5_CC_NOTFOUND) { + goto cleanup; } - /* allocate storage for TGT pointers. */ - ret_tgts = (krb5_creds **)calloc(nservers+1, sizeof(krb5_creds)); - if (!ret_tgts) { - retval = ENOMEM; - goto errout; + + /* didn't find it in the cache so try and get one */ + /* with current tgt. */ + + if (!valid_keytype(tgt.keyblock.keytype)) { + retval = KRB5_PROG_KEYTYPE_NOSUPP; + goto cleanup; } - *tgts = ret_tgts; - for (nservers = 0; *next_server; next_server++, nservers++) { - krb5_data *tmpdata; - - if (!valid_keytype(tgt.keyblock.keytype)) { - retval = KRB5_PROG_KEYTYPE_NOSUPP; - goto errout; - } - /* now get the TGTs */ + + krb5_free_cred_contents(&tgtq); + memset(&tgtq, 0, sizeof(tgtq)); + tgtq.times = tgt.times; + if (retval = krb5_copy_principal(tgt.client, &tgtq.client)) + goto cleanup; + if(retval = krb5_copy_principal(int_server, &tgtq.server)) + goto cleanup; + tgtq.is_skey = FALSE; + tgtq.ticket_flags = tgt.ticket_flags; + etype = TGT_ETYPE; + if(retval = krb5_get_cred_via_tgt(&tgt, + FLAGS2OPTS(tgtq.ticket_flags), + etype, + krb5_kdc_req_sumtype, + &tgtq)) { + + /* + * 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(&tgtq); - memset((char *)&tgtq, 0, sizeof(tgtq)); - tgtq.times = tgt.times; + memset(&tgtq, 0, sizeof(tgtq)); if (retval = krb5_copy_principal(tgt.client, &tgtq.client)) - goto errout; + goto cleanup; - /* ask each realm for a tgt to the end */ - if (retval = krb5_copy_data(krb5_princ_realm(*next_server), - &tmpdata)) { - goto errout; + krb5_free_principal(int_server); + int_server = NULL; + if (retval = krb5_tgtname(krb5_princ_realm(*next_server), + krb5_princ_realm(*top_server), + &int_server)) { + goto cleanup; } - free(krb5_princ_realm(final_server)->data); - krb5_princ_set_realm(final_server, tmpdata); - if (retval = krb5_copy_principal(final_server, &tgtq.server)) - goto errout; - - tgtq.is_skey = FALSE; - tgtq.ticket_flags = tgt.ticket_flags; - - etype = krb5_keytype_array[tgt.keyblock.keytype]->system->proto_enctype; - if (retval = krb5_get_cred_via_tgt(&tgt, - flags2options(tgtq.ticket_flags), - etype, - krb5_kdc_req_sumtype, - &tgtq)) - goto errout; - - /* make sure the returned ticket is somewhere in the remaining - list, but we can tolerate different expected issuing realms */ - while (*++next_server && - !krb5_principal_compare(&(next_server[0])[1], - &(tgtq.server[1]))); - if (!next_server) { - /* what we got back wasn't in the list! */ - retval = KRB5_KDCREP_MODIFIED; - goto errout; + + if(retval = krb5_copy_principal(int_server, &tgtq.server)) + goto cleanup; + + if(retval = krb5_cc_retrieve_cred(ccache, + KRB5_TC_MATCH_SRV_NAMEONLY, + &tgtq, + &tgt)) { + if (retval != KRB5_CC_NOTFOUND) { + goto cleanup; + } + + /* not in the cache so try and get one with our current tgt. */ + + if (!valid_keytype(tgt.keyblock.keytype)) { + retval = KRB5_PROG_KEYTYPE_NOSUPP; + goto cleanup; + } + + krb5_free_cred_contents(&tgtq); + memset(&tgtq, 0, sizeof(tgtq)); + tgtq.times = tgt.times; + if (retval = krb5_copy_principal(tgt.client, &tgtq.client)) + goto cleanup; + if(retval = krb5_copy_principal(int_server, &tgtq.server)) + goto cleanup; + tgtq.is_skey = FALSE; + tgtq.ticket_flags = tgt.ticket_flags; + etype = TGT_ETYPE; + if (retval = krb5_get_cred_via_tgt(&tgt, + FLAGS2OPTS(tgtq.ticket_flags), + etype, + krb5_kdc_req_sumtype, + &tgtq)) { + continue; + } + + /* save tgt in return array */ + if (retval = krb5_copy_creds(&tgtq, &ret_tgts[ntgts])) { + goto cleanup; + } + + tgt = *ret_tgts[ntgts++]; } - - /* save tgt in return array */ - if (retval = krb5_copy_creds(&tgtq, &ret_tgts[nservers])) - goto errout; - tgt = *ret_tgts[nservers]; - returning_tgt = 1; /* don't free it below... */ + + /* 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(next_server[0], 1); + krb5_data *realm_2 = krb5_princ_component(tgtq.server, 1); + if (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(&tgtq, &ret_tgts[ntgts])) { + goto cleanup; + } + + tgt = *ret_tgts[ntgts++]; + + /* we're done if it is the target */ + + if (!*next_server++) break; + } } - /* got/finally have tgt! */ - if (!valid_keytype(tgt.keyblock.keytype)) { - retval = KRB5_PROG_KEYTYPE_NOSUPP; - goto errout; - } - etype = krb5_keytype_array[tgt.keyblock.keytype]->system->proto_enctype; - - if (cred->second_ticket.length) - retval = krb5_get_cred_via_2tgt(&tgt, - KDC_OPT_ENC_TKT_IN_SKEY | flags2options(tgt.ticket_flags), - etype, krb5_kdc_req_sumtype, cred); - - else - retval = krb5_get_cred_via_tgt(&tgt, - flags2options(tgt.ticket_flags), - etype, - krb5_kdc_req_sumtype, - cred); -errout: - if (!returning_tgt) - krb5_free_cred_contents(&tgt); - if (final_server) - krb5_free_principal(final_server); - krb5_free_cred_contents(&tgtq); - if (tgs_list) - krb5_free_realm_tree(tgs_list); - return retval; + } + + /* got/finally have tgt! try for the creds */ + + if (!valid_keytype(tgt.keyblock.keytype)) { + retval = KRB5_PROG_KEYTYPE_NOSUPP; + goto cleanup; + } + + etype = TGT_ETYPE; + if (cred->second_ticket.length) { + retval = krb5_get_cred_via_2tgt(&tgt, + KDC_OPT_ENC_TKT_IN_SKEY | + FLAGS2OPTS(tgt.ticket_flags), + etype, + krb5_kdc_req_sumtype, + cred); + } + else { + retval = krb5_get_cred_via_tgt(&tgt, + FLAGS2OPTS(tgt.ticket_flags), + etype, + krb5_kdc_req_sumtype, + cred); + } + + /* cleanup and return */ + +cleanup: + + if(tgs_list) krb5_free_realm_tree(tgs_list); + krb5_free_cred_contents(&tgtq); + if (int_server) krb5_free_principal(int_server); + if (ntgts == 0) { + *tgts = NULL; + if (ret_tgts) free(ret_tgts); + krb5_free_cred_contents(&tgt); + } + return(retval); } diff --git a/src/lib/krb5/krb/gc_via_tgt.c b/src/lib/krb5/krb/gc_via_tgt.c index 23dc668f3..03ae702c4 100644 --- a/src/lib/krb5/krb/gc_via_tgt.c +++ b/src/lib/krb5/krb/gc_via_tgt.c @@ -58,17 +58,20 @@ OLDDECLARG(krb5_creds *, cred) krb5_response tgsrep; /* tgt->client must be equal to cred->client */ - /* tgt->server must be equal to krbtgt/realmof(cred->client) */ + if (!krb5_principal_compare(tgt->client, cred->client)) return KRB5_PRINC_NOMATCH; if (!tgt->ticket.length) return(KRB5_NO_TKT_SUPPLIED); - if (retval = krb5_tgtname(krb5_princ_realm(cred->server), - krb5_princ_realm(cred->client), &tempprinc)) + /* check if we have the right TGT */ + /* tgt->server must be equal to */ + /* krbtgt/realmof(cred->server)@realmof(tgt->server) */ + + if(retval = krb5_tgtname(krb5_princ_realm(cred->server), + krb5_princ_realm(tgt->server), &tempprinc)) return(retval); - if (!krb5_principal_compare(tempprinc, tgt->server)) { krb5_free_principal(tempprinc); return KRB5_PRINC_NOMATCH; -- 2.26.2