[SSSD] [PATCH] Look up originalDN if not available during LDAP auth

Jakub Hrozek jhrozek at redhat.com
Tue Oct 1 19:03:57 UTC 2013


Hi,

the attached two patches fix #2077. I wrote the patches even though
there are unresolved 1.11 issues because there was a customer looking
for a test package.
-------------- next part --------------
>From 51d7d8c1bc310396be1a7148e09a73f6a31e8734 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 1 Oct 2013 17:44:07 +0200
Subject: [PATCH 1/2] LDAP: Split out a request to search for a user w/o saving

Related:
https://fedorahosted.org/sssd/ticket/2077

Certain situations require that a user entry is downloaded for further
inpection, but not saved to the sysdb right away. This patch splits the
previously monolithic request into one that just downloads the data and
one that uses the new one to download and save the user.
---
 src/providers/ldap/sdap_async.h       |  16 ++++
 src/providers/ldap/sdap_async_users.c | 162 +++++++++++++++++++++++++++-------
 2 files changed, 146 insertions(+), 32 deletions(-)

diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
index c8031c9a9d527a6d808f1ddce096de23850ebfd6..dbf572cdc82b100ba9c26b4853f05db1ba5fa4ed 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -58,6 +58,22 @@ errno_t sdap_connect_host_recv(TALLOC_CTX *mem_ctx,
                                struct tevent_req *req,
                                struct sdap_handle **_sh);
 
+/* Search users in LDAP, return them as attrs */
+struct tevent_req *sdap_search_user_send(TALLOC_CTX *memctx,
+                                         struct tevent_context *ev,
+                                         struct sss_domain_info *dom,
+                                         struct sdap_options *opts,
+                                         struct sdap_search_base **search_bases,
+                                         struct sdap_handle *sh,
+                                         const char **attrs,
+                                         const char *filter,
+                                         int timeout,
+                                         bool enumeration);
+int sdap_search_user_recv(TALLOC_CTX *memctx, struct tevent_req *req,
+                          char **higher_usn, struct sysdb_attrs ***users,
+                          size_t *count);
+
+/* Search users in LDAP using the request above, save them to cache */
 struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx,
                                        struct tevent_context *ev,
                                        struct sss_domain_info *dom,
diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c
index 860e8feaf1bfcc99ce0640be417acc2bbf563887..3a7f18d9d1046d40ec8167baa17ccc640bceeb12 100644
--- a/src/providers/ldap/sdap_async_users.c
+++ b/src/providers/ldap/sdap_async_users.c
@@ -568,15 +568,15 @@ done:
 
 /* ==Search-Users-with-filter============================================= */
 
