ipa/SOURCES/0042-ipa-kdb-enforce-PAC-presence-on-TGT-for-TGS-REQ_rhel#110061.patch
2025-10-02 11:38:49 +00:00

231 lines
7.6 KiB
Diff

From 4784cb826f7cfd01471c29cfb51bdf6d34d6d643 Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Tue, 9 Sep 2025 12:45:24 -0300
Subject: [PATCH] ipa-kdb: enforce PAC presence on TGT for TGS-REQ
MS-KILE's PA-PAC-REQUEST sequence allows the Kerberos client to request
a TGT without a PAC. At the moment, there is no way to configure the MIT
KDC to reject such request.
This commit enforces the presence of the PAC when processing TGTs
provided by TGS-REQ. It ensures the server principal of the TGT is the
same as the one in PAC_CLIENT_INFO (i.e. enforces client principal
canonicalization) with integrity check.
Only one exception is applied: this check is skipped for local TGTs on
domain where the MS-PAC generator is not initialized (i.e. domains where
SID generation was not executed yet).
Signed-off-by: Julien Rische <jrische@redhat.com>
---
daemons/ipa-kdb/ipa_kdb.h | 9 +++
daemons/ipa-kdb/ipa_kdb_common.c | 18 ++++++
daemons/ipa-kdb/ipa_kdb_kdcpolicy.c | 2 +-
daemons/ipa-kdb/ipa_kdb_mspac.c | 87 ++++++++++++++++++++++++++++
daemons/ipa-kdb/ipa_kdb_principals.c | 21 +------
5 files changed, 116 insertions(+), 21 deletions(-)
diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
index 85cabe142..7bad8c85f 100644
--- a/daemons/ipa-kdb/ipa_kdb.h
+++ b/daemons/ipa-kdb/ipa_kdb.h
@@ -434,6 +434,14 @@ ipadb_check_for_bronze_bit_attack(krb5_context context,
# endif
#endif
+/* Check the ticket provided in a TGS-REQ. In some situations, the ticket is
+ * expected to contain a PAC. If it is not the case, or if the function is
+ * enable to decode an authorization-data element, it fails.
+ * Any failure should result in the TGS-REQ to be rejected. */
+krb5_error_code ipadb_enforce_pac(krb5_context kcontext,
+ const krb5_ticket *ticket,
+ const char **status);
+
/* DELEGATION CHECKS */
krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
@@ -472,3 +480,4 @@ 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 ipadb_is_tgs_princ(krb5_context kcontext, krb5_const_principal princ);
diff --git a/daemons/ipa-kdb/ipa_kdb_common.c b/daemons/ipa-kdb/ipa_kdb_common.c
index 42e0856d0..eb0b0d129 100644
--- a/daemons/ipa-kdb/ipa_kdb_common.c
+++ b/daemons/ipa-kdb/ipa_kdb_common.c
@@ -704,3 +704,21 @@ krb5_error_code ipadb_multibase_search(struct ipadb_context *ipactx,
return ipadb_simple_ldap_to_kerr(ret);
}
+bool
+ipadb_is_tgs_princ(krb5_context kcontext, krb5_const_principal princ)
+{
+ krb5_data *primary;
+ size_t l_tgs_name;
+
+ if (2 != krb5_princ_size(kcontext, princ))
+ return false;
+
+ primary = krb5_princ_component(kcontext, princ, 0);
+
+ l_tgs_name = strlen(KRB5_TGS_NAME);
+
+ if (l_tgs_name != primary->length)
+ return false;
+
+ return 0 == memcmp(primary->data, KRB5_TGS_NAME, l_tgs_name);
+}
diff --git a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
index d6d618d1d..a92a9a0ad 100644
--- a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
+++ b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
@@ -207,7 +207,7 @@ ipa_kdcpolicy_check_tgs(krb5_context context, krb5_kdcpolicy_moddata moddata,
*lifetime_out = 0;
*renew_lifetime_out = 0;
- return 0;
+ return ipadb_enforce_pac(context, ticket, status);
}
krb5_error_code kdcpolicy_ipakdb_initvt(krb5_context context,
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
index 0964d112a..c4085fca5 100644
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
@@ -3344,6 +3344,93 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
return KRB5_KDB_NOENTRY;
}
+static krb5_error_code
+check_for_pac(krb5_context kcontext, krb5_authdata **authdata, bool *pac_present)
+{
+ krb5_error_code kerr = ENOENT;
+ size_t i, j;
+ krb5_authdata **ifrel = NULL;
+
+ for (i = 0; authdata && authdata[i]; ++i) {
+ if (authdata[i]->ad_type != KRB5_AUTHDATA_IF_RELEVANT) {
+ continue;
+ }
+
+ kerr = krb5_decode_authdata_container(kcontext,
+ KRB5_AUTHDATA_IF_RELEVANT,
+ authdata[i], &ifrel);
+ if (kerr) {
+ goto end;
+ }
+
+ for (j = 0; ifrel[j]; ++j) {
+ if (ifrel[j]->ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
+ break;
+ }
+ }
+ if (ifrel[j]) {
+ break;
+ }
+
+ krb5_free_authdata(kcontext, ifrel);
+ ifrel = NULL;
+ }
+
+ *pac_present = ifrel;
+ kerr = 0;
+
+end:
+ krb5_free_authdata(kcontext, ifrel);
+ return kerr;
+}
+
+krb5_error_code
+ipadb_enforce_pac(krb5_context kcontext, const krb5_ticket *ticket,
+ const char **status)
+{
+ struct ipadb_context *ipactx;
+ bool pac_present;
+ krb5_error_code kerr;
+
+ /* Filter TGTs only */
+ if (!ipadb_is_tgs_princ(kcontext, ticket->server)) {
+ kerr = 0;
+ goto end;
+ }
+
+ /* Get IPA context */
+ ipactx = ipadb_get_context(kcontext);
+ if (!ipactx) {
+ kerr = KRB5_KDB_DBNOTINITED;
+ goto end;
+ }
+
+ /* If local TGT but PAC generator not initialized, skip PAC enforcement */
+ if (krb5_realm_compare(kcontext, ipactx->local_tgs, ticket->server) &&
+ !ipactx->mspac)
+ {
+ krb5_klog_syslog(LOG_WARNING, "MS-PAC not available. This makes "
+ "FreeIPA vulnerable to privilege escalation exploit "
+ "(CVE-2025-7493). Please generate SIDs to enable PAC "
+ "support.");
+ kerr = 0;
+ goto end;
+ }
+
+ /* Search for the PAC, fail if it cannot be found */
+ kerr = check_for_pac(kcontext, ticket->enc_part2->authorization_data,
+ &pac_present);
+ if (kerr) {
+ *status = "PAC_ENFORCEMENT_CANNOT_DECODE_TGT_AUTHDATA";
+ } else if (!pac_present) {
+ kerr = ENOENT;
+ *status = "PAC_ENFORCEMENT_TGT_WITHOUT_PAC";
+ }
+
+end:
+ return kerr;
+}
+
#if KRB5_KDB_DAL_MAJOR_VERSION <= 8
# ifdef HAVE_KRB5_PAC_FULL_SIGN_COMPAT
krb5_error_code
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
index 6ee274053..11e084739 100644
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
@@ -183,25 +183,6 @@ done:
return ret;
}
-static bool
-is_tgs_princ(krb5_context kcontext, krb5_const_principal princ)
-{
- krb5_data *primary;
- size_t l_tgs_name;
-
- if (2 != krb5_princ_size(kcontext, princ))
- return false;
-
- primary = krb5_princ_component(kcontext, princ, 0);
-
- l_tgs_name = strlen(KRB5_TGS_NAME);
-
- if (l_tgs_name != primary->length)
- return false;
-
- return 0 == memcmp(primary->data, KRB5_TGS_NAME, l_tgs_name);
-}
-
static krb5_error_code ipadb_set_tl_data(krb5_db_entry *entry,
krb5_int16 type,
krb5_ui_2 length,
@@ -1882,7 +1863,7 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext,
#if KRB5_KDB_DAL_MAJOR_VERSION <= 8
/* If TGS principal, some virtual attributes may be added */
- if (is_tgs_princ(kcontext, (*entry)->princ)) {
+ if (ipadb_is_tgs_princ(kcontext, (*entry)->princ)) {
kerr = krb5_dbe_set_string(kcontext, *entry,
KRB5_KDB_SK_OPTIONAL_AD_SIGNEDPATH,
"true");
--
2.51.0