Blame SOURCES/0055-AD-read-trusted-domains-from-local-domain-as-well.patch

7500e1
From 95adf488f94f5968f6cfba9e3bef74c07c02ccff Mon Sep 17 00:00:00 2001
7500e1
From: Sumit Bose <sbose@redhat.com>
7500e1
Date: Tue, 16 Feb 2021 14:30:55 +0100
7500e1
Subject: [PATCH 55/55] AD: read trusted domains from local domain as well
7500e1
MIME-Version: 1.0
7500e1
Content-Type: text/plain; charset=UTF-8
7500e1
Content-Transfer-Encoding: 8bit
7500e1
7500e1
Currently SSSD only uses information stored in a domain controller of
7500e1
the forest root domain to get the names of other trusted domains in the
7500e1
forest. Depending on how the forest was created the forest root might
7500e1
not have LDAP objects for all domains in the forest. It looks like a
7500e1
typical case are child domains of other domains in the forest.
7500e1
7500e1
As a start SSSD can now include trusted domains stored in the LDAP tree
7500e1
of a local domain controller as well. In a long run it would make sense
7500e1
to allow SSSD to explicitly search for domain by looking up DNS entries
7500e1
and checking a potential domain controller with a CLDAP ping.
7500e1
7500e1
Resolves: https://github.com/SSSD/sssd/issues/5528
7500e1
7500e1
:feature: Besides trusted domains known by the forest root, trusted
7500e1
          domains known by the local domain are used as well.
7500e1
7500e1
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
7500e1
---
7500e1
 src/providers/ad/ad_subdomains.c | 105 +++++++++++++++++++++++++------
7500e1
 1 file changed, 86 insertions(+), 19 deletions(-)
7500e1
7500e1
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
7500e1
index f5b0be6c2..3eb49c93f 100644
7500e1
--- a/src/providers/ad/ad_subdomains.c
7500e1
+++ b/src/providers/ad/ad_subdomains.c
7500e1
@@ -45,6 +45,7 @@
7500e1
 #define AD_AT_TRUST_TYPE    "trustType"
7500e1
 #define AD_AT_TRUST_PARTNER "trustPartner"
7500e1
 #define AD_AT_TRUST_ATTRS   "trustAttributes"
7500e1
+#define AD_AT_DOMAIN_NAME   "cn"
7500e1
 
7500e1
 /* trustType=2 denotes uplevel (NT5 and later) trusted domains. See
7500e1
  * http://msdn.microsoft.com/en-us/library/windows/desktop/ms680342%28v=vs.85%29.aspx
7500e1
@@ -56,7 +57,6 @@
7500e1
  */
7500e1
 #define SLAVE_DOMAIN_FILTER_BASE "(objectclass=trustedDomain)(trustType=2)(!(msDS-TrustForestTrustInfo=*))"
7500e1
 #define SLAVE_DOMAIN_FILTER      "(&"SLAVE_DOMAIN_FILTER_BASE")"
7500e1
-#define FOREST_ROOT_FILTER_FMT   "(&"SLAVE_DOMAIN_FILTER_BASE"(cn=%s))"
7500e1
 
