From 9e4cf84d268590431d0283f96edace36cb7d736d Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Wed, 22 Sep 2010 10:54:44 -0400
Subject: [PATCH 2/2] Add fake groups during initgroups

---
 src/providers/ldap/sdap_async_accounts.c |  284 ++++++++++++++++++++++++++----
 1 files changed, 253 insertions(+), 31 deletions(-)

diff --git a/src/providers/ldap/sdap_async_accounts.c b/src/providers/ldap/sdap_async_accounts.c
index 9dfde58..be35882 100644
--- a/src/providers/ldap/sdap_async_accounts.c
+++ b/src/providers/ldap/sdap_async_accounts.c
@@ -1476,6 +1476,175 @@ int sdap_get_groups_recv(struct tevent_req *req,
     return EOK;
 }
 
+/* ==Add-Fake-Groups====================================================== */
+struct sdap_add_fake_groups_state {
+    struct tevent_context *ev;
+    struct sysdb_ctx *sysdb;
+    struct sysdb_handle *handle;
+    struct sss_domain_info *dom;
+
+    char **groupnames;
+    int cur;
+
+    struct sysdb_attrs **ldap_groups;
+    int ldap_groups_count;
+};
+
+static void sdap_add_fake_groups_wakeup(struct tevent_req *subreq);
+static void sdap_add_fake_groups_next(struct tevent_req *subreq);
+static void sdap_add_fake_groups_added(struct tevent_req *subreq);
+
+static
+struct tevent_req *sdap_add_fake_groups_send(TALLOC_CTX *memctx,
+                                             struct tevent_context *ev,
+                                             struct sysdb_ctx *sysdb,
+                                             struct sysdb_handle *handle,
+                                             struct sss_domain_info *dom,
+                                             char **groupnames,
+                                             struct sysdb_attrs **ldap_groups,
+                                             int ldap_groups_count)
+{
+    struct tevent_req *req, *subreq;
+    struct sdap_add_fake_groups_state *state;
+    struct timeval tv = { 0, 0 };
+
+
+    req = tevent_req_create(memctx, &state, struct sdap_add_fake_groups_state);
+    if (!req) return NULL;
+
+    state->ev = ev;
+    state->sysdb = sysdb;
+    state->handle = handle;
+    state->dom = dom;
+    state->groupnames = groupnames;
+    state->ldap_groups = ldap_groups;
+    state->ldap_groups_count = ldap_groups_count;
+    state->cur = 0;
+
+    subreq = tevent_wakeup_send(req, ev, tv);
+    if (!subreq) {
+        talloc_zfree(req);
+        return NULL;
+    }
+    tevent_req_set_callback(subreq, sdap_add_fake_groups_wakeup, req);
+
+    return req;
+}
+
+
+static void sdap_add_fake_groups_wakeup(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+
+    sdap_add_fake_groups_next(req);
+}
+
+static void sdap_add_fake_groups_next(struct tevent_req *req)
+{
+    struct sdap_add_fake_groups_state *state = tevent_req_data(req,
+                                           struct sdap_add_fake_groups_state);
+    errno_t ret;
+    int ai;
+    const char *name;
+    gid_t gid;
+    struct ldb_message_element *el;
+    struct tevent_req *add_req = NULL;
+
+    DEBUG(7, ("Group #%d [%s] is not cached, need to add a fake entry\n",
+                state->cur, state->groupnames[state->cur]));
+
+    /* The group is not in sysdb, need to add a fake entry */
+    for (ai=0; ai < state->ldap_groups_count; ai++) {
+        ret = sysdb_attrs_get_el(state->ldap_groups[ai],
+                                 SYSDB_NAME,
+                                 &el);
+        if (ret) {
+            tevent_req_error(req, ret);
+            return;
+        }
+        if (el->num_values == 0) {
+            tevent_req_error(req, EINVAL);
+            return;
+        }
+        name = (const char *)el->values[0].data;
+
+        if (strcmp(name, state->groupnames[state->cur]) == 0) {
+            ret = sysdb_attrs_get_el(state->ldap_groups[ai],
+                                     SYSDB_GIDNUM,
+                                     &el);
+            if (ret) {
+                tevent_req_error(req, ret);
+                return;
+            }
+            if (el->num_values == 0) {
+                DEBUG(1, ("no gid provided for [%s]\n",
+                            name));
+                tevent_req_error(req, EINVAL);
+                return;
+            }
+
+            errno = 0;
+            gid = (gid_t) strtol((const char *)el->values[0].data, NULL, 0);
+            if (errno) {
+                tevent_req_error(req, errno);
+                return;
+            }
+
+            add_req = sysdb_add_fake_group_send(state,
+                                                state->ev,
+                                                state->handle,
+                                                state->dom,
+                                                state->groupnames[state->cur],
+                                                gid);
+            if (add_req == NULL) {
+                tevent_req_error(req, EIO);
+                return;
+            }
+
+            tevent_req_set_callback(add_req,
+                                    sdap_add_fake_groups_added,
+                                    req);
+            return;
+        }
+    }
+
+    if (ai == state->ldap_groups_count) {
+        tevent_req_error(req, EINVAL);
+    }
+}
+
+static void sdap_add_fake_groups_added(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct sdap_add_fake_groups_state *state = tevent_req_data(req,
+                                           struct sdap_add_fake_groups_state);
+    errno_t ret;
+
+    ret = sysdb_add_fake_group_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    state->cur++;
+    if (state->groupnames[state->cur]) {
+        sdap_add_fake_groups_next(req);
+        return;
+    }
+
+    tevent_req_done(req);
+    return;
+}
+
+int sdap_add_fake_groups_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
 
 /* ==Initgr-call-(groups-a-user-is-member-of)-RFC2307-Classic/BIS========= */
 
