[SSSD] [PATCH] Ability to set a domain as case sensitive or insensitive

Jakub Hrozek jhrozek at redhat.com
Sun Dec 11 22:04:30 UTC 2011


On Fri, Dec 09, 2011 at 03:18:38PM +0100, Jan Zelený wrote:
> > On Thu, Dec 08, 2011 at 11:02:05AM +0100, Jakub Hrozek wrote:
> > > [PATCH 1/6] sss_utf8_tolower utility function+unit tests
> > > This will be used later on to lowercase the usernames. Also includes
> > > unit tests for the whole sss_utf8 module.
> 
> Nack, it seems that this patch breaks the test suite (or perhaps it's the last 
> one, I'm not 100% sure.
> 

I ran "make check" in-tree and didn't see any error, but I think I see
the bug now.

The root cause was that I included sss_tc_utf8_tolower() into util.c which
made it depend on sss_utf8.c and by extension on the UTF8 library of choice.
At the same time the memberof plugin depends on util.c so we would had
linked the plugin with the UTF8 library.

I decided to include the talloc wrapper in a new module "sss_tc_utf8.c"
-- I think a module with a single function is a lesser evil than linking
the whole memberof module with an UTF8 library (which might be even GLib..)

A revised patch is attached.

> > > [PATCH 2/6] Responders: Split getting domain by name into separate
> > > function A utility function originally written for the SUDO responder,
> > > but it turns out it's useful earlier
> 
> Ack
> 
> > > [PATCH 3/6] Use the case sensitivity flag in responders
> > > Reads the configuration option and includes its handling in the
> > > responders.
> > > Unfortunately some parts like negative cache are more complex because one
> > > domain can be case sensitive and another insensitive during multidomain
> > > searches.
> 
> Nack,
> why are you using talloc strdup in functions like nss_cmd_getgrnam_search? I 
> mean talloc_strdup(dctx, cmdctx->name). I suspect it's for better consistency 
> with behavior of the sss_tc_utf8_tolower() but there is no real need for it 
> IMO.

To be able to free the domain-specific name inside the domain loop which
I forgot to do in the first iteration of patches.

I changed the patch to free "name" on every iteration to avoid clinging
to allocated memory.

> 
> The pd_set_primary_name() doesn't seem like a part of the patch, or is it?
>I'd 
> prefer separate patch for it and places where it is used. I'm also thinking 
> whether or not should it also detect the case_sensitive flag.
> 

The reasoning begind pd_set_primary_name() is username canonicalization.

sysdb_getpwnam() can return data based on both the real name or the
lowercased alias, because the filter it uses matches both "name" and
"nameAlias".  Only "name" corresponds to RDN, though. On many places,
including auth and access, SSSD depends on searching "user by name" by
constructing a user DN based on the username and domain name,.

There is no need to pass the case_sensitive flag, strcmp is already case
sensitive and the name we search with is lowercased in the previous
hunk if case_sensitive is true.

> > > [PATCH 4/6] Refactor saving sdap entities
> > > There was too much code duplication between
> > > sdap_save_{user,group,netgroup} and my later patch would add even more.
> > > This patch removes the most egregious cases.
> 
> Nack,
> I think that the "multivalued" argument is really not necessary, is it? 
> Without it, there is also no need for those two wrapper macros.
> 

It's actually required to maintain the same behaviour as the original code.

In some cases such as when storing modifyTimestamp we would only store
the first value unconditionally even if the attribute was multivalued on
the server. This is the case for both original and refactored code.

I haven't used the macro for storing all string attributes. Some attributes
like ldap_user_principal are munged before saving to sysdb and that's
difficult to generalize withut some kind of callback, but still, only the
first value is saved even if the original attribute was multivalued.

Is there any value in throwing an error like we do with the POSIX set
of attributes?

> I noticed that at one place, there was strdup() called before adding the 
> string to sysdb_attrs. I didn't inspect this much further, so my question is 
> whether it would make any sense to do that in sdap_attrs_add_ldap_attr() and 
> sdap_save_all_names()?

Which strdup was that? At one place isn't really descriptive.

I haven't touched the cases where we need to pass the attribute values out
(like USN), the other cases were duping the value on user_attrs which is
allocated on tmpctx and never stolen or moved elsewhere.

> 
> > > [PATCH 5/6] Use the case sensitivity flag in the LDAP provider
> > > When saving users,groups and netgroups, saves the lowercase version of
> > > the name as an alias in addition to the original name that is still
> > > stored as name as well as part of RDN.
> 
> Nack,
> I don't think there is a need for aliases to be both in the list both 
> untransformed and in lowercase. Please check that and if it isn't necessary, 
> please remove it.

If the duplicate lowercase attribute is removed, aliases wouldn't work
if the user switches the case sensitivity flag of a domain from false to
true (because only secondaryname would be stored and client might ask for
secondaryName as stored on server).

An extra sysdb attribute is not much harm, really.

> 
> Variable lowername in sdap_save_all_names() is not used. Also instead of 
> argument "dom", I would prefer "case_sensitive" being directly given to the 
> sdap_save_all_names().

Thanks, fixed.

> 
> > > [PATCH 6/6] Use the case sensitivity flag in the simple access provider
> > > Performs string comparisons in case sensitive or insensitive manner
> > > depending on the configuration.
> 
> Ack
> 
> Jan

It seems that there are no issues with the general approach so I will
file a ticket to handle LOCAL provider (which will probably get deferred)
and prepare another patch to handle the proxy provider.
-------------- next part --------------
From c0a7d1f9c863d9f5c1847a1847fb558703df686a Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 6 Dec 2011 14:57:58 +0100
Subject: [PATCH 1/6] sss_utf8_tolower utility function+unit tests

---
 Makefile.am                            |    3 +-
 src/tests/util-tests.c                 |   72 ++++++++++++++++++++++++++++++++
 src/util/{sss_utf8.h => sss_tc_utf8.c} |   34 ++++++---------
 src/util/sss_utf8.c                    |   38 +++++++++++++++++
 src/util/sss_utf8.h                    |    5 ++
 src/util/util.c                        |    1 +
 src/util/util.h                        |    4 ++
 7 files changed, 136 insertions(+), 21 deletions(-)
 copy src/util/{sss_utf8.h => sss_tc_utf8.c} (63%)

diff --git a/Makefile.am b/Makefile.am
index 8d75b8767bd30532f9a241ca1518545d602beffb..b8fb9e094c57c1c1ddbc51d10232ea64236e813a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -382,7 +382,8 @@ libsss_util_la_SOURCES = \
     src/util/strtonum.c \
     src/util/check_and_open.c \
     src/util/refcount.c \
-    src/util/sss_utf8.c
+    src/util/sss_utf8.c \
+    src/util/sss_tc_utf8.c
 libsss_util_la_LIBADD = \
     $(SSSD_LIBS) \
     $(UNICODE_LIBS) \
diff --git a/src/tests/util-tests.c b/src/tests/util-tests.c
index 52d302778ed3c819d3ffeef96e7b52c778a15825..d0636651f1abecb10240927aa2a011841e74447b 100644
--- a/src/tests/util-tests.c
+++ b/src/tests/util-tests.c
@@ -26,6 +26,7 @@
 #include <talloc.h>
 #include <check.h>
 #include "util/util.h"
+#include "util/sss_utf8.h"
 #include "tests/common.h"
 
 START_TEST(test_parse_args)
@@ -312,6 +313,68 @@ START_TEST(test_size_t_overflow)
 }
 END_TEST
 
+START_TEST(test_utf8_lowercase)
+{
+    const uint8_t munchen_utf8_upcase[] = { 'M', 0xC3, 0x9C, 'N', 'C', 'H', 'E', 'N', 0x0 };
+    const uint8_t munchen_utf8_lowcase[] = { 'm', 0xC3, 0xBC, 'n', 'c', 'h', 'e', 'n', 0x0 };
+    uint8_t *lcase;
+
+    lcase = sss_utf8_tolower(munchen_utf8_upcase);
+    fail_if(memcmp(lcase, munchen_utf8_lowcase, strlen((const char *) lcase)));
+    sss_utf8_free(lcase);
+}
+END_TEST
+
+START_TEST(test_utf8_talloc_lowercase)
+{
+    const uint8_t munchen_utf8_upcase[] = { 'M', 0xC3, 0x9C, 'N', 'C', 'H', 'E', 'N', 0x0 };
+    const uint8_t munchen_utf8_lowcase[] = { 'm', 0xC3, 0xBC, 'n', 'c', 'h', 'e', 'n', 0x0 };
+    char *lcase;
+
+    TALLOC_CTX *test_ctx;
+    test_ctx = talloc_new(NULL);
+    fail_if(test_ctx == NULL);
+
+    lcase = sss_tc_utf8_tolower(test_ctx, (const char *) munchen_utf8_upcase);
+    fail_if(memcmp(lcase, munchen_utf8_lowcase, strlen((const char *) lcase)));
+    talloc_free(test_ctx);
+}
+END_TEST
+
+START_TEST(test_utf8_caseeq)
+{
+    const uint8_t munchen_utf8_upcase[] = { 'M', 0xC3, 0x9C, 'N', 'C', 'H', 'E', 'N', 0x0 };
+    const uint8_t munchen_utf8_lowcase[] = { 'm', 0xC3, 0xBC, 'n', 'c', 'h', 'e', 'n', 0x0 };
+    const uint8_t czech_utf8_lowcase[] = { 0xC4, 0x8D, 'e', 'c', 'h', 0x0 };
+    const uint8_t czech_utf8_upcase[] = { 0xC4, 0x8C, 'e', 'c', 'h', 0x0 };
+    const uint8_t czech_utf8_lowcase_neg[] = { 0xC4, 0x8E, 'e', 'c', 'h', 0x0 };
+    errno_t ret;
+
+    ret = sss_utf8_case_eq(munchen_utf8_upcase, munchen_utf8_lowcase);
+    fail_unless(ret == EOK, "Latin 1 Supplement comparison failed\n");
+
+    ret = sss_utf8_case_eq(czech_utf8_upcase, czech_utf8_lowcase);
+    fail_unless(ret == EOK, "Latin Extended A comparison failed\n");
+
+    ret = sss_utf8_case_eq(czech_utf8_upcase, czech_utf8_lowcase_neg);
+    fail_if(ret == EOK, "Negative test succeeded\n");
+}
+END_TEST
+
+START_TEST(test_utf8_check)
+{
+    const char *invalid = "ad\351la\357d";
+    const uint8_t valid[] = { 'M', 0xC3, 0x9C, 'N', 'C', 'H', 'E', 'N', 0x0 };
+    bool ret;
+
+    ret = sss_utf8_check(valid, strlen((const char *) valid));
+    fail_unless(ret == true, "Positive test failed\n");
+
+    ret = sss_utf8_check((const uint8_t *) invalid, strlen(invalid));
+    fail_unless(ret == false, "Negative test succeeded\n");
+}
+END_TEST
+
 Suite *util_suite(void)
 {
     Suite *s = suite_create("util");
@@ -324,7 +387,16 @@ Suite *util_suite(void)
     tcase_add_test (tc_util, test_parse_args);
     tcase_set_timeout(tc_util, 60);
 
+    TCase *tc_utf8 = tcase_create("utf8");
+    tcase_add_test (tc_util, test_utf8_lowercase);
+    tcase_add_test (tc_util, test_utf8_talloc_lowercase);
+    tcase_add_test (tc_util, test_utf8_caseeq);
+    tcase_add_test (tc_util, test_utf8_check);
+
+    tcase_set_timeout(tc_utf8, 60);
+
     suite_add_tcase (s, tc_util);
+    suite_add_tcase (s, tc_utf8);
 
     return s;
 }