7500e1
 /* Attributes of schema objects. See e.g.
7500e1
  * https://docs.microsoft.com/en-us/windows/desktop/AD/characteristics-of-attributes
7500e1
@@ -646,6 +646,10 @@ done:
7500e1
     return ret;
7500e1
 }
7500e1
 
7500e1
+/* How many times we keep a domain not found during searches before it will be
7500e1
+ * removed. */
7500e1
+#define MAX_NOT_FOUND 6
7500e1
+
7500e1
 static errno_t ad_subdomains_refresh(struct be_ctx *be_ctx,
7500e1
                                      struct sdap_idmap_ctx *idmap_ctx,
7500e1
                                      struct sdap_options *opts,
7500e1
@@ -706,6 +710,25 @@ static errno_t ad_subdomains_refresh(struct be_ctx *be_ctx,
7500e1
         }
7500e1
 
7500e1
         if (c >= num_subdomains) {
7500e1
+            DEBUG(SSSDBG_CONF_SETTINGS, "Domain [%s] not in current list.\n",
7500e1
+                                        dom->name);
7500e1
+            /* Since the forest root might not have trustedDomain objects for
7500e1
+             * each domain in the forest, especially e.g. for child-domains of
7500e1
+             * child-domains, we cannot reliable say if a domain is still
7500e1
+             * present or not.
7500e1
+             * Maybe it would work to check the crossRef objects in
7500e1
+             * CN=Partitions,CN=Configuration as well to understand if a
7500e1
+             * domain is still known in the forest or not.
7500e1
+             * For the time being we use a counter, if a domain was not found
7500e1
+             * after multiple attempts it will be deleted. */
7500e1
+
7500e1
+            if (dom->not_found_counter++ < MAX_NOT_FOUND) {
7500e1
+                DEBUG(SSSDBG_TRACE_ALL,
7500e1
+                      "Domain [%s] was not found [%zu] times.\n", dom->name,
7500e1
+                      dom->not_found_counter);
7500e1
+                continue;
7500e1
+            }
7500e1
+
7500e1
             /* ok this subdomain does not exist anymore, let's clean up */
7500e1
             sss_domain_set_state(dom, DOM_DISABLED);
7500e1
 
7500e1
@@ -740,6 +763,7 @@ static errno_t ad_subdomains_refresh(struct be_ctx *be_ctx,
7500e1
             /* terminate all requests for this subdomain so we can free it */
7500e1
             dp_terminate_domain_requests(be_ctx->provider, dom->name);
7500e1
             talloc_zfree(sdom);
7500e1
+
7500e1
         } else {
7500e1
             /* ok let's try to update it */
7500e1
             ret = ad_subdom_enumerates(domain, subdomains[c], &enumerate);
7500e1
@@ -747,6 +771,7 @@ static errno_t ad_subdomains_refresh(struct be_ctx *be_ctx,
7500e1
                 goto done;
7500e1
             }
7500e1
 
7500e1
+            dom->not_found_counter = 0;
7500e1
             ret = ad_subdom_store(be_ctx->cdb, idmap_ctx, domain,
7500e1
                                   subdomains[c], enumerate);
7500e1
             if (ret) {
7500e1
@@ -1307,10 +1332,9 @@ ad_get_root_domain_send(TALLOC_CTX *mem_ctx,
7500e1
     struct tevent_req *req;
7500e1
     struct sdap_options *opts;
7500e1
     errno_t ret;
7500e1
-    const char *filter;
7500e1
     const char *attrs[] = { AD_AT_FLATNAME, AD_AT_TRUST_PARTNER,
7500e1
                             AD_AT_SID, AD_AT_TRUST_TYPE,
7500e1
-                            AD_AT_TRUST_ATTRS, NULL };
7500e1
+                            AD_AT_TRUST_ATTRS, AD_AT_DOMAIN_NAME, NULL };
7500e1
 
7500e1
     req = tevent_req_create(mem_ctx, &state, struct ad_get_root_domain_state);
7500e1
     if (req == NULL) {
7500e1
@@ -1335,15 +1359,10 @@ ad_get_root_domain_send(TALLOC_CTX *mem_ctx,
7500e1
     state->domain = domain;
7500e1
     state->forest = forest;
7500e1
 
7500e1
-    filter = talloc_asprintf(state, FOREST_ROOT_FILTER_FMT, forest);
7500e1
-    if (filter == NULL) {
7500e1
-        ret = ENOMEM;
7500e1
-        goto immediately;
7500e1
-    }
7500e1
-
7500e1
     subreq = sdap_search_bases_return_first_send(state, ev, opts, sh,
7500e1
                                                  opts->sdom->search_bases,
7500e1
-                                                 NULL, false, 0, filter, attrs,
7500e1
+                                                 NULL, false, 0,
7500e1
+                                                 SLAVE_DOMAIN_FILTER, attrs,
7500e1
                                                  NULL);
7500e1
     if (subreq == NULL) {
7500e1
         ret = ENOMEM;
7500e1
@@ -1365,11 +1384,33 @@ immediately:
7500e1
     return req;
7500e1
 }
7500e1
 
7500e1
+static struct sysdb_attrs *find_domain(size_t count, struct sysdb_attrs **reply,
7500e1
+                                       const char *dom_name)
7500e1
+{
7500e1
+    size_t c;
7500e1
+    const char *name;
7500e1
+    int ret;
7500e1
+
7500e1
+    for (c = 0; c < count; c++) {
7500e1
+        ret = sysdb_attrs_get_string(reply[c], AD_AT_DOMAIN_NAME, &name);
7500e1
+        if (ret != EOK) {
7500e1
+            DEBUG(SSSDBG_OP_FAILURE, "Failed to find domain name, skipping");
7500e1
+            continue;
7500e1
+        }
7500e1
+        if (strcasecmp(name, dom_name) == 0) {
7500e1
+            return reply[c];
7500e1
+        }
7500e1
+    }
7500e1
+
7500e1
+    return NULL;
7500e1
+}
7500e1
+
7500e1
 static void ad_get_root_domain_done(struct tevent_req *subreq)
7500e1
 {
7500e1
     struct tevent_req *req;
7500e1
     struct ad_get_root_domain_state *state;
7500e1
     errno_t ret;
7500e1
+    bool has_changes = false;
7500e1
 
7500e1
     req = tevent_req_callback_data(subreq, struct tevent_req);
7500e1
     state = tevent_req_data(req, struct ad_get_root_domain_state);
7500e1
@@ -1384,7 +1425,37 @@ static void ad_get_root_domain_done(struct tevent_req *subreq)
7500e1
         goto done;
7500e1
     }
7500e1
 
7500e1
-    if (state->reply_count == 0) {
7500e1
+    find_domain(state->reply_count, state->reply, state->forest);
7500e1
+
7500e1
+    if (state->reply_count == 0
7500e1
+            || find_domain(state->reply_count, state->reply,
7500e1
+                           state->forest) == NULL) {
7500e1
+
7500e1
+        if (state->reply_count > 0) {
7500e1
+            /* refresh the other domains we have found before checking forest
7500e1
+             * root */
7500e1
+            ret = ad_subdomains_refresh(state->be_ctx, state->idmap_ctx,
7500e1
+                                        state->opts,
7500e1
+                                        state->reply, state->reply_count, false,
7500e1
+                                        &state->sd_ctx->last_refreshed,
7500e1
+                                        &has_changes);
7500e1
+            if (ret != EOK) {
7500e1
+                DEBUG(SSSDBG_OP_FAILURE,
7500e1
+                      "ad_subdomains_refresh failed [%d]: %s\n",
7500e1
+                      ret, sss_strerror(ret));
7500e1
+                goto done;
7500e1
+            }
7500e1
+
7500e1
+            if (has_changes) {
7500e1
+                ret = ad_subdom_reinit(state->sd_ctx);
7500e1
+                if (ret != EOK) {
7500e1
+                    DEBUG(SSSDBG_OP_FAILURE,
7500e1
+                          "Could not reinitialize subdomains\n");
7500e1
+                    goto done;
7500e1
+                }
7500e1
+            }
7500e1
+        }
7500e1
+
7500e1
         DEBUG(SSSDBG_OP_FAILURE,
7500e1
               "No information provided for root domain, trying directly.\n");
7500e1
         subreq = ad_check_domain_send(state, state->ev, state->be_ctx,
7500e1
@@ -1397,11 +1468,6 @@ static void ad_get_root_domain_done(struct tevent_req *subreq)
7500e1
         }
7500e1
         tevent_req_set_callback(subreq, ad_check_root_domain_done, req);
7500e1
         return;
7500e1
-    } else if (state->reply_count > 1) {
7500e1
-        DEBUG(SSSDBG_CRIT_FAILURE, "Multiple results for root domain search, "
7500e1
-              "domain list might be incomplete!\n");
7500e1
-        ret = ERR_MALFORMED_ENTRY;
7500e1
-        goto done;
7500e1
     }
7500e1
 
7500e1
     ret = ad_get_root_domain_refresh(state);
7500e1
@@ -1519,7 +1585,7 @@ ad_get_root_domain_refresh(struct ad_get_root_domain_state *state)
7500e1
     errno_t ret;
7500e1
 
7500e1
     ret = ad_subdomains_refresh(state->be_ctx, state->idmap_ctx, state->opts,
7500e1
-                                state->reply, state->reply_count, true,
7500e1
+                                state->reply, state->reply_count, false,
7500e1
                                 &state->sd_ctx->last_refreshed,
7500e1
                                 &has_changes);
7500e1
     if (ret != EOK) {
7500e1
@@ -1536,8 +1602,9 @@ ad_get_root_domain_refresh(struct ad_get_root_domain_state *state)
7500e1
         }
7500e1
     }
7500e1
 
7500e1
-    state->root_domain_attrs = state->reply[0];
7500e1
-    root_domain = ads_get_root_domain(state->be_ctx, state->reply[0]);
7500e1
+    state->root_domain_attrs = find_domain(state->reply_count, state->reply,
7500e1
+                                           state->forest);
7500e1
+    root_domain = ads_get_root_domain(state->be_ctx, state->root_domain_attrs);
7500e1
     if (root_domain == NULL) {
7500e1
         DEBUG(SSSDBG_OP_FAILURE, "Could not find the root domain\n");
7500e1
         ret = EFAULT;
7500e1
-- 
7500e1
2.26.3
7500e1