diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
commit | 74aa0bc6779af38018a03fd2cf4419fe85917904 (patch) | |
tree | 9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/providers/ipa/ipa_sudo_conversion.c | |
parent | Initial commit. (diff) | |
download | sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip |
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/providers/ipa/ipa_sudo_conversion.c')
-rw-r--r-- | src/providers/ipa/ipa_sudo_conversion.c | 1369 |
1 files changed, 1369 insertions, 0 deletions
diff --git a/src/providers/ipa/ipa_sudo_conversion.c b/src/providers/ipa/ipa_sudo_conversion.c new file mode 100644 index 0000000..220d937 --- /dev/null +++ b/src/providers/ipa/ipa_sudo_conversion.c @@ -0,0 +1,1369 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2015 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 <ldb.h> +#include <talloc.h> +#include <dhash.h> + +#include "providers/ldap/sdap.h" +#include "providers/ipa/ipa_common.h" +#include "providers/ipa/ipa_dn.h" +#include "db/sysdb_sudo.h" +#include "db/sysdb.h" +#include "util/util.h" + +#define SUDO_DN_CMDGROUPS "sudocmdgroups" +#define SUDO_DN_CMDS "sudocmds" +#define SUDO_DN_CONTAINER "sudo" +#define SUDO_DN_CN "cn" + +#define MATCHDN(cat) SUDO_DN_CN, (cat), SUDO_DN_CN, SUDO_DN_CONTAINER +#define MATCHDN_CMDGROUPS MATCHDN(SUDO_DN_CMDGROUPS) +#define MATCHDN_CMDS MATCHDN(SUDO_DN_CMDS) + +#define MATCHRDN_CMDGROUPS(map) (map)[IPA_AT_SUDOCMDGROUP_NAME].name, MATCHDN_CMDGROUPS +#define MATCHRDN_CMDS(attr, map) (map)[attr].name, MATCHDN_CMDS + +#define MATCHRDN_USER(map) (map)[SDAP_AT_USER_NAME].name, "cn", "users", "cn", "accounts" +#define MATCHRDN_GROUP(map) (map)[SDAP_AT_GROUP_NAME].name, "cn", "groups", "cn", "accounts" +#define MATCHRDN_HOST(map) (map)[SDAP_AT_HOST_FQDN].name, "cn", "computers", "cn", "accounts" +#define MATCHRDN_HOSTGROUP(map) (map)[IPA_AT_HOSTGROUP_NAME].name, "cn", "hostgroups", "cn", "accounts" + +struct ipa_sudo_conv { + struct sss_domain_info *dom; + + struct sdap_attr_map *map_rule; + struct sdap_attr_map *map_cmdgroup; + struct sdap_attr_map *map_cmd; + struct sdap_attr_map *map_user; + struct sdap_attr_map *map_group; + struct sdap_attr_map *map_host; + struct sdap_attr_map *map_hostgroup; + + hash_table_t *rules; + hash_table_t *cmdgroups; + hash_table_t *cmds; +}; + +struct ipa_sudo_dn_list { + struct ipa_sudo_dn_list *prev, *next; + const char *dn; +}; + +struct ipa_sudo_rulemember { + struct ipa_sudo_dn_list *cmdgroups; + struct ipa_sudo_dn_list *cmds; +}; + +struct ipa_sudo_rule { + struct sysdb_attrs *attrs; + struct ipa_sudo_rulemember allow; + struct ipa_sudo_rulemember deny; +}; + +struct ipa_sudo_cmdgroup { + struct ipa_sudo_dn_list *cmds; + const char **expanded; +}; + +static size_t +ipa_sudo_dn_list_count(struct ipa_sudo_dn_list *list) +{ + struct ipa_sudo_dn_list *item; + size_t i; + + for (i = 0, item = list; item != NULL; item = item->next, i++) { + /* no op */ + } + + return i; +} + +static errno_t +ipa_sudo_conv_store(hash_table_t *table, + const char *key, + void *value) +{ + hash_key_t hkey; + hash_value_t hvalue; + int hret; + + if (table == NULL || key == NULL) { + return EINVAL; + } + + hkey.type = HASH_KEY_STRING; + hkey.str = discard_const(key); + + /* If value is NULL we don't want to override existing entry. */ + if (value == NULL && hash_has_key(table, &hkey)) { + return EEXIST; + } + + hvalue.type = HASH_VALUE_PTR; + hvalue.ptr = value; + + hret = hash_enter(table, &hkey, &hvalue); + if (hret != HASH_SUCCESS) { + return EIO; + } + + if (value != NULL) { + talloc_steal(table, value); + } + + return EOK; +} + +static void * +ipa_sudo_conv_lookup(hash_table_t *table, + const char *key) +{ + hash_key_t hkey; + hash_value_t hvalue; + int hret; + + hkey.type = HASH_KEY_STRING; + hkey.str = discard_const(key); + + hret = hash_lookup(table, &hkey, &hvalue); + if (hret == HASH_ERROR_KEY_NOT_FOUND) { + DEBUG(SSSDBG_OP_FAILURE, "Key not found %s\n", key); + return NULL; + } else if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup value [%d]\n", hret); + return NULL; + } + + return hvalue.ptr; +} + +static errno_t +store_rulemember(TALLOC_CTX *mem_ctx, + struct ipa_sudo_dn_list **list, + hash_table_t *table, + const char *dn) +{ + struct ipa_sudo_dn_list *item; + errno_t ret; + + item = talloc_zero(mem_ctx, struct ipa_sudo_dn_list); + if (item == NULL) { + return ENOMEM; + } + + ret = ipa_sudo_conv_store(table, dn, NULL); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to store DN %s [%d]: %s\n", + dn, ret, sss_strerror(ret)); + goto done; + } + + item->dn = talloc_steal(item, dn); + DLIST_ADD(*list, item); + +done: + if (ret != EOK && ret != EEXIST) { + talloc_free(item); + } + + return ret; +} + +static bool is_ipacmdgroup(struct ipa_sudo_conv *conv, const char *dn) +{ + if (ipa_check_rdn_bool(conv->dom->sysdb, dn, + MATCHRDN_CMDGROUPS(conv->map_cmdgroup))) { + return true; + } + + return false; +} + +static bool is_ipacmd(struct ipa_sudo_conv *conv, const char *dn) +{ + if (ipa_check_rdn_bool(conv->dom->sysdb, dn, + MATCHRDN_CMDS(IPA_AT_SUDOCMD_UUID, conv->map_cmd))) { + return true; + } + + /* For older versions of FreeIPA than 3.1. */ + if (ipa_check_rdn_bool(conv->dom->sysdb, dn, + MATCHRDN_CMDS(IPA_AT_SUDOCMD_CMD, conv->map_cmd))) { + return true; + } + + return false; +} + +static errno_t +process_rulemember(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + struct ipa_sudo_rulemember *rulemember, + struct sysdb_attrs *rule, + const char *attr) +{ + TALLOC_CTX *tmp_ctx; + const char **members; + errno_t ret; + int i; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = sysdb_attrs_get_string_array(rule, attr, tmp_ctx, &members); + if (ret == ENOENT) { + ret = EOK; + goto done; + } else if (ret != EOK) { + goto done; + } + + for (i = 0; members[i] != NULL; i++) { + if (is_ipacmdgroup(conv, members[i])) { + ret = store_rulemember(mem_ctx, &rulemember->cmdgroups, + conv->cmdgroups, members[i]); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_INTERNAL, "Found sudo command group %s\n", + members[i]); + } else if (ret != EEXIST) { + goto done; + } + } else if (is_ipacmd(conv, members[i])) { + ret = store_rulemember(mem_ctx, &rulemember->cmds, + conv->cmds, members[i]); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_INTERNAL, "Found sudo command %s\n", + members[i]); + } else if (ret != EEXIST) { + goto done; + } + } else { + DEBUG(SSSDBG_MINOR_FAILURE, "Invalid member DN %s, skipping...\n", + members[i]); + continue; + } + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +process_allowcmd(struct ipa_sudo_conv *conv, + struct ipa_sudo_rule *rule) +{ + return process_rulemember(rule, conv, &rule->allow, rule->attrs, + SYSDB_IPA_SUDORULE_ALLOWCMD); +} + +static errno_t +process_denycmd(struct ipa_sudo_conv *conv, + struct ipa_sudo_rule *rule) +{ + return process_rulemember(rule, conv, &rule->deny, rule->attrs, + SYSDB_IPA_SUDORULE_DENYCMD); +} + +static errno_t +process_cmdgroupmember(struct ipa_sudo_conv *conv, + struct ipa_sudo_cmdgroup *cmdgroup, + struct sysdb_attrs *attrs) +{ + TALLOC_CTX *tmp_ctx; + struct ipa_sudo_dn_list *item; + const char **members; + errno_t ret; + int i; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = sysdb_attrs_get_string_array(attrs, SYSDB_MEMBER, tmp_ctx, &members); + if (ret == ENOENT) { + ret = EOK; + goto done; + } else if (ret != EOK) { + goto done; + } + + for (i = 0; members[i] != NULL; i++) { + ret = ipa_sudo_conv_store(conv->cmds, members[i], NULL); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_INTERNAL, "Found sudo command %s\n", + members[i]); + } else if (ret != EEXIST) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to store DN [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + item = talloc_zero(tmp_ctx, struct ipa_sudo_dn_list); + if (item == NULL) { + ret = ENOMEM; + goto done; + } + + item->dn = talloc_steal(item, members[i]); + DLIST_ADD(cmdgroup->cmds, item); + talloc_steal(cmdgroup, item); + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +struct ipa_sudo_conv * +ipa_sudo_conv_init(TALLOC_CTX *mem_ctx, + struct sss_domain_info *dom, + struct sdap_attr_map *map_rule, + struct sdap_attr_map *map_cmdgroup, + struct sdap_attr_map *map_cmd, + struct sdap_attr_map *map_user, + struct sdap_attr_map *map_group, + struct sdap_attr_map *map_host, + struct sdap_attr_map *map_hostgroup) +{ + struct ipa_sudo_conv *conv; + errno_t ret; + + conv = talloc_zero(mem_ctx, struct ipa_sudo_conv); + if (conv == NULL) { + return NULL; + } + + conv->dom = dom; + conv->map_rule = map_rule; + conv->map_cmdgroup = map_cmdgroup; + conv->map_cmd = map_cmd; + conv->map_user = map_user; + conv->map_group = map_group; + conv->map_host = map_host; + conv->map_hostgroup = map_hostgroup; + + ret = sss_hash_create(conv, 0, &conv->rules); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + ret = sss_hash_create(conv, 0, &conv->cmdgroups); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + ret = sss_hash_create(conv, 0, &conv->cmds); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + +done: + if (ret != EOK) { + talloc_free(conv); + return NULL; + } + + return conv; +} + +errno_t +ipa_sudo_conv_rules(struct ipa_sudo_conv *conv, + struct sysdb_attrs **rules, + size_t num_rules) +{ + struct ipa_sudo_rule *rule = NULL; + const char *key; + errno_t ret; + size_t i; + + if (num_rules == 0) { + /* We're done here. */ + return EOK; + } + + for (i = 0; i < num_rules; i++) { + ret = sysdb_attrs_get_string(rules[i], SYSDB_NAME, &key); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Failed to get rule name, skipping " + "[%d]: %s\n", ret, sss_strerror(ret)); + continue; + } + + rule = talloc_zero(conv->rules, struct ipa_sudo_rule); + if (rule == NULL) { + ret = ENOMEM; + goto done; + } + + rule->attrs = rules[i]; + + ret = process_allowcmd(conv, rule); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to process memberAllowCmd " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } + + ret = process_denycmd(conv, rule); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to process memberDenyCmd " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } + + ret = ipa_sudo_conv_store(conv->rules, key, rule); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to store rule into table " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + talloc_steal(rule, rule->attrs); + rule = NULL; + } + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(rule); + } + + return ret; +} + +errno_t +ipa_sudo_conv_cmdgroups(struct ipa_sudo_conv *conv, + struct sysdb_attrs **cmdgroups, + size_t num_cmdgroups) +{ + struct ipa_sudo_cmdgroup *cmdgroup = NULL; + const char *key; + errno_t ret; + size_t i; + + if (num_cmdgroups == 0) { + /* We're done here. */ + return EOK; + } + + for (i = 0; i < num_cmdgroups; i++) { + ret = sysdb_attrs_get_string(cmdgroups[i], SYSDB_ORIG_DN, &key); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Failed to get command group DN, " + "skipping [%d]: %s\n", ret, sss_strerror(ret)); + continue; + } + + cmdgroup = talloc_zero(conv->cmdgroups, struct ipa_sudo_cmdgroup); + if (cmdgroup == NULL) { + ret = ENOMEM; + goto done; + } + + ret = process_cmdgroupmember(conv, cmdgroup, cmdgroups[i]); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to process member " + "[%d]: %s\n", ret, sss_strerror(ret)); + return ret; + } + + ret = ipa_sudo_conv_store(conv->cmdgroups, key, cmdgroup); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to store command group into " + "table [%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + cmdgroup = NULL; + } + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(cmdgroup); + } + + return ret; +} + +errno_t +ipa_sudo_conv_cmds(struct ipa_sudo_conv *conv, + struct sysdb_attrs **cmds, + size_t num_cmds) +{ + const char *key; + const char *cmd; + errno_t ret; + size_t i; + + if (num_cmds == 0) { + /* We're done here. */ + return EOK; + } + + for (i = 0; i < num_cmds; i++) { + ret = sysdb_attrs_get_string(cmds[i], SYSDB_ORIG_DN, &key); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Failed to get command DN, skipping " + "[%d]: %s\n", ret, sss_strerror(ret)); + continue; + } + + ret = sysdb_attrs_get_string(cmds[i], SYSDB_IPA_SUDOCMD_SUDOCMD, &cmd); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Failed to get command, skipping " + "[%d]: %s\n", ret, sss_strerror(ret)); + continue; + } + + ret = ipa_sudo_conv_store(conv->cmds, key, discard_const(cmd)); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to store command into table " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + } + + ret = EOK; + +done: + return ret; +} + +bool +ipa_sudo_conv_has_cmdgroups(struct ipa_sudo_conv *conv) +{ + return hash_count(conv->cmdgroups) == 0; +} + +bool +ipa_sudo_conv_has_cmds(struct ipa_sudo_conv *conv) +{ + return hash_count(conv->cmds) == 0; +} + +bool +ipa_sudo_cmdgroups_exceed_threshold(struct ipa_sudo_conv *conv, int threshold) +{ + return (hash_count(conv->cmdgroups)) > threshold; +} +bool +ipa_sudo_cmds_exceed_threshold(struct ipa_sudo_conv *conv, int threshold) +{ + return (hash_count(conv->cmds)) > threshold; +} + +typedef errno_t (*ipa_sudo_conv_rdn_fn)(TALLOC_CTX *mem_ctx, + struct sdap_attr_map *map, + struct sysdb_ctx *sysdb, + const char *dn, + char **_rdn_val, + const char **_rdn_attr); + +static errno_t get_sudo_cmdgroup_rdn(TALLOC_CTX *mem_ctx, + struct sdap_attr_map *map, + struct sysdb_ctx *sysdb, + const char *dn, + char **_rdn_val, + const char **_rdn_attr) +{ + char *rdn_val; + errno_t ret; + + ret = ipa_get_rdn(mem_ctx, sysdb, dn, &rdn_val, + MATCHRDN_CMDGROUPS(map)); + if (ret != EOK) { + return ret; + } + + *_rdn_val = rdn_val; + *_rdn_attr = map[IPA_AT_SUDOCMDGROUP_NAME].name; + + return EOK; +} + +static errno_t get_sudo_cmd_rdn(TALLOC_CTX *mem_ctx, + struct sdap_attr_map *map, + struct sysdb_ctx *sysdb, + const char *dn, + char **_rdn_val, + const char **_rdn_attr) +{ + char *rdn_val; + errno_t ret; + + ret = ipa_get_rdn(mem_ctx, sysdb, dn, &rdn_val, + MATCHRDN_CMDS(IPA_AT_SUDOCMD_UUID, map)); + if (ret == EOK) { + *_rdn_val = rdn_val; + *_rdn_attr = map[IPA_AT_SUDOCMD_UUID].name; + + return EOK; + } else if (ret != ENOENT) { + return ret; + } + + /* For older versions of FreeIPA than 3.1. */ + ret = ipa_get_rdn(mem_ctx, sysdb, dn, &rdn_val, + MATCHRDN_CMDS(IPA_AT_SUDOCMD_CMD, map)); + if (ret != EOK) { + return ret; + } + + *_rdn_val = rdn_val; + *_rdn_attr = map[IPA_AT_SUDOCMD_CMD].name; + + return EOK; +} + +static char * +build_filter(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + hash_table_t *table, + struct sdap_attr_map *map, + ipa_sudo_conv_rdn_fn rdn_fn) +{ + TALLOC_CTX *tmp_ctx; + hash_key_t *keys; + unsigned long int count; + unsigned long int i; + char *filter = NULL; + char *rdn_val; + const char *rdn_attr; + char *safe_rdn; + errno_t ret; + int hret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return NULL; + } + + hret = hash_keys(table, &count, &keys); + if (hret != HASH_SUCCESS) { + ret = ENOMEM; + goto done; + } + + talloc_steal(tmp_ctx, keys); + + filter = talloc_strdup(tmp_ctx, ""); + if (filter == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < count; i++) { + ret = rdn_fn(tmp_ctx, map, sysdb, keys[i].str, &rdn_val, &rdn_attr); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get member %s [%d]: %s\n", + keys[i].str, ret, sss_strerror(ret)); + goto done; + } + + ret = sss_filter_sanitize(tmp_ctx, rdn_val, &safe_rdn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to sanitize DN " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + filter = talloc_asprintf_append(filter, "(%s=%s)", rdn_attr, safe_rdn); + if (filter == NULL) { + ret = ENOMEM; + goto done; + } + } + + /* objectClass is always first */ + filter = talloc_asprintf(filter, "(&(objectClass=%s)(|%s))", + map[0].name, filter); + if (filter == NULL) { + ret = ENOMEM; + goto done; + } + + talloc_steal(mem_ctx, filter); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + + if (ret != EOK) { + return NULL; + } + + return filter; +} + +char * +ipa_sudo_conv_cmdgroup_filter(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + int cmd_threshold) +{ + if (ipa_sudo_cmdgroups_exceed_threshold(conv, cmd_threshold)) { + DEBUG(SSSDBG_TRACE_FUNC, + "Command threshold [%d] exceeded, retrieving all sudo command " + "groups\n", cmd_threshold); + return talloc_asprintf(mem_ctx, "(objectClass=%s)", + conv->map_cmdgroup->name); + } else { + return build_filter(mem_ctx, conv->dom->sysdb, conv->cmdgroups, + conv->map_cmdgroup, get_sudo_cmdgroup_rdn); + } +} + +char * +ipa_sudo_conv_cmd_filter(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + int cmd_threshold) +{ + if (ipa_sudo_cmdgroups_exceed_threshold(conv, cmd_threshold)) { + DEBUG(SSSDBG_TRACE_FUNC, + "Command threshold [%d] exceeded, retrieving all sudo commands\n", + cmd_threshold); + return talloc_asprintf(mem_ctx, "(objectClass=%s)", + conv->map_cmd->name); + } else { + return build_filter(mem_ctx, conv->dom->sysdb, conv->cmds, + conv->map_cmd, get_sudo_cmd_rdn); + } +} + +struct ipa_sudo_conv_result_ctx { + struct ipa_sudo_conv *conv; + struct sysdb_attrs **rules; + size_t num_rules; + errno_t ret; +}; + +static const char * +convert_host(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + const char *value, + bool *skip_entry) +{ + char *rdn; + const char *group; + errno_t ret; + + *skip_entry = false; + + ret = ipa_get_rdn(mem_ctx, conv->dom->sysdb, value, &rdn, + MATCHRDN_HOST(conv->map_host)); + if (ret == EOK) { + return rdn; + } else if (ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n", + value, ret, sss_strerror(ret)); + return NULL; + } + + ret = ipa_get_rdn(mem_ctx, conv->dom->sysdb, value, &rdn, + MATCHRDN_HOSTGROUP(conv->map_hostgroup)); + if (ret == ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected DN %s: Skipping\n", value); + *skip_entry = true; + return NULL; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n", + value, ret, sss_strerror(ret)); + return NULL; + } + + group = talloc_asprintf(mem_ctx, "+%s", rdn); + talloc_free(rdn); + + return group; +} + +static const char * +convert_user(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + const char *value, + bool *skip_entry) +{ + char *rdn; + const char *group; + errno_t ret; + + *skip_entry = false; + + ret = ipa_get_rdn(mem_ctx, conv->dom->sysdb, value, &rdn, + MATCHRDN_USER(conv->map_user)); + if (ret == EOK) { + return rdn; + } else if (ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n", + value, ret, sss_strerror(ret)); + return NULL; + } + + ret = ipa_get_rdn(mem_ctx, conv->dom->sysdb, value, &rdn, + MATCHRDN_GROUP(conv->map_group)); + if (ret == ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected DN %s: Skipping\n", value); + *skip_entry = true; + return NULL; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n", + value, ret, sss_strerror(ret)); + return NULL; + } + + group = talloc_asprintf(mem_ctx, "%%%s", rdn); + talloc_free(rdn); + + return group; +} + +static const char * +convert_user_fqdn(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + const char *value, + bool *skip_entry) +{ + const char *shortname = NULL; + char *fqdn = NULL; + + *skip_entry = false; + + shortname = convert_user(mem_ctx, conv, value, skip_entry); + if (shortname == NULL) { + return NULL; + } + + fqdn = sss_create_internal_fqname(mem_ctx, shortname, conv->dom->name); + talloc_free(discard_const(shortname)); + return fqdn; +} + +static const char * +convert_ext_user(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + const char *value, + bool *skip_entry) +{ + /* If value is already fully qualified, return it as it is */ + if (strrchr(value, '@') != NULL) { + return talloc_strdup(mem_ctx, value); + } + return sss_create_internal_fqname(mem_ctx, value, conv->dom->name); +} + +static const char * +convert_group(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + const char *value, + bool *skip_entry) +{ + char *rdn; + errno_t ret; + + *skip_entry = false; + + ret = ipa_get_rdn(mem_ctx, conv->dom->sysdb, value, &rdn, + MATCHRDN_GROUP(conv->map_group)); + if (ret == ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected DN %s: Skipping\n", value); + *skip_entry = true; + return NULL; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n", + value, ret, sss_strerror(ret)); + return NULL; + } + + return rdn; +} + +static const char * +convert_group_fqdn(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + const char *value, + bool *skip_entry) +{ + const char *shortname = NULL; + char *fqdn = NULL; + + *skip_entry = false; + + shortname = convert_group(mem_ctx, conv, value, skip_entry); + if (shortname == NULL) { + return NULL; + } + + fqdn = sss_create_internal_fqname(mem_ctx, shortname, conv->dom->name); + talloc_free(discard_const(shortname)); + return fqdn; +} + +static const char * +convert_runasextusergroup(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + const char *value, + bool *skip_entry) +{ + if (value == NULL) + return NULL; + + if (value[0] == '%') + return talloc_strdup(mem_ctx, value); + + return talloc_asprintf(mem_ctx, "%%%s", value); +} + +static const char * +convert_cat(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + const char *value, + bool *skip_entry) +{ + + *skip_entry = false; + + if (strcmp(value, "all") == 0) { + return talloc_strdup(mem_ctx, "ALL"); + } + + return value; +} + +static errno_t +convert_attributes(struct ipa_sudo_conv *conv, + struct ipa_sudo_rule *rule, + struct sysdb_attrs *attrs) +{ + TALLOC_CTX *tmp_ctx; + const char **values; + const char *value; + errno_t ret; + int i, j; + bool skip_entry; + static struct { + const char *ipa; + const char *sudo; + const char *(*conv_fn)(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + const char *value, + bool *skip_entry); + } table[] = {{SYSDB_NAME, SYSDB_SUDO_CACHE_AT_CN , NULL}, + {SYSDB_IPA_SUDORULE_HOST, SYSDB_SUDO_CACHE_AT_HOST , convert_host}, + {SYSDB_IPA_SUDORULE_USER, SYSDB_SUDO_CACHE_AT_USER , convert_user_fqdn}, + {SYSDB_IPA_SUDORULE_RUNASUSER, SYSDB_SUDO_CACHE_AT_RUNASUSER , convert_user_fqdn}, + {SYSDB_IPA_SUDORULE_RUNASGROUP, SYSDB_SUDO_CACHE_AT_RUNASGROUP , convert_group_fqdn}, + {SYSDB_IPA_SUDORULE_OPTION, SYSDB_SUDO_CACHE_AT_OPTION , NULL}, + {SYSDB_IPA_SUDORULE_NOTAFTER, SYSDB_SUDO_CACHE_AT_NOTAFTER , NULL}, + {SYSDB_IPA_SUDORULE_NOTBEFORE, SYSDB_SUDO_CACHE_AT_NOTBEFORE , NULL}, + {SYSDB_IPA_SUDORULE_SUDOORDER, SYSDB_SUDO_CACHE_AT_ORDER , NULL}, + {SYSDB_IPA_SUDORULE_CMDCATEGORY, SYSDB_SUDO_CACHE_AT_COMMAND , convert_cat}, + {SYSDB_IPA_SUDORULE_HOSTCATEGORY, SYSDB_SUDO_CACHE_AT_HOST , convert_cat}, + {SYSDB_IPA_SUDORULE_USERCATEGORY, SYSDB_SUDO_CACHE_AT_USER , convert_cat}, + {SYSDB_IPA_SUDORULE_RUNASUSERCATEGORY, SYSDB_SUDO_CACHE_AT_RUNASUSER , convert_cat}, + {SYSDB_IPA_SUDORULE_RUNASGROUPCATEGORY, SYSDB_SUDO_CACHE_AT_RUNASGROUP , convert_cat}, + {SYSDB_IPA_SUDORULE_RUNASEXTUSER, SYSDB_SUDO_CACHE_AT_RUNASUSER , NULL}, + {SYSDB_IPA_SUDORULE_RUNASEXTGROUP, SYSDB_SUDO_CACHE_AT_RUNASGROUP , NULL}, + {SYSDB_IPA_SUDORULE_RUNASEXTUSERGROUP, SYSDB_SUDO_CACHE_AT_RUNASUSER , convert_runasextusergroup}, + {SYSDB_IPA_SUDORULE_EXTUSER, SYSDB_SUDO_CACHE_AT_USER , convert_ext_user}, + {SYSDB_IPA_SUDORULE_ALLOWCMD, SYSDB_IPA_SUDORULE_ORIGCMD , NULL}, + {SYSDB_IPA_SUDORULE_DENYCMD, SYSDB_IPA_SUDORULE_ORIGCMD , NULL}, + {NULL, NULL, NULL}}; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + for (i = 0; table[i].ipa != NULL; i++) { + ret = sysdb_attrs_get_string_array(rule->attrs, table[i].ipa, + tmp_ctx, &values); + if (ret == ENOENT) { + continue; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read attribute " + "%s [%d]: %s\n", table[i].ipa, ret, sss_strerror(ret)); + goto done; + } + + for (j = 0; values[j] != NULL; j++) { + if (table[i].conv_fn != NULL) { + value = table[i].conv_fn(tmp_ctx, conv, values[j], &skip_entry); + if (value == NULL) { + if (skip_entry) { + continue; + } else { + ret = ENOMEM; + goto done; + } + } + } else { + value = values[j]; + } + + ret = sysdb_attrs_add_string_safe(attrs, table[i].sudo, value); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add attribute " + "%s [%d]: %s\n", table[i].sudo, ret, sss_strerror(ret)); + goto done; + } + } + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static const char ** +combine_cmdgroups(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + struct ipa_sudo_dn_list *list) +{ + TALLOC_CTX *tmp_ctx; + struct ipa_sudo_cmdgroup *cmdgroup; + struct ipa_sudo_dn_list *listitem; + const char **values = NULL; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return NULL; + } + + values = talloc_zero_array(tmp_ctx, const char *, 1); + if (values == NULL) { + talloc_free(tmp_ctx); + return NULL; + } + + DLIST_FOR_EACH(listitem, list) { + cmdgroup = ipa_sudo_conv_lookup(conv->cmdgroups, listitem->dn); + if (cmdgroup == NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, + "ipa_sudo_conv_lookup failed for DN:%s\n", listitem->dn); + continue; + } + + ret = add_strings_lists(mem_ctx, values, cmdgroup->expanded, + false, &values); + if (ret != EOK) { + talloc_free(tmp_ctx); + return NULL; + } + } + + talloc_steal(mem_ctx, values); + talloc_free(tmp_ctx); + + return values; +} + +static const char ** +combine_cmds(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + struct ipa_sudo_dn_list *list) +{ + struct ipa_sudo_dn_list *listitem; + const char **values; + const char *command; + size_t count; + size_t i; + + count = ipa_sudo_dn_list_count(list); + + values = talloc_zero_array(mem_ctx, const char *, count + 1); + if (values == NULL) { + return NULL; + } + + i = 0; + DLIST_FOR_EACH(listitem, list) { + command = ipa_sudo_conv_lookup(conv->cmds, listitem->dn); + if (command == NULL) { + continue; + } + + values[i] = command; + i++; + } + + return values; +} + +static errno_t +build_sudocommand(struct ipa_sudo_conv *conv, + struct ipa_sudo_rulemember *mlist, + struct sysdb_attrs *attrs, + char prefix) +{ + TALLOC_CTX *tmp_ctx; + const char **cmds[2]; + const char *command; + errno_t ret; + int i, j; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + cmds[0] = combine_cmdgroups(tmp_ctx, conv, mlist->cmdgroups); + if (cmds[0] == NULL) { + ret = ENOMEM; + goto done; + } + + cmds[1] = combine_cmds(tmp_ctx, conv, mlist->cmds); + if (cmds[1] == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < 2; i++) { + for (j = 0; cmds[i][j] != NULL; j++) { + if (prefix == '\0') { + command = cmds[i][j]; + } else { + command = talloc_asprintf(tmp_ctx, "%c%s", prefix, cmds[i][j]); + if (command == NULL) { + ret = ENOMEM; + goto done; + } + } + + ret = sysdb_attrs_add_string_safe(attrs, + SYSDB_SUDO_CACHE_AT_COMMAND, command); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add attribute " + "%s [%d]: %s\n", SYSDB_SUDO_CACHE_AT_COMMAND, + ret, sss_strerror(ret)); + goto done; + } + } + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +convert_sudocommand(struct ipa_sudo_conv *conv, + struct ipa_sudo_rule *rule, + struct sysdb_attrs *attrs) +{ + TALLOC_CTX *tmp_ctx; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = build_sudocommand(conv, &rule->allow, attrs, '\0'); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build allow commands " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + ret = build_sudocommand(conv, &rule->deny, attrs, '!'); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build deny commands " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static bool +rules_iterator(hash_entry_t *item, + void *user_data) +{ + struct ipa_sudo_conv_result_ctx *ctx = user_data; + struct ipa_sudo_rule *rule = item->value.ptr; + struct sysdb_attrs *attrs; + + if (ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: ctx is NULL\n"); + return false; + } + + if (rule == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: rule is NULL\n"); + ctx->ret = ERR_INTERNAL; + return false; + } + + attrs = sysdb_new_attrs(ctx->rules); + if (attrs == NULL) { + ctx->ret = ENOMEM; + return false; + } + + ctx->ret = convert_attributes(ctx->conv, rule, attrs); + if (ctx->ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to convert attributes [%d]: %s\n", + ctx->ret, sss_strerror(ctx->ret)); + talloc_free(attrs); + return false; + } + + ctx->ret = convert_sudocommand(ctx->conv, rule, attrs); + if (ctx->ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to build sudoCommand [%d]: %s\n", + ctx->ret, sss_strerror(ctx->ret)); + talloc_free(attrs); + return false; + } + + ctx->rules[ctx->num_rules] = attrs; + ctx->num_rules++; + + return true; +} + +static bool +cmdgroups_iterator(hash_entry_t *item, + void *user_data) +{ + struct ipa_sudo_conv_result_ctx *ctx = user_data; + struct ipa_sudo_cmdgroup *cmdgroup = item->value.ptr; + const char **values; + + if (ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: ctx is NULL\n"); + return false; + } + + if (cmdgroup == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: rule is NULL\n"); + ctx->ret = ERR_INTERNAL; + return false; + } + + values = combine_cmds(cmdgroup, ctx->conv, cmdgroup->cmds); + if (values == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand commands\n"); + ctx->ret = ENOMEM; + return false; + } + + cmdgroup->expanded = values; + ctx->ret = EOK; + + return true; +} + +errno_t +ipa_sudo_conv_result(TALLOC_CTX *mem_ctx, + struct ipa_sudo_conv *conv, + struct sysdb_attrs ***_rules, + size_t *_num_rules) +{ + struct ipa_sudo_conv_result_ctx ctx; + struct sysdb_attrs **rules; + unsigned long num_rules; + int hret; + + num_rules = hash_count(conv->rules); + if (num_rules == 0) { + *_rules = NULL; + *_num_rules = 0; + return EOK; + } + + ctx.conv = conv; + ctx.rules = NULL; + ctx.num_rules = 0; + + /* If there are no cmdgroups the iterator is not called and ctx.ret is + * uninitialized. Since it is ok that there are no cmdgroups initializing + * ctx.ret to EOK. */ + ctx.ret = EOK; + + /* Expand commands in command groups. */ + hret = hash_iterate(conv->cmdgroups, cmdgroups_iterator, &ctx); + if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to iterate over command groups " + "[%d]\n", hret); + return EIO; + } + + if (ctx.ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand command groups " + "[%d]: %s\n", ctx.ret, sss_strerror(ctx.ret)); + return ctx.ret; + } + + /* Convert rules. */ + rules = talloc_zero_array(mem_ctx, struct sysdb_attrs *, num_rules); + if (rules == NULL) { + return ENOMEM; + } + + ctx.rules = rules; + ctx.num_rules = 0; + + hret = hash_iterate(conv->rules, rules_iterator, &ctx); + if (hret != HASH_SUCCESS) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to iterate over rules [%d]\n", hret); + return EIO; + } + + if (ctx.ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to convert rules [%d]: %s\n", + ctx.ret, sss_strerror(ctx.ret)); + talloc_free(rules); + return ctx.ret; + } + + *_rules = ctx.rules; + *_num_rules = ctx.num_rules; + + return EOK; +} |