/* 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/ldap/sdap_sudo_shared.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; } static errno_t ipa_sudo_highest_usn(TALLOC_CTX *mem_ctx, struct sysdb_attrs **attrs, size_t num_attrs, char **current_usn) { errno_t ret; char *usn; ret = sysdb_get_highest_usn(mem_ctx, attrs, num_attrs, &usn); if (ret != EOK) { DEBUG(SSSDBG_MINOR_FAILURE, "Unable to get highest USN [%d]: %s\n", ret, sss_strerror(ret)); return ret; } if (sysdb_compare_usn(usn, *current_usn) > 0) { talloc_free(*current_usn); *current_usn = usn; return EOK; } talloc_free(usn); return EOK; } static errno_t ipa_sudo_assoc_rules_filter(TALLOC_CTX *mem_ctx, struct sysdb_attrs **cmdgroups, size_t num_cmdgroups, char **_filter) { TALLOC_CTX *tmp_ctx; const char *origdn; char *sanitized; char *filter; errno_t ret; size_t i; if (num_cmdgroups == 0) { return ENOENT; } tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { return ENOMEM; } filter = talloc_strdup(tmp_ctx, ""); if (filter == NULL) { ret = ENOMEM; goto done; } for (i = 0; i < num_cmdgroups; i++) { ret = sysdb_attrs_get_string(cmdgroups[i], SYSDB_ORIG_DN, &origdn); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get original dn [%d]: %s\n", ret, sss_strerror(ret)); ret = ERR_INTERNAL; goto done; } ret = sss_filter_sanitize(tmp_ctx, origdn, &sanitized); if (ret != EOK) { goto done; } filter = talloc_asprintf_append(filter, "(%s=%s)", SYSDB_IPA_SUDORULE_ORIGCMD, sanitized); if (filter == NULL) { ret = ENOMEM; goto done; } } filter = talloc_asprintf(tmp_ctx, "(&(objectClass=%s)(|%s)))", SYSDB_SUDO_CACHE_OC, filter); if (filter == NULL) { ret = ENOMEM; goto done; } *_filter = talloc_steal(mem_ctx, filter); ret = EOK; done: talloc_free(tmp_ctx); return ret; } static errno_t ipa_sudo_assoc_rules(TALLOC_CTX *mem_ctx, struct sss_domain_info *domain, struct sysdb_attrs **cmdgroups, size_t num_cmdgroups, struct sysdb_attrs ***_rules, size_t *_num_rules) { TALLOC_CTX *tmp_ctx; const char *attrs[] = {SYSDB_NAME, NULL}; struct sysdb_attrs **rules; struct ldb_message **msgs; size_t num_rules; char *filter; errno_t ret; tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { return ENOMEM; } ret = ipa_sudo_assoc_rules_filter(tmp_ctx, cmdgroups, num_cmdgroups, &filter); if (ret != EOK) { goto done; } ret = sysdb_search_custom(tmp_ctx, domain, filter, SUDORULE_SUBDIR, attrs, &num_rules, &msgs); if (ret == ENOENT) { *_rules = NULL; *_num_rules = 0; ret = EOK; goto done; } else if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up sudo rules [%d]: %s\n", ret, sss_strerror(ret)); goto done; } ret = sysdb_msg2attrs(tmp_ctx, num_rules, msgs, &rules); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not convert ldb message to " "sysdb_attrs [%d]: %s\n", ret, sss_strerror(ret)); goto done; } *_rules = talloc_steal(mem_ctx, rules); *_num_rules = num_rules; done: talloc_free(tmp_ctx); return ret; } static errno_t ipa_sudo_filter_rules_bycmdgroups(TALLOC_CTX *mem_ctx, struct sss_domain_info *domain, struct sysdb_attrs **cmdgroups, size_t num_cmdgroups, struct sdap_attr_map *map_rule, char **_filter) { TALLOC_CTX *tmp_ctx; struct sysdb_attrs **rules; size_t num_rules; const char *name; char *sanitized; char *filter; errno_t ret; size_t i; if (num_cmdgroups == 0) { *_filter = NULL; return EOK; } tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { return ENOMEM; } ret = ipa_sudo_assoc_rules(tmp_ctx, domain, cmdgroups, num_cmdgroups, &rules, &num_rules); if (ret != EOK) { goto done; } if (num_rules == 0) { *_filter = NULL; ret = EOK; goto done; } filter = talloc_strdup(tmp_ctx, ""); if (filter == NULL) { ret = ENOMEM; goto done; } for (i = 0; i < num_rules; i++) { ret = sysdb_attrs_get_string(rules[i], SYSDB_NAME, &name); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get name [%d]: %s\n", ret, sss_strerror(ret)); goto done; } ret = sss_filter_sanitize(tmp_ctx, name, &sanitized); if (ret != EOK) { goto done; } filter = talloc_asprintf_append(filter, "(%s=%s)", map_rule[IPA_AT_SUDORULE_NAME].name, sanitized); if (filter == NULL) { ret = ENOMEM; goto done; } } filter = talloc_asprintf(tmp_ctx, "(|%s)", filter); if (filter == NULL) { ret = ENOMEM; goto done; } *_filter = talloc_steal(mem_ctx, filter); ret = EOK; done: talloc_free(tmp_ctx); return ret; } struct ipa_sudo_fetch_state { struct tevent_context *ev; struct sysdb_ctx *sysdb; struct sss_domain_info *domain; struct ipa_sudo_ctx *sudo_ctx; struct sdap_options *sdap_opts; struct ipa_hostinfo *host; struct sdap_handle *sh; const char *search_filter; const char *cmdgroups_filter; 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; char *usn; }; static errno_t ipa_sudo_fetch_addtl_cmdgroups(struct tevent_req *req); static void ipa_sudo_fetch_addtl_cmdgroups_done(struct tevent_req *subreq); 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 sss_domain_info *domain, 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, const char *cmdgroups_filter, const char *search_filter) { 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->domain = domain; state->sudo_ctx = sudo_ctx; state->sdap_opts = sudo_ctx->sdap_opts; state->host = host; state->sh = sh; state->search_filter = search_filter == NULL ? "" : search_filter; state->cmdgroups_filter = cmdgroups_filter; 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; } if (state->cmdgroups_filter != NULL) { /* We need to fetch additional cmdgroups that may not be revealed * during normal search. Such as when using entryUSN filter in smart * refresh, some command groups may have change but none rule was * modified but we need to fetch associated rules anyway. */ ret = ipa_sudo_fetch_addtl_cmdgroups(req); } else { 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_addtl_cmdgroups(struct tevent_req *req) { struct ipa_sudo_fetch_state *state; struct tevent_req *subreq; struct sdap_attr_map *map; char *filter; DEBUG(SSSDBG_TRACE_FUNC, "About to fetch additional command groups\n"); state = tevent_req_data(req, struct ipa_sudo_fetch_state); map = state->map_cmdgroup; filter = talloc_asprintf(state, "(&(objectClass=%s)%s)", map[IPA_OC_SUDOCMDGROUP].name, state->cmdgroups_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_addtl_cmdgroups_done, req); return EAGAIN; } static void ipa_sudo_fetch_addtl_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; char *filter; 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 additional command groups\n", num_attrs); ret = ipa_sudo_filter_rules_bycmdgroups(state, state->domain, attrs, num_attrs, state->map_rule, &filter); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to construct rules filter " "[%d]: %s\n", ret, sss_strerror(ret)); goto done; } state->search_filter = sdap_or_filters(state, state->search_filter, filter); if (state->search_filter == NULL) { ret = ENOMEM; goto done; } ret = ipa_sudo_fetch_rules(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_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%s)", map[IPA_OC_SUDORULE].name, map[IPA_AT_SUDORULE_ENABLED].name, host_filter, state->search_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_highest_usn(state, attrs, num_attrs, &state->usn); if (ret != EOK) { 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_highest_usn(state, attrs, num_attrs, &state->usn); if (ret != EOK) { 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, char **_usn) { 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; *_usn = talloc_steal(mem_ctx, state->usn); 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 *cmdgroups_filter; 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 *cmdgroups_filter, 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->cmdgroups_filter = talloc_strdup(state, cmdgroups_filter); if (cmdgroups_filter != NULL && state->cmdgroups_filter == NULL) { 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->domain, 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, state->cmdgroups_filter, state->search_filter); 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; char *usn = NULL; 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, &usn); 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; if (usn != NULL) { sdap_sudo_set_usn(state->sudo_ctx->id_ctx->srv_opts, usn); } 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; }