diff --git a/src/util/sss_utf8.h b/src/util/sss_tc_utf8.c
similarity index 63%
copy from src/util/sss_utf8.h
copy to src/util/sss_tc_utf8.c
index 37dcff959b1a86110fba919082b075c7f4963f02..28d4f3f18fbb9c38412d6aac34a05c2d09fc33d7 100644
--- a/src/util/sss_utf8.h
+++ b/src/util/sss_tc_utf8.c
@@ -1,8 +1,6 @@
 /*
-    SSSD
-
     Authors:
-        Stephen Gallagher <sgallagh at redhat.com>
+        Jakub Hrozek <jhrozek at redhat.com>
 
     Copyright (C) 2011 Red Hat
 
@@ -20,24 +18,20 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#ifndef SSS_UTF8_H_
-#define SSS_UTF8_H_
+#include <talloc.h>
+#include "util/sss_utf8.h"
 
-#ifdef HAVE_LIBUNISTRING
-#include <unistr.h>
-#include <unicase.h>
-#elif HAVE_GLIB2
-#include <glib.h>
-#endif
-#include "util/util.h"
+char *
+sss_tc_utf8_tolower(TALLOC_CTX *mem_ctx, const char *s)
+{
+    uint8_t *lower;
+    char *ret;
 
-#ifndef ENOMATCH
-#define ENOMATCH -1
-#endif
+    lower = sss_utf8_tolower((const uint8_t *) s);
+    if (!lower) return NULL;
 
-bool sss_utf8_check(const uint8_t *s, size_t n);
+    ret = talloc_strdup(mem_ctx, (const char *) lower);
+    sss_utf8_free(lower);
+    return ret;
+}
 
-errno_t sss_utf8_case_eq(const uint8_t *s1, const uint8_t *s2);
-
-
-#endif /* SSS_UTF8_H_ */
diff --git a/src/util/sss_utf8.c b/src/util/sss_utf8.c
index 4a98233bb97bbfc77a60bba6a24ef04bf78bfa56..82487683110cb371b998e5cccbcc4352fbed50a4 100644
--- a/src/util/sss_utf8.c
+++ b/src/util/sss_utf8.c
@@ -24,6 +24,44 @@
 #include "sss_utf8.h"
 
 #ifdef HAVE_LIBUNISTRING
