231 lines
7.6 KiB
Diff
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
|
|
|