sssd/0027-IPA-SUDO-Implement-full-refresh.patch

2373 lines
68 KiB
Diff
Raw Normal View History

From b55cdd3a298b5edd5ddc26beebfa6379843ebe21 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Fri, 11 Dec 2015 15:00:40 +0100
Subject: [PATCH 27/49] IPA SUDO: Implement full refresh
Reviewed-by: Sumit Bose <sbose@redhat.com>
(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 <pbrezina@redhat.com>
+
+ Copyright (C) 2015 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <tevent.h>
+#include <dhash.h>
+
+#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 <pbrezina@redhat.com>
+
+ Copyright (C) 2015 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <ldb.h>
+#include <talloc.h>
+#include <dhash.h>
+
+#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 <pbrezina@redhat.com>
+
+ Copyright (C) 2015 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <errno.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#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