URL: https://github.com/SSSD/sssd/pull/5281
Author: thalman
 Title: #5281: Dn with spaces for 1.16
Action: synchronized

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/5281/head:pr5281
git checkout pr5281
From cd6baeace161fa67535be8feaafa73756dc92ad8 Mon Sep 17 00:00:00 2001
From: Tomas Halman <thal...@redhat.com>
Date: Fri, 31 Jul 2020 11:12:02 +0200
Subject: [PATCH 1/5] UTIL: DN sanitization

Some of the ldap servers returns DN in attributes such as isMemberOf
with spaces like dc=example, dc=com. That should be fine and we
should ignore them (cut them out) instead of escaping.

Resolves:
https://github.com/SSSD/sssd/issues/5261

Reviewed-by: Alexey Tikhonov <atikh...@redhat.com>
(cherry picked from commit 093061f553ab0a2c316794221e79779fb1bd40d2)
---
 src/tests/cmocka/test_utils.c |  70 +++++++++++++++++++
 src/util/util.c               | 126 ++++++++++++++++++++++++++++++++++
 src/util/util.h               |  20 ++++++
 3 files changed, 216 insertions(+)

diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
index bd2c9e65da..aa245f00b2 100644
--- a/src/tests/cmocka/test_utils.c
+++ b/src/tests/cmocka/test_utils.c
@@ -1935,6 +1935,73 @@ static void test_sss_get_domain_mappings_content(void **state)
      * capaths might not be as expected. */
 }
 
