On 07/27/2015 05:27 PM, Pavel Březina wrote:
On 07/27/2015 05:07 PM, Jakub Hrozek wrote:
On Mon, Jul 27, 2015 at 12:00:25PM +0200, Pavel Březina wrote:
On 07/26/2015 08:14 PM, Jakub Hrozek wrote:
On Fri, Jul 24, 2015 at 01:08:17PM +0200, Pavel Březina wrote:
https://fedorahosted.org/sssd/ticket/2584
btw (unrelated to these patches) it seems strange to require the
user of
the sysdb database to connect to the db and then separately initialize
subdomains...I would vote for merging that code..

+1

I'll file a ticket for michal.


+
+#include <talloc.h>
+#include <popt.h>
+
+#include "confdb/confdb.h"
+
+struct sss_tool_ctx {
+    struct confdb_ctx *confdb;
+    char *confdb_path;
+
+    char *default_domain;
+    struct sss_domain_info *domains;

I wonder if the tool_ctx must be open for consumers? Currently only
domains is used, maybe we could get away with a getter and an opaque
structure? We can always make the structure public in future, but if
it's public from the start, then it's easier to add cruft..

I think it is not worth the effort with such simple structure. I am
all for opaque structures but here is just no gain in code safety. It
would
only make code lines bigger.

Fine.


+enum sss_tool_domain {
+    SSS_TOOL_DOMAIN_REQUIRED,
+    SSS_TOOL_DOMAIN_OPTIONAL
+};
+
+int sss_tool_parse_name(TALLOC_CTX *mem_ctx,
+                        struct sss_tool_ctx *tool_ctx,
+                        const char *input,
+                        enum sss_tool_domain require_domain,

Do we need require_domain now that you added the getpwnam?

No, removed.

Thanks

[...]


Hmm looks like you should have a check for res validity here, for cases
no domain match..

Added a dom validity.

Thanks.


btw it might be nice to split this large function into smaller ones.
Maybe the do-while check could be a separate function returning res.

There is no way to split it that would be actually beneficial. There are
three blocks:
a) beginning - getpwnam
b) middle - iteration
c) end - linearized dn

If you take away the iteration you can directly return dn from the new
function since you don't need res at all so that would also took away
the
end. And the beginning belongs to the function were iteration is IMHO.

OK


 From 45e80988edc6dab703209068942fad5378d38e72 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrez...@redhat.com>
Date: Fri, 24 Jul 2015 09:55:28 +0200
Subject: [PATCH 1/3] SYSDB: prepare for LOCAL view

Objects doesn't have to have overrideDN specified when using LOCAL view.
Since the view is not stored on the server we do not want to contact
LDAP therefore we special case LOCAL view saying that it is OK that
this attribute is missing.

Preparation for:
https://fedorahosted.org/sssd/ticket/2584
---
  src/db/sysdb.h                      |  3 +-
  src/db/sysdb_views.c                |  7 +++++
  src/tests/cmocka/test_sysdb_views.c | 62
+++++++++++++++++++++++++++++++++++++
  3 files changed, 71 insertions(+), 1 deletion(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index
0f745ccb1a646d77ba4ad3d714d5f4dce0a51211..3bb2e50320594d1b6f7e3daa3a64134e46b60d22
100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -157,9 +157,10 @@
  #define SYSDB_AD_ACCOUNT_EXPIRES "adAccountExpires"
  #define SYSDB_AD_USER_ACCOUNT_CONTROL "adUserAccountControl"

+#define SYSDB_DEFAULT_VIEW_NAME "default"
+#define SYSDB_LOCAL_VIEW_NAME "LOCAL" /* reserved for client-side
overrides */
  #define SYSDB_VIEW_CLASS "view"
  #define SYSDB_VIEW_NAME "viewName"
-#define SYSDB_DEFAULT_VIEW_NAME "default"
  #define SYSDB_OVERRIDE_CLASS "overrride"
  #define SYSDB_OVERRIDE_ANCHOR_UUID "overrideAnchorUUID"
  #define SYSDB_OVERRIDE_USER_CLASS "userOverride"
diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c
index
aadd6018f4d1e2ca33e2e00dd8b13b55a8c03f3e..f4560344e992d8245e37a5a4e2f74c7b70ce41ec
100644
--- a/src/db/sysdb_views.c
+++ b/src/db/sysdb_views.c
@@ -1186,9 +1186,16 @@ errno_t sysdb_add_overrides_to_object(struct
sss_domain_info *domain,
          override_dn_str = ldb_msg_find_attr_as_string(obj,

SYSDB_OVERRIDE_DN, NULL);
          if (override_dn_str == NULL) {
+            if (strcmp(domain->view_name, SYSDB_LOCAL_VIEW_NAME) ==
0) {
+                /* LOCAL view doesn't have to have overrideDN
specified. */
+                ret = EOK;
+                goto done;
+            }
+
              DEBUG(SSSDBG_CRIT_FAILURE,
                    "Missing override DN for objext [%s].\n",
                    ldb_dn_get_linearized(obj->dn));
+
              ret = ENOENT;
              goto done;
          }
diff --git a/src/tests/cmocka/test_sysdb_views.c
b/src/tests/cmocka/test_sysdb_views.c
index
1fb598219e9ee581e465ddbb32ba9f2544600c26..5d2d50ef94093664465305b53831ed878cf2c871
100644
--- a/src/tests/cmocka/test_sysdb_views.c
+++ b/src/tests/cmocka/test_sysdb_views.c
@@ -275,6 +275,64 @@ void test_sysdb_add_overrides_to_object(void
**state)
      assert_int_equal(ldb_val_string_cmp(&el->values[1],
"OVERRIDEKEY2"), 0);
  }

+void test_sysdb_add_overrides_to_object_local(void **state)
+{
+    int ret;
+    struct ldb_message *orig;
+    struct ldb_message_element *el;
+    char *tmp_str;
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct
sysdb_test_ctx);
+
+    orig = ldb_msg_new(test_ctx);
+    assert_non_null(orig);
+
+    tmp_str = talloc_strdup(orig,  "ORIGNAME");

Please put assert_non_null here and to other assignments to tmp_str in
these two tests, otherwise ACK

+    ret = ldb_msg_add_string(orig, SYSDB_NAME, tmp_str);
+    assert_int_equal(ret, EOK);
+
+    tmp_str = talloc_strdup(orig,  "ORIGGECOS");
+    ret = ldb_msg_add_string(orig, SYSDB_GECOS, tmp_str);
+    assert_int_equal(ret, EOK);
+
+    test_ctx->domain->has_views = true;
+    test_ctx->domain->view_name = "LOCAL";
+
+    ret = sysdb_add_overrides_to_object(test_ctx->domain, orig,
NULL, NULL);
+    assert_int_equal(ret, EOK);
+}

 From 5a7b46aee3acd9a1d40cc85cb8085e9d43f48a7e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrez...@redhat.com>
