/* Authors: Pavel Březina 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 . */ #include #include #include #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)[IPA_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 sysdb_ctx *sysdb; 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->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->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->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 sysdb_ctx *sysdb, 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->sysdb = sysdb; 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, 20, &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, 20, &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, 20, &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; } 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; 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) { return build_filter(mem_ctx, conv->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) { return build_filter(mem_ctx, conv->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) { char *rdn; const char *group; errno_t ret; ret = ipa_get_rdn(mem_ctx, conv->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->sysdb, value, &rdn, MATCHRDN_HOSTGROUP(conv->map_hostgroup)); if (ret == ENOENT) { DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected DN %s\n", value); return NULL; } else if (ret != EOK) { DEBUG(SSSDBG_OP_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) { char *rdn; const char *group; errno_t ret; ret = ipa_get_rdn(mem_ctx, conv->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->sysdb, value, &rdn, MATCHRDN_GROUP(conv->map_group)); if (ret == ENOENT) { DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected DN %s\n", value); return NULL; } else if (ret != EOK) { DEBUG(SSSDBG_OP_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_group(TALLOC_CTX *mem_ctx, struct ipa_sudo_conv *conv, const char *value) { char *rdn; errno_t ret; ret = ipa_get_rdn(mem_ctx, conv->sysdb, value, &rdn, MATCHRDN_GROUP(conv->map_group)); if (ret == ENOENT) { DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected DN %s\n", value); return NULL; } else if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "ipa_get_rdn() failed on value %s [%d]: %s\n", value, ret, sss_strerror(ret)); return NULL; } return rdn; } static const char * convert_runasextusergroup(TALLOC_CTX *mem_ctx, struct ipa_sudo_conv *conv, const char *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) { 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; static struct { const char *ipa; const char *sudo; const char *(*conv_fn)(TALLOC_CTX *mem_ctx, struct ipa_sudo_conv *conv, const char *value); } 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}, {SYSDB_IPA_SUDORULE_RUNASUSER, SYSDB_SUDO_CACHE_AT_RUNASUSER , convert_user}, {SYSDB_IPA_SUDORULE_RUNASGROUP, SYSDB_SUDO_CACHE_AT_RUNASGROUP , convert_group}, {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 , NULL}, {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]); if (value == NULL) { 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, discard_const(&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; }