Compare commits

...

10 Commits

Author SHA1 Message Date
openeuler-ci-bot
665dcc13f8
!123 [sync] PR-118: Fix CVE-2024-21733,CVE-2023-24998,CVE-2023-28709,CVE-2023-42795
From: @openeuler-sync-bot 
Reviewed-by: @wk333 
Signed-off-by: @wk333
2024-01-23 06:51:44 +00:00
wk333
f1986b1f50 Fix CVE-2024-21733,CVE-2023-24998,CVE-2023-28709,CVE-2023-42795
(cherry picked from commit 5342785c692e11ff9c1d5532a41e7ed4279738e3)
2024-01-23 14:40:36 +08:00
openeuler-ci-bot
443db40922
!109 [sync] PR-105: Fix CVE-2023-45648
From: @openeuler-sync-bot 
Reviewed-by: @caodongxia 
Signed-off-by: @caodongxia
2023-10-20 06:52:46 +00:00
wk333
3b62a74c8f Fix CVE-2023-45648
(cherry picked from commit 5498d082575e35e9a093247f836e35d7404a50f0)
2023-10-20 11:20:21 +08:00
openeuler-ci-bot
bed19cdd54
!102 [sync] PR-97: 修复 CVE-2023-41080 和解决软连接指向为空
From: @openeuler-sync-bot 
Reviewed-by: @caodongxia 
Signed-off-by: @caodongxia
2023-09-08 01:16:29 +00:00
wk333
83ef3afbd3 Fix CVE-2023-41080 and path error
(cherry picked from commit f97d0f9c3c3264550aba593e17204beb67719a3a)
2023-09-07 15:46:41 +08:00
openeuler-ci-bot
3d18fb8a4f
!93 [sync] PR-90: Fix CVE-2023-28708
From: @openeuler-sync-bot 
Reviewed-by: @caodongxia 
Signed-off-by: @caodongxia
2023-04-18 02:49:29 +00:00
wk333
b63424bbab Fix CVE-2023-28708
(cherry picked from commit 1549ee4b2a7582006038148dc089e166fc06d499)
2023-04-17 20:22:25 +08:00
openeuler-ci-bot
740390ce3e
!79 fix CVE-2022-42252
From: @xu_lei_123 
Reviewed-by: @caodongxia 
Signed-off-by: @caodongxia
2022-12-28 07:48:18 +00:00
xu_lei_123
9ea4787224 fix CVE-2022-42252 2022-12-26 10:42:41 +08:00
10 changed files with 1861 additions and 4 deletions

127
CVE-2022-42252.patch Normal file
View File

