ipa/SOURCES/0027-kdb-fix-vulnerability-in-GCD-rules-handling.patch

342 lines
12 KiB
Diff
Raw Normal View History

2024-06-11 09:20:42 +00:00
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