summaryrefslogtreecommitdiffstats
path: root/src/providers/ipa/ipa_sudo_conversion.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
commit74aa0bc6779af38018a03fd2cf4419fe85917904 (patch)
tree9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/providers/ipa/ipa_sudo_conversion.c
parentInitial commit. (diff)
downloadsssd-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.c1369
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;
+}