Blame SOURCES/0034-AD-Add-Global-Catalog-usability-check-in-subdomain-c.patch

cdf651
From 7f9567ba8d62536c4aeb68897316781e82116c21 Mon Sep 17 00:00:00 2001
cdf651
From: Jakub Hrozek <jhrozek@redhat.com>
cdf651
Date: Wed, 4 Jul 2018 13:14:40 +0200
cdf651
Subject: [PATCH] AD: Add Global Catalog usability check in subdomain code by
cdf651
 looking at the schema
cdf651
MIME-Version: 1.0
cdf651
Content-Type: text/plain; charset=UTF-8
cdf651
Content-Transfer-Encoding: 8bit
cdf651
cdf651
Addsa a new tevent request which checks for the presence of uidNumber
cdf651
and gidNumber under the schema naming context, which is typically
cdf651
cn=schema,cn=configuration,$BASEDN. For both objects representing each of
cdf651
the attributes, the isMemberOfPartialAttributeSet attribute is requested. If
cdf651
this attribute is set to TRUE, then the attribute corresponding to this
cdf651
schema object had been replicated to the Global Catalog.
cdf651
cdf651
Because the isMemberOfPartialAttributeSet is not replicated to the GC
cdf651
itself, we use the LDAP connection for the search.
cdf651
cdf651
Related:
cdf651
https://pagure.io/SSSD/sssd/issue/3755
cdf651
cdf651
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
cdf651
(cherry picked from commit ba96e7b839b875946f03787a3a57f259230a0fef)
cdf651
---
cdf651
 src/providers/ad/ad_subdomains.c | 308 +++++++++++++++++++++++++++++--
cdf651
 1 file changed, 288 insertions(+), 20 deletions(-)
cdf651
cdf651
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
cdf651
index 84886e920b37f8803d85ce0903b74e6c809a8904..549c2c1f76d4bbccdf03c6ab619fba5f9186358f 100644
cdf651
--- a/src/providers/ad/ad_subdomains.c
cdf651
+++ b/src/providers/ad/ad_subdomains.c
cdf651
@@ -54,9 +54,39 @@
cdf651
 #define SLAVE_DOMAIN_FILTER      "(&"SLAVE_DOMAIN_FILTER_BASE")"
cdf651
 #define FOREST_ROOT_FILTER_FMT   "(&"SLAVE_DOMAIN_FILTER_BASE"(cn=%s))"
cdf651
 
cdf651
+/* Attributes of schema objects. See e.g.
cdf651
+ * https://docs.microsoft.com/en-us/windows/desktop/AD/characteristics-of-attributes
cdf651
+ * for more details
cdf651
+ */
cdf651
+#define AD_SCHEMA_AT_OC         "attributeSchema"
cdf651
+#define AD_AT_SCHEMA_NAME       "cn"
cdf651
+#define AD_AT_SCHEMA_IS_REPL    "isMemberOfPartialAttributeSet"
cdf651
+
cdf651
 /* do not refresh more often than every 5 seconds for now */
cdf651
 #define AD_SUBDOMAIN_REFRESH_LIMIT 5
cdf651
 
cdf651
+static void
cdf651
+ad_disable_gc(struct ad_options *ad_options)
cdf651
+{
cdf651
+    errno_t ret;
cdf651
+
cdf651
+    if (dp_opt_get_bool(ad_options->basic, AD_ENABLE_GC) == false) {
cdf651
+        return;
cdf651
+    }
cdf651
+
cdf651
+    DEBUG(SSSDBG_IMPORTANT_INFO, "POSIX attributes were requested "
cdf651
+          "but are not present on the server side. Global Catalog "
cdf651
+          "lookups will be disabled\n");
cdf651
+
cdf651
+    ret = dp_opt_set_bool(ad_options->basic,
cdf651
+                          AD_ENABLE_GC, false);
cdf651
+    if (ret != EOK) {
cdf651
+        DEBUG(SSSDBG_MINOR_FAILURE,
cdf651
+                "Could not turn off GC support\n");
cdf651
+        /* Not fatal */
cdf651
+    }
cdf651
+}
cdf651
+
cdf651
 static struct sss_domain_info *
cdf651
 ads_get_root_domain(struct be_ctx *be_ctx, struct sysdb_attrs *attrs)
