9ba0639961
CVE-2013-0287 (bnc#809153) (forwarded request 160207 from rhafer) OBS-URL: https://build.opensuse.org/request/show/160208 OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/sssd?expand=0&rev=44
1625 lines
55 KiB
Diff
1625 lines
55 KiB
Diff
From ba1193c7b950a3849e04e28e60d83eece5ee49bc Mon Sep 17 00:00:00 2001
|
|
From: Jakub Hrozek <jhrozek@redhat.com>
|
|
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 <sbose@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 577c6d3..ab2612d 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.");
|
|
+ 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
|
|
|