summaryrefslogtreecommitdiffstats
path: root/src/providers/simple
diff options
context:
space:
mode:
Diffstat (limited to 'src/providers/simple')
-rw-r--r--src/providers/simple/simple_access.c31
-rw-r--r--src/providers/simple/simple_access.h11
-rw-r--r--src/providers/simple/simple_access_check.c735
3 files changed, 655 insertions, 122 deletions
diff --git a/src/providers/simple/simple_access.c b/src/providers/simple/simple_access.c
index 3dcea8691..e617e93dc 100644
--- a/src/providers/simple/simple_access.c
+++ b/src/providers/simple/simple_access.c
@@ -32,12 +32,13 @@
#define CONFDB_SIMPLE_ALLOW_GROUPS "simple_allow_groups"
#define CONFDB_SIMPLE_DENY_GROUPS "simple_deny_groups"
+static void simple_access_check(struct tevent_req *req);
+
void simple_access_handler(struct be_req *be_req)
{
struct be_ctx *be_ctx = be_req_get_be_ctx(be_req);
- int ret;
- bool access_granted = false;
struct pam_data *pd;
+ struct tevent_req *req;
struct simple_ctx *ctx;
pd = talloc_get_type(be_req_get_data(be_req), struct pam_data);
@@ -53,7 +54,30 @@ void simple_access_handler(struct be_req *be_req)
ctx = talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
struct simple_ctx);
- ret = simple_access_check(ctx, pd->user, &access_granted);
+ req = simple_access_check_send(be_req, be_ctx->ev, ctx, pd->user);
+ if (!req) {
+ pd->pam_status = PAM_SYSTEM_ERR;
+ goto done;
+ }
+ tevent_req_set_callback(req, simple_access_check, be_req);
+ return;
+
+done:
+ be_req_terminate(be_req, DP_ERR_OK, pd->pam_status, NULL);
+}
+
+static void simple_access_check(struct tevent_req *req)
+{
+ bool access_granted = false;
+ errno_t ret;
+ struct pam_data *pd;
+ struct be_req *be_req;
+
+ be_req = tevent_req_callback_data(req, struct be_req);
+ pd = talloc_get_type(be_req_get_data(be_req), struct pam_data);
+
+ ret = simple_access_check_recv(req, &access_granted);
+ talloc_free(req);
if (ret != EOK) {
pd->pam_status = PAM_SYSTEM_ERR;
goto done;
@@ -87,6 +111,7 @@ int sssm_simple_access_init(struct be_ctx *bectx, struct bet_ops **ops,
}
ctx->domain = bectx->domain;
+ ctx->be_ctx = bectx;
/* Users */
ret = confdb_get_string_as_list(bectx->cdb, ctx, bectx->conf_path,
diff --git a/src/providers/simple/simple_access.h b/src/providers/simple/simple_access.h
index 2ddf27692..15dfaceb2 100644
--- a/src/providers/simple/simple_access.h
+++ b/src/providers/simple/simple_access.h
@@ -26,6 +26,7 @@
struct simple_ctx {
struct sss_domain_info *domain;
+ struct be_ctx *be_ctx;
char **allow_users;
char **deny_users;
@@ -33,6 +34,12 @@ struct simple_ctx {
char **deny_groups;
};
-errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
- bool *access_granted);
+struct tevent_req *simple_access_check_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct simple_ctx *ctx,
+ const char *username);
+
+errno_t simple_access_check_recv(struct tevent_req *req,
+ bool *access_granted);
+
#endif /* __SIMPLE_ACCESS_H__ */
diff --git a/src/providers/simple/simple_access_check.c b/src/providers/simple/simple_access_check.c
index cb5f52825..6475e773e 100644
--- a/src/providers/simple/simple_access_check.c
+++ b/src/providers/simple/simple_access_check.c
@@ -19,36 +19,40 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "providers/dp_backend.h"
#include "providers/simple/simple_access.h"
#include "util/sss_utf8.h"
#include "db/sysdb.h"
-errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
- bool *access_granted)
+static bool
+is_posix(const struct ldb_message *group)
{
- int i, j;
- errno_t ret;
- TALLOC_CTX *tmp_ctx = NULL;
- const char *user_attrs[] = { SYSDB_MEMBEROF,
- SYSDB_GIDNUM,
- NULL };
- const char *group_attrs[] = { SYSDB_NAME,
- NULL };
- struct ldb_message *msg;
- struct ldb_message_element *el;
- char **groups;
- const char *primary_group;
- gid_t gid;
- bool matched;
- bool cs = ctx->domain->case_sensitive;
+ 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;
+}
- *access_granted = 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(9, ("User [%s] found in allow list, access granted.\n",
+ DEBUG(SSSDBG_TRACE_LIBS,
+ ("User [%s] found in allow list, access granted.\n",
username));
/* Do not return immediately on explicit allow
@@ -62,6 +66,8 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
/* If neither allow rule is in place, we'll assume allowed
* unless a deny rule disables us below.
*/
+ DEBUG(SSSDBG_TRACE_LIBS,
+ ("No allow rule, assumuing allow unless explicitly denied\n"));
*access_granted = true;
}
@@ -69,7 +75,8 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
if (ctx->deny_users != NULL) {
for(i = 0; ctx->deny_users[i] != NULL; i++) {
if (sss_string_equal(cs, username, ctx->deny_users[i])) {
- DEBUG(9, ("User [%s] found in deny list, access denied.\n",
+ DEBUG(SSSDBG_TRACE_LIBS,
+ ("User [%s] found in deny list, access denied.\n",
username));
/* Return immediately on explicit denial */
@@ -79,97 +86,16 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
}
}
- if (!ctx->allow_groups && !ctx->deny_groups) {
- /* There are no group restrictions, so just return
- * here with whatever we've decided.
- */
- return EOK;
- }
-
- /* Now get a list of this user's groups and check those against the
- * simple_allow_groups list.
- */
- tmp_ctx = talloc_new(NULL);
- if (!tmp_ctx) {
- ret = ENOMEM;
- goto done;
- }
-
- ret = sysdb_search_user_by_name(tmp_ctx, ctx->domain->sysdb, ctx->domain,
- username, user_attrs, &msg);
- if (ret != EOK) {
- DEBUG(1, ("Could not look up username [%s]: [%d][%s]\n",
- username, ret, strerror(ret)));
- goto done;
- }
-
- /* Construct a list of the user's groups */
- el = ldb_msg_find_element(msg, SYSDB_MEMBEROF);
- if (el && el->num_values) {
- /* Get the groups from the memberOf entries
- * Allocate the array with room for both the NULL
- * terminator and the primary group
- */
- groups = talloc_array(tmp_ctx, char *, el->num_values + 2);
- if (!groups) {
- ret = ENOMEM;
- goto done;
- }
-
- for (j = 0; j < el->num_values; j++) {
- ret = sysdb_group_dn_name(
- ctx->domain->sysdb, tmp_ctx,
- (char *)el->values[j].data,
- &groups[j]);
- if (ret != EOK) {
- goto done;
- }
- }
- } else {
- /* User is not a member of any groups except primary */
- groups = talloc_array(tmp_ctx, char *, 2);
- if (!groups) {
- ret = ENOMEM;
- goto done;
- }
- j = 0;
- }
-
- /* Get the user's primary group */
- gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
- if (!gid) {
- ret = EINVAL;
- goto done;
- }
- talloc_zfree(msg);
-
- ret = sysdb_search_group_by_gid(tmp_ctx, ctx->domain->sysdb, ctx->domain,
- gid, group_attrs, &msg);
- if (ret != EOK) {
- DEBUG(1, ("Could not look up primary group [%lu]: [%d][%s]\n",
- gid, ret, strerror(ret)));
- /* We have to treat this as non-fatal, because the primary
- * group may be local to the machine and not available in
- * our ID provider.
- */
- } else {
- primary_group = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
- if (!primary_group) {
- ret = EINVAL;
- goto done;
- }
-
- groups[j] = talloc_strdup(tmp_ctx, primary_group);
- if (!groups[j]) {
- ret = ENOMEM;
- goto done;
- }
- j++;
-
- talloc_zfree(msg);
- }
+ return EAGAIN;
+}
- groups[j] = NULL;
+static errno_t
+simple_check_groups(struct simple_ctx *ctx, const char *username,
+ const char **group_names, bool *access_granted)
+{
+ bool matched;
+ int i, j;
+ bool cs = ctx->domain->case_sensitive;
/* Now process allow and deny group rules
* If access was already granted above, we'll skip
@@ -178,8 +104,8 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
if (ctx->allow_groups && !*access_granted) {
matched = false;
for (i = 0; ctx->allow_groups[i]; i++) {
- for(j = 0; groups[j]; j++) {
- if (sss_string_equal(cs, groups[j], ctx->allow_groups[i])) {
+ for(j = 0; group_names[j]; j++) {
+ if (sss_string_equal(cs, group_names[j], ctx->allow_groups[i])) {
matched = true;
break;
}
@@ -189,6 +115,9 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
* processing early
*/
if (matched) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ ("Group [%s] found in allow list, access granted.\n",
+ group_names[j]));
*access_granted = true;
break;
}
@@ -199,8 +128,8 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
if (ctx->deny_groups) {
matched = false;
for (i = 0; ctx->deny_groups[i]; i++) {
- for(j = 0; groups[j]; j++) {
- if (sss_string_equal(cs, groups[j], ctx->deny_groups[i])) {
+ for(j = 0; group_names[j]; j++) {
+ if (sss_string_equal(cs, group_names[j], ctx->deny_groups[i])) {
matched = true;
break;
}
@@ -210,15 +139,587 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
* processing early
*/
if (matched) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ ("Group [%s] found in deny list, access denied.\n",
+ group_names[j]));
*access_granted = false;
break;
}
}
}
- ret = EOK;
+ return EOK;
+}
+
+struct simple_resolve_group_state {
+ gid_t gid;
+ struct simple_ctx *ctx;
+
+ const char *name;
+};
+
+static errno_t
+simple_resolve_group_check(struct simple_resolve_group_state *state);
+static void simple_resolve_group_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+simple_resolve_group_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct simple_ctx *ctx,
+ gid_t gid)
+{
+ errno_t ret;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct simple_resolve_group_state *state;
+ struct be_acct_req *ar;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct simple_resolve_group_state);
+ if (!req) return NULL;
+
+ state->gid = gid;
+ state->ctx = ctx;
+
+ /* First check if the group was updated already. If it was (maybe its
+ * parent was updated first), then just shortcut */
+ ret = simple_resolve_group_check(state);
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS, ("Group already updated\n"));
+ ret = EOK;
+ goto done;
+ } else if (ret != EAGAIN) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ ("Cannot check if group was already updated\n"));
+ goto done;
+ }
+ /* EAGAIN - still needs update */
+
+ ar = talloc(state, struct be_acct_req);
+ if (!ar) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ar->entry_type = BE_REQ_GROUP;
+ ar->attr_type = BE_ATTR_CORE;
+ ar->filter_type = BE_FILTER_IDNUM;
+ ar->filter_value = talloc_asprintf(ar, "%llu", (unsigned long long) gid);
+ ar->domain = talloc_strdup(ar, ctx->domain->name);
+ if (!ar->domain || !ar->filter_value) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ subreq = be_get_account_info_send(state, ev, NULL, ctx->be_ctx, ar);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, simple_resolve_group_done, req);
+
+ return req;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t
+simple_resolve_group_check(struct simple_resolve_group_state *state)
+{
+ errno_t ret;
+ struct ldb_message *group;
+ const char *group_attrs[] = { SYSDB_NAME, SYSDB_POSIX,
+ SYSDB_GIDNUM, NULL };
+
+ /* Check the cache by GID again and fetch the name */
+ ret = sysdb_search_group_by_gid(state, state->ctx->domain->sysdb,
+ state->ctx->domain, state->gid,
+ group_attrs, &group);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ ("Could not look up group by gid [%lu]: [%d][%s]\n",
+ state->gid, ret, strerror(ret)));
+ return ret;
+ }
+
+ state->name = ldb_msg_find_attr_as_string(group, SYSDB_NAME, NULL);
+ if (!state->name) {
+ DEBUG(SSSDBG_OP_FAILURE, ("No group name\n"));
+ return ENOENT;
+ }
+
+ if (is_posix(group) == false) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ ("The group is still non-POSIX\n"));
+ return EAGAIN;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, ("Got POSIX group\n"));
+ return EOK;
+}
+
+static void simple_resolve_group_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct simple_resolve_group_state *state;
+ int err_maj;
+ int err_min;
+ errno_t ret;
+ const char *err_msg;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct simple_resolve_group_state);
+
+ ret = be_get_account_info_recv(subreq, state,
+ &err_maj, &err_min, &err_msg);
+ talloc_zfree(subreq);
+ if (ret) {
+ DEBUG(SSSDBG_OP_FAILURE, ("be_get_account_info_recv failed\n"));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (err_maj) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Cannot refresh data from DP: %u,%u: %s\n",
+ err_maj, err_min, err_msg));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ /* Check the cache by GID again and fetch the name */
+ ret = simple_resolve_group_check(state);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, ("Refresh failed\n"));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static errno_t
+simple_resolve_group_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ const char **name)
+{
+ struct simple_resolve_group_state *state;
+
+ state = tevent_req_data(req, struct simple_resolve_group_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *name = talloc_strdup(mem_ctx, state->name);
+ return EOK;
+}
+
+struct simple_check_groups_state {
+ struct tevent_context *ev;
+ struct simple_ctx *ctx;
+
+ gid_t *lookup_gids;
+ size_t num_gids;
+ size_t giter;
+
+ const char **group_names;
+ size_t num_names;
+};
+
+static void simple_check_get_groups_next(struct tevent_req *subreq);
+
+static errno_t
+simple_check_get_groups_primary(struct simple_check_groups_state *state,
+ gid_t gid);
+static errno_t
+simple_check_process_group(struct simple_check_groups_state *state,
+ struct ldb_message *group);
+
+static struct tevent_req *
+simple_check_get_groups_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct simple_ctx *ctx,
+ const char *username)
+{
+ errno_t ret;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct simple_check_groups_state *state;
+ const char *attrs[] = { SYSDB_NAME, SYSDB_POSIX, SYSDB_GIDNUM, NULL };
+ size_t group_count;
+ struct ldb_message *user;
+ struct ldb_message **groups;
+ int i;
+ gid_t gid;
+ char *cname;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct simple_check_groups_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->ctx = ctx;
+
+ cname = sss_get_cased_name(state, username, ctx->domain->case_sensitive);
+ if (!cname) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, ("Looking up groups for user %s\n", cname));
+
+ ret = sysdb_search_user_by_name(state, ctx->domain->sysdb, ctx->domain,
+ cname, attrs, &user);
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE, ("No such user %s\n", cname));
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ ("Could not look up username [%s]: [%d][%s]\n",
+ username, ret, strerror(ret)));
+ goto done;
+ }
+
+ ret = sysdb_asq_search(state, ctx->domain->sysdb,
+ user->dn, NULL, SYSDB_MEMBEROF,
+ attrs, &group_count, &groups);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ ("User %s is a member of %d supplemental groups\n",
+ cname, group_count));
+
+ /* One extra space for terminator, one extra space for private group */
+ state->group_names = talloc_zero_array(state, const char *, group_count + 2);
+ state->lookup_gids = talloc_zero_array(state, gid_t, group_count + 2);
+ if (!state->group_names || !state->lookup_gids) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i=0; i < group_count; i++) {
+ /* Some providers (like the AD provider) might perform initgroups
+ * without resolving the group names. In order for the simple access
+ * provider to work correctly, we need to resolve the groups before
+ * performing the access check. In AD provider, the situation is
+ * even more tricky b/c the groups HAVE name, but their name
+ * attribute is set to SID and they are set as non-POSIX
+ */
+ ret = simple_check_process_group(state, groups[i]);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ gid = ldb_msg_find_attr_as_uint64(user, SYSDB_GIDNUM, 0);
+ if (!gid) {
+ DEBUG(SSSDBG_MINOR_FAILURE, ("User %s has no gid?\n", cname));
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = simple_check_get_groups_primary(state, gid);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (state->num_gids == 0) {
+ /* If all groups could have been resolved by name, we are
+ * done
+ */
+ DEBUG(SSSDBG_TRACE_FUNC, ("All groups had name attribute\n"));
+ ret = EOK;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, ("Need to resolve %d groups\n", state->num_gids));
+ state->giter = 0;
+ subreq = simple_resolve_group_send(req, state->ev, state->ctx,
+ state->lookup_gids[state->giter]);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, simple_check_get_groups_next, req);
+
+ return req;
done:
- talloc_free(tmp_ctx);
- return ret;
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void simple_check_get_groups_next(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct simple_check_groups_state *state =
+ tevent_req_data(req, struct simple_check_groups_state);
+ errno_t ret;
+
+ ret = simple_resolve_group_recv(subreq, state->group_names,
+ &state->group_names[state->num_names]);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ ("Could not resolve name of group with GID %llu\n",
+ state->lookup_gids[state->giter]));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->num_names++;
+ state->giter++;
+
+ if (state->giter < state->num_gids) {
+ subreq = simple_resolve_group_send(req, state->ev, state->ctx,
+ state->lookup_gids[state->giter]);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, simple_check_get_groups_next, req);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, ("All groups resolved. Done.\n"));
+ tevent_req_done(req);
+}
+
+static errno_t
+simple_check_process_group(struct simple_check_groups_state *state,
+ struct ldb_message *group)
+{
+ const char *name;
+ gid_t gid;
+ bool posix;
+
+ posix = is_posix(group);
+ name = ldb_msg_find_attr_as_string(group, SYSDB_NAME, NULL);
+ gid = ldb_msg_find_attr_as_uint64(group, SYSDB_GIDNUM, 0);
+
+ /* With the current sysdb layout, every group has a name */
+ if (name == NULL) {
+ return EINVAL;
+ }
+
+ if (gid == 0) {
+ if (posix == true) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("POSIX group without GID\n"));
+ return EINVAL;
+ }
+
+ /* Non-posix group with a name. Still can be used for access
+ * control as the name should point to the real name, no SID
+ */
+ state->group_names[state->num_names] = talloc_strdup(state->group_names,
+ name);
+ if (!state->group_names[state->num_names]) {
+ return ENOMEM;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, ("Adding group %s\n", name));
+ state->num_names++;
+ return EOK;
+ }
+
+ /* Here are only groups with a name and gid. POSIX group can already
+ * be used, non-POSIX groups can be resolved */
+ if (posix) {
+ state->group_names[state->num_names] = talloc_strdup(state->group_names,
+ name);
+ if (!state->group_names[state->num_names]) {
+ return ENOMEM;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, ("Adding group %s\n", name));
+ state->num_names++;
+ return EOK;
+ }
+
+ /* Non-posix group with a GID. Needs resolving */
+ state->lookup_gids[state->num_gids] = gid;
+ DEBUG(SSSDBG_TRACE_INTERNAL, ("Adding GID %llu\n", gid));
+ state->num_gids++;
+ return EOK;
+}
+
+static errno_t
+simple_check_get_groups_primary(struct simple_check_groups_state *state,
+ gid_t gid)
+{
+ errno_t ret;
+ const char *group_attrs[] = { SYSDB_NAME, SYSDB_POSIX,
+ SYSDB_GIDNUM, NULL };
+ struct ldb_message *msg;
+
+ ret = sysdb_search_group_by_gid(state, state->ctx->domain->sysdb,
+ state->ctx->domain,
+ gid, group_attrs, &msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ ("Could not look up primary group [%lu]: [%d][%s]\n",
+ gid, ret, strerror(ret)));
+ /* We have to treat this as non-fatal, because the primary
+ * group may be local to the machine and not available in
+ * our ID provider.
+ */
+ } else {
+ ret = simple_check_process_group(state, msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, ("Cannot process primary group\n"));
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t
+simple_check_get_groups_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ const char ***_group_names)
+{
+ struct simple_check_groups_state *state;
+
+ state = tevent_req_data(req, struct simple_check_groups_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_group_names = talloc_steal(mem_ctx, state->group_names);
+ return EOK;
+}
+
+struct simple_access_check_state {
+ bool access_granted;
+ struct simple_ctx *ctx;
+ const char *username;
+
+ const char **group_names;
+};
+
+static void simple_access_check_done(struct tevent_req *subreq);
+
+struct tevent_req *simple_access_check_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct simple_ctx *ctx,
+ const char *username)
+{
+ errno_t ret;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct simple_access_check_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct simple_access_check_state);
+ if (!req) return NULL;
+
+ state->access_granted = false;
+ state->ctx = ctx;
+ state->username = talloc_strdup(state, username);
+ if (!state->username) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_FUNC_DATA, ("Simple access check for %s\n", username));
+
+ ret = simple_check_users(ctx, username, &state->access_granted);
+ if (ret != EAGAIN) {
+ /* Both access denied and an error */
+ goto immediate;
+ }
+
+ if (!ctx->allow_groups && !ctx->deny_groups) {
+ /* There are no group restrictions, so just return
+ * here with whatever we've decided.
+ */
+ DEBUG(SSSDBG_TRACE_LIBS, ("No group restrictions, end request\n"));
+ ret = EOK;
+ goto immediate;
+ }
+
+ /* The group names might not be available. Fire a request to
+ * gather them. In most cases, the request will just shortcut
+ */
+ subreq = simple_check_get_groups_send(state, ev, ctx, username);
+ if (!subreq) {
+ ret = EIO;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, simple_access_check_done, req);
+
+ return req;
+
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+
+static void simple_access_check_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct simple_access_check_state *state =
+ tevent_req_data(req, struct simple_access_check_state);
+ errno_t ret;
+
+ /* We know the names now. Run the check. */
+ ret = simple_check_get_groups_recv(subreq, state, &state->group_names);
+ talloc_zfree(subreq);
+ if (ret == ENOENT) {
+ /* If the user wasn't found, just shortcut */
+ state->access_granted = false;
+ tevent_req_done(req);
+ return;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ ("Could not collect groups of user %s\n", state->username));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = simple_check_groups(state->ctx, state->username,
+ state->group_names, &state->access_granted);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ /* Now just return whatever we decided */
+ DEBUG(SSSDBG_TRACE_INTERNAL, ("Group check done\n"));
+ tevent_req_done(req);
+}
+
+errno_t simple_access_check_recv(struct tevent_req *req, bool *access_granted)
+{
+ struct simple_access_check_state *state =
+ tevent_req_data(req, struct simple_access_check_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ DEBUG(SSSDBG_TRACE_LIBS,
+ ("Access %sgranted\n", state->access_granted ? "" : "not "));
+ if (access_granted) {
+ *access_granted = state->access_granted;
+ }
+
+ return EOK;
}