>From 24655f677acf9f64201a611ababdcfeff4317037 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Sat, 15 Aug 2015 16:42:27 +0200
Subject: [PATCH 2/2] sss_override: add import and export

Resolves:
https://fedorahosted.org/sssd/ticket/2737
---
 Makefile.am                    |   2 +
 src/tools/common/sss_colondb.c | 298 ++++++++++++++
 src/tools/common/sss_colondb.h |  71 ++++
 src/tools/sss_override.c       | 866 +++++++++++++++++++++++++++++++++++------
 4 files changed, 1128 insertions(+), 109 deletions(-)
 create mode 100644 src/tools/common/sss_colondb.c
 create mode 100644 src/tools/common/sss_colondb.h

diff --git a/Makefile.am b/Makefile.am
index ed107fd5dc76b768176a3d7236b0bf1c75f212bf..f8c7c29df08c2ba9e74508c0f84ac57f6e6bac26 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -651,6 +651,7 @@ dist_noinst_HEADERS = \
     src/lib/sifp/sss_sifp_private.h \
     src/tests/cmocka/test_utils.h \
     src/tools/common/sss_tools.h \
+    src/tools/common/sss_colondb.h \
     $(NULL)
 
 
@@ -1331,6 +1332,7 @@ sss_signal_LDADD = \
 
 sss_override_SOURCES = \
     src/tools/sss_override.c \
+    src/tools/common/sss_colondb.c \
     $(SSSD_TOOLS_OBJ) \
     $(NULL)
 sss_override_LDADD = \
