Blob Blame History Raw
From 2519d5ea3757862bebc17d73d74c7e5c57bdc815 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Tue, 16 Feb 2021 14:30:55 +0100
Subject: [PATCH 66/66] AD: read trusted domains from local domain as well
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Currently SSSD only uses information stored in a domain controller of
the forest root domain to get the names of other trusted domains in the
forest. Depending on how the forest was created the forest root might
not have LDAP objects for all domains in the forest. It looks like a
typical case are child domains of other domains in the forest.

As a start SSSD can now include trusted domains stored in the LDAP tree
of a local domain controller as well. In a long run it would make sense
to allow SSSD to explicitly search for domain by looking up DNS entries
and checking a potential domain controller with a CLDAP ping.

Resolves: https://github.com/SSSD/sssd/issues/5528

:feature: Besides trusted domains known by the forest root, trusted
          domains known by the local domain are used as well.

Reviewed-by: Pavel Březina <pbrezina@redhat.com>
(cherry picked from commit 95adf488f94f5968f6cfba9e3bef74c07c02ccff)

Reviewed-by: Pavel Březina <pbrezina@redhat.com>
---
 src/providers/ad/ad_subdomains.c | 105 +++++++++++++++++++++++++------
 1 file changed, 86 insertions(+), 19 deletions(-)

diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
index ba4efe975..16aecbc64 100644
--- a/src/providers/ad/ad_subdomains.c
+++ b/src/providers/ad/ad_subdomains.c
@@ -45,6 +45,7 @@
 #define AD_AT_TRUST_TYPE    "trustType"
 #define AD_AT_TRUST_PARTNER "trustPartner"
 #define AD_AT_TRUST_ATTRS   "trustAttributes"
+#define AD_AT_DOMAIN_NAME   "cn"
 
 /* trustType=2 denotes uplevel (NT5 and later) trusted domains. See
  * http://msdn.microsoft.com/en-us/library/windows/desktop/ms680342%28v=vs.85%29.aspx
@@ -56,7 +57,6 @@
  */
 #define SLAVE_DOMAIN_FILTER_BASE "(objectclass=trustedDomain)(trustType=2)(!(msDS-TrustForestTrustInfo=*))"
 #define SLAVE_DOMAIN_FILTER      "(&"SLAVE_DOMAIN_FILTER_BASE")"
