diff options
Diffstat (limited to 'src/tools/sss_override.c')
-rw-r--r-- | src/tools/sss_override.c | 1975 |
1 files changed, 1975 insertions, 0 deletions
diff --git a/src/tools/sss_override.c b/src/tools/sss_override.c new file mode 100644 index 0000000..cfd8f17 --- /dev/null +++ b/src/tools/sss_override.c @@ -0,0 +1,1975 @@ +/* + 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 <stdlib.h> + +#include "util/util.h" +#include "util/crypto/sss_crypto.h" +#include "db/sysdb.h" +#include "tools/common/sss_tools.h" +#include "tools/common/sss_colondb.h" + +#define LOCALVIEW SYSDB_LOCAL_VIEW_NAME +#define ORIGNAME "originalName" + +struct override_user { + const char *input_name; + const char *orig_name; + const char *sysdb_name; + struct sss_domain_info *domain; + + const char *name; + uid_t uid; + gid_t gid; + const char *home; + const char *shell; + const char *gecos; + const char *cert; +}; + +struct override_group { + const char *input_name; + const char *orig_name; + const char *sysdb_name; + struct sss_domain_info *domain; + + const char *name; + gid_t gid; +}; + +static errno_t parse_cmdline(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + struct poptOption *options, + const char **_input_name, + const char **_orig_name, + struct sss_domain_info **_domain) +{ + enum sss_tool_opt require; + const char *input_name = NULL; + const char *orig_name; + struct sss_domain_info *domain; + errno_t ret; + + *_input_name = NULL; + require = options == NULL ? SSS_TOOL_OPT_OPTIONAL : SSS_TOOL_OPT_REQUIRED; + + ret = sss_tool_popt_ex(cmdline, options, require, + NULL, NULL, "NAME", _("Specify name."), + SSS_TOOL_OPT_REQUIRED, &input_name, NULL); + if (ret != EXIT_SUCCESS) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n"); + return ret; + } + + ret = sss_tool_parse_name(tool_ctx, tool_ctx, input_name, + &orig_name, &domain); + if (ret != EOK) { + ERROR("Unable to parse name %s.\n", input_name); + free(discard_const(input_name)); + return ret; + } + + *_input_name = input_name; + *_orig_name = orig_name; + *_domain = domain; + + return EXIT_SUCCESS; +} + +static errno_t parse_cmdline_user_add(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + struct override_user *user) +{ + struct poptOption options[] = { + {"name", 'n', POPT_ARG_STRING, &user->name, 0, _("Override name"), NULL }, + {"uid", 'u', POPT_ARG_INT, &user->uid, 0, _("Override uid (non-zero value)"), NULL }, + {"gid", 'g', POPT_ARG_INT, &user->gid, 0, _("Override gid (non-zero value)"), NULL }, + {"home", 'h', POPT_ARG_STRING, &user->home, 0, _("Override home directory"), NULL }, + {"shell", 's', POPT_ARG_STRING, &user->shell, 0, _("Override shell"), NULL }, + {"gecos", 'c', POPT_ARG_STRING, &user->gecos, 0, _("Override gecos"), NULL }, + {"certificate", 'x', POPT_ARG_STRING, &user->cert, 0, _("Override certificate"), NULL }, + POPT_TABLEEND + }; + + return parse_cmdline(cmdline, tool_ctx, options, &user->input_name, + &user->orig_name, &user->domain); +} + +static errno_t parse_cmdline_user_del(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + struct override_user *user) +{ + return parse_cmdline(cmdline, tool_ctx, NULL, &user->input_name, + &user->orig_name, &user->domain); +} + +static errno_t parse_cmdline_user_show(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + struct override_user *user) +{ + return parse_cmdline(cmdline, tool_ctx, NULL, &user->input_name, + &user->orig_name, &user->domain); +} + +static errno_t parse_cmdline_group_add(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + struct override_group *group) +{ + struct poptOption options[] = { + {"name", 'n', POPT_ARG_STRING, &group->name, 0, _("Override name"), NULL }, + {"gid", 'g', POPT_ARG_INT, &group->gid, 0, _("Override gid"), NULL }, + POPT_TABLEEND + }; + + return parse_cmdline(cmdline, tool_ctx, options, &group->input_name, + &group->orig_name, &group->domain); +} + +static errno_t parse_cmdline_group_del(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + struct override_group *group) +{ + return parse_cmdline(cmdline, tool_ctx, NULL, &group->input_name, + &group->orig_name, &group->domain); +} + +static errno_t parse_cmdline_group_show(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + struct override_group *group) +{ + return parse_cmdline(cmdline, tool_ctx, NULL, &group->input_name, + &group->orig_name, &group->domain); +} + +static errno_t parse_cmdline_find(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + struct sss_domain_info **_dom) +{ + struct sss_domain_info *dom; + const char *domname = NULL; + errno_t ret; + struct poptOption options[] = { + {"domain", 'd', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, + &domname, 0, _("Domain name"), NULL }, + POPT_TABLEEND + }; + + ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL, + NULL, NULL, NULL, NULL, SSS_TOOL_OPT_REQUIRED, + NULL, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n"); + return ret; + } + + if (domname == NULL) { + *_dom = NULL; + return EOK; + } + + dom = find_domain_by_name(tool_ctx->domains, domname, true); + if (dom == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to find domain %s\n", domname); + ERROR("Unable to find domain %s\n", domname); + return EINVAL; + } + + *_dom = dom; + + return EOK; +} + +static errno_t parse_cmdline_import(struct sss_cmdline *cmdline, + const char **_file) +{ + errno_t ret; + + ret = sss_tool_popt_ex(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL, + NULL, NULL, "FILE", "File to import the data from.", + SSS_TOOL_OPT_REQUIRED, _file, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n"); + return ret; + } + + return EOK; +} + +static errno_t parse_cmdline_export(struct sss_cmdline *cmdline, + const char **_file) +{ + errno_t ret; + + ret = sss_tool_popt_ex(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL, + NULL, NULL, "FILE", "File to export the data to.", + SSS_TOOL_OPT_REQUIRED, _file, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n"); + return ret; + } + + return EOK; +} + +static errno_t prepare_view(struct sss_domain_info *domain) +{ + char *viewname = NULL; + errno_t ret; + + ret = sysdb_get_view_name(NULL, domain->sysdb, &viewname); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_view_name() failed.\n"); + return ret; + } + + if (ret == EOK) { + if (is_local_view(viewname)) { + DEBUG(SSSDBG_TRACE_FUNC, "%s view is already present.\n", viewname); + ret = EOK; + goto done; + } else if (viewname != NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, "There already exists view %s. " + "Only one view is supported. Nothing to do.\n", viewname); + ret = EEXIST; + goto done; + } + } + + DEBUG(SSSDBG_TRACE_FUNC, "Creating %s view.\n", LOCALVIEW); + + ret = sysdb_update_view_name(domain->sysdb, LOCALVIEW); + if (ret == EOK) { + printf("SSSD needs to be restarted for the changes to take effect.\n"); + } + +done: + talloc_free(viewname); + return ret; +} + +errno_t prepare_view_msg(struct sss_domain_info *domain) +{ + errno_t ret; + + ret = prepare_view(domain); + if (ret == EEXIST) { + ERROR("Other than " LOCALVIEW " view already exists " + "in domain %s.\n", domain->name); + } else if (ret != EOK) { + ERROR("Unable to prepare " LOCALVIEW + " view in domain %s.\n", domain->name); + } + + return ret; +} + +static char *build_anchor(TALLOC_CTX *mem_ctx, const char *obj_dn) +{ + char *anchor; + char *safe_dn; + errno_t ret; + + ret = sysdb_dn_sanitize(mem_ctx, obj_dn, &safe_dn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_dn_sanitize() failed\n"); + return NULL; + } + + anchor = talloc_asprintf(mem_ctx, ":%s:%s", LOCALVIEW, safe_dn); + + talloc_free(safe_dn); + + return anchor; +} + +static struct sysdb_attrs *build_attrs(TALLOC_CTX *mem_ctx, + struct sss_domain_info *dom, + const char *name, + uid_t uid, + gid_t gid, + const char *home, + const char *shell, + const char *gecos, + const char *cert) +{ + struct sysdb_attrs *attrs; + errno_t ret; + char *fqname; + + attrs = sysdb_new_attrs(mem_ctx); + if (attrs == NULL) { + return NULL; + } + + if (name != NULL) { + fqname = sss_create_internal_fqname(attrs, name, dom->name); + if (fqname == NULL) { + return NULL; + } + + ret = sysdb_attrs_add_string(attrs, SYSDB_NAME, fqname); + talloc_free(fqname); + if (ret != EOK) { + goto done; + } + } + + if (uid != 0) { + ret = sysdb_attrs_add_uint32(attrs, SYSDB_UIDNUM, uid); + if (ret != EOK) { + goto done; + } + } + + if (gid != 0) { + ret = sysdb_attrs_add_uint32(attrs, SYSDB_GIDNUM, gid); + if (ret != EOK) { + goto done; + } + } + + if (home != NULL) { + ret = sysdb_attrs_add_string(attrs, SYSDB_HOMEDIR, home); + if (ret != EOK) { + goto done; + } + } + + if (shell != NULL) { + ret = sysdb_attrs_add_string(attrs, SYSDB_SHELL, shell); + if (ret != EOK) { + goto done; + } + } + + if (gecos != NULL) { + ret = sysdb_attrs_add_string(attrs, SYSDB_GECOS, gecos); + if (ret != EOK) { + goto done; + } + } + + if (cert != NULL) { + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, cert); + if (ret != EOK) { + goto done; + } + } + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(attrs); + return NULL; + } + + return attrs; +} + +static struct sysdb_attrs *build_user_attrs(TALLOC_CTX *mem_ctx, + struct override_user *user) +{ + return build_attrs(mem_ctx, user->domain, user->name, user->uid, user->gid, + user->home, user->shell, user->gecos, user->cert); +} + +static struct sysdb_attrs *build_group_attrs(TALLOC_CTX *mem_ctx, + struct override_group *group) +{ + return build_attrs(mem_ctx, group->domain, group->name, 0, group->gid, + 0, NULL, NULL, NULL); +} + +static char *get_fqname(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *name) +{ + char *fqname = NULL; + char *dummy_domain = NULL; + errno_t ret; + TALLOC_CTX *tmp_ctx; + char *shortname; + struct sss_domain_info *dom; + + if (domain == NULL || domain->names == NULL) { + return NULL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return NULL; + } + + /* the name stored in sysdb already contains the lowercased domain */ + ret = sss_parse_internal_fqname(tmp_ctx, name, &shortname, &dummy_domain); + if (ret != EOK) { + DEBUG(SSSDBG_TRACE_FUNC, + "sss_parse_internal_fqname failed [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + dom = find_domain_by_name(get_domains_head(domain), dummy_domain, true); + if (dom == NULL) { + goto done; + } + + /* Get length. */ + fqname = sss_tc_fqname(mem_ctx, dom->names, dom, shortname); +done: + talloc_free(tmp_ctx); + return fqname; +} + +static char *get_sysname(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *name) +{ + if (domain == NULL || !domain->fqnames) { + return talloc_strdup(mem_ctx, name); + } + + return sss_tc_fqname(mem_ctx, domain->names, domain, name); +} + +static struct sss_domain_info * +get_object_domain(enum sysdb_member_type type, + const char *name, + struct sss_domain_info *domain, + struct sss_domain_info *domains) +{ + TALLOC_CTX *tmp_ctx; + struct sss_domain_info *dom = NULL; + struct ldb_result *res; + const char *strtype; + char *sysname; + char *fqname = NULL; + bool check_next; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return NULL; + } + + sysname = get_sysname(tmp_ctx, domain, name); + if (sysname == NULL) { + ret = ENOMEM; + goto done; + } + + /* Ensure that the object is in cache. */ + switch (type) { + case SYSDB_MEMBER_USER: + if (getpwnam(sysname) == NULL) { + ret = ENOENT; + goto done; + } + break; + case SYSDB_MEMBER_GROUP: + if (getgrnam(sysname) == NULL) { + ret = ENOENT; + goto done; + } + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported member type %d\n", type); + ret = ERR_INTERNAL; + goto done; + } + + /* Find domain if it is unknown. */ + if (domain == NULL) { + check_next = true; + dom = domains; + } else { + check_next = false; + dom = domain; + } + + do { + talloc_zfree(fqname); + fqname = sss_create_internal_fqname(tmp_ctx, name, dom->name); + if (fqname == NULL) { + ret = ENOMEM; + goto done; + } + + switch (type) { + case SYSDB_MEMBER_USER: + DEBUG(SSSDBG_TRACE_FUNC, "Trying to find user %s@%s\n", + name, dom->name); + ret = sysdb_getpwnam(tmp_ctx, dom, fqname, &res); + strtype = "user"; + break; + case SYSDB_MEMBER_GROUP: + DEBUG(SSSDBG_TRACE_FUNC, "Trying to find group %s@%s\n", + name, dom->name); + ret = sysdb_getgrnam(tmp_ctx, dom, fqname, &res); + strtype = "group"; + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported member type %d\n", type); + ret = ERR_INTERNAL; + goto done; + } + + if (ret == EOK && res->count == 0) { + ret = ENOENT; + + if (check_next) { + dom = dom->next; + continue; + } + } + + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to find %s %s@%s [%d]: %s\n", + strtype, name, dom->name, ret, sss_strerror(ret)); + goto done; + } else if (res->count != 1) { + DEBUG(SSSDBG_CRIT_FAILURE, "More than one %s found?\n", strtype); + ret = ERR_INTERNAL; + goto done; + } + + check_next = false; + } while (check_next && dom != NULL); + + if (dom == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "No domain match for %s\n", name); + ret = ENOENT; + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Domain of %s %s is %s\n", + strtype, name, dom->name); + +done: + talloc_free(tmp_ctx); + + if (ret != EOK) { + return NULL; + } + + return dom; +} + +static errno_t get_user_domain_msg(struct sss_tool_ctx *tool_ctx, + struct override_user *user) +{ + struct sss_domain_info *newdom; + const char *domname; + + newdom = get_object_domain(SYSDB_MEMBER_USER, user->orig_name, + user->domain, tool_ctx->domains); + if (newdom == NULL) { + domname = user->domain == NULL ? "[unknown]" : user->domain->name; + ERROR("Unable to find user %s@%s.\n", user->orig_name, domname); + return ENOENT; + } + + user->sysdb_name = sss_create_internal_fqname(tool_ctx, user->orig_name, + newdom->name); + if (user->sysdb_name == NULL) { + return ENOMEM; + } + + user->domain = newdom; + return EOK; +} + +static errno_t get_group_domain_msg(struct sss_tool_ctx *tool_ctx, + struct override_group *group) +{ + struct sss_domain_info *newdom; + const char *domname; + + newdom = get_object_domain(SYSDB_MEMBER_GROUP, group->orig_name, + group->domain, tool_ctx->domains); + if (newdom == NULL) { + domname = group->domain == NULL ? "[unknown]" : group->domain->name; + ERROR("Unable to find group %s@%s.\n", group->orig_name, domname); + return ENOENT; + } + + group->sysdb_name = sss_create_internal_fqname(tool_ctx, group->orig_name, + newdom->name); + if (group->sysdb_name == NULL) { + return ENOMEM; + } + + group->domain = newdom; + return EOK; +} + +static errno_t get_object_dn(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + enum sysdb_member_type type, + const char *name, + struct ldb_dn **_ldb_dn, + const char **_str_dn) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_dn *ldb_dn; + const char *str_dn; + errno_t ret; + struct ldb_result *res; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); + return ENOMEM; + } + + switch (type) { + case SYSDB_MEMBER_USER: + ret = sysdb_getpwnam(tmp_ctx, domain, name, &res); + break; + case SYSDB_MEMBER_GROUP: + ret = sysdb_getgrnam(tmp_ctx, domain, name, &res); + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported member type %d\n", type); + ret = ERR_INTERNAL; + goto done; + } + + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to look up original object in cache.\n"); + goto done; + } + + if (res->count == 0) { + DEBUG(SSSDBG_MINOR_FAILURE, "Original object not found in cache.\n"); + ret = ENOENT; + goto done; + } else if (res->count > 1) { + DEBUG(SSSDBG_CRIT_FAILURE, + "There are multiple object with name [%s] in the cache.\n", name); + ret = EINVAL; + goto done; + } + + ldb_dn = res->msgs[0]->dn; + + if (ldb_dn == NULL) { + ret = ENOMEM; + goto done; + } + + if (_str_dn != NULL) { + str_dn = talloc_strdup(tmp_ctx, ldb_dn_get_linearized(ldb_dn)); + if (str_dn == NULL) { + ret = ENOMEM; + goto done; + } + + *_str_dn = talloc_steal(mem_ctx, str_dn); + } + + if (_ldb_dn != NULL) { + *_ldb_dn = talloc_steal(mem_ctx, ldb_dn); + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + + return ret; +} + +static errno_t override_object_add(struct sss_domain_info *domain, + enum sysdb_member_type type, + struct sysdb_attrs *attrs, + const char *name) +{ + TALLOC_CTX *tmp_ctx; + const char *anchor; + struct ldb_dn *ldb_dn; + const char *str_dn; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = get_object_dn(tmp_ctx, domain, type, name, &ldb_dn, &str_dn); + if (ret != EOK) { + goto done; + } + + anchor = build_anchor(tmp_ctx, str_dn); + if (anchor == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_attrs_add_string(attrs, SYSDB_OVERRIDE_ANCHOR_UUID, anchor); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Creating override for %s\n", str_dn); + + ret = sysdb_store_override(domain, LOCALVIEW, type, attrs, ldb_dn); + +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t override_fqn(TALLOC_CTX *mem_ctx, + struct sss_tool_ctx *tool_ctx, + struct sss_domain_info *domain, + const char *input, + const char **_name) +{ + struct sss_domain_info *dom; + errno_t ret; + + if (input == NULL) { + return EOK; + } + + ret = sss_tool_parse_name(mem_ctx, tool_ctx, input, _name, &dom); + if (ret == EAGAIN) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to find domain from " + "fqn %s\n", input); + ERROR("Changing domain is not allowed!\n"); + ret = EINVAL; + } else if (ret == EOK && dom != NULL && dom != domain) { + DEBUG(SSSDBG_OP_FAILURE, "Trying to change domain from " + "%s to %s, not allowed!\n", domain->name, dom->name); + ERROR("Changing domain is not allowed!\n"); + ret = EINVAL; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse name %s [%d]: %s\n", + input, ret, sss_strerror(ret)); + } + + return ret; +} + +static errno_t override_user(struct sss_tool_ctx *tool_ctx, + struct override_user *input_user) +{ + TALLOC_CTX *tmp_ctx; + struct override_user user; + struct sysdb_attrs *attrs; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); + return ENOMEM; + } + + user = *input_user; + + /* We need to parse the name and ensure that domain did not change. */ + ret = override_fqn(tmp_ctx, tool_ctx, user.domain, user.name, &user.name); + if (ret != EOK) { + goto done; + } + + ret = prepare_view_msg(user.domain); + if (ret != EOK) { + goto done; + } + + attrs = build_user_attrs(tool_ctx, &user); + if (attrs == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build sysdb attrs.\n"); + ret = ENOMEM; + goto done; + } + + ret = override_object_add(user.domain, SYSDB_MEMBER_USER, attrs, + user.sysdb_name); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add override object.\n"); + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t override_group(struct sss_tool_ctx *tool_ctx, + struct override_group *input_group) +{ + TALLOC_CTX *tmp_ctx; + struct override_group group; + struct sysdb_attrs *attrs; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); + return ENOMEM; + } + + group = *input_group; + + /* We need to parse the name and ensure that domain did not change. */ + ret = override_fqn(tmp_ctx, tool_ctx, group.domain, group.name, + &group.name); + if (ret != EOK) { + goto done; + } + + ret = prepare_view_msg(group.domain); + if (ret != EOK) { + goto done; + } + + attrs = build_group_attrs(tool_ctx, &group); + if (attrs == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build sysdb attrs.\n"); + ret = ENOMEM; + goto done; + } + + ret = override_object_add(group.domain, SYSDB_MEMBER_GROUP, attrs, + group.sysdb_name); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add override object.\n"); + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t override_object_del(struct sss_domain_info *domain, + enum sysdb_member_type type, + const char *name) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_message *msg; + struct ldb_dn *override_dn; + struct ldb_dn *ldb_dn; + const char *str_dn; + const char *anchor; + errno_t ret; + int sret; + bool in_transaction = false; + struct ldb_context *ldb = sysdb_ctx_get_ldb(domain->sysdb); + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = get_object_dn(tmp_ctx, domain, type, name, &ldb_dn, &str_dn); + if (ret != EOK) { + goto done; + } + + anchor = build_anchor(tmp_ctx, str_dn); + if (anchor == NULL) { + ret = ENOMEM; + goto done; + } + + override_dn = ldb_dn_new_fmt(tmp_ctx, ldb, + SYSDB_TMPL_OVERRIDE, anchor, LOCALVIEW); + if (override_dn == NULL) { + ret = ENOMEM; + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Removing override for %s\n", str_dn); + + ret = sysdb_transaction_start(domain->sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_transaction_start() failed.\n"); + goto done; + } + in_transaction = true; + + ret = sysdb_delete_entry(domain->sysdb, override_dn, true); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sysdb_delete_entry() failed.\n"); + goto done; + } + + msg = ldb_msg_new(tmp_ctx); + if (msg == NULL) { + ret = ENOMEM; + goto done; + } + + msg->dn = talloc_steal(msg, ldb_dn); + if (msg->dn == NULL) { + ret = ENOMEM; + goto done; + } + + ret = ldb_msg_add_empty(msg, SYSDB_OVERRIDE_DN, LDB_FLAG_MOD_DELETE, NULL); + if (ret != LDB_SUCCESS) { + DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_empty() failed\n"); + ret = sss_ldb_error_to_errno(ret); + goto done; + } + + ret = ldb_modify(ldb, msg); + if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_ATTRIBUTE) { + DEBUG(SSSDBG_OP_FAILURE, + "ldb_modify() failed: [%s](%d)[%s]\n", + ldb_strerror(ret), ret, ldb_errstring(ldb)); + ret = sss_ldb_error_to_errno(ret); + goto done; + } + + ret = sysdb_transaction_commit(domain->sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n"); + goto done; + } + in_transaction = false; + + ret = EOK; + +done: + if (in_transaction) { + sret = sysdb_transaction_cancel(domain->sysdb); + if (sret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n"); + } + } + + talloc_free(tmp_ctx); + return ret; +} + +static errno_t append_name(struct sss_domain_info *domain, + struct ldb_message *override) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_context *ldb = sysdb_ctx_get_ldb(domain->sysdb); + struct ldb_dn *dn; + struct ldb_message **msgs; + const char *attrs[] = {SYSDB_NAME, NULL}; + const char *name; + const char *fqname; + size_t count; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n"); + return ENOMEM; + } + + dn = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, override, + SYSDB_OVERRIDE_OBJECT_DN); + if (dn == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Missing overrideObjectDN?\n"); + ret = ERR_INTERNAL; + goto done; + } + + ret = sysdb_search_entry(tmp_ctx, domain->sysdb, dn, LDB_SCOPE_BASE, + NULL, attrs, &count, &msgs); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_search_entry() failed [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } else if (count != 1) { + DEBUG(SSSDBG_CRIT_FAILURE, "More than one user found?\n"); + ret = ERR_INTERNAL; + goto done; + } + + name = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL); + if (name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Object with no name?\n"); + ret = ERR_INTERNAL; + goto done; + } + + fqname = get_fqname(tmp_ctx, domain, name); + if (fqname == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get fqname\n"); + ret = ENOMEM; + goto done; + } + + ret = ldb_msg_add_string(override, ORIGNAME, fqname); + if (ret != LDB_SUCCESS) { + ret = sss_ldb_error_to_errno(ret); + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add attribute to msg\n"); + goto done; + } + + talloc_steal(override, fqname); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + + return ret; +} + +static errno_t list_overrides(TALLOC_CTX *mem_ctx, + const char *base_filter, + const char *ext_filter, + const char **attrs, + struct sss_domain_info *domain, + size_t *_count, + struct ldb_message ***_msgs) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_dn *dn; + struct ldb_context *ldb = sysdb_ctx_get_ldb(domain->sysdb); + size_t count; + struct ldb_message **msgs; + const char *filter; + size_t i; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n"); + return ENOMEM; + } + + filter = base_filter; + if (ext_filter != NULL) { + filter = talloc_asprintf(tmp_ctx, "(&%s%s)", filter, ext_filter); + if (filter == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed.\n"); + ret = ENOMEM; + goto done; + } + } + + /* Acquire list of override objects. */ + dn = ldb_dn_new_fmt(tmp_ctx, ldb, SYSDB_TMPL_VIEW_SEARCH_BASE, LOCALVIEW); + if (dn == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new_fmt() failed.\n"); + ret = EIO; + goto done; + } + + ret = sysdb_search_entry(tmp_ctx, domain->sysdb, dn, LDB_SCOPE_SUBTREE, + filter, attrs, &count, &msgs); + if (ret == ENOENT) { + *_msgs = NULL; + *_count = 0; + ret = EOK; + goto done; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_search_entry() failed [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + /* Amend messages with original name. */ + for (i = 0; i < count; i++) { + ret = append_name(domain, msgs[i]); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to append name [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + } + + *_msgs = talloc_steal(mem_ctx, msgs); + *_count = count; + + ret = EOK; + +done: + talloc_free(tmp_ctx); + + return ret; +} + +static struct override_user * +list_user_overrides(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *filter) +{ + TALLOC_CTX *tmp_ctx; + struct override_user *objs = NULL; + struct ldb_message **msgs; + size_t count; + size_t i; + errno_t ret; + const char *attrs[] = SYSDB_PW_ATTRS; + struct ldb_message_element *el; + const char *fqname; + char *name; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n"); + return NULL; + } + + ret = list_overrides(tmp_ctx, "(objectClass=" SYSDB_OVERRIDE_USER_CLASS ")", + filter, attrs, domain, &count, &msgs); + if (ret != EOK) { + goto done; + } + + objs = talloc_zero_array(tmp_ctx, struct override_user, count + 1); + if (objs == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < count; i++) { + objs[i].orig_name = ldb_msg_find_attr_as_string(msgs[i], ORIGNAME, + NULL); + if (objs[i].orig_name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Missing name?!\n"); + ret = ERR_INTERNAL; + goto done; + } + + fqname = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL); + if (fqname != NULL) { + ret = sss_parse_internal_fqname(tmp_ctx, fqname, &name, NULL); + if (ret != EOK) { + ret = ERR_WRONG_NAME_FORMAT; + goto done; + } + objs[i].name = talloc_steal(objs, name); + } + + objs[i].uid = ldb_msg_find_attr_as_uint(msgs[i], SYSDB_UIDNUM, 0); + objs[i].gid = ldb_msg_find_attr_as_uint(msgs[i], SYSDB_GIDNUM, 0); + objs[i].home = ldb_msg_find_attr_as_string(msgs[i], SYSDB_HOMEDIR, NULL); + objs[i].shell = ldb_msg_find_attr_as_string(msgs[i], SYSDB_SHELL, NULL); + objs[i].gecos = ldb_msg_find_attr_as_string(msgs[i], SYSDB_GECOS, NULL); + + el = ldb_msg_find_element(msgs[i], SYSDB_USER_CERT); + if (el != NULL && el->num_values > 0) { + /* Currently we support only 1 certificate override */ + objs[i].cert = sss_base64_encode(objs, el->values[0].data, + el->values[0].length); + if (objs[i].cert == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sss_base64_encode failed.\n"); + ret = ERR_INTERNAL; + goto done; + } + } else { + objs[i].cert = NULL; + } + + talloc_steal(objs, objs[i].orig_name); + talloc_steal(objs, objs[i].home); + talloc_steal(objs, objs[i].shell); + talloc_steal(objs, objs[i].gecos); + } + + talloc_steal(mem_ctx, objs); + +done: + talloc_free(tmp_ctx); + + if (ret != EOK) { + return NULL; + } + + return objs; +} + +static struct override_group * +list_group_overrides(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *filter) +{ + TALLOC_CTX *tmp_ctx; + struct override_group *objs = NULL; + struct ldb_message **msgs; + size_t count; + size_t i; + errno_t ret; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + const char *fqname; + char *name; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n"); + return NULL; + } + + ret = list_overrides(tmp_ctx, "(objectClass=" SYSDB_OVERRIDE_GROUP_CLASS ")", + filter, attrs, domain, &count, &msgs); + if (ret != EOK) { + goto done; + } + + objs = talloc_zero_array(tmp_ctx, struct override_group, count + 1); + if (objs == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < count; i++) { + objs[i].orig_name = ldb_msg_find_attr_as_string(msgs[i], ORIGNAME, + NULL); + if (objs[i].orig_name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Missing name?!\n"); + ret = ERR_INTERNAL; + goto done; + } + talloc_steal(objs, objs[i].orig_name); + + fqname = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL); + if (fqname != NULL) { + ret = sss_parse_internal_fqname(tmp_ctx, fqname, &name, NULL); + if (ret != EOK) { + ret = ERR_WRONG_NAME_FORMAT; + goto done; + } + objs[i].name = talloc_steal(objs, name); + } + + objs[i].gid = ldb_msg_find_attr_as_uint(msgs[i], SYSDB_GIDNUM, 0); + } + + talloc_steal(mem_ctx, objs); + +done: + talloc_free(tmp_ctx); + + if (ret != EOK) { + return NULL; + } + + return objs; +} + +static errno_t user_export(const char *filename, + struct sss_domain_info *dom, + bool iterate, + const char *filter) +{ + TALLOC_CTX *tmp_ctx; + struct sss_colondb *db; + struct override_user *objs; + errno_t ret; + int i; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); + return ENOMEM; + } + + db = sss_colondb_open(tmp_ctx, SSS_COLONDB_WRITE, filename); + if (db == NULL) { + ERROR("Unable to open %s.\n", + filename == NULL ? "stdout" : filename); + ret = EIO; + goto done; + } + + do { + objs = list_user_overrides(tmp_ctx, dom, filter); + if (objs == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get override objects\n"); + ret = ENOMEM; + goto done; + } + + for (i = 0; objs[i].orig_name != NULL; i++) { + /** + * Format: orig_name:name:uid:gid:gecos:home:shell:certificate + */ + struct sss_colondb_write_field table[] = { + {SSS_COLONDB_STRING, {.str = objs[i].orig_name}}, + {SSS_COLONDB_STRING, {.str = objs[i].name}}, + {SSS_COLONDB_UINT32, {.uint32 = objs[i].uid}}, + {SSS_COLONDB_UINT32, {.uint32 = objs[i].gid}}, + {SSS_COLONDB_STRING, {.str = objs[i].gecos}}, + {SSS_COLONDB_STRING, {.str = objs[i].home}}, + {SSS_COLONDB_STRING, {.str = objs[i].shell}}, + {SSS_COLONDB_STRING, {.str = objs[i].cert}}, + {SSS_COLONDB_SENTINEL, {0}} + }; + + ret = sss_colondb_writeline(db, table); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to write line to db\n"); + goto done; + } + } + + /* All overrides are under the same subtree, so we don't want to + * descent into subdomains. */ + dom = get_next_domain(dom, false); + } while (dom != NULL && iterate); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + + return ret; +} + +static errno_t group_export(const char *filename, + struct sss_domain_info *dom, + bool iterate, + const char *filter) +{ + TALLOC_CTX *tmp_ctx; + struct sss_colondb *db; + struct override_group *objs; + errno_t ret; + int i; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); + return ENOMEM; + } + + + db = sss_colondb_open(tmp_ctx, SSS_COLONDB_WRITE, filename); + if (db == NULL) { + ERROR("Unable to open %s.\n", + filename == NULL ? "stdout" : filename); + ret = EIO; + goto done; + } + + do { + objs = list_group_overrides(tmp_ctx, dom, filter); + if (objs == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get override objects\n"); + ret = ENOMEM; + goto done; + } + + for (i = 0; objs[i].orig_name != NULL; i++) { + /** + * Format: orig_name:name:gid + */ + struct sss_colondb_write_field table[] = { + {SSS_COLONDB_STRING, {.str = objs[i].orig_name}}, + {SSS_COLONDB_STRING, {.str = objs[i].name}}, + {SSS_COLONDB_UINT32, {.uint32 = objs[i].gid}}, + {SSS_COLONDB_SENTINEL, {0}} + }; + + ret = sss_colondb_writeline(db, table); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to write line to db\n"); + goto done; + } + } + + /* All overrides are under the same subtree, so we don't want to + * descent into subdomains. */ + dom = get_next_domain(dom, false); + } while (dom != NULL && iterate); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + + return ret; +} + +static int override_user_add(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + void *pvt) +{ + struct override_user user = {NULL}; + errno_t ret; + + ret = parse_cmdline_user_add(cmdline, tool_ctx, &user); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n"); + goto done; + } + + ret = get_user_domain_msg(tool_ctx, &user); + if (ret != EOK) { + goto done; + } + + ret = override_user(tool_ctx, &user); + if (ret != EOK) { + goto done; + } + + ret = EOK; + +done: + free(discard_const(user.input_name)); + + return ret; +} + +static int override_user_del(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + void *pvt) +{ + struct override_user user = {NULL}; + errno_t ret; + + ret = parse_cmdline_user_del(cmdline, tool_ctx, &user); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n"); + goto done; + } + + ret = get_user_domain_msg(tool_ctx, &user); + if (ret != EOK) { + goto done; + } + + ret = override_object_del(user.domain, SYSDB_MEMBER_USER, user.sysdb_name); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to delete override object.\n"); + goto done; + } + + ret = EOK; + +done: + free(discard_const(user.input_name)); + + return ret; +} + +static int override_user_find(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + void *pvt) +{ + struct sss_domain_info *dom; + bool iterate; + errno_t ret; + + ret = parse_cmdline_find(cmdline, tool_ctx, &dom); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n"); + return ret; + } + + if (dom == NULL) { + dom = tool_ctx->domains; + iterate = true; + } else { + iterate = false; + } + + ret = user_export(NULL, dom, iterate, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to export users\n"); + return ret; + } + + return EOK; +} + +static int override_user_show(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + void *pvt) +{ + TALLOC_CTX *tmp_ctx; + struct override_user input = {NULL}; + const char *dn; + char *anchor; + const char *filter; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n"); + return ENOMEM; + } + + ret = parse_cmdline_user_show(cmdline, tool_ctx, &input); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n"); + goto done; + } + + ret = get_user_domain_msg(tool_ctx, &input); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get object domain\n"); + goto done; + } + + ret = get_object_dn(tmp_ctx, input.domain, SYSDB_MEMBER_USER, + input.sysdb_name, NULL, &dn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get object dn\n"); + goto done; + } + + anchor = build_anchor(tmp_ctx, dn); + if (anchor == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sss_filter_sanitize(tmp_ctx, anchor, &anchor); + if (ret != EOK) { + ret = ENOMEM; + goto done; + } + + filter = talloc_asprintf(tmp_ctx, "(%s=%s)", + SYSDB_OVERRIDE_ANCHOR_UUID, anchor); + if (filter == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n"); + ret = ENOMEM; + goto done; + } + + ret = user_export(NULL, input.domain, false, filter); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to export users\n"); + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + free(discard_const(input.input_name)); + + return ret; +} + +static int override_user_import(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + void *pvt) +{ + TALLOC_CTX *tmp_ctx; + struct sss_colondb *db; + const char *filename = NULL; + struct override_user obj = {0}; + int linenum = 1; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n"); + return EXIT_FAILURE; + } + + /** + * Format: orig_name:name:uid:gid:gecos:home:shell:certificate + */ + struct sss_colondb_read_field table[] = { + {SSS_COLONDB_STRING, {.str = &obj.input_name}}, + {SSS_COLONDB_STRING, {.str = &obj.name}}, + {SSS_COLONDB_UINT32, {.uint32 = &obj.uid}}, + {SSS_COLONDB_UINT32, {.uint32 = &obj.gid}}, + {SSS_COLONDB_STRING, {.str = &obj.gecos}}, + {SSS_COLONDB_STRING, {.str = &obj.home}}, + {SSS_COLONDB_STRING, {.str = &obj.shell}}, + {SSS_COLONDB_STRING, {.str = &obj.cert}}, + {SSS_COLONDB_SENTINEL, {0}} + }; + + ret = parse_cmdline_import(cmdline, &filename); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n"); + goto done; + } + + db = sss_colondb_open(tool_ctx, SSS_COLONDB_READ, filename); + if (db == NULL) { + ERROR("Unable to open %s.\n", filename); + ret = EIO; + goto done; + } + + while ((ret = sss_colondb_readline(tmp_ctx, db, table)) == EOK) { + linenum++; + + ret = sss_tool_parse_name(tool_ctx, tool_ctx, obj.input_name, + &obj.orig_name, &obj.domain); + if (ret != EOK) { + ERROR("Unable to parse name %s.\n", obj.input_name); + goto done; + } + + ret = get_user_domain_msg(tool_ctx, &obj); + if (ret != EOK) { + goto done; + } + + ret = override_user(tool_ctx, &obj); + if (ret != EOK) { + goto done; + } + + talloc_free_children(tmp_ctx); + } + + if (ret != EOF) { + ERROR("Invalid format on line %d. " + "Use --debug option for more information.\n", linenum); + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + free(discard_const(filename)); + + return ret; +} + +static int override_user_export(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + void *pvt) +{ + const char *filename = NULL; + errno_t ret; + + ret = parse_cmdline_export(cmdline, &filename); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n"); + goto done; + } + + ret = user_export(filename, tool_ctx->domains, true, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to export users\n"); + goto done; + } + + ret = EOK; + +done: + free(discard_const(filename)); + + return ret; +} + +static int override_group_add(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + void *pvt) +{ + struct override_group group = {NULL}; + errno_t ret; + + ret = parse_cmdline_group_add(cmdline, tool_ctx, &group); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n"); + goto done; + } + + ret = get_group_domain_msg(tool_ctx, &group); + if (ret != EOK) { + goto done; + } + + ret = override_group(tool_ctx, &group); + if (ret != EOK) { + goto done; + } + + ret = EOK; + +done: + free(discard_const(group.input_name)); + + return ret; +} + +static int override_group_del(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + void *pvt) +{ + struct override_group group = {NULL}; + errno_t ret; + + ret = parse_cmdline_group_del(cmdline, tool_ctx, &group); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n"); + goto done; + } + + ret = get_group_domain_msg(tool_ctx, &group); + if (ret != EOK) { + goto done; + } + + ret = override_object_del(group.domain, SYSDB_MEMBER_GROUP, + group.sysdb_name); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to delete override object.\n"); + goto done; + } + + ret = EOK; + +done: + free(discard_const(group.input_name)); + + return ret; +} + +static int override_group_find(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + void *pvt) +{ + struct sss_domain_info *dom; + bool iterate; + errno_t ret; + + ret = parse_cmdline_find(cmdline, tool_ctx, &dom); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n"); + return ret; + } + + if (dom == NULL) { + dom = tool_ctx->domains; + iterate = true; + } else { + iterate = false; + } + + ret = group_export(NULL, dom, iterate, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to export groups\n"); + return ret; + } + + return EOK; +} + +static int override_group_show(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + void *pvt) +{ + TALLOC_CTX *tmp_ctx; + struct override_group input = {NULL}; + const char *dn; + char *anchor; + const char *filter; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n"); + return ENOMEM; + } + + ret = parse_cmdline_group_show(cmdline, tool_ctx, &input); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n"); + goto done; + } + + ret = get_group_domain_msg(tool_ctx, &input); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get object domain\n"); + goto done; + } + + ret = get_object_dn(tmp_ctx, input.domain, SYSDB_MEMBER_GROUP, + input.sysdb_name, NULL, &dn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get object dn\n"); + goto done; + } + + anchor = build_anchor(tmp_ctx, dn); + if (anchor == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sss_filter_sanitize(tmp_ctx, anchor, &anchor); + if (ret != EOK) { + ret = ENOMEM; + goto done; + } + + filter = talloc_asprintf(tmp_ctx, "(%s=%s)", + SYSDB_OVERRIDE_ANCHOR_UUID, anchor); + if (filter == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n"); + ret = ENOMEM; + goto done; + } + + ret = group_export(NULL, input.domain, false, filter); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to export groups\n"); + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + free(discard_const(input.input_name)); + + return ret; +} + +static int override_group_import(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + void *pvt) +{ + TALLOC_CTX *tmp_ctx; + struct sss_colondb *db; + const char *filename = NULL; + struct override_group obj = {0}; + int linenum = 1; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n"); + return ENOMEM; + } + + /** + * Format: orig_name:name:gid + */ + struct sss_colondb_read_field table[] = { + {SSS_COLONDB_STRING, {.str = &obj.input_name}}, + {SSS_COLONDB_STRING, {.str = &obj.name}}, + {SSS_COLONDB_UINT32, {.uint32 = &obj.gid}}, + {SSS_COLONDB_SENTINEL, {0}} + }; + + ret = parse_cmdline_import(cmdline, &filename); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n"); + goto done; + } + + db = sss_colondb_open(tool_ctx, SSS_COLONDB_READ, filename); + if (db == NULL) { + ERROR("Unable to open %s.\n", filename); + ret = EIO; + goto done; + } + + while ((ret = sss_colondb_readline(tmp_ctx, db, table)) == EOK) { + linenum++; + + ret = sss_tool_parse_name(tool_ctx, tool_ctx, obj.input_name, + &obj.orig_name, &obj.domain); + if (ret != EOK) { + ERROR("Unable to parse name %s.\n", obj.input_name); + goto done; + } + + ret = get_group_domain_msg(tool_ctx, &obj); + if (ret != EOK) { + goto done; + } + + ret = override_group(tool_ctx, &obj); + if (ret != EOK) { + goto done; + } + + talloc_free_children(tmp_ctx); + } + + if (ret != EOF) { + ERROR("Invalid format on line %d. " + "Use --debug option for more information.\n", linenum); + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + free(discard_const(filename)); + + return ret; +} + +static int override_group_export(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + void *pvt) +{ + const char *filename = NULL; + errno_t ret; + + ret = parse_cmdline_export(cmdline, &filename); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n"); + goto done; + } + + ret = group_export(filename, tool_ctx->domains, true, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to export groups\n"); + goto done; + } + + ret = EOK; + +done: + free(discard_const(filename)); + + return ret; +} + +int main(int argc, const char **argv) +{ + struct sss_route_cmd commands[] = { + SSS_TOOL_COMMAND_NOMSG("user-add", 0, override_user_add), + SSS_TOOL_COMMAND_NOMSG("user-del", 0, override_user_del), + SSS_TOOL_COMMAND_NOMSG("user-find", 0, override_user_find), + SSS_TOOL_COMMAND_NOMSG("user-show", 0, override_user_show), + SSS_TOOL_COMMAND_NOMSG("user-import", 0, override_user_import), + SSS_TOOL_COMMAND_NOMSG("user-export", 0, override_user_export), + SSS_TOOL_COMMAND_NOMSG("group-add", 0, override_group_add), + SSS_TOOL_COMMAND_NOMSG("group-del", 0, override_group_del), + SSS_TOOL_COMMAND_NOMSG("group-find", 0, override_group_find), + SSS_TOOL_COMMAND_NOMSG("group-show", 0, override_group_show), + SSS_TOOL_COMMAND_NOMSG("group-import", 0, override_group_import), + SSS_TOOL_COMMAND_NOMSG("group-export", 0, override_group_export), + SSS_TOOL_LAST + }; + + return sss_tool_main(argc, argv, commands, NULL); +} |