Date: Wed, 22 Jul 2015 10:02:02 +0200
Subject: [PATCH 2/3] TOOLS: add common command framework

ACK


 From 3babec59cb170457c441000b66ff76895526aba7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrez...@redhat.com>
Date: Fri, 24 Jul 2015 09:58:11 +0200
Subject: [PATCH 3/3] TOOLS: add sss_override for local overrides

Resolves:
https://fedorahosted.org/sssd/ticket/2584

There was one more Coverity warning:
Error: UNINIT (CWE-457): [#def1]
sssd-1.13.1/src/tools/sss_override.c:202: var_decl: Declaring variable
"ret" without initializer.
sssd-1.13.1/src/tools/sss_override.c:252: uninit_use: Using
uninitialized value "ret".
#  250|
#  251|   done:
#  252|->     if (ret != EOK) {
#  253|           talloc_free(attrs);
#  254|           return NULL;

Otherwise looks good.
_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel


Thanks, new patches are attached. In addition to your comments:
- one new patch that adds null check in original unit test
- man page and tool change that says that zero ids are not supported

New patches are attached as for irc discussion. Diff:

diff --git a/src/man/sss_override.8.xml b/src/man/sss_override.8.xml
index 88df5a0..307cb55 100644
--- a/src/man/sss_override.8.xml
+++ b/src/man/sss_override.8.xml
@@ -33,6 +33,10 @@
             view and allows to change selected values of specific user
             and groups. This change takes effect only on local machine.
         </para>
+        <para>
+ Overrides data are stored in SSSD cache. If the cache is deleted
+            all local overrides are lost.
+        </para>
     </refsect1>

     <refsect1 id='commands'>
diff --git a/src/tools/sss_override.c b/src/tools/sss_override.c
index c007350..676327d 100644
--- a/src/tools/sss_override.c
+++ b/src/tools/sss_override.c
@@ -565,7 +565,8 @@ static int override_user_add(struct sss_cmdline *cmdline,
     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);
+                user.orig_name,
+                user.domain == NULL ? "[unknown]" : user.domain->name);
         return EXIT_FAILURE;
     }




>From db9ba08b8c359eec318fa3dfc28e038618ea35f7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrez...@redhat.com>
Date: Mon, 27 Jul 2015 17:24:45 +0200
Subject: [PATCH 1/4] VIEWS TEST: add null-check

---
 src/tests/cmocka/test_sysdb_views.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/tests/cmocka/test_sysdb_views.c b/src/tests/cmocka/test_sysdb_views.c
index 1fb598219e9ee581e465ddbb32ba9f2544600c26..123d4c5cb613f41e1bca9e89feed701a1e86f8d3 100644
--- a/src/tests/cmocka/test_sysdb_views.c
+++ b/src/tests/cmocka/test_sysdb_views.c
@@ -225,10 +225,12 @@ void test_sysdb_add_overrides_to_object(void **state)
     assert_non_null(orig);
 
     tmp_str = talloc_strdup(orig,  "ORIGNAME");
+    assert_non_null(tmp_str);
     ret = ldb_msg_add_string(orig, SYSDB_NAME, tmp_str);
     assert_int_equal(ret, EOK);
 
     tmp_str = talloc_strdup(orig,  "ORIGGECOS");
+    assert_non_null(tmp_str);
     ret = ldb_msg_add_string(orig, SYSDB_GECOS, tmp_str);
     assert_int_equal(ret, EOK);
 
@@ -236,18 +238,22 @@ void test_sysdb_add_overrides_to_object(void **state)
     assert_non_null(override);
 
     tmp_str = talloc_strdup(override, "OVERRIDENAME");
+    assert_non_null(tmp_str);
     ret = ldb_msg_add_string(override, SYSDB_NAME, tmp_str);
     assert_int_equal(ret, EOK);
 
     tmp_str = talloc_strdup(override, "OVERRIDEGECOS");
+    assert_non_null(tmp_str);
     ret = ldb_msg_add_string(override, SYSDB_GECOS, tmp_str);
     assert_int_equal(ret, EOK);
 
     tmp_str = talloc_strdup(override, "OVERRIDEKEY1");
+    assert_non_null(tmp_str);
     ret = ldb_msg_add_string(override, SYSDB_SSH_PUBKEY, tmp_str);
     assert_int_equal(ret, EOK);
 
     tmp_str = talloc_strdup(override, "OVERRIDEKEY2");
+    assert_non_null(tmp_str);
     ret = ldb_msg_add_string(override, SYSDB_SSH_PUBKEY, tmp_str);
     assert_int_equal(ret, EOK);
 
-- 
1.9.3

>From 9b67c5777f51cedd1b6669d0f103c232e80e9ad6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrez...@redhat.com>
Date: Fri, 24 Jul 2015 09:55:28 +0200
Subject: [PATCH 2/4] SYSDB: prepare for LOCAL view

Objects doesn't have to have overrideDN specified when using LOCAL view.
Since the view is not stored on the server we do not want to contact
LDAP therefore we special case LOCAL view saying that it is OK that
this attribute is missing.

Preparation for:
https://fedorahosted.org/sssd/ticket/2584
---
 src/db/sysdb.h                      |  3 +-
 src/db/sysdb_views.c                |  7 ++++
 src/tests/cmocka/test_sysdb_views.c | 66 +++++++++++++++++++++++++++++++++++++
 3 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 0f745ccb1a646d77ba4ad3d714d5f4dce0a51211..3bb2e50320594d1b6f7e3daa3a64134e46b60d22 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -157,9 +157,10 @@
 #define SYSDB_AD_ACCOUNT_EXPIRES "adAccountExpires"
 #define SYSDB_AD_USER_ACCOUNT_CONTROL "adUserAccountControl"
 
