diff --git a/kdc/krb5tgs.c b/kdc/krb5tgs.c index 6fddc830c..1d04d0432 100644 --- a/kdc/krb5tgs.c +++ b/kdc/krb5tgs.c @@ -1092,23 +1092,6 @@ out: return ret; } -/* - * - */ - -static const char * -find_rpath(krb5_context context, Realm crealm, Realm srealm) -{ - const char *new_realm = krb5_config_get_string(context, - NULL, - "capaths", - crealm, - srealm, - NULL); - return new_realm; -} - - static krb5_boolean need_referral(krb5_context context, krb5_kdc_configuration *config, const KDCOptions * const options, krb5_principal server, @@ -1538,6 +1521,12 @@ tgs_build_reply(krb5_context context, krb5_keyblock sessionkey; krb5_kvno kvno; krb5_data rspac; + const char *tgt_realm = /* Realm of TGT issuer */ + krb5_principal_get_realm(context, krbtgt->entry.principal); + const char *our_realm = /* Realm of this KDC */ + krb5_principal_get_comp_string(context, krbtgt->entry.principal, 1); + char **capath = NULL; + size_t num_capath = 0; hdb_entry_ex *krbtgt_out = NULL; @@ -1545,7 +1534,6 @@ tgs_build_reply(krb5_context context, PrincipalName *s; Realm r; - int nloop = 0; EncTicketPart adtkt; char opt_str[128]; int signedpath = 0; @@ -1654,11 +1642,10 @@ server_lookup: kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", sp); goto out; } else if (ret == HDB_ERR_WRONG_REALM) { - if (ref_realm) - free(ref_realm); + free(ref_realm); ref_realm = strdup(server->entry.principal->realm); if (ref_realm == NULL) { - ret = ENOMEM; + ret = krb5_enomem(context); goto out; } @@ -1668,12 +1655,12 @@ server_lookup: ref_realm, spn); krb5_free_principal(context, sp); sp = NULL; - free(spn); - spn = NULL; ret = krb5_make_principal(context, &sp, r, KRB5_TGS_NAME, ref_realm, NULL); if (ret) goto out; + free(spn); + spn = NULL; ret = krb5_unparse_name(context, sp, &spn); if (ret) goto out; @@ -1685,26 +1672,36 @@ server_lookup: krb5_realm *realms; if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) { - if (nloop++ < 2) { - new_rlm = find_rpath(context, tgt->crealm, req_rlm); - if(new_rlm) { - kdc_log(context, config, 5, "krbtgt for realm %s " - "not found, trying %s", - req_rlm, new_rlm); - krb5_free_principal(context, sp); - free(spn); - krb5_make_principal(context, &sp, r, - KRB5_TGS_NAME, new_rlm, NULL); - ret = krb5_unparse_name(context, sp, &spn); - if (ret) - goto out; + if (capath == NULL) { + ret = _krb5_find_capath(context, tgt->crealm, our_realm, + req_rlm, TRUE, &capath, &num_capath); + if (ret) + goto out; + } + new_rlm = num_capath > 0 ? capath[--num_capath] : NULL; + if (new_rlm) { + kdc_log(context, config, 5, "krbtgt from %s via %s for " + "realm %s not found, trying %s", tgt->crealm, + our_realm, req_rlm, new_rlm); - if (ref_realm) - free(ref_realm); - ref_realm = strdup(new_rlm); - goto server_lookup; - } - } + free(ref_realm); + ref_realm = strdup(new_rlm); + if (ref_realm == NULL) { + ret = krb5_enomem(context); + goto out; + } + + krb5_free_principal(context, sp); + sp = NULL; + krb5_make_principal(context, &sp, r, + KRB5_TGS_NAME, ref_realm, NULL); + free(spn); + spn = NULL; + ret = krb5_unparse_name(context, sp, &spn); + if (ret) + goto out; + goto server_lookup; + } } else if (need_referral(context, config, &b->kdc_options, sp, &realms)) { if (strcmp(realms[0], sp->realm) != 0) { kdc_log(context, config, 5, @@ -1712,17 +1709,18 @@ server_lookup: "server %s that was not found", realms[0], spn); krb5_free_principal(context, sp); - free(spn); + sp = NULL; krb5_make_principal(context, &sp, r, KRB5_TGS_NAME, realms[0], NULL); + free(spn); + spn = NULL; ret = krb5_unparse_name(context, sp, &spn); if (ret) { krb5_free_host_realm(context, realms); goto out; } - if (ref_realm) - free(ref_realm); + free(ref_realm); ref_realm = strdup(realms[0]); krb5_free_host_realm(context, realms); @@ -1827,29 +1825,24 @@ server_lookup: * have been an incoming trust) */ - { - const char *remote_realm = - krb5_principal_get_comp_string(context, krbtgt->entry.principal, 1); - - ret = krb5_make_principal(context, - &krbtgt_out_principal, - remote_realm, - KRB5_TGS_NAME, - remote_realm, - NULL); - if(ret) { - kdc_log(context, config, 0, - "Failed to make krbtgt principal name object for " - "authz-data signatures"); - goto out; - } - ret = krb5_unparse_name(context, krbtgt_out_principal, &krbtgt_out_n); - if (ret) { - kdc_log(context, config, 0, - "Failed to make krbtgt principal name object for " - "authz-data signatures"); - goto out; - } + ret = krb5_make_principal(context, + &krbtgt_out_principal, + our_realm, + KRB5_TGS_NAME, + our_realm, + NULL); + if (ret) { + kdc_log(context, config, 0, + "Failed to make krbtgt principal name object for " + "authz-data signatures"); + goto out; + } + ret = krb5_unparse_name(context, krbtgt_out_principal, &krbtgt_out_n); + if (ret) { + kdc_log(context, config, 0, + "Failed to make krbtgt principal name object for " + "authz-data signatures"); + goto out; } ret = _kdc_db_fetch(context, config, krbtgt_out_principal, @@ -2349,6 +2342,7 @@ out: free(cpn); free(dpn); free(krbtgt_out_n); + _krb5_free_capath(context, capath); krb5_data_free(&rspac); krb5_free_keyblock_contents(context, &sessionkey); @@ -2367,8 +2361,7 @@ out: krb5_free_principal(context, dp); krb5_free_principal(context, sp); krb5_free_principal(context, krbtgt_out_principal); - if (ref_realm) - free(ref_realm); + free(ref_realm); free_METHOD_DATA(&enc_pa_data); free_EncTicketPart(&adtkt); diff --git a/tests/kdc/check-kdc.in b/tests/kdc/check-kdc.in index 308e231a7..4714b2620 100644 --- a/tests/kdc/check-kdc.in +++ b/tests/kdc/check-kdc.in @@ -54,6 +54,17 @@ R5=SOME-REALM5.FR R6=SOME-REALM6.US R7=SOME-REALM7.UK +H1=H1.$R +H2=H2.$R +H3=H3.$H2 +H4=H4.$H2 + +r=`echo "$R" | tr '[A-Z]' '[a-z]'` +h1=`echo "${H1}" | tr '[A-Z]' '[a-z]'` +h2=`echo "${H2}" | tr '[A-Z]' '[a-z]'` +h3=`echo "${H3}" | tr '[A-Z]' '[a-z]'` +h4=`echo "${H4}" | tr '[A-Z]' '[a-z]'` + port=@port@ pwport=@pwport@ @@ -137,6 +148,30 @@ ${kadmin} \ --realm-max-renewable-life=1month \ ${R7} || exit 1 +${kadmin} \ + init \ + --realm-max-ticket-life=1day \ + --realm-max-renewable-life=1month \ + ${H1} || exit 1 + +${kadmin} \ + init \ + --realm-max-ticket-life=1day \ + --realm-max-renewable-life=1month \ + ${H2} || exit 1 + +${kadmin} \ + init \ + --realm-max-ticket-life=1day \ + --realm-max-renewable-life=1month \ + ${H3} || exit 1 + +${kadmin} \ + init \ + --realm-max-ticket-life=1day \ + --realm-max-renewable-life=1month \ + ${H4} || exit 1 + ${kadmin} \ init \ --realm-max-ticket-life=1day \ @@ -149,12 +184,21 @@ ${kadmin} cpw -r krbtgt/${R}@${R} || exit 1 ${kadmin} cpw -r krbtgt/${R}@${R} || exit 1 ${kadmin} add -p foo --use-defaults foo@${R} || exit 1 +${kadmin} add -p foo --use-defaults foo/host.${r}@${R} || exit 1 ${kadmin} add -p foo --use-defaults foo@${R2} || exit 1 ${kadmin} add -p foo --use-defaults foo@${R3} || exit 1 ${kadmin} add -p foo --use-defaults foo@${R4} || exit 1 ${kadmin5} add -p foo --use-defaults foo@${R5} || exit 1 ${kadmin} add -p foo --use-defaults foo@${R6} || exit 1 ${kadmin} add -p foo --use-defaults foo@${R7} || exit 1 +${kadmin} add -p foo --use-defaults foo@${H1} || exit 1 +${kadmin} add -p foo --use-defaults foo/host.${h1}@${H1} || exit 1 +${kadmin} add -p foo --use-defaults foo@${H2} || exit 1 +${kadmin} add -p foo --use-defaults foo/host.${h2}@${H2} || exit 1 +${kadmin} add -p foo --use-defaults foo@${H3} || exit 1 +${kadmin} add -p foo --use-defaults foo/host.${h3}@${H3} || exit 1 +${kadmin} add -p foo --use-defaults foo@${H4} || exit 1 +${kadmin} add -p foo --use-defaults foo/host.${h4}@${H4} || exit 1 ${kadmin} add -p bar --use-defaults bar@${R} || exit 1 ${kadmin} add -p foo --use-defaults remove@${R} || exit 1 ${kadmin} add -p nop --use-defaults ${server}@${R} || exit 1 @@ -205,6 +249,18 @@ ${kadmin} add -p cross2 --use-defaults krbtgt/${R5}@${R6} || exit 1 ${kadmin} add -p cross1 --use-defaults krbtgt/${R7}@${R6} || exit 1 ${kadmin} add -p cross2 --use-defaults krbtgt/${R6}@${R7} || exit 1 +${kadmin} add -p cross1 --use-defaults krbtgt/${H1}@${R} || exit 1 +${kadmin} add -p cross2 --use-defaults krbtgt/${R}@${H1} || exit 1 + +${kadmin} add -p cross1 --use-defaults krbtgt/${H2}@${R} || exit 1 +${kadmin} add -p cross2 --use-defaults krbtgt/${R}@${H2} || exit 1 + +${kadmin} add -p cross1 --use-defaults krbtgt/${H3}@${H2} || exit 1 +${kadmin} add -p cross2 --use-defaults krbtgt/${H2}@${H3} || exit 1 + +${kadmin} add -p cross1 --use-defaults krbtgt/${H3}@${H4} || exit 1 +${kadmin} add -p cross2 --use-defaults krbtgt/${H4}@${H3} || exit 1 + ${kadmin} add -p foo --use-defaults pw-expire@${R} || exit 1 ${kadmin} modify --pw-expiration-time=+1day pw-expire@${R} || exit 1 @@ -228,6 +284,10 @@ ${kadmin} check ${R4} || exit 1 ${kadmin5} check ${R5} || exit 1 ${kadmin} check ${R6} || exit 1 ${kadmin} check ${R7} || exit 1 +${kadmin} check ${H1} || exit 1 +${kadmin} check ${H2} || exit 1 +${kadmin} check ${H3} || exit 1 +${kadmin} check ${H4} || exit 1 echo "Extracting enctypes" ${ktutil} -k ${keytab} list > tempfile || exit 1 @@ -354,6 +414,30 @@ echo "Getting x-realm tickets with capaths for $R -> $R5" ${kgetcred} foo@${R5} || { ec=1 ; eval "${testfailed}"; } ${kdestroy} +echo "Testing hierarchical referral logic" +${kinit} --password-file=${objdir}/foopassword \ + -e ${aesenctype} -e ${aesenctype} \ + foo@${H3} || \ + { ec=1 ; eval "${testfailed}"; } + +echo "Getting x-realm tickets with hierarchical referrals for $H3 -> $H1" +${kgetcred} --hostbased --canonicalize foo host.${h1} || { ec=1 ; eval "${testfailed}"; } +echo "Getting x-realm tickets with hierarchical referrals for $H3 -> $R" +${kgetcred} --hostbased --canonicalize foo host.${r} || { ec=1 ; eval "${testfailed}"; } +echo "Getting x-realm tickets with hierarchical referrals for $H3 -> $H2" +${kgetcred} --hostbased --canonicalize foo host.${h2} || { ec=1 ; eval "${testfailed}"; } +${kdestroy} + +echo "Testing multi-hop [capaths] referral logic" +${kinit} --password-file=${objdir}/foopassword \ + -e ${aesenctype} -e ${aesenctype} \ + foo@${H4} || \ + { ec=1 ; eval "${testfailed}"; } + +echo "Getting x-realm tickets with [capaths] referrals for $H4 -> $H1" +${kgetcred} --hostbased --canonicalize foo/host.${h1}@${H4} || { ec=1 ; eval "${testfailed}"; } +${kdestroy} + echo "Testing forwardable/renewable flag copying in TGS-REQ" ${kinit} -f --renewable -r 5d --password-file=${objdir}/foopassword foo@$R || \ { ec=1 ; eval "${testfailed}"; } diff --git a/tests/kdc/krb5.conf.in b/tests/kdc/krb5.conf.in index aa3b44c64..0e0240799 100644 --- a/tests/kdc/krb5.conf.in +++ b/tests/kdc/krb5.conf.in @@ -43,10 +43,26 @@ TEST-HTTP.H5L.SE = { kdc = http/localhost:@port@ } + H1.TEST.H5L.SE = { + kdc = localhost:@port@ + } + H2.TEST.H5L.SE = { + kdc = localhost:@port@ + } + H3.H2.TEST.H5L.SE = { + kdc = localhost:@port@ + } + H4.H2.TEST.H5L.SE = { + kdc = localhost:@port@ + } [domain_realm] .test.h5l.se = TEST.H5L.SE .sub.test.h5l.se = SUB.TEST.H5L.SE + .h1.test.h5l.se = H1.TEST.H5L.SE + .h2.test.h5l.se = H2.TEST.H5L.SE + .h3.h2.test.h5l.se = H3.H2.TEST.H5L.SE + .h4.h2.test.h5l.se = H4.H2.TEST.H5L.SE .example.com = TEST2.H5L.SE localhost = TEST.H5L.SE .localdomain = TEST.H5L.SE @@ -131,3 +147,13 @@ SOME-REALM7.UK = SOME-REALM6.US SOME-REALM7.UK = SOME-REALM5.FR } + H4.H2.TEST.H5L.SE = { + H1.TEST.H5L.SE = H3.H2.TEST.H5L.SE + H1.TEST.H5L.SE = H2.TEST.H5L.SE + H1.TEST.H5L.SE = TEST.H5L.SE + + TEST.H5L.SE = H3.H2.TEST.H5L.SE + TEST.H5L.SE = H2.TEST.H5L.SE + + H2.TEST.H5L.SE = H3.H2.TEST.H5L.SE + }