+void sss_utf8_free(void *ptr)
+{
+    return free(ptr);
+}
+#elif HAVE_GLIB2
+void sss_utf8_free(void *ptr)
+{
+    return g_free(ptr);
+}
+#else
+#error No unicode library
+#endif
+
+#ifdef HAVE_LIBUNISTRING
+uint8_t *sss_utf8_tolower(const uint8_t *s)
+{
+    size_t llen;
+    uint8_t *lower;
+
+    lower = u8_tolower(s, u8_strlen(s), NULL, NULL, NULL, &llen);
+    if (!lower) return NULL;
+
+    lower = realloc(lower, sizeof(uint8_t) * (llen+1));
+    if (!lower) return NULL;
+
+    lower[llen] = '\0';
+    return lower;
+}
+#elif HAVE_GLIB2
+uint8_t *sss_utf8_tolower(const uint8_t *s)
+{
+    return (uint8_t *) g_utf8_strdown((const gchar *) s, -1);
+}
+#else
+#error No unicode library
+#endif
+
+#ifdef HAVE_LIBUNISTRING
 bool sss_utf8_check(const uint8_t *s, size_t n)
 {
     if (u8_check(s, n) == NULL) {
diff --git a/src/util/sss_utf8.h b/src/util/sss_utf8.h
index 37dcff959b1a86110fba919082b075c7f4963f02..7be54d69b1ca347dd477d6b68cf5eb8af85c4c46 100644
--- a/src/util/sss_utf8.h
+++ b/src/util/sss_utf8.h
@@ -35,6 +35,11 @@
 #define ENOMATCH -1
 #endif
 
+void sss_utf8_free(void *ptr);
+
+/* The result must be freed with sss_utf8_free() */
+uint8_t *sss_utf8_tolower(const uint8_t *s);
+
 bool sss_utf8_check(const uint8_t *s, size_t n);
 
 errno_t sss_utf8_case_eq(const uint8_t *s1, const uint8_t *s2);
diff --git a/src/util/util.c b/src/util/util.c
index b4b1b124317377fe22612576e8e2d8d3256c14ce..f525c915b6886f8f0e51f0026831e6301c2df8ff 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -23,6 +23,7 @@
 
 #include "talloc.h"
 #include "util/util.h"
+#include "util/sss_utf8.h"
 #include "dhash.h"
 
 /* split a string into an allocated array of strings.
diff --git a/src/util/util.h b/src/util/util.h
index 9a00647179b010f7dd1b9744b3aaa013cc6e5769..00fb67d3800c33b1949441e6da4a36de64ed5671 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -502,4 +502,8 @@ errno_t sss_filter_sanitize(TALLOC_CTX *mem_ctx,
 char *
 sss_escape_ip_address(TALLOC_CTX *mem_ctx, int family, const char *addr);
 
+/* from sss_tc_utf8.c */
+char *
+sss_tc_utf8_tolower(TALLOC_CTX *mem_ctx, const char *s);
+
 #endif /* __SSSD_UTIL_H__ */
-- 
1.7.7.3

-------------- next part --------------
From cc14f2a2257b5d2d3bb5c588117231849b90f0cb Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 5 Dec 2011 16:00:44 +0100
Subject: [PATCH 2/6] Responders: Split getting domain by name into separate
 function

---
 src/responder/common/responder.h        |    2 ++
 src/responder/common/responder_common.c |   13 +++++++++++++
 src/responder/nss/nsssrv_cmd.c          |   19 +++----------------
 src/responder/nss/nsssrv_netgroup.c     |    2 +-
 src/responder/pam/pamsrv_cmd.c          |    7 ++-----
 5 files changed, 21 insertions(+), 22 deletions(-)

diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
index 3b5ab8a42136c17f0f71a202f8ae9367edfa6afb..f6784e916bbf2b7f191d2a53b6670c2a29adb996 100644
--- a/src/responder/common/responder.h
+++ b/src/responder/common/responder.h
@@ -150,6 +150,8 @@ int sss_parse_name(TALLOC_CTX *memctx,
 
 int sss_dp_get_domain_conn(struct resp_ctx *rctx, const char *domain,
                            struct be_conn **_conn);
+struct sss_domain_info *
+responder_get_domain(struct sss_domain_info *doms, const char *domain);
 
 /* responder_cmd.c */
 int sss_cmd_execute(struct cli_ctx *cctx, struct sss_cmd_table *sss_cmds);
diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
index 1b28a92ef6c6569a52aecf37a4e37a1c7af95de1..99b1a23aa8581e01fb2a02d36657df6c20717812 100644
--- a/src/responder/common/responder_common.c
+++ b/src/responder/common/responder_common.c
@@ -624,6 +624,19 @@ int sss_dp_get_domain_conn(struct resp_ctx *rctx, const char *domain,
     return EOK;
 }
 
+struct sss_domain_info *
+responder_get_domain(struct sss_domain_info *doms, const char *domain)
+{
+    struct sss_domain_info *dom;
+
+    for (dom = doms; dom; dom = dom->next) {
+        if (strcasecmp(dom->name, domain) == 0) break;
+    }
+    if (!dom) DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown domain [%s]!\n", domain));
+
+    return dom;
+}
+
 int responder_logrotate(DBusMessage *message,
                         struct sbus_connection *conn)
 {
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 720813fda1efb0004b6add4fdcfa01156f1c3f12..f0270acd26659e8005a043fd9e154723cc868d0d 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -45,19 +45,6 @@ static int nss_cmd_send_error(struct nss_cmd_ctx *cmdctx, int err)
     return EOK;
 }
 
-struct sss_domain_info *nss_get_dom(struct sss_domain_info *doms,
-                                    const char *domain)
-{
-    struct sss_domain_info *dom;
-
-    for (dom = doms; dom; dom = dom->next) {
-        if (strcasecmp(dom->name, domain) == 0) break;
-    }
-    if (!dom) DEBUG(2, ("Unknown domain [%s]!\n", domain));
-
-    return dom;
-}
-
 int fill_empty(struct sss_packet *packet)
 {
     uint8_t *body;
@@ -932,7 +919,7 @@ static int nss_cmd_getpwnam(struct cli_ctx *cctx)
               cmdctx->name, domname?domname:"<ALL>"));
 
     if (domname) {
-        dctx->domain = nss_get_dom(cctx->rctx->domains, domname);
+        dctx->domain = responder_get_domain(cctx->rctx->domains, domname);
         if (!dctx->domain) {
             ret = ENOENT;
             goto done;
@@ -2219,7 +2206,7 @@ static int nss_cmd_getgrnam(struct cli_ctx *cctx)
               cmdctx->name, domname?domname:"<ALL>"));
 
     if (domname) {
-        dctx->domain = nss_get_dom(cctx->rctx->domains, domname);
+        dctx->domain = responder_get_domain(cctx->rctx->domains, domname);
         if (!dctx->domain) {
             ret = ENOENT;
             goto done;
@@ -3277,7 +3264,7 @@ static int nss_cmd_initgroups(struct cli_ctx *cctx)
               cmdctx->name, domname?domname:"<ALL>"));
 
     if (domname) {
-        dctx->domain = nss_get_dom(cctx->rctx->domains, domname);
+        dctx->domain = responder_get_domain(cctx->rctx->domains, domname);
         if (!dctx->domain) {
             ret = ENOENT;
             goto done;
diff --git a/src/responder/nss/nsssrv_netgroup.c b/src/responder/nss/nsssrv_netgroup.c
index 7d5665d41788c26cdb71538af4bbbd9f31db1614..09d07df105c2ab0681a63e58bfdd231711429eb5 100644
--- a/src/responder/nss/nsssrv_netgroup.c
+++ b/src/responder/nss/nsssrv_netgroup.c
@@ -204,7 +204,7 @@ static struct tevent_req *setnetgrent_send(TALLOC_CTX *mem_ctx,
               state->netgr_shortname, domname?domname:"<ALL>"));
 
     if (domname) {
-        dctx->domain = nss_get_dom(client->rctx->domains, domname);
+        dctx->domain = responder_get_domain(client->rctx->domains, domname);
         if (!dctx->domain) {
             goto error;
         }
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index 40df755d6af7e83d51c923f75bd0c19e4e7044de..5f77697fd33779d9cb48f993d7a8368cef8b3928 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -756,14 +756,11 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
 
     /* now check user is valid */
     if (pd->domain) {
-        for (dom = cctx->rctx->domains; dom; dom = dom->next) {
-            if (strcasecmp(dom->name, pd->domain) == 0) break;
-        }
-        if (!dom) {
+        preq->domain = responder_get_domain(cctx->rctx->domains, pd->domain);
+        if (preq->domain) {
             ret = ENOENT;
             goto done;
         }
-        preq->domain = dom;
     }
     else {
         for (dom = preq->cctx->rctx->domains; dom; dom = dom->next) {
-- 
1.7.7.3

-------------- next part --------------
From 504a7e8750169300bea1bbcf4c4a2e4dcdfe7804 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 16 Nov 2011 14:09:59 +0100
Subject: [PATCH 3/6] Use the case sensitivity flag in responders

---
 src/confdb/confdb.c                     |    7 +
 src/confdb/confdb.h                     |    2 +
 src/config/SSSDConfig.py                |    1 +
 src/config/SSSDConfigTest.py            |    2 +
 src/config/etc/sssd.api.conf            |    1 +
 src/man/sssd.conf.5.xml                 |   12 ++
 src/responder/common/negcache.c         |  267 +++++++++++++++++++++----------
 src/responder/common/negcache.h         |   10 +-
 src/responder/common/responder_common.c |    1 -
 src/responder/nss/nsssrv_cmd.c          |   39 +++--
 src/responder/nss/nsssrv_netgroup.c     |   21 ++-
 src/responder/pam/pamsrv_cmd.c          |   34 ++++-
 12 files changed, 285 insertions(+), 112 deletions(-)

diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
index 3f668dd3dec337afc05cea4459a4eff123161cb9..9ebe0127da81c7d26e75eca4e4136472317e534d 100644
--- a/src/confdb/confdb.c
+++ b/src/confdb/confdb.c
@@ -856,6 +856,13 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
         goto done;
     }
 
+    ret = get_entry_as_bool(res->msgs[0], &domain->case_sensitive,
+                            CONFDB_DOMAIN_CASE_SENSITIVE, true);
+    if(ret != EOK) {
+        DEBUG(0, ("Invalid value for %s\n", CONFDB_DOMAIN_CASE_SENSITIVE));
+        goto done;
+    }
+
     *_domain = domain;
     ret = EOK;
 done:
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 266e4d27189dd14d282045cc5c7e857ebc00f974..2ebbf4f46369161a443b5ba80a58245990651948 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -117,6 +117,7 @@
 #define CONFDB_DOMAIN_FAMILY_ORDER "lookup_family_order"
 #define CONFDB_DOMAIN_ACCOUNT_CACHE_EXPIRATION "account_cache_expiration"
 #define CONFDB_DOMAIN_OVERRIDE_GID "override_gid"
+#define CONFDB_DOMAIN_CASE_SENSITIVE "case_sensitive"
 
 /* Local Provider */
 #define CONFDB_LOCAL_DEFAULT_SHELL   "default_shell"
@@ -150,6 +151,7 @@ struct sss_domain_info {
 
     bool cache_credentials;
     bool legacy_passwords;
+    bool case_sensitive;
 
     gid_t override_gid;
     const char *override_homedir;
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index 84c9bdc527bd02233eb22164593eeba8c5378196..814e1d1f7ab8531bfad620a16dc7bfda834cd58d 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -92,6 +92,7 @@ option_strings = {
     'dns_resolver_timeout' : _('How long to wait for replies from DNS when resolving servers (seconds)'),
     'dns_discovery_domain' : _('The domain part of service discovery DNS query'),
     'override_gid' : _('Override GID value from the identity provider with this value'),
+    'case_sensitive' : _('Treat usernames as case sensitive'),
 
     # [provider/ipa]
     'ipa_domain' : _('IPA domain'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index 16ddfe5012654dc1600b79ad90493a409b0d5c9d..8421a09147dcbc25f628b4b75f278b4824413d18 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -484,6 +484,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'dns_resolver_timeout',
             'dns_discovery_domain',
             'override_gid',
+            'case_sensitive',
             'override_homedir',
             'id_provider',
             'auth_provider',
@@ -801,6 +802,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'dns_resolver_timeout',
             'dns_discovery_domain',
             'override_gid',
+            'case_sensitive',
             'override_homedir',
             'id_provider',
             'auth_provider',
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 6813901ed7c2598724323a5c06529ddd62121a58..c2c425cefa9ebbb2a4169e0a13d4de2594a275dd 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -72,6 +72,7 @@ filter_groups = list, str, false
 dns_resolver_timeout = int, None, false
 dns_discovery_domain = str, None, false
 override_gid = int, None, false
+case_sensitive = bool, None, false
 override_homedir = str, None, false
 
 
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 1ed408f2e1b5ccda3f551744cbc3ea8cac7a74bf..944d8bf351beea10649e3f1eedb7b352a46be302 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -931,6 +931,18 @@
                         </para>
                     </listitem>
                 </varlistentry>
+
+                <varlistentry>
+                    <term>case_sensitive (boolean)</term>
+                    <listitem>
+                        <para>
+                            Treat user and group names as case sensitive
+                        </para>
+                        <para>
+                            Default: True
+                        </para>
+                    </listitem>
+                </varlistentry>
             </variablelist>
         </para>
 
diff --git a/src/responder/common/negcache.c b/src/responder/common/negcache.c
index 3926574a12c898db30a1fe22ec763c2ed1d94f72..aa89bc2ab38096f98d95c032aa4df105828c2345 100644
--- a/src/responder/common/negcache.c
+++ b/src/responder/common/negcache.c
@@ -21,6 +21,7 @@
 
 #include "util/util.h"
 #include "confdb/confdb.h"
+#include "responder/common/responder.h"
 #include <fcntl.h>
 #include <time.h>
 #include "tdb.h"
@@ -158,55 +159,98 @@ done:
     return ret;
 }
 
+static int sss_ncache_check_user_int(struct sss_nc_ctx *ctx, int ttl,
+                                     const char *domain, const char *name)
+{
+    char *str;
+    int ret;
+
+    if (!name || !*name) return EINVAL;
+
+    str = talloc_asprintf(ctx, "%s/%s/%s", NC_USER_PREFIX, domain, name);
+    if (!str) return ENOMEM;
+
+    ret = sss_ncache_check_str(ctx, str, ttl);
+
+    talloc_free(str);
+    return ret;
+}
+
+static int sss_ncache_check_group_int(struct sss_nc_ctx *ctx, int ttl,
+                                      const char *domain, const char *name)
+{
+    char *str;
+    int ret;
+
+    if (!name || !*name) return EINVAL;
+
+    str = talloc_asprintf(ctx, "%s/%s/%s", NC_GROUP_PREFIX, domain, name);
+    if (!str) return ENOMEM;
+
+    ret = sss_ncache_check_str(ctx, str, ttl);
+
+    talloc_free(str);
+    return ret;
+}
+
+static int sss_ncache_check_netgr_int(struct sss_nc_ctx *ctx, int ttl,
+                                      const char *domain, const char *name)
+{
+    char *str;
+    int ret;
+
+    if (!name || !*name) return EINVAL;
+
+    str = talloc_asprintf(ctx, "%s/%s/%s", NC_NETGROUP_PREFIX, domain, name);
+    if (!str) return ENOMEM;
+
+    ret = sss_ncache_check_str(ctx, str, ttl);
+
+    talloc_free(str);
+    return ret;
+}
+
+typedef int (*ncache_check_byname_fn_t)(struct sss_nc_ctx *, int,
+                                        const char *, const char *);
+
+static int sss_cache_check_ent(struct sss_nc_ctx *ctx, int ttl,
+                               struct sss_domain_info *dom, const char *name,
+                               ncache_check_byname_fn_t checker)
+{
+    char *lower;
+    errno_t ret;
+
+    if (dom->case_sensitive == false) {
+        lower = sss_tc_utf8_tolower(ctx, name);
+        if (!lower) return ENOMEM;
+        ret = checker(ctx, ttl, dom->name, lower);
+        talloc_free(lower);
+    } else {
+        ret = checker(ctx, ttl, dom->name, name);
+    }
+
+    return ret;
+}
+
 int sss_ncache_check_user(struct sss_nc_ctx *ctx, int ttl,
-                          const char *domain, const char *name)
+                          struct sss_domain_info *dom, const char *name)
 {
-    char *str;
-    int ret;
-
-    if (!name || !*name) return EINVAL;
-
-    str = talloc_asprintf(ctx, "%s/%s/%s", NC_USER_PREFIX, domain, name);
-    if (!str) return ENOMEM;
-
-    ret = sss_ncache_check_str(ctx, str, ttl);
-
-    talloc_free(str);
-    return ret;
+    return sss_cache_check_ent(ctx, ttl, dom, name,
+                               sss_ncache_check_user_int);
 }
 
 int sss_ncache_check_group(struct sss_nc_ctx *ctx, int ttl,
-                           const char *domain, const char *name)
+                           struct sss_domain_info *dom, const char *name)
 {
-    char *str;
-    int ret;
-
-    if (!name || !*name) return EINVAL;
-
-    str = talloc_asprintf(ctx, "%s/%s/%s", NC_GROUP_PREFIX, domain, name);
-    if (!str) return ENOMEM;
-
-    ret = sss_ncache_check_str(ctx, str, ttl);
-
-    talloc_free(str);
-    return ret;
+    return sss_cache_check_ent(ctx, ttl, dom, name,
+                               sss_ncache_check_group_int);
 }
 
 int sss_ncache_check_netgr(struct sss_nc_ctx *ctx, int ttl,
-                           const char *domain, const char *name)
+                           struct sss_domain_info *dom, const char *name)
 {
-    char *str;
-    int ret;
-
-    if (!name || !*name) return EINVAL;
-
-    str = talloc_asprintf(ctx, "%s/%s/%s", NC_NETGROUP_PREFIX, domain, name);
-    if (!str) return ENOMEM;
-
-    ret = sss_ncache_check_str(ctx, str, ttl);
-
-    talloc_free(str);
-    return ret;
+    return sss_cache_check_ent(ctx, ttl, dom, name,
+                               sss_ncache_check_netgr_int);
 }
 
 int sss_ncache_check_uid(struct sss_nc_ctx *ctx, int ttl, uid_t uid)
@@ -237,55 +281,96 @@ int sss_ncache_check_gid(struct sss_nc_ctx *ctx, int ttl, gid_t gid)
     return ret;
 }
 
+static int sss_ncache_set_user_int(struct sss_nc_ctx *ctx, bool permanent,
+                                   const char *domain, const char *name)
+{
+    char *str;
+    int ret;
+
+    if (!name || !*name) return EINVAL;
+
+    str = talloc_asprintf(ctx, "%s/%s/%s", NC_USER_PREFIX, domain, name);
+    if (!str) return ENOMEM;
+
+    ret = sss_ncache_set_str(ctx, str, permanent);
+
+    talloc_free(str);
+    return ret;
+}
+
+static int sss_ncache_set_group_int(struct sss_nc_ctx *ctx, bool permanent,
+                                    const char *domain, const char *name)
+{
+    char *str;
+    int ret;
+
+    if (!name || !*name) return EINVAL;
+
+    str = talloc_asprintf(ctx, "%s/%s/%s", NC_GROUP_PREFIX, domain, name);
+    if (!str) return ENOMEM;
+
+    ret = sss_ncache_set_str(ctx, str, permanent);
+
+    talloc_free(str);
+    return ret;
+}
+
+static int sss_ncache_set_netgr_int(struct sss_nc_ctx *ctx, bool permanent,
+                                    const char *domain, const char *name)
+{
+    char *str;
+    int ret;
+
+    if (!name || !*name) return EINVAL;
+
+    str = talloc_asprintf(ctx, "%s/%s/%s", NC_NETGROUP_PREFIX, domain, name);
+    if (!str) return ENOMEM;
+
+    ret = sss_ncache_set_str(ctx, str, permanent);
+
+    talloc_free(str);
+    return ret;
+}
+
+typedef int (*ncache_set_byname_fn_t)(struct sss_nc_ctx *, bool,
+                                      const char *, const char *);
+
+static int sss_ncache_set_ent(struct sss_nc_ctx *ctx, bool permanent,
+                              struct sss_domain_info *dom, const char *name,
+                              ncache_set_byname_fn_t setter)
+{
+    char *lower;
+    errno_t ret;
+
+    if (dom->case_sensitive == false) {
+        lower = sss_tc_utf8_tolower(ctx, name);
+        if (!lower) return ENOMEM;
+        ret = setter(ctx, permanent, dom->name, lower);
+        talloc_free(lower);
+    } else {
+        ret = setter(ctx, permanent, dom->name, name);
+    }
+
+    return ret;
+}
+
+
 int sss_ncache_set_user(struct sss_nc_ctx *ctx, bool permanent,
-                        const char *domain, const char *name)
+                        struct sss_domain_info *dom, const char *name)
 {
-    char *str;
-    int ret;
-
-    if (!name || !*name) return EINVAL;
-
-    str = talloc_asprintf(ctx, "%s/%s/%s", NC_USER_PREFIX, domain, name);
-    if (!str) return ENOMEM;
-
-    ret = sss_ncache_set_str(ctx, str, permanent);
-
-    talloc_free(str);
-    return ret;
+    return sss_ncache_set_ent(ctx, permanent, dom, name, sss_ncache_set_user_int);
 }
 
 int sss_ncache_set_group(struct sss_nc_ctx *ctx, bool permanent,
-                        const char *domain, const char *name)
+                         struct sss_domain_info *dom, const char *name)
 {
-    char *str;
-    int ret;
-
-    if (!name || !*name) return EINVAL;
-
-    str = talloc_asprintf(ctx, "%s/%s/%s", NC_GROUP_PREFIX, domain, name);
-    if (!str) return ENOMEM;
-
-    ret = sss_ncache_set_str(ctx, str, permanent);
-
-    talloc_free(str);
-    return ret;
+    return sss_ncache_set_ent(ctx, permanent, dom, name, sss_ncache_set_group_int);
 }
 
 int sss_ncache_set_netgr(struct sss_nc_ctx *ctx, bool permanent,
-                         const char *domain, const char *name)
+                         struct sss_domain_info *dom, const char *name)
 {
-    char *str;
-    int ret;
-
-    if (!name || !*name) return EINVAL;
-
-    str = talloc_asprintf(ctx, "%s/%s/%s", NC_NETGROUP_PREFIX, domain, name);
-    if (!str) return ENOMEM;
-
-    ret = sss_ncache_set_str(ctx, str, permanent);
-
-    talloc_free(str);
-    return ret;
+    return sss_ncache_set_ent(ctx, permanent, dom, name, sss_ncache_set_netgr_int);
 }
 
 int sss_ncache_set_uid(struct sss_nc_ctx *ctx, bool permanent, uid_t uid)
@@ -409,7 +494,7 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache,
                 continue;
             }
 