@@ -1490,6 +1659,12 @@ struct sdap_initgr_rfc2307_state {
     struct sysdb_handle *handle;
     char **ldap_grouplist;
 
+    struct sysdb_attrs **ldap_groups;
+    size_t ldap_groups_count;
+
+    char **add_groups;
+    char **del_groups;
+
     struct sdap_op *op;
 };
 
@@ -1559,21 +1734,21 @@ static void sdap_initgr_rfc2307_process(struct tevent_req *subreq)
 {
     struct tevent_req *req;
     struct sdap_initgr_rfc2307_state *state;
-    struct sysdb_attrs **groups;
-    size_t count;
     int ret;
 
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct sdap_initgr_rfc2307_state);
 
-    ret = sdap_get_generic_recv(subreq, state, &count, &groups);
+    ret = sdap_get_generic_recv(subreq, state,
+                                &state->ldap_groups_count,
+                                &state->ldap_groups);
     talloc_zfree(subreq);
     if (ret) {
         tevent_req_error(req, ret);
         return;
     }
 
-    if (count == 0) {
+    if (state->ldap_groups_count == 0) {
         /* No groups for this user in LDAP
          * We need to ensure that there are no groups
          * in the sysdb either.
@@ -1582,7 +1757,8 @@ static void sdap_initgr_rfc2307_process(struct tevent_req *subreq)
         state->ldap_grouplist = NULL;
     }
     else {
-        ret = sysdb_attrs_to_list(state, groups, count,
+        ret = sysdb_attrs_to_list(state,
+                                  state->ldap_groups, state->ldap_groups_count,
                                   SYSDB_NAME,
                                   &state->ldap_grouplist);
         if (ret != EOK) {
@@ -1645,6 +1821,9 @@ static void sdap_initgr_rfc2307_get_sysdb_groups(struct tevent_req *subreq)
 
 static void
 sdap_initgr_rfc2307_update_sysdb_groups_done(struct tevent_req *subreq);
+static void
+sdap_initgr_rfc2307_update_sysdb_groups_step(struct tevent_req *subreq);
+
 static void sdap_initgr_rfc2307_update_sysdb_groups(struct tevent_req *subreq)
 {
     struct tevent_req *req =
@@ -1655,8 +1834,6 @@ static void sdap_initgr_rfc2307_update_sysdb_groups(struct tevent_req *subreq)
     struct ldb_message *reply;
     struct ldb_message_element *groups;
     char **sysdb_grouplist;
-    char **add_groups;
-    char **del_groups;
 
     ret = sysdb_search_user_recv(subreq, state, &reply);
     talloc_zfree(subreq);
@@ -1668,47 +1845,62 @@ static void sdap_initgr_rfc2307_update_sysdb_groups(struct tevent_req *subreq)
     groups = ldb_msg_find_element(reply, SYSDB_MEMBEROF);
     if (!groups || groups->num_values == 0) {
         DEBUG(6, ("User is not a member of any groups\n"));
+        sysdb_grouplist = NULL;
+    } else {
+        sysdb_grouplist = talloc_array(state, char *,
+                                       groups->num_values+1);
+        if (!sysdb_grouplist) {
+            tevent_req_error(req, ENOMEM);
+            return;
+        }
 
-        tevent_req_done(req);
-        return;
-    }
-
-    sysdb_grouplist = talloc_array(state, char *,
-                                          groups->num_values+1);
-    if (!sysdb_grouplist) {
-        tevent_req_error(req, ENOMEM);
-        return;
-    }
+        /* Get a list of the groups by groupname only */
+        for (i=0; i < groups->num_values; i++) {
+            ret = sysdb_group_dn_name(state->sysdb,
+                    sysdb_grouplist,
+                    (const char *)groups->values[i].data,
+                    &sysdb_grouplist[i]);
+            if (ret != EOK) {
+                tevent_req_error(req, ENOMEM);
+                return;
+            }
+        }
 
-    /* Get a list of the groups by groupname only */
-    for (i=0; i < groups->num_values; i++) {
-         ret = sysdb_group_dn_name(state->sysdb,
-                                   sysdb_grouplist,
-                                   (const char *)groups->values[i].data,
-                                   &sysdb_grouplist[i]);
-         if (ret != EOK) {
-             tevent_req_error(req, ENOMEM);
-             return;
-         }
+        sysdb_grouplist[groups->num_values] = NULL;
     }
 
-    sysdb_grouplist[groups->num_values] = NULL;
-
     /* Find the differences between the sysdb and ldap lists
      * Groups in ldap only must be added to the sysdb;
      * groups in the sysdb only must be removed.
      */
     ret = diff_string_lists(state,
                             state->ldap_grouplist, sysdb_grouplist,
-                            &add_groups, &del_groups, NULL);
+                            &state->add_groups, &state->del_groups, NULL);
     if (ret != EOK) {
         tevent_req_error(req, ret);
         return;
     }
 
+    if (state->add_groups && state->add_groups[0]) {
+        subreq = sdap_add_fake_groups_send(state, state->ev, state->sysdb,
+                                           state->handle, state->dom,
+                                           state->add_groups,
+                                           state->ldap_groups,
+                                           state->ldap_groups_count);
+        if (!subreq) {
+            tevent_req_error(req, EIO);
+            return;
+        }
+
+        tevent_req_set_callback(subreq,
+                                sdap_initgr_rfc2307_update_sysdb_groups_step,
+                                req);
+        return;
+    }
+
     subreq = sysdb_update_members_send(state, state->ev, state->handle,
                                        state->dom, state->name,
-                                       add_groups, del_groups);
+                                       state->add_groups, state->del_groups);
     if (!subreq) {
         tevent_req_error(req, EIO);
         return;
@@ -1720,6 +1912,36 @@ static void sdap_initgr_rfc2307_update_sysdb_groups(struct tevent_req *subreq)
 }
 
 static void
+sdap_initgr_rfc2307_update_sysdb_groups_step(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req =
+            tevent_req_callback_data(subreq, struct tevent_req);
+    struct sdap_initgr_rfc2307_state *state =
+            tevent_req_data(req, struct sdap_initgr_rfc2307_state);
+    struct tevent_req *updatereq;
+
+    ret = sdap_add_fake_groups_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    updatereq = sysdb_update_members_send(state, state->ev, state->handle,
+                                       state->dom, state->name,
+                                       state->add_groups, state->del_groups);
+    if (!updatereq) {
+        tevent_req_error(req, EIO);
+        return;
+    }
+
+    tevent_req_set_callback(updatereq,
+                            sdap_initgr_rfc2307_update_sysdb_groups_done,
+                            req);
+}
+
+static void
 sdap_initgr_rfc2307_transaction_done(struct tevent_req *subreq);
 static void
 sdap_initgr_rfc2307_update_sysdb_groups_done(struct tevent_req *subreq)
-- 
1.7.2.3