+#define SYSDB_DEFAULT_VIEW_NAME "default"
+#define SYSDB_LOCAL_VIEW_NAME "LOCAL" /* reserved for client-side overrides */
 #define SYSDB_VIEW_CLASS "view"
 #define SYSDB_VIEW_NAME "viewName"
-#define SYSDB_DEFAULT_VIEW_NAME "default"
 #define SYSDB_OVERRIDE_CLASS "overrride"
 #define SYSDB_OVERRIDE_ANCHOR_UUID "overrideAnchorUUID"
 #define SYSDB_OVERRIDE_USER_CLASS "userOverride"
diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c
index aadd6018f4d1e2ca33e2e00dd8b13b55a8c03f3e..f4560344e992d8245e37a5a4e2f74c7b70ce41ec 100644
--- a/src/db/sysdb_views.c
+++ b/src/db/sysdb_views.c
@@ -1186,9 +1186,16 @@ errno_t sysdb_add_overrides_to_object(struct sss_domain_info *domain,
         override_dn_str = ldb_msg_find_attr_as_string(obj,
                                                       SYSDB_OVERRIDE_DN, NULL);
         if (override_dn_str == NULL) {
+            if (strcmp(domain->view_name, SYSDB_LOCAL_VIEW_NAME) == 0) {
+                /* LOCAL view doesn't have to have overrideDN specified. */
+                ret = EOK;
+                goto done;
+            }
+
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "Missing override DN for objext [%s].\n",
                   ldb_dn_get_linearized(obj->dn));
+
             ret = ENOENT;
             goto done;
         }
diff --git a/src/tests/cmocka/test_sysdb_views.c b/src/tests/cmocka/test_sysdb_views.c
index 123d4c5cb613f41e1bca9e89feed701a1e86f8d3..83007b76a625edef67109850648b2d71645e22bb 100644
--- a/src/tests/cmocka/test_sysdb_views.c
+++ b/src/tests/cmocka/test_sysdb_views.c
@@ -281,6 +281,68 @@ void test_sysdb_add_overrides_to_object(void **state)
     assert_int_equal(ldb_val_string_cmp(&el->values[1], "OVERRIDEKEY2"), 0);
 }
 
+void test_sysdb_add_overrides_to_object_local(void **state)
+{
+    int ret;
+    struct ldb_message *orig;
+    struct ldb_message_element *el;
+    char *tmp_str;
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct sysdb_test_ctx);
+
+    orig = ldb_msg_new(test_ctx);
+    assert_non_null(orig);
+
+    tmp_str = talloc_strdup(orig,  "ORIGNAME");
+    assert_non_null(tmp_str);
+    ret = ldb_msg_add_string(orig, SYSDB_NAME, tmp_str);
+    assert_int_equal(ret, EOK);
+
+    tmp_str = talloc_strdup(orig,  "ORIGGECOS");
+    assert_non_null(tmp_str);
+    ret = ldb_msg_add_string(orig, SYSDB_GECOS, tmp_str);
+    assert_int_equal(ret, EOK);
+
+    test_ctx->domain->has_views = true;
+    test_ctx->domain->view_name = "LOCAL";
+
+    ret = sysdb_add_overrides_to_object(test_ctx->domain, orig, NULL, NULL);
+    assert_int_equal(ret, EOK);
+}
+
+void test_sysdb_add_overrides_to_object_missing_overridedn(void **state)
+{
+    int ret;
+    struct ldb_message *orig;
+    struct ldb_message_element *el;
+    char *tmp_str;
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct sysdb_test_ctx);
+
+    orig = ldb_msg_new(test_ctx);
+    assert_non_null(orig);
+
+    orig->dn = ldb_dn_new(orig, test_ctx->domain->sysdb->ldb,
+                          "cn=somedn,dc=example,dc=com");
+    assert_non_null(orig->dn);
+
+    tmp_str = talloc_strdup(orig,  "ORIGNAME");
+    assert_non_null(tmp_str);
+    ret = ldb_msg_add_string(orig, SYSDB_NAME, tmp_str);
+    assert_int_equal(ret, EOK);
+
+    tmp_str = talloc_strdup(orig,  "ORIGGECOS");
+    assert_non_null(tmp_str);
+    ret = ldb_msg_add_string(orig, SYSDB_GECOS, tmp_str);
+    assert_int_equal(ret, EOK);
+
+    test_ctx->domain->has_views = true;
+    test_ctx->domain->view_name = "NON-LOCAL";
+
+    ret = sysdb_add_overrides_to_object(test_ctx->domain, orig, NULL, NULL);
+    assert_int_equal(ret, ENOENT);
+}
+
 void test_split_ipa_anchor(void **state)
 {
     int ret;
@@ -923,6 +985,10 @@ int main(int argc, const char *argv[])
                                         test_sysdb_setup, test_sysdb_teardown),
         cmocka_unit_test_setup_teardown(test_sysdb_add_overrides_to_object,
                                         test_sysdb_setup, test_sysdb_teardown),
