diff options
Diffstat (limited to 'src/sss_client/sudo')
-rw-r--r-- | src/sss_client/sudo/sss_sudo.c | 251 | ||||
-rw-r--r-- | src/sss_client/sudo/sss_sudo.h | 195 | ||||
-rw-r--r-- | src/sss_client/sudo/sss_sudo_private.h | 33 | ||||
-rw-r--r-- | src/sss_client/sudo/sss_sudo_response.c | 257 |
4 files changed, 736 insertions, 0 deletions
diff --git a/src/sss_client/sudo/sss_sudo.c b/src/sss_client/sudo/sss_sudo.c new file mode 100644 index 0000000..6c86b8f --- /dev/null +++ b/src/sss_client/sudo/sss_sudo.c @@ -0,0 +1,251 @@ +/* + 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 "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "util/util.h" +#include "sss_client/sss_cli.h" +#include "sss_client/sudo/sss_sudo.h" +#include "sss_client/sudo/sss_sudo_private.h" + +static int sss_sudo_create_query(uid_t uid, + const char *username, + uint8_t **_query, + size_t *_query_len); + +static void sss_sudo_free_rules(unsigned int num_rules, + struct sss_sudo_rule *rules); + +static void sss_sudo_free_attrs(unsigned int num_attrs, + struct sss_sudo_attr *attrs); + +static int sss_sudo_send_recv_generic(enum sss_cli_command command, + uid_t uid, + const char *username, + uint32_t *_error, + char **_domainname, + struct sss_sudo_result **_result) +{ + struct sss_cli_req_data request; + uint8_t *query_buf = NULL; + size_t query_len = 0; + uint8_t *reply_buf = NULL; + size_t reply_len = 0; + int errnop = 0; + int ret = 0; + + /* create query */ + + ret = sss_sudo_create_query(uid, username, &query_buf, &query_len); + if (ret != EOK) { + goto done; + } + + request.len = query_len; + request.data = (const void*)query_buf; + + /* send query and receive response */ + + errnop = 0; + ret = sss_sudo_make_request(command, &request, + &reply_buf, &reply_len, &errnop); + if (ret != SSS_STATUS_SUCCESS) { + ret = errnop; + goto done; + } + + /* parse structure */ + + ret = sss_sudo_parse_response((const char*)reply_buf, reply_len, + _domainname, _result, _error); + +done: + free(query_buf); + free(reply_buf); + return ret; +} + +int sss_sudo_send_recv(uid_t uid, + const char *username, + const char *domainname, + uint32_t *_error, + struct sss_sudo_result **_result) +{ + int ret; + + if (username == NULL || strlen(username) == 0) { + return EINVAL; + } + + /* send query and receive response */ + + ret = sss_sudo_send_recv_generic(SSS_SUDO_GET_SUDORULES, uid, username, + _error, NULL, _result); + return ret; +} + +int sss_sudo_send_recv_defaults(uid_t uid, + const char *username, + uint32_t *_error, + char **_domainname, + struct sss_sudo_result **_result) +{ + if (username == NULL || strlen(username) == 0) { + return EINVAL; + } + + return sss_sudo_send_recv_generic(SSS_SUDO_GET_DEFAULTS, uid, username, + _error, _domainname, _result); +} + +static int sss_sudo_create_query(uid_t uid, const char *username, + uint8_t **_query, size_t *_query_len) +{ + uint8_t *data = NULL; + size_t username_len = strlen(username) * sizeof(char) + 1; + size_t data_len = sizeof(uid_t) + username_len; + size_t offset = 0; + + data = (uint8_t*)malloc(data_len * sizeof(uint8_t)); + if (data == NULL) { + return ENOMEM; + } + + SAFEALIGN_SET_VALUE(data, uid, uid_t, &offset); + memcpy(data + offset, username, username_len); + + *_query = data; + *_query_len = data_len; + + return EOK; +} + +int sss_sudo_get_values(struct sss_sudo_rule *e, + const char *attrname, char ***_values) +{ + struct sss_sudo_attr *attr = NULL; + char **values = NULL; + int i, j; + + for (i = 0; i < e->num_attrs; i++) { + attr = e->attrs + i; + if (strcasecmp(attr->name, attrname) == 0) { + values = calloc(attr->num_values + 1, sizeof(char*)); + if (values == NULL) { + return ENOMEM; + } + + for (j = 0; j < attr->num_values; j++) { + values[j] = strdup(attr->values[j]); + if (values[j] == NULL) { + sss_sudo_free_values(values); + return ENOMEM; + } + } + + values[attr->num_values] = NULL; + + break; + } + } + + if (values == NULL) { + return ENOENT; + } + + *_values = values; + + return EOK; +} + +void sss_sudo_free_values(char **values) +{ + char **value = NULL; + + if (values == NULL) { + return; + } + + for (value = values; *value != NULL; value++) { + free(*value); + } + + free(values); +} + +void sss_sudo_free_result(struct sss_sudo_result *result) +{ + if (result == NULL) { + return; + } + + sss_sudo_free_rules(result->num_rules, result->rules); + free(result); +} + +void sss_sudo_free_rules(unsigned int num_rules, struct sss_sudo_rule *rules) +{ + struct sss_sudo_rule *rule = NULL; + int i; + + if (rules == NULL) { + return; + } + + for (i = 0; i < num_rules; i++) { + rule = rules + i; + + sss_sudo_free_attrs(rule->num_attrs, rule->attrs); + rule->attrs = NULL; + } + + free(rules); +} + +void sss_sudo_free_attrs(unsigned int num_attrs, struct sss_sudo_attr *attrs) +{ + struct sss_sudo_attr *attr = NULL; + int i, j; + + if (attrs == NULL) { + return; + } + + for (i = 0; i < num_attrs; i++) { + attr = attrs + i; + + free(attr->name); + attr->name = NULL; + + for (j = 0; j < attr->num_values; j++) { + free(attr->values[j]); + attr->values[j] = NULL; + } + + free(attr->values); + } + + free(attrs); +} diff --git a/src/sss_client/sudo/sss_sudo.h b/src/sss_client/sudo/sss_sudo.h new file mode 100644 index 0000000..dc41d9f --- /dev/null +++ b/src/sss_client/sudo/sss_sudo.h @@ -0,0 +1,195 @@ +/* + 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 SSS_SUDO_H_ +#define SSS_SUDO_H_ + +/** + * @defgroup libsss_sudo A library for communication between SUDO and SSSD + * libsss_sudo provides a mechanism to for a SUDO plugin + * to communicate with the sudo responder of SSSD. + * + * @{ + */ + +#include <stdint.h> +#include <sys/types.h> + +/** The value returned when the communication with SUDO is successful and + * the user was found in one of the domains + */ +#define SSS_SUDO_ERROR_OK 0 + +/** + * Component of an sss_rule structure. The component + * has exactly one name and one or more values. + * + */ +struct sss_sudo_attr { + /** The attribute name */ + char *name; + /** A string array that contains all the attribute values */ + char **values; + + /** The number of values the attribute contains. + * + * Attributes are multivalued in general. + */ + unsigned int num_values; +}; + +/** + * One sudo rule. The rule consists of one or more + * attributes of sss_attr type + */ +struct sss_sudo_rule { + /** The number of attributes in the rule */ + unsigned int num_attrs; + + /** List of rule attributes */ + struct sss_sudo_attr *attrs; +}; + +/** + * A result object returned from SSSD. + * + * The result consists of zero or more sss_rule elements. + */ +struct sss_sudo_result { + /** + * The number of rules for the user + * + * In case the user exists in one of SSSD domains + * but no rules match for him, the num_rules element + * is 0. + */ + unsigned int num_rules; + + /** List of rules found */ + struct sss_sudo_rule *rules; +}; + +/** + * @brief Send a request to SSSD to retrieve all SUDO rules for a given + * user. + * + * @param[in] uid The uid of the user to retrieve the rules for. + * @param[in] username The username to retrieve the rules for + * @param[in] domainname The domain name the user is a member of. + * @param[out] _error The result of the search in SSSD's domains. If the + * user was present in the domain, the _error code is + * SSS_SUDO_ERROR_OK and the _result structure is + * returned even if it was empty (in other words + * _result->num_rules == 0). Other problems are returned + * as errno codes. Most prominently these are ENOENT + * (the user was not found with SSSD), EIO (SSSD + * encountered an internal problem) and EINVAL + * (malformed query). + * @param[out] _result Newly allocated structure sss_result that contains + * the rules for the user. If no rules were found but + * the user was valid, this structure is "empty", which + * means that the num_rules member is 0. + * + * @return 0 on success and other errno values on failure. The return value + * denotes whether communication with SSSD was successful. It does not + * tell whether the result contains any rules or whether SSSD knew the + * user at all. That information is transferred in the _error parameter. + */ +int sss_sudo_send_recv(uid_t uid, + const char *username, + const char *domainname, + uint32_t *_error, + struct sss_sudo_result **_result); + +/** + * @brief Send a request to SSSD to retrieve the default options, commonly + * stored in the "cn=defaults" record, + * + * @param[in] uid The uid of the user to retrieve the rules for. + * + * @param[in] username The username to retrieve the rules for. + * + * @param[out] _error The result of the search in SSSD's domains. If the + * options were present in the domain, the _error code + * is SSS_SUDO_ERROR_OK and the _result structure is + * returned even if it was empty (in other words + * _result->num_rules == 0). Other problems are returned + * as errno codes. + * + * @param[out] _domainname The domain name the user is a member of. + * + * @param[out] _result Newly allocated structure sss_result that contains + * the options. If no options were found this structure + * is "empty", which means that the num_rules member + * is 0. + * + * @return 0 on success and other errno values on failure. The return value + * denotes whether communication with SSSD was successful. It does not + * tell whether the result contains any rules or whether SSSD knew the + * user at all. That information is transferred in the _error parameter. + * + * @note The _domainname should be freed using free(). + */ +int sss_sudo_send_recv_defaults(uid_t uid, + const char *username, + uint32_t *_error, + char **_domainname, + struct sss_sudo_result **_result); + +/** + * @brief Free the sss_result structure returned by sss_sudo_send_recv + * + * @param[in] result The sss_result structure to free. The structure was + * previously returned by sss_sudo_get_values(). + */ +void sss_sudo_free_result(struct sss_sudo_result *result); + +/** + * @brief Get all values for a given attribute in an sss_rule + * + * @param[in] e The sss_rule to get values from + * @param[in] attrname The name of the attribute to query from the rule + * @param[out] values A newly allocated list of values the attribute has in + * rule. On success, this parameter is an array of + * NULL-terminated strings, the last element is a NULL + * pointer. On failure (including when the attribute is + * not found), the pointer address is not changed. + * + * @return 0 on success, ENOENT in case the attribute is not found and other + * errno values on failure. + * + * @note the returned values should be freed using sss_sudo_free_values() + */ +int sss_sudo_get_values(struct sss_sudo_rule *e, + const char *attrname, + char ***values); + +/** + * @brief Free the values returned by sss_sudo_get_values + * + * @param[in] values The list of values to free. The values were previously + * returned by sss_sudo_get_values() + */ +void sss_sudo_free_values(char **values); + +/** + * @} + */ +#endif /* SSS_SUDO_H_ */ diff --git a/src/sss_client/sudo/sss_sudo_private.h b/src/sss_client/sudo/sss_sudo_private.h new file mode 100644 index 0000000..2827a94 --- /dev/null +++ b/src/sss_client/sudo/sss_sudo_private.h @@ -0,0 +1,33 @@ +/* + 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 SSS_SUDO_PRIVATE_H_ +#define SSS_SUDO_PRIVATE_H_ + +#include <stdint.h> +#include "sss_client/sudo/sss_sudo.h" + +int sss_sudo_parse_response(const char *message, + size_t message_len, + char **_domainname, + struct sss_sudo_result **_result, + uint32_t *_error); + +#endif /* SSS_SUDO_PRIVATE_H_ */ diff --git a/src/sss_client/sudo/sss_sudo_response.c b/src/sss_client/sudo/sss_sudo_response.c new file mode 100644 index 0000000..7d4bcc5 --- /dev/null +++ b/src/sss_client/sudo/sss_sudo_response.c @@ -0,0 +1,257 @@ +/* + 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 "config.h" + +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <stdint.h> + +#include "sss_client/sss_cli.h" +#include "sss_client/sudo/sss_sudo.h" +#include "sss_client/sudo/sss_sudo_private.h" + +static int sss_sudo_parse_rule(const char *message, + size_t message_len, + size_t *_cursor, + struct sss_sudo_rule *_rule); + +static int sss_sudo_parse_attr(const char *message, + size_t message_len, + size_t *_cursor, + struct sss_sudo_attr *_attr); + +static int sss_sudo_parse_uint32(const char *message, + size_t message_len, + size_t *_cursor, + uint32_t *_number); + +static int sss_sudo_parse_string(const char *message, + size_t message_len, + size_t *_cursor, + char **_str); + +int sss_sudo_parse_response(const char *message, + size_t message_len, + char **_domainname, + struct sss_sudo_result **_result, + uint32_t *_error) +{ + struct sss_sudo_result *result = NULL; + char *domainname = NULL; + size_t cursor = 0; + int ret = EOK; + int i = 0; + + /* error code */ + ret = sss_sudo_parse_uint32(message, message_len, &cursor, _error); + if (ret != EOK || *_error != SSS_SUDO_ERROR_OK) { + return ret; + } + + /* domain name - deprecated + * it won't be used, but we will read it anyway to ease parsing + * TODO: when possible change the protocol */ + ret = sss_sudo_parse_string(message, message_len, &cursor, &domainname); + if (ret != EOK) { + return ret; + } + + free(domainname); + if (_domainname != NULL) { + *_domainname = NULL; + } + + /* result */ + result = malloc(sizeof(struct sss_sudo_result)); + if (result == NULL) { + return ENOMEM; + } + + memset(result, 0, sizeof(struct sss_sudo_result)); + + /* rules_num */ + ret = sss_sudo_parse_uint32(message, message_len, + &cursor, &result->num_rules); + if (ret != EOK) { + goto fail; + } + + /* rules */ + result->rules = calloc(result->num_rules, sizeof(struct sss_sudo_rule)); + if (result->rules == NULL) { + ret = ENOMEM; + goto fail; + } + + for (i = 0; i < result->num_rules; i++) { + ret = sss_sudo_parse_rule(message, message_len, + &cursor, &result->rules[i]); + if (ret != EOK) { + goto fail; + } + } + + *_result = result; + + return EOK; + +fail: + sss_sudo_free_result(result); + return ret; +} + +int sss_sudo_parse_rule(const char *message, + size_t message_len, + size_t *_cursor, + struct sss_sudo_rule *_rule) +{ + int ret = EOK; + int i = 0; + + /* attrs_num */ + ret = sss_sudo_parse_uint32(message, message_len, + _cursor, &_rule->num_attrs); + if (ret != EOK) { + return ret; + } + + /* attrs */ + _rule->attrs = calloc(_rule->num_attrs, sizeof(struct sss_sudo_attr)); + if (_rule->attrs == NULL) { + return ENOMEM; + } + + for (i = 0; i < _rule->num_attrs; i++) { + ret = sss_sudo_parse_attr(message, message_len, + _cursor, &_rule->attrs[i]); + if (ret != EOK) { + return ret; + } + } + + return EOK; +} + +int sss_sudo_parse_attr(const char *message, + size_t message_len, + size_t *_cursor, + struct sss_sudo_attr *_attr) +{ + char *str = NULL; + int ret = EOK; + int i = 0; + + /* name */ + ret = sss_sudo_parse_string(message, message_len, _cursor, &str); + if (ret != EOK) { + return ret; + } + _attr->name = str; + + /* values_num */ + ret = sss_sudo_parse_uint32(message, message_len, + _cursor, &_attr->num_values); + if (ret != EOK) { + return ret; + } + + /* values */ + _attr->values = calloc(_attr->num_values, sizeof(const char*)); + if (_attr->values == NULL) { + return ENOMEM; + } + + for (i = 0; i < _attr->num_values; i++) { + ret = sss_sudo_parse_string(message, message_len, _cursor, &str); + if (ret != EOK) { + return ret; + } + _attr->values[i] = str; + } + + return EOK; +} + +int sss_sudo_parse_uint32(const char *message, + size_t message_len, + size_t *_cursor, + uint32_t *_number) +{ + size_t start_pos = 0; + + if (_cursor == NULL) { + return EINVAL; + } + + start_pos = *_cursor; + + if (start_pos + sizeof(uint32_t) > message_len) { + return EINVAL; + } + + /* expanded SAFEALIGN_COPY_UINT32 macro from util.h */ + memcpy(_number, message + start_pos, sizeof(uint32_t)); + *_cursor = start_pos + sizeof(uint32_t); + + return EOK; +} + +int sss_sudo_parse_string(const char *message, + size_t message_len, + size_t *_cursor, + char **_str) +{ + const char *current = NULL; + char *str = NULL; + size_t start_pos = 0; + size_t len = 0; + size_t maxlen = 0; + + if (_cursor == NULL) { + return EINVAL; + } + + start_pos = *_cursor; + maxlen = message_len - start_pos; + + if (start_pos >= message_len ) { + return EINVAL; + } + + current = message + start_pos; + len = strnlen(current, maxlen); + if (len == maxlen) { + /* the string exceeds message length */ + return EINVAL; + } + + str = strndup(current, len); + if (str == NULL) { + return ENOMEM; + } + + /* go after \0 */ + *_cursor = start_pos + len + 1; + *_str = str; + + return EOK; +} |