Backport of:

From e3c15d9365fcec0389c9b3ac7bc38871854c64a7 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Wed, 24 Jun 2026 14:42:11 +0200
Subject: [PATCH 23/23] CVE-2026-58222 s4:ldap_server: don't allow untrusted
 compare requests for confidential attributes

This means we apply acl checks against the search filter similar
to normal ldb searches.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=16148

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
 source4/ldap_server/ldap_backend.c | 27 ++++++++++++++++++++-------
 1 file changed, 20 insertions(+), 7 deletions(-)

--- a/source4/ldap_server/ldap_backend.c
+++ b/source4/ldap_server/ldap_backend.c
@@ -28,6 +28,7 @@
 #include "param/param.h"
 #include "samba/service_stream.h"
 #include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
 #include <ldb_errors.h>
 #include <ldb_module.h>
 #include "ldb_wrap.h"
@@ -1406,6 +1407,7 @@ static NTSTATUS ldapsrv_CompareRequest(s
 	struct ldb_dn *dn;
 	const char *attrs[1];
 	const char *errstr = NULL;
+	const char *value = NULL;
 	const char *filter = NULL;
 	int result = LDAP_SUCCESS;
 	int ldb_ret;
@@ -1416,24 +1418,34 @@ static NTSTATUS ldapsrv_CompareRequest(s
 	local_ctx = talloc_named(call, 0, "CompareRequest local_memory_context");
 	NT_STATUS_HAVE_NO_MEMORY(local_ctx);
 
+	compare_r = ldapsrv_init_reply(call, LDAP_TAG_CompareResponse);
+	NT_STATUS_HAVE_NO_MEMORY(compare_r);
+
 	dn = ldb_dn_new(local_ctx, samdb, req->dn);
 	NT_STATUS_HAVE_NO_MEMORY(dn);
 
 	DEBUG(10, ("CompareRequest: dn: [%s]\n", req->dn));
-	filter = talloc_asprintf(local_ctx, "(%s=%*s)", req->attribute, 
-				 (int)req->value.length, req->value.data);
+
+	if (!ldb_valid_attr_name(req->attribute)) {
+		result = LDAP_INVALID_ATTRIBUTE_SYNTAX;
+		errstr = "Invalid Compare attribute name";
+		goto reply;
+	}
+	value = ldb_binary_encode(local_ctx, req->value);
+	NT_STATUS_HAVE_NO_MEMORY(value);
+
+	filter = talloc_asprintf(local_ctx, "%s=%s", req->attribute, value);
 	NT_STATUS_HAVE_NO_MEMORY(filter);
 
 	DEBUGADD(10, ("CompareRequest: attribute: [%s]\n", filter));
 
 	attrs[0] = NULL;
 
-	compare_r = ldapsrv_init_reply(call, LDAP_TAG_CompareResponse);
-	NT_STATUS_HAVE_NO_MEMORY(compare_r);
-
 	if (result == LDAP_SUCCESS) {
-		ldb_ret = ldb_search(samdb, local_ctx, &res,
-				     dn, LDB_SCOPE_BASE, attrs, "%s", filter);
+		ldb_ret = dsdb_search(samdb, local_ctx, &res,
+				      dn, LDB_SCOPE_BASE, attrs,
+				      DSDB_MARK_REQ_UNTRUSTED,
+				      "%s", filter);
 		if (ldb_ret != LDB_SUCCESS) {
 			result = map_ldb_error(local_ctx, ldb_ret,
 					       ldb_errstring(samdb), &errstr);
@@ -1455,6 +1467,7 @@ static NTSTATUS ldapsrv_CompareRequest(s
 		}
 	}
 
+reply:
 	compare = &compare_r->msg->r.CompareResponse;
 	compare->dn = NULL;
 	compare->resultcode = result;