+        cmocka_unit_test_setup_teardown(test_sysdb_add_overrides_to_object_local,
+                                        test_sysdb_setup, test_sysdb_teardown),
+        cmocka_unit_test_setup_teardown(test_sysdb_add_overrides_to_object_missing_overridedn,
+                                        test_sysdb_setup, test_sysdb_teardown),
         cmocka_unit_test_setup_teardown(test_split_ipa_anchor,
                                         test_sysdb_setup, test_sysdb_teardown),
         cmocka_unit_test_setup_teardown(test_sysdb_delete_view_tree,
-- 
1.9.3

>From 39b1bf17cb52e000c8ef74862fb88a4762176730 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrez...@redhat.com>
Date: Wed, 22 Jul 2015 10:02:02 +0200
Subject: [PATCH 3/4] TOOLS: add common command framework

Add general framework to simplify creating "cmd COMMAND [OPTIONS...]"
style tools.

Preparation for:
https://fedorahosted.org/sssd/ticket/2584
---
 Makefile.am                  |   5 +-
 src/tools/common/sss_tools.c | 406 +++++++++++++++++++++++++++++++++++++++++++
 src/tools/common/sss_tools.h |  91 ++++++++++
 3 files changed, 501 insertions(+), 1 deletion(-)
 create mode 100644 src/tools/common/sss_tools.c
 create mode 100644 src/tools/common/sss_tools.h

diff --git a/Makefile.am b/Makefile.am
index b8cbc6df23ded1edb945a709b6dbe1c44eb54017..1edecc483c61d04562b7bfd9086146e93963b74e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -445,7 +445,9 @@ SSSD_TOOLS_OBJ = \
     src/tools/tools_util.c \
     src/tools/files.c \
     src/tools/selinux.c \
-    src/util/nscd.c
+    src/tools/common/sss_tools.c \
+    src/util/nscd.c \
+    $(NULL)
 
 SSSD_LCL_TOOLS_OBJ = \
     src/sss_client/common.c \
@@ -641,6 +643,7 @@ dist_noinst_HEADERS = \
     src/lib/idmap/sss_idmap_private.h \
     src/lib/sifp/sss_sifp_private.h \
     src/tests/cmocka/test_utils.h \
+    src/tools/common/sss_tools.h \
     $(NULL)
 
 
diff --git a/src/tools/common/sss_tools.c b/src/tools/common/sss_tools.c
new file mode 100644
index 0000000000000000000000000000000000000000..6bbce3a25ddddc0b23ebc108a917a38e94981b65
--- /dev/null
+++ b/src/tools/common/sss_tools.c
@@ -0,0 +1,406 @@
+/*
+    Authors:
+        Pavel Březina <pbrez...@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 <talloc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <popt.h>
+
+#include "config.h"
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "db/sysdb.h"
+#include "tools/common/sss_tools.h"
+
+struct sss_cmdline {
+    const char *exec; /* argv[0] */
+    const char *command; /* command name */
+    int argc; /* rest of arguments */
+    const char **argv;
+};
+
+static void sss_tool_common_opts(struct sss_tool_ctx *tool_ctx,
+                                 int *argc, const char **argv)
+{
+    poptContext pc;
+    int debug = SSSDBG_DEFAULT;
+    int orig_argc = *argc;
+    int opt;
+
+    struct poptOption options[] = {
+        {"debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_STRIP, &debug,
+            0, _("The debug level to run with"), NULL },
+        POPT_TABLEEND
+    };
+
+    pc = poptGetContext(argv[0], orig_argc, argv, options, 0);
+    while ((opt = poptGetNextOpt(pc)) != -1) {
+        /* do nothing */
+    }
+
+    /* Strip common options from arguments. We will discard_const here,
+     * since it is not worth the trouble to convert it back and forth. */
+    *argc = poptStrippedArgv(pc, orig_argc, discard_const_p(char *, argv));
+
+    DEBUG_CLI_INIT(debug);
+
+    poptFreeContext(pc);
+}
+
+static errno_t sss_tool_confdb_init(TALLOC_CTX *mem_ctx,
+                                    struct confdb_ctx **_confdb)
+{
+    struct confdb_ctx *confdb;
+    char *path;
+    errno_t ret;
+
+    path = talloc_asprintf(mem_ctx, "%s/%s", DB_PATH, CONFDB_FILE);
+    if (path == NULL) {
+        return ENOMEM;
+    }
+
+    ret = confdb_init(mem_ctx, &confdb, path);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Could not initialize connection to the confdb\n");
+        talloc_free(path);
+        return ret;
+    }
+
+    if (_confdb != NULL) {
+        *_confdb = confdb;
+    }
+
+    return EOK;
+}
+
+static errno_t sss_tool_domains_init(TALLOC_CTX *mem_ctx,
+                                     struct confdb_ctx *confdb,
+                                     struct sss_domain_info **_domains)
+{
+    struct sss_domain_info *domains;
+    struct sss_domain_info *dom;
+    errno_t ret;
+
+    ret = confdb_get_domains(confdb, &domains);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup domains [%d]: %s\n",
+                                   ret, sss_strerror(ret));
+        return ret;
+    }
+
+    ret = sysdb_init(mem_ctx, domains, false);
+    SYSDB_VERSION_ERROR(ret);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Could not initialize connection to the sysdb\n");
+        return ret;
+    }
+
+    for (dom = domains; dom != NULL; dom = get_next_domain(dom, true)) {
+        if (!IS_SUBDOMAIN(dom)) {
+            /* Update list of subdomains for this domain */
+            ret = sysdb_update_subdomains(dom);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_MINOR_FAILURE,
+                      "Failed to update subdomains for domain %s.\n",
+                      dom->name);
+            }
+        }
+    }
+
+    for (dom = domains; dom != NULL; dom = get_next_domain(dom, true)) {
+        ret = sss_names_init(mem_ctx, confdb, dom->name, &dom->names);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "sss_names_init() failed\n");
+            return ret;
+        }
+    }
+
+    *_domains = domains;
+
+    return ret;
+}
+
+struct sss_tool_ctx *sss_tool_init(TALLOC_CTX *mem_ctx,
+                                   int *argc, const char **argv)
+{
+    struct sss_tool_ctx *tool_ctx;
+    errno_t ret;
+
+    tool_ctx = talloc_zero(mem_ctx, struct sss_tool_ctx);
+    if (tool_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n");
+        return NULL;
+    }
+
+    sss_tool_common_opts(tool_ctx, argc, argv);
+
+    /* Connect to confdb. */
+    ret = sss_tool_confdb_init(tool_ctx, &tool_ctx->confdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to open confdb [%d]: %s\n",
+                                   ret, sss_strerror(ret));
+        goto done;
+    }
+
+    /* Setup domains. */
+    ret = sss_tool_domains_init(tool_ctx, tool_ctx->confdb, &tool_ctx->domains);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup domains [%d]: %s\n",
+                                   ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = confdb_get_string(tool_ctx->confdb, tool_ctx,
+                            CONFDB_MONITOR_CONF_ENTRY,
+                            CONFDB_MONITOR_DEFAULT_DOMAIN,
+                            NULL, &tool_ctx->default_domain);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot get the default domain [%d]: %s\n",
+                                 ret, strerror(ret));
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        talloc_zfree(tool_ctx);
+    }
+
+    return tool_ctx;
+}
+
+int sss_tool_usage(const char *tool_name,
+                   struct sss_route_cmd *commands)
+{
+    int i;
+
+    fprintf(stderr, _("Usage:\n%s COMMAND COMMAND-ARGS\n\n"), tool_name);
+    fprintf(stderr, _("Available commands:\n"));
+
+    for (i = 0; commands[i].command != NULL; i++) {
+        fprintf(stderr, "* %s\n", commands[i].command);
+    }
+
+    return EXIT_FAILURE;
+}
+
+int sss_tool_route(int argc, const char **argv,
+                   struct sss_tool_ctx *tool_ctx,
+                   struct sss_route_cmd *commands,
+                   void *pvt)
+{
+    struct sss_cmdline cmdline;
+    const char *cmd;
+    int i;
+
+    if (commands == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Bug: commands can't be NULL!\n");
+        return EXIT_FAILURE;
+    }
+
+    if (argc < 2) {
+        return sss_tool_usage(argv[0], commands);
+    }
+
+    cmd = argv[1];
+    for (i = 0; commands[i].command != NULL; i++) {
+        if (strcmp(commands[i].command, cmd) == 0) {
+            cmdline.exec = argv[0];
+            cmdline.command = argv[1];
+            cmdline.argc = argc - 2;
+            cmdline.argv = argv + 2;
+
+            return commands[i].fn(&cmdline, tool_ctx, pvt);
+        }
+    }
+
+    return sss_tool_usage(argv[0], commands);
+}
+
+int sss_tool_popt_ex(struct sss_cmdline *cmdline,
+                     struct poptOption *options,
+                     enum sss_tool_opt require_option,
+                     sss_popt_fn popt_fn,
+                     void *popt_fn_pvt,
+                     const char *fopt_name,
+                     const char *fopt_help,
+                     const char **_fopt)
+{
+    const char *optstr;
+    char *help;
+    poptContext pc;
+    int ret;
+
+    /* Create help option string. We always need to append command name since
+     * we use POPT_CONTEXT_KEEP_FIRST. */
+    optstr = options == NULL ? "" : _(" [OPTIONS...]");
+    if (fopt_name == NULL) {
+        help = talloc_asprintf(NULL, "%s %s%s",
+                               cmdline->exec, cmdline->command, optstr);
+    } else {
+        help = talloc_asprintf(NULL, "%s %s %s%s",
+                           cmdline->exec, cmdline->command, fopt_name, optstr);
+    }
+    if (help == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+        return EXIT_FAILURE;
+    }
+
+    /* Create popt context. This function is supposed to be called on
+     * command argv which does not contain executable (argv[0]), therefore
+     * we need to use KEEP_FIRST that ensures argv[0] is also processed. */
+    pc = poptGetContext(cmdline->exec, cmdline->argc, cmdline->argv,
+                        options, POPT_CONTEXT_KEEP_FIRST);
+
+    poptSetOtherOptionHelp(pc, help);
+
+    /* Parse options. Invoke custom function if provided. If no parsing
+     * function is provided, print error on unknown option. */
+    while ((ret = poptGetNextOpt(pc)) != -1) {
+        if (popt_fn != NULL) {
+            ret = popt_fn(pc, ret, popt_fn_pvt);
+            if (ret != EOK) {
+                ret = EXIT_FAILURE;
+                goto done;
+            }
+        } else {
+            fprintf(stderr, _("Invalid option %s: %s\n\n"),
+                    poptBadOption(pc, 0), poptStrerror(ret));
+            poptPrintHelp(pc, stderr, 0);
+            ret = EXIT_FAILURE;
+            goto done;
+        }
+    }
+
+    /* Parse free option which is always required if requested. */
+    if (_fopt != NULL) {
+        *_fopt = poptGetArg(pc);
+        if (*_fopt == NULL) {
+            fprintf(stderr, _("Missing option: %s\n\n"), fopt_help);
+            poptPrintHelp(pc, stderr, 0);
+            ret = EXIT_FAILURE;
+            goto done;
+        }
+
+        /* No more arguments expected. If something follows it is an error. */
+        if (poptGetArg(pc)) {
+            fprintf(stderr, _("Only one free argument is expected!\n\n"));
+            poptPrintHelp(pc, stderr, 0);
+            ret = EXIT_FAILURE;
+            goto done;
+        }
+    }
+
+    /* If at least one option is required and not provided, print error. */
+    if (require_option == SSS_TOOL_OPT_REQUIRED
+            && ((_fopt != NULL && cmdline->argc < 2) || cmdline->argc < 1)) {
+        fprintf(stderr, _("At least one option is required!\n\n"));
+        poptPrintHelp(pc, stderr, 0);
+        ret = EXIT_FAILURE;
+        goto done;
+    }
+
+    ret = EXIT_SUCCESS;
+
+done:
+    poptFreeContext(pc);
+    talloc_free(help);
+    return ret;
+}
+
+int sss_tool_popt(struct sss_cmdline *cmdline,
+                  struct poptOption *options,
+                  enum sss_tool_opt require_option,
+                  sss_popt_fn popt_fn,
+                  void *popt_fn_pvt)
+{
+    return sss_tool_popt_ex(cmdline, options, require_option,
+                            popt_fn, popt_fn_pvt, NULL, NULL, NULL);
+}
+
+int sss_tool_main(int argc, const char **argv,
+                  struct sss_route_cmd *commands,
+                  void *pvt)
+{
+    struct sss_tool_ctx *tool_ctx;
+    uid_t uid;
+    int ret;
+
+    uid = getuid();
+    if (uid != 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Running under %d, must be root\n", uid);
+        ERROR("%1$s must be run as root\n", argv[0]);
+        return EXIT_FAILURE;
+    }
+
+    tool_ctx = sss_tool_init(NULL, &argc, argv);
+    if (tool_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tool context\n");
+        return EXIT_FAILURE;
+    }
+
+    ret = sss_tool_route(argc, argv, tool_ctx, commands, pvt);
+    talloc_free(tool_ctx);
+
+    return ret;
+}
+
+int sss_tool_parse_name(TALLOC_CTX *mem_ctx,
+                        struct sss_tool_ctx *tool_ctx,
+                        const char *input,
+                        const char **_username,
+                        struct sss_domain_info **_domain)
+{
+    char *username = NULL;
+    char *domname = NULL;
+    struct sss_domain_info *domain;
+    int ret;
+
+    ret = sss_parse_name_for_domains(mem_ctx, tool_ctx->domains,
+                                     tool_ctx->default_domain, input,
+                                     &domname, &username);
+    if (ret == EAGAIN) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to find domain. The domain name may "
+              "be a subdomain that was not yet found.\n");
+        goto done;
+    } else if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse name [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    domain = find_domain_by_name(tool_ctx->domains, domname, true);
+
+    *_username = username;
+    *_domain = domain;
+
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        talloc_zfree(username);
+        talloc_zfree(domname);
+    }
+
+    return ret;
+}
diff --git a/src/tools/common/sss_tools.h b/src/tools/common/sss_tools.h
new file mode 100644
index 0000000000000000000000000000000000000000..cfe11d06e1dadf8e49efe155c8a53f99a31e97fb
--- /dev/null
+++ b/src/tools/common/sss_tools.h
@@ -0,0 +1,91 @@
+/*
+    Authors:
+        Pavel Březina <pbrez...@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_TOOLS_H_
+#define _SSS_TOOLS_H_
+
+#include <talloc.h>
+#include <popt.h>
+
+#include "confdb/confdb.h"
+
+struct sss_tool_ctx {
+    struct confdb_ctx *confdb;
+
+    char *default_domain;
+    struct sss_domain_info *domains;
+};
+
+struct sss_tool_ctx *sss_tool_init(TALLOC_CTX *mem_ctx,
+                                   int *argc, const char **argv);
+
+struct sss_cmdline;
+
+typedef int
+(*sss_route_fn)(struct sss_cmdline *cmdline,
+                struct sss_tool_ctx *tool_ctx,
+                void *pvt);
+
+struct sss_route_cmd {
+    const char *command;
+    sss_route_fn fn;
+};
+
+int sss_tool_usage(const char *tool_name,
+                   struct sss_route_cmd *commands);
+
+int sss_tool_route(int argc, const char **argv,
+                   struct sss_tool_ctx *tool_ctx,
+                   struct sss_route_cmd *commands,
+                   void *pvt);
+
+typedef int (*sss_popt_fn)(poptContext pc, char option, void *pvt);
+
+enum sss_tool_opt {
+    SSS_TOOL_OPT_REQUIRED,
+    SSS_TOOL_OPT_OPTIONAL
+};
+
+int sss_tool_popt_ex(struct sss_cmdline *cmdline,
+                     struct poptOption *options,
+                     enum sss_tool_opt require_option,
+                     sss_popt_fn popt_fn,
+                     void *popt_fn_pvt,
+                     const char *free_opt_name,
+                     const char *free_opt_help,
+                     const char **_free_opt);
+
+int sss_tool_popt(struct sss_cmdline *cmdline,
+                  struct poptOption *options,
+                  enum sss_tool_opt require_option,
+                  sss_popt_fn popt_fn,
+                  void *popt_fn_pvt);
+
+int sss_tool_main(int argc, const char **argv,
+                  struct sss_route_cmd *commands,
+                  void *pvt);
+
+int sss_tool_parse_name(TALLOC_CTX *mem_ctx,
+                        struct sss_tool_ctx *tool_ctx,
+                        const char *input,
+                        const char **_username,
+                        struct sss_domain_info **_domain);
+
+#endif /* SRC_TOOLS_COMMON_SSS_TOOLS_H_ */
-- 
1.9.3

