From c5aac7b79c102317286825077fbe7b04f95c08e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= Date: Fri, 16 Dec 2011 13:29:16 +0100 Subject: [PATCH 08/11] SUDO Integration - responder --- Makefile.am | 17 ++ src/confdb/confdb.h | 3 + src/monitor/monitor.c | 2 +- src/responder/sudo/sudosrv.c | 194 ++++++++++++++++++++++++ src/responder/sudo/sudosrv_cmd.c | 227 ++++++++++++++++++++++++++++ src/responder/sudo/sudosrv_dp.c | 220 +++++++++++++++++++++++++++ src/responder/sudo/sudosrv_get_sudorules.c | 113 ++++++++++++++ src/responder/sudo/sudosrv_private.h | 109 +++++++++++++ src/responder/sudo/sudosrv_query.c | 178 ++++++++++++++++++++++ src/sss_client/sss_cli.h | 3 + 10 files changed, 1065 insertions(+), 1 deletions(-) create mode 100644 src/responder/sudo/sudosrv.c create mode 100644 src/responder/sudo/sudosrv_cmd.c create mode 100644 src/responder/sudo/sudosrv_dp.c create mode 100644 src/responder/sudo/sudosrv_get_sudorules.c create mode 100644 src/responder/sudo/sudosrv_private.h create mode 100644 src/responder/sudo/sudosrv_query.c diff --git a/Makefile.am b/Makefile.am index f1b07b9..da1a9ef 100644 --- a/Makefile.am +++ b/Makefile.am @@ -76,6 +76,9 @@ sssdlibexec_PROGRAMS = \ krb5_child \ ldap_child \ proxy_child +if BUILD_SUDO +sssdlibexec_PROGRAMS += sssd_sudo +endif if HAVE_CHECK non_interactive_check_based_tests = \ @@ -310,6 +313,7 @@ dist_noinst_HEADERS = \ src/responder/nss/nsssrv_private.h \ src/responder/nss/nsssrv_netgroup.h \ src/responder/common/negcache.h \ + src/responder/sudo/sudosrv_private.h \ src/sbus/sbus_client.h \ src/sbus/sssd_dbus.h \ src/sbus/sssd_dbus_private.h \ @@ -442,6 +446,19 @@ sssd_pam_LDADD = \ $(SSSD_LIBS) \ libsss_util.la +if BUILD_SUDO +sssd_sudo_SOURCES = \ + src/responder/sudo/sudosrv.c \ + src/responder/sudo/sudosrv_cmd.c \ + src/responder/sudo/sudosrv_get_sudorules.c \ + src/responder/sudo/sudosrv_query.c \ + src/responder/sudo/sudosrv_dp.c \ + $(SSSD_RESPONDER_OBJ) +sssd_sudo_LDADD = \ + $(SSSD_LIBS) \ + libsss_util.la +endif + sssd_be_SOURCES = \ src/providers/data_provider_be.c \ src/providers/data_provider_fo.c \ diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h index c4b3347..0343afb 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -91,6 +91,9 @@ #define CONFDB_PAM_ID_TIMEOUT "pam_id_timeout" #define CONFDB_PAM_PWD_EXPIRATION_WARNING "pam_pwd_expiration_warning" +/* SUDO */ +#define CONFDB_SUDO_CONF_ENTRY "config/sudo" + /* Data Provider */ #define CONFDB_DP_CONF_ENTRY "config/dp" diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c index 61786ea..dc6f03d 100644 --- a/src/monitor/monitor.c +++ b/src/monitor/monitor.c @@ -824,7 +824,7 @@ static int check_local_domain_unique(struct sss_domain_info *domains) static char *check_services(char **services) { - const char *known_services[] = { "nss", "pam", NULL }; + const char *known_services[] = { "nss", "pam", "sudo", NULL }; int i; int ii; diff --git a/src/responder/sudo/sudosrv.c b/src/responder/sudo/sudosrv.c new file mode 100644 index 0000000..841be43 --- /dev/null +++ b/src/responder/sudo/sudosrv.c @@ -0,0 +1,194 @@ +/* + Authors: + Pavel Březina + + 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 . +*/ + +#include + +#include "util/util.h" +#include "confdb/confdb.h" +#include "monitor/monitor_interfaces.h" +#include "responder/common/responder.h" +#include "responder/sudo/sudosrv_private.h" +#include "providers/data_provider.h" + +struct sbus_method monitor_sudo_methods[] = { + { MON_CLI_METHOD_PING, monitor_common_pong }, + { MON_CLI_METHOD_RES_INIT, monitor_common_res_init }, + { MON_CLI_METHOD_ROTATE, responder_logrotate }, + { NULL, NULL } +}; + +struct sbus_interface monitor_sudo_interface = { + MONITOR_INTERFACE, + MONITOR_PATH, + SBUS_DEFAULT_VTABLE, + monitor_sudo_methods, + NULL +}; + +static struct sbus_method sudo_dp_methods[] = { + { NULL, NULL } +}; + +struct sbus_interface sudo_dp_interface = { + DP_INTERFACE, + DP_PATH, + SBUS_DEFAULT_VTABLE, + sudo_dp_methods, + NULL +}; + +static void sudo_dp_reconnect_init(struct sbus_connection *conn, + int status, + void *pvt) +{ + struct be_conn *be_conn = talloc_get_type(pvt, struct be_conn); + int ret; + + /* Did we reconnect successfully? */ + if (status == SBUS_RECONNECT_SUCCESS) { + DEBUG(SSSDBG_TRACE_FUNC, ("Reconnected to the Data Provider.\n")); + + /* Identify ourselves to the data provider */ + ret = dp_common_send_id(be_conn->conn, + DATA_PROVIDER_VERSION, + "SUDO"); + /* all fine */ + if (ret == EOK) { + handle_requests_after_reconnect(); + return; + } + } + + /* Failed to reconnect */ + DEBUG(SSSDBG_FATAL_FAILURE, ("Could not reconnect to %s provider.\n", + be_conn->domain->name)); +} + +int sudo_process_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct confdb_ctx *cdb) +{ + struct sss_cmd_table *sudo_cmds; + struct sudo_ctx *sudo_ctx; + struct be_conn *iter; + int ret; + int max_retries; + + sudo_ctx = talloc_zero(mem_ctx, struct sudo_ctx); + if (!sudo_ctx) { + DEBUG(SSSDBG_FATAL_FAILURE, ("fatal error initializing sudo_ctx\n")); + return ENOMEM; + } + + sudo_cmds = get_sudo_cmds(); + ret = sss_process_init(sudo_ctx, ev, cdb, + sudo_cmds, + SSS_SUDO_SOCKET_NAME, NULL, + CONFDB_SUDO_CONF_ENTRY, + SSS_SUDO_SBUS_SERVICE_NAME, + SSS_SUDO_SBUS_SERVICE_VERSION, + &monitor_sudo_interface, + "SUDO", + &sudo_dp_interface, + &sudo_ctx->rctx); + if (ret != EOK) { + return ret; + } + sudo_ctx->rctx->pvt_ctx = sudo_ctx; + + /* Enable automatic reconnection to the Data Provider */ + ret = confdb_get_int(sudo_ctx->rctx->cdb, sudo_ctx->rctx, + CONFDB_SUDO_CONF_ENTRY, + CONFDB_SERVICE_RECON_RETRIES, + 3, &max_retries); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + ("Failed to set up automatic reconnection\n")); + return ret; + } + + for (iter = sudo_ctx->rctx->be_conns; iter; iter = iter->next) { + sbus_reconnect_init(iter->conn, max_retries, + sudo_dp_reconnect_init, iter); + } + + DEBUG(SSSDBG_TRACE_FUNC, ("SUDO Initialization complete\n")); + + return EOK; +} + +int main(int argc, const char *argv[]) +{ + int opt; + poptContext pc; + struct main_context *main_ctx; + int ret; + + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_MAIN_OPTS + POPT_TABLEEND + }; + + /* Set debug level to invalid value so we can deside if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + 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); + + CONVERT_AND_SET_DEBUG_LEVEL(debug_level); + + /* set up things like debug, signals, daemonization, etc... */ + debug_log_file = "sssd_sudo"; + + ret = server_setup("sssd[sudo]", 0, CONFDB_SUDO_CONF_ENTRY, &main_ctx); + 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); + 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..72e608b --- /dev/null +++ b/src/responder/sudo/sudosrv_cmd.c @@ -0,0 +1,227 @@ +/* + Authors: + Pavel Březina + + 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 . +*/ + +#include +#include +#include + +#include "util/util.h" +#include "responder/common/responder.h" +#include "responder/common/responder_packet.h" +#include "responder/sudo/sudosrv_private.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; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) return ENOMEM; + + ret = sss_packet_new(cli_ctx->creq, 0, + sss_packet_get_cmd(cli_ctx->creq->in), + &cli_ctx->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(cli_ctx->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(cli_ctx->creq->out, &packet_body, &packet_len); + memcpy(packet_body, response_body, response_len); + + sss_packet_set_error(cli_ctx->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; + + ret = sudosrv_response_append_uint32(mem_ctx, error, + &response_body, &response_len); + if (ret != EOK) { + return ret; + } + + return sudosrv_cmd_send_reply(cmd_ctx, response_body, response_len); +} + +errno_t sudosrv_cmd_done(struct sudo_dom_ctx *dctx, int ret) +{ + uint8_t *response_body = NULL; + size_t response_len = 0; + + switch (ret) { + case EOK: + /* send result */ + ret = sudosrv_get_sudorules_build_response(dctx->cmd_ctx, SSS_SUDO_ERROR_OK, + dctx->res_count, dctx->res, + &response_body, &response_len); + if (ret != EOK) { + return EFAULT; + } + + ret = sudosrv_cmd_send_reply(dctx->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(dctx->cmd_ctx, dctx->cmd_ctx, ret); + break; + } + + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Fatal error, killing connection!\n")); + talloc_free(dctx->cmd_ctx->cli_ctx); + return EFAULT; + } + + return EOK; +} + +static int sudosrv_cmd_get_sudorules(struct cli_ctx *cli_ctx) +{ + char *rawname = NULL; + char *domname = NULL; + uint8_t *query_body = NULL; + size_t query_len = 0; + int ret = EOK; + struct sudo_cmd_ctx *cmd_ctx = NULL; + struct sudo_dom_ctx *dctx = NULL; + + cmd_ctx = talloc_zero(cli_ctx, struct sudo_cmd_ctx); + if (!cmd_ctx) { + ret = ENOMEM; + goto done; + } + cmd_ctx->cli_ctx = cli_ctx; + + dctx = talloc_zero(cmd_ctx, struct sudo_dom_ctx); + if (!dctx) { + ret = ENOMEM; + goto done; + } + dctx->cmd_ctx = cmd_ctx; + + /* get query */ + sss_packet_get_body(cli_ctx->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; + } + + /* parse query */ + rawname = sudosrv_get_sudorules_parse_query(cmd_ctx, + (const char*)query_body, + query_len); + if (rawname == NULL) { + ret = ENOMEM; + DEBUG(SSSDBG_CRIT_FAILURE, + ("Unable to parse query: %s\n", strerror(ret))); + goto done; + } + + domname = NULL; + ret = sss_parse_name(cmd_ctx, cli_ctx->rctx->names, rawname, + &domname, &cmd_ctx->username); + if (ret != EOK) { + DEBUG(2, ("Invalid name received [%s]\n", rawname)); + ret = ENOENT; + goto done; + } + + DEBUG(SSSDBG_FUNC_DATA, ("Requesting sudo rules for [%s] from [%s]\n", + cmd_ctx->username, domname ? domname : "")); + + if (domname) { + dctx->domain = responder_get_domain(cli_ctx->rctx->domains, domname); + if (!dctx->domain) { + ret = ENOENT; + goto done; + } + } else { + /* this is a multidomain search */ + dctx->domain = cli_ctx->rctx->domains; + cmd_ctx->check_next = true; + } + + /* ok, find it ! */ + ret = sudosrv_get_sudorules(dctx); + +done: + return sudosrv_cmd_done(dctx, ret); +} + +struct cli_protocol_version *register_cli_protocol_version(void) +{ + static struct cli_protocol_version sudo_cli_protocol_version[] = { + {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_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..0c621f5 --- /dev/null +++ b/src/responder/sudo/sudosrv_dp.c @@ -0,0 +1,220 @@ +/* + Authors: + Pavel Březina + Jakub Hrozek + + 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 . +*/ + +#include +#include +#include +#include "sbus/sssd_dbus.h" + +#include "util/util.h" +#include "sbus/sbus_client.h" +#include "providers/data_provider.h" +#include "responder/common/responder.h" +#include "responder/sudo/sudosrv_private.h" + +struct sudo_dp_refresh_state { + dbus_uint16_t err_maj; + dbus_uint32_t err_min; +}; + +/* FIXME -- need to keep track of a running request + * and just queue a callback + * OR reuse the common dp requests + */ +static void sudosrv_dp_process_reply(DBusPendingCall *pending, void *ptr); + +struct tevent_req * sudosrv_dp_refresh_send(struct resp_ctx *rctx, + struct sss_domain_info *dom, + const char *username) +{ + struct be_conn *be_conn; + struct sudo_dp_refresh_state *state; + DBusMessage *msg; + dbus_bool_t dbret; + int ret; + const int timeout = SSS_CLI_SOCKET_TIMEOUT / 2; + struct tevent_req *req; + + /* Cache refresh requests need to be allocated on the responder context + * so that they don't go away if a client disconnects. The worst- + * case scenario here is that the cache is updated without any + * client expecting a response. + */ + req = tevent_req_create(rctx, &state, struct sudo_dp_refresh_state); + if (!req) return NULL; + + /* double check dp_ctx has actually been initialized. + * in some pathological cases it may happen that sudo starts up before + * dp connection code is actually able to establish a connection. + */ + ret = sss_dp_get_domain_conn(rctx, dom->name, &be_conn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("The Data Provider connection for %s is not available! " + "This maybe a bug, it shouldn't happen!\n", + dom->name)); + ret = EIO; + goto error; + } + + msg = dbus_message_new_method_call(NULL, + DP_PATH, + DP_INTERFACE, + DP_METHOD_SUDOHANDLER); + if (msg == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, ("Out of memory?!\n")); + ret = ENOMEM; + goto error; + } + + if (username != NULL) { + dbret = dbus_message_append_args(msg, + DBUS_TYPE_STRING, &username, + DBUS_TYPE_INVALID); + if (!dbret) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to generate dbus reply\n")); + ret = EIO; + goto error; + } + } + + DEBUG(SSSDBG_TRACE_FUNC, ("Sending SUDOers refresh request\n")); + ret = sbus_conn_send(be_conn->conn, msg, + timeout, sudosrv_dp_process_reply, + req, NULL); + dbus_message_unref(msg); + + return req; + +error: + tevent_req_error(req, ret); + tevent_req_post(req, rctx->ev); + return req; +} + +static int sudosrv_dp_get_reply(DBusPendingCall *pending, + dbus_uint16_t *err_maj, + dbus_uint32_t *err_min, + char **err_msg) +{ + DBusMessage *reply; + DBusError dbus_error; + dbus_bool_t ret; + int type; + int err = EOK; + + dbus_error_init(&dbus_error); + + reply = dbus_pending_call_steal_reply(pending); + if (!reply) { + /* reply should never be null. This function shouldn't be called + * until reply is valid or timeout has occurred. If reply is NULL + * here, something is seriously wrong and we should bail out. + */ + DEBUG(0, ("Severe error. A reply callback was called but no reply was received and no timeout occurred\n")); + + err = EIO; + goto done; + } + + type = dbus_message_get_type(reply); + switch (type) { + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + ret = dbus_message_get_args(reply, &dbus_error, + DBUS_TYPE_UINT16, err_maj, + DBUS_TYPE_UINT32, err_min, + DBUS_TYPE_STRING, err_msg, + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(1,("Failed to parse message\n")); + if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error); + err = EIO; + goto done; + } + + DEBUG(4, ("Got reply (%u, %u, %s) from Data Provider\n", + (unsigned int)*err_maj, (unsigned int)*err_min, *err_msg)); + + break; + + case DBUS_MESSAGE_TYPE_ERROR: + if (strcmp(dbus_message_get_error_name(reply), + DBUS_ERROR_NO_REPLY) == 0) { + err = ETIME; + goto done; + } + DEBUG(0,("The Data Provider returned an error [%s]\n", + dbus_message_get_error_name(reply))); + /* Falling through to default intentionally*/ + default: + /* + * Timeout or other error occurred or something + * unexpected happened. + * It doesn't matter which, because either way we + * know that this connection isn't trustworthy. + * We'll destroy it now. + */ + + err = EIO; + } + +done: + dbus_pending_call_unref(pending); + dbus_message_unref(reply); + + return err; +} + +static void sudosrv_dp_process_reply(DBusPendingCall *pending, void *ptr) +{ + struct tevent_req *req; + errno_t ret; + char *err_msg; + struct sudo_dp_refresh_state *state; + + req = talloc_get_type(ptr, struct tevent_req); + state = tevent_req_data(req, struct sudo_dp_refresh_state); + + ret = sudosrv_dp_get_reply(pending, &state->err_maj, &state->err_min, &err_msg); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to get a reply from DP! " + "err_maj: %d err_min: %d err_msg: [%s]\n", + state->err_maj, state->err_min, err_msg ? err_msg : "none set")); + tevent_req_error(req, EIO); + return; + } + + tevent_req_done(req); +} + +errno_t sudosrv_dp_refresh_recv(struct tevent_req *req, + dbus_uint16_t *_err_maj, + dbus_uint32_t *_err_min) +{ + struct sudo_dp_refresh_state *state; + state = tevent_req_data(req, struct sudo_dp_refresh_state); + + if (_err_maj) *_err_maj = state->err_maj; + if (_err_min) *_err_min = state->err_min; + + TEVENT_REQ_RETURN_ON_ERROR(req); + 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..aa466c8 --- /dev/null +++ b/src/responder/sudo/sudosrv_get_sudorules.c @@ -0,0 +1,113 @@ +/* + Authors: + Pavel Březina + Jakub Hrozek + + 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 . +*/ + +#include +#include +#include + +#include "util/util.h" +#include "db/sysdb_sudo.h" +#include "responder/sudo/sudosrv_private.h" + +errno_t sudosrv_get_sudorules(struct sudo_dom_ctx *dctx) +{ + dctx->res = NULL; + dctx->res_count = 0; + + return EOK; +} + +char * sudosrv_get_sudorules_parse_query(TALLOC_CTX *mem_ctx, + const char *query_body, + int query_len) +{ + if (query_len < 2 || ((query_len - 1) != strlen(query_body))) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid query.\n")); + return NULL; + } + + return talloc_strdup(mem_ctx, query_body); +} + +/* + * Response format: + * ... + * = ... + * = \0\0\0... + * + * if is not SSS_SUDO_ERROR_OK, the rest of the data is skipped. + */ +int sudosrv_get_sudorules_build_response(TALLOC_CTX *mem_ctx, + uint32_t error, + int 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; + 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; + } + + /* error code */ + ret = sudosrv_response_append_uint32(tmp_ctx, error, + &response_body, &response_len); + if (ret != EOK) { + goto fail; + } + + if (error != SSS_SUDO_ERROR_OK) { + goto done; + } + + /* rules count */ + ret = sudosrv_response_append_uint32(tmp_ctx, (uint32_t)rules_num, + &response_body, &response_len); + if (ret != EOK) { + goto fail; + } + + /* rules */ + for (i = 0; i < rules_num; i++) { + 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; +} diff --git a/src/responder/sudo/sudosrv_private.h b/src/responder/sudo/sudosrv_private.h new file mode 100644 index 0000000..7401570 --- /dev/null +++ b/src/responder/sudo/sudosrv_private.h @@ -0,0 +1,109 @@ +/* + Authors: + Pavel Březina + + 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 . +*/ + +#ifndef _SUDOSRV_PRIVATE_H_ +#define _SUDOSRV_PRIVATE_H_ + +#include +#include + +#include "src/db/sysdb.h" +#include "responder/common/responder.h" + +#define SSS_SUDO_ERROR_OK 0 +#define SSS_SUDO_SBUS_SERVICE_VERSION 0x0001 +#define SSS_SUDO_SBUS_SERVICE_NAME "sudo" + +struct sudo_ctx { + struct resp_ctx *rctx; +}; + +struct sudo_cmd_ctx { + struct cli_ctx *cli_ctx; + char *username; + bool check_next; +}; + +struct sudo_dom_ctx { + struct sudo_cmd_ctx *cmd_ctx; + struct sss_domain_info *domain; + bool check_provider; + + /* cache results */ + struct ldb_result *user; + struct sysdb_attrs **res; + size_t res_count; +}; + +struct sudo_dp_request { + struct cli_ctx *cctx; + struct sss_domain_info *domain; +}; + +struct sss_cmd_table *get_sudo_cmds(void); + +errno_t sudosrv_cmd_done(struct sudo_dom_ctx *dctx, int ret); + +struct tevent_req * sudosrv_dp_refresh_send(struct resp_ctx *rctx, + struct sss_domain_info *dom, + const char *username); + +errno_t sudosrv_dp_refresh_recv(struct tevent_req *req, + dbus_uint16_t *_err_maj, + dbus_uint32_t *_err_min); + +errno_t sudosrv_get_sudorules(struct sudo_dom_ctx *dctx); + +char * sudosrv_get_sudorules_parse_query(TALLOC_CTX *mem_ctx, + const char *query_body, + int query_len); + +int sudosrv_get_sudorules_build_response(TALLOC_CTX *mem_ctx, + uint32_t error, + int rules_num, + struct sysdb_attrs **rules, + uint8_t **_response_body, + size_t *_response_len); + +int sudosrv_response_append_string(TALLOC_CTX *mem_ctx, + const char *str, + size_t str_len, + uint8_t **_response_body, + size_t *_response_len); + +int sudosrv_response_append_uint32(TALLOC_CTX *mem_ctx, + uint32_t number, + uint8_t **_response_body, + size_t *_response_len); + +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); + +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); + +#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..8b98da6 --- /dev/null +++ b/src/responder/sudo/sudosrv_query.c @@ -0,0 +1,178 @@ +/* + Authors: + Pavel Březina + + 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 . +*/ + +#include +#include +#include +#include + +#include "util/util.h" +#include "responder/sudo/sudosrv_private.h" + +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; +} + +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(int)); + 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; +} + +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; +} + +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; + 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; + } + + /* 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++) { + if (strlen((char*)(values[i].data)) != values[i].length) { + DEBUG(SSSDBG_CRIT_FAILURE, ("value is not a string")); + ret = EINVAL; + goto done; + } + + ret = sudosrv_response_append_string(tmp_ctx, + (const char*)values[i].data, + values[i].length + 1, + &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; +} diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h index 33e9441..f9bbda9 100644 --- a/src/sss_client/sss_cli.h +++ b/src/sss_client/sss_cli.h @@ -144,6 +144,9 @@ enum sss_cli_command { SSS_NSS_ENDSPENT = 0x00B5, #endif +/* SUDO */ + SSS_SUDO_GET_SUDORULES = 0x00C1, + /* PAM related calls */ SSS_PAM_AUTHENTICATE = 0x00F1, /**< see pam_sm_authenticate(3) for * details. -- 1.7.6.4