sssd/0031-IPA-SUDO-Implement-smart-refresh.patch
2016-01-19 18:23:34 +01:00

611 lines
19 KiB
Diff

From 5edf5c55bb259ac29454493d06097c5fab8a2199 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Fri, 18 Dec 2015 13:05:41 +0100
Subject: [PATCH 31/49] IPA SUDO: Implement smart refresh
Reviewed-by: Sumit Bose <sbose@redhat.com>
(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