393 lines
15 KiB
Diff
393 lines
15 KiB
Diff
|
From 3968a8ddb1b0e55db8217031f92feb4d2ee25c4d Mon Sep 17 00:00:00 2001
|
||
|
From: Sumit Bose <sbose@redhat.com>
|
||
|
Date: Fri, 22 Jan 2016 18:14:45 +0100
|
||
|
Subject: [PATCH 088/108] sdap: improve filtering of multiple results in GC
|
||
|
lookups
|
||
|
|
||
|
The Global Catalog of AD contains some information about all users and
|
||
|
groups in an AD forest. Users from different domain in the forest can
|
||
|
have the same name. The most obvious example is the Administrator user
|
||
|
which is present in all domains. Although SSSD uses a domain specific
|
||
|
search base for looking up users in the GC the search might still return
|
||
|
multiple results if there is a user with the same name in one of the
|
||
|
child (or grand-child ...) domains because of the hierarchic nature of
|
||
|
the LDAP tree. Limiting the search depth would not help because users
|
||
|
can be created in deeply nested OUs.
|
||
|
|
||
|
Currently SSSD expects in this case that the user object is store in
|
||
|
CN=Users or below. This works for all default users like Administrator
|
||
|
but in general users can be created anywhere in the directory tree. If a
|
||
|
user is created outside of CN=Users and there is a user with the same
|
||
|
name in a child domain the initgroups command to look up the
|
||
|
group-memberships of the user fails because it is not clear which of the
|
||
|
two results should be used (initgroups for the child domain user works
|
||
|
fine).
|
||
|
|
||
|
This patch adds an additional scheme to select the right result based on
|
||
|
the domain component attribute name 'dc'. This attribute indicates an
|
||
|
additional component in the domain name and hence a child domain. So as
|
||
|
long as the result contains a dc component following out search base it
|
||
|
cannot be the object we are looking for. This scheme includes the old
|
||
|
CN=Users based one but since it is more expensive I kept the old scheme
|
||
|
which so far worked all the time and only use the new one if the old one
|
||
|
fails.
|
||
|
|
||
|
Resolves https://fedorahosted.org/sssd/ticket/2961
|
||
|
|
||
|
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
|
||
|
(cherry picked from commit 5ff7a765434ed0b4d37564ade26d7761d06f81c3)
|
||
|
(cherry picked from commit 52ea2caa4d21a980902cd0f2fd77ceba25062a8c)
|
||
|
---
|
||
|
src/db/sysdb.h | 6 ++
|
||
|
src/db/sysdb_subdomains.c | 153 +++++++++++++++++++++++++++++
|
||
|
src/providers/ldap/sdap_async_initgroups.c | 48 ++-------
|
||
|
src/tests/cmocka/test_sysdb_subdomains.c | 73 ++++++++++++++
|
||
|
4 files changed, 238 insertions(+), 42 deletions(-)
|
||
|
|
||
|
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
|
||
|
index bb8ca08b12d7eee08d36e5e2f4ac47df686b1d69..4b2feffd058f314d4b0d7270d5a5b242d6555e39 100644
|
||
|
--- a/src/db/sysdb.h
|
||
|
+++ b/src/db/sysdb.h
|
||
|
@@ -1228,4 +1228,10 @@ errno_t sysdb_handle_original_uuid(const char *orig_name,
|
||
|
const char *src_name,
|
||
|
struct sysdb_attrs *dest_attrs,
|
||
|
const char *dest_name);
|
||
|
+
|
||
|
+errno_t sysdb_try_to_find_expected_dn(struct sss_domain_info *dom,
|
||
|
+ const char *domain_component_name,
|
||
|
+ struct sysdb_attrs **usr_attrs,
|
||
|
+ size_t count,
|
||
|
+ struct sysdb_attrs **exp_usr);
|
||
|
#endif /* __SYS_DB_H__ */
|
||
|
diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
|
||
|
index b2bf1a0742171b7beccb44fa915c8adba51fefa3..456e6621b3434a9dbf2e611ad880facbc171c174 100644
|
||
|
--- a/src/db/sysdb_subdomains.c
|
||
|
+++ b/src/db/sysdb_subdomains.c
|
||
|
@@ -1049,3 +1049,156 @@ done:
|
||
|
talloc_free(tmp_ctx);
|
||
|
return ret;
|
||
|
}
|
||
|
+
|
||
|
+errno_t sysdb_try_to_find_expected_dn(struct sss_domain_info *dom,
|
||
|
+ const char *domain_component_name,
|
||
|
+ struct sysdb_attrs **usr_attrs,
|
||
|
+ size_t count,
|
||
|
+ struct sysdb_attrs **exp_usr)
|
||
|
+{
|
||
|
+ char *dom_basedn;
|
||
|
+ size_t dom_basedn_len;
|
||
|
+ char *expected_basedn;
|
||
|
+ size_t expected_basedn_len;
|
||
|
+ size_t dn_len;
|
||
|
+ const char *orig_dn;
|
||
|
+ size_t c = 0;
|
||
|
+ int ret;
|
||
|
+ TALLOC_CTX *tmp_ctx;
|
||
|
+ struct ldb_context *ldb_ctx;
|
||
|
+ struct ldb_dn *ldb_dom_basedn;
|
||
|
+ int dom_basedn_comp_num;
|
||
|
+ struct ldb_dn *ldb_dn;
|
||
|
+ int dn_comp_num;
|
||
|
+ const char *component_name;
|
||
|
+ struct sysdb_attrs *result = NULL;
|
||
|
+ const char *result_dn_str = NULL;
|
||
|
+
|
||
|
+ if (dom == NULL || domain_component_name == NULL || usr_attrs == NULL
|
||
|
+ || count == 0) {
|
||
|
+ return EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ tmp_ctx = talloc_new(NULL);
|
||
|
+ if (tmp_ctx == NULL) {
|
||
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
|
||
|
+ return ENOMEM;
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = domain_to_basedn(tmp_ctx, dom->name, &dom_basedn);
|
||
|
+ if (ret != EOK) {
|
||
|
+ DEBUG(SSSDBG_OP_FAILURE, "domain_to_basedn failed.\n");
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+ expected_basedn = talloc_asprintf(tmp_ctx, "%s%s", "cn=users,", dom_basedn);
|
||
|
+ if (expected_basedn == NULL) {
|
||
|
+ ret = ENOMEM;
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+
|
||
|
+ ldb_ctx = sysdb_ctx_get_ldb(dom->sysdb);
|
||
|
+ if (ldb_ctx == NULL) {
|
||
|
+ DEBUG(SSSDBG_OP_FAILURE, "Missing ldb context.\n");
|
||
|
+ ret = EINVAL;
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+
|
||
|
+ ldb_dom_basedn = ldb_dn_new(tmp_ctx, ldb_ctx, dom_basedn);
|
||
|
+ if (ldb_dom_basedn == NULL) {
|
||
|
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
|
||
|
+ ret = ENOMEM;
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+
|
||
|
+ dom_basedn_comp_num = ldb_dn_get_comp_num(ldb_dom_basedn);
|
||
|
+ dom_basedn_comp_num++;
|
||
|
+
|
||
|
+ DEBUG(SSSDBG_TRACE_ALL, "Expected BaseDN is [%s].\n", expected_basedn);
|
||
|
+ expected_basedn_len = strlen(expected_basedn);
|
||
|
+ dom_basedn_len = strlen(dom_basedn);
|
||
|
+
|
||
|
+ for (c = 0; c < count; c++) {
|
||
|
+ ret = sysdb_attrs_get_string(usr_attrs[c], SYSDB_ORIG_DN, &orig_dn);
|
||
|
+ if (ret != EOK) {
|
||
|
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+ dn_len = strlen(orig_dn);
|
||
|
+
|
||
|
+ if (dn_len > expected_basedn_len
|
||
|
+ && strcasecmp(orig_dn + (dn_len - expected_basedn_len),
|
||
|
+ expected_basedn) == 0) {
|
||
|
+ DEBUG(SSSDBG_TRACE_ALL,
|
||
|
+ "Found matching dn [%s].\n", orig_dn);
|
||
|
+ if (result != NULL) {
|
||
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
||
|
+ "Found 2 matching DN [%s] and [%s], expecting only 1.\n",
|
||
|
+ result_dn_str, orig_dn);
|
||
|
+ ret = EINVAL;
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+ result = usr_attrs[c];
|
||
|
+ result_dn_str = orig_dn;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (result == NULL) {
|
||
|
+ for (c = 0; c < count; c++) {
|
||
|
+ ret = sysdb_attrs_get_string(usr_attrs[c], SYSDB_ORIG_DN, &orig_dn);
|
||
|
+ if (ret != EOK) {
|
||
|
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+ dn_len = strlen(orig_dn);
|
||
|
+
|
||
|
+ if (dn_len > dom_basedn_len
|
||
|
+ && strcasecmp(orig_dn + (dn_len - dom_basedn_len),
|
||
|
+ dom_basedn) == 0) {
|
||
|
+ ldb_dn = ldb_dn_new(tmp_ctx, ldb_ctx, orig_dn);
|
||
|
+ if (ldb_dn == NULL) {
|
||
|
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed");
|
||
|
+ ret = ENOMEM;
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+
|
||
|
+ dn_comp_num = ldb_dn_get_comp_num(ldb_dn);
|
||
|
+ if (dn_comp_num > dom_basedn_comp_num) {
|
||
|
+ component_name = ldb_dn_get_component_name(ldb_dn,
|
||
|
+ (dn_comp_num - dom_basedn_comp_num));
|
||
|
+ DEBUG(SSSDBG_TRACE_ALL, "Comparing [%s] and [%s].\n",
|
||
|
+ component_name,
|
||
|
+ domain_component_name);
|
||
|
+ if (component_name != NULL
|
||
|
+ && strcasecmp(component_name,
|
||
|
+ domain_component_name) != 0) {
|
||
|
+ DEBUG(SSSDBG_TRACE_ALL,
|
||
|
+ "Found matching dn [%s].\n", orig_dn);
|
||
|
+ if (result != NULL) {
|
||
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
||
|
+ "Found 2 matching DN [%s] and [%s], "
|
||
|
+ "expecting only 1.\n", result_dn_str, orig_dn);
|
||
|
+ ret = EINVAL;
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+ result = usr_attrs[c];
|
||
|
+ result_dn_str = orig_dn;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (result == NULL) {
|
||
|
+ DEBUG(SSSDBG_OP_FAILURE, "No matching DN found.\n");
|
||
|
+ ret = ENOENT;
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+
|
||
|
+ *exp_usr = result;
|
||
|
+
|
||
|
+ ret = EOK;
|
||
|
+done:
|
||
|
+ talloc_free(tmp_ctx);
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
|
||
|
index 1e5f5ab49896b234bec0c7a2c1429f30d90ae32a..059b18354362a76376da9321118b8fdb12282b9a 100644
|
||
|
--- a/src/providers/ldap/sdap_async_initgroups.c
|
||
|
+++ b/src/providers/ldap/sdap_async_initgroups.c
|
||
|
@@ -2832,10 +2832,6 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
|
||
|
const char *orig_dn;
|
||
|
const char *cname;
|
||
|
bool in_transaction = false;
|
||
|
- char *expected_basedn;
|
||
|
- size_t expected_basedn_len;
|
||
|
- size_t dn_len;
|
||
|
- size_t c = 0;
|
||
|
|
||
|
DEBUG(SSSDBG_TRACE_ALL, "Receiving info for the user\n");
|
||
|
|
||
|
@@ -2872,54 +2868,22 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
|
||
|
tevent_req_error(req, ret);
|
||
|
return;
|
||
|
}
|
||
|
+ } else if (count == 1) {
|
||
|
+ state->orig_user = usr_attrs[0];
|
||
|
} else if (count != 1) {
|
||
|
DEBUG(SSSDBG_OP_FAILURE,
|
||
|
"Expected one user entry and got %zu\n", count);
|
||
|
|
||
|
- ret = domain_to_basedn(state, state->dom->name, &expected_basedn);
|
||
|
+ ret = sysdb_try_to_find_expected_dn(state->dom, "dc", usr_attrs, count,
|
||
|
+ &state->orig_user);
|
||
|
if (ret != EOK) {
|
||
|
- DEBUG(SSSDBG_OP_FAILURE, "domain_to_basedn failed.\n");
|
||
|
- tevent_req_error(req, ret);
|
||
|
- return;
|
||
|
- }
|
||
|
- expected_basedn = talloc_asprintf(state, "%s%s",
|
||
|
- "cn=users,", expected_basedn);
|
||
|
- if (expected_basedn == NULL) {
|
||
|
- DEBUG(SSSDBG_OP_FAILURE, "talloc_append failed.\n");
|
||
|
- tevent_req_error(req, ENOMEM);
|
||
|
- return;
|
||
|
- }
|
||
|
-
|
||
|
- DEBUG(SSSDBG_TRACE_ALL, "Expected BaseDN is [%s].\n", expected_basedn);
|
||
|
- expected_basedn_len = strlen(expected_basedn);
|
||
|
-
|
||
|
- for (c = 0; c < count; c++) {
|
||
|
- ret = sysdb_attrs_get_string(usr_attrs[c], SYSDB_ORIG_DN, &orig_dn);
|
||
|
- if (ret != EOK) {
|
||
|
- DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
|
||
|
- tevent_req_error(req, ret);
|
||
|
- return;
|
||
|
- }
|
||
|
- dn_len = strlen(orig_dn);
|
||
|
-
|
||
|
- if (dn_len > expected_basedn_len
|
||
|
- && strcasecmp(orig_dn + (dn_len - expected_basedn_len),
|
||
|
- expected_basedn) == 0) {
|
||
|
- DEBUG(SSSDBG_TRACE_ALL,
|
||
|
- "Found matching dn [%s].\n", orig_dn);
|
||
|
- break;
|
||
|
- }
|
||
|
- }
|
||
|
-
|
||
|
- if (c == count) {
|
||
|
- DEBUG(SSSDBG_OP_FAILURE, "No matching DN found.\n");
|
||
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
||
|
+ "try_to_find_expected_dn failed. No matching DN found.\n");
|
||
|
tevent_req_error(req, EINVAL);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- state->orig_user = usr_attrs[c];
|
||
|
-
|
||
|
ret = sysdb_transaction_start(state->sysdb);
|
||
|
if (ret) {
|
||
|
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
|
||
|
diff --git a/src/tests/cmocka/test_sysdb_subdomains.c b/src/tests/cmocka/test_sysdb_subdomains.c
|
||
|
index 701bfb726ff7e950d4439b3dc1a3bee437c9e7ed..f55c2918015900351483e3471bf946ea60872dae 100644
|
||
|
--- a/src/tests/cmocka/test_sysdb_subdomains.c
|
||
|
+++ b/src/tests/cmocka/test_sysdb_subdomains.c
|
||
|
@@ -509,6 +509,76 @@ static void test_sysdb_link_ad_multidom(void **state)
|
||
|
|
||
|
}
|
||
|
|
||
|
+static void test_try_to_find_expected_dn(void **state)
|
||
|
+{
|
||
|
+ int ret;
|
||
|
+ struct sysdb_attrs *result;
|
||
|
+ struct sysdb_attrs *usr_attrs[10] = { NULL };
|
||
|
+ struct sss_domain_info *dom;
|
||
|
+ struct subdom_test_ctx *test_ctx =
|
||
|
+ talloc_get_type(*state, struct subdom_test_ctx);
|
||
|
+
|
||
|
+ dom = find_domain_by_name(test_ctx->tctx->dom,
|
||
|
+ "child2.test_sysdb_subdomains_2", true);
|
||
|
+ assert_non_null(dom);
|
||
|
+
|
||
|
+ usr_attrs[0] = sysdb_new_attrs(test_ctx);
|
||
|
+ assert_non_null(usr_attrs[0]);
|
||
|
+
|
||
|
+ ret = sysdb_attrs_add_string(usr_attrs[0], SYSDB_ORIG_DN,
|
||
|
+ "uid=user,cn=abc,dc=c2,dc=child2,dc=test_sysdb_subdomains_2");
|
||
|
+ assert_int_equal(ret, EOK);
|
||
|
+
|
||
|
+ ret = sysdb_try_to_find_expected_dn(NULL, NULL, NULL, 0, NULL);
|
||
|
+ assert_int_equal(ret, EINVAL);
|
||
|
+
|
||
|
+ ret = sysdb_try_to_find_expected_dn(dom, "dc", usr_attrs, 1, &result);
|
||
|
+ assert_int_equal(ret, ENOENT);
|
||
|
+
|
||
|
+ ret = sysdb_try_to_find_expected_dn(dom, "xy", usr_attrs, 1, &result);
|
||
|
+ assert_int_equal(ret, EOK);
|
||
|
+ assert_ptr_equal(result, usr_attrs[0]);
|
||
|
+
|
||
|
+ usr_attrs[1] = sysdb_new_attrs(test_ctx);
|
||
|
+ assert_non_null(usr_attrs[1]);
|
||
|
+
|
||
|
+ ret = sysdb_attrs_add_string(usr_attrs[1], SYSDB_ORIG_DN,
|
||
|
+ "uid=user1,cn=abc,dc=child2,dc=test_sysdb_subdomains_2");
|
||
|
+ assert_int_equal(ret, EOK);
|
||
|
+
|
||
|
+ usr_attrs[2] = sysdb_new_attrs(test_ctx);
|
||
|
+ assert_non_null(usr_attrs[2]);
|
||
|
+
|
||
|
+ ret = sysdb_attrs_add_string(usr_attrs[2], SYSDB_ORIG_DN,
|
||
|
+ "uid=user2,cn=abc,dc=c2,dc=child2,dc=test_sysdb_subdomains_2");
|
||
|
+ assert_int_equal(ret, EOK);
|
||
|
+
|
||
|
+ ret = sysdb_try_to_find_expected_dn(dom, "dc", usr_attrs, 3, &result);
|
||
|
+ assert_int_equal(ret, EOK);
|
||
|
+ assert_ptr_equal(result, usr_attrs[1]);
|
||
|
+
|
||
|
+ ret = sysdb_try_to_find_expected_dn(dom, "xy", usr_attrs, 3, &result);
|
||
|
+ assert_int_equal(ret, EINVAL);
|
||
|
+
|
||
|
+ /* Make sure cn=users match is preferred */
|
||
|
+ talloc_free(usr_attrs[2]);
|
||
|
+ usr_attrs[2] = sysdb_new_attrs(test_ctx);
|
||
|
+ assert_non_null(usr_attrs[2]);
|
||
|
+
|
||
|
+ ret = sysdb_attrs_add_string(usr_attrs[2], SYSDB_ORIG_DN,
|
||
|
+ "uid=user2,cn=abc,cn=users,dc=child2,dc=test_sysdb_subdomains_2");
|
||
|
+ assert_int_equal(ret, EOK);
|
||
|
+
|
||
|
+ ret = sysdb_try_to_find_expected_dn(dom, "dc", usr_attrs, 3, &result);
|
||
|
+ assert_int_equal(ret, EOK);
|
||
|
+ assert_ptr_equal(result, usr_attrs[2]);
|
||
|
+
|
||
|
+
|
||
|
+ talloc_free(usr_attrs[0]);
|
||
|
+ talloc_free(usr_attrs[1]);
|
||
|
+ talloc_free(usr_attrs[2]);
|
||
|
+}
|
||
|
+
|
||
|
int main(int argc, const char *argv[])
|
||
|
{
|
||
|
int rv;
|
||
|
@@ -542,6 +612,9 @@ int main(int argc, const char *argv[])
|
||
|
cmocka_unit_test_setup_teardown(test_sysdb_link_ad_multidom,
|
||
|
test_sysdb_subdom_setup,
|
||
|
test_sysdb_subdom_teardown),
|
||
|
+ cmocka_unit_test_setup_teardown(test_try_to_find_expected_dn,
|
||
|
+ test_sysdb_subdom_setup,
|
||
|
+ test_sysdb_subdom_teardown),
|
||
|
};
|
||
|
|
||
|
/* Set debug level to invalid value so we can deside if -d 0 was used. */
|
||
|
--
|
||
|
2.7.3
|
||
|
|