[SSSD] [PATCHES] [PUSHED] Look up missing names in the simple access provider

Jakub Hrozek jhrozek at redhat.com
Tue Mar 19 21:42:53 UTC 2013


Hi,

the attached patches fix CVE-2013-0287. They were reviewed off-list by
Simo, Pavel and Ondra, so I pushed them already a while ago. For posterity,
I'm also sending them to the sssd-devel list. Even retroactive comments
or nacks are appreciated.

The attached patches only apply to master, sssd-1-9 versions will follow
up shortly.
-------------- next part --------------
>From 7619be9f6bf649665fcbeee9e6b120f9f9cba2a5 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 22 Feb 2013 11:01:38 +0100
Subject: [PATCH 1/4] Provide a be_get_account_info_send function

In order to resolve group names in the simple access provider we need to
contact the Data Provider in a generic fashion from the access provider.
We can't call any particular implementation (like sdap_generic_send())
because we have no idea what kind of provider is configured as the
id_provider.

This patch splits introduces the be_file_account_request() function into
the data_provider_be module and makes it public.

A future patch should make the be_get_account_info function use the
be_get_account_info_send function.
---
 src/providers/data_provider_be.c | 149 ++++++++++++++++++++++++++++++++++-----
 src/providers/dp_backend.h       |  14 ++++
 2 files changed, 144 insertions(+), 19 deletions(-)

diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c
index 08fa9bf032ba2a5531c71d8c653222478a0014e8..32548b6bef62d855ce3a52994b1bb5cedb9cd5f6 100644
--- a/src/providers/data_provider_be.c
+++ b/src/providers/data_provider_be.c
@@ -768,6 +768,34 @@ static errno_t be_initgroups_prereq(struct be_req *be_req)
 }
 
 static errno_t