>From 49ceba67b14f671e320c3803c476d1c9dc23fd1c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrez...@redhat.com>
Date: Fri, 24 Jul 2015 09:58:11 +0200
Subject: [PATCH 4/4] TOOLS: add sss_override for local overrides

Resolves:
https://fedorahosted.org/sssd/ticket/2584
---
 Makefile.am                |  16 +-
 contrib/sssd.spec.in       |   2 +
 src/man/Makefile.am        |   1 +
 src/man/po/po4a.cfg        |   1 +
 src/man/sss_override.8.xml | 108 +++++++
 src/tools/sss_override.c   | 718 +++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 845 insertions(+), 1 deletion(-)
 create mode 100644 src/man/sss_override.8.xml
 create mode 100644 src/tools/sss_override.c

diff --git a/Makefile.am b/Makefile.am
index 1edecc483c61d04562b7bfd9086146e93963b74e..912bfc6641465ef5cd2ff2cce9975b4027c3218d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -117,7 +117,9 @@ sbin_PROGRAMS = \
     sss_groupshow \
     sss_cache \
     sss_debuglevel \
-    sss_seed
+    sss_override \
+    sss_seed \
+    $(NULL)
 
 sssdlibexec_PROGRAMS = \
     sssd_nss \
@@ -1297,6 +1299,18 @@ sss_signal_LDADD = \
     $(SSSD_INTERNAL_LTLIBS) \
     $(NULL)
 
