From 8b0b15f8255169cba59efdf81130aa1c0b25fed2 Mon Sep 17 00:00:00 2001
From: Ondrej Kos <okos@redhat.com>
Date: Wed, 11 Sep 2013 19:17:29 +0200
Subject: [PATCH 3/3] AD: Add lookup for SIDs not found in cache during
 TokenGroups

SIDs, that are not found in sysdb during tokengroups initgroups call
will be queried to LDAP and saved to sysdb.

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

Conflicts:
	src/providers/ldap/sdap_async_private.h
---
 src/providers/ldap/sdap_async.h               |  14 ++
 src/providers/ldap/sdap_async_initgroups.c    |  38 +++-
 src/providers/ldap/sdap_async_initgroups_ad.c | 290 ++++++++++++++++++++++++++
 3 files changed, 340 insertions(+), 2 deletions(-)

diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
index 0c682992a4037c30a347a19f06529d1920d2c099..2e461ac9f957fc6d4653a2b5aae3d2e367cfeed6 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -294,6 +294,20 @@ sdap_get_ad_tokengroups_initgroups_recv(struct tevent_req *req,
                                         bool *_lookup_sids,
                                         const char ***_group_sid_list);
 
+struct tevent_req *
+sdap_get_ad_token_initgr_sidlookup_send(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+                                        struct sdap_options *opts,
+                                        struct sysdb_ctx *sysdb,
+                                        struct sss_domain_info *domain,
+                                        struct sdap_handle *sh,
+                                        const char *name,
+                                        const char *orig_dn,
+                                        const char **group_sid_list);
+
+errno_t
+sdap_get_ad_token_initgr_sidlookup_recv(struct tevent_req *req);
+
 errno_t sdap_add_incomplete_groups(struct sysdb_ctx *sysdb,
                                    struct sss_domain_info *domain,
                                    struct sdap_options *opts,
diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
index 31c33e785cd408bbc737c16433b97df9a84185ab..257a7b542b8e3bdc4d2a6329a209d9be9976d7cd 100644
--- a/src/providers/ldap/sdap_async_initgroups.c
+++ b/src/providers/ldap/sdap_async_initgroups.c
@@ -2560,6 +2560,7 @@ struct sdap_get_initgr_state {
     struct sdap_search_base **user_search_bases;
 
     bool use_id_mapping;
+    bool sids_looked_up;
 };
 
 static errno_t sdap_get_initgr_next_base(struct tevent_req *req);
@@ -2598,6 +2599,7 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
     state->cname = NULL;
     state->timeout = dp_opt_get_int(state->opts->basic, SDAP_SEARCH_TIMEOUT);
     state->user_base_iter = 0;
+    state->sids_looked_up = false;
     state->user_search_bases = sdom->user_search_bases;
     if (!state->user_search_bases) {
         DEBUG(SSSDBG_CRIT_FAILURE,
@@ -2880,6 +2882,7 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
     char *dom_sid_str;
     char *group_sid_str;
     struct sdap_options *opts = state->opts;
+    const char *orig_dn;
     const char **group_sid_list;
     bool lookup_sids = false;
 
@@ -2896,8 +2899,12 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
 
     case SDAP_SCHEMA_RFC2307BIS:
     case SDAP_SCHEMA_AD:
-        if (state->opts->dc_functional_level >= DS_BEHAVIOR_WIN2008) {
-            ret = sdap_get_ad_tokengroups_initgroups_recv(subreq, &lookup_sids, &group_sid_list);
+        if (state->sids_looked_up) {
+            ret = sdap_get_ad_token_initgr_sidlookup_recv(subreq);
+        } else if (state->opts->dc_functional_level >= DS_BEHAVIOR_WIN2008) {
+            ret = sdap_get_ad_tokengroups_initgroups_recv(subreq,
+                                                          &lookup_sids,
+                                                          &group_sid_list);
         }
         else if (state->opts->support_matching_rule
                 && dp_opt_get_bool(state->opts->basic,
@@ -2921,6 +2928,33 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
     if (lookup_sids) {
         DEBUG(SSSDBG_TRACE_FUNC, ("Some of users TokenGroups not found in "
                                   "cache, will perform LDAP lookup.\n"));
+
+        ret = sysdb_attrs_get_string(state->orig_user,
+                                     SYSDB_ORIG_DN,
+                                     &orig_dn);
+        if (ret != EOK) {
+            tevent_req_error(req, ret);
+            return;
+        }
+
+        subreq = sdap_get_ad_token_initgr_sidlookup_send(state, state->ev,
+                                                         state->opts,
+                                                         state->sysdb,
+                                                         state->dom,
+                                                         state->sh,
+                                                         state->cname,
+                                                         orig_dn,
+                                                         group_sid_list);
+
+        if (!subreq) {
+            tevent_req_error(req, ENOMEM);
+            return;
+        }
+
+        state->sids_looked_up = true;
+
+        tevent_req_set_callback(subreq, sdap_get_initgr_done, req);
+        return;
     }
 
     DEBUG(9, ("Initgroups done\n"));
diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
index 723b009c223118160e310e6a5efd5c4d12a85883..3ab35fada97f3af1b9d29202ea571fc8774b45e9 100644
--- a/src/providers/ldap/sdap_async_initgroups_ad.c
+++ b/src/providers/ldap/sdap_async_initgroups_ad.c
@@ -3,6 +3,7 @@
 
     Authors:
         Stephen Gallagher <sgallagh@redhat.com>
+        Ondrej Kos <okos@redhat.com>
 
     Copyright (C) 2012 Red Hat
 
@@ -649,3 +650,292 @@ sdap_get_ad_tokengroups_initgroups_recv(struct tevent_req *req,
 
     return EOK;
 }
+
+/* Lookup SIDs fetched by TokenGroups call and not found in db */
+
+struct sdap_ad_token_initgr_sidlookup_state {
+    struct tevent_context *ev;
+    struct sdap_options *opts;
+    struct sysdb_ctx *sysdb;
+    struct sss_domain_info *domain;
+    struct sdap_handle *sh;
+    struct sdap_search_base **search_bases;
+    struct sysdb_attrs **ldap_groups;
+    const char **attrs;
+    const char *username;
+    const char *base_filter;
+    size_t ldap_groups_count;
+    size_t base_iter;
+    char *filter;
+    int timeout;
+};
+
+static void
+sdap_get_ad_token_initgr_sidlookup_process(struct tevent_req *subreq);
+
+static errno_t
+sdap_get_ad_token_initgr_sidlookup_next_base(struct tevent_req *req);
+
+struct tevent_req *
+sdap_get_ad_token_initgr_sidlookup_send(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+                                        struct sdap_options *opts,
+                                        struct sysdb_ctx *sysdb,
+                                        struct sss_domain_info *domain,
+                                        struct sdap_handle *sh,
+                                        const char *name,
+                                        const char *orig_dn,
+                                        const char **group_sid_list)
+{
+    errno_t ret;
+    int i;
+    struct tevent_req *req;
+    struct sdap_ad_token_initgr_sidlookup_state *state;
+    const char **filter_members;
+    char *clean_orig_dn;
+    char *sid_filter = NULL;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct sdap_ad_token_initgr_sidlookup_state);
+    if (!req) return NULL;
+
+    state->ev = ev;
+    state->opts = opts;
+    state->sysdb = sysdb;
+    state->domain = domain;
+    state->sh = sh;
+    state->search_bases = opts->sdom->group_search_bases;
+    state->ldap_groups = NULL;
+    state->username = name;
+    state->ldap_groups_count = 0;
+    state->base_iter = 0;
+    state->timeout = dp_opt_get_int(state->opts->basic, SDAP_SEARCH_TIMEOUT);
+
+    filter_members = talloc_array(state, const char *, 2);
+    if (!filter_members) {
+        ret = ENOMEM;
+        goto done;
+    }
+    filter_members[0] = opts->group_map[SDAP_AT_GROUP_MEMBER].name;
+    filter_members[1] = NULL;
+
+    ret = build_attrs_from_map(state, opts->group_map, SDAP_OPTS_GROUP,
+                               filter_members, &state->attrs, NULL);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("Could not build attribute map: [%s]\n",
+                                     strerror(ret)));
+        goto done;
+    }
+
+    ret = sss_filter_sanitize(state, orig_dn, &clean_orig_dn);
+    if (ret != EOK) goto done;
+
+    i = 0;
+    while (group_sid_list[i]) {
+        sid_filter = talloc_asprintf(state, "%s(%s=%s)",
+                                     sid_filter ? sid_filter : "",
+                                     opts->group_map[SDAP_AT_GROUP_OBJECTSID].name,
+                                     group_sid_list[i]);
+        i++;
+    }
+
+    state->base_filter =
+            talloc_asprintf(state, "(&(objectclass=%s)(|%s)(!(%s=0)))",
+                            opts->group_map[SDAP_OC_GROUP].name,
+                            sid_filter,
+                            opts->group_map[SDAP_AT_GROUP_GID].name);
+    if (!state->base_filter) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    talloc_zfree(clean_orig_dn);
+
+    ret = sdap_get_ad_token_initgr_sidlookup_next_base(req);
+
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, ev);
+    }
+
+    return req;
+}
+
+static errno_t
+sdap_get_ad_token_initgr_sidlookup_next_base(struct tevent_req *req)
+{
+    struct tevent_req *subreq;
+    struct sdap_ad_token_initgr_sidlookup_state *state;
+
+    state = tevent_req_data(req, struct sdap_ad_token_initgr_sidlookup_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) {
+        return ENOMEM;
+    }
+
+    subreq = sdap_get_generic_send(
+            state, state->ev, state->opts, state->sh,
+            state->search_bases[state->base_iter]->basedn,
+            state->search_bases[state->base_iter]->scope,
+            state->filter, state->attrs,
+            state->opts->group_map, SDAP_OPTS_GROUP,
+            state->timeout,
+            true);
+    if (!subreq) {
+        talloc_zfree(req);
+        return ENOMEM;
+    }
+
+    tevent_req_set_callback(subreq,
+                            sdap_get_ad_token_initgr_sidlookup_process,
+                            req);
+
+    return EOK;
+}
+
+static void
+sdap_get_ad_token_initgr_sidlookup_process(struct tevent_req *subreq)
+{
+    errno_t ret;
+    errno_t tret;
+    size_t i;
+    size_t count;
+    char **groupnames = NULL;
+    bool in_transaction = false;
+    struct sysdb_attrs **ldap_groups;
+    struct tevent_req *req =
+            tevent_req_callback_data(subreq, struct tevent_req);
+    struct sdap_ad_token_initgr_sidlookup_state *state =
+            tevent_req_data(req, struct sdap_ad_token_initgr_sidlookup_state);
+
+    ret = sdap_get_generic_recv(subreq, state, &count, &ldap_groups);
+
+    talloc_zfree(subreq);
+    if (ret) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    DEBUG(SSSDBG_TRACE_LIBS, ("Found %d parent groups for user [%s]\n",
+                              count, state->username));
+
+    /* Add this batch of groups to the list */
+    if (count > 0) {
+        state->ldap_groups =
+                talloc_realloc(state,
+                               state->ldap_groups,
+                               struct sysdb_attrs *,
+                               state->ldap_groups_count + count + 1);
+        if (!state->ldap_groups) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        /* Copy the new groups into the list.
+         */
+        for (i = 0; i < count; i++) {
+            state->ldap_groups[state->ldap_groups_count + i] =
+                talloc_steal(state->ldap_groups, ldap_groups[i]);
+        }
+
+        state->ldap_groups_count += count;
+
+        state->ldap_groups[state->ldap_groups_count] = NULL;
+    }
+
+    state->base_iter++;
+
+    /* Check for additional search bases, and iterate
+     * through again.
+     */
+    if (state->search_bases[state->base_iter] != NULL) {
+        ret = sdap_get_ad_token_initgr_sidlookup_next_base(req);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
+    if (state->ldap_groups_count > 0) {
+        ret = sysdb_attrs_primary_name_list(
+                        state->sysdb,
+                        state,
+                        state->ldap_groups,
+                        state->ldap_groups_count,
+                        state->opts->group_map[SDAP_AT_GROUP_NAME].name,
+                        &groupnames);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
+    ret = sysdb_transaction_start(state->sysdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to start transaction\n"));
+        goto done;
+    }
+    in_transaction = true;
+
+    ret = sdap_add_incomplete_groups(state->sysdb,
+                                     state->domain,
+                                     state->opts,
+                                     groupnames,
+                                     state->ldap_groups,
+                                     state->ldap_groups_count);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("Could not add incomplete groups [%d]:%s\n",
+                   ret, strerror(ret)));
+        goto done;
+    }
+
+    i = 0;
+    while (groupnames[i]) {
+        ret = sysdb_add_group_member(state->sysdb,
+                                     state->domain,
+                                     groupnames[i],
+                                     state->username,
+                                     SYSDB_MEMBER_USER);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Could not add member [%s] to group "
+                                        "[%s]. Skipping.\n",
+                                        state->username,
+                                        groupnames[i]));
+        }
+        i++;
+    }
+
+    ret = sysdb_transaction_commit(state->sysdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to commit transaction\n"));
+        goto done;
+    }
+    in_transaction = false;
+
+    DEBUG(SSSDBG_TRACE_FUNC, ("Saving user groups done.\n"));
+
+done:
+    if (in_transaction) {
+        tret = sysdb_transaction_cancel(state->sysdb);
+        if (tret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to cancel transaction\n"));
+        }
+    }
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_done(req);
+}
+
+errno_t
+sdap_get_ad_token_initgr_sidlookup_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
-- 
1.8.1.4

