>From cdea42772d615736c576c45ad3510a53d73ec887 Mon Sep 17 00:00:00 2001
From: Simo Sorce <ssorce@redhat.com>
Date: Thu, 29 Oct 2009 15:02:41 -0400
Subject: [PATCH] Add support to get rootDSE from the LDAP server.

Also fic sdap_get_generic_send() to be a bit more "generic" :-)
Also figs bugs within it.

This patch allow us 2 good things.
A) we check that the server effectively supports GSSAPI auth before we try to
use it.
B) against IPA it substantially cuts delays when the server is offline because
it uses a 5 second async timeout on the connection and doesn't try to do a
slow synchronous kinit+sasl_bind if the server is not even available.
---
 server/providers/ldap/ldap_common.h |    3 +
 server/providers/ldap/ldap_id.c     |   40 +++-
 server/providers/ldap/sdap.c        |   40 ++++-
 server/providers/ldap/sdap.h        |    3 +
 server/providers/ldap/sdap_async.c  |  407 ++++++++++++++++++++++++++--------
 server/providers/ldap/sdap_async.h  |   26 ++-
 6 files changed, 398 insertions(+), 121 deletions(-)

diff --git a/server/providers/ldap/ldap_common.h b/server/providers/ldap/ldap_common.h
index a6e77e9..c9d8578 100644
--- a/server/providers/ldap/ldap_common.h
+++ b/server/providers/ldap/ldap_common.h
@@ -30,6 +30,9 @@ struct sdap_id_ctx {
 
     struct sdap_options *opts;
 
+    /* what rootDSE returns */
+    struct sysdb_attrs *rootDSE;
+
     /* global sdap handler */
     struct sdap_handle *gsh;
 
diff --git a/server/providers/ldap/ldap_id.c b/server/providers/ldap/ldap_id.c
index 9fd0d4f..7e69c3d 100644
--- a/server/providers/ldap/ldap_id.c
+++ b/server/providers/ldap/ldap_id.c
@@ -147,7 +147,7 @@ static struct tevent_req *users_get_send(TALLOC_CTX *memctx,
 
         /* FIXME: add option to decide if tls should be used
          * or SASL/GSSAPI, etc ... */
-        subreq = sdap_cli_connect_send(state, ev, ctx->opts);
+        subreq = sdap_cli_connect_send(state, ev, ctx->opts, &ctx->rootDSE);
         if (!subreq) {
             ret = ENOMEM;
             goto fail;
@@ -185,9 +185,13 @@ static void users_get_connect_done(struct tevent_req *subreq)
                                                      struct users_get_state);
     int ret;
 
-    ret = sdap_cli_connect_recv(subreq, state->ctx, &state->ctx->gsh);
+    ret = sdap_cli_connect_recv(subreq, state->ctx,
+                                &state->ctx->gsh, &state->ctx->rootDSE);
     talloc_zfree(subreq);
     if (ret) {
+        if (ret == ENOTSUP) {
+            DEBUG(0, ("Authentication mechanism not Supported by server"));
+        }
         tevent_req_error(req, ret);
         return;
     }
@@ -314,7 +318,7 @@ static struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
 
         /* FIXME: add option to decide if tls should be used
          * or SASL/GSSAPI, etc ... */
-        subreq = sdap_cli_connect_send(state, ev, ctx->opts);
+        subreq = sdap_cli_connect_send(state, ev, ctx->opts, &ctx->rootDSE);
         if (!subreq) {
             ret = ENOMEM;
             goto fail;
@@ -352,9 +356,13 @@ static void groups_get_connect_done(struct tevent_req *subreq)
                                                      struct groups_get_state);
     int ret;
 
-    ret = sdap_cli_connect_recv(subreq, state->ctx, &state->ctx->gsh);
+    ret = sdap_cli_connect_recv(subreq, state->ctx,
+                                &state->ctx->gsh, &state->ctx->rootDSE);
     talloc_zfree(subreq);
     if (ret) {
+        if (ret == ENOTSUP) {
+            DEBUG(0, ("Authentication mechanism not Supported by server"));
+        }
         tevent_req_error(req, ret);
         return;
     }
@@ -456,7 +464,7 @@ static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx,
 
         /* FIXME: add option to decide if tls should be used
          * or SASL/GSSAPI, etc ... */
-        subreq = sdap_cli_connect_send(state, ev, ctx->opts);
+        subreq = sdap_cli_connect_send(state, ev, ctx->opts, &ctx->rootDSE);
         if (!subreq) {
             ret = ENOMEM;
             goto fail;
@@ -494,9 +502,13 @@ static void groups_by_user_connect_done(struct tevent_req *subreq)
                                                      struct groups_by_user_state);
     int ret;
 
-    ret = sdap_cli_connect_recv(subreq, state->ctx, &state->ctx->gsh);
+    ret = sdap_cli_connect_recv(subreq, state->ctx,
+                                &state->ctx->gsh, &state->ctx->rootDSE);
     talloc_zfree(subreq);
     if (ret) {
+        if (ret == ENOTSUP) {
+            DEBUG(0, ("Authentication mechanism not Supported by server"));
+        }
         tevent_req_error(req, ret);
         return;
     }
@@ -912,7 +924,7 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
 
         /* FIXME: add option to decide if tls should be used
          * or SASL/GSSAPI, etc ... */
-        subreq = sdap_cli_connect_send(state, ev, ctx->opts);
+        subreq = sdap_cli_connect_send(state, ev, ctx->opts, &ctx->rootDSE);
         if (!subreq) {
             ret = ENOMEM;
             goto fail;
@@ -951,9 +963,13 @@ static void enum_users_connect_done(struct tevent_req *subreq)
                                                      struct enum_users_state);
     int ret;
 
-    ret = sdap_cli_connect_recv(subreq, state->ctx, &state->ctx->gsh);
+    ret = sdap_cli_connect_recv(subreq, state->ctx,
+                                &state->ctx->gsh, &state->ctx->rootDSE);
     talloc_zfree(subreq);
     if (ret) {
+        if (ret == ENOTSUP) {
+            DEBUG(0, ("Authentication mechanism not Supported by server"));
+        }
         tevent_req_error(req, ret);
         return;
     }
@@ -1059,7 +1075,7 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
 
         /* FIXME: add option to decide if tls should be used
          * or SASL/GSSAPI, etc ... */
-        subreq = sdap_cli_connect_send(state, ev, ctx->opts);
+        subreq = sdap_cli_connect_send(state, ev, ctx->opts, &ctx->rootDSE);
         if (!subreq) {
             ret = ENOMEM;
             goto fail;
@@ -1097,9 +1113,13 @@ static void enum_groups_connect_done(struct tevent_req *subreq)
                                                  struct enum_groups_state);
     int ret;
 
-    ret = sdap_cli_connect_recv(subreq, state->ctx, &state->ctx->gsh);
+    ret = sdap_cli_connect_recv(subreq, state->ctx,
+                                &state->ctx->gsh, &state->ctx->rootDSE);
     talloc_zfree(subreq);
     if (ret) {
+        if (ret == ENOTSUP) {
+            DEBUG(0, ("Authentication mechanism not Supported by server"));
+        }
         tevent_req_error(req, ret);
         return;
     }
diff --git a/server/providers/ldap/sdap.c b/server/providers/ldap/sdap.c
index 9268692..d0ea9ff 100644
--- a/server/providers/ldap/sdap.c
+++ b/server/providers/ldap/sdap.c
@@ -192,9 +192,9 @@ fail:
 }
 
 int sdap_parse_generic_entry(TALLOC_CTX *memctx,
-                                    struct sdap_handle *sh,
-                                    struct sdap_msg *sm,
-                                    struct sysdb_attrs **_attrs)
+                             struct sdap_handle *sh,
+                             struct sdap_msg *sm,
+                             struct sysdb_attrs **_attrs)
 {
     struct sysdb_attrs *attrs;
     BerElement *ber = NULL;
@@ -386,3 +386,37 @@ errno_t setup_tls_config(struct dp_option *basic_opts)
 
     return EOK;
 }
