From 9a0bcbbe82aec0bde30c34ef62320820cd7c0a76 Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Sat, 30 Oct 2021 10:09:27 +0300 Subject: [PATCH] ipa-kdb: add support for PAC_REQUESTER_SID buffer CVE-2020-25721 mitigation: KDC must provide the new PAC_REQUESTER_SID buffer with ObjectSID value associated with the requester's principal. The mitigation only works if NDR library supports the PAC_REQUESTER_SID buffer type. In case we cannot detect it at compile time, a warning will be displayed at configure stage. Fixes: https://pagure.io/freeipa/issue/9031 Signed-off-by: Alexander Bokovoy Reviewed-by: Rob Crittenden Reviewed-By: Rob Crittenden --- daemons/ipa-kdb/ipa_kdb_mspac.c | 131 +++++++++++++++++++++++++++++++- server.m4 | 7 ++ 2 files changed, 134 insertions(+), 4 deletions(-) diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c index 75cb4f3b7..ca6daccf1 100644 --- a/daemons/ipa-kdb/ipa_kdb_mspac.c +++ b/daemons/ipa-kdb/ipa_kdb_mspac.c @@ -812,6 +812,55 @@ static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx, return ret; } +#ifdef HAVE_PAC_REQUESTER_SID +static krb5_error_code ipadb_get_requester_sid(krb5_context context, + krb5_pac pac, + struct dom_sid *sid) +{ + enum ndr_err_code ndr_err; + krb5_error_code ret; + DATA_BLOB pac_requester_sid_in; + krb5_data k5pac_requester_sid_in; + union PAC_INFO info; + TALLOC_CTX *tmp_ctx; + struct ipadb_context *ipactx; + + ipactx = ipadb_get_context(context); + if (!ipactx) { + return KRB5_KDB_DBNOTINITED; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_REQUESTER_SID, + &k5pac_requester_sid_in); + if (ret != 0) { + talloc_free(tmp_ctx); + return ret; + } + + pac_requester_sid_in = data_blob_const(k5pac_requester_sid_in.data, + k5pac_requester_sid_in.length); + + ndr_err = ndr_pull_union_blob(&pac_requester_sid_in, tmp_ctx, &info, + PAC_TYPE_REQUESTER_SID, + (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO); + krb5_free_data_contents(context, &k5pac_requester_sid_in); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(tmp_ctx); + return EINVAL; + } + + *sid = info.requester_sid.sid; + + talloc_free(tmp_ctx); + return 0; +} +#endif + static krb5_error_code ipadb_get_sid_from_pac(TALLOC_CTX *ctx, struct PAC_LOGON_INFO *info, struct dom_sid *sid) @@ -976,6 +1025,33 @@ static krb5_error_code ipadb_get_pac(krb5_context kcontext, kerr = krb5_pac_add_buffer(kcontext, *pac, KRB5_PAC_UPN_DNS_INFO, &data); +#ifdef HAVE_PAC_REQUESTER_SID + { + union PAC_INFO pac_requester_sid; + /* == Package PAC_REQUESTER_SID == */ + memset(&pac_requester_sid, 0, sizeof(pac_requester_sid)); + + pac_requester_sid.requester_sid.sid = client_sid; + + ndr_err = ndr_push_union_blob(&pac_data, tmpctx, &pac_requester_sid, + PAC_TYPE_REQUESTER_SID, + (ndr_push_flags_fn_t)ndr_push_PAC_INFO); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + kerr = KRB5_KDB_INTERNAL_ERROR; + goto done; + } + + data.magic = KV5M_DATA; + data.data = (char *)pac_data.data; + data.length = pac_data.length; + + kerr = krb5_pac_add_buffer(kcontext, *pac, PAC_TYPE_REQUESTER_SID, &data); + if (kerr) { + goto done; + } + } +#endif + done: ldap_msgfree(results); talloc_free(tmpctx); @@ -1457,7 +1533,19 @@ static krb5_error_code check_logon_info_consistent(krb5_context context, return KRB5_KDB_DBNOTINITED; } - /* check exact sid */ + /* We are asked to verify the PAC for our own principal, + * check that our own view on the PAC details is up to date */ + if (ipactx->mspac->domsid.num_auths == 0) { + /* Force re-init of KDB's view on our domain */ + kerr = ipadb_reinit_mspac(ipactx, true); + if (kerr != 0) { + krb5_klog_syslog(LOG_ERR, + "PAC issue: unable to update realm's view on PAC info"); + return KRB5KDC_ERR_POLICY; + } + } + + /* check exact domain SID */ result = dom_sid_check(&ipactx->mspac->domsid, info->info->info3.base.domain_sid, true); if (!result) { @@ -1466,7 +1554,7 @@ static krb5_error_code check_logon_info_consistent(krb5_context context, char *dom = dom_sid_string(memctx, &ipactx->mspac->domsid); krb5_klog_syslog(LOG_ERR, "PAC issue: PAC record claims domain SID different " "to local domain SID: local [%s], PAC [%s]", - dom ? dom : "", + dom ? dom : "", sid ? sid : ""); return KRB5KDC_ERR_POLICY; } @@ -1746,12 +1834,14 @@ krb5_error_code filter_logon_info(krb5_context context, static krb5_error_code ipadb_check_logon_info(krb5_context context, krb5_const_principal client_princ, krb5_boolean is_cross_realm, - krb5_data *pac_blob) + krb5_data *pac_blob, + struct dom_sid *requester_sid) { struct PAC_LOGON_INFO_CTR info; krb5_error_code kerr; TALLOC_CTX *tmpctx; krb5_data origin_realm = client_princ->realm; + bool result; tmpctx = talloc_new(NULL); if (!tmpctx) { @@ -1763,6 +1853,28 @@ static krb5_error_code ipadb_check_logon_info(krb5_context context, goto done; } + /* Check that requester SID is the same as in the PAC entry */ + if (requester_sid != NULL) { + struct dom_sid client_sid; + kerr = ipadb_get_sid_from_pac(tmpctx, info.info, &client_sid); + if (kerr) { + goto done; + } + result = dom_sid_check(&client_sid, requester_sid, true); + if (!result) { + /* memctx is freed by the caller */ + char *pac_sid = dom_sid_string(tmpctx, &client_sid); + char *req_sid = dom_sid_string(tmpctx, requester_sid); + krb5_klog_syslog(LOG_ERR, "PAC issue: PAC has a SID " + "different from what PAC requester claims. " + "PAC [%s] vs PAC requester [%s]", + pac_sid ? pac_sid : "", + req_sid ? req_sid : ""); + kerr = KRB5KDC_ERR_POLICY; + goto done; + } + } + if (!is_cross_realm) { /* For local realm case we need to check whether the PAC is for our user * but we don't need to process further */ @@ -1962,6 +2074,8 @@ static krb5_error_code ipadb_verify_pac(krb5_context context, krb5_data pac_blob = { 0 , 0, NULL}; bool is_cross_realm = false; size_t i; + struct dom_sid *requester_sid = NULL; + struct dom_sid req_sid; kerr = krb5_pac_parse(context, authdata[0]->contents, @@ -2006,8 +2120,17 @@ static krb5_error_code ipadb_verify_pac(krb5_context context, goto done; } + memset(&req_sid, '\0', sizeof(struct dom_sid)); +#ifdef HAVE_PAC_REQUESTER_SID + kerr = ipadb_get_requester_sid(context, old_pac, &req_sid); + if (kerr == 0) { + requester_sid = &req_sid; + } +#endif + kerr = ipadb_check_logon_info(context, - client_princ, is_cross_realm, &pac_blob); + client_princ, is_cross_realm, &pac_blob, + requester_sid); if (kerr != 0) { goto done; } diff --git a/server.m4 b/server.m4 index 65c82d25a..648423dd4 100644 --- a/server.m4 +++ b/server.m4 @@ -109,6 +109,13 @@ AC_CHECK_MEMBER( [[#include #include ]]) +AC_CHECK_MEMBER( + [struct PAC_REQUESTER_SID.sid], + [AC_DEFINE([HAVE_PAC_REQUESTER_SID], [1], + [struct PAC_REQUESTER_SID is available.])], + [AC_MSG_NOTICE([struct PAC_REQUESTER_SID is not available, account protection is not active])], + [[#include + #include ]]) CFLAGS="$bck_cflags" LIBPDB_NAME=""