[sssd/f17] Rebase to 1.9.4

Jakub Hrozek jhrozek at fedoraproject.org
Mon Mar 25 13:45:08 UTC 2013


commit 40c3ae988fbeb03f3ae1b9b31d6288ae3d8f3a39
Author: Jakub Hrozek <jhrozek at redhat.com>
Date:   Mon Mar 25 13:20:29 2013 +0100

    Rebase to 1.9.4

 .gitignore                                         |    1 +
 0001-krb-recreate-ccache-if-it-was-deleted.patch   |   39 +
 ...replace-invalid-characters-with-underscor.patch |   69 +
 ...-Fix-the-krb5-password-expiration-warning.patch |   39 +
 ...it-tests-for-simple-access-test-by-groups.patch |  412 +++++
 ...ile-main-in-DP-if-UNIT_TESTING-is-defined.patch |   40 +
 ...ovide-a-be_get_account_info_send-function.patch |  236 +++
 ...esolve-GIDs-in-the-simple-access-provider.patch | 1622 ++++++++++++++++++++
 sources                                            |    2 +-
 sssd.spec                                          |  117 ++-
 10 files changed, 2557 insertions(+), 20 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index e99f7d2..9f0a81c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,4 @@ sssd-1.2.91.tar.gz
 /sssd-1.8.4.tar.gz
 /sssd-1.8.5.tar.gz
 /sssd-1.8.6.tar.gz
