Blame SOURCES/0011-LDAP-Search-for-original-DN-during-auth-if-it-s-miss.patch

2fc102
From 8285fdca515e103eed41625a444de6fe72c5daa7 Mon Sep 17 00:00:00 2001
2fc102
From: Jakub Hrozek <jhrozek@redhat.com>
2fc102
Date: Tue, 1 Oct 2013 17:44:55 +0200
2fc102
Subject: [PATCH 11/11] LDAP: Search for original DN during auth if it's
2fc102
 missing
2fc102
2fc102
Resolves: https://fedorahosted.org/sssd/ticket/2077
2fc102
2fc102
If during the LDAP authentication we find out that the originalDN to
2fc102
bind as is missing (because the ID module is not LDAP based), we can try
2fc102
to look up the user from LDAP without saving him just in order to
2fc102
receive the originalDN.
2fc102
---
2fc102
 src/providers/ldap/ldap_auth.c | 210 +++++++++++++++++++++++++++++++++++++----
2fc102
 1 file changed, 194 insertions(+), 16 deletions(-)
2fc102
2fc102
diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
2fc102
index f9bab4b2b72a061c88fbf18d3f8401b673f79619..ddaeb09c8ae06812855a1daec2fc3399eb4be361 100644
2fc102
--- a/src/providers/ldap/ldap_auth.c
2fc102
+++ b/src/providers/ldap/ldap_auth.c
2fc102
@@ -343,6 +343,146 @@ shadow_fail:
2fc102
 }
2fc102
 
2fc102
 /* ==Get-User-DN========================================================== */