-#define FOREST_ROOT_FILTER_FMT   "(&"SLAVE_DOMAIN_FILTER_BASE"(cn=%s))"
 
 /* Attributes of schema objects. See e.g.
  * https://docs.microsoft.com/en-us/windows/desktop/AD/characteristics-of-attributes
@@ -646,6 +646,10 @@ done:
     return ret;
 }
 
+/* How many times we keep a domain not found during searches before it will be
+ * removed. */
+#define MAX_NOT_FOUND 6
+
 static errno_t ad_subdomains_refresh(struct be_ctx *be_ctx,
                                      struct sdap_idmap_ctx *idmap_ctx,
                                      struct sdap_options *opts,
@@ -706,6 +710,25 @@ static errno_t ad_subdomains_refresh(struct be_ctx *be_ctx,
         }
 
         if (c >= num_subdomains) {
+            DEBUG(SSSDBG_CONF_SETTINGS, "Domain [%s] not in current list.\n",
+                                        dom->name);
+            /* Since the forest root might not have trustedDomain objects for
+             * each domain in the forest, especially e.g. for child-domains of
+             * child-domains, we cannot reliable say if a domain is still
+             * present or not.
+             * Maybe it would work to check the crossRef objects in
+             * CN=Partitions,CN=Configuration as well to understand if a
+             * domain is still known in the forest or not.
+             * For the time being we use a counter, if a domain was not found
+             * after multiple attempts it will be deleted. */
+
+            if (dom->not_found_counter++ < MAX_NOT_FOUND) {
+                DEBUG(SSSDBG_TRACE_ALL,
+                      "Domain [%s] was not found [%zu] times.\n", dom->name,
+                      dom->not_found_counter);
+                continue;
+            }
+
             /* ok this subdomain does not exist anymore, let's clean up */
             sss_domain_set_state(dom, DOM_DISABLED);
 
@@ -743,6 +766,7 @@ static errno_t ad_subdomains_refresh(struct be_ctx *be_ctx,
             /* terminate all requests for this subdomain so we can free it */
             dp_terminate_domain_requests(be_ctx->provider, dom->name);
             talloc_zfree(sdom);
+
         } else {
             /* ok let's try to update it */
             ret = ad_subdom_enumerates(domain, subdomains[c], &enumerate);
@@ -750,6 +774,7 @@ static errno_t ad_subdomains_refresh(struct be_ctx *be_ctx,
                 goto done;
             }
 
+            dom->not_found_counter = 0;
             ret = ad_subdom_store(be_ctx->cdb, idmap_ctx, domain,
                                   subdomains[c], enumerate);
             if (ret) {
@@ -1310,10 +1335,9 @@ ad_get_root_domain_send(TALLOC_CTX *mem_ctx,
     struct tevent_req *req;
     struct sdap_options *opts;
     errno_t ret;
-    const char *filter;
     const char *attrs[] = { AD_AT_FLATNAME, AD_AT_TRUST_PARTNER,
                             AD_AT_SID, AD_AT_TRUST_TYPE,
-                            AD_AT_TRUST_ATTRS, NULL };
+                            AD_AT_TRUST_ATTRS, AD_AT_DOMAIN_NAME, NULL };
 
     req = tevent_req_create(mem_ctx, &state, struct ad_get_root_domain_state);
     if (req == NULL) {
@@ -1338,15 +1362,10 @@ ad_get_root_domain_send(TALLOC_CTX *mem_ctx,
     state->domain = domain;
     state->forest = forest;
 
-    filter = talloc_asprintf(state, FOREST_ROOT_FILTER_FMT, forest);
-    if (filter == NULL) {
-        ret = ENOMEM;
-        goto immediately;
-    }
-
     subreq = sdap_search_bases_return_first_send(state, ev, opts, sh,
                                                  opts->sdom->search_bases,
-                                                 NULL, false, 0, filter, attrs,
+                                                 NULL, false, 0,
+                                                 SLAVE_DOMAIN_FILTER, attrs,
                                                  NULL);
     if (subreq == NULL) {
         ret = ENOMEM;
@@ -1368,11 +1387,33 @@ immediately:
     return req;
 }
 
+static struct sysdb_attrs *find_domain(size_t count, struct sysdb_attrs **reply,
+                                       const char *dom_name)
+{
+    size_t c;
+    const char *name;
+    int ret;
+
+    for (c = 0; c < count; c++) {
+        ret = sysdb_attrs_get_string(reply[c], AD_AT_DOMAIN_NAME, &name);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "Failed to find domain name, skipping");
+            continue;
+        }
+        if (strcasecmp(name, dom_name) == 0) {
+            return reply[c];
+        }
+    }
+
+    return NULL;
+}
+
 static void ad_get_root_domain_done(struct tevent_req *subreq)
 {
     struct tevent_req *req;
     struct ad_get_root_domain_state *state;
     errno_t ret;
+    bool has_changes = false;
 
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct ad_get_root_domain_state);
@@ -1387,7 +1428,37 @@ static void ad_get_root_domain_done(struct tevent_req *subreq)
         goto done;
     }
 
-    if (state->reply_count == 0) {
+    find_domain(state->reply_count, state->reply, state->forest);
+
+    if (state->reply_count == 0
+            || find_domain(state->reply_count, state->reply,
+                           state->forest) == NULL) {
+
+        if (state->reply_count > 0) {
+            /* refresh the other domains we have found before checking forest
+             * root */
+            ret = ad_subdomains_refresh(state->be_ctx, state->idmap_ctx,
+                                        state->opts,
+                                        state->reply, state->reply_count, false,
+                                        &state->sd_ctx->last_refreshed,
+                                        &has_changes);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE,
+                      "ad_subdomains_refresh failed [%d]: %s\n",
+                      ret, sss_strerror(ret));
+                goto done;
+            }
+
+            if (has_changes) {
+                ret = ad_subdom_reinit(state->sd_ctx);
+                if (ret != EOK) {
+                    DEBUG(SSSDBG_OP_FAILURE,
+                          "Could not reinitialize subdomains\n");
+                    goto done;
+                }
+            }
+        }
+
         DEBUG(SSSDBG_OP_FAILURE,
               "No information provided for root domain, trying directly.\n");
         subreq = ad_check_domain_send(state, state->ev, state->be_ctx,
@@ -1400,11 +1471,6 @@ static void ad_get_root_domain_done(struct tevent_req *subreq)
         }
         tevent_req_set_callback(subreq, ad_check_root_domain_done, req);
         return;
-    } else if (state->reply_count > 1) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Multiple results for root domain search, "
-              "domain list might be incomplete!\n");
-        ret = ERR_MALFORMED_ENTRY;
-        goto done;
     }
 
     ret = ad_get_root_domain_refresh(state);
@@ -1522,7 +1588,7 @@ ad_get_root_domain_refresh(struct ad_get_root_domain_state *state)
     errno_t ret;
 
     ret = ad_subdomains_refresh(state->be_ctx, state->idmap_ctx, state->opts,
-                                state->reply, state->reply_count, true,
+                                state->reply, state->reply_count, false,
                                 &state->sd_ctx->last_refreshed,
                                 &has_changes);
     if (ret != EOK) {
@@ -1539,8 +1605,9 @@ ad_get_root_domain_refresh(struct ad_get_root_domain_state *state)
         }
     }
 
-    state->root_domain_attrs = state->reply[0];
-    root_domain = ads_get_root_domain(state->be_ctx, state->reply[0]);
+    state->root_domain_attrs = find_domain(state->reply_count, state->reply,
+                                           state->forest);
+    root_domain = ads_get_root_domain(state->be_ctx, state->root_domain_attrs);
     if (root_domain == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, "Could not find the root domain\n");
         ret = EFAULT;
-- 
2.26.3