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 <abokovoy@redhat.com>
Signed-off-by: Isaac Boukris <iboukris@redhat.com>
Reviewed-By: Isaac Boukris <iboukris@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
This commit is contained in:
Alexander Bokovoy 2020-05-01 12:50:41 +03:00
parent 0f881ca0f2
commit 44a255d423
2 changed files with 71 additions and 10 deletions

View File

@ -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;

View File

@ -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;