+
+
+bool sdap_rootdse_sasl_mech_is_supported(struct sysdb_attrs *rootdse,
+                                         const char *sasl_mech)
+{
+    struct ldb_message_element *el = NULL;
+    struct ldb_val *val;
+    int i;
+
+    for (i = 0; i < rootdse->num; i++) {
+        if (strcasecmp(rootdse->a[i].name, "supportedSASLMechanisms")) {
+            continue;
+        }
+        el = &rootdse->a[i];
+        break;
+    }
+
+    if (!el) {
+        /* no supported SASL Mechanism at all ? */
+        return false;
+    }
+
+    for (i = 0; i < el->num_values; i++) {
+        val = &el->values[i];
+        if (strncasecmp(sasl_mech, (const char *)val->data, val->length)) {
+            continue;
+        }
+        return true;
+    }
+
+    return false;
+}
+
+
diff --git a/server/providers/ldap/sdap.h b/server/providers/ldap/sdap.h
index f4e5aac..152d46b 100644
--- a/server/providers/ldap/sdap.h
+++ b/server/providers/ldap/sdap.h
@@ -215,4 +215,7 @@ int sdap_get_msg_dn(TALLOC_CTX *memctx, struct sdap_handle *sh,
 
 errno_t setup_tls_config(struct dp_option *basic_opts);
 
+bool sdap_rootdse_sasl_mech_is_supported(struct sysdb_attrs *rootdse,
+                                         const char *sasl_mech);
+
 #endif /* _SDAP_H_ */
diff --git a/server/providers/ldap/sdap_async.c b/server/providers/ldap/sdap_async.c
index 28e4fa4..ae6806f 100644
--- a/server/providers/ldap/sdap_async.c
+++ b/server/providers/ldap/sdap_async.c
@@ -2276,7 +2276,6 @@ static void sdap_get_users_transaction(struct tevent_req *subreq)
     }
     DEBUG(8, ("ldap_search_ext called, msgid = %d\n", msgid));
 