+sss_override_SOURCES = \
+    src/tools/sss_override.c \
+    $(SSSD_TOOLS_OBJ) \
+    $(NULL)
+sss_override_LDADD = \
+    $(TOOLS_LIBS) \
+    $(SSSD_INTERNAL_LTLIBS) \
+    $(NULL)
+sss_override_CFLAGS = \
+    $(AM_CFLAGS) \
+    $(NULL)
+
 if BUILD_SUDO
 sss_sudo_cli_SOURCES = \
     src/sss_client/common.c \
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 15f7c582cba1b9052e180596625be7dd5749599f..f050501ff9d0711a0da7f094ee968cae87a3f49b 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -837,6 +837,7 @@ rm -rf $RPM_BUILD_ROOT
 %{_sbindir}/sss_groupmod
 %{_sbindir}/sss_groupshow
 %{_sbindir}/sss_obfuscate
+%{_sbindir}/sss_override
 %{_sbindir}/sss_debuglevel
 %{_sbindir}/sss_seed
 %{_mandir}/man8/sss_groupadd.8*
@@ -847,6 +848,7 @@ rm -rf $RPM_BUILD_ROOT
 %{_mandir}/man8/sss_userdel.8*
 %{_mandir}/man8/sss_usermod.8*
 %{_mandir}/man8/sss_obfuscate.8*
+%{_mandir}/man8/sss_override.8*
 %{_mandir}/man8/sss_debuglevel.8*
 %{_mandir}/man8/sss_seed.8*
 
