184 lines
6.1 KiB
Diff
184 lines
6.1 KiB
Diff
From 3e8d6e681f8dbe79e4595549f78c42649b3573a2 Mon Sep 17 00:00:00 2001
|
|
From: Andrew Bartlett <abartlet@samba.org>
|
|
Date: Thu, 18 Nov 2021 16:09:47 +1300
|
|
Subject: [PATCH] CVE-2021-3670 ldap_server: Clearly log LDAP queries and
|
|
timeouts
|
|
|
|
This puts all the detail on one line so it can be searched
|
|
by IP address and connecting SID.
|
|
|
|
This relies on the anr handling as otherwise this log
|
|
becomes the expanded query, not the original one.
|
|
|
|
RN: Provide clear logs of the LDAP search and who made it, including
|
|
a warning (at log level 3) for queries that are 1/4 of the hard timeout.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14694
|
|
|
|
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
|
|
Autobuild-User(master): Douglas Bagnall <dbagnall@samba.org>
|
|
Autobuild-Date(master): Thu Nov 25 02:30:42 UTC 2021 on sn-devel-184
|
|
|
|
(cherry picked from commit 3507e96b3dcf0c0b8eff7b2c08ffccaf0812a393)
|
|
---
|
|
source4/ldap_server/ldap_backend.c | 121 +++++++++++++++++++++++------
|
|
1 file changed, 99 insertions(+), 22 deletions(-)
|
|
|
|
diff --git a/source4/ldap_server/ldap_backend.c b/source4/ldap_server/ldap_backend.c
|
|
index 78d0de951c54..e37b87f62eed 100644
|
|
--- a/source4/ldap_server/ldap_backend.c
|
|
+++ b/source4/ldap_server/ldap_backend.c
|
|
@@ -33,6 +33,7 @@
|
|
#include "ldb_wrap.h"
|
|
#include "lib/tsocket/tsocket.h"
|
|
#include "libcli/ldap/ldap_proto.h"
|
|
+#include "source4/auth/auth.h"
|
|
|
|
static int map_ldb_error(TALLOC_CTX *mem_ctx, int ldb_err,
|
|
const char *add_err_string, const char **errstring)
|
|
@@ -729,6 +729,15 @@ static NTSTATUS ldapsrv_SearchRequest(struct ldapsrv_call *call)
|
|
unsigned int i;
|
|
int extended_type = 1;
|
|
|
|
+ /*
|
|
+ * Warn for searches that are longer than 1/4 of the
|
|
+ * search_timeout, being 30sec by default
|
|
+ */
|
|
+ struct timeval start_time = timeval_current();
|
|
+ struct timeval warning_time
|
|
+ = timeval_add(&start_time,
|
|
+ call->conn->limits.search_timeout / 4,
|
|
+ 0);
|
|
|
|
local_ctx = talloc_new(call);
|
|
NT_STATUS_HAVE_NO_MEMORY(local_ctx);
|
|
@@ -736,29 +745,27 @@ static NTSTATUS ldapsrv_SearchRequest(struct ldapsrv_call *call)
|
|
basedn = ldb_dn_new(local_ctx, samdb, req->basedn);
|
|
NT_STATUS_HAVE_NO_MEMORY(basedn);
|
|
|
|
- DEBUG(10, ("SearchRequest: basedn: [%s]\n", req->basedn));
|
|
- DEBUG(10, ("SearchRequest: filter: [%s]\n", ldb_filter_from_tree(call, req->tree)));
|
|
-
|
|
switch (req->scope) {
|
|
- case LDAP_SEARCH_SCOPE_BASE:
|
|
- scope_str = "BASE";
|
|
- scope = LDB_SCOPE_BASE;
|
|
- break;
|
|
- case LDAP_SEARCH_SCOPE_SINGLE:
|
|
- scope_str = "ONE";
|
|
- scope = LDB_SCOPE_ONELEVEL;
|
|
- break;
|
|
- case LDAP_SEARCH_SCOPE_SUB:
|
|
- scope_str = "SUB";
|
|
- scope = LDB_SCOPE_SUBTREE;
|
|
- break;
|
|
- default:
|
|
- result = LDAP_PROTOCOL_ERROR;
|
|
- map_ldb_error(local_ctx, LDB_ERR_PROTOCOL_ERROR, NULL,
|
|
- &errstr);
|
|
- errstr = talloc_asprintf(local_ctx,
|
|
- "%s. Invalid scope", errstr);
|
|
- goto reply;
|
|
+ case LDAP_SEARCH_SCOPE_BASE:
|
|
+ scope_str = "BASE";
|
|
+ scope = LDB_SCOPE_BASE;
|
|
+ break;
|
|
+ case LDAP_SEARCH_SCOPE_SINGLE:
|
|
+ scope_str = "ONE";
|
|
+ scope = LDB_SCOPE_ONELEVEL;
|
|
+ break;
|
|
+ case LDAP_SEARCH_SCOPE_SUB:
|
|
+ scope_str = "SUB";
|
|
+ scope = LDB_SCOPE_SUBTREE;
|
|
+ break;
|
|
+ default:
|
|
+ result = LDAP_PROTOCOL_ERROR;
|
|
+ map_ldb_error(local_ctx, LDB_ERR_PROTOCOL_ERROR, NULL,
|
|
+ &errstr);
|
|
+ scope_str = "<Invalid scope>";
|
|
+ errstr = talloc_asprintf(local_ctx,
|
|
+ "%s. Invalid scope", errstr);
|
|
+ goto reply;
|
|
}
|
|
DEBUG(10,("SearchRequest: scope: [%s]\n", scope_str));
|
|
|
|
@@ -911,6 +918,76 @@ static NTSTATUS ldapsrv_SearchRequest(struct ldapsrv_call *call)
|
|
}
|
|
|
|
reply:
|
|
+
|
|
+ /*
|
|
+ * This looks like duplicated code - because it is - but
|
|
+ * otherwise the work in the parameters will be done
|
|
+ * regardless, this way the functions only execuate when the
|
|
+ * log level is set.
|
|
+ *
|
|
+ * The basedn is re-obtained as a string to escape it
|
|
+ */
|
|
+ if ((req->timelimit == 0 || call->conn->limits.search_timeout < req->timelimit)
|
|
+ && ldb_ret == LDB_ERR_TIME_LIMIT_EXCEEDED) {
|
|
+ struct dom_sid_buf sid_buf;
|
|
+ DBG_WARNING("MaxQueryDuration(%d) timeout exceeded "
|
|
+ "in SearchRequest by %s from %s filter: [%s] "
|
|
+ "basedn: [%s] "
|
|
+ "scope: [%s]\n",
|
|
+ call->conn->limits.search_timeout,
|
|
+ dom_sid_str_buf(&call->conn->session_info->security_token->sids[0],
|
|
+ &sid_buf),
|
|
+ tsocket_address_string(call->conn->connection->remote_address,
|
|
+ call),
|
|
+ ldb_filter_from_tree(call, req->tree),
|
|
+ ldb_dn_get_extended_linearized(call, basedn, 1),
|
|
+ scope_str);
|
|
+ for (i=0; i < req->num_attributes; i++) {
|
|
+ DBG_WARNING("MaxQueryDuration timeout exceeded attrs: [%s]\n",
|
|
+ req->attributes[i]);
|
|
+ }
|
|
+
|
|
+ } else if (timeval_expired(&warning_time)) {
|
|
+ struct dom_sid_buf sid_buf;
|
|
+ DBG_NOTICE("Long LDAP Query: Duration was %.2fs, "
|
|
+ "MaxQueryDuration(%d)/4 == %d "
|
|
+ "in SearchRequest by %s from %s filter: [%s] "
|
|
+ "basedn: [%s] "
|
|
+ "scope: [%s] "
|
|
+ "result: %s\n",
|
|
+ timeval_elapsed(&start_time),
|
|
+ call->conn->limits.search_timeout,
|
|
+ call->conn->limits.search_timeout / 4,
|
|
+ dom_sid_str_buf(&call->conn->session_info->security_token->sids[0],
|
|
+ &sid_buf),
|
|
+ tsocket_address_string(call->conn->connection->remote_address,
|
|
+ call),
|
|
+ ldb_filter_from_tree(call, req->tree),
|
|
+ ldb_dn_get_extended_linearized(call, basedn, 1),
|
|
+ scope_str,
|
|
+ ldb_strerror(ldb_ret));
|
|
+ for (i=0; i < req->num_attributes; i++) {
|
|
+ DBG_NOTICE("Long LDAP Query attrs: [%s]\n",
|
|
+ req->attributes[i]);
|
|
+ }
|
|
+ } else {
|
|
+ struct dom_sid_buf sid_buf;
|
|
+ DBG_INFO("LDAP Query: Duration was %.2fs, "
|
|
+ "SearchRequest by %s from %s filter: [%s] "
|
|
+ "basedn: [%s] "
|
|
+ "scope: [%s] "
|
|
+ "result: %s\n",
|
|
+ timeval_elapsed(&start_time),
|
|
+ dom_sid_str_buf(&call->conn->session_info->security_token->sids[0],
|
|
+ &sid_buf),
|
|
+ tsocket_address_string(call->conn->connection->remote_address,
|
|
+ call),
|
|
+ ldb_filter_from_tree(call, req->tree),
|
|
+ ldb_dn_get_extended_linearized(call, basedn, 1),
|
|
+ scope_str,
|
|
+ ldb_strerror(ldb_ret));
|
|
+ }
|
|
+
|
|
DLIST_REMOVE(call->conn->pending_calls, call);
|
|
call->notification.busy = false;
|
|
|