diff --git a/src/tools/common/sss_colondb.c b/src/tools/common/sss_colondb.c
new file mode 100644
index 0000000000000000000000000000000000000000..be9396832e93fa3b646255dae214a34fe4d6c29c
--- /dev/null
+++ b/src/tools/common/sss_colondb.c
@@ -0,0 +1,298 @@
+/*
+    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/strtonum.h"
+#include "tools/common/sss_colondb.h"
+
+#define IS_STD_FILE(db) ((db)->file == stdin || (db)->file == stdout)
+#define IS_LINE_END(str) ((str) == NULL || *(str) == '\0' || *(str) == '\n')
+
+static char *read_field_as_string(char *line,
+                                  const char **_value)
+{
+    char *rest;
+    char *value;
+
+    if (line == NULL || *line == '\0' || *line == '\n') {
+        /* There is nothing else to read. */
+        rest = NULL;
+        value = NULL;
+        goto done;
+    }
+
+    if (*line == ':') {
+        /* Special case for empty value. */
+        *line = '\0';
+        rest = line + 1;
+        value = NULL;
+        goto done;
+    }
+
+    /* Value starts at current position. */
+    value = line;
+
+    /* Find next field delimiter. */
+    rest = strchr(line, ':');
+    if (rest == NULL) {
+        /* There is no more field. Remove \n from the end. */
+        rest = strchr(line, '\n');
+        if (rest != NULL) {
+            *rest = '\0';
+            rest = NULL;
+        }
+        goto done;
+    }
+
+    /* Remove it and step one character further. */
+    *rest = '\0';
+    rest++;
+
+done:
+    *_value = value;
+
+    return rest;
+}
+
+static char *read_field_as_uint32(char *line,
+                                  uint32_t *_value)
+{
+    const char *str;
+    char *rest;
+    errno_t ret;
+
+    rest = read_field_as_string(line, &str);
+    if (rest == NULL || str == NULL) {
+        *_value = 0;
+        return rest;
+    }
+
+    *_value = strtouint32(str, NULL, 10);
+    if (errno != 0) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse number [%d]: %s\n",
+              ret, sss_strerror(ret));
+
+        *_value = 0;
+    }
+
+    return rest;
+}
+
+struct sss_colondb {
+    FILE *file;
+    enum sss_colondb_mode mode;
+};
+
+errno_t sss_colondb_readline(TALLOC_CTX *mem_ctx,
+                             struct sss_colondb *db,
+                             struct sss_colondb_read_field *table)
+{
+    int readchars;
+    size_t linelen = 0;
+    char *line = NULL;
+    char *rest;
+    errno_t ret;
+    int i;
+
+    if (db->mode != SSS_COLONDB_READ) {
+        return ERR_INTERNAL;
+    }
+
+    readchars = getline(&line, &linelen, db->file);
+    if (readchars == -1) {
+        /* Nothing was read. */
+        if (errno != 0) {
+            ret = errno;
+            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read line [%d]: %s",
+                  ret, sss_strerror(ret));
+            return ret;
+        }
+
+        return EOF;
+    }
+
+    /* Copy line to mem_ctx. */
+    rest = talloc_strdup(mem_ctx, line);
+    if (rest == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+        return ENOMEM;
+    }
+    free(line);
+    line = rest;
+
+    for (i = 0; table[i].type != SSS_COLONDB_SENTINEL; i++) {
+        switch (table[i].type) {
+        case SSS_COLONDB_UINT32:
+            rest = read_field_as_uint32(rest, table[i].data.uint32);
+            break;
+        case SSS_COLONDB_STRING:
+            rest = read_field_as_string(rest, table[i].data.str);
+            break;
+        case SSS_COLONDB_SENTINEL:
+            DEBUG(SSSDBG_CRIT_FAILURE, "Trying to process sentinel?!\n");
+            ret = ERR_INTERNAL;
+            goto done;
+        }
+
+        if (rest == NULL && table[i + 1].type != SSS_COLONDB_SENTINEL) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Line contains less values than expected!\n");
+            ret = EINVAL;
+            goto done;
+        } else if (rest != NULL && table[i + 1].type == SSS_COLONDB_SENTINEL) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Line contains more values than expected!\n");
+            ret = EINVAL;
+            goto done;
+        }
+    }
+
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        talloc_free(line);
+    }
+
+    return ret;
+}
+
+errno_t sss_colondb_writeline(struct sss_colondb *db,
+                              struct sss_colondb_write_field *table)
+{
+    TALLOC_CTX *tmp_ctx;
+    char *line = NULL;
+    errno_t ret;
+    int i;
+
+    if (db->mode != SSS_COLONDB_WRITE) {
+        return ERR_INTERNAL;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n");
+        return ENOMEM;
+    }
+
+    for (i = 0; table[i].type != SSS_COLONDB_SENTINEL; i++) {
+        switch (table[i].type) {
+        case SSS_COLONDB_UINT32:
+            if (table[i].data.uint32 == 0) {
+                line = talloc_asprintf_append(line, ":");
+            } else {
+                line = talloc_asprintf_append(line, ":%u", table[i].data.uint32);
+            }
+            break;
+        case SSS_COLONDB_STRING:
+            if (table[i].data.str == NULL) {
+                line = talloc_asprintf_append(line, ":");
+            } else {
+                line = talloc_asprintf_append(line, ":%s", table[i].data.str);
+            }
+            break;
+        case SSS_COLONDB_SENTINEL:
+            DEBUG(SSSDBG_CRIT_FAILURE, "Trying to process sentinel?!\n");
+            ret = ERR_INTERNAL;
+            goto done;
+        }
+
+        if (line == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    /* Remove starting : */
+    line++;
+
+    fprintf(db->file, "%s\n", line);
+    fflush(db->file);
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+
+    return ret;
+}
+
+static int sss_colondb_close(void *pvt)
+{
+    struct sss_colondb *db = talloc_get_type(pvt, struct sss_colondb);
+
+    if (db->file == NULL || IS_STD_FILE(db)) {
+        return 0;
+    }
+
+    fclose(db->file);
+    db->file = NULL;
+
+    return 0;
+}
+
+struct sss_colondb *sss_colondb_open(TALLOC_CTX *mem_ctx,
+                                     enum sss_colondb_mode mode,
+                                     const char *filename)
+{
+    FILE *fp;
+    const char *fmode;
+    struct sss_colondb *db;
+
+    switch (mode) {
+    case SSS_COLONDB_READ:
+        fmode = "r";
+        if (filename == NULL) {
+            fp = stdin;
+        }
+        break;
+    case SSS_COLONDB_WRITE:
+        fmode = "w";
+        if (filename == NULL) {
+            fp = stdout;
+        }
+        break;
+    }
+
+    if (filename != NULL) {
+        errno = 0;
+        fp = fopen(filename, fmode);
+        if (fp == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to open file %s\n", filename);
+            return NULL;
+        }
+    }
+
+    db = talloc_zero(mem_ctx, struct sss_colondb);
+    if (db == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n");
+        return NULL;
+    }
+
+    db->file = fp;
+    db->mode = mode;
+
+    talloc_set_destructor((TALLOC_CTX *)db, sss_colondb_close);
+
+    return db;
+}
diff --git a/src/tools/common/sss_colondb.h b/src/tools/common/sss_colondb.h
new file mode 100644
index 0000000000000000000000000000000000000000..13e9c010bb6a233a5f939b453f80b29622fff841
--- /dev/null
+++ b/src/tools/common/sss_colondb.h
@@ -0,0 +1,71 @@
+/*
+    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/>.
+*/
+
+#ifndef _SSS_COLONDB_H_
+#define _SSS_COLONDB_H_
+
+#include <stdlib.h>
+#include <talloc.h>
+
+struct sss_colondb;
+
+enum sss_colondb_mode {
+    SSS_COLONDB_READ,
+    SSS_COLONDB_WRITE
+};
+
+enum sss_colondb_type {
+    SSS_COLONDB_UINT32,
+    SSS_COLONDB_STRING,
+    SSS_COLONDB_SENTINEL
+};
+
+union sss_colondb_write_data {
+    uint32_t uint32;
+    const char *str;
+};
+
+union sss_colondb_read_data {
+    uint32_t *uint32;
+    const char **str;
+};
+
+struct sss_colondb_write_field {
+    enum sss_colondb_type type;
+    union sss_colondb_write_data data;
+};
+
+struct sss_colondb_read_field {
+    enum sss_colondb_type type;
+    union sss_colondb_read_data data;
+};
+
+struct sss_colondb *sss_colondb_open(TALLOC_CTX *mem_ctx,
+                                     enum sss_colondb_mode mode,
+                                     const char *filename);
+
+errno_t sss_colondb_readline(TALLOC_CTX *mem_ctx,
+                             struct sss_colondb *db,
+                             struct sss_colondb_read_field *table);
+
+errno_t sss_colondb_writeline(struct sss_colondb *db,
+                              struct sss_colondb_write_field *table);
+
+#endif /* _SSS_COLONDB_H_ */
diff --git a/src/tools/sss_override.c b/src/tools/sss_override.c
index e84a7b922dfcf179f8010dc4cced0eafd89a2c76..40d0aa233be0b3c8f64c7102ca8cb3b250ca151b 100644
--- a/src/tools/sss_override.c
+++ b/src/tools/sss_override.c
@@ -23,8 +23,10 @@
 #include "util/util.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;
@@ -135,6 +137,40 @@ static int parse_cmdline_group_del(struct sss_cmdline *cmdline,
                          &group->orig_name, &group->domain);
 }
 
+static int parse_cmdline_import(struct sss_cmdline *cmdline,
+                                struct sss_tool_ctx *tool_ctx,
+                                const char **_file)
+{
+    int ret;
+
+    ret = sss_tool_popt_ex(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL,
+                           NULL, NULL, "FILE", "File to import the data from.",
+                           _file);
+    if (ret != EXIT_SUCCESS) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+        return ret;
+    }
+
+    return EXIT_SUCCESS;
+}
+
+static int parse_cmdline_export(struct sss_cmdline *cmdline,
+                                struct sss_tool_ctx *tool_ctx,
+                                const char **_file)
+{
+    int ret;
+
+    ret = sss_tool_popt_ex(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL,
+                           NULL, NULL, "FILE", "File to export the data to.",
+                           _file);
+    if (ret != EXIT_SUCCESS) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+        return ret;
+    }
+
+    return EXIT_SUCCESS;
+}
+
 static errno_t prepare_view(struct sss_domain_info *domain)
 {
     char *viewname = NULL;
@@ -171,6 +207,22 @@ done:
     return ret;
 }
 
