Blame SOURCES/0003-ad-filter-trusted-domains.patch

72e12a
From 4c48c4a7792961cf8a228c76975ac370d32904e1 Mon Sep 17 00:00:00 2001
72e12a
From: Sumit Bose <sbose@redhat.com>
72e12a
Date: Wed, 6 Oct 2021 13:03:27 +0200
72e12a
Subject: [PATCH] ad: filter trusted domains
72e12a
MIME-Version: 1.0
72e12a
Content-Type: text/plain; charset=UTF-8
72e12a
Content-Transfer-Encoding: 8bit
72e12a
72e12a
The fix for https://github.com/SSSD/sssd/issues/5528 might discover
72e12a
domains which are not trusted (one-way trust) or are from a different
72e12a
forest (direct trust). Both should be ignored because they are not
72e12a
trusted or can currently not be handled properly. This patch filters out
72e12a
those domains.
72e12a
72e12a
Resolves: https://github.com/SSSD/sssd/issues/5819
72e12a
72e12a
Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
72e12a
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
72e12a
---
72e12a
 src/providers/ad/ad_subdomains.c | 104 +++++++++++++++++++++++++++++--
72e12a
 1 file changed, 99 insertions(+), 5 deletions(-)
72e12a
72e12a
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
72e12a
index 3eb49c93f..ac463026f 100644
72e12a
--- a/src/providers/ad/ad_subdomains.c
72e12a
+++ b/src/providers/ad/ad_subdomains.c
72e12a
@@ -46,6 +46,7 @@
72e12a
 #define AD_AT_TRUST_PARTNER "trustPartner"
72e12a
 #define AD_AT_TRUST_ATTRS   "trustAttributes"
72e12a
 #define AD_AT_DOMAIN_NAME   "cn"
72e12a
+#define AD_AT_TRUST_DIRECTION   "trustDirection"
72e12a
 
72e12a
 /* trustType=2 denotes uplevel (NT5 and later) trusted domains. See
72e12a
  * http://msdn.microsoft.com/en-us/library/windows/desktop/ms680342%28v=vs.85%29.aspx
72e12a
@@ -69,6 +70,12 @@
72e12a
 /* do not refresh more often than every 5 seconds for now */
72e12a
 #define AD_SUBDOMAIN_REFRESH_LIMIT 5
72e12a
 
72e12a
+/* Flags of trustAttributes attribute, see MS-ADTS 6.1.6.7.9 for details */
72e12a
+#define TRUST_ATTRIBUTE_WITHIN_FOREST 0x00000020
72e12a
+
72e12a
+/* Flags for trustDirection attribute, see MS-ADTS 6.1.6.7.12 for details */
72e12a
+#define TRUST_DIRECTION_OUTBOUND 0x00000002
72e12a
+
72e12a
 static void
72e12a
 ad_disable_gc(struct ad_options *ad_options)
72e12a
 {
72e12a
@@ -646,6 +653,85 @@ done:
72e12a
     return ret;
72e12a
 }
72e12a
 
72e12a
+/* When reading trusted domains from the local DC we are basically interested
72e12a
+ * in domains from the local forest we are trusting, i.e. users from this
72e12a
+ * domain can connect to us. To not unnecessarily bloat the list of domains
72e12a
+ * and make multi-domain searches slow we filter domains from other forest and
72e12a
+ * domains we do not trust.
72e12a
+ * In future we might add config options to broaden the scope and allow more
72e12a
+ * domains.
72e12a
+ * If ad_filter_domains() returns successfully with EOK in input array is not
72e12a
+ * valid anymore and should be freed by the caller. */
72e12a
+static errno_t ad_filter_domains(TALLOC_CTX *mem_ctx,
72e12a
+                                 struct sysdb_attrs **subdomains,
72e12a
+                                 size_t num_subdomains,
72e12a
+                                 struct sysdb_attrs ***_sd_out,
72e12a
+                                 size_t *_num_sd_out)
72e12a
+{
72e12a
+    int ret;
72e12a
+    size_t c;
72e12a
+    uint32_t tmp_uint32_t;
72e12a
+    const char *value;
72e12a
+    struct sysdb_attrs **sd_out;
72e12a
+    size_t num_sd_out = 0;
72e12a
+
72e12a
+    sd_out = talloc_zero_array(mem_ctx, struct sysdb_attrs *,
72e12a
+                               num_subdomains + 1);
72e12a
+    if (sd_out == NULL) {
72e12a
+        DEBUG(SSSDBG_OP_FAILURE,
72e12a
+              "Failed to allocate memory for sub-domain list.\n");
72e12a
+        return ENOMEM;
72e12a
+    }
72e12a
+
72e12a
+    for (c = 0; c < num_subdomains; c++) {
72e12a
+        ret = sysdb_attrs_get_string(subdomains[c], AD_AT_TRUST_PARTNER,
72e12a
+                                     &value);
72e12a
+        if (ret != EOK) {
72e12a
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
72e12a
+            talloc_free(sd_out);
72e12a
+            return ret;
72e12a
+        }
72e12a
+
72e12a
+        /* Ignore direct trusts to domains from other forests
72e12a
+         * (TRUST_ATTRIBUTE_WITHIN_FOREST is not set) or domains we do not
72e12a
+         * trust (TRUST_DIRECTION_OUTBOUND is not set) */
72e12a
+
72e12a
+        tmp_uint32_t = 0;
72e12a
+        ret = sysdb_attrs_get_uint32_t(subdomains[c], AD_AT_TRUST_ATTRS,
72e12a
+                                       &tmp_uint32_t);
72e12a
+        if (ret != EOK
72e12a
+                || (tmp_uint32_t & TRUST_ATTRIBUTE_WITHIN_FOREST) == 0) {
72e12a
+            DEBUG(SSSDBG_FUNC_DATA,
72e12a
+                  "TRUST_ATTRIBUTE_WITHIN_FOREST not set for [%s].\n",
72e12a
+                  value);
72e12a
+            continue;
72e12a
+        }
72e12a
+
72e12a
+        tmp_uint32_t = 0;
72e12a
+        ret = sysdb_attrs_get_uint32_t(subdomains[c], AD_AT_TRUST_DIRECTION,
72e12a
+                                       &tmp_uint32_t);
72e12a
+        if (ret != EOK
72e12a
+                || (tmp_uint32_t & TRUST_DIRECTION_OUTBOUND) == 0) {
72e12a
+            DEBUG(SSSDBG_FUNC_DATA,
72e12a
+                  "TRUST_DIRECTION_OUTBOUND not set for [%s].\n",
72e12a
+                  value);
72e12a
+            continue;
72e12a
+        }
72e12a
+
72e12a
+        sd_out[num_sd_out] = subdomains[c];
72e12a
+        num_sd_out++;
72e12a
+    }
72e12a
+
72e12a
+    for (c = 0; c < num_sd_out; c++) {
72e12a
+        sd_out[c] = talloc_steal(sd_out, sd_out[c]);
72e12a
+    }
72e12a
+
72e12a
+    *_sd_out = sd_out;
72e12a
+    *_num_sd_out = num_sd_out;
72e12a
+
72e12a
+    return EOK;
72e12a
+}
72e12a
+
72e12a
 /* How many times we keep a domain not found during searches before it will be
72e12a
  * removed. */
