From e28b760ac18f0c51ee6cdfc65f9c69abfe9bd126 Mon Sep 17 00:00:00 2001
From: Ondrej Kos <okos@redhat.com>
Date: Tue, 3 Sep 2013 12:27:17 +0200
Subject: [PATCH 2/2] AD: Enable TokenGroups initgroups lookup

This is first implementation of getting TokenGroups lookup working.

If all of the group SIDs that are fetched via the users TokenGroups
attribute are in sysdb, the membership is processed this way. If any of
the groups is missing in the cache, it falls back to rfc2307bis
initgroups lookup.

Resolves:
https://fedorahosted.org/sssd/ticket/1568
---
 src/providers/ldap/sdap_async.h               |  1 +
 src/providers/ldap/sdap_async_initgroups.c    | 64 ++++++++++++++++++++----
 src/providers/ldap/sdap_async_initgroups_ad.c | 71 +++++++++++++++++----------
 src/providers/ldap/sdap_async_private.h       |  2 +
 4 files changed, 103 insertions(+), 35 deletions(-)

diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
index c8031c9a9d527a6d808f1ddce096de23850ebfd6..432a561237fa70075b9c20609a5d661078d53019 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -279,6 +279,7 @@ sdap_get_ad_match_rule_initgroups_recv(struct tevent_req *req);
 
 struct tevent_req *
 sdap_get_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
+                                        void *istate,
                                         struct tevent_context *ev,
                                         struct sdap_options *opts,
                                         struct sysdb_ctx *sysdb,
diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
index 315834153408ebc8b5e4837ea51b26460dc0469a..163654023c7aa7ea140950452302f03eb0206486 100644
--- a/src/providers/ldap/sdap_async_initgroups.c
+++ b/src/providers/ldap/sdap_async_initgroups.c
@@ -2571,8 +2571,10 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
     state->name = name;
     state->grp_attrs = grp_attrs;
     state->orig_user = NULL;
+    state->cname = NULL;
     state->timeout = dp_opt_get_int(state->opts->basic, SDAP_SEARCH_TIMEOUT);
     state->user_base_iter = 0;