2fc102
+struct get_user_dn_state {
2fc102
+    const char *username;
2fc102
+
2fc102
+    char *orig_dn;
2fc102
+};
2fc102
+
2fc102
+static void get_user_dn_done(struct tevent_req *subreq);
2fc102
+
2fc102
+static struct tevent_req *get_user_dn_send(TALLOC_CTX *memctx,
2fc102
+                                           struct tevent_context *ev,
2fc102
+                                           struct sss_domain_info *domain,
2fc102
+                                           struct sdap_handle *sh,
2fc102
+                                           struct sdap_options *opts,
2fc102
+                                           const char *username)
2fc102
+{
2fc102
+    struct tevent_req *req;
2fc102
+    struct tevent_req *subreq;
2fc102
+    struct get_user_dn_state *state;
2fc102
+    char *clean_name;
2fc102
+    char *filter;
2fc102
+    const char **attrs;
2fc102
+    errno_t ret;
2fc102
+
2fc102
+    req = tevent_req_create(memctx, &state, struct get_user_dn_state);
2fc102
+    if (!req) return NULL;
2fc102
+
2fc102
+    state->username = username;
2fc102
+
2fc102
+    ret = sss_filter_sanitize(state, username, &clean_name);
2fc102
+    if (ret != EOK) {
2fc102
+        goto done;
2fc102
+    }
2fc102
+
2fc102
+    filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))",
2fc102
+                             opts->user_map[SDAP_AT_USER_NAME].name,
2fc102
+                             clean_name,
2fc102
+                             opts->user_map[SDAP_OC_USER].name);
2fc102
+    talloc_zfree(clean_name);
2fc102
+    if (filter == NULL) {
2fc102
+        DEBUG(SSSDBG_OP_FAILURE, ("Failed to build the base filter\n"));
2fc102
+        ret = ENOMEM;
2fc102
+        goto done;
2fc102
+    }
2fc102
+
2fc102
+    /* We're mostly interested in the DN anyway */
2fc102
+    attrs = talloc_array(state, const char *, 3);
2fc102
+    if (attrs == NULL) {
2fc102
+        ret = ENOMEM;
2fc102
+        goto done;
2fc102
+    }
2fc102
+    attrs[0] = "objectclass";
2fc102
+    attrs[1] = opts->user_map[SDAP_AT_USER_NAME].name;
2fc102
+    attrs[2] = NULL;
2fc102
+
2fc102
+    subreq = sdap_search_user_send(state, ev, domain, opts,
2fc102
+                                   opts->sdom->user_search_bases,
2fc102
+                                   sh, attrs, filter,
2fc102
+                                   dp_opt_get_int(opts->basic,
2fc102
+                                                  SDAP_SEARCH_TIMEOUT),
2fc102
+                                   false);
2fc102
+    if (!subreq) {
2fc102
+        ret = ENOMEM;
2fc102
+        goto done;
2fc102
+    }
2fc102
+    tevent_req_set_callback(subreq, get_user_dn_done, req);
2fc102
+    return req;
2fc102
+
2fc102
+done:
2fc102
+    if (ret == EOK) {
2fc102
+        tevent_req_done(req);
2fc102
+    } else {
2fc102
+        tevent_req_error(req, ret);
2fc102
+    }
2fc102
+    tevent_req_post(req, ev);
2fc102
+    return req;
2fc102
+}
2fc102
+
2fc102
+static void get_user_dn_done(struct tevent_req *subreq)
2fc102
+{
2fc102
+    errno_t ret;
2fc102
+    struct tevent_req *req = tevent_req_callback_data(subreq,
2fc102
+                                                      struct tevent_req);
2fc102
+    struct get_user_dn_state *state = tevent_req_data(req,
2fc102
+                                                    struct get_user_dn_state);
2fc102
+    struct ldb_message_element *el;
2fc102
+    struct sysdb_attrs **users;
2fc102
+    size_t count;
2fc102
+
2fc102
+    ret = sdap_search_user_recv(state, subreq, NULL, &users, &count);
2fc102
+    talloc_zfree(subreq);
2fc102
+    if (ret && ret != ENOENT) {
2fc102
+        DEBUG(SSSDBG_OP_FAILURE, ("Failed to retrieve users\n"));
2fc102
+        tevent_req_error(req, ret);
2fc102
+        return;
2fc102
+    }
2fc102
+
2fc102
+    if (count == 0) {
2fc102
+        DEBUG(SSSDBG_OP_FAILURE, ("No such user\n"));
2fc102
+        tevent_req_error(req, ENOMEM);
2fc102
+        return;
2fc102
+    } else if (count > 1) {
2fc102
+        DEBUG(SSSDBG_OP_FAILURE, ("Multiple users matched\n"));
2fc102
+        tevent_req_error(req, EIO);
2fc102
+        return;
2fc102
+    }
2fc102
+
2fc102
+    /* exactly one user. Get the originalDN */
2fc102
+    ret = sysdb_attrs_get_el_ext(users[0], SYSDB_ORIG_DN, false, &el);
2fc102
+    if (ret != EOK) {
2fc102
+        DEBUG(SSSDBG_OP_FAILURE,
2fc102
+              ("originalDN is not available for [%s].\n", state->username));
2fc102
+        tevent_req_error(req, ret);
2fc102
+        return;
2fc102
+    }
2fc102
+
2fc102
+    state->orig_dn = talloc_strdup(state, (const char *) el->values[0].data);
2fc102
+    if (state->orig_dn == NULL) {
2fc102
+        tevent_req_error(req, ENOMEM);
2fc102
+        return;
2fc102
+    }
2fc102
+
2fc102
+    DEBUG(SSSDBG_TRACE_INTERNAL, ("Found originalDN [%s] for [%s]\n",
2fc102
+          state->orig_dn, state->username));
2fc102
+    tevent_req_done(req);
2fc102
+}
2fc102
+
2fc102
+static int get_user_dn_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req,
2fc102
+                            char **orig_dn)
2fc102
+{
2fc102
+    struct get_user_dn_state *state = tevent_req_data(req,
2fc102
+                                                    struct get_user_dn_state);
2fc102
+
2fc102
+    if (orig_dn) {
2fc102
+        *orig_dn = talloc_move(mem_ctx, &state->orig_dn);
2fc102
+    }
2fc102
+
2fc102
+    TEVENT_REQ_RETURN_ON_ERROR(req);
2fc102
+
2fc102
+    return EOK;
2fc102
+}
2fc102
 
