Blame SOURCES/0074-AD-Implement-a-real-getAccountDomain-handler-for-the.patch

ced1f5
From 427a1f162e0ceb97e4e9491f81048646bd144910 Mon Sep 17 00:00:00 2001
ced1f5
From: Jakub Hrozek <jhrozek@redhat.com>
ced1f5
Date: Tue, 7 Nov 2017 17:01:34 +0100
ced1f5
Subject: [PATCH 74/83] AD: Implement a real getAccountDomain handler for the
ced1f5
 AD provider
ced1f5
MIME-Version: 1.0
ced1f5
Content-Type: text/plain; charset=UTF-8
ced1f5
Content-Transfer-Encoding: 8bit
ced1f5
ced1f5
After this patch, the AD provider drops the default getAccountDomain
ced1f5
handler in favor of the handler added in this patch.
ced1f5
ced1f5
The handler first checks if the domain is eligible for locating
ced1f5
the domain of an ID with the help of the Global Catalog at all, which
ced1f5
only happens if:
ced1f5
    - the Global Catalog is enabled
ced1f5
    - POSIX IDs are used, not ID-mapping
ced1f5
    - the Global catalog contains some POSIX IDs
ced1f5
ced1f5
If all these hold true, then the Global Catalog is searched with
ced1f5
an empty search base, which searches the whole GC. If a single entry
ced1f5
is returned, its original DN is converted to a domain name and returned.
ced1f5
ced1f5
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
ced1f5
Reviewed-by: Sumit Bose <sbose@redhat.com>
ced1f5
(cherry picked from commit 095844d6b48aef483c33e5a369a405ae686e044d)
ced1f5
---
ced1f5
 src/providers/ad/ad_id.c   | 469 +++++++++++++++++++++++++++++++++++++++++++++
ced1f5
 src/providers/ad/ad_id.h   |  10 +
ced1f5
 src/providers/ad/ad_init.c |   4 +-
ced1f5
 3 files changed, 481 insertions(+), 2 deletions(-)
ced1f5
ced1f5
diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
ced1f5
index e14ada386f16851a65097952c85e57b7acda14aa..0b8f49819405c7dbbfa18b5359f7743441dc65e5 100644
ced1f5
--- a/src/providers/ad/ad_id.c
ced1f5
+++ b/src/providers/ad/ad_id.c
ced1f5
@@ -27,6 +27,7 @@
ced1f5
 #include "providers/ad/ad_pac.h"
ced1f5
 #include "providers/ldap/sdap_async_enum.h"
ced1f5
 #include "providers/ldap/sdap_idmap.h"
ced1f5
+#include "providers/ldap/sdap_async.h"
ced1f5
 
ced1f5
 static void
ced1f5
 disable_gc(struct ad_options *ad_options)
ced1f5
@@ -1076,3 +1077,471 @@ ad_enumeration_recv(struct tevent_req *req)
ced1f5
     return EOK;
ced1f5
 }
ced1f5
 
