ipa release 4.9.13-10
- kdb: apply combinatorial logic for ticket flags (CVE-2024-3183) Resolves: RHEL-29927 - kdb: fix vulnerability in GCD rules handling (CVE-2024-2698) Resolves: RHEL-29692 Signed-off-by: Julien Rische <jrische@redhat.com>
This commit is contained in:
parent
7b21739b0c
commit
1c57bc6872
341
0027-kdb-fix-vulnerability-in-GCD-rules-handling.patch
Normal file
341
0027-kdb-fix-vulnerability-in-GCD-rules-handling.patch
Normal file
@ -0,0 +1,341 @@
|
||||
From 0a48726e104282fb40d8f471ebb306bc9134cb0c Mon Sep 17 00:00:00 2001
|
||||
From: Julien Rische <jrische@redhat.com>
|
||||
Date: Tue, 19 Mar 2024 12:24:40 +0100
|
||||
Subject: [PATCH] kdb: fix vulnerability in GCD rules handling
|
||||
|
||||
The initial implementation of MS-SFU by MIT Kerberos was missing some
|
||||
a condition for granting the "forwardable" flag on S4U2Self tickets.
|
||||
Fixing this mistake required to add a special case for the
|
||||
check_allowed_to_delegate() function: if the target service argument is
|
||||
NULL, then it means the KDC is probing for general constrained
|
||||
delegation rules, not actually checking a specific S4U2Proxy request.
|
||||
|
||||
In commit e86807b5, the behavior of ipadb_match_acl() was modified to
|
||||
match the changes from upstream MIT Kerberos a441fbe3. However, a
|
||||
mistake resulted in this mechanism to apply in cases where target
|
||||
service argument is set AND unset. This results in S4U2Proxy requests to
|
||||
be accepted regardless of the fact there is a matching service
|
||||
delegation rule or not.
|
||||
|
||||
This vulnerability does not affect services having RBCD (resource-based
|
||||
constrained delegation) rules.
|
||||
|
||||
This fixes CVE-2024-2698
|
||||
|
||||
Signed-off-by: Julien Rische <jrische@redhat.com>
|
||||
---
|
||||
daemons/ipa-kdb/README.s4u2proxy.txt | 19 ++-
|
||||
daemons/ipa-kdb/ipa_kdb_delegation.c | 191 +++++++++++++++------------
|
||||
2 files changed, 118 insertions(+), 92 deletions(-)
|
||||
|
||||
diff --git a/daemons/ipa-kdb/README.s4u2proxy.txt b/daemons/ipa-kdb/README.s4u2proxy.txt
|
||||
index 254fcc4d1..ab34aff36 100644
|
||||
--- a/daemons/ipa-kdb/README.s4u2proxy.txt
|
||||
+++ b/daemons/ipa-kdb/README.s4u2proxy.txt
|
||||
@@ -12,9 +12,7 @@ much more easily managed.
|
||||
|
||||
The grouping mechanism has been built so that lookup is highly optimized
|
||||
and is basically reduced to a single search that uses the derefernce
|
||||
-control. Speed is very important in this case because KDC operations
|
||||
-time out very quickly and unless we add a caching layer in ipa-kdb we
|
||||
-must keep the number of searches down to avoid client timeouts.
|
||||
+control.
|
||||
|
||||
The grouping mechanism is very simple a groupOfPrincipals object is
|
||||
introduced, this Auxiliary class have a single optional attribute called
|
||||
@@ -112,8 +110,7 @@ kinit -kt /etc/httpd/conf/ipa.keytab HTTP/ipaserver.example.com
|
||||
kvno -U admin HTTP/ipaserver.example.com
|
||||
|
||||
# Perform S4U2Proxy
|
||||
-kvno -k /etc/httpd/conf/ipa.keytab -U admin -P HTTP/ipaserver.example.com
|
||||
-ldap/ipaserver.example.com
|
||||
+kvno -U admin -P ldap/ipaserver.example.com
|
||||
|
||||
|
||||
If this works it means you successfully impersonated the admin user with
|
||||
@@ -125,6 +122,18 @@ modprinc -ok_to_auth_as_delegate HTTP/ipaserver.example.com
|
||||
Simo.
|
||||
|
||||
|
||||
+If IPA is compiled with krb5 1.20 and newer (KDB DAL >= 9), then the
|
||||
+behavior of S4U2Self changes: S4U2Self TGS-REQs produce forwardable
|
||||
+tickets for all requesters, except if the requester principal is set as
|
||||
+the proxy (impersonating service) in at least one `servicedelegation`
|
||||
+rule. In this case, even if the rule has no target, the KDC will
|
||||
+response to S4U2Self requests with a non-forwardable ticket. Hence,
|
||||
+granting the `ok_to_auth_as_delegate` permission to the proxy service
|
||||
+remains the only way for this service to obtain the evidence ticket
|
||||
+required for general constrained delegation requests if this ticket is
|
||||
+not provided by the client.
|
||||
+
|
||||
+
|
||||
[1]
|
||||
Note that here I use the term proxy in a different way than it is used in
|
||||
the krb interfaces. It may seem a bit confusing but I think people will
|
||||
diff --git a/daemons/ipa-kdb/ipa_kdb_delegation.c b/daemons/ipa-kdb/ipa_kdb_delegation.c
|
||||
index de82174ad..3581f3c79 100644
|
||||
--- a/daemons/ipa-kdb/ipa_kdb_delegation.c
|
||||
+++ b/daemons/ipa-kdb/ipa_kdb_delegation.c
|
||||
@@ -91,120 +91,110 @@ static bool ipadb_match_member(char *princ, LDAPDerefRes *dres)
|
||||
return false;
|
||||
}
|
||||
|
||||
-static krb5_error_code ipadb_match_acl(krb5_context kcontext,
|
||||
- LDAPMessage *results,
|
||||
- krb5_const_principal client,
|
||||
- krb5_const_principal target)
|
||||
+#if KRB5_KDB_DAL_MAJOR_VERSION >= 9
|
||||
+static krb5_error_code
|
||||
+ipadb_has_acl(krb5_context kcontext, LDAPMessage *ldap_acl, bool *res)
|
||||
{
|
||||
struct ipadb_context *ipactx;
|
||||
- krb5_error_code kerr;
|
||||
- LDAPMessage *lentry;
|
||||
- LDAPDerefRes *deref_results;
|
||||
- LDAPDerefRes *dres;
|
||||
- char *client_princ = NULL;
|
||||
- char *target_princ = NULL;
|
||||
- bool client_missing;
|
||||
- bool client_found;
|
||||
- bool target_found;
|
||||
- bool is_constraint_delegation = false;
|
||||
- size_t nrules = 0;
|
||||
- int ret;
|
||||
+ bool in_res = false;
|
||||
+ krb5_error_code kerr = 0;
|
||||
|
||||
ipactx = ipadb_get_context(kcontext);
|
||||
- if (!ipactx) {
|
||||
+ if (!ipactx)
|
||||
return KRB5_KDB_DBNOTINITED;
|
||||
- }
|
||||
|
||||
- if ((client != NULL) && (target != NULL)) {
|
||||
- kerr = krb5_unparse_name(kcontext, client, &client_princ);
|
||||
- if (kerr != 0) {
|
||||
- goto done;
|
||||
- }
|
||||
- kerr = krb5_unparse_name(kcontext, target, &target_princ);
|
||||
- if (kerr != 0) {
|
||||
- goto done;
|
||||
- }
|
||||
- } else {
|
||||
- is_constraint_delegation = true;
|
||||
+ switch (ldap_count_entries(ipactx->lcontext, ldap_acl)) {
|
||||
+ case 0:
|
||||
+ break;
|
||||
+ case -1:
|
||||
+ kerr = EINVAL;
|
||||
+ goto end;
|
||||
+ default:
|
||||
+ in_res = true;
|
||||
+ goto end;
|
||||
}
|
||||
|
||||
- lentry = ldap_first_entry(ipactx->lcontext, results);
|
||||
- if (!lentry) {
|
||||
- kerr = ENOENT;
|
||||
- goto done;
|
||||
- }
|
||||
+end:
|
||||
+ if (res)
|
||||
+ *res = in_res;
|
||||
+
|
||||
+ return kerr;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+static krb5_error_code
|
||||
+ipadb_match_acl(krb5_context kcontext, LDAPMessage *ldap_acl,
|
||||
+ krb5_const_principal client, krb5_const_principal target)
|
||||
+{
|
||||
+ struct ipadb_context *ipactx;
|
||||
+ LDAPMessage *rule;
|
||||
+ LDAPDerefRes *acis, *aci;
|
||||
+ char *client_princ = NULL, *target_princ= NULL;
|
||||
+ bool client_missing, client_found, target_found;
|
||||
+ int lerr;
|
||||
+ krb5_error_code kerr;
|
||||
+
|
||||
+ ipactx = ipadb_get_context(kcontext);
|
||||
+ if (!ipactx)
|
||||
+ return KRB5_KDB_DBNOTINITED;
|
||||
+
|
||||
+ kerr = krb5_unparse_name(kcontext, client, &client_princ);
|
||||
+ if (kerr)
|
||||
+ goto end;
|
||||
+
|
||||
+ kerr = krb5_unparse_name(kcontext, target, &target_princ);
|
||||
+ if (kerr)
|
||||
+ goto end;
|
||||
|
||||
/* the default is that we fail */
|
||||
- kerr = ENOENT;
|
||||
+ kerr = KRB5KDC_ERR_BADOPTION;
|
||||
|
||||
- while (lentry) {
|
||||
+ for (rule = ldap_first_entry(ipactx->lcontext, ldap_acl);
|
||||
+ rule;
|
||||
+ rule = ldap_next_entry(ipactx->lcontext, rule))
|
||||
+ {
|
||||
/* both client and target must be found in the same ACI */
|
||||
client_missing = true;
|
||||
client_found = false;
|
||||
target_found = false;
|
||||
|
||||
- ret = ipadb_ldap_deref_results(ipactx->lcontext, lentry,
|
||||
- &deref_results);
|
||||
- switch (ret) {
|
||||
+ lerr = ipadb_ldap_deref_results(ipactx->lcontext, rule, &acis);
|
||||
+ switch (lerr) {
|
||||
case 0:
|
||||
- for (dres = deref_results; dres; dres = dres->next) {
|
||||
- nrules++;
|
||||
- if (is_constraint_delegation) {
|
||||
- /*
|
||||
- Microsoft revised the S4U2Proxy rules for forwardable
|
||||
- tickets. All S4U2Proxy operations require forwardable
|
||||
- evidence tickets, but S4U2Self should issue a
|
||||
- forwardable ticket if the requesting service has no
|
||||
- ok-to-auth-as-delegate bit but also no constrained
|
||||
- delegation privileges for traditional S4U2Proxy.
|
||||
- Implement these rules, extending the
|
||||
- check_allowed_to_delegate() DAL method so that the KDC
|
||||
- can ask if a principal has any delegation privileges.
|
||||
-
|
||||
- Since target principal is NULL and client principal is
|
||||
- NULL in this case, we simply calculate number of rules associated
|
||||
- with the server principal to decide whether to deny forwardable bit
|
||||
- */
|
||||
- continue;
|
||||
- }
|
||||
- if (client_found == false &&
|
||||
- strcasecmp(dres->derefAttr, "ipaAllowToImpersonate") == 0) {
|
||||
+ for (aci = acis; aci; aci = aci->next) {
|
||||
+ if (!client_found &&
|
||||
+ 0 == strcasecmp(aci->derefAttr, "ipaAllowToImpersonate"))
|
||||
+ {
|
||||
/* NOTE: client_missing is used to signal that the
|
||||
* attribute was completely missing. This signals that
|
||||
* ANY client is allowed to be impersonated.
|
||||
* This logic is valid only for clients, not for targets */
|
||||
client_missing = false;
|
||||
- client_found = ipadb_match_member(client_princ, dres);
|
||||
+ client_found = ipadb_match_member(client_princ, aci);
|
||||
}
|
||||
- if (target_found == false &&
|
||||
- strcasecmp(dres->derefAttr, "ipaAllowedTarget") == 0) {
|
||||
- target_found = ipadb_match_member(target_princ, dres);
|
||||
+ if (!target_found &&
|
||||
+ 0 == strcasecmp(aci->derefAttr, "ipaAllowedTarget"))
|
||||
+ {
|
||||
+ target_found = ipadb_match_member(target_princ, aci);
|
||||
}
|
||||
}
|
||||
|
||||
- ldap_derefresponse_free(deref_results);
|
||||
+ ldap_derefresponse_free(acis);
|
||||
break;
|
||||
case ENOENT:
|
||||
break;
|
||||
default:
|
||||
- kerr = ret;
|
||||
- goto done;
|
||||
+ kerr = lerr;
|
||||
+ goto end;
|
||||
}
|
||||
|
||||
- if ((client_found == true || client_missing == true) &&
|
||||
- target_found == true) {
|
||||
+ if ((client_found || client_missing) && target_found) {
|
||||
kerr = 0;
|
||||
- goto done;
|
||||
+ goto end;
|
||||
}
|
||||
-
|
||||
- lentry = ldap_next_entry(ipactx->lcontext, lentry);
|
||||
- }
|
||||
-
|
||||
- if (nrules > 0) {
|
||||
- kerr = 0;
|
||||
}
|
||||
|
||||
-done:
|
||||
+end:
|
||||
krb5_free_unparsed_name(kcontext, client_princ);
|
||||
krb5_free_unparsed_name(kcontext, target_princ);
|
||||
return kerr;
|
||||
@@ -223,7 +213,7 @@ krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
|
||||
char *srv_principal = NULL;
|
||||
krb5_db_entry *proxy_entry = NULL;
|
||||
struct ipadb_e_data *ied_server, *ied_proxy;
|
||||
- LDAPMessage *res = NULL;
|
||||
+ LDAPMessage *ldap_gcd_acl = NULL;
|
||||
|
||||
if (proxy != NULL) {
|
||||
/* Handle the case where server == proxy, this is allowed in S4U */
|
||||
@@ -261,26 +251,53 @@ krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
|
||||
goto done;
|
||||
}
|
||||
|
||||
- kerr = ipadb_get_delegation_acl(kcontext, srv_principal, &res);
|
||||
+ /* Load general constrained delegation rules */
|
||||
+ kerr = ipadb_get_delegation_acl(kcontext, srv_principal, &ldap_gcd_acl);
|
||||
if (kerr) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
- kerr = ipadb_match_acl(kcontext, res, client, proxy);
|
||||
- if (kerr) {
|
||||
- goto done;
|
||||
+#if KRB5_KDB_DAL_MAJOR_VERSION >= 9
|
||||
+ /*
|
||||
+ * Microsoft revised the S4U2Proxy rules for forwardable tickets. All
|
||||
+ * S4U2Proxy operations require forwardable evidence tickets, but
|
||||
+ * S4U2Self should issue a forwardable ticket if the requesting service
|
||||
+ * has no ok-to-auth-as-delegate bit but also no constrained delegation
|
||||
+ * privileges for traditional S4U2Proxy. Implement these rules,
|
||||
+ * extending the check_allowed_to_delegate() DAL method so that the KDC
|
||||
+ * can ask if a principal has any delegation privileges.
|
||||
+ *
|
||||
+ * If target service principal is NULL, and the impersonating service has
|
||||
+ * at least one GCD rule, then succeed.
|
||||
+ */
|
||||
+ if (!proxy) {
|
||||
+ bool has_gcd_rules;
|
||||
+
|
||||
+ kerr = ipadb_has_acl(kcontext, ldap_gcd_acl, &has_gcd_rules);
|
||||
+ if (!kerr)
|
||||
+ kerr = has_gcd_rules ? 0 : KRB5KDC_ERR_BADOPTION;
|
||||
+ } else if (client) {
|
||||
+#else
|
||||
+ if (client && proxy) {
|
||||
+#endif
|
||||
+ kerr = ipadb_match_acl(kcontext, ldap_gcd_acl, client, proxy);
|
||||
+ } else {
|
||||
+ /* client and/or proxy is missing */
|
||||
+ kerr = KRB5KDC_ERR_BADOPTION;
|
||||
}
|
||||
+ if (kerr)
|
||||
+ goto done;
|
||||
|
||||
done:
|
||||
if (kerr) {
|
||||
-#if KRB5_KDB_DAL_MAJOR_VERSION < 9
|
||||
- kerr = KRB5KDC_ERR_POLICY;
|
||||
-#else
|
||||
+#if KRB5_KDB_DAL_MAJOR_VERSION >= 9
|
||||
kerr = KRB5KDC_ERR_BADOPTION;
|
||||
+#else
|
||||
+ kerr = KRB5KDC_ERR_POLICY;
|
||||
#endif
|
||||
}
|
||||
ipadb_free_principal(kcontext, proxy_entry);
|
||||
krb5_free_unparsed_name(kcontext, srv_principal);
|
||||
- ldap_msgfree(res);
|
||||
+ ldap_msgfree(ldap_gcd_acl);
|
||||
return kerr;
|
||||
}
|
||||
--
|
||||
2.44.0
|
||||
|
548
0028-kdb-apply-combinatorial-logic-for-ticket-flags.patch
Normal file
548
0028-kdb-apply-combinatorial-logic-for-ticket-flags.patch
Normal file
@ -0,0 +1,548 @@
|
||||
From 70d23b8f6bbcabe2eb621ffa5009866b43e5570a Mon Sep 17 00:00:00 2001
|
||||
From: Julien Rische <jrische@redhat.com>
|
||||
Date: Mon, 25 Mar 2024 18:25:52 +0200
|
||||
Subject: [PATCH] kdb: apply combinatorial logic for ticket flags
|
||||
|
||||
The initial design for ticket flags was implementing this logic:
|
||||
* If a ticket policy is defined for the principal entry, use flags from
|
||||
this policy if they are set. Otherwise, use default ticket flags.
|
||||
* If no ticket policy is defined for the principal entry, but there is a
|
||||
global one, use flags from the global ticket policy if they are set.
|
||||
Otherwise, use default ticket flags.
|
||||
* If no policy (principal nor global) is defined, use default ticket
|
||||
flags.
|
||||
|
||||
However, this logic was broken by a1165ffb which introduced creation of
|
||||
a principal-level ticket policy in case the ticket flag set is modified.
|
||||
This was typically the case for the -allow_tix flag, which was set
|
||||
virtually by the KDB driver when a user was locked until they initialize
|
||||
their password on first kinit pre-authentication.
|
||||
|
||||
This was causing multiple issues, which are mitigated by the new
|
||||
approach:
|
||||
|
||||
Now flags from each level are combined together. There flags like
|
||||
+requires_preauth which are set systematically by the KDB diver, as
|
||||
well as -allow_tix which is set based on the value of "nsAccountLock".
|
||||
This commit also adds the implicit -allow_svr ticket flag for user
|
||||
principals to protect users against Kerberoast-type attacks. None of
|
||||
these flags are stored in the LDAP database, they are hard-coded in the
|
||||
KDB driver.
|
||||
|
||||
In addition to these "virtual" ticket flags, flags from both global and
|
||||
principal ticket policies are applied (if these policies exist).
|
||||
|
||||
Principal ticket policies are not supported for hosts and services, but
|
||||
this is only an HTTP API limitation. The "krbTicketPolicyAux" object
|
||||
class is supported for all account types. This is required for ticket
|
||||
flags like +ok_to_auth_as_delegate. Such flags can be set using "ipa
|
||||
host-mod" and "ipa serivce-mod", or using kadmin's "modprinc".
|
||||
|
||||
It is possible to ignore flags from the global ticket policy or default
|
||||
flags like -allow_svr for a user principal by setting the
|
||||
"final_user_tkt_flags" string attribute to "true" in kadmin. In this
|
||||
case, any ticket flag can be configured in the principal ticket policy,
|
||||
except requires_preauth and allow_tix.
|
||||
|
||||
This fixes CVE-2024-3183
|
||||
|
||||
Signed-off-by: Julien Rische <jrische@redhat.com>
|
||||
---
|
||||
daemons/ipa-kdb/ipa_kdb.h | 43 ++++
|
||||
daemons/ipa-kdb/ipa_kdb_principals.c | 306 ++++++++++++++++++++++-----
|
||||
util/ipa_krb5.c | 18 ++
|
||||
util/ipa_krb5.h | 4 +
|
||||
4 files changed, 319 insertions(+), 52 deletions(-)
|
||||
|
||||
diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
|
||||
index 7baf4697f..85cabe142 100644
|
||||
--- a/daemons/ipa-kdb/ipa_kdb.h
|
||||
+++ b/daemons/ipa-kdb/ipa_kdb.h
|
||||
@@ -94,6 +94,34 @@
|
||||
#define IPA_KRB_AUTHZ_DATA_ATTR "ipaKrbAuthzData"
|
||||
#define IPA_USER_AUTH_TYPE "ipaUserAuthType"
|
||||
|
||||
+/* Virtual managed ticket flags like "-allow_tix", are always controlled by the
|
||||
+ * "nsAccountLock" attribute, such flags should never be set in the database.
|
||||
+ * The following expression combine all of them, and is used to filter them
|
||||
+ * out. */
|
||||
+#define IPA_KDB_TKTFLAGS_VIRTUAL_MANAGED_ALL (KRB5_KDB_DISALLOW_ALL_TIX)
|
||||
+
|
||||
+/* Virtual static ticket flags are hard-coded in the KDB driver. */
|
||||
+/* Virtual static mandatory flags are set systematically and implicitly for all
|
||||
+ * principals. They are filtered out from database ticket flags updates.
|
||||
+ * (However, "KRB5_KDB_REQUIRES_PRE_AUTH" can still be unset by the
|
||||
+ * "KDC:Disable Default Preauth for SPNs" global setting) */
|
||||
+#define IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_MANDATORY (KRB5_KDB_REQUIRES_PRE_AUTH)
|
||||
+/* Virtual static default ticket flags are implicitly set for user and non-user
|
||||
+ * (SPN) principals, and not stored in the database.
|
||||
+ * (Except if the "IPA_KDB_STRATTR_FINAL_TKTFLAGS" string attribute is "true"
|
||||
+ * the principal) */
|
||||
+/* Virtual static default user ticket flags are set for users only. The
|
||||
+ * "-allow_svr" flag is set to protect them from CVE-2024-3183. */
|
||||
+#define IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_DEFAULTS_USER (KRB5_KDB_DISALLOW_SVR)
|
||||
+#define IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_DEFAULTS_SPN (0)
|
||||
+
|
||||
+/* If this string attribute is set to "true", then only the virtual managed and
|
||||
+ * virtual static mandatory ticket flags are applied and filtered out from
|
||||
+ * database read and write operations for the concerned user principal.
|
||||
+ * Configurable principal ticket flags are applied, but not the configurable
|
||||
+ * global ticket policy flags. */
|
||||
+#define IPA_KDB_STRATTR_FINAL_USER_TKTFLAGS "final_user_tkt_flags"
|
||||
+
|
||||
struct ipadb_mspac;
|
||||
struct dom_sid;
|
||||
|
||||
@@ -178,6 +206,21 @@ struct ipadb_e_data {
|
||||
struct dom_sid *sid;
|
||||
};
|
||||
|
||||
+inline static krb5_error_code
|
||||
+ipadb_get_edata(krb5_db_entry *entry, struct ipadb_e_data **ied)
|
||||
+{
|
||||
+ struct ipadb_e_data *in_ied;
|
||||
+
|
||||
+ in_ied = (struct ipadb_e_data *)entry->e_data;
|
||||
+ if (!in_ied || in_ied->magic != IPA_E_DATA_MAGIC)
|
||||
+ return EINVAL;
|
||||
+
|
||||
+ if (ied)
|
||||
+ *ied = in_ied;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
struct ipadb_context *ipadb_get_context(krb5_context kcontext);
|
||||
int ipadb_get_connection(struct ipadb_context *ipactx);
|
||||
|
||||
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
|
||||
index 07cc87746..cb18861bb 100644
|
||||
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
|
||||
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
|
||||
@@ -706,9 +706,10 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
|
||||
"krbTicketFlags", &result);
|
||||
if (ret == 0) {
|
||||
entry->attributes = result;
|
||||
- } else {
|
||||
- *polmask |= TKTFLAGS_BIT;
|
||||
}
|
||||
+ /* Since principal, global policy, and virtual ticket flags are combined,
|
||||
+ * they must always be resolved. */
|
||||
+ *polmask |= TKTFLAGS_BIT;
|
||||
|
||||
ret = ipadb_ldap_attr_to_int(lcontext, lentry,
|
||||
"krbMaxTicketLife", &result);
|
||||
@@ -1251,23 +1252,150 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static krb5_flags maybe_require_preauth(struct ipadb_context *ipactx,
|
||||
- krb5_db_entry *entry)
|
||||
+static krb5_error_code
|
||||
+are_final_tktflags(struct ipadb_context *ipactx, krb5_db_entry *entry,
|
||||
+ bool *final_tktflags)
|
||||
{
|
||||
- const struct ipadb_global_config *config;
|
||||
+ krb5_error_code kerr;
|
||||
struct ipadb_e_data *ied;
|
||||
+ char *str = NULL;
|
||||
+ bool in_final_tktflags = false;
|
||||
|
||||
- config = ipadb_get_global_config(ipactx);
|
||||
- if (config && config->disable_preauth_for_spns) {
|
||||
- ied = (struct ipadb_e_data *)entry->e_data;
|
||||
- if (ied && ied->ipa_user != true) {
|
||||
- /* not a user, assume SPN */
|
||||
- return 0;
|
||||
- }
|
||||
+ kerr = ipadb_get_edata(entry, &ied);
|
||||
+ if (kerr)
|
||||
+ goto end;
|
||||
+
|
||||
+ if (!ied->ipa_user) {
|
||||
+ kerr = 0;
|
||||
+ goto end;
|
||||
+ }
|
||||
+
|
||||
+ kerr = krb5_dbe_get_string(ipactx->kcontext, entry,
|
||||
+ IPA_KDB_STRATTR_FINAL_USER_TKTFLAGS, &str);
|
||||
+ if (kerr)
|
||||
+ goto end;
|
||||
+
|
||||
+ in_final_tktflags = str && ipa_krb5_parse_bool(str);
|
||||
+
|
||||
+end:
|
||||
+ if (final_tktflags)
|
||||
+ *final_tktflags = in_final_tktflags;
|
||||
+
|
||||
+ krb5_dbe_free_string(ipactx->kcontext, str);
|
||||
+ return kerr;
|
||||
+}
|
||||
+
|
||||
+static krb5_error_code
|
||||
+add_virtual_static_tktflags(struct ipadb_context *ipactx, krb5_db_entry *entry,
|
||||
+ krb5_flags *tktflags)
|
||||
+{
|
||||
+ krb5_error_code kerr;
|
||||
+ krb5_flags vsflg;
|
||||
+ bool final_tktflags;
|
||||
+ const struct ipadb_global_config *gcfg;
|
||||
+ struct ipadb_e_data *ied;
|
||||
+
|
||||
+ vsflg = IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_MANDATORY;
|
||||
+
|
||||
+ kerr = ipadb_get_edata(entry, &ied);
|
||||
+ if (kerr)
|
||||
+ goto end;
|
||||
+
|
||||
+ kerr = are_final_tktflags(ipactx, entry, &final_tktflags);
|
||||
+ if (kerr)
|
||||
+ goto end;
|
||||
+
|
||||
+ /* In practice, principal ticket flags cannot be final for SPNs. */
|
||||
+ if (!final_tktflags)
|
||||
+ vsflg |= ied->ipa_user ? IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_DEFAULTS_USER
|
||||
+ : IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_DEFAULTS_SPN;
|
||||
+
|
||||
+ if (!ied->ipa_user) {
|
||||
+ gcfg = ipadb_get_global_config(ipactx);
|
||||
+ if (gcfg && gcfg->disable_preauth_for_spns)
|
||||
+ vsflg &= ~KRB5_KDB_REQUIRES_PRE_AUTH;
|
||||
}
|
||||
|
||||
- /* By default require preauth for all principals */
|
||||
- return KRB5_KDB_REQUIRES_PRE_AUTH;
|
||||
+ if (tktflags)
|
||||
+ *tktflags |= vsflg;
|
||||
+
|
||||
+end:
|
||||
+ return kerr;
|
||||
+}
|
||||
+
|
||||
+static krb5_error_code
|
||||
+get_virtual_static_tktflags_mask(struct ipadb_context *ipactx,
|
||||
+ krb5_db_entry *entry, krb5_flags *mask)
|
||||
+{
|
||||
+ krb5_error_code kerr;
|
||||
+ krb5_flags flags = IPA_KDB_TKTFLAGS_VIRTUAL_MANAGED_ALL;
|
||||
+
|
||||
+ kerr = add_virtual_static_tktflags(ipactx, entry, &flags);
|
||||
+ if (kerr)
|
||||
+ goto end;
|
||||
+
|
||||
+ if (mask)
|
||||
+ *mask = ~flags;
|
||||
+
|
||||
+ kerr = 0;
|
||||
+
|
||||
+end:
|
||||
+ return kerr;
|
||||
+}
|
||||
+
|
||||
+/* Add ticket flags from the global ticket policy if it exists, otherwise
|
||||
+ * succeed. If the global ticket policy is set, the "exists" parameter is set to
|
||||
+ * true. */
|
||||
+static krb5_error_code
|
||||
+add_global_ticket_policy_flags(struct ipadb_context *ipactx,
|
||||
+ bool *gtpol_exists, krb5_flags *tktflags)
|
||||
+{
|
||||
+ krb5_error_code kerr;
|
||||
+ char *policy_dn;
|
||||
+ char *tktflags_attr[] = { "krbticketflags", NULL };
|
||||
+ LDAPMessage *res = NULL, *first;
|
||||
+ int ec, ldap_tktflags;
|
||||
+ bool in_gtpol_exists = false;
|
||||
+
|
||||
+ ec = asprintf(&policy_dn, "cn=%s,cn=kerberos,%s", ipactx->realm,
|
||||
+ ipactx->base);
|
||||
+ if (-1 == ec) {
|
||||
+ kerr = ENOMEM;
|
||||
+ goto end;
|
||||
+ }
|
||||
+
|
||||
+ kerr = ipadb_simple_search(ipactx, policy_dn, LDAP_SCOPE_BASE,
|
||||
+ "(objectclass=krbticketpolicyaux)",
|
||||
+ tktflags_attr, &res);
|
||||
+ if (kerr) {
|
||||
+ if (KRB5_KDB_NOENTRY == kerr)
|
||||
+ kerr = 0;
|
||||
+ goto end;
|
||||
+ }
|
||||
+
|
||||
+ first = ldap_first_entry(ipactx->lcontext, res);
|
||||
+ if (!first) {
|
||||
+ kerr = 0;
|
||||
+ goto end;
|
||||
+ }
|
||||
+
|
||||
+ in_gtpol_exists = true;
|
||||
+
|
||||
+ ec = ipadb_ldap_attr_to_int(ipactx->lcontext, first, "krbticketflags",
|
||||
+ &ldap_tktflags);
|
||||
+ if (0 == ec && tktflags) {
|
||||
+ *tktflags |= (krb5_flags)ldap_tktflags;
|
||||
+ }
|
||||
+
|
||||
+ kerr = 0;
|
||||
+
|
||||
+end:
|
||||
+ if (gtpol_exists)
|
||||
+ *gtpol_exists = in_gtpol_exists;
|
||||
+
|
||||
+ ldap_msgfree(res);
|
||||
+ free(policy_dn);
|
||||
+ return kerr;
|
||||
}
|
||||
|
||||
static krb5_error_code ipadb_fetch_tktpolicy(krb5_context kcontext,
|
||||
@@ -1280,6 +1408,7 @@ static krb5_error_code ipadb_fetch_tktpolicy(krb5_context kcontext,
|
||||
char *policy_dn = NULL;
|
||||
LDAPMessage *res = NULL;
|
||||
LDAPMessage *first;
|
||||
+ bool final_tktflags, has_local_tktpolicy = true;
|
||||
int result;
|
||||
int ret;
|
||||
|
||||
@@ -1288,12 +1417,18 @@ static krb5_error_code ipadb_fetch_tktpolicy(krb5_context kcontext,
|
||||
return KRB5_KDB_DBNOTINITED;
|
||||
}
|
||||
|
||||
+ kerr = are_final_tktflags(ipactx, entry, &final_tktflags);
|
||||
+ if (kerr)
|
||||
+ goto done;
|
||||
+
|
||||
ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
||||
"krbticketpolicyreference", &policy_dn);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
break;
|
||||
case ENOENT:
|
||||
+ /* If no principal ticket policy, fallback to the global one. */
|
||||
+ has_local_tktpolicy = false;
|
||||
ret = asprintf(&policy_dn, "cn=%s,cn=kerberos,%s",
|
||||
ipactx->realm, ipactx->base);
|
||||
if (ret == -1) {
|
||||
@@ -1337,12 +1472,13 @@ static krb5_error_code ipadb_fetch_tktpolicy(krb5_context kcontext,
|
||||
}
|
||||
}
|
||||
if (polmask & TKTFLAGS_BIT) {
|
||||
- ret = ipadb_ldap_attr_to_int(ipactx->lcontext, first,
|
||||
- "krbticketflags", &result);
|
||||
- if (ret == 0) {
|
||||
- entry->attributes |= result;
|
||||
- } else {
|
||||
- entry->attributes |= maybe_require_preauth(ipactx, entry);
|
||||
+ /* If global ticket policy is being applied, set flags only if
|
||||
+ * user principal ticket flags are not final. */
|
||||
+ if (has_local_tktpolicy || !final_tktflags) {
|
||||
+ ret = ipadb_ldap_attr_to_int(ipactx->lcontext, first,
|
||||
+ "krbticketflags", &result);
|
||||
+ if (ret == 0)
|
||||
+ entry->attributes |= result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1366,13 +1502,27 @@ static krb5_error_code ipadb_fetch_tktpolicy(krb5_context kcontext,
|
||||
if (polmask & MAXRENEWABLEAGE_BIT) {
|
||||
entry->max_renewable_life = 604800;
|
||||
}
|
||||
- if (polmask & TKTFLAGS_BIT) {
|
||||
- entry->attributes |= maybe_require_preauth(ipactx, entry);
|
||||
- }
|
||||
|
||||
kerr = 0;
|
||||
}
|
||||
|
||||
+ if (polmask & TKTFLAGS_BIT) {
|
||||
+ /* If the principal ticket flags were applied, then flags from the
|
||||
+ * global ticket policy has to be applied atop of them if user principal
|
||||
+ * ticket flags are not final. */
|
||||
+ if (has_local_tktpolicy && !final_tktflags) {
|
||||
+ kerr = add_global_ticket_policy_flags(ipactx, NULL,
|
||||
+ &entry->attributes);
|
||||
+ if (kerr)
|
||||
+ goto done;
|
||||
+ }
|
||||
+
|
||||
+ /* Virtual static ticket flags are set regardless of database content */
|
||||
+ kerr = add_virtual_static_tktflags(ipactx, entry, &entry->attributes);
|
||||
+ if (kerr)
|
||||
+ goto done;
|
||||
+ }
|
||||
+
|
||||
done:
|
||||
ldap_msgfree(res);
|
||||
free(policy_dn);
|
||||
@@ -2275,6 +2425,85 @@ static krb5_error_code ipadb_get_ldap_mod_auth_ind(krb5_context kcontext,
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static krb5_error_code
|
||||
+update_tktflags(krb5_context kcontext, struct ipadb_mods *imods,
|
||||
+ krb5_db_entry *entry, int mod_op)
|
||||
+{
|
||||
+ krb5_error_code kerr;
|
||||
+ struct ipadb_context *ipactx;
|
||||
+ struct ipadb_e_data *ied;
|
||||
+ bool final_tktflags;
|
||||
+ krb5_flags tktflags_mask;
|
||||
+ int tktflags;
|
||||
+
|
||||
+ kerr = ipadb_get_edata(entry, &ied);
|
||||
+ if (kerr)
|
||||
+ goto end;
|
||||
+
|
||||
+ ipactx = ipadb_get_context(kcontext);
|
||||
+ if (!ipactx) {
|
||||
+ kerr = KRB5_KDB_DBNOTINITED;
|
||||
+ goto end;
|
||||
+ }
|
||||
+
|
||||
+ kerr = get_virtual_static_tktflags_mask(ipactx, entry, &tktflags_mask);
|
||||
+ if (kerr)
|
||||
+ goto end;
|
||||
+
|
||||
+ kerr = are_final_tktflags(ipactx, entry, &final_tktflags);
|
||||
+ if (kerr)
|
||||
+ goto end;
|
||||
+
|
||||
+ /* Flags from the global ticket policy are filtered out only if the user
|
||||
+ * principal flags are not final. */
|
||||
+ if (!final_tktflags) {
|
||||
+ krb5_flags gtpol_tktflags = 0;
|
||||
+
|
||||
+ kerr = add_global_ticket_policy_flags(ipactx, NULL, >pol_tktflags);
|
||||
+ if (kerr)
|
||||
+ goto end;
|
||||
+
|
||||
+ tktflags_mask &= ~gtpol_tktflags;
|
||||
+ }
|
||||
+
|
||||
+ tktflags = (int)(entry->attributes & tktflags_mask);
|
||||
+
|
||||
+ if (LDAP_MOD_REPLACE == mod_op && !ied->has_tktpolaux) {
|
||||
+ if (0 == tktflags) {
|
||||
+ /* No point initializing principal ticket policy if there are no
|
||||
+ * flags left after filtering out virtual and global ticket policy
|
||||
+ * ones. */
|
||||
+ kerr = 0;
|
||||
+ goto end;
|
||||
+ }
|
||||
+
|
||||
+ /* if the object does not have the krbTicketPolicyAux class
|
||||
+ * we need to add it or this will fail, only for modifications.
|
||||
+ * We always add this objectclass by default when doing an add
|
||||
+ * from scratch. */
|
||||
+ kerr = ipadb_get_ldap_mod_str(imods, "objectclass",
|
||||
+ "krbTicketPolicyAux", LDAP_MOD_ADD);
|
||||
+ if (kerr)
|
||||
+ goto end;
|
||||
+ }
|
||||
+
|
||||
+ kerr = ipadb_get_ldap_mod_int(imods, "krbTicketFlags", tktflags, mod_op);
|
||||
+ if (kerr)
|
||||
+ goto end;
|
||||
+
|
||||
+ /* If there are no custom ticket flags set in the principal, remove the
|
||||
+ * "krbTicketFlags" attribute. */
|
||||
+ if (0 == tktflags) {
|
||||
+ kerr = ipadb_get_ldap_mod_int(imods, "krbTicketFlags", tktflags,
|
||||
+ LDAP_MOD_DELETE);
|
||||
+ if (kerr)
|
||||
+ goto end;
|
||||
+ }
|
||||
+
|
||||
+end:
|
||||
+ return kerr;
|
||||
+}
|
||||
+
|
||||
static krb5_error_code ipadb_entry_to_mods(krb5_context kcontext,
|
||||
struct ipadb_mods *imods,
|
||||
krb5_db_entry *entry,
|
||||
@@ -2350,36 +2579,9 @@ static krb5_error_code ipadb_entry_to_mods(krb5_context kcontext,
|
||||
|
||||
/* KADM5_ATTRIBUTES */
|
||||
if (entry->mask & KMASK_ATTRIBUTES) {
|
||||
- /* if the object does not have the krbTicketPolicyAux class
|
||||
- * we need to add it or this will fail, only for modifications.
|
||||
- * We always add this objectclass by default when doing an add
|
||||
- * from scratch. */
|
||||
- if ((mod_op == LDAP_MOD_REPLACE) && entry->e_data) {
|
||||
- struct ipadb_e_data *ied;
|
||||
-
|
||||
- ied = (struct ipadb_e_data *)entry->e_data;
|
||||
- if (ied->magic != IPA_E_DATA_MAGIC) {
|
||||
- kerr = EINVAL;
|
||||
- goto done;
|
||||
- }
|
||||
-
|
||||
- if (!ied->has_tktpolaux) {
|
||||
- kerr = ipadb_get_ldap_mod_str(imods, "objectclass",
|
||||
- "krbTicketPolicyAux",
|
||||
- LDAP_MOD_ADD);
|
||||
- if (kerr) {
|
||||
- goto done;
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- kerr = ipadb_get_ldap_mod_int(imods,
|
||||
- "krbTicketFlags",
|
||||
- (int)entry->attributes,
|
||||
- mod_op);
|
||||
- if (kerr) {
|
||||
+ kerr = update_tktflags(kcontext, imods, entry, mod_op);
|
||||
+ if (kerr)
|
||||
goto done;
|
||||
- }
|
||||
}
|
||||
|
||||
/* KADM5_MAX_LIFE */
|
||||
diff --git a/util/ipa_krb5.c b/util/ipa_krb5.c
|
||||
index 1ba6d25ee..2e663c506 100644
|
||||
--- a/util/ipa_krb5.c
|
||||
+++ b/util/ipa_krb5.c
|
||||
@@ -38,6 +38,12 @@ const char *ipapwd_password_max_len_errmsg = \
|
||||
TOSTR(IPAPWD_PASSWORD_MAX_LEN) \
|
||||
" chars)!";
|
||||
|
||||
+/* Case-insensitive string values to by parsed as boolean true */
|
||||
+static const char *const conf_yes[] = {
|
||||
+ "y", "yes", "true", "t", "1", "on",
|
||||
+ NULL,
|
||||
+};
|
||||
+
|
||||
/* Salt types */
|
||||
#define KRB5P_SALT_SIZE 16
|
||||
|
||||
@@ -1237,3 +1243,15 @@ done:
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
+
|
||||
+bool ipa_krb5_parse_bool(const char *str)
|
||||
+{
|
||||
+ const char *const *p;
|
||||
+
|
||||
+ for (p = conf_yes; *p; p++) {
|
||||
+ if (!strcasecmp(*p, str))
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
diff --git a/util/ipa_krb5.h b/util/ipa_krb5.h
|
||||
index 7d2ebae98..d0280940a 100644
|
||||
--- a/util/ipa_krb5.h
|
||||
+++ b/util/ipa_krb5.h
|
||||
@@ -174,3 +174,7 @@ static inline bool
|
||||
krb5_ts_after(krb5_timestamp a, krb5_timestamp b) {
|
||||
return (uint32_t)a > (uint32_t)b;
|
||||
}
|
||||
+
|
||||
+/* Implement boolean string parsing function from MIT krb5:
|
||||
+ * src/lib/krb5/krb/libdef_parse.c:_krb5_conf_boolean() */
|
||||
+bool ipa_krb5_parse_bool(const char *str);
|
||||
--
|
||||
2.44.0
|
||||
|
10
ipa.spec
10
ipa.spec
@ -190,7 +190,7 @@
|
||||
|
||||
Name: %{package_name}
|
||||
Version: %{IPA_VERSION}
|
||||
Release: 9%{?rc_version:.%rc_version}%{?dist}
|
||||
Release: 10%{?rc_version:.%rc_version}%{?dist}
|
||||
Summary: The Identity, Policy and Audit system
|
||||
|
||||
License: GPLv3+
|
||||
@ -235,6 +235,8 @@ Patch0023: 0023-rpcserver-validate-Kerberos-principal-name-before-running-k
|
||||
Patch0024: 0024-Vault-add-additional-fallback-to-RSA-OAEP-wrapping-algo_rhel#28259.patch
|
||||
Patch0025: 0025-dcerpc-invalidate-forest-trust-intfo-cache-when-filtering-out-realm-domains_rhel#28559.patch
|
||||
Patch0026: 0026-backport-test-fixes_rhel#29908.patch
|
||||
Patch0027: 0027-kdb-fix-vulnerability-in-GCD-rules-handling.patch
|
||||
Patch0028: 0028-kdb-apply-combinatorial-logic-for-ticket-flags.patch
|
||||
%if 0%{?rhel} >= 8
|
||||
Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch
|
||||
Patch1002: 1002-Revert-freeipa.spec-depend-on-bind-dnssec-utils.patch
|
||||
@ -1750,6 +1752,12 @@ fi
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Tue Apr 30 2024 Julien Rische <jrische@redhat.com> - 4.9.13-10
|
||||
- kdb: apply combinatorial logic for ticket flags (CVE-2024-3183)
|
||||
Resolves: RHEL-29927
|
||||
- kdb: fix vulnerability in GCD rules handling (CVE-2024-2698)
|
||||
Resolves: RHEL-29692
|
||||
|
||||
* Fri Apr 12 2024 Rafael Jeffman <rjeffman@redhat.com> - 9.4.13-9
|
||||
- dcerpc: invalidate forest trust intfo cache when filtering out realm domains
|
||||
Resolves: RHEL-28559
|
||||
|
Loading…
Reference in New Issue
Block a user