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

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