-    /* FIXME: get timeouts from configuration, for now 10 minutes */
     ret = sdap_op_add(state, state->ev, state->sh, msgid,
                       sdap_get_users_done, req,
                       dp_opt_get_int(state->opts->basic,
@@ -3233,22 +3232,127 @@ int sdap_exop_modify_passwd_recv(struct tevent_req *req,
     return EOK;
 }
 
+/* ==Fetch-RootDSE============================================= */
+
+struct sdap_get_rootdse_state {
+    struct tevent_context *ev;
+    struct sdap_options *opts;
+    struct sdap_handle *sh;
+
+    struct sysdb_attrs *rootdse;
+};
+
+static void sdap_get_rootdse_done(struct tevent_req *subreq);
+
+struct tevent_req *sdap_get_rootdse_send(TALLOC_CTX *memctx,
+                                         struct tevent_context *ev,
+                                         struct sdap_options *opts,
+                                         struct sdap_handle *sh)
+{
+    struct tevent_req *req, *subreq;
+    struct sdap_get_rootdse_state *state;
+
+    DEBUG(9, ("Getting rootdse\n"));
+
+    req = tevent_req_create(memctx, &state, struct sdap_get_rootdse_state);
+    if (!req) return NULL;
+
+    state->ev = ev;
+    state->opts = opts;
+    state->sh = sh;
+    state->rootdse = NULL;
+
+    subreq = sdap_get_generic_send(state, ev, opts, sh,
+                                   "", LDAP_SCOPE_BASE,
+                                   "(objectclass=*)", NULL);
+    if (!subreq) {
+        talloc_zfree(req);
+        return NULL;
+    }
+    tevent_req_set_callback(subreq, sdap_get_rootdse_done, req);
+
+    return req;
+}
+
+static void sdap_get_rootdse_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct sdap_get_rootdse_state *state = tevent_req_data(req,
+                                             struct sdap_get_rootdse_state);
+    struct sysdb_attrs **results;
+    size_t num_results;
+    int ret;
+
+    ret = sdap_get_generic_recv(subreq, state, &num_results, &results);
+    talloc_zfree(subreq);
+    if (ret) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    if (num_results == 0 || !results) {
+        DEBUG(2, ("No RootDSE for server ?!\n"));
+        tevent_req_error(req, ENOENT);
+        return;
+    }
+
+    if (num_results > 1) {
+        DEBUG(2, ("Multiple replies when searching for RootDSE ??\n"));
+        tevent_req_error(req, EIO);
+        return;
+    }
+
+    state->rootdse = talloc_steal(state, results[0]);
+    talloc_zfree(results);
+
+    DEBUG(9, ("Got rootdse\n"));
+
+    tevent_req_done(req);
+}
+
+static int sdap_get_rootdse_recv(struct tevent_req *req,
+                                 TALLOC_CTX *memctx,
+                                 struct sysdb_attrs **rootdse)
+{
+    struct sdap_get_rootdse_state *state = tevent_req_data(req,
+                                             struct sdap_get_rootdse_state);
+    enum tevent_req_state tstate;
+    uint64_t err;
+
+    if (tevent_req_is_error(req, &tstate, &err)) {
+        if (err) return err;
+        return EIO;
+    }
+
+    *rootdse = talloc_steal(memctx, state->rootdse);
+
+    return EOK;
+}
+
 /* ==Client connect============================================ */
 
 struct sdap_cli_connect_state {
     struct tevent_context *ev;
     struct sdap_options *opts;
 
+    struct sysdb_attrs *rootdse;
+    bool use_rootdse;
     struct sdap_handle *sh;
 };
 
 static void sdap_cli_connect_done(struct tevent_req *subreq);
+static void sdap_cli_rootdse_step(struct tevent_req *req);
+static void sdap_cli_rootdse_done(struct tevent_req *subreq);
+static void sdap_cli_kinit_step(struct tevent_req *req);
 static void sdap_cli_kinit_done(struct tevent_req *subreq);
-static void sdap_cli_bind_done(struct tevent_req *subreq);
+static void sdap_cli_auth_step(struct tevent_req *req);
+static void sdap_cli_auth_done(struct tevent_req *subreq);
 
 struct tevent_req *sdap_cli_connect_send(TALLOC_CTX *memctx,
                                          struct tevent_context *ev,
-                                         struct sdap_options *opts)
+                                         struct sdap_options *opts,
+                                         struct sysdb_attrs **rootdse)
 {
     struct tevent_req *req, *subreq;
     struct sdap_cli_connect_state *state;
@@ -3258,6 +3362,13 @@ struct tevent_req *sdap_cli_connect_send(TALLOC_CTX *memctx,
 
     state->ev = ev;
     state->opts = opts;
+    if (rootdse) {
+        state->use_rootdse = true;
+        state->rootdse = *rootdse;
+    } else {
+        state->use_rootdse = false;
+        state->rootdse = NULL;
+    }
 
     subreq = sdap_connect_send(state, ev, opts,
                                dp_opt_get_bool(opts->basic, SDAP_ID_TLS));
@@ -3275,7 +3386,7 @@ static void sdap_cli_connect_done(struct tevent_req *subreq)
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
     struct sdap_cli_connect_state *state = tevent_req_data(req,
-                                                struct sdap_cli_connect_state);
+                                             struct sdap_cli_connect_state);
     const char *sasl_mech;
     int ret;
 
@@ -3286,50 +3397,120 @@ static void sdap_cli_connect_done(struct tevent_req *subreq)
         return;
     }
 
