From 03b69729320d9e1128631c82e19822482034683a Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Mon, 20 Sep 2010 11:52:27 -0400
Subject: [PATCH 1/2] sysdb interface for adding incomplete group entries

Useful for optimizing the initgroups operation.
---
 src/db/sysdb.h                 |    8 ++
 src/db/sysdb_ops.c             |  175 ++++++++++++++++++++++++++++++++++++++++
 src/responder/nss/nsssrv_cmd.c |   13 ++-
 src/tests/sysdb-tests.c        |   72 ++++++++++++++++
 4 files changed, 263 insertions(+), 5 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 04ce49e..620628f 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -483,6 +483,14 @@ struct tevent_req *sysdb_add_group_send(TALLOC_CTX *mem_ctx,
                                         int cache_timeout);
 int sysdb_add_group_recv(struct tevent_req *req);
 
+/* Add a fake (expired) group */
+struct tevent_req *sysdb_add_incomplete_group_send(TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct sysdb_handle *handle,
+                                             struct sss_domain_info *domain,
+                                             const char *name, gid_t gid);
+int sysdb_add_incomplete_group_recv(struct tevent_req *req);
+
 /* mod_op must be either LDB_FLAG_MOD_ADD or LDB_FLAG_MOD_DELETE */
 struct tevent_req *sysdb_mod_group_member_send(TALLOC_CTX *mem_ctx,
                                                struct tevent_context *ev,
diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
index 7ab53e1..8fc8e7a 100644
--- a/src/db/sysdb_ops.c
+++ b/src/db/sysdb_ops.c
@@ -2448,6 +2448,181 @@ int sysdb_add_group_recv(struct tevent_req *req)
 }
 
 