+/sssd-1.9.4.tar.gz
diff --git a/0001-krb-recreate-ccache-if-it-was-deleted.patch b/0001-krb-recreate-ccache-if-it-was-deleted.patch
new file mode 100644
index 0000000..51f8634
--- /dev/null
+++ b/0001-krb-recreate-ccache-if-it-was-deleted.patch
@@ -0,0 +1,39 @@
+From cae3bf6af22855adc8dd7b270e11207f0a33c385 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina at redhat.com>
+Date: Wed, 30 Jan 2013 13:45:27 +0100
+Subject: [PATCH] krb: recreate ccache if it was deleted
+
+https://fedorahosted.org/sssd/ticket/1512
+
+If directory where a ccache file was stored was missing and user
+was still logged in, we erroneously considered the ccache file
+still active. Thus the ccache file was not recreated and user was
+unable to login.
+---
+ src/providers/krb5/krb5_utils.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
+index b770714be030076203b6578f90ef726226cb72f8..7b56be52497ae66fa536b76ca0561ec3cc3208ce 100644
+--- a/src/providers/krb5/krb5_utils.c
++++ b/src/providers/krb5/krb5_utils.c
+@@ -770,8 +770,15 @@ cc_residual_is_used(uid_t uid, const char *ccname,
+ 
+     ret = lstat(ccname, &stat_buf);
+ 
+-    if (ret == -1 && errno != ENOENT) {
++    if (ret == -1) {
+         ret = errno;
++        if (ret == ENOENT) {
++            DEBUG(SSSDBG_FUNC_DATA, ("Cache file [%s] does not exists, "
++                                     "it will be recreated\n", ccname));
++            *result = false;
++            return EOK;
++        }
++
+         DEBUG(SSSDBG_OP_FAILURE,
+               ("stat failed [%d][%s].\n", ret, strerror(ret)));
+         return ret;
+-- 
+1.7.11.7
+
diff --git a/0002-subdomains-replace-invalid-characters-with-underscor.patch b/0002-subdomains-replace-invalid-characters-with-underscor.patch
new file mode 100644
index 0000000..c2564cc
--- /dev/null
+++ b/0002-subdomains-replace-invalid-characters-with-underscor.patch
@@ -0,0 +1,69 @@
+From a0388dc52f5461f72f8221c9bb7c92008e1fe2c5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina at redhat.com>
+Date: Fri, 1 Feb 2013 12:17:47 +0100
+Subject: [PATCH] subdomains: replace invalid characters with underscore in
+ krb5 mapping file name
+
+https://fedorahosted.org/sssd/ticket/1795
+
+Only alpha-numeric chars, dashes and underscores are allowed in
+krb5 include directory.
+---
+ src/providers/ipa/ipa_subdomains.c | 26 +++++++++++++++++++++++++-
+ 1 file changed, 25 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
+index ef6195d19de72be7fd2b12a309b33fcf20e0e3a1..f959c4e6eb1d830e3990f552c9f4cf962298ef48 100644
+--- a/src/providers/ipa/ipa_subdomains.c
++++ b/src/providers/ipa/ipa_subdomains.c
+@@ -287,22 +287,46 @@ ipa_subdomains_write_mappings(struct sss_domain_info *domain,
+     errno_t err;
+     TALLOC_CTX *tmp_ctx;
+     const char *mapping_file;
++    char *sanitized_domain;
+     char *tmp_file = NULL;
+     int fd = -1;
+     mode_t old_mode;
+     FILE *fstream = NULL;
+     size_t i;
+ 
++    if (domain == NULL || domain->name == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("No domain name provided\n"));
++        return EINVAL;
++    }
++
+     tmp_ctx = talloc_new(NULL);
+     if (!tmp_ctx) return ENOMEM;
+ 
++    sanitized_domain = talloc_strdup(tmp_ctx, domain->name);
++    if (sanitized_domain == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_strdup() failed\n"));
++        return ENOMEM;
++    }
++
++    /* only alpha-numeric chars, dashes and underscores are allowed in
++     * krb5 include directory */
++    for (i = 0; sanitized_domain[i] != '\0'; i++) {
++        if (!isalnum(sanitized_domain[i])
++                && sanitized_domain[i] != '-' && sanitized_domain[i] != '_') {
++            sanitized_domain[i] = '_';
++        }
++    }
++
+     mapping_file = talloc_asprintf(tmp_ctx, "%s/domain_realm_%s",
+-                                   IPA_SUBDOMAIN_MAPPING_DIR, domain->name);
++                                   IPA_SUBDOMAIN_MAPPING_DIR, sanitized_domain);
+     if (!mapping_file) {
+         ret = ENOMEM;
+         goto done;
+     }
+ 
++    DEBUG(SSSDBG_FUNC_DATA, ("Mapping file for domain [%s] is [%s]\n",
++                             domain->name, mapping_file));
++
+     tmp_file = talloc_asprintf(tmp_ctx, "%sXXXXXX", mapping_file);
+     if (tmp_file == NULL) {
+         ret = ENOMEM;
+-- 
+1.7.11.7
+
diff --git a/0003-Fix-the-krb5-password-expiration-warning.patch b/0003-Fix-the-krb5-password-expiration-warning.patch
new file mode 100644
index 0000000..3996494
--- /dev/null
+++ b/0003-Fix-the-krb5-password-expiration-warning.patch
@@ -0,0 +1,39 @@
+From e354a96bbca5da8525ee51f91907e75af897b856 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek at redhat.com>
+Date: Thu, 14 Feb 2013 10:13:59 +0100
+Subject: [PATCH] Fix the krb5 password expiration warning
+
+https://fedorahosted.org/sssd/ticket/1808
+---
+ src/confdb/confdb.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
+index 31efd9443be8490715961c8a45f9352bd1ade653..31c48bd28aee37008687e7e255ebf2ef2d79798a 100644
+--- a/src/confdb/confdb.c
++++ b/src/confdb/confdb.c
+@@ -1020,7 +1020,11 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
+         goto done;
+     }
+ 
+-    /* Set the PAM warning time, if specified */
++    /* Set the PAM warning time, if specified. If not specified, pass on
++     * the "not set" value of "-1" which means "use provider default". The
++     * value 0 means "always display the warning if server sends one" */
++    domain->pwd_expiration_warning = -1;
++
+     val = ldb_msg_find_attr_as_int(res->msgs[0],
+                                    CONFDB_DOMAIN_PWD_EXPIRATION_WARNING,
+                                    -1);
+@@ -1035,6 +1039,8 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
+     }
+ 
+     if (val > 0) {
++        DEBUG(SSSDBG_CONF_SETTINGS,
++              ("Setting domain password expiration warning to %d days\n", val));
+         /* The value is in days, transform it to seconds */
+         domain->pwd_expiration_warning = val * 24 * 3600;
+     }
+-- 
+1.8.1.2
+
diff --git a/0004-Add-unit-tests-for-simple-access-test-by-groups.patch b/0004-Add-unit-tests-for-simple-access-test-by-groups.patch
new file mode 100644
index 0000000..a219fb0
--- /dev/null
+++ b/0004-Add-unit-tests-for-simple-access-test-by-groups.patch
@@ -0,0 +1,412 @@
+From 754b09b5444e6da88ed58d6deaed8b815e268b6b 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 | 283 +++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 252 insertions(+), 31 deletions(-)
+
+diff --git a/src/tests/simple_access-tests.c b/src/tests/simple_access-tests.c
+index c61814eb54c1aa5138a1b45653f9384228c5456a..577c6d334edda513fd0f1e42a859ea333ba5ba23 100644
+--- a/src/tests/simple_access-tests.c
++++ b/src/tests/simple_access-tests.c
+@@ -30,39 +30,152 @@
+ #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;
+ 
+-    ctx->domain = talloc_zero(ctx, struct sss_domain_info);
+-    fail_unless(ctx != NULL, "Cannot create domain in simple context.");
+-    ctx->domain->case_sensitive = true;
++    /* 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 = sysdb_init_domain_and_sysdb(test_ctx, test_ctx->confdb, "local",
++                                      TESTS_PATH,
++                                      &test_ctx->ctx->domain, &test_ctx->ctx->sysdb);
++    fail_if(ret != EOK, "Could not initialize connection to the sysdb (%d)", ret);
++    test_ctx->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->ctx->sysdb,
++                           "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->ctx->sysdb,
++                           "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->ctx->sysdb,
++                           "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->ctx->sysdb,
++                          "g1", 321, NULL, 0, 0);
++    fail_if(ret != EOK, "Could not add g1");
++
++    ret = sysdb_add_group(test_ctx->ctx->sysdb,
++                          "g2", 654, NULL, 0, 0);
++    fail_if(ret != EOK, "Could not add g2");
++
++    ret = sysdb_add_group_member(test_ctx->ctx->sysdb,
++                                 "g1", "u1", SYSDB_MEMBER_USER);
++    fail_if(ret != EOK, "Could not add u1 to g1");
++
++    ret = sysdb_add_group_member(test_ctx->ctx->sysdb,
++                                 "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->ctx->sysdb, "u1", 0);
++    fail_if(ret != EOK, "Could not delete u1");
++    ret = sysdb_delete_user(test_ctx->ctx->sysdb, "u2", 0);
++    fail_if(ret != EOK, "Could not delete u2");
++    ret = sysdb_delete_user(test_ctx->ctx->sysdb, "u3", 0);
++    fail_if(ret != EOK, "Could not delete u3");
++    ret = sysdb_delete_group(test_ctx->ctx->sysdb, "g1", 0);
++    fail_if(ret != EOK, "Could not delete g1");
++    ret = sysdb_delete_group(test_ctx->ctx->sysdb, "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 +187,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 +207,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 +227,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 +247,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 +266,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 +363,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 +380,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 +412,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
+
diff --git a/0005-Do-not-compile-main-in-DP-if-UNIT_TESTING-is-defined.patch b/0005-Do-not-compile-main-in-DP-if-UNIT_TESTING-is-defined.patch
new file mode 100644
index 0000000..73d5b84
--- /dev/null
+++ b/0005-Do-not-compile-main-in-DP-if-UNIT_TESTING-is-defined.patch
@@ -0,0 +1,40 @@
+From 26590d31f492dbbd36be6d0bde46a4bd3b221edb 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 f85a04d09b5b41b17be611c333324f7207242979..33590aeef0231427642916c6a2f9bc391c165c21 100644
+--- a/src/providers/data_provider_be.c
++++ b/src/providers/data_provider_be.c
+@@ -2651,6 +2651,7 @@ fail:
+     return ret;
+ }
+ 
++#ifndef UNIT_TESTING
+ int main(int argc, const char *argv[])
+ {
+     int opt;
+@@ -2732,6 +2733,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
+
diff --git a/0006-Provide-a-be_get_account_info_send-function.patch b/0006-Provide-a-be_get_account_info_send-function.patch
new file mode 100644
index 0000000..766e21c
--- /dev/null
+++ b/0006-Provide-a-be_get_account_info_send-function.patch
@@ -0,0 +1,236 @@
+From b63830b142053f99bfe954d4be5a2b0f68ce3a93 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 | 153 ++++++++++++++++++++++++++++++++++-----
+ src/providers/dp_backend.h       |  15 ++++
+ 2 files changed, 149 insertions(+), 19 deletions(-)
+
+diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c
+index b261bf8d456829a513ec352c8290d2011bd3526a..f85a04d09b5b41b17be611c333324f7207242979 100644
+--- a/src/providers/data_provider_be.c
++++ b/src/providers/data_provider_be.c
+@@ -717,6 +717,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,
+@@ -742,6 +770,110 @@ 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 = talloc_zero(mem_ctx, struct be_req);
++    if (be_req == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    be_req->becli = becli;
++    be_req->be_ctx = be_ctx;
++    be_req->fn = be_get_account_info_done;
++    be_req->pvt = req;
++
++    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;
+@@ -845,8 +977,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)) {
+@@ -893,26 +1023,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 58a9b7490df8aab06a2a15f8c0fed9ac5ed33600..743b6f4ffe73fe9ec7404218184a7133aba054c6 100644
+--- a/src/providers/dp_backend.h
++++ b/src/providers/dp_backend.h
+@@ -258,4 +258,19 @@ int be_fo_run_callbacks_at_next_request(struct be_ctx *ctx,
+                                         const char *service_name);
+ 
+ void reset_fo(struct be_ctx *be_ctx);
++
++/* 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
+
diff --git a/0007-Resolve-GIDs-in-the-simple-access-provider.patch b/0007-Resolve-GIDs-in-the-simple-access-provider.patch
new file mode 100644
index 0000000..c06f900
--- /dev/null
+++ b/0007-Resolve-GIDs-in-the-simple-access-provider.patch
@@ -0,0 +1,1622 @@
+From 8b8019fe3dd1564fba657e219ec20ff816c7ffdb 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                                |  17 +-
+ src/providers/simple/simple_access.c       | 230 ++-------
+ src/providers/simple/simple_access.h       |  11 +-
+ src/providers/simple/simple_access_check.c | 723 +++++++++++++++++++++++++++++
+ src/tests/simple_access-tests.c            | 373 ++++++++++-----
+ 5 files changed, 1040 insertions(+), 314 deletions(-)
+ create mode 100644 src/providers/simple/simple_access_check.c
+
+diff --git a/Makefile.am b/Makefile.am
+index dc0465a8cb6b441cfc810a5da8c448b034cc4b7a..eea535e81ac572713810f8b97dacc58ed221b440 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -1008,14 +1008,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.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
+@@ -1347,7 +1355,8 @@ libsss_proxy_la_LDFLAGS = \
+     -module
+ 
+ libsss_simple_la_SOURCES = \
+-    src/providers/simple/simple_access.c
++    src/providers/simple/simple_access.c \
++    src/providers/simple/simple_access_check.c
+ libsss_simple_la_CFLAGS = \
+     $(AM_CFLAGS)
+ libsss_simple_la_LIBADD = \
+diff --git a/src/providers/simple/simple_access.c b/src/providers/simple/simple_access.c
+index 70d1f07282d472089a2c6eac05479c39eb03c606..d53a04b4d73bfa4fae23b4522d01cf1e947e7d64 100644
+--- a/src/providers/simple/simple_access.c
++++ b/src/providers/simple/simple_access.c
+@@ -35,211 +35,13 @@
+ #define CONFDB_SIMPLE_ALLOW_GROUPS "simple_allow_groups"
+ #define CONFDB_SIMPLE_DENY_GROUPS "simple_deny_groups"
+ 
+-errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
+-                            bool *access_granted)
+-{
+-    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;
+-    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",
+-                      username));
+-
+-                /* Do not return immediately on explicit allow
+-                 * We need to make sure none of the user's groups
+-                 * are denied.
+-                 */
+-                *access_granted = true;
+-            }
+-        }
+-    } else if (!ctx->allow_groups) {
+-        /* If neither allow rule is in place, we'll assume allowed
+-         * unless a deny rule disables us below.
+-         */
+-        *access_granted = true;
+-    }
+-
+-    /* Next check whether this user has been specifically denied */
+-    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",
+-                      username));
+-
+-                /* Return immediately on explicit denial */
+-                *access_granted = false;
+-                return EOK;
+-            }
+-        }
+-    }
+-
+-    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->sysdb,
+-                                    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->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->sysdb,
+-                                    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;
+-
+-    /* Now process allow and deny group rules
+-     * If access was already granted above, we'll skip
+-     * this redundant rule check
+-     */
+-    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])) {
+-                    matched = true;
+-                    break;
+-                }
+-            }
+-
+-            /* If any group has matched, we can skip out on the
+-             * processing early
+-             */
+-            if (matched) {
+-                *access_granted = true;
+-                break;
+-            }
+-        }
+-    }
+-
+-    /* Finally, process the deny group rules */
+-    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])) {
+-                    matched = true;
+-                    break;
+-                }
+-            }
+-
+-            /* If any group has matched, we can skip out on the
+-             * processing early
+-             */
+-            if (matched) {
+-                *access_granted = false;
+-                break;
+-            }
+-        }
+-    }
+-
+-    ret = EOK;
+-
+-done:
+-    talloc_free(tmp_ctx);
+-    return ret;
+-}
++static void simple_access_check(struct tevent_req *req);
+ 
+ void simple_access_handler(struct be_req *be_req)
+ {
+-    int ret;
+-    bool access_granted = false;
++    struct be_ctx *be_ctx = be_req->be_ctx;
+     struct pam_data *pd;
++    struct tevent_req *req;
+     struct simple_ctx *ctx;
+ 
+     pd = talloc_get_type(be_req->req_data, struct pam_data);
+@@ -255,7 +57,30 @@ void simple_access_handler(struct be_req *be_req)
+     ctx = talloc_get_type(be_req->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->fn(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->req_data, 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;
+@@ -290,6 +115,7 @@ int sssm_simple_access_init(struct be_ctx *bectx, struct bet_ops **ops,
+ 
+     ctx->sysdb = bectx->sysdb;
+     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 abcf61ac29f31b3e424dd12340759ee6cc8c9489..1de9d898bdc3a58bbce2e2c74a02796fa5c3c609 100644
+--- a/src/providers/simple/simple_access.h
++++ b/src/providers/simple/simple_access.h
+@@ -29,6 +29,7 @@
+ struct simple_ctx {
+     struct sysdb_ctx *sysdb;
+     struct sss_domain_info *domain;
++    struct be_ctx *be_ctx;
+ 
+     char **allow_users;
+     char **deny_users;
+@@ -36,6 +37,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
+new file mode 100644
+index 0000000000000000000000000000000000000000..a9e8f632e8af6aae467571db6f093a5b07b5ed91
+--- /dev/null
++++ b/src/providers/simple/simple_access_check.c
+@@ -0,0 +1,723 @@
++/*
++   SSSD
++
++   Simple access control
++
++   Copyright (C) Sumit Bose <sbose at redhat.com> 2010
++
++   This program is free software; you can redistribute it and/or modify
++   it under the terms of the GNU General Public License as published by
++   the Free Software Foundation; either version 3 of the License, or
++   (at your option) any later version.
++
++   This program is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++   GNU General Public License for more details.
++
++   You should have received a copy of the GNU General Public License
++   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"
++
++static bool
++is_posix(const struct ldb_message *group)
++{
++    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;
++
++    /* 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(SSSDBG_TRACE_LIBS,
++                      ("User [%s] found in allow list, access granted.\n",
++                      username));
++
++                /* Do not return immediately on explicit allow
++                 * We need to make sure none of the user's groups
++                 * are denied.
++                 */
++                *access_granted = true;
++            }
++        }
++    } else if (!ctx->allow_groups) {
++        /* 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;
++    }
++
++    /* Next check whether this user has been specifically denied */
++    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(SSSDBG_TRACE_LIBS,
++                      ("User [%s] found in deny list, access denied.\n",
++                      username));
++
++                /* Return immediately on explicit denial */
++                *access_granted = false;
++                return EOK;
++            }
++        }
++    }
++
++    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
++     * this redundant rule check
++     */
++    if (ctx->allow_groups && !*access_granted) {
++        matched = false;
++        for (i = 0; ctx->allow_groups[i]; i++) {
++            for(j = 0; group_names[j]; j++) {
++                if (sss_string_equal(cs, group_names[j], ctx->allow_groups[i])) {
++                    matched = true;
++                    break;
++                }
++            }
++
++            /* If any group has matched, we can skip out on the
++             * processing early
++             */
++            if (matched) {
++                DEBUG(SSSDBG_TRACE_LIBS,
++                      ("Group [%s] found in allow list, access granted.\n",
++                      group_names[j]));
++                *access_granted = true;
++                break;
++            }
++        }
++    }
++
++    /* Finally, process the deny group rules */
++    if (ctx->deny_groups) {
++        matched = false;
++        for (i = 0; ctx->deny_groups[i]; i++) {
++            for(j = 0; group_names[j]; j++) {
++                if (sss_string_equal(cs, group_names[j], ctx->deny_groups[i])) {
++                    matched = true;
++                    break;
++                }
++            }
++
++            /* If any group has matched, we can skip out on the
++             * processing early
++             */
++            if (matched) {
++                DEBUG(SSSDBG_TRACE_LIBS,
++                      ("Group [%s] found in deny list, access denied.\n",
++                      group_names[j]));
++                *access_granted = false;
++                break;
++            }
++        }
++    }
++
++    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->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,
++                                    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:
++    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,
++                                    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 577c6d334edda513fd0f1e42a859ea333ba5ba23..ab2612db85b6cfe46c87a1fc8b17655dc7a795bb 100644
+--- a/src/tests/simple_access-tests.c
++++ b/src/tests/simple_access-tests.c
+@@ -27,6 +27,7 @@
+ #include <check.h>
+ 
+ #include "confdb/confdb.h"
++#include "db/sysdb_private.h"
+ #include "providers/simple/simple_access.h"
+ #include "tests/common.h"
+ 
+@@ -35,16 +36,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 +77,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,6 +126,7 @@ void setup_simple(void)
+                                       &test_ctx->ctx->domain, &test_ctx->ctx->sysdb);
+     fail_if(ret != EOK, "Could not initialize connection to the sysdb (%d)", ret);
+     test_ctx->ctx->domain->case_sensitive = true;
++    test_ctx->ctx->sysdb->mpg = false; /* Simulate an LDAP domain better */
+ }
+ 
+ void teardown_simple(void)
+@@ -117,18 +146,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->ctx->sysdb,
++                          "pvt", 999, NULL, 0, 0);
++    fail_if(ret != EOK, "Could not add private group");
++
+     ret = sysdb_store_user(test_ctx->ctx->sysdb,
+-                           "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->ctx->sysdb,
+-                           "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->ctx->sysdb,
+-                           "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");
+ 
+@@ -163,190 +196,317 @@ void teardown_simple_group(void)
+     fail_if(ret != EOK, "Could not delete g1");
+     ret = sysdb_delete_group(test_ctx->ctx->sysdb, "g2", 0);
+     fail_if(ret != EOK, "Could not delete g2");
++    ret = sysdb_delete_group(test_ctx->ctx->sysdb, "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
+ 
+@@ -361,6 +521,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
+
diff --git a/sources b/sources
index 27b29fb..a070340 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-af1854a392114b26f9bd7dfa6cad9943  sssd-1.8.6.tar.gz
+a72cda079a287e62a4beaa4d4f48fa89  sssd-1.9.4.tar.gz
diff --git a/sssd.spec b/sssd.spec
index d9e0c3c..c555385 100644
--- a/sssd.spec
+++ b/sssd.spec
@@ -15,7 +15,7 @@
 %global ldb_version 1.1.6
 
 Name: sssd
-Version: 1.8.6
+Version: 1.9.4
 Release: 1%{?dist}
 Group: Applications/System
 Summary: System Security Services Daemon
@@ -25,6 +25,13 @@ Source0: https://fedorahosted.org/released/sssd/%{name}-%{version}.tar.gz
 BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
 
 ### Patches ###
+Patch0001:  0001-krb-recreate-ccache-if-it-was-deleted.patch
+Patch0002:  0002-subdomains-replace-invalid-characters-with-underscor.patch
+Patch0003:  0003-Fix-the-krb5-password-expiration-warning.patch
+Patch0004:  0004-Add-unit-tests-for-simple-access-test-by-groups.patch
+Patch0005:  0005-Do-not-compile-main-in-DP-if-UNIT_TESTING-is-defined.patch
+Patch0006:  0006-Provide-a-be_get_account_info_send-function.patch
+Patch0007:  0007-Resolve-GIDs-in-the-simple-access-provider.patch
 
 ### Dependencies ###
 
@@ -34,15 +41,17 @@ Requires: libtdb%{?_isa} >= 1.1.3
 Requires: sssd-client%{?_isa} = %{version}-%{release}
 Requires: cyrus-sasl-gssapi%{?_isa}
 Requires: libipa_hbac%{?_isa} = %{version}-%{release}
+Requires: libsss_idmap%{?_isa} = %{version}-%{release}
 Requires: krb5-libs%{?_isa} >= 1.10
-Requires(post): systemd-units initscripts chkconfig /sbin/ldconfig
+Requires(post): systemd-units initscripts chkconfig
 Requires(preun): systemd-units initscripts chkconfig
-Requires(postun): systemd-units initscripts chkconfig /sbin/ldconfig
+Requires(postun): systemd-units initscripts chkconfig
 
 %global servicename sssd
 %global sssdstatedir %{_localstatedir}/lib/sss
 %global dbpath %{sssdstatedir}/db
 %global pipepath %{sssdstatedir}/pipes
+%global mcpath %{sssdstatedir}/mc
 %global pubconfpath %{sssdstatedir}/pubconf
 
 ### Build Dependencies ###
@@ -88,7 +97,10 @@ BuildRequires: libnl-devel
 BuildRequires: gettext-devel
 BuildRequires: pkgconfig
 BuildRequires: glib2-devel
+BuildRequires: diffstat
 BuildRequires: findutils
+BuildRequires: samba4-devel >= samba4-4.0.0-59beta2
+BuildRequires: selinux-policy-targeted
 
 %description
 Provides a set of daemons to manage access to remote directories and
@@ -101,6 +113,8 @@ services for projects like FreeIPA.
 Summary: SSSD Client libraries for NSS and PAM
 Group: Applications/System
 License: LGPLv3+
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
 
 %description client
 Provides the libraries needed by the PAM and NSS stacks to connect to the SSSD
@@ -110,19 +124,43 @@ service.
 Summary: Userspace tools for use with the SSSD
 Group: Applications/System
 License: GPLv3+
-Requires: sssd%{?_isa} = %{version}-%{release}
+Requires: sssd = %{version}-%{release}
 
 %description tools
 Provides userspace tools for manipulating users, groups, and nested groups in
 SSSD when using id_provider = local in /etc/sssd/sssd.conf.
 
-Also provides a userspace tool for generating an obfuscated LDAP password for
-use with ldap_default_authtok_type = obfuscated_password.
+Also provides several other administrative tools:
+    * sss_cache to expire cached entries
+    * sss_debuglevel to change the debug level on the fly
+    * sss_seed which pre-creates a user entry for use in kickstarts
+    * sss_obfuscate for generating an obfuscated LDAP password
+
+%package -n libsss_idmap
+Summary: FreeIPA Idmap library
+Group: Development/Libraries
+License: LGPLv3+
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description -n libsss_idmap
+Utility library to convert SIDs to Unix uids and gids
+
+%package -n libsss_idmap-devel
+Summary: FreeIPA Idmap library
+Group: Development/Libraries
+License: LGPLv3+
+Requires: libsss_idmap = %{version}-%{release}
+
+%description -n libsss_idmap-devel
+Utility library to SIDs to Unix uids and gids
 
 %package -n libipa_hbac
 Summary: FreeIPA HBAC Evaluator library
 Group: Development/Libraries
 License: LGPLv3+
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
 
 %description -n libipa_hbac
 Utility library to validate FreeIPA HBAC rules for authorization requests
@@ -131,7 +169,7 @@ Utility library to validate FreeIPA HBAC rules for authorization requests
 Summary: FreeIPA HBAC Evaluator library
 Group: Development/Libraries
 License: LGPLv3+
-Requires: libipa_hbac%{?_isa} = %{version}-%{release}
+Requires: libipa_hbac = %{version}-%{release}
 
 %description -n libipa_hbac-devel
 Utility library to validate FreeIPA HBAC rules for authorization requests
@@ -140,7 +178,7 @@ Utility library to validate FreeIPA HBAC rules for authorization requests
 Summary: Python bindings for the FreeIPA HBAC Evaluator library
 Group: Development/Libraries
 License: LGPLv3+
-Requires: libipa_hbac%{?_isa} = %{version}-%{release}
+Requires: libipa_hbac = %{version}-%{release}
 
 %description -n libipa_hbac-python
 The libipa_hbac-python contains the bindings so that libipa_hbac can be
@@ -150,6 +188,9 @@ used by Python applications.
 Summary: A library to allow communication between SUDO and SSSD
 Group: Development/Libraries
 License: LGPLv3+
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+Requires: sssd = %{version}-%{release}
 
 %description -n libsss_sudo
 A utility library to allow communication between SUDO and SSSD
@@ -158,7 +199,7 @@ A utility library to allow communication between SUDO and SSSD
 Summary: A library to allow communication between SUDO and SSSD
 Group: Development/Libraries
 License: LGPLv3+
-Requires: libsss_sudo%{?_isa} = %{version}-%{release}
+Requires: libsss_sudo = %{version}-%{release}
 
 %description -n libsss_sudo-devel
 A utility library to allow communication between SUDO and SSSD
@@ -192,6 +233,7 @@ autoreconf -ivf
     --with-db-path=%{dbpath} \
     --with-pipe-path=%{pipepath} \
     --with-pubconf-path=%{pubconfpath} \
+    --with-mcache-path=%{mcpath} \
     --with-init-dir=%{_initrddir} \
     --with-krb5-rcache-dir=%{_localstatedir}/cache/krb5rcache \
     --enable-nsslibdir=/%{_lib} \
@@ -251,6 +293,7 @@ do
 done
 
 touch sssd_tools.lang
+touch sssd_client.lang
 for man in `find $RPM_BUILD_ROOT/%{_mandir}/??/man?/ -type f | sed -e "s#$RPM_BUILD_ROOT/%{_mandir}/##"`
 do
     lang=`echo $man | cut -c 1-2`
@@ -258,6 +301,9 @@ do
         sss_*)
             echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_tools.lang
             ;;
