ifp_groups.c revision 8fe171bf5a7a570591418e6548105f1d5a0097b3
/*
Authors:
Pavel Březina <pbrezina@redhat.com>
Copyright (C) 2015 Red Hat
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 <talloc.h>
#include <tevent.h>
#include "util/util.h"
#include "db/sysdb.h"
#include "util/strtonum.h"
#include "sbus/sssd_dbus_errors.h"
#include "responder/common/responder.h"
#include "responder/common/responder_cache_req.h"
#include "responder/ifp/ifp_groups.h"
#include "responder/ifp/ifp_users.h"
char * ifp_groups_build_path_from_msg(TALLOC_CTX *mem_ctx,
struct sss_domain_info *domain,
struct ldb_message *msg)
{
const char *gid;
gid = ldb_msg_find_attr_as_string(msg, SYSDB_GIDNUM, NULL);
if (gid == NULL) {
return NULL;
}
return sbus_opath_compose(mem_ctx, IFP_PATH_GROUPS, domain->name, gid);
}
static errno_t ifp_groups_decompose_path(struct sss_domain_info *domains,
const char *path,
struct sss_domain_info **_domain,
gid_t *_gid)
{
char **parts = NULL;
struct sss_domain_info *domain;
gid_t gid;
errno_t ret;
ret = sbus_opath_decompose_exact(NULL, path, IFP_PATH_GROUPS, 2, &parts);
if (ret != EOK) {
return ret;
}
domain = find_domain_by_name(domains, parts[0], false);
if (domain == NULL) {
ret = ERR_DOMAIN_NOT_FOUND;
goto done;
}
gid = strtouint32(parts[1], NULL, 10);
if (errno != 0) {
ret = errno;
goto done;
}
*_domain = domain;
*_gid = gid;
done:
talloc_free(parts);
return ret;
}
static void ifp_groups_find_by_name_done(struct tevent_req *req);
int ifp_groups_find_by_name(struct sbus_request *sbus_req,
void *data,
const char *name)
{
struct ifp_ctx *ctx;
struct tevent_req *req;
ctx = talloc_get_type(data, struct ifp_ctx);
if (ctx == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n");
return ERR_INTERNAL;
}
req = cache_req_group_by_name_send(sbus_req, ctx->rctx->ev, ctx->rctx,
ctx->ncache, ctx->neg_timeout, 0,
NULL, name);
if (req == NULL) {
return ENOMEM;
}
tevent_req_set_callback(req, ifp_groups_find_by_name_done, sbus_req);
return EOK;
}
static void
ifp_groups_find_by_name_done(struct tevent_req *req)
{
DBusError *error;
struct sbus_request *sbus_req;
struct sss_domain_info *domain;
struct ldb_result *result;
char *object_path;
errno_t ret;
sbus_req = tevent_req_callback_data(req, struct sbus_request);
ret = cache_req_group_by_name_recv(sbus_req, req, &result, &domain, NULL);
talloc_zfree(req);
if (ret == ENOENT) {
error = sbus_error_new(sbus_req, SBUS_ERROR_NOT_FOUND,
"Group not found");
goto done;
} else if (ret != EOK) {
error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED, "Failed to fetch "
"group [%d]: %s\n", ret, sss_strerror(ret));
goto done;
}
object_path = ifp_groups_build_path_from_msg(sbus_req, domain,
result->msgs[0]);
if (object_path == NULL) {
error = sbus_error_new(sbus_req, SBUS_ERROR_INTERNAL,
"Failed to compose object path");
goto done;
}
ret = EOK;
done:
if (ret != EOK) {
sbus_request_fail_and_finish(sbus_req, error);
return;
}
iface_ifp_groups_FindByName_finish(sbus_req, object_path);
return;
}
static void ifp_groups_find_by_id_done(struct tevent_req *req);
int ifp_groups_find_by_id(struct sbus_request *sbus_req,
void *data,
uint32_t id)
{
struct ifp_ctx *ctx;
struct tevent_req *req;
ctx = talloc_get_type(data, struct ifp_ctx);
if (ctx == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n");
return ERR_INTERNAL;
}
req = cache_req_group_by_id_send(sbus_req, ctx->rctx->ev, ctx->rctx,
ctx->ncache, ctx->neg_timeout, 0,
NULL, id);
if (req == NULL) {
return ENOMEM;
}
tevent_req_set_callback(req, ifp_groups_find_by_id_done, sbus_req);
return EOK;
}
static void
ifp_groups_find_by_id_done(struct tevent_req *req)
{
DBusError *error;
struct sbus_request *sbus_req;
struct sss_domain_info *domain;
struct ldb_result *result;
char *object_path;
errno_t ret;
sbus_req = tevent_req_callback_data(req, struct sbus_request);
ret = cache_req_group_by_id_recv(sbus_req, req, &result, &domain);
talloc_zfree(req);
if (ret == ENOENT) {
error = sbus_error_new(sbus_req, SBUS_ERROR_NOT_FOUND,
"Group not found");
goto done;
} else if (ret != EOK) {
error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED, "Failed to fetch "
"group [%d]: %s\n", ret, sss_strerror(ret));
goto done;
}
object_path = ifp_groups_build_path_from_msg(sbus_req, domain,
result->msgs[0]);
if (object_path == NULL) {
error = sbus_error_new(sbus_req, SBUS_ERROR_INTERNAL,
"Failed to compose object path");
goto done;
}
done:
if (ret != EOK) {
sbus_request_fail_and_finish(sbus_req, error);
return;
}
iface_ifp_groups_FindByID_finish(sbus_req, object_path);
return;
}
int ifp_groups_list_by_name(struct sbus_request *sbus_req,
void *data,
const char *filter,
uint32_t limit)
{
return EOK;
}
int ifp_groups_list_by_domain_and_name(struct sbus_request *sbus_req,
void *data,
const char *domain,
const char *filter,
uint32_t limit)
{
return EOK;
}
static errno_t
ifp_groups_group_get(struct sbus_request *sbus_req,
void *data,
gid_t *_gid,
struct sss_domain_info **_domain,
struct ldb_message **_group)
{
struct ifp_ctx *ctx;
struct sss_domain_info *domain;
struct ldb_result *res;
uid_t gid;
errno_t ret;
ctx = talloc_get_type(data, struct ifp_ctx);
if (ctx == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n");
return ERR_INTERNAL;
}
ret = ifp_groups_decompose_path(ctx->rctx->domains, sbus_req->path,
&domain, &gid);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Unable to decompose object path"
"[%s] [%d]: %s\n", sbus_req->path, ret, sss_strerror(ret));
return ret;
}
if (_group != NULL) {
ret = sysdb_getgrgid_with_views(sbus_req, domain, gid, &res);
if (ret == EOK && res->count == 0) {
*_group = NULL;
ret = ENOENT;
}
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup group %u@%s [%d]: %s\n",
gid, domain->name, ret, sss_strerror(ret));
} else {
*_group = res->msgs[0];
}
}
if (ret == EOK || ret == ENOENT) {
if (_gid != NULL) {
*_gid = gid;
}
if (_domain != NULL) {
*_domain = domain;
}
}
return ret;
}
struct resolv_ghosts_state {
struct tevent_context *ev;
struct sbus_request *sbus_req;
struct ifp_ctx *ctx;
void *data;
struct sss_domain_info *domain;
const char **ghosts;
int index;
};
static void resolv_ghosts_group_done(struct tevent_req *subreq);
static errno_t resolv_ghosts_step(struct tevent_req *req);
static void resolv_ghosts_done(struct tevent_req *subreq);
static struct tevent_req *resolv_ghosts_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sbus_request *sbus_req,
void *data)
{
struct resolv_ghosts_state *state;
struct sss_domain_info *domain;
struct tevent_req *req;
struct tevent_req *subreq;
struct ldb_message *group;
struct ifp_ctx *ctx;
const char *name;
errno_t ret;
req = tevent_req_create(mem_ctx, &state, struct resolv_ghosts_state);
if (req == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
return NULL;
}
ctx = talloc_get_type(data, struct ifp_ctx);
if (ctx == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n");
ret = ERR_INTERNAL;
goto immediately;
}
state->ev = ev;
state->sbus_req = sbus_req;
state->ctx = ctx;
state->data = data;
ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &group);
if (ret != EOK) {
goto immediately;
}
name = ldb_msg_find_attr_as_string(group, SYSDB_NAME, NULL);
if (name == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Group name is empty!\n");
ret = ERR_INTERNAL;
goto immediately;
}
subreq = cache_req_group_by_name_send(state, ev, ctx->rctx,
ctx->ncache, ctx->neg_timeout, 0,
domain->name, name);
if (subreq == NULL) {
ret = ENOMEM;
goto immediately;
}
tevent_req_set_callback(subreq, resolv_ghosts_group_done, req);
return req;
immediately:
if (ret == EOK) {
tevent_req_done(req);
} else {
tevent_req_error(req, ret);
}
tevent_req_post(req, ev);
return req;
}
static void resolv_ghosts_group_done(struct tevent_req *subreq)
{
struct resolv_ghosts_state *state;
struct ldb_message_element *el;
struct ldb_message *group;
struct tevent_req *req;
errno_t ret;
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct resolv_ghosts_state);
ret = ifp_groups_group_get(state->sbus_req, state->data, NULL,
&state->domain, &group);
if (ret != EOK) {
goto done;
}
el = ldb_msg_find_element(group, SYSDB_GHOST);
if (el == NULL) {
ret = ENOMEM;
goto done;
}
if (el->num_values == 0) {
ret = EOK;
goto done;
}
state->ghosts = sss_ldb_el_to_string_list(state, el);
if (state->ghosts == NULL) {
ret = ENOMEM;
goto done;
}
state->index = 0;
ret = resolv_ghosts_step(req);
done:
if (ret == EOK) {
tevent_req_done(req);
} else if (ret != EAGAIN) {
tevent_req_error(req, ret);
}
}
errno_t resolv_ghosts_step(struct tevent_req *req)
{
struct resolv_ghosts_state *state;
struct tevent_req *subreq;
state = tevent_req_data(req, struct resolv_ghosts_state);
if (state->ghosts[state->index] == NULL) {
return EOK;
}
subreq = cache_req_user_by_name_send(state, state->ev, state->ctx->rctx,
state->ctx->ncache, state->ctx->neg_timeout,
0, state->domain->name,
state->ghosts[state->index]);
if (subreq == NULL) {
return ENOMEM;
}
tevent_req_set_callback(subreq, resolv_ghosts_done, req);
state->index++;
return EAGAIN;
}
static void resolv_ghosts_done(struct tevent_req *subreq)
{
struct resolv_ghosts_state *state = NULL;
struct tevent_req *req = NULL;
errno_t ret;
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct resolv_ghosts_state);
ret = cache_req_user_by_name_recv(state, subreq, NULL, NULL, NULL);
talloc_zfree(subreq);
if (ret != EOK) {
goto done;
}
ret = resolv_ghosts_step(req);
done:
if (ret == EOK) {
tevent_req_done(req);
} else if (ret != EAGAIN) {
tevent_req_error(req, ret);
}
}
static errno_t resolv_ghosts_recv(struct tevent_req *req)
{
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
static void ifp_groups_group_update_member_list_done(struct tevent_req *req);
int ifp_groups_group_update_member_list(struct sbus_request *sbus_req,
void *data)
{
struct tevent_req *subreq;
struct ifp_ctx *ctx;
ctx = talloc_get_type(data, struct ifp_ctx);
if (ctx == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n");
return ERR_INTERNAL;
}
subreq = resolv_ghosts_send(sbus_req, ctx->rctx->ev, sbus_req, data);
if (subreq == NULL) {
return ENOMEM;
}
tevent_req_set_callback(subreq, ifp_groups_group_update_member_list_done,
sbus_req);
return EOK;
}
static void ifp_groups_group_update_member_list_done(struct tevent_req *subreq)
{
DBusError *error;
struct sbus_request *sbus_req;
errno_t ret;
sbus_req = tevent_req_callback_data(subreq, struct sbus_request);
ret = resolv_ghosts_recv(subreq);
talloc_zfree(subreq);
if (ret != EOK) {
error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED,
"Unable to resolve ghost members [%d]: %s\n",
ret, sss_strerror(ret));
sbus_request_fail_and_finish(sbus_req, error);
return;
}
iface_ifp_groups_group_UpdateMemberList_finish(sbus_req);
return;
}
void ifp_groups_group_get_name(struct sbus_request *sbus_req,
void *data,
const char **_out)
{
struct ldb_message *msg;
struct sss_domain_info *domain;
errno_t ret;
ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &msg);
if (ret != EOK) {
*_out = NULL;
return;
}
*_out = sss_view_ldb_msg_find_attr_as_string(domain, msg, SYSDB_NAME, NULL);
return;
}
void ifp_groups_group_get_gid_number(struct sbus_request *sbus_req,
void *data,
uint32_t *_out)
{
struct ldb_message *msg;
struct sss_domain_info *domain;
errno_t ret;
ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &msg);
if (ret != EOK) {
*_out = 0;
return;
}
*_out = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_GIDNUM, 0);
return;
}
static errno_t
ifp_groups_group_get_members(TALLOC_CTX *mem_ctx,
struct sbus_request *sbus_req,
void *data,
const char ***_users,
int *_num_users,
const char ***_groups,
int *_num_groups)
{
TALLOC_CTX *tmp_ctx;
struct sss_domain_info *domain;
struct ldb_message *group;
struct ldb_message **members;
size_t num_members;
const char *class;
const char **users;
const char **groups;
int num_users;
int num_groups;
int i;
errno_t ret;
const char *attrs[] = {SYSDB_OBJECTCLASS, SYSDB_UIDNUM,
SYSDB_GIDNUM, NULL};
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
return ENOMEM;
}
ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &group);
if (ret != EOK) {
goto done;
}
ret = sysdb_asq_search(tmp_ctx, domain, group->dn, NULL, SYSDB_MEMBER,
attrs, &num_members, &members);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Unable to perform ASQ search [%d]: %s\n",
ret, sss_strerror(ret));
goto done;
}
if (num_members == 0) {
users = NULL;
groups = NULL;
num_users = 0;
num_groups = 0;
ret = EOK;
goto done;
}
users = talloc_zero_array(tmp_ctx, const char *, num_members + 1);
if (users == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
ret = ENOMEM;
goto done;
}
groups = talloc_zero_array(tmp_ctx, const char *, num_members + 1);
if (groups == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
ret = ENOMEM;
goto done;
}
num_users = 0;
num_groups = 0;
for (i = 0; i < num_members; i++) {
class = ldb_msg_find_attr_as_string(members[i], SYSDB_OBJECTCLASS,
NULL);
if (class == NULL) {
ret = ERR_INTERNAL;
goto done;
}
if (strcmp(class, SYSDB_USER_CLASS) == 0) {
users[num_users] = ifp_users_build_path_from_msg(users, domain,
members[i]);
if (users[num_users] == NULL) {
ret = ENOMEM;
goto done;
}
num_users++;
} else if (strcmp(class, SYSDB_GROUP_CLASS) == 0) {
groups[num_groups] = ifp_groups_build_path_from_msg(groups,
domain, members[i]);
if (groups[num_groups] == NULL) {
ret = ENOMEM;
goto done;
}
num_groups++;
} else {
DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected object class %s\n", class);
ret = ERR_INTERNAL;
goto done;
}
}
ret = EOK;
done:
if (ret == EOK) {
if (_users != NULL) {
*_users = talloc_steal(mem_ctx, users);
}
if (_num_users != NULL) {
*_num_users = num_users;
}
if (_groups != NULL) {
*_groups = talloc_steal(mem_ctx, groups);
}
if (_num_groups != NULL) {
*_num_groups = num_groups;
}
}
talloc_free(tmp_ctx);
return ret;
}
void ifp_groups_group_get_users(struct sbus_request *sbus_req,
void *data,
const char ***_out,
int *_size)
{
errno_t ret;
*_out = NULL;
*_size = 0;
ret = ifp_groups_group_get_members(sbus_req, sbus_req, data, _out, _size,
NULL, NULL);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Unable to acquire groups members\n");
}
}
void ifp_groups_group_get_groups(struct sbus_request *sbus_req,
void *data,
const char ***_out,
int *_size)
{
errno_t ret;
*_out = NULL;
*_size = 0;
ret = ifp_groups_group_get_members(sbus_req, sbus_req, data, NULL, NULL,
_out, _size);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Unable to acquire groups members\n");
}
}