+
+static void test_sss_filter_sanitize_dn(void **state)
+{
+    TALLOC_CTX *tmp_ctx;
+    char *trimmed;
+    int ret;
+    const char *DN = "cn=user,ou=people,dc=example,dc=com";
+
+    tmp_ctx = talloc_new(NULL);
+    assert_non_null(tmp_ctx);
+
+    /* test that we remove spaces around '=' and ','*/
+    ret = sss_filter_sanitize_dn(tmp_ctx, DN, &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    ret = sss_filter_sanitize_dn(tmp_ctx, "cn=user,ou=people,dc=example,dc=com", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    ret = sss_filter_sanitize_dn(tmp_ctx, "cn= user,ou =people,dc = example,dc  =  com", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    ret = sss_filter_sanitize_dn(tmp_ctx, "cn=user, ou=people ,dc=example , dc=com", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    ret = sss_filter_sanitize_dn(tmp_ctx, "cn=user,  ou=people  ,dc=example  ,   dc=com", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    ret = sss_filter_sanitize_dn(tmp_ctx, "cn= user, ou =people ,dc = example  ,  dc  = com", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    ret = sss_filter_sanitize_dn(tmp_ctx, " cn=user,ou=people,dc=example,dc=com ", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    ret = sss_filter_sanitize_dn(tmp_ctx, "  cn=user, ou=people, dc=example, dc=com  ", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(DN, trimmed);
+    talloc_free(trimmed);
+
+    /* test that we keep spaces inside a value */
+    ret = sss_filter_sanitize_dn(tmp_ctx, "cn = user one, ou=people  branch, dc=example, dc=com", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal("cn=user\\20one,ou=people\\20\\20branch,dc=example,dc=com", trimmed);
+    talloc_free(trimmed);
+
+    /* test that we keep escape special chars like () */
+    ret = sss_filter_sanitize_dn(tmp_ctx, "cn = user one, ou=p(e)ople, dc=example, dc=com", &trimmed);
+    assert_int_equal(ret, EOK);
+    assert_string_equal("cn=user\\20one,ou=p\\28e\\29ople,dc=example,dc=com", trimmed);
+    talloc_free(trimmed);
+
+    talloc_free(tmp_ctx);
+}
+
 int main(int argc, const char *argv[])
 {
     poptContext pc;
@@ -2044,6 +2111,9 @@ int main(int argc, const char *argv[])
         cmocka_unit_test_setup_teardown(test_sss_ptr_hash_without_cb,
                                         setup_leak_tests,
                                         teardown_leak_tests),
+        cmocka_unit_test_setup_teardown(test_sss_filter_sanitize_dn,
+                                        setup_leak_tests,
+                                        teardown_leak_tests),
     };
 
     /* Set debug level to invalid value so we can decide if -d 0 was used. */
diff --git a/src/util/util.c b/src/util/util.c
index e3efa7fefe..aaf3609c32 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -530,6 +530,132 @@ errno_t sss_filter_sanitize(TALLOC_CTX *mem_ctx,
     return sss_filter_sanitize_ex(mem_ctx, input, sanitized, NULL);
 }
 
+/* There is similar function ldap_dn_normalize in openldap.
+ * To avoid dependecies across project we have this own func.
+ * Also ldb can do this but doesn't handle all the cases
+ */
+static errno_t sss_trim_dn(TALLOC_CTX *mem_ctx,
+                           const char *input,
+                           char **trimmed)
+{
+    int i = 0;
+    int o = 0;
+    int s;
+    char *output;
+    enum sss_trim_dn_state {
+        SSS_TRIM_DN_STATE_READING_NAME,
+        SSS_TRIM_DN_STATE_READING_VALUE
+    } state = SSS_TRIM_DN_STATE_READING_NAME;
+
+    *trimmed = NULL;
+
+    output = talloc_array(mem_ctx, char, strlen(input) + 1);
+    if (!output) {
+        return ENOMEM;
+    }
+
+    /* skip leading spaces */
+    while(isspace(input[i])) {
+        ++i;
+    }
+
+    while(input[i] != '\0') {
+        if (!isspace(input[i])) {
+            switch (input[i]) {
+            case '=':
+                output[o++] = input[i++];
+                if (state == SSS_TRIM_DN_STATE_READING_NAME) {
+                    while (isspace(input[i])) {
+                        ++i;
+                    }
+                    state = SSS_TRIM_DN_STATE_READING_VALUE;
+                }
+                break;
+            case ',':
+                output[o++] = input[i++];
+                if (state == SSS_TRIM_DN_STATE_READING_VALUE) {
+                    while (isspace(input[i])) {
+                        ++i;
+                    }
+                    state = SSS_TRIM_DN_STATE_READING_NAME;
+                }
+                break;
+            case '\\':
+                output[o++] = input[i++];
+                if (input[i] != '\0') {
+                    output[o++] = input[i++];
+                }
+                break;
+            default:
+                if (input[i] != '\0') {
+                    output[o++] = input[i++];
+                }
+                break;
+            }
+
+            continue;
+        }
+
+        /* non escaped space found */
+        s = 1;
+        while (isspace(input[i + s])) {
+            ++s;
+        }
+
+        switch (state) {
+        case SSS_TRIM_DN_STATE_READING_NAME:
+            if (input[i + s] != '=') {
+                /* this is not trailing space - should not be removed */
+                while (isspace(input[i])) {
+                    output[o++] = input[i++];
+                }
+            } else {
+                i += s;
+            }
+            break;
+        case SSS_TRIM_DN_STATE_READING_VALUE:
+            if (input[i + s] != ',') {
+                /* this is not trailing space - should not be removed */
+                while (isspace(input[i])) {
+                    output[o++] = input[i++];
+                }
+            } else {
+                i += s;
+            }
+            break;
+        }
+    }
+
+    output[o--] = '\0';
+
+    /* trim trailing space */
+    while (o >= 0 && isspace(output[o])) {
+        output[o--] = '\0';
+    }
+
+    *trimmed = output;
+    return EOK;
+}
+
+errno_t sss_filter_sanitize_dn(TALLOC_CTX *mem_ctx,
+                               const char *input,
+                               char **sanitized)
+{
+    errno_t ret;
+    char *trimmed_dn = NULL;
+
+    ret = sss_trim_dn(mem_ctx, input, &trimmed_dn);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = sss_filter_sanitize_ex(mem_ctx, trimmed_dn, sanitized, NULL);
+
+ done:
+    talloc_free(trimmed_dn);
+    return ret;
+}
+
 char *
 sss_escape_ip_address(TALLOC_CTX *mem_ctx, int family, const char *addr)
 {
diff --git a/src/util/util.h b/src/util/util.h
index 8dc887cab6..d7d2017fa7 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -477,6 +477,26 @@ errno_t sss_filter_sanitize_for_dom(TALLOC_CTX *mem_ctx,
                                     char **sanitized,
                                     char **lc_sanitized);
 
+/* Sanitize an input string (e.g. a DN) for use in
+ * an LDAP/LDB filter
+ *
+ * It is basically the same as sss_filter_sanitize(_ex),
+ * just extra spaces inside DN around '=' and ',' are removed
+ * before sanitizing other characters . According the documentation
+ * spaces in DN are allowed and some ldap servers can return them
+ * in isMemberOf or member attributes.
+ *
+ * (dc = my example, dc = com => dc=my\20example,dc=com)
+ *
+ * Returns a newly-constructed string attached to mem_ctx
+ * It will fail only on an out of memory condition, where it
+ * will return ENOMEM.
+ *
+ */
+errno_t sss_filter_sanitize_dn(TALLOC_CTX *mem_ctx,
+                               const char *input,
+                               char **sanitized);
+
 char *
 sss_escape_ip_address(TALLOC_CTX *mem_ctx, int family, const char *addr);
 

From 677bf518cbb63fda826fbb1dd728be9df44cd277 Mon Sep 17 00:00:00 2001
From: Tomas Halman <thal...@redhat.com>
Date: Fri, 31 Jul 2020 11:21:44 +0200
Subject: [PATCH 2/5] UTIL: Use sss_sanitize_dn where we deal with DN

Resolves:
https://github.com/SSSD/sssd/issues/5261

Reviewed-by: Alexey Tikhonov <atikh...@redhat.com>
(cherry picked from commit 21b9417e14ce35a2548c309642325ac43103d51e)
---
 src/db/sysdb_ops.c                         | 2 +-
 src/providers/ipa/ipa_deskprofile_rules.c  | 2 +-
 src/providers/ipa/ipa_hbac_rules.c         | 2 +-
 src/providers/ipa/ipa_netgroups.c          | 2 +-
 src/providers/ldap/sdap_async_groups.c     | 2 +-
 src/providers/ldap/sdap_async_groups_ad.c  | 2 +-
 src/providers/ldap/sdap_async_initgroups.c | 4 ++--
 7 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
index b51c821aec..d4ad69e39e 100644
--- a/src/db/sysdb_ops.c
+++ b/src/db/sysdb_ops.c
@@ -3494,7 +3494,7 @@ errno_t sysdb_search_by_orig_dn(TALLOC_CTX *mem_ctx,
         return ENOMEM;
     }
 
-    ret = sss_filter_sanitize(tmp_ctx, member_dn, &sanitized_dn);
+    ret = sss_filter_sanitize_dn(tmp_ctx, member_dn, &sanitized_dn);
     if (ret != EOK) {
         goto done;
     }
diff --git a/src/providers/ipa/ipa_deskprofile_rules.c b/src/providers/ipa/ipa_deskprofile_rules.c
index 65994356e8..cce6184db6 100644
--- a/src/providers/ipa/ipa_deskprofile_rules.c
+++ b/src/providers/ipa/ipa_deskprofile_rules.c
@@ -91,7 +91,7 @@ ipa_deskprofile_rule_info_send(TALLOC_CTX *mem_ctx,
         goto immediate;
     }
 
-    ret = sss_filter_sanitize(state, host_dn, &host_dn_clean);
+    ret = sss_filter_sanitize_dn(state, host_dn, &host_dn_clean);
     if (ret != EOK) {
         goto immediate;
     }
diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c
index 0634a277e2..e2c97ae3db 100644
--- a/src/providers/ipa/ipa_hbac_rules.c
+++ b/src/providers/ipa/ipa_hbac_rules.c
@@ -84,7 +84,7 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
         goto immediate;
     }
 
-    ret = sss_filter_sanitize(state, host_dn, &host_dn_clean);
+    ret = sss_filter_sanitize_dn(state, host_dn, &host_dn_clean);
     if (ret != EOK) goto immediate;
 
     state->ev = ev;
diff --git a/src/providers/ipa/ipa_netgroups.c b/src/providers/ipa/ipa_netgroups.c
index 05ebac7582..e14f48fb0a 100644
--- a/src/providers/ipa/ipa_netgroups.c
+++ b/src/providers/ipa/ipa_netgroups.c
@@ -376,7 +376,7 @@ static void ipa_get_netgroups_process(struct tevent_req *subreq)
             continue;
         }
 
-        ret = sss_filter_sanitize(state, orig_dn, &dn);
+        ret = sss_filter_sanitize_dn(state, orig_dn, &dn);
         if (ret != EOK) {
             goto done;
         }
diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
index 09e15bc3d0..abe2ed2759 100644
--- a/src/providers/ldap/sdap_async_groups.c
+++ b/src/providers/ldap/sdap_async_groups.c
@@ -52,7 +52,7 @@ static int sdap_find_entry_by_origDN(TALLOC_CTX *memctx,
         return ENOMEM;
     }
 
-    ret = sss_filter_sanitize(tmpctx, orig_dn, &sanitized_dn);
+    ret = sss_filter_sanitize_dn(tmpctx, orig_dn, &sanitized_dn);
     if (ret != EOK) {
         ret = ENOMEM;
         goto done;
diff --git a/src/providers/ldap/sdap_async_groups_ad.c b/src/providers/ldap/sdap_async_groups_ad.c
index 3f842b26da..c954398bbc 100644
--- a/src/providers/ldap/sdap_async_groups_ad.c
+++ b/src/providers/ldap/sdap_async_groups_ad.c
@@ -91,7 +91,7 @@ sdap_get_ad_match_rule_members_send(TALLOC_CTX *mem_ctx,
     }
 
     /* Sanitize it in case we have special characters in DN */
-    ret = sss_filter_sanitize(state, group_dn, &sanitized_group_dn);
+    ret = sss_filter_sanitize_dn(state, group_dn, &sanitized_group_dn);
     if (ret != EOK) {
         DEBUG(SSSDBG_MINOR_FAILURE,
               "Could not sanitize group DN: %s\n",
diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
index 620782b6fe..055bdaefc5 100644
--- a/src/providers/ldap/sdap_async_initgroups.c
+++ b/src/providers/ldap/sdap_async_initgroups.c
@@ -1647,7 +1647,7 @@ static struct tevent_req *sdap_initgr_rfc2307bis_send(
                                attr_filter, &state->attrs, NULL);
     if (ret != EOK) goto done;
 
-    ret = sss_filter_sanitize(state, orig_dn, &clean_orig_dn);
+    ret = sss_filter_sanitize_dn(state, orig_dn, &clean_orig_dn);
     if (ret != EOK) goto done;
 
     use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
@@ -2429,7 +2429,7 @@ static errno_t rfc2307bis_nested_groups_step(struct tevent_req *req)
         goto done;
     }
 
-    ret = sss_filter_sanitize(tmp_ctx, state->orig_dn, &clean_orig_dn);
+    ret = sss_filter_sanitize_dn(tmp_ctx, state->orig_dn, &clean_orig_dn);
     if (ret != EOK) {
         goto done;
     }

From 7d97530a8fb68e588acc4ee50a6902a0838051cc Mon Sep 17 00:00:00 2001
From: Tomas Halman <thal...@redhat.com>
Date: Wed, 19 Aug 2020 15:17:44 +0200
Subject: [PATCH 3/5] UTIL: Use sss_sanitize_dn where we deal with DN 2

Tests show that also ldb_dn_get_linearized can
return DN with extra spaces. We have to trim that too.

Resolves:
https://github.com/SSSD/sssd/issues/5261

Reviewed-by: Alexey Tikhonov <atikh...@redhat.com>
(cherry picked from commit fe0f1e64e8a77dadde699495c7eb368ce61ac992)
---
 src/ldb_modules/memberof.c           | 6 +++---
 src/providers/ldap/ldap_id_cleanup.c | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/ldb_modules/memberof.c b/src/ldb_modules/memberof.c
index dae51938b7..5de3b7c3b5 100644
--- a/src/ldb_modules/memberof.c
+++ b/src/ldb_modules/memberof.c
@@ -1364,7 +1364,7 @@ static int memberof_del(struct ldb_module *module, struct ldb_request *req)
         return LDB_ERR_OPERATIONS_ERROR;
     }
 
-    sret = sss_filter_sanitize(del_ctx, dn, &clean_dn);
+    sret = sss_filter_sanitize_dn(del_ctx, dn, &clean_dn);
     if (sret != 0) {
         talloc_free(ctx);
         return LDB_ERR_OPERATIONS_ERROR;
@@ -1781,7 +1781,7 @@ static int mbof_del_execute_op(struct mbof_del_operation *delop)
         return LDB_ERR_OPERATIONS_ERROR;
     }
 
-    ret = sss_filter_sanitize(del_ctx, dn, &clean_dn);
+    ret = sss_filter_sanitize_dn(del_ctx, dn, &clean_dn);
     if (ret != 0) {
         return LDB_ERR_OPERATIONS_ERROR;
     }
@@ -3054,7 +3054,7 @@ static int mbof_get_ghost_from_parent(struct mbof_mod_del_op *igh)
         return LDB_ERR_OPERATIONS_ERROR;
     }
 
-    ret = sss_filter_sanitize(igh, dn, &clean_dn);
+    ret = sss_filter_sanitize_dn(igh, dn, &clean_dn);
     if (ret != 0) {
         return LDB_ERR_OPERATIONS_ERROR;
     }
diff --git a/src/providers/ldap/ldap_id_cleanup.c b/src/providers/ldap/ldap_id_cleanup.c
index 8c0f0c18ba..cd10126f4c 100644
--- a/src/providers/ldap/ldap_id_cleanup.c
+++ b/src/providers/ldap/ldap_id_cleanup.c
@@ -422,7 +422,7 @@ static int cleanup_groups(TALLOC_CTX *memctx,
         }
 
         /* sanitize dn */
-        ret = sss_filter_sanitize(tmpctx, dn, &sanitized_dn);
+        ret = sss_filter_sanitize_dn(tmpctx, dn, &sanitized_dn);
         if (ret != EOK) {
             DEBUG(SSSDBG_MINOR_FAILURE,
                   "sss_filter_sanitize failed: %s:[%d]\n",

From ba6738945b7a1f6779c94b456b1ef31716ad996a Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Wed, 26 Aug 2020 15:40:53 +0200
Subject: [PATCH 4/5] ldap: use member DN to create ghost user hash table

Reviewed-by: Alexey Tikhonov <atikh...@redhat.com>
(cherry picked from commit 50d0d154cedb6915ab321b47c40851c40e91cf41)
---
 src/db/sysdb.h                                |  1 +
 src/providers/ldap/sdap.c                     | 10 ++++++++++
 src/providers/ldap/sdap_async_groups.c        | 17 +++++++++++++++-
 src/providers/ldap/sdap_async_nested_groups.c | 20 +++++++++++++++++--
 4 files changed, 45 insertions(+), 3 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index a2bc8ed3be..679763bad4 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -129,6 +129,7 @@
 #define SYSDB_UPN "userPrincipalName"
 #define SYSDB_CANONICAL_UPN "canonicalUserPrincipalName"
 #define SYSDB_CCACHE_FILE "ccacheFile"
+#define SYSDB_DN_FOR_MEMBER_HASH_TABLE "dnForMemberHashTable"
 
 #define SYSDB_ORIG_DN "originalDN"
 #define SYSDB_ORIG_MODSTAMP "originalModifyTimestamp"
diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c
index a9c8b92b85..a1a00df568 100644
--- a/src/providers/ldap/sdap.c
+++ b/src/providers/ldap/sdap.c
@@ -771,6 +771,16 @@ errno_t sdap_parse_deref(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
+        /* The dereference control seems to return the DN from the dereference
+         * attribute (e.g. member) so we can use it as key for the hash table
+         * later. */
+        ret = sysdb_attrs_add_string(res[mi]->attrs,
+                                     SYSDB_DN_FOR_MEMBER_HASH_TABLE, orig_dn);
+        if (ret) {
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
+            goto done;
+        }
+
         for (dval = dref->attrVals; dval != NULL; dval = dval->next) {
             DEBUG(SSSDBG_TRACE_INTERNAL,
                   "Dereferenced attribute: %s\n", dval->type);
diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
index abe2ed2759..4e3c524a4f 100644
--- a/src/providers/ldap/sdap_async_groups.c
+++ b/src/providers/ldap/sdap_async_groups.c
@@ -2509,6 +2509,7 @@ static errno_t sdap_nested_group_populate_users(TALLOC_CTX *mem_ctx,
     struct ldb_message_element *el;
     const char *username;
     const char *original_dn;
+    const char *hash_key_dn;
     struct sss_domain_info *user_dom;
     struct sdap_domain *sdap_dom;
 
@@ -2607,8 +2608,22 @@ static errno_t sdap_nested_group_populate_users(TALLOC_CTX *mem_ctx,
                                        SYSDB_MOD_REP);
             if (ret != EOK) goto done;
         } else {
+            /* The DN of the user object and the DN in the member attribute
+             * might differ, e.g. in case. Since we later search the hash with
+             * DNs from the member attribute we should try to use DN from the
+             * member attribute here as well. This should be added earlier in
+             * the SYSDB_DN_FOR_MEMBER_HASH_TABLE attribute. If this does not
+             * exists we fall-back to original_dn which should work in the
+             * most cases as well. */
+            ret = sysdb_attrs_get_string(users[i],
+                                         SYSDB_DN_FOR_MEMBER_HASH_TABLE,
+                                         &hash_key_dn);
+            if (ret != EOK) {
+                hash_key_dn = original_dn;
+            }
+
             key.type = HASH_KEY_STRING;
-            key.str = talloc_steal(ghosts, discard_const(original_dn));
+            key.str = talloc_steal(ghosts, discard_const(hash_key_dn));
             value.type = HASH_VALUE_PTR;
             /* Already qualified from sdap_get_user_primary_name() */
             value.ptr = talloc_steal(ghosts, discard_const(username));
diff --git a/src/providers/ldap/sdap_async_nested_groups.c b/src/providers/ldap/sdap_async_nested_groups.c
index 055de29cad..300d3de64b 100644
--- a/src/providers/ldap/sdap_async_nested_groups.c
+++ b/src/providers/ldap/sdap_async_nested_groups.c
@@ -241,9 +241,12 @@ static errno_t sdap_nested_group_hash_entry(hash_table_t *table,
     const char *name = NULL;
     errno_t ret;
 
-    ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &name);
+    ret = sysdb_attrs_get_string(entry, SYSDB_DN_FOR_MEMBER_HASH_TABLE, &name);
     if (ret != EOK) {
-        return ret;
+        ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &name);
+        if (ret != EOK) {
+            return ret;
+        }
     }
 
     return sdap_nested_group_hash_insert(table, name, entry, false, table_name);
@@ -1495,6 +1498,19 @@ sdap_nested_group_single_step_process(struct tevent_req *subreq)
             }
         }
 
+        /* The original DN of the user object itself might differ from the one
+         * used in the member attribute, e.g. different case. To make sure if
+         * can be found in a hash table when iterating over group members the
+         * DN from the member attribute used for the search as saved as well.
+         */
+        ret = sysdb_attrs_add_string(entry,
+                                     SYSDB_DN_FOR_MEMBER_HASH_TABLE,
+                                     state->current_member->dn);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
+            goto done;
+        }
+
         /* save user in hash table */
         ret = sdap_nested_group_hash_user(state->group_ctx, entry);
         if (ret == EEXIST) {

From c97c72c0ca98b4a8e034bc49c1a1d094c3fd2134 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Wed, 2 Sep 2020 12:37:27 +0200
Subject: [PATCH 5/5] intg: allow member DN to have a different case

Make sure that group members are added properly to the group even if the
user DN in the RFC2307bis member attribute differs in case from the
original DN of the user object.

Reviewed-by: Alexey Tikhonov <atikh...@redhat.com>
(cherry picked from commit 88631392e9172ae4fa3e411398516a2f39f0060e)
---
 src/tests/intg/test_ldap.py | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/src/tests/intg/test_ldap.py b/src/tests/intg/test_ldap.py
index c432068f91..2e88ac7d1d 100644
--- a/src/tests/intg/test_ldap.py
+++ b/src/tests/intg/test_ldap.py
@@ -303,6 +303,25 @@ def sanity_rfc2307_bis(request, ldap_conn):
     return None
 
 
+@pytest.fixture
+def member_with_different_cases_rfc2307_bis(request, ldap_conn):
+    """
+    Create a group where the user DN values of the RFC2307bis member attribute
+    differ in case from the original DN of the user object.
+    """
+    ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn)
+    ent_list.add_user("user1", 1001, 2001)
+    ent_list.add_user("user2", 1002, 2002)
+
+    ent_list.add_group_bis("two_user_group", 2012, ["USER1", "uSeR2"])
+
+    create_ldap_fixture(request, ldap_conn, ent_list)
+    conf = format_basic_conf(ldap_conn, SCHEMA_RFC2307_BIS)
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+    return None
+
+
 def expected_list_to_name_dict(entries):
     return dict((u["name"], u) for u in entries)
 
@@ -394,6 +413,22 @@ def test_sanity_rfc2307_bis(ldap_conn, sanity_rfc2307_bis):
         grp.getgrgid(1)
 
 
+def test_member_with_different_cases_rfc2307_bis(
+                                      ldap_conn,
+                                      member_with_different_cases_rfc2307_bis):
+    """
+    Regression test for https://bugzilla.redhat.com/show_bug.cgi?id=1817122
+    Make sure that group members are added properly to the group even if the
+    user DN in the RFC2307bis member attribute differs in case from the
+    original DN of the user object.
+    """
+    group_pattern = expected_list_to_name_dict([
+        dict(name='two_user_group', passwd='*', gid=2012,
+             mem=ent.contains_only("user1", "user2")),
+    ])
+    ent.assert_each_group_by_name(group_pattern)
+
+
 @pytest.fixture
 def refresh_after_cleanup_task(request, ldap_conn):
     ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn)
_______________________________________________
sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org
Fedora Code of Conduct: 
https://docs.fedoraproject.org/en-US/project/code-of-conduct/
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: 
https://lists.fedorahosted.org/archives/list/sssd-devel@lists.fedorahosted.org

Reply via email to