179 lines
5.7 KiB
Diff
179 lines
5.7 KiB
Diff
From 208bbf8cfda200deaeddfad77e4b43d54e692ba5 Mon Sep 17 00:00:00 2001
|
|
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Date: Wed, 20 Oct 2021 17:09:21 +1300
|
|
Subject: [PATCH 157/266] CVE-2020-25722 s4/dsdb modules: add
|
|
dsdb_get_expected_new_values()
|
|
|
|
This function collects a superset of all the new values for the specified
|
|
attribute that could result from an ldb add or modify message.
|
|
|
|
In most cases -- where there is a single add or modify -- the exact set
|
|
of added values is returned, and this is done reasonably efficiently
|
|
using the existing element. Where it gets complicated is when there are
|
|
multiple elements for the same attribute in a message. Anything added
|
|
before a replace or delete will be included in these results but may not
|
|
end up in the database if the message runs its course. Examples:
|
|
|
|
sequence result
|
|
1. ADD the element is returned (exact)
|
|
2. REPLACE the element is returned (exact)
|
|
3. ADD, ADD both elements are concatenated together (exact)
|
|
4. ADD, REPLACE both elements are concatenated together (superset)
|
|
5. REPLACE, ADD both elements are concatenated together (exact)
|
|
6. ADD, DEL, ADD adds are concatenated together (superset)
|
|
7. REPLACE, REPLACE both concatenated (superset)
|
|
8. DEL, ADD last element is returned (exact)
|
|
|
|
Why this? In the past we have treated dsdb_get_single_valued_attr() as if
|
|
it returned the complete set of possible database changes, when in fact it
|
|
only returned the last non-delete. That is, it could have missed values
|
|
in examples 3-7 above.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876
|
|
|
|
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
|
|
|
|
Conflict:NA
|
|
Reference:https://gitlab.com/samba-team/samba/-/commit/208bbf8cfda200deaeddfad77e4b43d54e692ba5
|
|
|
|
---
|
|
source4/dsdb/samdb/ldb_modules/util.c | 121 ++++++++++++++++++++++++++
|
|
1 file changed, 121 insertions(+)
|
|
|
|
diff --git a/source4/dsdb/samdb/ldb_modules/util.c b/source4/dsdb/samdb/ldb_modules/util.c
|
|
index 9519ecfa928..da152e4d754 100644
|
|
--- a/source4/dsdb/samdb/ldb_modules/util.c
|
|
+++ b/source4/dsdb/samdb/ldb_modules/util.c
|
|
@@ -1441,6 +1441,127 @@ void dsdb_req_chain_debug(struct ldb_request *req, int level)
|
|
talloc_free(s);
|
|
}
|
|
|
|
+/*
|
|
+ * Get all the values that *might* be added by an ldb message, as a composite
|
|
+ * ldb element.
|
|
+ *
|
|
+ * This is useful when we need to check all the possible values against some
|
|
+ * criteria.
|
|
+ *
|
|
+ * In cases where a modify message mixes multiple ADDs, DELETEs, and REPLACES,
|
|
+ * the returned element might contain more values than would actually end up
|
|
+ * in the database if the message was run to its conclusion.
|
|
+ *
|
|
+ * If the operation is not LDB_ADD or LDB_MODIFY, an operations error is
|
|
+ * returned.
|
|
+ *
|
|
+ * The returned element might not be new, and should not be modified or freed
|
|
+ * before the message is finished.
|
|
+ */
|
|
+
|
|
+int dsdb_get_expected_new_values(TALLOC_CTX *mem_ctx,
|
|
+ const struct ldb_message *msg,
|
|
+ const char *attr_name,
|
|
+ struct ldb_message_element **el,
|
|
+ enum ldb_request_type operation)
|
|
+{
|
|
+ unsigned int i;
|
|
+ unsigned int el_count = 0;
|
|
+ unsigned int val_count = 0;
|
|
+ struct ldb_val *v = NULL;
|
|
+ struct ldb_message_element *_el = NULL;
|
|
+ *el = NULL;
|
|
+
|
|
+ if (operation != LDB_ADD && operation != LDB_MODIFY) {
|
|
+ DBG_ERR("inapplicable operation type: %d\n", operation);
|
|
+ return LDB_ERR_OPERATIONS_ERROR;
|
|
+ }
|
|
+
|
|
+ /* count the adding or replacing elements */
|
|
+ for (i = 0; i < msg->num_elements; i++) {
|
|
+ if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
|
|
+ unsigned int tmp;
|
|
+ if ((operation == LDB_MODIFY) &&
|
|
+ (LDB_FLAG_MOD_TYPE(msg->elements[i].flags)
|
|
+ == LDB_FLAG_MOD_DELETE)) {
|
|
+ continue;
|
|
+ }
|
|
+ el_count++;
|
|
+ tmp = val_count + msg->elements[i].num_values;
|
|
+ if (unlikely(tmp < val_count)) {
|
|
+ DBG_ERR("too many values for one element!");
|
|
+ return LDB_ERR_OPERATIONS_ERROR;
|
|
+ }
|
|
+ val_count = tmp;
|
|
+ }
|
|
+ }
|
|
+ if (el_count == 0) {
|
|
+ /* nothing to see here */
|
|
+ return LDB_SUCCESS;
|
|
+ }
|
|
+
|
|
+ if (el_count == 1 || val_count == 0) {
|
|
+ /*
|
|
+ * There is one effective element, which we can return as-is,
|
|
+ * OR there are only elements with zero values -- any of which
|
|
+ * will do.
|
|
+ */
|
|
+ for (i = 0; i < msg->num_elements; i++) {
|
|
+ if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
|
|
+ if ((operation == LDB_MODIFY) &&
|
|
+ (LDB_FLAG_MOD_TYPE(msg->elements[i].flags)
|
|
+ == LDB_FLAG_MOD_DELETE)) {
|
|
+ continue;
|
|
+ }
|
|
+ *el = &msg->elements[i];
|
|
+ return LDB_SUCCESS;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ _el = talloc_zero(mem_ctx, struct ldb_message_element);
|
|
+ if (_el == NULL) {
|
|
+ return LDB_ERR_OPERATIONS_ERROR;
|
|
+ }
|
|
+ _el->name = attr_name;
|
|
+
|
|
+ if (val_count == 0) {
|
|
+ /*
|
|
+ * Seems unlikely, but sometimes we might be adding zero
|
|
+ * values in multiple separate elements. The talloc zero has
|
|
+ * already set the expected values = NULL, num_values = 0.
|
|
+ */
|
|
+ *el = _el;
|
|
+ return LDB_SUCCESS;
|
|
+ }
|
|
+
|
|
+ _el->values = talloc_array(_el, struct ldb_val, val_count);
|
|
+ if (_el->values == NULL) {
|
|
+ talloc_free(_el);
|
|
+ return LDB_ERR_OPERATIONS_ERROR;
|
|
+ }
|
|
+ _el->num_values = val_count;
|
|
+
|
|
+ v = _el->values;
|
|
+
|
|
+ for (i = 0; i < val_count; i++) {
|
|
+ if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
|
|
+ if ((operation == LDB_MODIFY) &&
|
|
+ (LDB_FLAG_MOD_TYPE(msg->elements[i].flags)
|
|
+ == LDB_FLAG_MOD_DELETE)) {
|
|
+ continue;
|
|
+ }
|
|
+ memcpy(v,
|
|
+ msg->elements[i].values,
|
|
+ msg->elements[i].num_values);
|
|
+ v += msg->elements[i].num_values;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *el = _el;
|
|
+ return LDB_SUCCESS;
|
|
+}
|
|
+
|
|
/*
|
|
* Gets back a single-valued attribute by the rules of the DSDB triggers when
|
|
* performing a modify operation.
|
|
--
|
|
2.23.0
|
|
|