+be_file_account_request(struct be_req *be_req, struct be_acct_req *ar)
+{
+    errno_t ret;
+    struct be_ctx *be_ctx = be_req->be_ctx;
+
+    be_req->req_data = ar;
+
+    /* see if we need a pre request call, only done for initgroups for now */
+    if ((ar->entry_type & 0xFF) == BE_REQ_INITGROUPS) {
+        ret = be_initgroups_prereq(be_req);
+        if (ret) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Prerequest failed"));
+            return ret;
+        }
+    }
+
+    /* process request */
+    ret = be_file_request(be_ctx, be_req,
+                          be_ctx->bet_info[BET_ID].bet_ops->handler);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to file request"));
+        return ret;
+    }
+
+    return EOK;
+}
+
+static errno_t
 split_name_extended(TALLOC_CTX *mem_ctx,
                     const char *filter,
                     char **name,
@@ -793,6 +821,106 @@ split_name_extended(TALLOC_CTX *mem_ctx,
     return EOK;
 }
 
+static void
+be_get_account_info_done(struct be_req *be_req,
+                         int dp_err, int dp_ret,
+                         const char *errstr);
+
+struct be_get_account_info_state {
+    int err_maj;
+    int err_min;
+    const char *err_msg;
+};
+
+struct tevent_req *
+be_get_account_info_send(TALLOC_CTX *mem_ctx,
+                         struct tevent_context *ev,
+                         struct be_client *becli,
+                         struct be_ctx *be_ctx,
+                         struct be_acct_req *ar)
+{
+    struct tevent_req *req;
+    struct be_get_account_info_state *state;
+    struct be_req *be_req;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct be_get_account_info_state);
+    if (!req) return NULL;
+
+    be_req = be_req_create(state, becli, be_ctx,
+                           be_get_account_info_done, req);
+    if (!be_req) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = be_file_account_request(be_req, ar);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    return req;
+
+done:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static void
+be_get_account_info_done(struct be_req *be_req,
+                         int dp_err, int dp_ret,
+                         const char *errstr)
+{
+    struct tevent_req *req;
+    struct be_get_account_info_state *state;
+
+    req = talloc_get_type(be_req->pvt, struct tevent_req);
+    state = tevent_req_data(req, struct be_get_account_info_state);
+
+    state->err_maj = dp_err;
+    state->err_min = dp_ret;
+    if (errstr) {
+        state->err_msg = talloc_strdup(state, errstr);
+        if (state->err_msg == NULL) {
+            talloc_free(be_req);
+            tevent_req_error(req, ENOMEM);
+            return;
+        }
+    }
+
+    talloc_free(be_req);
+    tevent_req_done(req);
+}
+
+errno_t be_get_account_info_recv(struct tevent_req *req,
+                                 TALLOC_CTX *mem_ctx,
+                                 int *_err_maj,
+                                 int *_err_min,
+                                 const char **_err_msg)
+{
+    struct be_get_account_info_state *state;
+
+    state = tevent_req_data(req, struct be_get_account_info_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    if (_err_maj) {
+        *_err_maj = state->err_maj;
+    }
+
+    if (_err_min) {
+        *_err_min = state->err_min;
+    }
+
+    if (_err_msg) {
+        *_err_msg = talloc_steal(mem_ctx, state->err_msg);
+    }
+
+    return EOK;
+}
+
 static int be_get_account_info(DBusMessage *message, struct sbus_connection *conn)
 {
     struct be_acct_req *req;
@@ -893,8 +1021,6 @@ static int be_get_account_info(DBusMessage *message, struct sbus_connection *con
         goto done;
     }
 
-    be_req->req_data = req;
-
     if ((attr_type != BE_ATTR_CORE) &&
         (attr_type != BE_ATTR_MEM) &&
         (attr_type != BE_ATTR_ALL)) {
@@ -941,26 +1067,11 @@ static int be_get_account_info(DBusMessage *message, struct sbus_connection *con
         goto done;
     }
 
-    /* see if we need a pre request call, only done for initgroups for now */
-    if ((type & 0xFF) == BE_REQ_INITGROUPS) {
-        ret = be_initgroups_prereq(be_req);
-        if (ret) {
-            err_maj = DP_ERR_FATAL;
-            err_min = ret;
-            err_msg = "Prerequest failed";
-            goto done;
-        }
-    }
-
-    /* process request */
-
-    ret = be_file_request(becli->bectx->bet_info[BET_ID].pvt_bet_data,
-                          be_req,
-                          becli->bectx->bet_info[BET_ID].bet_ops->handler);
+    ret = be_file_account_request(be_req, req);
     if (ret != EOK) {
         err_maj = DP_ERR_FATAL;
         err_min = ret;
-        err_msg = "Failed to file request";
+        err_msg = "Cannot file account request";
         goto done;
     }
 
diff --git a/src/providers/dp_backend.h b/src/providers/dp_backend.h
index 28fb4a22a581e2cfbc2b916026dea2ab35371d76..1a546a858999af9a7179e5366afd61e052b07ef4 100644
--- a/src/providers/dp_backend.h
+++ b/src/providers/dp_backend.h
@@ -249,4 +249,18 @@ void *be_req_get_data(struct be_req *be_req);
 void be_req_terminate(struct be_req *be_req,
                       int dp_err_type, int errnum, const char *errstr);
 
+/* Request account information */
+struct tevent_req *
+be_get_account_info_send(TALLOC_CTX *mem_ctx,
+                         struct tevent_context *ev,
+                         struct be_client *becli,
+                         struct be_ctx *be_ctx,
+                         struct be_acct_req *ar);
+
+errno_t be_get_account_info_recv(struct tevent_req *req,
+                                 TALLOC_CTX *mem_ctx,
+                                 int *_err_maj,
+                                 int *_err_min,
+                                 const char **_err_msg);
+
 #endif /* __DP_BACKEND_H___ */
-- 
1.8.1.4

-------------- next part --------------
>From 6837eee3f7f81c0ee454d3718d67d7f3cc6b48ef Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sun, 3 Mar 2013 21:43:44 +0100
Subject: [PATCH 2/4] Add unit tests for simple access test by groups

I realized that the current unit tests for the simple access provider
only tested the user directives. To have a baseline and be able to
detect new bugs in the upcoming patch, I implemented unit tests for the
group lists, too.
---
 src/tests/simple_access-tests.c | 284 +++++++++++++++++++++++++++++++++++-----
 1 file changed, 253 insertions(+), 31 deletions(-)

diff --git a/src/tests/simple_access-tests.c b/src/tests/simple_access-tests.c
index 486f87d9ac57ce7b6d7acb0d69731d7958816781..19c72b66e2db30262d99c3a677f6fbc6256da64f 100644
--- a/src/tests/simple_access-tests.c
+++ b/src/tests/simple_access-tests.c
@@ -30,39 +30,153 @@
 #include "providers/simple/simple_access.h"
 #include "tests/common.h"
 
+#define TESTS_PATH "tests_simple_access"
+#define TEST_CONF_FILE "tests_conf.ldb"
+
 const char *ulist_1[] = {"u1", "u2", NULL};
+const char *glist_1[] = {"g1", "g2", NULL};
 
-struct simple_ctx *ctx = NULL;
+struct simple_test_ctx *test_ctx = NULL;
+
+struct simple_test_ctx {
+    struct sysdb_ctx *sysdb;
+    struct confdb_ctx *confdb;
+
+    struct simple_ctx *ctx;
+};
 
 void setup_simple(void)
 {
-    fail_unless(ctx == NULL, "Simple context already initialized.");
-    ctx = talloc_zero(NULL, struct simple_ctx);
-    fail_unless(ctx != NULL, "Cannot create simple context.");
+    errno_t ret;
+    char *conf_db;
+    const char *val[2];
+    val[1] = NULL;
+
+    /* Create tests directory if it doesn't exist */
+    /* (relative to current dir) */
+    ret = mkdir(TESTS_PATH, 0775);
+    fail_if(ret == -1 && errno != EEXIST,
+            "Could not create %s directory", TESTS_PATH);
+
+    fail_unless(test_ctx == NULL, "Simple context already initialized.");
+    test_ctx = talloc_zero(NULL, struct simple_test_ctx);
+    fail_unless(test_ctx != NULL, "Cannot create simple test context.");
+
+    test_ctx->ctx = talloc_zero(test_ctx, struct simple_ctx);
+    fail_unless(test_ctx->ctx != NULL, "Cannot create simple context.");
+
+    conf_db = talloc_asprintf(test_ctx, "%s/%s", TESTS_PATH, TEST_CONF_FILE);
+    fail_if(conf_db == NULL, "Out of memory, aborting!");
+    DEBUG(SSSDBG_TRACE_LIBS, ("CONFDB: %s\n", conf_db));
+
+    /* Connect to the conf db */
+    ret = confdb_init(test_ctx, &test_ctx->confdb, conf_db);
+    fail_if(ret != EOK, "Could not initialize connection to the confdb");
+
+    val[0] = "LOCAL";
+    ret = confdb_add_param(test_ctx->confdb, true,
+                           "config/sssd", "domains", val);
+    fail_if(ret != EOK, "Could not initialize domains placeholder");
+
+    val[0] = "local";
+    ret = confdb_add_param(test_ctx->confdb, true,
+                           "config/domain/LOCAL", "id_provider", val);
+    fail_if(ret != EOK, "Could not initialize provider");
+
+    val[0] = "TRUE";
+    ret = confdb_add_param(test_ctx->confdb, true,
+                           "config/domain/LOCAL", "enumerate", val);
+    fail_if(ret != EOK, "Could not initialize LOCAL domain");
+
+    val[0] = "TRUE";
+    ret = confdb_add_param(test_ctx->confdb, true,
+                           "config/domain/LOCAL", "cache_credentials", val);
+    fail_if(ret != EOK, "Could not initialize LOCAL domain");
+
+    ret = sssd_domain_init(test_ctx, test_ctx->confdb, "local",
+                           TESTS_PATH, &test_ctx->ctx->domain);
+    fail_if(ret != EOK, "Could not initialize connection to the sysdb (%d)", ret);
+    test_ctx->sysdb = test_ctx->ctx->domain->sysdb;
+    test_ctx->ctx->domain->case_sensitive = true;
 
-    ctx->domain = talloc_zero(ctx, struct sss_domain_info);
-    fail_unless(ctx != NULL, "Cannot create domain in simple context.");
-    ctx->domain->case_sensitive = true;
 }
 
 void teardown_simple(void)
 {
     int ret;
-    fail_unless(ctx != NULL, "Simple context already freed.");
-    ret = talloc_free(ctx);
-    ctx = NULL;
+    fail_unless(test_ctx != NULL, "Simple context already freed.");
+    ret = talloc_free(test_ctx);
+    test_ctx = NULL;
     fail_unless(ret == 0, "Connot free simple context.");
 }
 
+void setup_simple_group(void)
+{
+    errno_t ret;
+
+    setup_simple();
+
+    /* Add test users u1 and u2 that would be members of test groups
+     * g1 and g2 respectively */
+    ret = sysdb_store_user(test_ctx->sysdb, test_ctx->ctx->domain,
+                           "u1", NULL, 123, 0, "u1", "/home/u1",
+                           "/bin/bash", NULL, NULL, NULL, -1, 0);
+    fail_if(ret != EOK, "Could not add u1");
+
+    ret = sysdb_store_user(test_ctx->sysdb, test_ctx->ctx->domain,
+                           "u2", NULL, 456, 0, "u1", "/home/u1",
+                           "/bin/bash", NULL, NULL, NULL, -1, 0);
+    fail_if(ret != EOK, "Could not add u2");
+
+    ret = sysdb_store_user(test_ctx->sysdb, test_ctx->ctx->domain,
+                           "u3", NULL, 789, 0, "u1", "/home/u1",
+                           "/bin/bash", NULL, NULL, NULL, -1, 0);
+    fail_if(ret != EOK, "Could not add u3");
+
+    ret = sysdb_add_group(test_ctx->sysdb, test_ctx->ctx->domain,
+                          "g1", 321, NULL, 0, 0);
+    fail_if(ret != EOK, "Could not add g1");
+
+    ret = sysdb_add_group(test_ctx->sysdb, test_ctx->ctx->domain,
+                          "g2", 654, NULL, 0, 0);
+    fail_if(ret != EOK, "Could not add g2");
+
+    ret = sysdb_add_group_member(test_ctx->sysdb, test_ctx->ctx->domain,
+                                 "g1", "u1", SYSDB_MEMBER_USER);
+    fail_if(ret != EOK, "Could not add u1 to g1");
+
+    ret = sysdb_add_group_member(test_ctx->sysdb, test_ctx->ctx->domain,
+                                 "g2", "u2", SYSDB_MEMBER_USER);
+    fail_if(ret != EOK, "Could not add u2 to g2");
+}
+
+void teardown_simple_group(void)
+{
+    errno_t ret;
+
+    ret = sysdb_delete_user(test_ctx->sysdb, test_ctx->ctx->domain, "u1", 0);
+    fail_if(ret != EOK, "Could not delete u1");
+    ret = sysdb_delete_user(test_ctx->sysdb, test_ctx->ctx->domain, "u2", 0);
+    fail_if(ret != EOK, "Could not delete u2");
+    ret = sysdb_delete_user(test_ctx->sysdb, test_ctx->ctx->domain, "u3", 0);
+    fail_if(ret != EOK, "Could not delete u3");
+    ret = sysdb_delete_group(test_ctx->sysdb, test_ctx->ctx->domain, "g1", 0);
+    fail_if(ret != EOK, "Could not delete g1");
+    ret = sysdb_delete_group(test_ctx->sysdb, test_ctx->ctx->domain, "g2", 0);
+    fail_if(ret != EOK, "Could not delete g2");
+
+    teardown_simple();
+}
+
 START_TEST(test_both_empty)
 {
     int ret;
     bool access_granted = false;
 
-    ctx->allow_users = NULL;
-    ctx->deny_users = NULL;
+    test_ctx->ctx->allow_users = NULL;
+    test_ctx->ctx->deny_users = NULL;
 
-    ret = simple_access_check(ctx, "u1", &access_granted);
+    ret = simple_access_check(test_ctx->ctx, "u1", &access_granted);
     fail_unless(ret == EOK, "access_simple_check failed.");
     fail_unless(access_granted == true, "Access denied "
                                         "while both lists are empty.");
@@ -74,15 +188,15 @@ START_TEST(test_allow_empty)
     int ret;
     bool access_granted = true;
 
-    ctx->allow_users = NULL;
-    ctx->deny_users = discard_const(ulist_1);
+    test_ctx->ctx->allow_users = NULL;
+    test_ctx->ctx->deny_users = discard_const(ulist_1);
 
-    ret = simple_access_check(ctx, "u1", &access_granted);
+    ret = simple_access_check(test_ctx->ctx, "u1", &access_granted);
     fail_unless(ret == EOK, "access_simple_check failed.");
     fail_unless(access_granted == false, "Access granted "
                                          "while user is in deny list.");
 
-    ret = simple_access_check(ctx, "u3", &access_granted);
+    ret = simple_access_check(test_ctx->ctx, "u3", &access_granted);
     fail_unless(ret == EOK, "access_simple_check failed.");
     fail_unless(access_granted == true, "Access denied "
                                          "while user is not in deny list.");
@@ -94,15 +208,15 @@ START_TEST(test_deny_empty)
     int ret;
     bool access_granted = false;
 
-    ctx->allow_users = discard_const(ulist_1);
-    ctx->deny_users = NULL;
+    test_ctx->ctx->allow_users = discard_const(ulist_1);
+    test_ctx->ctx->deny_users = NULL;
 
-    ret = simple_access_check(ctx, "u1", &access_granted);
+    ret = simple_access_check(test_ctx->ctx, "u1", &access_granted);
     fail_unless(ret == EOK, "access_simple_check failed.");
     fail_unless(access_granted == true, "Access denied "
                                         "while user is in allow list.");
 
-    ret = simple_access_check(ctx, "u3", &access_granted);
+    ret = simple_access_check(test_ctx->ctx, "u3", &access_granted);
     fail_unless(ret == EOK, "access_simple_check failed.");
     fail_unless(access_granted == false, "Access granted "
                                         "while user is not in allow list.");
@@ -114,15 +228,15 @@ START_TEST(test_both_set)
     int ret;
     bool access_granted = false;
 
-    ctx->allow_users = discard_const(ulist_1);
-    ctx->deny_users = discard_const(ulist_1);
+    test_ctx->ctx->allow_users = discard_const(ulist_1);
+    test_ctx->ctx->deny_users = discard_const(ulist_1);
 
-    ret = simple_access_check(ctx, "u1", &access_granted);
+    ret = simple_access_check(test_ctx->ctx, "u1", &access_granted);
     fail_unless(ret == EOK, "access_simple_check failed.");
     fail_unless(access_granted == false, "Access granted "
                                          "while user is in deny list.");
 
-    ret = simple_access_check(ctx, "u3", &access_granted);
+    ret = simple_access_check(test_ctx->ctx, "u3", &access_granted);
     fail_unless(ret == EOK, "access_simple_check failed.");
     fail_unless(access_granted == false, "Access granted "
                                         "while user is not in allow list.");
@@ -134,18 +248,18 @@ START_TEST(test_case)
     int ret;
     bool access_granted = false;
 
-    ctx->allow_users = discard_const(ulist_1);
-    ctx->deny_users = NULL;
+    test_ctx->ctx->allow_users = discard_const(ulist_1);
+    test_ctx->ctx->deny_users = NULL;
 
-    ret = simple_access_check(ctx, "U1", &access_granted);
+    ret = simple_access_check(test_ctx->ctx, "U1", &access_granted);
     fail_unless(ret == EOK, "access_simple_check failed.");
     fail_unless(access_granted == false, "Access granted "
                                          "for user with different case "
                                          "in case-sensitive domain");
 
-    ctx->domain->case_sensitive = false;
+    test_ctx->ctx->domain->case_sensitive = false;
 
-    ret = simple_access_check(ctx, "U1", &access_granted);
+    ret = simple_access_check(test_ctx->ctx, "U1", &access_granted);
     fail_unless(ret == EOK, "access_simple_check failed.");
     fail_unless(access_granted == true, "Access denied "
                                         "for user with different case "
@@ -153,11 +267,95 @@ START_TEST(test_case)
 }
 END_TEST
 
+START_TEST(test_group_allow_empty)
+{
+    int ret;
+    bool access_granted = true;
+
+    test_ctx->ctx->allow_groups = NULL;
+    test_ctx->ctx->deny_groups = discard_const(glist_1);
+
+    ret = simple_access_check(test_ctx->ctx, "u1", &access_granted);
+    fail_unless(ret == EOK, "access_simple_check failed.");
+    fail_unless(access_granted == false, "Access granted "
+                                         "while group is in deny list.");
+
+    ret = simple_access_check(test_ctx->ctx, "u3", &access_granted);
+    fail_unless(ret == EOK, "access_simple_check failed.");
+    fail_unless(access_granted == true, "Access denied "
+                                         "while group is not in deny list.");
+}
+END_TEST
+
+START_TEST(test_group_deny_empty)
+{
+    int ret;
+    bool access_granted = false;
+
+    test_ctx->ctx->allow_groups = discard_const(glist_1);
+    test_ctx->ctx->deny_groups = NULL;
+
+    ret = simple_access_check(test_ctx->ctx, "u1", &access_granted);
+    fail_unless(ret == EOK, "access_simple_check failed.");
+    fail_unless(access_granted == true, "Access denied "
+                                        "while group is in allow list.");
+
+    ret = simple_access_check(test_ctx->ctx, "u3", &access_granted);
+    fail_unless(ret == EOK, "access_simple_check failed.");
+    fail_unless(access_granted == false, "Access granted "
+                                        "while group is not in allow list.");
+}
+END_TEST
+
+START_TEST(test_group_both_set)
+{
+    int ret;
+    bool access_granted = false;
+
+    test_ctx->ctx->allow_groups = discard_const(ulist_1);
+    test_ctx->ctx->deny_groups = discard_const(ulist_1);
+
+    ret = simple_access_check(test_ctx->ctx, "u1", &access_granted);
+    fail_unless(ret == EOK, "access_simple_check failed.");
+    fail_unless(access_granted == false, "Access granted "
+                                         "while group is in deny list.");
+
+    ret = simple_access_check(test_ctx->ctx, "u3", &access_granted);
+    fail_unless(ret == EOK, "access_simple_check failed.");
+    fail_unless(access_granted == false, "Access granted "
+                                        "while group is not in allow list.");
+}
+END_TEST
+
+START_TEST(test_group_case)
+{
+    int ret;
+    bool access_granted = false;
+
+    test_ctx->ctx->allow_groups = discard_const(ulist_1);
+    test_ctx->ctx->deny_groups = NULL;
+
+    ret = simple_access_check(test_ctx->ctx, "U1", &access_granted);
+    fail_unless(ret == EOK, "access_simple_check failed.");
+    fail_unless(access_granted == false, "Access granted "
+                                         "for group with different case "
+                                         "in case-sensitive domain");
+
+    test_ctx->ctx->domain->case_sensitive = false;
+
+    ret = simple_access_check(test_ctx->ctx, "U1", &access_granted);
+    fail_unless(ret == EOK, "access_simple_check failed.");
+    fail_unless(access_granted == true, "Access denied "
+                                        "for group with different case "
+                                        "in case-insensitive domain");
+}
+END_TEST
+
 Suite *access_simple_suite (void)
 {
     Suite *s = suite_create("access_simple");
 
-    TCase *tc_allow_deny = tcase_create("allow/deny");
+    TCase *tc_allow_deny = tcase_create("user allow/deny");
     tcase_add_checked_fixture(tc_allow_deny, setup_simple, teardown_simple);
     tcase_add_test(tc_allow_deny, test_both_empty);
     tcase_add_test(tc_allow_deny, test_allow_empty);
@@ -166,6 +364,15 @@ Suite *access_simple_suite (void)
     tcase_add_test(tc_allow_deny, test_case);
     suite_add_tcase(s, tc_allow_deny);
 
+    TCase *tc_grp_allow_deny = tcase_create("group allow/deny");
+    tcase_add_checked_fixture(tc_grp_allow_deny,
+                              setup_simple_group, teardown_simple_group);
+    tcase_add_test(tc_grp_allow_deny, test_group_allow_empty);
+    tcase_add_test(tc_grp_allow_deny, test_group_deny_empty);
+    tcase_add_test(tc_grp_allow_deny, test_group_both_set);
+    tcase_add_test(tc_grp_allow_deny, test_group_case);
+    suite_add_tcase(s, tc_grp_allow_deny);
+
     return s;
 }
 
@@ -174,6 +381,7 @@ int main(int argc, const char *argv[])
     int opt;
     poptContext pc;
     int number_failed;
+    int ret;
 
     struct poptOption long_options[] = {
         POPT_AUTOHELP
@@ -205,6 +413,20 @@ int main(int argc, const char *argv[])
     srunner_run_all(sr, CK_ENV);
     number_failed = srunner_ntests_failed(sr);
     srunner_free(sr);
+
+    ret = unlink(TESTS_PATH"/"TEST_CONF_FILE);
+    if (ret != EOK) {
+        fprintf(stderr, "Could not delete the test config ldb file (%d) (%s)\n",
+                errno, strerror(errno));
+        return EXIT_FAILURE;
+    }
+    ret = unlink(TESTS_PATH"/"LOCAL_SYSDB_FILE);
+    if (ret != EOK) {
+        fprintf(stderr, "Could not delete the test config ldb file (%d) (%s)\n",
+                errno, strerror(errno));
+        return EXIT_FAILURE;
+    }
+
     return (number_failed==0 ? EXIT_SUCCESS : EXIT_FAILURE);
 }
 
-- 
1.8.1.4

-------------- next part --------------
>From 6569d57e3bc168e6e83d70333b48c5cb43aa04c4 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 4 Mar 2013 16:37:04 +0100
Subject: [PATCH 3/4] Do not compile main() in DP if UNIT_TESTING is defined

The simple access provider unit tests now need to link against the Data
Provider when they start using the be_file_account_request() function.
But then we would start having conflicts as at least the main()
functions would clash.

If UNIT_TESTING is defined, then the data_provider_be.c module does not
contain the main() function and can be linked against directly from
another module that contains its own main() function
---
 src/providers/data_provider_be.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c
index 32548b6bef62d855ce3a52994b1bb5cedb9cd5f6..df077b249df6cf79d0a50c6fe2c28955b63c7316 100644
--- a/src/providers/data_provider_be.c
+++ b/src/providers/data_provider_be.c
@@ -2686,6 +2686,7 @@ fail:
     return ret;
 }
 
+#ifndef UNIT_TESTING
 int main(int argc, const char *argv[])
 {
     int opt;
@@ -2766,6 +2767,7 @@ int main(int argc, const char *argv[])
 
     return 0;
 }
+#endif
 
 static int data_provider_res_init(DBusMessage *message,
                                   struct sbus_connection *conn)
-- 
1.8.1.4

-------------- next part --------------
>From c0bca1722d6f9dfb654ad78397be70f79ff39af1 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sat, 23 Feb 2013 10:44:54 +0100
Subject: [PATCH 4/4] Resolve GIDs in the simple access provider

Changes the simple access provider's interface to be asynchronous. When
the simple access provider encounters a group that has gid, but no
meaningful name, it attempts to resolve the name using the
be_file_account_request function.

Some providers (like the AD provider) might perform initgroups
without resolving the group names. In order for the simple access
provider to work correctly, we need to resolve the groups before
performing the access check. In AD provider, the situation is
even more tricky b/c the groups HAVE name, but their name
attribute is set to SID and they are set as non-POSIX
---
 Makefile.am                                |  15 +-
 src/providers/simple/simple_access.c       |  31 +-
 src/providers/simple/simple_access.h       |  11 +-
 src/providers/simple/simple_access_check.c | 737 ++++++++++++++++++++++++-----
 src/tests/simple_access-tests.c            | 373 ++++++++++-----
 5 files changed, 933 insertions(+), 234 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 265da9c7923568c7d422ba80743af3eea0ffcade..211472195b7d210bc1a1ed3f59612d7007dbc28b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1032,15 +1032,22 @@ ad_ldap_opt_tests_LDADD = \
 
 simple_access_tests_SOURCES = \
     src/tests/simple_access-tests.c \
-    src/tests/common.c \
-    src/providers/simple/simple_access_check.c
+    src/providers/simple/simple_access_check.c \
+    src/providers/data_provider_be.c \
+    src/providers/data_provider_fo.c \
+    src/providers/data_provider_callbacks.c \
+    $(SSSD_FAILOVER_OBJ)
 simple_access_tests_CFLAGS = \
     $(AM_CFLAGS) \
-    $(CHECK_CFLAGS)
+    $(CHECK_CFLAGS) \
+    -DUNIT_TESTING
 simple_access_tests_LDADD = \
     $(SSSD_LIBS) \
+    $(CARES_LIBS) \
     $(CHECK_LIBS) \
-    libsss_util.la
+    $(PAM_LIBS) \
+    libsss_util.la \
+    libsss_test_common.la
 
 util_tests_SOURCES = \
     src/tests/util-tests.c
diff --git a/src/providers/simple/simple_access.c b/src/providers/simple/simple_access.c
index 3dcea86915c538fc9603647597386690885214b5..e617e93dc7972dbb31b20aac11fa722d0ca7b748 100644
--- a/src/providers/simple/simple_access.c
+++ b/src/providers/simple/simple_access.c
@@ -32,12 +32,13 @@
 #define CONFDB_SIMPLE_ALLOW_GROUPS "simple_allow_groups"
 #define CONFDB_SIMPLE_DENY_GROUPS "simple_deny_groups"
 
+static void simple_access_check(struct tevent_req *req);
+
 void simple_access_handler(struct be_req *be_req)
 {
     struct be_ctx *be_ctx = be_req_get_be_ctx(be_req);
-    int ret;
-    bool access_granted = false;
     struct pam_data *pd;
+    struct tevent_req *req;
     struct simple_ctx *ctx;
 
     pd = talloc_get_type(be_req_get_data(be_req), struct pam_data);
@@ -53,7 +54,30 @@ void simple_access_handler(struct be_req *be_req)
     ctx = talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
                           struct simple_ctx);
 
-    ret = simple_access_check(ctx, pd->user, &access_granted);
+    req = simple_access_check_send(be_req, be_ctx->ev, ctx, pd->user);
+    if (!req) {
+        pd->pam_status = PAM_SYSTEM_ERR;
+        goto done;
+    }
+    tevent_req_set_callback(req, simple_access_check, be_req);
+    return;
+
+done:
+    be_req_terminate(be_req, DP_ERR_OK, pd->pam_status, NULL);
+}
+
+static void simple_access_check(struct tevent_req *req)
+{
+    bool access_granted = false;
+    errno_t ret;
+    struct pam_data *pd;
+    struct be_req *be_req;
+
+    be_req = tevent_req_callback_data(req, struct be_req);
+    pd = talloc_get_type(be_req_get_data(be_req), struct pam_data);
+
+    ret = simple_access_check_recv(req, &access_granted);
+    talloc_free(req);
     if (ret != EOK) {
         pd->pam_status = PAM_SYSTEM_ERR;
         goto done;
@@ -87,6 +111,7 @@ int sssm_simple_access_init(struct be_ctx *bectx, struct bet_ops **ops,
     }
 
     ctx->domain = bectx->domain;
+    ctx->be_ctx = bectx;
 
     /* Users */
     ret = confdb_get_string_as_list(bectx->cdb, ctx, bectx->conf_path,
diff --git a/src/providers/simple/simple_access.h b/src/providers/simple/simple_access.h
index 2ddf27692d6f0d1fccfe30e707e4281d811aaa1b..15dfaceb2d9a6670d3559e4a945c2c7a633fad44 100644
--- a/src/providers/simple/simple_access.h
+++ b/src/providers/simple/simple_access.h
@@ -26,6 +26,7 @@
 
 struct simple_ctx {
     struct sss_domain_info *domain;
+    struct be_ctx *be_ctx;
 
     char **allow_users;
     char **deny_users;
@@ -33,6 +34,12 @@ struct simple_ctx {
     char **deny_groups;
 };
 
-errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
-                            bool *access_granted);
+struct tevent_req *simple_access_check_send(TALLOC_CTX *mem_ctx,
+                                            struct tevent_context *ev,
+                                            struct simple_ctx *ctx,
+                                            const char *username);
+
+errno_t simple_access_check_recv(struct tevent_req *req,
+                                 bool *access_granted);
+
 #endif /* __SIMPLE_ACCESS_H__ */
diff --git a/src/providers/simple/simple_access_check.c b/src/providers/simple/simple_access_check.c
index cb5f5282597a39876d4cdc359396349bdec29883..6475e773e058ff20f804c450f17f177711a92644 100644
--- a/src/providers/simple/simple_access_check.c
+++ b/src/providers/simple/simple_access_check.c
@@ -19,36 +19,40 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#include "providers/dp_backend.h"
 #include "providers/simple/simple_access.h"
 #include "util/sss_utf8.h"
 #include "db/sysdb.h"
 
-errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
-                            bool *access_granted)
+static bool
+is_posix(const struct ldb_message *group)
 {
-    int i, j;
-    errno_t ret;
-    TALLOC_CTX *tmp_ctx = NULL;
-    const char *user_attrs[] = { SYSDB_MEMBEROF,
-                                 SYSDB_GIDNUM,
-                                 NULL };
-    const char *group_attrs[] = { SYSDB_NAME,
-                                  NULL };
-    struct ldb_message *msg;
-    struct ldb_message_element *el;
-    char **groups;
-    const char *primary_group;
-    gid_t gid;
-    bool matched;
+    const char *val;
+
+    val = ldb_msg_find_attr_as_string(group, SYSDB_POSIX, NULL);
+    if (!val || /* Groups are posix by default */
+        strcasecmp(val, "TRUE") == 0) {
+        return true;
+    }
+
+    return false;
+}
+
+/* Returns EOK if the result is definitive, EAGAIN if only partial result
+ */
+static errno_t
+simple_check_users(struct simple_ctx *ctx, const char *username,
+                   bool *access_granted)
+{
+    int i;
     bool cs = ctx->domain->case_sensitive;
 
-    *access_granted = false;
-
     /* First, check whether the user is in the allowed users list */
     if (ctx->allow_users != NULL) {
         for(i = 0; ctx->allow_users[i] != NULL; i++) {
             if (sss_string_equal(cs, username, ctx->allow_users[i])) {
-                DEBUG(9, ("User [%s] found in allow list, access granted.\n",
+                DEBUG(SSSDBG_TRACE_LIBS,
+                      ("User [%s] found in allow list, access granted.\n",
                       username));
 
                 /* Do not return immediately on explicit allow
@@ -62,6 +66,8 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
         /* If neither allow rule is in place, we'll assume allowed
          * unless a deny rule disables us below.
          */
+        DEBUG(SSSDBG_TRACE_LIBS,
+              ("No allow rule, assumuing allow unless explicitly denied\n"));
         *access_granted = true;
     }
 
@@ -69,7 +75,8 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
     if (ctx->deny_users != NULL) {
         for(i = 0; ctx->deny_users[i] != NULL; i++) {
             if (sss_string_equal(cs, username, ctx->deny_users[i])) {
-                DEBUG(9, ("User [%s] found in deny list, access denied.\n",
+                DEBUG(SSSDBG_TRACE_LIBS,
+                      ("User [%s] found in deny list, access denied.\n",
                       username));
 
                 /* Return immediately on explicit denial */
@@ -79,97 +86,16 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
         }
     }
 
-    if (!ctx->allow_groups && !ctx->deny_groups) {
-        /* There are no group restrictions, so just return
-         * here with whatever we've decided.
-         */
-        return EOK;
-    }
-
-    /* Now get a list of this user's groups and check those against the
-     * simple_allow_groups list.
-     */
-    tmp_ctx = talloc_new(NULL);
-    if (!tmp_ctx) {
-        ret = ENOMEM;
-        goto done;
-    }
-
-    ret = sysdb_search_user_by_name(tmp_ctx, ctx->domain->sysdb, ctx->domain,
-                                    username, user_attrs, &msg);
-    if (ret != EOK) {
-        DEBUG(1, ("Could not look up username [%s]: [%d][%s]\n",
-                  username, ret, strerror(ret)));
-        goto done;
-    }
-
-    /* Construct a list of the user's groups */
-    el = ldb_msg_find_element(msg, SYSDB_MEMBEROF);
-    if (el && el->num_values) {
-        /* Get the groups from the memberOf entries
-         * Allocate the array with room for both the NULL
-         * terminator and the primary group
-         */
-        groups = talloc_array(tmp_ctx, char *, el->num_values + 2);
-        if (!groups) {
-            ret = ENOMEM;
-            goto done;
-        }
-
-        for (j = 0; j < el->num_values; j++) {
-            ret = sysdb_group_dn_name(
-                    ctx->domain->sysdb, tmp_ctx,
-                    (char *)el->values[j].data,
-                    &groups[j]);
-            if (ret != EOK) {
-                goto done;
-            }
-        }
-    } else {
-        /* User is not a member of any groups except primary */
-        groups = talloc_array(tmp_ctx, char *, 2);
-        if (!groups) {
-            ret = ENOMEM;
-            goto done;
-        }
-        j = 0;
-    }
-
-    /* Get the user's primary group */
-    gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
-    if (!gid) {
-        ret = EINVAL;
-        goto done;
-    }
-    talloc_zfree(msg);
-
-    ret = sysdb_search_group_by_gid(tmp_ctx, ctx->domain->sysdb, ctx->domain,
-                                    gid, group_attrs, &msg);
-    if (ret != EOK) {
-        DEBUG(1, ("Could not look up primary group [%lu]: [%d][%s]\n",
-                  gid, ret, strerror(ret)));
-        /* We have to treat this as non-fatal, because the primary
-         * group may be local to the machine and not available in
-         * our ID provider.
-         */
-    } else {
-        primary_group = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
-        if (!primary_group) {
-            ret = EINVAL;
-            goto done;
-        }
-
-        groups[j] = talloc_strdup(tmp_ctx, primary_group);
-        if (!groups[j]) {
-            ret = ENOMEM;
-            goto done;
-        }
-        j++;
-
-        talloc_zfree(msg);
-    }
-
-    groups[j] = NULL;
+    return EAGAIN;
+}
+
+static errno_t
+simple_check_groups(struct simple_ctx *ctx, const char *username,
+                    const char **group_names, bool *access_granted)
+{
+    bool matched;
+    int i, j;
+    bool cs = ctx->domain->case_sensitive;
 
     /* Now process allow and deny group rules
      * If access was already granted above, we'll skip
@@ -178,8 +104,8 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
     if (ctx->allow_groups && !*access_granted) {
         matched = false;
         for (i = 0; ctx->allow_groups[i]; i++) {
-            for(j = 0; groups[j]; j++) {
-                if (sss_string_equal(cs, groups[j], ctx->allow_groups[i])) {
+            for(j = 0; group_names[j]; j++) {
+                if (sss_string_equal(cs, group_names[j], ctx->allow_groups[i])) {
                     matched = true;
                     break;
                 }
@@ -189,6 +115,9 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
              * processing early
              */
             if (matched) {
+                DEBUG(SSSDBG_TRACE_LIBS,
+                      ("Group [%s] found in allow list, access granted.\n",
+                      group_names[j]));
                 *access_granted = true;
                 break;
             }
@@ -199,8 +128,8 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
     if (ctx->deny_groups) {
         matched = false;
         for (i = 0; ctx->deny_groups[i]; i++) {
-            for(j = 0; groups[j]; j++) {
-                if (sss_string_equal(cs, groups[j], ctx->deny_groups[i])) {
+            for(j = 0; group_names[j]; j++) {
+                if (sss_string_equal(cs, group_names[j], ctx->deny_groups[i])) {
                     matched = true;
                     break;
                 }
@@ -210,15 +139,587 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
              * processing early
              */
             if (matched) {
+                DEBUG(SSSDBG_TRACE_LIBS,
+                      ("Group [%s] found in deny list, access denied.\n",
+                      group_names[j]));
                 *access_granted = false;
                 break;
             }
         }
     }
 
-    ret = EOK;
+    return EOK;
+}
+
+struct simple_resolve_group_state {
+    gid_t gid;
+    struct simple_ctx *ctx;
+
+    const char *name;
+};
+
+static errno_t
+simple_resolve_group_check(struct simple_resolve_group_state *state);
+static void simple_resolve_group_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+simple_resolve_group_send(TALLOC_CTX *mem_ctx,
+                          struct tevent_context *ev,
+                          struct simple_ctx *ctx,
+                          gid_t gid)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct simple_resolve_group_state *state;
+    struct be_acct_req *ar;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct simple_resolve_group_state);
+    if (!req) return NULL;
+
+    state->gid = gid;
+    state->ctx = ctx;
+
+    /* First check if the group was updated already. If it was (maybe its
+     * parent was updated first), then just shortcut */
+    ret = simple_resolve_group_check(state);
+    if (ret == EOK) {
+        DEBUG(SSSDBG_TRACE_LIBS, ("Group already updated\n"));
+        ret = EOK;
+        goto done;
+    } else if (ret != EAGAIN) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Cannot check if group was already updated\n"));
+        goto done;
+    }
+    /* EAGAIN - still needs update */
+
+    ar = talloc(state, struct be_acct_req);
+    if (!ar) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ar->entry_type = BE_REQ_GROUP;
+    ar->attr_type = BE_ATTR_CORE;
+    ar->filter_type = BE_FILTER_IDNUM;
+    ar->filter_value = talloc_asprintf(ar, "%llu", (unsigned long long) gid);
+    ar->domain = talloc_strdup(ar, ctx->domain->name);
+    if (!ar->domain || !ar->filter_value) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    subreq = be_get_account_info_send(state, ev, NULL, ctx->be_ctx, ar);
+    if (!subreq) {
+        ret = ENOMEM;
+        goto done;
+    }
+    tevent_req_set_callback(subreq, simple_resolve_group_done, req);
+
+    return req;
+
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t
+simple_resolve_group_check(struct simple_resolve_group_state *state)
+{
+    errno_t ret;
+    struct ldb_message *group;
+    const char *group_attrs[] = { SYSDB_NAME, SYSDB_POSIX,
+                                  SYSDB_GIDNUM, NULL };
+
+    /* Check the cache by GID again and fetch the name */
+    ret = sysdb_search_group_by_gid(state, state->ctx->domain->sysdb,
+                                    state->ctx->domain, state->gid,
+                                    group_attrs, &group);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+               ("Could not look up group by gid [%lu]: [%d][%s]\n",
+               state->gid, ret, strerror(ret)));
+        return ret;
+    }
+
+    state->name = ldb_msg_find_attr_as_string(group, SYSDB_NAME, NULL);
+    if (!state->name) {
+        DEBUG(SSSDBG_OP_FAILURE, ("No group name\n"));
+        return ENOENT;
+    }
+
+    if (is_posix(group) == false) {
+        DEBUG(SSSDBG_TRACE_LIBS,
+              ("The group is still non-POSIX\n"));
+        return EAGAIN;
+    }
+
+    DEBUG(SSSDBG_TRACE_LIBS, ("Got POSIX group\n"));
+    return EOK;
+}
+
+static void simple_resolve_group_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct simple_resolve_group_state *state;
+    int err_maj;
+    int err_min;
+    errno_t ret;
+    const char *err_msg;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct simple_resolve_group_state);
+
+    ret = be_get_account_info_recv(subreq, state,
+                                   &err_maj, &err_min, &err_msg);
+    talloc_zfree(subreq);
+    if (ret) {
+        DEBUG(SSSDBG_OP_FAILURE, ("be_get_account_info_recv failed\n"));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    if (err_maj) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("Cannot refresh data from DP: %u,%u: %s\n",
+              err_maj, err_min, err_msg));
+        tevent_req_error(req, EIO);
+        return;
+    }
+
+    /* Check the cache by GID again and fetch the name */
+    ret = simple_resolve_group_check(state);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Refresh failed\n"));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+static errno_t
+simple_resolve_group_recv(struct tevent_req *req,
+                          TALLOC_CTX *mem_ctx,
+                          const char **name)
+{
+    struct simple_resolve_group_state *state;
+
+    state = tevent_req_data(req, struct simple_resolve_group_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *name = talloc_strdup(mem_ctx, state->name);
+    return EOK;
+}
+
+struct simple_check_groups_state {
+    struct tevent_context *ev;
+    struct simple_ctx *ctx;
+
+    gid_t *lookup_gids;
+    size_t num_gids;
+    size_t giter;
+
+    const char **group_names;
+    size_t num_names;
+};
+
+static void simple_check_get_groups_next(struct tevent_req *subreq);
+
+static errno_t
+simple_check_get_groups_primary(struct simple_check_groups_state *state,
+                                gid_t gid);
+static errno_t
+simple_check_process_group(struct simple_check_groups_state *state,
+                           struct ldb_message *group);
+
+static struct tevent_req *
+simple_check_get_groups_send(TALLOC_CTX *mem_ctx,
+                             struct tevent_context *ev,
+                             struct simple_ctx *ctx,
+                             const char *username)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct simple_check_groups_state *state;
+    const char *attrs[] = { SYSDB_NAME, SYSDB_POSIX, SYSDB_GIDNUM, NULL };
+    size_t group_count;
+    struct ldb_message *user;
+    struct ldb_message **groups;
+    int i;
+    gid_t gid;
+    char *cname;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct simple_check_groups_state);
+    if (!req) return NULL;
+
+    state->ev = ev;
+    state->ctx = ctx;
+
+    cname = sss_get_cased_name(state, username, ctx->domain->case_sensitive);
+    if (!cname) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_LIBS, ("Looking up groups for user %s\n", cname));
+
+    ret = sysdb_search_user_by_name(state, ctx->domain->sysdb, ctx->domain,
+                                    cname, attrs, &user);
+    if (ret == ENOENT) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("No such user %s\n", cname));
+        goto done;
+    } else if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Could not look up username [%s]: [%d][%s]\n",
+              username, ret, strerror(ret)));
+        goto done;
+    }
+
+    ret = sysdb_asq_search(state, ctx->domain->sysdb,
+                           user->dn, NULL, SYSDB_MEMBEROF,
+                           attrs, &group_count, &groups);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC,
+          ("User %s is a member of %d supplemental groups\n",
+           cname, group_count));
+
+    /* One extra space for terminator, one extra space for private group */
+    state->group_names = talloc_zero_array(state, const char *, group_count + 2);
+    state->lookup_gids = talloc_zero_array(state, gid_t, group_count + 2);
+    if (!state->group_names || !state->lookup_gids) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    for (i=0; i < group_count; i++) {
+        /* Some providers (like the AD provider) might perform initgroups
+         * without resolving the group names. In order for the simple access
+         * provider to work correctly, we need to resolve the groups before
+         * performing the access check. In AD provider, the situation is
+         * even more tricky b/c the groups HAVE name, but their name
+         * attribute is set to SID and they are set as non-POSIX
+         */
+        ret = simple_check_process_group(state, groups[i]);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
+    gid = ldb_msg_find_attr_as_uint64(user, SYSDB_GIDNUM, 0);
+    if (!gid) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("User %s has no gid?\n", cname));
+        ret = EINVAL;
+        goto done;
+    }
+
+    ret = simple_check_get_groups_primary(state, gid);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    if (state->num_gids == 0) {
+        /* If all groups could have been resolved by name, we are
+         * done
+         */
+        DEBUG(SSSDBG_TRACE_FUNC, ("All groups had name attribute\n"));
+        ret = EOK;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, ("Need to resolve %d groups\n", state->num_gids));
+    state->giter = 0;
+    subreq = simple_resolve_group_send(req, state->ev, state->ctx,
+                                       state->lookup_gids[state->giter]);
+    if (!subreq) {
+        ret = ENOMEM;
+        goto done;
+    }
+    tevent_req_set_callback(subreq, simple_check_get_groups_next, req);
+
+    return req;
 
 done:
-    talloc_free(tmp_ctx);
-    return ret;
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static void simple_check_get_groups_next(struct tevent_req *subreq)
+{
+    struct tevent_req *req =
+                        tevent_req_callback_data(subreq, struct tevent_req);
+    struct simple_check_groups_state *state =
+                        tevent_req_data(req, struct simple_check_groups_state);
+    errno_t ret;
+
+    ret = simple_resolve_group_recv(subreq, state->group_names,
+                                    &state->group_names[state->num_names]);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Could not resolve name of group with GID %llu\n",
+              state->lookup_gids[state->giter]));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    state->num_names++;
+    state->giter++;
+
+    if (state->giter < state->num_gids) {
+        subreq = simple_resolve_group_send(req, state->ev, state->ctx,
+                                           state->lookup_gids[state->giter]);
+        if (!subreq) {
+            tevent_req_error(req, ENOMEM);
+            return;
+        }
+        tevent_req_set_callback(subreq, simple_check_get_groups_next, req);
+        return;
+    }
+
+    DEBUG(SSSDBG_TRACE_INTERNAL, ("All groups resolved. Done.\n"));
+    tevent_req_done(req);
+}
+
+static errno_t
+simple_check_process_group(struct simple_check_groups_state *state,
+                           struct ldb_message *group)
+{
+    const char *name;
+    gid_t gid;
+    bool posix;
+
+    posix = is_posix(group);
+    name = ldb_msg_find_attr_as_string(group, SYSDB_NAME, NULL);
+    gid = ldb_msg_find_attr_as_uint64(group, SYSDB_GIDNUM, 0);
+
+    /* With the current sysdb layout, every group has a name */
+    if (name == NULL) {
+        return EINVAL;
+    }
+
+    if (gid == 0) {
+        if (posix == true) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("POSIX group without GID\n"));
+            return EINVAL;
+        }
+
+        /* Non-posix group with a name. Still can be used for access
+         * control as the name should point to the real name, no SID
+         */
+        state->group_names[state->num_names] = talloc_strdup(state->group_names,
+                                                             name);
+        if (!state->group_names[state->num_names]) {
+            return ENOMEM;
+        }
+        DEBUG(SSSDBG_TRACE_INTERNAL, ("Adding group %s\n", name));
+        state->num_names++;
+        return EOK;
+    }
+
+    /* Here are only groups with a name and gid. POSIX group can already
+     * be used, non-POSIX groups can be resolved */
+    if (posix) {
+        state->group_names[state->num_names] = talloc_strdup(state->group_names,
+                                                             name);
+        if (!state->group_names[state->num_names]) {
+            return ENOMEM;
+        }
+        DEBUG(SSSDBG_TRACE_INTERNAL, ("Adding group %s\n", name));
+        state->num_names++;
+        return EOK;
+    }
+
+    /* Non-posix group with a GID. Needs resolving */
+    state->lookup_gids[state->num_gids] = gid;
+    DEBUG(SSSDBG_TRACE_INTERNAL, ("Adding GID %llu\n", gid));
+    state->num_gids++;
+    return EOK;
+}
+
+static errno_t
+simple_check_get_groups_primary(struct simple_check_groups_state *state,
+                                gid_t gid)
+{
+    errno_t ret;
+    const char *group_attrs[] = { SYSDB_NAME, SYSDB_POSIX,
+                                  SYSDB_GIDNUM, NULL };
+    struct ldb_message *msg;
+
+    ret = sysdb_search_group_by_gid(state, state->ctx->domain->sysdb,
+                                    state->ctx->domain,
+                                    gid, group_attrs, &msg);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+               ("Could not look up primary group [%lu]: [%d][%s]\n",
+               gid, ret, strerror(ret)));
+        /* We have to treat this as non-fatal, because the primary
+         * group may be local to the machine and not available in
+         * our ID provider.
+         */
+    } else {
+        ret = simple_check_process_group(state, msg);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("Cannot process primary group\n"));
+            return ret;
+        }
+    }
+
+    return EOK;
+}
+
+static errno_t
+simple_check_get_groups_recv(struct tevent_req *req,
+                             TALLOC_CTX *mem_ctx,
+                             const char ***_group_names)
+{
+    struct simple_check_groups_state *state;
+
+    state = tevent_req_data(req, struct simple_check_groups_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *_group_names = talloc_steal(mem_ctx, state->group_names);
+    return EOK;
+}
+
+struct simple_access_check_state {
+    bool access_granted;
+    struct simple_ctx *ctx;
+    const char *username;
+
+    const char **group_names;
+};
+
+static void simple_access_check_done(struct tevent_req *subreq);
+
+struct tevent_req *simple_access_check_send(TALLOC_CTX *mem_ctx,
+                                            struct tevent_context *ev,
+                                            struct simple_ctx *ctx,
+                                            const char *username)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct simple_access_check_state *state;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct simple_access_check_state);
+    if (!req) return NULL;
+
+    state->access_granted = false;
+    state->ctx = ctx;
+    state->username = talloc_strdup(state, username);
+    if (!state->username) {
+        ret = ENOMEM;
+        goto immediate;
+    }
+
+    DEBUG(SSSDBG_FUNC_DATA, ("Simple access check for %s\n", username));
+
+    ret = simple_check_users(ctx, username, &state->access_granted);
+    if (ret != EAGAIN) {
+        /* Both access denied and an error */
+        goto immediate;
+    }
+
+    if (!ctx->allow_groups && !ctx->deny_groups) {
+        /* There are no group restrictions, so just return
+         * here with whatever we've decided.
+         */
+        DEBUG(SSSDBG_TRACE_LIBS, ("No group restrictions, end request\n"));
+        ret = EOK;
+        goto immediate;
+    }
+
+    /* The group names might not be available. Fire a request to
+     * gather them. In most cases, the request will just shortcut
+     */
+    subreq = simple_check_get_groups_send(state, ev, ctx, username);
+    if (!subreq) {
+        ret = EIO;
+        goto immediate;
+    }
+    tevent_req_set_callback(subreq, simple_access_check_done, req);
+
+    return req;
+
+immediate:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+
+static void simple_access_check_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req =
+                        tevent_req_callback_data(subreq, struct tevent_req);
+    struct simple_access_check_state *state =
+                        tevent_req_data(req, struct simple_access_check_state);
+    errno_t ret;
+
+    /* We know the names now. Run the check. */
+    ret = simple_check_get_groups_recv(subreq, state, &state->group_names);
+    talloc_zfree(subreq);
+    if (ret == ENOENT) {
+        /* If the user wasn't found, just shortcut */
+        state->access_granted = false;
+        tevent_req_done(req);
+        return;
+    } else if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Could not collect groups of user %s\n", state->username));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    ret = simple_check_groups(state->ctx, state->username,
+                              state->group_names, &state->access_granted);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    /* Now just return whatever we decided */
+    DEBUG(SSSDBG_TRACE_INTERNAL, ("Group check done\n"));
+    tevent_req_done(req);
+}
+
+errno_t simple_access_check_recv(struct tevent_req *req, bool *access_granted)
+{
+    struct simple_access_check_state *state =
+                        tevent_req_data(req, struct simple_access_check_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    DEBUG(SSSDBG_TRACE_LIBS,
+          ("Access %sgranted\n", state->access_granted ? "" : "not "));
+    if (access_granted) {
+        *access_granted = state->access_granted;
+    }
+
+    return EOK;
 }
diff --git a/src/tests/simple_access-tests.c b/src/tests/simple_access-tests.c
index 19c72b66e2db30262d99c3a677f6fbc6256da64f..1c2d1a9ea83a005ecb3a8ecc847df496f383961f 100644
--- a/src/tests/simple_access-tests.c
+++ b/src/tests/simple_access-tests.c
@@ -35,16 +35,40 @@
 
 const char *ulist_1[] = {"u1", "u2", NULL};
 const char *glist_1[] = {"g1", "g2", NULL};
+const char *glist_1_case[] = {"G1", "G2", NULL};
 
 struct simple_test_ctx *test_ctx = NULL;
 
 struct simple_test_ctx {
     struct sysdb_ctx *sysdb;
     struct confdb_ctx *confdb;
+    struct tevent_context *ev;
+    bool done;
+    int error;
 
+    bool access_granted;
     struct simple_ctx *ctx;
 };
 
+static int test_loop(struct simple_test_ctx *tctx)
+{
+    while (!tctx->done)
+        tevent_loop_once(tctx->ev);
+
+    return tctx->error;
+}
+
+static void simple_access_check_done(struct tevent_req *req)
+{
+    struct simple_test_ctx *tctx =
+                        tevent_req_callback_data(req, struct simple_test_ctx);
+
+
+    tctx->error = simple_access_check_recv(req, &tctx->access_granted);
+    talloc_free(req);
+    tctx->done = true;
+}
+
 void setup_simple(void)
 {
     errno_t ret;
@@ -52,19 +76,22 @@ void setup_simple(void)
     const char *val[2];
     val[1] = NULL;
 
-    /* Create tests directory if it doesn't exist */
-    /* (relative to current dir) */
-    ret = mkdir(TESTS_PATH, 0775);
-    fail_if(ret == -1 && errno != EEXIST,
-            "Could not create %s directory", TESTS_PATH);
-
     fail_unless(test_ctx == NULL, "Simple context already initialized.");
     test_ctx = talloc_zero(NULL, struct simple_test_ctx);
     fail_unless(test_ctx != NULL, "Cannot create simple test context.");
 
+    test_ctx->ev = tevent_context_init(test_ctx);
+    fail_unless(test_ctx->ev != NULL, "Cannot create tevent context.");
+
     test_ctx->ctx = talloc_zero(test_ctx, struct simple_ctx);
     fail_unless(test_ctx->ctx != NULL, "Cannot create simple context.");
 
+    /* Create tests directory if it doesn't exist */
+    /* (relative to current dir) */
+    ret = mkdir(TESTS_PATH, 0775);
+    fail_if(ret == -1 && errno != EEXIST,
+            "Could not create %s directory", TESTS_PATH);
+
     conf_db = talloc_asprintf(test_ctx, "%s/%s", TESTS_PATH, TEST_CONF_FILE);
     fail_if(conf_db == NULL, "Out of memory, aborting!");
     DEBUG(SSSDBG_TRACE_LIBS, ("CONFDB: %s\n", conf_db));
@@ -98,7 +125,7 @@ void setup_simple(void)
     fail_if(ret != EOK, "Could not initialize connection to the sysdb (%d)", ret);
     test_ctx->sysdb = test_ctx->ctx->domain->sysdb;
     test_ctx->ctx->domain->case_sensitive = true;
-
+    test_ctx->ctx->domain->mpg = false; /* Simulate an LDAP domain better */
 }
 
 void teardown_simple(void)
@@ -118,18 +145,22 @@ void setup_simple_group(void)
 
     /* Add test users u1 and u2 that would be members of test groups
      * g1 and g2 respectively */
+    ret = sysdb_add_group(test_ctx->sysdb, test_ctx->ctx->domain,
+                          "pvt", 999, NULL, 0, 0);
+    fail_if(ret != EOK, "Could not add private group");
+
     ret = sysdb_store_user(test_ctx->sysdb, test_ctx->ctx->domain,
-                           "u1", NULL, 123, 0, "u1", "/home/u1",
+                           "u1", NULL, 123, 999, "u1", "/home/u1",
                            "/bin/bash", NULL, NULL, NULL, -1, 0);
     fail_if(ret != EOK, "Could not add u1");
 
     ret = sysdb_store_user(test_ctx->sysdb, test_ctx->ctx->domain,
-                           "u2", NULL, 456, 0, "u1", "/home/u1",
+                           "u2", NULL, 456, 999, "u1", "/home/u1",
                            "/bin/bash", NULL, NULL, NULL, -1, 0);
     fail_if(ret != EOK, "Could not add u2");
 
     ret = sysdb_store_user(test_ctx->sysdb, test_ctx->ctx->domain,
-                           "u3", NULL, 789, 0, "u1", "/home/u1",
+                           "u3", NULL, 789, 999, "u1", "/home/u1",
                            "/bin/bash", NULL, NULL, NULL, -1, 0);
     fail_if(ret != EOK, "Could not add u3");
 
@@ -164,190 +195,317 @@ void teardown_simple_group(void)
     fail_if(ret != EOK, "Could not delete g1");
     ret = sysdb_delete_group(test_ctx->sysdb, test_ctx->ctx->domain, "g2", 0);
     fail_if(ret != EOK, "Could not delete g2");
+    ret = sysdb_delete_group(test_ctx->sysdb, test_ctx->ctx->domain, "pvt", 0);
+    fail_if(ret != EOK, "Could not delete pvt");
 
     teardown_simple();
 }
 
 START_TEST(test_both_empty)
 {
-    int ret;
-    bool access_granted = false;
+    struct tevent_req *req;
 
     test_ctx->ctx->allow_users = NULL;
     test_ctx->ctx->deny_users = NULL;
 
-    ret = simple_access_check(test_ctx->ctx, "u1", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == true, "Access denied "
-                                        "while both lists are empty.");
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "u1");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == true,
+                "Access denied while both lists are empty.");
 }
 END_TEST
 
 START_TEST(test_allow_empty)
 {
-    int ret;
-    bool access_granted = true;
+    struct tevent_req *req;
 
     test_ctx->ctx->allow_users = NULL;
     test_ctx->ctx->deny_users = discard_const(ulist_1);
 
-    ret = simple_access_check(test_ctx->ctx, "u1", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == false, "Access granted "
-                                         "while user is in deny list.");
-
-    ret = simple_access_check(test_ctx->ctx, "u3", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == true, "Access denied "
-                                         "while user is not in deny list.");
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "u1");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+    test_ctx->done = false;
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == false,
+                "Access granted while user is in deny list.");
+
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "u3");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == true,
+                "Access denied while user is not in deny list.");
 }
 END_TEST
 
 START_TEST(test_deny_empty)
 {
-    int ret;
-    bool access_granted = false;
+    struct tevent_req *req;
 
     test_ctx->ctx->allow_users = discard_const(ulist_1);
     test_ctx->ctx->deny_users = NULL;
 
-    ret = simple_access_check(test_ctx->ctx, "u1", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == true, "Access denied "
-                                        "while user is in allow list.");
-
-    ret = simple_access_check(test_ctx->ctx, "u3", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == false, "Access granted "
-                                        "while user is not in allow list.");
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "u1");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+    test_ctx->done = false;
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == true,
+                "Access denied while user is in allow list.");
+
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "u3");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == false,
+                "Access granted while user is not in allow list.");
 }
 END_TEST
 
 START_TEST(test_both_set)
 {
-    int ret;
-    bool access_granted = false;
+    struct tevent_req *req;
 
     test_ctx->ctx->allow_users = discard_const(ulist_1);
     test_ctx->ctx->deny_users = discard_const(ulist_1);
 
-    ret = simple_access_check(test_ctx->ctx, "u1", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == false, "Access granted "
-                                         "while user is in deny list.");
-
-    ret = simple_access_check(test_ctx->ctx, "u3", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == false, "Access granted "
-                                        "while user is not in allow list.");
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "u1");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+    test_ctx->done = false;
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == false,
+                "Access granted while user is in deny list.");
+
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "u3");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == false,
+                "Access granted while user is not in allow list.");
 }
 END_TEST
 
 START_TEST(test_case)
 {
-    int ret;
-    bool access_granted = false;
+    struct tevent_req *req;
 
     test_ctx->ctx->allow_users = discard_const(ulist_1);
     test_ctx->ctx->deny_users = NULL;
 
-    ret = simple_access_check(test_ctx->ctx, "U1", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == false, "Access granted "
-                                         "for user with different case "
-                                         "in case-sensitive domain");
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "U1");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+    test_ctx->done = false;
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == false,
+                "Access granted for user with different case "
+                "in case-sensitive domain");
 
     test_ctx->ctx->domain->case_sensitive = false;
 