+    if (state->use_rootdse && !state->rootdse) {
+        /* fetch the rootDSE this time */
+        sdap_cli_rootdse_step(req);
+        return;
+    }
+
     sasl_mech = dp_opt_get_string(state->opts->basic, SDAP_SASL_MECH);
+
+    if (state->use_rootdse) {
+        /* check if server claims to support GSSAPI */
+        if (!sdap_rootdse_sasl_mech_is_supported(state->rootdse,
+                                                 sasl_mech)) {
+            tevent_req_error(req, ENOTSUP);
+            return;
+        }
+    }
+
     if (sasl_mech && (strcasecmp(sasl_mech, "GSSAPI") == 0)) {
         if (dp_opt_get_bool(state->opts->basic, SDAP_KRB5_KINIT)) {
-            subreq = sdap_kinit_send(state, state->ev, state->sh,
-                                dp_opt_get_string(state->opts->basic,
-                                                           SDAP_KRB5_KEYTAB),
-                                dp_opt_get_string(state->opts->basic,
-                                                           SDAP_SASL_AUTHID),
-                                dp_opt_get_string(state->opts->basic,
-                                                           SDAP_KRB5_REALM));
-            if (!subreq) {
-                tevent_req_error(req, ENOMEM);
-                return;
-            }
-            tevent_req_set_callback(subreq, sdap_cli_kinit_done, req);
+            sdap_cli_kinit_step(req);
             return;
         }
     }
 