+errno_t prepare_view_msg(struct sss_domain_info *domain)
+{
+    errno_t ret;
+
+    ret = prepare_view(domain);
+    if (ret == EEXIST) {
+        fprintf(stderr, _("Other than " LOCALVIEW " view already exist "
+                "in domain %s.\n"), domain->name);
+    } else if (ret != EOK) {
+        fprintf(stderr, _("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;
@@ -272,17 +324,15 @@ static struct sysdb_attrs *build_group_attrs(TALLOC_CTX *mem_ctx,
     return build_attrs(mem_ctx, group->name, 0, group->gid, 0, NULL, NULL);
 }
 
-static const char *get_object_dn_and_domain(TALLOC_CTX *mem_ctx,
-                                         enum sysdb_member_type type,
-                                         const char *name,
-                                         struct sss_domain_info *domain,
-                                         struct sss_domain_info *domains,
-                                         struct sss_domain_info **_new_domain)
+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;
     struct ldb_result *res;
-    const char *dn;
     const char *strtype;
     bool check_next;
     errno_t ret;
@@ -372,18 +422,6 @@ static const char *get_object_dn_and_domain(TALLOC_CTX *mem_ctx,
     DEBUG(SSSDBG_TRACE_FUNC, "Domain of %s %s is %s\n",
           strtype, name, dom->name);
 
-    dn = ldb_dn_get_linearized(res->msgs[0]->dn);
-    if (dn == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "ldb_dn_get_linearized() failed.\n");
-        ret = ENOMEM;
-        goto done;
-    }
-
-    talloc_steal(mem_ctx, dn);
-    *_new_domain = dom;
-
-    ret = EOK;
-
 done:
     talloc_free(tmp_ctx);
 
@@ -391,35 +429,94 @@ done:
         return NULL;
     }
 
-    return dn;
+    return dom;
 }
 
-static const char * get_user_dn_and_domain(TALLOC_CTX *mem_ctx,
-                                           struct sss_domain_info *domains,
-                                           struct override_user *user)
+static errno_t get_user_domain_msg(struct sss_tool_ctx *tool_ctx,
+                                   struct override_user *user)
 {
-    return get_object_dn_and_domain(mem_ctx, SYSDB_MEMBER_USER,
-                         user->orig_name, user->domain, domains,
-                         &user->domain);
+    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;
+        fprintf(stderr, _("Unable to find user %s@%s.\n"),
+                user->orig_name, domname);
+        return ENOENT;
+    }
+
+    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;
+        fprintf(stderr, _("Unable to find group %s@%s.\n"),
+                group->orig_name, domname);
+        return ENOENT;
+    }
+
+    group->domain = newdom;
+    return EOK;
 }
 