2fc102
 static int get_user_dn(TALLOC_CTX *memctx,
2fc102
                        struct sysdb_ctx *sysdb,
2fc102
@@ -391,25 +531,20 @@ static int get_user_dn(TALLOC_CTX *memctx,
2fc102
 
2fc102
     switch (res->count) {
2fc102
     case 0:
2fc102
-        /* FIXME: not in cache, needs a true search */
2fc102
-        ret = ENOENT;
2fc102
+        /* No such user entry? Look it up */
2fc102
+        ret = EAGAIN;
2fc102
         break;
2fc102
 
2fc102
     case 1:
2fc102
         dn = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_ORIG_DN, NULL);
2fc102
-        if (dn) {
2fc102
-            dn = talloc_strdup(tmpctx, dn);
2fc102
-        } else {
2fc102
-            /* TODO: try to search ldap server ? */
2fc102
-
2fc102
-            /* FIXME: remove once we store originalDN on every call
2fc102
-             * NOTE: this is wrong, works only with some DITs */
2fc102
-            dn = talloc_asprintf(tmpctx, "%s=%s,%s",
2fc102
-                                 opts->user_map[SDAP_AT_USER_NAME].name,
2fc102
-                                 username,
2fc102
-                                 dp_opt_get_string(opts->basic,
2fc102
-                                                   SDAP_USER_SEARCH_BASE));
2fc102
+        if (dn == NULL) {
2fc102
+            /* The user entry has no original DN. This is the case when the ID
2fc102
+             * provider is not LDAP-based (proxy perhaps) */
2fc102
+            ret = EAGAIN;
2fc102
+            break;
2fc102
         }
2fc102
+
2fc102
+        dn = talloc_strdup(tmpctx, dn);
2fc102
         if (!dn) {
2fc102
             ret = ENOMEM;
2fc102
             break;
2fc102
@@ -466,6 +601,8 @@ struct auth_state {
2fc102
 };
2fc102
 
2fc102
 static struct tevent_req *auth_get_server(struct tevent_req *req);
2fc102
+static void auth_get_dn_done(struct tevent_req *subreq);
2fc102
+static void auth_do_bind(struct tevent_req *req);
2fc102
 static void auth_resolve_done(struct tevent_req *subreq);
2fc102
 static void auth_connect_done(struct tevent_req *subreq);
2fc102
 static void auth_bind_user_done(struct tevent_req *subreq);
2fc102
@@ -610,11 +747,52 @@ static void auth_connect_done(struct tevent_req *subreq)
2fc102
     ret = get_user_dn(state, state->ctx->be->domain->sysdb, state->ctx->be->domain,
2fc102
                       state->ctx->opts, state->username, &state->dn,
2fc102
                       &state->pw_expire_type, &state->pw_expire_data);
2fc102
-    if (ret) {
2fc102
-        tevent_req_error(req, ret);
2fc102
+    if (ret == EOK) {
2fc102
+        /* All required user data was pre-cached during an identity lookup.
2fc102
+         * We can proceed with the bind */
2fc102
+        auth_do_bind(req);
2fc102
+        return;
2fc102
+    } else if (ret == EAGAIN) {
2fc102
+        /* The cached user entry was missing the bind DN. Need to look
2fc102
+         * it up based on user name in order to perform the bind */
2fc102
+        subreq = get_user_dn_send(req, state->ev, state->ctx->be->domain,
2fc102
+                                  state->sh, state->ctx->opts, state->username);
2fc102
+        if (subreq == NULL) {
2fc102
+            tevent_req_error(req, ENOMEM);
2fc102
+            return;
2fc102
+        }
2fc102
+        tevent_req_set_callback(subreq, auth_get_dn_done, req);
2fc102
+        return;
2fc102
+    }
2fc102
+
2fc102
+    tevent_req_error(req, ret);
2fc102
+    return;
2fc102
+}
2fc102
+
2fc102
+static void auth_get_dn_done(struct tevent_req *subreq)
2fc102
+{
2fc102
+    struct tevent_req *req = tevent_req_callback_data(subreq,
2fc102
+                                                      struct tevent_req);
2fc102
+    struct auth_state *state = tevent_req_data(req, struct auth_state);
2fc102
+    errno_t ret;
2fc102
+
2fc102
+    ret = get_user_dn_recv(state, subreq, &state->dn);
2fc102
+    talloc_zfree(subreq);
2fc102
+    if (ret != EOK) {
2fc102
+        tevent_req_error(req, ERR_ACCOUNT_UNKNOWN);
2fc102
         return;
2fc102
     }
2fc102
 
2fc102
+    /* The DN was found with an LDAP lookup
2fc102
+     * We can proceed with the bind */
2fc102
+    return auth_do_bind(req);
2fc102
+}
2fc102
+
2fc102
+static void auth_do_bind(struct tevent_req *req)
2fc102
+{
2fc102
+    struct auth_state *state = tevent_req_data(req, struct auth_state);
2fc102
+    struct tevent_req *subreq;
2fc102
+
2fc102
     subreq = sdap_auth_send(state, state->ev, state->sh,
2fc102
                             NULL, NULL, state->dn,
2fc102
                             state->authtok);
2fc102
-- 
2fc102
1.8.4.2
2fc102