-    ret = simple_access_check(test_ctx->ctx, "U1", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == true, "Access denied "
-                                        "for user with different case "
-                                        "in case-insensitive domain");
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "U1");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+    test_ctx->done = false;
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == true,
+                "Access denied for user with different case "
+                "in case-sensitive domain");
 }
 END_TEST
 
+START_TEST(test_unknown_user)
+{
+    struct tevent_req *req;
+
+    test_ctx->ctx->allow_users = discard_const(ulist_1);
+    test_ctx->ctx->deny_users = NULL;
+
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "foo");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+    test_ctx->done = false;
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == false,
+                "Access granted for user not present in domain");
+}
+END_TEST
+
+
 START_TEST(test_group_allow_empty)
 {
-    int ret;
-    bool access_granted = true;
+    struct tevent_req *req;
 
     test_ctx->ctx->allow_groups = NULL;
     test_ctx->ctx->deny_groups = discard_const(glist_1);
 
-    ret = simple_access_check(test_ctx->ctx, "u1", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == false, "Access granted "
-                                         "while group is in deny list.");
-
-    ret = simple_access_check(test_ctx->ctx, "u3", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == true, "Access denied "
-                                         "while group is not in deny list.");
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "u1");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+    test_ctx->done = false;
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == false,
+                "Access granted while group is in deny list.");
+
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "u3");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == true,
+                "Access denied while group is not in deny list.");
 }
 END_TEST
 
 START_TEST(test_group_deny_empty)
 {
-    int ret;
-    bool access_granted = false;
+    struct tevent_req *req;
 
     test_ctx->ctx->allow_groups = discard_const(glist_1);
     test_ctx->ctx->deny_groups = NULL;
 
-    ret = simple_access_check(test_ctx->ctx, "u1", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == true, "Access denied "
-                                        "while group is in allow list.");
-
-    ret = simple_access_check(test_ctx->ctx, "u3", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == false, "Access granted "
-                                        "while group is not in allow list.");
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "u1");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+    test_ctx->done = false;
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == true,
+                "Access denied while user is in allow list.");
+
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "u3");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == false,
+                "Access granted while user is not in allow list.");
 }
 END_TEST
 
 START_TEST(test_group_both_set)
 {
-    int ret;
-    bool access_granted = false;
+    struct tevent_req *req;
 
     test_ctx->ctx->allow_groups = discard_const(ulist_1);
     test_ctx->ctx->deny_groups = discard_const(ulist_1);
 
-    ret = simple_access_check(test_ctx->ctx, "u1", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == false, "Access granted "
-                                         "while group is in deny list.");
-
-    ret = simple_access_check(test_ctx->ctx, "u3", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == false, "Access granted "
-                                        "while group is not in allow list.");
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "u1");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+    test_ctx->done = false;
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == false,
+                "Access granted while user is in deny list.");
+
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "u3");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == false,
+                "Access granted while user is not in allow list.");
 }
 END_TEST
 
 START_TEST(test_group_case)
 {
-    int ret;
-    bool access_granted = false;
+    struct tevent_req *req;
 
-    test_ctx->ctx->allow_groups = discard_const(ulist_1);
+    test_ctx->ctx->allow_groups = discard_const(glist_1_case);
     test_ctx->ctx->deny_groups = NULL;
 
-    ret = simple_access_check(test_ctx->ctx, "U1", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == false, "Access granted "
-                                         "for group with different case "
-                                         "in case-sensitive domain");
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "U1");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+    test_ctx->done = false;
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == false,
+                "Access granted for user with different case "
+                "in case-sensitive domain");
 
     test_ctx->ctx->domain->case_sensitive = false;
 