cdf651
 {
cdf651
@@ -1261,6 +1291,212 @@ static errno_t ad_get_root_domain_recv(TALLOC_CTX *mem_ctx,
cdf651
     return EOK;
cdf651
 }
cdf651
 
cdf651
+static void ad_check_gc_usability_search_done(struct tevent_req *subreq);
cdf651
+
cdf651
+struct ad_check_gc_usability_state {
cdf651
+    struct sdap_options *sdap_opts;
cdf651
+
cdf651
+    const char *attrs[3];
cdf651
+
cdf651
+    bool is_gc_usable;
cdf651
+};
cdf651
+
cdf651
+static struct tevent_req *
cdf651
+ad_check_gc_usability_send(TALLOC_CTX *mem_ctx,
cdf651
+                           struct tevent_context *ev,
cdf651
+                           struct ad_options *ad_options,
cdf651
+                           struct sdap_options *sdap_opts,
cdf651
+                           struct sdap_id_op *op,
cdf651
+                           const char *domain_name,
cdf651
+                           const char *domain_sid)
cdf651
+{
cdf651
+    struct ad_check_gc_usability_state *state = NULL;
cdf651
+    struct tevent_req *req = NULL;
cdf651
+    struct tevent_req *subreq = NULL;
cdf651
+    const char *filter = NULL;
cdf651
+    errno_t ret;
cdf651
+    bool uses_id_mapping;
cdf651
+
cdf651
+    req = tevent_req_create(mem_ctx, &state,
cdf651
+                            struct ad_check_gc_usability_state);
cdf651
+    if (req == NULL) {
cdf651
+        return NULL;
cdf651
+    }
cdf651
+    state->sdap_opts = sdap_opts;
cdf651
+    state->is_gc_usable = false;
cdf651
+
cdf651
+    if (dp_opt_get_bool(ad_options->basic, AD_ENABLE_GC) == false) {
cdf651
+        DEBUG(SSSDBG_TRACE_FUNC, "GC explicitly disabled\n");
cdf651
+        state->is_gc_usable = false;
cdf651
+        ret = EOK;
cdf651
+        goto immediately;
cdf651
+    }
cdf651
+
cdf651
+    uses_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
cdf651
+                                                        sdap_opts->idmap_ctx,
cdf651
+                                                        domain_name,
cdf651
+                                                        domain_sid);
cdf651
+    if (uses_id_mapping == true) {
cdf651
+        DEBUG(SSSDBG_TRACE_FUNC, "GC always usable while ID mapping\n");
cdf651
+        state->is_gc_usable = true;
cdf651
+        ret = EOK;
cdf651
+        goto immediately;
cdf651
+    }
cdf651
+
cdf651
+    /* The schema partition is replicated across all DCs in the forest, so
cdf651
+     * it's safe to use the baseDN even if e.g. joined to a child domain
cdf651
+     * even though the base DN "looks" like a part of the forest root
cdf651
+     * tree. On the other hand, it doesn't make sense to guess the value
cdf651
+     * if we can't detect it from the rootDSE.
cdf651
+     */
cdf651
+    if (state->sdap_opts->schema_basedn == NULL) {
cdf651
+        DEBUG(SSSDBG_TRACE_FUNC,
cdf651
+              "No idea where to look for the schema, disabling GC\n");
cdf651
+        state->is_gc_usable = false;
cdf651
+        ret = EOK;
cdf651
+        goto immediately;
cdf651
+    }
cdf651
+
cdf651
+    state->attrs[0] = AD_AT_SCHEMA_NAME;
cdf651
+    state->attrs[1] = AD_AT_SCHEMA_IS_REPL;
cdf651
+    state->attrs[2] = NULL;
cdf651
+
cdf651
+    DEBUG(SSSDBG_TRACE_FUNC, "Checking for POSIX attributes in GC\n");
cdf651
+
cdf651
+    filter = talloc_asprintf(
cdf651
+                        state,
cdf651
+                        "(&(objectclass=%s)(|(%s=%s)(%s=%s)))",
cdf651
+                        AD_SCHEMA_AT_OC,
cdf651
+                        AD_AT_SCHEMA_NAME,
cdf651
+                        state->sdap_opts->user_map[SDAP_AT_USER_UID].name,
cdf651
+                        AD_AT_SCHEMA_NAME,
cdf651
+                        state->sdap_opts->group_map[SDAP_AT_GROUP_GID].name);
cdf651
+    if (filter == NULL) {
cdf651
+        ret = ENOMEM;
cdf651
+        goto immediately;
cdf651
+    }
cdf651
+
cdf651
+    subreq = sdap_get_generic_send(state,
cdf651
+                                   ev,
cdf651
+                                   state->sdap_opts,
cdf651
+                                   sdap_id_op_handle(op),
cdf651
+                                   state->sdap_opts->schema_basedn,
cdf651
+                                   LDAP_SCOPE_SUBTREE,
cdf651
+                                   filter,
cdf651
+                                   state->attrs,
cdf651
+                                   NULL, 0,
cdf651
+                                   dp_opt_get_int(state->sdap_opts->basic,
cdf651
+                                                  SDAP_SEARCH_TIMEOUT),
cdf651
+                                   false);
cdf651
+    if (subreq == NULL) {
cdf651
+        ret = ENOMEM;
cdf651
+        goto immediately;
cdf651
+    }
cdf651
+    tevent_req_set_callback(subreq, ad_check_gc_usability_search_done, req);
cdf651
+
cdf651
+    return req;
cdf651
+
cdf651
+immediately:
cdf651
+    if (ret == EOK) {
cdf651
+        tevent_req_done(req);
cdf651
+    } else {
cdf651
+        tevent_req_error(req, ret);
cdf651
+    }
cdf651
+    tevent_req_post(req, ev);
cdf651
+
cdf651
+    return req;
cdf651
+}
cdf651
+
cdf651
+static void ad_check_gc_usability_search_done(struct tevent_req *subreq)
cdf651
+{
cdf651
+    struct tevent_req *req = tevent_req_callback_data(subreq,
cdf651
+                                                      struct tevent_req);
cdf651
+    struct ad_check_gc_usability_state *state = tevent_req_data(req,
cdf651
+                                          struct ad_check_gc_usability_state);
cdf651
+    errno_t ret;
cdf651
+    size_t reply_count;
cdf651
+    struct sysdb_attrs **reply = NULL;
cdf651
+    bool uid = false;
cdf651
+    bool gid = false;
cdf651
+
cdf651
+    ret = sdap_get_generic_recv(subreq, state, &reply_count, &reply);
cdf651
+    talloc_zfree(subreq);
cdf651
+    if (ret != EOK) {
cdf651
+        DEBUG(SSSDBG_OP_FAILURE,
cdf651
+              "sdap_get_generic_recv failed [%d]: %s\n",
cdf651
+              ret, strerror(ret));
cdf651
+        /* We continue to finish sdap_id_op. */
cdf651
+    }
cdf651
+
cdf651
+    if (reply_count == 0) {
cdf651
+        DEBUG(SSSDBG_TRACE_LIBS,
cdf651
+              "Nothing found, so no POSIX attrs can exist\n");
cdf651
+        state->is_gc_usable = false;
cdf651
+        tevent_req_done(req);
cdf651
+        return;
cdf651
+    }
cdf651
+
cdf651
+    for (size_t i = 0; i < reply_count; i++) {
cdf651
+        const char *name = NULL;
cdf651
+        const char *is_in_partial_set = NULL;
cdf651
+        bool *val = NULL;
cdf651
+
cdf651
+        ret = sysdb_attrs_get_string(reply[i], AD_AT_SCHEMA_NAME, &name);
cdf651
+        if (ret != EOK) {
cdf651
+            DEBUG(SSSDBG_MINOR_FAILURE, "Cannot get "AD_AT_SCHEMA_NAME);
cdf651
+            continue;
cdf651
+        }
cdf651
+
cdf651
+        if (strcasecmp(name, state->sdap_opts->user_map[SDAP_AT_USER_UID].name) == 0) {
cdf651
+            val = &ui;;
cdf651
+        } else if (strcasecmp(name, state->sdap_opts->user_map[SDAP_AT_USER_GID].name) == 0) {
cdf651
+            val = &gid;
cdf651
+        } else {
cdf651
+            DEBUG(SSSDBG_MINOR_FAILURE, "Unexpected attribute\n");
cdf651
+            continue;
cdf651
+        }
cdf651
+
cdf651
+        ret = sysdb_attrs_get_string(reply[i],
cdf651
+                                     AD_AT_SCHEMA_IS_REPL,
cdf651
+                                     &is_in_partial_set);
cdf651
+        if (ret != EOK) {
cdf651
+            DEBUG(SSSDBG_MINOR_FAILURE, "Cannot get "AD_AT_SCHEMA_IS_REPL);
cdf651
+            continue;
cdf651
+        }
cdf651
+
cdf651
+        if (strcasecmp(is_in_partial_set, "true") == 0) {
cdf651
+            *val = true;
cdf651
+        }
cdf651
+    }
cdf651
+
cdf651
+    if (uid == true && gid == true) {
cdf651
+        state->is_gc_usable = true;
cdf651
+    }
cdf651
+
cdf651
+    if (state->is_gc_usable == true) {
cdf651
+        DEBUG(SSSDBG_FUNC_DATA, "Server has POSIX attributes. Global Catalog will "
cdf651
+                                "be used for user and group lookups. Note that if "
cdf651
+                                "only a subset of POSIX attributes is present "
cdf651
+                                "in GC, the non-replicated attributes are "
cdf651
+                                "currently not read from the LDAP port\n");
cdf651
+    }
cdf651
+
cdf651
+    tevent_req_done(req);
cdf651
+}
cdf651
+
cdf651
+static errno_t ad_check_gc_usability_recv(struct tevent_req *req,
cdf651
+                                          bool *_is_gc_usable)
cdf651
+{
cdf651
+    struct ad_check_gc_usability_state *state = NULL;
cdf651
+
cdf651
+    state = tevent_req_data(req, struct ad_check_gc_usability_state);
cdf651
+
cdf651
+    TEVENT_REQ_RETURN_ON_ERROR(req);
cdf651
+
cdf651
+    *_is_gc_usable = state->is_gc_usable;
cdf651
+    return EOK;
cdf651
+}
cdf651
+
cdf651
 struct ad_subdomains_refresh_state {
cdf651
     struct tevent_context *ev;
cdf651
     struct be_ctx *be_ctx;
cdf651
@@ -1268,11 +1504,14 @@ struct ad_subdomains_refresh_state {
cdf651
     struct sdap_id_op *sdap_op;
cdf651
     struct sdap_id_ctx *id_ctx;
cdf651
     struct ad_options *ad_options;
cdf651
+
cdf651
+    char *forest;
cdf651
 };
cdf651
 
cdf651
 static errno_t ad_subdomains_refresh_retry(struct tevent_req *req);
cdf651
 static void ad_subdomains_refresh_connect_done(struct tevent_req *subreq);
cdf651
 static void ad_subdomains_refresh_master_done(struct tevent_req *subreq);
cdf651
+static void ad_subdomains_refresh_gc_check_done(struct tevent_req *subreq);
cdf651
 static void ad_subdomains_refresh_root_done(struct tevent_req *subreq);
cdf651
 static void ad_subdomains_refresh_done(struct tevent_req *subreq);
cdf651
 
cdf651
@@ -1385,37 +1624,73 @@ static void ad_subdomains_refresh_master_done(struct tevent_req *subreq)
cdf651
     struct ad_subdomains_refresh_state *state;
cdf651
     struct tevent_req *req;
cdf651
     const char *realm;
cdf651
-    const char *ad_domain;
cdf651
     char *master_sid;
cdf651
     char *flat_name;
cdf651
-    char *forest;
cdf651
     errno_t ret;
cdf651
 
cdf651
     req = tevent_req_callback_data(subreq, struct tevent_req);
cdf651
     state = tevent_req_data(req, struct ad_subdomains_refresh_state);
cdf651
 
cdf651
     ret = ad_master_domain_recv(subreq, state, &flat_name, &master_sid,
cdf651
-                                NULL, &forest);
cdf651
+                                NULL, &state->forest);
cdf651
     talloc_zfree(subreq);
