simple_access_check.c revision a3c8390d19593b1e5277d95bfb4ab206d4785150
842ae4bd224140319ae7feec1872b93dfd491143fielding Simple access control
842ae4bd224140319ae7feec1872b93dfd491143fielding Copyright (C) Sumit Bose <sbose@redhat.com> 2010
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd This program is free software; you can redistribute it and/or modify
945173cae9e0f894a50aec717acea9399680fdd5bnicholes it under the terms of the GNU General Public License as published by
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd the Free Software Foundation; either version 3 of the License, or
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd (at your option) any later version.
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd This program is distributed in the hope that it will be useful,
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd but WITHOUT ANY WARRANTY; without even the implied warranty of
945173cae9e0f894a50aec717acea9399680fdd5bnicholes MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
945173cae9e0f894a50aec717acea9399680fdd5bnicholes GNU General Public License for more details.
945173cae9e0f894a50aec717acea9399680fdd5bnicholes You should have received a copy of the GNU General Public License
945173cae9e0f894a50aec717acea9399680fdd5bnicholes along with this program. If not, see <http://www.gnu.org/licenses/>.
945173cae9e0f894a50aec717acea9399680fdd5bnicholes const char *val;
945173cae9e0f894a50aec717acea9399680fdd5bnicholes val = ldb_msg_find_attr_as_string(group, SYSDB_POSIX, NULL);
945173cae9e0f894a50aec717acea9399680fdd5bnicholes return true;
945173cae9e0f894a50aec717acea9399680fdd5bnicholes return false;
945173cae9e0f894a50aec717acea9399680fdd5bnicholes/* Returns EOK if the result is definitive, EAGAIN if only partial result
945173cae9e0f894a50aec717acea9399680fdd5bnicholessimple_check_users(struct simple_ctx *ctx, const char *username,
945173cae9e0f894a50aec717acea9399680fdd5bnicholes /* First, check whether the user is in the allowed users list */
945173cae9e0f894a50aec717acea9399680fdd5bnicholes domain = find_subdomain_by_object_name(ctx->domain,
9558e9fdb620dd6f42ca93beac6c3ab734086706bnicholes if (sss_string_equal(domain->case_sensitive, username,
945173cae9e0f894a50aec717acea9399680fdd5bnicholes "User [%s] found in allow list, access granted.\n",
945173cae9e0f894a50aec717acea9399680fdd5bnicholes /* Do not return immediately on explicit allow
945173cae9e0f894a50aec717acea9399680fdd5bnicholes * We need to make sure none of the user's groups
945173cae9e0f894a50aec717acea9399680fdd5bnicholes * are denied.
945173cae9e0f894a50aec717acea9399680fdd5bnicholes /* If neither allow rule is in place, we'll assume allowed
945173cae9e0f894a50aec717acea9399680fdd5bnicholes * unless a deny rule disables us below.
945173cae9e0f894a50aec717acea9399680fdd5bnicholes "No allow rule, assumuing allow unless explicitly denied\n");
945173cae9e0f894a50aec717acea9399680fdd5bnicholes /* Next check whether this user has been specifically denied */
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes domain = find_subdomain_by_object_name(ctx->domain,
ca47a2b6bcea23e8af185c68f256dcbbfd2a0f9dtrawick if (sss_string_equal(domain->case_sensitive, username,
ca47a2b6bcea23e8af185c68f256dcbbfd2a0f9dtrawick "User [%s] found in deny list, access denied.\n",
ca47a2b6bcea23e8af185c68f256dcbbfd2a0f9dtrawick /* Return immediately on explicit denial */
6e68ad13348b2b614939ae365470728026ff38betrawicksimple_check_groups(struct simple_ctx *ctx, const char **group_names,
945173cae9e0f894a50aec717acea9399680fdd5bnicholes /* Now process allow and deny group rules
945173cae9e0f894a50aec717acea9399680fdd5bnicholes * If access was already granted above, we'll skip
345aaf5706b61fecdedf85f06936b4ebe2f441e0bnicholes * this redundant rule check
945173cae9e0f894a50aec717acea9399680fdd5bnicholes domain = find_subdomain_by_object_name(ctx->domain,
945173cae9e0f894a50aec717acea9399680fdd5bnicholes for(j = 0; group_names[j]; j++) {
66a6ca2064281d93f6b7e8393ca2622458e21ed3bnicholes /* If any group has matched, we can skip out on the
945173cae9e0f894a50aec717acea9399680fdd5bnicholes * processing early
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes "Group [%s] found in allow list, access granted.\n",
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholes /* Finally, process the deny group rules */
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes domain = find_subdomain_by_object_name(ctx->domain,
945173cae9e0f894a50aec717acea9399680fdd5bnicholes for(j = 0; group_names[j]; j++) {
ecc16907392dd9a7a11037d54aa463cc1149788abnicholes /* If any group has matched, we can skip out on the
945173cae9e0f894a50aec717acea9399680fdd5bnicholes * processing early
c2d0a204f2777824f9c49c30296cfc2ae8ff4b0bjwoolley "Group [%s] found in deny list, access denied.\n",
3e155218733389e7b1ea3a9ffd0aea533fd929cechrisd const char *name;
3e155218733389e7b1ea3a9ffd0aea533fd929cechrisdsimple_resolve_group_check(struct simple_resolve_group_state *state);
3e155218733389e7b1ea3a9ffd0aea533fd929cechrisdstatic void simple_resolve_group_done(struct tevent_req *subreq);
945173cae9e0f894a50aec717acea9399680fdd5bnicholesstatic struct tevent_req *
c4fbc4018fd2b6716673a38ee27eeb36cba41c5djwoolley /* First check if the group was updated already. If it was (maybe its
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes * parent was updated first), then just shortcut */
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes DEBUG(SSSDBG_TRACE_LIBS, "Group already updated\n");
945173cae9e0f894a50aec717acea9399680fdd5bnicholes "Cannot check if group was already updated [%d]: %s\n",
945173cae9e0f894a50aec717acea9399680fdd5bnicholes /* EAGAIN - still needs update */
b5b31852ab27739ab90febad74faefe8dab5b24efuankg ar->filter_value = talloc_asprintf(ar, "%llu", (unsigned long long) gid);
b5b31852ab27739ab90febad74faefe8dab5b24efuankg subreq = be_get_account_info_send(state, ev, NULL, ctx->be_ctx, ar);
945173cae9e0f894a50aec717acea9399680fdd5bnicholes tevent_req_set_callback(subreq, simple_resolve_group_done, req);
8ab4d23ce5b402430c92e7540a1953523afbae4fbnicholessimple_resolve_group_check(struct simple_resolve_group_state *state)
945173cae9e0f894a50aec717acea9399680fdd5bnicholes const char *group_attrs[] = { SYSDB_NAME, SYSDB_POSIX,
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes /* Check the cache by GID again and fetch the name */
f4d8b0f32a6e28c425a3460b12ee3cc2a760b113bnicholes ret = sysdb_search_group_by_gid(state, state->domain, state->gid,
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes /* The group is missing, we will try to update it. */
945173cae9e0f894a50aec717acea9399680fdd5bnicholes "Could not look up group by gid [%"SPRIgid"]: [%d][%s]\n",
945173cae9e0f894a50aec717acea9399680fdd5bnicholes state->name = ldb_msg_find_attr_as_string(group, SYSDB_NAME, NULL);
945173cae9e0f894a50aec717acea9399680fdd5bnicholes "The group is still non-POSIX\n");
ef51c0782d5ae6867ee33fab6ed29fc4745ed66fbnicholesstatic void simple_resolve_group_done(struct tevent_req *subreq)
8eddc914b28a460d7c590331ee9313d1fd9ae125bnicholes const char *err_msg;
e970053cef302d9a33c4d6f848adc004cc2e916dbnicholes req = tevent_req_callback_data(subreq, struct tevent_req);
945173cae9e0f894a50aec717acea9399680fdd5bnicholes state = tevent_req_data(req, struct simple_resolve_group_state);
945173cae9e0f894a50aec717acea9399680fdd5bnicholes DEBUG(SSSDBG_OP_FAILURE, "be_get_account_info_recv failed\n");
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes "Cannot refresh data from DP: %u,%u: %s\n",
e4d36aa1eb0631a1b696c7a70d696f9c869bddccjwoolley /* Check the cache by GID again and fetch the name */
945173cae9e0f894a50aec717acea9399680fdd5bnicholes const char **name)
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes state = tevent_req_data(req, struct simple_resolve_group_state);
945173cae9e0f894a50aec717acea9399680fdd5bnicholes const char **group_names;
945173cae9e0f894a50aec717acea9399680fdd5bnicholesstatic void simple_check_get_groups_next(struct tevent_req *subreq);
c2d0a204f2777824f9c49c30296cfc2ae8ff4b0bjwoolleysimple_check_get_groups_primary(struct simple_check_groups_state *state,
e8f95a682820a599fe41b22977010636be5c2717jimsimple_check_process_group(struct simple_check_groups_state *state,
945173cae9e0f894a50aec717acea9399680fdd5bnicholesstatic struct tevent_req *
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes const char *attrs[] = { SYSDB_NAME, SYSDB_POSIX, SYSDB_GIDNUM,
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes DEBUG(SSSDBG_TRACE_LIBS, "Looking up groups for user %s\n", username);
945173cae9e0f894a50aec717acea9399680fdd5bnicholes /* get domain from username */
945173cae9e0f894a50aec717acea9399680fdd5bnicholes state->domain = find_subdomain_by_object_name(ctx->domain, username);
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes DEBUG(SSSDBG_CRIT_FAILURE, "Invalid user %s!\n", username);
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes ret = sysdb_search_user_by_name(state, state->domain, username, attrs,
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes DEBUG(SSSDBG_MINOR_FAILURE, "No such user %s\n", username);
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes "Could not look up username [%s]: [%d][%s]\n",
66a6ca2064281d93f6b7e8393ca2622458e21ed3bnicholes "User %s is a member of %zu supplemental groups\n",
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes /* One extra space for terminator, one extra space for private group */
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes state->group_names = talloc_zero_array(state, const char *, group_count + 2);
65a99b1db8af484b996b11cd3a73e3192bce145dbnicholes state->lookup_groups = talloc_zero_array(state, struct simple_group,
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes if (!state->group_names || !state->lookup_groups) {
66a6ca2064281d93f6b7e8393ca2622458e21ed3bnicholes for (i=0; i < group_count; i++) {
945173cae9e0f894a50aec717acea9399680fdd5bnicholes /* Some providers (like the AD provider) might perform initgroups
66a6ca2064281d93f6b7e8393ca2622458e21ed3bnicholes * without resolving the group names. In order for the simple access
66a6ca2064281d93f6b7e8393ca2622458e21ed3bnicholes * provider to work correctly, we need to resolve the groups before
66a6ca2064281d93f6b7e8393ca2622458e21ed3bnicholes * performing the access check. In AD provider, the situation is
abb33f4c0ab7b5e2a1b404b913776a3f5487d69bbnicholes * even more tricky b/c the groups HAVE name, but their name
9179fa90e821c964d10f28b97fc6acee776af7cfwrowe * attribute is set to SID and they are set as non-POSIX
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes ret = simple_check_process_group(state, groups[i]);
abb33f4c0ab7b5e2a1b404b913776a3f5487d69bbnicholes gid = ldb_msg_find_attr_as_uint64(user, SYSDB_GIDNUM, 0);
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes DEBUG(SSSDBG_MINOR_FAILURE, "User %s has no gid?\n", username);
65a99b1db8af484b996b11cd3a73e3192bce145dbnicholes /* If all groups could have been resolved by name, we are
65a99b1db8af484b996b11cd3a73e3192bce145dbnicholes DEBUG(SSSDBG_TRACE_FUNC, "All groups had name attribute\n");
4ceb1c7cc31a6fa57903b73d23201f84ba41727ebnicholes DEBUG(SSSDBG_TRACE_FUNC, "Need to resolve %zu groups\n",
4ceb1c7cc31a6fa57903b73d23201f84ba41727ebnicholes subreq = simple_resolve_group_send(req, state->ev, state->ctx,
4ceb1c7cc31a6fa57903b73d23201f84ba41727ebnicholes tevent_req_set_callback(subreq, simple_check_get_groups_next, req);
945173cae9e0f894a50aec717acea9399680fdd5bnicholesstatic void simple_check_get_groups_next(struct tevent_req *subreq)
945173cae9e0f894a50aec717acea9399680fdd5bnicholes tevent_req_callback_data(subreq, struct tevent_req);
945173cae9e0f894a50aec717acea9399680fdd5bnicholes tevent_req_data(req, struct simple_check_groups_state);
2fc50921b88defeb7127985dfe4b4130175e069ejwoolley ret = simple_resolve_group_recv(subreq, state->group_names,
d7d24786c80ad1ae337b916a0a44b2a7b8fcb54drbb "Could not resolve name of group with GID %"SPRIgid"\n",
945173cae9e0f894a50aec717acea9399680fdd5bnicholes subreq = simple_resolve_group_send(req, state->ev, state->ctx,
945173cae9e0f894a50aec717acea9399680fdd5bnicholes tevent_req_set_callback(subreq, simple_check_get_groups_next, req);
8b6db5ee2c727568cccb16a035c90ab970d310febnicholes DEBUG(SSSDBG_TRACE_INTERNAL, "All groups resolved. Done.\n");
945173cae9e0f894a50aec717acea9399680fdd5bnicholessimple_check_process_group(struct simple_check_groups_state *state,
945173cae9e0f894a50aec717acea9399680fdd5bnicholes const char *name;
e8f95a682820a599fe41b22977010636be5c2717jim name = ldb_msg_find_attr_as_string(group, SYSDB_NAME, NULL);
ca47a2b6bcea23e8af185c68f256dcbbfd2a0f9dtrawick gid = ldb_msg_find_attr_as_uint64(group, SYSDB_GIDNUM, 0);
945173cae9e0f894a50aec717acea9399680fdd5bnicholes /* With the current sysdb layout, every group has a name */
945173cae9e0f894a50aec717acea9399680fdd5bnicholes if (gid == 0) {
945173cae9e0f894a50aec717acea9399680fdd5bnicholes if (posix == true) {
945173cae9e0f894a50aec717acea9399680fdd5bnicholes DEBUG(SSSDBG_CRIT_FAILURE, "POSIX group without GID\n");
945173cae9e0f894a50aec717acea9399680fdd5bnicholes /* Non-posix group with a name. Still can be used for access
945173cae9e0f894a50aec717acea9399680fdd5bnicholes * control as the name should point to the real name, no SID
945173cae9e0f894a50aec717acea9399680fdd5bnicholes state->group_names[state->num_names] = talloc_strdup(state->group_names,
945173cae9e0f894a50aec717acea9399680fdd5bnicholes DEBUG(SSSDBG_TRACE_INTERNAL, "Adding group %s\n", name);
945173cae9e0f894a50aec717acea9399680fdd5bnicholes /* Here are only groups with a name and gid. POSIX group can already
945173cae9e0f894a50aec717acea9399680fdd5bnicholes * be used, non-POSIX groups can be resolved */
945173cae9e0f894a50aec717acea9399680fdd5bnicholes state->group_names[state->num_names] = talloc_strdup(state->group_names,
945173cae9e0f894a50aec717acea9399680fdd5bnicholes DEBUG(SSSDBG_TRACE_INTERNAL, "Adding group %s\n", name);
945173cae9e0f894a50aec717acea9399680fdd5bnicholes /* Try to get group SID and assign it a domain */
945173cae9e0f894a50aec717acea9399680fdd5bnicholes group_sid = ldb_msg_find_attr_as_string(group, SYSDB_SID_STR, NULL);
945173cae9e0f894a50aec717acea9399680fdd5bnicholes /* We will look it up in main domain. */
945173cae9e0f894a50aec717acea9399680fdd5bnicholes domain = find_subdomain_by_sid(state->ctx->domain, group_sid);
945173cae9e0f894a50aec717acea9399680fdd5bnicholes DEBUG(SSSDBG_CRIT_FAILURE, "There is no domain information for "
945173cae9e0f894a50aec717acea9399680fdd5bnicholes /* It is a non-posix group with a GID. Needs resolving */
945173cae9e0f894a50aec717acea9399680fdd5bnicholes state->lookup_groups[state->num_groups].domain = domain;
945173cae9e0f894a50aec717acea9399680fdd5bnicholes state->lookup_groups[state->num_groups].gid = gid;
945173cae9e0f894a50aec717acea9399680fdd5bnicholes DEBUG(SSSDBG_TRACE_INTERNAL, "Adding GID %"SPRIgid"\n", gid);
945173cae9e0f894a50aec717acea9399680fdd5bnicholessimple_check_get_groups_primary(struct simple_check_groups_state *state,
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes const char *group_attrs[] = { SYSDB_NAME, SYSDB_POSIX,
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes ret = sysdb_search_group_by_gid(state, state->domain, gid, group_attrs,
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes "Could not look up primary group [%"SPRIgid"]: [%d][%s]\n",
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes /* We have to treat this as non-fatal, because the primary
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes * group may be local to the machine and not available in
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes * our ID provider.
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes DEBUG(SSSDBG_OP_FAILURE, "Cannot process primary group\n");
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholessimple_check_get_groups_recv(struct tevent_req *req,
945173cae9e0f894a50aec717acea9399680fdd5bnicholes const char ***_group_names)
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes state = tevent_req_data(req, struct simple_check_groups_state);
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes *_group_names = talloc_steal(mem_ctx, state->group_names);
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes const char **group_names;
185aa71728867671e105178b4c66fbc22b65ae26sfstatic void simple_access_check_done(struct tevent_req *subreq);
3fa816e4832a1c70600bdfd6fc5ef60e9f1c18bbsfstruct tevent_req *simple_access_check_send(TALLOC_CTX *mem_ctx,
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes DEBUG(SSSDBG_FUNC_DATA, "Simple access check for %s\n", username);
7858523f0ca7d8d4365f6639fdb79b5200eff7bbbnicholes ret = simple_check_users(ctx, username, &state->access_granted);
945173cae9e0f894a50aec717acea9399680fdd5bnicholes /* EAGAIN -- check groups */
b5b31852ab27739ab90febad74faefe8dab5b24efuankg /* There are no group restrictions, so just return
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholes * here with whatever we've decided.
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholes DEBUG(SSSDBG_TRACE_LIBS, "No group restrictions, end request\n");
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes /* The group names might not be available. Fire a request to
e8f95a682820a599fe41b22977010636be5c2717jim * gather them. In most cases, the request will just shortcut
abb33f4c0ab7b5e2a1b404b913776a3f5487d69bbnicholes subreq = simple_check_get_groups_send(state, ev, ctx, username);
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholes tevent_req_set_callback(subreq, simple_access_check_done, req);
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholesstatic void simple_access_check_done(struct tevent_req *subreq)
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholes tevent_req_callback_data(subreq, struct tevent_req);
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholes tevent_req_data(req, struct simple_access_check_state);
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholes /* We know the names now. Run the check. */
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholes ret = simple_check_get_groups_recv(subreq, state, &state->group_names);
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholes /* If the user wasn't found, just shortcut */
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholes "Could not collect groups of user %s\n", state->username);
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholes ret = simple_check_groups(state->ctx, state->group_names,
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholes DEBUG(SSSDBG_OP_FAILURE, "Could not check group access [%d]: %s\n",
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholes /* Now just return whatever we decided */
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholes DEBUG(SSSDBG_TRACE_INTERNAL, "Group check done\n");
2eb905c271e33af72f0a31c9a818169e65ece8c6bnicholeserrno_t simple_access_check_recv(struct tevent_req *req, bool *access_granted)
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes tevent_req_data(req, struct simple_access_check_state);
b6215bd93a599b7962b7ed6387b4990de3a8adb5bnicholes "Access %sgranted\n", state->access_granted ? "" : "not ");