@ -0,0 +1,127 @@
diff -upr tomcat-9.0.10_back/java/org/apache/coyote/http11/Http11InputBuffer.java tomcat-9.0.10/java/org/apache/coyote/http11/Http11InputBuffer.java
--- tomcat-9.0.10_back/java/org/apache/coyote/http11/Http11InputBuffer.java 2022-12-14 10:39:12.917000000 +0800
+++ tomcat-9.0.10/java/org/apache/coyote/http11/Http11InputBuffer.java 2022-12-14 10:48:31.180863424 +0800
@@ -821,7 +821,7 @@ public class Http11InputBuffer implement
headerData.lastSignificantChar = pos;
byteBuffer.position(byteBuffer.position() - 1);
// skipLine() will handle the error
- return skipLine();
+ return skipLine(false);
}
// chr is next byte of header name. Convert to lowercase.
@@ -832,7 +832,7 @@ public class Http11InputBuffer implement
// Skip the line and ignore the header
if (headerParsePos == HeaderParsePosition.HEADER_SKIPLINE) {
- return skipLine();
+ return skipLine(false);
}
//
@@ -883,15 +883,11 @@ public class Http11InputBuffer implement
} else if (prevChr == Constants.CR && chr == Constants.LF) {
eol = true;
} else if (prevChr == Constants.CR) {
- // Invalid value
- // Delete the header (it will be the most recent one)
- headers.removeHeader(headers.size() - 1);
- return skipLine();
+ // Invalid value - also need to delete header
+ return skipLine(true);
} else if (chr != Constants.HT && HttpParser.isControl(chr)) {
- // Invalid value
- // Delete the header (it will be the most recent one)
- headers.removeHeader(headers.size() - 1);
- return skipLine();
+ // Invalid value - also need to delete header
+ return skipLine(true);
} else if (chr == Constants.SP || chr == Constants.HT) {
byteBuffer.put(headerData.realPos, chr);
headerData.realPos++;
@@ -939,7 +935,27 @@ public class Http11InputBuffer implement
}
- private HeaderParseStatus skipLine() throws IOException {
+ private HeaderParseStatus skipLine(boolean deleteHeader) throws IOException {
+ boolean rejectThisHeader = rejectIllegalHeader;
+ // Check if rejectIllegalHeader is disabled and needs to be overridden
+ // for this header. The header name is required to determine if this
+ // override is required. The header name is only available once the
+ // header has been created. If the header has been created then
+ // deleteHeader will be true.
+ if (!rejectThisHeader && deleteHeader) {
+ if (headers.getName(headers.size() - 1).equalsIgnoreCase("content-length")) {
+ // Malformed content-length headers must always be rejected
+ // RFC 9112, section 6.3, bullet 5.
+ rejectThisHeader = true;
+ } else {
+ // Only need to delete the header if the request isn't going to
+ // be rejected (it will be the most recent one)
+ headers.removeHeader(headers.size() - 1);
+ }
+ }
+
+ // Parse the rest of the invalid header so we can construct a useful
+ // exception and/or debug message.
headerParsePos = HeaderParsePosition.HEADER_SKIPLINE;
boolean eol = false;
@@ -967,12 +983,12 @@ public class Http11InputBuffer implement
headerData.lastSignificantChar = pos;
}
}
- if (rejectIllegalHeader || log.isDebugEnabled()) {
+ if (rejectThisHeader || log.isDebugEnabled()) {
String message = sm.getString("iib.invalidheader",
new String(byteBuffer.array(), headerData.start,
headerData.lastSignificantChar - headerData.start + 1,
StandardCharsets.ISO_8859_1));
- if (rejectIllegalHeader) {
+ if (rejectThisHeader) {
throw new IllegalArgumentException(message);
}
log.debug(message);
diff -upr tomcat-9.0.10_back/test/org/apache/coyote/http11/TestHttp11InputBuffer.java tomcat-9.0.10/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
--- tomcat-9.0.10_back/test/org/apache/coyote/http11/TestHttp11InputBuffer.java 2022-12-14 10:39:12.971000000 +0800
+++ tomcat-9.0.10/test/org/apache/coyote/http11/TestHttp11InputBuffer.java 2022-12-14 10:51:16.845501479 +0800
@@ -643,6 +643,38 @@ public class TestHttp11InputBuffer exten
Assert.assertTrue(client.isResponseBodyOK());
}
+ @Test
+ public void testInvalidContentLength01() {
+ doTestInvalidContentLength(false);
+ }
+
+
+ @Test
+ public void testInvalidContentLength02() {
+ doTestInvalidContentLength(true);
+ }
+
+
+ private void doTestInvalidContentLength(boolean rejectIllegalHeader) {
+ getTomcatInstance().getConnector().setProperty("rejectIllegalHeader", Boolean.toString(rejectIllegalHeader));
+
+ String[] request = new String[1];
+ request[0] =
+ "POST /test HTTP/1.1" + CRLF +
+ "Host: localhost:8080" + CRLF +
+ "Content-Length: 12\u000734" + CRLF +
+ "Connection: close" + CRLF +
+ CRLF;
+
+ InvalidClient client = new InvalidClient(request);
+
+ client.doRequest();
+ Assert.assertTrue(client.getResponseLine(), client.isResponse400());
+ Assert.assertTrue(client.isResponseBodyOK());
+ }
+
+
+
/**
* Bug 48839 test client.

253
CVE-2023-24998.patch Normal file
View File

@ -0,0 +1,253 @@
From cf77cc545de0488fb89e24294151504a7432df74 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 13 Dec 2022 17:55:34 +0000
Subject: [PATCH] Update packaged renamed fork of Commons File Upload
Origin: https://github.com/apache/tomcat/commit/cf77cc545de0488fb89e24294151504a7432df74
---
.../apache/catalina/connector/Request.java | 12 ++++-
.../apache/tomcat/util/http/Parameters.java | 4 ++
.../util/http/fileupload/FileUploadBase.java | 29 +++++++++++
.../impl/FileCountLimitExceededException.java | 50 +++++++++++++++++++
webapps/docs/changelog.xml | 8 +++
webapps/docs/config/ajp.xml | 15 +++---
webapps/docs/config/http.xml | 15 +++---
7 files changed, 119 insertions(+), 14 deletions(-)
create mode 100644 java/org/apache/tomcat/util/http/fileupload/impl/FileCountLimitExceededException.java
diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java
index 889d5e7..87ab732 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -2769,8 +2769,9 @@ public class Request implements HttpServletRequest {
}
}
- Parameters parameters = coyoteRequest.getParameters();
- parameters.setLimit(getConnector().getMaxParameterCount());
+ int maxParameterCount = getConnector().getMaxParameterCount();
+ Parameters parameters = coyoteRequest.getParameters();
+ parameters.setLimit(maxParameterCount);
boolean success = false;
try {
@@ -2814,6 +2815,13 @@ public class Request implements HttpServletRequest {
upload.setFileItemFactory(factory);
upload.setFileSizeMax(mce.getMaxFileSize());
upload.setSizeMax(mce.getMaxRequestSize());
+ if (maxParameterCount > -1) {
+ // There is a limit. The limit for parts needs to be reduced by
+ // the number of parameters we have already parsed.
+ // Must be under the limit else parsing parameters would have
+ // triggered an exception.
+ upload.setFileCountMax(maxParameterCount - parameters.size());
+ }
parts = new ArrayList<>();
try {
diff --git a/java/org/apache/tomcat/util/http/Parameters.java b/java/org/apache/tomcat/util/http/Parameters.java
index 5bd9ba7..08c6ffd 100644
--- a/java/org/apache/tomcat/util/http/Parameters.java
+++ b/java/org/apache/tomcat/util/http/Parameters.java
@@ -124,6 +124,10 @@ public final class Parameters {
}
}
+ public int size() {
+ return parameterCount;
+ }
+
public void recycle() {
parameterCount = 0;
diff --git a/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java b/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
index eb5a487..5506754 100644
--- a/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
+++ b/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
@@ -26,6 +26,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
+import org.apache.tomcat.util.http.fileupload.impl.FileCountLimitExceededException;
import org.apache.tomcat.util.http.fileupload.MultipartStream.ItemInputStream;
import org.apache.tomcat.util.http.fileupload.util.Closeable;
import org.apache.tomcat.util.http.fileupload.util.FileItemHeadersImpl;
@@ -131,6 +132,12 @@ public abstract class FileUploadBase {
* to {@link #sizeMax}. A value of -1 indicates no maximum.
*/
private long fileSizeMax = -1;
+
+ /**
+ * The maximum permitted number of files that may be uploaded in a single
+ * request. A value of -1 indicates no maximum.
+ */
+ private long fileCountMax = -1;
/**
* The content encoding to use when reading part headers.
@@ -208,6 +215,24 @@ public abstract class FileUploadBase {
this.fileSizeMax = fileSizeMax;
}
+ /**
+ * Returns the maximum number of files allowed in a single request.
+ *
+ * @return The maximum number of files allowed in a single request.
+ */
+ public long getFileCountMax() {
+ return fileCountMax;
+ }
+
+ /**
+ * Sets the maximum number of files allowed per request/
+ *
+ * @param fileCountMax The new limit. {@code -1} means no limit.
+ */
+ public void setFileCountMax(long fileCountMax) {
+ this.fileCountMax = fileCountMax;
+ }
+
/**
* Retrieves the character encoding used when reading the headers of an
* individual part. When not specified, or <code>null</code>, the request
@@ -283,6 +308,10 @@ public abstract class FileUploadBase {
throw new NullPointerException("No FileItemFactory has been set.");
}
while (iter.hasNext()) {
+ if (items.size() == fileCountMax) {
+ // The next item will exceed the limit.
+ throw new FileCountLimitExceededException(ATTACHMENT, getFileCountMax());
+ }
final FileItemStream item = iter.next();
// Don't use getName() here to prevent an InvalidFileNameException.
final String fileName = ((FileItemIteratorImpl.FileItemStreamImpl) item).name;
diff --git a/java/org/apache/tomcat/util/http/fileupload/impl/FileCountLimitExceededException.java b/java/org/apache/tomcat/util/http/fileupload/impl/FileCountLimitExceededException.java
new file mode 100644
index 0000000..958f681
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/impl/FileCountLimitExceededException.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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
+ *
+ * http://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 org.apache.tomcat.util.http.fileupload.impl;
+
+import org.apache.tomcat.util.http.fileupload.FileUploadException;
+
+/**
+ * This exception is thrown if a request contains more files than the specified
+ * limit.
+ */
+public class FileCountLimitExceededException extends FileUploadException {
+
+ private static final long serialVersionUID = 2408766352570556046L;
+
+ private final long limit;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param message The detail message
+ * @param limit The limit that was exceeded
+ */
+ public FileCountLimitExceededException(final String message, final long limit) {
+ super(message);
+ this.limit = limit;
+ }
+
+ /**
+ * Retrieves the limit that was exceeded.
+ *
+ * @return The limit that was exceeded by the request
+ */
+ public long getLimit() {
+ return limit;
+ }
+}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 835b0d0..0268d87 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -44,6 +44,14 @@
They eventually become mixed with the numbered issues. (I.e., numbered
issues do not "pop up" wrt. others).
-->
+ <subsection name="Other">
+ <changelog>
+ <update>
+ Update the internal fork of Apache Commons FileUpload to 34eb241
+ (2023-01-03, 2.0-SNAPSHOT). (markt)
+ </update>
+ </changelog>
+ </subsection>
<section name="Tomcat 9.0.10 (markt)">
<subsection name="Catalina">
<changelog>
diff --git a/webapps/docs/config/ajp.xml b/webapps/docs/config/ajp.xml
index 622e7ca..38c5269 100644
--- a/webapps/docs/config/ajp.xml
+++ b/webapps/docs/config/ajp.xml
@@ -114,12 +114,15 @@
</attribute>
<attribute name="maxParameterCount" required="false">
- <p>The maximum number of parameter and value pairs (GET plus POST) which
- will be automatically parsed by the container. Parameter and value pairs
- beyond this limit will be ignored. A value of less than 0 means no limit.
- If not specified, a default of 10000 is used. Note that
- <code>FailedRequestFilter</code> <a href="filter.html">filter</a> can be
- used to reject requests that hit the limit.</p>
+ <p>The maximum total number of request parameters (including uploaded
+ files) obtained from the query string and, for POST requests, the request
+ body if the content type is
+ <code>application/x-www-form-urlencoded</code> or
+ <code>multipart/form-data</code>. Request parameters beyond this limit
+ will be ignored. A value of less than 0 means no limit. If not specified,
+ a default of 10000 is used. Note that <code>FailedRequestFilter</code>
+ <a href="filter.html">filter</a> can be used to reject requests that
+ exceed the limit.</p>
</attribute>
<attribute name="maxPostSize" required="false">
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index 3902c9a..52ad063 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -111,12 +111,15 @@
</attribute>
<attribute name="maxParameterCount" required="false">
- <p>The maximum number of parameter and value pairs (GET plus POST) which
- will be automatically parsed by the container. Parameter and value pairs
- beyond this limit will be ignored. A value of less than 0 means no limit.
- If not specified, a default of 10000 is used. Note that
- <code>FailedRequestFilter</code> <a href="filter.html">filter</a> can be
- used to reject requests that hit the limit.</p>
+ <p>The maximum total number of request parameters (including uploaded
+ files) obtained from the query string and, for POST requests, the request
+ body if the content type is
+ <code>application/x-www-form-urlencoded</code> or
+ <code>multipart/form-data</code>. Request parameters beyond this limit
+ will be ignored. A value of less than 0 means no limit. If not specified,
+ a default of 10000 is used. Note that <code>FailedRequestFilter</code>
+ <a href="filter.html">filter</a> can be used to reject requests that
+ exceed the limit.</p>
</attribute>
<attribute name="maxPostSize" required="false">
--
2.33.0

238
CVE-2023-28708-pre.patch Normal file
View File

@ -0,0 +1,238 @@
From 09e214c09c78a48ea96b0137555b3c2a98a1bfab Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 31 Mar 2020 14:03:17 +0100
Subject: [PATCH] Make the HTTP/2 connection ID and stream Id available to
applications
Origin: https://github.com/apache/tomcat/commit/09e214c09c78a48ea96b0137555b3c2a98a1bfab
---
java/org/apache/catalina/Globals.java | 16 ++++++++
.../apache/catalina/connector/Request.java | 27 +++++++++++++
java/org/apache/coyote/AbstractProcessor.java | 39 +++++++++++++++++++
java/org/apache/coyote/ActionCode.java | 14 ++++++-
.../apache/coyote/http2/StreamProcessor.java | 12 ++++++
webapps/docs/changelog.xml | 5 +++
webapps/docs/config/http2.xml | 9 +++++
7 files changed, 121 insertions(+), 1 deletion(-)
diff --git a/java/org/apache/catalina/Globals.java b/java/org/apache/catalina/Globals.java
index 994902b..c19d69c 100644
--- a/java/org/apache/catalina/Globals.java
+++ b/java/org/apache/catalina/Globals.java
@@ -112,6 +112,22 @@ public final class Globals {
"org.apache.catalina.NAMED";
+ /**
+ * The request attribute used to expose the current connection ID associated
+ * with the request, if any. Used with multiplexing protocols such as
+ * HTTTP/2.
+ */
+ public static final String CONNECTION_ID = "org.apache.coyote.connectionID";
+
+
+ /**
+ * The request attribute used to expose the current stream ID associated
+ * with the request, if any. Used with multiplexing protocols such as
+ * HTTTP/2.
+ */
+ public static final String STREAM_ID = "org.apache.coyote.streamID";
+
+
/**
* The servlet context attribute under which we store a flag used
* to mark this request as having been processed by the SSIServlet.
diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java
index c4cc26a..94065ef 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -40,6 +40,7 @@ import java.util.TimeZone;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
import javax.naming.NamingException;
import javax.security.auth.Subject;
@@ -3487,6 +3488,32 @@ public class Request implements HttpServletRequest {
// NO-OP
}
});
+ specialAttributes.put(Globals.CONNECTION_ID,
+ new SpecialAttributeAdapter() {
+ @Override
+ public Object get(Request request, String name) {
+ AtomicReference<Object> result = new AtomicReference<>();
+ request.getCoyoteRequest().action(ActionCode.CONNECTION_ID, result);
+ return result.get();
+ }
+ @Override
+ public void set(Request request, String name, Object value) {
+ // NO-OP
+ }
+ });
+ specialAttributes.put(Globals.STREAM_ID,
+ new SpecialAttributeAdapter() {
+ @Override
+ public Object get(Request request, String name) {
+ AtomicReference<Object> result = new AtomicReference<>();
+ request.getCoyoteRequest().action(ActionCode.STREAM_ID, result);
+ return result.get();
+ }
+ @Override
+ public void set(Request request, String name, Object value) {
+ // NO-OP
+ }
+ });
for (SimpleDateFormat sdf : formatsTemplate) {
sdf.setTimeZone(GMT_ZONE);
diff --git a/java/org/apache/coyote/AbstractProcessor.java b/java/org/apache/coyote/AbstractProcessor.java
index 5be2cb8..b351cb7 100644
--- a/java/org/apache/coyote/AbstractProcessor.java
+++ b/java/org/apache/coyote/AbstractProcessor.java
@@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.RequestDispatcher;
@@ -589,6 +590,20 @@ public abstract class AbstractProcessor extends AbstractProcessorLight implement
result.set(isTrailerFieldsSupported());
break;
}
+
+ // Identifiers associated with multiplexing protocols like HTTP/2
+ case CONNECTION_ID: {
+ @SuppressWarnings("unchecked")
+ AtomicReference<Object> result = (AtomicReference<Object>) param;
+ result.set(getConnectionID());
+ break;
+ }
+ case STREAM_ID: {
+ @SuppressWarnings("unchecked")
+ AtomicReference<Object> result = (AtomicReference<Object>) param;
+ result.set(getStreamID());
+ break;
+ }
}
}
@@ -889,6 +904,30 @@ public abstract class AbstractProcessor extends AbstractProcessorLight implement
}
+ /**
+ * Protocols that support multiplexing (e.g. HTTP/2) should override this
+ * method and return the appropriate ID.
+ *
+ * @return The stream ID associated with this request or {@code null} if a
+ * multiplexing protocol is not being used
+ */
+ protected Object getConnectionID() {
+ return null;
+ }
+
+
+ /**
+ * Protocols that support multiplexing (e.g. HTTP/2) should override this
+ * method and return the appropriate ID.
+ *
+ * @return The stream ID associated with this request or {@code null} if a
+ * multiplexing protocol is not being used
+ */
+ protected Object getStreamID() {
+ return null;
+ }
+
+
/**
* Flush any pending writes. Used during non-blocking writes to flush any
* remaining data from a previous incomplete write.
diff --git a/java/org/apache/coyote/ActionCode.java b/java/org/apache/coyote/ActionCode.java
index 3ff4c21..5c5af4f 100644
--- a/java/org/apache/coyote/ActionCode.java
+++ b/java/org/apache/coyote/ActionCode.java
@@ -265,5 +265,17 @@ public enum ActionCode {
* once an HTTP/1.1 response has been committed, it will no longer support
* trailer fields.
*/
- IS_TRAILER_FIELDS_SUPPORTED
+ IS_TRAILER_FIELDS_SUPPORTED,
+
+ /**
+ * Obtain the connection identifier for the request. Used with multiplexing
+ * protocols such as HTTP/2.
+ */
+ CONNECTION_ID,
+
+ /**
+ * Obtain the stream identifier for the request. Used with multiplexing
+ * protocols such as HTTP/2.
+ */
+ STREAM_ID
}
diff --git a/java/org/apache/coyote/http2/StreamProcessor.java b/java/org/apache/coyote/http2/StreamProcessor.java
index d9c1c82..fd833ec 100644
--- a/java/org/apache/coyote/http2/StreamProcessor.java
+++ b/java/org/apache/coyote/http2/StreamProcessor.java
@@ -300,6 +300,18 @@ class StreamProcessor extends AbstractProcessor {
}
+ @Override
+ protected Object getConnectionID() {
+ return stream.getConnectionId();
+ }
+
+
+ @Override
+ protected Object getStreamID() {
+ return stream.getIdentifier().toString();
+ }
+
+
@Override
public final void recycle() {
// StreamProcessor instances are not re-used.
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index a97e15d..8837628 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -247,6 +247,11 @@
Ensure that the HTTP/1.1 processor is correctly recycled when a direct
connection to h2c is made. (markt)
</fix>
+ <add>
+ Expose the HTTP/2 connection ID and stream ID to applications via the
+ request attributes <code>org.apache.coyote.connectionID</code> and
+ <code>org.apache.coyote.streamID</code> respectively. (markt)
+ </add>
</changelog>
</subsection>
<subsection name="Jasper">
diff --git a/webapps/docs/config/http2.xml b/webapps/docs/config/http2.xml
index 83b8acd..145a184 100644
--- a/webapps/docs/config/http2.xml
+++ b/webapps/docs/config/http2.xml
@@ -44,6 +44,15 @@
the Servlet API is fundamentally blocking, each HTTP/2 stream requires a
dedicated container thread for the duration of that stream.</p>
+ <p>Requests processed using HTTP/2 will have the following additional request
+ attributes available:</p>
+ <ul>
+ <li><code>org.apache.coyote.connectionID</code> will return the HTTP/2
+ connection ID</li>
+ <li><code>org.apache.coyote.streamID</code> will return the HTTP/2 stream
+ ID</li>
+ </ul>
+
</section>
--
2.33.0

232
CVE-2023-28708.patch Normal file
View File

@ -0,0 +1,232 @@
From 3b51230764da595bb19e8d0962dd8c69ab40dfab Mon Sep 17 00:00:00 2001
From: lihan <lihan@apache.org>
Date: Fri, 10 Feb 2023 10:01:27 +0800
Subject: [PATCH] Fix BZ 66471 - JSessionId secure attribute missing with
RemoteIpFilter and X-Forwarded-Proto set to https
https://bz.apache.org/bugzilla/show_bug.cgi?id=66471
Origin: https://github.com/apache/tomcat/commit/3b51230764da595bb19e8d0962dd8c69ab40dfab
---
java/org/apache/catalina/Globals.java | 8 ++
.../apache/catalina/connector/Request.java | 14 +++
.../catalina/filters/RemoteIpFilter.java | 7 +-
.../catalina/filters/TestRemoteIpFilter.java | 96 ++++++++++++++-----
webapps/docs/changelog.xml | 5 +
5 files changed, 101 insertions(+), 29 deletions(-)
diff --git a/java/org/apache/catalina/Globals.java b/java/org/apache/catalina/Globals.java
index c19d69c..2e9a377 100644
--- a/java/org/apache/catalina/Globals.java
+++ b/java/org/apache/catalina/Globals.java
@@ -160,6 +160,14 @@ public final class Globals {
org.apache.coyote.Constants.SENDFILE_SUPPORTED_ATTR;
+ /**
+ * The request attribute that is set to the value of {@code Boolean.TRUE}
+ * if {@link org.apache.catalina.filters.RemoteIpFilter} determines
+ * that this request was submitted via a secure channel.
+ */
+ public static final String REMOTE_IP_FILTER_SECURE = "org.apache.catalina.filters.RemoteIpFilter.secure";
+
+
/**
* The request attribute that can be used by a servlet to pass
* to the connector the name of the file that is to be served
diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java
index 94065ef..889d5e7 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -3501,6 +3501,20 @@ public class Request implements HttpServletRequest {
// NO-OP
}
});
+ specialAttributes.put(Globals.REMOTE_IP_FILTER_SECURE,
+ new SpecialAttributeAdapter() {
+ @Override
+ public Object get(Request request, String name) {
+ return Boolean.valueOf(request.isSecure());
+ }
+
+ @Override
+ public void set(Request request, String name, Object value) {
+ if (value instanceof Boolean) {
+ request.setSecure(((Boolean) value).booleanValue());
+ }
+ }
+ });
specialAttributes.put(Globals.STREAM_ID,
new SpecialAttributeAdapter() {
@Override
diff --git a/java/org/apache/catalina/filters/RemoteIpFilter.java b/java/org/apache/catalina/filters/RemoteIpFilter.java
index b9f6655..e978cfb 100644
--- a/java/org/apache/catalina/filters/RemoteIpFilter.java
+++ b/java/org/apache/catalina/filters/RemoteIpFilter.java
@@ -577,11 +577,6 @@ public class RemoteIpFilter extends GenericFilter {
return serverPort;
}
- @Override
- public boolean isSecure() {
- return secure;
- }
-
public void removeHeader(String name) {
Map.Entry<String, List<String>> header = getHeaderEntry(name);
if (header != null) {
@@ -617,7 +612,7 @@ public class RemoteIpFilter extends GenericFilter {
}
public void setSecure(boolean secure) {
- this.secure = secure;
+ super.getRequest().setAttribute(Globals.REMOTE_IP_FILTER_SECURE, Boolean.valueOf(secure));
}
public void setServerPort(int serverPort) {
diff --git a/test/org/apache/catalina/filters/TestRemoteIpFilter.java b/test/org/apache/catalina/filters/TestRemoteIpFilter.java
index f7f2093..109fdd2 100644
--- a/test/org/apache/catalina/filters/TestRemoteIpFilter.java
+++ b/test/org/apache/catalina/filters/TestRemoteIpFilter.java
@@ -81,15 +81,21 @@ public class TestRemoteIpFilter extends TomcatBaseTest {
private static final long serialVersionUID = 1L;
- private transient HttpServletRequest request;
-
- public HttpServletRequest getRequest() {
- return request;
- }
+ public String remoteAddr;
+ public String remoteHost;
+ public String scheme;
+ public String serverName;
+ public int serverPort;
+ public boolean isSecure;
@Override
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- this.request = request;
+ this.isSecure = request.isSecure();
+ this.remoteAddr = request.getRemoteAddr();
+ this.remoteHost = request.getRemoteHost();
+ this.scheme = request.getScheme();
+ this.serverName = request.getServerName();
+ this.serverPort = request.getServerPort();
PrintWriter writer = response.getWriter();
writer.println("request.remoteAddr=" + request.getRemoteAddr());
@@ -127,16 +133,6 @@ public class TestRemoteIpFilter extends TomcatBaseTest {
getCoyoteRequest().scheme().setString(scheme);
}
- @Override
- public void setAttribute(String name, Object value) {
- getCoyoteRequest().getAttributes().put(name, value);
- }
-
- @Override
- public Object getAttribute(String name) {
- return getCoyoteRequest().getAttributes().get(name);
- }
-
@Override
public String getServerName() {
return "localhost";
@@ -667,16 +663,70 @@ public class TestRemoteIpFilter extends TomcatBaseTest {
// VALIDATE
Assert.assertEquals(HttpURLConnection.HTTP_OK, httpURLConnection.getResponseCode());
- HttpServletRequest request = mockServlet.getRequest();
- Assert.assertNotNull(request);
// VALIDATE X-FORWARDED-FOR
- Assert.assertEquals(expectedRemoteAddr, request.getRemoteAddr());
- Assert.assertEquals(expectedRemoteAddr, request.getRemoteHost());
+ Assert.assertEquals(expectedRemoteAddr, mockServlet.remoteAddr);
+ Assert.assertEquals(expectedRemoteAddr, mockServlet.remoteHost);
// VALIDATE X-FORWARDED-PROTO
- Assert.assertTrue(request.isSecure());
- Assert.assertEquals("https", request.getScheme());
- Assert.assertEquals(443, request.getServerPort());
+ Assert.assertTrue(mockServlet.isSecure);
+ Assert.assertEquals("https", mockServlet.scheme);
+ Assert.assertEquals(443, mockServlet.serverPort);
+ }
+
+ @Test
+ public void testJSessionIdSecureAttributeMissing() throws Exception {
+
+ // mostly default configuration : enable "x-forwarded-proto"
+ Map<String, String> remoteIpFilterParameter = new HashMap<>();
+ remoteIpFilterParameter.put("protocolHeader", "x-forwarded-proto");
+
+ // SETUP
+ Tomcat tomcat = getTomcatInstance();
+ Context root = tomcat.addContext("", TEMP_DIR);
+
+ FilterDef filterDef = new FilterDef();
+ filterDef.getParameterMap().putAll(remoteIpFilterParameter);
+ filterDef.setFilterClass(RemoteIpFilter.class.getName());
+ filterDef.setFilterName(RemoteIpFilter.class.getName());
+
+ root.addFilterDef(filterDef);
+
+ FilterMap filterMap = new FilterMap();
+ filterMap.setFilterName(RemoteIpFilter.class.getName());
+ filterMap.addURLPatternDecoded("*");
+ root.addFilterMap(filterMap);
+
+ Bug66471Servlet bug66471Servlet = new Bug66471Servlet();
+
+ Tomcat.addServlet(root, bug66471Servlet.getClass().getName(), bug66471Servlet);
+ root.addServletMappingDecoded("/test", bug66471Servlet.getClass().getName());
+
+ getTomcatInstance().start();
+
+ Map<String, List<String>> resHeaders = new HashMap<>();
+ Map<String, List<String>> reqHeaders = new HashMap<>();
+ String expectedRemoteAddr = "my-remote-addr";
+ List<String> forwardedFor = new ArrayList<>(1);
+ forwardedFor.add(expectedRemoteAddr);
+ List<String> forwardedProto = new ArrayList<>(1);
+ forwardedProto.add("https");
+ reqHeaders.put("x-forwarded-for", forwardedFor);
+ reqHeaders.put("x-forwarded-proto", forwardedProto);
+
+ getUrl("http://localhost:" + tomcat.getConnector().getLocalPort() +
+ "/test", null, reqHeaders, resHeaders);
+ String setCookie = resHeaders.get("Set-Cookie").get(0);
+ Assert.assertTrue(setCookie.contains("Secure"));
+ Assert.assertTrue(bug66471Servlet.isSecure.booleanValue());
+ }
+ public static class Bug66471Servlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+ public Boolean isSecure;
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ req.getSession();
+ isSecure = (Boolean) req.getAttribute(Globals.REMOTE_IP_FILTER_SECURE);
+ }
}
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 8837628..15be3ed 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -478,6 +478,11 @@
Improve handling of overflow in the UTF-8 decoder with supplementary
characters. (markt)
</fix>
+ <fix>
+ <bug>66471</bug>: Fix JSessionId secure attribute missing When
+ <code>RemoteIpFilter</code> determines that this request was submitted
+ via a secure channel. (lihan)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">
--
2.33.0

35
CVE-2023-28709.patch Normal file
View File

@ -0,0 +1,35 @@
From fbd81421629afe8b8a3922d59020cde81caea861 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 11 Apr 2023 16:41:44 +0100
Subject: [PATCH] Fix parameter counting logic
Origin: https://github.com/apache/tomcat/commit/fbd81421629afe8b8a3922d59020cde81caea861
---
java/org/apache/tomcat/util/http/Parameters.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/org/apache/tomcat/util/http/Parameters.java b/java/org/apache/tomcat/util/http/Parameters.java
index 08c6ffd..a19453d 100644
--- a/java/org/apache/tomcat/util/http/Parameters.java
+++ b/java/org/apache/tomcat/util/http/Parameters.java
@@ -205,14 +205,14 @@ public final class Parameters {
return;
}
- parameterCount ++;
- if (limit > -1 && parameterCount > limit) {
+ if (limit > -1 && parameterCount >= limit) {
// Processing this parameter will push us over the limit. ISE is
// what Request.parseParts() uses for requests that are too big
setParseFailedReason(FailReason.TOO_MANY_PARAMETERS);
throw new IllegalStateException(sm.getString(
"parameters.maxCountFail", Integer.valueOf(limit)));
}
+ parameterCount ++;
ArrayList<String> values = paramHashValues.get(key);
if (values == null) {
--
2.33.0

29
CVE-2023-41080.patch Normal file
View File

@ -0,0 +1,29 @@
From 77c0ce2d169efa248b64b992e547aad549ec906b Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 22 Aug 2023 11:31:23 -0700
Subject: [PATCH] Avoid protocol relative redirects
Origin: https://github.com/apache/tomcat/commit/77c0ce2d169efa248b64b992e547aad549ec906b
---
.../apache/catalina/authenticator/FormAuthenticator.java | 6 ++++++
webapps/docs/changelog.xml | 3 +++
2 files changed, 9 insertions(+)
diff --git a/java/org/apache/catalina/authenticator/FormAuthenticator.java b/java/org/apache/catalina/authenticator/FormAuthenticator.java
index a57db51776b..d54cc62182e 100644
--- a/java/org/apache/catalina/authenticator/FormAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/FormAuthenticator.java
@@ -747,6 +747,12 @@ protected String savedRequestURL(Session session) {
sb.append('?');
sb.append(saved.getQueryString());
}
+
+ // Avoid protocol relative redirects
+ while (sb.length() > 1 && sb.charAt(1) == '/') {
+ sb.deleteCharAt(0);
+ }
+
return sb.toString();
}
}

251
CVE-2023-42795.patch Normal file
View File

@ -0,0 +1,251 @@
From 44d05d75d696ca10ce251e4e370511e38f20ae75 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Thu, 5 Oct 2023 20:52:46 +0100
Subject: [PATCH] Improve handling of failures during recycle() methods
Origin: https://github.com/apache/tomcat/commit/44d05d75d696ca10ce251e4e370511e38f20ae75
---
.../catalina/connector/LocalStrings.properties | 1 +
java/org/apache/catalina/connector/Request.java | 7 ++++---
.../catalina/core/ApplicationHttpRequest.java | 16 ++++++++++++----
.../apache/catalina/core/LocalStrings.properties | 1 +
.../catalina/core/LocalStrings_es.properties | 2 ++
.../catalina/core/LocalStrings_fr.properties | 1 +
.../catalina/core/LocalStrings_ja.properties | 1 +
.../org/apache/tomcat/util/buf/B2CConverter.java | 11 ++++++++++-
.../org/apache/tomcat/util/buf/C2BConverter.java | 15 ++++++++++++++-
.../tomcat/util/buf/LocalStrings.properties | 3 +++
10 files changed, 49 insertions(+), 9 deletions(-)
diff --git a/java/org/apache/catalina/connector/LocalStrings.properties b/java/org/apache/catalina/connector/LocalStrings.properties
index 86c6487..596805b 100644
--- a/java/org/apache/catalina/connector/LocalStrings.properties
+++ b/java/org/apache/catalina/connector/LocalStrings.properties
@@ -47,6 +47,7 @@ coyoteRequest.setAttribute.namenull=Cannot call setAttribute with a null name
coyoteRequest.attributeEvent=Exception thrown by attributes event listener
coyoteRequest.parseParameters=Exception thrown whilst processing POSTed parameters
coyoteRequest.postTooLarge=Parameters were not parsed because the size of the posted data was too big. Use the maxPostSize attribute of the connector to resolve this if the application should accept large POSTs.
+coyoteRequest.deletePartFailed=Failed to deleted temporary file used for part [{0}]
coyoteRequest.chunkedPostTooLarge=Parameters were not parsed because the size of the posted data was too big. Because this request was a chunked request, it could not be processed further. Use the maxPostSize attribute of the connector to resolve this if the application should accept large POSTs.
coyoteRequest.alreadyAuthenticated=This request has already been authenticated
coyoteRequest.authenticate.ise=Cannot call authenticate() after the response has been committed
diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java
index 889d5e7..de53769 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -465,8 +465,9 @@ public class Request implements HttpServletRequest {
for (Part part: parts) {
try {
part.delete();
- } catch (IOException ignored) {
- // ApplicationPart.delete() never throws an IOEx
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ log.warn(sm.getString("coyoteRequest.deletePartFailed", part.getName()), t);
}
}
parts = null;
@@ -518,8 +519,8 @@ public class Request implements HttpServletRequest {
asyncSupported = null;
if (asyncContext!=null) {
asyncContext.recycle();
+ asyncContext = null;
}
- asyncContext = null;
}
diff --git a/java/org/apache/catalina/core/ApplicationHttpRequest.java b/java/org/apache/catalina/core/ApplicationHttpRequest.java
index fc3a1d6..0b5b4f5 100644
--- a/java/org/apache/catalina/core/ApplicationHttpRequest.java
+++ b/java/org/apache/catalina/core/ApplicationHttpRequest.java
@@ -29,6 +29,8 @@ import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.Arrays;
+import java.util.HashMap;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
@@ -48,9 +50,12 @@ import org.apache.catalina.Session;
import org.apache.catalina.connector.RequestFacade;
import org.apache.catalina.util.ParameterMap;
import org.apache.catalina.util.RequestUtil;
+import org.apache.catalina.util.URLEncoder;
+import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.Parameters;
+import org.apache.tomcat.util.res.StringManager;
/**
@@ -70,9 +75,7 @@ import org.apache.tomcat.util.http.Parameters;
*/
class ApplicationHttpRequest extends HttpServletRequestWrapper {
-
- // ------------------------------------------------------- Static Variables
-
+ private static final StringManager sm = StringManager.getManager(ApplicationHttpRequest.class);
/**
* The set of attribute names that are special for request dispatchers.
@@ -626,7 +629,12 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
*/
public void recycle() {
if (session != null) {
- session.endAccess();
+ try {
+ session.endAccess();
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ context.getLogger().warn(sm.getString("applicationHttpRequest.sessionEndAccessFail"), t);
+ }
}
}
diff --git a/java/org/apache/catalina/core/LocalStrings.properties b/java/org/apache/catalina/core/LocalStrings.properties
index c5b55b1..0e17e3c 100644
--- a/java/org/apache/catalina/core/LocalStrings.properties
+++ b/java/org/apache/catalina/core/LocalStrings.properties
@@ -55,6 +55,7 @@ applicationFilterConfig.release=Failed to destroy the filter named [{0}] of type
applicationFilterRegistration.nullInitParam=Unable to set initialisation parameter for filter due to null name and/or value. Name [{0}], Value [{1}]
applicationFilterRegistration.nullInitParams=Unable to set initialisation parameters for filter due to null name and/or value. Name [{0}], Value [{1}]
+applicationHttpRequest.sessionEndAccessFail=Exception triggered ending access to session while recycling request
applicationPushBuilder.methodInvalid=The HTTP method for a push request must be both cacheable and safe but [{0}] is not
applicationPushBuilder.methodNotToken=HTTP methods must be tokens but [{0}] contains a non-token character
applicationPushBuilder.noCoyoteRequest=Unable to find the underlying Coyote request object (which is required to create a push request) from the request of type [{0}]
diff --git a/java/org/apache/catalina/core/LocalStrings_es.properties b/java/org/apache/catalina/core/LocalStrings_es.properties
index f138d17..e6a9ab2 100644
--- a/java/org/apache/catalina/core/LocalStrings_es.properties
+++ b/java/org/apache/catalina/core/LocalStrings_es.properties
@@ -43,6 +43,8 @@ applicationFilterConfig.jmxUnregister = Se ha completado el desregistro JMX para
applicationFilterConfig.jmxUnregisterFail = Ha fallado el desregistro JMX para el filtro del tipo [{0}] y nombre [{1}]
applicationFilterRegistration.nullInitParam = No puedo poner el par\u00E1metro de inicializaci\u00F3n para el filtro debido a un nombre nulo y/o valor. Nombre [{0}], Valor [{1}]
applicationFilterRegistration.nullInitParams = No puedo poner los par\u00E1metros de inicializaci\u00F3n para el filtro debido a un nombre nulo y/o valor. Nombre [{0}], Valor [{1}]
+applicationHttpRequest.sessionEndAccessFail=Excepción disparada acabando acceso a sesión mientras se reciclaba el requerimiento
+
applicationServletRegistration.setServletSecurity.iae = Se ha especificado restricci\u00F3n Null para el servlet [{0}] desplegado en el contexto con el nombre [{1}]
applicationServletRegistration.setServletSecurity.ise = No se pueden a\u00F1adir restricciones de seguridad al servlet [{0}] desplegado en el contexto con el nombre [{1}] ya que el contexto ya ha sido inicializado.
aprListener.aprInit = La biblioteca nativa de Apache Tomcat basada en ARP que permite un rendimiento \u00F3ptimo en entornos de desarrollo no ha sido hallada en java.library.path: [{0}]
diff --git a/java/org/apache/catalina/core/LocalStrings_fr.properties b/java/org/apache/catalina/core/LocalStrings_fr.properties
index dfc1cf7..91ead47 100644
--- a/java/org/apache/catalina/core/LocalStrings_fr.properties
+++ b/java/org/apache/catalina/core/LocalStrings_fr.properties
@@ -59,6 +59,7 @@ standardContext.startFailed=Erreur de d\u00e9marrage du contexte [{0}] suite aux
standardContext.startingContext=Exception lors du d\u00e9marrage du contexte [{0}]
standardContext.stoppingContext=Exception \u00e0 l''arr\u00eat du Context [{0}]
standardContext.resourcesStart=Erreur lors du d\u00e9marrage des ressources statiques
+applicationHttpRequest.sessionEndAccessFail=Exception lancée durant l'arrêt de l'accès à la session durant le recyclage de la requête
standardContext.urlPattern.patternWarning=ATTENTION: Le mod\u00e8le (pattern) URL [{0}] doit commencer par un ''/'' dans l''API Servlet 2.4
standardEngine.noHost=Aucune h\u00f4te (host) ne correspond au nom de serveur [{0}]
standardEngine.notHost=Le fils d''un moteur (child of an Engine) doit \u00eatre un h\u00f4te
diff --git a/java/org/apache/catalina/core/LocalStrings_ja.properties b/java/org/apache/catalina/core/LocalStrings_ja.properties
index d34d598..ae85dd4 100644
--- a/java/org/apache/catalina/core/LocalStrings_ja.properties
+++ b/java/org/apache/catalina/core/LocalStrings_ja.properties
@@ -66,6 +66,7 @@ standardEngine.notParent=\u30a8\u30f3\u30b8\u30f3\u306f\u89aa\u306e\u30b3\u30f3\
standardHost.clientAbort=\u30ea\u30e2\u30fc\u30c8\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304c\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u4e2d\u6b62\u3057\u307e\u3057\u305f, IOException: [{0}]
standardHost.invalidErrorReportValveClass=\u6307\u5b9a\u3055\u308c\u305f\u30a8\u30e9\u30fc\u30ea\u30dd\u30fc\u30c8\u30d0\u30eb\u30d6\u30af\u30e9\u30b9\u3092\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093: [{0}]
standardHost.noContext=\u3053\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u51e6\u7406\u3059\u308b\u305f\u3081\u306b\u8a2d\u5b9a\u3055\u308c\u305f\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u304c\u3042\u308a\u307e\u305b\u3093
+applicationHttpRequest.sessionEndAccessFail=リクエストの再利用中に行ったセッションへのアクセス終了処理で例外が送出されました。
standardHost.notContext=\u30db\u30b9\u30c8\u306e\u5b50\u4f9b\u306f\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
standardHost.nullName=\u30db\u30b9\u30c8\u540d\u304c\u5fc5\u8981\u3067\u3059
standardService.start.name=\u30b5\u30fc\u30d3\u30b9 [{0}] \u3092\u8d77\u52d5\u3057\u307e\u3059
diff --git a/java/org/apache/tomcat/util/buf/B2CConverter.java b/java/org/apache/tomcat/util/buf/B2CConverter.java
index f046ad7..1e3e1f4 100644
--- a/java/org/apache/tomcat/util/buf/B2CConverter.java
+++ b/java/org/apache/tomcat/util/buf/B2CConverter.java
@@ -27,6 +27,9 @@ import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Locale;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.ExceptionUtils;
import java.util.Map;
import org.apache.tomcat.util.res.StringManager;
@@ -35,6 +38,7 @@ import org.apache.tomcat.util.res.StringManager;
* NIO based character decoder.
*/
public class B2CConverter {
+ private static final Log log = LogFactory.getLog(B2CConverter.class);
private static final StringManager sm = StringManager.getManager(B2CConverter.class);
@@ -120,7 +124,12 @@ public class B2CConverter {
* Reset the decoder state.
*/
public void recycle() {
- decoder.reset();
+ try {
+ decoder.reset();
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ log.warn(sm.getString("b2cConverter.decoderResetFail", decoder.charset()), t);
+ }
leftovers.position(0);
}
diff --git a/java/org/apache/tomcat/util/buf/C2BConverter.java b/java/org/apache/tomcat/util/buf/C2BConverter.java
index e5062de..f3b4dd7 100644
--- a/java/org/apache/tomcat/util/buf/C2BConverter.java
+++ b/java/org/apache/tomcat/util/buf/C2BConverter.java
@@ -24,11 +24,19 @@ import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.res.StringManager;
+
/**
* NIO based character encoder.
*/
public final class C2BConverter {
+ private static final Log log = LogFactory.getLog(C2BConverter.class);
+ private static final StringManager sm = StringManager.getManager(C2BConverter.class);
+
private final CharsetEncoder encoder;
private ByteBuffer bb = null;
private CharBuffer cb = null;
@@ -50,7 +58,12 @@ public final class C2BConverter {
* Reset the encoder state.
*/
public void recycle() {
- encoder.reset();
+ try {
+ encoder.reset();
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ log.warn(sm.getString("c2bConverter.decoderResetFail", encoder.charset()), t);
+ }
leftovers.position(0);
}
diff --git a/java/org/apache/tomcat/util/buf/LocalStrings.properties b/java/org/apache/tomcat/util/buf/LocalStrings.properties
index c8a8d3b..574f6c2 100644
--- a/java/org/apache/tomcat/util/buf/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/buf/LocalStrings.properties
@@ -13,9 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+b2cConverter.decoderResetFail=Failed to reset instance of decoder for character set [{0}]
b2cConverter.unknownEncoding=The character encoding [{0}] is not supported
c2bConverter.recycleFailed=Failed to recycle the C2B Converter. Creating new BufferedWriter, WriteConvertor and IntermediateOutputStream.
+c2bConverter.encoderResetFail=Failed to reset instance of encoder for character set [{0}]
+
hexUtils.fromHex.oddDigits=The input must consist of an even number of hex digits
hexUtils.fromHex.nonHex=The input must consist only of hex digits
--
2.33.0

89
CVE-2023-45648.patch Normal file
View File

@ -0,0 +1,89 @@
Description: Align processing of trailer headers with standard processing
Origin: upstream, https://github.com/apache/tomcat/commit/59583245639d8c42ae0009f4a4a70464d3ea70a0
--- a/java/org/apache/coyote/http11/Http11InputBuffer.java
+++ b/java/org/apache/coyote/http11/Http11InputBuffer.java
@@ -818,6 +818,12 @@
*/
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.
+ */
//
// Check for blank line
//
byte chr = 0;
byte prevChr = 0;
while (headerParsePos == HeaderParsePosition.HEADER_START) {
// Read new bytes if needed
@@ -950,7 +956,7 @@
} 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) {
--- a/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
+++ b/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
@@ -30,6 +30,7 @@
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;
@@ -443,6 +444,13 @@
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;
@@ -489,6 +497,9 @@
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);
}
@@ -550,7 +561,9 @@
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);
--- a/java/org/apache/coyote/http11/filters/LocalStrings.properties
+++ b/java/org/apache/coyote/http11/filters/LocalStrings.properties
@@ -21,6 +21,8 @@
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

579
CVE-2024-21733.patch Normal file
View File

@ -0,0 +1,579 @@
From ce4b154e7b48f66bd98858626347747cd2514311 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Thu, 18 Feb 2021 16:41:57 +0000
Subject: [PATCH] Ensure ReadListener.onError() is fired if client drops the
connection
Origin:
https://github.com/apache/tomcat/commit/659b28c00d94e2a9049e0a8ac1e02bd4d36dd005
https://github.com/apache/tomcat/commit/f562edd3302866f34c0ca9fa97f6ff414450f1ae
https://github.com/apache/tomcat/commit/918146f9d04af67d904b47c440acaab14380521b
https://github.com/apache/tomcat/commit/504445cd2c618fb1edbfeda62e07e1c29b4d285c
https://github.com/apache/tomcat/commit/ce4b154e7b48f66bd98858626347747cd2514311
---
.../catalina/core/StandardWrapperValve.java | 2 +
.../coyote/http11/Http11InputBuffer.java | 43 +++-
.../coyote/http11/Http11OutputBuffer.java | 15 +-
.../catalina/core/TestAsyncContextImpl.java | 172 +++++++++++++++-
.../nonblocking/TestNonBlockingAPI.java | 192 ++++++++++++++++++
5 files changed, 412 insertions(+), 12 deletions(-)
diff --git a/java/org/apache/catalina/core/StandardWrapperValve.java b/java/org/apache/catalina/core/StandardWrapperValve.java
index 27f136a..89f5915 100644
--- a/java/org/apache/catalina/core/StandardWrapperValve.java
+++ b/java/org/apache/catalina/core/StandardWrapperValve.java
@@ -29,6 +29,7 @@ import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletResponse;
+import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.LifecycleException;
@@ -174,6 +175,7 @@ final class StandardWrapperValve
// Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
+ Container container = this.container;
try {
if ((servlet != null) && (filterChain != null)) {
// Swallow output if needed
diff --git a/java/org/apache/coyote/http11/Http11InputBuffer.java b/java/org/apache/coyote/http11/Http11InputBuffer.java
index 27392d4..db596b4 100644
--- a/java/org/apache/coyote/http11/Http11InputBuffer.java
+++ b/java/org/apache/coyote/http11/Http11InputBuffer.java
@@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import org.apache.coyote.CloseNowException;
import org.apache.coyote.InputBuffer;
import org.apache.coyote.Request;
import org.apache.juli.logging.Log;
@@ -382,10 +383,6 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
parsingRequestLineStart = byteBuffer.position();
parsingRequestLinePhase = 2;
- if (log.isDebugEnabled()) {
- log.debug("Received ["
- + new String(byteBuffer.array(), byteBuffer.position(), byteBuffer.remaining(), StandardCharsets.ISO_8859_1) + "]");
- }
}
if (parsingRequestLinePhase == 2) {
//
@@ -709,6 +706,16 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
*/
private boolean fill(boolean block) throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug("Before fill(): parsingHeader: [" + parsingHeader +
+ "], parsingRequestLine: [" + parsingRequestLine +
+ "], parsingRequestLinePhase: [" + parsingRequestLinePhase +
+ "], parsingRequestLineStart: [" + parsingRequestLineStart +
+ "], byteBuffer.position(): [" + byteBuffer.position() +
+ "], byteBuffer.limit(): [" + byteBuffer.limit() +
+ "], end: [" + end + "]");
+ }
+
if (parsingHeader) {
if (byteBuffer.limit() >= headerBufferSize) {
if (parsingRequestLine) {
@@ -721,13 +728,31 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
byteBuffer.limit(end).position(end);
}
+ int nRead = -1;
byteBuffer.mark();
- if (byteBuffer.position() < byteBuffer.limit()) {
- byteBuffer.position(byteBuffer.limit());
+ try {
+ if (byteBuffer.position() < byteBuffer.limit()) {
+ byteBuffer.position(byteBuffer.limit());
+ }
+ byteBuffer.limit(byteBuffer.capacity());
+ SocketWrapperBase<?> socketWrapper = this.wrapper;
+ if (socketWrapper != null) {
+ nRead = socketWrapper.read(block, byteBuffer);
+ } else {
+ throw new CloseNowException(sm.getString("iib.eof.error"));
+ }
+ } finally {
+ // Ensure that the buffer limit and position are returned to a
+ // consistent "ready for read" state if an error occurs during in
+ // the above code block.
+ byteBuffer.limit(byteBuffer.position()).reset();
}
- byteBuffer.limit(byteBuffer.capacity());
- int nRead = wrapper.read(block, byteBuffer);
- byteBuffer.limit(byteBuffer.position()).reset();
+
+ if (log.isDebugEnabled()) {
+ log.debug("Received ["
+ + new String(byteBuffer.array(), byteBuffer.position(), byteBuffer.remaining(), StandardCharsets.ISO_8859_1) + "]");
+ }
+
if (nRead > 0) {
return true;
} else if (nRead == -1) {
diff --git a/java/org/apache/coyote/http11/Http11OutputBuffer.java b/java/org/apache/coyote/http11/Http11OutputBuffer.java
index aa5ad48..c369837 100644
--- a/java/org/apache/coyote/http11/Http11OutputBuffer.java
+++ b/java/org/apache/coyote/http11/Http11OutputBuffer.java
@@ -21,6 +21,7 @@ import java.nio.ByteBuffer;
import java.util.Arrays;
import org.apache.coyote.ActionCode;
+import org.apache.coyote.CloseNowException;
import org.apache.coyote.Response;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
@@ -303,7 +304,12 @@ public class Http11OutputBuffer implements HttpOutputBuffer {
// Sending the response header buffer
headerBuffer.flip();
try {
- socketWrapper.write(isBlocking(), headerBuffer);
+ SocketWrapperBase<?> socketWrapper = this.socketWrapper;
+ if (socketWrapper != null) {
+ socketWrapper.write(isBlocking(), headerBuffer);
+ } else {
+ throw new CloseNowException(sm.getString("iob.failedwrite"));
+ }
} finally {
headerBuffer.position(0).limit(headerBuffer.capacity());
}
@@ -527,7 +533,12 @@ public class Http11OutputBuffer implements HttpOutputBuffer {
public int doWrite(ByteBuffer chunk) throws IOException {
try {
int len = chunk.remaining();
- socketWrapper.write(isBlocking(), chunk);
+ SocketWrapperBase<?> socketWrapper = Http11OutputBuffer.this.socketWrapper;
+ if (socketWrapper != null) {
+ socketWrapper.write(isBlocking(), chunk);
+ } else {
+ throw new CloseNowException(sm.getString("iob.failedwrite"));
+ }
len -= chunk.remaining();
byteCount += len;
return len;
diff --git a/test/org/apache/catalina/core/TestAsyncContextImpl.java b/test/org/apache/catalina/core/TestAsyncContextImpl.java
index 3f6524b..4023a74 100644
--- a/test/org/apache/catalina/core/TestAsyncContextImpl.java
+++ b/test/org/apache/catalina/core/TestAsyncContextImpl.java
@@ -17,6 +17,7 @@
package org.apache.catalina.core;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URI;
import java.net.URISyntaxException;
@@ -819,7 +820,7 @@ public class TestAsyncContextImpl extends TomcatBaseTest {
}
}
- private static class TrackingListener implements AsyncListener {
+ public static class TrackingListener implements AsyncListener {
private final boolean completeOnError;
private final boolean completeOnTimeout;
@@ -2653,4 +2654,173 @@ public class TestAsyncContextImpl extends TomcatBaseTest {
}
}
+
+
+
+ /*
+ * Tests an error on an async thread when the client closes the connection
+ * before fully writing the request body.
+ *
+ * Required sequence is:
+ * - enter Servlet's service() method
+ * - startAsync()
+ * - start async thread
+ * - read partial body
+ * - close client connection
+ * - read on async thread -> I/O error
+ * - exit Servlet's service() method
+ *
+ * This test makes extensive use of instance fields in the Servlet that
+ * would normally be considered very poor practice. It is only safe in this
+ * test as the Servlet only processes a single request.
+ */
+ @Test
+ public void testCanceledPost() throws Exception {
+ CountDownLatch partialReadLatch = new CountDownLatch(1);
+ CountDownLatch clientCloseLatch = new CountDownLatch(1);
+ CountDownLatch threadCompleteLatch = new CountDownLatch(1);
+
+ AtomicBoolean testFailed = new AtomicBoolean(true);
+
+ // Setup Tomcat instance
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+
+ PostServlet postServlet = new PostServlet(partialReadLatch, clientCloseLatch, threadCompleteLatch, testFailed);
+ Wrapper wrapper = Tomcat.addServlet(ctx, "postServlet", postServlet);
+ wrapper.setAsyncSupported(true);
+ ctx.addServletMappingDecoded("/*", "postServlet");
+
+ tomcat.start();
+
+ PostClient client = new PostClient();
+ client.setPort(getPort());
+ client.setRequest(new String[] { "POST / HTTP/1.1" + SimpleHttpClient.CRLF +
+ "Host: localhost:" + SimpleHttpClient.CRLF +
+ "Content-Length: 100" + SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF +
+ "This is 16 bytes"
+ });
+ client.connect();
+ client.sendRequest();
+
+ // Wait server to read partial request body
+ partialReadLatch.await();
+
+ client.disconnect();
+
+ clientCloseLatch.countDown();
+
+ threadCompleteLatch.await();
+
+ Assert.assertFalse(testFailed.get());
+ }
+
+
+ private static final class PostClient extends SimpleHttpClient {
+
+ @Override
+ public boolean isResponseBodyOK() {
+ return true;
+ }
+ }
+
+
+ private static final class PostServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ private final transient CountDownLatch partialReadLatch;
+ private final transient CountDownLatch clientCloseLatch;
+ private final transient CountDownLatch threadCompleteLatch;
+ private final AtomicBoolean testFailed;
+
+ public PostServlet(CountDownLatch doPostLatch, CountDownLatch clientCloseLatch,
+ CountDownLatch threadCompleteLatch, AtomicBoolean testFailed) {
+ this.partialReadLatch = doPostLatch;
+ this.clientCloseLatch = clientCloseLatch;
+ this.threadCompleteLatch = threadCompleteLatch;
+ this.testFailed = testFailed;
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ AsyncContext ac = req.startAsync();
+ Thread t = new PostServletThread(ac, partialReadLatch, clientCloseLatch, threadCompleteLatch, testFailed);
+ t.start();
+
+ try {
+ threadCompleteLatch.await();
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ }
+
+
+ private static final class PostServletThread extends Thread {
+
+ private final AsyncContext ac;
+ private final CountDownLatch partialReadLatch;
+ private final CountDownLatch clientCloseLatch;
+ private final CountDownLatch threadCompleteLatch;
+ private final AtomicBoolean testFailed;
+
+ public PostServletThread(AsyncContext ac, CountDownLatch partialReadLatch, CountDownLatch clientCloseLatch,
+ CountDownLatch threadCompleteLatch, AtomicBoolean testFailed) {
+ this.ac = ac;
+ this.partialReadLatch = partialReadLatch;
+ this.clientCloseLatch = clientCloseLatch;
+ this.threadCompleteLatch = threadCompleteLatch;
+ this.testFailed = testFailed;
+ }
+
+ @Override
+ public void run() {
+ try {
+ int bytesRead = 0;
+ byte[] buffer = new byte[32];
+ InputStream is = null;
+
+ try {
+ is = ac.getRequest().getInputStream();
+
+ // Read the partial request body
+ while (bytesRead < 16) {
+ int read = is.read(buffer);
+ if (read == -1) {
+ // Error condition
+ return;
+ }
+ bytesRead += read;
+ }
+ } catch (IOException ioe) {
+ // Error condition
+ return;
+ } finally {
+ partialReadLatch.countDown();
+ }
+
+ // Wait for client to close connection
+ clientCloseLatch.await();
+
+ // Read again
+ try {
+ is.read();
+ } catch (IOException e) {
+ e.printStackTrace();
+ // Required. Clear the error marker.
+ testFailed.set(false);
+ }
+ } catch (InterruptedException e) {
+ // Ignore
+ } finally {
+ threadCompleteLatch.countDown();
+ }
+ }
+ }
}
diff --git a/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java b/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java
index 7130b11..6868375 100644
--- a/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java
+++ b/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java
@@ -32,6 +32,9 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
import javax.net.SocketFactory;
import javax.servlet.AsyncContext;
@@ -44,6 +47,7 @@ import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -52,7 +56,9 @@ import org.junit.Ignore;
import org.junit.Test;
import org.apache.catalina.Context;
+import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.BytesStreamer;
+import org.apache.catalina.startup.SimpleHttpClient;
import org.apache.catalina.startup.TesterServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
@@ -997,4 +1003,190 @@ public class TestNonBlockingAPI extends TomcatBaseTest {
}
}
+
+
+ /*
+ * Tests an error on an non-blocking read when the client closes the
+ * connection before fully writing the request body.
+ *
+ * Required sequence is:
+ * - enter Servlet's service() method
+ * - startAsync()
+ * - configure non-blocking read
+ * - read partial body
+ * - close client connection
+ * - error is triggered
+ * - exit Servlet's service() method
+ *
+ * This test makes extensive use of instance fields in the Servlet that
+ * would normally be considered very poor practice. It is only safe in this
+ * test as the Servlet only processes a single request.
+ */
+ @Test
+ public void testCanceledPost() throws Exception {
+
+ LogManager.getLogManager().getLogger("org.apache.coyote").setLevel(Level.ALL);
+ LogManager.getLogManager().getLogger("org.apache.tomcat.util.net").setLevel(Level.ALL);
+
+ CountDownLatch partialReadLatch = new CountDownLatch(1);
+ CountDownLatch completeLatch = new CountDownLatch(1);
+
+ AtomicBoolean testFailed = new AtomicBoolean(true);
+
+ // Setup Tomcat instance
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+
+ PostServlet postServlet = new PostServlet(partialReadLatch, completeLatch, testFailed);
+ Wrapper wrapper = Tomcat.addServlet(ctx, "postServlet", postServlet);
+ wrapper.setAsyncSupported(true);
+ ctx.addServletMappingDecoded("/*", "postServlet");
+
+ tomcat.start();
+
+ PostClient client = new PostClient();
+ client.setPort(getPort());
+ client.setRequest(new String[] { "POST / HTTP/1.1" + SimpleHttpClient.CRLF +
+ "Host: localhost:" + SimpleHttpClient.CRLF +
+ "Content-Length: 100" + SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF +
+ "This is 16 bytes"
+ });
+ client.connect();
+ client.sendRequest();
+
+ // Wait server to read partial request body
+ partialReadLatch.await();
+
+ client.disconnect();
+
+ completeLatch.await();
+
+ Assert.assertFalse(testFailed.get());
+ }
+
+
+ private static final class PostClient extends SimpleHttpClient {
+
+ @Override
+ public boolean isResponseBodyOK() {
+ return true;
+ }
+ }
+
+
+ private static final class PostServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ private final transient CountDownLatch partialReadLatch;
+ private final transient CountDownLatch completeLatch;
+ private final AtomicBoolean testFailed;
+
+ public PostServlet(CountDownLatch doPostLatch, CountDownLatch completeLatch, AtomicBoolean testFailed) {
+ this.partialReadLatch = doPostLatch;
+ this.completeLatch = completeLatch;
+ this.testFailed = testFailed;
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ AsyncContext ac = req.startAsync();
+ ac.setTimeout(-1);
+ CanceledPostAsyncListener asyncListener = new CanceledPostAsyncListener(completeLatch);
+ ac.addListener(asyncListener);
+
+ CanceledPostReadListener readListener = new CanceledPostReadListener(ac, partialReadLatch, testFailed);
+ req.getInputStream().setReadListener(readListener);
+ }
+ }
+
+
+ private static final class CanceledPostAsyncListener implements AsyncListener {
+
+ private final transient CountDownLatch completeLatch;
+
+ public CanceledPostAsyncListener(CountDownLatch completeLatch) {
+ this.completeLatch = completeLatch;
+ }
+
+ @Override
+ public void onComplete(AsyncEvent event) throws IOException {
+ System.out.println("complete");
+ completeLatch.countDown();
+ }
+
+ @Override
+ public void onTimeout(AsyncEvent event) throws IOException {
+ System.out.println("onTimeout");
+ }
+
+ @Override
+ public void onError(AsyncEvent event) throws IOException {
+ System.out.println("onError-async");
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent event) throws IOException {
+ System.out.println("onStartAsync");
+ }
+ }
+
+ private static final class CanceledPostReadListener implements ReadListener {
+
+ private final AsyncContext ac;
+ private final CountDownLatch partialReadLatch;
+ private final AtomicBoolean testFailed;
+ private int totalRead = 0;
+
+ public CanceledPostReadListener(AsyncContext ac, CountDownLatch partialReadLatch, AtomicBoolean testFailed) {
+ this.ac = ac;
+ this.partialReadLatch = partialReadLatch;
+ this.testFailed = testFailed;
+ }
+
+ @Override
+ public void onDataAvailable() throws IOException {
+ ServletInputStream sis = ac.getRequest().getInputStream();
+ boolean isReady;
+
+ byte[] buffer = new byte[32];
+ do {
+ if (partialReadLatch.getCount() == 0) {
+ System.out.println("debug");
+ }
+ int bytesRead = sis.read(buffer);
+
+ if (bytesRead == -1) {
+ return;
+ }
+ totalRead += bytesRead;
+ isReady = sis.isReady();
+ System.out.println("Read [" + bytesRead +
+ "], buffer [" + new String(buffer, 0, bytesRead, StandardCharsets.UTF_8) +
+ "], total read [" + totalRead +
+ "], isReady [" + isReady + "]");
+ } while (isReady);
+ if (totalRead == 16) {
+ partialReadLatch.countDown();
+ }
+ }
+
+ @Override
+ public void onAllDataRead() throws IOException {
+ ac.complete();
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ throwable.printStackTrace();
+ // This is the expected behaviour so clear the failed flag.
+ testFailed.set(false);
+ ac.complete();
+ }
+ }
}
--
2.33.0

View File

@ -13,7 +13,7 @@
Name: tomcat
Epoch: 1
Version: %{major_version}.%{minor_version}.%{micro_version}
Release: 28
Release: 33
Summary: Implementation of the Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies
License: ASL 2.0
URL: http://tomcat.apache.org/
@ -101,6 +101,15 @@ Patch6056: CVE-2021-30640-8.patch
Patch6057: CVE-2021-41079.patch
Patch6058: CVE-2021-42340.patch
Patch6069: CVE-2022-23181.patch
Patch6070: CVE-2022-42252.patch
Patch6071: CVE-2023-28708-pre.patch
Patch6072: CVE-2023-28708.patch
Patch6073: CVE-2023-41080.patch
Patch6074: CVE-2023-45648.patch
Patch6075: CVE-2024-21733.patch
Patch6076: CVE-2023-24998.patch
Patch6077: CVE-2023-28709.patch
Patch6078: CVE-2023-42795.patch
BuildRequires: ecj >= 1:4.6.1 findutils apache-commons-collections apache-commons-daemon
BuildRequires: apache-commons-dbcp apache-commons-pool tomcat-taglibs-standard ant
@ -108,9 +117,8 @@ BuildRequires: jpackage-utils >= 0:1.7.0 java-devel >= 1:1.8.0 junit javapackage
BuildRequires: geronimo-saaj aqute-bndlib aqute-bnd systemd-units wsdl4j geronimo-jaxrpc
Requires: procps jpackage-utils java-headless >= 1:1.8.0 apache-commons-daemon
Requires: tomcat-taglibs-standard >= 0:1.1 ecj
Requires: libtcnative-1-0 >= 1.2.14
Requires: tomcat-taglibs-standard >= 0:1.1 ecj libtcnative-1-0 >= 1.2.14
Requires: apache-commons-dbcp apache-commons-pool apache-commons-collections
Requires(pre): shadow-utils
Requires(post): chkconfig
@ -502,6 +510,22 @@ fi
%{_javadocdir}/%{name}
%changelog
* Tue Jan 23 2024 wangkai <13474090681@163.com> - 1:9.0.10-33
- Fix CVE-2024-21733,CVE-2023-24998,CVE-2023-28709,CVE-2023-42795
* Fri Oct 20 2023 wangkai <13474090681@163.com> - 1:9.0.10-32
- Fix CVE-2023-45648
* Thu Sep 07 2023 wangkai <13474090681@163.com> - 1:9.0.10-31
- Fix CVE-2023-41080
- Add requires apache-commons-dbcp,pool,collections for fix path error
* Mon Apr 17 2023 wangkai <13474090681@163.com> - 1:9.0.10-30
- Fix CVE-2023-28708
* Wed Dec 21 2022 xulei <xulei@xfusion.com> - 1:9.0.10-29
- Fix CVE-2022-42252
* Fri Aug 12 2022 liyanan <liyanan32@h-partners.com> - 1:9.0.10-28
- Update build-classpath calls to ECJ to specify the JAR we want to use