File tomcat-9.0.36-CVE-2023-45648.patch of Package tomcat.30728
From 59583245639d8c42ae0009f4a4a70464d3ea70a0 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Thu, 5 Oct 2023 20:51:48 +0100
Subject: [PATCH] Align processing of trailer headers with standard processing
---
.../apache/coyote/http11/Http11InputBuffer.java | 8 +++++++-
.../coyote/http11/filters/ChunkedInputFilter.java | 15 ++++++++++++++-
.../coyote/http11/filters/LocalStrings.properties | 2 ++
webapps/docs/changelog.xml | 3 +++
4 files changed, 26 insertions(+), 2 deletions(-)
Index: apache-tomcat-9.0.36-src/java/org/apache/coyote/http11/Http11InputBuffer.java
===================================================================
--- apache-tomcat-9.0.36-src.orig/java/org/apache/coyote/http11/Http11InputBuffer.java
+++ apache-tomcat-9.0.36-src/java/org/apache/coyote/http11/Http11InputBuffer.java
@@ -802,6 +802,12 @@ public class Http11InputBuffer implement
*/
private HeaderParseStatus parseHeader() throws IOException {
+ /*
+ * Implementation note: Any changes to this method probably need to be echoed in
+ * ChunkedInputFilter.parseHeader(). Why not use a common implementation? In short, this code uses non-blocking
+ * reads whereas ChunkedInputFilter using blocking reads. The code is just different enough that a common
+ * implementation wasn't viewed as practical.
+ */
while (headerParsePos == HeaderParsePosition.HEADER_START) {
// Read new bytes if needed
@@ -934,7 +940,7 @@ public class Http11InputBuffer implement
} else if (prevChr == Constants.CR) {
// Invalid value - also need to delete header
return skipLine(true);
- } else if (chr != Constants.HT && HttpParser.isControl(chr)) {
+ } else if (HttpParser.isControl(chr) && chr != Constants.HT) {
// Invalid value - also need to delete header
return skipLine(true);
} else if (chr == Constants.SP || chr == Constants.HT) {
Index: apache-tomcat-9.0.36-src/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
===================================================================
--- apache-tomcat-9.0.36-src.orig/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
+++ apache-tomcat-9.0.36-src/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
@@ -31,6 +31,7 @@ import org.apache.coyote.http11.Constant
import org.apache.coyote.http11.InputFilter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.HexUtils;
+import org.apache.tomcat.util.http.parser.HttpParser;
import org.apache.tomcat.util.net.ApplicationBufferHandler;
import org.apache.tomcat.util.res.StringManager;
@@ -435,6 +436,13 @@ public class ChunkedInputFilter implemen
private boolean parseHeader() throws IOException {
+ /*
+ * Implementation note: Any changes to this method probably need to be echoed in
+ * Http11InputBuffer.parseHeader(). Why not use a common implementation? In short, this code uses blocking
+ * reads whereas Http11InputBuffer using non-blocking reads. The code is just different enough that a common
+ * implementation wasn't viewed as practical.
+ */
+
Map<String,String> headers = request.getTrailerFields();
byte chr = 0;
@@ -481,6 +489,9 @@ public class ChunkedInputFilter implemen
if (chr == Constants.COLON) {
colon = true;
+ } else if (!HttpParser.isToken(chr)) {
+ // Non-token characters are illegal in header names
+ throw new IOException(sm.getString("chunkedInputFilter.invalidTrailerHeaderName"));
} else {
trailingHeaders.append(chr);
}
@@ -542,7 +553,9 @@ public class ChunkedInputFilter implemen
if (chr == Constants.CR || chr == Constants.LF) {
parseCRLF(true);
eol = true;
- } else if (chr == Constants.SP) {
+ } else if (HttpParser.isControl(chr) && chr != Constants.HT) {
+ throw new IOException(sm.getString("chunkedInputFilter.invalidTrailerHeaderValue"));
+ } else if (chr == Constants.SP || chr == Constants.HT) {
trailingHeaders.append(chr);
} else {
trailingHeaders.append(chr);
Index: apache-tomcat-9.0.36-src/java/org/apache/coyote/http11/filters/LocalStrings.properties
===================================================================
--- apache-tomcat-9.0.36-src.orig/java/org/apache/coyote/http11/filters/LocalStrings.properties
+++ apache-tomcat-9.0.36-src/java/org/apache/coyote/http11/filters/LocalStrings.properties
@@ -21,6 +21,8 @@ chunkedInputFilter.invalidCrlfCRCR=Inval
chunkedInputFilter.invalidCrlfNoCR=Invalid end of line sequence (No CR before LF)
chunkedInputFilter.invalidCrlfNoData=Invalid end of line sequence (no data available to read)
chunkedInputFilter.invalidHeader=Invalid chunk header
+chunkedInputFilter.invalidTrailerHeaderName=Invalid trailer header name (non-token character in name)
+chunkedInputFilter.invalidTrailerHeaderValue=Invalid trailer header value (control character in value)
chunkedInputFilter.maxExtension=maxExtensionSize exceeded
chunkedInputFilter.maxTrailer=maxTrailerSize exceeded
Index: apache-tomcat-9.0.36-src/webapps/docs/changelog.xml
===================================================================
--- apache-tomcat-9.0.36-src.orig/webapps/docs/changelog.xml
+++ apache-tomcat-9.0.36-src/webapps/docs/changelog.xml
@@ -147,6 +147,9 @@
malformed <code>content-length</code> header should always be rejected
with a 400 response. (markt)
</fix>
+ <fix>
+ Align validation of HTTP trailer fields with standard fields. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Jasper">