diff --git a/src/man/Makefile.am b/src/man/Makefile.am
index 1ef1da48cce74f7d1ad77e3751ee6ac3450f0259..70cadf5951f56b78ff0bfbcb303e255478af5fec 100644
--- a/src/man/Makefile.am
+++ b/src/man/Makefile.am
@@ -51,6 +51,7 @@ man_MANS = \
     sssd-krb5.5 sssd-simple.5 \
     sssd_krb5_locator_plugin.8 sss_groupshow.8 \
     pam_sss.8 sss_obfuscate.8 sss_cache.8 sss_debuglevel.8 sss_seed.8 \
+    sss_override.8
     $(NULL)
 
 if BUILD_SAMBA
diff --git a/src/man/po/po4a.cfg b/src/man/po/po4a.cfg
index 25d20c6f0c23a0900469573d47ab96ebc8898e50..67e87ba7006f0bb1346e5b845428f2bed1a324db 100644
--- a/src/man/po/po4a.cfg
+++ b/src/man/po/po4a.cfg
@@ -11,6 +11,7 @@
 [type:docbook] sssd-sudo.5.xml $lang:$(builddir)/$lang/sssd-sudo.5.xml
 [type:docbook] sssd.8.xml $lang:$(builddir)/$lang/sssd.8.xml
 [type:docbook] sss_obfuscate.8.xml $lang:$(builddir)/$lang/sss_obfuscate.8.xml
+[type:docbook] sss_override.8.xml $lang:$(builddir)/$lang/sss_override.8.xml
 [type:docbook] sss_useradd.8.xml $lang:$(builddir)/$lang/sss_useradd.8.xml
 [type:docbook] sssd-krb5.5.xml $lang:$(builddir)/$lang/sssd-krb5.5.xml
 [type:docbook] sss_groupadd.8.xml $lang:$(builddir)/$lang/sss_groupadd.8.xml
