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 <abokovoy@redhat.com>
Reviewed-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
This commit is contained in:
Alexander Bokovoy 2021-10-30 10:09:27 +03:00 committed by Rob Crittenden
parent 23336160f2
commit 9a0bcbbe82
2 changed files with 134 additions and 4 deletions

View File

@ -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 : "<failed to display>",
dom ? dom : "<failed to display>",
sid ? sid : "<failed to display>");
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 : "<failed to display>",
req_sid ? req_sid : "<failed to display>");
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;
}

View File

@ -109,6 +109,13 @@ AC_CHECK_MEMBER(
[[#include <ndr.h>
#include <gen_ndr/krb5pac.h>]])
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 <ndr.h>
#include <gen_ndr/krb5pac.h>]])
CFLAGS="$bck_cflags"
LIBPDB_NAME=""