-            ret = sss_ncache_set_user(ncache, true, dom->name, name);
+            ret = sss_ncache_set_user(ncache, true, dom, name);
             if (ret != EOK) {
                 DEBUG(1, ("Failed to store permanent user filter for [%s]"
                           " (%d [%s])\n", filter_list[i],
@@ -447,7 +532,14 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache,
             continue;
         }
         if (domainname) {
-            ret = sss_ncache_set_user(ncache, true, domainname, name);
+            dom = responder_get_domain(domain_list, domainname);
+            if (!dom) {
+                DEBUG(SSSDBG_CRIT_FAILURE,
+                      ("Invalid domain name [%s]\n", domainname));
+                continue;
+            }
+
+            ret = sss_ncache_set_user(ncache, true, dom, name);
             if (ret != EOK) {
                 DEBUG(1, ("Failed to store permanent user filter for [%s]"
                           " (%d [%s])\n", filter_list[i],
@@ -456,7 +548,7 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache,
             }
         } else {
             for (dom = domain_list; dom; dom = dom->next) {
-                ret = sss_ncache_set_user(ncache, true, dom->name, name);
+                ret = sss_ncache_set_user(ncache, true, dom, name);
                 if (ret != EOK) {
                    DEBUG(1, ("Failed to store permanent user filter for"
                              " [%s:%s] (%d [%s])\n",
@@ -499,7 +591,7 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache,
                 continue;
             }
 
-            ret = sss_ncache_set_group(ncache, true, dom->name, name);
+            ret = sss_ncache_set_group(ncache, true, dom, name);
             if (ret != EOK) {
                 DEBUG(1, ("Failed to store permanent group filter for [%s]"
                           " (%d [%s])\n", filter_list[i],
@@ -537,7 +629,14 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache,
             continue;
         }
         if (domainname) {
-            ret = sss_ncache_set_group(ncache, true, domainname, name);
+            dom = responder_get_domain(domain_list, domainname);
+            if (!dom) {
+                DEBUG(SSSDBG_CRIT_FAILURE,
+                      ("Invalid domain name [%s]\n", domainname));
+                continue;
+            }
+
+            ret = sss_ncache_set_group(ncache, true, dom, name);
             if (ret != EOK) {
                 DEBUG(1, ("Failed to store permanent group filter for"
                           " [%s] (%d [%s])\n", filter_list[i],
@@ -546,7 +645,7 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache,
             }
         } else {
             for (dom = domain_list; dom; dom = dom->next) {
-                ret = sss_ncache_set_group(ncache, true, dom->name, name);
+                ret = sss_ncache_set_group(ncache, true, dom, name);
                 if (ret != EOK) {
                    DEBUG(1, ("Failed to store permanent group filter for"
                              " [%s:%s] (%d [%s])\n",
diff --git a/src/responder/common/negcache.h b/src/responder/common/negcache.h
index fc857fce1608b2d71a75de10a6d63158dc20ce1e..72b99c969043d42c6f1329d8b0d549cadaddb998 100644
--- a/src/responder/common/negcache.h
+++ b/src/responder/common/negcache.h
@@ -29,9 +29,9 @@ int sss_ncache_init(TALLOC_CTX *memctx, struct sss_nc_ctx **_ctx);
 
 /* check if the user is expired according to the passed in time to live */
 int sss_ncache_check_user(struct sss_nc_ctx *ctx, int ttl,
-                          const char *domain, const char *name);
+                          struct sss_domain_info *dom, const char *name);
 int sss_ncache_check_group(struct sss_nc_ctx *ctx, int ttl,
-                          const char *domain, const char *name);
+                           struct sss_domain_info *dom, const char *name);
 int sss_ncache_check_netgr(struct sss_nc_ctx *ctx, int ttl,
                            const char *domain, const char *name);
 int sss_ncache_check_uid(struct sss_nc_ctx *ctx, int ttl, uid_t uid);
@@ -42,11 +42,11 @@ int sss_ncache_check_gid(struct sss_nc_ctx *ctx, int ttl, gid_t gid);
  * and the negative cache never expires (used to permanently filter out
  * users and groups) */
 int sss_ncache_set_user(struct sss_nc_ctx *ctx, bool permanent,
-                        const char *domain, const char *name);
+                        struct sss_domain_info *dom, const char *name);
 int sss_ncache_set_group(struct sss_nc_ctx *ctx, bool permanent,
-                        const char *domain, const char *name);
+                         struct sss_domain_info *dom, const char *name);
 int sss_ncache_set_netgr(struct sss_nc_ctx *ctx, bool permanent,
-                         const char *domain, const char *name);
+                         struct sss_domain_info *dom, const char *name);
 int sss_ncache_set_uid(struct sss_nc_ctx *ctx, bool permanent, uid_t uid);
 int sss_ncache_set_gid(struct sss_nc_ctx *ctx, bool permanent, gid_t gid);
 
diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
index 99b1a23aa8581e01fb2a02d36657df6c20717812..a48ac556abe82579bf9455bf7b1eb94917e391b6 100644
--- a/src/responder/common/responder_common.c
+++ b/src/responder/common/responder_common.c
@@ -33,7 +33,6 @@
 #include <errno.h>
 #include <popt.h>
 #include "util/util.h"
-#include "util/sss_utf8.h"
 #include "db/sysdb.h"
 #include "confdb/confdb.h"
 #include "dbus/dbus.h"
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index f0270acd26659e8005a043fd9e154723cc868d0d..b545c5ca854cba633728de55ed5c59aa7696146f 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -394,7 +394,7 @@ static int fill_pwent(struct sss_packet *packet,
         if (filter_users) {
             ncret = sss_ncache_check_user(nctx->ncache,
                                         nctx->neg_timeout,
-                                        domain, name);
+                                        dom, name);
             if (ncret == EEXIST) {
                 DEBUG(4, ("User [%s@%s] filtered out! (negative cache)\n",
                           name, domain));
@@ -715,7 +715,7 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
     struct sss_domain_info *dom = dctx->domain;
     struct cli_ctx *cctx = cmdctx->cctx;
-    const char *name = cmdctx->name;
+    char *name = NULL;
     struct sysdb_ctx *sysdb;
     struct nss_ctx *nctx;
     int ret;
@@ -740,10 +740,15 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
         /* make sure to update the dctx if we changed domain */
         dctx->domain = dom;
 
+        talloc_free(name);
+        name = dom->case_sensitive ? talloc_strdup(dctx, cmdctx->name) :
+                                   sss_tc_utf8_tolower(dctx, cmdctx->name);
+        if (!name) return ENOMEM;
+
         /* verify this user has not yet been negatively cached,
         * or has been permanently filtered */
         ret = sss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
-                                    dom->name, name);
+                                    dom, name);
 
         /* if neg cached, return we didn't find it */
         if (ret == EEXIST) {
@@ -781,7 +786,7 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
 
         if (dctx->res->count == 0 && !dctx->check_provider) {
             /* set negative cache only if not result of cache check */
-            ret = sss_ncache_set_user(nctx->ncache, false, dom->name, name);
+            ret = sss_ncache_set_user(nctx->ncache, false, dom, name);
             if (ret != EOK) {
                 return ret;
             }
@@ -1788,7 +1793,7 @@ static int fill_grent(struct sss_packet *packet,
 
         if (filter_groups) {
             ret = sss_ncache_check_group(nctx->ncache,
-                                         nctx->neg_timeout, domain, name);
+                                         nctx->neg_timeout, dom, name);
             if (ret == EEXIST) {
                 DEBUG(4, ("Group [%s@%s] filtered out! (negative cache)\n",
                           name, domain));
@@ -1866,7 +1871,7 @@ static int fill_grent(struct sss_packet *packet,
                 if (nctx->filter_users_in_groups) {
                     ret = sss_ncache_check_user(nctx->ncache,
                                                 nctx->neg_timeout,
-                                                domain, name);
+                                                dom, name);
                     if (ret == EEXIST) {
                         DEBUG(6, ("Group [%s] member [%s@%s] filtered out!"
                                   " (negative cache)\n",
@@ -2002,7 +2007,7 @@ static int nss_cmd_getgrnam_search(struct nss_dom_ctx *dctx)
     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
     struct sss_domain_info *dom = dctx->domain;
     struct cli_ctx *cctx = cmdctx->cctx;
-    const char *name = cmdctx->name;
+    char *name = NULL;
     struct sysdb_ctx *sysdb;
     struct nss_ctx *nctx;
     int ret;
@@ -2027,10 +2032,15 @@ static int nss_cmd_getgrnam_search(struct nss_dom_ctx *dctx)
         /* make sure to update the dctx if we changed domain */
         dctx->domain = dom;
 
+        talloc_free(name);
+        name = dom->case_sensitive ? talloc_strdup(dctx, cmdctx->name) :
+                                     sss_tc_utf8_tolower(dctx, cmdctx->name);
+        if (!name) return ENOMEM;
+
         /* verify this group has not yet been negatively cached,
         * or has been permanently filtered */
         ret = sss_ncache_check_group(nctx->ncache, nctx->neg_timeout,
-                                     dom->name, name);
+                                     dom, name);
 
         /* if neg cached, return we didn't find it */
         if (ret == EEXIST) {
@@ -2068,7 +2078,7 @@ static int nss_cmd_getgrnam_search(struct nss_dom_ctx *dctx)
 
         if (dctx->res->count == 0 && !dctx->check_provider) {
             /* set negative cache only if not result of cache check */
-            ret = sss_ncache_set_group(nctx->ncache, false, dom->name, name);
+            ret = sss_ncache_set_group(nctx->ncache, false, dom, name);
             if (ret != EOK) {
                 return ret;
             }
@@ -3068,7 +3078,7 @@ static int nss_cmd_initgroups_search(struct nss_dom_ctx *dctx)
     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
     struct sss_domain_info *dom = dctx->domain;
     struct cli_ctx *cctx = cmdctx->cctx;
-    const char *name = cmdctx->name;
+    const char *name = NULL;
     struct sysdb_ctx *sysdb;
     struct nss_ctx *nctx;
     int ret;
@@ -3093,10 +3103,15 @@ static int nss_cmd_initgroups_search(struct nss_dom_ctx *dctx)
         /* make sure to update the dctx if we changed domain */
         dctx->domain = dom;
 
+        talloc_free(NULL);
+        name = dom->case_sensitive ? talloc_strdup(dctx, cmdctx->name) :
+                                     sss_tc_utf8_tolower(dctx, cmdctx->name);
+        if (!name) return ENOMEM;
+
         /* verify this user has not yet been negatively cached,
         * or has been permanently filtered */
         ret = sss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
-                                    dom->name, name);
+                                    dom, name);
 
         /* if neg cached, return we didn't find it */
         if (ret == EEXIST) {
@@ -3130,7 +3145,7 @@ static int nss_cmd_initgroups_search(struct nss_dom_ctx *dctx)
 
         if (dctx->res->count == 0 && !dctx->check_provider) {
             /* set negative cache only if not result of cache check */
-            ret = sss_ncache_set_user(nctx->ncache, false, dom->name, name);
+            ret = sss_ncache_set_user(nctx->ncache, false, dom, name);
             if (ret != EOK) {
                 return ret;
             }
diff --git a/src/responder/nss/nsssrv_netgroup.c b/src/responder/nss/nsssrv_netgroup.c
index 09d07df105c2ab0681a63e58bfdd231711429eb5..113bcdd3ff98f7e8a2a29b9ef7074fee656bba29 100644
--- a/src/responder/nss/nsssrv_netgroup.c
+++ b/src/responder/nss/nsssrv_netgroup.c
@@ -378,6 +378,7 @@ static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
     struct sss_domain_info *dom = step_ctx->dctx->domain;
     struct getent_ctx *netgr;
     struct sysdb_ctx *sysdb;
+    char *name = NULL;
 
     /* Check each domain for this netgroup name */
     while (dom) {
@@ -400,8 +401,13 @@ static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
         /* make sure to update the dctx if we changed domain */
         step_ctx->dctx->domain = dom;
 
+        talloc_free(NULL);
+        name = dom->case_sensitive ? \
+                    talloc_strdup(step_ctx, step_ctx->name) :
+                    sss_tc_utf8_tolower(step_ctx, step_ctx->name);
+
         DEBUG(4, ("Requesting info for [%s@%s]\n",
-                  step_ctx->name, dom->name));
+                  name, dom->name));
         ret = sysdb_get_ctx_from_list(step_ctx->rctx->db_list, dom, &sysdb);
         if (ret != EOK) {
             DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
@@ -409,7 +415,7 @@ static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
         }
 
         /* Look up the netgroup in the cache */
-        ret = sysdb_getnetgr(step_ctx->dctx, sysdb, step_ctx->name,
+        ret = sysdb_getnetgr(step_ctx->dctx, sysdb, name,
                              &step_ctx->dctx->res);
         if (ret == ENOENT) {
             /* This netgroup was not found in this domain */
@@ -442,7 +448,7 @@ static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
         if (ret == ENOENT) {
             /* This netgroup was not found in this domain */
             DEBUG(2, ("No results for netgroup %s (domain %s)\n",
-                      step_ctx->name, dom->name));
+                      name, dom->name));
 
             if (!step_ctx->dctx->check_provider) {
                 if (step_ctx->check_next) {
@@ -469,7 +475,7 @@ static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
                               step_ctx->nctx,
                               step_ctx->dctx->res,
                               SSS_DP_NETGR,
-                              step_ctx->name, 0,
+                              name, 0,
                               lookup_netgr_dp_callback,
                               step_ctx);
             if (ret != EOK) {
@@ -482,7 +488,7 @@ static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
 
         /* Results found */
         DEBUG(6, ("Returning info for netgroup [%s@%s]\n",
-                  step_ctx->name, dom->name));
+                  name, dom->name));
         netgr->ready = true;
         netgr->found = true;
         set_netgr_lifetime(dom->entry_cache_timeout, step_ctx, netgr);
@@ -490,8 +496,7 @@ static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
     }
 
     /* If we've gotten here, then no domain contained this netgroup */
-    DEBUG(2, ("No matching domain found for [%s], fail!\n",
-              step_ctx->name));
+    DEBUG(2, ("No matching domain found for [%s], fail!\n", name));
 
     netgr = talloc_zero(step_ctx->nctx, struct getent_ctx);
     if (netgr == NULL) {
@@ -501,7 +506,7 @@ static errno_t lookup_netgr_step(struct setent_step_ctx *step_ctx)
         netgr->found = false;
         netgr->entries = NULL;
         netgr->lookup_table = step_ctx->nctx->netgroups;
-        netgr->name = talloc_strdup(netgr, step_ctx->name);
+        netgr->name = talloc_strdup(netgr, name);
         if (netgr->name == NULL) {
             DEBUG(1, ("talloc_strdup failed.\n"));
             talloc_free(netgr);
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index 5f77697fd33779d9cb48f993d7a8368cef8b3928..24ccaf13354c60a0ca4e71f84a3f478cac3f8f87 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -93,6 +93,25 @@ static int extract_uint32_t(uint32_t *var, size_t size, uint8_t *body,
     return EOK;
 }
 
+static int pd_set_primary_name(const struct ldb_message *msg,struct pam_data *pd)
+{
+    const char *name;
+
+    name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, 0);
+    if (!name) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("A user with no name?\n"));
+        return EIO;
+    }
+
+    if (strcmp(pd->user, name)) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("User's primary name is %s\n", name));
+        pd->user = talloc_strdup(pd, name);
+        if (!pd->user) return ENOMEM;
+    }
+
+    return EOK;
+}
+
 static int pam_parse_in_data_v2(struct sss_names_ctx *snctx,
                              struct pam_data *pd,
                              uint8_t *body, size_t blen)
@@ -767,7 +786,7 @@ static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
             if (dom->fqnames) continue;
 
             ncret = sss_ncache_check_user(pctx->ncache, pctx->neg_timeout,
-                                          dom->name, pd->user);
+                                          dom, pd->user);
             if (ncret == ENOENT) {
                 /* User not found in the negative cache
                  * Proceed with PAM actions
@@ -810,7 +829,7 @@ static int pam_check_user_search(struct pam_auth_req *preq)
 {
     struct sss_domain_info *dom = preq->domain;
     struct cli_ctx *cctx = preq->cctx;
-    const char *name = preq->pd->user;
+    const char *name = NULL;
     struct sysdb_ctx *sysdb;
     time_t cacheExpire;
     int ret;
@@ -835,6 +854,10 @@ static int pam_check_user_search(struct pam_auth_req *preq)
         /* make sure to update the preq if we changed domain */
         preq->domain = dom;
 
+        talloc_free(NULL);
+        name = dom->case_sensitive ? talloc_strdup(preq, preq->pd->user) :
+                                sss_tc_utf8_tolower(preq, preq->pd->user);
+
         /* Refresh the user's cache entry on any PAM query
          * We put a timeout in the client context so that we limit
          * the number of updates within a reasonable timeout
@@ -890,6 +913,13 @@ static int pam_check_user_search(struct pam_auth_req *preq)
 
         DEBUG(6, ("Returning info for user [%s@%s]\n", name, dom->name));
 
+        /* We might have searched by alias. Pass on the primary name */
+        ret = pd_set_primary_name(preq->res->msgs[0], preq->pd);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Could not canonicalize username\n"));
+            return ret;
+        }
+
         return EOK;
     }
 
-- 
1.7.7.3

-------------- next part --------------
From 8ff2ede6dbcfd86a6405f2bee1a44294e917ea4c Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 16 Nov 2011 13:57:26 +0100
Subject: [PATCH 4/6] Refactor saving sdap entities

There was too much code duplication between
sdap_save_{user,group,netgroup}. This patch removes the most egregious ones.
---
 src/providers/ldap/sdap_async.c           |   80 +++++++++++++++++++++++
 src/providers/ldap/sdap_async.h           |   21 ++++++
 src/providers/ldap/sdap_async_groups.c    |   49 +++-----------
 src/providers/ldap/sdap_async_netgroups.c |   77 +++++------------------
 src/providers/ldap/sdap_async_users.c     |   98 ++++++-----------------------
 5 files changed, 146 insertions(+), 179 deletions(-)

diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
index b1177e274bd3cf6d00d4cfa773556731b5997e97..98291e6e2e8ac13fe244c8cb1f332df30e20aa4e 100644
--- a/src/providers/ldap/sdap_async.c
+++ b/src/providers/ldap/sdap_async.c
@@ -1983,3 +1983,83 @@ done:
     return ret;
 }
 
+errno_t
+sdap_attrs_add_ldap_attr(struct sysdb_attrs *ldap_attrs,
+                         const char *attr_name,
+                         const char *attr_desc,
+                         bool multivalued,
+                         const char *name,
+                         struct sysdb_attrs *attrs)
+{
+    errno_t ret;
+    struct ldb_message_element *el;
+    const char *objname = name ?: "object";
+    const char *desc = attr_desc ?: attr_name;
+    unsigned int num_values, i;
+
+    ret = sysdb_attrs_get_el(ldap_attrs, attr_name, &el);
+    if (ret) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Could not get %s from the "
+              "list of the LDAP attributes [%d]: %s\n", ret, strerror(ret)));
+        return ret;
+    }
+
+    if (el->num_values == 0) {
+        DEBUG(SSSDBG_TRACE_INTERNAL, ("%s is not available "
+              "for [%s].\n", desc, objname));
+    } else {
+        num_values = multivalued ? el->num_values : 1;
+        for (i = 0; i < num_values; i++) {
+            DEBUG(SSSDBG_TRACE_INTERNAL, ("Adding %s [%s] to attributes "
+                  "of [%s].\n", desc, el->values[i].data, objname));
+
+            ret = sysdb_attrs_add_string(attrs, attr_name,
+                                         (const char *) el->values[i].data);
+            if (ret) {
+                return ret;
+            }
+        }
+    }
+
+    return EOK;
+}
+
+
+errno_t
+sdap_save_all_names(const char *name,
+                    struct sysdb_attrs *ldap_attrs,
+                    struct sysdb_attrs *attrs)
+{
+    const char **aliases = NULL;
+    errno_t ret;
+    TALLOC_CTX *tmp_ctx;
+    int i;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_attrs_get_aliases(tmp_ctx, ldap_attrs, name, &aliases);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Failed to get the alias list"));
+        goto done;
+    }
+
+    for (i = 0; aliases[i]; i++) {
+        ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS,
+                                     aliases[i]);
+        if (ret) {
+            DEBUG(SSSDBG_OP_FAILURE, ("Failed to add alias [%s] into the "
+                                      "attribute list\n", aliases[i]));
+            goto done;
+        }
+    }
+
+    ret = EOK;
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
index 4ba2770c989968e5398feeca0dbeb8a99206a009..f53af1e01e683ac0918f8ee5d74e1c19713c4bb7 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -189,4 +189,25 @@ errno_t sdap_check_aliases(struct sysdb_ctx *sysdb,
                            struct sss_domain_info *dom,
                            struct sdap_options *opts,
                            bool steal_memberships);
+
+errno_t
+sdap_attrs_add_ldap_attr(struct sysdb_attrs *ldap_attrs,
+                         const char *attr_name,
+                         const char *attr_desc,
+                         bool multivalued,
+                         const char *name,
+                         struct sysdb_attrs *attrs);
+
+#define sdap_attrs_add_string(ldap_attrs, attr_name, attr_desc, name, attrs) \
+        sdap_attrs_add_ldap_attr(ldap_attrs, attr_name, attr_desc, \
+                                 false, name, attrs)
+
+#define sdap_attrs_add_list(ldap_attrs, attr_name, attr_desc, name, attrs) \
+    sdap_attrs_add_ldap_attr(ldap_attrs, attr_name, attr_desc,   \
+                             true, name, attrs)
+
+errno_t sdap_save_all_names(const char *name,
+                            struct sysdb_attrs *ldap_attrs,
+                            struct sysdb_attrs *attrs);
+
 #endif /* _SDAP_ASYNC_H_ */
diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
index ead3fd544ae74923332c0a3780c2454845b5f1e6..d2c15dedaf7417d97a91a587bc1145b21c3ebf92 100644
--- a/src/providers/ldap/sdap_async_groups.c
+++ b/src/providers/ldap/sdap_async_groups.c
@@ -222,11 +222,9 @@ static int sdap_save_group(TALLOC_CTX *memctx,
     const char *name = NULL;
     gid_t gid;
     int ret;
-    int i;
     char *usn_value = NULL;
     TALLOC_CTX *tmpctx = NULL;
     bool posix_group;
-    const char **aliases = NULL;
 
     tmpctx = talloc_new(memctx);
     if (!tmpctx) {
@@ -282,38 +280,19 @@ static int sdap_save_group(TALLOC_CTX *memctx,
         /* Group ID OK */
     }
 
-    ret = sysdb_attrs_get_el(attrs, SYSDB_ORIG_DN, &el);
-    if (ret) {
+    ret = sdap_attrs_add_string(attrs, SYSDB_ORIG_DN, "original DN",
+                                name, group_attrs);
+    if (ret != EOK) {
         goto fail;
     }
-    if (el->num_values == 0) {
-        DEBUG(7, ("Original DN is not available for [%s].\n", name));
-    } else {
-        DEBUG(7, ("Adding original DN [%s] to attributes of [%s].\n",
-                  el->values[0].data, name));
-        ret = sysdb_attrs_add_string(group_attrs, SYSDB_ORIG_DN,
-                                     (const char *) el->values[0].data);
-        if (ret) {
-            goto fail;
-        }
-    }
 
-    ret = sysdb_attrs_get_el(attrs,
-                      opts->group_map[SDAP_AT_GROUP_MODSTAMP].sys_name, &el);
-    if (ret) {
+    ret = sdap_attrs_add_string(attrs,
+                            opts->group_map[SDAP_AT_GROUP_MODSTAMP].sys_name,
+                            "original mod-Timestamp",
+                            name, group_attrs);
+    if (ret != EOK) {
         goto fail;
     }
-    if (el->num_values == 0) {
-        DEBUG(7, ("Original mod-Timestamp is not available for [%s].\n",
-                  name));
-    } else {
-        ret = sysdb_attrs_add_string(group_attrs,
-                          opts->group_map[SDAP_AT_GROUP_MODSTAMP].sys_name,
-                          (const char*)el->values[0].data);
-        if (ret) {
-            goto fail;
-        }
-    }
 
     ret = sysdb_attrs_get_el(attrs,
                       opts->group_map[SDAP_AT_GROUP_USN].sys_name, &el);
@@ -369,20 +348,12 @@ static int sdap_save_group(TALLOC_CTX *memctx,
         }
     }
 
-    ret = sysdb_attrs_get_aliases(tmpctx, attrs, name, &aliases);
+    ret = sdap_save_all_names(name, attrs, group_attrs);
     if (ret != EOK) {
-        DEBUG(1, ("Failed to get the alias list\n"));
+        DEBUG(1, ("Failed to save user names\n"));
         goto fail;
     }
 
-    for (i = 0; aliases[i]; i++) {
-        ret = sysdb_attrs_add_string(group_attrs, SYSDB_NAME_ALIAS,
-                                        aliases[i]);
-        if (ret) {
-            goto fail;
-        }
-    }
-
     DEBUG(6, ("Storing info for group %s\n", name));
 
     ret = sdap_store_group_with_gid(ctx,
diff --git a/src/providers/ldap/sdap_async_netgroups.c b/src/providers/ldap/sdap_async_netgroups.c
index bb2e1bb2ce85b16e1437140cb841264d206ff7c0..2cc8d42ad4e7d47bbf020d932e605a6e3dc5a1db 100644
--- a/src/providers/ldap/sdap_async_netgroups.c
+++ b/src/providers/ldap/sdap_async_netgroups.c
@@ -48,7 +48,6 @@ static errno_t sdap_save_netgroup(TALLOC_CTX *memctx,
     const char *name = NULL;
     int ret;
     char *timestamp = NULL;
-    size_t c;
 
     ret = sysdb_attrs_get_el(attrs,
                              opts->netgroup_map[SDAP_AT_NETGROUP_NAME].sys_name,
@@ -66,21 +65,12 @@ static errno_t sdap_save_netgroup(TALLOC_CTX *memctx,
         goto fail;
     }
 
-    ret = sysdb_attrs_get_el(attrs, SYSDB_ORIG_DN, &el);
-    if (ret) {
+    ret = sdap_attrs_add_string(attrs, SYSDB_ORIG_DN,
+                                "original DN",
+                                name, netgroup_attrs);
+    if (ret != EOK) {
         goto fail;
     }
-    if (el->num_values == 0) {
-        DEBUG(7, ("Original DN is not available for [%s].\n", name));
-    } else {
-        DEBUG(7, ("Adding original DN [%s] to attributes of [%s].\n",
-                  el->values[0].data, name));
-        ret = sysdb_attrs_add_string(netgroup_attrs, SYSDB_ORIG_DN,
-                                     (const char *)el->values[0].data);
-        if (ret) {
-            goto fail;
-        }
-    }
 
     ret = sysdb_attrs_get_el(attrs,
                          opts->netgroup_map[SDAP_AT_NETGROUP_MODSTAMP].sys_name,
@@ -105,64 +95,27 @@ static errno_t sdap_save_netgroup(TALLOC_CTX *memctx,
         }
     }
 
-    ret = sysdb_attrs_get_el(attrs,
-                           opts->netgroup_map[SDAP_AT_NETGROUP_TRIPLE].sys_name,
-                           &el);
-    if (ret) {
+    ret = sdap_attrs_add_list(attrs,
+                        opts->netgroup_map[SDAP_AT_NETGROUP_TRIPLE].sys_name,
+                        "netgroup triple",
+                        name, netgroup_attrs);
+    if (ret != EOK) {
         goto fail;
     }
-    if (el->num_values == 0) {
-        DEBUG(7, ("No netgroup triples for netgroup [%s].\n", name));
-    } else {
-        for(c = 0; c < el->num_values; c++) {
-            ret = sysdb_attrs_add_string(netgroup_attrs,
-                           opts->netgroup_map[SDAP_AT_NETGROUP_TRIPLE].sys_name,
-                            (const char*)el->values[c].data);
-            if (ret) {
-                goto fail;
-            }
-        }
-    }
 
-    ret = sysdb_attrs_get_el(attrs,
-                       opts->netgroup_map[SDAP_AT_NETGROUP_MEMBER].sys_name,
-                       &el);
+    ret = sdap_attrs_add_list(attrs,
+                        opts->netgroup_map[SDAP_AT_NETGROUP_MEMBER].sys_name,
+                        "original members",
+                        name, netgroup_attrs);
     if (ret != EOK) {
         goto fail;
     }
-    if (el->num_values == 0) {
-        DEBUG(7, ("No original members for netgroup [%s]\n", name));
-
-    } else {
-        DEBUG(7, ("Adding original members to netgroup [%s]\n", name));
-        for(c = 0; c < el->num_values; c++) {
-            ret = sysdb_attrs_add_string(netgroup_attrs,
-                       opts->netgroup_map[SDAP_AT_NETGROUP_MEMBER].sys_name,
-                       (const char*)el->values[c].data);
-            if (ret) {
-                goto fail;
-            }
-        }
-    }
 
-
-    ret = sysdb_attrs_get_el(attrs, SYSDB_NETGROUP_MEMBER, &el);
+    ret = sdap_attrs_add_list(attrs, SYSDB_NETGROUP_MEMBER,
+                        "members", name, netgroup_attrs);
     if (ret != EOK) {
         goto fail;
     }
-    if (el->num_values == 0) {
-        DEBUG(7, ("No members for netgroup [%s]\n", name));
-
-    } else {
-        DEBUG(7, ("Adding members to netgroup [%s]\n", name));
-        for(c = 0; c < el->num_values; c++) {
-            ret = sysdb_attrs_add_string(netgroup_attrs, SYSDB_NETGROUP_MEMBER,
-                                         (const char*)el->values[c].data);
-            if (ret) {
-                goto fail;
-            }
-        }
-    }
 
     DEBUG(6, ("Storing info for netgroup %s\n", name));
 
diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c
index cf9a8d33cbd4deac12287852cbe78ce465f0ac61..c929e2048b310803c26be619f48cbda5d7a6bfc1 100644
--- a/src/providers/ldap/sdap_async_users.c
+++ b/src/providers/ldap/sdap_async_users.c
@@ -29,7 +29,6 @@
 /* ==Save-User-Entry====================================================== */
 
 /* FIXME: support storing additional attributes */
-
 int sdap_save_user(TALLOC_CTX *memctx,
                    struct sysdb_ctx *ctx,
                    struct sdap_options *opts,
@@ -52,17 +51,14 @@ int sdap_save_user(TALLOC_CTX *memctx,
     struct sysdb_attrs *user_attrs;
     char *upn = NULL;
     size_t i;
-    char *val = NULL;
     int cache_timeout;
     char *usn_value = NULL;
-    size_t c;
     char **missing = NULL;
-    const char **aliases = NULL;
     TALLOC_CTX *tmpctx = NULL;
 
     DEBUG(9, ("Save user\n"));
 
-    tmpctx = talloc_new(memctx);
+    tmpctx = talloc_new(NULL);
     if (!tmpctx) {
         ret = ENOMEM;
         goto fail;
@@ -151,57 +147,27 @@ int sdap_save_user(TALLOC_CTX *memctx,
         goto fail;
     }
 
-    ret = sysdb_attrs_get_el(attrs, SYSDB_ORIG_DN, &el);
-    if (ret) {
+    ret = sdap_attrs_add_string(attrs, SYSDB_ORIG_DN,
+                                "original DN",
+                                name, user_attrs);
+    if (ret != EOK) {
         goto fail;
     }
-    if (el->num_values == 0) {
-        DEBUG(7, ("Original DN is not available for [%s].\n", name));
-    } else {
-        DEBUG(7, ("Adding original DN [%s] to attributes of [%s].\n",
-                  el->values[0].data, name));
-        ret = sysdb_attrs_add_string(user_attrs, SYSDB_ORIG_DN,
-                                     (const char *) el->values[0].data);
-        if (ret) {
-            goto fail;
-        }
-    }
 
-    ret = sysdb_attrs_get_el(attrs, SYSDB_MEMBEROF, &el);
-    if (ret) {
+    ret = sdap_attrs_add_list(attrs, SYSDB_MEMBEROF,
+                              "original memberOf",
+                              name, user_attrs);
+    if (ret != EOK) {
         goto fail;
     }
-    if (el->num_values == 0) {
-        DEBUG(7, ("Original memberOf is not available for [%s].\n",
-                  name));
-    } else {
-        DEBUG(7, ("Adding original memberOf attributes to [%s].\n",
-                  name));
-        for (i = 0; i < el->num_values; i++) {
-            ret = sysdb_attrs_add_string(user_attrs, SYSDB_ORIG_MEMBEROF,
-                                         (const char *) el->values[i].data);
-            if (ret) {
-                goto fail;
-            }
-        }
-    }
 
-    ret = sysdb_attrs_get_el(attrs,
-                      opts->user_map[SDAP_AT_USER_MODSTAMP].sys_name, &el);
-    if (ret) {
+    ret = sdap_attrs_add_string(attrs,
+                            opts->user_map[SDAP_AT_USER_MODSTAMP].sys_name,
+                            "original mod-Timestamp",
+                            name, user_attrs);
+    if (ret != EOK) {
         goto fail;
     }
-    if (el->num_values == 0) {
-        DEBUG(7, ("Original mod-Timestamp is not available for [%s].\n",
-                  name));
-    } else {
-        ret = sysdb_attrs_add_string(user_attrs,
-                          opts->user_map[SDAP_AT_USER_MODSTAMP].sys_name,
-                          (const char*)el->values[0].data);
-        if (ret) {
-            goto fail;
-        }
-    }
 
     ret = sysdb_attrs_get_el(attrs,
                       opts->user_map[SDAP_AT_USER_USN].sys_name, &el);
@@ -218,7 +184,7 @@ int sdap_save_user(TALLOC_CTX *memctx,
         if (ret) {
             goto fail;
         }
-        usn_value = talloc_strdup(memctx, (const char*)el->values[0].data);
+        usn_value = talloc_strdup(tmpctx, (const char*)el->values[0].data);
         if (!usn_value) {
             ret = ENOMEM;
             goto fail;
@@ -250,27 +216,11 @@ int sdap_save_user(TALLOC_CTX *memctx,
     }
 
     for (i = SDAP_FIRST_EXTRA_USER_AT; i < SDAP_OPTS_USER; i++) {
-        ret = sysdb_attrs_get_el(attrs, opts->user_map[i].sys_name, &el);
+        ret = sdap_attrs_add_list(attrs, opts->user_map[i].sys_name,
+                                  NULL, name, user_attrs);
         if (ret) {
             goto fail;
         }
-        if (el->num_values > 0) {
-            for (c = 0; c < el->num_values; c++) {
-                DEBUG(9, ("Adding [%s]=[%s] to user attributes.\n",
-                          opts->user_map[i].sys_name,
-                          (const char*) el->values[c].data));
-                val = talloc_strdup(user_attrs, (const char*) el->values[c].data);
-                if (val == NULL) {
-                    ret = ENOMEM;
-                    goto fail;
-                }
-                ret = sysdb_attrs_add_string(user_attrs,
-                                             opts->user_map[i].sys_name, val);
-                if (ret) {
-                    goto fail;
-                }
-            }
-        }
     }
 
     cache_timeout = dp_opt_get_int(opts->basic, SDAP_ENTRY_CACHE_TIMEOUT);
@@ -284,20 +234,12 @@ int sdap_save_user(TALLOC_CTX *memctx,
         }
     }
 
-    ret = sysdb_attrs_get_aliases(tmpctx, attrs, name, &aliases);
+    ret = sdap_save_all_names(name, attrs, user_attrs);
     if (ret != EOK) {
-        DEBUG(1, ("Failed to get the alias list"));
+        DEBUG(1, ("Failed to save user names\n"));
         goto fail;
     }
 
-    for (i = 0; aliases[i]; i++) {
-        ret = sysdb_attrs_add_string(user_attrs, SYSDB_NAME_ALIAS,
-                                     aliases[i]);
-        if (ret) {
-            goto fail;
-        }
-    }
-
     /* Make sure that any attributes we requested from LDAP that we
      * did not receive are also removed from the sysdb
      */
@@ -320,7 +262,7 @@ int sdap_save_user(TALLOC_CTX *memctx,
     if (ret) goto fail;
 
     if (_usn_value) {
-        *_usn_value = usn_value;
+        *_usn_value = talloc_steal(memctx, usn_value);
     }
 
     talloc_steal(memctx, user_attrs);
-- 
1.7.7.3

-------------- next part --------------
From b324be4f3de8c4228a3124190f6c99ab266a8115 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 6 Dec 2011 15:02:37 +0100
Subject: [PATCH 5/6] Use the case sensitivity flag in the LDAP provider

---
 src/db/sysdb.c                            |   36 +++++++++++++++++++++++++---
 src/db/sysdb.h                            |    1 +
 src/providers/ldap/sdap_async.c           |    9 ++++---
 src/providers/ldap/sdap_async.h           |    1 +
 src/providers/ldap/sdap_async_groups.c    |    4 +-
 src/providers/ldap/sdap_async_netgroups.c |    9 +++++++
 src/providers/ldap/sdap_async_users.c     |    2 +-
 7 files changed, 51 insertions(+), 11 deletions(-)

diff --git a/src/db/sysdb.c b/src/db/sysdb.c
index c49399f7df37d946b975a403ac522b9fbbb82c97..02d2feabbcdce6374e93a4036f2879ff7356dd4c 100644
--- a/src/db/sysdb.c
+++ b/src/db/sysdb.c
@@ -22,6 +22,7 @@
 
 #include "util/util.h"
 #include "util/strtonum.h"
+#include "util/sss_utf8.h"
 #include "db/sysdb_private.h"
 #include "confdb/confdb.h"
 #include <time.h>
@@ -1587,18 +1588,22 @@ done:
  * Given a primary name returned by sysdb_attrs_primary_name(), this function
  * returns the other SYSDB_NAME attribute values so they can be saved as
  * SYSDB_NAME_ALIAS into cache.
+ *
+ * If lowercase is set, all aliases are duplicated in lowercase as well.
  */
 errno_t sysdb_attrs_get_aliases(TALLOC_CTX *mem_ctx,
                                 struct sysdb_attrs *attrs,
                                 const char *primary,
+                                bool lowercase,
                                 const char ***_aliases)
 {
     TALLOC_CTX *tmp_ctx = NULL;
     struct ldb_message_element *sysdb_name_el;
-    size_t i, ai;
+    size_t i, ai, num;
     errno_t ret;
     const char **aliases = NULL;
     const char *name;
+    char *lower;
 
     if (_aliases == NULL) return EINVAL;
 
@@ -1615,8 +1620,8 @@ errno_t sysdb_attrs_get_aliases(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    aliases = talloc_array(tmp_ctx, const char *,
-                           sysdb_name_el->num_values);
+    num = lowercase ? 2 * sysdb_name_el->num_values : sysdb_name_el->num_values;
+    aliases = talloc_array(tmp_ctx, const char *, num+1);
     if (!aliases) {
         ret = ENOMEM;
         goto done;
@@ -1626,11 +1631,34 @@ errno_t sysdb_attrs_get_aliases(TALLOC_CTX *mem_ctx,
     for (i=0; i < sysdb_name_el->num_values; i++) {
         name = (const char *)sysdb_name_el->values[i].data;
         if (strcmp(primary, name) != 0) {
-            aliases[ai] = name;
+            aliases[ai] = talloc_strdup(aliases, name);
+            if (!aliases[ai]) {
+                ret = ENOMEM;
+                goto done;
+            }
             ai++;
         }
     }
 
+    if (lowercase) {
+        DEBUG(SSSDBG_TRACE_INTERNAL,
+              ("Domain is case-insensitive; will add lowercased aliases\n"));
+        for (i=0; i < sysdb_name_el->num_values; i++) {
+            name = (const char *)sysdb_name_el->values[i].data;
+            lower = sss_tc_utf8_tolower(tmp_ctx, name);
+            if (!lower) {
+                ret = ENOMEM;
+                goto done;
+            }
+
+            if (strcmp(name, lower) != 0) {
+                aliases[ai] = talloc_strdup(aliases, lower);
+                ai++;
+            }
+            talloc_free(lower);
+        }
+    }
+
     aliases[ai] = NULL;
     ret = EOK;
 done:
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 5fc36ab5a39542838354a29c54a17ff4541b5a98..82d1fd0a9bef879943f642c1ed6f6ff9ef22663b 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -241,6 +241,7 @@ errno_t sysdb_attrs_primary_name(struct sysdb_ctx *sysdb,
 errno_t sysdb_attrs_get_aliases(TALLOC_CTX *mem_ctx,
                                 struct sysdb_attrs *attrs,
                                 const char *primary,
+                                bool lowercase,
                                 const char ***_aliases);
 errno_t sysdb_attrs_primary_name_list(struct sysdb_ctx *sysdb,
                                       TALLOC_CTX *mem_ctx,
diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
index 98291e6e2e8ac13fe244c8cb1f332df30e20aa4e..0719f74be6b8b167f00a69abcd13a98ea36fc2e1 100644
--- a/src/providers/ldap/sdap_async.c
+++ b/src/providers/ldap/sdap_async.c
@@ -1920,7 +1920,8 @@ errno_t sdap_check_aliases(struct sysdb_ctx *sysdb,
         goto done;
     }
 
-    ret = sysdb_attrs_get_aliases(tmp_ctx, user_attrs, name, &aliases);
+    ret = sysdb_attrs_get_aliases(tmp_ctx, user_attrs, name,
+                                  !dom->case_sensitive, &aliases);
     if (ret != EOK) {
         DEBUG(1, ("Failed to get the alias list\n"));
         goto done;
@@ -2024,10 +2025,10 @@ sdap_attrs_add_ldap_attr(struct sysdb_attrs *ldap_attrs,
     return EOK;
 }
 
-
 errno_t
 sdap_save_all_names(const char *name,
                     struct sysdb_attrs *ldap_attrs,
+                    bool lowercase,
                     struct sysdb_attrs *attrs)
 {
     const char **aliases = NULL;
@@ -2041,7 +2042,8 @@ sdap_save_all_names(const char *name,
         goto done;
     }
 
-    ret = sysdb_attrs_get_aliases(tmp_ctx, ldap_attrs, name, &aliases);
+    ret = sysdb_attrs_get_aliases(tmp_ctx, ldap_attrs, name,
+                                  lowercase, &aliases);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, ("Failed to get the alias list"));
         goto done;
@@ -2062,4 +2064,3 @@ done:
     talloc_free(tmp_ctx);
     return ret;
 }
-
diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
index f53af1e01e683ac0918f8ee5d74e1c19713c4bb7..2fd606bcaa52d557dee4c85782df0e2b4e6c6522 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -208,6 +208,7 @@ sdap_attrs_add_ldap_attr(struct sysdb_attrs *ldap_attrs,
 
 errno_t sdap_save_all_names(const char *name,
                             struct sysdb_attrs *ldap_attrs,
+                            bool lowercase,
                             struct sysdb_attrs *attrs);
 
 #endif /* _SDAP_ASYNC_H_ */
diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
index d2c15dedaf7417d97a91a587bc1145b21c3ebf92..357c28c191df1c01dbf3cdb2a11feb11aee86d29 100644
--- a/src/providers/ldap/sdap_async_groups.c
+++ b/src/providers/ldap/sdap_async_groups.c
@@ -348,9 +348,9 @@ static int sdap_save_group(TALLOC_CTX *memctx,
         }
     }
 
-    ret = sdap_save_all_names(name, attrs, group_attrs);
+    ret = sdap_save_all_names(name, attrs, !dom->case_sensitive, group_attrs);
     if (ret != EOK) {
-        DEBUG(1, ("Failed to save user names\n"));
+        DEBUG(1, ("Failed to save group names\n"));
         goto fail;
     }
 
diff --git a/src/providers/ldap/sdap_async_netgroups.c b/src/providers/ldap/sdap_async_netgroups.c
index 2cc8d42ad4e7d47bbf020d932e605a6e3dc5a1db..2a24128a9b3a1594813346fea98503b9a9af432b 100644
--- a/src/providers/ldap/sdap_async_netgroups.c
+++ b/src/providers/ldap/sdap_async_netgroups.c
@@ -38,6 +38,7 @@ bool is_dn(const char *str)
 
 static errno_t sdap_save_netgroup(TALLOC_CTX *memctx,
                                   struct sysdb_ctx *ctx,
+                                  struct sss_domain_info *dom,
                                   struct sdap_options *opts,
                                   struct sysdb_attrs *attrs,
                                   char **_timestamp,
@@ -119,6 +120,13 @@ static errno_t sdap_save_netgroup(TALLOC_CTX *memctx,
 
     DEBUG(6, ("Storing info for netgroup %s\n", name));
 
+    ret = sdap_save_all_names(name, attrs, !dom->case_sensitive,
+                              netgroup_attrs);
+    if (ret != EOK) {
+        DEBUG(1, ("Failed to save netgroup names\n"));
+        goto fail;
+    }
+
     ret = sysdb_add_netgroup(ctx, name, NULL, netgroup_attrs,
                              dp_opt_get_int(opts->basic,
                                             SDAP_ENTRY_CACHE_TIMEOUT), now);
@@ -671,6 +679,7 @@ static void netgr_translate_members_done(struct tevent_req *subreq)
     now = time(NULL);
     for (c = 0; c < state->count; c++) {
         ret = sdap_save_netgroup(state, state->sysdb,
+                                 state->dom,
                                  state->opts,
                                  state->netgroups[c],
                                  &state->higher_timestamp,
diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c
index c929e2048b310803c26be619f48cbda5d7a6bfc1..cccf75b8caecd34ba6d562e2e8b8e7e3d0e18e49 100644
--- a/src/providers/ldap/sdap_async_users.c
+++ b/src/providers/ldap/sdap_async_users.c
@@ -234,7 +234,7 @@ int sdap_save_user(TALLOC_CTX *memctx,
         }
     }
 
-    ret = sdap_save_all_names(name, attrs, user_attrs);
+    ret = sdap_save_all_names(name, attrs, !dom->case_sensitive, user_attrs);
     if (ret != EOK) {
         DEBUG(1, ("Failed to save user names\n"));
         goto fail;
-- 
1.7.7.3

-------------- next part --------------
From ffbbaf65af197518553d849b01fac558f48961e1 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 6 Dec 2011 17:08:27 +0100
Subject: [PATCH 6/6] Use the case sensitivity flag in the simple access
 provider

---
 src/providers/simple/simple_access.c |   12 ++++++++----
 src/tests/simple_access-tests.c      |   30 ++++++++++++++++++++++++++++++
 2 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/src/providers/simple/simple_access.c b/src/providers/simple/simple_access.c
index 4b9c3139853adc2c65eb65af69199f820a75ca89..614a8b40b3da366eb73691706e88e8c059d4a1ab 100644
--- a/src/providers/simple/simple_access.c
+++ b/src/providers/simple/simple_access.c
@@ -34,6 +34,8 @@
 #define CONFDB_SIMPLE_ALLOW_GROUPS "simple_allow_groups"
 #define CONFDB_SIMPLE_DENY_GROUPS "simple_deny_groups"
 
+typedef int (*strcmp_fn_t)(const char *, const char *);
+
 errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
                             bool *access_granted)
 {
@@ -51,13 +53,15 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
     const char *primary_group;
     gid_t gid;
     bool matched;
+    strcmp_fn_t string_compare;
 
+    string_compare = ctx->domain->case_sensitive ? strcmp : strcasecmp;
     *access_granted = false;
 
     /* First, check whether the user is in the allowed users list */
     if (ctx->allow_users != NULL) {
         for(i = 0; ctx->allow_users[i] != NULL; i++) {
-            if (strcmp(username, ctx->allow_users[i]) == 0) {
+            if (string_compare(username, ctx->allow_users[i]) == 0) {
                 DEBUG(9, ("User [%s] found in allow list, access granted.\n",
                       username));
 
@@ -78,7 +82,7 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
     /* Next check whether this user has been specifically denied */
     if (ctx->deny_users != NULL) {
         for(i = 0; ctx->deny_users[i] != NULL; i++) {
-            if (strcmp(username, ctx->deny_users[i]) == 0) {
+            if (string_compare(username, ctx->deny_users[i]) == 0) {
                 DEBUG(9, ("User [%s] found in deny list, access denied.\n",
                       username));
 
@@ -189,7 +193,7 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
         matched = false;
         for (i = 0; ctx->allow_groups[i]; i++) {
             for(j = 0; groups[j]; j++) {
-                if (strcmp(groups[j], ctx->allow_groups[i])== 0) {
+                if (string_compare(groups[j], ctx->allow_groups[i])== 0) {
                     matched = true;
                     break;
                 }
@@ -210,7 +214,7 @@ errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
         matched = false;
         for (i = 0; ctx->deny_groups[i]; i++) {
             for(j = 0; groups[j]; j++) {
-                if (strcmp(groups[j], ctx->deny_groups[i])== 0) {
+                if (string_compare(groups[j], ctx->deny_groups[i])== 0) {
                     matched = true;
                     break;
                 }
diff --git a/src/tests/simple_access-tests.c b/src/tests/simple_access-tests.c
index 9cd73d84c91b29fcc5a43bf12e22696d74aae545..5fa62a89b920b28f644398ac33bb31e204890af9 100644
--- a/src/tests/simple_access-tests.c
+++ b/src/tests/simple_access-tests.c
@@ -26,6 +26,7 @@
 #include <popt.h>
 #include <check.h>
 
+#include "confdb/confdb.h"
 #include "providers/simple/simple_access.h"
 
 const char *ulist_1[] = {"u1", "u2", NULL};
@@ -37,6 +38,10 @@ void setup_simple(void)
     fail_unless(ctx == NULL, "Simple context already initialized.");
     ctx = talloc_zero(NULL, struct simple_ctx);
     fail_unless(ctx != NULL, "Cannot create simple context.");
+
+    ctx->domain = talloc_zero(ctx, struct sss_domain_info);
+    fail_unless(ctx != NULL, "Cannot create domain in simple context.");
+    ctx->domain->case_sensitive = true;
 }
 
 void teardown_simple(void)
@@ -123,6 +128,30 @@ START_TEST(test_both_set)
 }
 END_TEST
 
+START_TEST(test_case)
+{
+    int ret;
+    bool access_granted = false;
+
+    ctx->allow_users = discard_const(ulist_1);
+    ctx->deny_users = discard_const(ulist_1);
+
+    ret = simple_access_check(ctx, "U1", &access_granted);
+    fail_unless(ret == EOK, "access_simple_check failed.");
+    fail_unless(access_granted == false, "Access granted "
+                                         "for user with different case "
+                                         "in case-sensitive domain");
+
+    ctx->domain->case_sensitive = false;
+
+    ret = simple_access_check(ctx, "U1", &access_granted);
+    fail_unless(ret == EOK, "access_simple_check failed.");
+    fail_unless(access_granted == false, "Access denied "
+                                         "for user with different case "
+                                         "in case-insensitive domain");
+}
+END_TEST
+
 Suite *access_simple_suite (void)
 {
     Suite *s = suite_create("access_simple");
@@ -133,6 +162,7 @@ Suite *access_simple_suite (void)
     tcase_add_test(tc_allow_deny, test_allow_empty);
     tcase_add_test(tc_allow_deny, test_deny_empty);
     tcase_add_test(tc_allow_deny, test_both_set);
+    tcase_add_test(tc_allow_deny, test_case);
     suite_add_tcase(s, tc_allow_deny);
 
     return s;
-- 
1.7.7.3



More information about the sssd-devel mailing list