+        sssd_krb5_*)
+            echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_client.lang
+            ;;
         pam_sss*)
             echo \%lang\(${lang}\) \%{_mandir}/${man}\* >> sssd_client.lang
             ;;
@@ -291,6 +337,7 @@ rm -rf $RPM_BUILD_ROOT
 %{_unitdir}/sssd.service
 %{_sbindir}/sssd
 
+%dir %{_libexecdir}/%{servicename}
 %{_libexecdir}/%{servicename}/krb5_child
 %{_libexecdir}/%{servicename}/ldap_child
 %{_libexecdir}/%{servicename}/proxy_child
@@ -300,12 +347,15 @@ rm -rf $RPM_BUILD_ROOT
 %{_libexecdir}/%{servicename}/sssd_autofs
 %{_libexecdir}/%{servicename}/sssd_ssh
 %{_libexecdir}/%{servicename}/sssd_sudo
+%{_libexecdir}/%{servicename}/sssd_pac
 
+%dir %{_libdir}/%{name}
 %{_libdir}/%{name}/libsss_ipa.so
 %{_libdir}/%{name}/libsss_krb5.so
 %{_libdir}/%{name}/libsss_ldap.so
 %{_libdir}/%{name}/libsss_proxy.so
 %{_libdir}/%{name}/libsss_simple.so
