From b55cdd3a298b5edd5ddc26beebfa6379843ebe21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= Date: Fri, 11 Dec 2015 15:00:40 +0100 Subject: [PATCH 27/49] IPA SUDO: Implement full refresh Reviewed-by: Sumit Bose (cherry picked from commit a641a13889d617aca6bd998025e9087e822ff7f0) --- Makefile.am | 5 +- src/providers/ipa/ipa_sudo.c | 75 +- src/providers/ipa/ipa_sudo.h | 75 ++ src/providers/ipa/ipa_sudo_async.c | 779 +++++++++++++++++++++ src/providers/ipa/ipa_sudo_conversion.c | 1158 +++++++++++++++++++++++++++++++ src/providers/ipa/ipa_sudo_refresh.c | 195 ++++++ 6 files changed, 2285 insertions(+), 2 deletions(-) create mode 100644 src/providers/ipa/ipa_sudo_async.c create mode 100644 src/providers/ipa/ipa_sudo_conversion.c create mode 100644 src/providers/ipa/ipa_sudo_refresh.c diff --git a/Makefile.am b/Makefile.am index 69905a9112114932e918adff94d0c285c09ed231..1c0b1aada9804b2ef35a09cf1b7bf5e9c65ee4e5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3044,7 +3044,10 @@ endif if BUILD_SUDO libsss_ipa_la_SOURCES += \ - src/providers/ipa/ipa_sudo.c + src/providers/ipa/ipa_sudo.c \ + src/providers/ipa/ipa_sudo_refresh.c \ + src/providers/ipa/ipa_sudo_conversion.c \ + src/providers/ipa/ipa_sudo_async.c endif if BUILD_SSH diff --git a/src/providers/ipa/ipa_sudo.c b/src/providers/ipa/ipa_sudo.c index e1b0c828806104336f3df9724484a4411b7fef30..3e73bd30fa86f394b3ef822d59c7b0e539c92ca2 100644 --- a/src/providers/ipa/ipa_sudo.c +++ b/src/providers/ipa/ipa_sudo.c @@ -147,6 +147,13 @@ ipa_sudo_init_ipa_schema(struct be_ctx *be_ctx, return ret; } + ret = ipa_sudo_ptask_setup(be_ctx, sudo_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup periodic tasks " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + *ops = &ipa_sudo_ops; *pvt_data = sudo_ctx; @@ -200,7 +207,73 @@ int ipa_sudo_init(struct be_ctx *be_ctx, } static void +ipa_sudo_reply(struct tevent_req *req) +{ + struct be_sudo_req *sudo_req; + struct be_req *be_req; + int dp_error; + int ret; + + be_req = tevent_req_callback_data(req, struct be_req); + sudo_req = talloc_get_type(be_req_get_data(be_req), struct be_sudo_req); + + switch (sudo_req->type) { + case BE_REQ_SUDO_FULL: + ret = ipa_sudo_full_refresh_recv(req, &dp_error); + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request type: %d\n", + sudo_req->type); + dp_error = DP_ERR_FATAL; + ret = ERR_INTERNAL; + break; + } + + talloc_zfree(req); + sdap_handler_done(be_req, dp_error, ret, sss_strerror(ret)); +} + +static void ipa_sudo_handler(struct be_req *be_req) { - sdap_handler_done(be_req, DP_ERR_FATAL, ERR_INTERNAL, "Not implemented yet."); + struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); + struct ipa_sudo_ctx *sudo_ctx; + struct be_sudo_req *sudo_req; + struct tevent_req *req; + int ret; + + if (be_is_offline(be_ctx)) { + sdap_handler_done(be_req, DP_ERR_OFFLINE, EAGAIN, "Offline"); + return; + } + + sudo_ctx = talloc_get_type(be_ctx->bet_info[BET_SUDO].pvt_bet_data, + struct ipa_sudo_ctx); + + sudo_req = talloc_get_type(be_req_get_data(be_req), struct be_sudo_req); + + switch (sudo_req->type) { + case BE_REQ_SUDO_FULL: + req = ipa_sudo_full_refresh_send(be_req, be_ctx->ev, sudo_ctx); + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request type: %d\n", + sudo_req->type); + ret = EINVAL; + goto fail; + } + + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send request: %d\n", + sudo_req->type); + ret = ENOMEM; + goto fail; + } + + tevent_req_set_callback(req, ipa_sudo_reply, be_req); + + return; + +fail: + sdap_handler_done(be_req, DP_ERR_FATAL, ret, NULL); } diff --git a/src/providers/ipa/ipa_sudo.h b/src/providers/ipa/ipa_sudo.h index 21251ed3dabfaebdc324c8d06ba8f1a0b82951b1..1ef50a7f352182bdc6607b2fd8ee3d72ccab391d 100644 --- a/src/providers/ipa/ipa_sudo.h +++ b/src/providers/ipa/ipa_sudo.h @@ -28,6 +28,9 @@ struct ipa_sudo_ctx { struct ipa_options *ipa_opts; struct sdap_options *sdap_opts; + bool full_refresh_done; + bool full_refresh_in_progress; + /* sudo */ struct sdap_attr_map *sudocmdgroup_map; struct sdap_attr_map *sudorule_map; @@ -35,4 +38,76 @@ struct ipa_sudo_ctx { struct sdap_search_base **sudo_sb; }; +errno_t +ipa_sudo_ptask_setup(struct be_ctx *be_ctx, struct ipa_sudo_ctx *sudo_ctx); + +struct tevent_req * +ipa_sudo_full_refresh_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ipa_sudo_ctx *sudo_ctx); + +int +ipa_sudo_full_refresh_recv(struct tevent_req *req, + int *dp_error); + +struct tevent_req * +ipa_sudo_refresh_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ipa_sudo_ctx *sudo_ctx, + const char *search_filter, + const char *delete_filter); + +errno_t +ipa_sudo_refresh_recv(struct tevent_req *req, + int *dp_error, + size_t *_num_rules); + +struct ipa_sudo_conv; + +struct ipa_sudo_conv * +ipa_sudo_conv_init(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sdap_attr_map *map_rule, + struct sdap_attr_map *map_cmdgroup, + struct sdap_attr_map *map_cmd, + struct sdap_attr_map *map_user, + struct sdap_attr_map *map_group, + struct sdap_attr_map *map_host, + struct sdap_attr_map *map_hostgroup); + +errno_t +ipa_sudo_conv_rules(struct ipa_sudo_conv *conv, + struct sysdb_attrs **rules, + size_t num_rules); + +errno_t +ipa_sudo_conv_cmdgroups(struct ipa_sudo_conv *conv, + struct sysdb_attrs **cmdgroups, + size_t num_cmdgroups); + +errno_t +ipa_sudo_conv_cmds(struct ipa_sudo_conv *conv, + struct sysdb_attrs **cmds, + size_t num_cmds); + +bool +ipa_sudo_conv_has_cmdgroups(struct ipa_sudo_conv *conv); + +bool +ipa_sudo_conv_has_cmds(struct ipa_sudo_conv *conv); + +char * +ipa_sudo_conv_cmdgroup_filter(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv); + +char * +ipa_sudo_conv_cmd_filter(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv); + +errno_t +ipa_sudo_conv_result(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + struct sysdb_attrs ***_rules, + size_t *_num_rules); + #endif /* _IPA_SUDO_H_ */ diff --git a/src/providers/ipa/ipa_sudo_async.c b/src/providers/ipa/ipa_sudo_async.c new file mode 100644 index 0000000000000000000000000000000000000000..9ddda1b41a0b3c6ceb33e6d665749948ae835a97 --- /dev/null +++ b/src/providers/ipa/ipa_sudo_async.c @@ -0,0 +1,779 @@ +/* + Authors: + Pavel Březina + + 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 . +*/ + +#include +#include +#include + +#include "providers/ldap/sdap_ops.h" +#include "providers/ipa/ipa_common.h" +#include "providers/ipa/ipa_hosts.h" +#include "providers/ipa/ipa_sudo.h" +#include "providers/ipa/ipa_dn.h" +#include "db/sysdb.h" +#include "db/sysdb_sudo.h" + +struct ipa_hostinfo { + size_t num_hosts; + size_t num_hostgroups; + struct sysdb_attrs **hosts; + struct sysdb_attrs **hostgroups; +}; + +static char * +ipa_sudo_filter_append_origdn(char *filter, + struct sysdb_attrs *attrs, + const char *attr_name) +{ + const char *origdn; + char *sanitizeddn; + errno_t ret; + + ret = sysdb_attrs_get_string(attrs, SYSDB_ORIG_DN, &origdn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get original DN " + "[%d]: %s\n", ret, sss_strerror(ret)); + return NULL; + } + + ret = sss_filter_sanitize(NULL, origdn, &sanitizeddn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to sanitize DN " + "[%d]: %s\n", ret, sss_strerror(ret)); + return NULL; + } + + filter = talloc_asprintf_append(filter, "(%s=%s)", attr_name, sanitizeddn); + talloc_free(sanitizeddn); + if (filter == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append() failed\n"); + } + + return filter; +} + +/** + * (|(hostCategory=ALL)(memberHost=$DN(fqdn))(memberHost=$DN(hostgroup))...) + */ +static char * +ipa_sudo_host_filter(TALLOC_CTX *mem_ctx, + struct ipa_hostinfo *host, + struct sdap_attr_map *map) +{ + TALLOC_CTX *tmp_ctx; + char *filter; + size_t i; + + /* If realloc fails we will free all data through tmp_ctx. */ + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return NULL; + } + + filter = talloc_asprintf(tmp_ctx, "(!(%s=*))", + map[IPA_AT_SUDORULE_HOST].name); + if (filter == NULL) { + goto fail; + } + + /* Append hostCategory=ALL */ + filter = talloc_asprintf_append(filter, "(%s=ALL)", + map[IPA_AT_SUDORULE_HOSTCATEGORY].name); + if (filter == NULL) { + goto fail; + } + + /* Append client machine */ + for (i = 0; i < host->num_hosts; i++) { + filter = ipa_sudo_filter_append_origdn(filter, host->hosts[i], + map[IPA_AT_SUDORULE_HOST].name); + if (filter == NULL) { + goto fail; + } + } + + /* Append hostgroups */ + for (i = 0; i < host->num_hostgroups; i++) { + filter = ipa_sudo_filter_append_origdn(filter, host->hostgroups[i], + map[IPA_AT_SUDORULE_HOST].name); + if (filter == NULL) { + goto fail; + } + } + + /* OR filters */ + filter = talloc_asprintf(tmp_ctx, "(|%s)", filter); + if (filter == NULL) { + goto fail; + } + + talloc_steal(mem_ctx, filter); + talloc_free(tmp_ctx); + return filter; + +fail: + talloc_free(tmp_ctx); + return NULL; +} + +struct ipa_sudo_fetch_state { + struct tevent_context *ev; + struct sysdb_ctx *sysdb; + struct ipa_sudo_ctx *sudo_ctx; + struct sdap_options *sdap_opts; + struct ipa_hostinfo *host; + struct sdap_handle *sh; + + struct sdap_attr_map *map_cmdgroup; + struct sdap_attr_map *map_rule; + struct sdap_attr_map *map_cmd; + struct sdap_search_base **sudo_sb; + + struct ipa_sudo_conv *conv; + struct sysdb_attrs **rules; + size_t num_rules; +}; + +static errno_t ipa_sudo_fetch_rules(struct tevent_req *req); +static void ipa_sudo_fetch_rules_done(struct tevent_req *subreq); +static errno_t ipa_sudo_fetch_cmdgroups(struct tevent_req *req); +static void ipa_sudo_fetch_cmdgroups_done(struct tevent_req *subreq); +static errno_t ipa_sudo_fetch_cmds(struct tevent_req *req); +static void ipa_sudo_fetch_cmds_done(struct tevent_req *subreq); +static void ipa_sudo_fetch_done(struct tevent_req *req); + +static struct tevent_req * +ipa_sudo_fetch_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct ipa_sudo_ctx *sudo_ctx, + struct ipa_hostinfo *host, + struct sdap_attr_map *map_user, + struct sdap_attr_map *map_group, + struct sdap_attr_map *map_host, + struct sdap_attr_map *map_hostgroup, + struct sdap_handle *sh) +{ + struct ipa_sudo_fetch_state *state = NULL; + struct tevent_req *req = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct ipa_sudo_fetch_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n")); + return NULL; + } + + state->ev = ev; + state->sysdb = sysdb; + state->sudo_ctx = sudo_ctx; + state->sdap_opts = sudo_ctx->sdap_opts; + state->host = host; + state->sh = sh; + + state->map_cmdgroup = sudo_ctx->sudocmdgroup_map; + state->map_rule = sudo_ctx->sudorule_map; + state->map_cmd = sudo_ctx->sudocmd_map; + state->sudo_sb = sudo_ctx->sudo_sb; + + state->conv = ipa_sudo_conv_init(state, sysdb, state->map_rule, + state->map_cmdgroup, state->map_cmd, + map_user, map_group, map_host, + map_hostgroup); + if (state->conv == NULL) { + ret = ENOMEM; + goto immediately; + } + + ret = ipa_sudo_fetch_rules(req); + if (ret != EAGAIN) { + goto immediately; + } + + return req; + +immediately: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, state->ev); + + return req; +} + +static errno_t +ipa_sudo_fetch_rules(struct tevent_req *req) +{ + struct ipa_sudo_fetch_state *state; + struct tevent_req *subreq; + struct sdap_attr_map *map; + char *host_filter; + char *filter; + + DEBUG(SSSDBG_TRACE_FUNC, "About to fetch sudo rules\n"); + + state = tevent_req_data(req, struct ipa_sudo_fetch_state); + map = state->map_rule; + + host_filter = ipa_sudo_host_filter(state, state->host, map); + if (host_filter == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build host filter\n"); + return ENOMEM; + } + + filter = talloc_asprintf(state, "(&(objectClass=%s)(%s=TRUE)%s)", + map[IPA_OC_SUDORULE].name, + map[IPA_AT_SUDORULE_ENABLED].name, + host_filter); + talloc_zfree(host_filter); + if (filter == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build filter\n"); + return ENOMEM; + } + + subreq = sdap_search_bases_send(state, state->ev, state->sdap_opts, + state->sh, state->sudo_sb, map, true, 0, + filter, NULL); + if (subreq == NULL) { + return ENOMEM; + } + + tevent_req_set_callback(subreq, ipa_sudo_fetch_rules_done, req); + return EAGAIN; +} + +static void +ipa_sudo_fetch_rules_done(struct tevent_req *subreq) +{ + struct ipa_sudo_fetch_state *state = NULL; + struct tevent_req *req = NULL; + struct sysdb_attrs **attrs; + size_t num_attrs; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_sudo_fetch_state); + + ret = sdap_search_bases_recv(subreq, state, &num_attrs, &attrs); + talloc_zfree(subreq); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_IMPORTANT_INFO, "Received %zu sudo rules\n", num_attrs); + + ret = ipa_sudo_conv_rules(state->conv, attrs, num_attrs); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed when converting rules " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + ret = ipa_sudo_fetch_cmdgroups(req); + +done: + if (ret == EOK) { + ipa_sudo_fetch_done(req); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + } + + return; +} + +static errno_t +ipa_sudo_fetch_cmdgroups(struct tevent_req *req) +{ + struct ipa_sudo_fetch_state *state; + struct tevent_req *subreq; + char *filter; + + DEBUG(SSSDBG_TRACE_FUNC, "About to fetch sudo command groups\n"); + + state = tevent_req_data(req, struct ipa_sudo_fetch_state); + + if (ipa_sudo_conv_has_cmdgroups(state->conv)) { + DEBUG(SSSDBG_TRACE_FUNC, "No command groups needs to be downloaded\n"); + return ipa_sudo_fetch_cmds(req); + } + + filter = ipa_sudo_conv_cmdgroup_filter(state, state->conv); + if (filter == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build filter\n"); + return ENOMEM; + } + + subreq = sdap_search_bases_send(state, state->ev, state->sdap_opts, + state->sh, state->sudo_sb, + state->map_cmdgroup, true, 0, + filter, NULL); + if (subreq == NULL) { + return ENOMEM; + } + + tevent_req_set_callback(subreq, ipa_sudo_fetch_cmdgroups_done, req); + return EAGAIN; +} + +static void +ipa_sudo_fetch_cmdgroups_done(struct tevent_req *subreq) +{ + struct ipa_sudo_fetch_state *state = NULL; + struct tevent_req *req = NULL; + struct sysdb_attrs **attrs; + size_t num_attrs; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_sudo_fetch_state); + + ret = sdap_search_bases_recv(subreq, state, &num_attrs, &attrs); + talloc_zfree(subreq); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_IMPORTANT_INFO, "Received %zu sudo command groups\n", + num_attrs); + + ret = ipa_sudo_conv_cmdgroups(state->conv, attrs, num_attrs); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed when converting command groups " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + ret = ipa_sudo_fetch_cmds(req); + +done: + if (ret == EOK) { + ipa_sudo_fetch_done(req); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + } + + return; +} + +static errno_t +ipa_sudo_fetch_cmds(struct tevent_req *req) +{ + struct ipa_sudo_fetch_state *state; + struct tevent_req *subreq; + char *filter; + + DEBUG(SSSDBG_TRACE_FUNC, "About to fetch sudo commands\n"); + + state = tevent_req_data(req, struct ipa_sudo_fetch_state); + + if (ipa_sudo_conv_has_cmds(state->conv)) { + DEBUG(SSSDBG_TRACE_FUNC, "No commands needs to be downloaded\n"); + return EOK; + } + + filter = ipa_sudo_conv_cmd_filter(state, state->conv); + if (filter == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build filter\n"); + return ENOMEM; + } + + subreq = sdap_search_bases_send(state, state->ev, state->sdap_opts, + state->sh, state->sudo_sb, + state->map_cmd, true, 0, + filter, NULL); + if (subreq == NULL) { + return ENOMEM; + } + + tevent_req_set_callback(subreq, ipa_sudo_fetch_cmds_done, req); + return EAGAIN; +} + +static void +ipa_sudo_fetch_cmds_done(struct tevent_req *subreq) +{ + struct ipa_sudo_fetch_state *state = NULL; + struct tevent_req *req = NULL; + struct sysdb_attrs **attrs; + size_t num_attrs; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_sudo_fetch_state); + + ret = sdap_search_bases_recv(subreq, state, &num_attrs, &attrs); + talloc_zfree(subreq); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_IMPORTANT_INFO, "Received %zu sudo commands\n", num_attrs); + + ret = ipa_sudo_conv_cmds(state->conv, attrs, num_attrs); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed when converting commands " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + +done: + if (ret == EOK) { + ipa_sudo_fetch_done(req); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + } + + return; +} + +static void +ipa_sudo_fetch_done(struct tevent_req *req) +{ + struct ipa_sudo_fetch_state *state = NULL; + errno_t ret; + + state = tevent_req_data(req, struct ipa_sudo_fetch_state); + + DEBUG(SSSDBG_TRACE_FUNC, "About to convert rules\n"); + + ret = ipa_sudo_conv_result(state, state->conv, + &state->rules, &state->num_rules); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to convert rules [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + ret = EOK; + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static errno_t +ipa_sudo_fetch_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct sysdb_attrs ***_rules, + size_t *_num_rules) +{ + struct ipa_sudo_fetch_state *state = NULL; + state = tevent_req_data(req, struct ipa_sudo_fetch_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_rules = talloc_steal(mem_ctx, state->rules); + *_num_rules = state->num_rules; + + return EOK; +} + + +struct ipa_sudo_refresh_state { + struct tevent_context *ev; + struct sysdb_ctx *sysdb; + struct sss_domain_info *domain; + struct ipa_sudo_ctx *sudo_ctx; + struct ipa_options *ipa_opts; + struct sdap_options *sdap_opts; + const char *search_filter; + const char *delete_filter; + + struct sdap_id_op *sdap_op; + struct sdap_handle *sh; + int dp_error; + + struct sysdb_attrs **rules; + size_t num_rules; +}; + +static errno_t ipa_sudo_refresh_retry(struct tevent_req *req); +static void ipa_sudo_refresh_connect_done(struct tevent_req *subreq); +static void ipa_sudo_refresh_host_done(struct tevent_req *subreq); +static void ipa_sudo_refresh_done(struct tevent_req *subreq); + +struct tevent_req * +ipa_sudo_refresh_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ipa_sudo_ctx *sudo_ctx, + const char *search_filter, + const char *delete_filter) +{ + struct ipa_sudo_refresh_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct ipa_sudo_refresh_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n")); + return NULL; + } + + state->ev = ev; + state->sysdb = sudo_ctx->id_ctx->be->domain->sysdb; + state->domain = sudo_ctx->id_ctx->be->domain; + state->sudo_ctx = sudo_ctx; + state->ipa_opts = sudo_ctx->ipa_opts; + state->sdap_opts = sudo_ctx->sdap_opts; + state->dp_error = DP_ERR_FATAL; + + state->sdap_op = sdap_id_op_create(state, + sudo_ctx->id_ctx->conn->conn_cache); + if (!state->sdap_op) { + DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create() failed\n"); + ret = ENOMEM; + goto immediately; + } + + state->search_filter = talloc_strdup(state, search_filter); + if (search_filter != NULL && state->search_filter == NULL) { + ret = ENOMEM; + goto immediately; + } + + state->delete_filter = talloc_strdup(state, delete_filter); + if (delete_filter != NULL && state->delete_filter == NULL) { + ret = ENOMEM; + goto immediately; + } + + ret = ipa_sudo_refresh_retry(req); + if (ret == EAGAIN) { + /* asynchronous processing */ + return req; + } + +immediately: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, state->ev); + + return req; +} + +static errno_t +ipa_sudo_refresh_retry(struct tevent_req *req) +{ + struct ipa_sudo_refresh_state *state; + struct tevent_req *subreq; + int ret; + + state = tevent_req_data(req, struct ipa_sudo_refresh_state); + + subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sdap_id_op_connect_send() failed: " + "%d(%s)\n", ret, strerror(ret)); + return ret; + } + + tevent_req_set_callback(subreq, ipa_sudo_refresh_connect_done, req); + + return EAGAIN; +} + +static void +ipa_sudo_refresh_connect_done(struct tevent_req *subreq) +{ + struct ipa_sudo_refresh_state *state; + const char *hostname; + struct tevent_req *req; + int dp_error; + int ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_sudo_refresh_state); + + ret = sdap_id_op_connect_recv(subreq, &dp_error); + talloc_zfree(subreq); + + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "SUDO LDAP connection failed " + "[%d]: %s\n", ret, strerror(ret)); + state->dp_error = dp_error; + tevent_req_error(req, ret); + return; + } + + state->sh = sdap_id_op_handle(state->sdap_op); + + DEBUG(SSSDBG_TRACE_FUNC, "SUDO LDAP connection successful\n"); + DEBUG(SSSDBG_TRACE_FUNC, "About to fetch host information\n"); + + /* Obtain host information. */ + hostname = dp_opt_get_string(state->ipa_opts->basic, IPA_HOSTNAME); + + subreq = ipa_host_info_send(state, state->ev, + state->sh, state->sdap_opts, hostname, + state->ipa_opts->host_map, + state->ipa_opts->hostgroup_map, + state->ipa_opts->host_search_bases); + if (subreq == NULL) { + state->dp_error = DP_ERR_FATAL; + tevent_req_error(req, ENOMEM); + return; + } + + tevent_req_set_callback(subreq, ipa_sudo_refresh_host_done, req); +} + +static void +ipa_sudo_refresh_host_done(struct tevent_req *subreq) +{ + struct ipa_sudo_refresh_state *state; + struct ipa_hostinfo *host; + struct tevent_req *req; + int ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_sudo_refresh_state); + + host = talloc_zero(state, struct ipa_hostinfo); + if (host == NULL) { + state->dp_error = DP_ERR_FATAL; + tevent_req_error(req, ENOMEM); + return; + } + + ret = ipa_host_info_recv(subreq, host, &host->num_hosts, &host->hosts, + &host->num_hostgroups, &host->hostgroups); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve host information " + "[%d]: %s\n", ret, sss_strerror(ret)); + state->dp_error = DP_ERR_FATAL; + tevent_req_error(req, ret); + return; + } + + subreq = ipa_sudo_fetch_send(state, state->ev, state->sysdb, + state->sudo_ctx, host, + state->sdap_opts->user_map, + state->sdap_opts->group_map, + state->ipa_opts->host_map, + state->ipa_opts->hostgroup_map, state->sh); + if (subreq == NULL) { + state->dp_error = DP_ERR_FATAL; + tevent_req_error(req, ENOMEM); + return; + } + + tevent_req_set_callback(subreq, ipa_sudo_refresh_done, req); +} + +static void +ipa_sudo_refresh_done(struct tevent_req *subreq) +{ + struct ipa_sudo_refresh_state *state; + struct tevent_req *req; + bool in_transaction = false; + errno_t sret; + int ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_sudo_refresh_state); + + ret = ipa_sudo_fetch_recv(state, subreq, &state->rules, &state->num_rules); + talloc_zfree(subreq); + + ret = sdap_id_op_done(state->sdap_op, ret, &state->dp_error); + if (state->dp_error == DP_ERR_OK && ret != EOK) { + /* retry */ + ret = ipa_sudo_refresh_retry(req); + if (ret != EOK) { + tevent_req_error(req, ret); + } + return; + } else if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + ret = sysdb_transaction_start(state->sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n"); + goto done; + } + in_transaction = true; + + ret = sysdb_sudo_purge(state->domain, state->delete_filter, + state->rules, state->num_rules); + if (ret != EOK) { + goto done; + } + + ret = sysdb_sudo_store(state->domain, state->rules, state->num_rules); + if (ret != EOK) { + goto done; + } + + ret = sysdb_transaction_commit(state->sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n"); + goto done; + } + in_transaction = false; + + DEBUG(SSSDBG_TRACE_FUNC, "Sudo rules are successfully stored in cache\n"); + +done: + if (in_transaction) { + sret = sysdb_transaction_cancel(state->sysdb); + if (sret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Could not cancel transaction\n"); + } + } + + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +errno_t +ipa_sudo_refresh_recv(struct tevent_req *req, + int *dp_error, + size_t *_num_rules) +{ + struct ipa_sudo_refresh_state *state = NULL; + state = tevent_req_data(req, struct ipa_sudo_refresh_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *dp_error = state->dp_error; + + if (_num_rules != NULL) { + *_num_rules = state->num_rules; + } + + return EOK; +} diff --git a/src/providers/ipa/ipa_sudo_conversion.c b/src/providers/ipa/ipa_sudo_conversion.c new file mode 100644 index 0000000000000000000000000000000000000000..2f28f837e62b42406ddda25b3f63832c1abb950d --- /dev/null +++ b/src/providers/ipa/ipa_sudo_conversion.c @@ -0,0 +1,1158 @@ +/* + Authors: + Pavel Březina + + 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 . +*/ + +#include +#include +#include + +#include "providers/ldap/sdap.h" +#include "providers/ipa/ipa_common.h" +#include "providers/ipa/ipa_dn.h" +#include "db/sysdb_sudo.h" +#include "db/sysdb.h" +#include "util/util.h" + +#define SUDO_DN_CMDGROUPS "sudocmdgroups" +#define SUDO_DN_CMDS "sudocmds" +#define SUDO_DN_CONTAINER "sudo" +#define SUDO_DN_CN "cn" + +#define MATCHDN(cat) SUDO_DN_CN, (cat), SUDO_DN_CN, SUDO_DN_CONTAINER +#define MATCHDN_CMDGROUPS MATCHDN(SUDO_DN_CMDGROUPS) +#define MATCHDN_CMDS MATCHDN(SUDO_DN_CMDS) + +#define MATCHRDN_CMDGROUPS(map) (map)[IPA_AT_SUDOCMDGROUP_NAME].name, MATCHDN_CMDGROUPS +#define MATCHRDN_CMDS(map) (map)[IPA_AT_SUDOCMD_UUID].name, MATCHDN_CMDS + +#define MATCHRDN_USER(map) (map)[SDAP_AT_USER_NAME].name, "cn", "users", "cn", "accounts" +#define MATCHRDN_GROUP(map) (map)[SDAP_AT_GROUP_NAME].name, "cn", "groups", "cn", "accounts" +#define MATCHRDN_HOST(map) (map)[IPA_AT_HOST_FQDN].name, "cn", "computers", "cn", "accounts" +#define MATCHRDN_HOSTGROUP(map) (map)[IPA_AT_HOSTGROUP_NAME].name, "cn", "hostgroups", "cn", "accounts" + +struct ipa_sudo_conv { + struct sysdb_ctx *sysdb; + + struct sdap_attr_map *map_rule; + struct sdap_attr_map *map_cmdgroup; + struct sdap_attr_map *map_cmd; + struct sdap_attr_map *map_user; + struct sdap_attr_map *map_group; + struct sdap_attr_map *map_host; + struct sdap_attr_map *map_hostgroup; + + hash_table_t *rules; + hash_table_t *cmdgroups; + hash_table_t *cmds; +}; + +struct ipa_sudo_dn_list { + struct ipa_sudo_dn_list *prev, *next; + const char *dn; +}; + +struct ipa_sudo_rulemember { + struct ipa_sudo_dn_list *cmdgroups; + struct ipa_sudo_dn_list *cmds; +}; + +struct ipa_sudo_rule { + struct sysdb_attrs *attrs; + struct ipa_sudo_rulemember allow; + struct ipa_sudo_rulemember deny; +}; + +struct ipa_sudo_cmdgroup { + struct ipa_sudo_dn_list *cmds; + const char **expanded; +}; + +static size_t +ipa_sudo_dn_list_count(struct ipa_sudo_dn_list *list) +{ + struct ipa_sudo_dn_list *item; + size_t i; + + for (i = 0, item = list; item != NULL; item = item->next, i++) { + /* no op */ + } + + return i; +} + +static errno_t +ipa_sudo_conv_store(hash_table_t *table, + const char *key, + void *value) +{ + hash_key_t hkey; + hash_value_t hvalue; + int hret; + + if (table == NULL || key == NULL) { + return EINVAL; + } + + hkey.type = HASH_KEY_STRING; + hkey.str = discard_const(key); + + /* If value is NULL we don't want to override existing entry. */ + if (value == NULL && hash_has_key(table, &hkey)) { + return EEXIST; + } + + hvalue.type = HASH_VALUE_PTR; + hvalue.ptr = value; + + hret = hash_enter(table, &hkey, &hvalue); + if (hret != HASH_SUCCESS) { + return EIO; + } + + if (value != NULL) { + talloc_steal(table, value); + } + + return EOK; +} + +static void * +ipa_sudo_conv_lookup(hash_table_t *table, + const char *key) +{ + hash_key_t hkey; + hash_value_t hvalue; + int hret; + + hkey.type = HASH_KEY_STRING; + hkey.str = discard_const(key); + + hret = hash_lookup(table, &hkey, &hvalue); + if (hret == HASH_ERROR_KEY_NOT_FOUND) { + DEBUG(SSSDBG_OP_FAILURE, "Key not found %s\n", key); + return NULL; + } else if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup value [%d]\n", hret); + return NULL; + } + + return hvalue.ptr; +} + +static errno_t +store_rulemember(TALLOC_CTX *mem_ctx, + struct ipa_sudo_dn_list **list, + hash_table_t *table, + const char *dn) +{ + struct ipa_sudo_dn_list *item; + errno_t ret; + + item = talloc_zero(mem_ctx, struct ipa_sudo_dn_list); + if (item == NULL) { + return ENOMEM; + } + + ret = ipa_sudo_conv_store(table, dn, NULL); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to store DN %s [%d]: %s\n", + dn, ret, sss_strerror(ret)); + goto done; + } + + item->dn = talloc_steal(item, dn); + DLIST_ADD(*list, item); + +done: + if (ret != EOK && ret != EEXIST) { + talloc_free(item); + } + + return ret; +} + +static errno_t +process_rulemember(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + struct ipa_sudo_rulemember *rulemember, + struct sysdb_attrs *rule, + const char *attr) +{ + TALLOC_CTX *tmp_ctx; + const char **members; + errno_t ret; + int i; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = sysdb_attrs_get_string_array(rule, attr, tmp_ctx, &members); + if (ret == ENOENT) { + ret = EOK; + goto done; + } else if (ret != EOK) { + goto done; + } + + for (i = 0; members[i] != NULL; i++) { + if (ipa_check_rdn_bool(conv->sysdb, members[i], + MATCHRDN_CMDGROUPS(conv->map_cmdgroup))) { + ret = store_rulemember(mem_ctx, &rulemember->cmdgroups, + conv->cmdgroups, members[i]); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_INTERNAL, "Found sudo command group %s\n", + members[i]); + } else if (ret != EEXIST) { + goto done; + } + } else if (ipa_check_rdn_bool(conv->sysdb, members[i], + MATCHRDN_CMDS(conv->map_cmd))) { + ret = store_rulemember(mem_ctx, &rulemember->cmds, + conv->cmds, members[i]); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_INTERNAL, "Found sudo command group %s\n", + members[i]); + } else if (ret != EEXIST) { + goto done; + } + } else { + DEBUG(SSSDBG_MINOR_FAILURE, "Invalid member DN %s, skipping...\n", + members[i]); + continue; + } + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +process_allowcmd(struct ipa_sudo_conv *conv, + struct ipa_sudo_rule *rule) +{ + return process_rulemember(rule, conv, &rule->allow, rule->attrs, + SYSDB_IPA_SUDORULE_ALLOWCMD); +} + +static errno_t +process_denycmd(struct ipa_sudo_conv *conv, + struct ipa_sudo_rule *rule) +{ + return process_rulemember(rule, conv, &rule->deny, rule->attrs, + SYSDB_IPA_SUDORULE_DENYCMD); +} + +static errno_t +process_cmdgroupmember(struct ipa_sudo_conv *conv, + struct ipa_sudo_cmdgroup *cmdgroup, + struct sysdb_attrs *attrs) +{ + TALLOC_CTX *tmp_ctx; + struct ipa_sudo_dn_list *item; + const char **members; + errno_t ret; + int i; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = sysdb_attrs_get_string_array(attrs, SYSDB_MEMBER, tmp_ctx, &members); + if (ret == ENOENT) { + ret = EOK; + goto done; + } else if (ret != EOK) { + goto done; + } + + for (i = 0; members[i] != NULL; i++) { + ret = ipa_sudo_conv_store(conv->cmds, members[i], NULL); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_INTERNAL, "Found sudo command %s\n", + members[i]); + } else if (ret != EEXIST) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to store DN [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + item = talloc_zero(tmp_ctx, struct ipa_sudo_dn_list); + if (item == NULL) { + ret = ENOMEM; + goto done; + } + + item->dn = talloc_steal(item, members[i]); + DLIST_ADD(cmdgroup->cmds, item); + talloc_steal(cmdgroup, item); + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +struct ipa_sudo_conv * +ipa_sudo_conv_init(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sdap_attr_map *map_rule, + struct sdap_attr_map *map_cmdgroup, + struct sdap_attr_map *map_cmd, + struct sdap_attr_map *map_user, + struct sdap_attr_map *map_group, + struct sdap_attr_map *map_host, + struct sdap_attr_map *map_hostgroup) +{ + struct ipa_sudo_conv *conv; + errno_t ret; + + conv = talloc_zero(mem_ctx, struct ipa_sudo_conv); + if (conv == NULL) { + return NULL; + } + + conv->sysdb = sysdb; + conv->map_rule = map_rule; + conv->map_cmdgroup = map_cmdgroup; + conv->map_cmd = map_cmd; + conv->map_user = map_user; + conv->map_group = map_group; + conv->map_host = map_host; + conv->map_hostgroup = map_hostgroup; + + ret = sss_hash_create(conv, 20, &conv->rules); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + ret = sss_hash_create(conv, 20, &conv->cmdgroups); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + ret = sss_hash_create(conv, 20, &conv->cmds); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + +done: + if (ret != EOK) { + talloc_free(conv); + return NULL; + } + + return conv; +} + +errno_t +ipa_sudo_conv_rules(struct ipa_sudo_conv *conv, + struct sysdb_attrs **rules, + size_t num_rules) +{ + struct ipa_sudo_rule *rule = NULL; + const char *key; + errno_t ret; + size_t i; + + if (num_rules == 0) { + /* We're done here. */ + return EOK; + } + + for (i = 0; i < num_rules; i++) { + ret = sysdb_attrs_get_string(rules[i], SYSDB_NAME, &key); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Failed to get rule name, skipping " + "[%d]: %s\n", ret, sss_strerror(ret)); + continue; + } + + rule = talloc_zero(conv->rules, struct ipa_sudo_rule); + if (rule == NULL) { + ret = ENOMEM; + goto done; + } + + rule->attrs = rules[i]; + + ret = process_allowcmd(conv, rule); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to process memberAllowCmd " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } + + ret = process_denycmd(conv, rule); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to process memberDenyCmd " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } + + ret = ipa_sudo_conv_store(conv->rules, key, rule); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to store rule into table " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + talloc_steal(rule, rule->attrs); + rule = NULL; + } + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(rule); + } + + return ret; +} + +errno_t +ipa_sudo_conv_cmdgroups(struct ipa_sudo_conv *conv, + struct sysdb_attrs **cmdgroups, + size_t num_cmdgroups) +{ + struct ipa_sudo_cmdgroup *cmdgroup = NULL; + const char *key; + errno_t ret; + size_t i; + + if (num_cmdgroups == 0) { + /* We're done here. */ + return EOK; + } + + for (i = 0; i < num_cmdgroups; i++) { + ret = sysdb_attrs_get_string(cmdgroups[i], SYSDB_ORIG_DN, &key); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Failed to get command group DN, " + "skipping [%d]: %s\n", ret, sss_strerror(ret)); + continue; + } + + cmdgroup = talloc_zero(conv->cmdgroups, struct ipa_sudo_cmdgroup); + if (cmdgroup == NULL) { + ret = ENOMEM; + goto done; + } + + ret = process_cmdgroupmember(conv, cmdgroup, cmdgroups[i]); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to process member " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } + + ret = ipa_sudo_conv_store(conv->cmdgroups, key, cmdgroup); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to store command group into " + "table [%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + cmdgroup = NULL; + } + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(cmdgroup); + } + + return ret; +} + +errno_t +ipa_sudo_conv_cmds(struct ipa_sudo_conv *conv, + struct sysdb_attrs **cmds, + size_t num_cmds) +{ + const char *key; + const char *cmd; + errno_t ret; + size_t i; + + if (num_cmds == 0) { + /* We're done here. */ + return EOK; + } + + for (i = 0; i < num_cmds; i++) { + ret = sysdb_attrs_get_string(cmds[i], SYSDB_ORIG_DN, &key); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Failed to get command DN, skipping " + "[%d]: %s\n", ret, sss_strerror(ret)); + continue; + } + + ret = sysdb_attrs_get_string(cmds[i], SYSDB_IPA_SUDOCMD_SUDOCMD, &cmd); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Failed to get command, skipping " + "[%d]: %s\n", ret, sss_strerror(ret)); + continue; + } + + ret = ipa_sudo_conv_store(conv->cmds, key, discard_const(cmd)); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to store command into table " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + } + + ret = EOK; + +done: + return ret; +} + +bool +ipa_sudo_conv_has_cmdgroups(struct ipa_sudo_conv *conv) +{ + return hash_count(conv->cmdgroups) == 0; +} + +bool +ipa_sudo_conv_has_cmds(struct ipa_sudo_conv *conv) +{ + return hash_count(conv->cmds) == 0; +} + +static char * +build_filter(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + hash_table_t *table, + const char *class, + const char *rdn_attr, + const char *category) +{ + TALLOC_CTX *tmp_ctx; + hash_key_t *keys; + unsigned long int count; + unsigned long int i; + char *filter; + char *rdn_val; + char *safe_rdn; + errno_t ret; + int hret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return NULL; + } + + hret = hash_keys(table, &count, &keys); + if (hret != HASH_SUCCESS) { + ret = ENOMEM; + goto done; + } + + talloc_steal(tmp_ctx, keys); + + filter = talloc_strdup(tmp_ctx, ""); + if (filter == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < count; i++) { + ret = ipa_get_rdn(tmp_ctx, sysdb, keys[i].str, &rdn_val, + rdn_attr, MATCHDN(category)); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get member %s [%d]: %s\n", + keys[i].str, ret, sss_strerror(ret)); + goto done; + } + + ret = sss_filter_sanitize(tmp_ctx, rdn_val, &safe_rdn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to sanitize DN " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + filter = talloc_asprintf_append(filter, "(%s=%s)", rdn_attr, safe_rdn); + if (filter == NULL) { + ret = ENOMEM; + goto done; + } + } + + filter = talloc_asprintf(filter, "(&(objectClass=%s)(|%s))", + class, filter); + if (filter == NULL) { + ret = ENOMEM; + goto done; + } + + talloc_steal(mem_ctx, filter); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + + if (ret != EOK) { + return NULL; + } + + return filter; +} + +char * +ipa_sudo_conv_cmdgroup_filter(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv) +{ + const char *rdn_attr = conv->map_cmdgroup[IPA_AT_SUDOCMDGROUP_NAME].name; + const char *class = conv->map_cmdgroup[IPA_OC_SUDOCMDGROUP].name; + + return build_filter(mem_ctx, conv->sysdb, conv->cmdgroups, class, + rdn_attr, SUDO_DN_CMDGROUPS); +} + +char * +ipa_sudo_conv_cmd_filter(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv) +{ + const char *rdn_attr = conv->map_cmd[IPA_AT_SUDOCMD_UUID].name; + const char *class = conv->map_cmd[IPA_OC_SUDOCMD].name; + + return build_filter(mem_ctx, conv->sysdb, conv->cmds, class, + rdn_attr, SUDO_DN_CMDS); +} + +struct ipa_sudo_conv_result_ctx { + struct ipa_sudo_conv *conv; + struct sysdb_attrs **rules; + size_t num_rules; + errno_t ret; +}; + +static const char * +convert_host(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + const char *value) +{ + char *rdn; + const char *group; + errno_t ret; + + ret = ipa_get_rdn(mem_ctx, conv->sysdb, value, &rdn, + MATCHRDN_HOST(conv->map_host)); + if (ret == EOK) { + return rdn; + } else if (ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n", + value, ret, sss_strerror(ret)); + return NULL; + } + + ret = ipa_get_rdn(mem_ctx, conv->sysdb, value, &rdn, + MATCHRDN_HOSTGROUP(conv->map_hostgroup)); + if (ret == ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected DN %s\n", value); + return NULL; + } else if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n", + value, ret, sss_strerror(ret)); + return NULL; + } + + group = talloc_asprintf(mem_ctx, "+%s", rdn); + talloc_free(rdn); + + return group; +} + +static const char * +convert_user(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + const char *value) +{ + char *rdn; + const char *group; + errno_t ret; + + ret = ipa_get_rdn(mem_ctx, conv->sysdb, value, &rdn, + MATCHRDN_USER(conv->map_user)); + if (ret == EOK) { + return rdn; + } else if (ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n", + value, ret, sss_strerror(ret)); + return NULL; + } + + ret = ipa_get_rdn(mem_ctx, conv->sysdb, value, &rdn, + MATCHRDN_GROUP(conv->map_group)); + if (ret == ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected DN %s\n", value); + return NULL; + } else if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n", + value, ret, sss_strerror(ret)); + return NULL; + } + + group = talloc_asprintf(mem_ctx, "%%%s", rdn); + talloc_free(rdn); + + return group; +} + +static const char * +convert_group(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + const char *value) +{ + char *rdn; + errno_t ret; + + ret = ipa_get_rdn(mem_ctx, conv->sysdb, value, &rdn, + MATCHRDN_GROUP(conv->map_group)); + if (ret == ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected DN %s\n", value); + return NULL; + } else if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n", + value, ret, sss_strerror(ret)); + return NULL; + } + + return rdn; +} + +static const char * +convert_cat(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + const char *value) +{ + if (strcmp(value, "all") == 0) { + return talloc_strdup(mem_ctx, "ALL"); + } + + return value; +} + +static errno_t +convert_attributes(struct ipa_sudo_conv *conv, + struct ipa_sudo_rule *rule, + struct sysdb_attrs *attrs) +{ + TALLOC_CTX *tmp_ctx; + const char **values; + const char *value; + errno_t ret; + int i, j; + static struct { + const char *ipa; + const char *sudo; + const char *(*conv_fn)(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + const char *value); + } table[] = {{SYSDB_NAME, SYSDB_SUDO_CACHE_AT_CN , NULL}, + {SYSDB_IPA_SUDORULE_HOST, SYSDB_SUDO_CACHE_AT_HOST , convert_host}, + {SYSDB_IPA_SUDORULE_USER, SYSDB_SUDO_CACHE_AT_USER , convert_user}, + {SYSDB_IPA_SUDORULE_RUNASUSER, SYSDB_SUDO_CACHE_AT_RUNASUSER , convert_user}, + {SYSDB_IPA_SUDORULE_RUNASGROUP, SYSDB_SUDO_CACHE_AT_RUNASGROUP , convert_group}, + {SYSDB_IPA_SUDORULE_OPTION, SYSDB_SUDO_CACHE_AT_OPTION , NULL}, + {SYSDB_IPA_SUDORULE_NOTAFTER, SYSDB_SUDO_CACHE_AT_NOTAFTER , NULL}, + {SYSDB_IPA_SUDORULE_NOTBEFORE, SYSDB_SUDO_CACHE_AT_NOTBEFORE , NULL}, + {SYSDB_IPA_SUDORULE_SUDOORDER, SYSDB_SUDO_CACHE_AT_ORDER , NULL}, + {SYSDB_IPA_SUDORULE_CMDCATEGORY, SYSDB_SUDO_CACHE_AT_COMMAND , convert_cat}, + {SYSDB_IPA_SUDORULE_HOSTCATEGORY, SYSDB_SUDO_CACHE_AT_HOST , convert_cat}, + {SYSDB_IPA_SUDORULE_USERCATEGORY, SYSDB_SUDO_CACHE_AT_USER , convert_cat}, + {SYSDB_IPA_SUDORULE_RUNASUSERCATEGORY, SYSDB_SUDO_CACHE_AT_RUNASUSER , convert_cat}, + {SYSDB_IPA_SUDORULE_RUNASGROUPCATEGORY, SYSDB_SUDO_CACHE_AT_RUNASGROUP , convert_cat}, + {SYSDB_IPA_SUDORULE_ALLOWCMD, SYSDB_IPA_SUDORULE_ORIGCMD , NULL}, + {SYSDB_IPA_SUDORULE_DENYCMD, SYSDB_IPA_SUDORULE_ORIGCMD , NULL}, + {NULL, NULL, NULL}}; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + for (i = 0; table[i].ipa != NULL; i++) { + ret = sysdb_attrs_get_string_array(rule->attrs, table[i].ipa, + tmp_ctx, &values); + if (ret == ENOENT) { + continue; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read attribute " + "%s [%d]: %s\n", table[i].ipa, ret, sss_strerror(ret)); + goto done; + } + + for (j = 0; values[j] != NULL; j++) { + if (table[i].conv_fn != NULL) { + value = table[i].conv_fn(tmp_ctx, conv, values[j]); + if (value == NULL) { + ret = ENOMEM; + goto done; + } + } else { + value = values[j]; + } + + ret = sysdb_attrs_add_string_safe(attrs, table[i].sudo, value); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add attribute " + "%s [%d]: %s\n", table[i].sudo, ret, sss_strerror(ret)); + goto done; + } + } + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static const char ** +combine_cmdgroups(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + struct ipa_sudo_dn_list *list) +{ + TALLOC_CTX *tmp_ctx; + struct ipa_sudo_cmdgroup *cmdgroup; + struct ipa_sudo_dn_list *listitem; + const char **values = NULL; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return NULL; + } + + values = talloc_zero_array(tmp_ctx, const char *, 1); + if (values == NULL) { + talloc_free(tmp_ctx); + return NULL; + } + + DLIST_FOR_EACH(listitem, list) { + cmdgroup = ipa_sudo_conv_lookup(conv->cmdgroups, listitem->dn); + + ret = add_strings_lists(mem_ctx, values, cmdgroup->expanded, + false, discard_const(&values)); + if (ret != EOK) { + talloc_free(tmp_ctx); + return NULL; + } + } + + talloc_steal(mem_ctx, values); + talloc_free(tmp_ctx); + + return values; +} + +static const char ** +combine_cmds(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + struct ipa_sudo_dn_list *list) +{ + struct ipa_sudo_dn_list *listitem; + const char **values; + const char *command; + size_t count; + size_t i; + + count = ipa_sudo_dn_list_count(list); + + values = talloc_zero_array(mem_ctx, const char *, count + 1); + if (values == NULL) { + return NULL; + } + + i = 0; + DLIST_FOR_EACH(listitem, list) { + command = ipa_sudo_conv_lookup(conv->cmds, listitem->dn); + if (command == NULL) { + continue; + } + + values[i] = command; + i++; + } + + return values; +} + +static errno_t +build_sudocommand(struct ipa_sudo_conv *conv, + struct ipa_sudo_rulemember *mlist, + struct sysdb_attrs *attrs, + char prefix) +{ + TALLOC_CTX *tmp_ctx; + const char **cmds[2]; + const char *command; + errno_t ret; + int i, j; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + cmds[0] = combine_cmdgroups(tmp_ctx, conv, mlist->cmdgroups); + if (cmds[0] == NULL) { + ret = ENOMEM; + goto done; + } + + cmds[1] = combine_cmds(tmp_ctx, conv, mlist->cmds); + if (cmds[1] == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < 2; i++) { + for (j = 0; cmds[i][j] != NULL; j++) { + if (prefix == '\0') { + command = cmds[i][j]; + } else { + command = talloc_asprintf(tmp_ctx, "%c%s", prefix, cmds[i][j]); + if (command == NULL) { + ret = ENOMEM; + goto done; + } + } + + ret = sysdb_attrs_add_string_safe(attrs, + SYSDB_SUDO_CACHE_AT_COMMAND, command); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add attribute " + "%s [%d]: %s\n", SYSDB_SUDO_CACHE_AT_COMMAND, + ret, sss_strerror(ret)); + goto done; + } + } + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +convert_sudocommand(struct ipa_sudo_conv *conv, + struct ipa_sudo_rule *rule, + struct sysdb_attrs *attrs) +{ + TALLOC_CTX *tmp_ctx; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = build_sudocommand(conv, &rule->allow, attrs, '\0'); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build allow commands " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + ret = build_sudocommand(conv, &rule->deny, attrs, '!'); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build deny commands " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static bool +rules_iterator(hash_entry_t *item, + void *user_data) +{ + struct ipa_sudo_conv_result_ctx *ctx = user_data; + struct ipa_sudo_rule *rule = item->value.ptr; + struct sysdb_attrs *attrs; + + if (ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: ctx is NULL\n"); + return false; + } + + if (rule == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: rule is NULL\n"); + ctx->ret = ERR_INTERNAL; + return false; + } + + attrs = sysdb_new_attrs(ctx->rules); + if (attrs == NULL) { + ctx->ret = ENOMEM; + return false; + } + + ctx->ret = convert_attributes(ctx->conv, rule, attrs); + if (ctx->ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to convert attributes [%d]: %s\n", + ctx->ret, sss_strerror(ctx->ret)); + talloc_free(attrs); + return false; + } + + ctx->ret = convert_sudocommand(ctx->conv, rule, attrs); + if (ctx->ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to build sudoCommand [%d]: %s\n", + ctx->ret, sss_strerror(ctx->ret)); + talloc_free(attrs); + return false; + } + + ctx->rules[ctx->num_rules] = attrs; + ctx->num_rules++; + + return true; +} + +static bool +cmdgroups_iterator(hash_entry_t *item, + void *user_data) +{ + struct ipa_sudo_conv_result_ctx *ctx = user_data; + struct ipa_sudo_cmdgroup *cmdgroup = item->value.ptr; + const char **values; + + if (ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: ctx is NULL\n"); + return false; + } + + if (cmdgroup == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: rule is NULL\n"); + ctx->ret = ERR_INTERNAL; + return false; + } + + values = combine_cmds(cmdgroup, ctx->conv, cmdgroup->cmds); + if (values == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand commands\n"); + ctx->ret = ENOMEM; + return false; + } + + cmdgroup->expanded = values; + ctx->ret = EOK; + + return true; +} + +errno_t +ipa_sudo_conv_result(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + struct sysdb_attrs ***_rules, + size_t *_num_rules) +{ + struct ipa_sudo_conv_result_ctx ctx; + struct sysdb_attrs **rules; + unsigned long num_rules; + int hret; + + num_rules = hash_count(conv->rules); + if (num_rules == 0) { + *_rules = NULL; + *_num_rules = 0; + return EOK; + } + + ctx.conv = conv; + ctx.rules = NULL; + ctx.num_rules = 0; + + /* If there are no cmdgroups the iterator is not called and ctx.ret is + * uninitialized. Since it is ok that there are no cmdgroups initializing + * ctx.ret to EOK. */ + ctx.ret = EOK; + + /* Expand commands in command groups. */ + hret = hash_iterate(conv->cmdgroups, cmdgroups_iterator, &ctx); + if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to iterate over command groups " + "[%d]\n", hret); + return EIO; + } + + if (ctx.ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand command grousp " + "[%d]: %s\n", ctx.ret, sss_strerror(ctx.ret)); + return ctx.ret; + } + + /* Convert rules. */ + rules = talloc_zero_array(mem_ctx, struct sysdb_attrs *, num_rules); + if (rules == NULL) { + return ENOMEM; + } + + ctx.rules = rules; + ctx.num_rules = 0; + + hret = hash_iterate(conv->rules, rules_iterator, &ctx); + if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to iterate over rules [%d]\n", hret); + return EIO; + } + + if (ctx.ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to convert rules [%d]: %s\n", + ctx.ret, sss_strerror(ctx.ret)); + talloc_free(rules); + return ctx.ret; + } + + *_rules = ctx.rules; + *_num_rules = ctx.num_rules; + + return EOK; +} diff --git a/src/providers/ipa/ipa_sudo_refresh.c b/src/providers/ipa/ipa_sudo_refresh.c new file mode 100644 index 0000000000000000000000000000000000000000..6fb8f66af607440ddcbb266c0b049ed99bf235b9 --- /dev/null +++ b/src/providers/ipa/ipa_sudo_refresh.c @@ -0,0 +1,195 @@ +/* + Authors: + Pavel Březina + + 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 . +*/ + +#include +#include +#include + +#include "util/util.h" +#include "providers/dp_ptask.h" +#include "providers/ipa/ipa_sudo.h" +#include "providers/ldap/sdap_sudo_shared.h" +#include "db/sysdb_sudo.h" + +struct ipa_sudo_full_refresh_state { + struct ipa_sudo_ctx *sudo_ctx; + struct sss_domain_info *domain; + int dp_error; +}; + +static void ipa_sudo_full_refresh_done(struct tevent_req *subreq); + +struct tevent_req * +ipa_sudo_full_refresh_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ipa_sudo_ctx *sudo_ctx) +{ + struct ipa_sudo_full_refresh_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + char *delete_filter; + int ret; + + req = tevent_req_create(mem_ctx, &state, + struct ipa_sudo_full_refresh_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + sudo_ctx->full_refresh_in_progress = true; + + state->domain = sudo_ctx->id_ctx->be->domain; + state->sudo_ctx = sudo_ctx; + + /* Remove all rules from cache */ + delete_filter = talloc_asprintf(state, "(%s=%s)", SYSDB_OBJECTCLASS, + SYSDB_SUDO_CACHE_OC); + if (delete_filter == NULL) { + ret = ENOMEM; + goto immediately; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Issuing a full refresh of sudo rules\n"); + + subreq = ipa_sudo_refresh_send(state, ev, sudo_ctx, NULL, delete_filter); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } + + tevent_req_set_callback(subreq, ipa_sudo_full_refresh_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 +ipa_sudo_full_refresh_done(struct tevent_req *subreq) +{ + struct ipa_sudo_full_refresh_state *state; + struct tevent_req *req; + int ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_sudo_full_refresh_state); + + ret = ipa_sudo_refresh_recv(subreq, &state->dp_error, NULL); + talloc_zfree(subreq); + if (ret != EOK || state->dp_error != DP_ERR_OK) { + goto done; + } + + state->sudo_ctx->full_refresh_done = true; + + ret = sysdb_sudo_set_last_full_refresh(state->domain, time(NULL)); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Unable to save time of " + "a successful full refresh\n"); + } + + DEBUG(SSSDBG_TRACE_FUNC, "Successful full refresh of sudo rules\n"); + +done: + state->sudo_ctx->full_refresh_in_progress = false; + + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int +ipa_sudo_full_refresh_recv(struct tevent_req *req, + int *dp_error) +{ + struct ipa_sudo_full_refresh_state *state; + state = tevent_req_data(req, struct ipa_sudo_full_refresh_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *dp_error = state->dp_error; + + return EOK; +} + +static struct tevent_req * +ipa_sudo_ptask_full_refresh_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct be_ptask *be_ptask, + void *pvt) +{ + struct ipa_sudo_ctx *sudo_ctx; + sudo_ctx = talloc_get_type(pvt, struct ipa_sudo_ctx); + + return ipa_sudo_full_refresh_send(mem_ctx, be_ctx->ev, sudo_ctx); +} + +static errno_t +ipa_sudo_ptask_full_refresh_recv(struct tevent_req *req) +{ + int dp_error; + + return ipa_sudo_full_refresh_recv(req, &dp_error); +} + +static struct tevent_req * +ipa_sudo_ptask_smart_refresh_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct be_ptask *be_ptask, + void *pvt) +{ + struct ipa_sudo_ctx *sudo_ctx; + sudo_ctx = talloc_get_type(pvt, struct ipa_sudo_ctx); + + return ipa_sudo_full_refresh_send(mem_ctx, be_ctx->ev, sudo_ctx); +} + +static errno_t +ipa_sudo_ptask_smart_refresh_recv(struct tevent_req *req) +{ + int dp_error; + + return ipa_sudo_full_refresh_recv(req, &dp_error); +} + +errno_t +ipa_sudo_ptask_setup(struct be_ctx *be_ctx, struct ipa_sudo_ctx *sudo_ctx) +{ + return sdap_sudo_ptask_setup_generic(be_ctx, sudo_ctx->id_ctx->opts->basic, + ipa_sudo_ptask_full_refresh_send, + ipa_sudo_ptask_full_refresh_recv, + ipa_sudo_ptask_smart_refresh_send, + ipa_sudo_ptask_smart_refresh_recv, + sudo_ctx); +} -- 2.5.0