cdf651
     if (ret != EOK) {
cdf651
         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get master domain information "
cdf651
               "[%d]: %s\n", ret, sss_strerror(ret));
cdf651
-        goto done;
cdf651
+        tevent_req_error(req, ret);
cdf651
+        return;
cdf651
     }
cdf651
 
cdf651
     realm = dp_opt_get_cstring(state->ad_options->basic, AD_KRB5_REALM);
cdf651
     if (realm == NULL) {
cdf651
         DEBUG(SSSDBG_CONF_SETTINGS, "Missing realm.\n");
cdf651
-        ret = EINVAL;
cdf651
-        goto done;
cdf651
+        tevent_req_error(req, EINVAL);
cdf651
+        return;
cdf651
     }
cdf651
 
cdf651
     ret = sysdb_master_domain_add_info(state->be_ctx->domain, realm,
cdf651
-                                       flat_name, master_sid, forest, NULL);
cdf651
+                                       flat_name, master_sid, state->forest, NULL);
cdf651
     if (ret != EOK) {
cdf651
         DEBUG(SSSDBG_OP_FAILURE, "Cannot save master domain info [%d]: %s\n",
cdf651
               ret, sss_strerror(ret));
cdf651
-        goto done;
cdf651
+        tevent_req_error(req, ret);
cdf651
+        return;
cdf651
+    }
cdf651
+
cdf651
+    subreq = ad_check_gc_usability_send(state,
cdf651
+                                        state->ev,
cdf651
+                                        state->ad_options,
cdf651
+                                        state->id_ctx->opts,
cdf651
+                                        state->sdap_op,
cdf651
+                                        state->be_ctx->domain->name,
cdf651
+                                        master_sid);
cdf651
+    if (subreq == NULL) {
cdf651
+        tevent_req_error(req, ENOMEM);
cdf651
+        return;
cdf651
+    }
cdf651
+    tevent_req_set_callback(subreq, ad_subdomains_refresh_gc_check_done, req);
cdf651
+}
cdf651
+
cdf651
+static void ad_subdomains_refresh_gc_check_done(struct tevent_req *subreq)
cdf651
+{
cdf651
+    struct ad_subdomains_refresh_state *state;
cdf651
+    struct tevent_req *req;
cdf651
+    const char *ad_domain;
cdf651
+    bool is_gc_usable;
cdf651
+    errno_t ret;
cdf651
+
cdf651
+    req = tevent_req_callback_data(subreq, struct tevent_req);
cdf651
+    state = tevent_req_data(req, struct ad_subdomains_refresh_state);
cdf651
+
cdf651
+    ret = ad_check_gc_usability_recv(subreq, &is_gc_usable);
cdf651
+    talloc_zfree(subreq);
cdf651
+    if (ret != EOK) {
cdf651
+        DEBUG(SSSDBG_MINOR_FAILURE, "Unable to get GC usability status\n");
cdf651
+        is_gc_usable = false;
cdf651
+    }
cdf651
+
cdf651
+    if (is_gc_usable == false) {
cdf651
+        ad_disable_gc(state->ad_options);
cdf651
     }
cdf651
 
cdf651
     /*
cdf651
@@ -1428,7 +1703,8 @@ static void ad_subdomains_refresh_master_done(struct tevent_req *subreq)
cdf651
                            state->be_ctx->domain->name) == 0) {
cdf651
                 DEBUG(SSSDBG_TRACE_FUNC,
cdf651
                       "No other enabled domain than master.\n");
cdf651
-                goto done;
cdf651
+                tevent_req_done(req);
cdf651
+                return;
cdf651
             }
cdf651
         }
cdf651
     }
cdf651
@@ -1440,24 +1716,16 @@ static void ad_subdomains_refresh_master_done(struct tevent_req *subreq)
cdf651
         ad_domain = state->sd_ctx->be_ctx->domain->name;
cdf651
     }
cdf651
 
cdf651
-    subreq = ad_get_root_domain_send(state, state->ev, ad_domain, forest,
cdf651
+    subreq = ad_get_root_domain_send(state, state->ev, ad_domain, state->forest,
cdf651
                                      sdap_id_op_handle(state->sdap_op),
cdf651
                                      state->sd_ctx);
cdf651
     if (subreq == NULL) {
cdf651
-        ret = ENOMEM;
cdf651
-        goto done;
cdf651
+        tevent_req_error(req, ENOMEM);
cdf651
+        return;
cdf651
     }
cdf651
 
cdf651
     tevent_req_set_callback(subreq, ad_subdomains_refresh_root_done, req);
cdf651
     return;
cdf651
-
cdf651
-done:
cdf651
-    if (ret != EOK) {
cdf651
-        tevent_req_error(req, ret);
cdf651
-        return;
cdf651
-    }
cdf651
-
cdf651
-    tevent_req_done(req);
cdf651
 }
cdf651
 
cdf651
 static void ad_subdomains_refresh_root_done(struct tevent_req *subreq)
cdf651
-- 
cdf651
2.17.1
cdf651