-struct sdap_get_users_state {
+struct sdap_search_user_state {
     struct tevent_context *ev;
     struct sdap_options *opts;
     struct sdap_handle *sh;
     struct sss_domain_info *dom;
-    struct sysdb_ctx *sysdb;
+
     const char **attrs;
     const char *base_filter;
-    char *filter;
+    const char *filter;
     int timeout;
     bool enumeration;
 
@@ -588,33 +588,31 @@ struct sdap_get_users_state {
     struct sdap_search_base **search_bases;
 };
 
-static errno_t sdap_get_users_next_base(struct tevent_req *req);
-static void sdap_get_users_process(struct tevent_req *subreq);
+static errno_t sdap_search_user_next_base(struct tevent_req *req);
+static void sdap_search_user_process(struct tevent_req *subreq);
 
-struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx,
-                                       struct tevent_context *ev,
-                                       struct sss_domain_info *dom,
-                                       struct sysdb_ctx *sysdb,
-                                       struct sdap_options *opts,
-                                       struct sdap_search_base **search_bases,
-                                       struct sdap_handle *sh,
-                                       const char **attrs,
-                                       const char *filter,
-                                       int timeout,
-                                       bool enumeration)
+struct tevent_req *sdap_search_user_send(TALLOC_CTX *memctx,
+                                         struct tevent_context *ev,
+                                         struct sss_domain_info *dom,
+                                         struct sdap_options *opts,
+                                         struct sdap_search_base **search_bases,
+                                         struct sdap_handle *sh,
+                                         const char **attrs,
+                                         const char *filter,
+                                         int timeout,
+                                         bool enumeration)
 {
     errno_t ret;
     struct tevent_req *req;
-    struct sdap_get_users_state *state;
+    struct sdap_search_user_state *state;
 
-    req = tevent_req_create(memctx, &state, struct sdap_get_users_state);
-    if (!req) return NULL;
+    req = tevent_req_create(memctx, &state, struct sdap_search_user_state);
+    if (req == NULL) return NULL;
 
     state->ev = ev;
     state->opts = opts;
     state->dom = dom;
     state->sh = sh;
-    state->sysdb = sysdb;
     state->attrs = attrs;
     state->higher_usn = NULL;
     state->users =  NULL;
@@ -632,7 +630,7 @@ struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx,
         goto done;
     }
 
-    ret = sdap_get_users_next_base(req);
+    ret = sdap_search_user_next_base(req);
 
 done:
     if (ret != EOK) {
@@ -643,18 +641,18 @@ done:
     return req;
 }
 
-static errno_t sdap_get_users_next_base(struct tevent_req *req)
+static errno_t sdap_search_user_next_base(struct tevent_req *req)
 {
     struct tevent_req *subreq;
-    struct sdap_get_users_state *state;
+    struct sdap_search_user_state *state;
 
-    state = tevent_req_data(req, struct sdap_get_users_state);
+    state = tevent_req_data(req, struct sdap_search_user_state);
 
     talloc_zfree(state->filter);
     state->filter = sdap_get_id_specific_filter(state,
                         state->base_filter,
                         state->search_bases[state->base_iter]->filter);
-    if (!state->filter) {
+    if (state->filter == NULL) {
         return ENOMEM;
     }
 
@@ -670,20 +668,20 @@ static errno_t sdap_get_users_next_base(struct tevent_req *req)
             state->opts->user_map, SDAP_OPTS_USER,
             state->timeout,
             state->enumeration); /* If we're enumerating, we need paging */
-    if (!subreq) {
+    if (subreq == NULL) {
         return ENOMEM;
     }
-    tevent_req_set_callback(subreq, sdap_get_users_process, req);
+    tevent_req_set_callback(subreq, sdap_search_user_process, req);
 
     return EOK;
 }
 
-static void sdap_get_users_process(struct tevent_req *subreq)
+static void sdap_search_user_process(struct tevent_req *subreq)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
-    struct sdap_get_users_state *state = tevent_req_data(req,
-                                            struct sdap_get_users_state);
+    struct sdap_search_user_state *state = tevent_req_data(req,
+                                            struct sdap_search_user_state);
     int ret;
     size_t count, i;
     struct sysdb_attrs **users;
@@ -733,7 +731,7 @@ static void sdap_get_users_process(struct tevent_req *subreq)
         state->base_iter++;
         if (state->search_bases[state->base_iter]) {
             /* There are more search bases to try */
-            ret = sdap_get_users_next_base(req);
+            ret = sdap_search_user_next_base(req);
             if (ret != EOK) {
                 tevent_req_error(req, ret);
             }
@@ -749,12 +747,112 @@ static void sdap_get_users_process(struct tevent_req *subreq)
         return;
     }
 
+    DEBUG(SSSDBG_TRACE_ALL, ("Retrieved total %zu users\n", state->count));
+    tevent_req_done(req);
+}
+
+
+int sdap_search_user_recv(TALLOC_CTX *memctx, struct tevent_req *req,
+                          char **higher_usn, struct sysdb_attrs ***users,
+                          size_t *count)
+{
+    struct sdap_search_user_state *state = tevent_req_data(req,
+                                            struct sdap_search_user_state);
+
+    if (higher_usn) {
+        *higher_usn = talloc_steal(memctx, state->higher_usn);
+    }
+
+    if (users) {
+        *users = talloc_steal(memctx, state->users);
+    }
+
+    if (count) {
+        *count = state->count;
+    }
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+/* ==Search-And-Save-Users-with-filter============================================= */
+struct sdap_get_users_state {
+    struct sysdb_ctx *sysdb;
+    struct sdap_options *opts;
+    struct sss_domain_info *dom;
+
+    char *higher_usn;
+    struct sysdb_attrs **users;
+    size_t count;
+};
+
+static void sdap_get_users_done(struct tevent_req *subreq);
+
+struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx,
+                                       struct tevent_context *ev,
+                                       struct sss_domain_info *dom,
+                                       struct sysdb_ctx *sysdb,
+                                       struct sdap_options *opts,
+                                       struct sdap_search_base **search_bases,
+                                       struct sdap_handle *sh,
+                                       const char **attrs,
+                                       const char *filter,
+                                       int timeout,
+                                       bool enumeration)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct sdap_get_users_state *state;
+
+    req = tevent_req_create(memctx, &state, struct sdap_get_users_state);
+    if (!req) return NULL;
+
+    state->sysdb = sysdb;
+    state->opts = opts;
+    state->dom = dom;
+
+    subreq = sdap_search_user_send(state, ev, dom, opts, search_bases,
+                                   sh, attrs, filter, timeout, enumeration);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+    tevent_req_set_callback(subreq, sdap_get_users_done, req);
+
+    ret = EOK;
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, ev);
+    }
+
+    return req;
+}
+
+static void sdap_get_users_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct sdap_get_users_state *state = tevent_req_data(req,
+                                            struct sdap_get_users_state);
+    int ret;
+
+    ret = sdap_search_user_recv(state, subreq, &state->higher_usn,
+                                &state->users, &state->count);
+    if (ret) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Failed to retrieve users\n"));
+        tevent_req_error(req, ret);
+        return;
+    }
+
     ret = sdap_save_users(state, state->sysdb,
                           state->dom, state->opts,
                           state->users, state->count,
                           &state->higher_usn);
     if (ret) {
-        DEBUG(2, ("Failed to store users.\n"));
+        DEBUG(SSSDBG_OP_FAILURE, ("Failed to store users.\n"));
         tevent_req_error(req, ret);
         return;
     }