-static const char * get_group_dn_and_domain(TALLOC_CTX *mem_ctx,
-                                            struct sss_domain_info *domains,
-                                            struct override_group *group)
+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)
 {
-    return get_object_dn_and_domain(mem_ctx, SYSDB_MEMBER_GROUP,
-                         group->orig_name, group->domain, domains,
-                         &group->domain);
+    struct ldb_dn *ldb_dn;
+
+    switch (type) {
+    case SYSDB_MEMBER_USER:
+       ldb_dn = sysdb_user_dn(mem_ctx, domain, name);
+       break;
+    case SYSDB_MEMBER_GROUP:
+       ldb_dn = sysdb_group_dn(mem_ctx, domain, name);
+       break;
+    default:
+       DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported member type %d\n", type);
+       return ERR_INTERNAL;
+    }
+
+    if (ldb_dn == NULL) {
+        return ENOMEM;
+    }
+
+    if (_str_dn != NULL) {
+        *_str_dn = ldb_dn_get_linearized(ldb_dn);
+    }
+
+    if (_ldb_dn != NULL) {
+        *_ldb_dn = ldb_dn;
+    } else {
+        talloc_free(ldb_dn);
+    }
+
+    return EOK;
 }
 
 static errno_t override_object_add(struct sss_domain_info *domain,
                                    enum sysdb_member_type type,
                                    struct sysdb_attrs *attrs,
-                                   const char *obj_dn)
+                                   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);
@@ -427,13 +524,12 @@ static errno_t override_object_add(struct sss_domain_info *domain,
         return ENOMEM;
     }
 
