From 55df27d8a5f2d8548e5b0cc708d94cc9f56c56a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= Date: Mon, 3 Oct 2011 17:23:35 +0200 Subject: [PATCH 3/8] SUDO integration - SUDO responder --- src/confdb/confdb.h | 3 + src/monitor/monitor.c | 2 +- src/responder/sudo/sudosrv.c | 217 +++++++++++++++++++++++++++++ src/responder/sudo/sudosrv.h | 39 +++++ src/responder/sudo/sudosrv_cmd.c | 285 ++++++++++++++++++++++++++++++++++++++ src/sss_client/sss_cli.h | 3 + 6 files changed, 548 insertions(+), 1 deletions(-) create mode 100644 src/responder/sudo/sudosrv.c create mode 100644 src/responder/sudo/sudosrv.h create mode 100644 src/responder/sudo/sudosrv_cmd.c diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h index 266e4d2..e50829e 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 1b7f87a..aac39ae 100644 --- a/src/monitor/monitor.c +++ b/src/monitor/monitor.c @@ -814,7 +814,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..7c90c53 --- /dev/null +++ b/src/responder/sudo/sudosrv.c @@ -0,0 +1,217 @@ +/* + 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.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(1, ("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(0, ("Could not reconnect to %s provider.\n", + be_conn->domain->name)); +} + +static int sudo_ctx_destructor(void *memctx) +{ + // struct sudo_ctx *sctx = (struct sudo_ctx*)memctx; + + // TODO + + return 0; +} + +int sudo_process_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct confdb_ctx *cdb) +{ + struct sss_cmd_table *sudo_cmds; + struct be_conn *iter; + struct sudo_ctx *sctx; + int ret; + int max_retries; + + sctx = talloc_zero(mem_ctx, struct sudo_ctx); + if (!sctx) { + DEBUG(SSSDBG_FATAL_FAILURE, ("fatal error initializing sudo_ctx\n")); + return ENOMEM; + } + talloc_set_destructor((TALLOC_CTX *)sctx, sudo_ctx_destructor); + + /* + * TODO set up negative cache + ret = sss_ncache_init(nctx, &nctx->ncache); + if (ret != EOK) { + DEBUG(0, ("fatal error initializing negative cache\n")); + return ret; + } + */ + + sudo_cmds = get_sudo_cmds(); + ret = sss_process_init(sctx, 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, + &sctx->rctx); + if (ret != EOK) { + return ret; + } + sctx->rctx->pvt_ctx = sctx; + + /* + * TODO get sudo config + ret = sudo_get_config(sctx, sctx->rctx, cdb); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, ("fatal error getting sudo config\n")); + return ret; + } + */ + + /* Enable automatic reconnection to the Data Provider */ + ret = confdb_get_int(sctx->rctx->cdb, sctx->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 = sctx->rctx->be_conns; iter; iter = iter->next) { + sbus_reconnect_init(iter->conn, max_retries, + sudo_dp_reconnect_init, iter); + } + + DEBUG(1, ("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.h b/src/responder/sudo/sudosrv.h new file mode 100644 index 0000000..9a453d1 --- /dev/null +++ b/src/responder/sudo/sudosrv.h @@ -0,0 +1,39 @@ +/* + 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_H_ +#define SUDOSRV_H_ + +#define SSS_SUDO_SBUS_SERVICE_VERSION 0x0001 +#define SSS_SUDO_SBUS_SERVICE_NAME "sudo" + +struct sudo_ctx { + struct resp_ctx *rctx; + struct sss_nc_ctx *ncache; + + int neg_timeout; + time_t id_timeout; +}; + +int sudo_cmd_execute(struct cli_ctx *cctx); + +struct sss_cmd_table *get_sudo_cmds(void); + +#endif /* SUDOSRV_H_ */ diff --git a/src/responder/sudo/sudosrv_cmd.c b/src/responder/sudo/sudosrv_cmd.c new file mode 100644 index 0000000..ee9060f --- /dev/null +++ b/src/responder/sudo/sudosrv_cmd.c @@ -0,0 +1,285 @@ +/* + 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 "sss_client/sudo_plugin/sss_sudoplugin.h" + +static int sudo_cmd_check_response(struct cli_ctx *cctx, + int return_code, + int argc, + char **argv, + char *command_info, + int command_info_length, + char **user_env) +{ +#define APPEND_ELEMENT(element, length) do { \ + iter_length = length; \ + sss_packet_grow(packet_out, iter_length * sizeof(char)); \ + sss_packet_get_body(packet_out, &body, &blen); \ + memcpy(body + packet_offset, element, iter_length); \ + packet_offset += iter_length; \ +} while(0) + +#define APPEND_ZERO() do { \ + sss_packet_grow(packet_out, 1 * sizeof(char)); \ + sss_packet_get_body(packet_out, &body, &blen); \ + body[packet_offset] = '\0'; \ + packet_offset++; \ +} while(0) + + int i; + int ret; + int iter_length; + uint8_t *body; + size_t blen; + size_t packet_offset = 0; + struct sss_packet *packet_out = NULL; + + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + return ret; + } + packet_out = cctx->creq->out; + + sss_packet_set_error(packet_out, EOK); + + /* fill data */ + + /* result */ + ret = sss_packet_grow(packet_out, sizeof(int)); + if (ret != EOK) { + return ret; + } + sss_packet_get_body(packet_out, &body, &blen); + SAFEALIGN_SET_VALUE(&body[packet_offset], return_code, int, &packet_offset); + + if (return_code == SSS_SUDO_RESPONSE_ALLOW) { + /* argv */ + for (i = 0; i < argc; i++) { + APPEND_ELEMENT(argv[i], strlen(argv[i]) + 1); + } + APPEND_ZERO(); + + /* command_info */ + APPEND_ELEMENT(command_info, command_info_length); + APPEND_ZERO(); + + /* user_env */ + APPEND_ZERO(); + APPEND_ZERO(); + } + + return EOK; + +#undef APPEND_ELEMENT +#undef APPEND_ZERO +} + +static int sudo_cmd_check_parse_query(TALLOC_CTX *mem_ctx, + char *query, + int query_length, + char **_command_out, + char ***_argv_out, + int *_argc_out) +{ + TALLOC_CTX *tmp_ctx; + char *current_position = query; + char **argv_out; + int argc_out = 0; + int ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed")); + ret = ENOMEM; + goto done; + } + + /* get command */ + + *_command_out = current_position; + current_position = strchr(current_position, '\0'); + if (current_position == NULL) { + ret = ESPIPE; + goto done; + } + current_position++; + + /* get argv */ + while (*current_position != '\0') { + argc_out++; + argv_out = talloc_realloc(tmp_ctx, argv_out, char*, argc_out); + argv_out[argc_out - 1] = current_position; + + current_position = strchr(current_position, '\0'); + if (current_position == NULL) { + ret = ESPIPE; + goto done; + } + current_position++; + } + current_position++; + + /* TODO get env_add */ + + /* TODO get user_env */ + + /* TODO get settings */ + + /* TODO get user_info */ + + + *_argc_out = argc_out; + *_argv_out = talloc_steal(mem_ctx, argv_out); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static int sudo_cmd_check_create_command_info(TALLOC_CTX *mem_ctx, + char *command, + char **command_info_out, + int *command_info_length_out) +{ + TALLOC_CTX *tmp_ctx; + char *command_info; + int command_info_length = 0; + int ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed")); + ret = ENOMEM; + goto done; + } + + command_info = talloc_asprintf(tmp_ctx, "command=%s", command); + if (command_info == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_asprintf() failed")); + ret = ENOMEM; + goto done; + } + command_info_length += strlen(command_info) + 1; /* with \0 */ + + /* TOOD support other fields */ + + *command_info_length_out = command_info_length; + *command_info_out = talloc_steal(mem_ctx, command_info); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static int sudo_cmd_check(struct cli_ctx *cctx) { + TALLOC_CTX *mem_ctx; + uint8_t *body; + size_t blen; + char *query = NULL; + char *command_in = NULL; + char **argv_in = NULL; + char *command_info_out = NULL; + int command_info_out_length = 0; + int argc_in = 0; + int ret; + int sudo_result; + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed")); + return ENOMEM; + } + + /* get query string */ + sss_packet_get_body(cctx->creq->in, &body, &blen); + if (blen <= 0 || body == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("Query string is empty")); + ret = EINVAL; + goto done; + } + query = (char*)body; + + /* parse query string */ + ret = sudo_cmd_check_parse_query(mem_ctx, query, blen, &command_in, + &argv_in, &argc_in); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Unable to parse query string")); + goto done; + } + + /* TODO can user run this command? */ + + sudo_result = SSS_SUDO_RESPONSE_ALLOW; + + /* create command info */ + if (sudo_result == SSS_SUDO_RESPONSE_ALLOW) { + ret = sudo_cmd_check_create_command_info(mem_ctx, command_in, + &command_info_out, + &command_info_out_length); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to create command info string")); + return ENOMEM; + } + } + + /* send response */ + sudo_cmd_check_response(cctx, sudo_result, argc_in, argv_in, + command_info_out, command_info_out_length, NULL); + + ret = EOK; + +done: + sss_cmd_done(cctx, NULL); + talloc_free(mem_ctx); + + return 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_CHECK, sudo_cmd_check}, + {SSS_CLI_NULL, NULL} + }; + + return sudo_cmds; +} diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h index efd83c9..16dec1a 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_CHECK = 0x00C1, + /* PAM related calls */ SSS_PAM_AUTHENTICATE = 0x00F1, /**< see pam_sm_authenticate(3) for * details. -- 1.7.6.4