The Samba-Bugzilla – Attachment 17834 Details for
Bug 15270
CVE-2023-0614 [SECURITY] Not-secret but access controlled LDAP attributes can be discovered
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch v7 backported to Samba 4.16 as v7.1. Includes only the fully required commits.
CVE-2023-0614-confidential-attrs-4.16-v7.1.patch (text/plain), 277.16 KB, created by
Jennifer Sutton
on 2023-03-15 21:04:44 UTC
(
hide
)
Description:
Patch v7 backported to Samba 4.16 as v7.1. Includes only the fully required commits.
Filename:
MIME Type:
Creator:
Jennifer Sutton
Created:
2023-03-15 21:04:44 UTC
Size:
277.16 KB
patch
obsolete
>From 197d1f09158ee0575cd92c461ed12bdbf4efc074 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Mon, 13 Mar 2023 14:25:56 +1300 >Subject: [PATCH 01/35] CVE-2023-0614 lib/ldb: Avoid allocation and memcpy() > for every wildcard match candidate > >The value can be quite large, the allocation will take much >longer than the actual match and is repeated per candidate >record. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15331 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz> >(cherry picked from commit cad96f59a08192df927fb1df4e9787c7f70991a2) > >[abartlet@samba.org Included in the security release as this > makes the new large_ldap.py timeout test more reliable] >--- > lib/ldb/common/ldb_match.c | 60 +++++++++++++++++++++++++++++++------- > 1 file changed, 50 insertions(+), 10 deletions(-) > >diff --git a/lib/ldb/common/ldb_match.c b/lib/ldb/common/ldb_match.c >index 2f4d41f3441..51376871b4c 100644 >--- a/lib/ldb/common/ldb_match.c >+++ b/lib/ldb/common/ldb_match.c >@@ -34,6 +34,7 @@ > > #include "ldb_private.h" > #include "dlinklist.h" >+#include "ldb_handlers.h" > > /* > check if the scope matches in a search result >@@ -259,20 +260,42 @@ static int ldb_wildcard_compare(struct ldb_context *ldb, > return LDB_SUCCESS; > } > >- if (a->syntax->canonicalise_fn(ldb, ldb, &value, &val) != 0) { >- return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; >+ /* No need to just copy this value for a binary match */ >+ if (a->syntax->canonicalise_fn != ldb_handler_copy) { >+ if (a->syntax->canonicalise_fn(ldb, ldb, &value, &val) != 0) { >+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; >+ } >+ >+ /* >+ * Only set save_p if we allocate (call >+ * a->syntax->canonicalise_fn()), as we >+ * talloc_free(save_p) below to clean up >+ */ >+ save_p = val.data; >+ } else { >+ val = value; > } > >- save_p = val.data; > cnk.data = NULL; > > if ( ! tree->u.substring.start_with_wildcard ) { >+ uint8_t *cnk_to_free = NULL; > > chunk = tree->u.substring.chunks[c]; >- if (a->syntax->canonicalise_fn(ldb, ldb, chunk, &cnk) != 0) goto mismatch; >+ /* No need to just copy this value for a binary match */ >+ if (a->syntax->canonicalise_fn != ldb_handler_copy) { >+ if (a->syntax->canonicalise_fn(ldb, ldb, chunk, &cnk) != 0) { >+ goto mismatch; >+ } >+ >+ cnk_to_free = cnk.data; >+ } else { >+ cnk = *chunk; >+ } > > /* This deals with wildcard prefix searches on binary attributes (eg objectGUID) */ > if (cnk.length > val.length) { >+ TALLOC_FREE(cnk_to_free); > goto mismatch; > } > /* >@@ -280,32 +303,47 @@ static int ldb_wildcard_compare(struct ldb_context *ldb, > * we can cope with this. > */ > if (cnk.length == 0) { >+ TALLOC_FREE(cnk_to_free); >+ goto mismatch; >+ } >+ >+ if (memcmp((char *)val.data, (char *)cnk.data, cnk.length) != 0) { >+ TALLOC_FREE(cnk_to_free); > goto mismatch; > } > >- if (memcmp((char *)val.data, (char *)cnk.data, cnk.length) != 0) goto mismatch; > val.length -= cnk.length; > val.data += cnk.length; > c++; >- talloc_free(cnk.data); >+ TALLOC_FREE(cnk_to_free); > cnk.data = NULL; > } > > while (tree->u.substring.chunks[c]) { > uint8_t *p; >+ uint8_t *cnk_to_free = NULL; > > chunk = tree->u.substring.chunks[c]; >- if(a->syntax->canonicalise_fn(ldb, ldb, chunk, &cnk) != 0) { >- goto mismatch; >+ /* No need to just copy this value for a binary match */ >+ if (a->syntax->canonicalise_fn != ldb_handler_copy) { >+ if (a->syntax->canonicalise_fn(ldb, ldb, chunk, &cnk) != 0) { >+ goto mismatch; >+ } >+ >+ cnk_to_free = cnk.data; >+ } else { >+ cnk = *chunk; > } > /* > * Empty strings are returned as length 0. Ensure > * we can cope with this. > */ > if (cnk.length == 0) { >+ TALLOC_FREE(cnk_to_free); > goto mismatch; > } > if (cnk.length > val.length) { >+ TALLOC_FREE(cnk_to_free); > goto mismatch; > } > >@@ -320,6 +358,8 @@ static int ldb_wildcard_compare(struct ldb_context *ldb, > cmp = memcmp(p, > cnk.data, > cnk.length); >+ TALLOC_FREE(cnk_to_free); >+ > if (cmp != 0) { > goto mismatch; > } >@@ -331,15 +371,16 @@ static int ldb_wildcard_compare(struct ldb_context *ldb, > p = memmem((const void *)val.data, val.length, > (const void *)cnk.data, cnk.length); > if (p == NULL) { >+ TALLOC_FREE(cnk_to_free); > goto mismatch; > } > /* move val to the end of the match */ > p += cnk.length; > val.length -= (p - val.data); > val.data = p; >+ TALLOC_FREE(cnk_to_free); > } > c++; >- TALLOC_FREE(cnk.data); > } > > talloc_free(save_p); >@@ -349,7 +390,6 @@ static int ldb_wildcard_compare(struct ldb_context *ldb, > mismatch: > *matched = false; > talloc_free(save_p); >- talloc_free(cnk.data); > return LDB_SUCCESS; > } > >-- >2.25.1 > > >From ba8c9eae1392bf21d3e36530eda1780defd22300 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Mon, 13 Mar 2023 17:20:00 +1300 >Subject: [PATCH 02/35] CVE-2023-0614 selftest: Use setUpClass() to reduce > "make test TESTS=large_ldap" time > >This reduces the elapsed time to 6m from 20m on my laptop. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15332 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz> > >Autobuild-User(master): Andrew Bartlett <abartlet@samba.org> >Autobuild-Date(master): Tue Mar 14 07:16:04 UTC 2023 on atb-devel-224 > >(cherry picked from commit b4a6c054ec6acefacd22cb7230a783d20cb07c05) > >[abartlet@samba.org Included in the security release as this > makes working on the large_ldap test practical by reducing > the elapsed time taken] >--- > source4/dsdb/tests/python/large_ldap.py | 69 +++++++++++++------------ > 1 file changed, 36 insertions(+), 33 deletions(-) > >diff --git a/source4/dsdb/tests/python/large_ldap.py b/source4/dsdb/tests/python/large_ldap.py >index f1fc13939e5..0805119a700 100644 >--- a/source4/dsdb/tests/python/large_ldap.py >+++ b/source4/dsdb/tests/python/large_ldap.py >@@ -66,30 +66,32 @@ creds = credopts.get_credentials(lp) > > class ManyLDAPTest(samba.tests.TestCase): > >- def setUp(self): >- super(ManyLDAPTest, self).setUp() >- self.ldb = SamDB(url, credentials=creds, session_info=system_session(lp), lp=lp) >- self.base_dn = self.ldb.domain_dn() >- self.OU_NAME_MANY="many_ou" + format(random.randint(0, 99999), "05") >- self.ou_dn = ldb.Dn(self.ldb, "ou=" + self.OU_NAME_MANY + "," + str(self.base_dn)) >- >- samba.tests.delete_force(self.ldb, self.ou_dn, >+ @classmethod >+ def setUpClass(cls): >+ super().setUpClass() >+ cls.ldb = SamDB(url, credentials=creds, session_info=system_session(lp), lp=lp) >+ cls.base_dn = self.ldb.domain_dn() >+ cls.OU_NAME_MANY="many_ou" + format(random.randint(0, 99999), "05") >+ cls.ou_dn = ldb.Dn(self.ldb, "ou=" + self.OU_NAME_MANY + "," + str(self.base_dn)) >+ >+ samba.tests.delete_force(cls.ldb, cls.ou_dn, > controls=['tree_delete:1']) > >- self.ldb.add({ >- "dn": self.ou_dn, >+ cls.ldb.add({ >+ "dn": cls.ou_dn, > "objectclass": "organizationalUnit", >- "ou": self.OU_NAME_MANY}) >+ "ou": cls.OU_NAME_MANY}) > > for x in range(2000): >- ou_name = self.OU_NAME_MANY + str(x) >- self.ldb.add({ >- "dn": "ou=" + ou_name + "," + str(self.ou_dn), >+ ou_name = cls.OU_NAME_MANY + str(x) >+ cls.ldb.add({ >+ "dn": "ou=" + ou_name + "," + str(cls.ou_dn), > "objectclass": "organizationalUnit", > "ou": ou_name}) > >- def tearDown(self): >- samba.tests.delete_force(self.ldb, self.ou_dn, >+ @classmethod >+ def tearDownClass(cls): >+ samba.tests.delete_force(cls.ldb, self.ou_dn, > controls=['tree_delete:1']) > > def test_unindexed_iterator_search(self): >@@ -117,34 +119,35 @@ class ManyLDAPTest(samba.tests.TestCase): > > class LargeLDAPTest(samba.tests.TestCase): > >- def setUp(self): >- super(LargeLDAPTest, self).setUp() >- self.ldb = SamDB(url, credentials=creds, session_info=system_session(lp), lp=lp) >- self.base_dn = self.ldb.domain_dn() >- self.USER_NAME = "large_user" + format(random.randint(0, 99999), "05") + "-" >- self.OU_NAME="large_user_ou" + format(random.randint(0, 99999), "05") >- self.ou_dn = ldb.Dn(self.ldb, "ou=" + self.OU_NAME + "," + str(self.base_dn)) >+ @classmethod >+ def setUpClass(cls): >+ cls.ldb = SamDB(url, credentials=creds, session_info=system_session(lp), lp=lp) >+ cls.base_dn = cls.ldb.domain_dn() >+ cls.USER_NAME = "large_user" + format(random.randint(0, 99999), "05") + "-" >+ cls.OU_NAME="large_user_ou" + format(random.randint(0, 99999), "05") >+ cls.ou_dn = ldb.Dn(cls.ldb, "ou=" + cls.OU_NAME + "," + str(cls.base_dn)) > >- samba.tests.delete_force(self.ldb, self.ou_dn, >+ samba.tests.delete_force(cls.ldb, cls.ou_dn, > controls=['tree_delete:1']) > >- self.ldb.add({ >- "dn": self.ou_dn, >+ cls.ldb.add({ >+ "dn": cls.ou_dn, > "objectclass": "organizationalUnit", >- "ou": self.OU_NAME}) >+ "ou": cls.OU_NAME}) > > for x in range(200): >- user_name = self.USER_NAME + format(x, "03") >- self.ldb.add({ >- "dn": "cn=" + user_name + "," + str(self.ou_dn), >+ user_name = cls.USER_NAME + format(x, "03") >+ cls.ldb.add({ >+ "dn": "cn=" + user_name + "," + str(cls.ou_dn), > "objectclass": "user", > "sAMAccountName": user_name, > "jpegPhoto": b'a' * (2 * 1024 * 1024)}) > >- def tearDown(self): >+ @classmethod >+ def tearDownClass(cls): > # Remake the connection for tear-down (old Samba drops the socket) >- self.ldb = SamDB(url, credentials=creds, session_info=system_session(lp), lp=lp) >- samba.tests.delete_force(self.ldb, self.ou_dn, >+ cls.ldb = SamDB(url, credentials=creds, session_info=system_session(lp), lp=lp) >+ samba.tests.delete_force(cls.ldb, cls.ou_dn, > controls=['tree_delete:1']) > > def test_unindexed_iterator_search(self): >-- >2.25.1 > > >From 369234f8d094856360630086914b0c19a2962393 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Fri, 3 Mar 2023 10:31:40 +1300 >Subject: [PATCH 02/35] CVE-2023-0614 dsdb: Alter timeout test in large_ldap.py > to be slower by matching on large objects > >This changes the slow aspect to be the object matching not the filter parsing. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > source4/dsdb/tests/python/large_ldap.py | 17 +++++++++++++++-- > 1 file changed, 15 insertions(+), 2 deletions(-) > >diff --git a/source4/dsdb/tests/python/large_ldap.py b/source4/dsdb/tests/python/large_ldap.py >index 0805119a700..f7569607cb2 100644 >--- a/source4/dsdb/tests/python/large_ldap.py >+++ b/source4/dsdb/tests/python/large_ldap.py >@@ -32,7 +32,7 @@ from samba.tests.subunitrun import SubunitOptions, TestProgram > import samba.getopt as options > > from samba.auth import system_session >-from samba import ldb >+from samba import ldb, sd_utils > from samba.samdb import SamDB > from samba.ndr import ndr_unpack > from samba import gensec >@@ -123,10 +123,13 @@ class LargeLDAPTest(samba.tests.TestCase): > def setUpClass(cls): > cls.ldb = SamDB(url, credentials=creds, session_info=system_session(lp), lp=lp) > cls.base_dn = cls.ldb.domain_dn() >+ >+ cls.sd_utils = sd_utils.SDUtils(cls.ldb) > cls.USER_NAME = "large_user" + format(random.randint(0, 99999), "05") + "-" > cls.OU_NAME="large_user_ou" + format(random.randint(0, 99999), "05") > cls.ou_dn = ldb.Dn(cls.ldb, "ou=" + cls.OU_NAME + "," + str(cls.base_dn)) > >+ > samba.tests.delete_force(cls.ldb, cls.ou_dn, > controls=['tree_delete:1']) > >@@ -249,6 +252,7 @@ class LargeLDAPTest(samba.tests.TestCase): > self.assertGreater(count, count_jpeg) > > def test_timeout(self): >+ > policy_dn = ldb.Dn(self.ldb, > 'CN=Default Query Policy,CN=Query-Policies,' > 'CN=Directory Service,CN=Windows NT,CN=Services,' >@@ -283,9 +286,19 @@ class LargeLDAPTest(samba.tests.TestCase): > session_info=system_session(lp), > lp=lp) > >+ for x in range(200): >+ user_name = self.USER_NAME + format(x, "03") >+ ace = "(OD;;RP;{6bc69afa-7bd9-4184-88f5-28762137eb6a};;S-1-%d)" % x >+ dn = ldb.Dn(self.ldb, "cn=" + user_name + "," + str(self.ou_dn)) >+ >+ # add an ACE that denies access to the above random attr >+ # for a not-existing user. This makes each SD distinct >+ # and so will slow SD parsing. >+ self.sd_utils.dacl_add_ace(dn, ace) >+ > # Create a large search expression that will take a long time to > # evaluate. >- expression = '(anr=l)' * 10000 >+ expression = f'(jpegPhoto=*X*)' * 1000 > expression = f'(|{expression})' > > # Perform the LDAP search. >-- >2.25.1 > > >From 3c3b94a94e18a5eca765597ba1a464c17f002e97 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 27 Jan 2023 07:57:27 +1300 >Subject: [PATCH 03/35] CVE-2023-0614 libcli/security: Make some parameters > const > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> > >[abartlet@samba.org Updated to add const to sec_access_check_ds() >instead of the sec_access_check_ds_implicit_owner() wrapper >found in 4.18 and later] >--- > libcli/security/access_check.c | 10 +++++----- > libcli/security/access_check.h | 2 +- > 2 files changed, 6 insertions(+), 6 deletions(-) > >diff --git a/libcli/security/access_check.c b/libcli/security/access_check.c >index 322f4fdb0c6..6508b6f3fe3 100644 >--- a/libcli/security/access_check.c >+++ b/libcli/security/access_check.c >@@ -394,7 +394,7 @@ NTSTATUS se_file_access_check(const struct security_descriptor *sd, > return NT_STATUS_OK; > } > >-static const struct GUID *get_ace_object_type(struct security_ace *ace) >+static const struct GUID *get_ace_object_type(const struct security_ace *ace) > { > if (ace->object.object.flags & SEC_ACE_OBJECT_TYPE_PRESENT) { > return &ace->object.object.type.type; >@@ -412,7 +412,7 @@ static const struct GUID *get_ace_object_type(struct security_ace *ace) > * rights to the object/attribute > * @returns NT_STATUS_OK, unless access was denied > */ >-static NTSTATUS check_object_specific_access(struct security_ace *ace, >+static NTSTATUS check_object_specific_access(const struct security_ace *ace, > struct object_tree *tree, > bool *grant_access) > { >@@ -505,7 +505,7 @@ NTSTATUS sec_access_check_ds(const struct security_descriptor *sd, > uint32_t access_desired, > uint32_t *access_granted, > struct object_tree *tree, >- struct dom_sid *replace_sid) >+ const struct dom_sid *replace_sid) > { > uint32_t i; > uint32_t bits_remaining; >@@ -556,8 +556,8 @@ NTSTATUS sec_access_check_ds(const struct security_descriptor *sd, > > /* check each ace in turn. */ > for (i=0; bits_remaining && i < sd->dacl->num_aces; i++) { >- struct dom_sid *trustee; >- struct security_ace *ace = &sd->dacl->aces[i]; >+ const struct dom_sid *trustee; >+ const struct security_ace *ace = &sd->dacl->aces[i]; > NTSTATUS status; > bool grant_access = false; > >diff --git a/libcli/security/access_check.h b/libcli/security/access_check.h >index 952589dacb7..04ea2127b61 100644 >--- a/libcli/security/access_check.h >+++ b/libcli/security/access_check.h >@@ -74,7 +74,7 @@ NTSTATUS sec_access_check_ds(const struct security_descriptor *sd, > uint32_t access_desired, > uint32_t *access_granted, > struct object_tree *tree, >- struct dom_sid *replace_sid); >+ const struct dom_sid *replace_sid); > > bool insert_in_object_tree(TALLOC_CTX *mem_ctx, > const struct GUID *guid, >-- >2.25.1 > > >From ed38fcf9e2c3143f15f4d7001e7a849b31ccfb5d Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 7 Feb 2023 09:29:51 +1300 >Subject: [PATCH 04/35] CVE-2023-0614 s4:dsdb: Use talloc_get_type_abort() more > consistently > >It is better to explicitly abort than to dereference a NULL pointer or >try to read data cast to the wrong type. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/dsdb/samdb/ldb_modules/acl_read.c | 4 ++-- > source4/dsdb/samdb/ldb_modules/acl_util.c | 2 +- > source4/dsdb/samdb/ldb_modules/linked_attributes.c | 2 +- > source4/dsdb/samdb/ldb_modules/password_hash.c | 2 +- > 4 files changed, 5 insertions(+), 5 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/acl_read.c b/source4/dsdb/samdb/ldb_modules/acl_read.c >index b221dcde445..16a1927183c 100644 >--- a/source4/dsdb/samdb/ldb_modules/acl_read.c >+++ b/source4/dsdb/samdb/ldb_modules/acl_read.c >@@ -268,7 +268,7 @@ static int aclread_get_sd_from_ldb_message(struct aclread_context *ac, > struct ldb_message_element *sd_element; > struct ldb_context *ldb = ldb_module_get_ctx(ac->module); > struct aclread_private *private_data >- = talloc_get_type(ldb_module_get_private(ac->module), >+ = talloc_get_type_abort(ldb_module_get_private(ac->module), > struct aclread_private); > enum ndr_err_code ndr_err; > >@@ -568,7 +568,7 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > const struct dsdb_class *objectclass; > bool suppress_result = false; > >- ac = talloc_get_type(req->context, struct aclread_context); >+ ac = talloc_get_type_abort(req->context, struct aclread_context); > ldb = ldb_module_get_ctx(ac->module); > if (!ares) { > return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR ); >diff --git a/source4/dsdb/samdb/ldb_modules/acl_util.c b/source4/dsdb/samdb/ldb_modules/acl_util.c >index 08a95c1c310..ebaac067b93 100644 >--- a/source4/dsdb/samdb/ldb_modules/acl_util.c >+++ b/source4/dsdb/samdb/ldb_modules/acl_util.c >@@ -298,7 +298,7 @@ uint32_t dsdb_request_sd_flags(struct ldb_request *req, bool *explicit) > > sd_control = ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID); > if (sd_control != NULL && sd_control->data != NULL) { >- struct ldb_sd_flags_control *sdctr = (struct ldb_sd_flags_control *)sd_control->data; >+ struct ldb_sd_flags_control *sdctr = talloc_get_type_abort(sd_control->data, struct ldb_sd_flags_control); > > sd_flags = sdctr->secinfo_flags; > >diff --git a/source4/dsdb/samdb/ldb_modules/linked_attributes.c b/source4/dsdb/samdb/ldb_modules/linked_attributes.c >index 5ef075f2037..317df9d3e0e 100644 >--- a/source4/dsdb/samdb/ldb_modules/linked_attributes.c >+++ b/source4/dsdb/samdb/ldb_modules/linked_attributes.c >@@ -104,7 +104,7 @@ static int handle_verify_name_control(TALLOC_CTX *ctx, struct ldb_context *ldb, > * If we are a GC let's remove the control, > * if there is a specified GC check that is us. > */ >- struct ldb_verify_name_control *lvnc = (struct ldb_verify_name_control *)control->data; >+ struct ldb_verify_name_control *lvnc = talloc_get_type_abort(control->data, struct ldb_verify_name_control); > if (samdb_is_gc(ldb)) { > /* Because we can't easily talloc a struct ldb_dn*/ > struct ldb_dn **dn = talloc_array(ctx, struct ldb_dn *, 1); >diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c >index fb4deeae9f5..da49038d6fc 100644 >--- a/source4/dsdb/samdb/ldb_modules/password_hash.c >+++ b/source4/dsdb/samdb/ldb_modules/password_hash.c >@@ -3887,7 +3887,7 @@ static void ph_apply_controls(struct ph_context *ac) > ctrl = ldb_request_get_control(ac->req, > DSDB_CONTROL_PASSWORD_CHANGE_OID); > if (ctrl != NULL) { >- ac->change = (struct dsdb_control_password_change *) ctrl->data; >+ ac->change = talloc_get_type_abort(ctrl->data, struct dsdb_control_password_change); > > /* Mark the "change" control as uncritical (done) */ > ctrl->critical = false; >-- >2.25.1 > > >From a091d18e6fc99d79a78b653c340c4ef52fc2e3b1 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 27 Jan 2023 08:00:32 +1300 >Subject: [PATCH 05/35] CVE-2023-0614 s4-acl: Make some parameters const > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> > >[abartlet@samba.org Adapted to code without newer > acl_check_access_on_attribute_implicit_owner name] >--- > source4/dsdb/samdb/ldb_modules/acl_util.c | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/acl_util.c b/source4/dsdb/samdb/ldb_modules/acl_util.c >index ebaac067b93..a7251407f44 100644 >--- a/source4/dsdb/samdb/ldb_modules/acl_util.c >+++ b/source4/dsdb/samdb/ldb_modules/acl_util.c >@@ -97,8 +97,8 @@ int dsdb_module_check_access_on_dn(struct ldb_module *module, > > int acl_check_access_on_attribute(struct ldb_module *module, > TALLOC_CTX *mem_ctx, >- struct security_descriptor *sd, >- struct dom_sid *rp_sid, >+ const struct security_descriptor *sd, >+ const struct dom_sid *rp_sid, > uint32_t access_mask, > const struct dsdb_attribute *attr, > const struct dsdb_class *objectclass) >-- >2.25.1 > > >From 41d8d2dd8528a4e7ce08119ee7d6d80bc9d50dde Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 27 Jan 2023 08:28:36 +1300 >Subject: [PATCH 06/35] CVE-2023-0614 ldb: Add functions for handling > inaccessible message elements > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > lib/ldb/common/ldb_msg.c | 26 ++++++++++++++++++++++++++ > lib/ldb/include/ldb_module.h | 4 ++++ > 2 files changed, 30 insertions(+) > >diff --git a/lib/ldb/common/ldb_msg.c b/lib/ldb/common/ldb_msg.c >index 9cd7998e21c..cbc7e32b2ba 100644 >--- a/lib/ldb/common/ldb_msg.c >+++ b/lib/ldb/common/ldb_msg.c >@@ -795,6 +795,32 @@ int ldb_msg_element_compare_name(struct ldb_message_element *el1, > return ldb_attr_cmp(el1->name, el2->name); > } > >+void ldb_msg_element_mark_inaccessible(struct ldb_message_element *el) >+{ >+ el->flags |= LDB_FLAG_INTERNAL_INACCESSIBLE_ATTRIBUTE; >+} >+ >+bool ldb_msg_element_is_inaccessible(const struct ldb_message_element *el) >+{ >+ return (el->flags & LDB_FLAG_INTERNAL_INACCESSIBLE_ATTRIBUTE) != 0; >+} >+ >+void ldb_msg_remove_inaccessible(struct ldb_message *msg) >+{ >+ unsigned i; >+ unsigned num_del = 0; >+ >+ for (i = 0; i < msg->num_elements; ++i) { >+ if (ldb_msg_element_is_inaccessible(&msg->elements[i])) { >+ ++num_del; >+ } else if (num_del) { >+ msg->elements[i - num_del] = msg->elements[i]; >+ } >+ } >+ >+ msg->num_elements -= num_del; >+} >+ > /* > convenience functions to return common types from a message > these return the first value if the attribute is multi-valued >diff --git a/lib/ldb/include/ldb_module.h b/lib/ldb/include/ldb_module.h >index 4c7c85a17f0..8481fd3991a 100644 >--- a/lib/ldb/include/ldb_module.h >+++ b/lib/ldb/include/ldb_module.h >@@ -513,6 +513,10 @@ struct ldb_extended_match_rule > int ldb_register_extended_match_rule(struct ldb_context *ldb, > const struct ldb_extended_match_rule *rule); > >+void ldb_msg_element_mark_inaccessible(struct ldb_message_element *el); >+bool ldb_msg_element_is_inaccessible(const struct ldb_message_element *el); >+void ldb_msg_remove_inaccessible(struct ldb_message *msg); >+ > /* > * these pack/unpack functions are exposed in the library for use by > * ldb tools like ldbdump and for use in tests, >-- >2.25.1 > > >From 8024d15f14a1fa3a63033ac9c2daad68d6717ec7 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 27 Jan 2023 08:29:33 +1300 >Subject: [PATCH 07/35] CVE-2023-0614 s4-acl: Use ldb functions for handling > inaccessible message elements > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/dsdb/samdb/ldb_modules/acl_read.c | 62 ++++------------------- > 1 file changed, 10 insertions(+), 52 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/acl_read.c b/source4/dsdb/samdb/ldb_modules/acl_read.c >index 16a1927183c..8814a816797 100644 >--- a/source4/dsdb/samdb/ldb_modules/acl_read.c >+++ b/source4/dsdb/samdb/ldb_modules/acl_read.c >@@ -70,14 +70,6 @@ struct aclread_private { > struct ldb_val sd_cached_blob; > }; > >-static void aclread_mark_inaccesslible(struct ldb_message_element *el) { >- el->flags |= LDB_FLAG_INTERNAL_INACCESSIBLE_ATTRIBUTE; >-} >- >-static bool aclread_is_inaccessible(struct ldb_message_element *el) { >- return el->flags & LDB_FLAG_INTERNAL_INACCESSIBLE_ATTRIBUTE; >-} >- > /* > * the object has a parent, so we have to check for visibility > * >@@ -557,11 +549,9 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > { > struct ldb_context *ldb; > struct aclread_context *ac; >- struct ldb_message *ret_msg; > struct ldb_message *msg; > int ret; >- size_t num_of_attrs = 0; >- unsigned int i, k = 0; >+ unsigned int i; > struct security_descriptor *sd = NULL; > struct dom_sid *sid = NULL; > TALLOC_CTX *tmp_ctx; >@@ -651,26 +641,26 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > msg->elements[i].name) == 0; > /* these attributes were added to perform access checks and must be removed */ > if (is_objectsid && ac->added_objectSid) { >- aclread_mark_inaccesslible(&msg->elements[i]); >+ ldb_msg_element_mark_inaccessible(&msg->elements[i]); > continue; > } > if (is_instancetype && ac->added_instanceType) { >- aclread_mark_inaccesslible(&msg->elements[i]); >+ ldb_msg_element_mark_inaccessible(&msg->elements[i]); > continue; > } > if (is_objectclass && ac->added_objectClass) { >- aclread_mark_inaccesslible(&msg->elements[i]); >+ ldb_msg_element_mark_inaccessible(&msg->elements[i]); > continue; > } > if (is_sd && ac->added_nTSecurityDescriptor) { >- aclread_mark_inaccesslible(&msg->elements[i]); >+ ldb_msg_element_mark_inaccessible(&msg->elements[i]); > continue; > } > > access_mask = get_attr_access_mask(attr, ac->sd_flags); > > if (access_mask == 0) { >- aclread_mark_inaccesslible(&msg->elements[i]); >+ ldb_msg_element_mark_inaccessible(&msg->elements[i]); > continue; > } > >@@ -714,7 +704,7 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > return LDB_SUCCESS; > } > } else { >- aclread_mark_inaccesslible(&msg->elements[i]); >+ ldb_msg_element_mark_inaccessible(&msg->elements[i]); > } > } else if (ret != LDB_SUCCESS) { > ldb_debug_set(ldb, LDB_DEBUG_FATAL, >@@ -757,44 +747,12 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > } > } > >- for (i=0; i < msg->num_elements; i++) { >- if (!aclread_is_inaccessible(&msg->elements[i])) { >- num_of_attrs++; >- } >- } >- /*create a new message to return*/ >- ret_msg = ldb_msg_new(ac->req); >- ret_msg->dn = msg->dn; >- talloc_steal(ret_msg, msg->dn); >- ret_msg->num_elements = num_of_attrs; >- if (num_of_attrs > 0) { >- ret_msg->elements = talloc_array(ret_msg, >- struct ldb_message_element, >- num_of_attrs); >- if (ret_msg->elements == NULL) { >- return ldb_oom(ldb); >- } >- for (i=0; i < msg->num_elements; i++) { >- bool to_remove = aclread_is_inaccessible(&msg->elements[i]); >- if (!to_remove) { >- ret_msg->elements[k] = msg->elements[i]; >- talloc_steal(ret_msg->elements, msg->elements[i].name); >- talloc_steal(ret_msg->elements, msg->elements[i].values); >- k++; >- } >- } >- /* >- * This should not be needed, but some modules >- * may allocate values on the wrong context... >- */ >- talloc_steal(ret_msg->elements, msg); >- } else { >- ret_msg->elements = NULL; >- } >+ ldb_msg_remove_inaccessible(msg); >+ > talloc_free(tmp_ctx); > > ac->num_entries++; >- return ldb_module_send_entry(ac->req, ret_msg, ares->controls); >+ return ldb_module_send_entry(ac->req, msg, ares->controls); > case LDB_REPLY_REFERRAL: > return ldb_module_send_referral(ac->req, ares->referral); > case LDB_REPLY_DONE: >-- >2.25.1 > > >From af01be323069a9bdd2b6955621450d6ed8594581 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Wed, 15 Feb 2023 12:34:51 +1300 >Subject: [PATCH 08/35] CVE-2023-0614 ldb:tests: Ensure ldb_val data is > zero-terminated > >If the value of an ldb message element is not zero-terminated, calling >ldb_msg_find_attr_as_string() will cause the function to read off the >end of the buffer in an attempt to verify that the value is >zero-terminated. This can cause unexpected behaviour and make the test >randomly fail. > >To avoid this, we must have a terminating null byte that is *not* >counted as part of the length, and so we must calculate the length with >strlen() rather than sizeof. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > lib/ldb/tests/ldb_filter_attrs_test.c | 171 +++++++++++++------------- > 1 file changed, 86 insertions(+), 85 deletions(-) > >diff --git a/lib/ldb/tests/ldb_filter_attrs_test.c b/lib/ldb/tests/ldb_filter_attrs_test.c >index 7d555e0da2e..442d9c77ed2 100644 >--- a/lib/ldb/tests/ldb_filter_attrs_test.c >+++ b/lib/ldb/tests/ldb_filter_attrs_test.c >@@ -36,6 +36,7 @@ > #include <stdarg.h> > #include <stddef.h> > #include <stdint.h> >+#include <string.h> > #include <setjmp.h> > #include <cmocka.h> > >@@ -96,10 +97,10 @@ static void test_filter_attrs_one_attr_matched(void **state) > > const char *attrs[] = {"foo", NULL}; > >- uint8_t value[] = "The value.......end"; >+ char value[] = "The value.......end"; > struct ldb_val value_1 = { >- .data = value, >- .length = (sizeof(value)) >+ .data = (uint8_t *)value, >+ .length = strlen(value) > }; > struct ldb_message_element element_1 = { > .name = "foo", >@@ -130,9 +131,9 @@ static void test_filter_attrs_one_attr_matched(void **state) > assert_string_equal(filtered_msg->elements[0].name, "foo"); > assert_int_equal(filtered_msg->elements[0].num_values, 1); > assert_int_equal(filtered_msg->elements[0].values[0].length, >- sizeof(value)); >+ strlen(value)); > assert_memory_equal(filtered_msg->elements[0].values[0].data, >- value, sizeof(value)); >+ value, strlen(value)); > } > > /* >@@ -148,10 +149,10 @@ static void test_filter_attrs_one_attr_matched_of_many(void **state) > > const char *attrs[] = {"foo", "bar", "baz", NULL}; > >- uint8_t value[] = "The value.......end"; >+ char value[] = "The value.......end"; > struct ldb_val value_1 = { >- .data = value, >- .length = (sizeof(value)) >+ .data = (uint8_t *)value, >+ .length = strlen(value) > }; > struct ldb_message_element element_1 = { > .name = "foo", >@@ -182,9 +183,9 @@ static void test_filter_attrs_one_attr_matched_of_many(void **state) > assert_string_equal(filtered_msg->elements[0].name, "foo"); > assert_int_equal(filtered_msg->elements[0].num_values, 1); > assert_int_equal(filtered_msg->elements[0].values[0].length, >- sizeof(value)); >+ strlen(value)); > assert_memory_equal(filtered_msg->elements[0].values[0].data, >- value, sizeof(value)); >+ value, strlen(value)); > } > > /* >@@ -201,15 +202,15 @@ static void test_filter_attrs_two_attr_matched_attrs(void **state) > /* deliberatly the other order */ > const char *attrs[] = {"bar", "foo", NULL}; > >- uint8_t value1[] = "The value.......end"; >- uint8_t value2[] = "The value..MUST.end"; >+ char value1[] = "The value.......end"; >+ char value2[] = "The value..MUST.end"; > struct ldb_val value_1 = { >- .data = value1, >- .length = (sizeof(value1)) >+ .data = (uint8_t *)value1, >+ .length = strlen(value1) > }; > struct ldb_val value_2 = { >- .data = value2, >- .length = (sizeof(value2)) >+ .data = (uint8_t *)value2, >+ .length = strlen(value2) > }; > > /* foo and bar are the other order to in attrs */ >@@ -251,15 +252,15 @@ static void test_filter_attrs_two_attr_matched_attrs(void **state) > assert_string_equal(filtered_msg->elements[0].name, "foo"); > assert_int_equal(filtered_msg->elements[0].num_values, 1); > assert_int_equal(filtered_msg->elements[0].values[0].length, >- sizeof(value1)); >+ strlen(value1)); > assert_memory_equal(filtered_msg->elements[0].values[0].data, >- value1, sizeof(value1)); >+ value1, strlen(value1)); > assert_string_equal(filtered_msg->elements[1].name, "bar"); > assert_int_equal(filtered_msg->elements[1].num_values, 1); > assert_int_equal(filtered_msg->elements[1].values[0].length, >- sizeof(value2)); >+ strlen(value2)); > assert_memory_equal(filtered_msg->elements[1].values[0].data, >- value2, sizeof(value2)); >+ value2, strlen(value2)); > } > > /* >@@ -276,15 +277,15 @@ static void test_filter_attrs_two_attr_matched_one_attr(void **state) > /* deliberatly the other order */ > const char *attrs[] = {"bar", NULL}; > >- uint8_t value1[] = "The value.......end"; >- uint8_t value2[] = "The value..MUST.end"; >+ char value1[] = "The value.......end"; >+ char value2[] = "The value..MUST.end"; > struct ldb_val value_1 = { >- .data = value1, >- .length = (sizeof(value1)) >+ .data = (uint8_t *)value1, >+ .length = strlen(value1) > }; > struct ldb_val value_2 = { >- .data = value2, >- .length = (sizeof(value2)) >+ .data = (uint8_t *)value2, >+ .length = strlen(value2) > }; > > /* foo and bar are the other order to in attrs */ >@@ -326,9 +327,9 @@ static void test_filter_attrs_two_attr_matched_one_attr(void **state) > assert_string_equal(filtered_msg->elements[0].name, "bar"); > assert_int_equal(filtered_msg->elements[0].num_values, 1); > assert_int_equal(filtered_msg->elements[0].values[0].length, >- sizeof(value2)); >+ strlen(value2)); > assert_memory_equal(filtered_msg->elements[0].values[0].data, >- value2, sizeof(value2)); >+ value2, strlen(value2)); > } > > /* >@@ -345,15 +346,15 @@ static void test_filter_attrs_two_dup_attr_matched_one_attr(void **state) > /* deliberatly the other order */ > const char *attrs[] = {"bar", NULL}; > >- uint8_t value1[] = "The value.......end"; >- uint8_t value2[] = "The value..MUST.end"; >+ char value1[] = "The value.......end"; >+ char value2[] = "The value..MUST.end"; > struct ldb_val value_1 = { >- .data = value1, >- .length = (sizeof(value1)) >+ .data = (uint8_t *)value1, >+ .length = strlen(value1) > }; > struct ldb_val value_2 = { >- .data = value2, >- .length = (sizeof(value2)) >+ .data = (uint8_t *)value2, >+ .length = strlen(value2) > }; > > /* foo and bar are the other order to in attrs */ >@@ -400,15 +401,15 @@ static void test_filter_attrs_two_dup_attr_matched_dup(void **state) > > const char *attrs[] = {"bar", "bar", NULL}; > >- uint8_t value1[] = "The value.......end"; >- uint8_t value2[] = "The value..MUST.end"; >+ char value1[] = "The value.......end"; >+ char value2[] = "The value..MUST.end"; > struct ldb_val value_1 = { >- .data = value1, >- .length = (sizeof(value1)) >+ .data = (uint8_t *)value1, >+ .length = strlen(value1) > }; > struct ldb_val value_2 = { >- .data = value2, >- .length = (sizeof(value2)) >+ .data = (uint8_t *)value2, >+ .length = strlen(value2) > }; > > /* foo and bar are the other order to in attrs */ >@@ -445,15 +446,15 @@ static void test_filter_attrs_two_dup_attr_matched_dup(void **state) > assert_string_equal(filtered_msg->elements[0].name, "bar"); > assert_int_equal(filtered_msg->elements[0].num_values, 1); > assert_int_equal(filtered_msg->elements[0].values[0].length, >- sizeof(value1)); >+ strlen(value1)); > assert_memory_equal(filtered_msg->elements[0].values[0].data, >- value1, sizeof(value1)); >+ value1, strlen(value1)); > assert_string_equal(filtered_msg->elements[1].name, "bar"); > assert_int_equal(filtered_msg->elements[1].num_values, 1); > assert_int_equal(filtered_msg->elements[1].values[0].length, >- sizeof(value2)); >+ strlen(value2)); > assert_memory_equal(filtered_msg->elements[1].values[0].data, >- value2, sizeof(value2)); >+ value2, strlen(value2)); > } > > /* >@@ -469,15 +470,15 @@ static void test_filter_attrs_two_dup_attr_matched_one_of_two(void **state) > > const char *attrs[] = {"bar", "foo", NULL}; > >- uint8_t value1[] = "The value.......end"; >- uint8_t value2[] = "The value..MUST.end"; >+ char value1[] = "The value.......end"; >+ char value2[] = "The value..MUST.end"; > struct ldb_val value_1 = { >- .data = value1, >- .length = (sizeof(value1)) >+ .data = (uint8_t *)value1, >+ .length = strlen(value1) > }; > struct ldb_val value_2 = { >- .data = value2, >- .length = (sizeof(value2)) >+ .data = (uint8_t *)value2, >+ .length = strlen(value2) > }; > > /* foo and bar are the other order to in attrs */ >@@ -514,15 +515,15 @@ static void test_filter_attrs_two_dup_attr_matched_one_of_two(void **state) > assert_string_equal(filtered_msg->elements[0].name, "bar"); > assert_int_equal(filtered_msg->elements[0].num_values, 1); > assert_int_equal(filtered_msg->elements[0].values[0].length, >- sizeof(value1)); >+ strlen(value1)); > assert_memory_equal(filtered_msg->elements[0].values[0].data, >- value1, sizeof(value1)); >+ value1, strlen(value1)); > assert_string_equal(filtered_msg->elements[1].name, "bar"); > assert_int_equal(filtered_msg->elements[1].num_values, 1); > assert_int_equal(filtered_msg->elements[1].values[0].length, >- sizeof(value2)); >+ strlen(value2)); > assert_memory_equal(filtered_msg->elements[1].values[0].data, >- value2, sizeof(value2)); >+ value2, strlen(value2)); > } > > /* >@@ -538,15 +539,15 @@ static void test_filter_attrs_two_dup_attr_matched_star(void **state) > > const char *attrs[] = {"*", "foo", NULL}; > >- uint8_t value1[] = "The value.......end"; >- uint8_t value2[] = "The value..MUST.end"; >+ char value1[] = "The value.......end"; >+ char value2[] = "The value..MUST.end"; > struct ldb_val value_1 = { >- .data = value1, >- .length = (sizeof(value1)) >+ .data = (uint8_t *)value1, >+ .length = strlen(value1) > }; > struct ldb_val value_2 = { >- .data = value2, >- .length = (sizeof(value2)) >+ .data = (uint8_t *)value2, >+ .length = strlen(value2) > }; > > /* foo and bar are the other order to in attrs */ >@@ -586,15 +587,15 @@ static void test_filter_attrs_two_dup_attr_matched_star(void **state) > assert_string_equal(filtered_msg->elements[0].name, "bar"); > assert_int_equal(filtered_msg->elements[0].num_values, 1); > assert_int_equal(filtered_msg->elements[0].values[0].length, >- sizeof(value1)); >+ strlen(value1)); > assert_memory_equal(filtered_msg->elements[0].values[0].data, >- value1, sizeof(value1)); >+ value1, strlen(value1)); > assert_string_equal(filtered_msg->elements[1].name, "bar"); > assert_int_equal(filtered_msg->elements[1].num_values, 1); > assert_int_equal(filtered_msg->elements[1].values[0].length, >- sizeof(value2)); >+ strlen(value2)); > assert_memory_equal(filtered_msg->elements[1].values[0].data, >- value2, sizeof(value2)); >+ value2, strlen(value2)); > /* > * assert the ldb_filter_attrs does not modify filtered_msg.dn > * in this case >@@ -619,10 +620,10 @@ static void test_filter_attrs_one_attr_matched_star(void **state) > > const char *attrs[] = {"*", NULL}; > >- uint8_t value[] = "The value.......end"; >+ char value[] = "The value.......end"; > struct ldb_val value_1 = { >- .data = value, >- .length = (sizeof(value)) >+ .data = (uint8_t *)value, >+ .length = strlen(value) > }; > struct ldb_message_element element_1 = { > .name = "foo", >@@ -676,15 +677,15 @@ static void test_filter_attrs_two_attr_matched_star(void **state) > > const char *attrs[] = {"*", NULL}; > >- uint8_t value1[] = "The value.......end"; >- uint8_t value2[] = "The value..MUST.end"; >+ char value1[] = "The value.......end"; >+ char value2[] = "The value..MUST.end"; > struct ldb_val value_1 = { >- .data = value1, >- .length = (sizeof(value1)) >+ .data = (uint8_t *)value1, >+ .length = strlen(value1) > }; > struct ldb_val value_2 = { >- .data = value2, >- .length = (sizeof(value2)) >+ .data = (uint8_t *)value2, >+ .length = strlen(value2) > }; > struct ldb_message_element elements[] = { > { >@@ -750,10 +751,10 @@ static void test_filter_attrs_one_attr_matched_star_no_dn(void **state) > > const char *attrs[] = {"*", NULL}; > >- uint8_t value[] = "The value.......end"; >+ char value[] = "The value.......end"; > struct ldb_val value_1 = { >- .data = value, >- .length = (sizeof(value)) >+ .data = (uint8_t *)value, >+ .length = strlen(value) > }; > struct ldb_message_element element_1 = { > .name = "foo", >@@ -789,10 +790,10 @@ static void test_filter_attrs_one_attr_matched_star_dn(void **state) > > const char *attrs[] = {"*", "distinguishedName", NULL}; > >- uint8_t value[] = "The value.......end"; >+ char value[] = "The value.......end"; > struct ldb_val value_1 = { >- .data = value, >- .length = (sizeof(value)) >+ .data = (uint8_t *)value, >+ .length = strlen(value) > }; > struct ldb_message_element element_1 = { > .name = "foo", >@@ -844,10 +845,10 @@ static void test_filter_attrs_one_attr_matched_dn(void **state) > > const char *attrs[] = {"distinguishedName", NULL}; > >- uint8_t value[] = "The value.......end"; >+ char value[] = "The value.......end"; > struct ldb_val value_1 = { >- .data = value, >- .length = (sizeof(value)) >+ .data = (uint8_t *)value, >+ .length = strlen(value) > }; > struct ldb_message_element element_1 = { > .name = "foo", >@@ -894,10 +895,10 @@ static void test_filter_attrs_one_attr_empty_list(void **state) > > const char *attrs[] = {NULL}; > >- uint8_t value[] = "The value.......end"; >+ char value[] = "The value.......end"; > struct ldb_val value_1 = { >- .data = value, >- .length = (sizeof(value)) >+ .data = (uint8_t *)value, >+ .length = strlen(value) > }; > struct ldb_message_element element_1 = { > .name = "foo", >-- >2.25.1 > > >From 6fa11fd15f9f05a897311ee5de9b92d74ad07edd Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Wed, 15 Feb 2023 14:08:57 +1300 >Subject: [PATCH 09/35] CVE-2023-0614 ldb:tests: Ensure all tests are accounted > for > >Add ldb_filter_attrs_test to the list of tests so that it actually gets >run. > >Remove a duplicate ldb_msg_test that was accidentally added in commit >5ca90e758ade97fb5e335029c7a1768094e70564. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > lib/ldb/wscript | 5 +++-- > 1 file changed, 3 insertions(+), 2 deletions(-) > >diff --git a/lib/ldb/wscript b/lib/ldb/wscript >index 16e5682148a..00af9dce79f 100644 >--- a/lib/ldb/wscript >+++ b/lib/ldb/wscript >@@ -627,7 +627,6 @@ def test(ctx): > 'ldb_msg_test', > 'ldb_tdb_mod_op_test', > 'ldb_tdb_guid_mod_op_test', >- 'ldb_msg_test', > 'ldb_tdb_kv_ops_test', > 'ldb_tdb_test', > 'ldb_match_test', >@@ -637,7 +636,9 @@ def test(ctx): > # on operations which the TDB backend does not currently > # support > # 'ldb_key_value_sub_txn_tdb_test' >- 'ldb_parse_test'] >+ 'ldb_parse_test', >+ 'ldb_filter_attrs_test', >+ ] > > # if LIB_LDAP and LIB_LBER defined, then we can test ldb_ldap backend > # behavior regression for bz#14413 >-- >2.25.1 > > >From 5c7b3fa6b68e34198cd60495144e122b1b817650 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 3 Mar 2023 17:23:42 +1300 >Subject: [PATCH 10/35] CVE-2023-0614 ldb: Add function to take ownership of an > ldb message > >Many places in Samba depend upon various components of an ldb message >being talloc allocated, and hence able to be used as talloc contexts. >The elements and values of an unpacked ldb message point to unowned data >inside the memory-mapped database, and this function ensures that such >messages have talloc ownership of said elements and values. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > lib/ldb/common/ldb_pack.c | 41 ++++++++++++++++++++++++++++++++++++ > lib/ldb/include/ldb_module.h | 4 ++++ > 2 files changed, 45 insertions(+) > >diff --git a/lib/ldb/common/ldb_pack.c b/lib/ldb/common/ldb_pack.c >index e7dd364008a..028d96a619a 100644 >--- a/lib/ldb/common/ldb_pack.c >+++ b/lib/ldb/common/ldb_pack.c >@@ -690,6 +690,7 @@ static int ldb_unpack_data_flags_v1(struct ldb_context *ldb, > element->values = NULL; > if ((flags & LDB_UNPACK_DATA_FLAG_NO_VALUES_ALLOC) && element->num_values == 1) { > element->values = &ldb_val_single_array[nelem]; >+ element->flags |= LDB_FLAG_INTERNAL_SHARED_VALUES; > } else if (element->num_values != 0) { > element->values = talloc_array(message->elements, > struct ldb_val, >@@ -932,6 +933,7 @@ static int ldb_unpack_data_flags_v2(struct ldb_context *ldb, > if ((flags & LDB_UNPACK_DATA_FLAG_NO_VALUES_ALLOC) && > element->num_values == 1) { > element->values = &ldb_val_single_array[nelem]; >+ element->flags |= LDB_FLAG_INTERNAL_SHARED_VALUES; > } else if (element->num_values != 0) { > element->values = talloc_array(message->elements, > struct ldb_val, >@@ -1259,3 +1261,42 @@ failed: > TALLOC_FREE(filtered_msg->elements); > return -1; > } >+ >+/* Have an unpacked ldb message take talloc ownership of its elements. */ >+int ldb_msg_elements_take_ownership(struct ldb_message *msg) >+{ >+ unsigned int i = 0; >+ >+ for (i = 0; i < msg->num_elements; i++) { >+ struct ldb_message_element *el = &msg->elements[i]; >+ const char *name; >+ unsigned int j; >+ >+ name = talloc_strdup(msg->elements, >+ el->name); >+ if (name == NULL) { >+ return -1; >+ } >+ el->name = name; >+ >+ if (el->flags & LDB_FLAG_INTERNAL_SHARED_VALUES) { >+ struct ldb_val *values = talloc_memdup(msg->elements, el->values, >+ sizeof(struct ldb_val) * el->num_values); >+ if (values == NULL) { >+ return -1; >+ } >+ el->values = values; >+ el->flags &= ~LDB_FLAG_INTERNAL_SHARED_VALUES; >+ } >+ >+ for (j = 0; j < el->num_values; j++) { >+ struct ldb_val val = ldb_val_dup(el->values, &el->values[j]); >+ if (val.data == NULL && el->values[j].length != 0) { >+ return -1; >+ } >+ el->values[j] = val; >+ } >+ } >+ >+ return LDB_SUCCESS; >+} >diff --git a/lib/ldb/include/ldb_module.h b/lib/ldb/include/ldb_module.h >index 8481fd3991a..8c7f33496fb 100644 >--- a/lib/ldb/include/ldb_module.h >+++ b/lib/ldb/include/ldb_module.h >@@ -542,6 +542,10 @@ int ldb_filter_attrs(struct ldb_context *ldb, > const struct ldb_message *msg, > const char *const *attrs, > struct ldb_message *filtered_msg); >+ >+/* Have an unpacked ldb message take talloc ownership of its elements. */ >+int ldb_msg_elements_take_ownership(struct ldb_message *msg); >+ > /* > * Unpack a ldb message from a linear buffer in ldb_val > * >-- >2.25.1 > > >From 5bcc311e85d7c965368b64fdb84deb04208250c5 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 3 Mar 2023 17:26:04 +1300 >Subject: [PATCH 11/35] CVE-2023-0614 ldb: Add function to remove excess > capacity from an ldb message > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> > >[abartlet@samba.org Adapted to conflict from lack of new >ldb_ascii_toupper() in ldb_private.h] >--- > lib/ldb/common/ldb_msg.c | 16 ++++++++++++++++ > lib/ldb/include/ldb_private.h | 3 +++ > 2 files changed, 19 insertions(+) > >diff --git a/lib/ldb/common/ldb_msg.c b/lib/ldb/common/ldb_msg.c >index cbc7e32b2ba..2ea2cce2e83 100644 >--- a/lib/ldb/common/ldb_msg.c >+++ b/lib/ldb/common/ldb_msg.c >@@ -1497,6 +1497,22 @@ void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr) > } > } > >+/* Reallocate elements to drop any excess capacity. */ >+void ldb_msg_shrink_to_fit(struct ldb_message *msg) >+{ >+ if (msg->num_elements > 0) { >+ struct ldb_message_element *elements = talloc_realloc(msg, >+ msg->elements, >+ struct ldb_message_element, >+ msg->num_elements); >+ if (elements != NULL) { >+ msg->elements = elements; >+ } >+ } else { >+ TALLOC_FREE(msg->elements); >+ } >+} >+ > /* > return a LDAP formatted GeneralizedTime string > */ >diff --git a/lib/ldb/include/ldb_private.h b/lib/ldb/include/ldb_private.h >index 4deb24691ca..338e71def6d 100644 >--- a/lib/ldb/include/ldb_private.h >+++ b/lib/ldb/include/ldb_private.h >@@ -317,4 +317,7 @@ int ldb_match_message(struct ldb_context *ldb, > const struct ldb_parse_tree *tree, > enum ldb_scope scope, bool *matched); > >+/* Reallocate elements to drop any excess capacity. */ >+void ldb_msg_shrink_to_fit(struct ldb_message *msg); >+ > #endif >-- >2.25.1 > > >From d060e20ca093792158cc94368bb8d1c0f4d1526f Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 3 Mar 2023 17:27:38 +1300 >Subject: [PATCH 12/35] CVE-2023-0614 ldb: Add function to add > distinguishedName to message > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> > >[abartlet@samba.org Adapted to conflict from lack of new >ldb_ascii_toupper() in ldb_private.h] >--- > lib/ldb/common/ldb_pack.c | 6 +++--- > lib/ldb/include/ldb_private.h | 5 +++++ > 2 files changed, 8 insertions(+), 3 deletions(-) > >diff --git a/lib/ldb/common/ldb_pack.c b/lib/ldb/common/ldb_pack.c >index 028d96a619a..b0b0d64a5ba 100644 >--- a/lib/ldb/common/ldb_pack.c >+++ b/lib/ldb/common/ldb_pack.c >@@ -1098,7 +1098,7 @@ int ldb_unpack_data(struct ldb_context *ldb, > /* > add the special distinguishedName element > */ >-static int msg_add_distinguished_name(struct ldb_message *msg) >+int ldb_msg_add_distinguished_name(struct ldb_message *msg) > { > const char *dn_attr = "distinguishedName"; > char *dn = NULL; >@@ -1158,7 +1158,7 @@ int ldb_filter_attrs(struct ldb_context *ldb, > > /* Shortcuts for the simple cases */ > } else if (add_dn && i == 1) { >- if (msg_add_distinguished_name(filtered_msg) != 0) { >+ if (ldb_msg_add_distinguished_name(filtered_msg) != 0) { > goto failed; > } > return 0; >@@ -1238,7 +1238,7 @@ int ldb_filter_attrs(struct ldb_context *ldb, > filtered_msg->num_elements = num_elements; > > if (add_dn) { >- if (msg_add_distinguished_name(filtered_msg) != 0) { >+ if (ldb_msg_add_distinguished_name(filtered_msg) != 0) { > goto failed; > } > } >diff --git a/lib/ldb/include/ldb_private.h b/lib/ldb/include/ldb_private.h >index 338e71def6d..ca43817d07a 100644 >--- a/lib/ldb/include/ldb_private.h >+++ b/lib/ldb/include/ldb_private.h >@@ -320,4 +320,9 @@ int ldb_match_message(struct ldb_context *ldb, > /* Reallocate elements to drop any excess capacity. */ > void ldb_msg_shrink_to_fit(struct ldb_message *msg); > >+/* >+ add the special distinguishedName element >+*/ >+int ldb_msg_add_distinguished_name(struct ldb_message *msg); >+ > #endif >-- >2.25.1 > > >From f6dc84ab06eec9611215a206c1f68f9a77dabcc1 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 3 Mar 2023 17:29:03 +1300 >Subject: [PATCH 13/35] CVE-2023-0614 ldb: Add function to filter message in > place > >At present this function is an exact duplicate of ldb_filter_attrs(), >but in the next commit we shall modify it to work in place, without the >need for the allocation of a second message. > >The test is a near duplicate of the existing test for >ldb_filter_attrs(). > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > lib/ldb/common/ldb_pack.c | 143 +++ > lib/ldb/include/ldb_module.h | 10 + > .../tests/ldb_filter_attrs_in_place_test.c | 989 ++++++++++++++++++ > lib/ldb/wscript | 6 + > 4 files changed, 1148 insertions(+) > create mode 100644 lib/ldb/tests/ldb_filter_attrs_in_place_test.c > >diff --git a/lib/ldb/common/ldb_pack.c b/lib/ldb/common/ldb_pack.c >index b0b0d64a5ba..f19ac73fa5e 100644 >--- a/lib/ldb/common/ldb_pack.c >+++ b/lib/ldb/common/ldb_pack.c >@@ -1262,6 +1262,149 @@ failed: > return -1; > } > >+/* >+ * filter the specified list of attributes from msg, >+ * adding requested attributes, and perhaps all for *, >+ * but not the DN to filtered_msg. >+ */ >+int ldb_filter_attrs_in_place(struct ldb_context *ldb, >+ const struct ldb_message *msg, >+ const char *const *attrs, >+ struct ldb_message *filtered_msg) >+{ >+ unsigned int i; >+ bool keep_all = false; >+ bool add_dn = false; >+ uint32_t num_elements; >+ uint32_t elements_size; >+ >+ if (attrs) { >+ /* check for special attrs */ >+ for (i = 0; attrs[i]; i++) { >+ int cmp = strcmp(attrs[i], "*"); >+ if (cmp == 0) { >+ keep_all = true; >+ break; >+ } >+ cmp = ldb_attr_cmp(attrs[i], "distinguishedName"); >+ if (cmp == 0) { >+ add_dn = true; >+ } >+ } >+ } else { >+ keep_all = true; >+ } >+ >+ if (keep_all) { >+ add_dn = true; >+ elements_size = msg->num_elements + 1; >+ >+ /* Shortcuts for the simple cases */ >+ } else if (add_dn && i == 1) { >+ if (ldb_msg_add_distinguished_name(filtered_msg) != 0) { >+ goto failed; >+ } >+ return 0; >+ } else if (i == 0) { >+ return 0; >+ >+ /* >+ * Otherwise we are copying at most as many elements as we >+ * have attributes >+ */ >+ } else { >+ elements_size = i; >+ } >+ >+ filtered_msg->elements = talloc_array(filtered_msg, >+ struct ldb_message_element, >+ elements_size); >+ if (filtered_msg->elements == NULL) goto failed; >+ >+ num_elements = 0; >+ >+ for (i = 0; i < msg->num_elements; i++) { >+ struct ldb_message_element *el = &msg->elements[i]; >+ >+ /* >+ * el2 is assigned after the Pigeonhole principle >+ * check below for clarity >+ */ >+ struct ldb_message_element *el2 = NULL; >+ unsigned int j; >+ >+ if (keep_all == false) { >+ bool found = false; >+ for (j = 0; attrs[j]; j++) { >+ int cmp = ldb_attr_cmp(el->name, attrs[j]); >+ if (cmp == 0) { >+ found = true; >+ break; >+ } >+ } >+ if (found == false) { >+ continue; >+ } >+ } >+ >+ /* >+ * Pigeonhole principle: we can't have more elements >+ * than the number of attributes if they are unique in >+ * the DB. >+ */ >+ if (num_elements >= elements_size) { >+ goto failed; >+ } >+ >+ el2 = &filtered_msg->elements[num_elements]; >+ >+ *el2 = *el; >+ el2->name = talloc_strdup(filtered_msg->elements, >+ el->name); >+ if (el2->name == NULL) { >+ goto failed; >+ } >+ el2->values = talloc_array(filtered_msg->elements, >+ struct ldb_val, el->num_values); >+ if (el2->values == NULL) { >+ goto failed; >+ } >+ for (j=0;j<el->num_values;j++) { >+ el2->values[j] = ldb_val_dup(el2->values, &el->values[j]); >+ if (el2->values[j].data == NULL && el->values[j].length != 0) { >+ goto failed; >+ } >+ } >+ num_elements++; >+ } >+ >+ filtered_msg->num_elements = num_elements; >+ >+ if (add_dn) { >+ if (ldb_msg_add_distinguished_name(filtered_msg) != 0) { >+ goto failed; >+ } >+ } >+ >+ if (filtered_msg->num_elements > 0) { >+ filtered_msg->elements >+ = talloc_realloc(filtered_msg, >+ filtered_msg->elements, >+ struct ldb_message_element, >+ filtered_msg->num_elements); >+ if (filtered_msg->elements == NULL) { >+ goto failed; >+ } >+ } else { >+ TALLOC_FREE(filtered_msg->elements); >+ } >+ >+ return 0; >+failed: >+ TALLOC_FREE(filtered_msg->elements); >+ return -1; >+} >+ > /* Have an unpacked ldb message take talloc ownership of its elements. */ > int ldb_msg_elements_take_ownership(struct ldb_message *msg) > { >diff --git a/lib/ldb/include/ldb_module.h b/lib/ldb/include/ldb_module.h >index 8c7f33496fb..105093cf38c 100644 >--- a/lib/ldb/include/ldb_module.h >+++ b/lib/ldb/include/ldb_module.h >@@ -543,6 +543,16 @@ int ldb_filter_attrs(struct ldb_context *ldb, > const char *const *attrs, > struct ldb_message *filtered_msg); > >+/* >+ * filter the specified list of attributes from msg, >+ * adding requested attributes, and perhaps all for *, >+ * but not the DN to filtered_msg. >+ */ >+int ldb_filter_attrs_in_place(struct ldb_context *ldb, >+ const struct ldb_message *msg, >+ const char *const *attrs, >+ struct ldb_message *filtered_msg); >+ > /* Have an unpacked ldb message take talloc ownership of its elements. */ > int ldb_msg_elements_take_ownership(struct ldb_message *msg); > >diff --git a/lib/ldb/tests/ldb_filter_attrs_in_place_test.c b/lib/ldb/tests/ldb_filter_attrs_in_place_test.c >new file mode 100644 >index 00000000000..bef961f8f9c >--- /dev/null >+++ b/lib/ldb/tests/ldb_filter_attrs_in_place_test.c >@@ -0,0 +1,989 @@ >+/* >+ * Tests exercising ldb_filter_attrs_in_place(). >+ * >+ * >+ * Copyright (C) Catalyst.NET Ltd 2017 >+ * Copyright (C) Andrew Bartlett <abartlet@samba.org> 2019 >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License as published by >+ * the Free Software Foundation; either version 3 of the License, or >+ * (at your option) any later version. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program. If not, see <http://www.gnu.org/licenses/>. >+ * >+ */ >+ >+/* >+ * from cmocka.c: >+ * These headers or their equivalents should be included prior to >+ * including >+ * this header file. >+ * >+ * #include <stdarg.h> >+ * #include <stddef.h> >+ * #include <setjmp.h> >+ * >+ * This allows test applications to use custom definitions of C standard >+ * library functions and types. >+ */ >+#include <stdarg.h> >+#include <stddef.h> >+#include <stdint.h> >+#include <string.h> >+#include <setjmp.h> >+#include <cmocka.h> >+ >+#include "../include/ldb.h" >+#include "../include/ldb_module.h" >+ >+struct ldbtest_ctx { >+ struct tevent_context *ev; >+ struct ldb_context *ldb; >+}; >+ >+/* >+ * NOTE WELL: >+ * >+ * This test checks the current behaviour of the function, however >+ * this is not in a public ABI and many of the tested behaviours are >+ * not ideal. If the behaviour is deliberatly improved, this test >+ * should be updated without worry to the new better behaviour. >+ * >+ * In particular the test is particularly to ensure the current >+ * behaviour is memory-safe. >+ */ >+ >+static int setup(void **state) >+{ >+ struct ldbtest_ctx *test_ctx; >+ >+ test_ctx = talloc_zero(NULL, struct ldbtest_ctx); >+ assert_non_null(test_ctx); >+ >+ test_ctx->ev = tevent_context_init(test_ctx); >+ assert_non_null(test_ctx->ev); >+ >+ test_ctx->ldb = ldb_init(test_ctx, test_ctx->ev); >+ assert_non_null(test_ctx->ldb); >+ >+ *state = test_ctx; >+ return 0; >+} >+ >+static int teardown(void **state) >+{ >+ talloc_free(*state); >+ return 0; >+} >+ >+ >+/* >+ * Test against a record with only one attribute, matching the one in >+ * the list >+ */ >+static void test_filter_attrs_one_attr_matched(void **state) >+{ >+ struct ldbtest_ctx *ctx = *state; >+ int ret; >+ >+ struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ >+ const char *attrs[] = {"foo", NULL}; >+ >+ char value[] = "The value.......end"; >+ struct ldb_val value_1 = { >+ .data = (uint8_t *)value, >+ .length = strlen(value) >+ }; >+ struct ldb_message_element element_1 = { >+ .name = "foo", >+ .num_values = 1, >+ .values = &value_1 >+ }; >+ struct ldb_message in = { >+ .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >+ .num_elements = 1, >+ .elements = &element_1, >+ }; >+ >+ assert_non_null(in.dn); >+ >+ ret = ldb_filter_attrs_in_place(ctx->ldb, >+ &in, >+ attrs, >+ filtered_msg); >+ assert_int_equal(ret, LDB_SUCCESS); >+ assert_non_null(filtered_msg); >+ >+ /* >+ * assert the ldb_filter_attrs_in_place does not read or modify >+ * filtered_msg.dn in this case >+ */ >+ assert_null(filtered_msg->dn); >+ assert_int_equal(filtered_msg->num_elements, 1); >+ assert_string_equal(filtered_msg->elements[0].name, "foo"); >+ assert_int_equal(filtered_msg->elements[0].num_values, 1); >+ assert_int_equal(filtered_msg->elements[0].values[0].length, >+ strlen(value)); >+ assert_memory_equal(filtered_msg->elements[0].values[0].data, >+ value, strlen(value)); >+} >+ >+/* >+ * Test against a record with only one attribute, matching the one of >+ * the multiple attributes in the list >+ */ >+static void test_filter_attrs_one_attr_matched_of_many(void **state) >+{ >+ struct ldbtest_ctx *ctx = *state; >+ int ret; >+ >+ struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ >+ const char *attrs[] = {"foo", "bar", "baz", NULL}; >+ >+ char value[] = "The value.......end"; >+ struct ldb_val value_1 = { >+ .data = (uint8_t *)value, >+ .length = strlen(value) >+ }; >+ struct ldb_message_element element_1 = { >+ .name = "foo", >+ .num_values = 1, >+ .values = &value_1 >+ }; >+ struct ldb_message in = { >+ .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >+ .num_elements = 1, >+ .elements = &element_1, >+ }; >+ >+ assert_non_null(in.dn); >+ >+ ret = ldb_filter_attrs_in_place(ctx->ldb, >+ &in, >+ attrs, >+ filtered_msg); >+ assert_int_equal(ret, LDB_SUCCESS); >+ assert_non_null(filtered_msg); >+ >+ /* >+ * assert the ldb_filter_attrs_in_place does not read or modify >+ * filtered_msg.dn in this case >+ */ >+ assert_null(filtered_msg->dn); >+ assert_int_equal(filtered_msg->num_elements, 1); >+ assert_string_equal(filtered_msg->elements[0].name, "foo"); >+ assert_int_equal(filtered_msg->elements[0].num_values, 1); >+ assert_int_equal(filtered_msg->elements[0].values[0].length, >+ strlen(value)); >+ assert_memory_equal(filtered_msg->elements[0].values[0].data, >+ value, strlen(value)); >+} >+ >+/* >+ * Test against a record with only one attribute, matching both >+ * attributes in the list >+ */ >+static void test_filter_attrs_two_attr_matched_attrs(void **state) >+{ >+ struct ldbtest_ctx *ctx = *state; >+ int ret; >+ >+ struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ >+ /* deliberatly the other order */ >+ const char *attrs[] = {"bar", "foo", NULL}; >+ >+ char value1[] = "The value.......end"; >+ char value2[] = "The value..MUST.end"; >+ struct ldb_val value_1 = { >+ .data = (uint8_t *)value1, >+ .length = strlen(value1) >+ }; >+ struct ldb_val value_2 = { >+ .data = (uint8_t *)value2, >+ .length = strlen(value2) >+ }; >+ >+ /* foo and bar are the other order to in attrs */ >+ struct ldb_message_element elements[] = { >+ { >+ .name = "foo", >+ .num_values = 1, >+ .values = &value_1 >+ }, >+ { >+ .name = "bar", >+ .num_values = 1, >+ .values = &value_2 >+ } >+ }; >+ struct ldb_message in = { >+ .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >+ .num_elements = 2, >+ .elements = elements, >+ }; >+ >+ assert_non_null(in.dn); >+ >+ ret = ldb_filter_attrs_in_place(ctx->ldb, >+ &in, >+ attrs, >+ filtered_msg); >+ assert_int_equal(ret, LDB_SUCCESS); >+ assert_non_null(filtered_msg); >+ assert_int_equal(filtered_msg->num_elements, 2); >+ >+ /* >+ * assert the ldb_filter_attrs_in_place does not read or modify >+ * filtered_msg.dn in this case >+ */ >+ assert_null(filtered_msg->dn); >+ >+ /* Assert that DB order is preserved */ >+ assert_string_equal(filtered_msg->elements[0].name, "foo"); >+ assert_int_equal(filtered_msg->elements[0].num_values, 1); >+ assert_int_equal(filtered_msg->elements[0].values[0].length, >+ strlen(value1)); >+ assert_memory_equal(filtered_msg->elements[0].values[0].data, >+ value1, strlen(value1)); >+ assert_string_equal(filtered_msg->elements[1].name, "bar"); >+ assert_int_equal(filtered_msg->elements[1].num_values, 1); >+ assert_int_equal(filtered_msg->elements[1].values[0].length, >+ strlen(value2)); >+ assert_memory_equal(filtered_msg->elements[1].values[0].data, >+ value2, strlen(value2)); >+} >+ >+/* >+ * Test against a record with two attributes, only of which is in >+ * the list >+ */ >+static void test_filter_attrs_two_attr_matched_one_attr(void **state) >+{ >+ struct ldbtest_ctx *ctx = *state; >+ int ret; >+ >+ struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ >+ /* deliberatly the other order */ >+ const char *attrs[] = {"bar", NULL}; >+ >+ char value1[] = "The value.......end"; >+ char value2[] = "The value..MUST.end"; >+ struct ldb_val value_1 = { >+ .data = (uint8_t *)value1, >+ .length = strlen(value1) >+ }; >+ struct ldb_val value_2 = { >+ .data = (uint8_t *)value2, >+ .length = strlen(value2) >+ }; >+ >+ /* foo and bar are the other order to in attrs */ >+ struct ldb_message_element elements[] = { >+ { >+ .name = "foo", >+ .num_values = 1, >+ .values = &value_1 >+ }, >+ { >+ .name = "bar", >+ .num_values = 1, >+ .values = &value_2 >+ } >+ }; >+ struct ldb_message in = { >+ .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >+ .num_elements = 2, >+ .elements = elements, >+ }; >+ >+ assert_non_null(in.dn); >+ >+ ret = ldb_filter_attrs_in_place(ctx->ldb, >+ &in, >+ attrs, >+ filtered_msg); >+ assert_int_equal(ret, LDB_SUCCESS); >+ assert_non_null(filtered_msg); >+ assert_int_equal(filtered_msg->num_elements, 1); >+ >+ /* >+ * assert the ldb_filter_attrs_in_place does not read or modify >+ * filtered_msg.dn in this case >+ */ >+ assert_null(filtered_msg->dn); >+ >+ /* Assert that DB order is preserved */ >+ assert_string_equal(filtered_msg->elements[0].name, "bar"); >+ assert_int_equal(filtered_msg->elements[0].num_values, 1); >+ assert_int_equal(filtered_msg->elements[0].values[0].length, >+ strlen(value2)); >+ assert_memory_equal(filtered_msg->elements[0].values[0].data, >+ value2, strlen(value2)); >+} >+ >+/* >+ * Test against a record with two attributes, both matching the one >+ * specified attribute in the list (a corrupt record) >+ */ >+static void test_filter_attrs_two_dup_attr_matched_one_attr(void **state) >+{ >+ struct ldbtest_ctx *ctx = *state; >+ int ret; >+ >+ struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ >+ /* deliberatly the other order */ >+ const char *attrs[] = {"bar", NULL}; >+ >+ char value1[] = "The value.......end"; >+ char value2[] = "The value..MUST.end"; >+ struct ldb_val value_1 = { >+ .data = (uint8_t *)value1, >+ .length = strlen(value1) >+ }; >+ struct ldb_val value_2 = { >+ .data = (uint8_t *)value2, >+ .length = strlen(value2) >+ }; >+ >+ /* foo and bar are the other order to in attrs */ >+ struct ldb_message_element elements[] = { >+ { >+ .name = "bar", >+ .num_values = 1, >+ .values = &value_1 >+ }, >+ { >+ .name = "bar", >+ .num_values = 1, >+ .values = &value_2 >+ } >+ }; >+ struct ldb_message in = { >+ .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >+ .num_elements = 2, >+ .elements = elements, >+ }; >+ >+ assert_non_null(in.dn); >+ >+ ret = ldb_filter_attrs_in_place(ctx->ldb, >+ &in, >+ attrs, >+ filtered_msg); >+ >+ /* This should fail the pidgenhole test */ >+ assert_int_equal(ret, -1); >+ assert_null(filtered_msg->elements); >+} >+ >+/* >+ * Test against a record with two attributes, both matching the one >+ * specified attribute in the list (a corrupt record) >+ */ >+static void test_filter_attrs_two_dup_attr_matched_dup(void **state) >+{ >+ struct ldbtest_ctx *ctx = *state; >+ int ret; >+ >+ struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ >+ const char *attrs[] = {"bar", "bar", NULL}; >+ >+ char value1[] = "The value.......end"; >+ char value2[] = "The value..MUST.end"; >+ struct ldb_val value_1 = { >+ .data = (uint8_t *)value1, >+ .length = strlen(value1) >+ }; >+ struct ldb_val value_2 = { >+ .data = (uint8_t *)value2, >+ .length = strlen(value2) >+ }; >+ >+ /* foo and bar are the other order to in attrs */ >+ struct ldb_message_element elements[] = { >+ { >+ .name = "bar", >+ .num_values = 1, >+ .values = &value_1 >+ }, >+ { >+ .name = "bar", >+ .num_values = 1, >+ .values = &value_2 >+ } >+ }; >+ struct ldb_message in = { >+ .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >+ .num_elements = 2, >+ .elements = elements, >+ }; >+ >+ assert_non_null(in.dn); >+ >+ ret = ldb_filter_attrs_in_place(ctx->ldb, >+ &in, >+ attrs, >+ filtered_msg); >+ >+ /* This does not fail the pidgenhole test */ >+ assert_int_equal(ret, LDB_SUCCESS); >+ assert_int_equal(filtered_msg->num_elements, 2); >+ >+ /* Assert that DB order is preserved */ >+ assert_string_equal(filtered_msg->elements[0].name, "bar"); >+ assert_int_equal(filtered_msg->elements[0].num_values, 1); >+ assert_int_equal(filtered_msg->elements[0].values[0].length, >+ strlen(value1)); >+ assert_memory_equal(filtered_msg->elements[0].values[0].data, >+ value1, strlen(value1)); >+ assert_string_equal(filtered_msg->elements[1].name, "bar"); >+ assert_int_equal(filtered_msg->elements[1].num_values, 1); >+ assert_int_equal(filtered_msg->elements[1].values[0].length, >+ strlen(value2)); >+ assert_memory_equal(filtered_msg->elements[1].values[0].data, >+ value2, strlen(value2)); >+} >+ >+/* >+ * Test against a record with two attributes, both matching one of the >+ * specified attributes in the list (a corrupt record) >+ */ >+static void test_filter_attrs_two_dup_attr_matched_one_of_two(void **state) >+{ >+ struct ldbtest_ctx *ctx = *state; >+ int ret; >+ >+ struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ >+ const char *attrs[] = {"bar", "foo", NULL}; >+ >+ char value1[] = "The value.......end"; >+ char value2[] = "The value..MUST.end"; >+ struct ldb_val value_1 = { >+ .data = (uint8_t *)value1, >+ .length = strlen(value1) >+ }; >+ struct ldb_val value_2 = { >+ .data = (uint8_t *)value2, >+ .length = strlen(value2) >+ }; >+ >+ /* foo and bar are the other order to in attrs */ >+ struct ldb_message_element elements[] = { >+ { >+ .name = "bar", >+ .num_values = 1, >+ .values = &value_1 >+ }, >+ { >+ .name = "bar", >+ .num_values = 1, >+ .values = &value_2 >+ } >+ }; >+ struct ldb_message in = { >+ .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >+ .num_elements = 2, >+ .elements = elements, >+ }; >+ >+ assert_non_null(in.dn); >+ >+ ret = ldb_filter_attrs_in_place(ctx->ldb, >+ &in, >+ attrs, >+ filtered_msg); >+ >+ /* This does not fail the pidgenhole test */ >+ assert_int_equal(ret, LDB_SUCCESS); >+ assert_int_equal(filtered_msg->num_elements, 2); >+ >+ /* Assert that DB order is preserved */ >+ assert_string_equal(filtered_msg->elements[0].name, "bar"); >+ assert_int_equal(filtered_msg->elements[0].num_values, 1); >+ assert_int_equal(filtered_msg->elements[0].values[0].length, >+ strlen(value1)); >+ assert_memory_equal(filtered_msg->elements[0].values[0].data, >+ value1, strlen(value1)); >+ assert_string_equal(filtered_msg->elements[1].name, "bar"); >+ assert_int_equal(filtered_msg->elements[1].num_values, 1); >+ assert_int_equal(filtered_msg->elements[1].values[0].length, >+ strlen(value2)); >+ assert_memory_equal(filtered_msg->elements[1].values[0].data, >+ value2, strlen(value2)); >+} >+ >+/* >+ * Test against a record with two attributes against * (but not the >+ * other named attribute) (a corrupt record) >+ */ >+static void test_filter_attrs_two_dup_attr_matched_star(void **state) >+{ >+ struct ldbtest_ctx *ctx = *state; >+ int ret; >+ >+ struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ >+ const char *attrs[] = {"*", "foo", NULL}; >+ >+ char value1[] = "The value.......end"; >+ char value2[] = "The value..MUST.end"; >+ struct ldb_val value_1 = { >+ .data = (uint8_t *)value1, >+ .length = strlen(value1) >+ }; >+ struct ldb_val value_2 = { >+ .data = (uint8_t *)value2, >+ .length = strlen(value2) >+ }; >+ >+ /* foo and bar are the other order to in attrs */ >+ struct ldb_message_element elements[] = { >+ { >+ .name = "bar", >+ .num_values = 1, >+ .values = &value_1 >+ }, >+ { >+ .name = "bar", >+ .num_values = 1, >+ .values = &value_2 >+ } >+ }; >+ struct ldb_message in = { >+ .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >+ .num_elements = 2, >+ .elements = elements, >+ }; >+ >+ assert_non_null(in.dn); >+ >+ /* Needed as * implies distinguishedName */ >+ filtered_msg->dn = in.dn; >+ >+ ret = ldb_filter_attrs_in_place(ctx->ldb, >+ &in, >+ attrs, >+ filtered_msg); >+ >+ /* This does not fail the pidgenhole test */ >+ assert_int_equal(ret, LDB_SUCCESS); >+ assert_int_equal(filtered_msg->num_elements, 3); >+ >+ /* Assert that DB order is preserved */ >+ assert_string_equal(filtered_msg->elements[0].name, "bar"); >+ assert_int_equal(filtered_msg->elements[0].num_values, 1); >+ assert_int_equal(filtered_msg->elements[0].values[0].length, >+ strlen(value1)); >+ assert_memory_equal(filtered_msg->elements[0].values[0].data, >+ value1, strlen(value1)); >+ assert_string_equal(filtered_msg->elements[1].name, "bar"); >+ assert_int_equal(filtered_msg->elements[1].num_values, 1); >+ assert_int_equal(filtered_msg->elements[1].values[0].length, >+ strlen(value2)); >+ assert_memory_equal(filtered_msg->elements[1].values[0].data, >+ value2, strlen(value2)); >+ /* >+ * assert the ldb_filter_attrs_in_place does not modify filtered_msg.dn >+ * in this case >+ */ >+ assert_ptr_equal(filtered_msg->dn, in.dn); >+ assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ "distinguishedName", >+ NULL), >+ ldb_dn_get_linearized(in.dn)); >+} >+ >+/* >+ * Test against a record with only one attribute, matching the * in >+ * the list >+ */ >+static void test_filter_attrs_one_attr_matched_star(void **state) >+{ >+ struct ldbtest_ctx *ctx = *state; >+ int ret; >+ >+ struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ >+ const char *attrs[] = {"*", NULL}; >+ >+ char value[] = "The value.......end"; >+ struct ldb_val value_1 = { >+ .data = (uint8_t *)value, >+ .length = strlen(value) >+ }; >+ struct ldb_message_element element_1 = { >+ .name = "foo", >+ .num_values = 1, >+ .values = &value_1 >+ }; >+ struct ldb_message in = { >+ .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >+ .num_elements = 1, >+ .elements = &element_1, >+ }; >+ >+ assert_non_null(in.dn); >+ >+ /* Needed as * implies distinguishedName */ >+ filtered_msg->dn = in.dn; >+ >+ ret = ldb_filter_attrs_in_place(ctx->ldb, >+ &in, >+ attrs, >+ filtered_msg); >+ assert_int_equal(ret, LDB_SUCCESS); >+ assert_non_null(filtered_msg); >+ assert_int_equal(filtered_msg->num_elements, 2); >+ >+ /* >+ * assert the ldb_filter_attrs_in_place does not modify filtered_msg.dn >+ * in this case >+ */ >+ assert_ptr_equal(filtered_msg->dn, in.dn); >+ assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ "distinguishedName", >+ NULL), >+ ldb_dn_get_linearized(in.dn)); >+ assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ "foo", >+ NULL), >+ value); >+} >+ >+/* >+ * Test against a record with two attributes, matching the * in >+ * the list >+ */ >+static void test_filter_attrs_two_attr_matched_star(void **state) >+{ >+ struct ldbtest_ctx *ctx = *state; >+ int ret; >+ >+ struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ >+ const char *attrs[] = {"*", NULL}; >+ >+ char value1[] = "The value.......end"; >+ char value2[] = "The value..MUST.end"; >+ struct ldb_val value_1 = { >+ .data = (uint8_t *)value1, >+ .length = strlen(value1) >+ }; >+ struct ldb_val value_2 = { >+ .data = (uint8_t *)value2, >+ .length = strlen(value2) >+ }; >+ struct ldb_message_element elements[] = { >+ { >+ .name = "foo", >+ .num_values = 1, >+ .values = &value_1 >+ }, >+ { >+ .name = "bar", >+ .num_values = 1, >+ .values = &value_2 >+ } >+ }; >+ struct ldb_message in = { >+ .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >+ .num_elements = 2, >+ .elements = elements, >+ }; >+ >+ assert_non_null(in.dn); >+ >+ /* Needed as * implies distinguishedName */ >+ filtered_msg->dn = in.dn; >+ >+ ret = ldb_filter_attrs_in_place(ctx->ldb, >+ &in, >+ attrs, >+ filtered_msg); >+ assert_int_equal(ret, LDB_SUCCESS); >+ assert_non_null(filtered_msg); >+ assert_int_equal(filtered_msg->num_elements, 3); >+ >+ /* >+ * assert the ldb_filter_attrs_in_place does not modify filtered_msg.dn >+ * in this case >+ */ >+ assert_ptr_equal(filtered_msg->dn, in.dn); >+ assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ "distinguishedName", >+ NULL), >+ ldb_dn_get_linearized(in.dn)); >+ assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ "foo", >+ NULL), >+ value1); >+ assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ "bar", >+ NULL), >+ value2); >+} >+ >+/* >+ * Test against a record with only one attribute, matching the * in >+ * the list, but without the DN being pre-filled. Fails due to need >+ * to contstruct the distinguishedName >+ */ >+static void test_filter_attrs_one_attr_matched_star_no_dn(void **state) >+{ >+ struct ldbtest_ctx *ctx = *state; >+ int ret; >+ >+ struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ >+ const char *attrs[] = {"*", NULL}; >+ >+ char value[] = "The value.......end"; >+ struct ldb_val value_1 = { >+ .data = (uint8_t *)value, >+ .length = strlen(value) >+ }; >+ struct ldb_message_element element_1 = { >+ .name = "foo", >+ .num_values = 1, >+ .values = &value_1 >+ }; >+ struct ldb_message in = { >+ .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >+ .num_elements = 1, >+ .elements = &element_1, >+ }; >+ >+ assert_non_null(in.dn); >+ >+ ret = ldb_filter_attrs_in_place(ctx->ldb, >+ &in, >+ attrs, >+ filtered_msg); >+ assert_int_equal(ret, -1); >+ assert_null(filtered_msg->elements); >+} >+ >+/* >+ * Test against a record with only one attribute, matching the * in >+ * the list plus requsesting distinguishedName >+ */ >+static void test_filter_attrs_one_attr_matched_star_dn(void **state) >+{ >+ struct ldbtest_ctx *ctx = *state; >+ int ret; >+ >+ struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ >+ const char *attrs[] = {"*", "distinguishedName", NULL}; >+ >+ char value[] = "The value.......end"; >+ struct ldb_val value_1 = { >+ .data = (uint8_t *)value, >+ .length = strlen(value) >+ }; >+ struct ldb_message_element element_1 = { >+ .name = "foo", >+ .num_values = 1, >+ .values = &value_1 >+ }; >+ struct ldb_message in = { >+ .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >+ .num_elements = 1, >+ .elements = &element_1, >+ }; >+ >+ assert_non_null(in.dn); >+ >+ /* Needed for distinguishedName */ >+ filtered_msg->dn = in.dn; >+ >+ ret = ldb_filter_attrs_in_place(ctx->ldb, >+ &in, >+ attrs, >+ filtered_msg); >+ assert_int_equal(ret, LDB_SUCCESS); >+ assert_non_null(filtered_msg); >+ assert_int_equal(filtered_msg->num_elements, 2); >+ >+ /* show that ldb_filter_attrs_in_place does not modify in.dn */ >+ assert_ptr_equal(filtered_msg->dn, in.dn); >+ >+ assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ "distinguishedName", >+ NULL), >+ ldb_dn_get_linearized(in.dn)); >+ assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ "foo", >+ NULL), >+ value); >+} >+ >+/* >+ * Test against a record with only one attribute, but returning >+ * distinguishedName from the list (only) >+ */ >+static void test_filter_attrs_one_attr_matched_dn(void **state) >+{ >+ struct ldbtest_ctx *ctx = *state; >+ int ret; >+ >+ struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ >+ const char *attrs[] = {"distinguishedName", NULL}; >+ >+ char value[] = "The value.......end"; >+ struct ldb_val value_1 = { >+ .data = (uint8_t *)value, >+ .length = strlen(value) >+ }; >+ struct ldb_message_element element_1 = { >+ .name = "foo", >+ .num_values = 1, >+ .values = &value_1 >+ }; >+ struct ldb_message in = { >+ .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >+ .num_elements = 1, >+ .elements = &element_1, >+ }; >+ >+ assert_non_null(in.dn); >+ >+ /* Needed for distinguishedName */ >+ filtered_msg->dn = in.dn; >+ >+ ret = ldb_filter_attrs_in_place(ctx->ldb, >+ &in, >+ attrs, >+ filtered_msg); >+ assert_int_equal(ret, LDB_SUCCESS); >+ assert_non_null(filtered_msg); >+ assert_int_equal(filtered_msg->num_elements, 1); >+ >+ /* show that ldb_filter_attrs_in_place does not modify in.dn */ >+ assert_ptr_equal(filtered_msg->dn, in.dn); >+ assert_string_equal(filtered_msg->elements[0].name, "distinguishedName"); >+ assert_int_equal(filtered_msg->elements[0].num_values, 1); >+ assert_string_equal(filtered_msg->elements[0].values[0].data, >+ ldb_dn_get_linearized(in.dn)); >+} >+ >+/* >+ * Test against a record with only one attribute, not matching the >+ * empty attribute list >+ */ >+static void test_filter_attrs_one_attr_empty_list(void **state) >+{ >+ struct ldbtest_ctx *ctx = *state; >+ int ret; >+ >+ struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ >+ const char *attrs[] = {NULL}; >+ >+ char value[] = "The value.......end"; >+ struct ldb_val value_1 = { >+ .data = (uint8_t *)value, >+ .length = strlen(value) >+ }; >+ struct ldb_message_element element_1 = { >+ .name = "foo", >+ .num_values = 1, >+ .values = &value_1 >+ }; >+ struct ldb_message in = { >+ .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >+ .num_elements = 1, >+ .elements = &element_1, >+ }; >+ >+ assert_non_null(in.dn); >+ >+ ret = ldb_filter_attrs_in_place(ctx->ldb, >+ &in, >+ attrs, >+ filtered_msg); >+ assert_int_equal(ret, LDB_SUCCESS); >+ assert_non_null(filtered_msg); >+ assert_int_equal(filtered_msg->num_elements, 0); >+ assert_null(filtered_msg->dn); >+ assert_null(filtered_msg->elements); >+} >+ >+int main(int argc, const char **argv) >+{ >+ const struct CMUnitTest tests[] = { >+ cmocka_unit_test_setup_teardown( >+ test_filter_attrs_one_attr_matched, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_filter_attrs_one_attr_matched_of_many, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_filter_attrs_two_attr_matched_attrs, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_filter_attrs_two_attr_matched_one_attr, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_filter_attrs_two_dup_attr_matched_one_attr, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_filter_attrs_two_dup_attr_matched_dup, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_filter_attrs_two_dup_attr_matched_one_of_two, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_filter_attrs_two_dup_attr_matched_star, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_filter_attrs_one_attr_matched_star, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_filter_attrs_two_attr_matched_star, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_filter_attrs_one_attr_matched_star_no_dn, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_filter_attrs_one_attr_matched_star_dn, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_filter_attrs_one_attr_matched_dn, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_filter_attrs_one_attr_empty_list, >+ setup, >+ teardown), >+ }; >+ >+ return cmocka_run_group_tests(tests, NULL, NULL); >+} >diff --git a/lib/ldb/wscript b/lib/ldb/wscript >index 00af9dce79f..9f687061b8c 100644 >--- a/lib/ldb/wscript >+++ b/lib/ldb/wscript >@@ -518,6 +518,11 @@ def build(bld): > deps='cmocka ldb ldb_tdb_err_map', > install=False) > >+ bld.SAMBA_BINARY('ldb_filter_attrs_in_place_test', >+ source='tests/ldb_filter_attrs_in_place_test.c', >+ deps='cmocka ldb ldb_tdb_err_map', >+ install=False) >+ > bld.SAMBA_BINARY('ldb_key_value_sub_txn_tdb_test', > bld.SUBDIR('ldb_key_value', > '''ldb_kv_search.c >@@ -638,6 +643,7 @@ def test(ctx): > # 'ldb_key_value_sub_txn_tdb_test' > 'ldb_parse_test', > 'ldb_filter_attrs_test', >+ 'ldb_filter_attrs_in_place_test', > ] > > # if LIB_LDAP and LIB_LBER defined, then we can test ldb_ldap backend >-- >2.25.1 > > >From 07f9bfb7de4230a3acfad4e44cb81c52bbf0a30f Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 3 Mar 2023 17:30:19 +1300 >Subject: [PATCH 14/35] CVE-2023-0614 ldb: Make ldb_filter_attrs_in_place() > work in place > >ldb_filter_attrs() previously did too much. Now its replacement, >ldb_filter_attrs_in_place(), only does the actual filtering, while >taking ownership of each element's values is handled in a separate >function, ldb_msg_elements_take_ownership(). > >Also, ldb_filter_attrs_in_place() no longer adds the distinguishedName >to the message if it is missing. That is handled in another function, >ldb_msg_add_distinguished_name(). > >As we're now modifying the original message rather than copying it into >a new one, we no longer need the filtered_msg parameter. > >We adapt a test, based on ldb_filter_attrs_test, to exercise the new >function. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > lib/ldb/common/ldb_pack.c | 129 +--- > lib/ldb/include/ldb_module.h | 11 +- > .../tests/ldb_filter_attrs_in_place_test.c | 609 ++++++++---------- > 3 files changed, 307 insertions(+), 442 deletions(-) > >diff --git a/lib/ldb/common/ldb_pack.c b/lib/ldb/common/ldb_pack.c >index f19ac73fa5e..28b9a8dfe07 100644 >--- a/lib/ldb/common/ldb_pack.c >+++ b/lib/ldb/common/ldb_pack.c >@@ -1264,19 +1264,16 @@ failed: > > /* > * filter the specified list of attributes from msg, >- * adding requested attributes, and perhaps all for *, >- * but not the DN to filtered_msg. >+ * adding requested attributes, and perhaps all for *. >+ * Unlike ldb_filter_attrs(), the DN will not be added >+ * if it is missing. > */ >-int ldb_filter_attrs_in_place(struct ldb_context *ldb, >- const struct ldb_message *msg, >- const char *const *attrs, >- struct ldb_message *filtered_msg) >+int ldb_filter_attrs_in_place(struct ldb_message *msg, >+ const char *const *attrs) > { >- unsigned int i; >+ unsigned int i = 0; > bool keep_all = false; >- bool add_dn = false; >- uint32_t num_elements; >- uint32_t elements_size; >+ unsigned int num_del = 0; > > if (attrs) { > /* check for special attrs */ >@@ -1286,123 +1283,41 @@ int ldb_filter_attrs_in_place(struct ldb_context *ldb, > keep_all = true; > break; > } >- cmp = ldb_attr_cmp(attrs[i], "distinguishedName"); >- if (cmp == 0) { >- add_dn = true; >- } > } >- } else { >- keep_all = true; >- } >- >- if (keep_all) { >- add_dn = true; >- elements_size = msg->num_elements + 1; >- >- /* Shortcuts for the simple cases */ >- } else if (add_dn && i == 1) { >- if (ldb_msg_add_distinguished_name(filtered_msg) != 0) { >- goto failed; >+ if (!keep_all && i == 0) { >+ msg->num_elements = 0; >+ return LDB_SUCCESS; > } >- return 0; >- } else if (i == 0) { >- return 0; >- >- /* >- * Otherwise we are copying at most as many elements as we >- * have attributes >- */ > } else { >- elements_size = i; >+ keep_all = true; > } > >- filtered_msg->elements = talloc_array(filtered_msg, >- struct ldb_message_element, >- elements_size); >- if (filtered_msg->elements == NULL) goto failed; >- >- num_elements = 0; >- > for (i = 0; i < msg->num_elements; i++) { >- struct ldb_message_element *el = &msg->elements[i]; >- >- /* >- * el2 is assigned after the Pigeonhole principle >- * check below for clarity >- */ >- struct ldb_message_element *el2 = NULL; >+ bool found = false; > unsigned int j; > >- if (keep_all == false) { >- bool found = false; >+ if (keep_all) { >+ found = true; >+ } else { > for (j = 0; attrs[j]; j++) { >- int cmp = ldb_attr_cmp(el->name, attrs[j]); >+ int cmp = ldb_attr_cmp(msg->elements[i].name, attrs[j]); > if (cmp == 0) { > found = true; > break; > } > } >- if (found == false) { >- continue; >- } >- } >- >- /* >- * Pigeonhole principle: we can't have more elements >- * than the number of attributes if they are unique in >- * the DB. >- */ >- if (num_elements >= elements_size) { >- goto failed; > } > >- el2 = &filtered_msg->elements[num_elements]; >- >- *el2 = *el; >- el2->name = talloc_strdup(filtered_msg->elements, >- el->name); >- if (el2->name == NULL) { >- goto failed; >- } >- el2->values = talloc_array(filtered_msg->elements, >- struct ldb_val, el->num_values); >- if (el2->values == NULL) { >- goto failed; >+ if (!found) { >+ ++num_del; >+ } else if (num_del != 0) { >+ msg->elements[i - num_del] = msg->elements[i]; > } >- for (j=0;j<el->num_values;j++) { >- el2->values[j] = ldb_val_dup(el2->values, &el->values[j]); >- if (el2->values[j].data == NULL && el->values[j].length != 0) { >- goto failed; >- } >- } >- num_elements++; > } > >- filtered_msg->num_elements = num_elements; >- >- if (add_dn) { >- if (ldb_msg_add_distinguished_name(filtered_msg) != 0) { >- goto failed; >- } >- } >+ msg->num_elements -= num_del; > >- if (filtered_msg->num_elements > 0) { >- filtered_msg->elements >- = talloc_realloc(filtered_msg, >- filtered_msg->elements, >- struct ldb_message_element, >- filtered_msg->num_elements); >- if (filtered_msg->elements == NULL) { >- goto failed; >- } >- } else { >- TALLOC_FREE(filtered_msg->elements); >- } >- >- return 0; >-failed: >- TALLOC_FREE(filtered_msg->elements); >- return -1; >+ return LDB_SUCCESS; > } > > /* Have an unpacked ldb message take talloc ownership of its elements. */ >diff --git a/lib/ldb/include/ldb_module.h b/lib/ldb/include/ldb_module.h >index 105093cf38c..4ae381ba5be 100644 >--- a/lib/ldb/include/ldb_module.h >+++ b/lib/ldb/include/ldb_module.h >@@ -545,13 +545,12 @@ int ldb_filter_attrs(struct ldb_context *ldb, > > /* > * filter the specified list of attributes from msg, >- * adding requested attributes, and perhaps all for *, >- * but not the DN to filtered_msg. >+ * adding requested attributes, and perhaps all for *. >+ * Unlike ldb_filter_attrs(), the DN will not be added >+ * if it is missing. > */ >-int ldb_filter_attrs_in_place(struct ldb_context *ldb, >- const struct ldb_message *msg, >- const char *const *attrs, >- struct ldb_message *filtered_msg); >+int ldb_filter_attrs_in_place(struct ldb_message *msg, >+ const char *const *attrs); > > /* Have an unpacked ldb message take talloc ownership of its elements. */ > int ldb_msg_elements_take_ownership(struct ldb_message *msg); >diff --git a/lib/ldb/tests/ldb_filter_attrs_in_place_test.c b/lib/ldb/tests/ldb_filter_attrs_in_place_test.c >index bef961f8f9c..da333c73c99 100644 >--- a/lib/ldb/tests/ldb_filter_attrs_in_place_test.c >+++ b/lib/ldb/tests/ldb_filter_attrs_in_place_test.c >@@ -83,17 +83,41 @@ static int teardown(void **state) > return 0; > } > >+static void msg_add_dn(struct ldb_message *msg) >+{ >+ const char *dn_attr = "distinguishedName"; >+ char *dn = NULL; >+ int ret; >+ >+ assert_null(ldb_msg_find_element(msg, dn_attr)); >+ >+ assert_non_null(msg->dn); >+ dn = ldb_dn_alloc_linearized(msg, msg->dn); >+ assert_non_null(dn); >+ >+ /* >+ * The message's elements must be talloc allocated to call >+ * ldb_msg_add_steal_string(). >+ */ >+ msg->elements = talloc_memdup(msg, >+ msg->elements, >+ msg->num_elements * sizeof(msg->elements[0])); >+ assert_non_null(msg->elements); >+ >+ ret = ldb_msg_add_steal_string(msg, dn_attr, dn); >+ assert_int_equal(ret, LDB_SUCCESS); >+} > > /* > * Test against a record with only one attribute, matching the one in > * the list > */ >-static void test_filter_attrs_one_attr_matched(void **state) >+static void test_filter_attrs_in_place_one_attr_matched(void **state) > { > struct ldbtest_ctx *ctx = *state; > int ret; > >- struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ struct ldb_message *msg = ldb_msg_new(ctx); > > const char *attrs[] = {"foo", NULL}; > >@@ -107,32 +131,25 @@ static void test_filter_attrs_one_attr_matched(void **state) > .num_values = 1, > .values = &value_1 > }; >- struct ldb_message in = { >- .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >- .num_elements = 1, >- .elements = &element_1, >- }; > >- assert_non_null(in.dn); >+ assert_non_null(msg); >+ msg->dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"); >+ msg->num_elements = 1; >+ msg->elements = &element_1; > >- ret = ldb_filter_attrs_in_place(ctx->ldb, >- &in, >- attrs, >- filtered_msg); >+ assert_non_null(msg->dn); >+ msg_add_dn(msg); >+ >+ ret = ldb_filter_attrs_in_place(msg, attrs); > assert_int_equal(ret, LDB_SUCCESS); >- assert_non_null(filtered_msg); > >- /* >- * assert the ldb_filter_attrs_in_place does not read or modify >- * filtered_msg.dn in this case >- */ >- assert_null(filtered_msg->dn); >- assert_int_equal(filtered_msg->num_elements, 1); >- assert_string_equal(filtered_msg->elements[0].name, "foo"); >- assert_int_equal(filtered_msg->elements[0].num_values, 1); >- assert_int_equal(filtered_msg->elements[0].values[0].length, >+ assert_non_null(msg->dn); >+ assert_int_equal(msg->num_elements, 1); >+ assert_string_equal(msg->elements[0].name, "foo"); >+ assert_int_equal(msg->elements[0].num_values, 1); >+ assert_int_equal(msg->elements[0].values[0].length, > strlen(value)); >- assert_memory_equal(filtered_msg->elements[0].values[0].data, >+ assert_memory_equal(msg->elements[0].values[0].data, > value, strlen(value)); > } > >@@ -140,12 +157,12 @@ static void test_filter_attrs_one_attr_matched(void **state) > * Test against a record with only one attribute, matching the one of > * the multiple attributes in the list > */ >-static void test_filter_attrs_one_attr_matched_of_many(void **state) >+static void test_filter_attrs_in_place_one_attr_matched_of_many(void **state) > { > struct ldbtest_ctx *ctx = *state; > int ret; > >- struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ struct ldb_message *msg = ldb_msg_new(ctx); > > const char *attrs[] = {"foo", "bar", "baz", NULL}; > >@@ -159,32 +176,25 @@ static void test_filter_attrs_one_attr_matched_of_many(void **state) > .num_values = 1, > .values = &value_1 > }; >- struct ldb_message in = { >- .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >- .num_elements = 1, >- .elements = &element_1, >- }; > >- assert_non_null(in.dn); >+ assert_non_null(msg); >+ msg->dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"); >+ msg->num_elements = 1; >+ msg->elements = &element_1; > >- ret = ldb_filter_attrs_in_place(ctx->ldb, >- &in, >- attrs, >- filtered_msg); >+ assert_non_null(msg->dn); >+ msg_add_dn(msg); >+ >+ ret = ldb_filter_attrs_in_place(msg, attrs); > assert_int_equal(ret, LDB_SUCCESS); >- assert_non_null(filtered_msg); > >- /* >- * assert the ldb_filter_attrs_in_place does not read or modify >- * filtered_msg.dn in this case >- */ >- assert_null(filtered_msg->dn); >- assert_int_equal(filtered_msg->num_elements, 1); >- assert_string_equal(filtered_msg->elements[0].name, "foo"); >- assert_int_equal(filtered_msg->elements[0].num_values, 1); >- assert_int_equal(filtered_msg->elements[0].values[0].length, >+ assert_non_null(msg->dn); >+ assert_int_equal(msg->num_elements, 1); >+ assert_string_equal(msg->elements[0].name, "foo"); >+ assert_int_equal(msg->elements[0].num_values, 1); >+ assert_int_equal(msg->elements[0].values[0].length, > strlen(value)); >- assert_memory_equal(filtered_msg->elements[0].values[0].data, >+ assert_memory_equal(msg->elements[0].values[0].data, > value, strlen(value)); > } > >@@ -192,12 +202,12 @@ static void test_filter_attrs_one_attr_matched_of_many(void **state) > * Test against a record with only one attribute, matching both > * attributes in the list > */ >-static void test_filter_attrs_two_attr_matched_attrs(void **state) >+static void test_filter_attrs_in_place_two_attr_matched_attrs(void **state) > { > struct ldbtest_ctx *ctx = *state; > int ret; > >- struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ struct ldb_message *msg = ldb_msg_new(ctx); > > /* deliberatly the other order */ > const char *attrs[] = {"bar", "foo", NULL}; >@@ -226,40 +236,33 @@ static void test_filter_attrs_two_attr_matched_attrs(void **state) > .values = &value_2 > } > }; >- struct ldb_message in = { >- .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >- .num_elements = 2, >- .elements = elements, >- }; > >- assert_non_null(in.dn); >+ assert_non_null(msg); >+ msg->dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"); >+ msg->num_elements = 2; >+ msg->elements = elements; > >- ret = ldb_filter_attrs_in_place(ctx->ldb, >- &in, >- attrs, >- filtered_msg); >+ assert_non_null(msg->dn); >+ msg_add_dn(msg); >+ >+ ret = ldb_filter_attrs_in_place(msg, attrs); > assert_int_equal(ret, LDB_SUCCESS); >- assert_non_null(filtered_msg); >- assert_int_equal(filtered_msg->num_elements, 2); >+ assert_int_equal(msg->num_elements, 2); > >- /* >- * assert the ldb_filter_attrs_in_place does not read or modify >- * filtered_msg.dn in this case >- */ >- assert_null(filtered_msg->dn); >+ assert_non_null(msg->dn); > > /* Assert that DB order is preserved */ >- assert_string_equal(filtered_msg->elements[0].name, "foo"); >- assert_int_equal(filtered_msg->elements[0].num_values, 1); >- assert_int_equal(filtered_msg->elements[0].values[0].length, >+ assert_string_equal(msg->elements[0].name, "foo"); >+ assert_int_equal(msg->elements[0].num_values, 1); >+ assert_int_equal(msg->elements[0].values[0].length, > strlen(value1)); >- assert_memory_equal(filtered_msg->elements[0].values[0].data, >+ assert_memory_equal(msg->elements[0].values[0].data, > value1, strlen(value1)); >- assert_string_equal(filtered_msg->elements[1].name, "bar"); >- assert_int_equal(filtered_msg->elements[1].num_values, 1); >- assert_int_equal(filtered_msg->elements[1].values[0].length, >+ assert_string_equal(msg->elements[1].name, "bar"); >+ assert_int_equal(msg->elements[1].num_values, 1); >+ assert_int_equal(msg->elements[1].values[0].length, > strlen(value2)); >- assert_memory_equal(filtered_msg->elements[1].values[0].data, >+ assert_memory_equal(msg->elements[1].values[0].data, > value2, strlen(value2)); > } > >@@ -267,14 +270,13 @@ static void test_filter_attrs_two_attr_matched_attrs(void **state) > * Test against a record with two attributes, only of which is in > * the list > */ >-static void test_filter_attrs_two_attr_matched_one_attr(void **state) >+static void test_filter_attrs_in_place_two_attr_matched_one_attr(void **state) > { > struct ldbtest_ctx *ctx = *state; > int ret; > >- struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ struct ldb_message *msg = ldb_msg_new(ctx); > >- /* deliberatly the other order */ > const char *attrs[] = {"bar", NULL}; > > char value1[] = "The value.......end"; >@@ -288,7 +290,6 @@ static void test_filter_attrs_two_attr_matched_one_attr(void **state) > .length = strlen(value2) > }; > >- /* foo and bar are the other order to in attrs */ > struct ldb_message_element elements[] = { > { > .name = "foo", >@@ -301,34 +302,27 @@ static void test_filter_attrs_two_attr_matched_one_attr(void **state) > .values = &value_2 > } > }; >- struct ldb_message in = { >- .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >- .num_elements = 2, >- .elements = elements, >- }; > >- assert_non_null(in.dn); >+ assert_non_null(msg); >+ msg->dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"); >+ msg->num_elements = 2; >+ msg->elements = elements; > >- ret = ldb_filter_attrs_in_place(ctx->ldb, >- &in, >- attrs, >- filtered_msg); >+ assert_non_null(msg->dn); >+ msg_add_dn(msg); >+ >+ ret = ldb_filter_attrs_in_place(msg, attrs); > assert_int_equal(ret, LDB_SUCCESS); >- assert_non_null(filtered_msg); >- assert_int_equal(filtered_msg->num_elements, 1); >+ assert_int_equal(msg->num_elements, 1); > >- /* >- * assert the ldb_filter_attrs_in_place does not read or modify >- * filtered_msg.dn in this case >- */ >- assert_null(filtered_msg->dn); >+ assert_non_null(msg->dn); > > /* Assert that DB order is preserved */ >- assert_string_equal(filtered_msg->elements[0].name, "bar"); >- assert_int_equal(filtered_msg->elements[0].num_values, 1); >- assert_int_equal(filtered_msg->elements[0].values[0].length, >+ assert_string_equal(msg->elements[0].name, "bar"); >+ assert_int_equal(msg->elements[0].num_values, 1); >+ assert_int_equal(msg->elements[0].values[0].length, > strlen(value2)); >- assert_memory_equal(filtered_msg->elements[0].values[0].data, >+ assert_memory_equal(msg->elements[0].values[0].data, > value2, strlen(value2)); > } > >@@ -336,14 +330,13 @@ static void test_filter_attrs_two_attr_matched_one_attr(void **state) > * Test against a record with two attributes, both matching the one > * specified attribute in the list (a corrupt record) > */ >-static void test_filter_attrs_two_dup_attr_matched_one_attr(void **state) >+static void test_filter_attrs_in_place_two_dup_attr_matched_one_attr(void **state) > { > struct ldbtest_ctx *ctx = *state; > int ret; > >- struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ struct ldb_message *msg = ldb_msg_new(ctx); > >- /* deliberatly the other order */ > const char *attrs[] = {"bar", NULL}; > > char value1[] = "The value.......end"; >@@ -357,7 +350,6 @@ static void test_filter_attrs_two_dup_attr_matched_one_attr(void **state) > .length = strlen(value2) > }; > >- /* foo and bar are the other order to in attrs */ > struct ldb_message_element elements[] = { > { > .name = "bar", >@@ -370,34 +362,49 @@ static void test_filter_attrs_two_dup_attr_matched_one_attr(void **state) > .values = &value_2 > } > }; >- struct ldb_message in = { >- .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >- .num_elements = 2, >- .elements = elements, >- }; > >- assert_non_null(in.dn); >+ assert_non_null(msg); >+ msg->dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"); >+ msg->num_elements = 2; >+ msg->elements = elements; >+ >+ assert_non_null(msg->dn); >+ msg_add_dn(msg); >+ >+ ret = ldb_filter_attrs_in_place(msg, attrs); > >- ret = ldb_filter_attrs_in_place(ctx->ldb, >- &in, >- attrs, >- filtered_msg); >+ /* Both elements match the filter */ >+ assert_int_equal(ret, LDB_SUCCESS); >+ assert_int_equal(msg->num_elements, 2); >+ >+ assert_non_null(msg->dn); > >- /* This should fail the pidgenhole test */ >- assert_int_equal(ret, -1); >- assert_null(filtered_msg->elements); >+ /* Assert that DB order is preserved */ >+ assert_string_equal(msg->elements[0].name, "bar"); >+ assert_int_equal(msg->elements[0].num_values, 1); >+ assert_int_equal(msg->elements[0].values[0].length, >+ strlen(value1)); >+ assert_memory_equal(msg->elements[0].values[0].data, >+ value1, strlen(value1)); >+ >+ assert_string_equal(msg->elements[1].name, "bar"); >+ assert_int_equal(msg->elements[1].num_values, 1); >+ assert_int_equal(msg->elements[1].values[0].length, >+ strlen(value2)); >+ assert_memory_equal(msg->elements[1].values[0].data, >+ value2, strlen(value2)); > } > > /* > * Test against a record with two attributes, both matching the one > * specified attribute in the list (a corrupt record) > */ >-static void test_filter_attrs_two_dup_attr_matched_dup(void **state) >+static void test_filter_attrs_in_place_two_dup_attr_matched_dup(void **state) > { > struct ldbtest_ctx *ctx = *state; > int ret; > >- struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ struct ldb_message *msg = ldb_msg_new(ctx); > > const char *attrs[] = {"bar", "bar", NULL}; > >@@ -412,7 +419,6 @@ static void test_filter_attrs_two_dup_attr_matched_dup(void **state) > .length = strlen(value2) > }; > >- /* foo and bar are the other order to in attrs */ > struct ldb_message_element elements[] = { > { > .name = "bar", >@@ -425,35 +431,33 @@ static void test_filter_attrs_two_dup_attr_matched_dup(void **state) > .values = &value_2 > } > }; >- struct ldb_message in = { >- .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >- .num_elements = 2, >- .elements = elements, >- }; > >- assert_non_null(in.dn); >+ assert_non_null(msg); >+ msg->dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"); >+ msg->num_elements = 2; >+ msg->elements = elements; >+ >+ assert_non_null(msg->dn); >+ msg_add_dn(msg); > >- ret = ldb_filter_attrs_in_place(ctx->ldb, >- &in, >- attrs, >- filtered_msg); >+ ret = ldb_filter_attrs_in_place(msg, attrs); > > /* This does not fail the pidgenhole test */ > assert_int_equal(ret, LDB_SUCCESS); >- assert_int_equal(filtered_msg->num_elements, 2); >+ assert_int_equal(msg->num_elements, 2); > > /* Assert that DB order is preserved */ >- assert_string_equal(filtered_msg->elements[0].name, "bar"); >- assert_int_equal(filtered_msg->elements[0].num_values, 1); >- assert_int_equal(filtered_msg->elements[0].values[0].length, >+ assert_string_equal(msg->elements[0].name, "bar"); >+ assert_int_equal(msg->elements[0].num_values, 1); >+ assert_int_equal(msg->elements[0].values[0].length, > strlen(value1)); >- assert_memory_equal(filtered_msg->elements[0].values[0].data, >+ assert_memory_equal(msg->elements[0].values[0].data, > value1, strlen(value1)); >- assert_string_equal(filtered_msg->elements[1].name, "bar"); >- assert_int_equal(filtered_msg->elements[1].num_values, 1); >- assert_int_equal(filtered_msg->elements[1].values[0].length, >+ assert_string_equal(msg->elements[1].name, "bar"); >+ assert_int_equal(msg->elements[1].num_values, 1); >+ assert_int_equal(msg->elements[1].values[0].length, > strlen(value2)); >- assert_memory_equal(filtered_msg->elements[1].values[0].data, >+ assert_memory_equal(msg->elements[1].values[0].data, > value2, strlen(value2)); > } > >@@ -461,12 +465,12 @@ static void test_filter_attrs_two_dup_attr_matched_dup(void **state) > * Test against a record with two attributes, both matching one of the > * specified attributes in the list (a corrupt record) > */ >-static void test_filter_attrs_two_dup_attr_matched_one_of_two(void **state) >+static void test_filter_attrs_in_place_two_dup_attr_matched_one_of_two(void **state) > { > struct ldbtest_ctx *ctx = *state; > int ret; > >- struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ struct ldb_message *msg = ldb_msg_new(ctx); > > const char *attrs[] = {"bar", "foo", NULL}; > >@@ -481,7 +485,6 @@ static void test_filter_attrs_two_dup_attr_matched_one_of_two(void **state) > .length = strlen(value2) > }; > >- /* foo and bar are the other order to in attrs */ > struct ldb_message_element elements[] = { > { > .name = "bar", >@@ -494,35 +497,33 @@ static void test_filter_attrs_two_dup_attr_matched_one_of_two(void **state) > .values = &value_2 > } > }; >- struct ldb_message in = { >- .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >- .num_elements = 2, >- .elements = elements, >- }; > >- assert_non_null(in.dn); >+ assert_non_null(msg); >+ msg->dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"); >+ msg->num_elements = 2; >+ msg->elements = elements; > >- ret = ldb_filter_attrs_in_place(ctx->ldb, >- &in, >- attrs, >- filtered_msg); >+ assert_non_null(msg->dn); >+ msg_add_dn(msg); >+ >+ ret = ldb_filter_attrs_in_place(msg, attrs); > > /* This does not fail the pidgenhole test */ > assert_int_equal(ret, LDB_SUCCESS); >- assert_int_equal(filtered_msg->num_elements, 2); >+ assert_int_equal(msg->num_elements, 2); > > /* Assert that DB order is preserved */ >- assert_string_equal(filtered_msg->elements[0].name, "bar"); >- assert_int_equal(filtered_msg->elements[0].num_values, 1); >- assert_int_equal(filtered_msg->elements[0].values[0].length, >+ assert_string_equal(msg->elements[0].name, "bar"); >+ assert_int_equal(msg->elements[0].num_values, 1); >+ assert_int_equal(msg->elements[0].values[0].length, > strlen(value1)); >- assert_memory_equal(filtered_msg->elements[0].values[0].data, >+ assert_memory_equal(msg->elements[0].values[0].data, > value1, strlen(value1)); >- assert_string_equal(filtered_msg->elements[1].name, "bar"); >- assert_int_equal(filtered_msg->elements[1].num_values, 1); >- assert_int_equal(filtered_msg->elements[1].values[0].length, >+ assert_string_equal(msg->elements[1].name, "bar"); >+ assert_int_equal(msg->elements[1].num_values, 1); >+ assert_int_equal(msg->elements[1].values[0].length, > strlen(value2)); >- assert_memory_equal(filtered_msg->elements[1].values[0].data, >+ assert_memory_equal(msg->elements[1].values[0].data, > value2, strlen(value2)); > } > >@@ -530,12 +531,12 @@ static void test_filter_attrs_two_dup_attr_matched_one_of_two(void **state) > * Test against a record with two attributes against * (but not the > * other named attribute) (a corrupt record) > */ >-static void test_filter_attrs_two_dup_attr_matched_star(void **state) >+static void test_filter_attrs_in_place_two_dup_attr_matched_star(void **state) > { > struct ldbtest_ctx *ctx = *state; > int ret; > >- struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ struct ldb_message *msg = ldb_msg_new(ctx); > > const char *attrs[] = {"*", "foo", NULL}; > >@@ -550,7 +551,6 @@ static void test_filter_attrs_two_dup_attr_matched_star(void **state) > .length = strlen(value2) > }; > >- /* foo and bar are the other order to in attrs */ > struct ldb_message_element elements[] = { > { > .name = "bar", >@@ -563,60 +563,52 @@ static void test_filter_attrs_two_dup_attr_matched_star(void **state) > .values = &value_2 > } > }; >- struct ldb_message in = { >- .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >- .num_elements = 2, >- .elements = elements, >- }; > >- assert_non_null(in.dn); >+ assert_non_null(msg); >+ msg->dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"); >+ msg->num_elements = 2; >+ msg->elements = elements; > >- /* Needed as * implies distinguishedName */ >- filtered_msg->dn = in.dn; >+ assert_non_null(msg->dn); >+ msg_add_dn(msg); > >- ret = ldb_filter_attrs_in_place(ctx->ldb, >- &in, >- attrs, >- filtered_msg); >+ ret = ldb_filter_attrs_in_place(msg, attrs); > > /* This does not fail the pidgenhole test */ > assert_int_equal(ret, LDB_SUCCESS); >- assert_int_equal(filtered_msg->num_elements, 3); >+ assert_int_equal(msg->num_elements, 3); > > /* Assert that DB order is preserved */ >- assert_string_equal(filtered_msg->elements[0].name, "bar"); >- assert_int_equal(filtered_msg->elements[0].num_values, 1); >- assert_int_equal(filtered_msg->elements[0].values[0].length, >+ assert_string_equal(msg->elements[0].name, "bar"); >+ assert_int_equal(msg->elements[0].num_values, 1); >+ assert_int_equal(msg->elements[0].values[0].length, > strlen(value1)); >- assert_memory_equal(filtered_msg->elements[0].values[0].data, >+ assert_memory_equal(msg->elements[0].values[0].data, > value1, strlen(value1)); >- assert_string_equal(filtered_msg->elements[1].name, "bar"); >- assert_int_equal(filtered_msg->elements[1].num_values, 1); >- assert_int_equal(filtered_msg->elements[1].values[0].length, >+ assert_string_equal(msg->elements[1].name, "bar"); >+ assert_int_equal(msg->elements[1].num_values, 1); >+ assert_int_equal(msg->elements[1].values[0].length, > strlen(value2)); >- assert_memory_equal(filtered_msg->elements[1].values[0].data, >+ assert_memory_equal(msg->elements[1].values[0].data, > value2, strlen(value2)); >- /* >- * assert the ldb_filter_attrs_in_place does not modify filtered_msg.dn >- * in this case >- */ >- assert_ptr_equal(filtered_msg->dn, in.dn); >- assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ >+ assert_non_null(msg->dn); >+ assert_string_equal(ldb_msg_find_attr_as_string(msg, > "distinguishedName", > NULL), >- ldb_dn_get_linearized(in.dn)); >+ ldb_dn_get_linearized(msg->dn)); > } > > /* > * Test against a record with only one attribute, matching the * in > * the list > */ >-static void test_filter_attrs_one_attr_matched_star(void **state) >+static void test_filter_attrs_in_place_one_attr_matched_star(void **state) > { > struct ldbtest_ctx *ctx = *state; > int ret; > >- struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ struct ldb_message *msg = ldb_msg_new(ctx); > > const char *attrs[] = {"*", NULL}; > >@@ -630,35 +622,25 @@ static void test_filter_attrs_one_attr_matched_star(void **state) > .num_values = 1, > .values = &value_1 > }; >- struct ldb_message in = { >- .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >- .num_elements = 1, >- .elements = &element_1, >- }; > >- assert_non_null(in.dn); >+ assert_non_null(msg); >+ msg->dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"); >+ msg->num_elements = 1; >+ msg->elements = &element_1; > >- /* Needed as * implies distinguishedName */ >- filtered_msg->dn = in.dn; >+ assert_non_null(msg->dn); >+ msg_add_dn(msg); > >- ret = ldb_filter_attrs_in_place(ctx->ldb, >- &in, >- attrs, >- filtered_msg); >+ ret = ldb_filter_attrs_in_place(msg, attrs); > assert_int_equal(ret, LDB_SUCCESS); >- assert_non_null(filtered_msg); >- assert_int_equal(filtered_msg->num_elements, 2); >+ assert_int_equal(msg->num_elements, 2); > >- /* >- * assert the ldb_filter_attrs_in_place does not modify filtered_msg.dn >- * in this case >- */ >- assert_ptr_equal(filtered_msg->dn, in.dn); >- assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ assert_non_null(msg->dn); >+ assert_string_equal(ldb_msg_find_attr_as_string(msg, > "distinguishedName", > NULL), >- ldb_dn_get_linearized(in.dn)); >- assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ ldb_dn_get_linearized(msg->dn)); >+ assert_string_equal(ldb_msg_find_attr_as_string(msg, > "foo", > NULL), > value); >@@ -668,12 +650,12 @@ static void test_filter_attrs_one_attr_matched_star(void **state) > * Test against a record with two attributes, matching the * in > * the list > */ >-static void test_filter_attrs_two_attr_matched_star(void **state) >+static void test_filter_attrs_in_place_two_attr_matched_star(void **state) > { > struct ldbtest_ctx *ctx = *state; > int ret; > >- struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ struct ldb_message *msg = ldb_msg_new(ctx); > > const char *attrs[] = {"*", NULL}; > >@@ -699,39 +681,29 @@ static void test_filter_attrs_two_attr_matched_star(void **state) > .values = &value_2 > } > }; >- struct ldb_message in = { >- .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >- .num_elements = 2, >- .elements = elements, >- }; > >- assert_non_null(in.dn); >+ assert_non_null(msg); >+ msg->dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"); >+ msg->num_elements = 2; >+ msg->elements = elements; > >- /* Needed as * implies distinguishedName */ >- filtered_msg->dn = in.dn; >+ assert_non_null(msg->dn); >+ msg_add_dn(msg); > >- ret = ldb_filter_attrs_in_place(ctx->ldb, >- &in, >- attrs, >- filtered_msg); >+ ret = ldb_filter_attrs_in_place(msg, attrs); > assert_int_equal(ret, LDB_SUCCESS); >- assert_non_null(filtered_msg); >- assert_int_equal(filtered_msg->num_elements, 3); >+ assert_int_equal(msg->num_elements, 3); > >- /* >- * assert the ldb_filter_attrs_in_place does not modify filtered_msg.dn >- * in this case >- */ >- assert_ptr_equal(filtered_msg->dn, in.dn); >- assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ assert_non_null(msg->dn); >+ assert_string_equal(ldb_msg_find_attr_as_string(msg, > "distinguishedName", > NULL), >- ldb_dn_get_linearized(in.dn)); >- assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ ldb_dn_get_linearized(msg->dn)); >+ assert_string_equal(ldb_msg_find_attr_as_string(msg, > "foo", > NULL), > value1); >- assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ assert_string_equal(ldb_msg_find_attr_as_string(msg, > "bar", > NULL), > value2); >@@ -739,15 +711,15 @@ static void test_filter_attrs_two_attr_matched_star(void **state) > > /* > * Test against a record with only one attribute, matching the * in >- * the list, but without the DN being pre-filled. Fails due to need >- * to contstruct the distinguishedName >+ * the list, but without the DN being pre-filled. Succeeds, but the >+ * distinguishedName is not added. > */ >-static void test_filter_attrs_one_attr_matched_star_no_dn(void **state) >+static void test_filter_attrs_in_place_one_attr_matched_star_no_dn(void **state) > { > struct ldbtest_ctx *ctx = *state; > int ret; > >- struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ struct ldb_message *msg = ldb_msg_new(ctx); > > const char *attrs[] = {"*", NULL}; > >@@ -761,32 +733,29 @@ static void test_filter_attrs_one_attr_matched_star_no_dn(void **state) > .num_values = 1, > .values = &value_1 > }; >- struct ldb_message in = { >- .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >- .num_elements = 1, >- .elements = &element_1, >- }; > >- assert_non_null(in.dn); >+ assert_non_null(msg); >+ msg->dn = NULL; >+ msg->num_elements = 1; >+ msg->elements = &element_1; >+ >+ assert_null(msg->dn); > >- ret = ldb_filter_attrs_in_place(ctx->ldb, >- &in, >- attrs, >- filtered_msg); >- assert_int_equal(ret, -1); >- assert_null(filtered_msg->elements); >+ ret = ldb_filter_attrs_in_place(msg, attrs); >+ assert_int_equal(ret, LDB_SUCCESS); >+ assert_int_equal(msg->num_elements, 1); > } > > /* > * Test against a record with only one attribute, matching the * in > * the list plus requsesting distinguishedName > */ >-static void test_filter_attrs_one_attr_matched_star_dn(void **state) >+static void test_filter_attrs_in_place_one_attr_matched_star_dn(void **state) > { > struct ldbtest_ctx *ctx = *state; > int ret; > >- struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ struct ldb_message *msg = ldb_msg_new(ctx); > > const char *attrs[] = {"*", "distinguishedName", NULL}; > >@@ -800,33 +769,26 @@ static void test_filter_attrs_one_attr_matched_star_dn(void **state) > .num_values = 1, > .values = &value_1 > }; >- struct ldb_message in = { >- .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >- .num_elements = 1, >- .elements = &element_1, >- }; > >- assert_non_null(in.dn); >+ assert_non_null(msg); >+ msg->dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"); >+ msg->num_elements = 1; >+ msg->elements = &element_1; > >- /* Needed for distinguishedName */ >- filtered_msg->dn = in.dn; >+ assert_non_null(msg->dn); >+ msg_add_dn(msg); > >- ret = ldb_filter_attrs_in_place(ctx->ldb, >- &in, >- attrs, >- filtered_msg); >+ ret = ldb_filter_attrs_in_place(msg, attrs); > assert_int_equal(ret, LDB_SUCCESS); >- assert_non_null(filtered_msg); >- assert_int_equal(filtered_msg->num_elements, 2); >+ assert_int_equal(msg->num_elements, 2); > >- /* show that ldb_filter_attrs_in_place does not modify in.dn */ >- assert_ptr_equal(filtered_msg->dn, in.dn); >+ assert_non_null(msg->dn); > >- assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ assert_string_equal(ldb_msg_find_attr_as_string(msg, > "distinguishedName", > NULL), >- ldb_dn_get_linearized(in.dn)); >- assert_string_equal(ldb_msg_find_attr_as_string(filtered_msg, >+ ldb_dn_get_linearized(msg->dn)); >+ assert_string_equal(ldb_msg_find_attr_as_string(msg, > "foo", > NULL), > value); >@@ -836,12 +798,12 @@ static void test_filter_attrs_one_attr_matched_star_dn(void **state) > * Test against a record with only one attribute, but returning > * distinguishedName from the list (only) > */ >-static void test_filter_attrs_one_attr_matched_dn(void **state) >+static void test_filter_attrs_in_place_one_attr_matched_dn(void **state) > { > struct ldbtest_ctx *ctx = *state; > int ret; > >- struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ struct ldb_message *msg = ldb_msg_new(ctx); > > const char *attrs[] = {"distinguishedName", NULL}; > >@@ -855,43 +817,36 @@ static void test_filter_attrs_one_attr_matched_dn(void **state) > .num_values = 1, > .values = &value_1 > }; >- struct ldb_message in = { >- .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >- .num_elements = 1, >- .elements = &element_1, >- }; > >- assert_non_null(in.dn); >+ assert_non_null(msg); >+ msg->dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"); >+ msg->num_elements = 1; >+ msg->elements = &element_1; > >- /* Needed for distinguishedName */ >- filtered_msg->dn = in.dn; >+ assert_non_null(msg->dn); >+ msg_add_dn(msg); > >- ret = ldb_filter_attrs_in_place(ctx->ldb, >- &in, >- attrs, >- filtered_msg); >+ ret = ldb_filter_attrs_in_place(msg, attrs); > assert_int_equal(ret, LDB_SUCCESS); >- assert_non_null(filtered_msg); >- assert_int_equal(filtered_msg->num_elements, 1); >- >- /* show that ldb_filter_attrs_in_place does not modify in.dn */ >- assert_ptr_equal(filtered_msg->dn, in.dn); >- assert_string_equal(filtered_msg->elements[0].name, "distinguishedName"); >- assert_int_equal(filtered_msg->elements[0].num_values, 1); >- assert_string_equal(filtered_msg->elements[0].values[0].data, >- ldb_dn_get_linearized(in.dn)); >+ assert_int_equal(msg->num_elements, 1); >+ >+ assert_non_null(msg->dn); >+ assert_string_equal(msg->elements[0].name, "distinguishedName"); >+ assert_int_equal(msg->elements[0].num_values, 1); >+ assert_string_equal(msg->elements[0].values[0].data, >+ ldb_dn_get_linearized(msg->dn)); > } > > /* > * Test against a record with only one attribute, not matching the > * empty attribute list > */ >-static void test_filter_attrs_one_attr_empty_list(void **state) >+static void test_filter_attrs_in_place_one_attr_empty_list(void **state) > { > struct ldbtest_ctx *ctx = *state; > int ret; > >- struct ldb_message *filtered_msg = ldb_msg_new(ctx); >+ struct ldb_message *msg = ldb_msg_new(ctx); > > const char *attrs[] = {NULL}; > >@@ -905,82 +860,78 @@ static void test_filter_attrs_one_attr_empty_list(void **state) > .num_values = 1, > .values = &value_1 > }; >- struct ldb_message in = { >- .dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"), >- .num_elements = 1, >- .elements = &element_1, >- }; > >- assert_non_null(in.dn); >+ assert_non_null(msg); >+ msg->dn = ldb_dn_new(ctx, ctx->ldb, "dc=samba,dc=org"); >+ msg->num_elements = 1; >+ msg->elements = &element_1; >+ >+ assert_non_null(msg->dn); >+ msg_add_dn(msg); > >- ret = ldb_filter_attrs_in_place(ctx->ldb, >- &in, >- attrs, >- filtered_msg); >+ ret = ldb_filter_attrs_in_place(msg, attrs); > assert_int_equal(ret, LDB_SUCCESS); >- assert_non_null(filtered_msg); >- assert_int_equal(filtered_msg->num_elements, 0); >- assert_null(filtered_msg->dn); >- assert_null(filtered_msg->elements); >+ assert_int_equal(msg->num_elements, 0); >+ assert_non_null(msg->dn); > } > > int main(int argc, const char **argv) > { > const struct CMUnitTest tests[] = { > cmocka_unit_test_setup_teardown( >- test_filter_attrs_one_attr_matched, >+ test_filter_attrs_in_place_one_attr_matched, > setup, > teardown), > cmocka_unit_test_setup_teardown( >- test_filter_attrs_one_attr_matched_of_many, >+ test_filter_attrs_in_place_one_attr_matched_of_many, > setup, > teardown), > cmocka_unit_test_setup_teardown( >- test_filter_attrs_two_attr_matched_attrs, >+ test_filter_attrs_in_place_two_attr_matched_attrs, > setup, > teardown), > cmocka_unit_test_setup_teardown( >- test_filter_attrs_two_attr_matched_one_attr, >+ test_filter_attrs_in_place_two_attr_matched_one_attr, > setup, > teardown), > cmocka_unit_test_setup_teardown( >- test_filter_attrs_two_dup_attr_matched_one_attr, >+ test_filter_attrs_in_place_two_dup_attr_matched_one_attr, > setup, > teardown), > cmocka_unit_test_setup_teardown( >- test_filter_attrs_two_dup_attr_matched_dup, >+ test_filter_attrs_in_place_two_dup_attr_matched_dup, > setup, > teardown), > cmocka_unit_test_setup_teardown( >- test_filter_attrs_two_dup_attr_matched_one_of_two, >+ test_filter_attrs_in_place_two_dup_attr_matched_one_of_two, > setup, > teardown), > cmocka_unit_test_setup_teardown( >- test_filter_attrs_two_dup_attr_matched_star, >+ test_filter_attrs_in_place_two_dup_attr_matched_star, > setup, > teardown), > cmocka_unit_test_setup_teardown( >- test_filter_attrs_one_attr_matched_star, >+ test_filter_attrs_in_place_one_attr_matched_star, > setup, > teardown), > cmocka_unit_test_setup_teardown( >- test_filter_attrs_two_attr_matched_star, >+ test_filter_attrs_in_place_two_attr_matched_star, > setup, > teardown), > cmocka_unit_test_setup_teardown( >- test_filter_attrs_one_attr_matched_star_no_dn, >+ test_filter_attrs_in_place_one_attr_matched_star_no_dn, > setup, > teardown), > cmocka_unit_test_setup_teardown( >- test_filter_attrs_one_attr_matched_star_dn, >+ test_filter_attrs_in_place_one_attr_matched_star_dn, > setup, > teardown), > cmocka_unit_test_setup_teardown( >- test_filter_attrs_one_attr_matched_dn, >+ test_filter_attrs_in_place_one_attr_matched_dn, > setup, > teardown), > cmocka_unit_test_setup_teardown( >- test_filter_attrs_one_attr_empty_list, >+ test_filter_attrs_in_place_one_attr_empty_list, > setup, > teardown), > }; >-- >2.25.1 > > >From 9fd4da345252de1790ddc0299565723db93094d6 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Mon, 27 Feb 2023 10:31:52 +1300 >Subject: [PATCH 15/35] CVE-2023-0614 ldb: Make use of > ldb_filter_attrs_in_place() > >Change all uses of ldb_kv_filter_attrs() to use >ldb_filter_attrs_in_place() instead. This function does less work than >its predecessor, and no longer requires the allocation of a second ldb >message. Some of the work is able to be split out into separate >functions that each accomplish a single task, with a purpose to make the >code clearer. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > lib/ldb/ldb_key_value/ldb_kv.h | 6 +- > lib/ldb/ldb_key_value/ldb_kv_index.c | 27 +++++---- > lib/ldb/ldb_key_value/ldb_kv_search.c | 86 ++++++++++++++------------- > source4/torture/ldb/ldb.c | 12 ++-- > 4 files changed, 66 insertions(+), 65 deletions(-) > >diff --git a/lib/ldb/ldb_key_value/ldb_kv.h b/lib/ldb/ldb_key_value/ldb_kv.h >index ac474b04b4c..7d5a40e76e9 100644 >--- a/lib/ldb/ldb_key_value/ldb_kv.h >+++ b/lib/ldb/ldb_key_value/ldb_kv.h >@@ -301,10 +301,8 @@ int ldb_kv_search_key(struct ldb_module *module, > const struct ldb_val ldb_key, > struct ldb_message *msg, > unsigned int unpack_flags); >-int ldb_kv_filter_attrs(struct ldb_context *ldb, >- const struct ldb_message *msg, >- const char *const *attrs, >- struct ldb_message *filtered_msg); >+int ldb_kv_filter_attrs_in_place(struct ldb_message *msg, >+ const char *const *attrs); > int ldb_kv_search(struct ldb_kv_context *ctx); > > /* >diff --git a/lib/ldb/ldb_key_value/ldb_kv_index.c b/lib/ldb/ldb_key_value/ldb_kv_index.c >index d70e5f619ef..203266ea8c9 100644 >--- a/lib/ldb/ldb_key_value/ldb_kv_index.c >+++ b/lib/ldb/ldb_key_value/ldb_kv_index.c >@@ -2264,7 +2264,6 @@ static int ldb_kv_index_filter(struct ldb_kv_private *ldb_kv, > { > struct ldb_context *ldb = ldb_module_get_ctx(ac->module); > struct ldb_message *msg; >- struct ldb_message *filtered_msg; > unsigned int i; > unsigned int num_keys = 0; > uint8_t previous_guid_key[LDB_KV_GUID_KEY_SIZE] = {0}; >@@ -2456,27 +2455,31 @@ static int ldb_kv_index_filter(struct ldb_kv_private *ldb_kv, > continue; > } > >- filtered_msg = ldb_msg_new(ac); >- if (filtered_msg == NULL) { >- TALLOC_FREE(keys); >- TALLOC_FREE(msg); >+ ret = ldb_msg_add_distinguished_name(msg); >+ if (ret == -1) { >+ talloc_free(msg); > return LDB_ERR_OPERATIONS_ERROR; > } > >- filtered_msg->dn = talloc_steal(filtered_msg, msg->dn); >- > /* filter the attributes that the user wants */ >- ret = ldb_kv_filter_attrs(ldb, msg, ac->attrs, filtered_msg); >+ ret = ldb_kv_filter_attrs_in_place(msg, ac->attrs); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(keys); >+ talloc_free(msg); >+ return LDB_ERR_OPERATIONS_ERROR; >+ } > >- talloc_free(msg); >+ ldb_msg_shrink_to_fit(msg); > >- if (ret == -1) { >- TALLOC_FREE(filtered_msg); >+ /* Ensure the message elements are all talloc'd. */ >+ ret = ldb_msg_elements_take_ownership(msg); >+ if (ret != LDB_SUCCESS) { > talloc_free(keys); >+ talloc_free(msg); > return LDB_ERR_OPERATIONS_ERROR; > } > >- ret = ldb_module_send_entry(ac->req, filtered_msg, NULL); >+ ret = ldb_module_send_entry(ac->req, msg, NULL); > if (ret != LDB_SUCCESS) { > /* Regardless of success or failure, the msg > * is the callbacks responsiblity, and should >diff --git a/lib/ldb/ldb_key_value/ldb_kv_search.c b/lib/ldb/ldb_key_value/ldb_kv_search.c >index 46031b99c16..f3333510eab 100644 >--- a/lib/ldb/ldb_key_value/ldb_kv_search.c >+++ b/lib/ldb/ldb_key_value/ldb_kv_search.c >@@ -292,15 +292,13 @@ int ldb_kv_search_dn1(struct ldb_module *module, > > /* > * filter the specified list of attributes from msg, >- * adding requested attributes, and perhaps all for *, >- * but not the DN to filtered_msg. >+ * adding requested attributes, and perhaps all for *. >+ * The DN will not be added if it is missing. > */ >-int ldb_kv_filter_attrs(struct ldb_context *ldb, >- const struct ldb_message *msg, >- const char *const *attrs, >- struct ldb_message *filtered_msg) >+int ldb_kv_filter_attrs_in_place(struct ldb_message *msg, >+ const char *const *attrs) > { >- return ldb_filter_attrs(ldb, msg, attrs, filtered_msg); >+ return ldb_filter_attrs_in_place(msg, attrs); > } > > /* >@@ -313,7 +311,7 @@ static int search_func(_UNUSED_ struct ldb_kv_private *ldb_kv, > { > struct ldb_context *ldb; > struct ldb_kv_context *ac; >- struct ldb_message *msg, *filtered_msg; >+ struct ldb_message *msg; > struct timeval now; > int ret, timeval_cmp; > bool matched; >@@ -410,25 +408,31 @@ static int search_func(_UNUSED_ struct ldb_kv_private *ldb_kv, > return 0; > } > >- filtered_msg = ldb_msg_new(ac); >- if (filtered_msg == NULL) { >- TALLOC_FREE(msg); >+ ret = ldb_msg_add_distinguished_name(msg); >+ if (ret == -1) { >+ talloc_free(msg); > return LDB_ERR_OPERATIONS_ERROR; > } > >- filtered_msg->dn = talloc_steal(filtered_msg, msg->dn); >- > /* filter the attributes that the user wants */ >- ret = ldb_kv_filter_attrs(ldb, msg, ac->attrs, filtered_msg); >- talloc_free(msg); >+ ret = ldb_kv_filter_attrs_in_place(msg, ac->attrs); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(msg); >+ ac->error = LDB_ERR_OPERATIONS_ERROR; >+ return -1; >+ } > >- if (ret == -1) { >- TALLOC_FREE(filtered_msg); >+ ldb_msg_shrink_to_fit(msg); >+ >+ /* Ensure the message elements are all talloc'd. */ >+ ret = ldb_msg_elements_take_ownership(msg); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(msg); > ac->error = LDB_ERR_OPERATIONS_ERROR; > return -1; > } > >- ret = ldb_module_send_entry(ac->req, filtered_msg, NULL); >+ ret = ldb_module_send_entry(ac->req, msg, NULL); > if (ret != LDB_SUCCESS) { > ac->request_terminated = true; > /* the callback failed, abort the operation */ >@@ -491,7 +495,7 @@ static int ldb_kv_search_full(struct ldb_kv_context *ctx) > static int ldb_kv_search_and_return_base(struct ldb_kv_private *ldb_kv, > struct ldb_kv_context *ctx) > { >- struct ldb_message *msg, *filtered_msg; >+ struct ldb_message *msg; > struct ldb_context *ldb = ldb_module_get_ctx(ctx->module); > const char *dn_linearized; > const char *msg_dn_linearized; >@@ -549,12 +553,6 @@ static int ldb_kv_search_and_return_base(struct ldb_kv_private *ldb_kv, > dn_linearized = ldb_dn_get_linearized(ctx->base); > msg_dn_linearized = ldb_dn_get_linearized(msg->dn); > >- filtered_msg = ldb_msg_new(ctx); >- if (filtered_msg == NULL) { >- talloc_free(msg); >- return LDB_ERR_OPERATIONS_ERROR; >- } >- > if (strcmp(dn_linearized, msg_dn_linearized) == 0) { > /* > * If the DN is exactly the same string, then >@@ -562,36 +560,42 @@ static int ldb_kv_search_and_return_base(struct ldb_kv_private *ldb_kv, > * returned result, as it has already been > * casefolded > */ >- filtered_msg->dn = ldb_dn_copy(filtered_msg, ctx->base); >+ struct ldb_dn *dn = ldb_dn_copy(msg, ctx->base); >+ if (dn != NULL) { >+ msg->dn = dn; >+ } > } > >- /* >- * If the ldb_dn_copy() failed, or if we did not choose that >- * optimisation (filtered_msg is zeroed at allocation), >- * steal the one from the unpack >- */ >- if (filtered_msg->dn == NULL) { >- filtered_msg->dn = talloc_steal(filtered_msg, msg->dn); >+ ret = ldb_msg_add_distinguished_name(msg); >+ if (ret == -1) { >+ talloc_free(msg); >+ return LDB_ERR_OPERATIONS_ERROR; > } > > /* > * filter the attributes that the user wants. > */ >- ret = ldb_kv_filter_attrs(ldb, msg, ctx->attrs, filtered_msg); >- if (ret == -1) { >+ ret = ldb_kv_filter_attrs_in_place(msg, ctx->attrs); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(msg); >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ >+ ldb_msg_shrink_to_fit(msg); >+ >+ /* Ensure the message elements are all talloc'd. */ >+ ret = ldb_msg_elements_take_ownership(msg); >+ if (ret != LDB_SUCCESS) { > talloc_free(msg); >- filtered_msg = NULL; > return LDB_ERR_OPERATIONS_ERROR; > } > > /* >- * Remove any extended components possibly copied in from >- * msg->dn, we just want the casefold components >+ * Remove any extended components, we just want the casefold components > */ >- ldb_dn_remove_extended_components(filtered_msg->dn); >- talloc_free(msg); >+ ldb_dn_remove_extended_components(msg->dn); > >- ret = ldb_module_send_entry(ctx->req, filtered_msg, NULL); >+ ret = ldb_module_send_entry(ctx->req, msg, NULL); > if (ret != LDB_SUCCESS) { > /* Regardless of success or failure, the msg > * is the callbacks responsiblity, and should >diff --git a/source4/torture/ldb/ldb.c b/source4/torture/ldb/ldb.c >index bd0ae3a382a..c170416bec4 100644 >--- a/source4/torture/ldb/ldb.c >+++ b/source4/torture/ldb/ldb.c >@@ -1634,7 +1634,6 @@ static bool torture_ldb_unpack_and_filter(struct torture_context *torture, > TALLOC_CTX *mem_ctx = talloc_new(torture); > struct ldb_context *ldb; > struct ldb_val data = *discard_const_p(struct ldb_val, data_p); >- struct ldb_message *unpack_msg = ldb_msg_new(mem_ctx); > struct ldb_message *msg = ldb_msg_new(mem_ctx); > const char *lookup_names[] = {"instanceType", "nonexistent", > "whenChanged", "objectClass", >@@ -1649,18 +1648,15 @@ static bool torture_ldb_unpack_and_filter(struct torture_context *torture, > "Failed to init samba"); > > torture_assert_int_equal(torture, >- ldb_unpack_data(ldb, &data, unpack_msg), >+ ldb_unpack_data(ldb, &data, msg), > 0, "ldb_unpack_data failed"); > >- torture_assert_int_equal(torture, unpack_msg->num_elements, 13, >+ torture_assert_int_equal(torture, msg->num_elements, 13, > "Got wrong count of elements"); > >- msg->dn = talloc_steal(msg, unpack_msg->dn); >- > torture_assert_int_equal(torture, >- ldb_filter_attrs(ldb, unpack_msg, >- lookup_names, msg), >- 0, "ldb_kv_filter_attrs failed"); >+ ldb_filter_attrs_in_place(msg, lookup_names), >+ 0, "ldb_filter_attrs_in_place failed"); > > /* Compare data in binary form */ > torture_assert_int_equal(torture, msg->num_elements, 6, >-- >2.25.1 > > >From 375cc49d2aa3d5b66fe60ba9e0d127210a28cf85 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 7 Feb 2023 09:35:24 +1300 >Subject: [PATCH 16/35] CVE-2023-0614 s4:dsdb/extended_dn_in: Don't modify a > search tree we don't own > >In extended_dn_fix_filter() we had: > > req->op.search.tree = ldb_parse_tree_copy_shallow(req, req->op.search.tree); > >which overwrote the parse tree on an existing ldb request with a fixed >up tree. This became a problem if a module performed another search with >that same request structure, as extended_dn_in would try to fix up the >already-modified tree for a second time. The fixed-up tree element now >having an extended DN, it would fall foul of the ldb_dn_match_allowed() >check in extended_dn_filter_callback(), and be replaced with an >ALWAYS_FALSE match rule. In practice this meant that <GUID={}> searches >would only work for one search in an ldb request, and fail for >subsequent ones. > >Fix this by creating a new request with the modified tree, and leaving >the original request unmodified. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > .../dsdb/samdb/ldb_modules/extended_dn_in.c | 40 +++++++++++++++---- > 1 file changed, 32 insertions(+), 8 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/extended_dn_in.c b/source4/dsdb/samdb/ldb_modules/extended_dn_in.c >index 2d0513a738b..1dc1e1f2d42 100644 >--- a/source4/dsdb/samdb/ldb_modules/extended_dn_in.c >+++ b/source4/dsdb/samdb/ldb_modules/extended_dn_in.c >@@ -48,6 +48,7 @@ > struct extended_search_context { > struct ldb_module *module; > struct ldb_request *req; >+ struct ldb_parse_tree *tree; > struct ldb_dn *basedn; > struct ldb_dn *dn; > char *wellknown_object; >@@ -200,7 +201,7 @@ static int extended_base_callback(struct ldb_request *req, struct ldb_reply *are > ldb_module_get_ctx(ac->module), ac->req, > ac->basedn, > ac->req->op.search.scope, >- ac->req->op.search.tree, >+ ac->tree, > ac->req->op.search.attrs, > ac->req->controls, > ac, extended_final_callback, >@@ -515,11 +516,14 @@ static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *privat > */ > static int extended_dn_fix_filter(struct ldb_module *module, > struct ldb_request *req, >- uint32_t default_dsdb_flags) >+ uint32_t default_dsdb_flags, >+ struct ldb_parse_tree **down_tree) > { > struct extended_dn_filter_ctx *filter_ctx; > int ret; > >+ *down_tree = NULL; >+ > filter_ctx = talloc_zero(req, struct extended_dn_filter_ctx); > if (filter_ctx == NULL) { > return ldb_module_oom(module); >@@ -550,12 +554,12 @@ static int extended_dn_fix_filter(struct ldb_module *module, > filter_ctx->test_only = false; > filter_ctx->matched = false; > >- req->op.search.tree = ldb_parse_tree_copy_shallow(req, req->op.search.tree); >- if (req->op.search.tree == NULL) { >+ *down_tree = ldb_parse_tree_copy_shallow(req, req->op.search.tree); >+ if (*down_tree == NULL) { > return ldb_oom(ldb_module_get_ctx(module)); > } > >- ret = ldb_parse_tree_walk(req->op.search.tree, extended_dn_filter_callback, filter_ctx); >+ ret = ldb_parse_tree_walk(*down_tree, extended_dn_filter_callback, filter_ctx); > if (ret != LDB_SUCCESS) { > talloc_free(filter_ctx); > return ret; >@@ -572,7 +576,8 @@ static int extended_dn_fix_filter(struct ldb_module *module, > static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn) > { > struct extended_search_context *ac; >- struct ldb_request *down_req; >+ struct ldb_request *down_req = NULL; >+ struct ldb_parse_tree *down_tree = NULL; > int ret; > struct ldb_dn *base_dn = NULL; > enum ldb_scope base_dn_scope = LDB_SCOPE_BASE; >@@ -595,7 +600,7 @@ static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req > } > > if (req->operation == LDB_SEARCH) { >- ret = extended_dn_fix_filter(module, req, dsdb_flags); >+ ret = extended_dn_fix_filter(module, req, dsdb_flags, &down_tree); > if (ret != LDB_SUCCESS) { > return ret; > } >@@ -603,7 +608,25 @@ static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req > > if (!ldb_dn_has_extended(dn)) { > /* Move along there isn't anything to see here */ >- return ldb_next_request(module, req); >+ if (down_tree == NULL) { >+ down_req = req; >+ } else { >+ ret = ldb_build_search_req_ex(&down_req, >+ ldb_module_get_ctx(module), req, >+ req->op.search.base, >+ req->op.search.scope, >+ down_tree, >+ req->op.search.attrs, >+ req->controls, >+ req, dsdb_next_callback, >+ req); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ LDB_REQ_SET_LOCATION(down_req); >+ } >+ >+ return ldb_next_request(module, down_req); > } else { > /* It looks like we need to map the DN */ > const struct ldb_val *sid_val, *guid_val, *wkguid_val; >@@ -690,6 +713,7 @@ static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req > > ac->module = module; > ac->req = req; >+ ac->tree = (down_tree != NULL) ? down_tree : req->op.search.tree; > ac->dn = dn; > ac->basedn = NULL; /* Filled in if the search finds the DN by SID/GUID etc */ > ac->wellknown_object = wellknown_object; >-- >2.25.1 > > >From d39b29e6403956349351829f616b9f237f872590 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 7 Feb 2023 09:48:37 +1300 >Subject: [PATCH 17/35] CVE-2023-0614 s4:dsdb:tests: Fix <GUID={}> search in > confidential attributes test > >The object returned by schema_format_value() is a bytes object. >Therefore the search expression would resemble: > >(lastKnownParent=<GUID=b'00000000-0000-0000-0000-000000000000'>) > >which, due to the extra characters, would fail to match anything. > >Fix it to be: > >(lastKnownParent=<GUID=00000000-0000-0000-0000-000000000000>) > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/dsdb/tests/python/confidential_attr.py | 6 +++--- > 1 file changed, 3 insertions(+), 3 deletions(-) > >diff --git a/source4/dsdb/tests/python/confidential_attr.py b/source4/dsdb/tests/python/confidential_attr.py >index d5c7785485a..1c9c456917a 100755 >--- a/source4/dsdb/tests/python/confidential_attr.py >+++ b/source4/dsdb/tests/python/confidential_attr.py >@@ -924,12 +924,12 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): > self.assert_negative_searches(has_rights_to="all", > samdb=self.ldb_admin) > >- def get_guid(self, dn): >+ def get_guid_string(self, dn): > """Returns an object's GUID (in string format)""" > res = self.ldb_admin.search(base=dn, attrs=["objectGUID"], > scope=SCOPE_BASE) > guid = res[0]['objectGUID'][0] >- return self.ldb_admin.schema_format_value("objectGUID", guid) >+ return self.ldb_admin.schema_format_value("objectGUID", guid).decode('utf-8') > > def make_attr_preserve_on_delete(self): > """Marks the attribute under test as being preserve on delete""" >@@ -978,7 +978,7 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): > # deleted objects, but only from this particular test run. We can do > # this by matching lastKnownParent against this test case's OU, which > # will match any deleted child objects. >- ou_guid = self.get_guid(self.ou) >+ ou_guid = self.get_guid_string(self.ou) > deleted_filter = "(lastKnownParent=<GUID={0}>)".format(ou_guid) > > # the extra-filter will get combined via AND with the search expression >-- >2.25.1 > > >From fb9f8dd2e765e6c44f1ac762d1bcbce4618ce24b Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Thu, 25 Aug 2022 20:15:33 +1200 >Subject: [PATCH 18/35] schema_samba4.ldif: Allocate previously added OIDs > >DSDB_CONTROL_FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE_OID was added >to source4/dsdb/samdb/samdb.h in commit >c2ab1f4696fa3f52918a126d0b37993a07f68bcb. > >DSDB_EXTENDED_SCHEMA_LOAD was added in commit >1fd4cdfafaa6a41c824d1b3d76635bf3e446de0f. > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 672ec6135f9ae3d7b5439523a4f456c19fb03a88) > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >[abartlet@samba.org This required as context for the above bug] >--- > source4/setup/schema_samba4.ldif | 2 ++ > 1 file changed, 2 insertions(+) > >diff --git a/source4/setup/schema_samba4.ldif b/source4/setup/schema_samba4.ldif >index a31b67750d4..54f48bde20e 100644 >--- a/source4/setup/schema_samba4.ldif >+++ b/source4/setup/schema_samba4.ldif >@@ -231,6 +231,7 @@ > #Allocated: DSDB_CONTROL_INVALID_NOT_IMPLEMENTED 1.3.6.1.4.1.7165.4.3.32 > #Allocated: DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID 1.3.6.1.4.1.7165.4.3.33 > #Allocated: DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID 1.3.6.1.4.1.7165.4.3.34 >+#Allocated: DSDB_CONTROL_FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE_OID 1.3.6.1.4.1.7165.4.3.35 > > > # Extended 1.3.6.1.4.1.7165.4.4.x >@@ -243,6 +244,7 @@ > #Allocated: DSDB_EXTENDED_SEC_DESC_PROPAGATION_OID 1.3.6.1.4.1.7165.4.4.7 > #Allocated: DSDB_EXTENDED_CREATE_OWN_RID_SET 1.3.6.1.4.1.7165.4.4.8 > #Allocated: DSDB_EXTENDED_ALLOCATE_RID 1.3.6.1.4.1.7165.4.4.9 >+#Allocated: DSDB_EXTENDED_SCHEMA_LOAD 1.3.6.1.4.1.7165.4.4.10 > > > ############ >-- >2.25.1 > > >From ab1fe129a48412ccdee600321b9f55f5c66bcc83 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 7 Feb 2023 09:25:48 +1300 >Subject: [PATCH 19/35] CVE-2023-0614 schema_samba4.ldif: Allocate previously > added OID > >DSDB_CONTROL_CALCULATED_DEFAULT_SD_OID was added in commit >08187833fee57a8dba6c67546dfca516cd1f9d7a. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/setup/schema_samba4.ldif | 1 + > 1 file changed, 1 insertion(+) > >diff --git a/source4/setup/schema_samba4.ldif b/source4/setup/schema_samba4.ldif >index 54f48bde20e..79800bfd6df 100644 >--- a/source4/setup/schema_samba4.ldif >+++ b/source4/setup/schema_samba4.ldif >@@ -232,6 +232,7 @@ > #Allocated: DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID 1.3.6.1.4.1.7165.4.3.33 > #Allocated: DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID 1.3.6.1.4.1.7165.4.3.34 > #Allocated: DSDB_CONTROL_FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE_OID 1.3.6.1.4.1.7165.4.3.35 >+#Allocated: DSDB_CONTROL_CALCULATED_DEFAULT_SD_OID 1.3.6.1.4.1.7165.4.3.36 > > > # Extended 1.3.6.1.4.1.7165.4.4.x >-- >2.25.1 > > >From 36856571e78468994ef94245c066f162f0a202f4 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 27 Jan 2023 08:32:41 +1300 >Subject: [PATCH 20/35] CVE-2023-0614 tests/krb5: Add test for confidential > attributes timing differences > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/confidential-attr-timing | 1 + > .../dsdb/tests/python/confidential_attr.py | 162 ++++++++++++++++++ > 2 files changed, 163 insertions(+) > create mode 100644 selftest/knownfail.d/confidential-attr-timing > >diff --git a/selftest/knownfail.d/confidential-attr-timing b/selftest/knownfail.d/confidential-attr-timing >new file mode 100644 >index 00000000000..e213cdb16d3 >--- /dev/null >+++ b/selftest/knownfail.d/confidential-attr-timing >@@ -0,0 +1 @@ >+^samba4.ldap.confidential_attr.python\(ad_dc_slowtests\).__main__.ConfidentialAttrTestDirsync.test_timing_attack\(ad_dc_slowtests\) >diff --git a/source4/dsdb/tests/python/confidential_attr.py b/source4/dsdb/tests/python/confidential_attr.py >index 1c9c456917a..031c9690ba6 100755 >--- a/source4/dsdb/tests/python/confidential_attr.py >+++ b/source4/dsdb/tests/python/confidential_attr.py >@@ -25,6 +25,9 @@ sys.path.insert(0, "bin/python") > > import samba > import os >+import random >+import statistics >+import time > from samba.tests.subunitrun import SubunitOptions, TestProgram > import samba.getopt as options > from ldb import SCOPE_BASE, SCOPE_SUBTREE >@@ -1022,4 +1025,163 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): > self.assert_conf_attr_searches(has_rights_to=0) > self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode) > >+ def test_timing_attack(self): >+ # Create the machine account. >+ mach_name = f'conf_timing_{random.randint(0, 0xffff)}' >+ mach_dn = Dn(self.ldb_admin, f'CN={mach_name},{self.ou}') >+ details = { >+ 'dn': mach_dn, >+ 'objectclass': 'computer', >+ 'sAMAccountName': f'{mach_name}$', >+ } >+ self.ldb_admin.add(details) >+ >+ # Get the machine account's GUID. >+ res = self.ldb_admin.search(mach_dn, >+ attrs=['objectGUID'], >+ scope=SCOPE_BASE) >+ mach_guid = res[0].get('objectGUID', idx=0) >+ >+ # Now we can create an msFVE-RecoveryInformation object that is a child >+ # of the machine account object. >+ recovery_dn = Dn(self.ldb_admin, str(mach_dn)) >+ recovery_dn.add_child('CN=recovery_info') >+ >+ secret_pw = 'Secret007' >+ not_secret_pw = 'Secret008' >+ >+ secret_pw_utf8 = secret_pw.encode('utf-8') >+ >+ # The crucial attribute, msFVE-RecoveryPassword, is a confidential >+ # attribute. >+ conf_attr = 'msFVE-RecoveryPassword' >+ >+ m = Message(recovery_dn) >+ m['objectClass'] = 'msFVE-RecoveryInformation' >+ m['msFVE-RecoveryGuid'] = mach_guid >+ m[conf_attr] = secret_pw >+ self.ldb_admin.add(m) >+ >+ attrs = [conf_attr] >+ >+ # Search for the confidential attribute as administrator, ensuring it >+ # is visible. >+ res = self.ldb_admin.search(recovery_dn, >+ attrs=attrs, >+ scope=SCOPE_BASE) >+ self.assertEqual(1, len(res)) >+ pw = res[0].get(conf_attr, idx=0) >+ self.assertEqual(secret_pw_utf8, pw) >+ >+ # Repeat the search with an expression matching on the confidential >+ # attribute. This should also work. >+ res = self.ldb_admin.search( >+ recovery_dn, >+ attrs=attrs, >+ expression=f'({conf_attr}={secret_pw})', >+ scope=SCOPE_BASE) >+ self.assertEqual(1, len(res)) >+ pw = res[0].get(conf_attr, idx=0) >+ self.assertEqual(secret_pw_utf8, pw) >+ >+ # Search for the attribute as an unprivileged user. It should not be >+ # visible. >+ user_res = self.ldb_user.search(recovery_dn, >+ attrs=attrs, >+ scope=SCOPE_BASE) >+ pw = user_res[0].get(conf_attr, idx=0) >+ # The attribute should be None. >+ self.assertIsNone(pw) >+ >+ # We use LDAP_MATCHING_RULE_TRANSITIVE_EVAL to create a search >+ # expression that takes a long time to execute, by setting off another >+ # search each time it is evaluated. It makes no difference that the >+ # object on which we're searching has no 'member' attribute. >+ dummy_dn = 'cn=user,cn=users,dc=samba,dc=example,dc=com' >+ slow_subexpr = f'(member:1.2.840.113556.1.4.1941:={dummy_dn})' >+ slow_expr = f'(|{slow_subexpr * 100})' >+ >+ # The full search expression. It comprises a match on the confidential >+ # attribute joined by an AND to our slow search expression, The AND >+ # operator is short-circuiting, so if our first subexpression fails to >+ # match, we'll bail out of the search early. Otherwise, we'll evaluate >+ # the slow part; as its subexpressions are joined by ORs, and will all >+ # fail to match, every one of them will need to be evaluated. By >+ # measuring how long the search takes, we'll be able to infer whether >+ # the confidential attribute matched or not. >+ >+ # This is bad if we are not an administrator, and are able to use this >+ # to determine the values of confidential attributes. Therefore we need >+ # to ensure we can't observe any difference in timing. >+ correct_expr = f'(&({conf_attr}={secret_pw}){slow_expr})' >+ wrong_expr = f'(&({conf_attr}={not_secret_pw}){slow_expr})' >+ >+ def standard_uncertainty_bounds(times): >+ mean = statistics.mean(times) >+ stdev = statistics.stdev(times, mean) >+ >+ return (mean - stdev, mean + stdev) >+ >+ # Perform a number of searches with both correct and incorrect >+ # expressions, and return the uncertainty bounds for each. >+ def time_searches(samdb): >+ warmup_samples = 3 >+ samples = 10 >+ matching_times = [] >+ non_matching_times = [] >+ >+ for _ in range(warmup_samples): >+ samdb.search(recovery_dn, >+ attrs=attrs, >+ expression=correct_expr, >+ scope=SCOPE_BASE) >+ >+ for _ in range(samples): >+ # Measure the time taken for a search, for both a matching and >+ # a non-matching search expression. >+ >+ prev = time.time() >+ samdb.search(recovery_dn, >+ attrs=attrs, >+ expression=correct_expr, >+ scope=SCOPE_BASE) >+ now = time.time() >+ matching_times.append(now - prev) >+ >+ prev = time.time() >+ samdb.search(recovery_dn, >+ attrs=attrs, >+ expression=wrong_expr, >+ scope=SCOPE_BASE) >+ now = time.time() >+ non_matching_times.append(now - prev) >+ >+ matching = standard_uncertainty_bounds(matching_times) >+ non_matching = standard_uncertainty_bounds(non_matching_times) >+ return matching, non_matching >+ >+ def assertRangesDistinct(a, b): >+ a0, a1 = a >+ b0, b1 = b >+ self.assertLess(min(a1, b1), max(a0, b0)) >+ >+ def assertRangesOverlap(a, b): >+ a0, a1 = a >+ b0, b1 = b >+ self.assertGreaterEqual(min(a1, b1), max(a0, b0)) >+ >+ # For an administrator, the uncertainty bounds for matching and >+ # non-matching searches should be distinct. This shows that the two >+ # cases are distinguishable, and therefore that confidential attributes >+ # are visible. >+ admin_matching, admin_non_matching = time_searches(self.ldb_admin) >+ assertRangesDistinct(admin_matching, admin_non_matching) >+ >+ # The user cannot view the confidential attribute, so the uncertainty >+ # bounds for matching and non-matching searches must overlap. The two >+ # cases must be indistinguishable. >+ user_matching, user_non_matching = time_searches(self.ldb_user) >+ assertRangesOverlap(user_matching, user_non_matching) >+ >+ > TestProgram(module=__name__, opts=subunitopts) >-- >2.25.1 > > >From 1d792b0853c94f05c29ba31b362635011236db29 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 3 Mar 2023 17:31:54 +1300 >Subject: [PATCH 21/35] CVE-2023-0614 ldb: Add ldb_parse_tree_get_attr() > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > lib/ldb/common/ldb_parse.c | 25 +++++++++++++++++++++++++ > lib/ldb/include/ldb_module.h | 3 +++ > 2 files changed, 28 insertions(+) > >diff --git a/lib/ldb/common/ldb_parse.c b/lib/ldb/common/ldb_parse.c >index f0045ad2093..2d102ff750e 100644 >--- a/lib/ldb/common/ldb_parse.c >+++ b/lib/ldb/common/ldb_parse.c >@@ -997,3 +997,28 @@ struct ldb_parse_tree *ldb_parse_tree_copy_shallow(TALLOC_CTX *mem_ctx, > > return nt; > } >+ >+/* Get the attribute (if any) associated with the top node of a parse tree. */ >+const char *ldb_parse_tree_get_attr(const struct ldb_parse_tree *tree) >+{ >+ switch (tree->operation) { >+ case LDB_OP_AND: >+ case LDB_OP_OR: >+ case LDB_OP_NOT: >+ return NULL; >+ case LDB_OP_EQUALITY: >+ return tree->u.equality.attr; >+ case LDB_OP_SUBSTRING: >+ return tree->u.substring.attr; >+ case LDB_OP_GREATER: >+ case LDB_OP_LESS: >+ case LDB_OP_APPROX: >+ return tree->u.comparison.attr; >+ case LDB_OP_PRESENT: >+ return tree->u.present.attr; >+ case LDB_OP_EXTENDED: >+ return tree->u.extended.attr; >+ } >+ >+ return NULL; >+} >diff --git a/lib/ldb/include/ldb_module.h b/lib/ldb/include/ldb_module.h >index 4ae381ba5be..bd369ed9512 100644 >--- a/lib/ldb/include/ldb_module.h >+++ b/lib/ldb/include/ldb_module.h >@@ -490,6 +490,9 @@ int ldb_init_module(const char *version); > */ > bool ldb_dn_replace_components(struct ldb_dn *dn, struct ldb_dn *new_dn); > >+/* Get the attribute (if any) associated with the top node of a parse tree. */ >+const char *ldb_parse_tree_get_attr(const struct ldb_parse_tree *tree); >+ > /* > walk a parse tree, calling the provided callback on each node > */ >-- >2.25.1 > > >From 3d540c63e4b1e7b37b1f9c096bd8d61d2bee2953 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Mon, 27 Feb 2023 13:40:33 +1300 >Subject: [PATCH 22/35] CVE-2023-0614 s4-acl: Split out logic to remove access > checking attributes > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/dsdb/samdb/ldb_modules/acl_read.c | 58 ++++++++++++++--------- > 1 file changed, 35 insertions(+), 23 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/acl_read.c b/source4/dsdb/samdb/ldb_modules/acl_read.c >index 8814a816797..3980c44345e 100644 >--- a/source4/dsdb/samdb/ldb_modules/acl_read.c >+++ b/source4/dsdb/samdb/ldb_modules/acl_read.c >@@ -545,6 +545,39 @@ static int check_search_ops_access(struct aclread_context *ac, > return ret; > } > >+/* >+ * Whether this attribute was added to perform access checks and must be >+ * removed. >+ */ >+static bool should_remove_attr(const char *attr, const struct aclread_context *ac) >+{ >+ if (ac->added_nTSecurityDescriptor && >+ ldb_attr_cmp("nTSecurityDescriptor", attr) == 0) >+ { >+ return true; >+ } >+ >+ if (ac->added_objectSid && >+ ldb_attr_cmp("objectSid", attr) == 0) >+ { >+ return true; >+ } >+ >+ if (ac->added_instanceType && >+ ldb_attr_cmp("instanceType", attr) == 0) >+ { >+ return true; >+ } >+ >+ if (ac->added_objectClass && >+ ldb_attr_cmp("objectClass", attr) == 0) >+ { >+ return true; >+ } >+ >+ return false; >+} >+ > static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > { > struct ldb_context *ldb; >@@ -619,7 +652,6 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > /* for every element in the message check RP */ > for (i=0; i < msg->num_elements; i++) { > const struct dsdb_attribute *attr; >- bool is_sd, is_objectsid, is_instancetype, is_objectclass; > uint32_t access_mask; > attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, > msg->elements[i].name); >@@ -631,28 +663,8 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > ret = LDB_ERR_OPERATIONS_ERROR; > goto fail; > } >- is_sd = ldb_attr_cmp("nTSecurityDescriptor", >- msg->elements[i].name) == 0; >- is_objectsid = ldb_attr_cmp("objectSid", >- msg->elements[i].name) == 0; >- is_instancetype = ldb_attr_cmp("instanceType", >- msg->elements[i].name) == 0; >- is_objectclass = ldb_attr_cmp("objectClass", >- msg->elements[i].name) == 0; >- /* these attributes were added to perform access checks and must be removed */ >- if (is_objectsid && ac->added_objectSid) { >- ldb_msg_element_mark_inaccessible(&msg->elements[i]); >- continue; >- } >- if (is_instancetype && ac->added_instanceType) { >- ldb_msg_element_mark_inaccessible(&msg->elements[i]); >- continue; >- } >- if (is_objectclass && ac->added_objectClass) { >- ldb_msg_element_mark_inaccessible(&msg->elements[i]); >- continue; >- } >- if (is_sd && ac->added_nTSecurityDescriptor) { >+ /* Remove attributes added to perform access checks. */ >+ if (should_remove_attr(msg->elements[i].name, ac)) { > ldb_msg_element_mark_inaccessible(&msg->elements[i]); > continue; > } >-- >2.25.1 > > >From 812dceb450d8ed8b8cc473b2cbf222f6d38c8302 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Mon, 27 Feb 2023 12:19:08 +1300 >Subject: [PATCH 23/35] CVE-2023-0614 s4-dsdb: Add samdb_result_dom_sid_buf() > >This function parses a SID from an ldb_message, similar to >samdb_result_dom_sid(), but does it without allocating anything. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> > >[abartlet@samba.org Adapted for simple conflicts due to > 56297449f9c2e94505a72a70a3a3c5990d00d37f trimming > trailing whitespace] >--- > source4/dsdb/common/util.c | 22 +++++++++++++++++++++- > 1 file changed, 21 insertions(+), 1 deletion(-) > >diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c >index 02472f8a89c..1436cd7bfec 100644 >--- a/source4/dsdb/common/util.c >+++ b/source4/dsdb/common/util.c >@@ -366,7 +366,27 @@ struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, const struct ldb_messa > } > > /* >- pull a guid structure from a objectGUID in a result set. >+ pull a dom_sid structure from a objectSid in a result set. >+*/ >+int samdb_result_dom_sid_buf(const struct ldb_message *msg, >+ const char *attr, >+ struct dom_sid *sid) >+{ >+ ssize_t ret; >+ const struct ldb_val *v = NULL; >+ v = ldb_msg_find_ldb_val(msg, attr); >+ if (v == NULL) { >+ return LDB_ERR_NO_SUCH_ATTRIBUTE; >+ } >+ ret = sid_parse(v->data, v->length, sid); >+ if (ret == -1) { >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ return LDB_SUCCESS; >+} >+ >+/* >+ pull a guid structure from a objectGUID in a result set. > */ > struct GUID samdb_result_guid(const struct ldb_message *msg, const char *attr) > { >-- >2.25.1 > > >From 9399bc6e0539998c70ca65f63ff78c9781f5a559 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Mon, 27 Feb 2023 13:55:36 +1300 >Subject: [PATCH 24/35] CVE-2023-0614 s4-acl: Split out function to set up > access checking variables > >These variables are often used together, and it is useful to have the >setup code in one place. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> > >[abartlet@samba.org adapted to the use of > acl_check_access_on_attribute as > acl_check_access_on_attribute_implicit_owner is > only in Samba 4.18 and newer] >--- > source4/dsdb/samdb/ldb_modules/acl_read.c | 113 +++++++++++++++------- > 1 file changed, 80 insertions(+), 33 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/acl_read.c b/source4/dsdb/samdb/ldb_modules/acl_read.c >index 3980c44345e..6659c71c965 100644 >--- a/source4/dsdb/samdb/ldb_modules/acl_read.c >+++ b/source4/dsdb/samdb/ldb_modules/acl_read.c >@@ -70,6 +70,13 @@ struct aclread_private { > struct ldb_val sd_cached_blob; > }; > >+struct access_check_context { >+ struct security_descriptor *sd; >+ struct dom_sid sid_buf; >+ const struct dom_sid *sid; >+ const struct dsdb_class *objectclass; >+}; >+ > /* > * the object has a parent, so we have to check for visibility > * >@@ -254,7 +261,7 @@ static int aclread_check_object_visible(struct aclread_context *ac, > */ > > static int aclread_get_sd_from_ldb_message(struct aclread_context *ac, >- struct ldb_message *acl_res, >+ const struct ldb_message *acl_res, > struct security_descriptor **sd) > { > struct ldb_message_element *sd_element; >@@ -358,7 +365,7 @@ static uint32_t get_attr_access_mask(const struct dsdb_attribute *attr, > struct parse_tree_aclread_ctx { > struct aclread_context *ac; > TALLOC_CTX *mem_ctx; >- struct dom_sid *sid; >+ const struct dom_sid *sid; > struct ldb_dn *dn; > struct security_descriptor *sd; > const struct dsdb_class *objectclass; >@@ -372,7 +379,7 @@ static int check_attr_access_rights(TALLOC_CTX *mem_ctx, const char *attr_name, > struct aclread_context *ac, > struct security_descriptor *sd, > const struct dsdb_class *objectclass, >- struct dom_sid *sid, struct ldb_dn *dn) >+ const struct dom_sid *sid, struct ldb_dn *dn) > { > int ret; > const struct dsdb_attribute *attr = NULL; >@@ -448,6 +455,69 @@ static const char * parse_tree_get_attr(struct ldb_parse_tree *tree) > return attr; > } > >+static int setup_access_check_context(struct aclread_context *ac, >+ const struct ldb_message *msg, >+ struct access_check_context *ctx) >+{ >+ int ret; >+ >+ /* >+ * Fetch the schema so we can check which attributes are >+ * considered confidential. >+ */ >+ if (ac->schema == NULL) { >+ struct ldb_context *ldb = ldb_module_get_ctx(ac->module); >+ >+ /* Cache the schema for later use. */ >+ ac->schema = dsdb_get_schema(ldb, ac); >+ >+ if (ac->schema == NULL) { >+ return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, >+ "aclread_callback: Error obtaining schema."); >+ } >+ } >+ >+ /* Fetch the object's security descriptor. */ >+ ret = aclread_get_sd_from_ldb_message(ac, msg, &ctx->sd); >+ if (ret != LDB_SUCCESS) { >+ ldb_debug_set(ldb_module_get_ctx(ac->module), LDB_DEBUG_FATAL, >+ "acl_read: cannot get descriptor of %s: %s\n", >+ ldb_dn_get_linearized(msg->dn), ldb_strerror(ret)); >+ return LDB_ERR_OPERATIONS_ERROR; >+ } else if (ctx->sd == NULL) { >+ ldb_debug_set(ldb_module_get_ctx(ac->module), LDB_DEBUG_FATAL, >+ "acl_read: cannot get descriptor of %s (attribute not found)\n", >+ ldb_dn_get_linearized(msg->dn)); >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ /* >+ * Get the most specific structural object class for the ACL check >+ */ >+ ctx->objectclass = dsdb_get_structural_oc_from_msg(ac->schema, msg); >+ if (ctx->objectclass == NULL) { >+ ldb_asprintf_errstring(ldb_module_get_ctx(ac->module), >+ "acl_read: Failed to find a structural class for %s", >+ ldb_dn_get_linearized(msg->dn)); >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ >+ /* Fetch the object's SID. */ >+ ret = samdb_result_dom_sid_buf(msg, "objectSid", &ctx->sid_buf); >+ if (ret == LDB_SUCCESS) { >+ ctx->sid = &ctx->sid_buf; >+ } else if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) { >+ /* This is expected. */ >+ ctx->sid = NULL; >+ } else { >+ ldb_asprintf_errstring(ldb_module_get_ctx(ac->module), >+ "acl_read: Failed to parse objectSid as dom_sid for %s", >+ ldb_dn_get_linearized(msg->dn)); >+ return ret; >+ } >+ >+ return LDB_SUCCESS; >+} >+ > /* > * Checks a single attribute in the search parse-tree to make sure the user has > * sufficient rights to view it. >@@ -522,7 +592,7 @@ static int check_search_ops_access(struct aclread_context *ac, > TALLOC_CTX *mem_ctx, > struct security_descriptor *sd, > const struct dsdb_class *objectclass, >- struct dom_sid *sid, struct ldb_dn *dn, >+ const struct dom_sid *sid, struct ldb_dn *dn, > bool *suppress_result) > { > int ret; >@@ -585,10 +655,8 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > struct ldb_message *msg; > int ret; > unsigned int i; >- struct security_descriptor *sd = NULL; >- struct dom_sid *sid = NULL; >+ struct access_check_context acl_ctx; > TALLOC_CTX *tmp_ctx; >- const struct dsdb_class *objectclass; > bool suppress_result = false; > > ac = talloc_get_type_abort(req->context, struct aclread_context); >@@ -604,32 +672,11 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > switch (ares->type) { > case LDB_REPLY_ENTRY: > msg = ares->message; >- ret = aclread_get_sd_from_ldb_message(ac, msg, &sd); >+ ret = setup_access_check_context(ac, msg, &acl_ctx); > if (ret != LDB_SUCCESS) { >- ldb_debug_set(ldb, LDB_DEBUG_FATAL, >- "acl_read: cannot get descriptor of %s: %s\n", >- ldb_dn_get_linearized(msg->dn), ldb_strerror(ret)); >- ret = LDB_ERR_OPERATIONS_ERROR; >- goto fail; >- } else if (sd == NULL) { >- ldb_debug_set(ldb, LDB_DEBUG_FATAL, >- "acl_read: cannot get descriptor of %s (attribute not found)\n", >- ldb_dn_get_linearized(msg->dn)); >- ret = LDB_ERR_OPERATIONS_ERROR; >- goto fail; >- } >- /* >- * Get the most specific structural object class for the ACL check >- */ >- objectclass = dsdb_get_structural_oc_from_msg(ac->schema, msg); >- if (objectclass == NULL) { >- ldb_asprintf_errstring(ldb, "acl_read: Failed to find a structural class for %s", >- ldb_dn_get_linearized(msg->dn)); >- ret = LDB_ERR_OPERATIONS_ERROR; >- goto fail; >+ return ret; > } > >- sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid"); > if (!ldb_dn_is_null(msg->dn)) { > /* > * this is a real object, so we have >@@ -678,8 +725,8 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > > ret = acl_check_access_on_attribute(ac->module, > tmp_ctx, >- sd, >- sid, >+ acl_ctx.sd, >+ acl_ctx.sid, > access_mask, > attr, > objectclass); >@@ -733,7 +780,7 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > * check access rights for the search attributes, as well as the > * attribute values actually being returned > */ >- ret = check_search_ops_access(ac, tmp_ctx, sd, objectclass, sid, >+ ret = check_search_ops_access(ac, tmp_ctx, acl_ctx.sd, acl_ctx.objectclass, acl_ctx.sid, > msg->dn, &suppress_result); > if (ret != LDB_SUCCESS) { > ldb_debug_set(ldb, LDB_DEBUG_FATAL, >-- >2.25.1 > > >From fb3b25df9650f772305c399fd59b251de4500034 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 3 Mar 2023 17:34:29 +1300 >Subject: [PATCH 25/35] CVE-2023-0614 ldb: Prevent disclosure of confidential > attributes > >Add a hook, acl_redact_msg_for_filter(), in the aclread module, that >marks inaccessible any message elements used by an LDAP search filter >that the user has no right to access. Make the various ldb_match_*() >functions check whether message elements are accessible, and refuse to >match any that are not. Remaining message elements, not mentioned in the >search filter, are checked in aclread_callback(), and any inaccessible >elements are removed at this point. > >Certain attributes, namely objectClass, distinguishedName, name, and >objectGUID, are always present, and hence the presence of said >attributes is always allowed to be checked in a search filter. This >corresponds with the behaviour of Windows. > >Further, we unconditionally allow the attributes isDeleted and >isRecycled in a check for presence or equality. Windows is not known to >make this special exception, but it seems mostly harmless, and should >mitigate the performance impact on searches made by the show_deleted >module. > >As a result of all these changes, our behaviour regarding confidential >attributes happens to match Windows more closely. For the test in >confidential_attr.py, we can now model our attribute handling with >DC_MODE_RETURN_ALL, which corresponds to the behaviour exhibited by >Windows. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org> > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> > >[abartlet@samba.org adapted due to Samba 4.17 and lower > not having the patches for CVE-2020-25720 and 4.16 and lower > not having the patches for CVE-2022-32743 ] >--- > lib/ldb-samba/ldb_matching_rules.c | 15 + > lib/ldb/common/ldb_match.c | 37 + > lib/ldb/include/ldb_module.h | 11 + > lib/ldb/include/ldb_private.h | 5 + > lib/ldb/ldb_key_value/ldb_kv_index.c | 8 + > lib/ldb/ldb_key_value/ldb_kv_search.c | 15 + > selftest/knownfail.d/confidential-attr-timing | 1 - > source4/dsdb/samdb/ldb_modules/acl.c | 183 +--- > source4/dsdb/samdb/ldb_modules/acl_read.c | 837 ++++++++++++------ > source4/dsdb/samdb/samdb.h | 2 + > .../dsdb/tests/python/confidential_attr.py | 12 +- > source4/setup/schema_samba4.ldif | 1 + > 12 files changed, 672 insertions(+), 455 deletions(-) > delete mode 100644 selftest/knownfail.d/confidential-attr-timing > >diff --git a/lib/ldb-samba/ldb_matching_rules.c b/lib/ldb-samba/ldb_matching_rules.c >index 827f3920ae8..0c4c31e49f9 100644 >--- a/lib/ldb-samba/ldb_matching_rules.c >+++ b/lib/ldb-samba/ldb_matching_rules.c >@@ -87,6 +87,11 @@ static int ldb_eval_transitive_filter_helper(TALLOC_CTX *mem_ctx, > return LDB_SUCCESS; > } > >+ if (ldb_msg_element_is_inaccessible(el)) { >+ *matched = false; >+ return LDB_SUCCESS; >+ } >+ > /* > * If the value to match is present in the attribute values of the > * current entry being visited, set matched to true and return OK >@@ -370,6 +375,11 @@ static int dsdb_match_for_dns_to_tombstone_time(struct ldb_context *ldb, > return LDB_SUCCESS; > } > >+ if (ldb_msg_element_is_inaccessible(el)) { >+ *matched = false; >+ return LDB_SUCCESS; >+ } >+ > session_info = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"), > struct auth_session_info); > if (session_info == NULL) { >@@ -489,6 +499,11 @@ static int dsdb_match_for_expunge(struct ldb_context *ldb, > return LDB_SUCCESS; > } > >+ if (ldb_msg_element_is_inaccessible(el)) { >+ *matched = false; >+ return LDB_SUCCESS; >+ } >+ > session_info > = talloc_get_type(ldb_get_opaque(ldb, DSDB_SESSION_INFO), > struct auth_session_info); >diff --git a/lib/ldb/common/ldb_match.c b/lib/ldb/common/ldb_match.c >index 2f4d41f3441..17314f1b9ca 100644 >--- a/lib/ldb/common/ldb_match.c >+++ b/lib/ldb/common/ldb_match.c >@@ -98,6 +98,11 @@ static int ldb_match_present(struct ldb_context *ldb, > return LDB_SUCCESS; > } > >+ if (ldb_msg_element_is_inaccessible(el)) { >+ *matched = false; >+ return LDB_SUCCESS; >+ } >+ > a = ldb_schema_attribute_by_name(ldb, el->name); > if (!a) { > return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; >@@ -139,6 +144,11 @@ static int ldb_match_comparison(struct ldb_context *ldb, > return LDB_SUCCESS; > } > >+ if (ldb_msg_element_is_inaccessible(el)) { >+ *matched = false; >+ return LDB_SUCCESS; >+ } >+ > a = ldb_schema_attribute_by_name(ldb, el->name); > if (!a) { > return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; >@@ -209,6 +219,11 @@ static int ldb_match_equality(struct ldb_context *ldb, > return LDB_SUCCESS; > } > >+ if (ldb_msg_element_is_inaccessible(el)) { >+ *matched = false; >+ return LDB_SUCCESS; >+ } >+ > a = ldb_schema_attribute_by_name(ldb, el->name); > if (a == NULL) { > return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; >@@ -370,6 +385,11 @@ static int ldb_match_substring(struct ldb_context *ldb, > return LDB_SUCCESS; > } > >+ if (ldb_msg_element_is_inaccessible(el)) { >+ *matched = false; >+ return LDB_SUCCESS; >+ } >+ > for (i = 0; i < el->num_values; i++) { > int ret; > ret = ldb_wildcard_compare(ldb, tree, el->values[i], matched); >@@ -443,6 +463,11 @@ static int ldb_match_bitmask(struct ldb_context *ldb, > return LDB_SUCCESS; > } > >+ if (ldb_msg_element_is_inaccessible(el)) { >+ *matched = false; >+ return LDB_SUCCESS; >+ } >+ > for (i=0;i<el->num_values;i++) { > int ret; > struct ldb_val *v = &el->values[i]; >@@ -741,3 +766,15 @@ int ldb_register_extended_match_rule(struct ldb_context *ldb, > return LDB_SUCCESS; > } > >+int ldb_register_redact_callback(struct ldb_context *ldb, >+ ldb_redact_fn redact_fn, >+ struct ldb_module *module) >+{ >+ if (ldb->redact.callback != NULL) { >+ return LDB_ERR_ENTRY_ALREADY_EXISTS; >+ } >+ >+ ldb->redact.callback = redact_fn; >+ ldb->redact.module = module; >+ return LDB_SUCCESS; >+} >diff --git a/lib/ldb/include/ldb_module.h b/lib/ldb/include/ldb_module.h >index bd369ed9512..0f14b1ad346 100644 >--- a/lib/ldb/include/ldb_module.h >+++ b/lib/ldb/include/ldb_module.h >@@ -102,6 +102,12 @@ struct ldb_module; > */ > #define LDB_FLAG_INTERNAL_SHARED_VALUES 0x200 > >+/* >+ * this attribute has been access checked. We know the user has the right to >+ * view it. Used internally in Samba aclread module. >+ */ >+#define LDB_FLAG_INTERNAL_ACCESS_CHECKED 0x400 >+ > /* an extended match rule that always fails to match */ > #define SAMBA_LDAP_MATCH_ALWAYS_FALSE "1.3.6.1.4.1.7165.4.5.1" > >@@ -520,6 +526,11 @@ void ldb_msg_element_mark_inaccessible(struct ldb_message_element *el); > bool ldb_msg_element_is_inaccessible(const struct ldb_message_element *el); > void ldb_msg_remove_inaccessible(struct ldb_message *msg); > >+typedef int (*ldb_redact_fn)(struct ldb_module *, struct ldb_request *, struct ldb_message *); >+int ldb_register_redact_callback(struct ldb_context *ldb, >+ ldb_redact_fn redact_fn, >+ struct ldb_module *module); >+ > /* > * these pack/unpack functions are exposed in the library for use by > * ldb tools like ldbdump and for use in tests, >diff --git a/lib/ldb/include/ldb_private.h b/lib/ldb/include/ldb_private.h >index ca43817d07a..b0a42f6421c 100644 >--- a/lib/ldb/include/ldb_private.h >+++ b/lib/ldb/include/ldb_private.h >@@ -119,6 +119,11 @@ struct ldb_context { > struct ldb_extended_match_entry *prev, *next; > } *extended_match_rules; > >+ struct { >+ struct ldb_module *module; >+ ldb_redact_fn callback; >+ } redact; >+ > /* custom utf8 functions */ > struct ldb_utf8_fns utf8_fns; > >diff --git a/lib/ldb/ldb_key_value/ldb_kv_index.c b/lib/ldb/ldb_key_value/ldb_kv_index.c >index 203266ea8c9..163052fecf7 100644 >--- a/lib/ldb/ldb_key_value/ldb_kv_index.c >+++ b/lib/ldb/ldb_key_value/ldb_kv_index.c >@@ -2428,6 +2428,14 @@ static int ldb_kv_index_filter(struct ldb_kv_private *ldb_kv, > return LDB_ERR_OPERATIONS_ERROR; > } > >+ if (ldb->redact.callback != NULL) { >+ ret = ldb->redact.callback(ldb->redact.module, ac->req, msg); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(msg); >+ return ret; >+ } >+ } >+ > /* > * We trust the index for LDB_SCOPE_ONELEVEL > * unless the index key has been truncated. >diff --git a/lib/ldb/ldb_key_value/ldb_kv_search.c b/lib/ldb/ldb_key_value/ldb_kv_search.c >index f3333510eab..d187ba223e1 100644 >--- a/lib/ldb/ldb_key_value/ldb_kv_search.c >+++ b/lib/ldb/ldb_key_value/ldb_kv_search.c >@@ -395,6 +395,14 @@ static int search_func(_UNUSED_ struct ldb_kv_private *ldb_kv, > } > } > >+ if (ldb->redact.callback != NULL) { >+ ret = ldb->redact.callback(ldb->redact.module, ac->req, msg); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(msg); >+ return ret; >+ } >+ } >+ > /* see if it matches the given expression */ > ret = ldb_match_msg_error(ldb, msg, > ac->tree, ac->base, ac->scope, &matched); >@@ -530,6 +538,13 @@ static int ldb_kv_search_and_return_base(struct ldb_kv_private *ldb_kv, > return ret; > } > >+ if (ldb->redact.callback != NULL) { >+ ret = ldb->redact.callback(ldb->redact.module, ctx->req, msg); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(msg); >+ return ret; >+ } >+ } > > /* > * We use this, not ldb_match_msg_error() as we know >diff --git a/selftest/knownfail.d/confidential-attr-timing b/selftest/knownfail.d/confidential-attr-timing >deleted file mode 100644 >index e213cdb16d3..00000000000 >--- a/selftest/knownfail.d/confidential-attr-timing >+++ /dev/null >@@ -1 +0,0 @@ >-^samba4.ldap.confidential_attr.python\(ad_dc_slowtests\).__main__.ConfidentialAttrTestDirsync.test_timing_attack\(ad_dc_slowtests\) >diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c >index 8016a2d4bd0..63282486b0c 100644 >--- a/source4/dsdb/samdb/ldb_modules/acl.c >+++ b/source4/dsdb/samdb/ldb_modules/acl.c >@@ -46,11 +46,6 @@ > #undef strcasecmp > #undef strncasecmp > >-struct extended_access_check_attribute { >- const char *oa_name; >- const uint32_t requires_rights; >-}; >- > struct acl_private { > bool acl_search; > const char **password_attrs; >@@ -58,7 +53,6 @@ struct acl_private { > uint64_t cached_schema_metadata_usn; > uint64_t cached_schema_loaded_usn; > const char **confidential_attrs; >- bool userPassword_support; > }; > > struct acl_context { >@@ -66,15 +60,12 @@ struct acl_context { > struct ldb_request *req; > bool am_system; > bool am_administrator; >- bool modify_search; > bool constructed_attrs; > bool allowedAttributes; > bool allowedAttributesEffective; > bool allowedChildClasses; > bool allowedChildClassesEffective; > bool sDRightsEffective; >- bool userPassword; >- const char * const *attrs; > struct dsdb_schema *schema; > }; > >@@ -83,25 +74,9 @@ static int acl_module_init(struct ldb_module *module) > struct ldb_context *ldb; > struct acl_private *data; > int ret; >- unsigned int i, n, j; >- TALLOC_CTX *mem_ctx; >- static const char * const attrs[] = { "passwordAttribute", NULL }; >- static const char * const secret_attrs[] = { >- DSDB_SECRET_ATTRIBUTES >- }; >- struct ldb_result *res; >- struct ldb_message *msg; >- struct ldb_message_element *password_attributes; > > ldb = ldb_module_get_ctx(module); > >- ret = ldb_mod_register_control(module, LDB_CONTROL_SD_FLAGS_OID); >- if (ret != LDB_SUCCESS) { >- ldb_debug(ldb, LDB_DEBUG_ERROR, >- "acl_module_init: Unable to register control with rootdse!\n"); >- return ldb_operr(ldb); >- } >- > data = talloc_zero(module, struct acl_private); > if (data == NULL) { > return ldb_oom(ldb); >@@ -111,91 +86,14 @@ static int acl_module_init(struct ldb_module *module) > NULL, "acl", "search", true); > ldb_module_set_private(module, data); > >- mem_ctx = talloc_new(module); >- if (!mem_ctx) { >- return ldb_oom(ldb); >- } >- >- ret = dsdb_module_search_dn(module, mem_ctx, &res, >- ldb_dn_new(mem_ctx, ldb, "@KLUDGEACL"), >- attrs, >- DSDB_FLAG_NEXT_MODULE | >- DSDB_FLAG_AS_SYSTEM, >- NULL); >- if (ret != LDB_SUCCESS) { >- goto done; >- } >- if (res->count == 0) { >- goto done; >- } >- >- if (res->count > 1) { >- talloc_free(mem_ctx); >- return LDB_ERR_CONSTRAINT_VIOLATION; >- } >- >- msg = res->msgs[0]; >- >- password_attributes = ldb_msg_find_element(msg, "passwordAttribute"); >- if (!password_attributes) { >- goto done; >- } >- data->password_attrs = talloc_array(data, const char *, >- password_attributes->num_values + >- ARRAY_SIZE(secret_attrs) + 1); >- if (!data->password_attrs) { >- talloc_free(mem_ctx); >- return ldb_oom(ldb); >- } >- >- n = 0; >- for (i=0; i < password_attributes->num_values; i++) { >- data->password_attrs[n] = (const char *)password_attributes->values[i].data; >- talloc_steal(data->password_attrs, password_attributes->values[i].data); >- n++; >- } >- >- for (i=0; i < ARRAY_SIZE(secret_attrs); i++) { >- bool found = false; >- >- for (j=0; j < n; j++) { >- if (strcasecmp(data->password_attrs[j], secret_attrs[i]) == 0) { >- found = true; >- break; >- } >- } >- >- if (found) { >- continue; >- } >- >- data->password_attrs[n] = talloc_strdup(data->password_attrs, >- secret_attrs[i]); >- if (data->password_attrs[n] == NULL) { >- talloc_free(mem_ctx); >- return ldb_oom(ldb); >- } >- n++; >- } >- data->password_attrs[n] = NULL; >- >-done: >- talloc_free(mem_ctx); >- ret = ldb_next_init(module); >- >+ ret = ldb_mod_register_control(module, LDB_CONTROL_SD_FLAGS_OID); > if (ret != LDB_SUCCESS) { >- return ret; >+ ldb_debug(ldb, LDB_DEBUG_ERROR, >+ "acl_module_init: Unable to register control with rootdse!\n"); >+ return ldb_operr(ldb); > } > >- /* >- * Check this after the modules have be initialised so we >- * can actually read the backend DB. >- */ >- data->userPassword_support >- = dsdb_user_password_support(module, >- module, >- NULL); >- return ret; >+ return ldb_next_init(module); > } > > static int acl_allowedAttributes(struct ldb_module *module, >@@ -2152,29 +2050,11 @@ static int acl_search_callback(struct ldb_request *req, struct ldb_reply *ares) > ares->controls); > } > >- if (data->password_attrs != NULL) { >- for (i = 0; data->password_attrs[i]; i++) { >- if ((!ac->userPassword) && >- (ldb_attr_cmp(data->password_attrs[i], >- "userPassword") == 0)) >- { >- continue; >- } >- >- ldb_msg_remove_attr(ares->message, data->password_attrs[i]); >- } >- } >- > if (ac->am_administrator) { > return ldb_module_send_entry(ac->req, ares->message, > ares->controls); > } > >- ret = acl_search_update_confidential_attrs(ac, data); >- if (ret != LDB_SUCCESS) { >- return ret; >- } >- > if (data->confidential_attrs != NULL) { > for (i = 0; data->confidential_attrs[i]; i++) { > ldb_msg_remove_attr(ares->message, >@@ -2199,11 +2079,12 @@ static int acl_search(struct ldb_module *module, struct ldb_request *req) > { > struct ldb_context *ldb; > struct acl_context *ac; >- struct ldb_parse_tree *down_tree; >+ struct ldb_parse_tree *down_tree = req->op.search.tree; > struct ldb_request *down_req; > struct acl_private *data; > int ret; > unsigned int i; >+ bool modify_search = true; > > if (ldb_dn_is_special(req->op.search.base)) { > return ldb_next_request(module, req); >@@ -2222,13 +2103,11 @@ static int acl_search(struct ldb_module *module, struct ldb_request *req) > ac->am_system = dsdb_module_am_system(module); > ac->am_administrator = dsdb_module_am_administrator(module); > ac->constructed_attrs = false; >- ac->modify_search = true; > ac->allowedAttributes = ldb_attr_in_list(req->op.search.attrs, "allowedAttributes"); > ac->allowedAttributesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedAttributesEffective"); > ac->allowedChildClasses = ldb_attr_in_list(req->op.search.attrs, "allowedChildClasses"); > ac->allowedChildClassesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedChildClassesEffective"); > ac->sDRightsEffective = ldb_attr_in_list(req->op.search.attrs, "sDRightsEffective"); >- ac->userPassword = true; > ac->schema = dsdb_get_schema(ldb, ac); > > ac->constructed_attrs |= ac->allowedAttributes; >@@ -2238,13 +2117,13 @@ static int acl_search(struct ldb_module *module, struct ldb_request *req) > ac->constructed_attrs |= ac->sDRightsEffective; > > if (data == NULL) { >- ac->modify_search = false; >+ modify_search = false; > } > if (ac->am_system) { >- ac->modify_search = false; >+ modify_search = false; > } > >- if (!ac->constructed_attrs && !ac->modify_search) { >+ if (!ac->constructed_attrs && !modify_search) { > talloc_free(ac); > return ldb_next_request(module, req); > } >@@ -2254,38 +2133,24 @@ static int acl_search(struct ldb_module *module, struct ldb_request *req) > return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, > "acl_private data is missing"); > } >- ac->userPassword = data->userPassword_support; >- >- ret = acl_search_update_confidential_attrs(ac, data); >- if (ret != LDB_SUCCESS) { >- return ret; >- } > >- down_tree = ldb_parse_tree_copy_shallow(ac, req->op.search.tree); >- if (down_tree == NULL) { >- return ldb_oom(ldb); >- } >+ if (!ac->am_system && !ac->am_administrator) { >+ ret = acl_search_update_confidential_attrs(ac, data); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } > >- if (!ac->am_system && data->password_attrs) { >- for (i = 0; data->password_attrs[i]; i++) { >- if ((!ac->userPassword) && >- (ldb_attr_cmp(data->password_attrs[i], >- "userPassword") == 0)) >- { >- continue; >+ if (data->confidential_attrs != NULL) { >+ down_tree = ldb_parse_tree_copy_shallow(ac, req->op.search.tree); >+ if (down_tree == NULL) { >+ return ldb_oom(ldb); > } > >- ldb_parse_tree_attr_replace(down_tree, >- data->password_attrs[i], >- "kludgeACLredactedattribute"); >- } >- } >- >- if (!ac->am_system && !ac->am_administrator && data->confidential_attrs) { >- for (i = 0; data->confidential_attrs[i]; i++) { >- ldb_parse_tree_attr_replace(down_tree, >- data->confidential_attrs[i], >- "kludgeACLredactedattribute"); >+ for (i = 0; data->confidential_attrs[i]; i++) { >+ ldb_parse_tree_attr_replace(down_tree, >+ data->confidential_attrs[i], >+ "kludgeACLredactedattribute"); >+ } > } > } > >diff --git a/source4/dsdb/samdb/ldb_modules/acl_read.c b/source4/dsdb/samdb/ldb_modules/acl_read.c >index 6659c71c965..8ca8607b925 100644 >--- a/source4/dsdb/samdb/ldb_modules/acl_read.c >+++ b/source4/dsdb/samdb/ldb_modules/acl_read.c >@@ -37,20 +37,25 @@ > #include "librpc/gen_ndr/ndr_security.h" > #include "param/param.h" > #include "dsdb/samdb/ldb_modules/util.h" >+#include "lib/util/binsearch.h" > > #undef strcasecmp > >+struct ldb_attr_vec { >+ const char** attrs; >+ size_t len; >+ size_t capacity; >+}; >+ > struct aclread_context { > struct ldb_module *module; > struct ldb_request *req; >- const char * const *attrs; > const struct dsdb_schema *schema; > uint32_t sd_flags; > bool added_nTSecurityDescriptor; > bool added_instanceType; > bool added_objectSid; > bool added_objectClass; >- bool indirsync; > > bool do_list_object_initialized; > bool do_list_object; >@@ -60,6 +65,11 @@ struct aclread_context { > /* cache on the last parent we checked in this search */ > struct ldb_dn *last_parent_dn; > int last_parent_check_ret; >+ >+ bool am_administrator; >+ >+ bool got_tree_attrs; >+ struct ldb_attr_vec tree_attrs; > }; > > struct aclread_private { >@@ -68,6 +78,7 @@ struct aclread_private { > /* cache of the last SD we read during any search */ > struct security_descriptor *sd_cached; > struct ldb_val sd_cached_blob; >+ const char **password_attrs; > }; > > struct access_check_context { >@@ -77,6 +88,183 @@ struct access_check_context { > const struct dsdb_class *objectclass; > }; > >+static void acl_element_mark_access_checked(struct ldb_message_element *el) >+{ >+ el->flags |= LDB_FLAG_INTERNAL_ACCESS_CHECKED; >+} >+ >+static bool acl_element_is_access_checked(const struct ldb_message_element *el) >+{ >+ return (el->flags & LDB_FLAG_INTERNAL_ACCESS_CHECKED) != 0; >+} >+ >+static bool attr_in_vec(const struct ldb_attr_vec *vec, const char *attr) >+{ >+ const char **found = NULL; >+ >+ if (vec == NULL) { >+ return false; >+ } >+ >+ BINARY_ARRAY_SEARCH_V(vec->attrs, >+ vec->len, >+ attr, >+ ldb_attr_cmp, >+ found); >+ return found != NULL; >+} >+ >+static int acl_attr_cmp_fn(const char *a, const char **b) >+{ >+ return ldb_attr_cmp(a, *b); >+} >+ >+static int attr_vec_add_unique(TALLOC_CTX *mem_ctx, >+ struct ldb_attr_vec *vec, >+ const char *attr) >+{ >+ const char **exact = NULL; >+ const char **next = NULL; >+ size_t next_idx = 0; >+ >+ BINARY_ARRAY_SEARCH_GTE(vec->attrs, >+ vec->len, >+ attr, >+ acl_attr_cmp_fn, >+ exact, >+ next); >+ if (exact != NULL) { >+ return LDB_SUCCESS; >+ } >+ >+ if (vec->len == SIZE_MAX) { >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ >+ if (next != NULL) { >+ next_idx = next - vec->attrs; >+ } >+ >+ if (vec->len >= vec->capacity) { >+ const char **attrs = NULL; >+ >+ if (vec->capacity == 0) { >+ vec->capacity = 4; >+ } else { >+ if (vec->capacity > SIZE_MAX / 2) { >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ vec->capacity *= 2; >+ } >+ >+ attrs = talloc_realloc(mem_ctx, vec->attrs, const char *, vec->capacity); >+ if (attrs == NULL) { >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ >+ vec->attrs = attrs; >+ } >+ SMB_ASSERT(vec->len < vec->capacity); >+ >+ if (next == NULL) { >+ vec->attrs[vec->len++] = attr; >+ } else { >+ size_t count = (vec->len - next_idx) * sizeof (vec->attrs[0]); >+ memmove(&vec->attrs[next_idx + 1], >+ &vec->attrs[next_idx], >+ count); >+ >+ vec->attrs[next_idx] = attr; >+ ++vec->len; >+ } >+ >+ return LDB_SUCCESS; >+} >+ >+static bool ldb_attr_always_present(const char *attr) >+{ >+ static const char * const attrs_always_present[] = { >+ "objectClass", >+ "distinguishedName", >+ "name", >+ "objectGUID", >+ NULL >+ }; >+ >+ return ldb_attr_in_list(attrs_always_present, attr); >+} >+ >+static bool ldb_attr_always_visible(const char *attr) >+{ >+ static const char * const attrs_always_visible[] = { >+ "isDeleted", >+ "isRecycled", >+ NULL >+ }; >+ >+ return ldb_attr_in_list(attrs_always_visible, attr); >+} >+ >+/* Collect a list of attributes required to match a given parse tree. */ >+static int ldb_parse_tree_collect_acl_attrs(struct ldb_module *module, >+ TALLOC_CTX *mem_ctx, >+ struct ldb_attr_vec *attrs, >+ const struct ldb_parse_tree *tree) >+{ >+ const char *attr = NULL; >+ unsigned int i; >+ int ret; >+ >+ if (tree == NULL) { >+ return 0; >+ } >+ >+ switch (tree->operation) { >+ case LDB_OP_OR: >+ case LDB_OP_AND: /* attributes stored in list of subtrees */ >+ for (i = 0; i < tree->u.list.num_elements; i++) { >+ ret = ldb_parse_tree_collect_acl_attrs(module, mem_ctx, >+ attrs, tree->u.list.elements[i]); >+ if (ret) { >+ return ret; >+ } >+ } >+ return 0; >+ >+ case LDB_OP_NOT: /* attributes stored in single subtree */ >+ return ldb_parse_tree_collect_acl_attrs(module, mem_ctx, attrs, tree->u.isnot.child); >+ >+ case LDB_OP_PRESENT: >+ /* >+ * If the search filter is checking for an attribute's presence, >+ * and the attribute is always present, we can skip access >+ * rights checks. Every object has these attributes, and so >+ * there's no security reason to hide their presence. >+ * Note: the acl.py tests (e.g. test_search1()) rely on this >+ * exception. I.e. even if we lack Read Property (RP) rights >+ * for a child object, it should still appear as a visible >+ * object in 'objectClass=*' searches, so long as we have List >+ * Contents (LC) rights for the object. >+ */ >+ if (ldb_attr_always_present(tree->u.present.attr)) { >+ /* No need to check this attribute. */ >+ return 0; >+ } >+ >+ FALL_THROUGH; >+ case LDB_OP_EQUALITY: >+ if (ldb_attr_always_visible(tree->u.present.attr)) { >+ /* No need to check this attribute. */ >+ return 0; >+ } >+ >+ FALL_THROUGH; >+ default: /* single attribute in tree */ >+ attr = ldb_parse_tree_get_attr(tree); >+ return attr_vec_add_unique(mem_ctx, attrs, attr); >+ } >+} >+ > /* > * the object has a parent, so we have to check for visibility > * >@@ -308,16 +496,11 @@ static int aclread_get_sd_from_ldb_message(struct aclread_context *ac, > } > > talloc_unlink(private_data, private_data->sd_cached_blob.data); >- if (ac->added_nTSecurityDescriptor) { >- private_data->sd_cached_blob = sd_element->values[0]; >- talloc_steal(private_data, sd_element->values[0].data); >- } else { >- private_data->sd_cached_blob = ldb_val_dup(private_data, >- &sd_element->values[0]); >- if (private_data->sd_cached_blob.data == NULL) { >- TALLOC_FREE(*sd); >- return ldb_operr(ldb); >- } >+ private_data->sd_cached_blob = ldb_val_dup(private_data, >+ &sd_element->values[0]); >+ if (private_data->sd_cached_blob.data == NULL) { >+ TALLOC_FREE(*sd); >+ return ldb_operr(ldb); > } > > talloc_unlink(private_data, private_data->sd_cached); >@@ -326,6 +509,27 @@ static int aclread_get_sd_from_ldb_message(struct aclread_context *ac, > return LDB_SUCCESS; > } > >+/* Check whether the attribute is a password attribute. */ >+static bool attr_is_secret(const char *attr, const struct aclread_private *private_data) >+{ >+ unsigned i; >+ >+ if (private_data->password_attrs == NULL) { >+ return false; >+ } >+ >+ for (i = 0; private_data->password_attrs[i] != NULL; ++i) { >+ const char *password_attr = private_data->password_attrs[i]; >+ if (ldb_attr_cmp(attr, password_attr) != 0) { >+ continue; >+ } >+ >+ return true; >+ } >+ >+ return false; >+} >+ > /* > * Returns the access mask required to read a given attribute > */ >@@ -361,61 +565,59 @@ static uint32_t get_attr_access_mask(const struct dsdb_attribute *attr, > return access_mask; > } > >-/* helper struct for traversing the attributes in the search-tree */ >-struct parse_tree_aclread_ctx { >- struct aclread_context *ac; >- TALLOC_CTX *mem_ctx; >- const struct dom_sid *sid; >- struct ldb_dn *dn; >- struct security_descriptor *sd; >- const struct dsdb_class *objectclass; >- bool suppress_result; >-}; >- > /* >- * Checks that the user has sufficient access rights to view an attribute >+ * Checks that the user has sufficient access rights to view an attribute, else >+ * marks it as inaccessible. > */ >-static int check_attr_access_rights(TALLOC_CTX *mem_ctx, const char *attr_name, >- struct aclread_context *ac, >- struct security_descriptor *sd, >- const struct dsdb_class *objectclass, >- const struct dom_sid *sid, struct ldb_dn *dn) >+static int acl_redact_attr(TALLOC_CTX *mem_ctx, >+ struct ldb_message_element *el, >+ struct aclread_context *ac, >+ const struct aclread_private *private_data, >+ const struct ldb_message *msg, >+ const struct dsdb_schema *schema, >+ const struct security_descriptor *sd, >+ const struct dom_sid *sid, >+ const struct dsdb_class *objectclass) > { > int ret; > const struct dsdb_attribute *attr = NULL; > uint32_t access_mask; > struct ldb_context *ldb = ldb_module_get_ctx(ac->module); > >- attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, attr_name); >+ if (attr_is_secret(el->name, private_data)) { >+ ldb_msg_element_mark_inaccessible(el); >+ return LDB_SUCCESS; >+ } >+ >+ /* Look up the attribute in the schema. */ >+ attr = dsdb_attribute_by_lDAPDisplayName(schema, el->name); > if (!attr) { > ldb_debug_set(ldb, >- LDB_DEBUG_TRACE, >- "acl_read: %s cannot find attr[%s] in schema," >- "ignoring\n", >- ldb_dn_get_linearized(dn), attr_name); >- return LDB_SUCCESS; >+ LDB_DEBUG_FATAL, >+ "acl_read: %s cannot find attr[%s] in schema\n", >+ ldb_dn_get_linearized(msg->dn), el->name); >+ return LDB_ERR_OPERATIONS_ERROR; > } > > access_mask = get_attr_access_mask(attr, ac->sd_flags); >- >- /* the access-mask should be non-zero. Skip attribute otherwise */ > if (access_mask == 0) { > DBG_ERR("Could not determine access mask for attribute %s\n", >- attr_name); >+ el->name); >+ ldb_msg_element_mark_inaccessible(el); > return LDB_SUCCESS; > } > >+ /* We must check whether the user has rights to view the attribute. */ >+ > ret = acl_check_access_on_attribute(ac->module, mem_ctx, sd, sid, > access_mask, attr, objectclass); > > if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { >- return ret; >- } >- >- if (ret != LDB_SUCCESS) { >+ ldb_msg_element_mark_inaccessible(el); >+ } else if (ret != LDB_SUCCESS) { > ldb_debug_set(ldb, LDB_DEBUG_FATAL, > "acl_read: %s check attr[%s] gives %s - %s\n", >- ldb_dn_get_linearized(dn), attr_name, >+ ldb_dn_get_linearized(msg->dn), el->name, > ldb_strerror(ret), ldb_errstring(ldb)); > return ret; > } >@@ -423,38 +625,6 @@ static int check_attr_access_rights(TALLOC_CTX *mem_ctx, const char *attr_name, > return LDB_SUCCESS; > } > >-/* >- * Returns the attribute name for this particular level of a search operation >- * parse-tree. >- */ >-static const char * parse_tree_get_attr(struct ldb_parse_tree *tree) >-{ >- const char *attr = NULL; >- >- switch (tree->operation) { >- case LDB_OP_EQUALITY: >- case LDB_OP_GREATER: >- case LDB_OP_LESS: >- case LDB_OP_APPROX: >- attr = tree->u.equality.attr; >- break; >- case LDB_OP_SUBSTRING: >- attr = tree->u.substring.attr; >- break; >- case LDB_OP_PRESENT: >- attr = tree->u.present.attr; >- break; >- case LDB_OP_EXTENDED: >- attr = tree->u.extended.attr; >- break; >- >- /* we'll check LDB_OP_AND/_OR/_NOT children later on in the walk */ >- default: >- break; >- } >- return attr; >-} >- > static int setup_access_check_context(struct aclread_context *ac, > const struct ldb_message *msg, > struct access_check_context *ctx) >@@ -518,103 +688,6 @@ static int setup_access_check_context(struct aclread_context *ac, > return LDB_SUCCESS; > } > >-/* >- * Checks a single attribute in the search parse-tree to make sure the user has >- * sufficient rights to view it. >- */ >-static int parse_tree_check_attr_access(struct ldb_parse_tree *tree, >- void *private_context) >-{ >- struct parse_tree_aclread_ctx *ctx = NULL; >- const char *attr_name = NULL; >- int ret; >- static const char * const attrs_always_present[] = { >- "objectClass", >- "distinguishedName", >- "name", >- "objectGUID", >- NULL >- }; >- >- ctx = (struct parse_tree_aclread_ctx *)private_context; >- >- /* >- * we can skip any further checking if we already know that this object >- * shouldn't be visible in this user's search >- */ >- if (ctx->suppress_result) { >- return LDB_SUCCESS; >- } >- >- /* skip this level of the search-tree if it has no attribute to check */ >- attr_name = parse_tree_get_attr(tree); >- if (attr_name == NULL) { >- return LDB_SUCCESS; >- } >- >- /* >- * If the search filter is checking for an attribute's presence, and the >- * attribute is always present, we can skip access rights checks. Every >- * object has these attributes, and so there's no security reason to >- * hide their presence. >- * Note: the acl.py tests (e.g. test_search1()) rely on this exception. >- * I.e. even if we lack Read Property (RP) rights for a child object, it >- * should still appear as a visible object in 'objectClass=*' searches, >- * so long as we have List Contents (LC) rights for the object. >- */ >- if (tree->operation == LDB_OP_PRESENT && >- is_attr_in_list(attrs_always_present, attr_name)) { >- return LDB_SUCCESS; >- } >- >- ret = check_attr_access_rights(ctx->mem_ctx, attr_name, ctx->ac, >- ctx->sd, ctx->objectclass, ctx->sid, >- ctx->dn); >- >- /* >- * if the user does not have the rights to view this attribute, then we >- * should not return the object as a search result, i.e. act as if the >- * object doesn't exist (for this particular user, at least) >- */ >- if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { >- ctx->suppress_result = true; >- return LDB_SUCCESS; >- } >- >- return ret; >-} >- >-/* >- * Traverse the search-tree to check that the user has sufficient access rights >- * to view all the attributes. >- */ >-static int check_search_ops_access(struct aclread_context *ac, >- TALLOC_CTX *mem_ctx, >- struct security_descriptor *sd, >- const struct dsdb_class *objectclass, >- const struct dom_sid *sid, struct ldb_dn *dn, >- bool *suppress_result) >-{ >- int ret; >- struct parse_tree_aclread_ctx ctx = { 0 }; >- struct ldb_parse_tree *tree = ac->req->op.search.tree; >- >- ctx.ac = ac; >- ctx.mem_ctx = mem_ctx; >- ctx.suppress_result = false; >- ctx.sid = sid; >- ctx.dn = dn; >- ctx.sd = sd; >- ctx.objectclass = objectclass; >- >- /* walk the search tree, checking each attribute as we go */ >- ret = ldb_parse_tree_walk(tree, parse_tree_check_attr_access, &ctx); >- >- /* return whether this search result should be hidden to this user */ >- *suppress_result = ctx.suppress_result; >- return ret; >-} >- > /* > * Whether this attribute was added to perform access checks and must be > * removed. >@@ -650,17 +723,14 @@ static bool should_remove_attr(const char *attr, const struct aclread_context *a > > static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > { >- struct ldb_context *ldb; > struct aclread_context *ac; >+ struct aclread_private *private_data = NULL; > struct ldb_message *msg; > int ret; > unsigned int i; > struct access_check_context acl_ctx; >- TALLOC_CTX *tmp_ctx; >- bool suppress_result = false; > > ac = talloc_get_type_abort(req->context, struct aclread_context); >- ldb = ldb_module_get_ctx(ac->module); > if (!ares) { > return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR ); > } >@@ -668,14 +738,9 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > return ldb_module_done(ac->req, ares->controls, > ares->response, ares->error); > } >- tmp_ctx = talloc_new(ac); > switch (ares->type) { > case LDB_REPLY_ENTRY: > msg = ares->message; >- ret = setup_access_check_context(ac, msg, &acl_ctx); >- if (ret != LDB_SUCCESS) { >- return ret; >- } > > if (!ldb_dn_is_null(msg->dn)) { > /* >@@ -684,132 +749,88 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > */ > ret = aclread_check_object_visible(ac, msg, req); > if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { >- talloc_free(tmp_ctx); > return LDB_SUCCESS; > } else if (ret != LDB_SUCCESS) { >+ struct ldb_context *ldb = ldb_module_get_ctx(ac->module); > ldb_debug_set(ldb, LDB_DEBUG_FATAL, > "acl_read: %s check parent %s - %s\n", > ldb_dn_get_linearized(msg->dn), > ldb_strerror(ret), > ldb_errstring(ldb)); >- goto fail; >+ return ldb_module_done(ac->req, NULL, NULL, ret); > } > } > > /* for every element in the message check RP */ >- for (i=0; i < msg->num_elements; i++) { >- const struct dsdb_attribute *attr; >- uint32_t access_mask; >- attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, >- msg->elements[i].name); >- if (!attr) { >- ldb_debug_set(ldb, LDB_DEBUG_FATAL, >- "acl_read: %s cannot find attr[%s] in of schema\n", >- ldb_dn_get_linearized(msg->dn), >- msg->elements[i].name); >- ret = LDB_ERR_OPERATIONS_ERROR; >- goto fail; >- } >+ for (i = 0; i < msg->num_elements; ++i) { >+ struct ldb_message_element *el = &msg->elements[i]; >+ > /* Remove attributes added to perform access checks. */ >- if (should_remove_attr(msg->elements[i].name, ac)) { >- ldb_msg_element_mark_inaccessible(&msg->elements[i]); >+ if (should_remove_attr(el->name, ac)) { >+ ldb_msg_element_mark_inaccessible(el); > continue; > } > >- access_mask = get_attr_access_mask(attr, ac->sd_flags); >- >- if (access_mask == 0) { >- ldb_msg_element_mark_inaccessible(&msg->elements[i]); >+ if (acl_element_is_access_checked(el)) { >+ /* We will have already checked this attribute. */ > continue; > } > >- ret = acl_check_access_on_attribute(ac->module, >- tmp_ctx, >- acl_ctx.sd, >- acl_ctx.sid, >- access_mask, >- attr, >- objectclass); >- > /* >- * Dirsync control needs the replpropertymetadata attribute >- * so return it as it will be removed by the control >- * in anycase. >+ * We need to fetch the security descriptor to check >+ * this attribute. > */ >- if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { >- bool in_search_filter; >- >- /* check if attr is part of the search filter */ >- in_search_filter = dsdb_attr_in_parse_tree(ac->req->op.search.tree, >- msg->elements[i].name); >- >- if (in_search_filter) { >- >- /* >- * We are doing dirysnc answers >- * and the object shouldn't be returned (normally) >- * but we will return it without replPropertyMetaData >- * so that the dirysync module will do what is needed >- * (remove the object if it is not deleted, or return >- * just the objectGUID if it's deleted). >- */ >- if (ac->indirsync) { >- ldb_msg_remove_attr(msg, "replPropertyMetaData"); >- break; >- } else { >- >- /* do not return this entry */ >- talloc_free(tmp_ctx); >- return LDB_SUCCESS; >- } >- } else { >- ldb_msg_element_mark_inaccessible(&msg->elements[i]); >- } >- } else if (ret != LDB_SUCCESS) { >- ldb_debug_set(ldb, LDB_DEBUG_FATAL, >- "acl_read: %s check attr[%s] gives %s - %s\n", >- ldb_dn_get_linearized(msg->dn), >- msg->elements[i].name, >- ldb_strerror(ret), >- ldb_errstring(ldb)); >- goto fail; >- } >+ break; > } > >- /* >- * check access rights for the search attributes, as well as the >- * attribute values actually being returned >- */ >- ret = check_search_ops_access(ac, tmp_ctx, acl_ctx.sd, acl_ctx.objectclass, acl_ctx.sid, >- msg->dn, &suppress_result); >+ if (i == msg->num_elements) { >+ /* All elements have been checked. */ >+ goto reply_entry_done; >+ } >+ >+ ret = setup_access_check_context(ac, msg, &acl_ctx); > if (ret != LDB_SUCCESS) { >- ldb_debug_set(ldb, LDB_DEBUG_FATAL, >- "acl_read: %s check search ops %s - %s\n", >- ldb_dn_get_linearized(msg->dn), >- ldb_strerror(ret), ldb_errstring(ldb)); >- goto fail; >+ return ret; > } > >- if (suppress_result) { >+ private_data = talloc_get_type_abort(ldb_module_get_private(ac->module), >+ struct aclread_private); >+ >+ for (/* begin where we left off */; i < msg->num_elements; ++i) { >+ struct ldb_message_element *el = &msg->elements[i]; >+ >+ /* Remove attributes added to perform access checks. */ >+ if (should_remove_attr(el->name, ac)) { >+ ldb_msg_element_mark_inaccessible(el); >+ continue; >+ } >+ >+ if (acl_element_is_access_checked(el)) { >+ /* We will have already checked this attribute. */ >+ continue; >+ } > > /* >- * As per the above logic, we strip replPropertyMetaData >- * out of the msg so that the dirysync module will do >- * what is needed (return just the objectGUID if it's, >- * deleted, or remove the object if it is not). >+ * We need to check whether the attribute is secret, >+ * confidential, or access-controlled. > */ >- if (ac->indirsync) { >- ldb_msg_remove_attr(msg, "replPropertyMetaData"); >- } else { >- talloc_free(tmp_ctx); >- return LDB_SUCCESS; >+ ret = acl_redact_attr(ac, >+ el, >+ ac, >+ private_data, >+ msg, >+ ac->schema, >+ acl_ctx.sd, >+ acl_ctx.sid, >+ acl_ctx.objectclass); >+ if (ret != LDB_SUCCESS) { >+ return ldb_module_done(ac->req, NULL, NULL, ret); > } > } > >+ reply_entry_done: > ldb_msg_remove_inaccessible(msg); > >- talloc_free(tmp_ctx); >- > ac->num_entries++; > return ldb_module_send_entry(ac->req, msg, ares->controls); > case LDB_REPLY_REFERRAL: >@@ -830,9 +851,6 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) > > } > return LDB_SUCCESS; >-fail: >- talloc_free(tmp_ctx); >- return ldb_module_done(ac->req, NULL, NULL, ret); > } > > >@@ -843,7 +861,6 @@ static int aclread_search(struct ldb_module *module, struct ldb_request *req) > struct aclread_context *ac; > struct ldb_request *down_req; > struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID); >- uint32_t flags = ldb_req_get_custom_flags(req); > struct ldb_result *res; > struct aclread_private *p; > bool need_sd = false; >@@ -878,15 +895,6 @@ static int aclread_search(struct ldb_module *module, struct ldb_request *req) > } > ac->module = module; > ac->req = req; >- ac->schema = dsdb_get_schema(ldb, req); >- if (flags & DSDB_ACL_CHECKS_DIRSYNC_FLAG) { >- ac->indirsync = true; >- } else { >- ac->indirsync = false; >- } >- if (!ac->schema) { >- return ldb_operr(ldb); >- } > > attrs = req->op.search.attrs; > if (attrs == NULL) { >@@ -943,7 +951,7 @@ static int aclread_search(struct ldb_module *module, struct ldb_request *req) > ac->added_nTSecurityDescriptor = true; > } > >- ac->attrs = req->op.search.attrs; >+ ac->am_administrator = dsdb_module_am_administrator(module); > > /* check accessibility of base */ > if (!ldb_dn_is_null(req->op.search.base)) { >@@ -987,19 +995,270 @@ static int aclread_search(struct ldb_module *module, struct ldb_request *req) > return LDB_ERR_OPERATIONS_ERROR; > } > >+ /* >+ * We provide 'ac' as the control value, which is then used by the >+ * callback to avoid double-work. >+ */ >+ ret = ldb_request_add_control(down_req, DSDB_CONTROL_ACL_READ_OID, false, ac); >+ if (ret != LDB_SUCCESS) { >+ return ldb_error(ldb, ret, >+ "acl_read: Error adding acl_read control."); >+ } >+ > return ldb_next_request(module, down_req); > } > >+/* >+ * Here we mark inaccessible attributes known to be looked for in the >+ * filter. This only redacts attributes found in the search expression. If any >+ * extended attribute match rules examine different attributes without their own >+ * access control checks, a security bypass is possible. >+ */ >+static int acl_redact_msg_for_filter(struct ldb_module *module, struct ldb_request *req, struct ldb_message *msg) >+{ >+ struct ldb_context *ldb = ldb_module_get_ctx(module); >+ const struct aclread_private *private_data = NULL; >+ struct ldb_control *control = NULL; >+ struct aclread_context *ac = NULL; >+ struct access_check_context acl_ctx; >+ int ret; >+ unsigned i; >+ >+ /* >+ * The private data contains a list of attributes which are to be >+ * considered secret. >+ */ >+ private_data = talloc_get_type(ldb_module_get_private(module), struct aclread_private); >+ if (private_data == NULL) { >+ return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, >+ "aclread_private data is missing"); >+ } >+ if (!private_data->enabled) { >+ return LDB_SUCCESS; >+ } >+ >+ control = ldb_request_get_control(req, DSDB_CONTROL_ACL_READ_OID); >+ if (control == NULL) { >+ /* >+ * We've bypassed the acl_read module for this request, and >+ * should skip redaction in this case. >+ */ >+ return LDB_SUCCESS; >+ } >+ >+ ac = talloc_get_type_abort(control->data, struct aclread_context); >+ >+ if (!ac->got_tree_attrs) { >+ ret = ldb_parse_tree_collect_acl_attrs(module, ac, &ac->tree_attrs, req->op.search.tree); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ ac->got_tree_attrs = true; >+ } >+ >+ for (i = 0; i < msg->num_elements; ++i) { >+ struct ldb_message_element *el = &msg->elements[i]; >+ >+ /* Is the attribute mentioned in the search expression? */ >+ if (attr_in_vec(&ac->tree_attrs, el->name)) { >+ /* >+ * We need to fetch the security descriptor to check >+ * this element. >+ */ >+ break; >+ } >+ >+ /* >+ * This attribute is not in the search filter, so we can leave >+ * handling it till aclread_callback(), by which time we know >+ * this object is a match. This saves work checking ACLs if the >+ * search is unindexed and most objects don't match the filter. >+ */ >+ } >+ >+ if (i == msg->num_elements) { >+ /* All elements have been checked. */ >+ return LDB_SUCCESS; >+ } >+ >+ ret = setup_access_check_context(ac, msg, &acl_ctx); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ >+ /* For every element in the message and the parse tree, check RP. */ >+ >+ for (/* begin where we left off */; i < msg->num_elements; ++i) { >+ struct ldb_message_element *el = &msg->elements[i]; >+ >+ /* Is the attribute mentioned in the search expression? */ >+ if (!attr_in_vec(&ac->tree_attrs, el->name)) { >+ /* >+ * If not, leave it for later and check the next >+ * attribute. >+ */ >+ continue; >+ } >+ >+ /* >+ * We need to check whether the attribute is secret, >+ * confidential, or access-controlled. >+ */ >+ ret = acl_redact_attr(ac, >+ el, >+ ac, >+ private_data, >+ msg, >+ ac->schema, >+ acl_ctx.sd, >+ acl_ctx.sid, >+ acl_ctx.objectclass); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ >+ acl_element_mark_access_checked(el); >+ } >+ >+ return LDB_SUCCESS; >+} >+ > static int aclread_init(struct ldb_module *module) > { > struct ldb_context *ldb = ldb_module_get_ctx(module); >+ unsigned int i, n, j; >+ TALLOC_CTX *mem_ctx = NULL; >+ int ret; >+ bool userPassword_support; >+ static const char * const attrs[] = { "passwordAttribute", NULL }; >+ static const char * const secret_attrs[] = { >+ DSDB_SECRET_ATTRIBUTES >+ }; >+ struct ldb_result *res; >+ struct ldb_message *msg; >+ struct ldb_message_element *password_attributes; > struct aclread_private *p = talloc_zero(module, struct aclread_private); > if (p == NULL) { > return ldb_module_oom(module); > } > p->enabled = lpcfg_parm_bool(ldb_get_opaque(ldb, "loadparm"), NULL, "acl", "search", true); >+ >+ ret = ldb_mod_register_control(module, LDB_CONTROL_SD_FLAGS_OID); >+ if (ret != LDB_SUCCESS) { >+ ldb_debug(ldb, LDB_DEBUG_ERROR, >+ "acl_module_init: Unable to register sd_flags control with rootdse!\n"); >+ return ldb_operr(ldb); >+ } >+ > ldb_module_set_private(module, p); >- return ldb_next_init(module); >+ >+ mem_ctx = talloc_new(module); >+ if (!mem_ctx) { >+ return ldb_oom(ldb); >+ } >+ >+ ret = dsdb_module_search_dn(module, mem_ctx, &res, >+ ldb_dn_new(mem_ctx, ldb, "@KLUDGEACL"), >+ attrs, >+ DSDB_FLAG_NEXT_MODULE | >+ DSDB_FLAG_AS_SYSTEM, >+ NULL); >+ if (ret != LDB_SUCCESS) { >+ goto done; >+ } >+ if (res->count == 0) { >+ goto done; >+ } >+ >+ if (res->count > 1) { >+ talloc_free(mem_ctx); >+ return LDB_ERR_CONSTRAINT_VIOLATION; >+ } >+ >+ msg = res->msgs[0]; >+ >+ password_attributes = ldb_msg_find_element(msg, "passwordAttribute"); >+ if (!password_attributes) { >+ goto done; >+ } >+ p->password_attrs = talloc_array(p, const char *, >+ password_attributes->num_values + >+ ARRAY_SIZE(secret_attrs) + 1); >+ if (!p->password_attrs) { >+ talloc_free(mem_ctx); >+ return ldb_oom(ldb); >+ } >+ >+ n = 0; >+ for (i=0; i < password_attributes->num_values; i++) { >+ p->password_attrs[n] = (const char *)password_attributes->values[i].data; >+ talloc_steal(p->password_attrs, password_attributes->values[i].data); >+ n++; >+ } >+ >+ for (i=0; i < ARRAY_SIZE(secret_attrs); i++) { >+ bool found = false; >+ >+ for (j=0; j < n; j++) { >+ if (strcasecmp(p->password_attrs[j], secret_attrs[i]) == 0) { >+ found = true; >+ break; >+ } >+ } >+ >+ if (found) { >+ continue; >+ } >+ >+ p->password_attrs[n] = talloc_strdup(p->password_attrs, >+ secret_attrs[i]); >+ if (p->password_attrs[n] == NULL) { >+ talloc_free(mem_ctx); >+ return ldb_oom(ldb); >+ } >+ n++; >+ } >+ p->password_attrs[n] = NULL; >+ >+ ret = ldb_register_redact_callback(ldb, acl_redact_msg_for_filter, module); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ >+done: >+ talloc_free(mem_ctx); >+ ret = ldb_next_init(module); >+ >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ >+ if (p->password_attrs != NULL) { >+ /* >+ * Check this after the modules have be initialised so we can >+ * actually read the backend DB. >+ */ >+ userPassword_support = dsdb_user_password_support(module, >+ module, >+ NULL); >+ if (!userPassword_support) { >+ /* >+ * Remove the userPassword attribute, as it is not >+ * considered secret. >+ */ >+ for (i = 0; p->password_attrs[i] != NULL; ++i) { >+ if (ldb_attr_cmp(p->password_attrs[i], "userPassword") == 0) { >+ break; >+ } >+ } >+ >+ /* Shift following elements backwards by one. */ >+ for (; p->password_attrs[i] != NULL; ++i) { >+ p->password_attrs[i] = p->password_attrs[i + 1]; >+ } >+ } >+ } >+ return ret; > } > > static const struct ldb_module_ops ldb_aclread_module_ops = { >diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h >index b0fdfeb3967..3c4c78822bf 100644 >--- a/source4/dsdb/samdb/samdb.h >+++ b/source4/dsdb/samdb/samdb.h >@@ -221,6 +221,8 @@ struct dsdb_control_transaction_identifier { > struct GUID transaction_guid; > }; > >+#define DSDB_CONTROL_ACL_READ_OID "1.3.6.1.4.1.7165.4.3.37" >+ > #define DSDB_EXTENDED_REPLICATED_OBJECTS_OID "1.3.6.1.4.1.7165.4.4.1" > struct dsdb_extended_replicated_object { > struct ldb_message *msg; >diff --git a/source4/dsdb/tests/python/confidential_attr.py b/source4/dsdb/tests/python/confidential_attr.py >index 031c9690ba6..6889d5a5560 100755 >--- a/source4/dsdb/tests/python/confidential_attr.py >+++ b/source4/dsdb/tests/python/confidential_attr.py >@@ -490,7 +490,7 @@ class ConfidentialAttrTest(ConfidentialAttrCommon): > self.make_attr_confidential() > > self.assert_conf_attr_searches(has_rights_to=0) >- dc_mode = self.guess_dc_mode() >+ dc_mode = DC_MODE_RETURN_ALL > self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode) > self.assert_attr_visible(expect_attr=False) > >@@ -503,7 +503,7 @@ class ConfidentialAttrTest(ConfidentialAttrCommon): > self.make_attr_confidential() > > self.assert_conf_attr_searches(has_rights_to=0) >- dc_mode = self.guess_dc_mode() >+ dc_mode = DC_MODE_RETURN_ALL > self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode) > self.assert_attr_visible(expect_attr=False) > >@@ -566,7 +566,7 @@ class ConfidentialAttrTest(ConfidentialAttrCommon): > self.make_attr_confidential() > > self.assert_conf_attr_searches(has_rights_to=0) >- dc_mode = self.guess_dc_mode() >+ dc_mode = DC_MODE_RETURN_ALL > self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode) > self.assert_attr_visible(expect_attr=False) > >@@ -741,7 +741,7 @@ class ConfidentialAttrTestDenyAcl(ConfidentialAttrCommon): > > # the user shouldn't be able to see the attribute anymore > self.assert_conf_attr_searches(has_rights_to="deny-one") >- dc_mode = self.guess_dc_mode() >+ dc_mode = DC_MODE_RETURN_ALL > self.assert_negative_searches(has_rights_to="deny-one", > dc_mode=dc_mode) > self.assert_attr_visible(expect_attr=False) >@@ -917,7 +917,7 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): > > self.assert_conf_attr_searches(has_rights_to=0) > self.assert_attr_visible(expect_attr=False) >- dc_mode = self.guess_dc_mode() >+ dc_mode = DC_MODE_RETURN_ALL > self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode) > > # as a final sanity-check, make sure the admin can still see the attr >@@ -1012,7 +1012,7 @@ class ConfidentialAttrTestDirsync(ConfidentialAttrCommon): > # check we can't see the objects now, even with using dirsync controls > self.assert_conf_attr_searches(has_rights_to=0) > self.assert_attr_visible(expect_attr=False) >- dc_mode = self.guess_dc_mode() >+ dc_mode = DC_MODE_RETURN_ALL > self.assert_negative_searches(has_rights_to=0, dc_mode=dc_mode) > > # now delete the users (except for the user whose LDB connection >diff --git a/source4/setup/schema_samba4.ldif b/source4/setup/schema_samba4.ldif >index 79800bfd6df..27c7e9dbb47 100644 >--- a/source4/setup/schema_samba4.ldif >+++ b/source4/setup/schema_samba4.ldif >@@ -233,6 +233,7 @@ > #Allocated: DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID 1.3.6.1.4.1.7165.4.3.34 > #Allocated: DSDB_CONTROL_FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE_OID 1.3.6.1.4.1.7165.4.3.35 > #Allocated: DSDB_CONTROL_CALCULATED_DEFAULT_SD_OID 1.3.6.1.4.1.7165.4.3.36 >+#Allocated: DSDB_CONTROL_ACL_READ_OID 1.3.6.1.4.1.7165.4.3.37 > > > # Extended 1.3.6.1.4.1.7165.4.4.x >-- >2.25.1 > > >From 522bf4e34a86af9adebf4b91248720d0711f103a Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Mon, 27 Feb 2023 13:31:44 +1300 >Subject: [PATCH 26/35] CVE-2023-0614 s4-acl: Avoid calling > dsdb_module_am_system() if we can help it > >If the AS_SYSTEM control is present, we know we have system privileges, >and have no need to call dsdb_module_am_system(). > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/dsdb/samdb/ldb_modules/acl_read.c | 11 ++++++++--- > 1 file changed, 8 insertions(+), 3 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/acl_read.c b/source4/dsdb/samdb/ldb_modules/acl_read.c >index 8ca8607b925..6dcc3c9b36e 100644 >--- a/source4/dsdb/samdb/ldb_modules/acl_read.c >+++ b/source4/dsdb/samdb/ldb_modules/acl_read.c >@@ -860,7 +860,7 @@ static int aclread_search(struct ldb_module *module, struct ldb_request *req) > int ret; > struct aclread_context *ac; > struct ldb_request *down_req; >- struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID); >+ bool am_system; > struct ldb_result *res; > struct aclread_private *p; > bool need_sd = false; >@@ -877,11 +877,16 @@ static int aclread_search(struct ldb_module *module, struct ldb_request *req) > ldb = ldb_module_get_ctx(module); > p = talloc_get_type(ldb_module_get_private(module), struct aclread_private); > >+ am_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID) != NULL; >+ if (!am_system) { >+ am_system = dsdb_module_am_system(module); >+ } >+ > /* skip access checks if we are system or system control is supplied > * or this is not LDAP server request */ > if (!p || !p->enabled || >- dsdb_module_am_system(module) >- || as_system || !is_untrusted) { >+ am_system || >+ !is_untrusted) { > return ldb_next_request(module, req); > } > /* no checks on special dn */ >-- >2.25.1 > > >From 0113200274dac53a015a65a29217d04f6c6cbbf3 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Thu, 16 Feb 2023 12:35:34 +1300 >Subject: [PATCH 27/35] CVE-2023-0614 ldb: Use binary search to check whether > attribute is secret > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/dsdb/samdb/ldb_modules/acl_read.c | 56 ++++++++++++++--------- > 1 file changed, 35 insertions(+), 21 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/acl_read.c b/source4/dsdb/samdb/ldb_modules/acl_read.c >index 6dcc3c9b36e..21f72fbe720 100644 >--- a/source4/dsdb/samdb/ldb_modules/acl_read.c >+++ b/source4/dsdb/samdb/ldb_modules/acl_read.c >@@ -79,6 +79,7 @@ struct aclread_private { > struct security_descriptor *sd_cached; > struct ldb_val sd_cached_blob; > const char **password_attrs; >+ size_t num_password_attrs; > }; > > struct access_check_context { >@@ -512,22 +513,18 @@ static int aclread_get_sd_from_ldb_message(struct aclread_context *ac, > /* Check whether the attribute is a password attribute. */ > static bool attr_is_secret(const char *attr, const struct aclread_private *private_data) > { >- unsigned i; >+ const char **found = NULL; > > if (private_data->password_attrs == NULL) { > return false; > } > >- for (i = 0; private_data->password_attrs[i] != NULL; ++i) { >- const char *password_attr = private_data->password_attrs[i]; >- if (ldb_attr_cmp(attr, password_attr) != 0) { >- continue; >- } >- >- return true; >- } >- >- return false; >+ BINARY_ARRAY_SEARCH_V(private_data->password_attrs, >+ private_data->num_password_attrs, >+ attr, >+ ldb_attr_cmp, >+ found); >+ return found != NULL; > } > > /* >@@ -1128,6 +1125,14 @@ static int acl_redact_msg_for_filter(struct ldb_module *module, struct ldb_reque > return LDB_SUCCESS; > } > >+static int ldb_attr_cmp_fn(const void *_a, const void *_b) >+{ >+ const char * const *a = _a; >+ const char * const *b = _b; >+ >+ return ldb_attr_cmp(*a, *b); >+} >+ > static int aclread_init(struct ldb_module *module) > { > struct ldb_context *ldb = ldb_module_get_ctx(module); >@@ -1188,7 +1193,7 @@ static int aclread_init(struct ldb_module *module) > } > p->password_attrs = talloc_array(p, const char *, > password_attributes->num_values + >- ARRAY_SIZE(secret_attrs) + 1); >+ ARRAY_SIZE(secret_attrs)); > if (!p->password_attrs) { > talloc_free(mem_ctx); > return ldb_oom(ldb); >@@ -1223,7 +1228,10 @@ static int aclread_init(struct ldb_module *module) > } > n++; > } >- p->password_attrs[n] = NULL; >+ p->num_password_attrs = n; >+ >+ /* Sort the password attributes so we can use binary search. */ >+ TYPESAFE_QSORT(p->password_attrs, p->num_password_attrs, ldb_attr_cmp_fn); > > ret = ldb_register_redact_callback(ldb, acl_redact_msg_for_filter, module); > if (ret != LDB_SUCCESS) { >@@ -1247,19 +1255,25 @@ done: > module, > NULL); > if (!userPassword_support) { >+ const char **found = NULL; >+ > /* > * Remove the userPassword attribute, as it is not > * considered secret. > */ >- for (i = 0; p->password_attrs[i] != NULL; ++i) { >- if (ldb_attr_cmp(p->password_attrs[i], "userPassword") == 0) { >- break; >+ BINARY_ARRAY_SEARCH_V(p->password_attrs, >+ p->num_password_attrs, >+ "userPassword", >+ ldb_attr_cmp, >+ found); >+ if (found != NULL) { >+ size_t found_idx = found - p->password_attrs; >+ >+ /* Shift following elements backwards by one. */ >+ for (i = found_idx; i < p->num_password_attrs - 1; ++i) { >+ p->password_attrs[i] = p->password_attrs[i + 1]; > } >- } >- >- /* Shift following elements backwards by one. */ >- for (; p->password_attrs[i] != NULL; ++i) { >- p->password_attrs[i] = p->password_attrs[i + 1]; >+ --p->num_password_attrs; > } > } > } >-- >2.25.1 > > >From 627245e019f2a2b670b4642731a00f11f43e61b4 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 14 Feb 2023 13:17:24 +1300 >Subject: [PATCH 28/35] CVE-2023-0614 ldb: Centralise checking for inaccessible > matches > >This makes it less likely that we forget to handle a case. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > lib/ldb-samba/ldb_matching_rules.c | 5 --- > lib/ldb/common/ldb_match.c | 56 +++++++++++++++++------------- > 2 files changed, 31 insertions(+), 30 deletions(-) > >diff --git a/lib/ldb-samba/ldb_matching_rules.c b/lib/ldb-samba/ldb_matching_rules.c >index 0c4c31e49f9..b86594c1823 100644 >--- a/lib/ldb-samba/ldb_matching_rules.c >+++ b/lib/ldb-samba/ldb_matching_rules.c >@@ -87,11 +87,6 @@ static int ldb_eval_transitive_filter_helper(TALLOC_CTX *mem_ctx, > return LDB_SUCCESS; > } > >- if (ldb_msg_element_is_inaccessible(el)) { >- *matched = false; >- return LDB_SUCCESS; >- } >- > /* > * If the value to match is present in the attribute values of the > * current entry being visited, set matched to true and return OK >diff --git a/lib/ldb/common/ldb_match.c b/lib/ldb/common/ldb_match.c >index 17314f1b9ca..645cb695ef1 100644 >--- a/lib/ldb/common/ldb_match.c >+++ b/lib/ldb/common/ldb_match.c >@@ -98,11 +98,6 @@ static int ldb_match_present(struct ldb_context *ldb, > return LDB_SUCCESS; > } > >- if (ldb_msg_element_is_inaccessible(el)) { >- *matched = false; >- return LDB_SUCCESS; >- } >- > a = ldb_schema_attribute_by_name(ldb, el->name); > if (!a) { > return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; >@@ -144,11 +139,6 @@ static int ldb_match_comparison(struct ldb_context *ldb, > return LDB_SUCCESS; > } > >- if (ldb_msg_element_is_inaccessible(el)) { >- *matched = false; >- return LDB_SUCCESS; >- } >- > a = ldb_schema_attribute_by_name(ldb, el->name); > if (!a) { > return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; >@@ -219,11 +209,6 @@ static int ldb_match_equality(struct ldb_context *ldb, > return LDB_SUCCESS; > } > >- if (ldb_msg_element_is_inaccessible(el)) { >- *matched = false; >- return LDB_SUCCESS; >- } >- > a = ldb_schema_attribute_by_name(ldb, el->name); > if (a == NULL) { > return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; >@@ -385,11 +370,6 @@ static int ldb_match_substring(struct ldb_context *ldb, > return LDB_SUCCESS; > } > >- if (ldb_msg_element_is_inaccessible(el)) { >- *matched = false; >- return LDB_SUCCESS; >- } >- > for (i = 0; i < el->num_values; i++) { > int ret; > ret = ldb_wildcard_compare(ldb, tree, el->values[i], matched); >@@ -463,11 +443,6 @@ static int ldb_match_bitmask(struct ldb_context *ldb, > return LDB_SUCCESS; > } > >- if (ldb_msg_element_is_inaccessible(el)) { >- *matched = false; >- return LDB_SUCCESS; >- } >- > for (i=0;i<el->num_values;i++) { > int ret; > struct ldb_val *v = &el->values[i]; >@@ -556,6 +531,26 @@ static int ldb_match_extended(struct ldb_context *ldb, > &tree->u.extended.value, matched); > } > >+static bool ldb_must_suppress_match(const struct ldb_message *msg, >+ const struct ldb_parse_tree *tree) >+{ >+ const char *attr = NULL; >+ struct ldb_message_element *el = NULL; >+ >+ attr = ldb_parse_tree_get_attr(tree); >+ if (attr == NULL) { >+ return false; >+ } >+ >+ /* find the message element */ >+ el = ldb_msg_find_element(msg, attr); >+ if (el == NULL) { >+ return false; >+ } >+ >+ return ldb_msg_element_is_inaccessible(el); >+} >+ > /* > Check if a particular message will match the given filter > >@@ -580,6 +575,17 @@ int ldb_match_message(struct ldb_context *ldb, > return LDB_SUCCESS; > } > >+ /* >+ * Suppress matches on confidential attributes (handled >+ * manually in extended matches as these can do custom things >+ * like read other parts of the DB or other attributes). >+ */ >+ if (tree->operation != LDB_OP_EXTENDED) { >+ if (ldb_must_suppress_match(msg, tree)) { >+ return LDB_SUCCESS; >+ } >+ } >+ > switch (tree->operation) { > case LDB_OP_AND: > for (i=0;i<tree->u.list.num_elements;i++) { >-- >2.25.1 > > >From 5be58846fe617ceb58cba072d7318616f0c2e5e6 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 3 Mar 2023 17:35:55 +1300 >Subject: [PATCH 29/35] CVE-2023-0614 ldb: Filter on search base before > redacting message > >Redaction may be expensive if we end up needing to fetch a security >descriptor to verify rights to an attribute. Checking the search scope >is probably cheaper, so do that first. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > lib/ldb/common/ldb_match.c | 8 +++--- > lib/ldb/include/ldb_private.h | 8 ++++++ > lib/ldb/ldb_key_value/ldb_kv_index.c | 40 +++++++++++++++------------ > lib/ldb/ldb_key_value/ldb_kv_search.c | 14 ++++++++-- > 4 files changed, 47 insertions(+), 23 deletions(-) > >diff --git a/lib/ldb/common/ldb_match.c b/lib/ldb/common/ldb_match.c >index 645cb695ef1..8e27ecbe723 100644 >--- a/lib/ldb/common/ldb_match.c >+++ b/lib/ldb/common/ldb_match.c >@@ -38,10 +38,10 @@ > /* > check if the scope matches in a search result > */ >-static int ldb_match_scope(struct ldb_context *ldb, >- struct ldb_dn *base, >- struct ldb_dn *dn, >- enum ldb_scope scope) >+int ldb_match_scope(struct ldb_context *ldb, >+ struct ldb_dn *base, >+ struct ldb_dn *dn, >+ enum ldb_scope scope) > { > int ret = 0; > >diff --git a/lib/ldb/include/ldb_private.h b/lib/ldb/include/ldb_private.h >index b0a42f6421c..5e29de34f79 100644 >--- a/lib/ldb/include/ldb_private.h >+++ b/lib/ldb/include/ldb_private.h >@@ -322,6 +322,14 @@ int ldb_match_message(struct ldb_context *ldb, > const struct ldb_parse_tree *tree, > enum ldb_scope scope, bool *matched); > >+/* >+ check if the scope matches in a search result >+*/ >+int ldb_match_scope(struct ldb_context *ldb, >+ struct ldb_dn *base, >+ struct ldb_dn *dn, >+ enum ldb_scope scope); >+ > /* Reallocate elements to drop any excess capacity. */ > void ldb_msg_shrink_to_fit(struct ldb_message *msg); > >diff --git a/lib/ldb/ldb_key_value/ldb_kv_index.c b/lib/ldb/ldb_key_value/ldb_kv_index.c >index 163052fecf7..aac0913f431 100644 >--- a/lib/ldb/ldb_key_value/ldb_kv_index.c >+++ b/lib/ldb/ldb_key_value/ldb_kv_index.c >@@ -2428,31 +2428,37 @@ static int ldb_kv_index_filter(struct ldb_kv_private *ldb_kv, > return LDB_ERR_OPERATIONS_ERROR; > } > >- if (ldb->redact.callback != NULL) { >- ret = ldb->redact.callback(ldb->redact.module, ac->req, msg); >- if (ret != LDB_SUCCESS) { >- talloc_free(msg); >- return ret; >- } >- } >- > /* > * We trust the index for LDB_SCOPE_ONELEVEL > * unless the index key has been truncated. > * > * LDB_SCOPE_BASE is not passed in by our only caller. > */ >- if (ac->scope == LDB_SCOPE_ONELEVEL && >- ldb_kv->cache->one_level_indexes && >- scope_one_truncation == KEY_NOT_TRUNCATED) { >- ret = ldb_match_message(ldb, msg, ac->tree, >- ac->scope, &matched); >- } else { >- ret = ldb_match_msg_error(ldb, msg, >- ac->tree, ac->base, >- ac->scope, &matched); >+ if (ac->scope != LDB_SCOPE_ONELEVEL || >+ !ldb_kv->cache->one_level_indexes || >+ scope_one_truncation != KEY_NOT_TRUNCATED) >+ { >+ /* >+ * The redaction callback may be expensive to call if it >+ * fetches a security descriptor. Check the DN early and >+ * bail out if it doesn't match the base. >+ */ >+ if (!ldb_match_scope(ldb, ac->base, msg->dn, ac->scope)) { >+ talloc_free(msg); >+ continue; >+ } >+ } >+ >+ if (ldb->redact.callback != NULL) { >+ ret = ldb->redact.callback(ldb->redact.module, ac->req, msg); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(msg); >+ return ret; >+ } > } > >+ ret = ldb_match_message(ldb, msg, ac->tree, >+ ac->scope, &matched); > if (ret != LDB_SUCCESS) { > talloc_free(keys); > talloc_free(msg); >diff --git a/lib/ldb/ldb_key_value/ldb_kv_search.c b/lib/ldb/ldb_key_value/ldb_kv_search.c >index d187ba223e1..27f68caef01 100644 >--- a/lib/ldb/ldb_key_value/ldb_kv_search.c >+++ b/lib/ldb/ldb_key_value/ldb_kv_search.c >@@ -395,6 +395,16 @@ static int search_func(_UNUSED_ struct ldb_kv_private *ldb_kv, > } > } > >+ /* >+ * The redaction callback may be expensive to call if it fetches a >+ * security descriptor. Check the DN early and bail out if it doesn't >+ * match the base. >+ */ >+ if (!ldb_match_scope(ldb, ac->base, msg->dn, ac->scope)) { >+ talloc_free(msg); >+ return 0; >+ } >+ > if (ldb->redact.callback != NULL) { > ret = ldb->redact.callback(ldb->redact.module, ac->req, msg); > if (ret != LDB_SUCCESS) { >@@ -404,8 +414,8 @@ static int search_func(_UNUSED_ struct ldb_kv_private *ldb_kv, > } > > /* see if it matches the given expression */ >- ret = ldb_match_msg_error(ldb, msg, >- ac->tree, ac->base, ac->scope, &matched); >+ ret = ldb_match_message(ldb, msg, >+ ac->tree, ac->scope, &matched); > if (ret != LDB_SUCCESS) { > talloc_free(msg); > ac->error = LDB_ERR_OPERATIONS_ERROR; >-- >2.25.1 > > >From 217c21e7807d8bb4753bf163273db12b95f0e610 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 24 Feb 2023 10:03:25 +1300 >Subject: [PATCH 30/35] CVE-2023-0614 s4-dsdb: Treat confidential attributes as > unindexed > >In the unlikely case that someone adds a confidential indexed attribute >to the schema, LDAP search expressions on that attribute could disclose >information via timing differences. Let's not use the index for searches >on confidential attributes. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/dsdb/samdb/ldb_modules/extended_dn_in.c | 10 +++++++++- > source4/dsdb/schema/schema_description.c | 7 +++++++ > source4/dsdb/schema/schema_init.c | 11 +++++++++-- > source4/dsdb/schema/schema_set.c | 9 ++++++++- > 4 files changed, 33 insertions(+), 4 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/extended_dn_in.c b/source4/dsdb/samdb/ldb_modules/extended_dn_in.c >index 1dc1e1f2d42..248bb66f039 100644 >--- a/source4/dsdb/samdb/ldb_modules/extended_dn_in.c >+++ b/source4/dsdb/samdb/ldb_modules/extended_dn_in.c >@@ -423,7 +423,15 @@ static int extended_dn_filter_callback(struct ldb_parse_tree *tree, void *privat > guid_val = ldb_dn_get_extended_component(dn, "GUID"); > sid_val = ldb_dn_get_extended_component(dn, "SID"); > >- if (!guid_val && !sid_val && (attribute->searchFlags & SEARCH_FLAG_ATTINDEX)) { >+ /* >+ * Is the attribute indexed? By treating confidential attributes >+ * as unindexed, we force searches to go through the unindexed >+ * search path, avoiding observable timing differences. >+ */ >+ if (!guid_val && !sid_val && >+ (attribute->searchFlags & SEARCH_FLAG_ATTINDEX) && >+ !(attribute->searchFlags & SEARCH_FLAG_CONFIDENTIAL)) >+ { > /* if it is indexed, then fixing the string DN will do > no good here, as we will not find the attribute in > the index. So for now fall through to a standard DN >diff --git a/source4/dsdb/schema/schema_description.c b/source4/dsdb/schema/schema_description.c >index 243a02a15f3..5fc70154bf8 100644 >--- a/source4/dsdb/schema/schema_description.c >+++ b/source4/dsdb/schema/schema_description.c >@@ -160,6 +160,13 @@ char *schema_attribute_to_extendedInfo(TALLOC_CTX *mem_ctx, const struct dsdb_at > attribute->rangeUpper, > GUID_hexstring(tmp_ctx, &attribute->schemaIDGUID), > GUID_hexstring(tmp_ctx, &attribute->attributeSecurityGUID), >+ /* >+ * We actually ignore the indexed >+ * flag for confidential >+ * attributes, but we'll include >+ * it for the purposes of >+ * description. >+ */ > (attribute->searchFlags & SEARCH_FLAG_ATTINDEX), > attribute->systemOnly); > talloc_free(tmp_ctx); >diff --git a/source4/dsdb/schema/schema_init.c b/source4/dsdb/schema/schema_init.c >index a3b00497b6b..c8197b86306 100644 >--- a/source4/dsdb/schema/schema_init.c >+++ b/source4/dsdb/schema/schema_init.c >@@ -514,8 +514,15 @@ static int dsdb_schema_setup_ldb_schema_attribute(struct ldb_context *ldb, > if (attr->isSingleValued) { > a->flags |= LDB_ATTR_FLAG_SINGLE_VALUE; > } >- >- if (attr->searchFlags & SEARCH_FLAG_ATTINDEX) { >+ >+ /* >+ * Is the attribute indexed? By treating confidential attributes as >+ * unindexed, we force searches to go through the unindexed search path, >+ * avoiding observable timing differences. >+ */ >+ if (attr->searchFlags & SEARCH_FLAG_ATTINDEX && >+ !(attr->searchFlags & SEARCH_FLAG_CONFIDENTIAL)) >+ { > a->flags |= LDB_ATTR_FLAG_INDEXED; > } > >diff --git a/source4/dsdb/schema/schema_set.c b/source4/dsdb/schema/schema_set.c >index 45faa0912ec..03cf2405595 100644 >--- a/source4/dsdb/schema/schema_set.c >+++ b/source4/dsdb/schema/schema_set.c >@@ -221,7 +221,14 @@ int dsdb_schema_set_indices_and_attributes(struct ldb_context *ldb, > break; > } > >- if (attr->searchFlags & SEARCH_FLAG_ATTINDEX) { >+ /* >+ * Is the attribute indexed? By treating confidential attributes >+ * as unindexed, we force searches to go through the unindexed >+ * search path, avoiding observable timing differences. >+ */ >+ if (attr->searchFlags & SEARCH_FLAG_ATTINDEX && >+ !(attr->searchFlags & SEARCH_FLAG_CONFIDENTIAL)) >+ { > /* > * When preparing to downgrade Samba, we need to write > * out an LDB without the new key word ORDERED_INTEGER. >-- >2.25.1 > > >From 903473a7c6d4ab0c27ec85a7af4623cc30cd6e2c Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Thu, 2 Mar 2023 16:31:17 +1300 >Subject: [PATCH 31/35] CVE-2023-0614 dsdb: Add DSDB_MARK_REQ_UNTRUSTED > >This will allow our dsdb helper search functions to mark the new >request as untrusted, forcing read ACL evaluation (per current behaviour). > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz> > >[abartlet@samba.org adapted due to Samba 4.16 and lower > not having the patches for CVE-2022-32743] >--- > source4/dsdb/common/util.c | 4 ++++ > source4/dsdb/common/util.h | 1 + > 2 files changed, 5 insertions(+) > >diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c >index 1436cd7bfec..c261aee3e59 100644 >--- a/source4/dsdb/common/util.c >+++ b/source4/dsdb/common/util.c >@@ -4606,6 +4606,10 @@ int dsdb_request_add_controls(struct ldb_request *req, uint32_t dsdb_flags) > } > } > >+ if (dsdb_flags & DSDB_MARK_REQ_UNTRUSTED) { >+ ldb_req_mark_untrusted(req); >+ } >+ > return LDB_SUCCESS; > } > >diff --git a/source4/dsdb/common/util.h b/source4/dsdb/common/util.h >index e1854644d53..5bb96d60b3c 100644 >--- a/source4/dsdb/common/util.h >+++ b/source4/dsdb/common/util.h >@@ -43,6 +43,7 @@ > #define DSDB_MODIFY_PARTIAL_REPLICA 0x04000 > #define DSDB_PASSWORD_BYPASS_LAST_SET 0x08000 > #define DSDB_REPLMD_VANISH_LINKS 0x10000 >+#define DSDB_MARK_REQ_UNTRUSTED 0x20000 > > bool is_attr_in_list(const char * const * attrs, const char *attr); > >-- >2.25.1 > > >From 211e794c1adc802fae263e16362ebfc0309d2699 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Fri, 3 Mar 2023 16:49:00 +1300 >Subject: [PATCH 32/35] CVE-2023-0614 dsdb: Add pre-cleanup and > self.addCleanup() of OU created in match_rules tests > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > lib/ldb-samba/tests/match_rules.py | 8 ++++++++ > 1 file changed, 8 insertions(+) > >diff --git a/lib/ldb-samba/tests/match_rules.py b/lib/ldb-samba/tests/match_rules.py >index abf485c9eab..2af1dd6a070 100755 >--- a/lib/ldb-samba/tests/match_rules.py >+++ b/lib/ldb-samba/tests/match_rules.py >@@ -31,11 +31,19 @@ class MatchRulesTests(samba.tests.TestCase): > self.ou_groups = "OU=groups,%s" % self.ou > self.ou_computers = "OU=computers,%s" % self.ou > >+ try: >+ self.ldb.delete(self.ou, ["tree_delete:1"]) >+ except LdbError as e: >+ pass >+ > # Add a organizational unit to create objects > self.ldb.add({ > "dn": self.ou, > "objectclass": "organizationalUnit"}) > >+ self.addCleanup(self.ldb.delete, self.ou, controls=['tree_delete:0']) >+ >+ > # Add the following OU hierarchy and set otherWellKnownObjects, > # which has BinaryDN syntax: > # >-- >2.25.1 > > >From 27a62a6cb24820598699f0e3249b48e8293ce6dc Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Thu, 2 Mar 2023 16:51:25 +1300 >Subject: [PATCH 33/35] CVE-2023-0614 lib/ldb-samba: Add test for > SAMBA_LDAP_MATCH_RULE_TRANSITIVE_EVAL / LDAP_MATCHING_RULE_IN_CHAIN with and > ACL hidden attributes > >The chain for transitive evaluation does consider ACLs, avoiding the disclosure of >confidential information. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > lib/ldb-samba/tests/match_rules.py | 127 ++++++++++++---------- > lib/ldb-samba/tests/match_rules_remote.py | 104 ++++++++++++++++++ > source4/selftest/tests.py | 1 + > 3 files changed, 175 insertions(+), 57 deletions(-) > create mode 100755 lib/ldb-samba/tests/match_rules_remote.py > >diff --git a/lib/ldb-samba/tests/match_rules.py b/lib/ldb-samba/tests/match_rules.py >index 2af1dd6a070..2fe6c3e2264 100755 >--- a/lib/ldb-samba/tests/match_rules.py >+++ b/lib/ldb-samba/tests/match_rules.py >@@ -20,13 +20,18 @@ from ldb import SCOPE_BASE, SCOPE_SUBTREE, SCOPE_ONELEVEL > # Windows appear to preserve casing of the RDN and uppercase the other keys. > > >-class MatchRulesTests(samba.tests.TestCase): >+class MatchRulesTestsBase(samba.tests.TestCase): > def setUp(self): >- super(MatchRulesTests, self).setUp() >- self.lp = lp >- self.ldb = SamDB(host, credentials=creds, session_info=system_session(lp), lp=lp) >+ super().setUp() >+ self.lp = self.sambaopts.get_loadparm() >+ self.creds = self.credopts.get_credentials(self.lp) >+ >+ self.ldb = SamDB(self.host, credentials=self.creds, >+ session_info=system_session(self.lp), >+ lp=self.lp) > self.base_dn = self.ldb.domain_dn() >- self.ou = "OU=matchrulestest,%s" % self.base_dn >+ self.ou_rdn = "OU=matchrulestest" >+ self.ou = self.ou_rdn + "," + self.base_dn > self.ou_users = "OU=users,%s" % self.ou > self.ou_groups = "OU=groups,%s" % self.ou > self.ou_computers = "OU=computers,%s" % self.ou >@@ -212,6 +217,39 @@ class MatchRulesTests(samba.tests.TestCase): > FLAG_MOD_ADD, "member") > self.ldb.modify(m) > >+ # Add a couple of ms-Exch-Configuration-Container to test forward-link >+ # attributes without backward link (addressBookRoots2) >+ # e1 >+ # |--> e2 >+ # | |--> c1 >+ self.ldb.add({ >+ "dn": "cn=e1,%s" % self.ou, >+ "objectclass": "msExchConfigurationContainer"}) >+ self.ldb.add({ >+ "dn": "cn=e2,%s" % self.ou, >+ "objectclass": "msExchConfigurationContainer"}) >+ >+ m = Message() >+ m.dn = Dn(self.ldb, "cn=e2,%s" % self.ou) >+ m["e1"] = MessageElement("cn=c1,%s" % self.ou_computers, >+ FLAG_MOD_ADD, "addressBookRoots2") >+ self.ldb.modify(m) >+ >+ m = Message() >+ m.dn = Dn(self.ldb, "cn=e1,%s" % self.ou) >+ m["e1"] = MessageElement("cn=e2,%s" % self.ou, >+ FLAG_MOD_ADD, "addressBookRoots2") >+ self.ldb.modify(m) >+ >+ >+ >+class MatchRulesTests(MatchRulesTestsBase): >+ def setUp(self): >+ self.sambaopts = sambaopts >+ self.credopts = credopts >+ self.host = host >+ super().setUp() >+ > # The msDS-RevealedUsers is owned by system and cannot be modified > # directly. Set the schemaUpgradeInProgress flag as workaround > # and create this hierarchy: >@@ -251,33 +289,6 @@ class MatchRulesTests(samba.tests.TestCase): > m["e1"] = MessageElement("0", FLAG_MOD_REPLACE, "schemaUpgradeInProgress") > self.ldb.modify(m) > >- # Add a couple of ms-Exch-Configuration-Container to test forward-link >- # attributes without backward link (addressBookRoots2) >- # e1 >- # |--> e2 >- # | |--> c1 >- self.ldb.add({ >- "dn": "cn=e1,%s" % self.ou, >- "objectclass": "msExchConfigurationContainer"}) >- self.ldb.add({ >- "dn": "cn=e2,%s" % self.ou, >- "objectclass": "msExchConfigurationContainer"}) >- >- m = Message() >- m.dn = Dn(self.ldb, "cn=e2,%s" % self.ou) >- m["e1"] = MessageElement("cn=c1,%s" % self.ou_computers, >- FLAG_MOD_ADD, "addressBookRoots2") >- self.ldb.modify(m) >- >- m = Message() >- m.dn = Dn(self.ldb, "cn=e1,%s" % self.ou) >- m["e1"] = MessageElement("cn=e2,%s" % self.ou, >- FLAG_MOD_ADD, "addressBookRoots2") >- self.ldb.modify(m) >- >- def tearDown(self): >- super(MatchRulesTests, self).tearDown() >- self.ldb.delete(self.ou, controls=['tree_delete:0']) > > def test_u1_member_of_g4(self): > # Search without transitive match must return 0 results >@@ -953,8 +964,12 @@ class MatchRulesTests(samba.tests.TestCase): > class MatchRuleConditionTests(samba.tests.TestCase): > def setUp(self): > super(MatchRuleConditionTests, self).setUp() >- self.lp = lp >- self.ldb = SamDB(host, credentials=creds, session_info=system_session(lp), lp=lp) >+ self.lp = sambaopts.get_loadparm() >+ self.creds = credopts.get_credentials(self.lp) >+ >+ self.ldb = SamDB(host, credentials=self.creds, >+ session_info=system_session(self.lp), >+ lp=self.lp) > self.base_dn = self.ldb.domain_dn() > self.ou = "OU=matchruleconditiontests,%s" % self.base_dn > self.ou_users = "OU=users,%s" % self.ou >@@ -1753,32 +1768,30 @@ class MatchRuleConditionTests(samba.tests.TestCase): > self.ou_groups, self.ou_computers)) > self.assertEqual(len(res1), 0) > >+if __name__ == "__main__": > >-parser = optparse.OptionParser("match_rules.py [options] <host>") >-sambaopts = options.SambaOptions(parser) >-parser.add_option_group(sambaopts) >-parser.add_option_group(options.VersionOptions(parser)) >- >-# use command line creds if available >-credopts = options.CredentialsOptions(parser) >-parser.add_option_group(credopts) >-opts, args = parser.parse_args() >-subunitopts = SubunitOptions(parser) >-parser.add_option_group(subunitopts) >+ parser = optparse.OptionParser("match_rules.py [options] <host>") >+ sambaopts = options.SambaOptions(parser) >+ parser.add_option_group(sambaopts) >+ parser.add_option_group(options.VersionOptions(parser)) > >-if len(args) < 1: >- parser.print_usage() >- sys.exit(1) >+ # use command line creds if available >+ credopts = options.CredentialsOptions(parser) >+ parser.add_option_group(credopts) >+ opts, args = parser.parse_args() >+ subunitopts = SubunitOptions(parser) >+ parser.add_option_group(subunitopts) > >-host = args[0] >+ if len(args) < 1: >+ parser.print_usage() >+ sys.exit(1) > >-lp = sambaopts.get_loadparm() >-creds = credopts.get_credentials(lp) >+ host = args[0] > >-if "://" not in host: >- if os.path.isfile(host): >- host = "tdb://%s" % host >- else: >- host = "ldap://%s" % host >+ if "://" not in host: >+ if os.path.isfile(host): >+ host = "tdb://%s" % host >+ else: >+ host = "ldap://%s" % host > >-TestProgram(module=__name__, opts=subunitopts) >+ TestProgram(module=__name__, opts=subunitopts) >diff --git a/lib/ldb-samba/tests/match_rules_remote.py b/lib/ldb-samba/tests/match_rules_remote.py >new file mode 100755 >index 00000000000..122231f2a60 >--- /dev/null >+++ b/lib/ldb-samba/tests/match_rules_remote.py >@@ -0,0 +1,104 @@ >+#!/usr/bin/env python3 >+ >+import optparse >+import sys >+import os >+import samba >+import samba.getopt as options >+ >+from samba.tests.subunitrun import SubunitOptions, TestProgram >+ >+from samba.samdb import SamDB >+from samba.auth import system_session >+from samba import sd_utils >+from samba.ndr import ndr_unpack >+from ldb import Message, MessageElement, Dn, LdbError >+from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE >+from ldb import SCOPE_BASE, SCOPE_SUBTREE, SCOPE_ONELEVEL >+ >+from match_rules import MatchRulesTestsBase >+ >+ >+class MatchRulesTestsUser(MatchRulesTestsBase): >+ def setUp(self): >+ self.sambaopts = sambaopts >+ self.credopts = credopts >+ self.host = host >+ super().setUp() >+ self.sd_utils = sd_utils.SDUtils(self.ldb) >+ >+ self.user_pass = "samba123@" >+ self.match_test_user = "matchtestuser" >+ self.ldb.newuser(self.match_test_user, >+ self.user_pass, >+ userou=self.ou_rdn) >+ user_creds = self.insta_creds(template=self.creds, >+ username=self.match_test_user, >+ userpass=self.user_pass) >+ self.user_ldb = SamDB(host, credentials=user_creds, lp=self.lp) >+ token_res = self.user_ldb.search(scope=SCOPE_BASE, >+ base="", >+ attrs=["tokenGroups"]) >+ self.user_sid = ndr_unpack(samba.dcerpc.security.dom_sid, >+ token_res[0]["tokenGroups"][0]) >+ >+ self.member_attr_guid = "bf9679c0-0de6-11d0-a285-00aa003049e2" >+ >+ def test_with_denied_link(self): >+ >+ # add an ACE that denies the user Read Property (RP) access to >+ # the member attr (which is similar to making the attribute >+ # confidential) >+ ace = "(OD;;RP;{0};;{1})".format(self.member_attr_guid, >+ self.user_sid) >+ g2_dn = Dn(self.ldb, "CN=g2,%s" % self.ou_groups) >+ >+ # add the ACE that denies access to the attr under test >+ self.sd_utils.dacl_add_ace(g2_dn, ace) >+ >+ # Search without transitive match must return 0 results >+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups, >+ scope=SCOPE_BASE, >+ expression="member=cn=u1,%s" % self.ou_users) >+ self.assertEqual(len(res1), 0) >+ >+ # Search with transitive match must return 1 results >+ res1 = self.ldb.search("cn=g4,%s" % self.ou_groups, >+ scope=SCOPE_BASE, >+ expression="member:1.2.840.113556.1.4.1941:=cn=u1,%s" % self.ou_users) >+ self.assertEqual(len(res1), 1) >+ self.assertEqual(str(res1[0].dn).lower(), ("CN=g4,%s" % self.ou_groups).lower()) >+ >+ # Search as a user match must return 0 results as the intermediate link can't be seen >+ res1 = self.user_ldb.search("cn=g4,%s" % self.ou_groups, >+ scope=SCOPE_BASE, >+ expression="member:1.2.840.113556.1.4.1941:=cn=u1,%s" % self.ou_users) >+ self.assertEqual(len(res1), 0) >+ >+ >+ >+parser = optparse.OptionParser("match_rules_remote.py [options] <host>") >+sambaopts = options.SambaOptions(parser) >+parser.add_option_group(sambaopts) >+parser.add_option_group(options.VersionOptions(parser)) >+ >+# use command line creds if available >+credopts = options.CredentialsOptions(parser) >+parser.add_option_group(credopts) >+opts, args = parser.parse_args() >+subunitopts = SubunitOptions(parser) >+parser.add_option_group(subunitopts) >+ >+if len(args) < 1: >+ parser.print_usage() >+ sys.exit(1) >+ >+host = args[0] >+ >+if "://" not in host: >+ if os.path.isfile(host): >+ host = "tdb://%s" % host >+ else: >+ host = "ldap://%s" % host >+ >+TestProgram(module=__name__, opts=subunitopts) >diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py >index 4707734a343..9aa34292789 100755 >--- a/source4/selftest/tests.py >+++ b/source4/selftest/tests.py >@@ -1267,6 +1267,7 @@ for env in ['ad_dc_default:local', 'schema_dc:local']: > plantestsuite_loadlist("samba4.urgent_replication.python(ad_dc_ntvfs)", "ad_dc_ntvfs:local", [python, os.path.join(DSDB_PYTEST_DIR, "urgent_replication.py"), '$PREFIX_ABS/ad_dc_ntvfs/private/sam.ldb', '$LOADLIST', '$LISTOPT']) > plantestsuite_loadlist("samba4.ldap.dirsync.python(ad_dc_ntvfs)", "ad_dc_ntvfs", [python, os.path.join(DSDB_PYTEST_DIR, "dirsync.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT']) > plantestsuite_loadlist("samba4.ldap.match_rules.python", "ad_dc_ntvfs", [python, os.path.join(srcdir(), "lib/ldb-samba/tests/match_rules.py"), '$PREFIX_ABS/ad_dc_ntvfs/private/sam.ldb', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT']) >+plantestsuite_loadlist("samba4.ldap.match_rules.python", "ad_dc_ntvfs", [python, os.path.join(srcdir(), "lib/ldb-samba/tests/match_rules_remote.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT']) > plantestsuite("samba4.ldap.index.python", "none", [python, os.path.join(srcdir(), "lib/ldb-samba/tests/index.py")]) > plantestsuite_loadlist("samba4.ldap.notification.python(ad_dc_ntvfs)", "ad_dc_ntvfs", [python, os.path.join(DSDB_PYTEST_DIR, "notification.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT']) > plantestsuite_loadlist("samba4.ldap.sites.python(ad_dc_default)", "ad_dc_default", [python, os.path.join(DSDB_PYTEST_DIR, "sites.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT']) >-- >2.25.1 > > >From a4a5cefcd2209ca7efadc979f8922e9a11457535 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Thu, 2 Mar 2023 17:24:15 +1300 >Subject: [PATCH 34/35] CVE-2023-0614 lib/ldb-samba Ensure ACLs are evaluated > on SAMBA_LDAP_MATCH_RULE_TRANSITIVE_EVAL / LDAP_MATCHING_RULE_IN_CHAIN > >Setting the LDB_HANDLE_FLAG_UNTRUSTED tells the acl_read module to operate on this request. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > lib/ldb-samba/ldb_matching_rules.c | 7 ++++++- > 1 file changed, 6 insertions(+), 1 deletion(-) > >diff --git a/lib/ldb-samba/ldb_matching_rules.c b/lib/ldb-samba/ldb_matching_rules.c >index b86594c1823..59d1385f4e3 100644 >--- a/lib/ldb-samba/ldb_matching_rules.c >+++ b/lib/ldb-samba/ldb_matching_rules.c >@@ -67,7 +67,12 @@ static int ldb_eval_transitive_filter_helper(TALLOC_CTX *mem_ctx, > * Note also that we don't have the original request > * here, so we can not apply controls or timeouts here. > */ >- ret = dsdb_search_dn(ldb, tmp_ctx, &res, to_visit->dn, attrs, 0); >+ ret = dsdb_search_dn(ldb, >+ tmp_ctx, >+ &res, >+ to_visit->dn, >+ attrs, >+ DSDB_MARK_REQ_UNTRUSTED); > if (ret != LDB_SUCCESS) { > talloc_free(tmp_ctx); > return ret; >-- >2.25.1 > > >From f1418ed39232e0f33afd314709c421969b49b70c Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Fri, 3 Mar 2023 17:52:13 +1300 >Subject: [PATCH 35/35] CVE-2023-0614 ldb: Release LDB 2.5.3 > >* CVE-2023-0614 Not-secret but access controlled LDAP attributes can be discovered (bug 15270) > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15270 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz> > >[abartlet@samba.org Adapted to LDB 2.5 series in Samba 4.16] >--- > lib/ldb/ABI/ldb-2.5.3.sigs | 301 ++++++++++++++++++++++++++++++ > lib/ldb/ABI/pyldb-util-2.5.3.sigs | 3 + > lib/ldb/wscript | 2 +- > 3 files changed, 305 insertions(+), 1 deletion(-) > create mode 100644 lib/ldb/ABI/ldb-2.5.3.sigs > create mode 100644 lib/ldb/ABI/pyldb-util-2.5.3.sigs > >diff --git a/lib/ldb/ABI/ldb-2.5.3.sigs b/lib/ldb/ABI/ldb-2.5.3.sigs >new file mode 100644 >index 00000000000..b4c5e20e8c7 >--- /dev/null >+++ b/lib/ldb/ABI/ldb-2.5.3.sigs >@@ -0,0 +1,301 @@ >+ldb_add: int (struct ldb_context *, const struct ldb_message *) >+ldb_any_comparison: int (struct ldb_context *, void *, ldb_attr_handler_t, const struct ldb_val *, const struct ldb_val *) >+ldb_asprintf_errstring: void (struct ldb_context *, const char *, ...) >+ldb_attr_casefold: char *(TALLOC_CTX *, const char *) >+ldb_attr_dn: int (const char *) >+ldb_attr_in_list: int (const char * const *, const char *) >+ldb_attr_list_copy: const char **(TALLOC_CTX *, const char * const *) >+ldb_attr_list_copy_add: const char **(TALLOC_CTX *, const char * const *, const char *) >+ldb_base64_decode: int (char *) >+ldb_base64_encode: char *(TALLOC_CTX *, const char *, int) >+ldb_binary_decode: struct ldb_val (TALLOC_CTX *, const char *) >+ldb_binary_encode: char *(TALLOC_CTX *, struct ldb_val) >+ldb_binary_encode_string: char *(TALLOC_CTX *, const char *) >+ldb_build_add_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) >+ldb_build_del_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) >+ldb_build_extended_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const char *, void *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) >+ldb_build_mod_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) >+ldb_build_rename_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, struct ldb_dn *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) >+ldb_build_search_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, enum ldb_scope, const char *, const char * const *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) >+ldb_build_search_req_ex: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, enum ldb_scope, struct ldb_parse_tree *, const char * const *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) >+ldb_casefold: char *(struct ldb_context *, TALLOC_CTX *, const char *, size_t) >+ldb_casefold_default: char *(void *, TALLOC_CTX *, const char *, size_t) >+ldb_check_critical_controls: int (struct ldb_control **) >+ldb_comparison_binary: int (struct ldb_context *, void *, const struct ldb_val *, const struct ldb_val *) >+ldb_comparison_fold: int (struct ldb_context *, void *, const struct ldb_val *, const struct ldb_val *) >+ldb_connect: int (struct ldb_context *, const char *, unsigned int, const char **) >+ldb_control_to_string: char *(TALLOC_CTX *, const struct ldb_control *) >+ldb_controls_except_specified: struct ldb_control **(struct ldb_control **, TALLOC_CTX *, struct ldb_control *) >+ldb_debug: void (struct ldb_context *, enum ldb_debug_level, const char *, ...) >+ldb_debug_add: void (struct ldb_context *, const char *, ...) >+ldb_debug_end: void (struct ldb_context *, enum ldb_debug_level) >+ldb_debug_set: void (struct ldb_context *, enum ldb_debug_level, const char *, ...) >+ldb_delete: int (struct ldb_context *, struct ldb_dn *) >+ldb_dn_add_base: bool (struct ldb_dn *, struct ldb_dn *) >+ldb_dn_add_base_fmt: bool (struct ldb_dn *, const char *, ...) >+ldb_dn_add_child: bool (struct ldb_dn *, struct ldb_dn *) >+ldb_dn_add_child_fmt: bool (struct ldb_dn *, const char *, ...) >+ldb_dn_add_child_val: bool (struct ldb_dn *, const char *, struct ldb_val) >+ldb_dn_alloc_casefold: char *(TALLOC_CTX *, struct ldb_dn *) >+ldb_dn_alloc_linearized: char *(TALLOC_CTX *, struct ldb_dn *) >+ldb_dn_canonical_ex_string: char *(TALLOC_CTX *, struct ldb_dn *) >+ldb_dn_canonical_string: char *(TALLOC_CTX *, struct ldb_dn *) >+ldb_dn_check_local: bool (struct ldb_module *, struct ldb_dn *) >+ldb_dn_check_special: bool (struct ldb_dn *, const char *) >+ldb_dn_compare: int (struct ldb_dn *, struct ldb_dn *) >+ldb_dn_compare_base: int (struct ldb_dn *, struct ldb_dn *) >+ldb_dn_copy: struct ldb_dn *(TALLOC_CTX *, struct ldb_dn *) >+ldb_dn_escape_value: char *(TALLOC_CTX *, struct ldb_val) >+ldb_dn_extended_add_syntax: int (struct ldb_context *, unsigned int, const struct ldb_dn_extended_syntax *) >+ldb_dn_extended_filter: void (struct ldb_dn *, const char * const *) >+ldb_dn_extended_syntax_by_name: const struct ldb_dn_extended_syntax *(struct ldb_context *, const char *) >+ldb_dn_from_ldb_val: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const struct ldb_val *) >+ldb_dn_get_casefold: const char *(struct ldb_dn *) >+ldb_dn_get_comp_num: int (struct ldb_dn *) >+ldb_dn_get_component_name: const char *(struct ldb_dn *, unsigned int) >+ldb_dn_get_component_val: const struct ldb_val *(struct ldb_dn *, unsigned int) >+ldb_dn_get_extended_comp_num: int (struct ldb_dn *) >+ldb_dn_get_extended_component: const struct ldb_val *(struct ldb_dn *, const char *) >+ldb_dn_get_extended_linearized: char *(TALLOC_CTX *, struct ldb_dn *, int) >+ldb_dn_get_ldb_context: struct ldb_context *(struct ldb_dn *) >+ldb_dn_get_linearized: const char *(struct ldb_dn *) >+ldb_dn_get_parent: struct ldb_dn *(TALLOC_CTX *, struct ldb_dn *) >+ldb_dn_get_rdn_name: const char *(struct ldb_dn *) >+ldb_dn_get_rdn_val: const struct ldb_val *(struct ldb_dn *) >+ldb_dn_has_extended: bool (struct ldb_dn *) >+ldb_dn_is_null: bool (struct ldb_dn *) >+ldb_dn_is_special: bool (struct ldb_dn *) >+ldb_dn_is_valid: bool (struct ldb_dn *) >+ldb_dn_map_local: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *) >+ldb_dn_map_rebase_remote: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *) >+ldb_dn_map_remote: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *) >+ldb_dn_minimise: bool (struct ldb_dn *) >+ldb_dn_new: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const char *) >+ldb_dn_new_fmt: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const char *, ...) >+ldb_dn_remove_base_components: bool (struct ldb_dn *, unsigned int) >+ldb_dn_remove_child_components: bool (struct ldb_dn *, unsigned int) >+ldb_dn_remove_extended_components: void (struct ldb_dn *) >+ldb_dn_replace_components: bool (struct ldb_dn *, struct ldb_dn *) >+ldb_dn_set_component: int (struct ldb_dn *, int, const char *, const struct ldb_val) >+ldb_dn_set_extended_component: int (struct ldb_dn *, const char *, const struct ldb_val *) >+ldb_dn_update_components: int (struct ldb_dn *, const struct ldb_dn *) >+ldb_dn_validate: bool (struct ldb_dn *) >+ldb_dump_results: void (struct ldb_context *, struct ldb_result *, FILE *) >+ldb_error_at: int (struct ldb_context *, int, const char *, const char *, int) >+ldb_errstring: const char *(struct ldb_context *) >+ldb_extended: int (struct ldb_context *, const char *, void *, struct ldb_result **) >+ldb_extended_default_callback: int (struct ldb_request *, struct ldb_reply *) >+ldb_filter_attrs: int (struct ldb_context *, const struct ldb_message *, const char * const *, struct ldb_message *) >+ldb_filter_attrs_in_place: int (struct ldb_message *, const char * const *) >+ldb_filter_from_tree: char *(TALLOC_CTX *, const struct ldb_parse_tree *) >+ldb_get_config_basedn: struct ldb_dn *(struct ldb_context *) >+ldb_get_create_perms: unsigned int (struct ldb_context *) >+ldb_get_default_basedn: struct ldb_dn *(struct ldb_context *) >+ldb_get_event_context: struct tevent_context *(struct ldb_context *) >+ldb_get_flags: unsigned int (struct ldb_context *) >+ldb_get_opaque: void *(struct ldb_context *, const char *) >+ldb_get_root_basedn: struct ldb_dn *(struct ldb_context *) >+ldb_get_schema_basedn: struct ldb_dn *(struct ldb_context *) >+ldb_global_init: int (void) >+ldb_handle_get_event_context: struct tevent_context *(struct ldb_handle *) >+ldb_handle_new: struct ldb_handle *(TALLOC_CTX *, struct ldb_context *) >+ldb_handle_use_global_event_context: void (struct ldb_handle *) >+ldb_handler_copy: int (struct ldb_context *, void *, const struct ldb_val *, struct ldb_val *) >+ldb_handler_fold: int (struct ldb_context *, void *, const struct ldb_val *, struct ldb_val *) >+ldb_init: struct ldb_context *(TALLOC_CTX *, struct tevent_context *) >+ldb_ldif_message_redacted_string: char *(struct ldb_context *, TALLOC_CTX *, enum ldb_changetype, const struct ldb_message *) >+ldb_ldif_message_string: char *(struct ldb_context *, TALLOC_CTX *, enum ldb_changetype, const struct ldb_message *) >+ldb_ldif_parse_modrdn: int (struct ldb_context *, const struct ldb_ldif *, TALLOC_CTX *, struct ldb_dn **, struct ldb_dn **, bool *, struct ldb_dn **, struct ldb_dn **) >+ldb_ldif_read: struct ldb_ldif *(struct ldb_context *, int (*)(void *), void *) >+ldb_ldif_read_file: struct ldb_ldif *(struct ldb_context *, FILE *) >+ldb_ldif_read_file_state: struct ldb_ldif *(struct ldb_context *, struct ldif_read_file_state *) >+ldb_ldif_read_free: void (struct ldb_context *, struct ldb_ldif *) >+ldb_ldif_read_string: struct ldb_ldif *(struct ldb_context *, const char **) >+ldb_ldif_write: int (struct ldb_context *, int (*)(void *, const char *, ...), void *, const struct ldb_ldif *) >+ldb_ldif_write_file: int (struct ldb_context *, FILE *, const struct ldb_ldif *) >+ldb_ldif_write_redacted_trace_string: char *(struct ldb_context *, TALLOC_CTX *, const struct ldb_ldif *) >+ldb_ldif_write_string: char *(struct ldb_context *, TALLOC_CTX *, const struct ldb_ldif *) >+ldb_load_modules: int (struct ldb_context *, const char **) >+ldb_map_add: int (struct ldb_module *, struct ldb_request *) >+ldb_map_delete: int (struct ldb_module *, struct ldb_request *) >+ldb_map_init: int (struct ldb_module *, const struct ldb_map_attribute *, const struct ldb_map_objectclass *, const char * const *, const char *, const char *) >+ldb_map_modify: int (struct ldb_module *, struct ldb_request *) >+ldb_map_rename: int (struct ldb_module *, struct ldb_request *) >+ldb_map_search: int (struct ldb_module *, struct ldb_request *) >+ldb_match_message: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, enum ldb_scope, bool *) >+ldb_match_msg: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, struct ldb_dn *, enum ldb_scope) >+ldb_match_msg_error: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, struct ldb_dn *, enum ldb_scope, bool *) >+ldb_match_msg_objectclass: int (const struct ldb_message *, const char *) >+ldb_match_scope: int (struct ldb_context *, struct ldb_dn *, struct ldb_dn *, enum ldb_scope) >+ldb_mod_register_control: int (struct ldb_module *, const char *) >+ldb_modify: int (struct ldb_context *, const struct ldb_message *) >+ldb_modify_default_callback: int (struct ldb_request *, struct ldb_reply *) >+ldb_module_call_chain: char *(struct ldb_request *, TALLOC_CTX *) >+ldb_module_connect_backend: int (struct ldb_context *, const char *, const char **, struct ldb_module **) >+ldb_module_done: int (struct ldb_request *, struct ldb_control **, struct ldb_extended *, int) >+ldb_module_flags: uint32_t (struct ldb_context *) >+ldb_module_get_ctx: struct ldb_context *(struct ldb_module *) >+ldb_module_get_name: const char *(struct ldb_module *) >+ldb_module_get_ops: const struct ldb_module_ops *(struct ldb_module *) >+ldb_module_get_private: void *(struct ldb_module *) >+ldb_module_init_chain: int (struct ldb_context *, struct ldb_module *) >+ldb_module_load_list: int (struct ldb_context *, const char **, struct ldb_module *, struct ldb_module **) >+ldb_module_new: struct ldb_module *(TALLOC_CTX *, struct ldb_context *, const char *, const struct ldb_module_ops *) >+ldb_module_next: struct ldb_module *(struct ldb_module *) >+ldb_module_popt_options: struct poptOption **(struct ldb_context *) >+ldb_module_send_entry: int (struct ldb_request *, struct ldb_message *, struct ldb_control **) >+ldb_module_send_referral: int (struct ldb_request *, char *) >+ldb_module_set_next: void (struct ldb_module *, struct ldb_module *) >+ldb_module_set_private: void (struct ldb_module *, void *) >+ldb_modules_hook: int (struct ldb_context *, enum ldb_module_hook_type) >+ldb_modules_list_from_string: const char **(struct ldb_context *, TALLOC_CTX *, const char *) >+ldb_modules_load: int (const char *, const char *) >+ldb_msg_add: int (struct ldb_message *, const struct ldb_message_element *, int) >+ldb_msg_add_distinguished_name: int (struct ldb_message *) >+ldb_msg_add_empty: int (struct ldb_message *, const char *, int, struct ldb_message_element **) >+ldb_msg_add_fmt: int (struct ldb_message *, const char *, const char *, ...) >+ldb_msg_add_linearized_dn: int (struct ldb_message *, const char *, struct ldb_dn *) >+ldb_msg_add_steal_string: int (struct ldb_message *, const char *, char *) >+ldb_msg_add_steal_value: int (struct ldb_message *, const char *, struct ldb_val *) >+ldb_msg_add_string: int (struct ldb_message *, const char *, const char *) >+ldb_msg_add_string_flags: int (struct ldb_message *, const char *, const char *, int) >+ldb_msg_add_value: int (struct ldb_message *, const char *, const struct ldb_val *, struct ldb_message_element **) >+ldb_msg_append_fmt: int (struct ldb_message *, int, const char *, const char *, ...) >+ldb_msg_append_linearized_dn: int (struct ldb_message *, const char *, struct ldb_dn *, int) >+ldb_msg_append_steal_string: int (struct ldb_message *, const char *, char *, int) >+ldb_msg_append_steal_value: int (struct ldb_message *, const char *, struct ldb_val *, int) >+ldb_msg_append_string: int (struct ldb_message *, const char *, const char *, int) >+ldb_msg_append_value: int (struct ldb_message *, const char *, const struct ldb_val *, int) >+ldb_msg_canonicalize: struct ldb_message *(struct ldb_context *, const struct ldb_message *) >+ldb_msg_check_string_attribute: int (const struct ldb_message *, const char *, const char *) >+ldb_msg_copy: struct ldb_message *(TALLOC_CTX *, const struct ldb_message *) >+ldb_msg_copy_attr: int (struct ldb_message *, const char *, const char *) >+ldb_msg_copy_shallow: struct ldb_message *(TALLOC_CTX *, const struct ldb_message *) >+ldb_msg_diff: struct ldb_message *(struct ldb_context *, struct ldb_message *, struct ldb_message *) >+ldb_msg_difference: int (struct ldb_context *, TALLOC_CTX *, struct ldb_message *, struct ldb_message *, struct ldb_message **) >+ldb_msg_element_add_value: int (TALLOC_CTX *, struct ldb_message_element *, const struct ldb_val *) >+ldb_msg_element_compare: int (struct ldb_message_element *, struct ldb_message_element *) >+ldb_msg_element_compare_name: int (struct ldb_message_element *, struct ldb_message_element *) >+ldb_msg_element_equal_ordered: bool (const struct ldb_message_element *, const struct ldb_message_element *) >+ldb_msg_element_is_inaccessible: bool (const struct ldb_message_element *) >+ldb_msg_element_mark_inaccessible: void (struct ldb_message_element *) >+ldb_msg_elements_take_ownership: int (struct ldb_message *) >+ldb_msg_find_attr_as_bool: int (const struct ldb_message *, const char *, int) >+ldb_msg_find_attr_as_dn: struct ldb_dn *(struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, const char *) >+ldb_msg_find_attr_as_double: double (const struct ldb_message *, const char *, double) >+ldb_msg_find_attr_as_int: int (const struct ldb_message *, const char *, int) >+ldb_msg_find_attr_as_int64: int64_t (const struct ldb_message *, const char *, int64_t) >+ldb_msg_find_attr_as_string: const char *(const struct ldb_message *, const char *, const char *) >+ldb_msg_find_attr_as_uint: unsigned int (const struct ldb_message *, const char *, unsigned int) >+ldb_msg_find_attr_as_uint64: uint64_t (const struct ldb_message *, const char *, uint64_t) >+ldb_msg_find_common_values: int (struct ldb_context *, TALLOC_CTX *, struct ldb_message_element *, struct ldb_message_element *, uint32_t) >+ldb_msg_find_duplicate_val: int (struct ldb_context *, TALLOC_CTX *, const struct ldb_message_element *, struct ldb_val **, uint32_t) >+ldb_msg_find_element: struct ldb_message_element *(const struct ldb_message *, const char *) >+ldb_msg_find_ldb_val: const struct ldb_val *(const struct ldb_message *, const char *) >+ldb_msg_find_val: struct ldb_val *(const struct ldb_message_element *, struct ldb_val *) >+ldb_msg_new: struct ldb_message *(TALLOC_CTX *) >+ldb_msg_normalize: int (struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_message **) >+ldb_msg_remove_attr: void (struct ldb_message *, const char *) >+ldb_msg_remove_element: void (struct ldb_message *, struct ldb_message_element *) >+ldb_msg_remove_inaccessible: void (struct ldb_message *) >+ldb_msg_rename_attr: int (struct ldb_message *, const char *, const char *) >+ldb_msg_sanity_check: int (struct ldb_context *, const struct ldb_message *) >+ldb_msg_shrink_to_fit: void (struct ldb_message *) >+ldb_msg_sort_elements: void (struct ldb_message *) >+ldb_next_del_trans: int (struct ldb_module *) >+ldb_next_end_trans: int (struct ldb_module *) >+ldb_next_init: int (struct ldb_module *) >+ldb_next_prepare_commit: int (struct ldb_module *) >+ldb_next_read_lock: int (struct ldb_module *) >+ldb_next_read_unlock: int (struct ldb_module *) >+ldb_next_remote_request: int (struct ldb_module *, struct ldb_request *) >+ldb_next_request: int (struct ldb_module *, struct ldb_request *) >+ldb_next_start_trans: int (struct ldb_module *) >+ldb_op_default_callback: int (struct ldb_request *, struct ldb_reply *) >+ldb_options_copy: const char **(TALLOC_CTX *, const char **) >+ldb_options_find: const char *(struct ldb_context *, const char **, const char *) >+ldb_options_get: const char **(struct ldb_context *) >+ldb_pack_data: int (struct ldb_context *, const struct ldb_message *, struct ldb_val *, uint32_t) >+ldb_parse_control_from_string: struct ldb_control *(struct ldb_context *, TALLOC_CTX *, const char *) >+ldb_parse_control_strings: struct ldb_control **(struct ldb_context *, TALLOC_CTX *, const char **) >+ldb_parse_tree: struct ldb_parse_tree *(TALLOC_CTX *, const char *) >+ldb_parse_tree_attr_replace: void (struct ldb_parse_tree *, const char *, const char *) >+ldb_parse_tree_copy_shallow: struct ldb_parse_tree *(TALLOC_CTX *, const struct ldb_parse_tree *) >+ldb_parse_tree_get_attr: const char *(const struct ldb_parse_tree *) >+ldb_parse_tree_walk: int (struct ldb_parse_tree *, int (*)(struct ldb_parse_tree *, void *), void *) >+ldb_qsort: void (void * const, size_t, size_t, void *, ldb_qsort_cmp_fn_t) >+ldb_register_backend: int (const char *, ldb_connect_fn, bool) >+ldb_register_extended_match_rule: int (struct ldb_context *, const struct ldb_extended_match_rule *) >+ldb_register_hook: int (ldb_hook_fn) >+ldb_register_module: int (const struct ldb_module_ops *) >+ldb_register_redact_callback: int (struct ldb_context *, ldb_redact_fn, struct ldb_module *) >+ldb_rename: int (struct ldb_context *, struct ldb_dn *, struct ldb_dn *) >+ldb_reply_add_control: int (struct ldb_reply *, const char *, bool, void *) >+ldb_reply_get_control: struct ldb_control *(struct ldb_reply *, const char *) >+ldb_req_get_custom_flags: uint32_t (struct ldb_request *) >+ldb_req_is_untrusted: bool (struct ldb_request *) >+ldb_req_location: const char *(struct ldb_request *) >+ldb_req_mark_trusted: void (struct ldb_request *) >+ldb_req_mark_untrusted: void (struct ldb_request *) >+ldb_req_set_custom_flags: void (struct ldb_request *, uint32_t) >+ldb_req_set_location: void (struct ldb_request *, const char *) >+ldb_request: int (struct ldb_context *, struct ldb_request *) >+ldb_request_add_control: int (struct ldb_request *, const char *, bool, void *) >+ldb_request_done: int (struct ldb_request *, int) >+ldb_request_get_control: struct ldb_control *(struct ldb_request *, const char *) >+ldb_request_get_status: int (struct ldb_request *) >+ldb_request_replace_control: int (struct ldb_request *, const char *, bool, void *) >+ldb_request_set_state: void (struct ldb_request *, int) >+ldb_reset_err_string: void (struct ldb_context *) >+ldb_save_controls: int (struct ldb_control *, struct ldb_request *, struct ldb_control ***) >+ldb_schema_attribute_add: int (struct ldb_context *, const char *, unsigned int, const char *) >+ldb_schema_attribute_add_with_syntax: int (struct ldb_context *, const char *, unsigned int, const struct ldb_schema_syntax *) >+ldb_schema_attribute_by_name: const struct ldb_schema_attribute *(struct ldb_context *, const char *) >+ldb_schema_attribute_fill_with_syntax: int (struct ldb_context *, TALLOC_CTX *, const char *, unsigned int, const struct ldb_schema_syntax *, struct ldb_schema_attribute *) >+ldb_schema_attribute_remove: void (struct ldb_context *, const char *) >+ldb_schema_attribute_remove_flagged: void (struct ldb_context *, unsigned int) >+ldb_schema_attribute_set_override_handler: void (struct ldb_context *, ldb_attribute_handler_override_fn_t, void *) >+ldb_schema_set_override_GUID_index: void (struct ldb_context *, const char *, const char *) >+ldb_schema_set_override_indexlist: void (struct ldb_context *, bool) >+ldb_search: int (struct ldb_context *, TALLOC_CTX *, struct ldb_result **, struct ldb_dn *, enum ldb_scope, const char * const *, const char *, ...) >+ldb_search_default_callback: int (struct ldb_request *, struct ldb_reply *) >+ldb_sequence_number: int (struct ldb_context *, enum ldb_sequence_type, uint64_t *) >+ldb_set_create_perms: void (struct ldb_context *, unsigned int) >+ldb_set_debug: int (struct ldb_context *, void (*)(void *, enum ldb_debug_level, const char *, va_list), void *) >+ldb_set_debug_stderr: int (struct ldb_context *) >+ldb_set_default_dns: void (struct ldb_context *) >+ldb_set_errstring: void (struct ldb_context *, const char *) >+ldb_set_event_context: void (struct ldb_context *, struct tevent_context *) >+ldb_set_flags: void (struct ldb_context *, unsigned int) >+ldb_set_modules_dir: void (struct ldb_context *, const char *) >+ldb_set_opaque: int (struct ldb_context *, const char *, void *) >+ldb_set_require_private_event_context: void (struct ldb_context *) >+ldb_set_timeout: int (struct ldb_context *, struct ldb_request *, int) >+ldb_set_timeout_from_prev_req: int (struct ldb_context *, struct ldb_request *, struct ldb_request *) >+ldb_set_utf8_default: void (struct ldb_context *) >+ldb_set_utf8_fns: void (struct ldb_context *, void *, char *(*)(void *, void *, const char *, size_t)) >+ldb_setup_wellknown_attributes: int (struct ldb_context *) >+ldb_should_b64_encode: int (struct ldb_context *, const struct ldb_val *) >+ldb_standard_syntax_by_name: const struct ldb_schema_syntax *(struct ldb_context *, const char *) >+ldb_strerror: const char *(int) >+ldb_string_to_time: time_t (const char *) >+ldb_string_utc_to_time: time_t (const char *) >+ldb_timestring: char *(TALLOC_CTX *, time_t) >+ldb_timestring_utc: char *(TALLOC_CTX *, time_t) >+ldb_transaction_cancel: int (struct ldb_context *) >+ldb_transaction_cancel_noerr: int (struct ldb_context *) >+ldb_transaction_commit: int (struct ldb_context *) >+ldb_transaction_prepare_commit: int (struct ldb_context *) >+ldb_transaction_start: int (struct ldb_context *) >+ldb_unpack_data: int (struct ldb_context *, const struct ldb_val *, struct ldb_message *) >+ldb_unpack_data_flags: int (struct ldb_context *, const struct ldb_val *, struct ldb_message *, unsigned int) >+ldb_unpack_get_format: int (const struct ldb_val *, uint32_t *) >+ldb_val_dup: struct ldb_val (TALLOC_CTX *, const struct ldb_val *) >+ldb_val_equal_exact: int (const struct ldb_val *, const struct ldb_val *) >+ldb_val_map_local: struct ldb_val (struct ldb_module *, void *, const struct ldb_map_attribute *, const struct ldb_val *) >+ldb_val_map_remote: struct ldb_val (struct ldb_module *, void *, const struct ldb_map_attribute *, const struct ldb_val *) >+ldb_val_string_cmp: int (const struct ldb_val *, const char *) >+ldb_val_to_time: int (const struct ldb_val *, time_t *) >+ldb_valid_attr_name: int (const char *) >+ldb_vdebug: void (struct ldb_context *, enum ldb_debug_level, const char *, va_list) >+ldb_wait: int (struct ldb_handle *, enum ldb_wait_type) >diff --git a/lib/ldb/ABI/pyldb-util-2.5.3.sigs b/lib/ldb/ABI/pyldb-util-2.5.3.sigs >new file mode 100644 >index 00000000000..164a806b2ff >--- /dev/null >+++ b/lib/ldb/ABI/pyldb-util-2.5.3.sigs >@@ -0,0 +1,3 @@ >+pyldb_Dn_FromDn: PyObject *(struct ldb_dn *) >+pyldb_Object_AsDn: bool (TALLOC_CTX *, PyObject *, struct ldb_context *, struct ldb_dn **) >+pyldb_check_type: bool (PyObject *, const char *) >diff --git a/lib/ldb/wscript b/lib/ldb/wscript >index 9f687061b8c..86b19552afe 100644 >--- a/lib/ldb/wscript >+++ b/lib/ldb/wscript >@@ -2,7 +2,7 @@ > > APPNAME = 'ldb' > # For Samba 4.16.x >-VERSION = '2.5.2' >+VERSION = '2.5.3' > > import sys, os > >-- >2.25.1 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Flags:
jsutton
:
review+
abartlet
:
review+
jsutton
:
ci-passed+
Actions:
View
Attachments on
bug 15270
:
17760
|
17767
|
17768
|
17769
|
17770
|
17771
|
17773
|
17778
|
17789
|
17791
|
17792
|
17793
|
17799
|
17800
|
17804
|
17805
|
17806
|
17807
|
17808
|
17819
|
17820
|
17821
|
17822
| 17834 |
17845