summaryrefslogtreecommitdiffstats
path: root/src/responder/sudo
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/responder/sudo/sudosrv.c223
-rw-r--r--src/responder/sudo/sudosrv_cmd.c303
-rw-r--r--src/responder/sudo/sudosrv_dp.c265
-rw-r--r--src/responder/sudo/sudosrv_get_sudorules.c887
-rw-r--r--src/responder/sudo/sudosrv_private.h112
-rw-r--r--src/responder/sudo/sudosrv_query.c307
6 files changed, 2097 insertions, 0 deletions
diff --git a/src/responder/sudo/sudosrv.c b/src/responder/sudo/sudosrv.c
new file mode 100644
index 0000000..8568e6e
--- /dev/null
+++ b/src/responder/sudo/sudosrv.c
@@ -0,0 +1,223 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2011 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 <popt.h>
+
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "responder/common/responder.h"
+#include "responder/sudo/sudosrv_private.h"
+#include "providers/data_provider.h"
+#include "responder/common/negcache.h"
+#include "sss_iface/sss_iface_async.h"
+
+int sudo_process_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb,
+ int pipe_fd)
+{
+ struct resp_ctx *rctx;
+ struct sss_cmd_table *sudo_cmds;
+ struct sudo_ctx *sudo_ctx;
+ int ret;
+
+ sudo_cmds = get_sudo_cmds();
+ ret = sss_process_init(mem_ctx, ev, cdb,
+ sudo_cmds,
+ SSS_SUDO_SOCKET_NAME, pipe_fd, /* custom permissions on socket */
+ NULL, -1, /* No private socket */
+ CONFDB_SUDO_CONF_ENTRY,
+ SSS_BUS_SUDO, SSS_SUDO_SBUS_SERVICE_NAME,
+ sss_connection_setup,
+ &rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "sss_process_init() failed\n");
+ return ret;
+ }
+
+ sudo_ctx = talloc_zero(rctx, struct sudo_ctx);
+ if (!sudo_ctx) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing sudo_ctx\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ sudo_ctx->rctx = rctx;
+ sudo_ctx->rctx->pvt_ctx = sudo_ctx;
+
+ sss_ncache_prepopulate(sudo_ctx->rctx->ncache, sudo_ctx->rctx->cdb, rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "failed to set ncache for sudo's filter_users\n");
+ goto fail;
+ }
+
+ /* Get sudo_timed option */
+ ret = confdb_get_bool(sudo_ctx->rctx->cdb,
+ CONFDB_SUDO_CONF_ENTRY, CONFDB_SUDO_TIMED,
+ CONFDB_DEFAULT_SUDO_TIMED,
+ &sudo_ctx->timed);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ /* Get sudo_inverse_order option */
+ ret = confdb_get_bool(sudo_ctx->rctx->cdb,
+ CONFDB_SUDO_CONF_ENTRY, CONFDB_SUDO_INVERSE_ORDER,
+ CONFDB_DEFAULT_SUDO_INVERSE_ORDER,
+ &sudo_ctx->inverse_order);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ /* Get sudo_inverse_order option */
+ ret = confdb_get_int(sudo_ctx->rctx->cdb,
+ CONFDB_SUDO_CONF_ENTRY, CONFDB_SUDO_THRESHOLD,
+ CONFDB_DEFAULT_SUDO_THRESHOLD,
+ &sudo_ctx->threshold);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ ret = schedule_get_domains_task(rctx, rctx->ev, rctx, NULL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "schedule_get_domains_tasks failed.\n");
+ goto fail;
+ }
+
+ /* The responder is initialized. Now tell it to the monitor. */
+ ret = sss_monitor_service_init(rctx, rctx->ev, SSS_BUS_SUDO,
+ SSS_SUDO_SBUS_SERVICE_NAME,
+ SSS_SUDO_SBUS_SERVICE_VERSION,
+ MT_SVC_SERVICE,
+ &rctx->last_request_time, &rctx->mon_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error setting up message bus\n");
+ goto fail;
+ }
+
+ ret = sss_resp_register_service_iface(rctx);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "SUDO Initialization complete\n");
+
+ return EOK;
+
+fail:
+ talloc_free(rctx);
+ return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ poptContext pc;
+ char *opt_logger = NULL;
+ struct main_context *main_ctx;
+ int ret;
+ int pipe_fd = -1;
+ uid_t uid = 0;
+ gid_t gid = 0;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_MAIN_OPTS
+ SSSD_LOGGER_OPTS
+ SSSD_SERVER_OPTS(uid, gid)
+ SSSD_RESPONDER_OPTS
+ POPT_TABLEEND
+ };
+
+ /* Set debug level to invalid value so we can decide if -d 0 was used. */
+ debug_level = SSSDBG_INVALID;
+
+ umask(DFL_RSP_UMASK);
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+ }
+
+ poptFreeContext(pc);
+
+ /* set up things like debug, signals, daemonization, etc. */
+ debug_log_file = "sssd_sudo";
+ DEBUG_INIT(debug_level, opt_logger);
+
+ if (!is_socket_activated()) {
+ /* Create pipe file descriptors here with right ownerschip */
+ ret = create_pipe_fd(SSS_SUDO_SOCKET_NAME, &pipe_fd, SSS_DFL_UMASK);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "create_pipe_fd failed [%d]: %s.\n",
+ ret, sss_strerror(ret));
+ return 4;
+ }
+
+ ret = chown(SSS_SUDO_SOCKET_NAME, uid, 0);
+ if (ret != 0) {
+ ret = errno;
+ close(pipe_fd);
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "create_pipe_fd failed [%d]: %s.\n",
+ ret, sss_strerror(ret));
+ return 5;
+ }
+ }
+
+ ret = server_setup("sudo", true, 0, uid, gid, CONFDB_SUDO_CONF_ENTRY,
+ &main_ctx, true);
+ if (ret != EOK) {
+ return 2;
+ }
+
+ ret = die_if_parent_died();
+ if (ret != EOK) {
+ /* This is not fatal, don't return */
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set up to exit "
+ "when parent process does\n");
+ }
+
+ ret = sudo_process_init(main_ctx,
+ main_ctx->event_ctx,
+ main_ctx->confdb_ctx, pipe_fd);
+ if (ret != EOK) {
+ return 3;
+ }
+
+ /* loop on main */
+ server_loop(main_ctx);
+
+ return 0;
+}
diff --git a/src/responder/sudo/sudosrv_cmd.c b/src/responder/sudo/sudosrv_cmd.c
new file mode 100644
index 0000000..63b548f
--- /dev/null
+++ b/src/responder/sudo/sudosrv_cmd.c
@@ -0,0 +1,303 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2011 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 <stdint.h>
+#include <errno.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "responder/common/responder.h"
+#include "responder/common/responder_packet.h"
+#include "responder/sudo/sudosrv_private.h"
+#include "db/sysdb_sudo.h"
+#include "sss_client/sss_cli.h"
+#include "responder/common/negcache.h"
+
+static errno_t sudosrv_cmd_send_reply(struct sudo_cmd_ctx *cmd_ctx,
+ uint8_t *response_body,
+ size_t response_len)
+{
+ errno_t ret;
+ uint8_t *packet_body = NULL;
+ size_t packet_len = 0;
+ struct cli_ctx *cli_ctx = cmd_ctx->cli_ctx;
+ struct cli_protocol *pctx;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ ret = sss_packet_new(pctx->creq, 0,
+ sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to create a new packet [%d]; %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sss_packet_grow(pctx->creq->out, response_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to create response: %s\n", strerror(ret));
+ goto done;
+ }
+ sss_packet_get_body(pctx->creq->out, &packet_body, &packet_len);
+ memcpy(packet_body, response_body, response_len);
+
+ sss_packet_set_error(pctx->creq->out, EOK);
+ sss_cmd_done(cmd_ctx->cli_ctx, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t sudosrv_cmd_send_error(TALLOC_CTX *mem_ctx,
+ struct sudo_cmd_ctx *cmd_ctx,
+ uint32_t error)
+{
+ uint8_t *response_body = NULL;
+ size_t response_len = 0;
+ int ret = EOK;
+
+ if (error == EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Everything is fine but we are "
+ "returning error?\n");
+ return EFAULT;
+ }
+
+ ret = sudosrv_build_response(mem_ctx, error, 0, NULL,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return sudosrv_cmd_send_reply(cmd_ctx, response_body, response_len);
+}
+
+errno_t sudosrv_cmd_reply(struct sudo_cmd_ctx *cmd_ctx, int ret)
+{
+ uint8_t *response_body = NULL;
+ size_t response_len = 0;
+ uint32_t num_rules = cmd_ctx->num_rules;
+ struct sysdb_attrs **rules = cmd_ctx->rules;
+
+ switch (ret) {
+ case EOK:
+ /*
+ * Parent of cmd_ctx->rules is in-memory cache, we must not talloc_free it!
+ */
+ if (cmd_ctx->sudo_ctx->timed) {
+ /* filter rules by time */
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Applying time restrictions on"
+ "%u rules\n", cmd_ctx->num_rules);
+
+ ret = sysdb_sudo_filter_rules_by_time(cmd_ctx, cmd_ctx->num_rules,
+ cmd_ctx->rules, 0,
+ &num_rules, &rules);
+ if (ret != EOK) {
+ return EFAULT;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Got %u rules after time filter\n",
+ num_rules);
+ }
+
+ /* send result */
+ ret = sudosrv_build_response(cmd_ctx, SSS_SUDO_ERROR_OK,
+ num_rules, rules,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ return EFAULT;
+ }
+
+ ret = sudosrv_cmd_send_reply(cmd_ctx, response_body, response_len);
+ break;
+
+ case EAGAIN:
+ /* async processing, just return here */
+ return EOK;
+
+ case EFAULT:
+ /* very bad error */
+ return EFAULT;
+
+
+ /* case ENOENT:
+ * - means user not found
+ * - send error ENOENT
+ */
+
+ default:
+ /* send error */
+ ret = sudosrv_cmd_send_error(cmd_ctx, cmd_ctx, ret);
+ break;
+ }
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Fatal error, killing connection!\n");
+ talloc_free(cmd_ctx->cli_ctx);
+ return EFAULT;
+ }
+
+ return EOK;
+}
+
+static void sudosrv_cmd_done(struct tevent_req *req);
+
+static int sudosrv_cmd(enum sss_sudo_type type, struct cli_ctx *cli_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct sudo_cmd_ctx *cmd_ctx = NULL;
+ uint8_t *query_body = NULL;
+ size_t query_len = 0;
+ struct cli_protocol *pctx;
+ uint32_t protocol;
+ errno_t ret;
+
+ /* create cmd_ctx */
+
+ cmd_ctx = talloc_zero(cli_ctx, struct sudo_cmd_ctx);
+ if (cmd_ctx == NULL) {
+ /* kill the connection here as we have no context for reply */
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n");
+ return ENOMEM;
+ }
+
+ cmd_ctx->cli_ctx = cli_ctx;
+ cmd_ctx->type = type;
+ cmd_ctx->sudo_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sudo_ctx);
+ if (cmd_ctx->sudo_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "sudo_ctx not set, killing connection!\n");
+ return EFAULT;
+ }
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+ protocol = pctx->cli_protocol_version->version;
+
+ /* if protocol is invalid return */
+ switch (protocol) {
+ case 0:
+ DEBUG(SSSDBG_FATAL_FAILURE, "Protocol [%d] is not secure. "
+ "SSSD does not allow to use this protocol.\n", protocol);
+ ret = EFAULT;
+ goto done;
+ break;
+ case SSS_SUDO_PROTOCOL_VERSION:
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Using protocol version [%d]\n",
+ protocol);
+ break;
+ default:
+ DEBUG(SSSDBG_FATAL_FAILURE, "Invalid protocol version [%d]!\n",
+ protocol);
+ ret = EFAULT;
+ goto done;
+ }
+
+ /* parse query */
+ sss_packet_get_body(pctx->creq->in, &query_body, &query_len);
+ if (query_len <= 0 || query_body == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Query is empty\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sudosrv_parse_query(cmd_ctx, query_body, query_len,
+ &cmd_ctx->rawname, &cmd_ctx->uid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse sudo query [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ req = sudosrv_get_rules_send(cmd_ctx, cli_ctx->ev, cmd_ctx->sudo_ctx,
+ cmd_ctx->type, cmd_ctx->uid,
+ cmd_ctx->rawname);
+ if (req == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(req, sudosrv_cmd_done, cmd_ctx);
+
+ ret = EAGAIN;
+
+done:
+ return sudosrv_cmd_reply(cmd_ctx, ret);
+}
+
+static void sudosrv_cmd_done(struct tevent_req *req)
+{
+ struct sudo_cmd_ctx *cmd_ctx;
+ errno_t ret;
+
+ cmd_ctx = tevent_req_callback_data(req, struct sudo_cmd_ctx);
+
+ ret = sudosrv_get_rules_recv(cmd_ctx, req, &cmd_ctx->rules,
+ &cmd_ctx->num_rules);
+ talloc_zfree(req);
+ if (ret != EOK) {
+ DEBUG((ret == ENOENT) ? SSSDBG_MINOR_FAILURE : SSSDBG_OP_FAILURE,
+ "Unable to obtain cached rules [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+done:
+ sudosrv_cmd_reply(cmd_ctx, ret);
+}
+
+static int sudosrv_cmd_get_sudorules(struct cli_ctx *cli_ctx)
+{
+ return sudosrv_cmd(SSS_SUDO_USER, cli_ctx);
+}
+
+static int sudosrv_cmd_get_defaults(struct cli_ctx *cli_ctx)
+{
+ return sudosrv_cmd(SSS_SUDO_DEFAULTS, cli_ctx);
+}
+
+struct cli_protocol_version *register_cli_protocol_version(void)
+{
+ static struct cli_protocol_version sudo_cli_protocol_version[] = {
+ {1, "2012-05-14", "require uid and domain"},
+ {0, NULL, NULL}
+ };
+
+ return sudo_cli_protocol_version;
+}
+
+struct sss_cmd_table *get_sudo_cmds(void) {
+ static struct sss_cmd_table sudo_cmds[] = {
+ {SSS_GET_VERSION, sss_cmd_get_version},
+ {SSS_SUDO_GET_SUDORULES, sudosrv_cmd_get_sudorules},
+ {SSS_SUDO_GET_DEFAULTS, sudosrv_cmd_get_defaults},
+ {SSS_CLI_NULL, NULL}
+ };
+
+ return sudo_cmds;
+}
diff --git a/src/responder/sudo/sudosrv_dp.c b/src/responder/sudo/sudosrv_dp.c
new file mode 100644
index 0000000..465c0f2
--- /dev/null
+++ b/src/responder/sudo/sudosrv_dp.c
@@ -0,0 +1,265 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2011 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 <dbus/dbus.h>
+
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "providers/data_provider_req.h"
+#include "responder/common/responder.h"
+#include "responder/sudo/sudosrv_private.h"
+#include "db/sysdb.h"
+#include "sss_iface/sss_iface_async.h"
+
+static DBusMessage *
+sss_dp_get_sudoers_msg(TALLOC_CTX *mem_ctx,
+ const char *bus_name,
+ struct sss_domain_info *dom,
+ bool fast_reply,
+ enum sss_dp_sudo_type type,
+ const char *name,
+ uint32_t num_rules,
+ struct sysdb_attrs **rules)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ dbus_bool_t dbret;
+ errno_t ret;
+ uint32_t be_type = 0;
+ uint32_t dp_flags = 0;
+ const char *rule_name = NULL;
+ uint32_t i;
+
+ switch (type) {
+ case SSS_DP_SUDO_REFRESH_RULES:
+ be_type = BE_REQ_SUDO_RULES;
+ break;
+ case SSS_DP_SUDO_FULL_REFRESH:
+ be_type = BE_REQ_SUDO_FULL;
+ break;
+ }
+
+ if (fast_reply) {
+ dp_flags |= DP_FAST_REPLY;
+ }
+
+ msg = dbus_message_new_method_call(bus_name,
+ SSS_BUS_PATH,
+ "sssd.dataprovider",
+ "sudoHandler");
+ if (msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n");
+ return NULL;
+ }
+
+ /* create the message */
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Creating SUDOers request for [%s][%u][%s][%u]\n",
+ dom->name, be_type, name, num_rules);
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ dbret = dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &dp_flags);
+ if (dbret == FALSE) {
+ goto fail;
+ }
+
+ /* BE TYPE */
+ dbret = dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &be_type);
+ if (dbret == FALSE) {
+ goto fail;
+ }
+
+ /* BE TYPE SPECIFIC */
+ if (be_type & BE_REQ_SUDO_RULES) {
+ dbret = dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32,
+ &num_rules);
+ if (dbret == FALSE) {
+ goto fail;
+ }
+
+ dbret = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING,
+ &array_iter);
+ if (dbret == FALSE) {
+ goto fail;
+ }
+
+ for (i = 0; i < num_rules; i++) {
+ ret = sysdb_attrs_get_string(rules[i], SYSDB_NAME, &rule_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not get rule name [%d]: %s\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ dbret = dbus_message_iter_append_basic(&array_iter,
+ DBUS_TYPE_STRING,
+ &rule_name);
+ if (dbret == FALSE) {
+ goto fail;
+ }
+ }
+
+ dbret = dbus_message_iter_close_container(&iter, &array_iter);
+ if (dbret == FALSE) {
+ goto fail;
+ }
+ }
+
+ return msg;
+
+fail:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build message\n");
+ dbus_message_unref(msg);
+ return NULL;
+}
+
+struct sss_dp_get_sudoers_state {
+ uint16_t dp_error;
+ uint32_t error;
+ const char *error_message;
+};
+
+static void sss_dp_get_sudoers_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sss_dp_get_sudoers_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *dom,
+ bool fast_reply,
+ enum sss_dp_sudo_type type,
+ const char *name,
+ uint32_t num_rules,
+ struct sysdb_attrs **rules)
+{
+ struct sss_dp_get_sudoers_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ struct be_conn *be_conn;
+ DBusMessage *msg;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sss_dp_get_sudoers_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ if (is_files_provider(dom)) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Domain %s does not check DP\n",
+ dom->name);
+ state->dp_error = DP_ERR_OK;
+ state->error = EOK;
+ state->error_message = talloc_strdup(state, "Success");
+ if (state->error_message == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = EOK;
+ goto done;
+ }
+
+ ret = sss_dp_get_domain_conn(rctx, dom->conn_name, &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "BUG: The Data Provider connection for %s is not available!\n",
+ dom->name);
+ ret = EIO;
+ goto done;
+ }
+
+ msg = sss_dp_get_sudoers_msg(state, be_conn->bus_name, dom, fast_reply,
+ type, name, num_rules, rules);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ subreq = sbus_call_dp_dp_sudoHandler_send(state, be_conn->conn, msg);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sss_dp_get_sudoers_done, req);
+
+ ret = EAGAIN;
+
+done:
+#ifdef BUILD_FILES_PROVIDER
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, rctx->ev);
+ } else
+#endif
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, rctx->ev);
+ }
+
+ return req;
+}
+
+static void sss_dp_get_sudoers_done(struct tevent_req *subreq)
+{
+ struct sss_dp_get_sudoers_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sss_dp_get_sudoers_state);
+
+ ret = sbus_call_dp_dp_sudoHandler_recv(state, subreq, &state->dp_error,
+ &state->error,
+ &state->error_message);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+sss_dp_get_sudoers_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ uint16_t *_dp_error,
+ uint32_t *_error,
+ const char ** _error_message)
+{
+ struct sss_dp_get_sudoers_state *state;
+ state = tevent_req_data(req, struct sss_dp_get_sudoers_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_dp_error = state->dp_error;
+ *_error = state->error;
+ *_error_message = talloc_steal(mem_ctx, state->error_message);
+
+ return EOK;
+}
diff --git a/src/responder/sudo/sudosrv_get_sudorules.c b/src/responder/sudo/sudosrv_get_sudorules.c
new file mode 100644
index 0000000..1bcfc37
--- /dev/null
+++ b/src/responder/sudo/sudosrv_get_sudorules.c
@@ -0,0 +1,887 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2011 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 "config.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "util/util.h"
+#include "db/sysdb_sudo.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/sudo/sudosrv_private.h"
+#include "providers/data_provider.h"
+
+static int
+sudo_order_cmp(const void *a, const void *b, bool lower_wins)
+{
+ struct sysdb_attrs *r1, *r2;
+ uint32_t o1, o2;
+ int ret;
+
+ r1 = * (struct sysdb_attrs * const *) a;
+ r2 = * (struct sysdb_attrs * const *) b;
+ if (!r1 || !r2) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Wrong data?\n");
+ return 0;
+ }
+
+ ret = sysdb_attrs_get_uint32_t(r1, SYSDB_SUDO_CACHE_AT_ORDER, &o1);
+ if (ret == ENOENT) {
+ /* man sudoers-ldap: If the sudoOrder attribute is not present,
+ * a value of 0 is assumed */
+ o1 = 0;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot get sudoOrder value\n");
+ return 0;
+ }
+
+ ret = sysdb_attrs_get_uint32_t(r2, SYSDB_SUDO_CACHE_AT_ORDER, &o2);
+ if (ret == ENOENT) {
+ /* man sudoers-ldap: If the sudoOrder attribute is not present,
+ * a value of 0 is assumed */
+ o2 = 0;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot get sudoOrder value\n");
+ return 0;
+ }
+
+ if (lower_wins) {
+ /* The lowest value takes priority. Original wrong SSSD behaviour. */
+ if (o1 > o2) {
+ return 1;
+ } else if (o1 < o2) {
+ return -1;
+ }
+ } else {
+ /* The higher value takes priority. Standard LDAP behaviour. */
+ if (o1 < o2) {
+ return 1;
+ } else if (o1 > o2) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+sudo_order_low_cmp_fn(const void *a, const void *b)
+{
+ return sudo_order_cmp(a, b, true);
+}
+
+static int
+sudo_order_high_cmp_fn(const void *a, const void *b)
+{
+ return sudo_order_cmp(a, b, false);
+}
+
+static errno_t
+sort_sudo_rules(struct sysdb_attrs **rules, size_t count, bool lower_wins)
+{
+ if (lower_wins) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Sorting rules with lower-wins logic\n");
+ qsort(rules, count, sizeof(struct sysdb_attrs *),
+ sudo_order_low_cmp_fn);
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC, "Sorting rules with higher-wins logic\n");
+ qsort(rules, count, sizeof(struct sysdb_attrs *),
+ sudo_order_high_cmp_fn);
+ }
+
+ return EOK;
+}
+
+static errno_t sudosrv_format_runas(struct resp_ctx *rctx,
+ struct sysdb_attrs *rule,
+ const char *attr)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message_element *el;
+ struct sss_domain_info *dom;
+ const char *value;
+ char *fqname;
+ unsigned int i;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_attrs_get_el_ext(rule, attr, false, &el);
+ if (ret == ENOENT) {
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get %s attribute "
+ "[%d]: %s\n", attr, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ value = (const char *)el->values[i].data;
+ if (value == NULL) {
+ continue;
+ }
+
+ dom = find_domain_by_object_name_ex(rctx->domains, value, true,
+ SSS_GND_DESCEND);
+ if (dom == NULL) {
+ continue;
+ }
+
+ ret = sss_output_fqname(tmp_ctx, dom, value,
+ rctx->override_space, &fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to convert %s to output fqname "
+ "[%d]: %s\n", value, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ talloc_free(el->values[i].data);
+ el->values[i].data = (uint8_t*)talloc_steal(el->values, fqname);
+ el->values[i].length = strlen(fqname);
+ }
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t sudosrv_format_rules(struct resp_ctx *rctx,
+ struct sysdb_attrs **rules,
+ uint32_t num_rules)
+{
+ uint32_t i;
+ errno_t ret = EOK;
+
+
+ for (i = 0; i < num_rules; i++) {
+ ret = sudosrv_format_runas(rctx, rules[i],
+ SYSDB_SUDO_CACHE_AT_RUNAS);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sudosrv_format_runas(rctx, rules[i],
+ SYSDB_SUDO_CACHE_AT_RUNASUSER);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sudosrv_format_runas(rctx, rules[i],
+ SYSDB_SUDO_CACHE_AT_RUNASGROUP);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static errno_t sudosrv_query_cache(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char **attrs,
+ const char *filter,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_count)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ size_t count;
+ struct sysdb_attrs **rules;
+ struct ldb_message **msgs;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ DEBUG(SSSDBG_FUNC_DATA, "Searching sysdb with [%s]\n", filter);
+
+ if (IS_SUBDOMAIN(domain)) {
+ /* rules are stored inside parent domain tree */
+ domain = domain->parent;
+ }
+
+ ret = sysdb_search_custom(tmp_ctx, domain, filter, SUDORULE_SUBDIR,
+ attrs, &count, &msgs);
+ if (ret == ENOENT) {
+ *_rules = NULL;
+ *_count = 0;
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up SUDO rules\n");
+ goto done;
+ }
+
+ ret = sysdb_msg2attrs(tmp_ctx, count, msgs, &rules);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not convert ldb message to sysdb_attrs\n");
+ goto done;
+ }
+
+ *_rules = talloc_steal(mem_ctx, rules);
+ *_count = (uint32_t)count;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sudosrv_expired_rules(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t uid,
+ const char *username,
+ char **groups,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules)
+{
+ const char *attrs[] = { SYSDB_NAME, NULL };
+ char *filter;
+ errno_t ret;
+
+ filter = sysdb_sudo_filter_expired(NULL, username, groups, uid);
+ if (filter == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sudosrv_query_cache(mem_ctx, domain, attrs, filter,
+ _rules, _num_rules);
+ talloc_free(filter);
+
+ return ret;
+}
+
+static errno_t sudosrv_cached_rules_by_user(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t cli_uid,
+ uid_t orig_uid,
+ const char *username,
+ char **groupnames,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_attrs **rules;
+ uint32_t num_rules;
+ uint32_t i;
+ const char *filter;
+ const char *val;
+ errno_t ret;
+ const char *attrs[] = { SYSDB_OBJECTCLASS,
+ SYSDB_SUDO_CACHE_AT_CN,
+ SYSDB_SUDO_CACHE_AT_HOST,
+ SYSDB_SUDO_CACHE_AT_COMMAND,
+ SYSDB_SUDO_CACHE_AT_OPTION,
+ SYSDB_SUDO_CACHE_AT_RUNAS,
+ SYSDB_SUDO_CACHE_AT_RUNASUSER,
+ SYSDB_SUDO_CACHE_AT_RUNASGROUP,
+ SYSDB_SUDO_CACHE_AT_NOTBEFORE,
+ SYSDB_SUDO_CACHE_AT_NOTAFTER,
+ SYSDB_SUDO_CACHE_AT_ORDER,
+ NULL };
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ filter = sysdb_sudo_filter_user(tmp_ctx, username, groupnames, orig_uid);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sudosrv_query_cache(tmp_ctx, domain, attrs, filter,
+ &rules, &num_rules);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ val = talloc_asprintf(tmp_ctx, "#%"SPRIuid, cli_uid);
+ if (val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Add sudoUser: #uid to prevent conflicts with fqnames. */
+ DEBUG(SSSDBG_TRACE_FUNC, "Replacing sudoUser attribute with "
+ "sudoUser: %s\n", val);
+ for (i = 0; i < num_rules; i++) {
+ ret = sysdb_attrs_add_string(rules[i], SYSDB_SUDO_CACHE_AT_USER, val);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to alter sudoUser attribute "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ }
+ }
+
+ *_rules = talloc_steal(mem_ctx, rules);
+ *_num_rules = num_rules;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sudosrv_cached_rules_by_ng(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t uid,
+ const char *username,
+ char **groupnames,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules)
+{
+ char *filter;
+ errno_t ret;
+ const char *attrs[] = { SYSDB_OBJECTCLASS,
+ SYSDB_SUDO_CACHE_AT_CN,
+ SYSDB_SUDO_CACHE_AT_USER,
+ SYSDB_SUDO_CACHE_AT_HOST,
+ SYSDB_SUDO_CACHE_AT_COMMAND,
+ SYSDB_SUDO_CACHE_AT_OPTION,
+ SYSDB_SUDO_CACHE_AT_RUNAS,
+ SYSDB_SUDO_CACHE_AT_RUNASUSER,
+ SYSDB_SUDO_CACHE_AT_RUNASGROUP,
+ SYSDB_SUDO_CACHE_AT_NOTBEFORE,
+ SYSDB_SUDO_CACHE_AT_NOTAFTER,
+ SYSDB_SUDO_CACHE_AT_ORDER,
+ NULL };
+
+ filter = sysdb_sudo_filter_netgroups(NULL, username, groupnames, uid);
+ if (filter == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sudosrv_query_cache(mem_ctx, domain, attrs, filter,
+ _rules, _num_rules);
+ talloc_free(filter);
+
+ return ret;
+}
+
+static errno_t sudosrv_cached_rules(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ uid_t cli_uid,
+ uid_t orig_uid,
+ const char *username,
+ char **groups,
+ bool inverse_order,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_attrs **user_rules;
+ struct sysdb_attrs **ng_rules;
+ struct sysdb_attrs **rules;
+ uint32_t num_user_rules;
+ uint32_t num_ng_rules;
+ uint32_t num_rules;
+ uint32_t rule_iter, i;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sudosrv_cached_rules_by_user(tmp_ctx, domain,
+ cli_uid, orig_uid, username, groups,
+ &user_rules, &num_user_rules);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sudosrv_cached_rules_by_ng(tmp_ctx, domain,
+ orig_uid, username, groups,
+ &ng_rules, &num_ng_rules);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ num_rules = num_user_rules + num_ng_rules;
+ if (num_rules == 0) {
+ *_rules = NULL;
+ *_num_rules = 0;
+ ret = EOK;
+ goto done;
+ }
+
+ rules = talloc_array(tmp_ctx, struct sysdb_attrs *, num_rules);
+ if (rules == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ rule_iter = 0;
+ for (i = 0; i < num_user_rules; rule_iter++, i++) {
+ rules[rule_iter] = talloc_steal(rules, user_rules[i]);
+ }
+
+ for (i = 0; i < num_ng_rules; rule_iter++, i++) {
+ rules[rule_iter] = talloc_steal(rules, ng_rules[i]);
+ }
+
+ ret = sort_sudo_rules(rules, num_rules, inverse_order);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not sort rules by sudoOrder\n");
+ goto done;
+ }
+
+ ret = sudosrv_format_rules(rctx, rules, num_rules);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not format sudo rules\n");
+ goto done;
+ }
+
+ *_rules = talloc_steal(mem_ctx, rules);
+ *_num_rules = num_rules;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sudosrv_cached_defaults(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules)
+{
+ char *filter;
+ errno_t ret;
+ const char *attrs[] = { SYSDB_OBJECTCLASS,
+ SYSDB_SUDO_CACHE_AT_CN,
+ SYSDB_SUDO_CACHE_AT_USER,
+ SYSDB_SUDO_CACHE_AT_HOST,
+ SYSDB_SUDO_CACHE_AT_COMMAND,
+ SYSDB_SUDO_CACHE_AT_OPTION,
+ SYSDB_SUDO_CACHE_AT_RUNAS,
+ SYSDB_SUDO_CACHE_AT_RUNASUSER,
+ SYSDB_SUDO_CACHE_AT_RUNASGROUP,
+ SYSDB_SUDO_CACHE_AT_NOTBEFORE,
+ SYSDB_SUDO_CACHE_AT_NOTAFTER,
+ SYSDB_SUDO_CACHE_AT_ORDER,
+ NULL };
+
+ filter = sysdb_sudo_filter_defaults(NULL);
+ if (filter == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sudosrv_query_cache(mem_ctx, domain, attrs, filter,
+ _rules, _num_rules);
+ talloc_free(filter);
+
+ return ret;
+}
+
+static errno_t sudosrv_fetch_rules(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ enum sss_sudo_type type,
+ struct sss_domain_info *domain,
+ uid_t cli_uid,
+ uid_t orig_uid,
+ const char *username,
+ char **groups,
+ bool inverse_order,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules)
+{
+ struct sysdb_attrs **rules = NULL;
+ const char *debug_name = "unknown";
+ uint32_t num_rules;
+ errno_t ret;
+
+ switch (type) {
+ case SSS_SUDO_USER:
+ DEBUG(SSSDBG_TRACE_FUNC, "Retrieving rules for [%s@%s]\n",
+ username, domain->name);
+ debug_name = "rules";
+
+ ret = sudosrv_cached_rules(mem_ctx, rctx, domain,
+ cli_uid, orig_uid, username, groups,
+ inverse_order, &rules, &num_rules);
+
+ break;
+ case SSS_SUDO_DEFAULTS:
+ debug_name = "default options";
+ DEBUG(SSSDBG_TRACE_FUNC, "Retrieving default options for [%s@%s]\n",
+ username, domain->name);
+
+ ret = sudosrv_cached_defaults(mem_ctx, domain, &rules, &num_rules);
+
+ break;
+ default:
+ ret = EINVAL;
+ }
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve %s [%d]: %s\n",
+ debug_name, ret, sss_strerror(ret));
+ return ret;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Returning %u %s for [%s@%s]\n",
+ num_rules, debug_name, username, domain->name);
+
+ *_rules = rules;
+ *_num_rules = num_rules;
+
+ return EOK;
+}
+
+static void
+sudosrv_dp_oob_req_done(struct tevent_req *req)
+{
+ DEBUG(SSSDBG_TRACE_FUNC, "Out of band refresh finished\n");
+ talloc_free(req);
+}
+
+struct sudosrv_refresh_rules_state {
+ struct resp_ctx *rctx;
+ struct sss_domain_info *domain;
+ const char *username;
+};
+
+static void sudosrv_refresh_rules_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+sudosrv_refresh_rules_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ int threshold,
+ uid_t uid,
+ const char *username,
+ char **groups)
+{
+ struct sudosrv_refresh_rules_state *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct sysdb_attrs **rules;
+ uint32_t num_rules;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct sudosrv_refresh_rules_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+
+ state->rctx = rctx;
+ state->domain = domain;
+ state->username = username;
+
+ ret = sudosrv_expired_rules(state, domain, uid, username, groups,
+ &rules, &num_rules);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to retrieve expired sudo rules [%d]: %s\n",
+ ret, strerror(ret));
+ goto immediately;
+ }
+
+ if (num_rules == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No expired rules were found for [%s@%s].\n",
+ username, domain->name);
+ ret = EOK;
+ goto immediately;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Refreshing %d expired rules of [%s@%s]\n",
+ num_rules, username, domain->name);
+
+ if (num_rules > threshold) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Rules threshold [%d] is reached, performing full refresh "
+ "instead.\n", threshold);
+
+ subreq = sss_dp_get_sudoers_send(state, rctx, domain, false,
+ SSS_DP_SUDO_FULL_REFRESH,
+ username, 0, NULL);
+ } else {
+ subreq = sss_dp_get_sudoers_send(state, rctx, domain, false,
+ SSS_DP_SUDO_REFRESH_RULES,
+ username, num_rules, rules);
+ }
+
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediately;
+ }
+
+ tevent_req_set_callback(subreq, sudosrv_refresh_rules_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 sudosrv_refresh_rules_done(struct tevent_req *subreq)
+{
+ struct sudosrv_refresh_rules_state *state;
+ struct tevent_req *req;
+ dbus_uint16_t err_maj;
+ dbus_uint32_t err_min;
+ const char *err_msg;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sudosrv_refresh_rules_state);
+
+ ret = sss_dp_get_sudoers_recv(state, subreq, &err_maj, &err_min, &err_msg);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to refresh rules [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ } else if (err_maj != 0 || err_min != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to get information from Data Provider, "
+ "Error: %u, %u, %s\n",
+ (unsigned int)err_maj, (unsigned int)err_min,
+ (err_msg == NULL ? "(null)" : err_msg));
+ goto done;
+ }
+
+ if (err_min == ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Some expired rules were removed from the server, scheduling "
+ "full refresh out of band\n");
+ subreq = sss_dp_get_sudoers_send(state->rctx, state->rctx,
+ state->domain, false,
+ SSS_DP_SUDO_FULL_REFRESH,
+ state->username, 0, NULL);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot issue DP request.\n");
+ ret = EOK; /* We don't care. */
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sudosrv_dp_oob_req_done, NULL);
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static errno_t sudosrv_refresh_rules_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+struct sudosrv_get_rules_state {
+ struct tevent_context *ev;
+ struct resp_ctx *rctx;
+ enum sss_sudo_type type;
+ uid_t cli_uid;
+ const char *username;
+ struct sss_domain_info *domain;
+ char **groups;
+ bool inverse_order;
+ int threshold;
+
+ uid_t orig_uid;
+ const char *orig_username;
+
+ struct sysdb_attrs **rules;
+ uint32_t num_rules;
+};
+
+static void sudosrv_get_rules_initgr_done(struct tevent_req *subreq);
+static void sudosrv_get_rules_done(struct tevent_req *subreq);
+
+struct tevent_req *sudosrv_get_rules_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sudo_ctx *sudo_ctx,
+ enum sss_sudo_type type,
+ uid_t cli_uid,
+ const char *username)
+{
+ struct sudosrv_get_rules_state *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sudosrv_get_rules_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->rctx = sudo_ctx->rctx;
+ state->type = type;
+ state->cli_uid = cli_uid;
+ state->inverse_order = sudo_ctx->inverse_order;
+ state->threshold = sudo_ctx->threshold;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Running initgroups for [%s]\n", username);
+
+ subreq = cache_req_initgr_by_name_send(state, ev, sudo_ctx->rctx,
+ sudo_ctx->rctx->ncache, 0,
+ CACHE_REQ_POSIX_DOM, NULL,
+ username);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ } else {
+ tevent_req_set_callback(subreq, sudosrv_get_rules_initgr_done, req);
+ }
+
+ return req;
+}
+
+static void sudosrv_get_rules_initgr_done(struct tevent_req *subreq)
+{
+ struct sudosrv_get_rules_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sudosrv_get_rules_state);
+
+ ret = cache_req_initgr_by_name_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ state->domain = result->domain;
+ state->username = talloc_steal(state, result->lookup_name);
+ talloc_zfree(result);
+
+ ret = sysdb_get_sudo_user_info(state, state->domain, state->username,
+ &state->orig_username,
+ &state->orig_uid,
+ &state->groups);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to obtain user groups [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ subreq = sudosrv_refresh_rules_send(state, state->ev, state->rctx,
+ state->domain, state->threshold,
+ state->orig_uid,
+ state->orig_username,
+ state->groups);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sudosrv_get_rules_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EOK && ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ return;
+ } else if (ret != EAGAIN) {
+ tevent_req_done(req);
+ }
+}
+
+static void sudosrv_get_rules_done(struct tevent_req *subreq)
+{
+ struct sudosrv_get_rules_state *state = NULL;
+ struct tevent_req *req = NULL;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sudosrv_get_rules_state);
+
+ ret = sudosrv_refresh_rules_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to refresh expired rules, we will return what is "
+ "in cache.\n");
+ }
+
+ ret = sudosrv_fetch_rules(state, state->rctx, state->type, state->domain,
+ state->cli_uid,
+ state->orig_uid,
+ state->orig_username,
+ state->groups,
+ state->inverse_order,
+ &state->rules, &state->num_rules);
+
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t sudosrv_get_rules_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules)
+{
+ struct sudosrv_get_rules_state *state = NULL;
+ state = tevent_req_data(req, struct sudosrv_get_rules_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_rules = talloc_steal(mem_ctx, state->rules);
+ *_num_rules = state->num_rules;
+
+ return EOK;
+}
diff --git a/src/responder/sudo/sudosrv_private.h b/src/responder/sudo/sudosrv_private.h
new file mode 100644
index 0000000..157afaa
--- /dev/null
+++ b/src/responder/sudo/sudosrv_private.h
@@ -0,0 +1,112 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2011 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/>.
+*/
+
+#ifndef _SUDOSRV_PRIVATE_H_
+#define _SUDOSRV_PRIVATE_H_
+
+#include <stdint.h>
+#include <talloc.h>
+#include <sys/types.h>
+
+#include "src/db/sysdb.h"
+#include "responder/common/responder.h"
+
+#define SSS_SUDO_ERROR_OK 0
+
+enum sss_dp_sudo_type {
+ SSS_DP_SUDO_REFRESH_RULES,
+ SSS_DP_SUDO_FULL_REFRESH
+};
+
+enum sss_sudo_type {
+ SSS_SUDO_DEFAULTS,
+ SSS_SUDO_USER
+};
+
+struct sudo_ctx {
+ struct resp_ctx *rctx;
+
+ /*
+ * options
+ */
+ bool timed;
+ bool inverse_order;
+ int threshold;
+};
+
+struct sudo_cmd_ctx {
+ struct cli_ctx *cli_ctx;
+ struct sudo_ctx *sudo_ctx;
+ enum sss_sudo_type type;
+
+ /* input data */
+ uid_t uid;
+ char *rawname;
+
+ /* output data */
+ struct sysdb_attrs **rules;
+ uint32_t num_rules;
+};
+
+struct sss_cmd_table *get_sudo_cmds(void);
+
+struct tevent_req *sudosrv_get_rules_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sudo_ctx *sudo_ctx,
+ enum sss_sudo_type type,
+ uid_t cli_uid,
+ const char *username);
+
+errno_t sudosrv_get_rules_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules);
+
+errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx,
+ uint8_t *query_body,
+ size_t query_len,
+ char **_rawname,
+ uid_t *_uid);
+
+errno_t sudosrv_build_response(TALLOC_CTX *mem_ctx,
+ uint32_t error,
+ uint32_t rules_num,
+ struct sysdb_attrs **rules,
+ uint8_t **_response_body,
+ size_t *_response_len);
+
+struct tevent_req *
+sss_dp_get_sudoers_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *dom,
+ bool fast_reply,
+ enum sss_dp_sudo_type type,
+ const char *name,
+ uint32_t num_rules,
+ struct sysdb_attrs **rules);
+
+errno_t
+sss_dp_get_sudoers_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ uint16_t *_dp_error,
+ uint32_t *_error,
+ const char ** _error_message);
+
+#endif /* _SUDOSRV_PRIVATE_H_ */
diff --git a/src/responder/sudo/sudosrv_query.c b/src/responder/sudo/sudosrv_query.c
new file mode 100644
index 0000000..a868ebe
--- /dev/null
+++ b/src/responder/sudo/sudosrv_query.c
@@ -0,0 +1,307 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2011 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 <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "util/util.h"
+#include "responder/sudo/sudosrv_private.h"
+
+static int sudosrv_response_append_string(TALLOC_CTX *mem_ctx,
+ const char *str,
+ size_t str_len,
+ uint8_t **_response_body,
+ size_t *_response_len)
+{
+ size_t response_len = *_response_len;
+ uint8_t *response_body = *_response_body;
+
+ response_body = talloc_realloc(mem_ctx, response_body, uint8_t,
+ response_len + (str_len * sizeof(char)));
+ if (response_body == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_realloc() failed\n");
+ return ENOMEM;
+ }
+ memcpy(response_body + response_len, str, str_len);
+ response_len += str_len;
+
+ *_response_body = response_body;
+ *_response_len = response_len;
+
+ return EOK;
+}
+
+static int sudosrv_response_append_uint32(TALLOC_CTX *mem_ctx,
+ uint32_t number,
+ uint8_t **_response_body,
+ size_t *_response_len)
+{
+ size_t response_len = *_response_len;
+ uint8_t *response_body = *_response_body;
+
+ response_body = talloc_realloc(mem_ctx, response_body, uint8_t,
+ response_len + sizeof(uint32_t));
+ if (response_body == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_realloc() failed\n");
+ return ENOMEM;
+ }
+ SAFEALIGN_SET_UINT32(response_body + response_len, number, &response_len);
+
+ *_response_body = response_body;
+ *_response_len = response_len;
+
+ return EOK;
+}
+
+static int sudosrv_response_append_attr(TALLOC_CTX *mem_ctx,
+ const char *name,
+ unsigned int values_num,
+ struct ldb_val *values,
+ uint8_t **_response_body,
+ size_t *_response_len)
+{
+ uint8_t *response_body = *_response_body;
+ size_t response_len = *_response_len;
+ TALLOC_CTX *tmp_ctx = NULL;
+ unsigned int i = 0;
+ int ret = EOK;
+ const char *strval;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ /* attr name */
+ ret = sudosrv_response_append_string(tmp_ctx, name, strlen(name) + 1,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* values count */
+ ret = sudosrv_response_append_uint32(tmp_ctx, values_num,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* values */
+ for (i = 0; i < values_num; i++) {
+ strval = (const char *) values[i].data;
+
+ if (strlen((strval)) != values[i].length) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "value is not a string\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sudosrv_response_append_string(tmp_ctx,
+ strval,
+ values[i].length + 1,
+ &response_body, &response_len);
+ DEBUG(SSSDBG_TRACE_INTERNAL, "%s:%s\n", name, strval);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ *_response_body = talloc_steal(mem_ctx, response_body);
+ *_response_len = response_len;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static int sudosrv_response_append_rule(TALLOC_CTX *mem_ctx,
+ int attrs_num,
+ struct ldb_message_element *attrs,
+ uint8_t **_response_body,
+ size_t *_response_len)
+{
+ uint8_t *response_body = *_response_body;
+ size_t response_len = *_response_len;
+ TALLOC_CTX *tmp_ctx = NULL;
+ int i = 0;
+ int ret = EOK;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ /* attrs count */
+ ret = sudosrv_response_append_uint32(tmp_ctx, attrs_num,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* attrs */
+ for (i = 0; i < attrs_num; i++) {
+ ret = sudosrv_response_append_attr(tmp_ctx, attrs[i].name,
+ attrs[i].num_values, attrs[i].values,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ *_response_body = talloc_steal(mem_ctx, response_body);
+ *_response_len = response_len;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/*
+ * Response format:
+ * <error_code(uint32_t)><domain(char*)>\0<num_entries(uint32_t)><rule1><rule2>...
+ * <ruleN> = <num_attrs(uint32_t)><attr1><attr2>...
+ * <attrN> = <name(char*)>\0<num_values(uint32_t)><value1(char*)>\0<value2(char*)>\0...
+ *
+ * if <error_code> is not SSS_SUDO_ERROR_OK, the rest of the data is skipped.
+ */
+errno_t sudosrv_build_response(TALLOC_CTX *mem_ctx,
+ uint32_t error,
+ uint32_t rules_num,
+ struct sysdb_attrs **rules,
+ uint8_t **_response_body,
+ size_t *_response_len)
+{
+ uint8_t *response_body = NULL;
+ size_t response_len = 0;
+ TALLOC_CTX *tmp_ctx = NULL;
+ uint32_t i = 0;
+ errno_t ret = EOK;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ /* error code */
+ ret = sudosrv_response_append_uint32(tmp_ctx, error,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto fail;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "error: [%"PRIu32"]\n", error);
+
+ if (error != SSS_SUDO_ERROR_OK) {
+ goto done;
+ }
+
+ /* domain name - deprecated
+ * TODO: when possible change the protocol */
+ ret = sudosrv_response_append_string(tmp_ctx, "\0", 1,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ /* rules count */
+ ret = sudosrv_response_append_uint32(tmp_ctx, rules_num,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto fail;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "rules_num: [%"PRIu32"]\n", rules_num);
+
+ /* rules */
+ for (i = 0; i < rules_num; i++) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "rule [%"PRIu32"]/[%"PRIu32"]\n", i+1, rules_num);
+ ret = sudosrv_response_append_rule(tmp_ctx, rules[i]->num, rules[i]->a,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto fail;
+ }
+ }
+
+done:
+ *_response_body = talloc_steal(mem_ctx, response_body);
+ *_response_len = response_len;
+
+ ret = EOK;
+
+fail:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx,
+ uint8_t *query_body,
+ size_t query_len,
+ char **_rawname,
+ uid_t *_uid)
+{
+ size_t offset = 0;
+ size_t rawname_len;
+ char *rawname;
+ uid_t uid;
+
+ /* uid */
+ if (query_len < sizeof(uid_t)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Query is too small\n");
+ return EINVAL;
+ }
+ safealign_memcpy(&uid, query_body, sizeof(uid_t), &offset);
+
+ /* username[@domain] */
+ rawname = (char*)(query_body + offset);
+ rawname_len = query_len - offset; /* strlen + zero */
+
+ if (rawname[rawname_len - 1] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Username is not zero terminated\n");
+ return EINVAL;
+ }
+
+ if (rawname_len < 2) { /* at least one character and zero */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Query does not contain username\n");
+ return EINVAL;
+ }
+
+ if (!sss_utf8_check((uint8_t*)rawname, rawname_len - 1)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Supplied data is not valid UTF-8 string\n");
+ return EINVAL;
+ }
+
+ rawname = talloc_strdup(mem_ctx, rawname);
+ if (rawname == NULL) {
+ return ENOMEM;
+ }
+
+ *_uid = uid;
+ *_rawname = rawname;
+
+ return EOK;
+}