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>
This commit is contained in:
Julien Rische authored and Antonio Torres committed 2025-09-30 10:22:47 +02:00
1 parent b866ec296b
commit bf76899127
5 files changed
+116 -21

No files matched your search

+9
View File
@@ -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);
+18
View File
@@ -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);
}
+1 -1
View File
@@ -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,
+87
View File
@@ -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;
}
+1 -20
View File
@@ -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)