+%{_libdir}/%{name}/libsss_ad.so
 
 %{ldb_modulesdir}/memberof.so
 %{_bindir}/sss_ssh_authorizedkeys
@@ -314,14 +364,19 @@ rm -rf $RPM_BUILD_ROOT
 %dir %{sssdstatedir}
 %dir %{_localstatedir}/cache/krb5rcache
 %attr(700,root,root) %dir %{dbpath}
+%attr(755,root,root) %dir %{mcpath}
+%ghost %attr(0644,root,root) %verify(not md5 size mtime) %{mcpath}/passwd
+%ghost %attr(0644,root,root) %verify(not md5 size mtime) %{mcpath}/group
 %attr(755,root,root) %dir %{pipepath}
 %attr(755,root,root) %dir %{pubconfpath}
+%attr(755,root,root) %dir %{pubconfpath}/krb5.include.d
 %attr(700,root,root) %dir %{pipepath}/private
 %attr(750,root,root) %dir %{_var}/log/%{name}
 %attr(700,root,root) %dir %{_sysconfdir}/sssd
 %ghost %attr(0600,root,root) %config(noreplace) %{_sysconfdir}/sssd/sssd.conf
 %config(noreplace) %{_sysconfdir}/logrotate.d/sssd
 %config(noreplace) %{_sysconfdir}/rwtab.d/sssd