+/* =Add-A-Fake-Group====================================================== */
+
+struct sysdb_add_incomplete_group_state {
+    struct tevent_context *ev;
+    struct sysdb_handle *handle;
+    struct sss_domain_info *domain;
+
+    const char *name;
+    gid_t gid;
+};
+
+static void sysdb_add_incomplete_group_user_check(struct tevent_req *subreq);
+static void sysdb_add_incomplete_group_basic_done(struct tevent_req *subreq);
+static void sysdb_add_incomplete_group_set_attrs_done(struct tevent_req *subreq);
+
+struct tevent_req *sysdb_add_incomplete_group_send(TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct sysdb_handle *handle,
+                                             struct sss_domain_info *domain,
+                                             const char *name, gid_t gid)
+{
+    struct tevent_req *req, *subreq;
+    struct sysdb_add_incomplete_group_state *state;
+    int ret;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct sysdb_add_incomplete_group_state);
+    if (!req) return NULL;
+
+    state->ev = ev;
+    state->handle = handle;
+    state->domain = domain;
+    state->name = name;
+    state->gid = gid;
+
+    if (handle->ctx->mpg) {
+        /* In MPG domains you can't have groups with the same name as users,
+         * search if a user with the same name exists.
+         * Don't worry about groups, if we try to add a group with the same
+         * name the operation will fail */
+
+        subreq = sysdb_search_user_by_name_send(state, ev, NULL, handle,
+                                                domain, name, NULL);
+        if (!subreq) {
+            ERROR_OUT(ret, ENOMEM, fail);
+        }
+        tevent_req_set_callback(subreq, sysdb_add_incomplete_group_user_check, req);
+        return req;
+    }
+
+    /* try to add the group */
+    subreq = sysdb_add_basic_group_send(state, ev, handle,
+                                        domain, name, gid);
+    if (!subreq) {
+        ERROR_OUT(ret, ENOMEM, fail);
+    }
+    tevent_req_set_callback(subreq, sysdb_add_incomplete_group_basic_done, req);
+    return req;
+
+fail:
+    DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
+    tevent_req_error(req, ret);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static void sysdb_add_incomplete_group_user_check(struct tevent_req *subreq)
+{
+
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct sysdb_add_incomplete_group_state *state = tevent_req_data(req,
+                                           struct sysdb_add_incomplete_group_state);
+    struct ldb_message *msg;
+    int ret;
+
+    /* We can succeed only if we get an ENOENT error, which means no users
+     * with the same name exist.
+     * If any other error is returned fail as well. */
+    ret = sysdb_search_user_recv(subreq, state, &msg);
+    talloc_zfree(subreq);
+    if (ret != ENOENT) {
+        if (ret == EOK) ret = EEXIST;
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    /* try to add the group */
+    subreq = sysdb_add_basic_group_send(state, state->ev,
+                                        state->handle, state->domain,
+                                        state->name, state->gid);
+    if (!subreq) {
+        DEBUG(6, ("Error: Out of memory\n"));
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+    tevent_req_set_callback(subreq, sysdb_add_incomplete_group_basic_done, req);
+}
+
+static void sysdb_add_incomplete_group_basic_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct sysdb_add_incomplete_group_state *state = tevent_req_data(req,
+                                           struct sysdb_add_incomplete_group_state);
+    int ret;
+    struct sysdb_attrs *attrs;
+    time_t now;
+
+    ret = sysdb_add_basic_group_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret) {
+        DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    attrs = sysdb_new_attrs(state);
+    if (!attrs) {
+        DEBUG(6, ("Error: Out of memory\n"));
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+
+    now = time(NULL);
+
+    ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+    if (ret) {
+        DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+                                 now-1);
+    if (ret) {
+        DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    subreq = sysdb_set_group_attr_send(state, state->ev,
+                                       state->handle, state->domain,
+                                       state->name, attrs,
+                                       SYSDB_MOD_REP);
+    if (!subreq) {
+        DEBUG(6, ("Error: Out of memory\n"));
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+    tevent_req_set_callback(subreq, sysdb_add_incomplete_group_set_attrs_done, req);
+}
+
+static void sysdb_add_incomplete_group_set_attrs_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    int ret;
+
+    ret = sysdb_set_group_attr_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret) {
+        DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+int sysdb_add_incomplete_group_recv(struct tevent_req *req)
+{
+    return sysdb_op_default_recv(req);
+}
+
 /* =Add-Or-Remove-Group-Memeber=========================================== */
 
 /* mod_op must be either SYSDB_MOD_ADD or SYSDB_MOD_DEL */
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 45334e2..a67f07d 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -1537,7 +1537,7 @@ static int fill_grent(struct sss_packet *packet,
         name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
         gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
         if (!name || !gid) {
-            DEBUG(1, ("Incomplete group object for %s[%llu]! Skipping\n",
+            DEBUG(1, ("Incomplete or fake group object for %s[%llu]! Skipping\n",
                       name?name:"<NULL>", (unsigned long long int)gid));
             continue;
         }
@@ -2764,7 +2764,7 @@ static int fill_initgr(struct sss_packet *packet, struct ldb_result *res)
     uint8_t *body;
     size_t blen;
     gid_t gid;
-    int ret, i, num;
+    int ret, i, num, total;
 
     if (res->count == 0) {
         return ENOENT;
@@ -2780,16 +2780,19 @@ static int fill_initgr(struct sss_packet *packet, struct ldb_result *res)
     sss_packet_get_body(packet, &body, &blen);
 
     /* skip first entry, it's the user entry */
+    total = 0;
     for (i = 0; i < num; i++) {
         gid = ldb_msg_find_attr_as_uint64(res->msgs[i + 1], SYSDB_GIDNUM, 0);
         if (!gid) {
-            DEBUG(1, ("Incomplete group object for initgroups! Aborting\n"));
-            return EFAULT;
+            DEBUG(1, ("Incomplete or fake group object for initgroups! "
+                      "Skipping\n"));
+            continue;
         }
         ((uint32_t *)body)[2 + i] = gid;
+        total++;
     }
 
-    ((uint32_t *)body)[0] = num; /* num results */
+    ((uint32_t *)body)[0] = total; /* num results */
     ((uint32_t *)body)[1] = 0; /* reserved */
 
     return EOK;
diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
index 5bdc005..d4d031b 100644
--- a/src/tests/sysdb-tests.c
+++ b/src/tests/sysdb-tests.c
@@ -511,6 +511,39 @@ static void test_store_group_done(struct tevent_req *subreq)
     return test_return(data, ret);
 }
 
+static void test_add_incomplete_group_done(struct tevent_req *subreq);
+
+static void test_add_incomplete_group(struct tevent_req *req)
+{
+    struct test_data *data = tevent_req_callback_data(req, struct test_data);
+    struct tevent_req *subreq;
+    int ret;
+
+    ret = sysdb_transaction_recv(req, data, &data->handle);
+    if (ret != EOK) {
+        return test_return(data, ret);
+    }
+
+    subreq = sysdb_add_incomplete_group_send(data, data->ev, data->handle,
+                                       data->ctx->domain, data->groupname,
+                                       data->gid);
+    if (!subreq) {
+        test_return(data, ret);
+    }
+    tevent_req_set_callback(subreq, test_add_incomplete_group_done, data);
+}
+
+static void test_add_incomplete_group_done(struct tevent_req *subreq)
+{
+    struct test_data *data = tevent_req_callback_data(subreq, struct test_data);
+    int ret;
+
+    ret = sysdb_add_group_recv(subreq);
+    talloc_zfree(subreq);
+
+    return test_return(data, ret);
+}
+
 static void test_remove_group_done(struct tevent_req *subreq);
 
 static void test_remove_group(struct tevent_req *req)
@@ -1179,6 +1212,41 @@ START_TEST (test_sysdb_store_group)
 }
 END_TEST
 
+START_TEST (test_sysdb_add_incomplete_group)
+{
+    struct sysdb_test_ctx *test_ctx;
+    struct test_data *data;
+    struct tevent_req *req;
+    int ret;
+
+    /* Setup */
+    ret = setup_sysdb_tests(&test_ctx);
+    if (ret != EOK) {
+        fail("Could not set up the test");
+        return;
+    }
+
+    data = talloc_zero(test_ctx, struct test_data);
+    data->ctx = test_ctx;
+    data->ev = test_ctx->ev;
+    data->gid = _i;
+    data->groupname = talloc_asprintf(data, "testgroup%d", _i);
+
+    req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb);
+    if (!req) {
+        ret = ENOMEM;
+    }
+
+    if (ret == EOK) {
+        tevent_req_set_callback(req, test_add_incomplete_group, data);
+        ret = test_loop(data);
+    }
+
+    fail_if(ret != EOK, "Could not store incomplete group #%d", _i);
+    talloc_free(test_ctx);
+}
+END_TEST
+
 START_TEST (test_sysdb_remove_local_user)
 {
     struct sysdb_test_ctx *test_ctx;
@@ -3372,6 +3440,10 @@ Suite *create_sysdb_suite(void)
     /* Create a new group */
     tcase_add_loop_test(tc_sysdb, test_sysdb_store_group, 28010, 28020);
 
+    /* Create and remove a incomplete group */
+    tcase_add_loop_test(tc_sysdb, test_sysdb_add_incomplete_group, 28020, 28030);
+    tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_group, 28020, 28030);
+
     /* Verify the groups were added */
 
     /* Verify the groups can be queried by GID */
-- 
1.7.2.3

