>From d76242ea5be9c9b7bf2dfbee9ba17b2d63d87612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= Date: Fri, 30 Sep 2011 14:10:28 +0200 Subject: [PATCH 2/5] SUDO integration - SUDO plugin --- src/sss_client/sudo_plugin/sss_sudoplugin.c | 427 +++++++++++++++++++++++++++ src/sss_client/sudo_plugin/sss_sudoplugin.h | 46 +++ 2 files changed, 473 insertions(+), 0 deletions(-) create mode 100644 src/sss_client/sudo_plugin/sss_sudoplugin.c create mode 100644 src/sss_client/sudo_plugin/sss_sudoplugin.h diff --git a/src/sss_client/sudo_plugin/sss_sudoplugin.c b/src/sss_client/sudo_plugin/sss_sudoplugin.c new file mode 100644 index 0000000..ec62dee --- /dev/null +++ b/src/sss_client/sudo_plugin/sss_sudoplugin.c @@ -0,0 +1,427 @@ +/* + 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 "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "sss_client/sudo_plugin/sss_sudoplugin.h" +#include "sss_client/sss_cli.h" + +int policy_open(unsigned int version, + sudo_conv_t conversation, + sudo_printf_t plugin_printf, + char * const settings[], + char * const user_info[], + char * const user_env[]); + +void policy_close(int exit_status, + int error); + +int policy_version(int verbose); + +int policy_list(int argc, + char * const argv[], + int verbose, + const char *list_user); + +int policy_check(int argc, + char * const argv[], + char *env_add[], + char **command_out[], + char **argv_out[], + char **user_env_out[]); + +static errno_t policy_check_create_query(const char *qualified_command_path, + char * const *settings, + char * const *user_info, + char * const *user_env, + char * const *env_add, + int argc, + char * const argv[], + char **query, + int *query_length); + +static errno_t policy_check_parse_response(char *response, + int response_length, + int *result, + char ***_command_out, + char ***_argv_out, + char ***_user_env_out); + +static char * get_qualified_command_path(const char *command); + + +/* SUDO Plugin structure */ +struct policy_plugin sss_sudo_policy = { + SUDO_POLICY_PLUGIN, + SUDO_API_VERSION, + policy_open, + policy_close, + policy_version, + policy_check, + policy_list, + NULL, /* validate */ + NULL, /* invalidate */ + NULL /* session init */ +}; + +/* Global plugin state initialized from policy_check with data passed by SUDO */ +struct plugin_state plugin = {NULL, NULL, NULL, NULL, NULL}; + +int policy_open(unsigned int version, + sudo_conv_t conversation, + sudo_printf_t _printf, + char * const settings[], + char * const user_info[], + char * const user_env[]) +{ + if (conversation == NULL || _printf == NULL) { + return SSS_SUDO_USAGE_ERROR; + } + + /* check Plugin API version */ + + /* + * TODO: Do we need to check major and minor version or major is enough? + */ + if (version != SUDO_API_VERSION) { + _printf(SUDO_CONV_ERROR_MSG, "The SSS SUDO plugin requires " + "API version %d. Your version is %d", + SUDO_API_VERSION, version); + + return SSS_SUDO_GENERAL_ERROR; + } + + /* save input variables */ + + plugin.conversation = conversation; + plugin.printf = _printf; + plugin.settings = settings; + plugin.user_info = user_info; + plugin.user_env = user_env; + + return SSS_SUDO_SUCCESS; +} + +void policy_close(int exit_status, int error) +{ + if (error) { + plugin.printf(SUDO_CONV_ERROR_MSG, "Command error: %s\n", strerror(error)); + } else if (WIFEXITED(exit_status)) { + plugin.printf(SUDO_CONV_INFO_MSG, "Command exited with status %d\n", + WEXITSTATUS(exit_status)); + } else if (WIFSIGNALED(exit_status)) { + plugin.printf(SUDO_CONV_INFO_MSG, "Command was killed by signal %d\n", + WTERMSIG(exit_status)); + } + + return; +} + +int policy_version(int verbose) +{ + return SSS_SUDO_SUCCESS; +} + +int policy_list(int argc, + char * const argv[], + int verbose, + const char *list_user) +{ + return SSS_SUDO_SUCCESS; +} + +int policy_check(int argc, + char * const argv[], + char *env_add[], + char **command_out[], + char **argv_out[], + char **user_env_out[]) +{ + char *qualified_command_path = NULL; + char *query = NULL; + int query_length = 0; + int ret = 0; + int errnop; + int sudo_result = SSS_SUDO_FAILURE; + struct sss_cli_req_data request_data; + uint8_t *reply_buf = NULL; + size_t reply_len; + + if (argc <= 0) { + plugin.printf(SUDO_CONV_ERROR_MSG, "sudo: no command specified!\n"); + return SSS_SUDO_USAGE_ERROR; + } + + /* Does command exists? */ + qualified_command_path = get_qualified_command_path(argv[0]); + if (qualified_command_path == NULL) { + plugin.printf(SUDO_CONV_ERROR_MSG, + "sudo: %s: command not found\n", argv[0]); + return SSS_SUDO_GENERAL_ERROR; + } + + /* create query data */ + ret = policy_check_create_query(qualified_command_path, plugin.settings, + plugin.user_info, plugin.user_env, + env_add, argc, argv, + &query, &query_length); + if (ret != EOK) { + plugin.printf(SUDO_CONV_ERROR_MSG, + "sudo: unable to create query string: %s\n", + strerror(ret)); + ret = SSS_SUDO_GENERAL_ERROR; + goto done; + } + request_data.len = query_length; + request_data.data = (const void*)query; + + /* send query */ + errnop = 0; + ret = sss_sudo_make_request(SSS_SUDO_CHECK, &request_data, &reply_buf, + &reply_len, &errnop); + if (errnop != 0) { + plugin.printf(SUDO_CONV_ERROR_MSG, + "Unable contact SSSD responder: %s\n", strerror(errnop)); + ret = SSS_SUDO_GENERAL_ERROR; + goto done; + } + + /* process response */ + ret = policy_check_parse_response((char*)reply_buf, reply_len, &sudo_result, + command_out, argv_out, user_env_out); + if (ret != EOK) { + plugin.printf(SUDO_CONV_ERROR_MSG, + "sudo: unable to parse response: %s\n", + strerror(ret)); + ret = SSS_SUDO_GENERAL_ERROR; + goto done; + } + + plugin.printf(SUDO_CONV_INFO_MSG, "CMD Return code: %d\n", sudo_result); + plugin.printf(SUDO_CONV_INFO_MSG, "errnop: %d\n", errnop); + + switch (sudo_result) { + case SSS_SUDO_RESPONSE_ALLOW: + ret = SSS_SUDO_SUCCESS; + break; + case SSS_SUDO_RESPONSE_AUTHENTICATE: + /* user can run the command, but must authenticate himself first */ + /* TODO */ + ret = SSS_SUDO_FAILURE; + break; + case SSS_SUDO_RESPONSE_DENY: + ret = SSS_SUDO_FAILURE; + break; + case SSS_SUDO_RESPONSE_UNKNOWN: + /* possibly local user, run sudoers plugin */ + /* TODO */ + ret = SSS_SUDO_GENERAL_ERROR; + plugin.printf(SUDO_CONV_ERROR_MSG, "sudo: Unknown user.\n"); + break; + } + +done: + free(query); + + return ret; +} + +/* + * Creates query string in format: + * qualified_command_path\0argv[0]\0argv[i]\0\0 + * env_add\0\0user_env\0\0settings\0\0user_info\0 + * + * where env_add, user_env, settings and user_info are in the form of + * NAME=VALUE pairs. + */ +errno_t policy_check_create_query(const char *qualified_command_path, + char * const *settings, + char * const *user_info, + char * const *user_env, + char * const *env_add, + int argc, + char * const argv[], + char **query, + int *query_length) +{ +#define APPEND_ELEMENT(element) do { \ + iter_length = strlen(element) + 1; /* with ending \0 */ \ + data = (char*)realloc(data, data_length + iter_length); \ + if (data == NULL) { \ + return ENOMEM; \ + } \ + memcpy(data + data_length, element, iter_length); \ + data_length += iter_length; \ +} while(0) + +#define APPEND_ZERO() do { \ + data_length++; \ + data = (char*)realloc(data, data_length); \ + data[data_length] = '\0'; \ +} while(0) + + char *data = NULL; + int data_length = 0; + char * const *iter = NULL; + int iter_length = 0; + int i = 0; + char * const *fields[4] = { env_add, user_env, settings, user_info }; + + /* append qualified_command_path */ + + APPEND_ELEMENT(qualified_command_path); + + /* append argv */ + + for (i = 0; i < argc; i++) { + APPEND_ELEMENT(argv[i]); + } + APPEND_ZERO(); + + /* append NAME=VALUE fields */ + for (i = 0; i < 4; i++) { + if (*(fields[i]) == NULL) { + APPEND_ZERO(); + APPEND_ZERO(); + } else { + for (iter = fields[i]; *iter != NULL; iter++) { + APPEND_ELEMENT(*iter); + } + APPEND_ZERO(); + } + } + + *query_length = data_length; + *query = data; + + return EOK; + +#undef APPEND_ELEMENT +#undef APPEND_ZERO +} + +errno_t policy_check_parse_response(char *response, + int response_length, + int *result, + char ***_command_out, + char ***_argv_out, + char ***_user_env_out) +{ +#define LOAD_ARRAY(element) do { \ + i = 0; \ + while (*current_position != '\0') { \ + i++; \ + element = (char**)realloc(element, i * sizeof(char*)); \ + element[i - 1] = current_position; \ + current_position = strchr(current_position, '\0'); \ + if (current_position == NULL) { \ + ret = ESPIPE; \ + goto done; \ + } \ + current_position++; \ + } \ + i++; \ + element = (char**)realloc(element, i * sizeof(char*)); \ + element[i - 1] = NULL; \ + current_position++; \ +} while(0) + + char *current_position = NULL; + char **command_out = NULL; + char **argv_out = NULL; + char **user_env_out = NULL; + int i = 0; + int ret; + + if (response_length <= 0 || response == NULL) { + return EINVAL; + } + + /* get responder result */ + *result = *((int*)response); + current_position = (char*)(((int*)response) + 1); + + /* get argv_out */ + LOAD_ARRAY(argv_out); + + /* get command_out */ + LOAD_ARRAY(command_out); + + /* get user_env_out */ + LOAD_ARRAY(user_env_out); + + /* copy output */ + *_command_out = command_out; + *_argv_out = argv_out; + *_user_env_out = user_env_out; + + ret = EOK; + +done: + return ret; + +#undef LOAD_ARRAY +} + +char * get_qualified_command_path(const char *command) +{ + struct stat sb; + char *path_env = NULL; + char *path = NULL; + char *cp = NULL; + char *qualified = NULL; + char pathbuf[PATH_MAX]; + + if (strchr(command, '/') != NULL) { + return NULL; + } + + path_env = getenv("PATH"); + path = path_env; + + do { + cp = strchr(path, ':'); + if (cp != NULL) { + *cp = '\0'; + } + + snprintf(pathbuf, sizeof(char) * PATH_MAX, "%s/%s", path, command); + if (stat(pathbuf, &sb) == 0) { + if (S_ISREG(sb.st_mode) + && (sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { + qualified = pathbuf; + break; + } + } + + path = cp + 1; + } while(cp != NULL); + + /* free(path_env); do not free since it is from getenv() */ + return qualified ? strdup(qualified) : NULL; +} diff --git a/src/sss_client/sudo_plugin/sss_sudoplugin.h b/src/sss_client/sudo_plugin/sss_sudoplugin.h new file mode 100644 index 0000000..464b0e6 --- /dev/null +++ b/src/sss_client/sudo_plugin/sss_sudoplugin.h @@ -0,0 +1,46 @@ +/* + 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 SSS_SUDOPLUGIN_H_ +#define SSS_SUDOPLUGIN_H_ + +#define SSS_SUDO_SUCCESS 1 +#define SSS_SUDO_FAILURE 0 +#define SSS_SUDO_GENERAL_ERROR -1 +#define SSS_SUDO_USAGE_ERROR -2 + +#define SSS_SUDO_RESPONSE_ALLOW 0 /* allow to run the command */ +#define SSS_SUDO_RESPONSE_DENY 1 /* disallow to run the command */ +#define SSS_SUDO_RESPONSE_UNKNOWN 2 /* user is not known, might be local */ +#define SSS_SUDO_RESPONSE_AUTHENTICATE 3 /* allow but authenticate user first */ + +#ifndef EOK + #define EOK 0 +#endif + +struct plugin_state { + sudo_conv_t conversation; + sudo_printf_t printf; + char * const *settings; + char * const *user_info; + char * const *user_env; +}; + +#endif /* SSS_SUDOPLUGIN_H_ */ -- 1.7.6