-    ret = simple_access_check(test_ctx->ctx, "U1", &access_granted);
-    fail_unless(ret == EOK, "access_simple_check failed.");
-    fail_unless(access_granted == true, "Access denied "
-                                        "for group with different case "
-                                        "in case-insensitive domain");
+    req = simple_access_check_send(test_ctx, test_ctx->ev,
+                                   test_ctx->ctx, "U1");
+    fail_unless(test_ctx != NULL, "Cannot create request\n");
+    tevent_req_set_callback(req, simple_access_check_done, test_ctx);
+
+    test_loop(test_ctx);
+    test_ctx->done = false;
+
+    fail_unless(test_ctx->error == EOK, "access_simple_check failed.");
+    fail_unless(test_ctx->access_granted == true,
+                "Access denied for user with different case "
+                "in case-sensitive domain");
 }
 END_TEST
 
@@ -362,6 +520,7 @@ Suite *access_simple_suite (void)
     tcase_add_test(tc_allow_deny, test_deny_empty);
     tcase_add_test(tc_allow_deny, test_both_set);
     tcase_add_test(tc_allow_deny, test_case);
+    tcase_add_test(tc_allow_deny, test_unknown_user);
     suite_add_tcase(s, tc_allow_deny);
 
     TCase *tc_grp_allow_deny = tcase_create("group allow/deny");
-- 
1.8.1.4



More information about the sssd-devel mailing list