diff --git a/src/man/sss_override.8.xml b/src/man/sss_override.8.xml
new file mode 100644
index 0000000000000000000000000000000000000000..307cb5585c3a7fc3d7e55363222f5078aeca7dce
--- /dev/null
+++ b/src/man/sss_override.8.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd";>
+<reference>
+<title>SSSD Manual pages</title>
+<refentry>
+    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"; href="include/upstream.xml" />
+
+    <refmeta>
+        <refentrytitle>sss_override</refentrytitle>
+        <manvolnum>8</manvolnum>
+    </refmeta>
+
+    <refnamediv id='name'>
+        <refname>sss_override</refname>
+        <refpurpose>create local overrides of user and group attributes</refpurpose>
+    </refnamediv>
+
+    <refsynopsisdiv id='synopsis'>
+        <cmdsynopsis>
+            <command>sss_override</command>
+            <arg choice='plain'><replaceable>COMMAND</replaceable></arg>
+            <arg choice='opt'>
+                <replaceable>options</replaceable>
+            </arg>
+        </cmdsynopsis>
+    </refsynopsisdiv>
+
+    <refsect1 id='description'>
+        <title>DESCRIPTION</title>
+        <para>
+            <command>sss_override</command> enables to create a client-side
+            view and allows to change selected values of specific user
+            and groups. This change takes effect only on local machine. 
+        </para>
+        <para>
+            Overrides data are stored in SSSD cache. If the cache is deleted
+            all local overrides are lost.
+        </para>
+    </refsect1>
+
+    <refsect1 id='commands'>
+        <title>AVAILABLE COMMANDS</title>
+        <para>
+            Argument <emphasis>NAME</emphasis> is the name of original object
+            in all commands. It is not possible to override
+            <emphasis>uid</emphasis> or <emphasis>gid</emphasis> to 0.
+        </para>
+        <variablelist remap='IP'>
+            <varlistentry>
+                <term>
+                    <option>user-add</option>
+                    <emphasis>NAME</emphasis>
+                    <optional><option>-n,--name</option> NAME</optional>
+                    <optional><option>-u,--uid</option> UID</optional>
+                    <optional><option>-g,--gid</option> GID</optional> 
+                    <optional><option>-h,--home</option> HOME</optional>
+                    <optional><option>-s,--shell</option> SHELL</optional>
+                    <optional><option>-c,--gecos</option> GECOS</optional>
+                </term>
+                <listitem>
+                    <para>
+                        Override attributes of an user.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>
+                    <option>user-del</option>
+                    <emphasis>NAME</emphasis>
+                </term>
+                <listitem>
+                    <para>
+                        Remove user overrides.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>
+                    <option>group-add</option>
+                    <emphasis>NAME</emphasis>
+                    <optional><option>-n,--name</option> NAME</optional>
+                    <optional><option>-g,--gid</option> GID</optional> 
+                </term>
+                <listitem>
+                    <para>
+                        Override attributes of a group.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>
+                    <option>group-del</option>
+                    <emphasis>NAME</emphasis>
+                </term>
+                <listitem>
+                    <para>
+                        Remove group overrides.
+                    </para>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+    </refsect1>
+
+    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"; href="include/seealso.xml" />
+
+</refentry>
+</reference>
diff --git a/src/tools/sss_override.c b/src/tools/sss_override.c
new file mode 100644
index 0000000000000000000000000000000000000000..676327dec4f91342c3c3464fd03e0f605eaa0fc7
--- /dev/null
+++ b/src/tools/sss_override.c
@@ -0,0 +1,718 @@
+/*
+    Authors:
+        Pavel Březina <pbrez...@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 "db/sysdb.h"
+#include "tools/common/sss_tools.h"
+
+#define LOCALVIEW SYSDB_LOCAL_VIEW_NAME
+
+struct override_user {
+    const char *input_name;
+    const char *orig_name;
+    struct sss_domain_info *domain;
+
+    const char *name;
+    uid_t uid;
+    gid_t gid;
+    const char *home;
+    const char *shell;
+    const char *gecos;
+};
+
+struct override_group {
+    const char *input_name;
+    const char *orig_name;
+    struct sss_domain_info *domain;
+
+    const char *name;
+    gid_t gid;
+};
+
+static int 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;
+    const char *orig_name;
+    struct sss_domain_info *domain;
+    int ret;
+
+    require = options == NULL ? SSS_TOOL_OPT_OPTIONAL : SSS_TOOL_OPT_REQUIRED;
+
+    ret = sss_tool_popt_ex(cmdline, options, require,
+                           NULL, NULL, "NAME", _("Specify name of modified "
+                           "object."), &input_name);
+    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) {
+        fprintf(stderr, _("Unable to parse name.\n"));
+        return ret;
+    }
+
+    *_input_name = input_name;
+    *_orig_name = orig_name;
+    *_domain = domain;
+
+    return EXIT_SUCCESS;
+}
+
+static int parse_cmdline_user_add(struct sss_cmdline *cmdline,
+                                  struct sss_tool_ctx *tool_ctx,
+                                  struct override_user *user)
+{
+    struct poptOption options[] = {
+        POPT_AUTOHELP
+        {"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 },
+        POPT_TABLEEND
+    };
+
+    return parse_cmdline(cmdline, tool_ctx, options, &user->input_name,
+                         &user->orig_name, &user->domain);
+}
+
+static int 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 int parse_cmdline_group_add(struct sss_cmdline *cmdline,
+                                   struct sss_tool_ctx *tool_ctx,
+                                   struct override_group *group)
+{
+    struct poptOption options[] = {
+        POPT_AUTOHELP
+        {"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 int 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 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_default_view(viewname)) {
+            DEBUG(SSSDBG_MINOR_FAILURE, "There already exists view %s. "
+                  "Only one view is supported. Nothing to do.\n", viewname);
+            ret = EEXIST;
+            goto done;
+        } else if (strcmp(viewname, LOCALVIEW) == 0) {
+            DEBUG(SSSDBG_TRACE_FUNC, "%s view is already present.\n", viewname);
+            ret = EOK;
+            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;
+}
+
+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,
+                                       const char *name,
+                                       uid_t uid,
+                                       gid_t gid,
+                                       const char *home,
+                                       const char *shell,
+                                       const char *gecos)
+{
+    struct sysdb_attrs *attrs;
+    errno_t ret;
+
+    attrs = sysdb_new_attrs(mem_ctx);
+    if (attrs == NULL) {
+        return NULL;
+    }
+
+    if (name != NULL) {
+        ret = sysdb_attrs_add_string(attrs, SYSDB_NAME, name);
+        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;
+        }
+    }
+
+    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->name, user->uid, user->gid, user->home,
+                       user->shell, user->gecos);
+}
+
+static struct sysdb_attrs *build_group_attrs(TALLOC_CTX *mem_ctx,
+                                             struct override_group *group)
+{
+    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)
+{
+    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;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return NULL;
+    }
+
+    /* Ensure that the object is in cache. */
+    switch (type) {
+    case SYSDB_MEMBER_USER:
+        if (getpwnam(name) == NULL) {
+            ret = ENOENT;
+            goto done;
+        }
+        break;
+    case SYSDB_MEMBER_GROUP:
+        if (getgrnam(name) == 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 {
+        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, name, &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, name, &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);
+
+    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);
+
+    if (ret != EOK) {
+        return NULL;
+    }
+
+    return dn;
+}
+
+static const char * get_user_dn_and_domain(TALLOC_CTX *mem_ctx,
+                                           struct sss_domain_info *domains,
+                                           struct override_user *user)
+{
+    return get_object_dn_and_domain(mem_ctx, SYSDB_MEMBER_USER,
+                         user->orig_name, user->domain, domains,
+                         &user->domain);
+}
+
+static const char * get_group_dn_and_domain(TALLOC_CTX *mem_ctx,
+                                            struct sss_domain_info *domains,
+                                            struct override_group *group)
+{
+    return get_object_dn_and_domain(mem_ctx, SYSDB_MEMBER_GROUP,
+                         group->orig_name, group->domain, domains,
+                         &group->domain);
+}
+
+static errno_t override_object_add(struct sss_domain_info *domain,
+                                   enum sysdb_member_type type,
+                                   struct sysdb_attrs *attrs,
+                                   const char *obj_dn)
+{
+    TALLOC_CTX *tmp_ctx;
+    const char *anchor;
+    struct ldb_dn *ldb_dn;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    ldb_dn = ldb_dn_new(tmp_ctx, sysdb_ctx_get_ldb(domain->sysdb), obj_dn);
+    if (ldb_dn == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    anchor = build_anchor(tmp_ctx, obj_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", obj_dn);
+
+    ret = sysdb_store_override(domain, LOCALVIEW, type, attrs, ldb_dn);
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+static errno_t override_object_del(struct sss_domain_info *domain,
+                                   const char *obj_dn)
+{
+    TALLOC_CTX *tmp_ctx;
+    const char *anchor;
+    struct ldb_dn *override_dn;
+    struct ldb_message *msg;
+    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;
+    }
+
+    anchor = build_anchor(tmp_ctx, obj_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", obj_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 = ldb_dn_new(msg, ldb, obj_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 = sysdb_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 = sysdb_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 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);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+        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");
+        return EXIT_FAILURE;
+    }
+
+    ret = override_object_add(user.domain, SYSDB_MEMBER_USER, attrs, dn);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add override object.\n");
+        return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
+}
+
+static int override_user_del(struct sss_cmdline *cmdline,
+                             struct sss_tool_ctx *tool_ctx,
+                             void *pvt)
+{
+    struct override_user user = {NULL};
+    const char *dn;
+    int ret;
+
+    ret = parse_cmdline_user_del(cmdline, tool_ctx, &user);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+        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);
+        return EXIT_FAILURE;
+    }
+
+    ret = override_object_del(user.domain, dn);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add override object.\n");
+        return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
+}
+
+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);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+        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");
+        return EXIT_FAILURE;
+    }
+
+    ret = override_object_add(group.domain, SYSDB_MEMBER_GROUP, attrs, dn);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add override object.\n");
+        return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
+}
+
+static int override_group_del(struct sss_cmdline *cmdline,
+                              struct sss_tool_ctx *tool_ctx,
+                              void *pvt)
+{
+    struct override_group group = {NULL};
+    const char *dn;
+    int ret;
+
+    ret = parse_cmdline_group_del(cmdline, tool_ctx, &group);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+        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 = override_object_del(group.domain, dn);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add override object.\n");
+        return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
+}
+
+int main(int argc, const char **argv)
+{
+    struct sss_route_cmd commands[] = {
+        {"user-add", override_user_add},
+        {"user-del", override_user_del},
+        {"group-add", override_group_add},
+        {"group-del", override_group_del},
+        {NULL, NULL}
+    };
+
+    return sss_tool_main(argc, argv, commands, NULL);
+}
-- 
1.9.3

_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel

Reply via email to