72e12a
 #define MAX_NOT_FOUND 6
72e12a
@@ -1125,7 +1211,7 @@ static void ad_get_slave_domain_connect_done(struct tevent_req *subreq)
72e12a
     errno_t ret;
72e12a
     const char *attrs[] = { AD_AT_FLATNAME, AD_AT_TRUST_PARTNER,
72e12a
                             AD_AT_SID, AD_AT_TRUST_TYPE,
72e12a
-                            AD_AT_TRUST_ATTRS, NULL };
72e12a
+                            AD_AT_TRUST_ATTRS, AD_AT_TRUST_DIRECTION, NULL };
72e12a
 
72e12a
     req = tevent_req_callback_data(subreq, struct tevent_req);
72e12a
     state = tevent_req_data(req, struct ad_get_slave_domain_state);
72e12a
@@ -1333,7 +1419,7 @@ ad_get_root_domain_send(TALLOC_CTX *mem_ctx,
72e12a
     struct sdap_options *opts;
72e12a
     errno_t ret;
72e12a
     const char *attrs[] = { AD_AT_FLATNAME, AD_AT_TRUST_PARTNER,
72e12a
-                            AD_AT_SID, AD_AT_TRUST_TYPE,
72e12a
+                            AD_AT_SID, AD_AT_TRUST_TYPE, AD_AT_TRUST_DIRECTION,
72e12a
                             AD_AT_TRUST_ATTRS, AD_AT_DOMAIN_NAME, NULL };
72e12a
 
72e12a
     req = tevent_req_create(mem_ctx, &state, struct ad_get_root_domain_state);
72e12a
@@ -1411,13 +1497,15 @@ static void ad_get_root_domain_done(struct tevent_req *subreq)
72e12a
     struct ad_get_root_domain_state *state;
72e12a
     errno_t ret;
72e12a
     bool has_changes = false;
72e12a
+    struct sysdb_attrs **unfiltered_reply;
72e12a
+    size_t unfiltered_reply_count;
72e12a
 
72e12a
     req = tevent_req_callback_data(subreq, struct tevent_req);
72e12a
     state = tevent_req_data(req, struct ad_get_root_domain_state);
72e12a
 
72e12a
     ret = sdap_search_bases_return_first_recv(subreq, state,
72e12a
-                                              &state->reply_count,
72e12a
-                                              &state->reply);
72e12a
+                                              &unfiltered_reply_count,
72e12a
+                                              &unfiltered_reply);
72e12a
     talloc_zfree(subreq);
72e12a
     if (ret != EOK) {
72e12a
         DEBUG(SSSDBG_OP_FAILURE, "Unable to lookup forest root information "
72e12a
@@ -1425,7 +1513,13 @@ static void ad_get_root_domain_done(struct tevent_req *subreq)
72e12a
         goto done;
72e12a
     }
72e12a
 
72e12a
-    find_domain(state->reply_count, state->reply, state->forest);
72e12a
+    ret = ad_filter_domains(state, unfiltered_reply, unfiltered_reply_count,
72e12a
+                            &state->reply, &state->reply_count);
72e12a
+    if (ret != EOK) {
72e12a
+        DEBUG(SSSDBG_OP_FAILURE,
72e12a
+              "Failed to filter list of returned domains.\n");
72e12a
+        goto done;
72e12a
+    }
72e12a
 
72e12a
     if (state->reply_count == 0
72e12a
             || find_domain(state->reply_count, state->reply,
72e12a
-- 
72e12a
2.26.3
72e12a