-    subreq = sdap_auth_send(state,
-                            state->ev,
-                            state->sh,
-                            sasl_mech,
-                            dp_opt_get_string(state->opts->basic,
-                                                        SDAP_SASL_AUTHID),
-                            dp_opt_get_string(state->opts->basic,
-                                                    SDAP_DEFAULT_BIND_DN),
-                            dp_opt_get_string(state->opts->basic,
-                                               SDAP_DEFAULT_AUTHTOK_TYPE),
-                            dp_opt_get_blob(state->opts->basic,
-                                                    SDAP_DEFAULT_AUTHTOK));
+    sdap_cli_auth_step(req);
+}
+
+static void sdap_cli_rootdse_step(struct tevent_req *req)
+{
+    struct sdap_cli_connect_state *state = tevent_req_data(req,
+                                             struct sdap_cli_connect_state);
+    struct tevent_req *subreq;
+    int ret;
+
+    subreq = sdap_get_rootdse_send(state, state->ev, state->opts, state->sh);
     if (!subreq) {
         tevent_req_error(req, ENOMEM);
         return;
     }
-    tevent_req_set_callback(subreq, sdap_cli_bind_done, req);
+    tevent_req_set_callback(subreq, sdap_cli_rootdse_done, req);
+
+    if (!state->sh->connected) {
+    /* this rootdse search is performed before we actually do a bind,
+     * so we need to set up the callbacks or we will never get notified
+     * of a reply */
+        state->sh->connected = true;
+        ret = sdap_install_ldap_callbacks(state->sh, state->ev);
+        if (ret) {
+            tevent_req_error(req, ret);
+        }
+    }
 }
 
-static void sdap_cli_kinit_done(struct tevent_req *subreq)
+static void sdap_cli_rootdse_done(struct tevent_req *subreq)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
     struct sdap_cli_connect_state *state = tevent_req_data(req,
-                                                struct sdap_cli_connect_state);
+                                             struct sdap_cli_connect_state);
+    const char *sasl_mech;
+    int ret;
+
+    ret = sdap_get_rootdse_recv(subreq, state, &state->rootdse);
+    talloc_zfree(subreq);
+    if (ret) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    sasl_mech = dp_opt_get_string(state->opts->basic, SDAP_SASL_MECH);
+
+    if (state->use_rootdse) {
+        /* check if server claims to support GSSAPI */
+        if (!sdap_rootdse_sasl_mech_is_supported(state->rootdse,
+                                                 sasl_mech)) {
+            tevent_req_error(req, ENOTSUP);
+            return;
+        }
+    }
+
+    if (sasl_mech && (strcasecmp(sasl_mech, "GSSAPI") == 0)) {
+        if (dp_opt_get_bool(state->opts->basic, SDAP_KRB5_KINIT)) {
+            sdap_cli_kinit_step(req);
+            return;
+        }
+    }
+
+    sdap_cli_auth_step(req);
+}
+
+static void sdap_cli_kinit_step(struct tevent_req *req)
+{
+    struct sdap_cli_connect_state *state = tevent_req_data(req,
+                                             struct sdap_cli_connect_state);
+    struct tevent_req *subreq;
+
+    subreq = sdap_kinit_send(state, state->ev, state->sh,
+                        dp_opt_get_string(state->opts->basic,
+                                                   SDAP_KRB5_KEYTAB),
+                        dp_opt_get_string(state->opts->basic,
+                                                   SDAP_SASL_AUTHID),
+                        dp_opt_get_string(state->opts->basic,
+                                                   SDAP_KRB5_REALM));
+    if (!subreq) {
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+    tevent_req_set_callback(subreq, sdap_cli_kinit_done, req);
+}
+
+static void sdap_cli_kinit_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
     enum sdap_result result;
     int ret;
 
@@ -3344,6 +3525,15 @@ static void sdap_cli_kinit_done(struct tevent_req *subreq)
         return;
     }
 
