From ee4fd091b3313bbbf70a8071ccb3d7b85b9bf7924cef8169b8e961b70612f501 Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Wed, 20 Mar 2013 10:22:42 +0000 Subject: [PATCH] Accepting request 160207 from home:rhafer:branches:network:ldap CVE-2013-0287 (bnc#809153) OBS-URL: https://build.opensuse.org/request/show/160207 OBS-URL: https://build.opensuse.org/package/show/network:ldap/sssd?expand=0&rev=91 --- ...sts-for-simple-access-test-by-groups.patch | 414 +++++ ...ain-in-DP-if-UNIT_TESTING-is-defined.patch | 41 + ...-a-be_get_account_info_send-function.patch | 237 +++ ...e-GIDs-in-the-simple-access-provider.patch | 1624 +++++++++++++++++ sssd.changes | 15 + sssd.spec | 8 +- 6 files changed, 2338 insertions(+), 1 deletion(-) create mode 100644 Add-unit-tests-for-simple-access-test-by-groups.patch create mode 100644 Do-not-compile-main-in-DP-if-UNIT_TESTING-is-defined.patch create mode 100644 Provide-a-be_get_account_info_send-function.patch create mode 100644 Resolve-GIDs-in-the-simple-access-provider.patch diff --git a/Add-unit-tests-for-simple-access-test-by-groups.patch b/Add-unit-tests-for-simple-access-test-by-groups.patch new file mode 100644 index 0000000..dcf284f --- /dev/null +++ b/Add-unit-tests-for-simple-access-test-by-groups.patch @@ -0,0 +1,414 @@ +From e5f0ef211e81fcd7a87d5e37b0aadca50201c6d6 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Sun, 3 Mar 2013 21:43:44 +0100 +Subject: 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. +(cherry picked from commit 754b09b5444e6da88ed58d6deaed8b815e268b6b) +--- + src/tests/simple_access-tests.c | 285 +++++++++++++++++++++++++++++++++++----- + 1 file changed, 253 insertions(+), 32 deletions(-) + +diff --git a/src/tests/simple_access-tests.c b/src/tests/simple_access-tests.c +index c61814e..577c6d3 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_test_ctx *test_ctx = NULL; ++ ++struct simple_test_ctx { ++ struct sysdb_ctx *sysdb; ++ struct confdb_ctx *confdb; + +-struct simple_ctx *ctx = NULL; ++ 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."); +- +- ctx->domain = talloc_zero(ctx, struct sss_domain_info); +- fail_unless(ctx != NULL, "Cannot create domain in simple context."); +- ctx->domain->case_sensitive = true; ++ errno_t ret; ++ char *conf_db; ++ const char *val[2]; ++ val[1] = NULL; ++ ++ /* Create tests directory if it doesn't exist */ ++ /* (relative to current dir) */ ++ ret = mkdir(TESTS_PATH, 0775); ++ fail_if(ret == -1 && errno != EEXIST, ++ "Could not create %s directory", TESTS_PATH); ++ ++ fail_unless(test_ctx == NULL, "Simple context already initialized."); ++ test_ctx = talloc_zero(NULL, struct simple_test_ctx); ++ fail_unless(test_ctx != NULL, "Cannot create simple test context."); ++ ++ test_ctx->ctx = talloc_zero(test_ctx, struct simple_ctx); ++ fail_unless(test_ctx->ctx != NULL, "Cannot create simple context."); ++ ++ conf_db = talloc_asprintf(test_ctx, "%s/%s", TESTS_PATH, TEST_CONF_FILE); ++ fail_if(conf_db == NULL, "Out of memory, aborting!"); ++ DEBUG(SSSDBG_TRACE_LIBS, ("CONFDB: %s\n", conf_db)); ++ ++ /* Connect to the conf db */ ++ ret = confdb_init(test_ctx, &test_ctx->confdb, conf_db); ++ fail_if(ret != EOK, "Could not initialize connection to the confdb"); ++ ++ val[0] = "LOCAL"; ++ ret = confdb_add_param(test_ctx->confdb, true, ++ "config/sssd", "domains", val); ++ fail_if(ret != EOK, "Could not initialize domains placeholder"); ++ ++ val[0] = "local"; ++ ret = confdb_add_param(test_ctx->confdb, true, ++ "config/domain/LOCAL", "id_provider", val); ++ fail_if(ret != EOK, "Could not initialize provider"); ++ ++ val[0] = "TRUE"; ++ ret = confdb_add_param(test_ctx->confdb, true, ++ "config/domain/LOCAL", "enumerate", val); ++ fail_if(ret != EOK, "Could not initialize LOCAL domain"); ++ ++ val[0] = "TRUE"; ++ ret = confdb_add_param(test_ctx->confdb, true, ++ "config/domain/LOCAL", "cache_credentials", val); ++ fail_if(ret != EOK, "Could not initialize LOCAL domain"); ++ ++ ret = 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/Do-not-compile-main-in-DP-if-UNIT_TESTING-is-defined.patch b/Do-not-compile-main-in-DP-if-UNIT_TESTING-is-defined.patch new file mode 100644 index 0000000..d66871b --- /dev/null +++ b/Do-not-compile-main-in-DP-if-UNIT_TESTING-is-defined.patch @@ -0,0 +1,41 @@ +From 8dfcfe629db83eb58dd6613aa174222cb853afb1 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Mon, 4 Mar 2013 16:37:04 +0100 +Subject: 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 +(cherry picked from commit 26590d31f492dbbd36be6d0bde46a4bd3b221edb) +--- + 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 f85a04d..33590ae 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/Provide-a-be_get_account_info_send-function.patch b/Provide-a-be_get_account_info_send-function.patch new file mode 100644 index 0000000..5e75c49 --- /dev/null +++ b/Provide-a-be_get_account_info_send-function.patch @@ -0,0 +1,237 @@ +From 455737c0b4b0c1bfeed54f2e27e397ce403acbca Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Fri, 22 Feb 2013 11:01:38 +0100 +Subject: 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. +(cherry picked from commit b63830b142053f99bfe954d4be5a2b0f68ce3a93) +--- + 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 b261bf8..f85a04d 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 58a9b74..743b6f4 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/Resolve-GIDs-in-the-simple-access-provider.patch b/Resolve-GIDs-in-the-simple-access-provider.patch new file mode 100644 index 0000000..91e09a4 --- /dev/null +++ b/Resolve-GIDs-in-the-simple-access-provider.patch @@ -0,0 +1,1624 @@ +From ba1193c7b950a3849e04e28e60d83eece5ee49bc Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Sat, 23 Feb 2013 10:44:54 +0100 +Subject: 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 +(cherry picked from commit 8b8019fe3dd1564fba657e219ec20ff816c7ffdb) +--- + Makefile.am | 17 +- + src/providers/simple/simple_access.c | 228 ++------- + src/providers/simple/simple_access.h | 11 +- + src/providers/simple/simple_access_check.c | 723 +++++++++++++++++++++++++++++ + src/tests/simple_access-tests.c | 361 ++++++++++---- + 5 files changed, 1033 insertions(+), 307 deletions(-) + create mode 100644 src/providers/simple/simple_access_check.c + +diff --git a/Makefile.am b/Makefile.am +index dda090d..223431d 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 70d1f07..d53a04b 100644 +--- a/src/providers/simple/simple_access.c ++++ b/src/providers/simple/simple_access.c +@@ -35,227 +35,52 @@ + #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; +- } ++static void simple_access_check(struct tevent_req *req); + +- /* 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; +- } +- } +- } ++void simple_access_handler(struct be_req *be_req) ++{ ++ struct be_ctx *be_ctx = be_req->be_ctx; ++ struct pam_data *pd; ++ struct tevent_req *req; ++ struct simple_ctx *ctx; + +- if (!ctx->allow_groups && !ctx->deny_groups) { +- /* There are no group restrictions, so just return +- * here with whatever we've decided. +- */ +- return EOK; +- } ++ pd = talloc_get_type(be_req->req_data, struct pam_data); + +- /* 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; +- } ++ pd->pam_status = PAM_SYSTEM_ERR; + +- 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))); ++ if (pd->cmd != SSS_PAM_ACCT_MGMT) { ++ DEBUG(4, ("simple access does not handles pam task %d.\n", pd->cmd)); ++ pd->pam_status = PAM_MODULE_UNKNOWN; + 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; +- } ++ ctx = talloc_get_type(be_req->be_ctx->bet_info[BET_ACCESS].pvt_bet_data, ++ struct simple_ctx); + +- /* Get the user's primary group */ +- gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0); +- if (!gid) { +- ret = EINVAL; ++ req = simple_access_check_send(be_req, be_ctx->ev, ctx, pd->user); ++ if (!req) { ++ pd->pam_status = PAM_SYSTEM_ERR; + 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; ++ tevent_req_set_callback(req, simple_access_check, be_req); ++ return; + + done: +- talloc_free(tmp_ctx); +- return ret; ++ be_req->fn(be_req, DP_ERR_OK, pd->pam_status, NULL); + } + +-void simple_access_handler(struct be_req *be_req) ++static void simple_access_check(struct tevent_req *req) + { +- int ret; + bool access_granted = false; ++ errno_t ret; + struct pam_data *pd; +- struct simple_ctx *ctx; ++ 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); + +- pd->pam_status = PAM_SYSTEM_ERR; +- +- if (pd->cmd != SSS_PAM_ACCT_MGMT) { +- DEBUG(4, ("simple access does not handles pam task %d.\n", pd->cmd)); +- pd->pam_status = PAM_MODULE_UNKNOWN; +- goto done; +- } +- +- 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); ++ 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 abcf61a..1de9d89 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 0000000..a9e8f63 +--- /dev/null ++++ b/src/providers/simple/simple_access_check.c +@@ -0,0 +1,723 @@ ++/* ++ SSSD ++ ++ Simple access control ++ ++ Copyright (C) Sumit Bose 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 . ++*/ ++ ++#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 577c6d3..ab2612d 100644 +--- a/src/tests/simple_access-tests.c ++++ b/src/tests/simple_access-tests.c +@@ -27,6 +27,7 @@ + #include + + #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."); ++ 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."); + +- 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, "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."); ++ 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."); + +- 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, "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."); ++ 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."); + +- 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, "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."); ++ 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; + +- 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."); ++ 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."); ++ 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; + +- 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."); ++ 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."); ++ 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; + +- 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."); ++ 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/sssd.changes b/sssd.changes index 9b75a64..c4cbf0f 100644 --- a/sssd.changes +++ b/sssd.changes @@ -1,3 +1,18 @@ +------------------------------------------------------------------- +Wed Mar 20 10:05:00 UTC 2013 - rhafer@suse.com + +- Fixed security issue: CVE-2013-0287 (bnc#809153): + When SSSD is configured as an Active Directory client by using + the new Active Directory provider or equivalent configuration + of the LDAP provider, the Simple Access Provider does not + handle access control correctly. If any groups are specified + with the simple_deny_groups option, the group members are + permitted access. New patches: + * Provide-a-be_get_account_info_send-function.patch + * Add-unit-tests-for-simple-access-test-by-groups.patch + * Do-not-compile-main-in-DP-if-UNIT_TESTING-is-defined.patch + * Resolve-GIDs-in-the-simple-access-provider.patch + ------------------------------------------------------------------- Tue Feb 26 08:29:43 UTC 2013 - jengelh@inai.de diff --git a/sssd.spec b/sssd.spec index e6ae108..d7103db 100644 --- a/sssd.spec +++ b/sssd.spec @@ -32,6 +32,12 @@ Patch1: 0005-implicit-decl.diff Patch2: sssd-ldflags.diff Patch3: sssd-no-ldb-check.diff Patch4: sssd-sysdb-binary-attrs.diff +# Fixes for CVE-2013-0287 (will be part of 1.9.5) when released +Patch5: Provide-a-be_get_account_info_send-function.patch +Patch6: Add-unit-tests-for-simple-access-test-by-groups.patch +Patch7: Do-not-compile-main-in-DP-if-UNIT_TESTING-is-defined.patch +Patch8: Resolve-GIDs-in-the-simple-access-provider.patch +# End Fixed for CVE-2013-0287 BuildRoot: %{_tmppath}/%{name}-%{version}-build %define servicename sssd @@ -200,7 +206,7 @@ Security Services Daemon (sssd). %prep %setup -q -%patch -P 1 -P 2 -P 3 -P 4 -p1 +%patch -P 1 -P 2 -P 3 -P 4 -P 5 -P 6 -P 7 -P 8 -p1 %build %if 0%{?suse_version} < 1210