/*
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)[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 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, 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->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)
{
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)
{
char *rdn;
const char *group;
errno_t ret;
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\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->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\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_fqdn(TALLOC_CTX *mem_ctx,
struct ipa_sudo_conv *conv,
const char *value)
{
const char *shortname = NULL;
char *fqdn = NULL;
shortname = convert_user(mem_ctx, conv, value);
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_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->dom->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_fqdn},
{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;
}