+%dir %{_datadir}/sssd
 %{_datadir}/sssd/sssd.api.conf
 %{_datadir}/sssd/sssd.api.d
 %{_mandir}/man1/sss_ssh_authorizedkeys.1*
@@ -331,9 +386,13 @@ rm -rf $RPM_BUILD_ROOT
 %{_mandir}/man5/sssd-krb5.5*
 %{_mandir}/man5/sssd-ldap.5*
 %{_mandir}/man5/sssd-simple.5*
+%{_mandir}/man5/sssd-ad.5*
+%{_mandir}/man5/sssd-sudo.5*
 %{_mandir}/man8/sssd.8*
 %{python_sitearch}/pysss.so
-%{python_sitelib}/*.py*
+%{python_sitearch}/pysss_murmur.so
+%dir %{python_sitelib}/SSSDConfig
+%{python_sitelib}/SSSDConfig/*.py*
 
 %files client -f sssd_client.lang
 %defattr(-,root,root,-)
@@ -341,6 +400,7 @@ rm -rf $RPM_BUILD_ROOT
 /%{_lib}/libnss_sss.so.2
 /%{_lib}/security/pam_sss.so
 %{_libdir}/krb5/plugins/libkrb5/sssd_krb5_locator_plugin.so
+%{_libdir}/krb5/plugins/authdata/sssd_pac_plugin.so
 %{_mandir}/man8/pam_sss.8*
 %{_mandir}/man8/sssd_krb5_locator_plugin.8*
 
@@ -357,6 +417,7 @@ rm -rf $RPM_BUILD_ROOT
 %{_sbindir}/sss_obfuscate
 %{_sbindir}/sss_cache
 %{_sbindir}/sss_debuglevel
+%{_sbindir}/sss_seed
 %{_mandir}/man8/sss_groupadd.8*
 %{_mandir}/man8/sss_groupdel.8*
 %{_mandir}/man8/sss_groupmod.8*
@@ -367,6 +428,19 @@ rm -rf $RPM_BUILD_ROOT
 %{_mandir}/man8/sss_obfuscate.8*
 %{_mandir}/man8/sss_cache.8*
 %{_mandir}/man8/sss_debuglevel.8*
+%{_mandir}/man8/sss_seed.8*
+
+%files -n libsss_idmap
+%defattr(-,root,root,-)
+%doc src/sss_client/COPYING src/sss_client/COPYING.LESSER
+%{_libdir}/libsss_idmap.so.*
+
+%files -n libsss_idmap-devel
+%defattr(-,root,root,-)
+%doc idmap_doc/html
+%{_includedir}/sss_idmap.h
+%{_libdir}/libsss_idmap.so
+%{_libdir}/pkgconfig/sss_idmap.pc
 
 %files -n libipa_hbac
 %defattr(-,root,root,-)
@@ -395,13 +469,11 @@ A utility library to allow communication between Autofs and SSSD
 %files -n libsss_sudo
 %defattr(-,root,root,-)
 %doc src/sss_client/COPYING src/sss_client/COPYING.LESSER
-%{_libdir}/libsss_sudo.so.*
+%{_libdir}/libsss_sudo.so*
 
 %files -n libsss_sudo-devel
 %doc libsss_sudo_doc/html
 %{_includedir}/sss_sudo.h
-%{_libdir}/libsss_sudo.so
-%{_libdir}/pkgconfig/libsss_sudo.pc
 
 %files -n libsss_autofs
 %defattr(-,root,root,-)
@@ -409,15 +481,13 @@ A utility library to allow communication between Autofs and SSSD
 %{_libdir}/sssd/modules/libsss_autofs.so*
 
 %post
-/sbin/ldconfig
-
 if [ $1 -ge 1 ] ; then
     # Initial installation
     /bin/systemctl daemon-reload >/dev/null 2>&1 || :
 fi
 
 %preun
-if [ $1 = 0 ]; then
+if [ $1 -eq 0 ]; then
      # Package removal, not upgrade
     /bin/systemctl --no-reload disable sssd.service > /dev/null 2>&1 || :
     /bin/systemctl stop sssd.service > /dev/null 2>&1 || :
@@ -437,11 +507,8 @@ fi
 
 
 %postun
-/sbin/ldconfig
 /bin/systemctl daemon-reload >/dev/null 2>&1 || :
 if [ $1 -ge 1 ] ; then
-    # On upgrade, reload init system configuration if we changed unit files
-    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
     /bin/systemctl try-restart sssd.service >/dev/null 2>&1 || :
 fi
 
@@ -453,7 +520,19 @@ fi
 
 %postun -n libipa_hbac -p /sbin/ldconfig
 
+%post -n libsss_idmap -p /sbin/ldconfig
+
+%postun -n libsss_idmap -p /sbin/ldconfig
+
+%post -n libsss_sudo -p /sbin/ldconfig
+
+%postun -n libsss_sudo -p /sbin/ldconfig
+
 %changelog
+* Mon Mar 25 2013 Jakub Hrozek <jhrozek at redhat.com> - 1.9.4-1
+- New upstream release 1.9.4
+- https://fedorahosted.org/sssd/wiki/Releases/Notes-1.9.4
+
 * Tue Jan 29 2013 Jakub Hrozek <jhrozek at redhat.com> - 1.8.6-1
 - New upstream release 1.8.6
 - https://fedorahosted.org/sssd/wiki/Releases/Notes-1.8.6


More information about the scm-commits mailing list