+    sdap_cli_auth_step(req);
+}
+
+static void sdap_cli_auth_step(struct tevent_req *req)
+{
+    struct sdap_cli_connect_state *state = tevent_req_data(req,
+                                             struct sdap_cli_connect_state);
+    struct tevent_req *subreq;
+
     subreq = sdap_auth_send(state,
                             state->ev,
                             state->sh,
@@ -3361,10 +3551,10 @@ static void sdap_cli_kinit_done(struct tevent_req *subreq)
         tevent_req_error(req, ENOMEM);
         return;
     }
-    tevent_req_set_callback(subreq, sdap_cli_bind_done, req);
+    tevent_req_set_callback(subreq, sdap_cli_auth_done, req);
 }
 
-static void sdap_cli_bind_done(struct tevent_req *subreq)
+static void sdap_cli_auth_done(struct tevent_req *subreq)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
@@ -3385,11 +3575,13 @@ static void sdap_cli_bind_done(struct tevent_req *subreq)
     tevent_req_done(req);
 }
 
-int sdap_cli_connect_recv(struct tevent_req *req, TALLOC_CTX *memctx,
-                          struct sdap_handle **gsh)
+int sdap_cli_connect_recv(struct tevent_req *req,
+                          TALLOC_CTX *memctx,
+                          struct sdap_handle **gsh,
+                          struct sysdb_attrs **rootdse)
 {
     struct sdap_cli_connect_state *state = tevent_req_data(req,
-                                                struct sdap_cli_connect_state);
+                                             struct sdap_cli_connect_state);
     enum tevent_req_state tstate;
     uint64_t err;
 
@@ -3398,10 +3590,28 @@ int sdap_cli_connect_recv(struct tevent_req *req, TALLOC_CTX *memctx,
         return EIO;
     }
 
-    *gsh = talloc_steal(memctx, state->sh);
-    if (!*gsh) {
-        return ENOMEM;
+    if (gsh) {
+        *gsh = talloc_steal(memctx, state->sh);
+        if (!*gsh) {
+            return ENOMEM;
+        }
+    } else {
+        talloc_zfree(state->sh);
     }
+
+    if (rootdse) {
+        if (state->use_rootdse) {
+            *rootdse = talloc_steal(memctx, state->rootdse);
+            if (!*rootdse) {
+                return ENOMEM;
+            }
+        } else {
+            *rootdse = NULL;
+        }
+    } else {
+        talloc_zfree(rootdse);
+    }
+
     return EOK;
 }
 
@@ -3411,35 +3621,33 @@ struct sdap_get_generic_state {
     struct tevent_context *ev;
     struct sdap_options *opts;
     struct sdap_handle *sh;
-    struct sss_domain_info *dom;
-    const char **attrs;
-    const char *filter;
     const char *search_base;
-    struct sysdb_attrs **reply;
-    size_t reply_max;
-    size_t reply_count;
+    int scope;
+    const char *filter;
+    const char **attrs;
 
-    struct sysdb_handle *handle;
     struct sdap_op *op;
+
+    size_t reply_max;
+    size_t reply_count;
+    struct sysdb_attrs **reply;
 };
 
-static errno_t add_to_reply(TALLOC_CTX *memctx,
-                            struct sdap_get_generic_state *state,
+static errno_t add_to_reply(struct sdap_get_generic_state *state,
                             struct sysdb_attrs *msg);
 
 static void sdap_get_generic_done(struct sdap_op *op,
-                                 struct sdap_msg *reply,
-                                 int error, void *pvt);
+                                  struct sdap_msg *reply,
+                                  int error, void *pvt);
 
 struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
