diff -Nru ldb-2.4.4/debian/changelog ldb-2.4.4/debian/changelog --- ldb-2.4.4/debian/changelog 2023-03-10 11:55:45.000000000 +0000 +++ ldb-2.4.4/debian/changelog 2023-03-30 12:16:21.000000000 +0000 @@ -1,3 +1,13 @@ +ldb (2:2.4.4-0ubuntu0.22.04.2) jammy-security; urgency=medium + + * SECURITY UPDATE: Access controlled AD LDAP attributes can be discovered + - debian/patches/CVE-2023-0614-*.patch: upstream patches to fix the + issue. + - debian/libldb2.symbols: added new symbols. + - CVE-2023-0614 + + -- Marc Deslauriers Thu, 30 Mar 2023 08:16:21 -0400 + ldb (2:2.4.4-0ubuntu0.22.04.1) jammy-security; urgency=medium * No-change rebuild to solve versioning issue preventing upgrades from diff -Nru ldb-2.4.4/debian/libldb2.symbols ldb-2.4.4/debian/libldb2.symbols --- ldb-2.4.4/debian/libldb2.symbols 2022-07-27 11:27:00.000000000 +0000 +++ ldb-2.4.4/debian/libldb2.symbols 2023-03-30 12:16:21.000000000 +0000 @@ -166,6 +166,7 @@ ldb_extended@LDB_0.9.10 0.9.21 ldb_extended_default_callback@LDB_0.9.10 0.9.21 ldb_filter_attrs@LDB_2.0.1 2:2.0.1 + ldb_filter_attrs_in_place@LDB_2.4.4 2:2.4.4-0ubuntu0.22.04.2 ldb_filter_from_tree@LDB_0.9.10 0.9.21 ldb_get_config_basedn@LDB_0.9.10 0.9.21 ldb_get_create_perms@LDB_0.9.10 0.9.21 @@ -205,6 +206,7 @@ ldb_match_msg@LDB_0.9.10 0.9.21 ldb_match_msg_error@LDB_0.9.15 0.9.21 ldb_match_msg_objectclass@LDB_0.9.10 0.9.21 + ldb_match_scope@LDB_2.4.4 2:2.4.4-0ubuntu0.22.04.2 ldb_mod_register_control@LDB_0.9.10 0.9.21 ldb_modify@LDB_0.9.10 0.9.21 ldb_modify_default_callback@LDB_0.9.12 0.9.21 @@ -229,6 +231,7 @@ ldb_modules_list_from_string@LDB_0.9.10 0.9.21 ldb_modules_load@LDB_0.9.18 0.9.21 ldb_msg_add@LDB_0.9.10 0.9.21 + ldb_msg_add_distinguished_name@LDB_2.4.4 2:2.4.4-0ubuntu0.22.04.2 ldb_msg_add_empty@LDB_0.9.10 0.9.21 ldb_msg_add_fmt@LDB_0.9.10 0.9.21 ldb_msg_add_linearized_dn@LDB_0.9.10 0.9.21 @@ -254,6 +257,9 @@ ldb_msg_element_compare@LDB_0.9.10 0.9.21 ldb_msg_element_compare_name@LDB_0.9.10 0.9.21 ldb_msg_element_equal_ordered@LDB_1.1.6 1:1.1.6 + ldb_msg_element_is_inaccessible@LDB_2.4.4 2:2.4.4-0ubuntu0.22.04.2 + ldb_msg_element_mark_inaccessible@LDB_2.4.4 2:2.4.4-0ubuntu0.22.04.2 + ldb_msg_elements_take_ownership@LDB_2.4.4 2:2.4.4-0ubuntu0.22.04.2 ldb_msg_find_attr_as_bool@LDB_0.9.10 0.9.21 ldb_msg_find_attr_as_dn@LDB_0.9.10 0.9.21 ldb_msg_find_attr_as_double@LDB_0.9.10 0.9.21 @@ -271,8 +277,10 @@ ldb_msg_normalize@LDB_0.9.15 0.9.21 ldb_msg_remove_attr@LDB_0.9.10 0.9.21 ldb_msg_remove_element@LDB_0.9.10 0.9.21 + ldb_msg_remove_inaccessible@LDB_2.4.4 2:2.4.4-0ubuntu0.22.04.2 ldb_msg_rename_attr@LDB_0.9.10 0.9.21 ldb_msg_sanity_check@LDB_0.9.10 0.9.21 + ldb_msg_shrink_to_fit@LDB_2.4.4 2:2.4.4-0ubuntu0.22.04.2 ldb_msg_sort_elements@LDB_0.9.10 0.9.21 ldb_next_del_trans@LDB_0.9.10 0.9.21 ldb_next_end_trans@LDB_0.9.10 0.9.21 @@ -293,12 +301,14 @@ ldb_parse_tree@LDB_0.9.10 0.9.21 ldb_parse_tree_attr_replace@LDB_0.9.10 0.9.21 ldb_parse_tree_copy_shallow@LDB_0.9.10 0.9.21 + ldb_parse_tree_get_attr@LDB_2.4.4 2:2.4.4-0ubuntu0.22.04.2 ldb_parse_tree_walk@LDB_1.1.2 1.1.2~ ldb_qsort@LDB_0.9.10 0.9.21 ldb_register_backend@LDB_0.9.10 0.9.21 ldb_register_extended_match_rule@LDB_1.1.19 1:1.1.20 ldb_register_hook@LDB_0.9.18 0.9.21 ldb_register_module@LDB_0.9.10 0.9.21 + ldb_register_redact_callback@LDB_2.4.4 2:2.4.4-0ubuntu0.22.04.2 ldb_rename@LDB_0.9.10 0.9.21 ldb_reply_add_control@LDB_0.9.10 0.9.21 ldb_reply_get_control@LDB_0.9.10 0.9.21 diff -Nru ldb-2.4.4/debian/patches/CVE-2023-0614-01.patch ldb-2.4.4/debian/patches/CVE-2023-0614-01.patch --- ldb-2.4.4/debian/patches/CVE-2023-0614-01.patch 1970-01-01 00:00:00.000000000 +0000 +++ ldb-2.4.4/debian/patches/CVE-2023-0614-01.patch 2023-03-30 12:02:25.000000000 +0000 @@ -0,0 +1,172 @@ +From ae3d2737949d9702c5526490c2155740a96a9adb Mon Sep 17 00:00:00 2001 +From: Andrew Bartlett +Date: Mon, 13 Mar 2023 14:25:56 +1300 +Subject: [PATCH] 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] +--- + lib/ldb/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.34.1 + diff -Nru ldb-2.4.4/debian/patches/CVE-2023-0614-07.patch ldb-2.4.4/debian/patches/CVE-2023-0614-07.patch --- ldb-2.4.4/debian/patches/CVE-2023-0614-07.patch 1970-01-01 00:00:00.000000000 +0000 +++ ldb-2.4.4/debian/patches/CVE-2023-0614-07.patch 2023-03-30 12:03:17.000000000 +0000 @@ -0,0 +1,70 @@ +From 0f8a3344501e3c07a690e8cf6783eddf5cb4d845 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 27 Jan 2023 08:28:36 +1300 +Subject: [PATCH] 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 +--- + lib/ldb/common/ldb_msg.c | 26 ++++++++++++++++++++++++++ + lib/ldb/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.34.1 + diff -Nru ldb-2.4.4/debian/patches/CVE-2023-0614-09.patch ldb-2.4.4/debian/patches/CVE-2023-0614-09.patch --- ldb-2.4.4/debian/patches/CVE-2023-0614-09.patch 1970-01-01 00:00:00.000000000 +0000 +++ ldb-2.4.4/debian/patches/CVE-2023-0614-09.patch 2023-03-30 12:03:52.000000000 +0000 @@ -0,0 +1,407 @@ +From 7153af801e59f4cfee54ae020bfca13c73f63e93 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Wed, 15 Feb 2023 12:34:51 +1300 +Subject: [PATCH] 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 +--- + lib/ldb/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.34.1 + diff -Nru ldb-2.4.4/debian/patches/CVE-2023-0614-10.patch ldb-2.4.4/debian/patches/CVE-2023-0614-10.patch --- ldb-2.4.4/debian/patches/CVE-2023-0614-10.patch 1970-01-01 00:00:00.000000000 +0000 +++ ldb-2.4.4/debian/patches/CVE-2023-0614-10.patch 2023-03-30 12:04:20.000000000 +0000 @@ -0,0 +1,40 @@ +From 6519d1d8fa1e1154a388a3bff319da2b0387f157 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Wed, 15 Feb 2023 14:08:57 +1300 +Subject: [PATCH] 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 +--- + lib/ldb/wscript | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/wscript ++++ b/wscript +@@ -631,7 +631,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', +@@ -641,7 +640,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 diff -Nru ldb-2.4.4/debian/patches/CVE-2023-0614-11.patch ldb-2.4.4/debian/patches/CVE-2023-0614-11.patch --- ldb-2.4.4/debian/patches/CVE-2023-0614-11.patch 1970-01-01 00:00:00.000000000 +0000 +++ ldb-2.4.4/debian/patches/CVE-2023-0614-11.patch 2023-03-30 12:04:44.000000000 +0000 @@ -0,0 +1,102 @@ +From 891ffeaf99d150e2a5707d71825e5533570aa974 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:23:42 +1300 +Subject: [PATCH] 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 +--- + lib/ldb/common/ldb_pack.c | 41 ++++++++++++++++++++++++++++++++++++ + lib/ldb/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.34.1 + diff -Nru ldb-2.4.4/debian/patches/CVE-2023-0614-12.patch ldb-2.4.4/debian/patches/CVE-2023-0614-12.patch --- ldb-2.4.4/debian/patches/CVE-2023-0614-12.patch 1970-01-01 00:00:00.000000000 +0000 +++ ldb-2.4.4/debian/patches/CVE-2023-0614-12.patch 2023-03-30 12:05:11.000000000 +0000 @@ -0,0 +1,60 @@ +From 873d4e465f333c487dc1bee748054b6b606c299b Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:26:04 +1300 +Subject: [PATCH] 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] +--- + lib/ldb/common/ldb_msg.c | 16 ++++++++++++++++ + lib/ldb/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.34.1 + diff -Nru ldb-2.4.4/debian/patches/CVE-2023-0614-13.patch ldb-2.4.4/debian/patches/CVE-2023-0614-13.patch --- ldb-2.4.4/debian/patches/CVE-2023-0614-13.patch 1970-01-01 00:00:00.000000000 +0000 +++ ldb-2.4.4/debian/patches/CVE-2023-0614-13.patch 2023-03-30 12:05:36.000000000 +0000 @@ -0,0 +1,66 @@ +From 7982090641e5199d2bbece3b7aa50f3e7342db12 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:27:38 +1300 +Subject: [PATCH] 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] +--- + lib/ldb/common/ldb_pack.c | 6 +++--- + lib/ldb/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.34.1 + diff -Nru ldb-2.4.4/debian/patches/CVE-2023-0614-14.patch ldb-2.4.4/debian/patches/CVE-2023-0614-14.patch --- ldb-2.4.4/debian/patches/CVE-2023-0614-14.patch 1970-01-01 00:00:00.000000000 +0000 +++ ldb-2.4.4/debian/patches/CVE-2023-0614-14.patch 2023-03-30 12:06:23.000000000 +0000 @@ -0,0 +1,1209 @@ +From 7c2d0e0a06e6c3523f1ad3fba514505ca094f2fd Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:29:03 +1300 +Subject: [PATCH] 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 +--- + lib/ldb/common/ldb_pack.c | 143 +++ + lib/ldb/include/ldb_module.h | 10 + + .../tests/ldb_filter_attrs_in_place_test.c | 989 ++++++++++++++++++ + lib/ldb/wscript | 6 + + 4 files changed, 1148 insertions(+) + create mode 100644 lib/ldb/tests/ldb_filter_attrs_in_place_test.c + +--- 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) + { +--- a/include/ldb_module.h ++++ b/include/ldb_module.h +@@ -543,6 +543,16 @@ int ldb_filter_attrs(struct ldb_context + 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); + +--- /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); ++} +--- a/wscript ++++ b/wscript +@@ -522,6 +522,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 +@@ -642,6 +647,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 diff -Nru ldb-2.4.4/debian/patches/CVE-2023-0614-15.patch ldb-2.4.4/debian/patches/CVE-2023-0614-15.patch --- ldb-2.4.4/debian/patches/CVE-2023-0614-15.patch 1970-01-01 00:00:00.000000000 +0000 +++ ldb-2.4.4/debian/patches/CVE-2023-0614-15.patch 2023-03-30 12:09:21.000000000 +0000 @@ -0,0 +1,1283 @@ +From 4addeaaf5da96ac8f620a0c27c2a576b17747dd2 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:30:19 +1300 +Subject: [PATCH] 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 +--- + lib/ldb/common/ldb_pack.c | 129 +--- + lib/ldb/include/ldb_module.h | 11 +- + .../tests/ldb_filter_attrs_in_place_test.c | 609 ++++++++---------- + 3 files changed, 307 insertions(+), 442 deletions(-) + +--- 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 + 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; +- } +- 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 (!found) { ++ ++num_del; ++ } else if (num_del != 0) { ++ msg->elements[i - num_del] = msg->elements[i]; + } + } + +- 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); +- } ++ msg->num_elements -= num_del; + +- return 0; +-failed: +- TALLOC_FREE(filtered_msg->elements); +- return -1; ++ return LDB_SUCCESS; + } + + /* Have an unpacked ldb message take talloc ownership of its elements. */ +--- a/include/ldb_module.h ++++ b/include/ldb_module.h +@@ -545,13 +545,12 @@ int ldb_filter_attrs(struct ldb_context + + /* + * 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); +--- 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_m + .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, ++ 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(msg, attrs); ++ assert_int_equal(ret, LDB_SUCCESS); ++ ++ 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_m + * 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_m + .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, ++ 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(msg, attrs); ++ assert_int_equal(ret, LDB_SUCCESS); ++ ++ 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_m + * 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_m + .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); + 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_m + * 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_m + .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_m + .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_m + * 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_at + .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_at + .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); ++ ++ 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); ++ ++ /* Both elements match the filter */ ++ assert_int_equal(ret, LDB_SUCCESS); ++ assert_int_equal(msg->num_elements, 2); ++ ++ assert_non_null(msg->dn); ++ ++ /* 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_at + .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_at + .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_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); + + /* 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_at + * 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_at + .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_at + .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_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); + + /* 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_at + * 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_at + .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_at + .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_m + .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_m + * 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_m + .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_m + + /* + * 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_m + .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); ++ 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(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_m + .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_m + * 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_m + .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)); ++ ++ 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(msg, attrs); ++ assert_int_equal(ret, LDB_SUCCESS); ++ 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_e + .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); ++ ++ 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(msg, attrs); ++ assert_int_equal(ret, LDB_SUCCESS); ++ 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), + }; diff -Nru ldb-2.4.4/debian/patches/CVE-2023-0614-16.patch ldb-2.4.4/debian/patches/CVE-2023-0614-16.patch --- ldb-2.4.4/debian/patches/CVE-2023-0614-16.patch 1970-01-01 00:00:00.000000000 +0000 +++ ldb-2.4.4/debian/patches/CVE-2023-0614-16.patch 2023-03-30 12:09:55.000000000 +0000 @@ -0,0 +1,248 @@ +Backport of: + +From 4bbdd6709bfe2ba31cee8968751a48a6d454f19e Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Mon, 27 Feb 2023 10:31:52 +1300 +Subject: [PATCH] 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 +--- + lib/ldb/ldb_key_value/ldb_kv.h | 6 +- + lib/ldb/ldb_key_value/ldb_kv_index.c | 27 +++++---- + lib/ldb/ldb_key_value/ldb_kv_search.c | 86 ++++++++++++++------------- + source4/torture/ldb/ldb.c | 12 ++-- + 4 files changed, 66 insertions(+), 65 deletions(-) + +--- 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 + 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); + + /* +--- 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 ld + { + 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 ld + 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 +--- 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 + + /* + * 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 l + { + 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 l + 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 + 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 + 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 + * returned result, as it has already been + * casefolded + */ +- filtered_msg->dn = ldb_dn_copy(filtered_msg, ctx->base); ++ struct ldb_dn *dn = ldb_dn_copy(msg, ctx->base); ++ if (dn != NULL) { ++ msg->dn = dn; ++ } + } + +- /* +- * If the ldb_dn_copy() failed, or if we did not choose that +- * optimisation (filtered_msg is zeroed at allocation), +- * steal the one from the unpack +- */ +- if (filtered_msg->dn == NULL) { +- filtered_msg->dn = talloc_steal(filtered_msg, msg->dn); ++ ret = ldb_msg_add_distinguished_name(msg); ++ if (ret == -1) { ++ talloc_free(msg); ++ return LDB_ERR_OPERATIONS_ERROR; + } + + /* + * filter the attributes that the user wants. + */ +- ret = ldb_kv_filter_attrs(ldb, msg, ctx->attrs, filtered_msg); +- if (ret == -1) { ++ ret = ldb_kv_filter_attrs_in_place(msg, ctx->attrs); ++ if (ret != LDB_SUCCESS) { ++ talloc_free(msg); ++ return LDB_ERR_OPERATIONS_ERROR; ++ } ++ ++ ldb_msg_shrink_to_fit(msg); ++ ++ /* Ensure the message elements are all talloc'd. */ ++ ret = ldb_msg_elements_take_ownership(msg); ++ if (ret != LDB_SUCCESS) { + talloc_free(msg); +- filtered_msg = NULL; + return LDB_ERR_OPERATIONS_ERROR; + } + + /* +- * Remove any extended components possibly copied in from +- * msg->dn, we just want the casefold components ++ * Remove any extended components, we just want the casefold components + */ +- ldb_dn_remove_extended_components(filtered_msg->dn); +- talloc_free(msg); ++ ldb_dn_remove_extended_components(msg->dn); + +- ret = ldb_module_send_entry(ctx->req, filtered_msg, NULL); ++ ret = ldb_module_send_entry(ctx->req, msg, NULL); + if (ret != LDB_SUCCESS) { + /* Regardless of success or failure, the msg + * is the callbacks responsiblity, and should diff -Nru ldb-2.4.4/debian/patches/CVE-2023-0614-22.patch ldb-2.4.4/debian/patches/CVE-2023-0614-22.patch --- ldb-2.4.4/debian/patches/CVE-2023-0614-22.patch 1970-01-01 00:00:00.000000000 +0000 +++ ldb-2.4.4/debian/patches/CVE-2023-0614-22.patch 2023-03-30 12:11:40.000000000 +0000 @@ -0,0 +1,64 @@ +From 0b0d8a8ece6ac0d18c7cbdb726d2c46cd6c88997 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:31:54 +1300 +Subject: [PATCH] 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 +--- + lib/ldb/common/ldb_parse.c | 25 +++++++++++++++++++++++++ + lib/ldb/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.34.1 + diff -Nru ldb-2.4.4/debian/patches/CVE-2023-0614-26.patch ldb-2.4.4/debian/patches/CVE-2023-0614-26.patch --- ldb-2.4.4/debian/patches/CVE-2023-0614-26.patch 1970-01-01 00:00:00.000000000 +0000 +++ ldb-2.4.4/debian/patches/CVE-2023-0614-26.patch 2023-03-30 12:13:32.000000000 +0000 @@ -0,0 +1,225 @@ +From 9447c4e81e04df5b8d775fb62f3440f0d9076002 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:34:29 +1300 +Subject: [PATCH] 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 and 4.16 and lower + not having the patches for CVE-2022-32743 ] +--- + lib/ldb-samba/ldb_matching_rules.c | 15 + + lib/ldb/common/ldb_match.c | 37 + + lib/ldb/include/ldb_module.h | 11 + + lib/ldb/include/ldb_private.h | 5 + + lib/ldb/ldb_key_value/ldb_kv_index.c | 8 + + lib/ldb/ldb_key_value/ldb_kv_search.c | 15 + + selftest/knownfail.d/confidential-attr-timing | 1 - + source4/dsdb/samdb/ldb_modules/acl.c | 183 +--- + source4/dsdb/samdb/ldb_modules/acl_read.c | 837 ++++++++++++------ + source4/dsdb/samdb/samdb.h | 2 + + .../dsdb/tests/python/confidential_attr.py | 12 +- + source4/setup/schema_samba4.ldif | 1 + + 12 files changed, 672 insertions(+), 455 deletions(-) + delete mode 100644 selftest/knownfail.d/confidential-attr-timing + +--- a/common/ldb_match.c ++++ b/common/ldb_match.c +@@ -99,6 +99,11 @@ static int ldb_match_present(struct 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; +@@ -140,6 +145,11 @@ static int ldb_match_comparison(struct l + 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; +@@ -210,6 +220,11 @@ static int ldb_match_equality(struct 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; +@@ -410,6 +425,11 @@ static int ldb_match_substring(struct ld + 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); +@@ -483,6 +503,11 @@ static int ldb_match_bitmask(struct 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]; +@@ -781,3 +806,15 @@ int ldb_register_extended_match_rule(str + 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; ++} +--- 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(s + 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, +--- 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; + +--- 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 ld + 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. +--- 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 l + } + } + ++ 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 + 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 -Nru ldb-2.4.4/debian/patches/CVE-2023-0614-29.patch ldb-2.4.4/debian/patches/CVE-2023-0614-29.patch --- ldb-2.4.4/debian/patches/CVE-2023-0614-29.patch 1970-01-01 00:00:00.000000000 +0000 +++ ldb-2.4.4/debian/patches/CVE-2023-0614-29.patch 2023-03-30 12:14:48.000000000 +0000 @@ -0,0 +1,126 @@ +Backport of: + +From d60683e5e9daf243e9a2acc203b567c3a6c92567 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Tue, 14 Feb 2023 13:17:24 +1300 +Subject: [PATCH] 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 +--- + lib/ldb-samba/ldb_matching_rules.c | 5 --- + lib/ldb/common/ldb_match.c | 56 +++++++++++++++++------------- + 2 files changed, 31 insertions(+), 30 deletions(-) + +--- a/common/ldb_match.c ++++ b/common/ldb_match.c +@@ -99,11 +99,6 @@ static int ldb_match_present(struct 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; +@@ -145,11 +140,6 @@ static int ldb_match_comparison(struct l + 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; +@@ -220,11 +210,6 @@ static int ldb_match_equality(struct 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; +@@ -425,11 +410,6 @@ static int ldb_match_substring(struct ld + 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); +@@ -503,11 +483,6 @@ static int ldb_match_bitmask(struct 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]; +@@ -596,6 +571,26 @@ static int ldb_match_extended(struct 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 + +@@ -620,6 +615,17 @@ int ldb_match_message(struct ldb_context + 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++) { diff -Nru ldb-2.4.4/debian/patches/CVE-2023-0614-30.patch ldb-2.4.4/debian/patches/CVE-2023-0614-30.patch --- ldb-2.4.4/debian/patches/CVE-2023-0614-30.patch 1970-01-01 00:00:00.000000000 +0000 +++ ldb-2.4.4/debian/patches/CVE-2023-0614-30.patch 2023-03-30 12:15:15.000000000 +0000 @@ -0,0 +1,153 @@ +From a74571b49f5476cde430f11cd7bc256f17925fe8 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Fri, 3 Mar 2023 17:35:55 +1300 +Subject: [PATCH] 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 +--- + lib/ldb/common/ldb_match.c | 8 +++--- + lib/ldb/include/ldb_private.h | 8 ++++++ + lib/ldb/ldb_key_value/ldb_kv_index.c | 40 +++++++++++++++------------ + lib/ldb/ldb_key_value/ldb_kv_search.c | 14 ++++++++-- + 4 files changed, 47 insertions(+), 23 deletions(-) + +diff --git a/common/ldb_match.c b/common/ldb_match.c +index 267498560e6..463a24ce3bc 100644 +--- a/common/ldb_match.c ++++ b/common/ldb_match.c +@@ -39,10 +39,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.34.1 + diff -Nru ldb-2.4.4/debian/patches/series ldb-2.4.4/debian/patches/series --- ldb-2.4.4/debian/patches/series 2022-02-15 21:29:01.000000000 +0000 +++ ldb-2.4.4/debian/patches/series 2023-03-30 12:15:01.000000000 +0000 @@ -3,3 +3,17 @@ Skip-test_guid_indexed_v1_db-on-mips64el-ppc64el-ia6.patch Skip-ldb_lmdb_free_list_test-on-ppc64el-ppc64-and-sp.patch Skip_failing_tests.diff +CVE-2023-0614-01.patch +CVE-2023-0614-07.patch +CVE-2023-0614-09.patch +CVE-2023-0614-10.patch +CVE-2023-0614-11.patch +CVE-2023-0614-12.patch +CVE-2023-0614-13.patch +CVE-2023-0614-14.patch +CVE-2023-0614-15.patch +CVE-2023-0614-16.patch +CVE-2023-0614-22.patch +CVE-2023-0614-26.patch +CVE-2023-0614-29.patch +CVE-2023-0614-30.patch