diff --git a/backport-0001-CVE-2023-0614.patch b/backport-0001-CVE-2023-0614.patch new file mode 100644 index 0000000000000000000000000000000000000000..1fd382b3b3de70e8c977c6ac9028a702006bccc2 --- /dev/null +++ b/backport-0001-CVE-2023-0614.patch @@ -0,0 +1,174 @@ +From 197d1f09158ee0575cd92c461ed12bdbf4efc074 Mon Sep 17 00:00:00 2001 +From: Andrew Bartlett +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 +Reviewed-by: Joseph Sutton +(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] + +Conflict: NA +Reference: https://attachments.samba.org/attachment.cgi?id=17821 +--- + common/ldb_match.c | 60 +++++++++++++++++++++++++++++++------- + 1 file changed, 50 insertions(+), 10 deletions(-) + +diff --git a/common/ldb_match.c b/common/ldb_match.c +index 2f4d41f3441..51376871b4c 100644 +--- a/common/ldb_match.c ++++ b/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 diff --git a/backport-0002-CVE-2023-0614.patch b/backport-0002-CVE-2023-0614.patch new file mode 100644 index 0000000000000000000000000000000000000000..8234477cee304cdd1f42311a5d3f59215d35e8a0 --- /dev/null +++ b/backport-0002-CVE-2023-0614.patch @@ -0,0 +1,72 @@ +From b01d3ae3261264236504475a26c54ab45dd2175f Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 27 Jan 2023 08:28:36 +1300 +Subject: [PATCH 05/34] 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 +Reviewed-by: Andrew Bartlett + +Conflict: NA +Reference: https://attachments.samba.org/attachment.cgi?id=17821 +--- + common/ldb_msg.c | 26 ++++++++++++++++++++++++++ + include/ldb_module.h | 4 ++++ + 2 files changed, 30 insertions(+) + +diff --git a/common/ldb_msg.c b/common/ldb_msg.c +index 9cd7998e21c..cbc7e32b2ba 100644 +--- a/common/ldb_msg.c ++++ b/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/include/ldb_module.h b/include/ldb_module.h +index 4c7c85a17f0..8481fd3991a 100644 +--- a/include/ldb_module.h ++++ b/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 diff --git a/backport-0003-CVE-2023-0614.patch b/backport-0003-CVE-2023-0614.patch new file mode 100644 index 0000000000000000000000000000000000000000..47be7eea02910d17fbf0f787acc634a30d78573c --- /dev/null +++ b/backport-0003-CVE-2023-0614.patch @@ -0,0 +1,409 @@ +From e7445d18badee6c3b1bbee48c689eb2629c31681 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Wed, 15 Feb 2023 12:34:51 +1300 +Subject: [PATCH 07/34] 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 +Reviewed-by: Andrew Bartlett + +Conflict: NA +Reference: https://attachments.samba.org/attachment.cgi?id=17821 +--- + tests/ldb_filter_attrs_test.c | 171 +++++++++++++------------- + 1 file changed, 86 insertions(+), 85 deletions(-) + +diff --git a/tests/ldb_filter_attrs_test.c b/tests/ldb_filter_attrs_test.c +index 7d555e0da2e..442d9c77ed2 100644 +--- a/tests/ldb_filter_attrs_test.c ++++ b/tests/ldb_filter_attrs_test.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -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 diff --git a/backport-0004-CVE-2023-0614.patch b/backport-0004-CVE-2023-0614.patch new file mode 100644 index 0000000000000000000000000000000000000000..8f32e016e5a2574435a22294fb9573ed0e2c0d9b --- /dev/null +++ b/backport-0004-CVE-2023-0614.patch @@ -0,0 +1,48 @@ +From 936bfcb6ef804d2224072f3770ca09fe2596ee1f Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Wed, 15 Feb 2023 14:08:57 +1300 +Subject: [PATCH 08/34] 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 +Reviewed-by: Andrew Bartlett + +Conflict: NA +Reference: https://attachments.samba.org/attachment.cgi?id=17821 +--- + wscript | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/wscript b/wscript +index 60bb7cf48b3..c862229822d 100644 +--- a/wscript ++++ b/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 diff --git a/backport-0005-CVE-2023-0614.patch b/backport-0005-CVE-2023-0614.patch new file mode 100644 index 0000000000000000000000000000000000000000..255b77abedcb3ffb7c034310a02b0ccb7beeea24 --- /dev/null +++ b/backport-0005-CVE-2023-0614.patch @@ -0,0 +1,104 @@ +From 83217ce77381f8faa3cde948e15a36db234d3033 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:23:42 +1300 +Subject: [PATCH 09/34] 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 +Reviewed-by: Andrew Bartlett + +Conflict: NA +Reference: https://attachments.samba.org/attachment.cgi?id=17821 +--- + common/ldb_pack.c | 41 ++++++++++++++++++++++++++++++++++++ + include/ldb_module.h | 4 ++++ + 2 files changed, 45 insertions(+) + +diff --git a/common/ldb_pack.c b/common/ldb_pack.c +index e7dd364008a..028d96a619a 100644 +--- a/common/ldb_pack.c ++++ b/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/include/ldb_module.h b/include/ldb_module.h +index 8481fd3991a..8c7f33496fb 100644 +--- a/include/ldb_module.h ++++ b/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 diff --git a/backport-0006-CVE-2023-0614.patch b/backport-0006-CVE-2023-0614.patch new file mode 100644 index 0000000000000000000000000000000000000000..141bacc1b8fc2ad705a49b75cd8504080713ae07 --- /dev/null +++ b/backport-0006-CVE-2023-0614.patch @@ -0,0 +1,62 @@ +From a9b625bc8ab00b83b55bcd21ba0df48e73e4df29 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:26:04 +1300 +Subject: [PATCH 10/34] 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 +Reviewed-by: Andrew Bartlett + +[abartlet@samba.org Adapted to conflict from lack of new +ldb_ascii_toupper() in ldb_private.h] + +Conflict: NA +Reference: https://attachments.samba.org/attachment.cgi?id=17821 +--- + common/ldb_msg.c | 16 ++++++++++++++++ + include/ldb_private.h | 3 +++ + 2 files changed, 19 insertions(+) + +diff --git a/common/ldb_msg.c b/common/ldb_msg.c +index cbc7e32b2ba..2ea2cce2e83 100644 +--- a/common/ldb_msg.c ++++ b/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/include/ldb_private.h b/include/ldb_private.h +index 4deb24691ca..338e71def6d 100644 +--- a/include/ldb_private.h ++++ b/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 diff --git a/backport-0007-CVE-2023-0614.patch b/backport-0007-CVE-2023-0614.patch new file mode 100644 index 0000000000000000000000000000000000000000..ed4a854431980c383e382b54bf4e111b26b8f1ef --- /dev/null +++ b/backport-0007-CVE-2023-0614.patch @@ -0,0 +1,68 @@ +From 8b7374780e3e7b67e51a1b54a09bf48d89fa9f26 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:27:38 +1300 +Subject: [PATCH 11/34] 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 +Reviewed-by: Andrew Bartlett + +[abartlet@samba.org Adapted to conflict from lack of new +ldb_ascii_toupper() in ldb_private.h] + +Conflict: NA +Reference: https://attachments.samba.org/attachment.cgi?id=17821 +--- + common/ldb_pack.c | 6 +++--- + include/ldb_private.h | 5 +++++ + 2 files changed, 8 insertions(+), 3 deletions(-) + +diff --git a/common/ldb_pack.c b/common/ldb_pack.c +index 028d96a619a..b0b0d64a5ba 100644 +--- a/common/ldb_pack.c ++++ b/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/include/ldb_private.h b/include/ldb_private.h +index 338e71def6d..ca43817d07a 100644 +--- a/include/ldb_private.h ++++ b/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 diff --git a/backport-0008-CVE-2023-0614.patch b/backport-0008-CVE-2023-0614.patch new file mode 100644 index 0000000000000000000000000000000000000000..cc7294c2fee65b3031b6b4f84585a33a33227014 --- /dev/null +++ b/backport-0008-CVE-2023-0614.patch @@ -0,0 +1,1224 @@ +From 4f8b4ce403ff68ca26d33d7272276052829c96f7 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:29:03 +1300 +Subject: [PATCH 12/34] 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 +Reviewed-by: Andrew Bartlett + +Conflict: NA +Reference: https://attachments.samba.org/attachment.cgi?id=17821 +--- + common/ldb_pack.c | 143 +++ + include/ldb_module.h | 10 + + .../tests/ldb_filter_attrs_in_place_test.c | 989 ++++++++++++++++++ + wscript | 6 + + 4 files changed, 1148 insertions(+) + create mode 100644 tests/ldb_filter_attrs_in_place_test.c + +diff --git a/common/ldb_pack.c b/common/ldb_pack.c +index b0b0d64a5ba..f19ac73fa5e 100644 +--- a/common/ldb_pack.c ++++ b/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;jnum_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/include/ldb_module.h b/include/ldb_module.h +index 8c7f33496fb..105093cf38c 100644 +--- a/include/ldb_module.h ++++ b/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/tests/ldb_filter_attrs_in_place_test.c b/tests/ldb_filter_attrs_in_place_test.c +new file mode 100644 +index 00000000000..bef961f8f9c +--- /dev/null ++++ b/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 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 . ++ * ++ */ ++ ++/* ++ * from cmocka.c: ++ * These headers or their equivalents should be included prior to ++ * including ++ * this header file. ++ * ++ * #include ++ * #include ++ * #include ++ * ++ * This allows test applications to use custom definitions of C standard ++ * library functions and types. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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/wscript b/wscript +index c862229822d..7e02309c1d5 100644 +--- a/wscript ++++ b/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 diff --git a/backport-0009-CVE-2023-0614.patch b/backport-0009-CVE-2023-0614.patch new file mode 100644 index 0000000000000000000000000000000000000000..89a8aeba9855946d3a2cae20e43cc0f03974ee00 --- /dev/null +++ b/backport-0009-CVE-2023-0614.patch @@ -0,0 +1,1275 @@ +From fd0f06e43f03bc8b8c5b4a978a119e4401c02160 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:30:19 +1300 +Subject: [PATCH 13/34] 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 +Reviewed-by: Andrew Bartlett + +Conflict: NA +Reference: https://attachments.samba.org/attachment.cgi?id=17821 +--- + common/ldb_pack.c | 129 +--- + include/ldb_module.h | 11 +- + .../tests/ldb_filter_attrs_in_place_test.c | 609 ++++++++---------- + 3 files changed, 307 insertions(+), 442 deletions(-) + +diff --git a/common/ldb_pack.c b/common/ldb_pack.c +index f19ac73fa5e..28b9a8dfe07 100644 +--- a/common/ldb_pack.c ++++ b/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;jnum_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/include/ldb_module.h b/include/ldb_module.h +index 105093cf38c..4ae381ba5be 100644 +--- a/include/ldb_module.h ++++ b/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/tests/ldb_filter_attrs_in_place_test.c b/tests/ldb_filter_attrs_in_place_test.c +index bef961f8f9c..da333c73c99 100644 +--- a/tests/ldb_filter_attrs_in_place_test.c ++++ b/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 diff --git a/backport-0010-CVE-2023-0614.patch b/backport-0010-CVE-2023-0614.patch new file mode 100644 index 0000000000000000000000000000000000000000..d75d15ec942ae49369372a385e8ab0f41b4fd057 --- /dev/null +++ b/backport-0010-CVE-2023-0614.patch @@ -0,0 +1,257 @@ +From 8e84201f508b2893b6ec9383c656cccb08b148ef Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Mon, 27 Feb 2023 10:31:52 +1300 +Subject: [PATCH 14/34] 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 +Reviewed-by: Andrew Bartlett + +Conflict: NA +Reference: https://attachments.samba.org/attachment.cgi?id=17821 +--- + ldb_key_value/ldb_kv.h | 6 +- + ldb_key_value/ldb_kv_index.c | 27 +++++---- + ldb_key_value/ldb_kv_search.c | 86 ++++++++++++++------------- + 4 files changed, 66 insertions(+), 65 deletions(-) + +diff --git a/ldb_key_value/ldb_kv.h b/ldb_key_value/ldb_kv.h +index ac474b04b4c..7d5a40e76e9 100644 +--- a/ldb_key_value/ldb_kv.h ++++ b/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/ldb_key_value/ldb_kv_index.c b/ldb_key_value/ldb_kv_index.c +index d70e5f619ef..203266ea8c9 100644 +--- a/ldb_key_value/ldb_kv_index.c ++++ b/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/ldb_key_value/ldb_kv_search.c b/ldb_key_value/ldb_kv_search.c +index 46031b99c16..f3333510eab 100644 +--- a/ldb_key_value/ldb_kv_search.c ++++ b/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 +-- +2.25.1 diff --git a/backport-0011-CVE-2023-0614.patch b/backport-0011-CVE-2023-0614.patch new file mode 100644 index 0000000000000000000000000000000000000000..15497bedcf43f0e218d5dd8a8b34350b2a70dc74 --- /dev/null +++ b/backport-0011-CVE-2023-0614.patch @@ -0,0 +1,66 @@ +From b23fb132e63a6d3d791469614593c43906686ac9 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:31:54 +1300 +Subject: [PATCH 20/34] 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 +Reviewed-by: Andrew Bartlett + +Conflict: NA +Reference: https://attachments.samba.org/attachment.cgi?id=17821 +--- + common/ldb_parse.c | 25 +++++++++++++++++++++++++ + include/ldb_module.h | 3 +++ + 2 files changed, 28 insertions(+) + +diff --git a/common/ldb_parse.c b/common/ldb_parse.c +index f0045ad2093..2d102ff750e 100644 +--- a/common/ldb_parse.c ++++ b/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/include/ldb_module.h b/include/ldb_module.h +index 4ae381ba5be..bd369ed9512 100644 +--- a/include/ldb_module.h ++++ b/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 diff --git a/backport-0012-CVE-2023-0614.patch b/backport-0012-CVE-2023-0614.patch new file mode 100644 index 0000000000000000000000000000000000000000..e7ecd5fb8f67e2b4f57ee6332937091b64111be2 --- /dev/null +++ b/backport-0012-CVE-2023-0614.patch @@ -0,0 +1,231 @@ +From f87af853e2db99269acea572dcc109bc6f797aa9 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:34:29 +1300 +Subject: [PATCH 24/34] 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 + +Signed-off-by: Joseph Sutton +Signed-off-by: Andrew Bartlett +Reviewed-by: Andrew Bartlett + +[abartlet@samba.org adapted due to Samba 4.17 and lower +not having the patches for CVE-2020-25720] + +Conflict: NA +Reference: https://attachments.samba.org/attachment.cgi?id=17821 +--- + common/ldb_match.c | 37 + + include/ldb_module.h | 11 + + include/ldb_private.h | 5 + + ldb_key_value/ldb_kv_index.c | 8 + + ldb_key_value/ldb_kv_search.c | 15 + + 12 files changed, 672 insertions(+), 455 deletions(-) + delete mode 100644 selftest/knownfail.d/confidential-attr-timing + +diff --git a/common/ldb_match.c b/common/ldb_match.c +index 2f4d41f3441..17314f1b9ca 100644 +--- a/common/ldb_match.c ++++ b/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;inum_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/include/ldb_module.h b/include/ldb_module.h +index bd369ed9512..0f14b1ad346 100644 +--- a/include/ldb_module.h ++++ b/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/include/ldb_private.h b/include/ldb_private.h +index ca43817d07a..b0a42f6421c 100644 +--- a/include/ldb_private.h ++++ b/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/ldb_key_value/ldb_kv_index.c b/ldb_key_value/ldb_kv_index.c +index 203266ea8c9..163052fecf7 100644 +--- a/ldb_key_value/ldb_kv_index.c ++++ b/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/ldb_key_value/ldb_kv_search.c b/ldb_key_value/ldb_kv_search.c +index f3333510eab..d187ba223e1 100644 +--- a/ldb_key_value/ldb_kv_search.c ++++ b/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/backport-0013-CVE-2023-0614.patch b/backport-0013-CVE-2023-0614.patch new file mode 100644 index 0000000000000000000000000000000000000000..be342b53ea532786521d5e16002cabaef40a1f9f --- /dev/null +++ b/backport-0013-CVE-2023-0614.patch @@ -0,0 +1,130 @@ +From 94efa3fc3053a623a7a5c3a4a6428356bc334152 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Tue, 14 Feb 2023 13:17:24 +1300 +Subject: [PATCH 27/34] 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 +Reviewed-by: Andrew Bartlett + +Conflict: NA +Reference: https://attachments.samba.org/attachment.cgi?id=17821 +--- + common/ldb_match.c | 56 +++++++++++++++++------------- + 2 files changed, 31 insertions(+), 30 deletions(-) + +diff --git a/common/ldb_match.c b/common/ldb_match.c +index 17314f1b9ca..645cb695ef1 100644 +--- a/common/ldb_match.c ++++ b/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;inum_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;iu.list.num_elements;i++) { +-- +2.25.1 diff --git a/backport-0014-CVE-2023-0614.patch b/backport-0014-CVE-2023-0614.patch new file mode 100644 index 0000000000000000000000000000000000000000..a23886d947d2414c27f348aa2bd430f73e791b48 --- /dev/null +++ b/backport-0014-CVE-2023-0614.patch @@ -0,0 +1,155 @@ +From a0c888bd0ed2ec5b4d84f9df241bebd5d428818c Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:35:55 +1300 +Subject: [PATCH 28/34] 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 +Reviewed-by: Andrew Bartlett + +Conflict: NA +Reference: https://attachments.samba.org/attachment.cgi?id=17821 +--- + common/ldb_match.c | 8 +++--- + include/ldb_private.h | 8 ++++++ + ldb_key_value/ldb_kv_index.c | 40 +++++++++++++++------------ + ldb_key_value/ldb_kv_search.c | 14 ++++++++-- + 4 files changed, 47 insertions(+), 23 deletions(-) + +diff --git a/common/ldb_match.c b/common/ldb_match.c +index 645cb695ef1..8e27ecbe723 100644 +--- a/common/ldb_match.c ++++ b/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/include/ldb_private.h b/include/ldb_private.h +index b0a42f6421c..5e29de34f79 100644 +--- a/include/ldb_private.h ++++ b/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/ldb_key_value/ldb_kv_index.c b/ldb_key_value/ldb_kv_index.c +index 163052fecf7..aac0913f431 100644 +--- a/ldb_key_value/ldb_kv_index.c ++++ b/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/ldb_key_value/ldb_kv_search.c b/ldb_key_value/ldb_kv_search.c +index d187ba223e1..27f68caef01 100644 +--- a/ldb_key_value/ldb_kv_search.c ++++ b/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 diff --git a/libldb.spec b/libldb.spec index 4022df92a8d0390e4dddff4413f70f121f5a0520..4bbb8472baabcad1fadd31c704d1e11c92b79b40 100644 --- a/libldb.spec +++ b/libldb.spec @@ -6,7 +6,7 @@ Name: libldb Version: 2.6.1 -Release: 1 +Release: 2 Summary: A schema-less, ldap like, API and database Requires: libtalloc%{?_isa} >= %{talloc_version} Requires: libtdb%{?_isa} >= %{tdb_version} @@ -17,6 +17,20 @@ Source0: http://samba.org/ftp/ldb/ldb-%{version}.tar.gz Source1: http://samba.org/ftp/ldb/ldb-%{version}.tar.asc Patch0: backport-Skip-ldb_lmdb_free_list_test-on-ppc64el-ppc64-and-sp.patch +patch0001: backport-0001-CVE-2023-0614.patch +patch0002: backport-0002-CVE-2023-0614.patch +patch0003: backport-0003-CVE-2023-0614.patch +patch0004: backport-0004-CVE-2023-0614.patch +patch0005: backport-0005-CVE-2023-0614.patch +patch0006: backport-0006-CVE-2023-0614.patch +patch0007: backport-0007-CVE-2023-0614.patch +patch0008: backport-0008-CVE-2023-0614.patch +patch0009: backport-0009-CVE-2023-0614.patch +patch0010: backport-0010-CVE-2023-0614.patch +patch0011: backport-0011-CVE-2023-0614.patch +patch0012: backport-0012-CVE-2023-0614.patch +patch0013: backport-0013-CVE-2023-0614.patch +patch0014: backport-0014-CVE-2023-0614.patch BuildRequires: gcc libtalloc-devel >= %{talloc_version} libtdb-devel >= %{tdb_version} BuildRequires: libtevent-devel >= %{tevent_version} lmdb-devel >= 0.9.16 popt-devel @@ -170,6 +184,12 @@ echo "%{_libdir}/ldb" > %{buildroot}/etc/ld.so.conf.d/%{name}-%{_arch}.conf %{_mandir}/man1/ldbsearch.1.* %changelog +* Sat Apr 01 2023 xinghe - 2.6.1-2 +- Type:CVE +- ID:CVE-2023-0614 +- SUG:NA +- DESC:fix CVE-2023-0614 + * Fri Nov 04 2022 yanglu - 2.6.1-1 - Type:requirement - ID:NA