simple_access_check.c revision 3d29430867cf92b2d71afa95abb679711231117c
/*
SSSD
Simple access control
Copyright (C) Sumit Bose <sbose@redhat.com> 2010
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/simple/simple_access.h"
#include "util/sss_utf8.h"
#define NON_EXIST_USR_ALLOW "The user %s does not exist. Possible typo in simple_allow_users.\n"
#define NON_EXIST_USR_DENY "The user %s does not exist. Possible typo in simple_deny_users.\n"
#define NON_EXIST_GRP_ALLOW "The group %s does not exist. Possible typo in simple_allow_groups.\n"
#define NON_EXIST_GRP_DENY "The group %s does not exist. Possible typo in simple_deny_groups.\n"
static bool
{
const char *val;
if (!val || /* Groups are posix by default */
return true;
}
return false;
}
/* Returns EOK if the result is definitive, EAGAIN if only partial result
*/
static errno_t
bool *access_granted)
{
int i;
/* First, check whether the user is in the allowed users list */
ctx->allow_users[i]);
ctx->allow_users[i]);
ctx->allow_users[i]);
continue;
}
ctx->allow_users[i])) {
"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. But there's no need to check username
* matches any more.
*/
*access_granted = true;
break;
}
}
} else if (!ctx->allow_groups) {
/* If neither allow rule is in place, we'll assume allowed
* unless a deny rule disables us below.
*/
"No allow rule, assumuing allow unless explicitly denied\n");
*access_granted = true;
}
/* Next check whether this user has been specifically denied */
ctx->deny_users[i]);
ctx->deny_users[i]);
ctx->deny_users[i]);
return EINVAL;
}
ctx->deny_users[i])) {
"User [%s] found in deny list, access denied.\n",
ctx->deny_users[i]);
/* Return immediately on explicit denial */
*access_granted = false;
return EOK;
}
}
}
return EAGAIN;
}
static errno_t
bool *access_granted)
{
bool matched;
int i, j;
/* Now process allow and deny group rules
* If access was already granted above, we'll skip
* this redundant rule check
*/
matched = false;
for (i = 0; ctx->allow_groups[i]; i++) {
ctx->allow_groups[i]);
ctx->allow_groups[i]);
ctx->allow_groups[i]);
continue;
}
for(j = 0; group_names[j]; j++) {
matched = true;
break;
}
}
/* If any group has matched, we can skip out on the
* processing early
*/
if (matched) {
"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++) {
ctx->deny_groups[i]);
ctx->deny_groups[i]);
ctx->deny_groups[i]);
return EINVAL;
}
for(j = 0; group_names[j]; j++) {
matched = true;
break;
}
}
/* If any group has matched, we can skip out on the
* processing early
*/
if (matched) {
"Group [%s] found in deny list, access denied.\n",
group_names[j]);
*access_granted = false;
break;
}
}
}
return EOK;
}
struct simple_resolve_group_state {
struct sss_domain_info *domain;
struct simple_ctx *ctx;
const char *name;
};
static errno_t
static struct tevent_req *
struct tevent_context *ev,
struct simple_ctx *ctx,
struct sss_domain_info *domain,
{
struct tevent_req *req;
struct tevent_req *subreq;
struct simple_resolve_group_state *state;
struct dp_id_data *ar;
struct simple_resolve_group_state);
/* First check if the group was updated already. If it was (maybe its
* parent was updated first), then just shortcut */
goto done;
"Cannot check if group was already updated [%d]: %s\n",
goto done;
}
/* EAGAIN - still needs update */
if (!ar) {
goto done;
}
goto done;
}
if (!subreq) {
goto done;
}
return req;
done:
} else {
}
return req;
}
static errno_t
{
struct ldb_message *group;
SYSDB_GIDNUM, NULL };
/* Check the cache by GID again and fetch the name */
group_attrs, &group);
/* The group is missing, we will try to update it. */
return EAGAIN;
return ret;
}
return ERR_ACCOUNT_UNKNOWN;
}
"The group is still non-POSIX\n");
return EAGAIN;
}
return EOK;
}
{
struct tevent_req *req;
struct simple_resolve_group_state *state;
struct dp_reply_std *reply;
if (ret) {
return;
}
"Cannot refresh data from DP: %u,%u: %s\n",
return;
}
/* Check the cache by GID again and fetch the name */
return;
}
}
static errno_t
const char **name)
{
struct simple_resolve_group_state *state;
return EOK;
}
struct simple_group {
struct sss_domain_info *domain;
};
struct simple_check_groups_state {
struct tevent_context *ev;
struct simple_ctx *ctx;
struct sss_domain_info *domain;
struct simple_group *lookup_groups;
const char **group_names;
bool failed_to_resolve_groups;
};
static errno_t
static errno_t
struct ldb_message *group);
static struct tevent_req *
struct tevent_context *ev,
struct simple_ctx *ctx,
const char *username)
{
struct tevent_req *req;
struct tevent_req *subreq;
struct simple_check_groups_state *state;
SYSDB_SID_STR, NULL };
struct ldb_message *user;
struct ldb_message **groups;
int i;
struct simple_check_groups_state);
state->failed_to_resolve_groups = false;
/* get domain from username */
goto done;
}
&user);
goto done;
"Could not look up username [%s]: [%d][%s]\n",
goto done;
}
goto done;
}
"User %s is a member of %zu supplemental groups\n",
/* One extra space for terminator, one extra space for private group */
group_count + 2);
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
*/
goto done;
}
}
if (!gid) {
goto done;
}
goto done;
}
if (state->num_groups == 0) {
/* If all groups could have been resolved by name, we are
* done
*/
goto done;
}
state->num_groups);
if (!subreq) {
goto done;
}
return req;
done:
} else {
}
return req;
}
{
struct tevent_req *req =
struct simple_check_groups_state *state =
state->failed_to_resolve_groups = true;
} else {
}
if (!subreq) {
return;
}
return;
}
}
static errno_t
struct ldb_message *group)
{
const char *name;
const char *group_sid;
struct sss_domain_info *domain;
bool posix;
/* With the current sysdb layout, every group has a name */
return EINVAL;
}
if (gid == 0) {
if (posix == true) {
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
*/
name);
return ENOMEM;
}
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) {
name);
return ENOMEM;
}
return EOK;
}
/* Try to get group SID and assign it a domain */
/* We will look it up in main domain. */
} else {
"SID %s\n", group_sid);
return ENOENT;
}
}
/* It is a non-posix group with a GID. Needs resolving */
state->num_groups++;
return EOK;
}
static errno_t
{
struct ldb_message *msg;
&msg);
/* 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 {
return ret;
}
}
return EOK;
}
static errno_t
const char ***_group_names)
{
struct simple_check_groups_state *state;
if (state->failed_to_resolve_groups) {
return ERR_SIMPLE_GROUPS_MISSING;
}
return EOK;
}
struct simple_access_check_state {
bool access_granted;
struct simple_ctx *ctx;
const char *username;
const char **group_names;
};
struct tevent_context *ev,
struct simple_ctx *ctx,
const char *username)
{
struct tevent_req *req;
struct tevent_req *subreq;
struct simple_access_check_state *state;
struct simple_access_check_state);
state->access_granted = false;
goto immediate;
}
goto immediate;
ret = ERR_INTERNAL;
goto immediate;
}
/* EAGAIN -- check groups */
/* There are no group restrictions, so just return
* here with whatever we've decided.
*/
goto immediate;
}
/* The group names might not be available. Fire a request to
* gather them. In most cases, the request will just shortcut
*/
if (!subreq) {
goto immediate;
}
return req;
} else {
}
return req;
}
{
struct tevent_req *req =
struct simple_access_check_state *state =
/* We know the names now. Run the check. */
/* If the user wasn't found, just shortcut */
state->access_granted = false;
return;
} else if (ret == ERR_SIMPLE_GROUPS_MISSING) {
"But no deny groups were defined so we can continue.\n");
} else {
"Some deny groups were defined, we can't continue\n");
return;
}
return;
}
&state->access_granted);
return;
}
/* Now just return whatever we decided */
}
{
struct simple_access_check_state *state =
if (access_granted) {
}
return EOK;
}