ced1f5
+static errno_t ad_get_account_domain_prepare_search(struct tevent_req *req);
ced1f5
+static errno_t ad_get_account_domain_connect_retry(struct tevent_req *req);
ced1f5
+static void ad_get_account_domain_connect_done(struct tevent_req *subreq);
ced1f5
+static void ad_get_account_domain_posix_check_done(struct tevent_req *subreq);
ced1f5
+static void ad_get_account_domain_search(struct tevent_req *req);
ced1f5
+static void ad_get_account_domain_search_done(struct tevent_req *subreq);
ced1f5
+static void ad_get_account_domain_evaluate(struct tevent_req *req);
ced1f5
+
ced1f5
+struct ad_get_account_domain_state {
ced1f5
+    struct tevent_context *ev;
ced1f5
+    struct ad_id_ctx *id_ctx;
ced1f5
+    struct sdap_id_ctx *sdap_id_ctx;
ced1f5
+    struct sdap_domain *sdom;
ced1f5
+    uint32_t entry_type;
ced1f5
+    uint32_t filter_type;
ced1f5
+    char *clean_filter;
ced1f5
+
ced1f5
+    bool twopass;
ced1f5
+
ced1f5
+    struct sdap_search_base **search_bases;
ced1f5
+    size_t base_iter;
ced1f5
+    const char *base_filter;
ced1f5
+    char *filter;
ced1f5
+    const char **attrs;
ced1f5
+    int dp_error;
ced1f5
+    struct dp_reply_std reply;
ced1f5
+    struct sdap_id_op *op;
ced1f5
+    struct sysdb_attrs **objects;
ced1f5
+    size_t count;
ced1f5
+
ced1f5
+    const char *found_domain_name;
ced1f5
+};
ced1f5
+
ced1f5
+struct tevent_req *
ced1f5
+ad_get_account_domain_send(TALLOC_CTX *mem_ctx,
ced1f5
+                           struct ad_id_ctx *id_ctx,
ced1f5
+                           struct dp_get_acct_domain_data *data,
ced1f5
+                           struct dp_req_params *params)
ced1f5
+{
ced1f5
+    struct ad_get_account_domain_state *state;
ced1f5
+    struct tevent_req *req;
ced1f5
+    errno_t ret;
ced1f5
+    bool use_id_mapping;
ced1f5
+
ced1f5
+    req = tevent_req_create(mem_ctx, &state,
ced1f5
+                            struct ad_get_account_domain_state);
ced1f5
+    if (req == NULL) {
ced1f5
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
ced1f5
+        return NULL;
ced1f5
+    }
ced1f5
+    state->ev = params->ev;
ced1f5
+    state->id_ctx = id_ctx;
ced1f5
+    state->sdap_id_ctx = id_ctx->sdap_id_ctx;
ced1f5
+    state->entry_type = data->entry_type & BE_REQ_TYPE_MASK;
ced1f5
+    state->filter_type = data->filter_type;
ced1f5
+    state->attrs = talloc_array(state, const char *, 2);
ced1f5
+    if (state->attrs == NULL) {
ced1f5
+        ret = ENOMEM;
ced1f5
+        goto immediately;
ced1f5
+    }
ced1f5
+    state->attrs[0] = "objectclass";
ced1f5
+    state->attrs[1] = NULL;
ced1f5
+
ced1f5
+    if (params->be_ctx->domain->mpg == true
ced1f5
+            || state->entry_type == BE_REQ_USER_AND_GROUP) {
ced1f5
+        state->twopass = true;
ced1f5
+        if (state->entry_type == BE_REQ_USER_AND_GROUP) {
ced1f5
+            state->entry_type = BE_REQ_GROUP;
ced1f5
+        }
ced1f5
+    }
ced1f5
+
ced1f5
+    /* The get-account-domain request only works with GC */
ced1f5
+    if (dp_opt_get_bool(id_ctx->ad_options->basic, AD_ENABLE_GC) == false) {
ced1f5
+        DEBUG(SSSDBG_CONF_SETTINGS,
ced1f5
+              "Global catalog support is not enabled, "
ced1f5
+              "cannot locate the account domain\n");
ced1f5
+        ret = ERR_GET_ACCT_DOM_NOT_SUPPORTED;
ced1f5
+        goto immediately;
ced1f5
+    }
ced1f5
+
ced1f5
+    state->sdom = sdap_domain_get(id_ctx->sdap_id_ctx->opts,
ced1f5
+                                  params->be_ctx->domain);
ced1f5
+    if (state->sdom == NULL) {
ced1f5
+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot find sdap_domain\n");
ced1f5
+        ret = EIO;
ced1f5
+        goto immediately;
ced1f5
+    }
ced1f5
+
ced1f5
+    /* Currently we only support locating the account domain
ced1f5
+     * if ID mapping is disabled. With ID mapping enabled, we can
ced1f5
+     * already shortcut the 'real' ID request
ced1f5
+     */
ced1f5
+    use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
ced1f5
+                                        state->sdap_id_ctx->opts->idmap_ctx,
ced1f5
+                                        state->sdom->dom->name,
ced1f5
+                                        state->sdom->dom->domain_id);
ced1f5
+    if (use_id_mapping == true) {
ced1f5
+        DEBUG(SSSDBG_CONF_SETTINGS,
ced1f5
+              "No point in locating domain with GC if ID-mapping "
ced1f5
+              "is enabled\n");
ced1f5
+        ret = ERR_GET_ACCT_DOM_NOT_SUPPORTED;
ced1f5
+        goto immediately;
ced1f5
+    }
ced1f5
+
ced1f5
+    ret = sss_filter_sanitize(state, data->filter_value, &state->clean_filter);
ced1f5
+    if (ret != EOK) {
ced1f5
+        DEBUG(SSSDBG_OP_FAILURE,
ced1f5
+              "Cannot sanitize filter [%d]: %s\n", ret, sss_strerror(ret));
ced1f5
+        goto immediately;
ced1f5
+    }
ced1f5
+
ced1f5
+    ret = ad_get_account_domain_prepare_search(req);
ced1f5
+    if (ret != EOK) {
ced1f5
+        goto immediately;
ced1f5
+    }
ced1f5
+
ced1f5
+    /* FIXME - should gc_ctx always default to ignore_offline on creation
ced1f5
+     * time rather than setting the flag on first use?
ced1f5
+     */
ced1f5
+    id_ctx->gc_ctx->ignore_mark_offline = true;
ced1f5
+    state->op = sdap_id_op_create(state, id_ctx->gc_ctx->conn_cache);
ced1f5
+    if (state->op == NULL) {
ced1f5
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed\n");
ced1f5
+        ret = ENOMEM;
ced1f5
+        goto immediately;
ced1f5
+    }
ced1f5
+
ced1f5
+    ret = ad_get_account_domain_connect_retry(req);
ced1f5
+    if (ret != EOK) {
ced1f5
+        DEBUG(SSSDBG_OP_FAILURE, "Connection error");
ced1f5
+        goto immediately;
ced1f5
+    }
ced1f5
+
ced1f5
+    return req;
ced1f5
+
ced1f5
+immediately:
ced1f5
+    dp_reply_std_set(&state->reply, DP_ERR_DECIDE, ret, NULL);
ced1f5
+
ced1f5
+    /* TODO For backward compatibility we always return EOK to DP now. */
ced1f5
+    tevent_req_done(req);
ced1f5
+    tevent_req_post(req, params->ev);
ced1f5
+
ced1f5
+    return req;
ced1f5
+}
ced1f5
+
ced1f5
+static errno_t ad_get_account_domain_prepare_search(struct tevent_req *req)
ced1f5
+{
ced1f5
+    struct ad_get_account_domain_state *state = tevent_req_data(req,
ced1f5
+                                          struct ad_get_account_domain_state);
ced1f5
+    const char *attr_name = NULL;
ced1f5
+    const char *objectclass = NULL;
ced1f5
+
ced1f5
+    switch (state->entry_type) {
ced1f5
+    case BE_REQ_USER:
ced1f5
+        state->search_bases = state->sdom->user_search_bases;
ced1f5
+        attr_name = state->sdap_id_ctx->opts->user_map[SDAP_AT_USER_UID].name;
ced1f5
+        objectclass = state->sdap_id_ctx->opts->user_map[SDAP_OC_USER].name;
ced1f5
+        break;
ced1f5
+    case BE_REQ_GROUP:
ced1f5
+        state->search_bases = state->sdom->group_search_bases;
ced1f5
+        attr_name = state->sdap_id_ctx->opts->group_map[SDAP_AT_GROUP_GID].name;
ced1f5
+        objectclass = state->sdap_id_ctx->opts->group_map[SDAP_OC_GROUP].name;
ced1f5
+        break;
ced1f5
+    default:
ced1f5
+        DEBUG(SSSDBG_OP_FAILURE,
ced1f5
+              "Unsupported request type %X\n",
ced1f5
+              state->entry_type & BE_REQ_TYPE_MASK);
ced1f5
+        return EINVAL;
ced1f5
+    }
ced1f5
+
ced1f5
+    switch (state->filter_type) {
ced1f5
+    case BE_FILTER_IDNUM:
ced1f5
+        break;
ced1f5
+    default:
ced1f5
+        DEBUG(SSSDBG_OP_FAILURE,
ced1f5
+              "Unsupported filter type %X\n", state->filter_type);
ced1f5
+        return EINVAL;
ced1f5
+    }
ced1f5
+
ced1f5
+    talloc_zfree(state->base_filter);
ced1f5
+    state->base_filter = talloc_asprintf(state,
ced1f5
+                                         "(&(%s=%s)(objectclass=%s))",
ced1f5
+                                         attr_name,
ced1f5
+                                         state->clean_filter,
ced1f5
+                                         objectclass);
ced1f5
+    if (state->base_filter == NULL) {
ced1f5
+        return ENOMEM;
ced1f5
+    }
ced1f5
+
ced1f5
+    return EOK;
ced1f5
+}
ced1f5
+
ced1f5
+static errno_t ad_get_account_domain_connect_retry(struct tevent_req *req)
ced1f5
+{
ced1f5
+    struct ad_get_account_domain_state *state = tevent_req_data(req,
ced1f5
+                                          struct ad_get_account_domain_state);
ced1f5
+    struct tevent_req *subreq;
ced1f5
+    errno_t ret;
ced1f5
+
ced1f5
+    subreq = sdap_id_op_connect_send(state->op, state, &ret;;
ced1f5
+    if (subreq == NULL) {
ced1f5
+        return ENOMEM;
ced1f5
+    }
ced1f5
+
ced1f5
+    tevent_req_set_callback(subreq, ad_get_account_domain_connect_done, req);
ced1f5
+    return ret;
ced1f5
+}
ced1f5
+
ced1f5
+static void ad_get_account_domain_connect_done(struct tevent_req *subreq)
ced1f5
+{
ced1f5
+    struct tevent_req *req = tevent_req_callback_data(subreq,
ced1f5
+                                                      struct tevent_req);
ced1f5
+    struct ad_get_account_domain_state *state = tevent_req_data(req,
ced1f5
+                                          struct ad_get_account_domain_state);
ced1f5
+    int dp_error = DP_ERR_FATAL;
ced1f5
+    errno_t ret;
ced1f5
+
ced1f5
+    ret = sdap_id_op_connect_recv(subreq, &dp_error);
ced1f5
+    talloc_zfree(subreq);
ced1f5
+
ced1f5
+    if (ret != EOK) {
ced1f5
+        state->dp_error = dp_error;
ced1f5
+        tevent_req_error(req, ret);
ced1f5
+        return;
ced1f5
+    }
ced1f5
+
ced1f5
+    /* If POSIX attributes have been requested with an AD server and we
ced1f5
+     * have no idea about POSIX attributes support, run a one-time check
ced1f5
+     */
ced1f5
+    if (state->sdap_id_ctx->srv_opts &&
ced1f5
+        state->sdap_id_ctx->srv_opts->posix_checked == false) {
ced1f5
+        subreq = sdap_gc_posix_check_send(state,
ced1f5
+                                          state->ev,
ced1f5
+                                          state->sdap_id_ctx->opts,
ced1f5
+                                          sdap_id_op_handle(state->op),
ced1f5
+                                          dp_opt_get_int(
ced1f5
+                                              state->sdap_id_ctx->opts->basic,
ced1f5
+                                              SDAP_SEARCH_TIMEOUT));
ced1f5
+        if (subreq == NULL) {
ced1f5
+            tevent_req_error(req, ENOMEM);
ced1f5
+            return;
ced1f5
+        }
ced1f5
+        tevent_req_set_callback(subreq, ad_get_account_domain_posix_check_done, req);
ced1f5
+        return;
ced1f5
+    }
ced1f5
+
ced1f5
+    ad_get_account_domain_search(req);
ced1f5
+}
ced1f5
+
ced1f5
+static void ad_get_account_domain_posix_check_done(struct tevent_req *subreq)
ced1f5
+{
ced1f5
+    struct tevent_req *req = tevent_req_callback_data(subreq,
ced1f5
+                                                      struct tevent_req);
ced1f5
+    struct ad_get_account_domain_state *state = tevent_req_data(req,
ced1f5
+                                          struct ad_get_account_domain_state);
ced1f5
+    int dp_error = DP_ERR_FATAL;
ced1f5
+    bool has_posix;
ced1f5
+    errno_t ret;
ced1f5
+    errno_t ret2;
ced1f5
+
ced1f5
+    ret = sdap_gc_posix_check_recv(subreq, &has_posix);
ced1f5
+    talloc_zfree(subreq);
ced1f5
+    if (ret != EOK) {
ced1f5
+        /* We can only finish the id_op on error as the connection
ced1f5
+         * is re-used by the real search
ced1f5
+         */
ced1f5
+        ret2 = sdap_id_op_done(state->op, ret, &dp_error);
ced1f5
+        if (dp_error == DP_ERR_OK && ret2 != EOK) {
ced1f5
+            /* retry */
ced1f5
+            ret = ad_get_account_domain_connect_retry(req);
ced1f5
+            if (ret != EOK) {
ced1f5
+                tevent_req_error(req, ret);
ced1f5
+            }
ced1f5
+            return;
ced1f5
+        }
ced1f5
+
ced1f5
+        tevent_req_error(req, ret);
ced1f5
+        return;
ced1f5
+    }
ced1f5
+
ced1f5
+    state->sdap_id_ctx->srv_opts->posix_checked = true;
ced1f5
+
ced1f5
+    /*
ced1f5
+     * If the GC has no POSIX attributes, there is nothing we can do.
ced1f5
+     * Return an error and let the responders disable the functionality
ced1f5
+     * from now on.
ced1f5
+     */
ced1f5
+    if (has_posix == false) {
ced1f5
+        DEBUG(SSSDBG_CONF_SETTINGS,
ced1f5
+              "The Global Catalog has no POSIX attributes\n");
ced1f5
+
ced1f5
+        disable_gc(state->id_ctx->ad_options);
ced1f5
+        dp_reply_std_set(&state->reply,
ced1f5
+                         DP_ERR_DECIDE, ERR_GET_ACCT_DOM_NOT_SUPPORTED,
ced1f5
+                         NULL);
ced1f5
+        tevent_req_done(req);
ced1f5
+        return;
ced1f5
+    }
ced1f5
+
ced1f5
+    ad_get_account_domain_search(req);
ced1f5
+}
ced1f5
+
ced1f5
+static void ad_get_account_domain_search(struct tevent_req *req)
ced1f5
+{
ced1f5
+    struct ad_get_account_domain_state *state = tevent_req_data(req,
ced1f5
+                                          struct ad_get_account_domain_state);
ced1f5
+    struct tevent_req *subreq;
ced1f5
+
ced1f5
+    talloc_zfree(state->filter);
ced1f5
+    state->filter = sdap_combine_filters(state, state->base_filter,
ced1f5
+                        state->search_bases[state->base_iter]->filter);
ced1f5
+    if (state->filter == NULL) {
ced1f5
+        tevent_req_error(req, ENOMEM);
ced1f5
+        return;
ced1f5
+    }
ced1f5
+
ced1f5
+    subreq = sdap_get_generic_send(state, state->ev, state->sdap_id_ctx->opts,
ced1f5
+                                   sdap_id_op_handle(state->op),
ced1f5
+                                   "",
ced1f5
+                                   LDAP_SCOPE_SUBTREE,
ced1f5
+                                   state->filter,
ced1f5
+                                   state->attrs, NULL, 0,
ced1f5
+                                   dp_opt_get_int(state->sdap_id_ctx->opts->basic,
ced1f5
+                                                  SDAP_SEARCH_TIMEOUT),
ced1f5
+                                   false);
ced1f5
+
ced1f5
+    if (subreq == NULL) {
ced1f5
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
ced1f5
+        tevent_req_error(req, EIO);
ced1f5
+        return;
ced1f5
+    }
ced1f5
+
ced1f5
+    tevent_req_set_callback(subreq, ad_get_account_domain_search_done, req);
ced1f5
+}
ced1f5
+
ced1f5
+static void ad_get_account_domain_search_done(struct tevent_req *subreq)
ced1f5
+{
ced1f5
+    struct tevent_req *req = tevent_req_callback_data(subreq,
ced1f5
+                                                      struct tevent_req);
ced1f5
+    struct ad_get_account_domain_state *state = tevent_req_data(req,
ced1f5
+                                          struct ad_get_account_domain_state);
ced1f5
+    size_t count;
ced1f5
+    struct sysdb_attrs **objects;
ced1f5
+    errno_t ret;
ced1f5
+
ced1f5
+    ret = sdap_get_generic_recv(subreq, state,
ced1f5
+                                &count, &objects);
ced1f5
+    talloc_zfree(subreq);
ced1f5
+    if (ret) {
ced1f5
+        tevent_req_error(req, ret);
ced1f5
+        return;
ced1f5
+    }
ced1f5
+
ced1f5
+    DEBUG(SSSDBG_TRACE_FUNC,
ced1f5
+          "Search returned %zu results.\n", count);
ced1f5
+
ced1f5
+    if (count > 0) {
ced1f5
+        size_t copied;
ced1f5
+
ced1f5
+        state->objects =
ced1f5
+                talloc_realloc(state,
ced1f5
+                               state->objects,
ced1f5
+                               struct sysdb_attrs *,
ced1f5
+                               state->count + count + 1);
ced1f5
+        if (!state->objects) {
ced1f5
+            tevent_req_error(req, ENOMEM);
ced1f5
+            return;
ced1f5
+        }
ced1f5
+
ced1f5
+        copied = sdap_steal_objects_in_dom(state->sdap_id_ctx->opts,
ced1f5
+                                           state->objects,
ced1f5
+                                           state->count,
ced1f5
+                                           NULL,
ced1f5
+                                           objects, count,
ced1f5
+                                           false);
ced1f5
+
ced1f5
+        state->count += copied;
ced1f5
+        state->objects[state->count] = NULL;
ced1f5
+    }
ced1f5
+
ced1f5
+    /* Even though we search with an empty search base (=across all domains)
ced1f5
+     * the reason we iterate over search bases is that the search bases can
ced1f5
+     * also contain a filter which might restrict the IDs we find
ced1f5
+     */
ced1f5
+    state->base_iter++;
ced1f5
+    if (state->search_bases[state->base_iter]) {
ced1f5
+        /* There are more search bases to try */
ced1f5
+        ad_get_account_domain_search(req);
ced1f5
+        return;
ced1f5
+    }
ced1f5
+
ced1f5
+    /* No more searches, evaluate results */
ced1f5
+    ad_get_account_domain_evaluate(req);
ced1f5
+}
ced1f5
+
ced1f5
+static void ad_get_account_domain_evaluate(struct tevent_req *req)
ced1f5
+{
ced1f5
+    struct ad_get_account_domain_state *state = tevent_req_data(req,
ced1f5
+                                          struct ad_get_account_domain_state);
ced1f5
+    struct sss_domain_info *obj_dom;
ced1f5
+    errno_t ret;
ced1f5
+
ced1f5
+    if (state->count == 0) {
ced1f5
+        if (state->twopass
ced1f5
+                && state->entry_type != BE_REQ_USER) {
ced1f5
+            DEBUG(SSSDBG_TRACE_FUNC, "Retrying search\n");
ced1f5
+
ced1f5
+            state->entry_type = BE_REQ_USER;
ced1f5
+            state->base_iter = 0;
ced1f5
+            ret = ad_get_account_domain_prepare_search(req);
ced1f5
+            if (ret != EOK) {
ced1f5
+                DEBUG(SSSDBG_OP_FAILURE, "Cannot retry search\n");
ced1f5
+                tevent_req_error(req, ret);
ced1f5
+                return;
ced1f5
+            }
ced1f5
+
ced1f5
+            ad_get_account_domain_search(req);
ced1f5
+            return;
ced1f5
+        }
ced1f5
+
ced1f5
+        DEBUG(SSSDBG_TRACE_FUNC, "Not found\n");
ced1f5
+        dp_reply_std_set(&state->reply, DP_ERR_DECIDE, ERR_NOT_FOUND, NULL);
ced1f5
+        tevent_req_done(req);
ced1f5
+        return;
ced1f5
+    } else if (state->count > 1) {
ced1f5
+        /* FIXME: If more than one entry was found, return error for now
ced1f5
+         * as the account requsts have no way of returning multiple
ced1f5
+         * messages back until we switch to the rdp_* requests
ced1f5
+         * from the responder side
ced1f5
+         */
ced1f5
+        DEBUG(SSSDBG_OP_FAILURE, "Multiple entries found, error!\n");
ced1f5
+        dp_reply_std_set(&state->reply, DP_ERR_DECIDE, ERANGE, NULL);
ced1f5
+        tevent_req_done(req);
ced1f5
+        return;
ced1f5
+    }
ced1f5
+
ced1f5
+    /* Exactly one entry was found */
ced1f5
+    obj_dom = sdap_get_object_domain(state->sdap_id_ctx->opts,
ced1f5
+                                     state->objects[0],
ced1f5
+                                     state->sdom->dom);
ced1f5
+    if (obj_dom == NULL) {
ced1f5
+        DEBUG(SSSDBG_OP_FAILURE,
ced1f5
+              "Could not match entry with domain!\n");
ced1f5
+        dp_reply_std_set(&state->reply, DP_ERR_DECIDE, ERR_NOT_FOUND, NULL);
ced1f5
+        tevent_req_done(req);
ced1f5
+        return;
ced1f5
+    }
ced1f5
+
ced1f5
+    DEBUG(SSSDBG_TRACE_INTERNAL,
ced1f5
+          "Found object in domain %s\n", obj_dom->name);
ced1f5
+    dp_reply_std_set(&state->reply, DP_ERR_DECIDE, EOK, obj_dom->name);
ced1f5
+    tevent_req_done(req);
ced1f5
+}
ced1f5
+
ced1f5
+errno_t ad_get_account_domain_recv(TALLOC_CTX *mem_ctx,
ced1f5
+                                   struct tevent_req *req,
ced1f5
+                                   struct dp_reply_std *data)
ced1f5
+{
ced1f5
+    struct ad_get_account_domain_state *state = NULL;
ced1f5
+
ced1f5
+    state = tevent_req_data(req, struct ad_get_account_domain_state);
ced1f5
+
ced1f5
+    TEVENT_REQ_RETURN_ON_ERROR(req);
ced1f5
+
ced1f5
+    *data = state->reply;
ced1f5
+
ced1f5
+    return EOK;
ced1f5
+}
ced1f5
diff --git a/src/providers/ad/ad_id.h b/src/providers/ad/ad_id.h
ced1f5
index 145fdc8f2dfdeda5a17b0ce5892a547da934c244..5154393c5f125f472c92155006aac14d04bbca1a 100644
ced1f5
--- a/src/providers/ad/ad_id.h
ced1f5
+++ b/src/providers/ad/ad_id.h
ced1f5
@@ -54,4 +54,14 @@ ad_enumeration_send(TALLOC_CTX *mem_ctx,
ced1f5
 errno_t
ced1f5
 ad_enumeration_recv(struct tevent_req *req);
ced1f5
 
ced1f5
+struct tevent_req *
ced1f5
+ad_get_account_domain_send(TALLOC_CTX *mem_ctx,
ced1f5
+                           struct ad_id_ctx *id_ctx,
ced1f5
+                           struct dp_get_acct_domain_data *data,
ced1f5
+                           struct dp_req_params *params);
ced1f5
+
ced1f5
+errno_t ad_get_account_domain_recv(TALLOC_CTX *mem_ctx,
ced1f5
+                                   struct tevent_req *req,
ced1f5
+                                   struct dp_reply_std *data);
ced1f5
+
ced1f5
 #endif /* AD_ID_H_ */
ced1f5
diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
ced1f5
index 7efb6aa71cbd2551422c87e0b0c5c1fe91390375..22a3ecf7e5a020da88b6c9164f5999d13a9aa5e3 100644
ced1f5
--- a/src/providers/ad/ad_init.c
ced1f5
+++ b/src/providers/ad/ad_init.c
ced1f5
@@ -511,8 +511,8 @@ errno_t sssm_ad_id_init(TALLOC_CTX *mem_ctx,
ced1f5
                   struct sdap_id_ctx, void, struct dp_reply_std);
ced1f5
 
ced1f5
     dp_set_method(dp_methods, DPM_ACCT_DOMAIN_HANDLER,
ced1f5
-                  default_account_domain_send, default_account_domain_recv, NULL,
ced1f5
-                  void, struct dp_get_acct_domain_data, struct dp_reply_std);
ced1f5
+                  ad_get_account_domain_send, ad_get_account_domain_recv, id_ctx,
ced1f5
+                  struct ad_id_ctx, struct dp_get_acct_domain_data, struct dp_reply_std);
ced1f5
 
ced1f5
     return EOK;
ced1f5
 }
ced1f5
-- 
ced1f5
2.14.3
ced1f5