+    state->failed_tokengroups = false;
     state->user_search_bases = sdom->user_search_bases;
     if (!state->user_search_bases) {
         DEBUG(SSSDBG_CRIT_FAILURE,
@@ -2745,13 +2747,16 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
         return;
     }
 
+    state->cname = cname;
+    talloc_steal(state, cname);
+
     DEBUG(9, ("Process user's groups\n"));
 
     switch (state->opts->schema_type) {
     case SDAP_SCHEMA_RFC2307:
         subreq = sdap_initgr_rfc2307_send(state, state->ev, state->opts,
                                           state->sysdb, state->dom, state->sh,
-                                          cname);
+                                          state->cname);
         if (!subreq) {
             tevent_req_error(req, ENOMEM);
             return;
@@ -2769,17 +2774,18 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
             return;
         }
 
-        if (state->use_id_mapping
-                && state->opts->dc_functional_level >= DS_BEHAVIOR_WIN2008) {
+        if (state->opts->dc_functional_level >= DS_BEHAVIOR_WIN2008) {
             /* Take advantage of AD's tokenGroups mechanism to look up all
              * parent groups in a single request.
              */
-            subreq = sdap_get_ad_tokengroups_initgroups_send(state, state->ev,
+            subreq = sdap_get_ad_tokengroups_initgroups_send(state, state,
+                                                             state->ev,
                                                              state->opts,
                                                              state->sysdb,
                                                              state->dom,
                                                              state->sh,
-                                                             cname, orig_dn,
+                                                             state->cname,
+                                                             orig_dn,
                                                              state->timeout);
         } else if (state->opts->support_matching_rule
                     && dp_opt_get_bool(state->opts->basic,
@@ -2792,13 +2798,17 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
                                                             state->sysdb,
                                                             state->dom,
                                                             state->sh,
-                                                            cname, orig_dn,
+                                                            state->cname,
+                                                            orig_dn,
                                                             state->timeout);
         } else {
-            subreq = sdap_initgr_rfc2307bis_send(
-                    state, state->ev, state->opts, state->sysdb,
-                    state->dom, state->sh,
-                    cname, orig_dn);
+            subreq = sdap_initgr_rfc2307bis_send(state, state->ev,
+                                                 state->opts,
+                                                 state->sysdb,
+                                                 state->dom,
+                                                 state->sh,
+                                                 state->cname,
+                                                 orig_dn);
         }
         if (!subreq) {
             tevent_req_error(req, ENOMEM);
@@ -2851,6 +2861,40 @@ 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;
+
+    if (state->failed_tokengroups) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("TokenGroups lookup failed, falling "
+                    "back to rfc2307bis initgroups call.\n"));
+
+        state->failed_tokengroups = false;
+
+        ret = sysdb_attrs_get_string(state->orig_user,
+                                     SYSDB_ORIG_DN,
+                                     &orig_dn);
+        if (ret != EOK) {
+            tevent_req_error(req, ret);
+            return;
+        }
+
+        subreq = sdap_initgr_rfc2307bis_send(state, state->ev,
+                                             state->opts,
+                                             state->sysdb,
+                                             state->dom,
+                                             state->sh,
+                                             state->cname,
+                                             orig_dn);
+
+        if (!subreq) {
+            tevent_req_error(req, ENOMEM);
+            return;
+        }
+
+        talloc_steal(subreq, orig_dn);
+        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 e5649a2b9793253a55c48fba69cd3a902c0dc967..d4f598db1195f635cf773ab55828a92c0dfcc1eb 100644
--- a/src/providers/ldap/sdap_async_initgroups_ad.c
+++ b/src/providers/ldap/sdap_async_initgroups_ad.c
@@ -305,6 +305,7 @@ struct sdap_ad_tokengroups_initgr_state {
     struct sss_domain_info *domain;
     struct sdap_handle *sh;
     const char *username;
+    struct sdap_get_initgr_state *istate;
 };
 
 static void
@@ -312,6 +313,7 @@ sdap_get_ad_tokengroups_initgroups_lookup_done(struct tevent_req *req);
 
 struct tevent_req *
 sdap_get_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
+                                        void *istate,
                                         struct tevent_context *ev,
                                         struct sdap_options *opts,
                                         struct sysdb_ctx *sysdb,
@@ -336,6 +338,7 @@ sdap_get_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
     state->domain = domain;
     state->sh = sh;
     state->username = name;
+    state->istate = (struct sdap_get_initgr_state *)istate;
 
     subreq = sdap_get_generic_send(
             state, state->ev, state->opts, state->sh,
@@ -464,20 +467,29 @@ sdap_get_ad_tokengroups_initgroups_lookup_done(struct tevent_req *subreq)
             DEBUG(SSSDBG_TRACE_FUNC, ("Skipping built-in object.\n"));
             ret = EOK;
             continue;
-        } else if (ret != EOK) {
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  ("Could not convert SID to GID: [%s]. Skipping\n",
-                   strerror(ret)));
-            continue;
         }
 
-        DEBUG(SSSDBG_TRACE_LIBS,
-              ("Processing membership GID [%lu]\n",
-               gid));
+        if (state->istate->use_id_mapping) {
+            if (ret != EOK) {
+                DEBUG(SSSDBG_MINOR_FAILURE,
+                      ("Could not convert SID to GID: [%s]. Skipping\n",
+                       strerror(ret)));
+                continue;
+            }
+
+            DEBUG(SSSDBG_TRACE_LIBS,
+                  ("Processing membership GID [%lu]\n",
+                   gid));
+
+            /* Check whether this GID already exists in the sysdb */
+            ret = sysdb_search_group_by_gid(tmp_ctx, state->sysdb, state->domain,
+                                            gid, attrs, &msg);
+        } else {
+            ret = sysdb_search_group_by_sid_str(tmp_ctx, state->sysdb,
+                                                state->domain, sid_str, attrs,
+                                                &msg);
+        }
 
-        /* Check whether this GID already exists in the sysdb */
-        ret = sysdb_search_group_by_gid(tmp_ctx, state->sysdb, state->domain,
-                                        gid, attrs, &msg);
         if (ret == EOK) {
             group_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
             if (!group_name) {
@@ -487,20 +499,29 @@ sdap_get_ad_tokengroups_initgroups_lookup_done(struct tevent_req *subreq)
                 goto done;
             }
         } else if (ret == ENOENT) {
-            /* This is a new group. For now, we will store it
-             * under the name of its SID. When a direct lookup of
-             * the group or its GID occurs, it will replace this
-             * temporary entry.
-             */
-            group_name = sid_str;
-            ret = sysdb_add_incomplete_group(state->sysdb,
-                                             state->domain,
-                                             group_name, gid,
-                                             NULL, sid_str, false, now);
-            if (ret != EOK) {
-                DEBUG(SSSDBG_MINOR_FAILURE,
-                      ("Could not create incomplete group: [%s]\n",
-                       strerror(ret)));
+            if (state->istate->use_id_mapping) {
+                /* This is a new group. For now, we will store it
+                 * under the name of its SID. When a direct lookup of
+                 * the group or its GID occurs, it will replace this
+                 * temporary entry.
+                 */
+                group_name = sid_str;
+                ret = sysdb_add_incomplete_group(state->sysdb,
+                                                 state->domain,
+                                                 group_name, gid,
+                                                 NULL, sid_str, false, now);
+                if (ret != EOK) {
+                    DEBUG(SSSDBG_MINOR_FAILURE,
+                          ("Could not create incomplete group: [%s]\n",
+                           strerror(ret)));
+                    goto done;
+                }
+            } else {
+                DEBUG(SSSDBG_MINOR_FAILURE, ("SID [%s] not in cache, "
+                            "performing fallback.\n", sid_str));
+
+                state->istate->failed_tokengroups = true;
+                ret = EOK;
                 goto done;
             }
         } else {
diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h
index 4d0b86529a46ca7da495e44d4d9deaa2dd4e625f..ba720835960319b49c389f4ca42c083bfe41879f 100644
--- a/src/providers/ldap/sdap_async_private.h
+++ b/src/providers/ldap/sdap_async_private.h
@@ -44,6 +44,7 @@ struct sdap_get_initgr_state {
     struct sdap_id_ctx *id_ctx;
     struct sdap_id_conn_ctx *conn;
     const char *name;
+    const char *cname;
     const char **grp_attrs;
     const char **user_attrs;
     const char *user_base_filter;
@@ -56,6 +57,7 @@ struct sdap_get_initgr_state {
     struct sdap_search_base **user_search_bases;
 
     bool use_id_mapping;
+    bool failed_tokengroups;
 };
 
 bool is_dn(const char *str);
-- 
1.8.1.4

