diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
commit | 74aa0bc6779af38018a03fd2cf4419fe85917904 (patch) | |
tree | 9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/responder/ifp/ifp_users.c | |
parent | Initial commit. (diff) | |
download | sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip |
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/responder/ifp/ifp_users.c')
-rw-r--r-- | src/responder/ifp/ifp_users.c | 2040 |
1 files changed, 2040 insertions, 0 deletions
diff --git a/src/responder/ifp/ifp_users.c b/src/responder/ifp/ifp_users.c new file mode 100644 index 0000000..7acd46e --- /dev/null +++ b/src/responder/ifp/ifp_users.c @@ -0,0 +1,2040 @@ +/* + 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 <string.h> + +#include "db/sysdb.h" +#include "util/util.h" +#include "util/strtonum.h" +#include "util/cert.h" +#include "util/child_common.h" +#include "util/crypto/sss_crypto.h" +#include "responder/common/responder.h" +#include "responder/common/cache_req/cache_req.h" +#include "responder/ifp/ifp_users.h" +#include "responder/ifp/ifp_groups.h" +#include "responder/ifp/ifp_cache.h" +#include "responder/ifp/ifp_iface/ifp_iface_async.h" + +char * ifp_users_build_path_from_msg(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + struct ldb_message *msg) +{ + const char *key = NULL; + + switch (domain->type) { + case DOM_TYPE_APPLICATION: + key = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); + break; + case DOM_TYPE_POSIX: + key = ldb_msg_find_attr_as_string(msg, SYSDB_UIDNUM, NULL); + break; + } + + + if (key == NULL) { + return NULL; + } + + return sbus_opath_compose(mem_ctx, IFP_PATH_USERS, domain->name, key); +} + +static errno_t ifp_users_decompose_path(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domains, + const char *path, + struct sss_domain_info **_domain, + char **_key) +{ + char **parts = NULL; + struct sss_domain_info *domain; + errno_t ret; + + ret = sbus_opath_decompose_expected(NULL, path, IFP_PATH_USERS, 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; + } + + *_domain = domain; + *_key = talloc_steal(mem_ctx, parts[1]); + +done: + talloc_free(parts); + return ret; +} + +static int ifp_users_list_copy(struct ifp_list_ctx *list_ctx, + struct sss_domain_info *domain, + struct ldb_result *result) +{ + size_t copy_count, i; + errno_t ret; + + ret = ifp_list_ctx_remaining_capacity(list_ctx, result->count, ©_count); + if (ret != EOK) { + goto done; + } + + for (i = 0; i < copy_count; i++) { + list_ctx->paths[list_ctx->path_count + i] = \ + ifp_users_build_path_from_msg(list_ctx->paths, + domain, + result->msgs[i]); + if (list_ctx->paths[list_ctx->path_count + i] == NULL) { + ret = ENOMEM; + goto done; + } + } + + list_ctx->path_count += copy_count; + ret = EOK; + +done: + return ret; +} + +struct ifp_users_find_by_name_state { + const char *path; +}; + +static void ifp_users_find_by_name_done(struct tevent_req *subreq); + +struct tevent_req * +ifp_users_find_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char *name) +{ + struct ifp_users_find_by_name_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct ifp_users_find_by_name_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + subreq = cache_req_user_by_name_send(state, ctx->rctx->ev, ctx->rctx, + ctx->rctx->ncache, 0, + CACHE_REQ_ANY_DOM, NULL, + name); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ifp_users_find_by_name_done, req); + + ret = EAGAIN; + +done: + if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static void ifp_users_find_by_name_done(struct tevent_req *subreq) +{ + struct ifp_users_find_by_name_state *state; + struct cache_req_result *result; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ifp_users_find_by_name_state); + + ret = cache_req_user_by_name_recv(state, subreq, &result); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Unable to find user [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + state->path = ifp_users_build_path_from_msg(state, result->domain, + result->msgs[0]); + if (state->path == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + tevent_req_done(req); + return; +} + +errno_t +ifp_users_find_by_name_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char **_path) +{ + struct ifp_users_find_by_name_state *state; + state = tevent_req_data(req, struct ifp_users_find_by_name_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_path = talloc_steal(mem_ctx, state->path); + + return EOK; +} + +struct ifp_users_find_by_id_state { + const char *path; +}; + +static void ifp_users_find_by_id_done(struct tevent_req *subreq); + +struct tevent_req * +ifp_users_find_by_id_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + uint32_t id) +{ + struct ifp_users_find_by_id_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct ifp_users_find_by_id_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + subreq = cache_req_user_by_id_send(state, ctx->rctx->ev, ctx->rctx, + ctx->rctx->ncache, 0, NULL, id); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ifp_users_find_by_id_done, req); + + ret = EAGAIN; + +done: + if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static void ifp_users_find_by_id_done(struct tevent_req *subreq) +{ + struct ifp_users_find_by_id_state *state; + struct cache_req_result *result; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ifp_users_find_by_id_state); + + ret = cache_req_user_by_id_recv(state, subreq, &result); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Unable to find user [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + state->path = ifp_users_build_path_from_msg(state, result->domain, + result->msgs[0]); + if (state->path == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + tevent_req_done(req); + return; +} + +errno_t +ifp_users_find_by_id_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char **_path) +{ + struct ifp_users_find_by_id_state *state; + state = tevent_req_data(req, struct ifp_users_find_by_id_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_path = talloc_steal(mem_ctx, state->path); + + return EOK; +} + +struct ifp_users_find_by_cert_state { + const char *path; +}; + +static void ifp_users_find_by_cert_done(struct tevent_req *subreq); + +struct tevent_req * +ifp_users_find_by_cert_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char *pem_cert) +{ + struct ifp_users_find_by_cert_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + char *derb64; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct ifp_users_find_by_cert_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + ret = sss_cert_pem_to_derb64(state, pem_cert, &derb64); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sss_cert_pem_to_derb64 failed.\n"); + goto done; + } + + subreq = cache_req_user_by_cert_send(state, ctx->rctx->ev, ctx->rctx, + ctx->rctx->ncache, 0, CACHE_REQ_ANY_DOM, + NULL, derb64); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ifp_users_find_by_cert_done, req); + + ret = EAGAIN; + +done: + if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static void ifp_users_find_by_cert_done(struct tevent_req *subreq) +{ + struct ifp_users_find_by_cert_state *state; + struct cache_req_result *result; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ifp_users_find_by_cert_state); + + ret = cache_req_user_by_cert_recv(state, subreq, &result); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Unable to find user [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (result->count > 1) { + DEBUG(SSSDBG_CRIT_FAILURE, "More than one user found. " + "Use ListByCertificate to get all.\n"); + tevent_req_error(req, EINVAL); + return; + } + + state->path = ifp_users_build_path_from_msg(state, result->domain, + result->msgs[0]); + if (state->path == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + tevent_req_done(req); + return; +} + +errno_t +ifp_users_find_by_cert_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char **_path) +{ + struct ifp_users_find_by_cert_state *state; + state = tevent_req_data(req, struct ifp_users_find_by_cert_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_path = talloc_steal(mem_ctx, state->path); + + return EOK; +} + +struct ifp_users_list_by_cert_state { + struct ifp_ctx *ifp_ctx; + struct ifp_list_ctx *list_ctx; + char *derb64; +}; + +static errno_t ifp_users_list_by_cert_step(struct tevent_req *req); +static void ifp_users_list_by_cert_done(struct tevent_req *subreq); + +struct tevent_req * +ifp_users_list_by_cert_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char *pem_cert, + uint32_t limit) +{ + struct ifp_users_list_by_cert_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct ifp_users_list_by_cert_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + ret = sss_cert_pem_to_derb64(state, pem_cert, &state->derb64); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sss_cert_pem_to_derb64 failed.\n"); + goto done; + } + + state->ifp_ctx = ctx; + state->list_ctx = ifp_list_ctx_new(state, ctx, NULL, state->derb64, limit); + if (state->list_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + ret = ifp_users_list_by_cert_step(req); + +done: + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, ev); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static errno_t +ifp_users_list_by_cert_step(struct tevent_req *req) +{ + struct ifp_users_list_by_cert_state *state; + struct tevent_req *subreq; + + state = tevent_req_data(req, struct ifp_users_list_by_cert_state); + + if (state->list_ctx->dom == NULL) { + return EOK; + } + + subreq = cache_req_user_by_cert_send(state->list_ctx, + state->ifp_ctx->rctx->ev, + state->ifp_ctx->rctx, + state->ifp_ctx->rctx->ncache, + 0, + CACHE_REQ_ANY_DOM, + state->list_ctx->dom->name, + state->list_ctx->filter); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + return ENOMEM; + } + + tevent_req_set_callback(subreq, ifp_users_list_by_cert_done, req); + + state->list_ctx->dom = get_next_domain(state->list_ctx->dom, + SSS_GND_DESCEND); + + return EAGAIN; +} + +static void ifp_users_list_by_cert_done(struct tevent_req *subreq) +{ + struct ifp_users_list_by_cert_state *state; + struct cache_req_result *result; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ifp_users_list_by_cert_state); + + ret = cache_req_user_by_cert_recv(state, subreq, &result); + talloc_zfree(subreq); + if (ret == EOK) { + ret = ifp_users_list_copy(state->list_ctx, result->domain, + result->ldb_result); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy domain result\n"); + tevent_req_error(req, ret); + return; + } + } else if (ret != ENOENT) { + DEBUG(SSSDBG_MINOR_FAILURE, "Unable to list groups [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + ret = ifp_users_list_by_cert_step(req); + if (ret == EOK) { + tevent_req_done(req); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + } +} + +errno_t +ifp_users_list_by_cert_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char ***_paths) +{ + struct ifp_users_list_by_cert_state *state; + state = tevent_req_data(req, struct ifp_users_list_by_cert_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_paths = talloc_steal(mem_ctx, state->list_ctx->paths); + + return EOK; +} + +struct ifp_users_find_by_name_and_cert_state { + struct ifp_ctx *ifp_ctx; + struct ifp_list_ctx *list_ctx; + const char *name; + const char *pem_cert; + char *derb64; + + const char *user_opath; +}; + +static void ifp_users_find_by_name_and_cert_name_done(struct tevent_req *subreq); +static errno_t ifp_users_find_by_name_and_cert_step(struct tevent_req *req); +static void ifp_users_find_by_name_and_cert_done(struct tevent_req *subreq); + +struct tevent_req * +ifp_users_find_by_name_and_cert_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char *name, + const char *pem_cert) +{ + struct ifp_users_find_by_name_and_cert_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct ifp_users_find_by_name_and_cert_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + state->ifp_ctx = ctx; + + if (!SBUS_REQ_STRING_IS_EMPTY(name)) { + state->name = talloc_strdup(state, name); + if (state->name == NULL) { + ret = ENOMEM; + goto done; + } + } + + if (!SBUS_REQ_STRING_IS_EMPTY(pem_cert)) { + state->pem_cert = talloc_strdup(state, pem_cert); + if (state->pem_cert == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sss_cert_pem_to_derb64(state, pem_cert, &state->derb64); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sss_cert_pem_to_derb64 failed.\n"); + goto done; + } + + /* FIXME: if unlimted searches with limit=0 will work please replace + * 100 with 0. */ + state->list_ctx = ifp_list_ctx_new(state, ctx, NULL, state->derb64, 100); + if (state->list_ctx == NULL) { + ret = ENOMEM; + goto done; + } + } + + if (state->name == NULL && state->pem_cert == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Empty arguments!\n"); + ret = EINVAL; + goto done; + } + + if (state->name != NULL) { + subreq = cache_req_user_by_name_send(state, ctx->rctx->ev, ctx->rctx, + ctx->rctx->ncache, 0, + CACHE_REQ_ANY_DOM, + NULL, state->name); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ifp_users_find_by_name_and_cert_name_done, req); + } else { + ret = ifp_users_find_by_name_and_cert_step(req); + goto done; + } + + ret = EAGAIN; + +done: + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, ev); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static void ifp_users_find_by_name_and_cert_name_done(struct tevent_req *subreq) +{ + struct ifp_users_find_by_name_and_cert_state *state; + struct cache_req_result *result; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ifp_users_find_by_name_and_cert_state); + + ret = cache_req_user_by_name_recv(state, subreq, &result); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + state->user_opath = ifp_users_build_path_from_msg(state, + result->domain, + result->msgs[0]); + if (state->user_opath == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + ret = ifp_users_find_by_name_and_cert_step(req); + if (ret == EOK) { + tevent_req_done(req); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + } + + return; +} + +static errno_t +ifp_users_find_by_name_and_cert_step(struct tevent_req *req) +{ + struct ifp_users_find_by_name_and_cert_state *state; + struct tevent_req *subreq; + + state = tevent_req_data(req, struct ifp_users_find_by_name_and_cert_state); + + if (state->list_ctx == NULL) { + if (state->name == NULL) { + return EINVAL; + } + + /* Nothing to search for. */ + return EOK; + } + + /* No more domains to try. */ + if (state->list_ctx->dom == NULL) { + return EOK; + } + + subreq = cache_req_user_by_cert_send(state->list_ctx, + state->ifp_ctx->rctx->ev, + state->ifp_ctx->rctx, + state->ifp_ctx->rctx->ncache, + 0, + CACHE_REQ_ANY_DOM, + state->list_ctx->dom->name, + state->list_ctx->filter); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + return ENOMEM; + } + + tevent_req_set_callback(subreq, ifp_users_find_by_name_and_cert_done, req); + + state->list_ctx->dom = get_next_domain(state->list_ctx->dom, + SSS_GND_DESCEND); + + return EAGAIN; +} + +static void ifp_users_find_by_name_and_cert_done(struct tevent_req *subreq) +{ + struct ifp_users_find_by_name_and_cert_state *state; + struct cache_req_result *result; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ifp_users_find_by_name_and_cert_state); + + ret = cache_req_user_by_cert_recv(state, subreq, &result); + talloc_zfree(subreq); + if (ret == EOK) { + ret = ifp_users_list_copy(state->list_ctx, result->domain, + result->ldb_result); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy domain result\n"); + tevent_req_error(req, ret); + return; + } + } else if (ret != ENOENT) { + DEBUG(SSSDBG_MINOR_FAILURE, "Unable to list groups [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + ret = ifp_users_find_by_name_and_cert_step(req); + if (ret == EOK) { + tevent_req_done(req); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + } + + return; +} + +errno_t +ifp_users_find_by_name_and_cert_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char **_path) +{ + struct ifp_users_find_by_name_and_cert_state *state; + size_t c; + + state = tevent_req_data(req, struct ifp_users_find_by_name_and_cert_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + /* If no name was given check if there is only one user mapped to the + * certificate and return its object path. Either no or more than one + * mapped users are errors in this case. + * The case where a given name could not be found is already handled in + * ifp_users_find_by_name_and_cert_name_done(). */ + if (state->user_opath == NULL) { + if (state->list_ctx == NULL || state->list_ctx->path_count == 0) { + return ENOENT; + } else if (state->list_ctx->path_count == 1) { + *_path = talloc_steal(mem_ctx, state->list_ctx->paths[0]); + return EOK; + } else { + return EEXIST; + } + } + + /* If there was no certificate given just return the object path of the + * user found by name. If a certificate was given an no mapped user was + * found return an error. */ + if (state->pem_cert == NULL) { + *_path = talloc_steal(mem_ctx, state->user_opath); + return EOK; + } else { + for (c = 0; c < state->list_ctx->path_count; c++) { + if (strcmp(state->user_opath, state->list_ctx->paths[c]) == 0) { + *_path = talloc_steal(mem_ctx, state->user_opath); + return EOK; + } + } + } + + return ENOENT; +} + +struct ifp_users_list_by_attr_state { + struct ifp_ctx *ifp_ctx; + struct ifp_list_ctx *list_ctx; +}; + +static errno_t ifp_users_list_by_attr_step(struct tevent_req *req); +static void ifp_users_list_by_attr_done(struct tevent_req *subreq); + +struct tevent_req * +ifp_users_list_by_attr_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char *attr, + const char *filter, + uint32_t limit) +{ + struct ifp_users_list_by_attr_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct ifp_users_list_by_attr_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + state->ifp_ctx = ctx; + state->list_ctx = ifp_list_ctx_new(state, ctx, attr, filter, limit); + if (state->list_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + ret = ifp_users_list_by_attr_step(req); + +done: + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, ev); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static errno_t +ifp_users_list_by_attr_step(struct tevent_req *req) +{ + struct ifp_users_list_by_attr_state *state; + struct tevent_req *subreq; + + state = tevent_req_data(req, struct ifp_users_list_by_attr_state); + + if (state->list_ctx->dom == NULL) { + return EOK; + } + + subreq = cache_req_user_by_filter_send(state->list_ctx, + state->ifp_ctx->rctx->ev, + state->ifp_ctx->rctx, + CACHE_REQ_ANY_DOM, + state->list_ctx->dom->name, + state->list_ctx->attr, + state->list_ctx->filter); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + return ENOMEM; + } + + tevent_req_set_callback(subreq, ifp_users_list_by_attr_done, req); + + state->list_ctx->dom = get_next_domain(state->list_ctx->dom, + SSS_GND_DESCEND); + + return EAGAIN; +} + +static void ifp_users_list_by_attr_done(struct tevent_req *subreq) +{ + struct ifp_users_list_by_attr_state *state; + struct cache_req_result *result; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ifp_users_list_by_attr_state); + + ret = cache_req_user_by_name_recv(state, subreq, &result); + talloc_zfree(subreq); + if (ret == EOK) { + ret = ifp_users_list_copy(state->list_ctx, result->domain, + result->ldb_result); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy domain result\n"); + tevent_req_error(req, ret); + return; + } + } else if (ret != ENOENT) { + DEBUG(SSSDBG_MINOR_FAILURE, "Unable to list users [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + ret = ifp_users_list_by_attr_step(req); + if (ret == EOK) { + tevent_req_done(req); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + } +} + +errno_t +ifp_users_list_by_attr_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char ***_paths) +{ + struct ifp_users_list_by_attr_state *state; + state = tevent_req_data(req, struct ifp_users_list_by_attr_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_paths = talloc_steal(mem_ctx, state->list_ctx->paths); + + return EOK; +} + +struct ifp_users_list_by_domain_and_name_state { + struct ifp_list_ctx *list_ctx; +}; + +static void ifp_users_list_by_domain_and_name_done(struct tevent_req *subreq); + +struct tevent_req * +ifp_users_list_by_domain_and_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char *domain, + const char *filter, + uint32_t limit) +{ + struct ifp_users_list_by_domain_and_name_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct ifp_users_list_by_domain_and_name_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + state->list_ctx = ifp_list_ctx_new(state, ctx, NULL, filter, limit); + if (state->list_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + subreq = cache_req_user_by_filter_send(state->list_ctx, ctx->rctx->ev, + ctx->rctx, CACHE_REQ_ANY_DOM, + domain, NULL, filter); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ifp_users_list_by_domain_and_name_done, req); + + ret = EAGAIN; + +done: + if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static void ifp_users_list_by_domain_and_name_done(struct tevent_req *subreq) +{ + struct ifp_users_list_by_domain_and_name_state *state; + struct cache_req_result *result; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ifp_users_list_by_domain_and_name_state); + + ret = cache_req_user_by_name_recv(state, subreq, &result); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + ret = ifp_users_list_copy(state->list_ctx, result->domain, + result->ldb_result); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy domain result\n"); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; +} + +errno_t +ifp_users_list_by_domain_and_name_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char ***_paths) +{ + struct ifp_users_list_by_domain_and_name_state *state; + state = tevent_req_data(req, struct ifp_users_list_by_domain_and_name_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_paths = talloc_steal(mem_ctx, state->list_ctx->paths); + + return EOK; +} + +struct ifp_users_find_by_valid_cert_state { + struct ifp_ctx *ifp_ctx; + struct tevent_context *ev; + const char *logfile; + int timeout; + char *ca_db; + char *verify_opts; + char *derb64; + const char **extra_args; + const char *path; + + struct sss_child_ctx_old *child_ctx; + struct child_io_fds *io; +}; + +static errno_t p11_child_exec(struct tevent_req *req); +static void +ifp_users_find_by_valid_cert_step(int child_status, + struct tevent_signal *sige, + void *pvt); +static void ifp_users_find_by_valid_cert_done(struct tevent_req *subreq); + +struct tevent_req * +ifp_users_find_by_valid_cert_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char *pem_cert) +{ + struct tevent_req *req; + struct ifp_users_find_by_valid_cert_state *state; + size_t arg_c = 0; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct ifp_users_find_by_valid_cert_state); + if (req == NULL) { + return NULL; + } + + state->ifp_ctx = ctx; + + ret = confdb_get_string(ctx->rctx->cdb, state, + CONFDB_IFP_CONF_ENTRY, CONFDB_SSH_CA_DB, + CONFDB_DEFAULT_SSH_CA_DB, &state->ca_db); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Error reading CA DB from confdb (%d) [%s]\n", + ret, strerror(ret)); + goto done; + } + + ret = confdb_get_int(ctx->rctx->cdb, CONFDB_IFP_CONF_ENTRY, + CONFDB_PAM_P11_CHILD_TIMEOUT, -1, + &state->timeout); + if (ret != EOK || state->timeout == -1) { + /* check pam configuration as well or use default */ + ret = confdb_get_int(ctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY, + CONFDB_PAM_P11_CHILD_TIMEOUT, + P11_CHILD_TIMEOUT_DEFAULT, + &state->timeout); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to read p11_child_timeout from confdb: [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + } + + ret = confdb_get_string(ctx->rctx->cdb, state, CONFDB_MONITOR_CONF_ENTRY, + CONFDB_MONITOR_CERT_VERIFICATION, NULL, + &state->verify_opts); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to read '"CONFDB_MONITOR_CERT_VERIFICATION"' from confdb: [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + state->ev = ev; + state->logfile = P11_CHILD_LOG_FILE; + state->io = talloc(state, struct child_io_fds); + if (state->io == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc failed.\n"); + ret = ENOMEM; + goto done; + } + state->io->write_to_child_fd = -1; + state->io->read_from_child_fd = -1; + talloc_set_destructor((void *) state->io, child_io_destructor); + + ret = sss_cert_pem_to_derb64(state, pem_cert, &state->derb64); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sss_cert_pem_to_derb64 failed.\n"); + goto done; + } + + state->extra_args = talloc_zero_array(state, const char *, 8); + if (state->extra_args == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n"); + ret = ENOMEM; + goto done; + } + state->extra_args[arg_c++] = state->derb64; + state->extra_args[arg_c++] = "--certificate"; + state->extra_args[arg_c++] = state->ca_db; + state->extra_args[arg_c++] = "--ca_db"; + if (state->verify_opts != NULL) { + state->extra_args[arg_c++] = state->verify_opts; + state->extra_args[arg_c++] = "--verify"; + } + state->extra_args[arg_c++] = "--verification"; + + ret = p11_child_exec(req); + +done: + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, ev); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static errno_t p11_child_exec(struct tevent_req *req) +{ + struct ifp_users_find_by_valid_cert_state *state; + int pipefd_from_child[2] = PIPE_INIT; + int pipefd_to_child[2] = PIPE_INIT; + pid_t child_pid; + struct timeval tv; + bool endtime; + int ret; + + state = tevent_req_data(req, struct ifp_users_find_by_valid_cert_state); + + ret = pipe(pipefd_from_child); + if (ret == -1) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "pipe failed [%d][%s].\n", ret, strerror(ret)); + goto done; + } + ret = pipe(pipefd_to_child); + if (ret == -1) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "pipe failed [%d][%s].\n", ret, strerror(ret)); + goto done; + } + + child_pid = fork(); + if (child_pid == 0) { /* child */ + exec_child_ex(state, pipefd_to_child, pipefd_from_child, + P11_CHILD_PATH, state->logfile, state->extra_args, + false, STDIN_FILENO, STDOUT_FILENO); + /* We should never get here */ + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Could not exec p11 child\n"); + return ret; + } else if (child_pid > 0) { /* parent */ + state->io->read_from_child_fd = pipefd_from_child[0]; + PIPE_FD_CLOSE(pipefd_from_child[1]); + sss_fd_nonblocking(state->io->read_from_child_fd); + + state->io->write_to_child_fd = pipefd_to_child[1]; + PIPE_FD_CLOSE(pipefd_to_child[0]); + sss_fd_nonblocking(state->io->write_to_child_fd); + + /* Set up SIGCHLD handler */ + ret = child_handler_setup(state->ev, child_pid, + ifp_users_find_by_valid_cert_step, + req, &state->child_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Could not set up child handlers [%d]: %s\n", + ret, sss_strerror(ret)); + ret = ERR_P11_CHILD; + goto done; + } + + /* Set up timeout handler */ + tv = tevent_timeval_current_ofs(state->timeout, 0); + endtime = tevent_req_set_endtime(req, state->ev, tv); + if (endtime == false) { + ret = ERR_P11_CHILD; + goto done; + } + /* Now either wait for the timeout to fire or the child to finish */ + } else { /* error */ + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, "fork failed [%d][%s].\n", + ret, sss_strerror(ret)); + goto done; + } + + return EAGAIN; + +done: + if (ret != EOK) { + PIPE_CLOSE(pipefd_from_child); + PIPE_CLOSE(pipefd_to_child); + } + + return ret; +} + +static void +ifp_users_find_by_valid_cert_step(int child_status, + struct tevent_signal *sige, + void *pvt) +{ + struct tevent_req *subreq; + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct ifp_users_find_by_valid_cert_state *state; + errno_t ret; + + state = tevent_req_data(req, struct ifp_users_find_by_valid_cert_state); + + PIPE_FD_CLOSE(state->io->read_from_child_fd); + PIPE_FD_CLOSE(state->io->write_to_child_fd); + + if (WIFEXITED(child_status)) { + if (WEXITSTATUS(child_status) == CA_DB_NOT_FOUND_EXIT_CODE) { + DEBUG(SSSDBG_OP_FAILURE, + P11_CHILD_PATH " failed [%d]: [%s].\n", + ERR_CA_DB_NOT_FOUND, sss_strerror(ERR_CA_DB_NOT_FOUND)); + tevent_req_error(req, ERR_CA_DB_NOT_FOUND); + return; + } else if (WEXITSTATUS(child_status) != 0) { + DEBUG(SSSDBG_OP_FAILURE, + P11_CHILD_PATH " failed with status [%d]. Check p11_child" + " logs for more information.\n", + WEXITSTATUS(child_status)); + tevent_req_error(req, ERR_INVALID_CERT); + return; + } + } else if (WIFSIGNALED(child_status)) { + DEBUG(SSSDBG_OP_FAILURE, + P11_CHILD_PATH " was terminated by signal [%d]. Check p11_child" + " logs for more information.\n", + WTERMSIG(child_status)); + tevent_req_error(req, ECHILD); + return; + } + + DEBUG(SSSDBG_TRACE_LIBS, "Certificate [%s] is valid.\n", + state->extra_args[0]); + + subreq = cache_req_user_by_cert_send(state, state->ifp_ctx->rctx->ev, + state->ifp_ctx->rctx, + state->ifp_ctx->rctx->ncache, 0, + CACHE_REQ_ANY_DOM, NULL, + state->derb64); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ifp_users_find_by_valid_cert_done, req); + + ret = EAGAIN; + +done: + if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, state->ifp_ctx->rctx->ev); + } + + return; +} + +static void ifp_users_find_by_valid_cert_done(struct tevent_req *subreq) +{ + struct ifp_users_find_by_valid_cert_state *state; + struct cache_req_result *result; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ifp_users_find_by_valid_cert_state); + + ret = cache_req_user_by_cert_recv(state, subreq, &result); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Unable to find user [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (result->count > 1) { + DEBUG(SSSDBG_CRIT_FAILURE, "More than one user found. " + "Use ListByCertificate to get all.\n"); + tevent_req_error(req, EINVAL); + return; + } + + state->path = ifp_users_build_path_from_msg(state, result->domain, + result->msgs[0]); + if (state->path == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + tevent_req_done(req); + return; +} + +errno_t +ifp_users_find_by_valid_cert_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char **_path) +{ + struct ifp_users_find_by_valid_cert_state *state; + + state = tevent_req_data(req, struct ifp_users_find_by_valid_cert_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_path = talloc_steal(mem_ctx, state->path); + + return EOK; +} + +static errno_t +ifp_users_get_from_cache(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *key, + struct ldb_message **_user) +{ + struct ldb_result *user_res = NULL; + errno_t ret; + uid_t uid; + char *endptr; + + switch (domain->type) { + case DOM_TYPE_POSIX: + uid = strtouint32(key, &endptr, 10); + if ((errno != 0) || *endptr || (key == endptr)) { + ret = errno ? errno : EINVAL; + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid UID value\n"); + goto done; + } + + ret = sysdb_getpwuid_with_views(mem_ctx, domain, uid, &user_res); + if (ret == EOK && user_res->count == 0) { + *_user = NULL; + ret = ENOENT; + goto done; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user %u@%s [%d]: %s\n", + uid, domain->name, ret, sss_strerror(ret)); + goto done; + } + break; + case DOM_TYPE_APPLICATION: + ret = sysdb_getpwnam_with_views(mem_ctx, domain, key, &user_res); + if (ret == EOK && user_res->count == 0) { + *_user = NULL; + ret = ENOENT; + goto done; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user %s@%s [%d]: %s\n", + key, domain->name, ret, sss_strerror(ret)); + goto done; + } + break; + } + + if (user_res->count > 1) { + DEBUG(SSSDBG_CRIT_FAILURE, "More users matched by the single key\n"); + ret = EIO; + goto done; + } + + *_user = talloc_steal(mem_ctx, user_res->msgs[0]); + + ret = EOK; + +done: + talloc_free(user_res); + + return ret; +} + +static errno_t +ifp_users_user_get(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ifp_ctx, + struct sss_domain_info **_domain, + struct ldb_message **_user) +{ + struct sss_domain_info *domain; + char *key; + errno_t ret; + + ret = ifp_users_decompose_path(NULL, + ifp_ctx->rctx->domains, sbus_req->path, + &domain, &key); + 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 (_user != NULL) { + ret = ifp_users_get_from_cache(mem_ctx, domain, key, _user); + } + + talloc_free(key); + + if (ret == EOK || ret == ENOENT) { + if (_domain != NULL) { + *_domain = domain; + } + } else if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve user from cache\n"); + } + + return ret; +} + +static errno_t +ifp_users_get_as_string(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ifp_ctx, + const char *attr, + const char **_out, + struct sss_domain_info **_domain) +{ + struct ldb_message *msg; + struct sss_domain_info *domain; + const char *out; + errno_t ret; + + ret = ifp_users_user_get(NULL, sbus_req, ifp_ctx, &domain, &msg); + if (ret != EOK) { + return ret; + } + + out = sss_view_ldb_msg_find_attr_as_string(domain, msg, attr, NULL); + if (out == NULL) { + talloc_free(msg); + return ENOENT; + } + + *_out = talloc_steal(mem_ctx, out); + talloc_free(msg); + + if (_domain != NULL) { + *_domain = domain; + } + + return EOK; +} + +static errno_t +ifp_users_get_name(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ifp_ctx, + const char *attr, + const char **_out) +{ + struct sss_domain_info *domain; + const char *in_name; + const char *out; + errno_t ret; + + ret = ifp_users_get_as_string(NULL, sbus_req, ifp_ctx, attr, + &in_name, &domain); + if (ret != EOK) { + return ret; + } + + out = ifp_format_name_attr(mem_ctx, ifp_ctx, in_name, domain); + talloc_free(discard_const(in_name)); + if (out == NULL) { + return ENOMEM; + } + + *_out = out; + + return EOK; +} + +static errno_t +ifp_users_get_as_uint32(struct sbus_request *sbus_req, + struct ifp_ctx *ifp_ctx, + const char *attr, + uint32_t *_out) +{ + struct ldb_message *msg; + struct sss_domain_info *domain; + errno_t ret; + + ret = ifp_users_user_get(NULL, sbus_req, ifp_ctx, &domain, &msg); + if (ret != EOK) { + return ret; + } + + *_out = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, attr, 0); + talloc_free(msg); + + return EOK; +} + +struct ifp_users_user_update_groups_list_state { + int dummy; +}; + +static void ifp_users_user_update_groups_list_done(struct tevent_req *subreq); + +struct tevent_req * +ifp_users_user_update_groups_list_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx) +{ + struct ifp_users_user_update_groups_list_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + struct sss_domain_info *domain; + struct ldb_message *user; + const char *username; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct ifp_users_user_update_groups_list_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + ret = ifp_users_user_get(state, sbus_req, ctx, &domain, &user); + if (ret != EOK) { + goto done; + } + + username = ldb_msg_find_attr_as_string(user, SYSDB_NAME, NULL); + if (username == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "User name is empty!\n"); + ret = ERR_INTERNAL; + goto done; + } + + subreq = cache_req_initgr_by_name_send(state, ctx->rctx->ev, ctx->rctx, + ctx->rctx->ncache, 0, + CACHE_REQ_ANY_DOM, domain->name, + username); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ifp_users_user_update_groups_list_done, req); + + ret = EAGAIN; + +done: + if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static void ifp_users_user_update_groups_list_done(struct tevent_req *subreq) +{ + struct ifp_users_user_update_groups_list_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ifp_users_user_update_groups_list_state); + + ret = cache_req_initgr_by_name_recv(state, subreq, NULL); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; +} + +errno_t +ifp_users_user_update_groups_list_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +errno_t +ifp_users_user_get_name(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char **_out) +{ + return ifp_users_get_name(mem_ctx, sbus_req, ctx, SYSDB_NAME, _out); +} + +errno_t +ifp_users_user_get_uid_number(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + uint32_t *_out) +{ + return ifp_users_get_as_uint32(sbus_req, ctx, SYSDB_UIDNUM, _out); +} + +errno_t +ifp_users_user_get_gid_number(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + uint32_t *_out) +{ + return ifp_users_get_as_uint32(sbus_req, ctx, SYSDB_GIDNUM, _out); +} + +errno_t +ifp_users_user_get_gecos(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char **_out) +{ + return ifp_users_get_as_string(mem_ctx, sbus_req, ctx, SYSDB_GECOS, _out, NULL); +} + +errno_t +ifp_users_user_get_home_directory(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char **_out) +{ + return ifp_users_get_as_string(mem_ctx, sbus_req, ctx, SYSDB_HOMEDIR, _out, NULL); +} + +errno_t +ifp_users_user_get_login_shell(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char **_out) +{ + return ifp_users_get_as_string(mem_ctx, sbus_req, ctx, SYSDB_SHELL, _out, NULL); +} + +errno_t +ifp_users_user_get_unique_id(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char **_out) +{ + return ifp_users_get_as_string(mem_ctx, sbus_req, ctx, SYSDB_UUID, _out, NULL); +} + +errno_t +ifp_users_user_get_groups(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ifp_ctx, + const char ***_out) +{ + TALLOC_CTX *tmp_ctx; + struct sss_domain_info *domain; + const char *username; + struct ldb_message *user; + struct ldb_result *res; + const char **out; + int num_groups; + gid_t gid; + errno_t ret; + int i; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); + return ENOMEM; + } + + ret = ifp_users_user_get(tmp_ctx, sbus_req, ifp_ctx, &domain, &user); + if (ret != EOK) { + return ret; + } + + username = ldb_msg_find_attr_as_string(user, SYSDB_NAME, NULL); + if (username == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "User name is empty!\n"); + return ERR_INTERNAL; + } + + /* Run initgroups. */ + ret = sysdb_initgroups_with_views(tmp_ctx, domain, username, &res); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get groups for %s@%s [%d]: %s\n", + username, domain->name, ret, sss_strerror(ret)); + goto done; + } + + if (res->count == 0) { + *_out = NULL; + ret = EOK; + goto done; + } + + out = talloc_zero_array(tmp_ctx, const char *, res->count + 1); + if (out == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n"); + ret = ENOMEM; + goto done; + } + + num_groups = 0; + for (i = 0; i < res->count; i++) { + gid = sss_view_ldb_msg_find_attr_as_uint64(domain, res->msgs[i], + SYSDB_GIDNUM, 0); + if (gid == 0 && domain->type == DOM_TYPE_POSIX) { + continue; + } + + out[num_groups] = ifp_groups_build_path_from_msg(out, + domain, + res->msgs[i]); + if (out[num_groups] == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "ifp_groups_build_path() failed\n"); + ret = ENOMEM; + goto done; + } + + num_groups++; + } + + *_out = talloc_steal(mem_ctx, out); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +errno_t +ifp_users_user_get_domainname(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ifp_ctx, + const char **_out) +{ + struct sss_domain_info *domain; + errno_t ret; + + ret = ifp_users_user_get(mem_ctx, sbus_req, ifp_ctx, &domain, NULL); + if (ret != EOK) { + return ret; + } + + *_out = domain->name; + + return EOK; +} + +errno_t +ifp_users_user_get_domain(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char **_out) +{ + const char *name; + const char *out; + errno_t ret; + + ret = ifp_users_user_get_domainname(NULL, sbus_req, ctx, &name); + if (ret != EOK) { + return ret; + } + + out = sbus_opath_compose(mem_ctx, IFP_PATH_DOMAINS, name); + if (out == NULL) { + return ENOMEM; + } + + *_out = out; + + return EOK; +} + +errno_t +ifp_users_user_get_extra_attributes(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ifp_ctx, + hash_table_t **_out) +{ + TALLOC_CTX *tmp_ctx; + struct sss_domain_info *domain; + struct ldb_message *base_user; + const char *name; + struct ldb_message **user; + struct ldb_message_element *el; + struct ldb_dn *basedn; + size_t count; + const char *filter; + const char **extra; + hash_table_t *table; + hash_key_t key; + hash_value_t value; + const char **values; + errno_t ret; + int hret; + int i; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); + return ENOMEM; + } + + extra = ifp_get_user_extra_attributes(tmp_ctx, ifp_ctx); + if (extra == NULL || extra[0] == NULL) { + DEBUG(SSSDBG_TRACE_ALL, "No extra attributes to return\n"); + *_out = NULL; + ret = EOK; + goto done; + } + + ret = ifp_users_user_get(tmp_ctx, sbus_req, ifp_ctx, &domain, &base_user); + if (ret != EOK) { + goto done; + } + + basedn = sysdb_user_base_dn(tmp_ctx, domain); + if (basedn == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_user_base_dn() failed\n"); + ret = ENOMEM; + goto done; + } + + name = ldb_msg_find_attr_as_string(base_user, SYSDB_NAME, NULL); + if (name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "A user with no name\n"); + ret = ERR_INTERNAL; + goto done; + } + + filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(%s=%s))", + SYSDB_OBJECTCATEGORY, SYSDB_USER_CLASS, + SYSDB_NAME, name); + if (filter == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n"); + ret = ENOMEM; + goto done; + } + + ret = sysdb_search_entry(tmp_ctx, domain->sysdb, basedn, + LDB_SCOPE_SUBTREE, filter, + extra, &count, &user); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + if (count == 0) { + DEBUG(SSSDBG_TRACE_FUNC, "User %s not found!\n", name); + ret = ENOENT; + goto done; + } else if (count > 1) { + DEBUG(SSSDBG_CRIT_FAILURE, "More than one entry found!\n"); + ret = ERR_INTERNAL; + goto done; + } + + ret = sss_hash_create(tmp_ctx, 0, &table); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table!\n"); + goto done; + } + + /* Read each extra attribute. */ + for (i = 0; extra[i] != NULL; i++) { + el = ldb_msg_find_element(user[0], extra[i]); + if (el == NULL) { + DEBUG(SSSDBG_TRACE_ALL, "Attribute %s not found, skipping...\n", + extra[i]); + continue; + } + + values = sss_ldb_el_to_string_list(table, el); + if (values == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sss_ldb_el_to_string_list() failed\n"); + ret = ENOMEM; + goto done; + } + + key.type = HASH_KEY_STRING; + key.str = talloc_strdup(table, extra[i]); + if (key.str == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n"); + ret = ENOMEM; + goto done; + } + + value.type = HASH_VALUE_PTR; + value.ptr = values; + + hret = hash_enter(table, &key, &value); + if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to insert entry " + "into hash table: %d\n", hret); + ret = EIO; + goto done; + } + } + + *_out = talloc_steal(mem_ctx, table); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + + return ret; +} + +errno_t +ifp_cache_list_user(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char ***_out) +{ + return ifp_cache_list(mem_ctx, ctx, IFP_CACHE_USER, _out); +} + +errno_t +ifp_cache_list_by_domain_user(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char *domain, + const char ***_out) +{ + return ifp_cache_list_by_domain(mem_ctx, ctx, domain, IFP_CACHE_USER, _out); +} + +errno_t +ifp_cache_object_store_user(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + bool *_result) +{ + struct sss_domain_info *domain; + struct ldb_message *user; + errno_t ret; + + ret = ifp_users_user_get(NULL, sbus_req, ctx, &domain, &user); + if (ret != EOK) { + return ret; + } + + ret = ifp_cache_object_store(domain, user->dn); + talloc_free(user); + + if (ret == EOK) { + *_result = true; + } + + return ret; +} + +errno_t +ifp_cache_object_remove_user(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + bool *_result) +{ + struct sss_domain_info *domain; + struct ldb_message *user; + errno_t ret; + + ret = ifp_users_user_get(NULL, sbus_req, ctx, &domain, &user); + if (ret != EOK) { + return ret; + } + + ret = ifp_cache_object_remove(domain, user->dn); + talloc_free(user); + + if (ret == EOK) { + *_result = true; + } + + return ret; +} + +struct tevent_req * +ifp_users_list_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char *filter, + uint32_t limit) +{ + return ifp_users_list_by_attr_send(mem_ctx, ev, sbus_req, ctx, NULL, + filter, limit); +} |