-                                       struct tevent_context *ev,
-                                       struct sss_domain_info *dom,
-                                       struct sysdb_ctx *sysdb,
-                                       struct sdap_options *opts,
-                                       struct sdap_handle *sh,
-                                       const char **attrs,
-                                       const char *filter,
-                                       const char *search_base)
+                                         struct tevent_context *ev,
+                                         struct sdap_options *opts,
+                                         struct sdap_handle *sh,
+                                         const char *search_base,
+                                         int scope,
+                                         const char *filter,
+                                         const char **attrs)
 {
     struct tevent_req *req = NULL;
     struct sdap_get_generic_state *state = NULL;
@@ -3452,29 +3660,30 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
 
     state->ev = ev;
     state->opts = opts;
-    state->dom = dom;
     state->sh = sh;
+    state->search_base = search_base;
+    state->scope = scope;
     state->filter = filter;
     state->attrs = attrs;
-    state->search_base = search_base;
-    state->reply = NULL;
+    state->op = NULL;
     state->reply_max = 0;
     state->reply_count = 0;
-    state->op = NULL;
-    state->handle = NULL;
+    state->reply = NULL;
 
     DEBUG(7, ("calling ldap_search_ext with [%s][%s].\n", state->filter,
                                                           state->search_base));
     if (debug_level >= 7) {
         int i;
 
-        for (i = 0; state->attrs[i]; i++) {
-            DEBUG(7, ("Requesting attrs: [%s]\n", state->attrs[i]));
+        if (state->attrs) {
+            for (i = 0; state->attrs[i]; i++) {
+                DEBUG(7, ("Requesting attrs: [%s]\n", state->attrs[i]));
+            }
         }
     }
 
     lret = ldap_search_ext(state->sh->ldap, state->search_base,
-                           LDAP_SCOPE_SUBTREE, state->filter,
+                           state->scope, state->filter,
                            discard_const(state->attrs),
                            false, NULL, NULL, NULL, 0, &msgid);
     if (lret != LDAP_SUCCESS) {
@@ -3486,7 +3695,8 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
 
     ret = sdap_op_add(state, state->ev, state->sh, msgid,
                       sdap_get_generic_done, req,
-                      dp_opt_get_int(state->opts->basic,SDAP_SEARCH_TIMEOUT),
+                      dp_opt_get_int(state->opts->basic,
+                                     SDAP_SEARCH_TIMEOUT),
                       &state->op);
     if (ret != EOK) {
         DEBUG(1, ("Failed to set up operation!\n"));
@@ -3509,8 +3719,10 @@ static void sdap_get_generic_done(struct sdap_op *op,
     struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
     struct sdap_get_generic_state *state = tevent_req_data(req,
                                             struct sdap_get_generic_state);
-    int ret;
     struct sysdb_attrs *attrs;
+    char *errmsg;
+    int result;
+    int ret;
 
     if (error) {
         tevent_req_error(req, error);
@@ -3534,7 +3746,7 @@ static void sdap_get_generic_done(struct sdap_op *op,
             return;
         }
 
-        ret = add_to_reply(state, state, attrs);
+        ret = add_to_reply(state, attrs);
         if (ret != EOK) {
             DEBUG(1, ("add_to_reply failed.\n"));
             tevent_req_error(req, ret);
@@ -3543,18 +3755,46 @@ static void sdap_get_generic_done(struct sdap_op *op,
 
         sdap_unlock_next_reply(state->op);
         break;
+
     case LDAP_RES_SEARCH_RESULT:
+        ret = ldap_parse_result(state->sh->ldap, reply->msg,
+                                &result, NULL, &errmsg, NULL, NULL, 0);
+        if (ret != LDAP_SUCCESS) {
+            DEBUG(2, ("ldap_parse_result failed (%d)\n", state->op->msgid));
+            tevent_req_error(req, EIO);
+            return;
+        }
+
+        DEBUG(3, ("Search result: %s(%d), %s\n",
+                  ldap_err2string(result), result, errmsg));
+
         tevent_req_done(req);
         return;
 
-        break;
     default:
         /* what is going on here !? */
         tevent_req_error(req, EIO);
         return;
     }
+}
 
-    return;
+static errno_t add_to_reply(struct sdap_get_generic_state *state,
+                            struct sysdb_attrs *msg)
+{
+    if (state->reply == NULL || state->reply_max == state->reply_count) {
+        state->reply_max += REPLY_REALLOC_INCREMENT;
+        state->reply = talloc_realloc(state, state->reply,
+                                      struct sysdb_attrs *,
+                                      state->reply_max);
+        if (state->reply == NULL) {
+            DEBUG(1, ("talloc_realloc failed.\n"));
+            return ENOMEM;
+        }
+    }
+
+    state->reply[state->reply_count++] = talloc_steal(state->reply, msg);
+
+    return EOK;
 }
 
 int sdap_get_generic_recv(struct tevent_req *req,
@@ -3566,7 +3806,6 @@ int sdap_get_generic_recv(struct tevent_req *req,
                                             struct sdap_get_generic_state);
     enum tevent_req_state tstate;
     uint64_t err;
-    int i;
 
     if (tevent_req_is_error(req, &tstate, &err)) {
         if (err) return err;
@@ -3575,31 +3814,7 @@ int sdap_get_generic_recv(struct tevent_req *req,
 
     *reply_count = state->reply_count;
     *reply = talloc_steal(mem_ctx, state->reply);
-    for (i = 0; i < state->reply_count; i++) {
-        talloc_steal(mem_ctx, state->reply[i]);
-    }
 
     return EOK;
 }
 
-static errno_t add_to_reply(TALLOC_CTX *memctx,
-                            struct sdap_get_generic_state *state,
-                            struct sysdb_attrs *msg)
-{
-    struct sysdb_attrs **dummy;
-
-    if (state->reply == NULL || state->reply_max == state->reply_count) {
-        state->reply_max += REPLY_REALLOC_INCREMENT;
-        dummy = talloc_realloc(memctx, state->reply, struct sysdb_attrs *,
-                               state->reply_max);
-        if (dummy == NULL) {
-            DEBUG(1, ("talloc_realloc failed.\n"));
-            return ENOMEM;
-        }
-        state->reply = dummy;
-    }
-
-    state->reply[state->reply_count++] = talloc_steal(state->reply, msg);
-
-    return EOK;
-}
diff --git a/server/providers/ldap/sdap_async.h b/server/providers/ldap/sdap_async.h
index 8410f3d..911933a 100644
--- a/server/providers/ldap/sdap_async.h
+++ b/server/providers/ldap/sdap_async.h
@@ -91,20 +91,22 @@ struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx,
 int sdap_exop_modify_passwd_recv(struct tevent_req *req, enum sdap_result *result);
 
 struct tevent_req *sdap_cli_connect_send(TALLOC_CTX *memctx,
-                                                struct tevent_context *ev,
-                                                struct sdap_options *opts);
-int sdap_cli_connect_recv(struct tevent_req *req, TALLOC_CTX *memctx,
-                                 struct sdap_handle **gsh);
+                                         struct tevent_context *ev,
+                                         struct sdap_options *opts,
+                                         struct sysdb_attrs **rootdse);
+int sdap_cli_connect_recv(struct tevent_req *req,
+                          TALLOC_CTX *memctx,
+                          struct sdap_handle **gsh,
+                          struct sysdb_attrs **rootdse);
 
 struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
-                                       struct tevent_context *ev,
-                                       struct sss_domain_info *dom,
-                                       struct sysdb_ctx *sysdb,
-                                       struct sdap_options *opts,
-                                       struct sdap_handle *sh,
-                                       const char **attrs,
-                                       const char *filter,
-                                       const char *search_base);
+                                         struct tevent_context *ev,
+                                         struct sdap_options *opts,
+                                         struct sdap_handle *sh,
+                                         const char *search_base,
+                                         int scope,
+                                         const char *filter,
+                                         const char **attrs);
 int sdap_get_generic_recv(struct tevent_req *req,
                          TALLOC_CTX *mem_ctx, size_t *reply_count,
                          struct sysdb_attrs ***reply_list);
-- 
1.6.2.5