-    ldb_dn = ldb_dn_new(tmp_ctx, sysdb_ctx_get_ldb(domain->sysdb), obj_dn);
-    if (ldb_dn == NULL) {
-        ret = ENOMEM;
+    ret = get_object_dn(tmp_ctx, domain, type, name, &ldb_dn, &str_dn);
+    if (ret != EOK) {
         goto done;
     }
 
-    anchor = build_anchor(tmp_ctx, obj_dn);
+    anchor = build_anchor(tmp_ctx, str_dn);
     if (anchor == NULL) {
         ret = ENOMEM;
         goto done;
@@ -444,7 +540,7 @@ static errno_t override_object_add(struct sss_domain_info *domain,
         goto done;
     }
 
-    DEBUG(SSSDBG_TRACE_FUNC, "Creating override for %s\n", obj_dn);
+    DEBUG(SSSDBG_TRACE_FUNC, "Creating override for %s\n", str_dn);
 
     ret = sysdb_store_override(domain, LOCALVIEW, type, attrs, ldb_dn);
 
@@ -453,13 +549,70 @@ done:
     return ret;
 }
 
+static errno_t override_user(struct sss_tool_ctx *tool_ctx,
+                             struct override_user *user)
+{
+    struct sysdb_attrs *attrs;
+    errno_t ret;
+
+    ret = prepare_view_msg(user->domain);
+    if (ret != EOK) {
+        return ret;
+    }
+
+    attrs = build_user_attrs(tool_ctx, user);
+    if (attrs == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build sysdb attrs.\n");
+        return ENOMEM;
+    }
+
+    ret = override_object_add(user->domain, SYSDB_MEMBER_USER, attrs,
+                              user->orig_name);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add override object.\n");
+        return ret;
+    }
+
+    return EOK;
+}
+
+static errno_t override_group(struct sss_tool_ctx *tool_ctx,
+                              struct override_group *group)
+{
+    struct sysdb_attrs *attrs;
+    errno_t ret;
+
+    ret = prepare_view_msg(group->domain);
+    if (ret != EOK) {
+        return ret;
+    }
+
+    attrs = build_group_attrs(tool_ctx, group);
+    if (attrs == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build sysdb attrs.\n");
+        return ENOMEM;
+    }
+
+    ret = override_object_add(group->domain, SYSDB_MEMBER_GROUP, attrs,
+                              group->orig_name);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add override object.\n");
+        return ret;
+    }
+
+    return EOK;
+}
+
 static errno_t override_object_del(struct sss_domain_info *domain,
-                                   const char *obj_dn)
+                                   enum sysdb_member_type type,
+                                   const char *name)
 {
     TALLOC_CTX *tmp_ctx;
-    const char *anchor;
-    struct ldb_dn *override_dn;
     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;
@@ -470,7 +623,12 @@ static errno_t override_object_del(struct sss_domain_info *domain,
         return ENOMEM;
     }
 
-    anchor = build_anchor(tmp_ctx, obj_dn);
+    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;
@@ -483,7 +641,7 @@ static errno_t override_object_del(struct sss_domain_info *domain,
         goto done;
     }
 
-    DEBUG(SSSDBG_TRACE_FUNC, "Removing override for %s\n", obj_dn);
+    DEBUG(SSSDBG_TRACE_FUNC, "Removing override for %s\n", str_dn);
 
     ret = sysdb_transaction_start(domain->sysdb);
     if (ret != EOK) {
@@ -504,7 +662,7 @@ static errno_t override_object_del(struct sss_domain_info *domain,
         goto done;
     }
 
-    msg->dn = ldb_dn_new(msg, ldb, obj_dn);
+    msg->dn = talloc_steal(msg, ldb_dn);
     if (msg->dn == NULL) {
         ret = ENOMEM;
         goto done;
@@ -547,13 +705,243 @@ done:
     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;
+    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;
+    }
+
+    ret = ldb_msg_add_string(override, ORIGNAME, name);
+    if (ret != LDB_SUCCESS) {
+        ret = sysdb_error_to_errno(ret);
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add attribute to msg\n");
+        goto done;
+    }
+
+    talloc_steal(override, name);
+
+done:
+    talloc_free(tmp_ctx);
+
+    return ret;
+}
+
+static errno_t list_overrides(TALLOC_CTX *mem_ctx,
+                              const char *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;
+    size_t i;
+    int ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n");
+        return ENOMEM;
+    }
+
+    /* 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 != 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)
+{
+    TALLOC_CTX *tmp_ctx;
+    struct override_user *objs;
+    struct ldb_message **msgs;
+    size_t count;
+    size_t i;
+    errno_t ret;
+    const char *attrs[] = SYSDB_PW_ATTRS;
+
+    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 ")",
+                         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;
+        }
+
+        objs[i].name = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL);
+        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);
+    }
+
+    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)
+{
+    TALLOC_CTX *tmp_ctx;
+    struct override_group *objs;
+    struct ldb_message **msgs;
+    size_t count;
+    size_t i;
+    errno_t ret;
+    const char *attrs[] = SYSDB_GRSRC_ATTRS;
+
+    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 ")",
+                         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;
+        }
+
+        objs[i].name = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL);
+        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 int override_user_add(struct sss_cmdline *cmdline,
                              struct sss_tool_ctx *tool_ctx,
                              void *pvt)
 {
     struct override_user user = {NULL};
-    struct sysdb_attrs *attrs;
-    const char *dn;
     int ret;
 
     ret = parse_cmdline_user_add(cmdline, tool_ctx, &user);
@@ -562,34 +950,13 @@ static int override_user_add(struct sss_cmdline *cmdline,
         return EXIT_FAILURE;
     }
 
-    dn = get_user_dn_and_domain(tool_ctx, tool_ctx->domains, &user);
-    if (dn == NULL) {
-        fprintf(stderr, _("Unable to find user %s@%s.\n"),
-                user.orig_name,
-                user.domain == NULL ? "[unknown]" : user.domain->name);
-        return EXIT_FAILURE;
-    }
-
-    ret = prepare_view(user.domain);
-    if (ret == EEXIST) {
-        fprintf(stderr, _("Other than LOCAL view already exist in "
-                "domain %s.\n"), user.domain->name);
-        return EXIT_FAILURE;
-    } else if (ret != EOK) {
-        fprintf(stderr, _("Unable to prepare view [%d]: %s.\n"),
-                ret, sss_strerror(ret));
-        return EXIT_FAILURE;
-    }
-
-    attrs = build_user_attrs(tool_ctx, &user);
-    if (attrs == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build sysdb attrs.\n");
+    ret = get_user_domain_msg(tool_ctx, &user);
+    if (ret != EOK) {
         return EXIT_FAILURE;
     }
 
-    ret = override_object_add(user.domain, SYSDB_MEMBER_USER, attrs, dn);
+    ret = override_user(tool_ctx, &user);
     if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add override object.\n");
         return EXIT_FAILURE;
     }
 
@@ -601,7 +968,6 @@ static int override_user_del(struct sss_cmdline *cmdline,
                              void *pvt)
 {
     struct override_user user = {NULL};
-    const char *dn;
     int ret;
 
     ret = parse_cmdline_user_del(cmdline, tool_ctx, &user);
@@ -610,29 +976,181 @@ static int override_user_del(struct sss_cmdline *cmdline,
         return EXIT_FAILURE;
     }
 
-    dn = get_user_dn_and_domain(tool_ctx, tool_ctx->domains, &user);
-    if (dn == NULL) {
-        fprintf(stderr, _("Unable to find user %s@%s.\n"),
-                user.orig_name, user.domain->name);
+    ret = get_user_domain_msg(tool_ctx, &user);
+    if (ret != EOK) {
         return EXIT_FAILURE;
     }
 
-    ret = override_object_del(user.domain, dn);
+    ret = override_object_del(user.domain, SYSDB_MEMBER_USER, user.orig_name);
     if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add override object.\n");
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to delete override object.\n");
         return EXIT_FAILURE;
     }
 
     return EXIT_SUCCESS;
 }
 
+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;
+    struct override_user obj;
+    int linenum = 1;
+    errno_t ret;
+    int exit;
+
+    /**
+     * Format: orig_name:name:uid:gid:gecos:home:shell
+     */
+    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_SENTINEL, {0}}
+    };
+
+    ret = parse_cmdline_import(cmdline, tool_ctx, &filename);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+        exit = EXIT_FAILURE;
+        goto done;
+    }
+
+    db = sss_colondb_open(tool_ctx, SSS_COLONDB_READ, filename);
+    if (db == NULL) {
+        fprintf(stderr, _("Unable to open %s.\n"), filename);
+        exit = EXIT_FAILURE;
+        goto done;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+        exit = EXIT_FAILURE;
+        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) {
+            fprintf(stderr, _("Unable to parse name %s.\n"), obj.input_name);
+            exit = EXIT_FAILURE;
+            goto done;
+        }
+
+        ret = get_user_domain_msg(tool_ctx, &obj);
+        if (ret != EOK) {
+            exit = EXIT_FAILURE;
+            goto done;
+        }
+
+        ret = override_user(tool_ctx, &obj);
+        if (ret != EOK) {
+            exit = EXIT_FAILURE;
+            goto done;
+        }
+
+        talloc_free_children(tmp_ctx);
+    }
+
+    if (ret != EOF) {
+        fprintf(stderr, _("Invalid format on line %d. "
+                "Use --debug option for more information.\n"), linenum);
+        exit = EXIT_FAILURE;
+        goto done;
+    }
+
+    exit = EXIT_SUCCESS;
+
+done:
+    talloc_free(tmp_ctx);
+    return exit;
+}
+
+static int override_user_export(struct sss_cmdline *cmdline,
+                                struct sss_tool_ctx *tool_ctx,
+                                void *pvt)
+{
+    struct sss_colondb *db;
+    const char *filename;
+    struct override_user *objs;
+    struct sss_domain_info *dom;
+    errno_t ret;
+    int exit;
+    int i;
+
+    ret = parse_cmdline_export(cmdline, tool_ctx, &filename);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+        exit = EXIT_FAILURE;
+        goto done;
+    }
+
+    db = sss_colondb_open(tool_ctx, SSS_COLONDB_WRITE, filename);
+    if (db == NULL) {
+        fprintf(stderr, _("Unable to open %s.\n"), filename);
+        exit = EXIT_FAILURE;
+        goto done;
+    }
+
+    dom = tool_ctx->domains;
+    do {
+        objs = list_user_overrides(tool_ctx, tool_ctx->domains);
+        if (objs == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get override objects\n");
+            exit = EXIT_FAILURE;
+            goto done;
+        }
+
+        for (i = 0; objs[i].orig_name != NULL; i++) {
+            /**
+             * Format: orig_name:name:uid:gid:gecos:home:shell
+             */
+            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_SENTINEL, {0}}
+            };
+
+            ret = sss_colondb_writeline(db, table);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_CRIT_FAILURE, "Unable to write line to db\n");
+                exit = EXIT_FAILURE;
+                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);
+
+    exit = EXIT_SUCCESS;
+
+done:
+    return exit;
+}
+
 static int override_group_add(struct sss_cmdline *cmdline,
                               struct sss_tool_ctx *tool_ctx,
                               void *pvt)
 {
     struct override_group group = {NULL};
-    struct sysdb_attrs *attrs;
-    const char *dn;
     int ret;
 
     ret = parse_cmdline_group_add(cmdline, tool_ctx, &group);
@@ -641,33 +1159,13 @@ static int override_group_add(struct sss_cmdline *cmdline,
         return EXIT_FAILURE;
     }
 
-    dn = get_group_dn_and_domain(tool_ctx, tool_ctx->domains, &group);
-    if (dn == NULL) {
-        fprintf(stderr, _("Unable to find group %s@%s.\n"),
-                group.orig_name, group.domain->name);
-        return EXIT_FAILURE;
-    }
-
-    ret = prepare_view(group.domain);
-    if (ret == EEXIST) {
-        fprintf(stderr, _("Other than LOCAL view already exist in "
-                "domain %s.\n"), group.domain->name);
-        return EXIT_FAILURE;
-    } else if (ret != EOK) {
-        fprintf(stderr, _("Unable to prepare view [%d]: %s.\n"),
-                ret, sss_strerror(ret));
-        return EXIT_FAILURE;
-    }
-
-    attrs = build_group_attrs(tool_ctx, &group);
-    if (attrs == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build sysdb attrs.\n");
+    ret = get_group_domain_msg(tool_ctx, &group);
+    if (ret != EOK) {
         return EXIT_FAILURE;
     }
 
-    ret = override_object_add(group.domain, SYSDB_MEMBER_GROUP, attrs, dn);
+    ret = override_group(tool_ctx, &group);
     if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add override object.\n");
         return EXIT_FAILURE;
     }
 
@@ -679,7 +1177,6 @@ static int override_group_del(struct sss_cmdline *cmdline,
                               void *pvt)
 {
     struct override_group group = {NULL};
-    const char *dn;
     int ret;
 
     ret = parse_cmdline_group_del(cmdline, tool_ctx, &group);
@@ -688,29 +1185,180 @@ static int override_group_del(struct sss_cmdline *cmdline,
         return EXIT_FAILURE;
     }
 
-    dn = get_group_dn_and_domain(tool_ctx, tool_ctx->domains, &group);
-    if (dn == NULL) {
-        fprintf(stderr, _("Unable to find group %s@%s.\n"),
-                group.orig_name, group.domain->name);
+    ret = get_group_domain_msg(tool_ctx, &group);
+    if (ret != EOK) {
         return EXIT_FAILURE;
     }
 
-    ret = override_object_del(group.domain, dn);
+    ret = override_object_del(group.domain, SYSDB_MEMBER_GROUP,
+                              group.orig_name);
     if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add override object.\n");
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to delete override object.\n");
         return EXIT_FAILURE;
     }
 
     return EXIT_SUCCESS;
 }
 
+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;
+    struct override_group obj;
+    int linenum = 1;
+    errno_t ret;
+    int exit;
+
+    /**
+     * 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, tool_ctx, &filename);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+        exit = EXIT_FAILURE;
+        goto done;
+    }
+
+    db = sss_colondb_open(tool_ctx, SSS_COLONDB_READ, filename);
+    if (db == NULL) {
+        fprintf(stderr, _("Unable to open %s.\n"), filename);
+        exit = EXIT_FAILURE;
+        goto done;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+        exit = EXIT_FAILURE;
+        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) {
+            fprintf(stderr, _("Unable to parse name %s.\n"), obj.input_name);
+            exit = EXIT_FAILURE;
+            goto done;
+        }
+
+        ret = get_group_domain_msg(tool_ctx, &obj);
+        if (ret != EOK) {
+            exit = EXIT_FAILURE;
+            goto done;
+        }
+
+        ret = override_group(tool_ctx, &obj);
+        if (ret != EOK) {
+            exit = EXIT_FAILURE;
+            goto done;
+        }
+
+        talloc_free_children(tmp_ctx);
+    }
+
+    if (ret != EOF) {
+        fprintf(stderr, _("Invalid format on line %d. "
+                "Use --debug option for more information.\n"), linenum);
+        exit = EXIT_FAILURE;
+        goto done;
+    }
+
+    exit = EXIT_SUCCESS;
+
+done:
+    talloc_free(tmp_ctx);
+    return exit;
+}
+
+static int override_group_export(struct sss_cmdline *cmdline,
+                                 struct sss_tool_ctx *tool_ctx,
+                                 void *pvt)
+{
+    struct sss_colondb *db;
+    const char *filename;
+    struct override_group *objs;
+    struct sss_domain_info *dom;
+    errno_t ret;
+    int exit;
+    int i;
+
+    ret = parse_cmdline_export(cmdline, tool_ctx, &filename);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+        exit = EXIT_FAILURE;
+        goto done;
+    }
+
+    db = sss_colondb_open(tool_ctx, SSS_COLONDB_WRITE, filename);
+    if (db == NULL) {
+        fprintf(stderr, _("Unable to open %s.\n"), filename);
+        exit = EXIT_FAILURE;
+        goto done;
+    }
+
+    dom = tool_ctx->domains;
+    do {
+        objs = list_group_overrides(tool_ctx, tool_ctx->domains);
+        if (objs == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get override objects\n");
+            exit = EXIT_FAILURE;
+            goto done;
+        }
+
+        for (i = 0; objs[i].orig_name != NULL; i++) {
+            /**
+             * Format: orig_name:name:uid:gid:gecos:home:shell
+             */
+            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");
+                exit = EXIT_FAILURE;
+                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);
+
+    exit = EXIT_SUCCESS;
+
+done:
+    return exit;
+}
+
 int main(int argc, const char **argv)
 {
     struct sss_route_cmd commands[] = {
         {"user-add", override_user_add},
         {"user-del", override_user_del},
+        {"user-import", override_user_import},
+        {"user-export", override_user_export},
         {"group-add", override_group_add},
         {"group-del", override_group_del},
+        {"group-import", override_group_import},
+        {"group-export", override_group_export},
         {NULL, NULL}
     };
 
-- 
1.9.3

