From 1990e3954b3c566a52697e38569ad472a62a7895 Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: May 27 2020 19:19:49 +0000 Subject: kdb: add minimal server referrals support for enterprise principals Implement minimal server referrals support for enterprise principals as defined in RFC 6806. Use krb5_pac_verify_ext() and krb5_pac_sign_ext() to support cross-realm S4U extensions. We have to verify/sign PAC and take the realm into account for S4U in these cases. The use of extended functions require krb5 1.17+. For PAC verification, we have to filter existing PAC CLIENT-INFO structure in cross-realm S4U case because otherwise old CLIENT-INFO would change the PAC principal due to adding or ommiting the realm in transition. Since a new PAC CLIENT-INFO will be provided by k5_insert_client_info() anyway, we can filter it in all cases. Generate PAC only for the first S4U2Self request to the client realm (client != NULL). Otherwise, use the PAC from the cross-realm ticket. The latter PAC belongs to the impersonated user. Foreign (inner) principal look up in non-AS request returns KRB5_KDB_NOENTRY. Finally, in PAC signing we have to take the realm into account as well for S4U2Self cross-realm operation. This does not work when compiling against krb5 1.17 at the moment because sign_authdata() callback does not know whether we are dealing with an issuing referral or not. In 1.18 a KDC will set a special client flag to signify this when asking KDB driver to sign a PAC record. Fixes: https://pagure.io/freeipa/issue/8319 Signed-off-by: Alexander Bokovoy Signed-off-by: Isaac Boukris Reviewed-By: Isaac Boukris Reviewed-By: Florence Blanc-Renaud --- diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c index 2556286..6e8fa20 100644 --- a/daemons/ipa-kdb/ipa_kdb_mspac.c +++ b/daemons/ipa-kdb/ipa_kdb_mspac.c @@ -1840,8 +1840,12 @@ static krb5_error_code ipadb_verify_pac(krb5_context context, priv_key = krbtgt_key; } - kerr = krb5_pac_verify(context, old_pac, authtime, - client_princ, srv_key, priv_key); + /* only pass with_realm TRUE when it is cross-realm ticket and S4U + * extension (S4U2Self or S4U2Proxy (RBCD)) was requested */ + kerr = krb5_pac_verify_ext(context, old_pac, authtime, + client_princ, srv_key, priv_key, + (is_cross_realm && + (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION))); if (kerr) { goto done; } @@ -1875,7 +1879,8 @@ static krb5_error_code ipadb_verify_pac(krb5_context context, for (i = 0; i < num_buffers; i++) { if (types[i] == KRB5_PAC_SERVER_CHECKSUM || - types[i] == KRB5_PAC_PRIVSVR_CHECKSUM) { + types[i] == KRB5_PAC_PRIVSVR_CHECKSUM || + types[i] == KRB5_PAC_CLIENT_INFO) { continue; } @@ -1933,6 +1938,7 @@ done: } static krb5_error_code ipadb_sign_pac(krb5_context context, + unsigned int flags, krb5_const_principal client_princ, krb5_db_entry *server, krb5_db_entry *krbtgt, @@ -1948,6 +1954,7 @@ static krb5_error_code ipadb_sign_pac(krb5_context context, krb5_principal krbtgt_princ = NULL; krb5_error_code kerr; char *princ = NULL; + bool is_issuing_referral = false; int ret; /* for cross realm trusts cases we need to sign with the right key. @@ -2006,8 +2013,17 @@ static krb5_error_code ipadb_sign_pac(krb5_context context, right_krbtgt_signing_key = krbtgt_key; } - kerr = krb5_pac_sign(context, pac, authtime, client_princ, - server_key, right_krbtgt_signing_key, pac_data); +#ifdef KRB5_KDB_FLAG_ISSUING_REFERRAL + is_issuing_referral = (flags & KRB5_KDB_FLAG_ISSUING_REFERRAL) != 0; +#endif + + /* only pass with_realm TRUE when it is cross-realm ticket and S4U2Self + * was requested */ + kerr = krb5_pac_sign_ext(context, pac, authtime, client_princ, server_key, + right_krbtgt_signing_key, + (is_issuing_referral && + (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION)), + pac_data); done: free(princ); @@ -2224,9 +2240,10 @@ krb5_error_code ipadb_sign_authdata(krb5_context context, } /* we need to create a PAC if we are requested one and this is an AS REQ, - * or we are doing protocol transition (s4u2self) */ + * or we are doing protocol transition (S4USelf) but not over cross-realm + */ if ((is_as_req && (flags & KRB5_KDB_FLAG_INCLUDE_PAC)) || - (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) { + ((flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION) && (client != NULL))) { make_ad = true; } @@ -2296,7 +2313,7 @@ krb5_error_code ipadb_sign_authdata(krb5_context context, goto done; } - kerr = ipadb_sign_pac(context, ks_client_princ, server, krbtgt, + kerr = ipadb_sign_pac(context, flags, ks_client_princ, server, krbtgt, server_key, krbtgt_key, authtime, pac, &pac_data); if (kerr != 0) { goto done; diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c index a647b96..57f8d01 100644 --- a/daemons/ipa-kdb/ipa_kdb_principals.c +++ b/daemons/ipa-kdb/ipa_kdb_principals.c @@ -1361,7 +1361,12 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext, upn->length - (realm - upn->data), &trusted_realm); } - if (kerr == 0) { + + if (kerr != 0) { + goto done; + } + + if (flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) { kentry = calloc(1, sizeof(krb5_db_entry)); if (!kentry) { kerr = ENOMEM; @@ -1378,8 +1383,47 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext, goto done; } *entry = kentry; + + goto done; + } else if (flags & KRB5_KDB_FLAG_INCLUDE_PAC) { + kerr = KRB5_KDB_NOENTRY; + goto done; + } else { + /* server referrals: lookup krbtgt/next_realm@our_realm */ + krb5_principal tgtp; + + kerr = krb5_build_principal_ext(kcontext, &tgtp, + strlen(ipactx->realm), + ipactx->realm, + KRB5_TGS_NAME_SIZE, + KRB5_TGS_NAME, + strlen(trusted_realm), + trusted_realm, 0); + if (kerr != 0) { + goto done; + } + + krb5_free_unparsed_name(kcontext, principal); + principal = NULL; + kerr = krb5_unparse_name(kcontext, tgtp, &principal); + krb5_free_principal(kcontext, tgtp); + if (kerr != 0) { + goto done; + } + + ldap_msgfree(res); + res = NULL; + kerr = ipadb_fetch_principals(ipactx, flags, principal, &res); + if (kerr != 0) { + goto done; + } + + kerr = ipadb_find_principal(kcontext, flags, res, &principal, + &lentry); + if (kerr != 0) { + goto done; + } } - goto done; } } else { goto done;