ipa-kdb: add krb5 1.20 support

Add basic krb5 1.20 integration without RBCD support. RBCD will come in
a separate series.

Fixes: https://pagure.io/freeipa/issue/9083
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Julien Rische <jrische@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
This commit is contained in:
Alexander Bokovoy 2022-06-07 11:41:19 +03:00
parent f0c72dcb87
commit e9ae0e350d
9 changed files with 213 additions and 29 deletions

View File

@ -798,11 +798,20 @@ kdb_vftabl kdb_function_table = {
#endif
#if (KRB5_KDB_DAL_MAJOR_VERSION == 9)
#error DAL version 9 is not supported yet
/* Version 9 removes sign_authdata and adds issue_pac method. It is a complete
* revamp of how PAC is issued, so we need to implement it differently to previous
* versions. */
krb5_error_code
ipadb_v9_issue_pac(krb5_context context, unsigned int flags,
krb5_db_entry *client,
krb5_keyblock *replaced_reply_key,
krb5_db_entry *server,
krb5_db_entry *signing_krbtgt,
krb5_timestamp authtime, krb5_pac old_pac,
krb5_pac new_pac,
krb5_data ***auth_indicators);
kdb_vftabl kdb_function_table = {
.maj_ver = KRB5_KDB_DAL_MAJOR_VERSION,
.min_ver = 0,
@ -831,7 +840,7 @@ kdb_vftabl kdb_function_table = {
.free_principal_e_data = ipadb_free_principal_e_data,
.get_s4u_x509_principal = NULL,
.allowed_to_delegate_from = NULL,
.issue_pac = NULL,
.issue_pac = ipadb_v9_issue_pac,
};
#endif

View File

@ -49,6 +49,22 @@
#include "ipa_krb5.h"
#include "ipa_pwd.h"
/* Difference between krb5 1.20 and previous versions. From
* krb5 commit a441fbe329ebbd7775eb5d4ccc4a05eef370f08b:
* Combine the KRB5_KDB_FLAG_ISSUE_PAC and
* KRB5_FLAG_CLIENT_REFERRALS_ONLY flags into KRB5_KDB_FLAG_CLIENT.
*
* Rename the KRB5_KDB_FLAG_CANONICALIZE flag to
* KRB5_KDB_FLAG_REFERRAL_OK, and only pass it to get_principal() for
* lookup operations that can use a realm referral.
* */
#if defined(KRB5_KDB_FLAG_CLIENT)
#define CLIENT_REFERRALS_FLAGS (KRB5_KDB_FLAG_REFERRAL_OK)
#else
#define CLIENT_REFERRALS_FLAGS (KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY)
#endif
/* easier to copy the defines here than to mess with kadm5/admin.h
* for now */
#define KMASK_PRINCIPAL 0x000001
@ -309,6 +325,7 @@ krb5_error_code ipadb_get_pwd_expiration(krb5_context context,
/* MS-PAC FUNCTIONS */
#if (KRB5_KDB_DAL_MAJOR_VERSION < 9)
krb5_error_code ipadb_sign_authdata(krb5_context context,
unsigned int flags,
krb5_const_principal client_princ,
@ -323,6 +340,18 @@ krb5_error_code ipadb_sign_authdata(krb5_context context,
krb5_authdata **tgt_auth_data,
krb5_authdata ***signed_auth_data);
#else
/* DAL 9 or later uses issue_pac */
krb5_error_code ipadb_v9_issue_pac(krb5_context context, unsigned int flags,
krb5_db_entry *client,
krb5_keyblock *replaced_reply_key,
krb5_db_entry *server,
krb5_db_entry *signing_krbtgt,
krb5_timestamp authtime, krb5_pac old_pac,
krb5_pac new_pac,
krb5_data ***auth_indicators);
#endif
krb5_error_code ipadb_reinit_mspac(struct ipadb_context *ipactx, bool force_reinit);
void ipadb_mspac_struct_free(struct ipadb_mspac **mspac);
@ -375,4 +404,4 @@ void ipa_certauth_free_moddata(krb5_certauth_moddata *moddata);
int ipadb_string_to_sid(const char *str, struct dom_sid *sid);
void alloc_sid(struct dom_sid **sid);
void free_sid(struct dom_sid **sid);
bool dom_sid_check(const struct dom_sid *sid1, const struct dom_sid *sid2, bool exact_check);
bool dom_sid_check(const struct dom_sid *sid1, const struct dom_sid *sid2, bool exact_check);

View File

@ -198,7 +198,7 @@ krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
/* Handle the case where server == proxy, this is allowed in S4U*/
kerr = ipadb_get_principal(kcontext, proxy,
KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY,
CLIENT_REFERRALS_FLAGS,
&proxy_entry);
if (kerr) {
goto done;

View File

@ -1155,7 +1155,7 @@ krb5_error_code ipadb_get_pac(krb5_context kcontext,
#ifdef HAVE_PAC_REQUESTER_SID
/* MS-KILE 3.3.5.6.4.8: add PAC_REQUESTER_SID only in TGT case */
if ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) != 0) {
if ((flags & (CLIENT_REFERRALS_FLAGS)) != 0) {
union PAC_INFO pac_requester_sid;
/* == Package PAC_REQUESTER_SID == */
memset(&pac_requester_sid, 0, sizeof(pac_requester_sid));
@ -1559,7 +1559,7 @@ static krb5_error_code save_logon_info(krb5_context context,
}
static struct ipadb_adtrusts *get_domain_from_realm(krb5_context context,
krb5_data realm)
krb5_data *realm)
{
struct ipadb_context *ipactx;
struct ipadb_adtrusts *domain;
@ -1576,10 +1576,10 @@ static struct ipadb_adtrusts *get_domain_from_realm(krb5_context context,
for (i = 0; i < ipactx->mspac->num_trusts; i++) {
domain = &ipactx->mspac->trusts[i];
if (strlen(domain->domain_name) != realm.length) {
if (strlen(domain->domain_name) != realm->length) {
continue;
}
if (strncasecmp(domain->domain_name, realm.data, realm.length) == 0) {
if (strncasecmp(domain->domain_name, realm->data, realm->length) == 0) {
return domain;
}
}
@ -1588,7 +1588,7 @@ static struct ipadb_adtrusts *get_domain_from_realm(krb5_context context,
}
static struct ipadb_adtrusts *get_domain_from_realm_update(krb5_context context,
krb5_data realm)
krb5_data *realm)
{
struct ipadb_context *ipactx;
struct ipadb_adtrusts *domain;
@ -1752,7 +1752,7 @@ done:
krb5_error_code filter_logon_info(krb5_context context,
TALLOC_CTX *memctx,
krb5_data realm,
krb5_data *realm,
struct PAC_LOGON_INFO_CTR *info)
{
@ -1978,6 +1978,7 @@ krb5_error_code filter_logon_info(krb5_context context,
static krb5_error_code ipadb_check_logon_info(krb5_context context,
krb5_db_entry *client,
krb5_db_entry *signing_krbtgt,
krb5_boolean is_cross_realm,
krb5_boolean is_s4u,
krb5_data *pac_blob,
@ -2052,7 +2053,13 @@ static krb5_error_code ipadb_check_logon_info(krb5_context context,
goto done;
}
kerr = filter_logon_info(context, tmpctx, origin_realm, &info);
if (client != NULL) {
origin_realm = client->princ->realm;
} else {
origin_realm = signing_krbtgt->princ->realm;
}
kerr = filter_logon_info(context, tmpctx, &origin_realm, &info);
if (kerr) {
goto done;
}
@ -2268,6 +2275,7 @@ krb5_error_code ipadb_common_verify_pac(krb5_context context,
kerr = ipadb_check_logon_info(context,
client,
signing_krbtgt,
is_cross_realm,
(flags & KRB5_KDB_FLAGS_S4U),
&pac_blob,
@ -2366,6 +2374,7 @@ krb5_error_code ipadb_common_verify_pac(krb5_context context,
}
}
#if !defined(KRB5_KDB_FLAG_CLIENT)
if (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
if (client == NULL) {
if (new_pac != *pac) {
@ -2382,6 +2391,7 @@ krb5_error_code ipadb_common_verify_pac(krb5_context context,
goto done;
}
}
#endif
*pac = new_pac;
@ -2496,9 +2506,12 @@ void get_authz_data_types(krb5_context context, krb5_db_entry *entry,
none_found = true;
}
} else {
krb5_klog_syslog(LOG_ERR, "Ignoring unsupported " \
"authorization data type [%s].",
authz_data_list[c]);
/* for out-of-realm entries we suppress warnings in our defaults */
if (entry != NULL) {
krb5_klog_syslog(LOG_ERR, "Ignoring unsupported " \
"authorization data type [%s].",
authz_data_list[c]);
}
}
}

View File

@ -53,7 +53,7 @@ struct ipadb_adtrusts {
char *dom_sid_string(TALLOC_CTX *memctx, const struct dom_sid *dom_sid);
krb5_error_code filter_logon_info(krb5_context context, TALLOC_CTX *memctx,
krb5_data realm, struct PAC_LOGON_INFO_CTR *info);
krb5_data *realm, struct PAC_LOGON_INFO_CTR *info);
void get_authz_data_types(krb5_context context, krb5_db_entry *entry,
bool *_with_pac, bool *_with_pad);

View File

@ -31,4 +31,108 @@
#include "ipa_kdb_mspac_private.h"
krb5_error_code
ipadb_v9_issue_pac(krb5_context context, unsigned int flags,
krb5_db_entry *client,
krb5_keyblock *replaced_reply_key,
krb5_db_entry *server,
krb5_db_entry *signing_krbtgt,
krb5_timestamp authtime,
krb5_pac old_pac,
krb5_pac new_pac,
krb5_data ***auth_indicators)
{
bool with_pac;
bool with_pad;
krb5_error_code kerr = 0;
bool is_as_req = flags & CLIENT_REFERRALS_FLAGS;
if (is_as_req) {
get_authz_data_types(context, client, &with_pac, &with_pad);
} else {
get_authz_data_types(context, server, &with_pac, &with_pad);
}
if (with_pad) {
krb5_klog_syslog(LOG_ERR, "PAD authorization data is requested but " \
"currently not supported.");
}
/*
* Get a new PAC for AS-REQ or S4U2Self for our realm.
*
* For a simple cross-realm S4U2Proxy there will be the following TGS
* requests after the client realm is identified:
*
* 1. server@SREALM to SREALM for krbtgt/CREALM@SREALM -- a regular TGS
* request with server's normal TGT and no S4U2Self padata.
* 2. server@SREALM to CREALM for server@SREALM (expressed as an
* enterprise principal), with the TGT from #1 as header ticket and
* S4U2Self padata identifying the client.
* 3. server@SREALM to SREALM for server@SREALM with S4U2Self padata,
* with the referral TGT from #2 as header ticket
*
* In request 2 the PROTOCOL_TRANSITION and CROSS_REALM flags are set,
* and the request is for a local client (so client != NULL) and we
* want to make a new PAC.
*
* In request 3 the PROTOCOL_TRANSITION and CROSS_REALM flags are also
* set, but the request is for a non-local client (so client == NULL)
* and we want to copy the subject PAC contained in the referral TGT.
*/
if (old_pac == NULL ||
(client != NULL && (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION))) {
/* generate initial PAC */
if (with_pac) {
krb5_boolean force_reinit_mspac = FALSE;
struct ipadb_context *ipactx = ipadb_get_context(context);
int result = 0;
if (!ipactx) {
kerr = ENOMEM;
goto done;
}
if (client != NULL) {
/* Be aggressive here: special case for discovering range type
* immediately after establishing the trust by IPA framework. For all
* other cases call ipadb_reinit_mspac() with force_reinit_mspac set
* to 'false' to make sure the information about trusted domains is
* updated on a regular basis for all worker processes. */
if ((krb5_princ_size(context, client->princ) == 2) &&
(strncmp(krb5_princ_component(context, client->princ, 0)->data, "HTTP",
krb5_princ_component(context, client->princ, 0)->length) == 0) &&
(ulc_casecmp(krb5_princ_component(context, client->princ, 1)->data,
krb5_princ_component(context, client->princ, 1)->length,
ipactx->kdc_hostname, strlen(ipactx->kdc_hostname),
NULL, NULL, &result) == 0)) {
force_reinit_mspac = TRUE;
}
}
(void)ipadb_reinit_mspac(ipactx, force_reinit_mspac);
/* MS-PAC needs proper configuration and if it is missing, we simply skip issuing one */
if (ipactx->mspac->flat_server_name == NULL) {
return KRB5_PLUGIN_OP_NOTSUPP;
}
kerr = ipadb_get_pac(context, flags,
client, server, replaced_reply_key,
authtime, &new_pac);
}
} else {
kerr = ipadb_common_verify_pac(context, flags,
client, server,
signing_krbtgt,
NULL,
authtime,
old_pac, &new_pac);
if (kerr == ENOENT) {
kerr = 0;
}
}
/* in krb5 1.20 no need to sign tickets anymore, KDC does it for us */
done:
return kerr;
}

View File

@ -1362,6 +1362,9 @@ static krb5_boolean is_request_for_us(krb5_context kcontext,
{
krb5_boolean for_us;
if (search_for == NULL) {
return FALSE;
}
for_us = krb5_realm_compare(kcontext, local_tgs, search_for) ||
krb5_principal_compare_any_realm(kcontext,
local_tgs, search_for);
@ -1379,11 +1382,17 @@ static krb5_error_code dbget_princ(krb5_context kcontext,
LDAPMessage *res = NULL;
LDAPMessage *lentry;
uint32_t pol;
krb5_boolean check = FALSE;
if ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) != 0 &&
(flags & KRB5_KDB_FLAG_CANONICALIZE) != 0) {
#if defined(KRB5_KDB_FLAG_CLIENT)
check = flags & KRB5_KDB_FLAG_CLIENT;
#else
check = (flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) &&
(flags & KRB5_KDB_FLAG_CANONICALIZE);
#endif
if (check) {
/* AS_REQ with canonicalization*/
krb5_principal norm_princ = NULL;
@ -1462,6 +1471,7 @@ static krb5_error_code dbget_alias(krb5_context kcontext,
char *trusted_realm = NULL;
krb5_db_entry *kentry = NULL;
krb5_data *realm;
krb5_boolean check = FALSE;
/* TODO: also support hostbased aliases */
@ -1507,8 +1517,14 @@ static krb5_error_code dbget_alias(krb5_context kcontext,
* both client and server referrals. But it is more useful to ignore it
* like Windows KDC does for client referrals.
*/
if (((flags & KRB5_KDB_FLAG_CANONICALIZE) == 0) &&
((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) == 0)) {
#if defined(KRB5_KDB_FLAG_CLIENT)
check = ((flags & KRB5_KDB_FLAG_CLIENT) == 0) &&
((flags & KRB5_KDB_FLAG_REFERRAL_OK) == 0);
#else
check = ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) == 0) &&
((flags & KRB5_KDB_FLAG_CANONICALIZE) == 0);
#endif
if (check) {
kerr = KRB5_KDB_NOENTRY;
goto done;
}
@ -1540,7 +1556,15 @@ static krb5_error_code dbget_alias(krb5_context kcontext,
/* This is a known trusted realm. Issue a referral depending on whether this
* is client or server referral request */
if (flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) {
#if defined(KRB5_KDB_FLAG_CLIENT)
check = (flags & KRB5_KDB_FLAG_CLIENT) && (flags & KRB5_KDB_FLAG_REFERRAL_OK);
#else
check = (flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) &&
((flags & KRB5_KDB_FLAG_CANONICALIZE) ||
search_for->type == KRB5_NT_ENTERPRISE_PRINCIPAL);
#endif
if (check) {
/* client referral out of realm, set next realm. */
kerr = krb5_set_principal_realm(kcontext, norm_princ, trusted_realm);
if (kerr != 0) {
@ -1559,7 +1583,12 @@ static krb5_error_code dbget_alias(krb5_context kcontext,
goto done;
}
if (flags & KRB5_KDB_FLAG_INCLUDE_PAC) {
#if defined(KRB5_KDB_FLAG_CLIENT)
check = flags & KRB5_KDB_FLAG_CLIENT;
#else
check = flags & KRB5_KDB_FLAG_INCLUDE_PAC;
#endif
if (check) {
/* TGS request where KDC wants to generate PAC
* but the principal is out of our realm */
kerr = KRB5_KDB_NOENTRY;

View File

@ -178,7 +178,7 @@ static int teardown(void **state)
extern krb5_error_code filter_logon_info(krb5_context context,
TALLOC_CTX *memctx,
krb5_data realm,
krb5_data *realm,
struct PAC_LOGON_INFO_CTR *info);
static void test_filter_logon_info(void **state)
@ -204,7 +204,7 @@ static void test_filter_logon_info(void **state)
"WRONG");
assert_non_null(info->info->info3.base.logon_domain.string);
kerr = filter_logon_info(test_ctx->krb5_ctx, test_ctx, realm, info);
kerr = filter_logon_info(test_ctx->krb5_ctx, test_ctx, &realm, info);
assert_int_equal(kerr, EINVAL);
info->info->info3.base.logon_domain.string = talloc_strdup(info->info,
@ -212,7 +212,7 @@ static void test_filter_logon_info(void **state)
assert_non_null(info->info->info3.base.logon_domain.string);
/* missing domain SID */
kerr = filter_logon_info(test_ctx->krb5_ctx, test_ctx, realm, info);
kerr = filter_logon_info(test_ctx->krb5_ctx, test_ctx, &realm, info);
assert_int_equal(kerr, EINVAL);
/* wrong domain SID */
@ -220,7 +220,7 @@ static void test_filter_logon_info(void **state)
assert_int_equal(ret, 0);
info->info->info3.base.domain_sid = &dom_sid;
kerr = filter_logon_info(test_ctx->krb5_ctx, test_ctx, realm, info);
kerr = filter_logon_info(test_ctx->krb5_ctx, test_ctx, &realm, info);
assert_int_equal(kerr, EINVAL);
/* matching domain SID */
@ -228,7 +228,7 @@ static void test_filter_logon_info(void **state)
assert_int_equal(ret, 0);
info->info->info3.base.domain_sid = &dom_sid;
kerr = filter_logon_info(test_ctx->krb5_ctx, test_ctx, realm, info);
kerr = filter_logon_info(test_ctx->krb5_ctx, test_ctx, &realm, info);
assert_int_equal(kerr, 0);
/* empty SIDs */
@ -243,7 +243,7 @@ static void test_filter_logon_info(void **state)
assert_non_null(info->info->info3.sids[c].sid);
}
kerr = filter_logon_info(test_ctx->krb5_ctx, NULL, realm, info);
kerr = filter_logon_info(test_ctx->krb5_ctx, NULL, &realm, info);
assert_int_equal(kerr, 0);
assert_int_equal(info->info->info3.sidcount, 3);
@ -297,7 +297,7 @@ static void test_filter_logon_info(void **state)
assert_int_equal(ret, 0);
}
kerr = filter_logon_info(test_ctx->krb5_ctx, NULL, realm, info);
kerr = filter_logon_info(test_ctx->krb5_ctx, NULL, &realm, info);
assert_int_equal(kerr, 0);
assert_int_equal(info->info->info3.sidcount, test_data[c].exp_sidcount);
if (test_data[c].exp_sidcount == 0) {

View File

@ -88,7 +88,7 @@ dnl ---------------------------------------------------------------------------
AC_CHECK_HEADER(kdb.h, [], [AC_MSG_ERROR([kdb.h not found])])
AC_CHECK_MEMBER([kdb_vftabl.issue_pac],
[have_kdb_issue_pac=yes],
[have_kdb_issue_pac=no])
[have_kdb_issue_pac=no], [#include <kdb.h>])
dnl ---------------------------------------------------------------------------
dnl - Check for UUID library