From 5edf5c55bb259ac29454493d06097c5fab8a2199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= Date: Fri, 18 Dec 2015 13:05:41 +0100 Subject: [PATCH 31/49] IPA SUDO: Implement smart refresh Reviewed-by: Sumit Bose (cherry picked from commit cc7f9b639144183eb4f8bd86e5bed077da7d4e35) --- src/providers/ipa/ipa_sudo.h | 1 + src/providers/ipa/ipa_sudo_async.c | 312 ++++++++++++++++++++++++++++++++++- src/providers/ipa/ipa_sudo_refresh.c | 132 ++++++++++++++- 3 files changed, 438 insertions(+), 7 deletions(-) diff --git a/src/providers/ipa/ipa_sudo.h b/src/providers/ipa/ipa_sudo.h index 9dd72948732f4b6e19f4a6546128c5319cd97bda..81ada14e46550fab815a7df262abd0b5fa11afd7 100644 --- a/src/providers/ipa/ipa_sudo.h +++ b/src/providers/ipa/ipa_sudo.h @@ -59,6 +59,7 @@ 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); diff --git a/src/providers/ipa/ipa_sudo_async.c b/src/providers/ipa/ipa_sudo_async.c index d52b97da17337b224c4be4b4fb65b0a99000e4b6..79e69ce962fd5cc2df0e9aac10a5469ffd73c6be 100644 --- a/src/providers/ipa/ipa_sudo_async.c +++ b/src/providers/ipa/ipa_sudo_async.c @@ -160,14 +160,217 @@ ipa_sudo_highest_usn(TALLOC_CTX *mem_ctx, 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; @@ -180,6 +383,8 @@ struct ipa_sudo_fetch_state { 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); @@ -191,6 +396,7 @@ 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, @@ -199,6 +405,7 @@ ipa_sudo_fetch_send(TALLOC_CTX *mem_ctx, 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; @@ -214,11 +421,13 @@ ipa_sudo_fetch_send(TALLOC_CTX *mem_ctx, 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; @@ -234,7 +443,15 @@ ipa_sudo_fetch_send(TALLOC_CTX *mem_ctx, goto immediately; } - ret = ipa_sudo_fetch_rules(req); + 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; } @@ -253,6 +470,87 @@ immediately: } 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; @@ -543,6 +841,7 @@ struct ipa_sudo_refresh_state { 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; @@ -563,6 +862,7 @@ 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) { @@ -592,6 +892,12 @@ ipa_sudo_refresh_send(TALLOC_CTX *mem_ctx, 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; @@ -716,13 +1022,13 @@ ipa_sudo_refresh_host_done(struct tevent_req *subreq) return; } - subreq = ipa_sudo_fetch_send(state, state->ev, state->sysdb, + 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->search_filter); + state->cmdgroups_filter, state->search_filter); if (subreq == NULL) { state->dp_error = DP_ERR_FATAL; tevent_req_error(req, ENOMEM); diff --git a/src/providers/ipa/ipa_sudo_refresh.c b/src/providers/ipa/ipa_sudo_refresh.c index f1b99c0de96dd2226eb3181ce44e54c019139c6e..bdde4a0026f224898a4987476f49122ea92a6052 100644 --- a/src/providers/ipa/ipa_sudo_refresh.c +++ b/src/providers/ipa/ipa_sudo_refresh.c @@ -69,7 +69,8 @@ ipa_sudo_full_refresh_send(TALLOC_CTX *mem_ctx, DEBUG(SSSDBG_TRACE_FUNC, "Issuing a full refresh of sudo rules\n"); - subreq = ipa_sudo_refresh_send(state, ev, sudo_ctx, NULL, delete_filter); + subreq = ipa_sudo_refresh_send(state, ev, sudo_ctx, + NULL, NULL, delete_filter); if (subreq == NULL) { ret = ENOMEM; goto immediately; @@ -141,6 +142,129 @@ ipa_sudo_full_refresh_recv(struct tevent_req *req, return EOK; } +struct ipa_sudo_smart_refresh_state { + int dp_error; +}; + +static void ipa_sudo_smart_refresh_done(struct tevent_req *subreq); + +static struct tevent_req * +ipa_sudo_smart_refresh_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ipa_sudo_ctx *sudo_ctx) +{ + struct sdap_server_opts *srv_opts = sudo_ctx->id_ctx->srv_opts; + struct ipa_sudo_smart_refresh_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + char *cmdgroups_filter; + char *search_filter; + const char *usn; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct ipa_sudo_smart_refresh_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + if (!sudo_ctx->full_refresh_done + || srv_opts == NULL || srv_opts->max_sudo_value == NULL) { + /* Perform full refresh first */ + DEBUG(SSSDBG_TRACE_FUNC, "USN value is unknown, " + "waiting for full refresh!\n"); + ret = EINVAL; + goto immediately; + } + + /* Download all rules from LDAP that are newer than usn */ + usn = srv_opts->max_sudo_value; + + cmdgroups_filter = talloc_asprintf(state, + "(&(%s>=%s)(!(%s=%s)))", + sudo_ctx->sudocmdgroup_map[IPA_AT_SUDOCMDGROUP_ENTRYUSN].name, usn, + sudo_ctx->sudocmdgroup_map[IPA_AT_SUDOCMDGROUP_ENTRYUSN].name, usn); + if (cmdgroups_filter == NULL) { + ret = ENOMEM; + goto immediately; + } + + search_filter = talloc_asprintf(state, + "(&(%s>=%s)(!(%s=%s)))", + sudo_ctx->sudorule_map[IPA_AT_SUDORULE_ENTRYUSN].name, usn, + sudo_ctx->sudorule_map[IPA_AT_SUDORULE_ENTRYUSN].name, usn); + if (search_filter == NULL) { + ret = ENOMEM; + goto immediately; + } + + /* Do not remove any rules that are already in the sysdb. */ + + DEBUG(SSSDBG_TRACE_FUNC, "Issuing a smart refresh of sudo rules " + "(USN > %s)\n", usn); + + subreq = ipa_sudo_refresh_send(state, ev, sudo_ctx, cmdgroups_filter, + search_filter, NULL); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } + + tevent_req_set_callback(subreq, ipa_sudo_smart_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_smart_refresh_done(struct tevent_req *subreq) +{ + struct tevent_req *req = NULL; + struct ipa_sudo_smart_refresh_state *state = NULL; + int ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_sudo_smart_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; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Successful smart refresh of sudo rules\n"); + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +int ipa_sudo_smart_refresh_recv(struct tevent_req *req, + int *dp_error) +{ + struct ipa_sudo_smart_refresh_state *state = NULL; + state = tevent_req_data(req, struct ipa_sudo_smart_refresh_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *dp_error = state->dp_error; + + return EOK; +} + struct ipa_sudo_rules_refresh_state { size_t num_rules; int dp_error; @@ -230,7 +354,7 @@ ipa_sudo_rules_refresh_send(TALLOC_CTX *mem_ctx, goto immediately; } - subreq = ipa_sudo_refresh_send(req, ev, sudo_ctx, search_filter, + subreq = ipa_sudo_refresh_send(req, ev, sudo_ctx, NULL, search_filter, delete_filter); if (subreq == NULL) { ret = ENOMEM; @@ -327,7 +451,7 @@ ipa_sudo_ptask_smart_refresh_send(TALLOC_CTX *mem_ctx, 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); + return ipa_sudo_smart_refresh_send(mem_ctx, be_ctx->ev, sudo_ctx); } static errno_t @@ -335,7 +459,7 @@ ipa_sudo_ptask_smart_refresh_recv(struct tevent_req *req) { int dp_error; - return ipa_sudo_full_refresh_recv(req, &dp_error); + return ipa_sudo_smart_refresh_recv(req, &dp_error); } errno_t -- 2.5.0