-- 
1.8.3.1

-------------- next part --------------
>From a0a9a4d0f868ae997f217f81262a5e2e7a62ee66 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 1 Oct 2013 17:44:55 +0200
Subject: [PATCH 2/2] LDAP: Search for original DN during auth if it's missing

Resolves: https://fedorahosted.org/sssd/ticket/2077

If during the LDAP authentication we find out that the originalDN to
bind as is missing (because the ID module is not LDAP based), we can try
to look up the user from LDAP without saving him just in order to
receive the originalDN.
---
 src/providers/ldap/ldap_auth.c | 211 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 195 insertions(+), 16 deletions(-)

diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
index f9bab4b2b72a061c88fbf18d3f8401b673f79619..a96d662374eb0225d48606e8c9df3119ab2f84b4 100644
--- a/src/providers/ldap/ldap_auth.c
+++ b/src/providers/ldap/ldap_auth.c
@@ -343,6 +343,147 @@ shadow_fail:
 }
 
 /* ==Get-User-DN========================================================== */
+struct get_user_dn_state {
+    const char *username;
+
+    char *orig_dn;
+};
+
+static void get_user_dn_done(struct tevent_req *subreq);
+
+static struct tevent_req *get_user_dn_send(TALLOC_CTX *memctx,
+                                           struct tevent_context *ev,
+                                           struct sss_domain_info *domain,
+                                           struct sdap_handle *sh,
+                                           struct sdap_options *opts,
+                                           const char *username)
+{
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct get_user_dn_state *state;
+    char *clean_name;
+    char *filter;
+    const char **attrs;
+    errno_t ret;
+
+    req = tevent_req_create(memctx, &state, struct get_user_dn_state);
+    if (!req) return NULL;
+
+    state->username = username;
+
+    ret = sss_filter_sanitize(state, username, &clean_name);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))",
+                             opts->user_map[SDAP_AT_USER_NAME].name,
+                             clean_name,
+                             opts->user_map[SDAP_OC_USER].name);
+    talloc_zfree(clean_name);
+    if (filter == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Failed to build the base filter\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* We're mostly interested in the DN anyway */
+    attrs = talloc_array(state, const char *, 3);
+    if (attrs == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+    attrs[0] = "objectclass";
+    attrs[1] = opts->user_map[SDAP_AT_USER_NAME].name;
+    attrs[2] = NULL;
+
+    subreq = sdap_search_user_send(state, ev, domain, opts,
+                                   opts->sdom->user_search_bases,
+                                   sh, attrs, filter,
+                                   dp_opt_get_int(opts->basic,
+                                                  SDAP_SEARCH_TIMEOUT),
+                                   false);
+    if (!subreq) {
+        ret = ENOMEM;
+        goto done;
+    }
+    tevent_req_set_callback(subreq, get_user_dn_done, req);
+    return req;
+
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static void get_user_dn_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct get_user_dn_state *state = tevent_req_data(req,
+                                                    struct get_user_dn_state);
+    struct ldb_message_element *el;
+    struct sysdb_attrs **users;
+    size_t count;
+
+    ret = sdap_search_user_recv(state, subreq, NULL, &users, &count);
+    talloc_zfree(subreq);
+    if (ret && ret != ENOENT) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Failed to retrieve users\n"));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    if (count == 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("No such user\n"));
+        tevent_req_error(req, ENOMEM);
+        return;
+    } else if (count > 1) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Multiple users matched\n"));
+        tevent_req_error(req, EIO);
+        return;
+    }
+
+    /* exactly one user. Get the originalDN */
+    ret = sysdb_attrs_get_el(users[0], SYSDB_ORIG_DN, &el);
+    if (ret || el == NULL || el->num_values == 0) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("originalDN is not available for [%s].\n", state->username));
+
+        tevent_req_error(req, ret ? ret : EIO);
+        return;
+    }
+
+    state->orig_dn = talloc_strdup(state, (const char *) el->values[0].data);
+    if (state->orig_dn == NULL) {
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+
+    DEBUG(SSSDBG_TRACE_INTERNAL, ("Found originalDN [%s] for [%s]\n",
+          state->orig_dn, state->username));
+    tevent_req_done(req);
+}
+
+static int get_user_dn_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req,
+                            char **orig_dn)
+{
+    struct get_user_dn_state *state = tevent_req_data(req,
+                                                    struct get_user_dn_state);
+
+    if (orig_dn) {
+        *orig_dn = talloc_move(mem_ctx, &state->orig_dn);
+    }
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
 
 static int get_user_dn(TALLOC_CTX *memctx,
                        struct sysdb_ctx *sysdb,
@@ -391,25 +532,20 @@ static int get_user_dn(TALLOC_CTX *memctx,
 
     switch (res->count) {
     case 0:
-        /* FIXME: not in cache, needs a true search */
-        ret = ENOENT;
+        /* No such user entry? Look it up */
+        ret = EAGAIN;
         break;
 
     case 1:
         dn = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_ORIG_DN, NULL);
-        if (dn) {
-            dn = talloc_strdup(tmpctx, dn);
-        } else {
-            /* TODO: try to search ldap server ? */
-
-            /* FIXME: remove once we store originalDN on every call
-             * NOTE: this is wrong, works only with some DITs */
-            dn = talloc_asprintf(tmpctx, "%s=%s,%s",
-                                 opts->user_map[SDAP_AT_USER_NAME].name,
-                                 username,
-                                 dp_opt_get_string(opts->basic,
-                                                   SDAP_USER_SEARCH_BASE));
+        if (dn == NULL) {
+            /* The user entry has no original DN. This is the case when the ID
+             * provider is not LDAP-based (proxy perhaps) */
+            ret = EAGAIN;
+            break;
         }
+
+        dn = talloc_strdup(tmpctx, dn);
         if (!dn) {
             ret = ENOMEM;
             break;
@@ -466,6 +602,8 @@ struct auth_state {
 };
 
 static struct tevent_req *auth_get_server(struct tevent_req *req);
+static void auth_get_dn_done(struct tevent_req *subreq);
+static void auth_do_bind(struct tevent_req *req);
 static void auth_resolve_done(struct tevent_req *subreq);
 static void auth_connect_done(struct tevent_req *subreq);
 static void auth_bind_user_done(struct tevent_req *subreq);
@@ -610,11 +748,52 @@ static void auth_connect_done(struct tevent_req *subreq)
     ret = get_user_dn(state, state->ctx->be->domain->sysdb, state->ctx->be->domain,
                       state->ctx->opts, state->username, &state->dn,
                       &state->pw_expire_type, &state->pw_expire_data);
-    if (ret) {
-        tevent_req_error(req, ret);
+    if (ret == EOK) {
+        /* All required user data was pre-cached during an identity lookup.
+         * We can proceed with the bind */
+        auth_do_bind(req);
+        return;
+    } else if (ret == EAGAIN) {
+        /* The cached user entry was missing the bind DN. Need to look
+         * it up based on user name in order to perform the bind */
+        subreq = get_user_dn_send(req, state->ev, state->ctx->be->domain,
+                                  state->sh, state->ctx->opts, state->username);
+        if (subreq == NULL) {
+            tevent_req_error(req, ENOMEM);
+            return;
+        }
+        tevent_req_set_callback(subreq, auth_get_dn_done, req);
+        return;
+    }
+
+    tevent_req_error(req, ret);
+    return;
+}
+
+static void auth_get_dn_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct auth_state *state = tevent_req_data(req, struct auth_state);
+    errno_t ret;
+
+    ret = get_user_dn_recv(state, subreq, &state->dn);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ERR_ACCOUNT_UNKNOWN);
         return;
     }
 
+    /* The DN was found with an LDAP lookup
+     * We can proceed with the bind */
+    return auth_do_bind(req);
+}
+
+static void auth_do_bind(struct tevent_req *req)
+{
+    struct auth_state *state = tevent_req_data(req, struct auth_state);
+    struct tevent_req *subreq;
+
     subreq = sdap_auth_send(state, state->ev, state->sh,
                             NULL, NULL, state->dn,
                             state->authtok);
-- 
1.8.3.1



More information about the sssd-devel mailing list