1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
|
From: Markus Koschany <apo@debian.org>
Date: Sun, 5 Nov 2023 22:29:58 +0100
Subject: CVE-2023-44487
Bug-Debian: https://bugs.debian.org/1054234
Origin: https://github.com/netty/netty/commit/58f75f665aa81a8cbcf6ffa74820042a285c5e61
---
.../AbstractHttp2ConnectionHandlerBuilder.java | 24 ++++++++-
.../codec/http2/Http2FrameCodecBuilder.java | 6 +++
.../codec/http2/Http2MaxRstFrameDecoder.java | 58 ++++++++++++++++++++++
.../codec/http2/Http2MaxRstFrameListener.java | 58 ++++++++++++++++++++++
.../codec/http2/Http2MultiplexCodecBuilder.java | 6 +++
5 files changed, 150 insertions(+), 2 deletions(-)
create mode 100644 codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MaxRstFrameDecoder.java
create mode 100644 codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MaxRstFrameListener.java
diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractHttp2ConnectionHandlerBuilder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractHttp2ConnectionHandlerBuilder.java
index f262b11..a904310 100644
--- a/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractHttp2ConnectionHandlerBuilder.java
+++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractHttp2ConnectionHandlerBuilder.java
@@ -109,6 +109,8 @@ public abstract class AbstractHttp2ConnectionHandlerBuilder<T extends Http2Conne
private boolean autoAckPingFrame = true;
private int maxQueuedControlFrames = Http2CodecUtil.DEFAULT_MAX_QUEUED_CONTROL_FRAMES;
private int maxConsecutiveEmptyFrames = 2;
+ private int maxRstFramesPerWindow = 200;
+ private int secondsPerWindow = 30;
/**
* Sets the {@link Http2Settings} to use for the initial connection settings exchange.
@@ -410,7 +412,7 @@ public abstract class AbstractHttp2ConnectionHandlerBuilder<T extends Http2Conne
/**
* Returns the maximum number of consecutive empty DATA frames (without end_of_stream flag) that are allowed before
- * the connection is closed. This allows to protected against the remote peer flooding us with such frames and
+ * the connection is closed. This allows to protect against the remote peer flooding us with such frames and
* so use up a lot of CPU. There is no valid use-case for empty DATA frames without end_of_stream flag.
*
* {@code 0} means no protection is in place.
@@ -421,7 +423,7 @@ public abstract class AbstractHttp2ConnectionHandlerBuilder<T extends Http2Conne
/**
* Sets the maximum number of consecutive empty DATA frames (without end_of_stream flag) that are allowed before
- * the connection is closed. This allows to protected against the remote peer flooding us with such frames and
+ * the connection is closed. This allows to protect against the remote peer flooding us with such frames and
* so use up a lot of CPU. There is no valid use-case for empty DATA frames without end_of_stream flag.
*
* {@code 0} means no protection should be applied.
@@ -433,6 +435,21 @@ public abstract class AbstractHttp2ConnectionHandlerBuilder<T extends Http2Conne
return self();
}
+ /**
+ * Sets the maximum number RST frames that are allowed per window before
+ * the connection is closed. This allows to protect against the remote peer flooding us with such frames and
+ * so use up a lot of CPU.
+ *
+ * {@code 0} for any of the parameters means no protection should be applied.
+ */
+ protected B decoderEnforceMaxRstFramesPerWindow(int maxRstFramesPerWindow, int secondsPerWindow) {
+ enforceNonCodecConstraints("decoderEnforceMaxRstFramesPerWindow");
+ this.maxRstFramesPerWindow = checkPositiveOrZero(
+ maxRstFramesPerWindow, "maxRstFramesPerWindow");
+ this.secondsPerWindow = checkPositiveOrZero(secondsPerWindow, "secondsPerWindow");
+ return self();
+ }
+
/**
* Determine if settings frame should automatically be acknowledged and applied.
* @return this.
@@ -545,6 +562,9 @@ public abstract class AbstractHttp2ConnectionHandlerBuilder<T extends Http2Conne
if (maxConsecutiveEmptyDataFrames > 0) {
decoder = new Http2EmptyDataFrameConnectionDecoder(decoder, maxConsecutiveEmptyDataFrames);
}
+ if (maxRstFramesPerWindow > 0 && secondsPerWindow > 0) {
+ decoder = new Http2MaxRstFrameDecoder(decoder, maxRstFramesPerWindow, secondsPerWindow);
+ }
final T handler;
try {
// Call the abstract build method
diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodecBuilder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodecBuilder.java
index fad31b2..241c9c5 100644
--- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodecBuilder.java
+++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodecBuilder.java
@@ -177,6 +177,12 @@ public class Http2FrameCodecBuilder extends
return super.decoderEnforceMaxConsecutiveEmptyDataFrames(maxConsecutiveEmptyFrames);
}
+ @Override
+ public Http2FrameCodecBuilder decoderEnforceMaxRstFramesPerWindow(
+ int maxConsecutiveEmptyFrames, int secondsPerWindow) {
+ return super.decoderEnforceMaxRstFramesPerWindow(maxConsecutiveEmptyFrames, secondsPerWindow);
+ }
+
/**
* Build a {@link Http2FrameCodec} object.
*/
diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MaxRstFrameDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MaxRstFrameDecoder.java
new file mode 100644
index 0000000..6ac6660
--- /dev/null
+++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MaxRstFrameDecoder.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.http2;
+
+import static io.netty.util.internal.ObjectUtil.checkPositive;
+
+
+/**
+ * Enforce a limit on the maximum number of RST frames that are allowed per a window
+ * before the connection will be closed with a GO_AWAY frame.
+ */
+final class Http2MaxRstFrameDecoder extends DecoratingHttp2ConnectionDecoder {
+ private final int maxRstFramesPerWindow;
+ private final int secondsPerWindow;
+
+ Http2MaxRstFrameDecoder(Http2ConnectionDecoder delegate, int maxRstFramesPerWindow, int secondsPerWindow) {
+ super(delegate);
+ this.maxRstFramesPerWindow = checkPositive(maxRstFramesPerWindow, "maxRstFramesPerWindow");
+ this.secondsPerWindow = checkPositive(secondsPerWindow, "secondsPerWindow");
+ }
+
+ @Override
+ public void frameListener(Http2FrameListener listener) {
+ if (listener != null) {
+ super.frameListener(new Http2MaxRstFrameListener(listener, maxRstFramesPerWindow, secondsPerWindow));
+ } else {
+ super.frameListener(null);
+ }
+ }
+
+ @Override
+ public Http2FrameListener frameListener() {
+ Http2FrameListener frameListener = frameListener0();
+ // Unwrap the original Http2FrameListener as we add this decoder under the hood.
+ if (frameListener instanceof Http2MaxRstFrameListener) {
+ return ((Http2MaxRstFrameListener) frameListener).listener;
+ }
+ return frameListener;
+ }
+
+ // Package-private for testing
+ Http2FrameListener frameListener0() {
+ return super.frameListener();
+ }
+}
diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MaxRstFrameListener.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MaxRstFrameListener.java
new file mode 100644
index 0000000..4603686
--- /dev/null
+++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MaxRstFrameListener.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.http2;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.util.internal.logging.InternalLogger;
+import io.netty.util.internal.logging.InternalLoggerFactory;
+
+import java.util.concurrent.TimeUnit;
+
+
+final class Http2MaxRstFrameListener extends Http2FrameListenerDecorator {
+ private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2MaxRstFrameListener.class);
+
+ private final long nanosPerWindow;
+ private final int maxRstFramesPerWindow;
+ private long lastRstFrameNano = System.nanoTime();
+ private int receivedRstInWindow;
+
+ Http2MaxRstFrameListener(Http2FrameListener listener, int maxRstFramesPerWindow, int secondsPerWindow) {
+ super(listener);
+ this.maxRstFramesPerWindow = maxRstFramesPerWindow;
+ this.nanosPerWindow = TimeUnit.SECONDS.toNanos(secondsPerWindow);
+ }
+
+ @Override
+ public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception {
+ long currentNano = System.nanoTime();
+ if (currentNano - lastRstFrameNano >= nanosPerWindow) {
+ lastRstFrameNano = currentNano;
+ receivedRstInWindow = 1;
+ } else {
+ receivedRstInWindow++;
+ if (receivedRstInWindow > maxRstFramesPerWindow) {
+ Http2Exception exception = Http2Exception.connectionError(Http2Error.ENHANCE_YOUR_CALM,
+ "Maximum number of RST frames reached");
+ logger.debug("{} Maximum number {} of RST frames reached within {} seconds, " +
+ "closing connection with {} error", ctx.channel(), maxRstFramesPerWindow,
+ TimeUnit.NANOSECONDS.toSeconds(nanosPerWindow), exception.error(), exception);
+ throw exception;
+ }
+ }
+ super.onRstStreamRead(ctx, streamId, errorCode);
+ }
+}
diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilder.java
index 5d0829e..a3c0bed 100644
--- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilder.java
+++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilder.java
@@ -206,6 +206,12 @@ public class Http2MultiplexCodecBuilder
return super.decoderEnforceMaxConsecutiveEmptyDataFrames(maxConsecutiveEmptyFrames);
}
+ @Override
+ public Http2MultiplexCodecBuilder decoderEnforceMaxRstFramesPerWindow(
+ int maxConsecutiveEmptyFrames, int secondsPerWindow) {
+ return super.decoderEnforceMaxRstFramesPerWindow(maxConsecutiveEmptyFrames, secondsPerWindow);
+ }
+
@Override
public Http2MultiplexCodec build() {
Http2FrameWriter frameWriter = this.frameWriter;
|