From 8b7cbc2dcced1334e2829a722d456cfa4105cf1c Mon Sep 17 00:00:00 2001
From: Valera V Harseko <vharseko@3a-systems.ru>
Date: Wed, 21 Oct 2026 08:53:55 +0000
Subject: [PATCH] GHSA-7778-jm7p-68g5 LDAP listener lets unauthenticated clients hold connections until the server stops accepting new ones
---
opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPClientConnection2.java | 43 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 43 insertions(+), 0 deletions(-)
diff --git a/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPClientConnection2.java b/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPClientConnection2.java
index 53f799f..78427f9 100644
--- a/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPClientConnection2.java
+++ b/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPClientConnection2.java
@@ -38,6 +38,7 @@
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@@ -136,6 +137,10 @@
/** The time that the last operation was completed. */
private final AtomicLong lastCompletionTime;
+ /** Whether a complete request has arrived on this connection, which ends its first request time limit. */
+ private volatile boolean requestReceived;
+ /** The task which closes this connection unless its first request arrives in time, or {@code null}. */
+ private volatile Future<?> firstRequestTimer;
/** The next operation ID that should be used for this connection. */
private final AtomicLong nextOperationID;
@@ -645,6 +650,7 @@
// Indicate that this connection is no longer valid.
connectionValid = false;
+ cancelFirstRequestTimer();
final LocalizableMessage cancelMessage;
if (message != null) {
@@ -943,6 +949,39 @@
}
/**
+ * Indicates whether a complete request has arrived on this connection.
+ *
+ * @return {@code true} once the first complete request has arrived, {@code false} before
+ */
+ boolean hasReceivedRequest() {
+ return requestReceived;
+ }
+
+ /**
+ * Sets the task which closes this connection unless its first request arrives in time. The task is cancelled once
+ * the first request arrives or the connection ends, so that neither it nor this connection waits out the limit.
+ *
+ * @param timer
+ * the scheduled task
+ */
+ void setFirstRequestTimer(final Future<?> timer) {
+ firstRequestTimer = timer;
+ // The connection may have ended before the task was set. Its first request cannot have come before: the task
+ // is set while the connection is accepted, and its requests are handled only once it is.
+ if (!connectionValid) {
+ cancelFirstRequestTimer();
+ }
+ }
+
+ private void cancelFirstRequestTimer() {
+ final Future<?> timer = firstRequestTimer;
+ if (timer != null) {
+ firstRequestTimer = null;
+ timer.cancel(false);
+ }
+ }
+
+ /**
* Returns the total number of operations initiated on this connection.
*
* @return the total number of operations on this connection
@@ -966,6 +1005,10 @@
*/
@Override
public Stream<Response> handle(final QueueingStrategy queueingStrategy, final LdapRequestEnvelope message) {
+ if (!requestReceived) {
+ requestReceived = true;
+ cancelFirstRequestTimer();
+ }
return streamFromPublisher(
new BlockingBackpressureSubscription(connectionHandler.getMaxBlockedWriteTimeLimit(),
Flowable.create(new FlowableOnSubscribe<Response>() {
--
Gitblit v1.10.0