/* 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 #include #include "util/util.h" #include "util/sss_utf8.h" #include "providers/dp_backend.h" #include "db/sysdb.h" #include "providers/simple/simple_access.h" #define CONFDB_SIMPLE_ALLOW_USERS "simple_allow_users" #define CONFDB_SIMPLE_DENY_USERS "simple_deny_users" #define CONFDB_SIMPLE_ALLOW_GROUPS "simple_allow_groups" #define CONFDB_SIMPLE_DENY_GROUPS "simple_deny_groups" errno_t simple_access_check(struct simple_ctx *ctx, const char *username, bool *access_granted) { int i, j; errno_t ret; TALLOC_CTX *tmp_ctx = NULL; const char *user_attrs[] = { SYSDB_MEMBEROF, SYSDB_GIDNUM, NULL }; const char *group_attrs[] = { SYSDB_NAME, NULL }; struct ldb_message *msg; struct ldb_message_element *el; char **groups; const char *primary_group; gid_t gid; bool matched; bool cs = ctx->domain->case_sensitive; *access_granted = false; /* First, check whether the user is in the allowed users list */ if (ctx->allow_users != NULL) { for(i = 0; ctx->allow_users[i] != NULL; i++) { if (sss_string_equal(cs, username, ctx->allow_users[i])) { DEBUG(9, ("User [%s] found in allow list, access granted.\n", username)); /* Do not return immediately on explicit allow * We need to make sure none of the user's groups * are denied. */ *access_granted = true; } } } else if (!ctx->allow_groups) { /* If neither allow rule is in place, we'll assume allowed * unless a deny rule disables us below. */ *access_granted = true; } /* Next check whether this user has been specifically denied */ if (ctx->deny_users != NULL) { for(i = 0; ctx->deny_users[i] != NULL; i++) { if (sss_string_equal(cs, username, ctx->deny_users[i])) { DEBUG(9, ("User [%s] found in deny list, access denied.\n", username)); /* Return immediately on explicit denial */ *access_granted = false; return EOK; } } } if (!ctx->allow_groups && !ctx->deny_groups) { /* There are no group restrictions, so just return * here with whatever we've decided. */ return EOK; } /* Now get a list of this user's groups and check those against the * simple_allow_groups list. */ tmp_ctx = talloc_new(NULL); if (!tmp_ctx) { ret = ENOMEM; goto done; } ret = sysdb_search_user_by_name(tmp_ctx, ctx->sysdb, username, user_attrs, &msg); if (ret != EOK) { DEBUG(1, ("Could not look up username [%s]: [%d][%s]\n", username, ret, strerror(ret))); goto done; } /* Construct a list of the user's groups */ el = ldb_msg_find_element(msg, SYSDB_MEMBEROF); if (el && el->num_values) { /* Get the groups from the memberOf entries * Allocate the array with room for both the NULL * terminator and the primary group */ groups = talloc_array(tmp_ctx, char *, el->num_values + 2); if (!groups) { ret = ENOMEM; goto done; } for (j = 0; j < el->num_values; j++) { ret = sysdb_group_dn_name( ctx->sysdb, tmp_ctx, (char *)el->values[j].data, &groups[j]); if (ret != EOK) { goto done; } } } else { /* User is not a member of any groups except primary */ groups = talloc_array(tmp_ctx, char *, 2); if (!groups) { ret = ENOMEM; goto done; } j = 0; } /* Get the user's primary group */ gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0); if (!gid) { ret = EINVAL; goto done; } talloc_zfree(msg); ret = sysdb_search_group_by_gid(tmp_ctx, ctx->sysdb, gid, group_attrs, &msg); if (ret != EOK) { DEBUG(1, ("Could not look up primary group [%lu]: [%d][%s]\n", gid, ret, strerror(ret))); /* We have to treat this as non-fatal, because the primary * group may be local to the machine and not available in * our ID provider. */ } else { primary_group = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); if (!primary_group) { ret = EINVAL; goto done; } groups[j] = talloc_strdup(tmp_ctx, primary_group); if (!groups[j]) { ret = ENOMEM; goto done; } j++; talloc_zfree(msg); } groups[j] = NULL; /* Now process allow and deny group rules * If access was already granted above, we'll skip * this redundant rule check */ if (ctx->allow_groups && !*access_granted) { matched = false; for (i = 0; ctx->allow_groups[i]; i++) { for(j = 0; groups[j]; j++) { if (sss_string_equal(cs, groups[j], ctx->allow_groups[i])) { matched = true; break; } } /* If any group has matched, we can skip out on the * processing early */ if (matched) { *access_granted = true; break; } } } /* Finally, process the deny group rules */ if (ctx->deny_groups) { matched = false; for (i = 0; ctx->deny_groups[i]; i++) { for(j = 0; groups[j]; j++) { if (sss_string_equal(cs, groups[j], ctx->deny_groups[i])) { matched = true; break; } } /* If any group has matched, we can skip out on the * processing early */ if (matched) { *access_granted = false; break; } } } ret = EOK; done: talloc_free(tmp_ctx); return ret; } void simple_access_handler(struct be_req *be_req) { int ret; bool access_granted = false; struct pam_data *pd; struct simple_ctx *ctx; 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); if (ret != EOK) { pd->pam_status = PAM_SYSTEM_ERR; goto done; } if (access_granted) { pd->pam_status = PAM_SUCCESS; } else { pd->pam_status = PAM_PERM_DENIED; } done: be_req->fn(be_req, DP_ERR_OK, pd->pam_status, NULL); } struct bet_ops simple_access_ops = { .handler = simple_access_handler, .finalize = NULL }; int sssm_simple_access_init(struct be_ctx *bectx, struct bet_ops **ops, void **pvt_data) { int ret = EINVAL; struct simple_ctx *ctx; ctx = talloc_zero(bectx, struct simple_ctx); if (ctx == NULL) { DEBUG(1, ("talloc_zero failed.\n")); return ENOMEM; } ctx->sysdb = bectx->sysdb; ctx->domain = bectx->domain; /* Users */ ret = confdb_get_string_as_list(bectx->cdb, ctx, bectx->conf_path, CONFDB_SIMPLE_ALLOW_USERS, &ctx->allow_users); if (ret != EOK) { if (ret == ENOENT) { DEBUG(9, ("Allow user list is empty.\n")); ctx->allow_users = NULL; } else { DEBUG(1, ("confdb_get_string_as_list failed.\n")); goto failed; } } ret = confdb_get_string_as_list(bectx->cdb, ctx, bectx->conf_path, CONFDB_SIMPLE_DENY_USERS, &ctx->deny_users); if (ret != EOK) { if (ret == ENOENT) { DEBUG(9, ("Deny user list is empty.\n")); ctx->deny_users = NULL; } else { DEBUG(1, ("confdb_get_string_as_list failed.\n")); goto failed; } } /* Groups */ ret = confdb_get_string_as_list(bectx->cdb, ctx, bectx->conf_path, CONFDB_SIMPLE_ALLOW_GROUPS, &ctx->allow_groups); if (ret != EOK) { if (ret == ENOENT) { DEBUG(9, ("Allow group list is empty.\n")); ctx->allow_groups = NULL; } else { DEBUG(1, ("confdb_get_string_as_list failed.\n")); goto failed; } } ret = confdb_get_string_as_list(bectx->cdb, ctx, bectx->conf_path, CONFDB_SIMPLE_DENY_GROUPS, &ctx->deny_groups); if (ret != EOK) { if (ret == ENOENT) { DEBUG(9, ("Deny user list is empty.\n")); ctx->deny_groups = NULL; } else { DEBUG(1, ("confdb_get_string_as_list failed.\n")); goto failed; } } if (!ctx->allow_users && !ctx->allow_groups && !ctx->deny_users && !ctx->deny_groups) { DEBUG(SSSDBG_OP_FAILURE, ("No rules supplied for simple access provider. " "Access will be granted for all users.\n")); } *ops = &simple_access_ops; *pvt_data = ctx; return EOK; failed: talloc_free(ctx); return ret; }