From 6d0d7136f5a49c0c4d5bad220e762920049124f6 Mon Sep 17 00:00:00 2001 From: Florence Blanc-Renaud Date: Tue, 30 Sep 2025 18:00:36 +0200 Subject: [PATCH] ipa-4.12.2-24 - Resolves: RHEL-118448 ipa: Privilege escalation from host to domain admin in FreeIPA Signed-off-by: Florence Blanc-Renaud --- ...ss-across-krbprincipalname-and-krbca.patch | 106 ++++++++ ...orce-PAC-presence-on-TGT-for-TGS-REQ.patch | 227 ++++++++++++++++++ ...end-test-for-unique-krbcanonicalname.patch | 93 +++++++ freeipa.spec | 13 +- 4 files changed, 436 insertions(+), 3 deletions(-) create mode 100644 0115-Enforce-uniqueness-across-krbprincipalname-and-krbca.patch create mode 100644 0116-ipa-kdb-enforce-PAC-presence-on-TGT-for-TGS-REQ.patch create mode 100644 0117-ipatests-extend-test-for-unique-krbcanonicalname.patch diff --git a/0115-Enforce-uniqueness-across-krbprincipalname-and-krbca.patch b/0115-Enforce-uniqueness-across-krbprincipalname-and-krbca.patch new file mode 100644 index 0000000..c378f4a --- /dev/null +++ b/0115-Enforce-uniqueness-across-krbprincipalname-and-krbca.patch @@ -0,0 +1,106 @@ +From 943032c9491df751d230132d6aa881ff9284cf21 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Thu, 10 Jul 2025 11:44:36 -0400 +Subject: [PATCH] Enforce uniqueness across krbprincipalname and + krbcanonicalname + +This relies on a fix in 389-ds that extends the uniqueness plugin +to be able to compare attributes with different matching syntax. + +This will prevent privilege escalation attacks if one of the +attributes is not set on an entry if it is set elsewhere. + +Signed-off-by: Rob Crittenden +--- + install/share/unique-attributes.ldif | 28 +++++----------------------- + install/updates/10-uniqueness.update | 27 +++++++++++++++++++++++---- + 2 files changed, 28 insertions(+), 27 deletions(-) + +diff --git a/install/share/unique-attributes.ldif b/install/share/unique-attributes.ldif +index 60f2c3470b3f2be7860c2bcc20babb07904f9b0c..b28d981b574d84e24509c07195526e25eddc0b75 100644 +--- a/install/share/unique-attributes.ldif ++++ b/install/share/unique-attributes.ldif +@@ -1,34 +1,16 @@ +-dn: cn=krbPrincipalName uniqueness,cn=plugins,cn=config ++dn: cn=kerberos name uniqueness,cn=plugins,cn=config + changetype: add + objectClass: top + objectClass: nsSlapdPlugin + objectClass: extensibleObject +-cn: krbPrincipalName uniqueness ++cn: kerberos name uniqueness + nsslapd-pluginPath: libattr-unique-plugin + nsslapd-pluginInitfunc: NSUniqueAttr_Init + nsslapd-pluginType: preoperation + nsslapd-pluginEnabled: on +-uniqueness-attribute-name: krbPrincipalName +-nsslapd-plugin-depends-on-type: database +-nsslapd-pluginId: NSUniqueAttr +-nsslapd-pluginVersion: 1.1.0 +-nsslapd-pluginVendor: Fedora Project +-nsslapd-pluginDescription: Enforce unique attribute values +-uniqueness-subtrees: $SUFFIX +-uniqueness-exclude-subtrees: cn=staged users,cn=accounts,cn=provisioning,$SUFFIX +-uniqueness-across-all-subtrees: on +- +-dn: cn=krbCanonicalName uniqueness,cn=plugins,cn=config +-changetype: add +-objectClass: top +-objectClass: nsSlapdPlugin +-objectClass: extensibleObject +-cn: krbCanonicalName uniqueness +-nsslapd-pluginPath: libattr-unique-plugin +-nsslapd-pluginInitfunc: NSUniqueAttr_Init +-nsslapd-pluginType: preoperation +-nsslapd-pluginEnabled: on +-uniqueness-attribute-name: krbCanonicalName ++uniqueness-attribute-name: krbPrincipalName:CaseIgnoreMatch: ++uniqueness-attribute-name: krbPrincipalAlias:CaseIgnoreMatch: ++uniqueness-attribute-name: krbCanonicalName:CaseIgnoreMatch: + nsslapd-plugin-depends-on-type: database + nsslapd-pluginId: NSUniqueAttr + nsslapd-pluginVersion: 1.1.0 +diff --git a/install/updates/10-uniqueness.update b/install/updates/10-uniqueness.update +index fa17911f2ed9c7bcaa851de0f0a4790a550e1c91..5c5bfd3e0f4f7b65fffe47844f778d6909181181 100644 +--- a/install/updates/10-uniqueness.update ++++ b/install/updates/10-uniqueness.update +@@ -63,13 +63,32 @@ add:uniqueness-subtree-entries-oc: posixAccount + + # krbPrincipalName uniqueness scopes Active/Delete containers + dn: cn=krbPrincipalName uniqueness,cn=plugins,cn=config +-add:uniqueness-exclude-subtrees: cn=staged users,cn=accounts,cn=provisioning,$SUFFIX +-add:uniqueness-across-all-subtrees: on ++deleteentry: cn=krbPrincipalName uniqueness,cn=plugins,cn=config + + # krbCanonicalName uniqueness scopes Active/Delete containers + dn: cn=krbCanonicalName uniqueness,cn=plugins,cn=config +-add:uniqueness-exclude-subtrees: cn=staged users,cn=accounts,cn=provisioning,$SUFFIX +-add:uniqueness-across-all-subtrees: on ++deleteentry: dn: cn=krbCanonicalName uniqueness,cn=plugins,cn=config ++ ++dn: cn=kerberos name uniqueness,cn=plugins,cn=config ++default:objectClass: top ++default:objectClass: nsSlapdPlugin ++default:objectClass: extensibleObject ++default:cn: kerberos name uniqueness ++default:nsslapd-pluginPath: libattr-unique-plugin ++default:nsslapd-pluginInitfunc: NSUniqueAttr_Init ++default:nsslapd-pluginType: preoperation ++default:nsslapd-pluginEnabled: on ++default:uniqueness-attribute-name: krbPrincipalName:CaseIgnoreMatch: ++default:uniqueness-attribute-name: krbPrincipalAlias:CaseIgnoreMatch: ++default:uniqueness-attribute-name: krbCanonicalName:CaseIgnoreMatch: ++default:nsslapd-plugin-depends-on-type: database ++default:nsslapd-pluginId: NSUniqueAttr ++default:nsslapd-pluginVersion: 1.1.0 ++default:nsslapd-pluginVendor: Fedora Project ++default:nsslapd-pluginDescription: Enforce unique attribute values ++default:uniqueness-subtrees: $SUFFIX ++default:uniqueness-exclude-subtrees: cn=staged users,cn=accounts,cn=provisioning,$SUFFIX ++default:uniqueness-across-all-subtrees: on + + # ipaUniqueID uniqueness scopes Active/Delete containers + dn: cn=ipaUniqueID uniqueness,cn=plugins,cn=config +-- +2.51.0 + diff --git a/0116-ipa-kdb-enforce-PAC-presence-on-TGT-for-TGS-REQ.patch b/0116-ipa-kdb-enforce-PAC-presence-on-TGT-for-TGS-REQ.patch new file mode 100644 index 0000000..0d939e8 --- /dev/null +++ b/0116-ipa-kdb-enforce-PAC-presence-on-TGT-for-TGS-REQ.patch @@ -0,0 +1,227 @@ +From ee838b21200a7e61398bd6a60be848bc1fa96985 Mon Sep 17 00:00:00 2001 +From: Julien Rische +Date: Fri, 18 Jul 2025 10:26:37 +0200 +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 +--- + 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 58a0339fc984c9b421aed23c6c1e6084f132421b..8fa0509956dfc1d0f2ba0c5363da463ba1189199 100644 +--- a/daemons/ipa-kdb/ipa_kdb.h ++++ b/daemons/ipa-kdb/ipa_kdb.h +@@ -431,6 +431,14 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext, + const char *test_realm, size_t size, + char **trusted_realm); + ++/* 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, +@@ -475,3 +483,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 ae7742a320ed714b703bf12a32811c0fd9eb75aa..fc603cbe2957f43698936e74a40fcbc0912f95bc 100644 +--- a/daemons/ipa-kdb/ipa_kdb_common.c ++++ b/daemons/ipa-kdb/ipa_kdb_common.c +@@ -767,3 +767,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 2802221c79fe63ab4bd33bfbe4859517f3d91ec5..aa8de3edac279ecf822af2b807e9378e28b125d0 100644 +--- a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c ++++ b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c +@@ -195,7 +195,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 9723103d8a77294ed7457d9b48bfc0d98b9ccef1..9a7aedd84850ac4b51ba97415bad9d6c071d0d0b 100644 +--- a/daemons/ipa-kdb/ipa_kdb_mspac.c ++++ b/daemons/ipa-kdb/ipa_kdb_mspac.c +@@ -3346,3 +3346,90 @@ 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; ++} +diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c +index a7e77e940ab61b27407076a834f3804b0e69c122..bb9e039923e821e8a20d01fbc42d92b402d3f961 100644 +--- a/daemons/ipa-kdb/ipa_kdb_principals.c ++++ b/daemons/ipa-kdb/ipa_kdb_principals.c +@@ -188,25 +188,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 + cmp_local_tgs_princ(krb5_context kcontext, const char *local_realm, + krb5_const_principal princ, bool *result) +@@ -2080,7 +2061,7 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext, + return kerr; + + /* 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 = cmp_local_tgs_princ(kcontext, ipactx->realm, (*entry)->princ, + &is_local_tgs_princ); + if (kerr) +-- +2.51.0 + diff --git a/0117-ipatests-extend-test-for-unique-krbcanonicalname.patch b/0117-ipatests-extend-test-for-unique-krbcanonicalname.patch new file mode 100644 index 0000000..00d7433 --- /dev/null +++ b/0117-ipatests-extend-test-for-unique-krbcanonicalname.patch @@ -0,0 +1,93 @@ +From 9f15d21bc0673696757406885e90647c3c7ad9a3 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Thu, 28 Aug 2025 15:31:39 +0200 +Subject: [PATCH] ipatests: extend test for unique krbcanonicalname + +Add a test ensuring that root@REALM cannot be added as +krbcanonicalname + +Add a test for PAC enforcement: +try to access a service using a TGT obtained without PAC. +Should fail as PAC is now enforced. + +Signed-off-by: Florence Blanc-Renaud +--- + ipatests/test_integration/test_commands.py | 44 ++++++++++++++++++++-- + 1 file changed, 40 insertions(+), 4 deletions(-) + +diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py +index 6aaf4f2995b61c538a9f99eed4a5a63b4df7cea7..dcffa6a8a00b5c9a7d41b8e131454666ef7eda85 100644 +--- a/ipatests/test_integration/test_commands.py ++++ b/ipatests/test_integration/test_commands.py +@@ -2239,7 +2239,7 @@ class TestIPACommandWithoutReplica(IntegrationTest): + hostname = master.hostname + realm = master.domain.realm + principal = f'test/{hostname}@{realm}' +- entry_ldif = textwrap.dedent(""" ++ entry_ldif_template = textwrap.dedent(""" + dn: krbprincipalname={principal},cn=services,cn=accounts,{base_dn} + changetype: add + ipakrbprincipalalias: test/{hostname}@{realm} +@@ -2250,13 +2250,15 @@ class TestIPACommandWithoutReplica(IntegrationTest): + objectclass: krbprincipal + objectclass: krbprincipalaux + objectclass: top +- krbcanonicalname: admin@{realm} ++ krbcanonicalname: {user}@{realm} + managedby: fqdn={hostname},cn=computers,cn=accounts,{base_dn} +- """).format( ++ """) ++ entry_ldif = entry_ldif_template.format( + base_dn=base_dn, + hostname=hostname, + principal=principal, +- realm=realm) ++ realm=realm, ++ user='admin') + tasks.kdestroy_all(master) + master.run_command( + ['kinit', '-kt', '/etc/krb5.keytab', f'host/{hostname}@{realm}']) +@@ -2269,6 +2271,40 @@ class TestIPACommandWithoutReplica(IntegrationTest): + raiseonerr=False) + assert "entry with the same attribute value" in result.stderr_text + ++ # Now try with root@realm instead of admin@realm ++ entry_ldif = entry_ldif_template.format( ++ base_dn=base_dn, ++ hostname=hostname, ++ principal=principal, ++ realm=realm, ++ user='root') ++ args = [ ++ 'ldapmodify', ++ '-Y', ++ 'GSSAPI' ++ ] ++ result = master.run_command(args, stdin_text=entry_ldif, ++ raiseonerr=False) ++ assert "entry with the same attribute value" in result.stderr_text ++ tasks.kdestroy_all(master) ++ ++ def test_no_request_pac(self): ++ # Try to use a TGT obtained without PAC ++ # Should fail as the presence of the PAC when processing TGTs ++ # provided by TGS-REQ is now enforced. ++ hostname = self.master.hostname ++ realm = self.master.domain.realm ++ self.master.run_command([ ++ 'kinit', '-kt', '/etc/krb5.keytab', f'host/{hostname}@{realm}', ++ '--no-request-pac' ++ ]) ++ result = self.master.run_command( ++ ['kvno', f'ldap/{hostname}@{realm}'], ++ raiseonerr=False ++ ) ++ assert result.returncode == 1 ++ assert "PAC_ENFORCEMENT_TGT_WITHOUT_PAC" in result.stderr_text ++ + + class TestIPAautomount(IntegrationTest): + @classmethod +-- +2.51.0 + diff --git a/freeipa.spec b/freeipa.spec index 56e6221..fea287f 100644 --- a/freeipa.spec +++ b/freeipa.spec @@ -77,8 +77,9 @@ %global ds_version 1.4.3.16-12 %global selinux_policy_version 3.14.3-107 %else -# version supporting LMDB and lib389.cli_ctl.dblib.run_dbscan utility -%global ds_version 2.1.0 +# version for Allow Uniqueness plugin to search uniqueness attributes +# using custom matching rules +%global ds_version 2.7.0-7 %global selinux_policy_version 38.1.1-1 %endif @@ -231,7 +232,7 @@ Name: %{package_name} Version: %{IPA_VERSION} -Release: 23%{?rc_version:.%rc_version}%{?dist} +Release: 24%{?rc_version:.%rc_version}%{?dist} Summary: The Identity, Policy and Audit system License: GPL-3.0-or-later @@ -369,6 +370,9 @@ Patch0111: 0111-ipasam-simplify-error-handling-in-fill_pdb_trusted_d.patch Patch0112: 0112-ipasam-address-signedness-warnings.patch Patch0113: 0113-ipasam-define-prototypes.patch Patch0114: 0114-ipasam-remove-definitions-which-included-from-ndr_dr.patch +Patch0115: 0115-Enforce-uniqueness-across-krbprincipalname-and-krbca.patch +Patch0116: 0116-ipa-kdb-enforce-PAC-presence-on-TGT-for-TGS-REQ.patch +Patch0117: 0117-ipatests-extend-test-for-unique-krbcanonicalname.patch Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch %endif %endif @@ -2022,6 +2026,9 @@ fi %endif %changelog +* Tue Sep 30 2025 Florence Blanc-Renaud - 4.12.2-24 +- Resolves: RHEL-118448 CVE-2025-7493 ipa: Privilege escalation from host to domain admin in FreeIPA + * Thu Sep 18 2025 Florence Blanc-Renaud - 4.12.2-23 - Related: RHEL-114548 Rebase Samba to the latest 4.23.x release