Compare commits

...

10 Commits

Author SHA1 Message Date
openeuler-ci-bot
6184ee62ec
!157 [sync] PR-156: Fix CVE-2024-50379 CVE-2024-54677
Merge pull request !157 from openeuler-sync-bot/sync-pr156-master-to-openEuler-24.03-LTS-SP1
2024-12-18 03:23:21 +00:00
wk333
e2a9a8ab35 Fix CVE-2024-50379 CVE-2024-54677
(cherry picked from commit 37b6fbcf4c334035b8423e43c24b2bea2397c27f)
2024-12-18 11:02:26 +08:00
openeuler-ci-bot
64f5c6745b
!155 [sync] PR-148: Add requires ecj for fix server error
From: @openeuler-sync-bot 
Reviewed-by: @wk333 
Signed-off-by: @wk333
2024-11-26 10:57:27 +00:00
wk333
d853d477ac Add requires ecj for fix server error
(cherry picked from commit 55caf8ac9e3aa4c058867c0be64d28522bdb6a22)
2024-11-26 17:33:56 +08:00
openeuler-ci-bot
ffe463b088
!146 [sync] PR-139: Fix CVE-2024-52318
From: @openeuler-sync-bot 
Reviewed-by: @wk333 
Signed-off-by: @wk333
2024-11-20 00:41:47 +00:00
wk333
c2c21090ae Fix CVE-2024-52318
(cherry picked from commit f6da21ee4a0a0605f41c9c798d211fbae42d4215)
2024-11-19 17:09:27 +08:00
openeuler-ci-bot
af5f386bce
!131 [sync] PR-129: Update to 9.0.96
From: @openeuler-sync-bot 
Reviewed-by: @wk333 
Signed-off-by: @wk333
2024-11-13 07:08:47 +00:00
yaqiangchen
8c89e47abb Update to 9.0.96
(cherry picked from commit a8d6c0b0fb017406507bda878a1f71b0124c7ebf)
2024-11-13 09:39:20 +08:00
openeuler-ci-bot
c299570657
!118 Fix CVE-2024-21733,CVE-2023-24998,CVE-2023-28709,CVE-2023-42795
From: @wk333 
Reviewed-by: @starlet-dx 
Signed-off-by: @starlet-dx
2024-01-23 06:35:33 +00:00
wk333
5342785c69 Fix CVE-2024-21733,CVE-2023-24998,CVE-2023-28709,CVE-2023-42795 2024-01-23 11:15:20 +08:00
98 changed files with 3118 additions and 7285 deletions

Binary file not shown.

View File

@ -1,27 +0,0 @@
--- a/webapps/docs/changelog.xml 2018-06-20 13:35:40.000000000 -0400
+++ b/webapps/docs/changelog_1.xml 2019-06-24 08:35:44.801000000 -0400
@@ -164,6 +164,10 @@
the authenticated Subject to include at least one Principal of a type
specified by <code>userClassNames</code>. (markt)
</fix>
+ <fix>
+ When generating a redirect to a directory in the Default Servlet, avoid
+ generating a protocol relative redirect. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">
--- a/java/org/apache/catalina/servlets/DefaultServlet.java 2018-06-20 13:35:34.000000000 -0400
+++ b/java/org/apache/catalina/servlets/DefaultServlet_1.java 2019-06-24 08:40:08.699000000 -0400
@@ -1324,6 +1324,10 @@ public class DefaultServlet extends Http
location.append('?');
location.append(request.getQueryString());
}
+ // Avoid protocol relative redirects
+ while (location.length() > 1 && location.charAt(1) == '/') {
+ location.deleteCharAt(0);
+ }
response.sendRedirect(response.encodeRedirectURL(location.toString()));
}

View File

@ -1,75 +0,0 @@
--- tomcat/java/org/apache/coyote/http2/Http2Protocol.java 2019/02/01 10:17:08 1852697
+++ tomcat/java/org/apache/coyote/http2/Http2Protocol.java 2019/02/01 10:28:01 1852698
@@ -42,8 +42,10 @@
public class Http2Protocol implements UpgradeProtocol {
static final long DEFAULT_READ_TIMEOUT = 10000;
- static final long DEFAULT_KEEP_ALIVE_TIMEOUT = -1;
static final long DEFAULT_WRITE_TIMEOUT = 10000;
+ static final long DEFAULT_KEEP_ALIVE_TIMEOUT = -1;
+ static final long DEFAULT_STREAM_READ_TIMEOUT = 20000;
+ static final long DEFAULT_STREAM_WRITE_TIMEOUT = 20000;
// The HTTP/2 specification recommends a minimum default of 100
static final long DEFAULT_MAX_CONCURRENT_STREAMS = 200;
// Maximum amount of streams which can be concurrently executed over
@@ -57,9 +59,14 @@
private static final byte[] ALPN_IDENTIFIER = ALPN_NAME.getBytes(StandardCharsets.UTF_8);
// All timeouts in milliseconds
+ // These are the socket level timeouts
private long readTimeout = DEFAULT_READ_TIMEOUT;
- private long keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
private long writeTimeout = DEFAULT_WRITE_TIMEOUT;
+ private long keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
+ // These are the stream level timeouts
+ private long streamReadTimeout = DEFAULT_STREAM_READ_TIMEOUT;
+ private long streamWriteTimeout = DEFAULT_STREAM_WRITE_TIMEOUT;
+
private long maxConcurrentStreams = DEFAULT_MAX_CONCURRENT_STREAMS;
private int maxConcurrentStreamExecution = DEFAULT_MAX_CONCURRENT_STREAM_EXECUTION;
// If a lower initial value is required, set it here but DO NOT change the
@@ -145,6 +152,16 @@
}
+ public long getWriteTimeout() {
+ return writeTimeout;
+ }
+
+
+ public void setWriteTimeout(long writeTimeout) {
+ this.writeTimeout = writeTimeout;
+ }
+
+
public long getKeepAliveTimeout() {
return keepAliveTimeout;
}
@@ -155,13 +172,23 @@
}
- public long getWriteTimeout() {
- return writeTimeout;
+ public long getStreamReadTimeout() {
+ return streamReadTimeout;
}
- public void setWriteTimeout(long writeTimeout) {
- this.writeTimeout = writeTimeout;
+ public void setStreamReadTimeout(long streamReadTimeout) {
+ this.streamReadTimeout = streamReadTimeout;
+ }
+
+
+ public long getStreamWriteTimeout() {
+ return streamWriteTimeout;
+ }
+
+
+ public void setStreamWriteTimeout(long streamWriteTimeout) {
+ this.streamWriteTimeout = streamWriteTimeout;
}

View File

@ -1,13 +0,0 @@
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java 2019-06-09 21:03:54.790000000 -0400
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java 2019-06-09 21:05:12.133000000 -0400
@@ -905,7 +905,7 @@ class Stream extends AbstractStream impl
throw new IOException(sm.getString("stream.inputBuffer.reset"));
}
- if (inBuffer.position() == 0) {
+ if (inBuffer.position() == 0 && isActive() && !isInputFinished()) {
String msg = sm.getString("stream.inputBuffer.readTimeout");
StreamException se = new StreamException(
msg, Http2Error.ENHANCE_YOUR_CALM, getIdAsInt());

View File

@ -1,21 +0,0 @@
--- a/java/org/apache/coyote/http2/Stream.java 2019-06-11 21:24:19.998000000 -0400
+++ b/java/org/apache/coyote/http2/Stream_1.java 2019-06-11 21:26:18.329000000 -0400
@@ -221,7 +221,7 @@ class Stream extends AbstractStream impl
if (windowSize == 0) {
String msg = sm.getString("stream.writeTimeout");
StreamException se = new StreamException(
- msg, Http2Error.ENHANCE_YOUR_CALM, getIdAsInt());
+ msg, Http2Error.ENHANCE_YOUR_CALM, getIdentifier().intValue());
// Prevent the application making further writes
streamOutputBuffer.closed = true;
// Prevent Tomcat's error handling trying to write
@@ -908,7 +908,7 @@ class Stream extends AbstractStream impl
if (inBuffer.position() == 0 && isActive() && !isInputFinished()) {
String msg = sm.getString("stream.inputBuffer.readTimeout");
StreamException se = new StreamException(
- msg, Http2Error.ENHANCE_YOUR_CALM, getIdAsInt());
+ msg, Http2Error.ENHANCE_YOUR_CALM, getIdentifier().intValue());
// Trigger a reset once control returns to Tomcat
coyoteResponse.setError();
streamOutputBuffer.reset = se;

View File

@ -1,246 +0,0 @@
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/catalina/connector/OutputBuffer.java apache-tomcat-9.0.10-src-bak/java/org/apache/catalina/connector/OutputBuffer.java
--- apache-tomcat-9.0.10-src/java/org/apache/catalina/connector/OutputBuffer.java 2018-06-20 13:35:33.000000000 -0400
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/catalina/connector/OutputBuffer.java 2019-06-09 20:28:02.836000000 -0400
@@ -33,6 +33,7 @@ import javax.servlet.http.HttpServletRes
import org.apache.catalina.Globals;
import org.apache.coyote.ActionCode;
+import org.apache.coyote.CloseNowException;
import org.apache.coyote.Response;
import org.apache.tomcat.util.buf.C2BConverter;
import org.apache.tomcat.util.res.StringManager;
@@ -326,6 +327,13 @@ public class OutputBuffer extends Writer
// real write to the adapter
try {
coyoteResponse.doWrite(buf);
+ } catch (CloseNowException e) {
+ // Catch this sub-class as it requires specific handling.
+ // Examples where this exception is thrown:
+ // - HTTP/2 stream timeout
+ // Prevent further output for this response
+ closed = true;
+ throw e;
} catch (IOException e) {
// An IOException on a write is almost always due to
// the remote client aborting the request. Wrap this
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/catalina/core/StandardWrapperValve.java apache-tomcat-9.0.10-src-bak/java/org/apache/catalina/core/StandardWrapperValve.java
--- apache-tomcat-9.0.10-src/java/org/apache/catalina/core/StandardWrapperValve.java 2018-06-20 13:35:34.000000000 -0400
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/catalina/core/StandardWrapperValve.java 2019-06-09 20:33:27.596000000 -0400
@@ -36,6 +36,7 @@ import org.apache.catalina.connector.Cli
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
+import org.apache.coyote.CloseNowException;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.log.SystemLogHandler;
@@ -201,7 +202,7 @@ final class StandardWrapperValve
}
}
- } catch (ClientAbortException e) {
+ } catch (ClientAbortException | CloseNowException e) {
throwable = e;
exception(request, response, e);
} catch (IOException e) {
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/LocalStrings.properties apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/LocalStrings.properties
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/LocalStrings.properties 2018-06-20 13:35:35.000000000 -0400
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/LocalStrings.properties 2019-06-09 20:34:32.307000000 -0400
@@ -93,6 +93,7 @@ stream.reset.fail=Connection [{0}], Stre
stream.reset.receive=Connection [{0}], Stream [{1}], Reset received due to [{2}]
stream.reset.send=Connection [{0}], Stream [{1}], Reset sent due to [{2}]
stream.trailerHeader.noEndOfStream=Connection [{0}], Stream [{1}], The trailer headers did not include the end of stream flag
+stream.writeTimeout=Timeout waiting for client to increase flow control window to permit stream data to be written
stream.inputBuffer.copy=Copying [{0}] bytes from inBuffer to outBuffer
stream.inputBuffer.dispatch=Data added to inBuffer when read interest is registered. Triggering a read dispatch
@@ -149,4 +150,4 @@ upgradeHandler.writeHeaders=Connection [
upgradeHandler.writePushHeaders=Connection [{0}], Stream [{1}], Pushed stream [{2}], EndOfStream [{3}]
writeStateMachine.endWrite.ise=It is illegal to specify [{0}] for the new state once a write has completed
-writeStateMachine.ise=It is illegal to call [{0}()] in state [{1}]
\ No newline at end of file
+writeStateMachine.ise=It is illegal to call [{0}()] in state [{1}]
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java 2018-06-20 13:35:35.000000000 -0400
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java 2019-06-09 20:38:30.109000000 -0400
@@ -211,7 +211,21 @@ class Stream extends AbstractStream impl
}
try {
if (block) {
- wait();
+ wait(handler.getProtocol().getStreamWriteTimeout());
+ windowSize = getWindowSize();
+ if (windowSize == 0) {
+ String msg = sm.getString("stream.writeTimeout");
+ StreamException se = new StreamException(
+ msg, Http2Error.ENHANCE_YOUR_CALM, getIdAsInt());
+ // Prevent the application making further writes
+ streamOutputBuffer.closed = true;
+ // Prevent Tomcat's error handling trying to write
+ coyoteResponse.setError();
+ coyoteResponse.setErrorReported();
+ // Trigger a reset once control returns to Tomcat
+ streamOutputBuffer.reset = se;
+ throw new CloseNowException(msg, se);
+ }
} else {
return 0;
}
@@ -221,7 +235,6 @@ class Stream extends AbstractStream impl
// Stream.
throw new IOException(e);
}
- windowSize = getWindowSize();
}
int allocation;
if (windowSize < reservation) {
@@ -660,6 +673,9 @@ class Stream extends AbstractStream impl
return !streamOutputBuffer.endOfStreamSent;
}
+ StreamException getResetException() {
+ return streamOutputBuffer.reset;
+ }
private static void push(final Http2UpgradeHandler handler, final Request request,
final Stream stream) throws IOException {
@@ -707,6 +723,7 @@ class Stream extends AbstractStream impl
private final ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);
private volatile long written = 0;
private volatile boolean closed = false;
+ private volatile StreamException reset = null;
private volatile boolean endOfStreamSent = false;
/* The write methods are synchronized to ensure that only one thread at
@@ -800,9 +817,14 @@ class Stream extends AbstractStream impl
@Override
public final void end() throws IOException {
- closed = true;
- flush(true);
- writeTrailers();
+ if (reset != null) {
+ throw new CloseNowException(reset);
+ }
+ if (!closed) {
+ closed = true;
+ flush(true);
+ writeTrailers();
+ }
}
/**
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/StreamProcessor.java apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/StreamProcessor.java
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/StreamProcessor.java 2018-06-20 13:35:35.000000000 -0400
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/StreamProcessor.java 2019-06-09 20:40:08.789000000 -0400
@@ -78,10 +78,13 @@ class StreamProcessor extends AbstractPr
stream.getIdentifier()), Http2Error.INTERNAL_ERROR);
stream.close(ce);
} else if (!getErrorState().isIoAllowed()) {
- StreamException se = new StreamException(sm.getString(
- "streamProcessor.error.stream", stream.getConnectionId(),
- stream.getIdentifier()), Http2Error.INTERNAL_ERROR,
- stream.getIdentifier().intValue());
+ StreamException se = stream.getResetException();
+ if (se == null) {
+ se = new StreamException(sm.getString(
+ "streamProcessor.error.stream", stream.getConnectionId(),
+ stream.getIdentifier()), Http2Error.INTERNAL_ERROR,
+ stream.getIdentifier().intValue());
+ }
stream.close(se);
}
}
diff -Nurp apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/Http2TestBase.java apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/Http2TestBase.java
--- apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/Http2TestBase.java 2018-06-20 13:35:38.000000000 -0400
+++ apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/Http2TestBase.java 2019-06-09 20:41:45.113000000 -0400
@@ -486,8 +486,10 @@ public abstract class Http2TestBase exte
Http2Protocol http2Protocol = new Http2Protocol();
// Short timeouts for now. May need to increase these for CI systems.
http2Protocol.setReadTimeout(2000);
- http2Protocol.setKeepAliveTimeout(5000);
http2Protocol.setWriteTimeout(2000);
+ http2Protocol.setKeepAliveTimeout(5000);
+ http2Protocol.setStreamReadTimeout(1000);
+ http2Protocol.setStreamWriteTimeout(1000);
http2Protocol.setMaxConcurrentStreams(maxConcurrentStreams);
connector.addUpgradeProtocol(http2Protocol);
}
diff -Nurp apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/TestHttp2Timeouts.java apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/TestHttp2Timeouts.java
--- apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/TestHttp2Timeouts.java 1969-12-31 19:00:00.000000000 -0500
+++ apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/TestHttp2Timeouts.java 2019-06-09 20:42:38.095000000 -0400
@@ -0,0 +1,73 @@
+/*
+ * 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.coyote.http2;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestHttp2Timeouts extends Http2TestBase {
+
+ @Override
+ @Before
+ public void http2Connect() throws Exception {
+ super.http2Connect();
+ sendSettings(0, false, new SettingValue(Setting.INITIAL_WINDOW_SIZE.getId(), 0));
+ }
+
+
+ /*
+ * Simple request won't fill buffer so timeout will occur in Tomcat internal
+ * code during response completion.
+ */
+ @Test
+ public void testClientWithEmptyWindow() throws Exception {
+ sendSimpleGetRequest(3);
+
+ // Settings
+ parser.readFrame(false);
+ // Headers
+ parser.readFrame(false);
+
+ output.clearTrace();
+
+ parser.readFrame(false);
+ Assert.assertEquals("3-RST-[11]\n", output.getTrace());
+ }
+
+
+ /*
+ * Large request will fill buffer so timeout will occur in application code
+ * during response write (when Tomcat commits the response and flushes the
+ * buffer as a result of the buffer filling).
+ */
+ @Test
+ public void testClientWithEmptyWindowLargeResponse() throws Exception {
+ sendLargeGetRequest(3);
+
+ // Settings
+ parser.readFrame(false);
+ // Headers
+ parser.readFrame(false);
+
+ output.clearTrace();
+
+ parser.readFrame(false);
+ Assert.assertEquals("3-RST-[11]\n", output.getTrace());
+ }
+
+}

View File

@ -1,202 +0,0 @@
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/LocalStrings.properties apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/LocalStrings.properties
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/LocalStrings.properties 2019-06-09 20:45:15.320000000 -0400
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/LocalStrings.properties 2019-06-09 20:46:36.793000000 -0400
@@ -98,6 +98,7 @@ stream.writeTimeout=Timeout waiting for
stream.inputBuffer.copy=Copying [{0}] bytes from inBuffer to outBuffer
stream.inputBuffer.dispatch=Data added to inBuffer when read interest is registered. Triggering a read dispatch
stream.inputBuffer.empty=The Stream input buffer is empty. Waiting for more data
+stream.inputBuffer.readTimeout=Timeout waiting to read data from client
stream.inputBuffer.reset=Stream reset
stream.inputBuffer.signal=Data added to inBuffer when read thread is waiting. Signalling that thread to continue
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java 2019-06-09 20:45:15.321000000 -0400
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java 2019-06-09 20:48:21.509000000 -0400
@@ -888,10 +888,22 @@ class Stream extends AbstractStream impl
if (log.isDebugEnabled()) {
log.debug(sm.getString("stream.inputBuffer.empty"));
}
- inBuffer.wait();
+
+ inBuffer.wait(handler.getProtocol().getStreamReadTimeout());
+
if (reset) {
throw new IOException(sm.getString("stream.inputBuffer.reset"));
}
+
+ if (inBuffer.position() == 0) {
+ String msg = sm.getString("stream.inputBuffer.readTimeout");
+ StreamException se = new StreamException(
+ msg, Http2Error.ENHANCE_YOUR_CALM, getIdAsInt());
+ // Trigger a reset once control returns to Tomcat
+ coyoteResponse.setError();
+ streamOutputBuffer.reset = se;
+ throw new CloseNowException(msg, se);
+ }
} catch (InterruptedException e) {
// Possible shutdown / rst or similar. Use an
// IOException to signal to the client that further I/O
diff -Nurp apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/Http2TestBase.java apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/Http2TestBase.java
--- apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/Http2TestBase.java 2019-06-09 20:45:15.323000000 -0400
+++ apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/Http2TestBase.java 2019-06-09 20:53:54.809000000 -0400
@@ -28,6 +28,7 @@ import java.nio.charset.StandardCharsets
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.Random;
import javax.net.SocketFactory;
@@ -300,6 +301,22 @@ public abstract class Http2TestBase exte
}
}
+ protected void sendParameterPostRequest(int streamId, byte[] padding, String body,
+ long contentLength, boolean useExpectation) throws IOException {
+ byte[] headersFrameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ byte[] dataFrameHeader = new byte[9];
+ ByteBuffer dataPayload = ByteBuffer.allocate(128);
+
+ buildPostRequest(headersFrameHeader, headersPayload, useExpectation,
+ "application/x-www-form-urlencoded", contentLength, "/parameter", dataFrameHeader,
+ dataPayload, padding, null, null, streamId);
+ writeFrame(headersFrameHeader, headersPayload);
+ if (body != null) {
+ dataPayload.put(body.getBytes(StandardCharsets.ISO_8859_1));
+ writeFrame(dataFrameHeader, dataPayload);
+ }
+ }
protected void buildPostRequest(byte[] headersFrameHeader, ByteBuffer headersPayload,
boolean useExpectation, byte[] dataFrameHeader, ByteBuffer dataPayload, byte[] padding,
@@ -311,14 +328,29 @@ public abstract class Http2TestBase exte
protected void buildPostRequest(byte[] headersFrameHeader, ByteBuffer headersPayload,
boolean useExpectation, byte[] dataFrameHeader, ByteBuffer dataPayload, byte[] padding,
byte[] trailersFrameHeader, ByteBuffer trailersPayload, int streamId) {
+ buildPostRequest(headersFrameHeader, headersPayload, useExpectation, null, -1, "/simple",
+ dataFrameHeader, dataPayload, padding, trailersFrameHeader, trailersPayload, streamId);
+ }
+
+ protected void buildPostRequest(byte[] headersFrameHeader, ByteBuffer headersPayload,
+ boolean useExpectation, String contentType, long contentLength, String path,
+ byte[] dataFrameHeader, ByteBuffer dataPayload, byte[] padding,
+ byte[] trailersFrameHeader, ByteBuffer trailersPayload, int streamId) {
+
MimeHeaders headers = new MimeHeaders();
headers.addValue(":method").setString("POST");
headers.addValue(":scheme").setString("http");
- headers.addValue(":path").setString("/simple");
+ headers.addValue(":path").setString(path);
headers.addValue(":authority").setString("localhost:" + getPort());
if (useExpectation) {
headers.addValue("expect").setString("100-continue");
}
+ if (contentType != null) {
+ headers.addValue("content-type").setString(contentType);
+ }
+ if (contentLength > -1) {
+ headers.addValue("content-length").setLong(contentLength);
+ }
hpackEncoder.encode(headers, headersPayload);
headersPayload.flip();
@@ -507,6 +539,8 @@ public abstract class Http2TestBase exte
ctxt.addServletMappingDecoded("/large", "large");
Tomcat.addServlet(ctxt, "cookie", new CookieServlet());
ctxt.addServletMappingDecoded("/cookie", "cookie");
+ Tomcat.addServlet(ctxt, "parameter", new ParameterServlet());
+ ctxt.addServletMappingDecoded("/parameter", "parameter");
tomcat.start();
}
@@ -1205,6 +1239,24 @@ public abstract class Http2TestBase exte
}
}
+
+ static class ParameterServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ Map<String,String[]> params = req.getParameterMap();
+
+ resp.setContentType("text/plain");
+ resp.setCharacterEncoding("UTF-8");
+
+ resp.getWriter().print(params.size());
+ }
+ }
+
static class SettingValue {
private final int setting;
diff -Nurp apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/TestHttp2Timeouts.java apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/TestHttp2Timeouts.java
--- apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/TestHttp2Timeouts.java 2019-06-09 20:45:15.323000000 -0400
+++ apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/TestHttp2Timeouts.java 2019-06-09 20:57:22.652000000 -0400
@@ -26,7 +26,6 @@ public class TestHttp2Timeouts extends H
@Before
public void http2Connect() throws Exception {
super.http2Connect();
- sendSettings(0, false, new SettingValue(Setting.INITIAL_WINDOW_SIZE.getId(), 0));
}
@@ -36,7 +35,7 @@ public class TestHttp2Timeouts extends H
*/
@Test
public void testClientWithEmptyWindow() throws Exception {
- sendSimpleGetRequest(3);
+ sendSettings(0, false, new SettingValue(Setting.INITIAL_WINDOW_SIZE.getId(), 0));
// Settings
parser.readFrame(false);
@@ -57,6 +56,7 @@ public class TestHttp2Timeouts extends H
*/
@Test
public void testClientWithEmptyWindowLargeResponse() throws Exception {
+ sendSettings(0, false, new SettingValue(Setting.INITIAL_WINDOW_SIZE.getId(), 0));
sendLargeGetRequest(3);
// Settings
@@ -70,4 +70,36 @@ public class TestHttp2Timeouts extends H
Assert.assertEquals("3-RST-[11]\n", output.getTrace());
}
+ /*
+ * Timeout with app reading request body directly.
+ */
+ @Test
+ public void testClientPostsNoBody() throws Exception {
+ sendSimplePostRequest(3, null, false);
+
+ // Headers
+ parser.readFrame(false);
+ output.clearTrace();
+
+ parser.readFrame(false);
+
+ Assert.assertEquals("3-RST-[11]\n", output.getTrace());
+ }
+
+
+ /*
+ * Timeout with app processing parameters.
+ */
+ @Test
+ public void testClientPostsNoParameters() throws Exception {
+ sendParameterPostRequest(3, null, null, 10, false);
+
+ // Headers
+ parser.readFrame(false);
+ output.clearTrace();
+
+ parser.readFrame(false);
+
+ Assert.assertEquals("3-RST-[11]\n", output.getTrace());
+ }
}

View File

@ -1,38 +0,0 @@
--- tomcat/java/org/apache/coyote/http2/Http2Protocol.java 2019/02/01 10:28:14 1852700
+++ tomcat/java/org/apache/coyote/http2/Http2Protocol.java 2019/02/01 10:28:18 1852701
@@ -41,9 +41,9 @@
public class Http2Protocol implements UpgradeProtocol {
- static final long DEFAULT_READ_TIMEOUT = 10000;
- static final long DEFAULT_WRITE_TIMEOUT = 10000;
- static final long DEFAULT_KEEP_ALIVE_TIMEOUT = -1;
+ static final long DEFAULT_READ_TIMEOUT = 5000;
+ static final long DEFAULT_WRITE_TIMEOUT = 5000;
+ static final long DEFAULT_KEEP_ALIVE_TIMEOUT = 20000;
static final long DEFAULT_STREAM_READ_TIMEOUT = 20000;
static final long DEFAULT_STREAM_WRITE_TIMEOUT = 20000;
// The HTTP/2 specification recommends a minimum default of 100
--- tomcat/java/org/apache/coyote/http2/Http2UpgradeHandler.java 2019/02/01 10:28:14 1852700
+++ tomcat/java/org/apache/coyote/http2/Http2UpgradeHandler.java 2019/02/01 10:28:18 1852701
@@ -329,9 +329,16 @@
}
}
}
- // No more frames to read so switch to the keep-alive
- // timeout.
- socketWrapper.setReadTimeout(protocol.getKeepAliveTimeout());
+
+ if (activeRemoteStreamCount.get() == 0) {
+ // No streams currently active. Use the keep-alive
+ // timeout for the connection.
+ socketWrapper.setReadTimeout(protocol.getKeepAliveTimeout());
+ } else {
+ // Streams currently active. Individual streams have
+ // timeouts so keep the connection open.
+ socketWrapper.setReadTimeout(-1);
+ }
} catch (Http2Exception ce) {
// Really ConnectionException
if (log.isDebugEnabled()) {

View File

@ -1,143 +0,0 @@
--- tomcat/java/org/apache/coyote/http2/Http2Protocol.java 2019/02/01 10:28:18 1852701
+++ tomcat/java/org/apache/coyote/http2/Http2Protocol.java 2019/02/01 10:28:22 1852702
@@ -54,6 +54,8 @@
// This default is defined by the HTTP/2 specification
static final int DEFAULT_INITIAL_WINDOW_SIZE = (1 << 16) - 1;
+ static final int DEFAULT_OVERHEAD_COUNT_FACTOR = 1;
+
private static final String HTTP_UPGRADE_NAME = "h2c";
private static final String ALPN_NAME = "h2";
private static final byte[] ALPN_IDENTIFIER = ALPN_NAME.getBytes(StandardCharsets.UTF_8);
@@ -79,6 +81,8 @@
private int maxHeaderSize = Constants.DEFAULT_MAX_HEADER_SIZE;
private int maxTrailerCount = Constants.DEFAULT_MAX_TRAILER_COUNT;
private int maxTrailerSize = Constants.DEFAULT_MAX_TRAILER_SIZE;
+ private int overheadCountFactor = DEFAULT_OVERHEAD_COUNT_FACTOR;
+
private boolean initiatePingDisabled = false;
private boolean useSendfile = true;
// Compression
@@ -306,6 +310,16 @@
}
+ public int getOverheadCountFactor() {
+ return overheadCountFactor;
+ }
+
+
+ public void setOverheadCountFactor(int overheadCountFactor) {
+ this.overheadCountFactor = overheadCountFactor;
+ }
+
+
public void setInitiatePingDisabled(boolean initiatePingDisabled) {
this.initiatePingDisabled = initiatePingDisabled;
}
--- tomcat/java/org/apache/coyote/http2/Http2UpgradeHandler.java 2019/02/01 10:28:18 1852701
+++ tomcat/java/org/apache/coyote/http2/Http2UpgradeHandler.java 2019/02/01 10:28:22 1852702
@@ -30,6 +30,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.http.WebConnection;
@@ -139,6 +140,9 @@
private AtomicInteger streamConcurrency = null;
private Queue<StreamRunnable> queuedRunnable = null;
+ // Track 'overhead' frames vs 'request/response' frames
+ private final AtomicLong overheadCount = new AtomicLong(-10);
+
Http2UpgradeHandler(Http2Protocol protocol, Adapter adapter, Request coyoteRequest) {
super (STREAM_ID_ZERO);
@@ -330,6 +334,10 @@
}
}
+ if (overheadCount.get() > 0) {
+ throw new ConnectionException("Too much overhead", Http2Error.ENHANCE_YOUR_CALM);
+ }
+
if (activeRemoteStreamCount.get() == 0) {
// No streams currently active. Use the keep-alive
// timeout for the connection.
@@ -638,6 +646,9 @@
log.debug(sm.getString("upgradeHandler.writeBody", connectionId, stream.getIdentifier(),
Integer.toString(len)));
}
+
+ reduceOverheadCount();
+
// Need to check this now since sending end of stream will change this.
boolean writeable = stream.canWrite();
byte[] header = new byte[9];
@@ -1193,6 +1204,16 @@
}
+ private void reduceOverheadCount() {
+ overheadCount.decrementAndGet();
+ }
+
+
+ private void increaseOverheadCount() {
+ overheadCount.addAndGet(getProtocol().getOverheadCountFactor());
+ }
+
+
// ----------------------------------------------- Http2Parser.Input methods
@Override
@@ -1247,6 +1268,7 @@
@Override
public ByteBuffer startRequestBodyFrame(int streamId, int payloadSize) throws Http2Exception {
+ reduceOverheadCount();
Stream stream = getStream(streamId, true);
stream.checkState(FrameType.DATA);
stream.receivedData(payloadSize);
@@ -1291,6 +1313,8 @@
// determines if a new stream is created or if this stream is ignored.
checkPauseState();
+ reduceOverheadCount();
+
if (connectionState.get().isNewStreamAllowed()) {
Stream stream = getStream(streamId, false);
if (stream == null) {
@@ -1340,6 +1364,9 @@
throw new ConnectionException(sm.getString("upgradeHandler.dependency.invalid",
getConnectionId(), Integer.valueOf(streamId)), Http2Error.PROTOCOL_ERROR);
}
+
+ increaseOverheadCount();
+
Stream stream = getStream(streamId, false);
if (stream == null) {
stream = createRemoteStream(streamId);
@@ -1384,6 +1411,9 @@
@Override
public void setting(Setting setting, long value) throws ConnectionException {
+
+ increaseOverheadCount();
+
// Special handling required
if (setting == Setting.INITIAL_WINDOW_SIZE) {
long oldValue = remoteSettings.getInitialWindowSize();
@@ -1425,6 +1455,9 @@
@Override
public void pingReceive(byte[] payload, boolean ack) throws IOException {
+ if (!ack) {
+ increaseOverheadCount();
+ }
pingManager.receivePing(payload, ack);
}

View File

@ -1,51 +0,0 @@
--- tomcat/webapps/docs/config/http2.xml 2019/02/01 10:28:22 1852702
+++ tomcat/webapps/docs/config/http2.xml 2019/02/01 10:28:26 1852703
@@ -125,9 +125,9 @@
<attribute name="keepAliveTimeout" required="false">
<p>The time, in milliseconds, that Tomcat will wait between HTTP/2 frames
- before closing the connection. Negative values will be treated as an
- infinite timeout. If not specified, a default value of <code>-1</code>
- will be used.</p>
+ when there is no active Stream before closing the connection. Negative
+ values will be treated as an infinite timeout. If not specified, a default
+ value of <code>20000</code> will be used.</p>
</attribute>
<attribute name="maxConcurrentStreamExecution" required="false">
@@ -192,7 +192,24 @@
<p>The time, in milliseconds, that Tomcat will wait for additional data
when a partial HTTP/2 frame has been received. Negative values will be
treated as an infinite timeout. If not specified, a default value of
- <code>10000</code> will be used.</p>
+ <code>5000</code> will be used.</p>
+ </attribute>
+
+ <attribute name="streamReadTimeout" required="false">
+ <p>The time, in milliseconds, that Tomcat will wait for additional data
+ frames to arrive for the stream when an application is performing a
+ blocking I/O read and additional data is required. Negative values will be
+ treated as an infinite timeout. If not specified, a default value of
+ <code>20000</code> will be used.</p>
+ </attribute>
+
+ <attribute name="streamWriteTimeout" required="false">
+ <p>The time, in milliseconds, that Tomcat will wait for additional window
+ update frames to arrive for the stream and/or conenction when an
+ application is performing a blocking I/O write and the stream and/or
+ connection flow control window is too small for the write to complete.
+ Negative values will be treated as an infinite timeout. If not specified,
+ a default value of <code>20000</code> will be used.</p>
</attribute>
<attribute name="useSendfile" required="false">
@@ -204,7 +221,7 @@
<p>The time, in milliseconds, that Tomcat will wait to write additional
data when an HTTP/2 frame has been partially written. Negative values will
be treated as an infinite timeout. If not specified, a default value of
- <code>10000</code> will be used.</p>
+ <code>5000</code> will be used.</p>
</attribute>
</attributes>

View File

@ -1,32 +0,0 @@
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java 2019-06-09 20:59:53.027000000 -0400
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java 2019-06-09 21:02:31.878000000 -0400
@@ -211,7 +211,12 @@ class Stream extends AbstractStream impl
}
try {
if (block) {
- wait(handler.getProtocol().getStreamWriteTimeout());
+ long writeTimeout = handler.getProtocol().getStreamWriteTimeout();
+ if (writeTimeout < 0) {
+ wait();
+ } else {
+ wait(writeTimeout);
+ }
windowSize = getWindowSize();
if (windowSize == 0) {
String msg = sm.getString("stream.writeTimeout");
@@ -889,7 +894,12 @@ class Stream extends AbstractStream impl
log.debug(sm.getString("stream.inputBuffer.empty"));
}
- inBuffer.wait(handler.getProtocol().getStreamReadTimeout());
+ long readTimeout = handler.getProtocol().getStreamReadTimeout();
+ if (readTimeout < 0) {
+ inBuffer.wait();
+ } else {
+ inBuffer.wait(readTimeout);
+ }
if (reset) {
throw new IOException(sm.getString("stream.inputBuffer.reset"));

View File

@ -1,24 +0,0 @@
--- tomcat/java/org/apache/coyote/http2/Http2UpgradeHandler.java 2019/02/01 10:28:30 1852704
+++ tomcat/java/org/apache/coyote/http2/Http2UpgradeHandler.java 2019/02/01 10:28:34 1852705
@@ -335,7 +335,9 @@
}
if (overheadCount.get() > 0) {
- throw new ConnectionException("Too much overhead", Http2Error.ENHANCE_YOUR_CALM);
+ throw new ConnectionException(
+ sm.getString("upgradeHandler.tooMuchOverhead", connectionId),
+ Http2Error.ENHANCE_YOUR_CALM);
}
if (activeRemoteStreamCount.get() == 0) {
--- tomcat/java/org/apache/coyote/http2/LocalStrings.properties 2019/02/01 10:28:30 1852704
+++ tomcat/java/org/apache/coyote/http2/LocalStrings.properties 2019/02/01 10:28:34 1852705
@@ -141,6 +141,7 @@
upgradeHandler.stream.notWritable=Connection [{0}], Stream [{1}], This stream is not writable
upgradeHandler.stream.old=A new remote stream ID of [{0}] was requested but the most recent stream was [{1}]
upgradeHandler.tooManyRemoteStreams=The client attempted to use more than [{0}] active streams
+upgradeHandler.tooMuchOverhead=Connection [{0}], Too much overhead so the connection will be closed
upgradeHandler.unexpectedAck=Connection [{0}], Stream [{1}], A settings acknowledgement was received when not expected
upgradeHandler.unexpectedEos=Unexpected end of stream
upgradeHandler.upgrade=Connection [{0}], HTTP/1.1 upgrade to stream [1]

View File

@ -1,24 +0,0 @@
--- tomcat/webapps/docs/config/http2.xml 2019/02/01 10:28:34 1852705
+++ tomcat/webapps/docs/config/http2.xml 2019/02/01 10:28:38 1852706
@@ -188,6 +188,20 @@
The default value is an empty String (regexp matching disabled).</p>
</attribute>
+ <attribute name="overheadCountFactor" required="false">
+ <p>The factor to apply when counting overhead frames to determine if a
+ connection has too high an overhead and should be closed. The overhead
+ count starts at <code>-10</code>. The count is decreased for each
+ data frame sent or received and each headers frame received. The count is
+ increased by the <code>overheadCountFactor</code>for each setting
+ received, priority frame received and ping received. If the overhead count
+ exceeds zero, the connection is closed. A value of less than
+ <code>1</code> disables this protection. In normal usage a value of
+ <code>3</code> or more will close the connection before any streams can
+ complete. If not specified, a default value of <code>1</code> will be
+ used.</p>
+ </attribute>
+
<attribute name="readTimeout" required="false">
<p>The time, in milliseconds, that Tomcat will wait for additional data
when a partial HTTP/2 frame has been received. Negative values will be

View File

@ -1,44 +0,0 @@
From 15fcd166ea2c1bb79e8541b8e1a43da9c452ceea Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Mon, 11 Mar 2019 11:33:03 +0000
Subject: [PATCH] Escape debug output to aid readability
reason: Escape debug output to aid readability, fix CVE CVE-2019-0221
https://github.com/apache/tomcat/commit/15fcd16
---
java/org/apache/catalina/ssi/SSIPrintenv.java | 3 +--
webapps/docs/changelog.xml | 3 +++
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/java/org/apache/catalina/ssi/SSIPrintenv.java b/java/org/apache/catalina/ssi/SSIPrintenv.java
index 97470b2..092542f 100644
--- a/java/org/apache/catalina/ssi/SSIPrintenv.java
+++ b/java/org/apache/catalina/ssi/SSIPrintenv.java
@@ -41,8 +41,7 @@ public class SSIPrintenv implements SSICommand {
} else {
Collection<String> variableNames = ssiMediator.getVariableNames();
for (String variableName : variableNames) {
- String variableValue = ssiMediator
- .getVariableValue(variableName);
+ String variableValue = ssiMediator.getVariableValue(variableName, "entity");
//This shouldn't happen, since all the variable names must
// have values
if (variableValue == null) {
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 697cf07..cbd3961 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -52,6 +52,9 @@
<code>Expires</code> header as required by HTTP specification
(RFC 7231, 7234). (kkolinko)
</fix>
+ <fix>
+ Encode the output of the SSI <code>printenv</code> command. (markt)
+ </fix>
</changelog>
</subsection>
</section>
--
1.8.3.1

View File

@ -1,129 +0,0 @@
From 7f748eb6bfaba5207c89dbd7d5adf50fae847145 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 30 Apr 2019 22:18:12 +0100
Subject: [PATCH] Expand HTTP/2 timeout handling to connection window
exhaustion on write.
https://github.com/apache/tomcat/commit/7f748eb
---
.../coyote/http2/Http2UpgradeHandler.java | 32 +++++++++++++++++--
java/org/apache/coyote/http2/Stream.java | 27 +++++++++-------
webapps/docs/changelog.xml | 4 +++
3 files changed, 50 insertions(+), 13 deletions(-)
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
index 1d8770a..ab0369a 100644
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -794,7 +794,26 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
}
if (allocation == 0) {
try {
- stream.wait();
+ // Connection level window is empty. Although this
+ // request is for a stream, use the connection
+ // timeout
+ long writeTimeout = protocol.getWriteTimeout();
+ if (writeTimeout < 0) {
+ stream.wait();
+ } else {
+ stream.wait(writeTimeout);
+ }
+ // Has this stream been granted an allocation
+ int[] value = backLogStreams.get(stream);
+ if (value[1] == 0) {
+ // No allocation
+ // Close the connection. Do this first since
+ // closing the stream will raise an exception
+ close();
+ // Close the stream (in app code so need to
+ // signal to app stream is closing)
+ stream.doWriteTimeout();
+ }
} catch (InterruptedException e) {
throw new IOException(sm.getString(
"upgradeHandler.windowSizeReservationInterrupted", connectionId,
@@ -985,11 +1004,20 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
private void close() {
- connectionState.set(ConnectionState.CLOSED);
+ ConnectionState previous = connectionState.getAndSet(ConnectionState.CLOSED);
+ if (previous == ConnectionState.CLOSED) {
+ // Already closed
+ return;
+ }
+
for (Stream stream : streams.values()) {
// The connection is closing. Close the associated streams as no
// longer required.
stream.receiveReset(Http2Error.CANCEL.getCode());
+ // Release any streams waiting for an allocation
+ synchronized (stream) {
+ stream.notifyAll();
+ }
}
try {
socketWrapper.close();
diff --git a/java/org/apache/coyote/http2/Stream.java b/java/org/apache/coyote/http2/Stream.java
index 2c4f67e..8b87b12 100644
--- a/java/org/apache/coyote/http2/Stream.java
+++ b/java/org/apache/coyote/http2/Stream.java
@@ -219,17 +219,7 @@ class Stream extends AbstractStream implements HeaderEmitter {
}
windowSize = getWindowSize();
if (windowSize == 0) {
- String msg = sm.getString("stream.writeTimeout");
- StreamException se = new StreamException(
- msg, Http2Error.ENHANCE_YOUR_CALM, getIdentifier().intValue());
- // Prevent the application making further writes
- streamOutputBuffer.closed = true;
- // Prevent Tomcat's error handling trying to write
- coyoteResponse.setError();
- coyoteResponse.setErrorReported();
- // Trigger a reset once control returns to Tomcat
- streamOutputBuffer.reset = se;
- throw new CloseNowException(msg, se);
+ doWriteTimeout();
}
} else {
return 0;
@@ -252,6 +242,21 @@ class Stream extends AbstractStream implements HeaderEmitter {
}
+ void doWriteTimeout() throws CloseNowException {
+ String msg = sm.getString("stream.writeTimeout");
+ StreamException se = new StreamException(
+ msg, Http2Error.ENHANCE_YOUR_CALM, getIdentifier().intValue());
+ // Prevent the application making further writes
+ streamOutputBuffer.closed = true;
+ // Prevent Tomcat's error handling trying to write
+ coyoteResponse.setError();
+ coyoteResponse.setErrorReported();
+ // Trigger a reset once control returns to Tomcat
+ streamOutputBuffer.reset = se;
+ throw new CloseNowException(msg, se);
+ }
+
+
@Override
public final void emitHeader(String name, String value) throws HpackException {
if (log.isDebugEnabled()) {
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index a8abf2d..5665df4 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -362,6 +362,10 @@
<update>
Update the internal fork of Commons DBCP 2 to 2.4.0. (markt)
</update>
+ <fix>
+ Expand HTTP/2 timeout handling to include connection window exhaustion
+ on write. (markt)
+ </fix>
</changelog>
</subsection>
</section>
--
2.19.1

View File

@ -1,28 +0,0 @@
From ada725a50a60867af3422c8e612aecaeea856a9a Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Fri, 3 May 2019 21:52:41 +0100
Subject: [PATCH] Fix test failures. Handle full allocation case.
https://github.com/apache/tomcat/commit/ada725a
---
java/org/apache/coyote/http2/Http2UpgradeHandler.java | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
index ab0369a..cadae44 100644
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -804,8 +804,10 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
stream.wait(writeTimeout);
}
// Has this stream been granted an allocation
+ // Note: If the stream in not in this Map then the
+ // requested write has been fully allocated
int[] value = backLogStreams.get(stream);
- if (value[1] == 0) {
+ if (value != null && value[1] == 0) {
// No allocation
// Close the connection. Do this first since
// closing the stream will raise an exception
--
2.19.1

View File

@ -1,112 +0,0 @@
From 1fc9f589dbdd8295cf313b2667ab041c425f99c3 Mon Sep 17 00:00:00 2001
From: remm <remm@apache.org>
Date: Thu, 14 Nov 2019 13:39:31 +0100
Subject: [PATCH] Refactor JMX remote RMI registry creation
---
.../mbeans/JmxRemoteLifecycleListener.java | 65 ++++++++++++++-----
1 file changed, 49 insertions(+), 16 deletions(-)
diff --git a/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java b/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
index ae04294..e832935 100644
--- a/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
+++ b/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
@@ -25,10 +25,11 @@ import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
+import java.rmi.AccessException;
import java.rmi.AlreadyBoundException;
+import java.rmi.NotBoundException;
+import java.rmi.Remote;
import java.rmi.RemoteException;
-import java.rmi.registry.LocateRegistry;
-import java.rmi.registry.Registry;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.security.NoSuchAlgorithmException;
@@ -301,18 +302,6 @@ public class JmxRemoteLifecycleListener implements LifecycleListener {
RMIClientSocketFactory registryCsf, RMIServerSocketFactory registrySsf,
RMIClientSocketFactory serverCsf, RMIServerSocketFactory serverSsf) {
- // Create the RMI registry
- Registry registry;
- try {
- registry = LocateRegistry.createRegistry(
- theRmiRegistryPort, registryCsf, registrySsf);
- } catch (RemoteException e) {
- log.error(sm.getString(
- "jmxRemoteLifecycleListener.createRegistryFailed",
- serverName, Integer.toString(theRmiRegistryPort)), e);
- return null;
- }
-
if (bindAddress == null) {
bindAddress = "localhost";
}
@@ -333,11 +322,20 @@ public class JmxRemoteLifecycleListener implements LifecycleListener {
cs = new RMIConnectorServer(serviceUrl, theEnv, server,
ManagementFactory.getPlatformMBeanServer());
cs.start();
- registry.bind("jmxrmi", server.toStub());
+ Remote jmxServer = server.toStub();
+ // Create the RMI registry
+ try {
+ new JmxRegistry(theRmiRegistryPort, registryCsf, registrySsf, "jmxrmi", jmxServer);
+ } catch (RemoteException e) {
+ log.error(sm.getString(
+ "jmxRemoteLifecycleListener.createRegistryFailed",
+ serverName, Integer.toString(theRmiRegistryPort)), e);
+ return null;
+ }
log.info(sm.getString("jmxRemoteLifecycleListener.start",
Integer.toString(theRmiRegistryPort),
Integer.toString(theRmiServerPort), serverName));
- } catch (IOException | AlreadyBoundException e) {
+ } catch (IOException e) {
log.error(sm.getString(
"jmxRemoteLifecycleListener.createServerFailed",
serverName), e);
@@ -493,4 +491,39 @@ public class JmxRemoteLifecycleListener implements LifecycleListener {
return true;
}
}
+
+
+ private static class JmxRegistry extends sun.rmi.registry.RegistryImpl {
+ private static final long serialVersionUID = -3772054804656428217L;
+ private final String jmxName;
+ private final Remote jmxServer;
+ public JmxRegistry(int port, RMIClientSocketFactory csf,
+ RMIServerSocketFactory ssf, String jmxName, Remote jmxServer) throws RemoteException {
+ super(port, csf, ssf);
+ this.jmxName = jmxName;
+ this.jmxServer = jmxServer;
+ }
+ @Override
+ public Remote lookup(String name)
+ throws RemoteException, NotBoundException {
+ return (jmxName.equals(name)) ? jmxServer : null;
+ }
+ @Override
+ public void bind(String name, Remote obj)
+ throws RemoteException, AlreadyBoundException, AccessException {
+ }
+ @Override
+ public void unbind(String name)
+ throws RemoteException, NotBoundException, AccessException {
+ }
+ @Override
+ public void rebind(String name, Remote obj)
+ throws RemoteException, AccessException {
+ }
+ @Override
+ public String[] list() throws RemoteException {
+ return new String[] { jmxName };
+ }
+ }
+
}
--
2.23.0

View File

@ -1,137 +0,0 @@
From fabfa49abf917e126dbcf299fed40a1ab96d6f7a Mon Sep 17 00:00:00 2001
From: wang_yue111 <wangyue92@huawei.com>
Date: Fri, 15 May 2020 17:17:57 +0800
Subject: [PATCH] 2
---
.../authenticator/AuthenticatorBase.java | 7 ++--
.../catalina/authenticator/Constants.java | 3 ++
.../authenticator/FormAuthenticator.java | 36 +++++--------------
3 files changed, 16 insertions(+), 30 deletions(-)
diff --git a/java/org/apache/catalina/authenticator/AuthenticatorBase.java b/java/org/apache/catalina/authenticator/AuthenticatorBase.java
index 880ebde..47d562b 100644
--- a/java/org/apache/catalina/authenticator/AuthenticatorBase.java
+++ b/java/org/apache/catalina/authenticator/AuthenticatorBase.java
@@ -1021,10 +1021,11 @@ public abstract class AuthenticatorBase extends ValveBase
}
// Cache the authentication information in our session, if any
- if (cache) {
- if (session != null) {
+ if (session != null) {
+ if (cache) {
session.setAuthType(authType);
session.setPrincipal(principal);
+ } else {
if (username != null) {
session.setNote(Constants.SESS_USERNAME_NOTE, username);
} else {
diff --git a/java/org/apache/catalina/authenticator/Constants.java b/java/org/apache/catalina/authenticator/Constants.java
index 452a4f0..c9580d6 100644
--- a/java/org/apache/catalina/authenticator/Constants.java
+++ b/java/org/apache/catalina/authenticator/Constants.java
@@ -93,7 +93,10 @@ public class Constants {
/**
* The previously authenticated principal (if caching is disabled).
+ *
+ * @deprecated Unused. Will be removed in Tomcat 10.
*/
+ @Deprecated
public static final String FORM_PRINCIPAL_NOTE =
"org.apache.catalina.authenticator.PRINCIPAL";
diff --git a/java/org/apache/catalina/authenticator/FormAuthenticator.java b/java/org/apache/catalina/authenticator/FormAuthenticator.java
index 1b54ddd..44c783e 100644
--- a/java/org/apache/catalina/authenticator/FormAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/FormAuthenticator.java
@@ -133,10 +133,6 @@ public class FormAuthenticator
protected boolean doAuthenticate(Request request, HttpServletResponse response)
throws IOException {
- if (checkForCachedAuthentication(request, response, true)) {
- return true;
- }
-
// References to objects we will need later
Session session = null;
Principal principal = null;
@@ -158,11 +154,8 @@ public class FormAuthenticator
principal =
context.getRealm().authenticate(username, password);
if (principal != null) {
- session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
+ register(request, response, principal, HttpServletRequest.FORM_AUTH, username, password);
if (!matchRequest(request)) {
- register(request, response, principal,
- HttpServletRequest.FORM_AUTH,
- username, password);
return true;
}
}
@@ -181,17 +174,6 @@ public class FormAuthenticator
+ session.getIdInternal()
+ "'");
}
- principal = (Principal)
- session.getNote(Constants.FORM_PRINCIPAL_NOTE);
- register(request, response, principal, HttpServletRequest.FORM_AUTH,
- (String) session.getNote(Constants.SESS_USERNAME_NOTE),
- (String) session.getNote(Constants.SESS_PASSWORD_NOTE));
- // If we're caching principals we no longer need the username
- // and password in the session, so remove them
- if (cache) {
- session.removeNote(Constants.SESS_USERNAME_NOTE);
- session.removeNote(Constants.SESS_PASSWORD_NOTE);
- }
if (restoreRequest(request, session)) {
if (log.isDebugEnabled()) {
log.debug("Proceed to restored request");
@@ -206,6 +188,12 @@ public class FormAuthenticator
}
}
+ // This check has to be after the previous check for a matching request
+ // because that matching request may also include a cached Principal.
+ if (checkForCachedAuthentication(request, response, true)) {
+ return true;
+ }
+
// Acquire references to objects we will need to evaluate
String contextPath = request.getContextPath();
String requestURI = request.getDecodedRequestURI();
@@ -297,12 +285,7 @@ public class FormAuthenticator
return false;
}
- // Save the authenticated Principal in our session
- session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
-
- // Save the username and password as well
- session.setNote(Constants.SESS_USERNAME_NOTE, username);
- session.setNote(Constants.SESS_PASSWORD_NOTE, password);
+ register(request, response, principal, HttpServletRequest.FORM_AUTH, username, password);
// Redirect the user to the original request URI (which will cause
// the original request to be restored)
@@ -510,7 +493,7 @@ public class FormAuthenticator
}
// Is there a saved principal?
- if (session.getNote(Constants.FORM_PRINCIPAL_NOTE) == null) {
+ if (cache && session.getPrincipal() == null || !cache && request.getPrincipal() == null) {
return false;
}
@@ -541,7 +524,6 @@ public class FormAuthenticator
SavedRequest saved = (SavedRequest)
session.getNote(Constants.FORM_REQUEST_NOTE);
session.removeNote(Constants.FORM_REQUEST_NOTE);
- session.removeNote(Constants.FORM_PRINCIPAL_NOTE);
if (saved == null) {
return false;
}
--
2.23.0

View File

@ -1,100 +0,0 @@
From 9a0231683a77e2957cea0fdee88b193b30b0c976 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Fri, 22 May 2020 11:27:49 +0100
Subject: [PATCH] Fix BZ 64467. Improve performance of closing idle streams
---
.../coyote/http2/Http2UpgradeHandler.java | 10 +++---
.../coyote/http2/TestHttp2Section_5_1.java | 31 ++++++++++++++++---
webapps/docs/changelog.xml | 4 +++
3 files changed, 36 insertions(+), 9 deletions(-)
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
index bd836940fb..f0d5f27bda 100644
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -1343,11 +1343,11 @@ public HeaderEmitter headersStart(int streamId, boolean headersEndStream)
}
- private void closeIdleStreams(int newMaxActiveRemoteStreamId) throws Http2Exception {
- for (int i = maxActiveRemoteStreamId + 2; i < newMaxActiveRemoteStreamId; i += 2) {
- Stream stream = getStream(i, false);
- if (stream != null) {
- stream.closeIfIdle();
+ private void closeIdleStreams(int newMaxActiveRemoteStreamId) {
+ for (Entry<Integer,Stream> entry : streams.entrySet()) {
+ if (entry.getKey().intValue() > maxActiveRemoteStreamId &&
+ entry.getKey().intValue() < newMaxActiveRemoteStreamId) {
+ entry.getValue().closeIfIdle();
}
}
maxActiveRemoteStreamId = newMaxActiveRemoteStreamId;
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_5_1.java b/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
index 2a466814e1..f878653ecf 100644
--- a/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
+++ b/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
@@ -147,21 +147,44 @@ public void testClientSendOldStream() throws Exception {
@Test
public void testImplicitClose() throws Exception {
+ doTestImplicitClose(5);
+ }
+
+
+ // https://bz.apache.org/bugzilla/show_bug.cgi?id=64467
+ @Test
+ public void testImplicitCloseLargeId() throws Exception {
+ doTestImplicitClose(Integer.MAX_VALUE - 8);
+ }
+
+
+ private void doTestImplicitClose(int lastStreamId) throws Exception {
+
+ long startFirst = System.nanoTime();
http2Connect();
+ long durationFirst = System.nanoTime() - startFirst;
sendPriority(3, 0, 16);
- sendPriority(5, 0, 16);
+ sendPriority(lastStreamId, 0, 16);
- sendSimpleGetRequest(5);
+ long startSecond = System.nanoTime();
+ sendSimpleGetRequest(lastStreamId);
readSimpleGetResponse();
- Assert.assertEquals(getSimpleResponseTrace(5), output.getTrace());
+ long durationSecond = System.nanoTime() - startSecond;
+
+ Assert.assertEquals(getSimpleResponseTrace(lastStreamId), output.getTrace());
output.clearTrace();
+ // Allow second request to take up to 5 times first request or up to 1 second - whichever is the larger - mainly
+ // to allow for CI systems under load that can exhibit significant timing variation.
+ Assert.assertTrue("First request took [" + durationFirst/1000000 + "ms], second request took [" +
+ durationSecond/1000000 + "ms]", durationSecond < 1000000000 || durationSecond < durationFirst * 3);
+
// Should trigger an error since stream 3 should have been implicitly
// closed.
sendSimpleGetRequest(3);
- handleGoAwayResponse(5);
+ handleGoAwayResponse(lastStreamId);
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 5665df4..7b81937 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -1803,6 +1803,10 @@
HTTP 205 responses. Additional fix to r1795278. Based on a patch
provided by Alexandr Saperov. (violetagg)
</fix>
+ <fix>
+ <bug>64467</bug>: Improve performance of closing idle HTTP/2 streams.
+ (markt)
+ </fix>
<update>
<bug>61345</bug>: Add a server listener that can be used to do system
property replacement from the property source configured in the

View File

@ -1,53 +0,0 @@
From 172977f04a5215128f1e278a688983dcd230f399 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Fri, 26 Jun 2020 12:49:50 +0100
Subject: [PATCH] Ensure HTTP/1.1 processor is recycled after a direct h2c
connection
---
java/org/apache/coyote/AbstractProtocol.java | 9 ++++++---
webapps/docs/changelog.xml | 4 ++++
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/java/org/apache/coyote/AbstractProtocol.java b/java/org/apache/coyote/AbstractProtocol.java
index cb326dc12e..5bc2212549 100644
--- a/java/org/apache/coyote/AbstractProtocol.java
+++ b/java/org/apache/coyote/AbstractProtocol.java
@@ -772,8 +772,10 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
// Assume direct HTTP/2 connection
UpgradeProtocol upgradeProtocol = getProtocol().getUpgradeProtocol("h2c");
if (upgradeProtocol != null) {
- processor = upgradeProtocol.getProcessor(
- wrapper, getProtocol().getAdapter());
+ // Release the Http11 processor to be re-used
+ release(processor);
+ // Create the upgrade processor
+ processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter());
wrapper.unRead(leftOverInput);
// Associate with the processor with the connection
connections.put(socket, processor);
@@ -785,7 +785,8 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
"abstractConnectionHandler.negotiatedProcessor.fail",
"h2c"));
}
- return SocketState.CLOSED;
+ // Exit loop and trigger appropriate clean-up
+ state = SocketState.CLOSED;
}
} else {
HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 5665df4..60cd317 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -232,6 +236,10 @@
<fix>
Avoid unnecessary processing of async timeouts. (markt)
</fix>
+ <fix>
+ Ensure that the HTTP/1.1 processor is correctly recycled when a direct
+ connection to h2c is made. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Jasper">

View File

@ -1,61 +0,0 @@
From 40fa74c74822711ab878079d0a69f7357926723d Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Mon, 29 Jun 2020 14:02:59 +0100
Subject: [PATCH] Fix BZ 64563 - additional payload length validation
https://bz.apache.org/bugzilla/show_bug.cgi?id=64563
---
java/org/apache/tomcat/websocket/LocalStrings.properties | 1 +
java/org/apache/tomcat/websocket/WsFrameBase.java | 7 +++++++
webapps/docs/changelog.xml | 8 ++++++++
3 files changed, 16 insertions(+)
diff --git a/java/org/apache/tomcat/websocket/LocalStrings.properties b/java/org/apache/tomcat/websocket/LocalStrings.properties
index 9412ffeb61..929822d94c 100644
--- a/java/org/apache/tomcat/websocket/LocalStrings.properties
+++ b/java/org/apache/tomcat/websocket/LocalStrings.properties
@@ -70,6 +70,7 @@ wsFrame.noContinuation=A new message was started when a continuation frame was e
wsFrame.notMasked=The client frame was not masked but all client frames must be masked
wsFrame.oneByteCloseCode=The client sent a close frame with a single byte payload which is not valid
wsFrame.partialHeaderComplete=WebSocket frame received. fin [{0}], rsv [{1}], OpCode [{2}], payload length [{3}]
+wsFrame.payloadMsbInvalid=An invalid WebSocket frame was received - the most significant bit of a 64-bit payload was illegally set
wsFrame.sessionClosed=The client data cannot be processed because the session has already been closed
wsFrame.suspendRequested=Suspend of the message receiving has already been requested.
wsFrame.textMessageTooBig=The decoded text message was too big for the output buffer and the endpoint does not support partial messages
diff --git a/java/org/apache/tomcat/websocket/WsFrameBase.java b/java/org/apache/tomcat/websocket/WsFrameBase.java
index 28cdc30036..4afad67534 100644
--- a/java/org/apache/tomcat/websocket/WsFrameBase.java
+++ b/java/org/apache/tomcat/websocket/WsFrameBase.java
@@ -261,6 +261,13 @@ private boolean processRemainingHeader() throws IOException {
} else if (payloadLength == 127) {
payloadLength = byteArrayToLong(inputBuffer.array(),
inputBuffer.arrayOffset() + inputBuffer.position(), 8);
+ // The most significant bit of those 8 bytes is required to be zero
+ // (see RFC 6455, section 5.2). If the most significant bit is set,
+ // the resulting payload length will be negative so test for that.
+ if (payloadLength < 0) {
+ throw new WsIOException(
+ new CloseReason(CloseCodes.PROTOCOL_ERROR, sm.getString("wsFrame.payloadMsbInvalid")));
+ }
inputBuffer.position(inputBuffer.position() + 8);
}
if (Util.isControl(opCode)) {
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index e75f367171..1d1a735c7e 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -127,6 +127,14 @@
</fix>
</changelog>
</subsection>
+ <subsection name="WebSocket">
+ <changelog>
+ <fix>
+ <bug>64563</bug>: Add additional validation of payload length for
+ WebSocket messages. (markt)
+ </fix>
+ </changelog>
+ </subsection>
<subsection name="Other">
<changelog>
<fix>

View File

@ -1,97 +0,0 @@
From 863b18e34f12085820ad02e86ca0ef7e961bb471 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Wed, 7 Aug 2019 18:59:07 +0100
Subject: [PATCH 1/5] Extend the timeout functionality to internal upgrade
processors
---
.../coyote/http11/upgrade/InternalHttpUpgradeHandler.java | 2 ++
.../coyote/http11/upgrade/UpgradeProcessorInternal.java | 6 ++++++
java/org/apache/coyote/http2/Http2UpgradeHandler.java | 6 ++++++
.../tomcat/websocket/server/WsHttpUpgradeHandler.java | 6 ++++++
.../coyote/http11/upgrade/TestUpgradeInternalHandler.java | 5 +++++
5 files changed, 25 insertions(+)
diff --git a/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java b/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java
index 936784e20c..426b1bdb89 100644
--- a/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java
+++ b/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java
@@ -32,6 +32,8 @@ public interface InternalHttpUpgradeHandler extends HttpUpgradeHandler {
SocketState upgradeDispatch(SocketEvent status);
+ void timeoutAsync(long now);
+
void setSocketWrapper(SocketWrapperBase<?> wrapper);
void setSslSupport(SSLSupport sslSupport);
diff --git a/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java b/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java
index 6397a72a53..f0f546072d 100644
--- a/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java
+++ b/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java
@@ -73,6 +73,12 @@ public class UpgradeProcessorInternal extends UpgradeProcessorBase {
}
+ @Override
+ public void timeoutAsync(long now) {
+ internalHttpUpgradeHandler.timeoutAsync(now);
+ }
+
+
// --------------------------------------------------- AutoCloseable methods
@Override
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
index 6206f5f5f2..da724652aa 100644
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -388,6 +388,12 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
}
+ @Override
+ public void timeoutAsync(long now) {
+ // TODO: Implement improved connection timeouts
+ }
+
+
ConnectionSettingsRemote getRemoteSettings() {
return remoteSettings;
}
diff --git a/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java b/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
index 0cde0e3672..5dd1c5a68c 100644
--- a/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
+++ b/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
@@ -189,6 +189,12 @@ public class WsHttpUpgradeHandler implements InternalHttpUpgradeHandler {
}
+ @Override
+ public void timeoutAsync(long now) {
+ // NO-OP
+ }
+
+
@Override
public void pause() {
// NO-OP
diff --git a/test/org/apache/coyote/http11/upgrade/TestUpgradeInternalHandler.java b/test/org/apache/coyote/http11/upgrade/TestUpgradeInternalHandler.java
index 367f06f5b3..183a8dac7f 100644
--- a/test/org/apache/coyote/http11/upgrade/TestUpgradeInternalHandler.java
+++ b/test/org/apache/coyote/http11/upgrade/TestUpgradeInternalHandler.java
@@ -256,6 +256,11 @@ public class TestUpgradeInternalHandler extends TomcatBaseTest {
return SocketState.UPGRADED;
}
+ @Override
+ public void timeoutAsync(long now) {
+ // NO-OP
+ }
+
@Override
public void setSocketWrapper(SocketWrapperBase<?> wrapper) {
this.wrapper = wrapper;
--
2.23.0

View File

@ -1,82 +0,0 @@
From 38ef1f624aaf045458b6fe055742fa680a96a9e1 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Thu, 7 Mar 2019 10:50:05 +0000
Subject: [PATCH 2/5] Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63223
---
java/org/apache/coyote/http2/Http2UpgradeHandler.java | 8 ++++++++
java/org/apache/coyote/http2/Stream.java | 5 +++++
java/org/apache/coyote/http2/StreamStateMachine.java | 8 +++++++-
3 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
index da724652aa..2330d12e09 100644
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -555,6 +555,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
synchronized (socketWrapper) {
doWriteHeaders(stream, pushedStreamId, mimeHeaders, endOfStream, payloadSize);
}
+ stream.sentHeaders();
if (endOfStream) {
stream.sentEndOfStream();
}
@@ -1178,6 +1179,13 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
void push(Request request, Stream associatedStream) throws IOException {
+ if (localSettings.getMaxConcurrentStreams() < activeRemoteStreamCount.incrementAndGet()) {
+ // If there are too many open streams, simply ignore the push
+ // request.
+ activeRemoteStreamCount.decrementAndGet();
+ return;
+ }
+
Stream pushStream;
// Synchronized since PUSH_PROMISE frames have to be sent in order. Once
diff --git a/java/org/apache/coyote/http2/Stream.java b/java/org/apache/coyote/http2/Stream.java
index 43aee9d656..629d0210b4 100644
--- a/java/org/apache/coyote/http2/Stream.java
+++ b/java/org/apache/coyote/http2/Stream.java
@@ -561,6 +561,11 @@ class Stream extends AbstractStream implements HeaderEmitter {
}
+ final void sentHeaders() {
+ state.sentHeaders();
+ }
+
+
final void sentEndOfStream() {
streamOutputBuffer.endOfStreamSent = true;
state.sentEndOfStream();
diff --git a/java/org/apache/coyote/http2/StreamStateMachine.java b/java/org/apache/coyote/http2/StreamStateMachine.java
index 3b67f865d3..d19bb0a255 100644
--- a/java/org/apache/coyote/http2/StreamStateMachine.java
+++ b/java/org/apache/coyote/http2/StreamStateMachine.java
@@ -53,6 +53,12 @@ class StreamStateMachine {
}
+ final synchronized void sentHeaders() {
+ // No change if currently OPEN
+ stateChange(State.RESERVED_LOCAL, State.HALF_CLOSED_REMOTE);
+ }
+
+
final synchronized void receivedStartOfHeaders() {
stateChange(State.IDLE, State.OPEN);
stateChange(State.RESERVED_REMOTE, State.HALF_CLOSED_LOCAL);
@@ -170,7 +176,7 @@ class StreamStateMachine {
Http2Error.PROTOCOL_ERROR, FrameType.PRIORITY,
FrameType.RST,
FrameType.WINDOW_UPDATE),
- RESERVED_REMOTE (false, false, true, true,
+ RESERVED_REMOTE (false, true, true, true,
Http2Error.PROTOCOL_ERROR, FrameType.HEADERS,
FrameType.PRIORITY,
FrameType.RST),
--
2.23.0

View File

@ -1,220 +0,0 @@
From 5d7f2eac857cc75757cfc58d003fbf17a23c2720 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Wed, 7 Aug 2019 17:02:37 +0100
Subject: [PATCH 3/5] Improve HTTP/2 connection timeout handling
---
.../http2/Http2AsyncUpgradeHandler.java | 6 +-
.../coyote/http2/Http2UpgradeHandler.java | 93 ++++++++++++++-----
2 files changed, 73 insertions(+), 26 deletions(-)
diff --git a/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java b/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java
index ba49986b5b..107d4bedd2 100644
--- a/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java
@@ -191,7 +191,7 @@ public class Http2AsyncUpgradeHandler extends Http2UpgradeHandler {
header[4] = FLAG_END_OF_STREAM;
stream.sentEndOfStream();
if (!stream.isActive()) {
- activeRemoteStreamCount.decrementAndGet();
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
}
}
if (writeable) {
@@ -297,7 +297,7 @@ public class Http2AsyncUpgradeHandler extends Http2UpgradeHandler {
header[4] = FLAG_END_OF_STREAM;
sendfile.stream.sentEndOfStream();
if (!sendfile.stream.isActive()) {
- activeRemoteStreamCount.decrementAndGet();
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
}
}
if (writeable) {
@@ -358,7 +358,7 @@ public class Http2AsyncUpgradeHandler extends Http2UpgradeHandler {
header[4] = FLAG_END_OF_STREAM;
sendfile.stream.sentEndOfStream();
if (!sendfile.stream.isActive()) {
- activeRemoteStreamCount.decrementAndGet();
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
}
}
if (writeable) {
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
index 2330d12e09..9c18bf0ca8 100644
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -133,6 +133,9 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
// Tracking for when the connection is blocked (windowSize < 1)
private final Map<AbstractStream,int[]> backLogStreams = new ConcurrentHashMap<>();
private long backLogSize = 0;
+ // The time at which the connection will timeout unless data arrives before
+ // then. -1 means no timeout.
+ private volatile long connectionTimeout = -1;
// Stream concurrency control
private AtomicInteger streamConcurrency = null;
@@ -313,8 +316,10 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
case OPEN_READ:
try {
// There is data to read so use the read timeout while
- // reading frames.
+ // reading frames ...
socketWrapper.setReadTimeout(protocol.getReadTimeout());
+ // ... and disable the connection timeout
+ setConnectionTimeout(-1);
while (true) {
try {
if (!parser.readFrame(false)) {
@@ -330,23 +335,22 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
stream.close(se);
}
}
+ if (overheadCount.get() > 0) {
+ throw new ConnectionException(
+ sm.getString("upgradeHandler.tooMuchOverhead", connectionId),
+ Http2Error.ENHANCE_YOUR_CALM);
+ }
}
- if (overheadCount.get() > 0) {
- throw new ConnectionException(
- sm.getString("upgradeHandler.tooMuchOverhead", connectionId),
- Http2Error.ENHANCE_YOUR_CALM);
- }
+ // Need to know the correct timeout before starting the read
+ // but that may not be known at this time if one or more
+ // requests are currently being processed so don't set a
+ // timeout for the socket...
+ socketWrapper.setReadTimeout(-1);
+
+ // ...set a timeout on the connection
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.get());
- if (activeRemoteStreamCount.get() == 0) {
- // No streams currently active. Use the keep-alive
- // timeout for the connection.
- socketWrapper.setReadTimeout(protocol.getKeepAliveTimeout());
- } else {
- // Streams currently active. Individual streams have
- // timeouts so keep the connection open.
- socketWrapper.setReadTimeout(-1);
- }
} catch (Http2Exception ce) {
// Really ConnectionException
if (log.isDebugEnabled()) {
@@ -367,9 +371,12 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
result = SocketState.UPGRADED;
break;
+ case TIMEOUT:
+ closeConnection(null);
+ break;
+
case DISCONNECT:
case ERROR:
- case TIMEOUT:
case STOP:
close();
break;
@@ -388,9 +395,41 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
}
+ /*
+ * Sets the connection timeout based on the current number of active
+ * streams.
+ */
+ protected void setConnectionTimeoutForStreamCount(int streamCount) {
+ if (streamCount == 0) {
+ // No streams currently active. Use the keep-alive
+ // timeout for the connection.
+ long keepAliveTimeout = protocol.getKeepAliveTimeout();
+ if (keepAliveTimeout == -1) {
+ setConnectionTimeout(-1);
+ } else {
+ setConnectionTimeout(System.currentTimeMillis() + keepAliveTimeout);
+ }
+ } else {
+ // Streams currently active. Individual streams have
+ // timeouts so keep the connection open.
+ setConnectionTimeout(-1);
+ }
+ }
+
+
+ private void setConnectionTimeout(long connectionTimeout) {
+ this.connectionTimeout = connectionTimeout;
+ }
+
+
@Override
public void timeoutAsync(long now) {
- // TODO: Implement improved connection timeouts
+ long connectionTimeout = this.connectionTimeout;
+ if (now == -1 || connectionTimeout > -1 && now > connectionTimeout) {
+ // Have to dispatch as this will be executed from a non-container
+ // thread.
+ socketWrapper.processSocket(SocketEvent.TIMEOUT, true);
+ }
}
@@ -499,9 +538,17 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
void closeConnection(Http2Exception ce) {
+ long code;
+ byte[] msg;
+ if (ce == null) {
+ code = Http2Error.NO_ERROR.getCode();
+ msg = null;
+ } else {
+ code = ce.getError().getCode();
+ msg = ce.getMessage().getBytes(StandardCharsets.UTF_8);
+ }
try {
- writeGoAwayFrame(maxProcessedStreamId, ce.getError().getCode(),
- ce.getMessage().getBytes(StandardCharsets.UTF_8));
+ writeGoAwayFrame(maxProcessedStreamId, code, msg);
} catch (IOException ioe) {
// Ignore. GOAWAY is sent on a best efforts basis and the original
// error has already been logged.
@@ -665,7 +712,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
header[4] = FLAG_END_OF_STREAM;
stream.sentEndOfStream();
if (!stream.isActive()) {
- activeRemoteStreamCount.decrementAndGet();
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
}
}
if (writeable) {
@@ -1182,7 +1229,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
if (localSettings.getMaxConcurrentStreams() < activeRemoteStreamCount.incrementAndGet()) {
// If there are too many open streams, simply ignore the push
// request.
- activeRemoteStreamCount.decrementAndGet();
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
return;
}
@@ -1301,7 +1348,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
if (stream != null) {
stream.receivedEndOfStream();
if (!stream.isActive()) {
- activeRemoteStreamCount.decrementAndGet();
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
}
}
}
@@ -1340,7 +1387,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
stream.receivedStartOfHeaders(headersEndStream);
closeIdleStreams(streamId);
if (localSettings.getMaxConcurrentStreams() < activeRemoteStreamCount.incrementAndGet()) {
- activeRemoteStreamCount.decrementAndGet();
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
throw new StreamException(sm.getString("upgradeHandler.tooManyRemoteStreams",
Long.toString(localSettings.getMaxConcurrentStreams())),
Http2Error.REFUSED_STREAM, streamId);
--
2.23.0

View File

@ -1,114 +0,0 @@
From 902356dc34097b50eb6ca3a74443be466e991a77 Mon Sep 17 00:00:00 2001
From: wangxiao65 <287608437@qq.com>
Date: Wed, 2 Dec 2020 11:07:21 +0800
Subject: [PATCH] CVE-2020-13943
---
java/org/apache/coyote/http2/Http2Parser.java | 2 +-
.../coyote/http2/Http2UpgradeHandler.java | 20 ++++++++++++-------
.../coyote/http2/TestHttp2Section_5_1.java | 20 ++++++++++++-------
3 files changed, 27 insertions(+), 15 deletions(-)
diff --git a/java/org/apache/coyote/http2/Http2Parser.java b/java/org/apache/coyote/http2/Http2Parser.java
index f5e19865b2..6ec659d1e0 100644
--- a/java/org/apache/coyote/http2/Http2Parser.java
+++ b/java/org/apache/coyote/http2/Http2Parser.java
@@ -721,7 +721,7 @@ class Http2Parser {
// Header frames
HeaderEmitter headersStart(int streamId, boolean headersEndStream)
throws Http2Exception, IOException;
- void headersEnd(int streamId) throws ConnectionException;
+ void headersEnd(int streamId) throws Http2Exception;
// Priority frames (also headers)
void reprioritise(int streamId, int parentStreamId, boolean exclusive, int weight)
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
index 9c18bf0ca8..af10cd0838 100644
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -1386,12 +1386,6 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
stream.checkState(FrameType.HEADERS);
stream.receivedStartOfHeaders(headersEndStream);
closeIdleStreams(streamId);
- if (localSettings.getMaxConcurrentStreams() < activeRemoteStreamCount.incrementAndGet()) {
- setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
- throw new StreamException(sm.getString("upgradeHandler.tooManyRemoteStreams",
- Long.toString(localSettings.getMaxConcurrentStreams())),
- Http2Error.REFUSED_STREAM, streamId);
- }
return stream;
} else {
if (log.isDebugEnabled()) {
@@ -1439,12 +1433,24 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
@Override
- public void headersEnd(int streamId) throws ConnectionException {
+ public void headersEnd(int streamId) throws Http2Exception {
Stream stream = getStream(streamId, connectionState.get().isNewStreamAllowed());
if (stream != null) {
setMaxProcessedStream(streamId);
if (stream.isActive()) {
if (stream.receivedEndOfHeaders()) {
+
+ if (localSettings.getMaxConcurrentStreams() < activeRemoteStreamCount.incrementAndGet()) {
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
+ // Ignoring maxConcurrentStreams increases the overhead count
+ increaseOverheadCount();
+ throw new StreamException(sm.getString("upgradeHandler.tooManyRemoteStreams",
+ Long.toString(localSettings.getMaxConcurrentStreams())),
+ Http2Error.REFUSED_STREAM, streamId);
+ }
+ // Valid new stream reduces the overhead count
+ reduceOverheadCount();
+
processStreamOnContainerThread(stream);
}
}
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_5_1.java b/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
index f878653ecf..0b937a9fb9 100644
--- a/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
+++ b/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
@@ -222,11 +222,11 @@ public class TestHttp2Section_5_1 extends Http2TestBase {
// Expecting
// 1 * headers
// 56k-1 of body (7 * ~8k)
- // 1 * error (could be in any order)
- for (int i = 0; i < 8; i++) {
+ // 1 * error
+ // for a total of 9 frames (could be in any order)
+ for (int i = 0; i < 9; i++) {
parser.readFrame(true);
}
- parser.readFrame(true);
Assert.assertTrue(output.getTrace(),
output.getTrace().contains("5-RST-[" +
@@ -238,14 +238,20 @@ public class TestHttp2Section_5_1 extends Http2TestBase {
// Release the remaining body
sendWindowUpdate(0, (1 << 31) - 2);
- // Allow for the 8k still in the stream window
+ // Allow for the ~8k still in the stream window
sendWindowUpdate(3, (1 << 31) - 8193);
- // 192k of body (24 * 8k)
- // 1 * error (could be in any order)
- for (int i = 0; i < 24; i++) {
+ // Read until the end of stream 3
+ while (!output.getTrace().contains("3-EndOfStream")) {
parser.readFrame(true);
}
+ output.clearTrace();
+
+ // Confirm another request can be sent once concurrency falls back below limit
+ sendSimpleGetRequest(7);
+ parser.readFrame(true);
+ parser.readFrame(true);
+ Assert.assertEquals(getSimpleResponseTrace(7), output.getTrace());
}
--
2.23.0

View File

@ -1,47 +0,0 @@
From d56293f816d6dc9e2b47107f208fa9e95db58c65 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Mon, 9 Nov 2020 19:23:12 +0000
Subject: [PATCH] Fix BZ 64830 - concurrency issue in HPACK decoder
https://bz.apache.org/bugzilla/show_bug.cgi?id=64830
---
java/org/apache/coyote/http2/HpackDecoder.java | 12 ++++--------
1 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/java/org/apache/coyote/http2/HpackDecoder.java b/java/org/apache/coyote/http2/HpackDecoder.java
index 551101b33a..517dc0b4ae 100644
--- a/java/org/apache/coyote/http2/HpackDecoder.java
+++ b/java/org/apache/coyote/http2/HpackDecoder.java
@@ -72,8 +72,6 @@
private volatile boolean countedCookie;
private volatile int headerSize = 0;
- private final StringBuilder stringBuilder = new StringBuilder();
-
HpackDecoder(int maxMemorySize) {
this.maxMemorySizeHard = maxMemorySize;
this.maxMemorySizeSoft = maxMemorySize;
@@ -222,19 +220,17 @@ private String readHpackString(ByteBuffer buffer) throws HpackException {
if (huffman) {
return readHuffmanString(length, buffer);
}
+ StringBuilder stringBuilder = new StringBuilder(length);
for (int i = 0; i < length; ++i) {
stringBuilder.append((char) buffer.get());
}
- String ret = stringBuilder.toString();
- stringBuilder.setLength(0);
- return ret;
+ return stringBuilder.toString();
}
private String readHuffmanString(int length, ByteBuffer buffer) throws HpackException {
+ StringBuilder stringBuilder = new StringBuilder(length);
HPackHuffman.decode(buffer, length, stringBuilder);
- String ret = stringBuilder.toString();
- stringBuilder.setLength(0);
- return ret;
+ return stringBuilder.toString();
}
private String handleIndexedHeaderName(int index) throws HpackException {

View File

@ -1,443 +0,0 @@
From 8bfb0ff7f25fe7555a5eb2f7984f73546c11aa26 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Mon, 6 Jan 2020 20:53:25 +0000
Subject: [PATCH] Stricter header value parsing
---
.../coyote/http11/AbstractHttp11Protocol.java | 51 ++++++++++---
.../coyote/http11/Http11InputBuffer.java | 51 +++++++++----
.../apache/coyote/http11/Http11Processor.java | 2 +-
.../apache/tomcat/util/http/MimeHeaders.java | 2 +-
.../tomcat/util/http/parser/HttpParser.java | 11 +++
.../coyote/http11/TestHttp11InputBuffer.java | 74 +++++++++++++++----
webapps/docs/config/http.xml | 11 ++-
8 files changed, 164 insertions(+), 43 deletions(-)
diff --git a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
index e5ab885..9d10cbf 100644
--- a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
+++ b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
@@ -136,27 +136,56 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
}
- private boolean rejectIllegalHeaderName = true;
+ private boolean rejectIllegalHeader = true;
/**
- * If an HTTP request is received that contains an illegal header name (i.e.
- * the header name is not a token) will the request be rejected (with a 400
- * response) or will the illegal header be ignored.
+ * If an HTTP request is received that contains an illegal header name or
+ * value (e.g. the header name is not a token) will the request be rejected
+ * (with a 400 response) or will the illegal header be ignored?
*
* @return {@code true} if the request will be rejected or {@code false} if
* the header will be ignored
*/
- public boolean getRejectIllegalHeaderName() { return rejectIllegalHeaderName; }
+ public boolean getRejectIllegalHeader() { return rejectIllegalHeader; }
/**
- * If an HTTP request is received that contains an illegal header name (i.e.
- * the header name is not a token) should the request be rejected (with a
- * 400 response) or should the illegal header be ignored.
+ * If an HTTP request is received that contains an illegal header name or
+ * value (e.g. the header name is not a token) should the request be
+ * rejected (with a 400 response) or should the illegal header be ignored?
+ *
+ * @param rejectIllegalHeader {@code true} to reject requests with illegal
+ * header names or values, {@code false} to
+ * ignore the header
+ */
+ public void setRejectIllegalHeader(boolean rejectIllegalHeader) {
+ this.rejectIllegalHeader = rejectIllegalHeader;
+ }
+ /**
+ * If an HTTP request is received that contains an illegal header name or
+ * value (e.g. the header name is not a token) will the request be rejected
+ * (with a 400 response) or will the illegal header be ignored?
+ *
+ * @return {@code true} if the request will be rejected or {@code false} if
+ * the header will be ignored
+ *
+ * @deprecated Now an alias for {@link #getRejectIllegalHeader()}. Will be
+ * removed in Tomcat 10 onwards.
+ */
+ @Deprecated
+ public boolean getRejectIllegalHeaderName() { return rejectIllegalHeader; }
+ /**
+ * If an HTTP request is received that contains an illegal header name or
+ * value (e.g. the header name is not a token) should the request be
+ * rejected (with a 400 response) or should the illegal header be ignored?
*
* @param rejectIllegalHeaderName {@code true} to reject requests with
- * illegal header names, {@code false} to
- * ignore the header
+ * illegal header names or values,
+ * {@code false} to ignore the header
+ *
+ * @deprecated Now an alias for {@link #setRejectIllegalHeader(boolean)}.
+ * Will be removed in Tomcat 10 onwards.
*/
+ @Deprecated
public void setRejectIllegalHeaderName(boolean rejectIllegalHeaderName) {
- this.rejectIllegalHeaderName = rejectIllegalHeaderName;
+ this.rejectIllegalHeader = rejectIllegalHeaderName;
}
diff --git a/java/org/apache/coyote/http11/Http11InputBuffer.java b/java/org/apache/coyote/http11/Http11InputBuffer.java
index 2dc7c17..57c670e 100644
--- a/java/org/apache/coyote/http11/Http11InputBuffer.java
+++ b/java/org/apache/coyote/http11/Http11InputBuffer.java
@@ -64,7 +64,7 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
private final MimeHeaders headers;
- private final boolean rejectIllegalHeaderName;
+ private final boolean rejectIllegalHeader;
/**
* State.
@@ -150,13 +150,13 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
// ----------------------------------------------------------- Constructors
public Http11InputBuffer(Request request, int headerBufferSize,
- boolean rejectIllegalHeaderName, HttpParser httpParser) {
+ boolean rejectIllegalHeader, HttpParser httpParser) {
this.request = request;
headers = request.getMimeHeaders();
this.headerBufferSize = headerBufferSize;
- this.rejectIllegalHeaderName = rejectIllegalHeaderName;
+ this.rejectIllegalHeader = rejectIllegalHeader;
this.httpParser = httpParser;
filterLibrary = new InputFilter[0];
@@ -752,6 +752,8 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
//
byte chr = 0;
+ byte prevChr = 0;
+
while (headerParsePos == HeaderParsePosition.HEADER_START) {
// Read new bytes if needed
@@ -762,17 +764,23 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
}
}
+ prevChr = chr;
chr = byteBuffer.get();
- if (chr == Constants.CR) {
- // Skip
- } else if (chr == Constants.LF) {
+ if (chr == Constants.CR && prevChr != Constants.CR) {
+ // Possible start of CRLF - process the next byte.
+ } else if (prevChr == Constants.CR && chr == Constants.LF) {
return HeaderParseStatus.DONE;
} else {
- byteBuffer.position(byteBuffer.position() - 1);
+ if (prevChr == 0) {
+ // Must have only read one byte
+ byteBuffer.position(byteBuffer.position() - 1);
+ } else {
+ // Must have read two bytes (first was CR, second was not LF)
+ byteBuffer.position(byteBuffer.position() - 2);
+ }
break;
}
-
}
if (headerParsePos == HeaderParsePosition.HEADER_START) {
@@ -868,11 +876,22 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
}
}
+ prevChr = chr;
chr = byteBuffer.get();
if (chr == Constants.CR) {
- // Skip
- } else if (chr == Constants.LF) {
+ // Possible start of CRLF - process the next byte.
+ } 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();
+ } 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();
} else if (chr == Constants.SP || chr == Constants.HT) {
byteBuffer.put(headerData.realPos, chr);
headerData.realPos++;
@@ -924,6 +943,9 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
headerParsePos = HeaderParsePosition.HEADER_SKIPLINE;
boolean eol = false;
+ byte chr = 0;
+ byte prevChr = 0;
+
// Reading bytes until the end of the line
while (!eol) {
@@ -935,21 +957,22 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
}
int pos = byteBuffer.position();
- byte chr = byteBuffer.get();
+ prevChr = chr;
+ chr = byteBuffer.get();
if (chr == Constants.CR) {
// Skip
- } else if (chr == Constants.LF) {
+ } else if (prevChr == Constants.CR && chr == Constants.LF) {
eol = true;
} else {
headerData.lastSignificantChar = pos;
}
}
- if (rejectIllegalHeaderName || log.isDebugEnabled()) {
+ if (rejectIllegalHeader || log.isDebugEnabled()) {
String message = sm.getString("iib.invalidheader",
new String(byteBuffer.array(), headerData.start,
headerData.lastSignificantChar - headerData.start + 1,
StandardCharsets.ISO_8859_1));
- if (rejectIllegalHeaderName) {
+ if (rejectIllegalHeader) {
throw new IllegalArgumentException(message);
}
log.debug(message);
diff --git a/java/org/apache/coyote/http11/Http11Processor.java b/java/org/apache/coyote/http11/Http11Processor.java
index df002e0..86556ec 100644
--- a/java/org/apache/coyote/http11/Http11Processor.java
+++ b/java/org/apache/coyote/http11/Http11Processor.java
@@ -153,7 +153,7 @@ public class Http11Processor extends AbstractProcessor {
protocol.getRelaxedQueryChars());
inputBuffer = new Http11InputBuffer(request, protocol.getMaxHttpHeaderSize(),
- protocol.getRejectIllegalHeaderName(), httpParser);
+ protocol.getRejectIllegalHeader(), httpParser);
request.setInputBuffer(inputBuffer);
outputBuffer = new Http11OutputBuffer(response, protocol.getMaxHttpHeaderSize());
diff --git a/java/org/apache/tomcat/util/http/MimeHeaders.java b/java/org/apache/tomcat/util/http/MimeHeaders.java
index 59504ee..b76b274 100644
--- a/java/org/apache/tomcat/util/http/MimeHeaders.java
+++ b/java/org/apache/tomcat/util/http/MimeHeaders.java
@@ -396,7 +396,7 @@ public class MimeHeaders {
* reset and swap with last header
* @param idx the index of the header to remove.
*/
- private void removeHeader(int idx) {
+ public void removeHeader(int idx) {
MimeHeaderField mh = headers[idx];
mh.recycle();
diff --git a/java/org/apache/tomcat/util/http/parser/HttpParser.java b/java/org/apache/tomcat/util/http/parser/HttpParser.java
index 827f2c5..644b1d1 100644
--- a/java/org/apache/tomcat/util/http/parser/HttpParser.java
+++ b/java/org/apache/tomcat/util/http/parser/HttpParser.java
@@ -317,6 +317,17 @@ public class HttpParser {
}
+ public static boolean isControl(int c) {
+ // Fast for valid control characters, slower for some incorrect
+ // ones
+ try {
+ return IS_CONTROL[c];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ return false;
+ }
+ }
+
+
// Skip any LWS and position to read the next character. The next character
// is returned as being able to 'peek()' it allows a small optimisation in
// some cases.
diff --git a/test/org/apache/coyote/http11/TestHttp11InputBuffer.java b/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
index 131fa21..e60a474 100644
--- a/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
+++ b/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
@@ -163,13 +163,13 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
@Test
- public void testBug51557Separators() throws Exception {
+ public void testBug51557SeparatorsInName() throws Exception {
char httpSeparators[] = new char[] {
'\t', ' ', '\"', '(', ')', ',', '/', ':', ';', '<',
'=', '>', '?', '@', '[', '\\', ']', '{', '}' };
for (char s : httpSeparators) {
- doTestBug51557Char(s);
+ doTestBug51557CharInName(s);
tearDown();
setUp();
}
@@ -177,13 +177,38 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
@Test
- public void testBug51557Ctl() throws Exception {
+ public void testBug51557CtlInName() throws Exception {
for (int i = 0; i < 31; i++) {
- doTestBug51557Char((char) i);
+ doTestBug51557CharInName((char) i);
+ tearDown();
+ setUp();
+ }
+ doTestBug51557CharInName((char) 127);
+ }
+
+
+ @Test
+ public void testBug51557CtlInValue() throws Exception {
+ for (int i = 0; i < 31; i++) {
+ if (i == '\t') {
+ // TAB is allowed
+ continue;
+ }
+ doTestBug51557InvalidCharInValue((char) i);
+ tearDown();
+ setUp();
+ }
+ doTestBug51557InvalidCharInValue((char) 127);
+ }
+
+
+ @Test
+ public void testBug51557ObsTextInValue() throws Exception {
+ for (int i = 128; i < 255; i++) {
+ doTestBug51557ValidCharInValue((char) i);
tearDown();
setUp();
}
- doTestBug51557Char((char) 127);
}
@@ -226,7 +251,7 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
}
- private void doTestBug51557Char(char s) {
+ private void doTestBug51557CharInName(char s) {
Bug51557Client client =
new Bug51557Client("X-Bug" + s + "51557", "invalid");
@@ -236,6 +261,29 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
Assert.assertTrue(client.isResponseBodyOK());
}
+
+ private void doTestBug51557InvalidCharInValue(char s) {
+ Bug51557Client client =
+ new Bug51557Client("X-Bug51557-Invalid", "invalid" + s + "invalid");
+
+ client.doRequest();
+ Assert.assertTrue("Testing [" + (int) s + "]", client.isResponse200());
+ Assert.assertEquals("Testing [" + (int) s + "]", "abcd", client.getResponseBody());
+ Assert.assertTrue(client.isResponseBodyOK());
+ }
+
+
+ private void doTestBug51557ValidCharInValue(char s) {
+ Bug51557Client client =
+ new Bug51557Client("X-Bug51557-Valid", "valid" + s + "valid");
+
+ client.doRequest();
+ Assert.assertTrue("Testing [" + (int) s + "]", client.isResponse200());
+ Assert.assertEquals("Testing [" + (int) s + "]", "valid" + s + "validabcd", client.getResponseBody());
+ Assert.assertTrue(client.isResponseBodyOK());
+ }
+
+
/**
* Bug 51557 test client.
*/
@@ -243,12 +291,12 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
private final String headerName;
private final String headerLine;
- private final boolean rejectIllegalHeaderName;
+ private final boolean rejectIllegalHeader;
public Bug51557Client(String headerName) {
this.headerName = headerName;
this.headerLine = headerName;
- this.rejectIllegalHeaderName = false;
+ this.rejectIllegalHeader = false;
}
public Bug51557Client(String headerName, String headerValue) {
@@ -256,10 +304,10 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
}
public Bug51557Client(String headerName, String headerValue,
- boolean rejectIllegalHeaderName) {
+ boolean rejectIllegalHeader) {
this.headerName = headerName;
this.headerLine = headerName + ": " + headerValue;
- this.rejectIllegalHeaderName = rejectIllegalHeaderName;
+ this.rejectIllegalHeader = rejectIllegalHeader;
}
private Exception doRequest() {
@@ -273,8 +321,8 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
try {
Connector connector = tomcat.getConnector();
- connector.setProperty("rejectIllegalHeaderName",
- Boolean.toString(rejectIllegalHeaderName));
+ connector.setProperty("rejectIllegalHeader",
+ Boolean.toString(rejectIllegalHeader));
tomcat.start();
setPort(connector.getLocalPort());
@@ -548,7 +596,7 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
try {
Connector connector = tomcat.getConnector();
- connector.setProperty("rejectIllegalHeaderName", "false");
+ connector.setProperty("rejectIllegalHeader", "false");
tomcat.start();
setPort(connector.getLocalPort());
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index ebb277d..3902c9a 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -551,14 +551,19 @@
expected concurrent requests (synchronous and asynchronous).</p>
</attribute>
- <attribute name="rejectIllegalHeaderName" required="false">
- <p>If an HTTP request is received that contains an illegal header name
- (i.e. the header name is not a token) this setting determines if the
+ <attribute name="rejectIllegalHeader" required="false">
+ <p>If an HTTP request is received that contains an illegal header name or
+ value (e.g. the header name is not a token) this setting determines if the
request will be rejected with a 400 response (<code>true</code>) or if the
illegal header be ignored (<code>false</code>). The default value is
<code>true</code> which will cause the request to be rejected.</p>
</attribute>
+ <attribute name="rejectIllegalHeaderName" required="false">
+ <p>This attribute is deprecated. It will be removed in Tomcat 10 onwards.
+ It is now an alias for <strong>rejectIllegalHeader</strong>.</p>
+ </attribute>
+
<attribute name="relaxedPathChars" required="false">
<p>The <a href="https://tools.ietf.org/rfc/rfc7230.txt">HTTP/1.1
specification</a> requires that certain characters are %nn encoded when
--
2.23.0

View File

@ -1,50 +0,0 @@
From 0e8a50f0a5958744bea1fd6768c862e04d3b7e75 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 21 Jan 2020 13:02:13 +0000
Subject: [PATCH] Change the default bind address for AJP to the loopback
address
---
java/org/apache/coyote/ajp/AbstractAjpProtocol.java | 4 ++++
webapps/docs/changelog.xml | 4 ++++
webapps/docs/config/ajp.xml | 5 +----
3 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
index 2500abd7ad..8e0593b771 100644
--- a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
+++ b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
@@ -16,6 +16,8 @@
*/
package org.apache.coyote.ajp;
+import java.net.InetAddress;
+
import org.apache.coyote.AbstractProtocol;
import org.apache.coyote.Processor;
import org.apache.coyote.UpgradeProtocol;
@@ -46,6 +48,8 @@ public AbstractAjpProtocol(AbstractEndpoint<S,?> endpoint) {
setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
// AJP does not use Send File
getEndpoint().setUseSendfile(false);
+ // AJP listens on loopback by default
+ getEndpoint().setAddress(InetAddress.getLoopbackAddress());
ConnectionHandler<S> cHandler = new ConnectionHandler<>(this);
setHandler(cHandler);
getEndpoint().setHandler(cHandler);
diff --git a/webapps/docs/config/ajp.xml b/webapps/docs/config/ajp.xml
index c70af91eae..5535a062e7 100644
--- a/webapps/docs/config/ajp.xml
+++ b/webapps/docs/config/ajp.xml
@@ -308,10 +308,7 @@
<attribute name="address" required="false">
<p>For servers with more than one IP address, this attribute
specifies which address will be used for listening on the specified
- port. By default, this port will be used on all IP addresses
- associated with the server. A value of <code>127.0.0.1</code>
- indicates that the Connector will only listen on the loopback
- interface.</p>
+ port. By default, the loopback address will be used.</p>
</attribute>
<attribute name="bindOnInit" required="false">

View File

@ -1,160 +0,0 @@
From 9ac90532e9a7d239f90952edb229b07c80a9a3eb Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 21 Jan 2020 14:24:33 +0000
Subject: [PATCH] Rename requiredSecret to secret and add secretRequired
AJP Connector will not start if secretRequired="true" and secret is set
to null or zero length String.
---
.../coyote/ajp/AbstractAjpProtocol.java | 49 +++++++++++++++++--
java/org/apache/coyote/ajp/AjpProcessor.java | 12 ++---
.../apache/coyote/ajp/LocalStrings.properties | 1 +
webapps/docs/config/ajp.xml | 12 ++++-
5 files changed, 72 insertions(+), 10 deletions(-)
diff --git a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
index 8e0593b771..81da7da6d1 100644
--- a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
+++ b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
@@ -143,17 +143,48 @@ public void setTomcatAuthorization(boolean tomcatAuthorization) {
}
- private String requiredSecret = null;
+ private String secret = null;
+ /**
+ * Set the secret that must be included with every request.
+ *
+ * @param secret The required secret
+ */
+ public void setSecret(String secret) {
+ this.secret = secret;
+ }
+ protected String getSecret() {
+ return secret;
+ }
/**
* Set the required secret that must be included with every request.
*
* @param requiredSecret The required secret
+ *
+ * @deprecated Replaced by {@link #setSecret(String)}.
+ * Will be removed in Tomcat 11 onwards
*/
+ @Deprecated
public void setRequiredSecret(String requiredSecret) {
- this.requiredSecret = requiredSecret;
+ setSecret(requiredSecret);
}
+ /**
+ * @return The current secret
+ *
+ * @deprecated Replaced by {@link #getSecret()}.
+ * Will be removed in Tomcat 11 onwards
+ */
+ @Deprecated
protected String getRequiredSecret() {
- return requiredSecret;
+ return getSecret();
+ }
+
+
+ private boolean secretRequired = true;
+ public void setSecretRequired(boolean secretRequired) {
+ this.secretRequired = secretRequired;
+ }
+ public boolean getSecretRequired() {
+ return secretRequired;
}
@@ -210,4 +241,16 @@ protected Processor createUpgradeProcessor(SocketWrapperBase<?> socket,
throw new IllegalStateException(sm.getString("ajpprotocol.noUpgradeHandler",
upgradeToken.getHttpUpgradeHandler().getClass().getName()));
}
+
+
+ @Override
+ public void init() throws Exception {
+ if (getSecretRequired()) {
+ String secret = getSecret();
+ if (secret == null || secret.length() == 0) {
+ throw new IllegalArgumentException(sm.getString("ajpprotocol.nosecret"));
+ }
+ }
+ super.init();
+ }
}
diff --git a/java/org/apache/coyote/ajp/AjpProcessor.java b/java/org/apache/coyote/ajp/AjpProcessor.java
index a3e628d2eb..d466de2c09 100644
--- a/java/org/apache/coyote/ajp/AjpProcessor.java
+++ b/java/org/apache/coyote/ajp/AjpProcessor.java
@@ -698,8 +698,8 @@ private void prepareRequest() {
}
// Decode extra attributes
- String requiredSecret = protocol.getRequiredSecret();
- boolean secret = false;
+ String secret = protocol.getSecret();
+ boolean secretPresentInRequest = false;
byte attributeCode;
while ((attributeCode = requestHeaderMessage.getByte())
!= Constants.SC_A_ARE_DONE) {
@@ -801,9 +801,9 @@ private void prepareRequest() {
case Constants.SC_A_SECRET:
requestHeaderMessage.getBytes(tmpMB);
- if (requiredSecret != null) {
- secret = true;
- if (!tmpMB.equals(requiredSecret)) {
+ if (secret != null) {
+ secretPresentInRequest = true;
+ if (!tmpMB.equals(secret)) {
response.setStatus(403);
setErrorState(ErrorState.CLOSE_CLEAN, null);
}
@@ -819,7 +819,7 @@ private void prepareRequest() {
}
// Check if secret was submitted if required
- if ((requiredSecret != null) && !secret) {
+ if ((secret != null) && !secretPresentInRequest) {
response.setStatus(403);
setErrorState(ErrorState.CLOSE_CLEAN, null);
}
diff --git a/java/org/apache/coyote/ajp/LocalStrings.properties b/java/org/apache/coyote/ajp/LocalStrings.properties
index 9b569bbc6e..01de92a424 100644
--- a/java/org/apache/coyote/ajp/LocalStrings.properties
+++ b/java/org/apache/coyote/ajp/LocalStrings.properties
@@ -28,6 +28,7 @@ ajpprocessor.request.prepare=Error preparing request
# limitations under the License.
ajpprotocol.noSSL=SSL is not supported with AJP. The SSL host configuration for [{0}] was ignored
+ajpprotocol.nosecret=The AJP Connector is configured with secretRequired="true" but the secret attribute is either null or "". This combination is not valid.
ajpprotocol.noUpgrade=Upgrade is not supported with AJP. The UpgradeProtocol configuration for [{0}] was ignored
ajpprotocol.noUpgradeHandler=Upgrade is not supported with AJP. The HttpUpgradeHandler [{0}] can not be processed
diff --git a/webapps/docs/config/ajp.xml b/webapps/docs/config/ajp.xml
index 5535a062e7..3999a13e66 100644
--- a/webapps/docs/config/ajp.xml
+++ b/webapps/docs/config/ajp.xml
@@ -428,8 +428,18 @@
expected concurrent requests (synchronous and asynchronous).</p>
</attribute>
- <attribute name="requiredSecret" required="false">
+ <attribute name="secret" required="false">
<p>Only requests from workers with this secret keyword will be accepted.
+ The default value is <code>null</code>. This attrbute must be specified
+ with a non-null, non-zero length value unless
+ <strong>secretRequired</strong> is explicitly configured to be
+ <code>false</code>.</p>
+ </attribute>
+
+ <attribute name="secretRequired" required="false">
+ <p>If this attribute is <code>true</code>, the AJP Connector will only
+ start if the <strong>secret</strong> attribute is configured with a
+ non-null, non-zero length value. The default value is <code>true</code>.
</p>
</attribute>

View File

@ -1,142 +0,0 @@
From 64fa5b99442589ef0bf2a7fcd71ad2bc68b35fad Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 21 Jan 2020 15:04:12 +0000
Subject: [PATCH] Add new AJP attribute allowedArbitraryRequestAttributes
Requests with unrecognised attributes will be blocked with a 403
---
.../coyote/ajp/AbstractAjpProtocol.java | 13 +++++++
java/org/apache/coyote/ajp/AjpProcessor.java | 36 ++++++++++++++++++-
webapps/docs/config/ajp.xml | 19 ++++++++++
4 files changed, 72 insertions(+), 1 deletion(-)
diff --git a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
index 81da7da6d1..a2f5e2844a 100644
--- a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
+++ b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
@@ -17,6 +17,7 @@
package org.apache.coyote.ajp;
import java.net.InetAddress;
+import java.util.regex.Pattern;
import org.apache.coyote.AbstractProtocol;
import org.apache.coyote.Processor;
@@ -188,6 +189,18 @@ public boolean getSecretRequired() {
}
+ private Pattern allowedArbitraryRequestAttributesPattern;
+ public void setAllowedArbitraryRequestAttributes(String allowedArbitraryRequestAttributes) {
+ this.allowedArbitraryRequestAttributesPattern = Pattern.compile(allowedArbitraryRequestAttributes);
+ }
+ public String getAllowedArbitraryRequestAttributes() {
+ return allowedArbitraryRequestAttributesPattern.pattern();
+ }
+ protected Pattern getAllowedArbitraryRequestAttributesPattern() {
+ return allowedArbitraryRequestAttributesPattern;
+ }
+
+
/**
* AJP packet size.
*/
diff --git a/java/org/apache/coyote/ajp/AjpProcessor.java b/java/org/apache/coyote/ajp/AjpProcessor.java
index d466de2c09..f3d783f546 100644
--- a/java/org/apache/coyote/ajp/AjpProcessor.java
+++ b/java/org/apache/coyote/ajp/AjpProcessor.java
@@ -25,6 +25,11 @@
import java.security.NoSuchProviderException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.servlet.http.HttpServletResponse;
@@ -78,6 +83,9 @@
private static final byte[] pongMessageArray;
+ private static final Set<String> javaxAttributes;
+
+
static {
// Allocate the end message array
AjpMessage endMessage = new AjpMessage(16);
@@ -118,6 +126,14 @@
pongMessageArray = new byte[pongMessage.getLen()];
System.arraycopy(pongMessage.getBuffer(), 0, pongMessageArray,
0, pongMessage.getLen());
+
+ // Build the Set of javax attributes
+ Set<String> s = new HashSet<>();
+ s.add("javax.servlet.request.cipher_suite");
+ s.add("javax.servlet.request.key_size");
+ s.add("javax.servlet.request.ssl_session");
+ s.add("javax.servlet.request.X509Certificate");
+ javaxAttributes= Collections.unmodifiableSet(s);
}
@@ -728,8 +744,26 @@ private void prepareRequest() {
}
} else if(n.equals(Constants.SC_A_SSL_PROTOCOL)) {
request.setAttribute(SSLSupport.PROTOCOL_VERSION_KEY, v);
+ } else if (n.equals("JK_LB_ACTIVATION")) {
+ request.setAttribute(n, v);
+ } else if (javaxAttributes.contains(n)) {
+ request.setAttribute(n, v);
} else {
- request.setAttribute(n, v );
+ // All 'known' attributes will be processed by the previous
+ // blocks. Any remaining attribute is an 'arbitrary' one.
+ Pattern pattern = protocol.getAllowedArbitraryRequestAttributesPattern();
+ if (pattern == null) {
+ response.setStatus(403);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ } else {
+ Matcher m = pattern.matcher(n);
+ if (m.matches()) {
+ request.setAttribute(n, v);
+ } else {
+ response.setStatus(403);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ }
+ }
}
break;
diff --git a/webapps/docs/config/ajp.xml b/webapps/docs/config/ajp.xml
index 3999a13e66..69348a18e2 100644
--- a/webapps/docs/config/ajp.xml
+++ b/webapps/docs/config/ajp.xml
@@ -311,6 +311,25 @@
port. By default, the loopback address will be used.</p>
</attribute>
+ <attribute name="allowedArbitraryRequestAttributes" required="false">
+ <p>The AJP protocol passes some information from the reverse proxy to the
+ AJP connector using request attributes. These attributes are:</p>
+ <ul>
+ <li>javax.servlet.request.cipher_suite</li>
+ <li>javax.servlet.request.key_size</li>
+ <li>javax.servlet.request.ssl_session</li>
+ <li>javax.servlet.request.X509Certificate</li>
+ <li>AJP_LOCAL_ADDR</li>
+ <li>AJP_REMOTE_PORT</li>
+ <li>AJP_SSL_PROTOCOL</li>
+ <li>JK_LB_ACTIVATION</li>
+ </ul>
+ <p>The AJP protocol supports the passing of arbitrary request attributes.
+ Requests containing arbitrary request attributes will be rejected with a
+ 403 response unless the entire attribute name matches this regular
+ expression. If not specified, the default value is <code>null</code>.</p>
+ </attribute>
+
<attribute name="bindOnInit" required="false">
<p>Controls when the socket used by the connector is bound. By default it
is bound when the connector is initiated and unbound when the connector is

View File

@ -1,86 +0,0 @@
From 5716044b61cb5b638d8f0de848ac64df03184bc7 Mon Sep 17 00:00:00 2001
From: wang_yue111 <wangyue92@huawei.com>
Date: Mon, 18 May 2020 15:23:19 +0800
Subject: [PATCH] 3
---
conf/server.xml | 5 ++++-
.../apache/coyote/ajp/AbstractAjpProtocol.java | 18 +++++++++---------
java/org/apache/coyote/ajp/AjpProcessor.java | 2 +-
webapps/docs/config/ajp.xml | 2 +-
4 files changed, 15 insertions(+), 12 deletions(-)
diff --git a/conf/server.xml b/conf/server.xml
index fce8922..81a4e16 100644
--- a/conf/server.xml
+++ b/conf/server.xml
@@ -113,7 +113,10 @@
-->
<!-- Define an AJP 1.3 Connector on port 8009 -->
- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
+ <Connector protocol="AJP/1.3"
+ address="::1"
+ port="8009"
+ redirectPort="8443" />
<!-- An Engine represents the entry point (within Catalina) that processes
diff --git a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
index a2f5e28..0bbd1e6 100644
--- a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
+++ b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
@@ -189,15 +189,15 @@ public abstract class AbstractAjpProtocol<S> extends AbstractProtocol<S> {
}
- private Pattern allowedArbitraryRequestAttributesPattern;
- public void setAllowedArbitraryRequestAttributes(String allowedArbitraryRequestAttributes) {
- this.allowedArbitraryRequestAttributesPattern = Pattern.compile(allowedArbitraryRequestAttributes);
- }
- public String getAllowedArbitraryRequestAttributes() {
- return allowedArbitraryRequestAttributesPattern.pattern();
- }
- protected Pattern getAllowedArbitraryRequestAttributesPattern() {
- return allowedArbitraryRequestAttributesPattern;
+ private Pattern allowedRequestAttributesPattern;
+ public void setAllowedRequestAttributesPattern(String allowedRequestAttributesPattern) {
+ this.allowedRequestAttributesPattern = Pattern.compile(allowedRequestAttributesPattern);
+ }
+ public String getAllowedRequestAttributesPattern() {
+ return allowedRequestAttributesPattern.pattern();
+ }
+ protected Pattern getAllowedRequestAttributesPatternInternal() {
+ return allowedRequestAttributesPattern;
}
diff --git a/java/org/apache/coyote/ajp/AjpProcessor.java b/java/org/apache/coyote/ajp/AjpProcessor.java
index cc11a20..bf2cf86 100644
--- a/java/org/apache/coyote/ajp/AjpProcessor.java
+++ b/java/org/apache/coyote/ajp/AjpProcessor.java
@@ -746,7 +746,7 @@ public class AjpProcessor extends AbstractProcessor {
} else {
// All 'known' attributes will be processed by the previous
// blocks. Any remaining attribute is an 'arbitrary' one.
- Pattern pattern = protocol.getAllowedArbitraryRequestAttributesPattern();
+ Pattern pattern = protocol.getAllowedRequestAttributesPatternInternal();
if (pattern == null) {
response.setStatus(403);
setErrorState(ErrorState.CLOSE_CLEAN, null);
diff --git a/webapps/docs/config/ajp.xml b/webapps/docs/config/ajp.xml
index 17107e4..622e7ca 100644
--- a/webapps/docs/config/ajp.xml
+++ b/webapps/docs/config/ajp.xml
@@ -311,7 +311,7 @@
port. By default, the loopback address will be used.</p>
</attribute>
- <attribute name="allowedArbitraryRequestAttributes" required="false">
+ <attribute name="allowedRequestAttributesPattern" required="false">
<p>The AJP protocol passes some information from the reverse proxy to the
AJP connector using request attributes. These attributes are:</p>
<ul>
--
2.23.0

View File

@ -1,32 +0,0 @@
From 49ad3f954f69c6e838c8cd112ad79aa5fa8e7153 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Wed, 5 Feb 2020 14:47:48 +0000
Subject: [PATCH] Refactor secret check
Moving the check to start allows invalid configurations to be fixed via
JMX and changes to be made followed by stop()/start() for those changes
to take effect.
---
java/org/apache/coyote/ajp/AbstractAjpProtocol.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
index 63ff6c5a63..7cfdf0accf 100644
--- a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
+++ b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
@@ -257,13 +257,13 @@ protected Processor createUpgradeProcessor(SocketWrapperBase<?> socket,
@Override
- public void init() throws Exception {
+ public void start() throws Exception {
if (getSecretRequired()) {
String secret = getSecret();
if (secret == null || secret.length() == 0) {
throw new IllegalArgumentException(sm.getString("ajpprotocol.nosecret"));
}
}
- super.init();
+ super.start();
}
}

View File

@ -1,84 +0,0 @@
From 3aa8f28db7efb311cdd1b6fe15a9cd3b167a2222 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 5 May 2020 15:50:15 +0100
Subject: [PATCH] Improve validation of storage location when using FileStore.
---
.../apache/catalina/session/FileStore.java | 19 +++++++++++++++++--
.../catalina/session/LocalStrings.properties | 1 +
webapps/docs/changelog.xml | 3 +++
3 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/java/org/apache/catalina/session/FileStore.java b/java/org/apache/catalina/session/FileStore.java
index 066d6035f1..cf3ea880fa 100644
--- a/java/org/apache/catalina/session/FileStore.java
+++ b/java/org/apache/catalina/session/FileStore.java
@@ -33,6 +33,8 @@
import org.apache.catalina.Globals;
import org.apache.catalina.Session;
import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
/**
* Concrete implementation of the <b>Store</b> interface that utilizes
@@ -43,6 +45,10 @@
*/
public final class FileStore extends StoreBase {
+ private static final Log log = LogFactory.getLog(FileStore.class);
+ private static final StringManager sm = StringManager.getManager(FileStore.class);
+
+
// ----------------------------------------------------- Constants
/**
@@ -341,11 +347,20 @@ private File directory() throws IOException {
* used in the file naming.
*/
private File file(String id) throws IOException {
- if (this.directory == null) {
+ File storageDir = directory();
+ if (storageDir == null) {
return null;
}
+
String filename = id + FILE_EXT;
- File file = new File(directory(), filename);
+ File file = new File(storageDir, filename);
+
+ // Check the file is within the storage directory
+ if (!file.getCanonicalPath().startsWith(storageDir.getCanonicalPath())) {
+ log.warn(sm.getString("fileStore.invalid", file.getPath(), id));
+ return null;
+ }
+
return file;
}
}
diff --git a/java/org/apache/catalina/session/LocalStrings.properties b/java/org/apache/catalina/session/LocalStrings.properties
index 5815915..d72bee4 100644
--- a/java/org/apache/catalina/session/LocalStrings.properties
+++ b/java/org/apache/catalina/session/LocalStrings.properties
@@ -16,6 +16,7 @@
fileStore.saving=Saving Session [{0}] to file [{1}]
fileStore.loading=Loading Session [{0}] from file [{1}]
fileStore.removing=Removing Session [{0}] at file [{1}]
+fileStore.invalid=Invalid persistence file [{0}] for session ID [{1}]
fileStore.createFailed=Unable to create directory [{0}] for the storage of session data
fileStore.deleteFailed=Unable to delete file [{0}] which is preventing the creation of the session storage location
fileStore.deleteSessionFailed=Unable to delete file [{0}] which is no longer required
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 5665df4..a384d62 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -171,6 +171,9 @@
When generating a redirect to a directory in the Default Servlet, avoid
generating a protocol relative redirect. (markt)
</fix>
+ <add>
+ Improve validation of storage location when using FileStore. (markt)
+ </add>
</changelog>
</subsection>
<subsection name="Coyote">

View File

@ -1,73 +0,0 @@
From 935fc5582dc25ae10bab6f9d5629ff8d996cb533 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Fri, 6 Nov 2020 19:03:57 +0000
Subject: [PATCH] Fix BZ 64871. Log if file access is blocked due to symlinks
https://bz.apache.org/bugzilla/show_bug.cgi?id=64871
---
.../webresources/AbstractFileResourceSet.java | 19 ++++++++++++++++++-
.../webresources/LocalStrings.properties | 2 ++
2 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/java/org/apache/catalina/webresources/AbstractFileResourceSet.java b/java/org/apache/catalina/webresources/AbstractFileResourceSet.java
index c7993411e9..59fc77157f 100644
--- a/java/org/apache/catalina/webresources/AbstractFileResourceSet.java
+++ b/java/org/apache/catalina/webresources/AbstractFileResourceSet.java
@@ -22,11 +22,15 @@
import java.net.URL;
import org.apache.catalina.LifecycleException;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.compat.JrePlatform;
import org.apache.tomcat.util.http.RequestUtil;
public abstract class AbstractFileResourceSet extends AbstractResourceSet {
+ private static final Log log = LogFactory.getLog(AbstractFileResourceSet.class);
+
protected static final String[] EMPTY_STRING_ARRAY = new String[0];
private File fileBase;
@@ -128,6 +132,19 @@ protected final File file(String name, boolean mustExist) {
canPath = normalize(canPath);
}
if (!canPath.equals(absPath)) {
+ if (!canPath.equalsIgnoreCase(absPath)) {
+ // Typically means symlinks are in use but being ignored. Given
+ // the symlink was likely created for a reason, log a warning
+ // that it was ignored.
+ String msg = sm.getString("abstractFileResourceSet.canonicalfileCheckFailed",
+ getRoot().getContext().getName(), absPath, canPath);
+ // Log issues with configuration files at a higher level
+ if(absPath.startsWith("/META-INF/") || absPath.startsWith("/WEB-INF/")) {
+ log.error(msg);
+ } else {
+ log.warn(msg);
+ }
+ }
return null;
}
@@ -144,7 +161,7 @@ private boolean isInvalidWindowsFilename(String name) {
// expression irrespective of input length.
for (int i = 0; i < len; i++) {
char c = name.charAt(i);
- if (c == '\"' || c == '<' || c == '>') {
+ if (c == '\"' || c == '<' || c == '>' || c == ':') {
// These characters are disallowed in Windows file names and
// there are known problems for file names with these characters
// when using File#getCanonicalPath().
diff --git a/java/org/apache/catalina/webresources/LocalStrings.properties b/java/org/apache/catalina/webresources/LocalStrings.properties
index fb9badc120..af9f9fe797 100644
--- a/java/org/apache/catalina/webresources/LocalStrings.properties
+++ b/java/org/apache/catalina/webresources/LocalStrings.properties
@@ -15,6 +15,8 @@
abstractArchiveResourceSet.setReadOnlyFalse=Archive based WebResourceSets such as those based on JARs are hard-coded to be read-only and may not be configured to be read-write
+abstractFileResourceSet.canonicalfileCheckFailed=Resource for web application [{0}] at path [{1}] was not loaded as the canonical path [{2}] did not match. Use of symlinks is one possible cause.
+
abstractResource.getContentFail=Unable to return [{0}] as a byte array
abstractResource.getContentTooLarge=Unable to return [{0}] as a byte array since the resource is [{1}] bytes in size which is larger than the maximum size of a byte array

View File

@ -1,53 +0,0 @@
From 995115a24bb868d1204a796f5b3170f62618a6bb Mon Sep 17 00:00:00 2001
From: wang_yue111 <648774160@qq.com>
Date: Thu, 11 Mar 2021 18:35:41 +0800
Subject: [PATCH] SocketWrapper.upgraded is no longer used
It used to be used to determine if the processor should be recycled. It
has been replaced by a flag on the processor.
---
java/org/apache/coyote/AbstractProtocol.java | 2 --
.../apache/tomcat/util/net/SocketWrapperBase.java | 12 ++++++++++++
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/java/org/apache/coyote/AbstractProtocol.java b/java/org/apache/coyote/AbstractProtocol.java
index 09ed0a9..9f83906 100644
--- a/java/org/apache/coyote/AbstractProtocol.java
+++ b/java/org/apache/coyote/AbstractProtocol.java
@@ -799,8 +799,6 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
processor, wrapper));
}
wrapper.unRead(leftOverInput);
- // Mark the connection as upgraded
- wrapper.setUpgraded(true);
// Associate with the processor with the connection
connections.put(socket, processor);
// Initialise the upgrade handler (which may trigger
diff --git a/java/org/apache/tomcat/util/net/SocketWrapperBase.java b/java/org/apache/tomcat/util/net/SocketWrapperBase.java
index 2479d6d..f8a79db 100644
--- a/java/org/apache/tomcat/util/net/SocketWrapperBase.java
+++ b/java/org/apache/tomcat/util/net/SocketWrapperBase.java
@@ -138,7 +138,19 @@ public abstract class SocketWrapperBase<E> {
}
}
+ /**
+ * @return {@code true} if the connection has been upgraded.
+ *
+ * @deprecated Unused. Will be removed in Tomcat 10.
+ */
+ @Deprecated
public boolean isUpgraded() { return upgraded; }
+ /**
+ * @param upgraded {@code true} if the connection has been upgraded.
+ *
+ * @deprecated Unused. Will be removed in Tomcat 10.
+ */
+ @Deprecated
public void setUpgraded(boolean upgraded) { this.upgraded = upgraded; }
public boolean isSecure() { return secure; }
public void setSecure(boolean secure) { this.secure = secure; }
--
2.23.0

View File

@ -1,45 +0,0 @@
From 7b8b7134813a356595eacf01fd9e8ea6b3752c8b Mon Sep 17 00:00:00 2001
From: wang_yue111 <648774160@qq.com>
Date: Thu, 11 Mar 2021 18:42:09 +0800
Subject: [PATCH] Simplify the code and fix an edge case for BZ 64830
https://bz.apache.org/bugzilla/show_bug.cgi?id=64830
---
java/org/apache/coyote/AbstractProtocol.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/java/org/apache/coyote/AbstractProtocol.java b/java/org/apache/coyote/AbstractProtocol.java
index 9f83906..b5c4d5b 100644
--- a/java/org/apache/coyote/AbstractProtocol.java
+++ b/java/org/apache/coyote/AbstractProtocol.java
@@ -766,8 +766,10 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
if (state == SocketState.UPGRADING) {
// Get the HTTP upgrade handler
UpgradeToken upgradeToken = processor.getUpgradeToken();
- // Retrieve leftover input
+ // Restore leftover input to the wrapper so the upgrade
+ // processor can process it.
ByteBuffer leftOverInput = processor.getLeftoverInput();
+ wrapper.unRead(leftOverInput);
if (upgradeToken == null) {
// Assume direct HTTP/2 connection
UpgradeProtocol upgradeProtocol = getProtocol().getUpgradeProtocol("h2c");
@@ -776,7 +778,6 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
release(processor);
// Create the upgrade processor
processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter());
- wrapper.unRead(leftOverInput);
// Associate with the processor with the connection
connections.put(socket, processor);
} else {
@@ -798,7 +799,6 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
getLog().debug(sm.getString("abstractConnectionHandler.upgradeCreate",
processor, wrapper));
}
- wrapper.unRead(leftOverInput);
// Associate with the processor with the connection
connections.put(socket, processor);
// Initialise the upgrade handler (which may trigger
--
2.23.0

View File

@ -1,24 +0,0 @@
From d63695a656f04e39bd1ad4dee0f2339b0e3b898f Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Wed, 3 Oct 2018 11:16:07 +0000
Subject: [PATCH] Ensure that a canonical path is always used for the docBase
of a Context to ensure consistent behaviour.
git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1842702 13f79535-47bb-0310-9956-ffa450edef68
---
java/org/apache/catalina/startup/ContextConfig.java | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
index be74f29168..f3935038fc 100644
--- a/java/org/apache/catalina/startup/ContextConfig.java
+++ b/java/org/apache/catalina/startup/ContextConfig.java
@@ -579,7 +579,7 @@ protected void fixDocBase() throws IOException {
File file = new File(docBase);
if (!file.isAbsolute()) {
- docBase = (new File(appBase, docBase)).getPath();
+ docBase = (new File(appBase, docBase)).getCanonicalPath();
} else {
docBase = file.getCanonicalPath();
}

View File

@ -1,28 +0,0 @@
From ad60947e42e666dc9c9d77315787ea9bb567e3fd Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Wed, 13 Mar 2019 11:18:45 +0000
Subject: [PATCH] Always process the docBase using absolute path during
deployment
Use absolute path to determine the Context name, deployment type,
whether the docBase is located within the appBase etc.
---
java/org/apache/catalina/startup/ContextConfig.java | 4 ++--
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
index ebd3d8221f..0c67af3bf4 100644
--- a/java/org/apache/catalina/startup/ContextConfig.java
+++ b/java/org/apache/catalina/startup/ContextConfig.java
@@ -582,9 +582,9 @@ protected void fixDocBase() throws IOException {
File file = new File(docBase);
if (!file.isAbsolute()) {
- docBase = (new File(appBase, docBase)).getCanonicalPath();
+ docBase = (new File(appBase, docBase)).getAbsolutePath();
} else {
- docBase = file.getCanonicalPath();
+ docBase = file.getAbsolutePath();
}
file = new File(docBase);
String origDocBase = docBase;

View File

@ -1,144 +0,0 @@
From 2c5066316f6b138c4130a87cae4db05d75afe150 Mon Sep 17 00:00:00 2001
From: wang_yue111 <648774160@qq.com>
Date: Fri, 12 Mar 2021 09:44:04 +0800
Subject: [PATCH] 2
---
.../catalina/startup/ContextConfig.java | 75 ++++++++++---------
1 file changed, 41 insertions(+), 34 deletions(-)
diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
index 89eb8d3..a4210f8 100644
--- a/java/org/apache/catalina/startup/ContextConfig.java
+++ b/java/org/apache/catalina/startup/ContextConfig.java
@@ -566,25 +566,29 @@ public class ContextConfig implements LifecycleListener {
Host host = (Host) context.getParent();
File appBase = host.getAppBaseFile();
- String docBase = context.getDocBase();
- if (docBase == null) {
+ // This could be blank, relative, absolute or canonical
+ String docBaseConfigured = context.getDocBase();
+ // If there is no explicit docBase, derive it from the path and version
+ if (docBaseConfigured == null) {
// Trying to guess the docBase according to the path
String path = context.getPath();
if (path == null) {
return;
}
ContextName cn = new ContextName(path, context.getWebappVersion());
- docBase = cn.getBaseName();
+ docBaseConfigured = cn.getBaseName();
}
- File file = new File(docBase);
- if (!file.isAbsolute()) {
- docBase = (new File(appBase, docBase)).getAbsolutePath();
- } else {
- docBase = file.getAbsolutePath();
- }
- file = new File(docBase);
- String origDocBase = docBase;
+ // Obtain the absolute docBase in String and File form
+ String docBaseAbsolute;
+ File docBaseConfiguredFile = new File(docBaseConfigured);
+ if (!docBaseConfiguredFile.isAbsolute()) {
+ docBaseAbsolute = (new File(appBase, docBaseConfigured)).getAbsolutePath();
+ } else {
+ docBaseAbsolute = docBaseConfiguredFile.getAbsolutePath();
+ }
+ File docBaseAbsoluteFile = new File(docBaseAbsolute);
+ String originalDocBase = docBaseAbsolute;
ContextName cn = new ContextName(context.getPath(), context.getWebappVersion());
String pathName = cn.getBaseName();
@@ -597,28 +601,29 @@ public class ContextConfig implements LifecycleListener {
}
}
- boolean docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar);
-
- if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory()) {
- URL war = UriUtil.buildJarUrl(new File(docBase));
+ // At this point we need to determine if we have a WAR file in the
+ // appBase that needs to be expanded. Therefore we consider the absolute
+ // docBase NOT the canonical docBase. This is because some users symlink
+ // WAR files into the appBase and we want this to work correctly.
+ boolean docBaseAbsoluteInAppBase = docBaseAbsolute.startsWith(appBase.getPath() + File.separatorChar);
+ if (docBaseAbsolute.toLowerCase(Locale.ENGLISH).endsWith(".war") && !docBaseAbsoluteFile.isDirectory()) {
+ URL war = UriUtil.buildJarUrl(docBaseAbsoluteFile);
if (unpackWARs) {
- docBase = ExpandWar.expand(host, war, pathName);
- file = new File(docBase);
- docBase = file.getCanonicalPath();
+ docBaseAbsolute = ExpandWar.expand(host, war, pathName);
+ docBaseAbsoluteFile = new File(docBaseAbsolute);
if (context instanceof StandardContext) {
- ((StandardContext) context).setOriginalDocBase(origDocBase);
+ ((StandardContext) context).setOriginalDocBase(originalDocBase);
}
} else {
ExpandWar.validate(host, war, pathName);
}
} else {
- File docDir = new File(docBase);
- File warFile = new File(docBase + ".war");
+ File docBaseAbsoluteFileWar = new File(docBaseAbsolute + ".war");
URL war = null;
- if (warFile.exists() && docBaseInAppBase) {
- war = UriUtil.buildJarUrl(warFile);
+ if (docBaseAbsoluteFileWar.exists() && docBaseAbsoluteInAppBase) {
+ war = UriUtil.buildJarUrl(docBaseAbsoluteFileWar);
}
- if (docDir.exists()) {
+ if (docBaseAbsoluteFile.exists()) {
if (war != null && unpackWARs) {
// Check if WAR needs to be re-expanded (e.g. if it has
// changed). Note: HostConfig.deployWar() takes care of
@@ -629,31 +634,33 @@ public class ContextConfig implements LifecycleListener {
} else {
if (war != null) {
if (unpackWARs) {
- docBase = ExpandWar.expand(host, war, pathName);
- file = new File(docBase);
- docBase = file.getCanonicalPath();
+ docBaseAbsolute = ExpandWar.expand(host, war, pathName);
+ docBaseAbsoluteFile = new File(docBaseAbsolute);
} else {
- docBase = warFile.getCanonicalPath();
+ docBaseAbsolute = docBaseAbsoluteFileWar.getAbsolutePath();
+ docBaseAbsoluteFile = docBaseAbsoluteFileWar;
ExpandWar.validate(host, war, pathName);
}
}
if (context instanceof StandardContext) {
- ((StandardContext) context).setOriginalDocBase(origDocBase);
+ ((StandardContext) context).setOriginalDocBase(originalDocBase);
}
}
}
- // Re-calculate now docBase is a canonical path
- docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar);
+ String docBaseCanonical = docBaseAbsoluteFile.getCanonicalPath();
- if (docBaseInAppBase) {
- docBase = docBase.substring(appBase.getPath().length());
+ // Re-calculate now docBase is a canonical path
+ boolean docBaseCanonicalInAppBase = docBaseCanonical.startsWith(appBase.getPath() + File.separatorChar);
+ String docBase;
+ if (docBaseCanonicalInAppBase) {
+ docBase = docBaseCanonical.substring(appBase.getPath().length());
docBase = docBase.replace(File.separatorChar, '/');
if (docBase.startsWith("/")) {
docBase = docBase.substring(1);
}
} else {
- docBase = docBase.replace(File.separatorChar, '/');
+ docBase = docBaseCanonical.replace(File.separatorChar, '/');
}
context.setDocBase(docBase);
--
2.23.0

View File

@ -1,157 +0,0 @@
From 48590d3fc54100031ba9d8c4f6362afb15c6697f Mon Sep 17 00:00:00 2001
From: wang_yue111 <648774160@qq.com>
Date: Fri, 12 Mar 2021 09:53:00 +0800
Subject: [PATCH] Use java.nio.file.Path for consistent sub-directory
checking
---
.../catalina/servlets/DefaultServlet.java | 2 +-
.../apache/catalina/session/FileStore.java | 2 +-
.../catalina/startup/ContextConfig.java | 3 ++-
.../apache/catalina/startup/ExpandWar.java | 21 +++++++------------
.../apache/catalina/startup/HostConfig.java | 3 +--
webapps/docs/changelog.xml | 4 ++++
6 files changed, 16 insertions(+), 19 deletions(-)
diff --git a/java/org/apache/catalina/servlets/DefaultServlet.java b/java/org/apache/catalina/servlets/DefaultServlet.java
index 8b453bf..5ad60ec 100644
--- a/java/org/apache/catalina/servlets/DefaultServlet.java
+++ b/java/org/apache/catalina/servlets/DefaultServlet.java
@@ -1992,7 +1992,7 @@ public class DefaultServlet extends HttpServlet {
// First check that the resulting path is under the provided base
try {
- if (!candidate.getCanonicalPath().startsWith(base.getCanonicalPath())) {
+ if (!candidate.getCanonicalFile().toPath().startsWith(base.getCanonicalFile().toPath())) {
return null;
}
} catch (IOException ioe) {
diff --git a/java/org/apache/catalina/session/FileStore.java b/java/org/apache/catalina/session/FileStore.java
index 0c7f728..f77b46a 100644
--- a/java/org/apache/catalina/session/FileStore.java
+++ b/java/org/apache/catalina/session/FileStore.java
@@ -356,7 +356,7 @@ public final class FileStore extends StoreBase {
File file = new File(storageDir, filename);
// Check the file is within the storage directory
- if (!file.getCanonicalPath().startsWith(storageDir.getCanonicalPath())) {
+ if (!file.getCanonicalFile().toPath().startsWith(storageDir.getCanonicalFile().toPath())) {
log.warn(sm.getString("fileStore.invalid", file.getPath(), id));
return null;
}
diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
index a4210f8..5202253 100644
--- a/java/org/apache/catalina/startup/ContextConfig.java
+++ b/java/org/apache/catalina/startup/ContextConfig.java
@@ -651,7 +651,8 @@ public class ContextConfig implements LifecycleListener {
String docBaseCanonical = docBaseAbsoluteFile.getCanonicalPath();
// Re-calculate now docBase is a canonical path
- boolean docBaseCanonicalInAppBase = docBaseCanonical.startsWith(appBase.getPath() + File.separatorChar);
+ boolean docBaseCanonicalInAppBase =
+ docBaseAbsoluteFile.getCanonicalFile().toPath().startsWith(appBase.toPath());
String docBase;
if (docBaseCanonicalInAppBase) {
docBase = docBaseCanonical.substring(appBase.getPath().length());
diff --git a/java/org/apache/catalina/startup/ExpandWar.java b/java/org/apache/catalina/startup/ExpandWar.java
index 7fd7144..55fe1f5 100644
--- a/java/org/apache/catalina/startup/ExpandWar.java
+++ b/java/org/apache/catalina/startup/ExpandWar.java
@@ -26,6 +26,7 @@ import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.FileChannel;
+import java.nio.file.Path;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -116,10 +117,7 @@ public class ExpandWar {
}
// Expand the WAR into the new document base directory
- String canonicalDocBasePrefix = docBase.getCanonicalPath();
- if (!canonicalDocBasePrefix.endsWith(File.separator)) {
- canonicalDocBasePrefix += File.separator;
- }
+ Path canonicalDocBasePath = docBase.getCanonicalFile().toPath();
// Creating war tracker parent (normally META-INF)
File warTrackerParent = warTracker.getParentFile();
@@ -134,14 +132,13 @@ public class ExpandWar {
JarEntry jarEntry = jarEntries.nextElement();
String name = jarEntry.getName();
File expandedFile = new File(docBase, name);
- if (!expandedFile.getCanonicalPath().startsWith(
- canonicalDocBasePrefix)) {
+ if (!expandedFile.getCanonicalFile().toPath().startsWith(canonicalDocBasePath)) {
// Trying to expand outside the docBase
// Throw an exception to stop the deployment
throw new IllegalArgumentException(
sm.getString("expandWar.illegalPath",war, name,
expandedFile.getCanonicalPath(),
- canonicalDocBasePrefix));
+ canonicalDocBasePath));
}
int last = name.lastIndexOf('/');
if (last >= 0) {
@@ -217,10 +214,7 @@ public class ExpandWar {
File docBase = new File(host.getAppBaseFile(), pathname);
// Calculate the document base directory
- String canonicalDocBasePrefix = docBase.getCanonicalPath();
- if (!canonicalDocBasePrefix.endsWith(File.separator)) {
- canonicalDocBasePrefix += File.separator;
- }
+ Path canonicalDocBasePath = docBase.getCanonicalFile().toPath();
JarURLConnection juc = (JarURLConnection) war.openConnection();
juc.setUseCaches(false);
try (JarFile jarFile = juc.getJarFile()) {
@@ -229,14 +223,13 @@ public class ExpandWar {
JarEntry jarEntry = jarEntries.nextElement();
String name = jarEntry.getName();
File expandedFile = new File(docBase, name);
- if (!expandedFile.getCanonicalPath().startsWith(
- canonicalDocBasePrefix)) {
+ if (!expandedFile.getCanonicalFile().toPath().startsWith(canonicalDocBasePath)) {
// Entry located outside the docBase
// Throw an exception to stop the deployment
throw new IllegalArgumentException(
sm.getString("expandWar.illegalPath",war, name,
expandedFile.getCanonicalPath(),
- canonicalDocBasePrefix));
+ canonicalDocBasePath));
}
}
} catch (IOException e) {
diff --git a/java/org/apache/catalina/startup/HostConfig.java b/java/org/apache/catalina/startup/HostConfig.java
index a4dad6f..d7bf6a2 100644
--- a/java/org/apache/catalina/startup/HostConfig.java
+++ b/java/org/apache/catalina/startup/HostConfig.java
@@ -597,8 +597,7 @@ public class HostConfig implements LifecycleListener {
docBase = new File(host.getAppBaseFile(), context.getDocBase());
}
// If external docBase, register .xml as redeploy first
- if (!docBase.getCanonicalPath().startsWith(
- host.getAppBaseFile().getAbsolutePath() + File.separator)) {
+ if (!docBase.getCanonicalFile().toPath().startsWith(host.getAppBaseFile().toPath())) {
isExternal = true;
deployedApp.redeployResources.put(
contextXml.getAbsolutePath(),
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 1fc4907..bc37288 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -947,6 +947,10 @@
Update the NSIS Installer used to build the Windows installer to version
3.03. (kkolinko)
</update>
+ <scode>
+ Use <code>java.nio.file.Path</code> to test for one directory being a
+ sub-directory of another in a consistent way. (markt)
+ </scode>
</changelog>
</subsection>
</section>
--
2.23.0

View File

@ -1,204 +0,0 @@
From 6a719704236d3ce02100606290ff59b6a11f6b20 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 13 Apr 2021 11:12:02 +0100
Subject: [PATCH] Add attribute value escaping to support user names containing ';'
---
java/org/apache/catalina/realm/JNDIRealm.java | 79 ++++++++++++++++-
.../TestJNDIRealmAttributeValueEscape.java | 86 +++++++++++++++++++
2 files changed, 163 insertions(+), 2 deletions(-)
create mode 100644 test/org/apache/catalina/realm/TestJNDIRealmAttributeValueEscape.java
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
index 19fa704..54921dc 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -1603,8 +1603,11 @@ public class JNDIRealm extends RealmBase {
if (username == null || userPatternArray[curUserPattern] == null)
return null;
- // Form the dn from the user pattern
- String dn = connection.userPatternFormatArray[curUserPattern].format(new String[] { username });
+ // Form the DistinguishedName from the user pattern.
+ // Escape in case username contains a character with special meaning in
+ // an attribute value.
+ String dn = connection.userPatternFormatArray[curUserPattern].format(
+ new String[] { doAttributeValueEscaping(username) });
try {
user = getUserByPattern(connection.context, username, attrIds, dn);
@@ -2820,6 +2823,78 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
}
+ /**
+ * Implements the necessary escaping to represent an attribute value as a
+ * String as per RFC 4514.
+ *
+ * @param input The original attribute value
+ * @return The string representation of the attribute value
+ */
+ protected String doAttributeValueEscaping(String input) {
+ int len = input.length();
+ StringBuilder result = new StringBuilder();
+
+ for (int i = 0; i < len; i++) {
+ char c = input.charAt(i);
+ switch (c) {
+ case ' ': {
+ if (i == 0 || i == (len -1)) {
+ result.append("\\20");
+ } else {
+ result.append(c);
+ }
+ break;
+ }
+ case '#': {
+ if (i == 0 ) {
+ result.append("\\23");
+ } else {
+ result.append(c);
+ }
+ break;
+ }
+ case '\"': {
+ result.append("\\22");
+ break;
+ }
+ case '+': {
+ result.append("\\2B");
+ break;
+ }
+ case ',': {
+ result.append("\\2C");
+ break;
+ }
+ case ';': {
+ result.append("\\3B");
+ break;
+ }
+ case '<': {
+ result.append("\\3C");
+ break;
+ }
+ case '>': {
+ result.append("\\3E");
+ break;
+ }
+ case '\\': {
+ result.append("\\5C");
+ break;
+ }
+ case '\u0000': {
+ result.append("\\00");
+ break;
+ }
+ default:
+ result.append(c);
+ }
+
+ }
+
+ return result.toString();
+ }
+
+
protected static String convertToHexEscape(String input) {
if (input.indexOf('\\') == -1) {
// No escaping present. Return original.
diff --git a/test/org/apache/catalina/realm/TestJNDIRealmAttributeValueEscape.java b/test/org/apache/catalina/realm/TestJNDIRealmAttributeValueEscape.java
new file mode 100644
index 0000000..677bcc5
--- /dev/null
+++ b/test/org/apache/catalina/realm/TestJNDIRealmAttributeValueEscape.java
@@ -0,0 +1,86 @@
+/*
+ * 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.catalina.realm;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+
+@RunWith(Parameterized.class)
+public class TestJNDIRealmAttributeValueEscape {
+
+ @Parameterized.Parameters(name = "{index}: in[{0}], out[{1}]")
+ public static Collection<Object[]> parameters() {
+ List<Object[]> parameterSets = new ArrayList<>();
+
+ // No escaping required
+ parameterSets.add(new String[] { "none", "none" });
+ // Simple cases (same order as RFC 4512 section 2)
+ // Each appearing at the beginning, middle and ent
+ parameterSets.add(new String[] { " test", "\\20test" });
+ parameterSets.add(new String[] { "te st", "te st" });
+ parameterSets.add(new String[] { "test ", "test\\20" });
+ parameterSets.add(new String[] { "#test", "\\23test" });
+ parameterSets.add(new String[] { "te#st", "te#st" });
+ parameterSets.add(new String[] { "test#", "test#" });
+ parameterSets.add(new String[] { "\"test", "\\22test" });
+ parameterSets.add(new String[] { "te\"st", "te\\22st" });
+ parameterSets.add(new String[] { "test\"", "test\\22" });
+ parameterSets.add(new String[] { "+test", "\\2Btest" });
+ parameterSets.add(new String[] { "te+st", "te\\2Bst" });
+ parameterSets.add(new String[] { "test+", "test\\2B" });
+ parameterSets.add(new String[] { ",test", "\\2Ctest" });
+ parameterSets.add(new String[] { "te,st", "te\\2Cst" });
+ parameterSets.add(new String[] { "test,", "test\\2C" });
+ parameterSets.add(new String[] { ";test", "\\3Btest" });
+ parameterSets.add(new String[] { "te;st", "te\\3Bst" });
+ parameterSets.add(new String[] { "test;", "test\\3B" });
+ parameterSets.add(new String[] { "<test", "\\3Ctest" });
+ parameterSets.add(new String[] { "te<st", "te\\3Cst" });
+ parameterSets.add(new String[] { "test<", "test\\3C" });
+ parameterSets.add(new String[] { ">test", "\\3Etest" });
+ parameterSets.add(new String[] { "te>st", "te\\3Est" });
+ parameterSets.add(new String[] { "test>", "test\\3E" });
+ parameterSets.add(new String[] { "\\test", "\\5Ctest" });
+ parameterSets.add(new String[] { "te\\st", "te\\5Cst" });
+ parameterSets.add(new String[] { "test\\", "test\\5C" });
+ parameterSets.add(new String[] { "\u0000test", "\\00test" });
+ parameterSets.add(new String[] { "te\u0000st", "te\\00st" });
+ parameterSets.add(new String[] { "test\u0000", "test\\00" });
+ return parameterSets;
+ }
+
+
+ @Parameter(0)
+ public String in;
+ @Parameter(1)
+ public String out;
+
+ private JNDIRealm realm = new JNDIRealm();
+
+ @Test
+ public void testConvertToHexEscape() throws Exception {
+ String result = realm.doAttributeValueEscaping(in);
+ Assert.assertEquals(out, result);
+ }
+}
\ No newline at end of file
--
2.23.0

View File

@ -1,71 +0,0 @@
From f9a89674c08b55677424df7bd41685e72316e6bf Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 13 Apr 2021 11:35:07 +0100
Subject: [PATCH] Rename for clarity
---
java/org/apache/catalina/realm/JNDIRealm.java | 30 +++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
index 54921dc..b60f393 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -1942,7 +1942,7 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
return list;
// Set up parameters for an appropriate search
- String filter = connection.roleFormat.format(new String[] { doRFC2254Encoding(dn), username, userRoleId });
+ String filter = connection.roleFormat.format(new String[] { doFilterEscaping(dn), username, userRoleId });
SearchControls controls = new SearchControls();
if (roleSubtree)
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
@@ -2010,7 +2010,7 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
Map<String, String> newThisRound = new HashMap<>(); // Stores the groups we find in this iteration
for (Entry<String, String> group : newGroups.entrySet()) {
- filter = connection.roleFormat.format(new String[] { doRFC2254Encoding(group.getKey()),
+ filter = connection.roleFormat.format(new String[] { doFilterEscaping(group.getKey()),
group.getValue(), group.getValue() });
if (containerLog.isTraceEnabled()) {
@@ -2730,10 +2730,36 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
* ) -&gt; \29
* \ -&gt; \5c
* \0 -&gt; \00
+ *
* @param inString string to escape according to RFC 2254 guidelines
+ *
* @return String the escaped/encoded result
+ *
+ * @deprecated Will be removed in Tomcat 10.1.x onwards
*/
+ @Deprecated
protected String doRFC2254Encoding(String inString) {
+ return doFilterEscaping(inString);
+ }
+
+
+ /**
+ * Given an LDAP search string, returns the string with certain characters
+ * escaped according to RFC 2254 guidelines.
+ * The character mapping is as follows:
+ * char -&gt; Replacement
+ * ---------------------------
+ * * -&gt; \2a
+ * ( -&gt; \28
+ * ) -&gt; \29
+ * \ -&gt; \5c
+ * \0 -&gt; \00
+ *
+ * @param inString string to escape according to RFC 2254 guidelines
+ *
+ * @return String the escaped/encoded result
+ */
+ protected String doFilterEscaping(String inString) {
StringBuilder buf = new StringBuilder(inString.length());
for (int i = 0; i < inString.length(); i++) {
char c = inString.charAt(i);
--
2.23.0

View File

@ -1,36 +0,0 @@
From 2e3924d0a8372ced148b42016432c038dd1ae487 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 13 Apr 2021 11:43:51 +0100
Subject: [PATCH] Expand tests and fix escaping issue when searching for users by filter
---
java/org/apache/catalina/realm/JNDIRealm.java | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
index b60f393..dcec473 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -1648,7 +1648,9 @@ public class JNDIRealm extends RealmBase {
return null;
// Form the search filter
- String filter = connection.userSearchFormat.format(new String[] { username });
+ // Escape in case username contains a character with special meaning in
+ // a search filter.
+ String filter = connection.userSearchFormat.format(new String[] { doFilterEscaping(username) });
// Set up the search controls
SearchControls constraints = new SearchControls();
@@ -1913,6 +1915,8 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
if (user == null)
return null;
+ // This is returned from the directory so will be attribute value
+ // escaped if required
String dn = user.getDN();
String username = user.getUserName();
String userRoleId = user.getUserRoleId();
--
2.23.0

View File

@ -1,37 +0,0 @@
From 954eb10e9957055f60ee1e427baabfa32fc3d78b Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 13 Apr 2021 12:11:35 +0100
Subject: [PATCH] Expand tests and fix an issue in escaping for group search
---
java/org/apache/catalina/realm/JNDIRealm.java | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
index dcec473..1021ce8 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -1918,6 +1918,8 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
// This is returned from the directory so will be attribute value
// escaped if required
String dn = user.getDN();
+ // This is the name the user provided to the authentication process so
+ // it will not be escaped
String username = user.getUserName();
String userRoleId = user.getUserRoleId();
@@ -1946,7 +1948,10 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
return list;
// Set up parameters for an appropriate search
- String filter = connection.roleFormat.format(new String[] { doFilterEscaping(dn), username, userRoleId });
+ String filter = connection.roleFormat.format(new String[] {
+ doFilterEscaping(dn),
+ doFilterEscaping(doAttributeValueEscaping(username)),
+ userRoleId });
SearchControls controls = new SearchControls();
if (roleSubtree)
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
--
2.23.0

View File

@ -1,32 +0,0 @@
From a13034d94c927286a7f4e17ab4f662727fbe6e9f Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 13 Apr 2021 12:20:06 +0100
Subject: [PATCH] Expand tests and fix escaping issue in userRoleAttribute filter
---
java/org/apache/catalina/realm/JNDIRealm.java | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
index 1021ce8..a3b6f86 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -1947,11 +1947,13 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
if ((connection.roleFormat == null) || (roleName == null))
return list;
- // Set up parameters for an appropriate search
+ // Set up parameters for an appropriate search filter
+ // The dn is already attribute value escaped but the others are not
+ // This is a filter so all input will require filter escaping
String filter = connection.roleFormat.format(new String[] {
doFilterEscaping(dn),
doFilterEscaping(doAttributeValueEscaping(username)),
- userRoleId });
+ doFilterEscaping(doAttributeValueEscaping(userRoleId)) });
SearchControls controls = new SearchControls();
if (roleSubtree)
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
--
2.23.0

View File

@ -1,32 +0,0 @@
From fd48ca875aaa46920b6d94fe737420d3985ad7d4 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 13 Apr 2021 12:54:24 +0100
Subject: [PATCH] Expanded tests to cover nested roles and fix escaping issues in search
---
java/org/apache/catalina/realm/JNDIRealm.java | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
index a3b6f86..cfe1c15 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -2021,8 +2021,13 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
Map<String, String> newThisRound = new HashMap<>(); // Stores the groups we find in this iteration
for (Entry<String, String> group : newGroups.entrySet()) {
- filter = connection.roleFormat.format(new String[] { doFilterEscaping(group.getKey()),
- group.getValue(), group.getValue() });
+ // Group key is already value escaped if required
+ // Group value is not value escaped
+ // Everything needs to be filter escaped
+ filter = connection.roleFormat.format(new String[] {
+ doFilterEscaping(group.getKey()),
+ doFilterEscaping(doAttributeValueEscaping(group.getValue())),
+ doFilterEscaping(doAttributeValueEscaping(group.getValue())) });
if (containerLog.isTraceEnabled()) {
containerLog.trace("Perform a nested group search with base "+ roleBase + " and filter " + filter);
--
2.23.0

View File

@ -1,35 +0,0 @@
From 3383668c05becf01fe175aba928177b648f327ec Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 13 Apr 2021 14:47:07 +0100
Subject: [PATCH] Expand testing to cover substitution in roleBase. Fix bugs.
The code incorrectly referred to the original roleBase rather than the local version that includes the substituted value(s).
---
java/org/apache/catalina/realm/JNDIRealm.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
index cfe1c15..c78068b 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -1988,7 +1988,7 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
Attributes attrs = result.getAttributes();
if (attrs == null)
continue;
- String dname = getDistinguishedName(connection.context, roleBase, result);
+ String dname = getDistinguishedName(connection.context, base, result);
String name = getAttributeValue(roleName, attrs);
if (name != null && dname != null) {
groupMap.put(dname, name);
@@ -2033,7 +2033,7 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
containerLog.trace("Perform a nested group search with base "+ roleBase + " and filter " + filter);
}
- results = searchAsUser(connection.context, user, roleBase, filter, controls,
+ results = searchAsUser(connection.context, user, base, filter, controls,
isRoleSearchAsUser());
try {
--
2.23.0

View File

@ -1,28 +0,0 @@
From c703ec491aca94cb17853808c7ce0c4fd99992bb Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 13 Apr 2021 15:19:31 +0100
Subject: [PATCH] Expand tests to cover escaping of substituted roleBaes values
While the UnboundedID LDAP SDK doesn't appear to have a preference some servers (Windows AD, OpenLDAP) do appear to.
---
java/org/apache/catalina/realm/JNDIRealm.java | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
index c78068b..7a8c5f6 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -1967,7 +1967,9 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
Name name = np.parse(dn);
String nameParts[] = new String[name.size()];
for (int i = 0; i < name.size(); i++) {
- nameParts[i] = name.get(i);
+ // May have been returned with \<char> escaping rather than
+ // \<hex><hex>. Make sure it is \<hex><hex>.
+ nameParts[i] = convertToHexEscape(name.get(i));
}
base = connection.roleBaseFormat.format(nameParts);
} else {
--
2.23.0

View File

@ -1,45 +0,0 @@
From 700d26b69df3f1003ce8443d5569911c36b113de Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 5 Mar 2019 19:19:32 +0000
Subject: [PATCH] Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63213
Ensure the correct escaping of group names when searching for nested
groups when the JNDIRealm is configured with roleNested set to true.
---
java/org/apache/catalina/realm/JNDIRealm.java | 3 ++-
webapps/docs/changelog.xml | 5 +++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
index e980bdf..034c0f0 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -2010,7 +2010,8 @@ public class JNDIRealm extends RealmBase {
Map<String, String> newThisRound = new HashMap<>(); // Stores the groups we find in this iteration
for (Entry<String, String> group : newGroups.entrySet()) {
- filter = roleFormat.format(new String[] { group.getKey(), group.getValue(), group.getValue() });
+ filter = roleFormat.format(new String[] { doRFC2254Encoding(group.getKey()),
+ group.getValue(), group.getValue() });
if (containerLog.isTraceEnabled()) {
containerLog.trace("Perform a nested group search with base "+ roleBase + " and filter " + filter);
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 35b8eab..f088e0d 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -55,6 +55,11 @@
<fix>
Encode the output of the SSI <code>printenv</code> command. (markt)
</fix>
+ <fix>
+ <bug>63213</bug>: Ensure the correct escaping of group names when
+ searching for nested groups when the JNDIRealm is configured with
+ <code>roleNested</code> set to <code>true</code>. (markt)
+ </fix>
</changelog>
</subsection>
</section>
--
2.23.0

View File

@ -1,44 +0,0 @@
From 824c531393aa030f161e1ec352a65b7e9302d6b6 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Fri, 26 Jul 2019 14:59:57 +0100
Subject: [PATCH] Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63550
Only use the alternateURL for the JNDIRealm when it has been specified
---
java/org/apache/catalina/realm/JNDIRealm.java | 4 ++++
webapps/docs/changelog.xml | 4 ++++
2 files changed, 8 insertions(+)
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
index 034c0f0..505dd13 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -2378,6 +2378,10 @@ public class JNDIRealm extends RealmBase {
context = createDirContext(getDirectoryContextEnvironment());
} catch (Exception e) {
+ if (alternateURL == null || alternateURL.length() == 0) {
+ // No alternate URL. Re-throw the exception.
+ throw e;
+ }
connectionAttempt = 1;
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index f088e0d..7bcc3d9 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -248,6 +248,10 @@
</subsection>
<subsection name="Jasper">
<changelog>
+ <fix>
+ <bug>63550</bug>: Only try the <code>alternateURL</code> in the
+ <code>JNDIRealm</code> if one has been specified. (markt)
+ </fix>
<add>
<bug>50234</bug>: Add the capability to generate a web-fragment.xml file
to JspC. (markt)
--
2.23.0

File diff suppressed because it is too large Load Diff

View File

@ -1,53 +0,0 @@
From 36710841d24807a6837757a24952ab5e6ced6ec8 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Wed, 23 Jan 2019 15:09:37 +0000
Subject: [PATCH] Refactor to simplify the fix for BZ 63026
git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc8.5.x/trunk@1851939 13f79535-47bb-0310-9956-ffa450edef68
---
java/org/apache/catalina/realm/JNDIRealm.java | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
index b624c5b..5714496 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -2763,6 +2763,7 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
// we need to composite a name with the base name, the context name, and
// the result name. For non-relative names, use the returned name.
String resultName = result.getName();
+ Name name;
if (result.isRelative()) {
if (containerLog.isTraceEnabled()) {
containerLog.trace(" search returned relative name: " + resultName);
@@ -2774,9 +2775,8 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
// Bugzilla 32269
Name entryName = parser.parse(new CompositeName(resultName).get(0));
- Name name = contextName.addAll(baseName);
+ name = contextName.addAll(baseName);
name = name.addAll(entryName);
- return name.toString();
} else {
if (containerLog.isTraceEnabled()) {
containerLog.trace(" search returned absolute name: " + resultName);
@@ -2792,14 +2792,14 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
"Search returned unparseable absolute name: " +
resultName );
}
- Name name = parser.parse(pathComponent.substring(1));
- return name.toString();
+ name = parser.parse(pathComponent.substring(1));
} catch ( URISyntaxException e ) {
throw new InvalidNameException(
"Search returned unparseable absolute name: " +
resultName );
}
}
+ return name.toString();
}
--
2.23.0

View File

@ -1,250 +0,0 @@
From 4bee1e769bce86cd53ce80eb18c15449ea0df34b Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Wed, 23 Jan 2019 15:11:07 +0000
Subject: [PATCH] Add a new attribute, forceDnHexEscape, to the JNDIRealm that
forces escaping in the String representation of a distinguished name to use
the \nn form. This may avoid issues with realms using Active Directory which
appears to be more tolerant of optional escaping when the \nn form is used.
git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc8.5.x/trunk@1851941 13f79535-47bb-0310-9956-ffa450edef68
---
java/org/apache/catalina/realm/JNDIRealm.java | 95 ++++++++++++++++++-
.../TestJNDIRealmConvertToHexEscape.java | 70 ++++++++++++++
webapps/docs/changelog.xml | 8 ++
webapps/docs/config/realm.xml | 9 ++
4 files changed, 181 insertions(+), 1 deletion(-)
create mode 100644 test/org/apache/catalina/realm/TestJNDIRealmConvertToHexEscape.java
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
index 5714496..19fa704 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -487,8 +487,19 @@ public class JNDIRealm extends RealmBase {
protected int connectionPoolSize = 1;
+ private boolean forceDnHexEscape = false;
+
+
// ------------------------------------------------------------- Properties
+ public boolean getForceDnHexEscape() {
+ return forceDnHexEscape;
+ }
+
+ public void setForceDnHexEscape(boolean forceDnHexEscape) {
+ this.forceDnHexEscape = forceDnHexEscape;
+ }
+
/**
* @return the type of authentication to use.
*/
@@ -2799,7 +2810,89 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
resultName );
}
}
- return name.toString();
+
+ if (getForceDnHexEscape()) {
+ // Bug 63026
+ return convertToHexEscape(name.toString());
+ } else {
+ return name.toString();
+ }
+ }
+
+
+ protected static String convertToHexEscape(String input) {
+ if (input.indexOf('\\') == -1) {
+ // No escaping present. Return original.
+ return input;
+ }
+
+ // +6 allows for 3 escaped characters by default
+ StringBuilder result = new StringBuilder(input.length() + 6);
+ boolean previousSlash = false;
+ for (int i = 0; i < input.length(); i++) {
+ char c = input.charAt(i);
+
+ if (previousSlash) {
+ switch (c) {
+ case ' ': {
+ result.append("\\20");
+ break;
+ }
+ case '\"': {
+ result.append("\\22");
+ break;
+ }
+ case '#': {
+ result.append("\\23");
+ break;
+ }
+ case '+': {
+ result.append("\\2B");
+ break;
+ }
+ case ',': {
+ result.append("\\2C");
+ break;
+ }
+ case ';': {
+ result.append("\\3B");
+ break;
+ }
+ case '<': {
+ result.append("\\3C");
+ break;
+ }
+ case '=': {
+ result.append("\\3D");
+ break;
+ }
+ case '>': {
+ result.append("\\3E");
+ break;
+ }
+ case '\\': {
+ result.append("\\5C");
+ break;
+ }
+ default:
+ result.append('\\');
+ result.append(c);
+ }
+ previousSlash = false;
+ } else {
+ if (c == '\\') {
+ previousSlash = true;
+ } else {
+ result.append(c);
+ }
+ }
+ }
+
+ if (previousSlash) {
+ result.append('\\');
+ }
+
+ return result.toString();
}
diff --git a/test/org/apache/catalina/realm/TestJNDIRealmConvertToHexEscape.java b/test/org/apache/catalina/realm/TestJNDIRealmConvertToHexEscape.java
new file mode 100644
index 0000000..8c610a3
--- /dev/null
+++ b/test/org/apache/catalina/realm/TestJNDIRealmConvertToHexEscape.java
@@ -0,0 +1,70 @@
+/*
+ * 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.catalina.realm;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+
+@RunWith(Parameterized.class)
+public class TestJNDIRealmConvertToHexEscape {
+
+ @Parameterized.Parameters(name = "{index}: in[{0}], out[{1}]")
+ public static Collection<Object[]> parameters() {
+ List<Object[]> parameterSets = new ArrayList<>();
+
+ parameterSets.add(new String[] { "none", "none" });
+ parameterSets.add(new String[] { "\\", "\\" });
+ parameterSets.add(new String[] { "\\\\", "\\5C" });
+ parameterSets.add(new String[] { "\\5C", "\\5C" });
+ parameterSets.add(new String[] { "\\ ", "\\20" });
+ parameterSets.add(new String[] { "\\20", "\\20" });
+ parameterSets.add(new String[] { "\\ foo", "\\20foo" });
+ parameterSets.add(new String[] { "\\20foo", "\\20foo" });
+ parameterSets.add(new String[] { "\\ foo", "\\20 foo" });
+ parameterSets.add(new String[] { "\\20 foo", "\\20 foo" });
+ parameterSets.add(new String[] { "\\ \\ foo", "\\20\\20foo" });
+ parameterSets.add(new String[] { "\\20\\20foo", "\\20\\20foo" });
+ parameterSets.add(new String[] { "foo\\ ", "foo\\20" });
+ parameterSets.add(new String[] { "foo\\20", "foo\\20" });
+ parameterSets.add(new String[] { "foo \\ ", "foo \\20" });
+ parameterSets.add(new String[] { "foo \\20", "foo \\20" });
+ parameterSets.add(new String[] { "foo\\ \\ ", "foo\\20\\20" });
+ parameterSets.add(new String[] { "foo\\20\\20", "foo\\20\\20" });
+
+ return parameterSets;
+ }
+
+
+ @Parameter(0)
+ public String in;
+ @Parameter(1)
+ public String out;
+
+
+ @Test
+ public void testConvertToHexEscape() throws Exception {
+ String result = JNDIRealm.convertToHexEscape(in);
+ Assert.assertEquals(out, result);
+ }
+}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 173c209..a7bb52c 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -794,6 +794,14 @@
a plain text response. Based on a suggestion from Muthukumar Marikani.
(markt)
</add>
+ <add>
+ <bug>63026</bug>: Add a new attribute, <code>forceDnHexEscape</code>, to
+ the <code>JNDIRealm</code> that forces escaping in the String
+ representation of a distinguished name to use the <code>\nn</code> form.
+ This may avoid issues with realms using Active Directory which appears
+ to be more tolerant of optional escaping when the <code>\nn</code> form
+ is used. (markt)
+ </add>
</changelog>
</subsection>
</section>
diff --git a/webapps/docs/config/realm.xml b/webapps/docs/config/realm.xml
index a4bc5ef..715ceb7 100644
--- a/webapps/docs/config/realm.xml
+++ b/webapps/docs/config/realm.xml
@@ -463,6 +463,15 @@
"finding" and "searching". If not specified, "always" is used.</p>
</attribute>
+ <attribute name="forceDnHexEscape" required="false">
+ <p>A setting of <code>true</code> forces escaping in the String
+ representation of a distinguished name to use the <code>\nn</code> form.
+ This may avoid issues with realms using Active Directory which appears
+ to be more tolerant of optional escaping when the <code>\nn</code> form
+ is used. If not specified, the default of <code>false</code> will be
+ used.</p>
+ </attribute>
+
<attribute name="hostnameVerifierClassName" required="false">
<p>The name of the class to use for hostname verification when
using StartTLS for securing the connection to the ldap server.
--
2.23.0

View File

@ -1,160 +0,0 @@
From 94a5f7b95adee95fbc945767a71c27e329970a80 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Mon, 10 May 2021 21:20:46 +0100
Subject: [PATCH] Remove support for the identity T-E header value
---
.../apache/coyote/http11/Http11Processor.java | 7 +-
.../coyote/http11/TestHttp11Processor.java | 95 ++++++++++++++-----
webapps/docs/changelog.xml | 6 ++
3 files changed, 78 insertions(+), 30 deletions(-)
diff --git a/java/org/apache/coyote/http11/Http11Processor.java b/java/org/apache/coyote/http11/Http11Processor.java
index 86556ec..c840c83 100644
--- a/java/org/apache/coyote/http11/Http11Processor.java
+++ b/java/org/apache/coyote/http11/Http11Processor.java
@@ -243,11 +243,8 @@ public class Http11Processor extends AbstractProcessor {
// encoding names are case insensitive. (RFC2616, section 3.6)
encodingName = encodingName.trim().toLowerCase(Locale.ENGLISH);
- if (encodingName.equals("identity")) {
- // Skip
- } else if (encodingName.equals("chunked")) {
- inputBuffer.addActiveFilter
- (inputFilters[Constants.CHUNKED_FILTER]);
+ if (encodingName.equals("chunked")) {
+ inputBuffer.addActiveFilter(inputFilters[Constants.CHUNKED_FILTER]);
contentDelimitation = true;
} else {
for (int i = pluggableFilterIndex; i < inputFilters.length; i++) {
diff --git a/test/org/apache/coyote/http11/TestHttp11Processor.java b/test/org/apache/coyote/http11/TestHttp11Processor.java
index 5357526..1d12007 100644
--- a/test/org/apache/coyote/http11/TestHttp11Processor.java
+++ b/test/org/apache/coyote/http11/TestHttp11Processor.java
@@ -249,31 +249,6 @@ public class TestHttp11Processor extends TomcatBaseTest {
}
- @Test
- public void testWithTEIdentity() throws Exception {
- getTomcatInstanceTestWebapp(false, true);
-
- String request =
- "POST /test/echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF +
- "Host: any" + SimpleHttpClient.CRLF +
- "Transfer-encoding: identity" + SimpleHttpClient.CRLF +
- "Content-Length: 9" + SimpleHttpClient.CRLF +
- "Content-Type: application/x-www-form-urlencoded" +
- SimpleHttpClient.CRLF +
- "Connection: close" + SimpleHttpClient.CRLF +
- SimpleHttpClient.CRLF +
- "test=data";
-
- Client client = new Client(getPort());
- client.setRequest(new String[] {request});
-
- client.connect();
- client.processRequest();
- Assert.assertTrue(client.isResponse200());
- Assert.assertTrue(client.getResponseBody().contains("test - data"));
- }
-
-
@Test
public void testWithTESavedRequest() throws Exception {
getTomcatInstanceTestWebapp(false, true);
@@ -1308,4 +1283,74 @@ public class TestHttp11Processor extends TomcatBaseTest {
// Expected response is a 200 response.
Assert.assertTrue(client.isResponse200());
}
+
+
+ @Test
+ public void testTEHeaderUnknown01() throws Exception {
+ doTestTEHeaderUnknown("identity");
+ }
+
+
+ @Test
+ public void testTEHeaderUnknown02() throws Exception {
+ doTestTEHeaderUnknown("identity, chunked");
+ }
+
+
+ @Test
+ public void testTEHeaderUnknown03() throws Exception {
+ doTestTEHeaderUnknown("unknown, chunked");
+ }
+
+
+ @Test
+ public void testTEHeaderUnknown04() throws Exception {
+ doTestTEHeaderUnknown("void");
+ }
+
+
+ @Test
+ public void testTEHeaderUnknown05() throws Exception {
+ doTestTEHeaderUnknown("void, chunked");
+ }
+
+
+ @Test
+ public void testTEHeaderUnknown06() throws Exception {
+ doTestTEHeaderUnknown("void, identity");
+ }
+
+
+ @Test
+ public void testTEHeaderUnknown07() throws Exception {
+ doTestTEHeaderUnknown("identity, void");
+ }
+
+
+ private void doTestTEHeaderUnknown(String headerValue) throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+
+ // Add servlet
+ Tomcat.addServlet(ctx, "TesterServlet", new TesterServlet(false));
+ ctx.addServletMappingDecoded("/foo", "TesterServlet");
+
+ tomcat.start();
+
+ String request =
+ "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF +
+ "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
+ "Transfer-Encoding: " + headerValue + SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF;
+
+ Client client = new Client(tomcat.getConnector().getLocalPort());
+ client.setRequest(new String[] {request});
+
+ client.connect();
+ client.processRequest(false);
+
+ Assert.assertTrue(client.isResponse501());
+ }
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index bc37288..94a0d94 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -309,6 +309,12 @@
Enable host name verification when using TLS with the WebSocket client.
(markt)
</fix>
+ <fix>
+ Remove support for the <code>identity</code> transfer encoding. The
+ inclusion of this encoding in RFC 2616 was an error that was corrected
+ in 2001. Requests using this transfer encoding will now receive a 501
+ response. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Web applications">
--
2.23.0

View File

@ -1,81 +0,0 @@
From 66bd71277cedd04af2772942c697e15d5c401de9 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Mon, 10 May 2021 21:59:44 +0100
Subject: [PATCH] Process T-E header from both HTTP 1.0 and HTTP 1.1.clients
---
.../apache/coyote/http11/Http11Processor.java | 4 ++-
.../coyote/http11/TestHttp11Processor.java | 28 +++++++++++++++++++
webapps/docs/changelog.xml | 4 +++
3 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/java/org/apache/coyote/http11/Http11Processor.java b/java/org/apache/coyote/http11/Http11Processor.java
index c840c83..4021355 100644
--- a/java/org/apache/coyote/http11/Http11Processor.java
+++ b/java/org/apache/coyote/http11/Http11Processor.java
@@ -766,7 +766,9 @@ public class Http11Processor extends AbstractProcessor {
InputFilter[] inputFilters = inputBuffer.getFilters();
// Parse transfer-encoding header
- if (http11) {
+ // HTTP specs say an HTTP 1.1 server should accept any recognised
+ // HTTP 1.x header from a 1.x client unless the specs says otherwise.
+ if (!http09) {
MessageBytes transferEncodingValueMB = headers.getValue("transfer-encoding");
if (transferEncodingValueMB != null) {
String transferEncodingValue = transferEncodingValueMB.toString();
diff --git a/test/org/apache/coyote/http11/TestHttp11Processor.java b/test/org/apache/coyote/http11/TestHttp11Processor.java
index 1d12007..84fdd42 100644
--- a/test/org/apache/coyote/http11/TestHttp11Processor.java
+++ b/test/org/apache/coyote/http11/TestHttp11Processor.java
@@ -1353,4 +1353,32 @@ public class TestHttp11Processor extends TomcatBaseTest {
Assert.assertTrue(client.isResponse501());
}
+
+
+ @Test
+ public void testWithTEChunkedHttp10() throws Exception {
+
+ getTomcatInstanceTestWebapp(false, true);
+
+ String request =
+ "POST /test/echo-params.jsp HTTP/1.0" + SimpleHttpClient.CRLF +
+ "Host: any" + SimpleHttpClient.CRLF +
+ "Transfer-encoding: chunked" + SimpleHttpClient.CRLF +
+ "Content-Type: application/x-www-form-urlencoded" +
+ SimpleHttpClient.CRLF +
+ "Connection: close" + SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF +
+ "9" + SimpleHttpClient.CRLF +
+ "test=data" + SimpleHttpClient.CRLF +
+ "0" + SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF;
+
+ Client client = new Client(getPort());
+ client.setRequest(new String[] {request});
+
+ client.connect();
+ client.processRequest();
+ Assert.assertTrue(client.isResponse200());
+ Assert.assertTrue(client.getResponseBody().contains("test - data"));
+ }
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 94a0d94..e47f3d6 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -315,6 +315,10 @@
in 2001. Requests using this transfer encoding will now receive a 501
response. (markt)
</fix>
+ <fix>
+ Process transfer encoding headers from both HTTP 1.0 and HTTP 1.1
+ clients. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Web applications">
--
2.23.0

View File

@ -1,137 +0,0 @@
From 74888576a60ec58ee99454e4202a0eb1a7720d98 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Mon, 10 May 2021 22:14:18 +0100
Subject: [PATCH] Ensure chunked, if present, is the last encoding in the list
---
.../apache/coyote/http11/Http11Processor.java | 13 ++++++++-
.../coyote/http11/TestHttp11Processor.java | 28 +++++++++++++------
webapps/docs/changelog.xml | 5 ++++
3 files changed, 36 insertions(+), 10 deletions(-)
diff --git a/java/org/apache/coyote/http11/Http11Processor.java b/java/org/apache/coyote/http11/Http11Processor.java
index 4021355..17932b9 100644
--- a/java/org/apache/coyote/http11/Http11Processor.java
+++ b/java/org/apache/coyote/http11/Http11Processor.java
@@ -238,11 +238,22 @@ public class Http11Processor extends AbstractProcessor {
* supported, a 501 response will be returned to the client.
*/
private void addInputFilter(InputFilter[] inputFilters, String encodingName) {
+ if (contentDelimitation) {
+ // Chunked has already been specified and it must be the final
+ // encoding.
+ // 400 - Bad request
+ response.setStatus(400);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("http11processor.request.prepare") +
+ " Tranfer encoding lists chunked before [" + encodingName + "]");
+ }
+ return;
+ }
// Trim provided encoding name and convert to lower case since transfer
// encoding names are case insensitive. (RFC2616, section 3.6)
encodingName = encodingName.trim().toLowerCase(Locale.ENGLISH);
-
if (encodingName.equals("chunked")) {
inputBuffer.addActiveFilter(inputFilters[Constants.CHUNKED_FILTER]);
contentDelimitation = true;
diff --git a/test/org/apache/coyote/http11/TestHttp11Processor.java b/test/org/apache/coyote/http11/TestHttp11Processor.java
index 84fdd42..ceb2601 100644
--- a/test/org/apache/coyote/http11/TestHttp11Processor.java
+++ b/test/org/apache/coyote/http11/TestHttp11Processor.java
@@ -1287,47 +1287,53 @@ public class TestHttp11Processor extends TomcatBaseTest {
@Test
public void testTEHeaderUnknown01() throws Exception {
- doTestTEHeaderUnknown("identity");
+ doTestTEHeaderInvalid("identity", false);
}
@Test
public void testTEHeaderUnknown02() throws Exception {
- doTestTEHeaderUnknown("identity, chunked");
+ doTestTEHeaderInvalid("identity, chunked", false);
}
@Test
public void testTEHeaderUnknown03() throws Exception {
- doTestTEHeaderUnknown("unknown, chunked");
+ doTestTEHeaderInvalid("unknown, chunked", false);
}
@Test
public void testTEHeaderUnknown04() throws Exception {
- doTestTEHeaderUnknown("void");
+ doTestTEHeaderInvalid("void", false);
}
@Test
public void testTEHeaderUnknown05() throws Exception {
- doTestTEHeaderUnknown("void, chunked");
+ doTestTEHeaderInvalid("void, chunked", false);
}
@Test
public void testTEHeaderUnknown06() throws Exception {
- doTestTEHeaderUnknown("void, identity");
+ doTestTEHeaderInvalid("void, identity", false);
}
@Test
public void testTEHeaderUnknown07() throws Exception {
- doTestTEHeaderUnknown("identity, void");
+ doTestTEHeaderInvalid("identity, void", false);
}
- private void doTestTEHeaderUnknown(String headerValue) throws Exception {
+ @Test
+ public void testTEHeaderChunkedNotLast01() throws Exception {
+ doTestTEHeaderInvalid("chunked, void", true);
+ }
+
+
+ private void doTestTEHeaderInvalid(String headerValue, boolean badRequest) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
@@ -1351,7 +1357,11 @@ public class TestHttp11Processor extends TomcatBaseTest {
client.connect();
client.processRequest(false);
- Assert.assertTrue(client.isResponse501());
+ if (badRequest) {
+ Assert.assertTrue(client.isResponse400());
+ } else {
+ Assert.assertTrue(client.isResponse501());
+ }
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index e47f3d6..35b8eab 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -319,6 +319,11 @@
Process transfer encoding headers from both HTTP 1.0 and HTTP 1.1
clients. (markt)
</fix>
+ <fix>
+ Ensure that if the transfer encoding header contains the
+ <code>chunked</code>, that the <code>chunked</code> encoding is the
+ final encoding listed. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Web applications">
--
2.23.0

View File

@ -1,42 +0,0 @@
From d4b340fa8feaf55831f9a59350578f7b6ca048b8 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Wed, 3 Mar 2021 12:00:46 +0000
Subject: [PATCH] Improve robustness
---
.../apache/tomcat/util/net/openssl/LocalStrings.properties | 1 +
java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java | 6 ++++--
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/java/org/apache/tomcat/util/net/openssl/LocalStrings.properties b/java/org/apache/tomcat/util/net/openssl/LocalStrings.properties
index 1919159..1ab5f43 100644
--- a/java/org/apache/tomcat/util/net/openssl/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/net/openssl/LocalStrings.properties
@@ -52,6 +52,7 @@ engine.nullCipherSuite=Null cipher suite
engine.unsupportedCipher=Unsupported cipher suite: [{0}] [{1}]
engine.emptyCipherSuite=Empty cipher suite
engine.failedCipherSuite=Failed to enable cipher suite [{0}]
+engine.failedToReadAvailableBytes=There are plain text bytes available to read but no bytes were read
engine.unsupportedProtocol=Protocol [{0}] is not supported
engine.unverifiedPeer=Peer unverified
engine.noSession=SSL session ID not available
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java
index 15c6f56..b837fd6 100644
--- a/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java
@@ -593,8 +593,10 @@ public final class OpenSSLEngine extends SSLEngine implements SSLUtil.ProtocolIn
throw new SSLException(e);
}
- if (bytesRead == 0) {
- break;
+ if (bytesRead <= 0) {
+ // This should not be possible. pendingApp is positive
+ // therefore the read should have read at least one byte.
+ throw new IllegalStateException(sm.getString("engine.failedToReadAvailableBytes"));
}
bytesProduced += bytesRead;
--
2.27.0

View File

@ -1,54 +0,0 @@
From cd2150ff02c592c1ab6da219302ff80f589559fe Mon Sep 17 00:00:00 2001
From: remm <remm@apache.org>
Date: Thu, 28 Oct 2021 11:32:47 +0800
Subject: [PATCH] Close WebConnection
---
.../tomcat/websocket/server/WsHttpUpgradeHandler.java | 3 +++
webapps/docs/changelog.xml | 8 ++++++++
2 files changed, 11 insertions(+)
diff --git a/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java b/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
index 5dd1c5a..703f17a 100644
--- a/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
+++ b/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
@@ -99,6 +99,7 @@ public class WsHttpUpgradeHandler implements InternalHttpUpgradeHandler {
@Override
public void init(WebConnection connection) {
+ this.connection = connection;
if (ep == null) {
throw new IllegalStateException(
sm.getString("wsHttpUpgradeHandler.noPreInit"));
@@ -203,7 +204,9 @@ public class WsHttpUpgradeHandler implements InternalHttpUpgradeHandler {
@Override
public void destroy() {
+ WebConnection connection = this.connection;
if (connection != null) {
+ this.connection = null;
try {
connection.close();
} catch (Exception e) {
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index a7bb52c..a97e15d 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -338,6 +338,14 @@
</fix>
</changelog>
</subsection>
+ <subsection name="WebSocket">
+ <changelog>
+ <fix>
+ The internal upgrade handler should close the associated
+ <code>WebConnection</code> on destroy. (remm)
+ </fix>
+ </changelog>
+ </subsection>
<subsection name="Web applications">
<changlog>
<fix>
--
2.27.0

View File

@ -1,30 +0,0 @@
From 1385c624b4a1e994426e810075c850edc38a700e Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Wed, 12 Jan 2022 11:11:29 +0000
Subject: [PATCH] Make calculation of session storage location more robust
---
java/org/apache/catalina/session/FileStore.java | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/java/org/apache/catalina/session/FileStore.java b/java/org/apache/catalina/session/FileStore.java
index cac6027abdc..e42a72a4c87 100644
--- a/java/org/apache/catalina/session/FileStore.java
+++ b/java/org/apache/catalina/session/FileStore.java
@@ -349,13 +349,14 @@ private File file(String id) throws IOException {
String filename = id + FILE_EXT;
File file = new File(storageDir, filename);
+ File canonicalFile = file.getCanonicalFile();
// Check the file is within the storage directory
- if (!file.getCanonicalFile().toPath().startsWith(storageDir.getCanonicalFile().toPath())) {
+ if (!canonicalFile.toPath().startsWith(storageDir.getCanonicalFile().toPath())) {
log.warn(sm.getString("fileStore.invalid", file.getPath(), id));
return null;
}
- return file;
+ return canonicalFile;
}
}

View File

@ -1,127 +0,0 @@
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.

View File

@ -1,238 +0,0 @@
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

View File

@ -1,232 +0,0 @@
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

View File

@ -1,29 +0,0 @@
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();
}
}

View File

@ -1,89 +0,0 @@
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

435
CVE-2024-50379-1.patch Normal file
View File

@ -0,0 +1,435 @@
From 43b507ebac9d268b1ea3d908e296cc6e46795c00 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Fri, 1 Nov 2024 16:35:17 +0000
Subject: [PATCH] Fix inconsistent resource metadata with current GET and
PUT/DELETE
Origin: https://github.com/apache/tomcat/commit/43b507ebac9d268b1ea3d908e296cc6e46795c00
Concurrent reads and writes (e.g. HTTP GET and PUT / DELETE) for the
same path cause corruption of the FileResource where some of the fields
are set as if the file exists and some as set as if it does not.
---
.../apache/catalina/WebResourceLockSet.java | 73 +++++++
.../catalina/webresources/DirResourceSet.java | 199 +++++++++++++++---
.../catalina/webresources/FileResource.java | 29 ++-
.../webresources/LocalStrings.properties | 1 +
4 files changed, 273 insertions(+), 29 deletions(-)
create mode 100644 java/org/apache/catalina/WebResourceLockSet.java
diff --git a/java/org/apache/catalina/WebResourceLockSet.java b/java/org/apache/catalina/WebResourceLockSet.java
new file mode 100644
index 000000000000..142472c33cd6
--- /dev/null
+++ b/java/org/apache/catalina/WebResourceLockSet.java
@@ -0,0 +1,73 @@
+/*
+ * 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.catalina;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Interface implemented by {@link WebResourceSet} implementations that wish to provide locking functionality.
+ */
+public interface WebResourceLockSet {
+
+ /**
+ * Lock the resource at the provided path for reading. The resource is not required to exist. Read locks are not
+ * exclusive.
+ *
+ * @param path The path to the resource to be locked for reading
+ *
+ * @return The {@link ResourceLock} that must be passed to {@link #unlockForRead(ResourceLock)} to release the lock
+ */
+ ResourceLock lockForRead(String path);
+
+ /**
+ * Release a read lock from the resource associated with the given {@link ResourceLock}.
+ *
+ * @param resourceLock The {@link ResourceLock} associated with the resource for which a read lock should be
+ * released
+ */
+ void unlockForRead(ResourceLock resourceLock);
+
+ /**
+ * Lock the resource at the provided path for writing. The resource is not required to exist. Write locks are
+ * exclusive.
+ *
+ * @param path The path to the resource to be locked for writing
+ *
+ * @return The {@link ResourceLock} that must be passed to {@link #unlockForWrite(ResourceLock)} to release the lock
+ */
+ ResourceLock lockForWrite(String path);
+
+ /**
+ * Release the write lock from the resource associated with the given {@link ResourceLock}.
+ *
+ * @param resourceLock The {@link ResourceLock} associated with the resource for which the write lock should be
+ * released
+ */
+ void unlockForWrite(ResourceLock resourceLock);
+
+
+ class ResourceLock {
+ public final AtomicInteger count = new AtomicInteger(0);
+ public final ReentrantReadWriteLock reentrantLock = new ReentrantReadWriteLock();
+ public final String key;
+
+ public ResourceLock(String key) {
+ this.key = key;
+ }
+ }
+}
diff --git a/java/org/apache/catalina/webresources/DirResourceSet.java b/java/org/apache/catalina/webresources/DirResourceSet.java
index a221c3f1c33d..02ddc6ec887c 100644
--- a/java/org/apache/catalina/webresources/DirResourceSet.java
+++ b/java/org/apache/catalina/webresources/DirResourceSet.java
@@ -22,24 +22,35 @@
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
import java.util.Set;
import java.util.jar.Manifest;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.WebResource;
+import org.apache.catalina.WebResourceLockSet;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.WebResourceRoot.ResourceSetType;
import org.apache.catalina.util.ResourceSet;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.http.RequestUtil;
/**
* Represents a {@link org.apache.catalina.WebResourceSet} based on a directory.
*/
-public class DirResourceSet extends AbstractFileResourceSet {
+public class DirResourceSet extends AbstractFileResourceSet implements WebResourceLockSet {
private static final Log log = LogFactory.getLog(DirResourceSet.class);
+ private boolean caseSensitive = true;
+
+ private Map<String,ResourceLock> resourceLocksByPath = new HashMap<>();
+ private Object resourceLocksByPathLock = new Object();
+
+
/**
* A no argument constructor is required for this to work with the digester.
*/
@@ -91,22 +102,33 @@ public WebResource getResource(String path) {
String webAppMount = getWebAppMount();
WebResourceRoot root = getRoot();
if (path.startsWith(webAppMount)) {
- File f = file(path.substring(webAppMount.length()), false);
- if (f == null) {
- return new EmptyResource(root, path);
- }
- if (!f.exists()) {
- return new EmptyResource(root, path, f);
- }
- if (f.isDirectory() && path.charAt(path.length() - 1) != '/') {
- path = path + '/';
+ /*
+ * Lock the path for reading until the WebResource has been constructed. The lock prevents concurrent reads
+ * and writes (e.g. HTTP GET and PUT / DELETE) for the same path causing corruption of the FileResource
+ * where some of the fields are set as if the file exists and some as set as if it does not.
+ */
+ ResourceLock lock = lockForRead(path);
+ try {
+ File f = file(path.substring(webAppMount.length()), false);
+ if (f == null) {
+ return new EmptyResource(root, path);
+ }
+ if (!f.exists()) {
+ return new EmptyResource(root, path, f);
+ }
+ if (f.isDirectory() && path.charAt(path.length() - 1) != '/') {
+ path = path + '/';
+ }
+ return new FileResource(root, path, f, isReadOnly(), getManifest(), this, lock.key);
+ } finally {
+ unlockForRead(lock);
}
- return new FileResource(root, path, f, isReadOnly(), getManifest());
} else {
return new EmptyResource(root, path);
}
}
+
@Override
public String[] list(String path) {
checkPath(path);
@@ -246,32 +268,42 @@ public boolean write(String path, InputStream is, boolean overwrite) {
return false;
}
- File dest = null;
String webAppMount = getWebAppMount();
- if (path.startsWith(webAppMount)) {
+ if (!path.startsWith(webAppMount)) {
+ return false;
+ }
+
+ File dest = null;
+ /*
+ * Lock the path for writing until the write is complete. The lock prevents concurrent reads and writes (e.g.
+ * HTTP GET and PUT / DELETE) for the same path causing corruption of the FileResource where some of the fields
+ * are set as if the file exists and some as set as if it does not.
+ */
+ ResourceLock lock = lockForWrite(path);
+ try {
dest = file(path.substring(webAppMount.length()), false);
if (dest == null) {
return false;
}
- } else {
- return false;
- }
- if (dest.exists() && !overwrite) {
- return false;
- }
+ if (dest.exists() && !overwrite) {
+ return false;
+ }
- try {
- if (overwrite) {
- Files.copy(is, dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
- } else {
- Files.copy(is, dest.toPath());
+ try {
+ if (overwrite) {
+ Files.copy(is, dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ } else {
+ Files.copy(is, dest.toPath());
+ }
+ } catch (IOException ioe) {
+ return false;
}
- } catch (IOException ioe) {
- return false;
- }
- return true;
+ return true;
+ } finally {
+ unlockForWrite(lock);
+ }
}
@Override
@@ -286,6 +318,7 @@ protected void checkType(File file) {
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
+ caseSensitive = isCaseSensitive();
// Is this an exploded web application?
if (getWebAppMount().equals("")) {
// Look for a manifest
@@ -299,4 +332,114 @@ protected void initInternal() throws LifecycleException {
}
}
}
+
+
+ /*
+ * Determines if this ResourceSet is based on a case sensitive file system or not.
+ */
+ private boolean isCaseSensitive() {
+ try {
+ String canonicalPath = getFileBase().getCanonicalPath();
+ File upper = new File(canonicalPath.toUpperCase(Locale.ENGLISH));
+ if (!canonicalPath.equals(upper.getCanonicalPath())) {
+ return true;
+ }
+ File lower = new File(canonicalPath.toLowerCase(Locale.ENGLISH));
+ if (!canonicalPath.equals(lower.getCanonicalPath())) {
+ return true;
+ }
+ /*
+ * Both upper and lower case versions of the current fileBase have the same canonical path so the file
+ * system must be case insensitive.
+ */
+ } catch (IOException ioe) {
+ log.warn(sm.getString("dirResourceSet.isCaseSensitive.fail", getFileBase().getAbsolutePath()), ioe);
+ }
+
+ return false;
+ }
+
+
+ private String getLockKey(String path) {
+ // Normalize path to ensure that the same key is used for the same path.
+ String normalisedPath = RequestUtil.normalize(path);
+ if (caseSensitive) {
+ return normalisedPath;
+ }
+ return normalisedPath.toLowerCase(Locale.ENGLISH);
+ }
+
+
+ @Override
+ public ResourceLock lockForRead(String path) {
+ String key = getLockKey(path);
+ ResourceLock resourceLock = null;
+ synchronized (resourceLocksByPathLock) {
+ /*
+ * Obtain the ResourceLock and increment the usage count inside the sync to ensure that that map always has
+ * a consistent view of the currently "in-use" ResourceLocks.
+ */
+ resourceLock = resourceLocksByPath.get(key);
+ if (resourceLock == null) {
+ resourceLock = new ResourceLock(key);
+ }
+ resourceLock.count.incrementAndGet();
+ }
+ // Obtain the lock outside the sync as it will block if there is a current write lock.
+ resourceLock.reentrantLock.readLock().lock();
+ return resourceLock;
+ }
+
+
+ @Override
+ public void unlockForRead(ResourceLock resourceLock) {
+ // Unlock outside the sync as there is no need to do it inside.
+ resourceLock.reentrantLock.readLock().unlock();
+ synchronized (resourceLocksByPathLock) {
+ /*
+ * Decrement the usage count and remove ResourceLocks no longer required inside the sync to ensure that that
+ * map always has a consistent view of the currently "in-use" ResourceLocks.
+ */
+ if (resourceLock.count.decrementAndGet() == 0) {
+ resourceLocksByPath.remove(resourceLock.key);
+ }
+ }
+ }
+
+
+ @Override
+ public ResourceLock lockForWrite(String path) {
+ String key = getLockKey(path);
+ ResourceLock resourceLock = null;
+ synchronized (resourceLocksByPathLock) {
+ /*
+ * Obtain the ResourceLock and increment the usage count inside the sync to ensure that that map always has
+ * a consistent view of the currently "in-use" ResourceLocks.
+ */
+ resourceLock = resourceLocksByPath.get(key);
+ if (resourceLock == null) {
+ resourceLock = new ResourceLock(key);
+ }
+ resourceLock.count.incrementAndGet();
+ }
+ // Obtain the lock outside the sync as it will block if there are any other current locks.
+ resourceLock.reentrantLock.writeLock().lock();
+ return resourceLock;
+ }
+
+
+ @Override
+ public void unlockForWrite(ResourceLock resourceLock) {
+ // Unlock outside the sync as there is no need to do it inside.
+ resourceLock.reentrantLock.writeLock().unlock();
+ synchronized (resourceLocksByPathLock) {
+ /*
+ * Decrement the usage count and remove ResourceLocks no longer required inside the sync to ensure that that
+ * map always has a consistent view of the currently "in-use" ResourceLocks.
+ */
+ if (resourceLock.count.decrementAndGet() == 0) {
+ resourceLocksByPath.remove(resourceLock.key);
+ }
+ }
+ }
}
diff --git a/java/org/apache/catalina/webresources/FileResource.java b/java/org/apache/catalina/webresources/FileResource.java
index 32ce5bfa62c3..354022f9096d 100644
--- a/java/org/apache/catalina/webresources/FileResource.java
+++ b/java/org/apache/catalina/webresources/FileResource.java
@@ -31,6 +31,8 @@
import java.security.cert.Certificate;
import java.util.jar.Manifest;
+import org.apache.catalina.WebResourceLockSet;
+import org.apache.catalina.WebResourceLockSet.ResourceLock;
import org.apache.catalina.WebResourceRoot;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
@@ -62,10 +64,20 @@ public class FileResource extends AbstractResource {
private final boolean readOnly;
private final Manifest manifest;
private final boolean needConvert;
+ private final WebResourceLockSet lockSet;
+ private final String lockKey;
public FileResource(WebResourceRoot root, String webAppPath, File resource, boolean readOnly, Manifest manifest) {
+ this(root, webAppPath, resource, readOnly, manifest, null, null);
+ }
+
+
+ public FileResource(WebResourceRoot root, String webAppPath, File resource, boolean readOnly, Manifest manifest,
+ WebResourceLockSet lockSet, String lockKey) {
super(root, webAppPath);
this.resource = resource;
+ this.lockSet = lockSet;
+ this.lockKey = lockKey;
if (webAppPath.charAt(webAppPath.length() - 1) == '/') {
String realName = resource.getName() + '/';
@@ -117,7 +129,22 @@ public boolean delete() {
if (readOnly) {
return false;
}
- return resource.delete();
+ /*
+ * Lock the path for writing until the delete is complete. The lock prevents concurrent reads and writes (e.g.
+ * HTTP GET and PUT / DELETE) for the same path causing corruption of the FileResource where some of the fields
+ * are set as if the file exists and some as set as if it does not.
+ */
+ ResourceLock lock = null;
+ if (lockSet != null) {
+ lock = lockSet.lockForWrite(lockKey);
+ }
+ try {
+ return resource.delete();
+ } finally {
+ if (lockSet != null) {
+ lockSet.unlockForWrite(lock);
+ }
+ }
}
@Override
diff --git a/java/org/apache/catalina/webresources/LocalStrings.properties b/java/org/apache/catalina/webresources/LocalStrings.properties
index 67d76d3b3b32..301c13eb8063 100644
--- a/java/org/apache/catalina/webresources/LocalStrings.properties
+++ b/java/org/apache/catalina/webresources/LocalStrings.properties
@@ -36,6 +36,7 @@ cachedResource.invalidURL=Unable to create an instance of CachedResourceURLStrea
classpathUrlStreamHandler.notFound=Unable to load the resource [{0}] using the thread context class loader or the current class''s class loader
+dirResourceSet.isCaseSensitive.fail=Error trying to determine if file system at [{0}] is case sensitive so assuming it is not case sensitive
dirResourceSet.manifestFail=Failed to read manifest from [{0}]
dirResourceSet.notDirectory=The directory specified by base and internal path [{0}]{1}[{2}] does not exist.
dirResourceSet.writeNpe=The input stream may not be null

32
CVE-2024-50379-2.patch Normal file
View File

@ -0,0 +1,32 @@
From 631500b0c9b2a2a2abb707e3de2e10a5936e5d41 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Fri, 15 Nov 2024 15:55:37 +0000
Subject: [PATCH] Fix for inconsistent metadata was incomplete
Origin: https://github.com/apache/tomcat/commit/631500b0c9b2a2a2abb707e3de2e10a5936e5d41
Need to add key to map once created so it is visible to other threads
---
java/org/apache/catalina/webresources/DirResourceSet.java | 2 ++
webapps/docs/changelog.xml | 4 ++++
2 files changed, 6 insertions(+)
diff --git a/java/org/apache/catalina/webresources/DirResourceSet.java b/java/org/apache/catalina/webresources/DirResourceSet.java
index 02ddc6ec887c..f1d4e86199ee 100644
--- a/java/org/apache/catalina/webresources/DirResourceSet.java
+++ b/java/org/apache/catalina/webresources/DirResourceSet.java
@@ -382,6 +382,7 @@ public ResourceLock lockForRead(String path) {
resourceLock = resourceLocksByPath.get(key);
if (resourceLock == null) {
resourceLock = new ResourceLock(key);
+ resourceLocksByPath.put(key, resourceLock);
}
resourceLock.count.incrementAndGet();
}
@@ -419,6 +420,7 @@ public ResourceLock lockForWrite(String path) {
resourceLock = resourceLocksByPath.get(key);
if (resourceLock == null) {
resourceLock = new ResourceLock(key);
+ resourceLocksByPath.put(key, resourceLock);
}
resourceLock.count.incrementAndGet();
}

164
CVE-2024-52318.patch Normal file
View File

@ -0,0 +1,164 @@
From 9813c5dd3259183f659bbb83312a5cf673cc1ebf Mon Sep 17 00:00:00 2001
From: remm <remm@apache.org>
Date: Tue, 15 Oct 2024 21:51:33 +0200
Subject: [PATCH] Fix JSP tag release
Origin: https://github.com/apache/tomcat/commit/9813c5dd3259183f659bbb83312a5cf673cc1ebf
BZ 69399: Fix regression caused by the improvement 69333 which caused
the tag release() to be called when using tag pooling, and to be
skipped when not using it.
Patch submitted by Michal Sobkiewicz.
---
.../org/apache/jasper/compiler/Generator.java | 2 +-
.../apache/jasper/compiler/TestGenerator.java | 51 +++++++++++++++++++
test/webapp/WEB-INF/bugs.tld | 5 ++
test/webapp/jsp/generator/release.jsp | 18 +++++++
webapps/docs/changelog.xml | 10 ++++
5 files changed, 85 insertions(+), 1 deletion(-)
create mode 100644 test/webapp/jsp/generator/release.jsp
diff --git a/java/org/apache/jasper/compiler/Generator.java b/java/org/apache/jasper/compiler/Generator.java
index 814c8bb9fe50..5df52c3d7adc 100644
--- a/java/org/apache/jasper/compiler/Generator.java
+++ b/java/org/apache/jasper/compiler/Generator.java
@@ -2603,7 +2603,7 @@ private void generateCustomEnd(Node.CustomTag n, String tagHandlerVar,
out.print(".reuse(");
out.print(tagHandlerVar);
out.println(");");
-
+ } else {
// Clean-up
out.printin("org.apache.jasper.runtime.JspRuntimeLibrary.releaseTag(");
out.print(tagHandlerVar);
diff --git a/test/org/apache/jasper/compiler/TestGenerator.java b/test/org/apache/jasper/compiler/TestGenerator.java
index f7e3223e331a..087936cd6eb2 100644
--- a/test/org/apache/jasper/compiler/TestGenerator.java
+++ b/test/org/apache/jasper/compiler/TestGenerator.java
@@ -526,6 +526,25 @@ public void setData(String data) {
}
}
+ private static boolean tagTesterTagReleaseReleased = false;
+
+ public static class TesterTagRelease extends TesterTag {
+ private String data;
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+
+ @Override
+ public void release() {
+ tagTesterTagReleaseReleased = true;
+ }
+ }
+
public static class DataPropertyEditor extends PropertyEditorSupport {
}
@@ -947,6 +966,38 @@ public void testBug65390() throws Exception {
Assert.assertEquals(body.toString(), HttpServletResponse.SC_OK, rc);
}
+ @Test
+ public void testTagReleaseWithPooling() throws Exception {
+ doTestTagRelease(true);
+ }
+
+ @Test
+ public void testTagReleaseWithoutPooling() throws Exception {
+ doTestTagRelease(false);
+ }
+
+ public void doTestTagRelease(boolean enablePooling) throws Exception {
+ tagTesterTagReleaseReleased = false;
+ Tomcat tomcat = getTomcatInstance();
+
+ File appDir = new File("test/webapp");
+ Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
+ ctxt.addServletContainerInitializer(new JasperInitializer(), null);
+
+ Tomcat.initWebappDefaults(ctxt);
+ Wrapper w = (Wrapper) ctxt.findChild("jsp");
+ w.addInitParameter("enablePooling", String.valueOf(enablePooling));
+
+ tomcat.start();
+
+ getUrl("http://localhost:" + getPort() + "/jsp/generator/release.jsp");
+ if (enablePooling) {
+ Assert.assertFalse(tagTesterTagReleaseReleased);
+ } else {
+ Assert.assertTrue(tagTesterTagReleaseReleased);
+ }
+ }
+
private void doTestJsp(String jspName) throws Exception {
doTestJsp(jspName, HttpServletResponse.SC_OK);
}
diff --git a/test/webapp/WEB-INF/bugs.tld b/test/webapp/WEB-INF/bugs.tld
index 81d050e284fa..a4e496a83357 100644
--- a/test/webapp/WEB-INF/bugs.tld
+++ b/test/webapp/WEB-INF/bugs.tld
@@ -108,6 +108,11 @@
<tag-class>org.apache.jasper.compiler.TestGenerator$TesterTagA</tag-class>
<body-content>JSP</body-content>
</tag>
+ <tag>
+ <name>TesterTagRelease</name>
+ <tag-class>org.apache.jasper.compiler.TestGenerator$TesterTagRelease</tag-class>
+ <body-content>JSP</body-content>
+ </tag>
<tag>
<name>TesterScriptingTag</name>
<tag-class>org.apache.jasper.compiler.TestGenerator$TesterScriptingTag</tag-class>
diff --git a/test/webapp/jsp/generator/release.jsp b/test/webapp/jsp/generator/release.jsp
new file mode 100644
index 000000000000..ae2d1d19f09a
--- /dev/null
+++ b/test/webapp/jsp/generator/release.jsp
@@ -0,0 +1,18 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/bugs" prefix="bugs" %>
+<bugs:TesterTagRelease/>
\ No newline at end of file
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 72932e81a5c2..4d34ec5008b5 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -173,6 +173,16 @@
</fix>
</changelog>
</subsection>
+ <subsection name="Jasper">
+ <changelog>
+ <fix>
+ <bug>69399</bug>: Fix regression caused by the improvement
+ <bug>69333</bug> which caused the tag <code>release</code> to be called
+ when using tag pooling, and to be skipped when not using it.
+ Patch submitted by Michal Sobkiewicz. (remm)
+ </fix>
+ </changelog>
+ </subsection>
<subsection name="Other">
<changelog>
<update>

48
CVE-2024-54677-1.patch Normal file
View File

@ -0,0 +1,48 @@
From 1d88dd3ffaed76188dd4ee32ce77709ce6e153cd Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Mon, 2 Dec 2024 16:36:31 +0000
Subject: [PATCH] Obfuscate session cookie values for JSON output as well as
HTML
Origin: https://github.com/apache/tomcat/commit/1d88dd3ffaed76188dd4ee32ce77709ce6e153cd
---
webapps/docs/changelog.xml | 4 ++++
.../WEB-INF/classes/RequestHeaderExample.java | 18 +++++++++++++++---
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/webapps/examples/WEB-INF/classes/RequestHeaderExample.java b/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
index b01c84f33e48..e32f8c233674 100644
--- a/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
+++ b/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
@@ -73,7 +73,7 @@ protected boolean prefersJSON(String acceptHeader) {
// text/html, application/html, etc.
if (accept.contains("html")) {
- return false;
+ return true;
}
}
return false;
@@ -138,8 +138,20 @@ protected void renderJSON(HttpServletRequest request, HttpServletResponse respon
String headerName = e.nextElement();
String headerValue = request.getHeader(headerName);
- out.append("{\"").append(JSONFilter.escape(headerName)).append("\":\"")
- .append(JSONFilter.escape(headerValue)).append("\"}");
+ out.append("{\"").append(JSONFilter.escape(headerName)).append("\":\"");
+
+
+ if (headerName.toLowerCase(Locale.ENGLISH).contains("cookie")) {
+ HttpSession session = request.getSession(false);
+ String sessionId = null;
+ if (session != null) {
+ sessionId = session.getId();
+ }
+ out.append(JSONFilter.escape(CookieFilter.filter(headerValue, sessionId)));
+ } else {
+ out.append(JSONFilter.escape(headerValue));
+ }
+ out.append("\"}");
if (e.hasMoreElements()) {
out.append(',');

61
CVE-2024-54677-2.patch Normal file
View File

@ -0,0 +1,61 @@
From 721544ea28e92549824b106be954a9f411867a1c Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Mon, 2 Dec 2024 17:39:05 +0000
Subject: [PATCH] Add the ability to delete session attributes.
Origin: https://github.com/apache/tomcat/commit/721544ea28e92549824b106be954a9f411867a1c
---
webapps/docs/changelog.xml | 4 ++++
.../examples/WEB-INF/classes/SessionExample.java | 14 +++++++++++---
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/webapps/examples/WEB-INF/classes/SessionExample.java b/webapps/examples/WEB-INF/classes/SessionExample.java
index 407e2b3c0b0f..b3de2f866956 100644
--- a/webapps/examples/WEB-INF/classes/SessionExample.java
+++ b/webapps/examples/WEB-INF/classes/SessionExample.java
@@ -1,3 +1,4 @@
+
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -16,6 +17,8 @@
import java.io.IOException;
import java.io.PrintWriter;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Enumeration;
import java.util.ResourceBundle;
@@ -75,7 +78,7 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
String dataName = request.getParameter("dataname");
String dataValue = request.getParameter("datavalue");
- if (dataName != null && dataValue != null) {
+ if (dataName != null) {
session.setAttribute(dataName, dataValue);
}
@@ -85,7 +88,12 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
while (names.hasMoreElements()) {
String name = names.nextElement();
String value = session.getAttribute(name).toString();
- out.println(HTMLFilter.filter(name) + " = " + HTMLFilter.filter(value) + "<br>");
+ out.println(HTMLFilter.filter(name) + " = " + HTMLFilter.filter(value));
+ out.print("<a href=\"");
+ out.print(HTMLFilter.filter(
+ response.encodeURL("SessionExample?dataname=" + URLEncoder.encode(name, StandardCharsets.UTF_8))));
+ out.println("\" >delete</a>");
+ out.println("<br>");
}
out.println("<P>");
@@ -117,7 +125,7 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
out.println("</form>");
out.print("<p><a href=\"");
- out.print(HTMLFilter.filter(response.encodeURL("SessionExample?dataname=foo&datavalue=bar")));
+ out.print(HTMLFilter.filter(response.encodeURL("SessionExample?dataname=exampleName&datavalue=exampleValue")));
out.println("\" >URL encoded </a>");
out.println("</body>");

137
CVE-2024-54677-3.patch Normal file
View File

@ -0,0 +1,137 @@
From 84065e26ca4555e63a922bb29b13b0a1c86b7654 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Mon, 2 Dec 2024 18:09:44 +0000
Subject: [PATCH] Add a limit of 10 attributes per session to the session
example
Origin: https://github.com/apache/tomcat/commit/84065e26ca4555e63a922bb29b13b0a1c86b7654
---
webapps/docs/changelog.xml | 4 +
.../WEB-INF/classes/SessionExample.java | 94 ++++++++++++-------
2 files changed, 64 insertions(+), 34 deletions(-)
diff --git a/webapps/examples/WEB-INF/classes/SessionExample.java b/webapps/examples/WEB-INF/classes/SessionExample.java
index b3de2f866956..60eaa2e03e4b 100644
--- a/webapps/examples/WEB-INF/classes/SessionExample.java
+++ b/webapps/examples/WEB-INF/classes/SessionExample.java
@@ -41,6 +41,9 @@ public class SessionExample extends HttpServlet {
private static final long serialVersionUID = 1L;
+ private static final int SESSION_ATTRIBUTE_COUNT_LIMIT = 10;
+
+
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
ResourceBundle rb = ResourceBundle.getBundle("LocalStrings", request.getLocale());
@@ -76,15 +79,34 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
out.println(rb.getString("sessions.lastaccessed") + " ");
out.println(new Date(session.getLastAccessedTime()));
+ // Count the existing attributes
+ int sessionAttributeCount = 0;
+ Enumeration<String> names = session.getAttributeNames();
+ while (names.hasMoreElements()) {
+ names.nextElement();
+ sessionAttributeCount++;
+ }
+
String dataName = request.getParameter("dataname");
String dataValue = request.getParameter("datavalue");
if (dataName != null) {
- session.setAttribute(dataName, dataValue);
+ if (dataValue == null) {
+ session.removeAttribute(dataName);
+ sessionAttributeCount--;
+ } else if (sessionAttributeCount < SESSION_ATTRIBUTE_COUNT_LIMIT) {
+ session.setAttribute(dataName, dataValue);
+ sessionAttributeCount++;
+ } else {
+ out.print("<p> Session attribute [");
+ out.print(HTMLFilter.filter(dataName));
+ out.print("] not added as there are already "+ SESSION_ATTRIBUTE_COUNT_LIMIT + " attributes in the ");
+ out.println("session. Delete an attribute before adding another.");
+ }
}
- out.println("<P>");
+ out.println("<p>");
out.println(rb.getString("sessions.data") + "<br>");
- Enumeration<String> names = session.getAttributeNames();
+ names = session.getAttributeNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
String value = session.getAttribute(name).toString();
@@ -96,37 +118,41 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
out.println("<br>");
}
- out.println("<P>");
- out.print("<form action=\"");
- out.print(response.encodeURL("SessionExample"));
- out.print("\" ");
- out.println("method=POST>");
- out.println(rb.getString("sessions.dataname"));
- out.println("<input type=text size=20 name=dataname>");
- out.println("<br>");
- out.println(rb.getString("sessions.datavalue"));
- out.println("<input type=text size=20 name=datavalue>");
- out.println("<br>");
- out.println("<input type=submit>");
- out.println("</form>");
-
- out.println("<P>GET based form:<br>");
- out.print("<form action=\"");
- out.print(response.encodeURL("SessionExample"));
- out.print("\" ");
- out.println("method=GET>");
- out.println(rb.getString("sessions.dataname"));
- out.println("<input type=text size=20 name=dataname>");
- out.println("<br>");
- out.println(rb.getString("sessions.datavalue"));
- out.println("<input type=text size=20 name=datavalue>");
- out.println("<br>");
- out.println("<input type=submit>");
- out.println("</form>");
-
- out.print("<p><a href=\"");
- out.print(HTMLFilter.filter(response.encodeURL("SessionExample?dataname=exampleName&datavalue=exampleValue")));
- out.println("\" >URL encoded </a>");
+ if (sessionAttributeCount < SESSION_ATTRIBUTE_COUNT_LIMIT) {
+ out.println("<p>");
+ out.print("<form action=\"");
+ out.print(response.encodeURL("SessionExample"));
+ out.print("\" ");
+ out.println("method=POST>");
+ out.println(rb.getString("sessions.dataname"));
+ out.println("<input type=text size=20 name=dataname>");
+ out.println("<br>");
+ out.println(rb.getString("sessions.datavalue"));
+ out.println("<input type=text size=20 name=datavalue>");
+ out.println("<br>");
+ out.println("<input type=submit>");
+ out.println("</form>");
+
+ out.println("<p>GET based form:<br>");
+ out.print("<form action=\"");
+ out.print(response.encodeURL("SessionExample"));
+ out.print("\" ");
+ out.println("method=GET>");
+ out.println(rb.getString("sessions.dataname"));
+ out.println("<input type=text size=20 name=dataname>");
+ out.println("<br>");
+ out.println(rb.getString("sessions.datavalue"));
+ out.println("<input type=text size=20 name=datavalue>");
+ out.println("<br>");
+ out.println("<input type=submit>");
+ out.println("</form>");
+
+ out.print("<p><a href=\"");
+ out.print(HTMLFilter.filter(response.encodeURL("SessionExample?dataname=exampleName&datavalue=exampleValue")));
+ out.println("\" >URL encoded </a>");
+ } else {
+ out.print("<p>You may not add more than " + SESSION_ATTRIBUTE_COUNT_LIMIT + " attributes to this session.");
+ }
out.println("</body>");
out.println("</html>");

31
CVE-2024-54677-4.patch Normal file
View File

@ -0,0 +1,31 @@
From 3315a9027a7eaab18f42625b97b569940ff1365d Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Mon, 2 Dec 2024 18:13:07 +0000
Subject: [PATCH] Fix backprot
Origin: https://github.com/apache/tomcat/commit/3315a9027a7eaab18f42625b97b569940ff1365d
---
webapps/examples/WEB-INF/classes/SessionExample.java | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/webapps/examples/WEB-INF/classes/SessionExample.java b/webapps/examples/WEB-INF/classes/SessionExample.java
index 60eaa2e03e4b..14e7c9701c84 100644
--- a/webapps/examples/WEB-INF/classes/SessionExample.java
+++ b/webapps/examples/WEB-INF/classes/SessionExample.java
@@ -18,7 +18,6 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Enumeration;
import java.util.ResourceBundle;
@@ -113,7 +112,7 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
out.println(HTMLFilter.filter(name) + " = " + HTMLFilter.filter(value));
out.print("<a href=\"");
out.print(HTMLFilter.filter(
- response.encodeURL("SessionExample?dataname=" + URLEncoder.encode(name, StandardCharsets.UTF_8))));
+ response.encodeURL("SessionExample?dataname=" + URLEncoder.encode(name, "UTF-8"))));
out.println("\" >delete</a>");
out.println("<br>");
}

97
CVE-2024-54677-5.patch Normal file
View File

@ -0,0 +1,97 @@
From c2f7ce21c3fb12caefee87c517a8bb4f80700044 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 3 Dec 2024 17:45:03 +0000
Subject: [PATCH] Limit to 10 attributes. Add option to delete attribute.
Origin: https://github.com/apache/tomcat/commit/c2f7ce21c3fb12caefee87c517a8bb4f80700044
---
webapps/docs/changelog.xml | 5 ++
.../examples/jsp/security/protected/index.jsp | 49 ++++++++++++++++---
2 files changed, 46 insertions(+), 8 deletions(-)
diff --git a/webapps/examples/jsp/security/protected/index.jsp b/webapps/examples/jsp/security/protected/index.jsp
index 09c23e721910..987a30fd1878 100644
--- a/webapps/examples/jsp/security/protected/index.jsp
+++ b/webapps/examples/jsp/security/protected/index.jsp
@@ -14,8 +14,10 @@
See the License for the specific language governing permissions and
limitations under the License.
--%>
-<%@ page import="java.util.Enumeration" %>
+<%@ page import="java.net.URLEncoder" %>
+<%@ page import="java.nio.charset.StandardCharsets" %>
<%@ page import="java.security.Principal" %>
+<%@ page import="java.util.Enumeration" %>
<%@ page import="org.apache.catalina.TomcatPrincipal" %>
<%
if (request.getParameter("logoff") != null) {
@@ -121,31 +123,62 @@ enter it here:
%>
<br><br>
+<%
+ // Count the existing attributes
+ int sessionAttributeCount = 0;
+ Enumeration<String> names = session.getAttributeNames();
+ while (names.hasMoreElements()) {
+ names.nextElement();
+ sessionAttributeCount++;
+ }
+
+ String dataName = request.getParameter("dataName");
+ String dataValue = request.getParameter("dataValue");
+ if (dataName != null) {
+ if (dataValue == null) {
+ session.removeAttribute(dataName);
+ sessionAttributeCount--;
+ } else if (sessionAttributeCount < 10) {
+ session.setAttribute(dataName, dataValue);
+ sessionAttributeCount++;
+ } else {
+%>
+<p>Session attribute [<%= util.HTMLFilter.filter(dataName) %>] not added as there are already 10 attributes in the
+session. Delete an attribute before adding another.</p>
+<%
+ }
+ }
+
+ if (sessionAttributeCount < 10) {
+%>
To add some data to the authenticated session, enter it here:
<form method="GET" action='<%= response.encodeURL("index.jsp") %>'>
<input type="text" name="dataName">
<input type="text" name="dataValue">
<input type="submit" >
</form>
-<br><br>
-
<%
- String dataName = request.getParameter("dataName");
- if (dataName != null) {
- session.setAttribute(dataName, request.getParameter("dataValue"));
+ } else {
+%>
+<p>You may not add more than 10 attributes to this session.</p>
+<%
}
%>
+<br><br>
+
<p>The authenticated session contains the following attributes:</p>
<table>
<tr><th>Name</th><th>Value</th></tr>
<%
- Enumeration<String> names = session.getAttributeNames();
+ names = session.getAttributeNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
+ String value = session.getAttribute(name).toString();
%>
<tr>
<td><%= util.HTMLFilter.filter(name) %></td>
- <td><%= util.HTMLFilter.filter(String.valueOf(session.getAttribute(name))) %></td>
+ <td><%= util.HTMLFilter.filter(value) %></td>
+ <td><a href='<%= response.encodeURL("index.jsp?dataName=" + URLEncoder.encode(name, StandardCharsets.UTF_8)) %>'>delete</a></td>
</tr>
<%
}

23
CVE-2024-54677-6.patch Normal file
View File

@ -0,0 +1,23 @@
From 75ff7e8622edcc024b268677aa789ee8f0880ecc Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Wed, 4 Dec 2024 07:27:40 +0000
Subject: [PATCH] Fix back-port
Origin: https://github.com/apache/tomcat/commit/75ff7e8622edcc024b268677aa789ee8f0880ecc
---
webapps/examples/jsp/security/protected/index.jsp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/webapps/examples/jsp/security/protected/index.jsp b/webapps/examples/jsp/security/protected/index.jsp
index 987a30fd1878..f4c90b8b715c 100644
--- a/webapps/examples/jsp/security/protected/index.jsp
+++ b/webapps/examples/jsp/security/protected/index.jsp
@@ -178,7 +178,7 @@ To add some data to the authenticated session, enter it here:
<tr>
<td><%= util.HTMLFilter.filter(name) %></td>
<td><%= util.HTMLFilter.filter(value) %></td>
- <td><a href='<%= response.encodeURL("index.jsp?dataName=" + URLEncoder.encode(name, StandardCharsets.UTF_8)) %>'>delete</a></td>
+ <td><a href='<%= response.encodeURL("index.jsp?dataName=" + URLEncoder.encode(name, "UTF-8")) %>'>delete</a></td>
</tr>
<%
}

270
CVE-2024-54677-7.patch Normal file
View File

@ -0,0 +1,270 @@
From 4d5cc6538d91386f950373ac8120e98c2c78ed3a Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Wed, 4 Dec 2024 15:14:17 +0000
Subject: [PATCH] Limit shopping cart to pre-defined items
Origin: https://github.com/apache/tomcat/commit/4d5cc6538d91386f950373ac8120e98c2c78ed3a
---
webapps/docs/changelog.xml | 4 ++
.../WEB-INF/classes/sessions/DummyCart.java | 42 +++++++++++--------
.../WEB-INF/classes/sessions/Item.java | 37 ++++++++++++++++
webapps/examples/jsp/index.html | 2 +-
webapps/examples/jsp/sessions/carts.jsp | 8 ++--
webapps/examples/jsp/sessions/crt.html | 7 +++-
.../jsp/sessions/{carts.html => shopping.jsp} | 41 +++++++++---------
7 files changed, 95 insertions(+), 46 deletions(-)
create mode 100644 webapps/examples/WEB-INF/classes/sessions/Item.java
rename webapps/examples/jsp/sessions/{carts.html => shopping.jsp} (75%)
diff --git a/webapps/examples/WEB-INF/classes/sessions/DummyCart.java b/webapps/examples/WEB-INF/classes/sessions/DummyCart.java
index 44decc98a9a2..7088557b9595 100644
--- a/webapps/examples/WEB-INF/classes/sessions/DummyCart.java
+++ b/webapps/examples/WEB-INF/classes/sessions/DummyCart.java
@@ -16,42 +16,50 @@
*/
package sessions;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
public class DummyCart {
- final List<String> items = Collections.synchronizedList(new ArrayList<>());
+ final Set<Item> items = Collections.synchronizedSet(new HashSet<>());
+ int itemId = -1;
String submit = null;
- String item = null;
- private void addItem(String name) {
- items.add(name);
+ public void setItemId(int itemId) {
+ this.itemId = itemId;
}
- private void removeItem(String name) {
- items.remove(name);
+ public void setSubmit(String s) {
+ submit = s;
}
- public void setItem(String name) {
- item = name;
+ private void addItem(int itemId) {
+ try {
+ items.add(Item.values()[itemId]);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // Ignore. Can only happen if user edits URL directly.
+ }
}
- public void setSubmit(String s) {
- submit = s;
+ private void removeItem(int itemId) {
+ try {
+ items.remove(Item.values()[itemId]);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // Ignore. Can only happen if user edits URL directly.
+ }
}
- public String[] getItems() {
- return items.toArray(new String[0]);
+ public Item[] getItems() {
+ return items.toArray(new Item[0]);
}
public void processRequest() {
// null value for submit - user hit enter instead of clicking on
// "add" or "remove"
if (submit == null || submit.equals("add")) {
- addItem(item);
+ addItem(itemId);
} else if (submit.equals("remove")) {
- removeItem(item);
+ removeItem(itemId);
}
// reset at the end of the request
@@ -61,6 +69,6 @@ public void processRequest() {
// reset
private void reset() {
submit = null;
- item = null;
+ itemId = -1;
}
}
diff --git a/webapps/examples/WEB-INF/classes/sessions/Item.java b/webapps/examples/WEB-INF/classes/sessions/Item.java
new file mode 100644
index 000000000000..447d04443744
--- /dev/null
+++ b/webapps/examples/WEB-INF/classes/sessions/Item.java
@@ -0,0 +1,37 @@
+/*
+ * 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 sessions;
+
+public enum Item {
+
+ VIDEO("Beavis & Butt-head Video collection"),
+ MOVIE("X-files movie"),
+ TAPES("Twin peaks tapes"),
+ CD("NIN CD"),
+ BOOK("JSP Book"),
+ TICKETS("Concert tickets");
+
+ private final String title;
+
+ Item(String title) {
+ this.title = title;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+}
diff --git a/webapps/examples/jsp/index.html b/webapps/examples/jsp/index.html
index b2371207cfe7..dc25005b2710 100644
--- a/webapps/examples/jsp/index.html
+++ b/webapps/examples/jsp/index.html
@@ -228,7 +228,7 @@ <h2>JSP 1.2 Examples</h2>
<tr>
<td>Carts</td>
-<td style="width: 30%;"><a href="sessions/carts.html"><img src="images/execute.gif" alt=""></a><a href="sessions/carts.html">Execute</a></td>
+<td style="width: 30%;"><a href="sessions/shopping.jsp"><img src="images/execute.gif" alt=""></a><a href="sessions/shopping.jsp">Execute</a></td>
<td style="width: 30%;"><a href="sessions/crt.html"><img src="images/code.gif" alt=""></a><a href="sessions/crt.html">Source</a></td>
</tr>
diff --git a/webapps/examples/jsp/sessions/carts.jsp b/webapps/examples/jsp/sessions/carts.jsp
index dc51495c4bec..fa5624dee6bd 100644
--- a/webapps/examples/jsp/sessions/carts.jsp
+++ b/webapps/examples/jsp/sessions/carts.jsp
@@ -27,10 +27,10 @@
<br> You have the following items in your cart:
<ol>
<%
- String[] items = cart.getItems();
- for (String item : items) {
+ Item[] items = cart.getItems();
+ for (Item item : items) {
%>
-<li> <% out.print(util.HTMLFilter.filter(item)); %>
+<li> <% out.print(util.HTMLFilter.filter(item.getTitle())); %>
<%
}
%>
@@ -39,5 +39,5 @@
</FONT>
<hr>
-<%@ include file ="carts.html" %>
+<%@ include file ="shopping.jsp" %>
</html>
diff --git a/webapps/examples/jsp/sessions/crt.html b/webapps/examples/jsp/sessions/crt.html
index 11e6edafe0e9..28ace66012c7 100644
--- a/webapps/examples/jsp/sessions/crt.html
+++ b/webapps/examples/jsp/sessions/crt.html
@@ -22,9 +22,12 @@
</head>
<body bgcolor="#FFFFFF">
-<p><font color="#0000FF"><a href="carts.html"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
+<p><font color="#0000FF"><a href="shopping.jsp"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
-<h3><a href="carts.jsp.html">Source Code for Cart Example<font color="#0000FF"></a>
+<h3><a href="shopping.jsp.html">Source Code for Cart Example Shopping Page<font color="#0000FF"></a>
+ </font> </h3>
+
+<h3><a href="carts.jsp.html">Source Code for Cart Example Cart Page<font color="#0000FF"></a>
</font> </h3>
<h3><a href="DummyCart.html">Property Sheet for DummyCart
diff --git a/webapps/examples/jsp/sessions/carts.html b/webapps/examples/jsp/sessions/shopping.jsp
similarity index 75%
rename from webapps/examples/jsp/sessions/carts.html
rename to webapps/examples/jsp/sessions/shopping.jsp
index 834ee0a594fd..3487b1029547 100644
--- a/webapps/examples/jsp/sessions/carts.html
+++ b/webapps/examples/jsp/sessions/shopping.jsp
@@ -1,5 +1,4 @@
-<html>
-<!--
+<%--
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.
@@ -14,33 +13,31 @@
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.
--->
-
+--%>
+<%@ page import="sessions.Item" %>
+<html>
<head>
- <title>carts</title>
+<title>Shopping Cart Example</title>
</head>
- <body bgcolor="white">
+<body bgcolor="white">
<font size = 5 color="#CC0000">
<form type=POST action=carts.jsp>
-<BR>
+<br>
Please enter item to add or remove:
<br>
-Add Item:
-
-<SELECT NAME="item">
-<OPTION>Beavis & Butt-head Video collection
-<OPTION>X-files movie
-<OPTION>Twin peaks tapes
-<OPTION>NIN CD
-<OPTION>JSP Book
-<OPTION>Concert tickets
-<OPTION>Love life
-<OPTION>Switch blade
-<OPTION>Rex, Rugs & Rock n' Roll
-</SELECT>
-
+Select Item:
+
+<select name="itemId">
+<%
+ for (Item item : Item.values()) {
+%>
+ <option value="<%= item.ordinal() %>"><%= item.getTitle() %></option>
+<%
+ }
+%>
+</select>
<br> <br>
<INPUT TYPE=submit name="submit" value="add">
@@ -48,6 +45,6 @@
</form>
-</FONT>
+</font>
</body>
</html>

717
CVE-2024-54677-8.patch Normal file
View File

@ -0,0 +1,717 @@
From 84c4af76e7a10fc7f8630ce62e6a46632ea4a90e Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Wed, 4 Dec 2024 18:41:34 +0000
Subject: [PATCH] Remove JSP calendar example.
Origin: https://github.com/apache/tomcat/commit/84c4af76e7a10fc7f8630ce62e6a46632ea4a90e
---
build.xml | 9 --
webapps/docs/changelog.xml | 3 +
.../examples/WEB-INF/classes/cal/Entries.java | 63 --------
.../examples/WEB-INF/classes/cal/Entry.java | 52 ------
.../WEB-INF/classes/cal/JspCalendar.java | 152 ------------------
.../WEB-INF/classes/cal/TableBean.java | 106 ------------
webapps/examples/jsp/cal/cal1.jsp | 94 -----------
webapps/examples/jsp/cal/cal2.jsp | 45 ------
webapps/examples/jsp/cal/calendar.html | 43 -----
webapps/examples/jsp/cal/login.html | 47 ------
webapps/examples/jsp/index.html | 8 -
11 files changed, 3 insertions(+), 619 deletions(-)
delete mode 100644 webapps/examples/WEB-INF/classes/cal/Entries.java
delete mode 100644 webapps/examples/WEB-INF/classes/cal/Entry.java
delete mode 100644 webapps/examples/WEB-INF/classes/cal/JspCalendar.java
delete mode 100644 webapps/examples/WEB-INF/classes/cal/TableBean.java
delete mode 100644 webapps/examples/jsp/cal/cal1.jsp
delete mode 100644 webapps/examples/jsp/cal/cal2.jsp
delete mode 100644 webapps/examples/jsp/cal/calendar.html
delete mode 100644 webapps/examples/jsp/cal/login.html
diff --git a/build.xml b/build.xml
index 52f5fa11252f..5b3e1738c283 100644
--- a/build.xml
+++ b/build.xml
@@ -1646,15 +1646,6 @@
</fileset>
</txt2html>
- <txt2html todir="${tomcat.build}/webapps/examples/jsp/cal">
- <fileset dir="webapps/examples/WEB-INF/classes/cal">
- <include name="Entries.java"/>
- <include name="Entry.java"/>
- <include name="JspCalendar.java"/>
- <include name="TableBean.java"/>
- </fileset>
- </txt2html>
-
<txt2html todir="${tomcat.build}/webapps/examples/jsp/jsptoserv">
<fileset dir="webapps/examples/WEB-INF/classes">
<include name="ServletToJsp.java"/>
diff --git a/webapps/examples/WEB-INF/classes/cal/Entries.java b/webapps/examples/WEB-INF/classes/cal/Entries.java
deleted file mode 100644
index cac611a03b1f..000000000000
--- a/webapps/examples/WEB-INF/classes/cal/Entries.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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 cal;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import javax.servlet.http.HttpServletRequest;
-
-public class Entries {
-
- private final Map<String, Entry> entries;
- private static final String[] time = { "8am", "9am", "10am", "11am",
- "12pm", "1pm", "2pm", "3pm", "4pm", "5pm", "6pm", "7pm", "8pm" };
- public static final int rows = 12;
-
- public Entries() {
- entries = new ConcurrentHashMap<>(rows);
- for (int i = 0; i < rows; i++) {
- entries.put(time[i], new Entry(time[i]));
- }
- }
-
- public int getRows() {
- return rows;
- }
-
- public Entry getEntry(int index) {
- return this.entries.get(time[index]);
- }
-
- public int getIndex(String tm) {
- for (int i = 0; i < rows; i++) {
- if (tm.equals(time[i])) {
- return i;
- }
- }
- return -1;
- }
-
- public void processRequest(HttpServletRequest request, String tm) {
- int index = getIndex(tm);
- if (index >= 0) {
- String descr = request.getParameter("description");
- entries.get(time[index]).setDescription(descr);
- }
- }
-
-}
diff --git a/webapps/examples/WEB-INF/classes/cal/Entry.java b/webapps/examples/WEB-INF/classes/cal/Entry.java
deleted file mode 100644
index ac248bfa3169..000000000000
--- a/webapps/examples/WEB-INF/classes/cal/Entry.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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 cal;
-
-public class Entry {
-
- final String hour;
- String description;
-
- public Entry(String hour) {
- this.hour = hour;
- this.description = "";
-
- }
-
- public String getHour() {
- return this.hour;
- }
-
- public String getColor() {
- if (description.equals("")) {
- return "lightblue";
- }
- return "red";
- }
-
- public String getDescription() {
- if (description.equals("")) {
- return "None";
- }
- return this.description;
- }
-
- public void setDescription(String descr) {
- description = descr;
- }
-
-}
diff --git a/webapps/examples/WEB-INF/classes/cal/JspCalendar.java b/webapps/examples/WEB-INF/classes/cal/JspCalendar.java
deleted file mode 100644
index 29541cccb400..000000000000
--- a/webapps/examples/WEB-INF/classes/cal/JspCalendar.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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 cal;
-
-import java.util.Calendar;
-import java.util.Date;
-
-public class JspCalendar {
- final Calendar calendar;
-
- public JspCalendar() {
- calendar = Calendar.getInstance();
- Date trialTime = new Date();
- calendar.setTime(trialTime);
- }
-
-
- public int getYear() {
- return calendar.get(Calendar.YEAR);
- }
-
- public String getMonth() {
- int m = getMonthInt();
- String[] months = new String [] { "January", "February", "March",
- "April", "May", "June",
- "July", "August", "September",
- "October", "November", "December" };
- if (m > 12) {
- return "Unknown to Man";
- }
-
- return months[m - 1];
-
- }
-
- public String getDay() {
- int x = getDayOfWeek();
- String[] days = new String[] {"Sunday", "Monday", "Tuesday", "Wednesday",
- "Thursday", "Friday", "Saturday"};
-
- if (x > 7) {
- return "Unknown to Man";
- }
-
- return days[x - 1];
-
- }
-
- public int getMonthInt() {
- return 1 + calendar.get(Calendar.MONTH);
- }
-
- public String getDate() {
- return getMonthInt() + "/" + getDayOfMonth() + "/" + getYear();
- }
-
- public String getCurrentDate() {
- Date dt = new Date ();
- calendar.setTime (dt);
- return getMonthInt() + "/" + getDayOfMonth() + "/" + getYear();
-
- }
-
- public String getNextDate() {
- calendar.set (Calendar.DAY_OF_MONTH, getDayOfMonth() + 1);
- return getDate ();
- }
-
- public String getPrevDate() {
- calendar.set (Calendar.DAY_OF_MONTH, getDayOfMonth() - 1);
- return getDate ();
- }
-
- public String getTime() {
- return getHour() + ":" + getMinute() + ":" + getSecond();
- }
-
- public int getDayOfMonth() {
- return calendar.get(Calendar.DAY_OF_MONTH);
- }
-
- public int getDayOfYear() {
- return calendar.get(Calendar.DAY_OF_YEAR);
- }
-
- public int getWeekOfYear() {
- return calendar.get(Calendar.WEEK_OF_YEAR);
- }
-
- public int getWeekOfMonth() {
- return calendar.get(Calendar.WEEK_OF_MONTH);
- }
-
- public int getDayOfWeek() {
- return calendar.get(Calendar.DAY_OF_WEEK);
- }
-
- public int getHour() {
- return calendar.get(Calendar.HOUR_OF_DAY);
- }
-
- public int getMinute() {
- return calendar.get(Calendar.MINUTE);
- }
-
-
- public int getSecond() {
- return calendar.get(Calendar.SECOND);
- }
-
-
- public int getEra() {
- return calendar.get(Calendar.ERA);
- }
-
- public String getUSTimeZone() {
- String[] zones = new String[] {"Hawaii", "Alaskan", "Pacific",
- "Mountain", "Central", "Eastern"};
-
- return zones[10 + getZoneOffset()];
- }
-
- public int getZoneOffset() {
- return calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000);
- }
-
-
- public int getDSTOffset() {
- return calendar.get(Calendar.DST_OFFSET)/(60*60*1000);
- }
-
-
- public int getAMPM() {
- return calendar.get(Calendar.AM_PM);
- }
-}
-
-
diff --git a/webapps/examples/WEB-INF/classes/cal/TableBean.java b/webapps/examples/WEB-INF/classes/cal/TableBean.java
deleted file mode 100644
index 9f1cc4a6cf7d..000000000000
--- a/webapps/examples/WEB-INF/classes/cal/TableBean.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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 cal;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import javax.servlet.http.HttpServletRequest;
-
-public class TableBean {
-
- private final Map<String, Entries> table;
- private final JspCalendar JspCal;
- private Entries entries;
- private String date;
- private String name = null;
- private String email = null;
- private boolean processError = false;
-
- public TableBean() {
- this.table = new ConcurrentHashMap<>(10);
- this.JspCal = new JspCalendar();
- this.date = JspCal.getCurrentDate();
- }
-
- public void setName(String nm) {
- this.name = nm;
- }
-
- public String getName() {
- return this.name;
- }
-
- public void setEmail(String mail) {
- this.email = mail;
- }
-
- public String getEmail() {
- return this.email;
- }
-
- public String getDate() {
- return this.date;
- }
-
- public Entries getEntries() {
- return this.entries;
- }
-
- public void processRequest(HttpServletRequest request) {
-
- // Get the name and e-mail.
- this.processError = false;
- if (name == null || name.equals("")) {
- setName(request.getParameter("name"));
- }
- if (email == null || email.equals("")) {
- setEmail(request.getParameter("email"));
- }
- if (name == null || email == null || name.equals("")
- || email.equals("")) {
- this.processError = true;
- return;
- }
-
- // Get the date.
- String dateR = request.getParameter("date");
- if (dateR == null) {
- date = JspCal.getCurrentDate();
- } else if (dateR.equalsIgnoreCase("next")) {
- date = JspCal.getNextDate();
- } else if (dateR.equalsIgnoreCase("prev")) {
- date = JspCal.getPrevDate();
- }
-
- entries = table.get(date);
- if (entries == null) {
- entries = new Entries();
- table.put(date, entries);
- }
-
- // If time is provided add the event.
- String time = request.getParameter("time");
- if (time != null) {
- entries.processRequest(request, time);
- }
- }
-
- public boolean getProcessError() {
- return this.processError;
- }
-}
diff --git a/webapps/examples/jsp/cal/cal1.jsp b/webapps/examples/jsp/cal/cal1.jsp
deleted file mode 100644
index ce29c13f9608..000000000000
--- a/webapps/examples/jsp/cal/cal1.jsp
+++ /dev/null
@@ -1,94 +0,0 @@
-<%--
- 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.
---%>
-<%@page contentType="text/html; charset=UTF-8" %>
-<HTML>
-<HEAD><TITLE>
- Calendar: A JSP APPLICATION
-</TITLE></HEAD>
-
-
-<BODY BGCOLOR="white">
-
-<%@ page language="java" import="cal.*" %>
-<jsp:useBean id="table" scope="session" class="cal.TableBean" />
-
-<%
- table.processRequest(request);
- if (table.getProcessError() == false) {
-%>
-
-<!-- HTML table goes here -->
-<CENTER>
-<TABLE WIDTH=60% BGCOLOR=yellow CELLPADDING=15>
-<TR>
-<TD ALIGN=CENTER> <A HREF=cal1.jsp?date=prev> prev </A>
-<TD ALIGN=CENTER> Calendar:<%= table.getDate() %></TD>
-<TD ALIGN=CENTER> <A HREF=cal1.jsp?date=next> next </A>
-</TR>
-</TABLE>
-
-<!-- the main table -->
-<TABLE WIDTH=60% BGCOLOR=lightblue BORDER=1 CELLPADDING=10>
-<TR>
-<TH> Time </TH>
-<TH> Appointment </TH>
-</TR>
-<FORM METHOD=POST ACTION=cal1.jsp>
-<%
- for(int i=0; i<table.getEntries().getRows(); i++) {
- cal.Entry entr = table.getEntries().getEntry(i);
-%>
- <TR>
- <TD>
- <A HREF=cal2.jsp?time=<%= entr.getHour() %>>
- <%= entr.getHour() %> </A>
- </TD>
- <TD BGCOLOR=<%= entr.getColor() %>>
- <% out.print(util.HTMLFilter.filter(entr.getDescription())); %>
- </TD>
- </TR>
-<%
- }
-%>
-</FORM>
-</TABLE>
-<BR>
-
-<!-- footer -->
-<TABLE WIDTH=60% BGCOLOR=yellow CELLPADDING=15>
-<TR>
-<TD ALIGN=CENTER> <% out.print(util.HTMLFilter.filter(table.getName())); %> :
- <% out.print(util.HTMLFilter.filter(table.getEmail())); %> </TD>
-</TR>
-</TABLE>
-</CENTER>
-
-<%
- } else {
-%>
-<font size=5>
- You must enter your name and email address correctly.
-</font>
-<%
- }
-%>
-
-
-</BODY>
-</HTML>
-
-
diff --git a/webapps/examples/jsp/cal/cal2.jsp b/webapps/examples/jsp/cal/cal2.jsp
deleted file mode 100644
index e7e14d8e0468..000000000000
--- a/webapps/examples/jsp/cal/cal2.jsp
+++ /dev/null
@@ -1,45 +0,0 @@
-<%--
- 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.
---%>
-<%@page contentType="text/html; charset=UTF-8" %>
-<HTML>
-<HEAD><TITLE>
- Calendar: A JSP APPLICATION
-</TITLE></HEAD>
-
-
-<BODY BGCOLOR="white">
-<jsp:useBean id="table" scope="session" class="cal.TableBean" />
-
-<%
- String time = request.getParameter ("time");
-%>
-
-<FONT SIZE=5> Please add the following event:
-<BR> <h3> Date <%= table.getDate() %>
-<BR> Time <%= util.HTMLFilter.filter(time) %> </h3>
-</FONT>
-<FORM METHOD=POST ACTION=cal1.jsp>
-<BR>
-<BR> <INPUT NAME="date" TYPE=HIDDEN VALUE="current">
-<BR> <INPUT NAME="time" TYPE=HIDDEN VALUE="<%= util.HTMLFilter.filter(time) %>">
-<BR> <h2> Description of the event <INPUT NAME="description" TYPE=TEXT SIZE=20> </h2>
-<BR> <INPUT TYPE=SUBMIT VALUE="submit">
-</FORM>
-
-</BODY>
-</HTML>
-
diff --git a/webapps/examples/jsp/cal/calendar.html b/webapps/examples/jsp/cal/calendar.html
deleted file mode 100644
index a0a3ea184134..000000000000
--- a/webapps/examples/jsp/cal/calendar.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<html>
-<!--
- 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.
--->
-
-<head>
-<title>Untitled Document</title>
-<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
-</head>
-
-<body bgcolor="#FFFFFF">
-<p><font color="#0000FF"><a href="login.html"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
-
-<h2> Source Code for Calendar Example. <br>
-<h3><a href="cal1.jsp.html">cal1.jsp<font color="#0000FF"></a>
- </font> </h3>
-<h3><a href="cal2.jsp.html">cal2.jsp<font color="#0000FF"></a>
- </font> </h3>
-
-<br>
-<h2> Beans.
-<h3><a href="TableBean.java.html">TableBean<font color="#0000FF"></a>
- </font> </h3>
-<h3><a href="Entries.java.html">Entries<font color="#0000FF"></a>
- </font> </h3>
-<h3><a href="Entry.java.html">Entry<font color="#0000FF"></a>
- </font> </h3>
-
-</body>
-</html>
diff --git a/webapps/examples/jsp/cal/login.html b/webapps/examples/jsp/cal/login.html
deleted file mode 100644
index 8a62eca07b4d..000000000000
--- a/webapps/examples/jsp/cal/login.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<html>
-<!--
- 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.
--->
-
-<head>
- <title> Login page for the calendar. </title>
-</head>
-
-<body bgcolor="white">
-<center>
-
- <font size=7 color="red"> Please Enter the following information: </font>
-
-<br>
- <form method=GET action=cal1.jsp>
-
- <font size=5> Name <input type=text name="name" size=20>
- </font>
- <br>
- <font size=5> Email <input type=text name="email" size=20>
- </font>
- <br>
- <input type=submit name=action value="Submit">
-
- </form>
-<hr>
-<font size=3 color="red"> Note: This application does not implement the complete
-functionality of a typical calendar application. It demonstrates a way JSP can
-be used with HTML tables and forms.</font>
-
-</center>
-</body>
-</html>
diff --git a/webapps/examples/jsp/index.html b/webapps/examples/jsp/index.html
index dc25005b2710..ed2da43b9a12 100644
--- a/webapps/examples/jsp/index.html
+++ b/webapps/examples/jsp/index.html
@@ -249,14 +249,6 @@ <h2>JSP 1.2 Examples</h2>
<td style="width: 30%;"><a href="colors/clr.html"><img src="images/code.gif" alt=""></a><a href="colors/clr.html">Source</a></td>
</tr>
-<tr>
-<td>Calendar</td>
-
-<td style="width: 30%;"><a href="cal/login.html"><img src="images/execute.gif" alt=""></a><a href="cal/login.html">Execute</a></td>
-
-<td style="width: 30%;"><a href="cal/calendar.html"><img src="images/code.gif" alt=""></a><a href="cal/calendar.html">Source</a></td>
-</tr>
-
<tr>
<td>Include</td>

22
CVE-2024-54677-9.patch Normal file
View File

@ -0,0 +1,22 @@
From 9ffd23fc27f5d1fc95bf97e5cea175c8968f4533 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Fri, 6 Dec 2024 10:44:05 +0000
Subject: [PATCH] Revert change made for testing
---
webapps/examples/WEB-INF/classes/RequestHeaderExample.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/webapps/examples/WEB-INF/classes/RequestHeaderExample.java b/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
index e32f8c233674..b324e0250d8a 100644
--- a/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
+++ b/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
@@ -73,7 +73,7 @@ protected boolean prefersJSON(String acceptHeader) {
// text/html, application/html, etc.
if (accept.contains("html")) {
- return true;
+ return false;
}
}
return false;

440
CVE-2024-54677-pre.patch Normal file
View File

@ -0,0 +1,440 @@
From 5adf342f83eebd5a5e0a83141c56955a25f3a0c3 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Mon, 2 Dec 2024 14:19:14 +0000
Subject: [PATCH] Code clean-up - formatting. No functional change
Origin: https://github.com/apache/tomcat/commit/5adf342f83eebd5a5e0a83141c56955a25f3a0c3
---
.../WEB-INF/classes/CookieExample.java | 32 +++++-------
.../WEB-INF/classes/HelloWorldExample.java | 19 +++----
.../WEB-INF/classes/RequestHeaderExample.java | 49 ++++++-------------
.../WEB-INF/classes/RequestInfoExample.java | 25 ++++------
.../WEB-INF/classes/RequestParamExample.java | 22 +++------
.../WEB-INF/classes/ServletToJsp.java | 19 ++++---
.../WEB-INF/classes/SessionExample.java | 23 +++------
7 files changed, 67 insertions(+), 122 deletions(-)
diff --git a/webapps/examples/WEB-INF/classes/CookieExample.java b/webapps/examples/WEB-INF/classes/CookieExample.java
index cbf375e861bc..32eb1f806ce8 100644
--- a/webapps/examples/WEB-INF/classes/CookieExample.java
+++ b/webapps/examples/WEB-INF/classes/CookieExample.java
@@ -40,11 +40,8 @@ public class CookieExample extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
- public void doGet(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, ServletException
- {
- ResourceBundle rb = ResourceBundle.getBundle("LocalStrings",request.getLocale());
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+ ResourceBundle rb = ResourceBundle.getBundle("LocalStrings", request.getLocale());
String cookieName = request.getParameter("cookiename");
String cookieValue = request.getParameter("cookievalue");
@@ -75,11 +72,11 @@ public void doGet(HttpServletRequest request,
// addition of a PathInfo issue
out.println("<a href=\"../cookies.html\">");
- out.println("<img src=\"../images/code.gif\" height=24 " +
- "width=24 align=right border=0 alt=\"view code\"></a>");
+ out.println(
+ "<img src=\"../images/code.gif\" height=24 " + "width=24 align=right border=0 alt=\"view code\"></a>");
out.println("<a href=\"../index.html\">");
- out.println("<img src=\"../images/return.gif\" height=24 " +
- "width=24 align=right border=0 alt=\"return\"></a>");
+ out.println(
+ "<img src=\"../images/return.gif\" height=24 " + "width=24 align=right border=0 alt=\"return\"></a>");
out.println("<h3>" + title + "</h3>");
@@ -95,9 +92,8 @@ public void doGet(HttpServletRequest request,
String cName = cookie.getName();
String cValue = cookie.getValue();
out.print("Cookie Name: " + HTMLFilter.filter(cName) + "<br>");
- out.println(" Cookie Value: "
- + HTMLFilter.filter(CookieFilter.filter(cName, cValue, sessionId))
- + "<br><br>");
+ out.println(" Cookie Value: " + HTMLFilter.filter(CookieFilter.filter(cName, cValue, sessionId)) +
+ "<br><br>");
}
} else {
out.println(rb.getString("cookies.no-cookies"));
@@ -106,10 +102,8 @@ public void doGet(HttpServletRequest request,
if (aCookie != null) {
out.println("<P>");
out.println(rb.getString("cookies.set") + "<br>");
- out.print(rb.getString("cookies.name") + " "
- + HTMLFilter.filter(cookieName) + "<br>");
- out.print(rb.getString("cookies.value") + " "
- + HTMLFilter.filter(cookieValue));
+ out.print(rb.getString("cookies.name") + " " + HTMLFilter.filter(cookieName) + "<br>");
+ out.print(rb.getString("cookies.value") + " " + HTMLFilter.filter(cookieValue));
}
out.println("<P>");
@@ -128,13 +122,9 @@ public void doGet(HttpServletRequest request,
}
@Override
- public void doPost(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, ServletException
- {
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
doGet(request, response);
}
}
-
diff --git a/webapps/examples/WEB-INF/classes/HelloWorldExample.java b/webapps/examples/WEB-INF/classes/HelloWorldExample.java
index 15911e23af85..346733aba9ab 100644
--- a/webapps/examples/WEB-INF/classes/HelloWorldExample.java
+++ b/webapps/examples/WEB-INF/classes/HelloWorldExample.java
@@ -1,3 +1,4 @@
+
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -34,12 +35,8 @@ public class HelloWorldExample extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
- public void doGet(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, ServletException
- {
- ResourceBundle rb =
- ResourceBundle.getBundle("LocalStrings",request.getLocale());
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+ ResourceBundle rb = ResourceBundle.getBundle("LocalStrings", request.getLocale());
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
@@ -64,16 +61,14 @@ public void doGet(HttpServletRequest request,
// addition of a PathInfo issue
out.println("<a href=\"../helloworld.html\">");
- out.println("<img src=\"../images/code.gif\" height=24 " +
- "width=24 align=right border=0 alt=\"view code\"></a>");
+ out.println(
+ "<img src=\"../images/code.gif\" height=24 " + "width=24 align=right border=0 alt=\"view code\"></a>");
out.println("<a href=\"../index.html\">");
- out.println("<img src=\"../images/return.gif\" height=24 " +
- "width=24 align=right border=0 alt=\"return\"></a>");
+ out.println(
+ "<img src=\"../images/return.gif\" height=24 " + "width=24 align=right border=0 alt=\"return\"></a>");
out.println("<h1>" + title + "</h1>");
out.println("</body>");
out.println("</html>");
}
}
-
-
diff --git a/webapps/examples/WEB-INF/classes/RequestHeaderExample.java b/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
index df2d2d51f78a..98c0e6a9878b 100644
--- a/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
+++ b/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
@@ -42,10 +42,7 @@ public class RequestHeaderExample extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
- public void doGet(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, ServletException
- {
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
if (prefersJSON(request.getHeader("Accept"))) {
renderJSON(request, response);
} else {
@@ -54,16 +51,12 @@ public void doGet(HttpServletRequest request,
}
/**
- * Returns true if the client appears to prefer a JSON response,
- * false otherwise.
- *
- * Note that this method is not very pedantic and uses only a very lazy
- * algorithm for checking whether JSON is "preferred".
+ * Returns true if the client appears to prefer a JSON response, false otherwise. Note that this method is not very
+ * pedantic and uses only a very lazy algorithm for checking whether JSON is "preferred".
*
* @param acceptHeader The value of the HTTP "Accept" header from the client.
*
- * @return true if the client appears to prefer a JSON response,
- * false otherwise.
+ * @return true if the client appears to prefer a JSON response, false otherwise.
*/
protected boolean prefersJSON(String acceptHeader) {
if (null == acceptHeader) {
@@ -87,11 +80,8 @@ protected boolean prefersJSON(String acceptHeader) {
return false;
}
- protected void renderHTML(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException
- {
- ResourceBundle rb = ResourceBundle.getBundle("LocalStrings",request.getLocale());
+ protected void renderHTML(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ ResourceBundle rb = ResourceBundle.getBundle("LocalStrings", request.getLocale());
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
@@ -113,11 +103,11 @@ protected void renderHTML(HttpServletRequest request,
// addition of a PathInfo issue
out.println("<a href=\"../reqheaders.html\">");
- out.println("<img src=\"../images/code.gif\" height=24 " +
- "width=24 align=right border=0 alt=\"view code\"></a>");
+ out.println(
+ "<img src=\"../images/code.gif\" height=24 " + "width=24 align=right border=0 alt=\"view code\"></a>");
out.println("<a href=\"../index.html\">");
- out.println("<img src=\"../images/return.gif\" height=24 " +
- "width=24 align=right border=0 alt=\"return\"></a>");
+ out.println(
+ "<img src=\"../images/return.gif\" height=24 " + "width=24 align=right border=0 alt=\"return\"></a>");
out.println("<h3>" + title + "</h3>");
out.println("<table border=0>");
@@ -143,9 +133,7 @@ protected void renderHTML(HttpServletRequest request,
out.println("</table>");
}
- protected void renderJSON(HttpServletRequest request, HttpServletResponse response)
- throws IOException
- {
+ protected void renderJSON(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
@@ -157,14 +145,10 @@ protected void renderJSON(HttpServletRequest request, HttpServletResponse respon
String headerName = e.nextElement();
String headerValue = request.getHeader(headerName);
- out.append("{\"")
- .append(JSONFilter.escape(headerName))
- .append("\":\"")
- .append(JSONFilter.escape(headerValue))
- .append("\"}")
- ;
+ out.append("{\"").append(JSONFilter.escape(headerName)).append("\":\"")
+ .append(JSONFilter.escape(headerValue)).append("\"}");
- if(e.hasMoreElements()) {
+ if (e.hasMoreElements()) {
out.append(',');
}
}
@@ -173,10 +157,7 @@ protected void renderJSON(HttpServletRequest request, HttpServletResponse respon
}
@Override
- public void doPost(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, ServletException
- {
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
doGet(request, response);
}
diff --git a/webapps/examples/WEB-INF/classes/RequestInfoExample.java b/webapps/examples/WEB-INF/classes/RequestInfoExample.java
index 9ea4668ad42d..57665bb2c51b 100644
--- a/webapps/examples/WEB-INF/classes/RequestInfoExample.java
+++ b/webapps/examples/WEB-INF/classes/RequestInfoExample.java
@@ -37,11 +37,8 @@ public class RequestInfoExample extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
- public void doGet(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, ServletException
- {
- ResourceBundle rb = ResourceBundle.getBundle("LocalStrings",request.getLocale());
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+ ResourceBundle rb = ResourceBundle.getBundle("LocalStrings", request.getLocale());
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
@@ -64,11 +61,11 @@ public void doGet(HttpServletRequest request,
// addition of a PathInfo issue
out.println("<a href=\"../reqinfo.html\">");
- out.println("<img src=\"../images/code.gif\" height=24 " +
- "width=24 align=right border=0 alt=\"view code\"></a>");
+ out.println(
+ "<img src=\"../images/code.gif\" height=24 " + "width=24 align=right border=0 alt=\"view code\"></a>");
out.println("<a href=\"../index.html\">");
- out.println("<img src=\"../images/return.gif\" height=24 " +
- "width=24 align=right border=0 alt=\"return\"></a>");
+ out.println(
+ "<img src=\"../images/return.gif\" height=24 " + "width=24 align=right border=0 alt=\"return\"></a>");
out.println("<h3>" + title + "</h3>");
out.println("<table border=0><tr><td>");
@@ -93,9 +90,8 @@ public void doGet(HttpServletRequest request,
out.println(HTMLFilter.filter(request.getRemoteAddr()));
out.println("</td></tr>");
- String cipherSuite=
- (String)request.getAttribute("javax.servlet.request.cipher_suite");
- if(cipherSuite!=null){
+ String cipherSuite = (String) request.getAttribute("javax.servlet.request.cipher_suite");
+ if (cipherSuite != null) {
out.println("<tr><td>");
out.println("SSLCipherSuite:");
out.println("</td><td>");
@@ -107,10 +103,7 @@ public void doGet(HttpServletRequest request,
}
@Override
- public void doPost(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, ServletException
- {
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
doGet(request, response);
}
diff --git a/webapps/examples/WEB-INF/classes/RequestParamExample.java b/webapps/examples/WEB-INF/classes/RequestParamExample.java
index cbcc3712b6d4..69482936c344 100644
--- a/webapps/examples/WEB-INF/classes/RequestParamExample.java
+++ b/webapps/examples/WEB-INF/classes/RequestParamExample.java
@@ -37,11 +37,8 @@ public class RequestParamExample extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
- public void doGet(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, ServletException
- {
- ResourceBundle rb = ResourceBundle.getBundle("LocalStrings",request.getLocale());
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+ ResourceBundle rb = ResourceBundle.getBundle("LocalStrings", request.getLocale());
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
@@ -58,18 +55,18 @@ public void doGet(HttpServletRequest request,
// img stuff not req'd for source code HTML showing
- // all links relative
+ // all links relative
// XXX
// making these absolute till we work out the
// addition of a PathInfo issue
out.println("<a href=\"../reqparams.html\">");
- out.println("<img src=\"../images/code.gif\" height=24 " +
- "width=24 align=right border=0 alt=\"view code\"></a>");
+ out.println(
+ "<img src=\"../images/code.gif\" height=24 " + "width=24 align=right border=0 alt=\"view code\"></a>");
out.println("<a href=\"../index.html\">");
- out.println("<img src=\"../images/return.gif\" height=24 " +
- "width=24 align=right border=0 alt=\"return\"></a>");
+ out.println(
+ "<img src=\"../images/return.gif\" height=24 " + "width=24 align=right border=0 alt=\"return\"></a>");
out.println("<h3>" + title + "</h3>");
String firstName = request.getParameter("firstname");
@@ -101,10 +98,7 @@ public void doGet(HttpServletRequest request,
}
@Override
- public void doPost(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, ServletException
- {
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
doGet(request, response);
}
diff --git a/webapps/examples/WEB-INF/classes/ServletToJsp.java b/webapps/examples/WEB-INF/classes/ServletToJsp.java
index a4903a9bba74..e83bbf377620 100644
--- a/webapps/examples/WEB-INF/classes/ServletToJsp.java
+++ b/webapps/examples/WEB-INF/classes/ServletToJsp.java
@@ -24,16 +24,15 @@ public class ServletToJsp extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
- public void doGet (HttpServletRequest request,
- HttpServletResponse response) {
+ public void doGet(HttpServletRequest request, HttpServletResponse response) {
- try {
- // Set the attribute and Forward to hello.jsp
- request.setAttribute ("servletName", "servletToJsp");
- getServletConfig().getServletContext().getRequestDispatcher(
- "/jsp/jsptoserv/hello.jsp").forward(request, response);
- } catch (Exception ex) {
- ex.printStackTrace ();
- }
+ try {
+ // Set the attribute and Forward to hello.jsp
+ request.setAttribute("servletName", "servletToJsp");
+ getServletConfig().getServletContext().getRequestDispatcher("/jsp/jsptoserv/hello.jsp").forward(request,
+ response);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
}
}
diff --git a/webapps/examples/WEB-INF/classes/SessionExample.java b/webapps/examples/WEB-INF/classes/SessionExample.java
index c71ef5f743bb..724b9b437d67 100644
--- a/webapps/examples/WEB-INF/classes/SessionExample.java
+++ b/webapps/examples/WEB-INF/classes/SessionExample.java
@@ -40,11 +40,8 @@ public class SessionExample extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
- public void doGet(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, ServletException
- {
- ResourceBundle rb = ResourceBundle.getBundle("LocalStrings",request.getLocale());
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+ ResourceBundle rb = ResourceBundle.getBundle("LocalStrings", request.getLocale());
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
@@ -68,11 +65,11 @@ public void doGet(HttpServletRequest request,
// addition of a PathInfo issue
out.println("<a href=\"../sessions.html\">");
- out.println("<img src=\"../images/code.gif\" height=24 " +
- "width=24 align=right border=0 alt=\"view code\"></a>");
+ out.println(
+ "<img src=\"../images/code.gif\" height=24 " + "width=24 align=right border=0 alt=\"view code\"></a>");
out.println("<a href=\"../index.html\">");
- out.println("<img src=\"../images/return.gif\" height=24 " +
- "width=24 align=right border=0 alt=\"return\"></a>");
+ out.println(
+ "<img src=\"../images/return.gif\" height=24 " + "width=24 align=right border=0 alt=\"return\"></a>");
out.println("<h3>" + title + "</h3>");
@@ -96,8 +93,7 @@ public void doGet(HttpServletRequest request,
while (names.hasMoreElements()) {
String name = names.nextElement();
String value = session.getAttribute(name).toString();
- out.println(HTMLFilter.filter(name) + " = "
- + HTMLFilter.filter(value) + "<br>");
+ out.println(HTMLFilter.filter(name) + " = " + HTMLFilter.filter(value) + "<br>");
}
out.println("<P>");
@@ -137,10 +133,7 @@ public void doGet(HttpServletRequest request,
}
@Override
- public void doPost(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, ServletException
- {
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
doGet(request, response);
}

Binary file not shown.

88
build-with-jdk-1.8.patch Normal file
View File

@ -0,0 +1,88 @@
diff --git a/build.xml b/build.xml
index 5c09831..9efcd81 100644
--- a/build.xml
+++ b/build.xml
@@ -108,8 +108,8 @@
<!-- Keep in sync with webapps/docs/tomcat-docs.xsl -->
<property name="compile.release" value="8"/>
<property name="min.java.version" value="8"/>
- <property name="build.java.version" value="17"/>
- <property name="release.java.version" value="17"/>
+ <property name="build.java.version" value="1.8"/>
+ <property name="release.java.version" value="1.8"/>
<!-- Check Java Build Version -->
<fail message="Java version ${build.java.version} or newer is required (${java.version} is installed)">
@@ -1497,8 +1497,6 @@
<target name="deploy" depends="package,build-docs,build-tomcat-jdbc,compile-webapp-examples"
description="Default. Builds a working Tomcat instance">
- <copy tofile="${tomcat.build}/bin/commons-daemon.jar" file="${commons-daemon.jar}" />
-
<!-- Copy scripts -->
<copy todir="${tomcat.build}/bin">
<fileset dir="bin">
diff --git a/java/org/apache/jasper/compiler/JDTCompiler.java b/java/org/apache/jasper/compiler/JDTCompiler.java
index 1ed4ef4..8339eff 100644
--- a/java/org/apache/jasper/compiler/JDTCompiler.java
+++ b/java/org/apache/jasper/compiler/JDTCompiler.java
@@ -297,17 +297,17 @@ public class JDTCompiler extends org.apache.jasper.compiler.Compiler {
} else if(opt.equals("10")) {
settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_10);
} else if(opt.equals("11")) {
- settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_11);
+ settings.put(CompilerOptions.OPTION_Source, "11");
} else if(opt.equals("12")) {
- settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_12);
+ settings.put(CompilerOptions.OPTION_Source, "12");
} else if(opt.equals("13")) {
- settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_13);
+ settings.put(CompilerOptions.OPTION_Source, "13");
} else if(opt.equals("14")) {
- settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_14);
+ settings.put(CompilerOptions.OPTION_Source, "14");
} else if(opt.equals("15")) {
- settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_15);
+ settings.put(CompilerOptions.OPTION_Source, "15");
} else if(opt.equals("16")) {
- settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_16);
+ settings.put(CompilerOptions.OPTION_Source, "16");
} else if(opt.equals("17")) {
// Constant not available in latest ECJ version that runs on
// Java 8.
@@ -389,23 +389,23 @@ public class JDTCompiler extends org.apache.jasper.compiler.Compiler {
settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_10);
settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_10);
} else if(opt.equals("11")) {
- settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_11);
- settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_11);
+ settings.put(CompilerOptions.OPTION_TargetPlatform, "11");
+ settings.put(CompilerOptions.OPTION_Compliance, "11");
} else if(opt.equals("12")) {
- settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_12);
- settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_12);
+ settings.put(CompilerOptions.OPTION_TargetPlatform, "12");
+ settings.put(CompilerOptions.OPTION_Compliance, "12");
} else if(opt.equals("13")) {
- settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_13);
- settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_13);
+ settings.put(CompilerOptions.OPTION_TargetPlatform, "13");
+ settings.put(CompilerOptions.OPTION_Compliance, "13");
} else if(opt.equals("14")) {
- settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_14);
- settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_14);
+ settings.put(CompilerOptions.OPTION_TargetPlatform, "14");
+ settings.put(CompilerOptions.OPTION_Compliance, "14");
} else if(opt.equals("15")) {
- settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_15);
- settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_15);
+ settings.put(CompilerOptions.OPTION_TargetPlatform, "15");
+ settings.put(CompilerOptions.OPTION_Compliance, "15");
} else if(opt.equals("16")) {
- settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_16);
- settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_16);
+ settings.put(CompilerOptions.OPTION_TargetPlatform, "16");
+ settings.put(CompilerOptions.OPTION_Compliance, "16");
} else if(opt.equals("17")) {
// Constant not available in latest ECJ version that runs on
// Java 8.

View File

@ -1,47 +0,0 @@
--- tomcat/build.xml.orig 2018-03-15 13:49:03.366863009 -0400
+++ tomcat/build.xml 2018-03-15 13:49:29.690870139 -0400
@@ -1777,7 +1777,7 @@ Apache Tomcat ${version} native binaries
additionalparam="-breakiterator -notimestamp"
maxmemory="512m"
failonerror="true"
- failonwarning="true">
+ failonwarning="false">
<classpath>
<path refid="compile.classpath"/>
<path refid="tomcat.webservices.classpath"/>
@@ -1798,7 +1798,7 @@ Apache Tomcat ${version} native binaries
additionalparam="-breakiterator -notimestamp"
maxmemory="512m"
failonerror="true"
- failonwarning="true">
+ failonwarning="false">
<classpath>
<path refid="compile.classpath"/>
<path refid="tomcat.webservices.classpath"/>
@@ -1819,7 +1819,7 @@ Apache Tomcat ${version} native binaries
additionalparam="-breakiterator -notimestamp"
maxmemory="512m"
failonerror="true"
- failonwarning="true">
+ failonwarning="false">
<classpath>
<path refid="compile.classpath"/>
<path refid="tomcat.webservices.classpath"/>
@@ -1840,7 +1840,7 @@ Apache Tomcat ${version} native binaries
additionalparam="-breakiterator -notimestamp"
maxmemory="512m"
failonerror="true"
- failonwarning="true">
+ failonwarning="false">
<classpath>
<path refid="compile.classpath"/>
<path refid="tomcat.webservices.classpath"/>
@@ -1860,7 +1860,7 @@ Apache Tomcat ${version} native binaries
additionalparam="-breakiterator -notimestamp ${java9.add.modules}"
maxmemory="512m"
failonerror="true"
- failonwarning="true">
+ failonwarning="false">
<classpath>
<path refid="compile.classpath"/>
<path refid="tomcat.webservices.classpath"/>

View File

@ -1,13 +0,0 @@
Manifest-Version: 1.0
Export-Package: javax.el;version="2.2.0"
Bundle-Vendor: %bundleProvider
Bundle-ClassPath: .
Bundle-Version: 2.2.0
Bundle-Name: %bundleName
Bundle-Localization: plugin
Bundle-ManifestVersion: 2
Bundle-SymbolicName: javax.el
DynamicImport-Package: org.apache.el
Bundle-RequiredExecutionEnvironment: J2SE-1.4,CDC-1.0/Foundation-1.0,J
2SE-1.3

View File

@ -1,40 +0,0 @@
Manifest-Version: 1.0
Export-Package: org.apache.jasper;version="8.0.26",org.apache.jasper.c
ompiler;version="8.0.26",org.apache.jasper.compiler.tagplugin;version
="8.0.26",org.apache.jasper.resources;version="8.0.26",org.apache.jas
per.runtime;version="8.0.26",org.apache.jasper.security;version="7.0.
19",org.apache.jasper.servlet;version="8.0.26",org.apache.jasper.tagp
lugins.jstl;version="8.0.26",org.apache.jasper.tagplugins.jstl.core;v
ersion="8.0.26",org.apache.jasper.util;version="8.0.26",org.apache.ja
sper.xmlparser;version="8.0.26"
Bundle-Vendor: %bundleProvider
Bundle-ClassPath: .
Bundle-Version: 8.0.26
Bundle-Localization: plugin
Bundle-Name: %bundleName
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.apache.jasper
Import-Package: javax.servlet;version="[2.4.0, 3.0.0]",javax.servlet.h
ttp;version="[2.4.0, 3.0.0]",javax.servlet.jsp;version="[2.0.0, 2.2.0
]",javax.servlet.jsp.el;version="[2.0.0, 2.2.0]",javax.servlet.jsp.re
sources;version="[2.0.0, 2.2.0]",javax.servlet.jsp.tagext;version="[2
.0.0, 2.2.0]",javax.servlet.resources;version="[2.4.0, 3.0.0]",javax.
xml.parsers,org.apache.commons.el;version="[1.0.0,2.0.0)",org.apache.
commons.logging;version="[1.0.0,2.0.0)",org.apache.tools.ant;resoluti
on:=optional,org.apache.tools.ant.taskdefs;resolution:=optional,org.a
pache.tools.ant.types;resolution:=optional,org.apache.tools.ant.util;
resolution:=optional,org.w3c.dom,org.xml.sax,org.xml.sax.ext,org.xml.
sax.helpers,org.apache.tomcat;version="8.0.26",org.apache.juli.loggin
g;version="8.0.26",javax.el;version="2.2.0",org.eclipse.jdt.internal.
compiler,org.eclipse.jdt.internal.compiler.parser,org.eclipse.jdt.int
ernal.compiler.parser.diagnose,org.eclipse.jdt.internal.compiler.flow
,org.eclipse.jdt.internal.compiler.util,org.eclipse.jdt.internal.comp
iler.impl,org.eclipse.jdt.internal.compiler.lookup,org.eclipse.jdt.in
ternal.compiler.codegen,org.eclipse.jdt.internal.compiler.batch,org.e
clipse.jdt.internal.compiler.classfmt,org.eclipse.jdt.internal.compil
er.ast,org.eclipse.jdt.internal.compiler.problem,org.eclipse.jdt.inte
rnal.compiler.env,org.eclipse.jdt.internal.core.util,org.eclipse.jdt.
core.compiler
Bundle-RequiredExecutionEnvironment: J2SE-1.4,CDC-1.0/Foundation-1.0,J
2SE-1.3

View File

@ -1,13 +0,0 @@
Manifest-Version: 1.0
Export-Package: org.apache.el;version="7.0.21"
Bundle-Vendor: %bundleProvider
Bundle-ClassPath: .
Bundle-Version: 8.0.26
Bundle-Name: %bundleName
Bundle-Localization: plugin
Bundle-ManifestVersion: 2
Import-Package: javax.el;version="2.2"
Bundle-SymbolicName: org.apache.el
Bundle-RequiredExecutionEnvironment: J2SE-1.4,CDC-1.0/Foundation-1.0,J
2SE-1.3

View File

@ -0,0 +1,7 @@
# Add the JAVA 9 specific start-up parameters required by Tomcat
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.lang=ALL-UNNAMED"
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.io=ALL-UNNAMED"
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.util=ALL-UNNAMED"
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.util.concurrent=ALL-UNNAMED"
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED"
export JDK_JAVA_OPTIONS

View File

@ -1,13 +0,0 @@
Manifest-Version: 1.0
Bundle-Vendor: %bundleProvider
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: CDC-1.0/Foundation-1.0,J2SE-1.3
Bundle-Name: %bundleName
Bundle-SymbolicName: javax.servlet.jsp
Export-Package: javax.servlet.jsp; version=2.2,javax.servlet.jsp.el; v
ersion=2.2,javax.servlet.jsp.resources; version=2.2,javax.servlet.jsp
.tagext; version=2.2
Bundle-Version: 2.2.0.v200806031607
Bundle-ManifestVersion: 2
Import-Package: javax.servlet; version=3.0,javax.servlet.http; version
=3.0,javax.servlet.resources; version=3.0,javax.el;version="2.2.0"

View File

@ -0,0 +1,74 @@
diff --git a/build.xml b/build.xml
index 1a4b255..ea50aeb 100644
--- a/build.xml
+++ b/build.xml
@@ -3296,7 +3296,7 @@ asf.ldap.username=${release.asfusername}
<target name="download-compile"
description="Download components necessary to compile"
- depends="setup-bnd">
+ >
<!-- Download Commons Daemon -->
<antcall target="downloadgz-2">
diff --git a/java/org/apache/el/ExpressionFactoryImpl.java b/java/org/apache/el/ExpressionFactoryImpl.java
index 3a6690a..03a2afe 100644
--- a/java/org/apache/el/ExpressionFactoryImpl.java
+++ b/java/org/apache/el/ExpressionFactoryImpl.java
@@ -34,7 +34,7 @@ import org.apache.el.util.MessageFactory;
*
* @author Jacob Hookom [jacob@hookom.net]
*/
-@aQute.bnd.annotation.spi.ServiceProvider(value = ExpressionFactory.class)
+//@aQute.bnd.annotation.spi.ServiceProvider(value = ExpressionFactory.class)
public class ExpressionFactoryImpl extends ExpressionFactory {
static {
diff --git a/java/org/apache/juli/logging/LogFactory.java b/java/org/apache/juli/logging/LogFactory.java
index bfc4238..acf989a 100644
--- a/java/org/apache/juli/logging/LogFactory.java
+++ b/java/org/apache/juli/logging/LogFactory.java
@@ -21,7 +21,7 @@ import java.nio.file.FileSystems;
import java.util.ServiceLoader;
import java.util.logging.LogManager;
-import aQute.bnd.annotation.spi.ServiceConsumer;
+//import aQute.bnd.annotation.spi.ServiceConsumer;
/**
* This is a modified LogFactory that uses a simple {@link ServiceLoader} based
@@ -63,7 +63,7 @@ import aQute.bnd.annotation.spi.ServiceConsumer;
* @author Costin Manolache
* @author Richard A. Sitze
*/
-@ServiceConsumer(value=Log.class)
+//@ServiceConsumer(value=Log.class)
public class LogFactory {
private static final LogFactory singleton = new LogFactory();
diff --git a/java/org/apache/tomcat/websocket/WsContainerProvider.java b/java/org/apache/tomcat/websocket/WsContainerProvider.java
index 4b0577c..e383290 100644
--- a/java/org/apache/tomcat/websocket/WsContainerProvider.java
+++ b/java/org/apache/tomcat/websocket/WsContainerProvider.java
@@ -19,7 +19,7 @@ package org.apache.tomcat.websocket;
import javax.websocket.ContainerProvider;
import javax.websocket.WebSocketContainer;
-@aQute.bnd.annotation.spi.ServiceProvider(value = ContainerProvider.class)
+//@aQute.bnd.annotation.spi.ServiceProvider(value = ContainerProvider.class)
public class WsContainerProvider extends ContainerProvider {
@Override
diff --git a/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java b/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java
index 00f492e..fe5c34d 100644
--- a/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java
+++ b/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java
@@ -26,7 +26,7 @@ import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
-@aQute.bnd.annotation.spi.ServiceProvider(value = ServerEndpointConfig.Configurator.class)
+//@aQute.bnd.annotation.spi.ServiceProvider(value = ServerEndpointConfig.Configurator.class)
public class DefaultServerEndpointConfigurator extends ServerEndpointConfig.Configurator {
@Override

208
rhbz-1857043.patch Normal file
View File

@ -0,0 +1,208 @@
diff -up a/build.xml.orig b/build.xml
--- a/build.xml.orig 2021-07-07 10:53:55.493742841 +0800
+++ b/build.xml 2021-07-07 11:09:43.107968515 +0800
@@ -1020,7 +1020,7 @@
filesDir="${tomcat.classes}"
filesId="files.annotations-api"
manifest="${tomcat.manifests}/annotations-api.jar.manifest"
- addOSGi="true" />
+ addOSGi="false" />
<!-- Servlet Implementation JAR File -->
<jarIt jarfile="${servlet-api.jar}"
@@ -1029,41 +1029,41 @@
manifest="${tomcat.manifests}/servlet-api.jar.manifest"
notice="${tomcat.manifests}/servlet-api.jar.notice"
license="${tomcat.manifests}/servlet-api.jar.license"
- addOSGi="true" />
+ addOSGi="false" />
<!-- EL Implementation JAR File -->
<jarIt jarfile="${el-api.jar}"
filesDir="${tomcat.classes}"
filesId="files.el-api"
manifest="${tomcat.manifests}/el-api.jar.manifest"
- addOSGi="true" />
+ addOSGi="false" />
<!-- JSP Implementation JAR File -->
<jarIt jarfile="${jsp-api.jar}"
filesDir="${tomcat.classes}"
filesId="files.jsp-api"
manifest="${tomcat.manifests}/jsp-api.jar.manifest"
- addOSGi="true" />
+ addOSGi="false" />
<!-- WebSocket API JAR File -->
<jarIt jarfile="${websocket-api.jar}"
filesDir="${tomcat.classes}"
filesId="files.websocket-api"
manifest="${tomcat.manifests}/websocket-api.jar.manifest"
- addOSGi="true" />
+ addOSGi="false" />
<!-- JASPIC API JAR File -->
<jarIt jarfile="${jaspic-api.jar}"
filesDir="${tomcat.classes}"
filesId="files.jaspic-api"
manifest="${tomcat.manifests}/jaspic-api.jar.manifest"
- addOSGi="true" />
+ addOSGi="false" />
<!-- Tomcat-juli JAR File -->
<jarIt jarfile="${tomcat-juli.jar}"
filesDir="${tomcat.classes}"
filesId="files.tomcat-juli"
- addOSGi="true" />
+ addOSGi="false" />
<!-- Bootstrap JAR File -->
<jarIt jarfile="${bootstrap.jar}"
@@ -1075,68 +1075,68 @@
<jarIt jarfile="${tomcat-util.jar}"
filesDir="${tomcat.classes}"
filesId="files.tomcat-util"
- addOSGi="true" />
+ addOSGi="false" />
<!-- Tomcat API JAR File -->
<jarIt jarfile="${tomcat-api.jar}"
filesDir="${tomcat.classes}"
filesId="files.tomcat-api"
- addOSGi="true" />
+ addOSGi="false" />
<!-- Tomcat Util Scan JAR File -->
<jarIt jarfile="${tomcat-util-scan.jar}"
filesDir="${tomcat.classes}"
filesId="files.tomcat-util-scan"
- addOSGi="true" />
+ addOSGi="false" />
<jarIt jarfile="${tomcat-jni.jar}"
filesDir="${tomcat.classes}"
filesId="files.tomcat-jni"
- addOSGi="true" />
+ addOSGi="false" />
<!-- Protocol handlers - Coyote -->
<jarIt jarfile="${tomcat-coyote.jar}"
filesDir="${tomcat.classes}"
filesId="files.tomcat-coyote"
- addOSGi="true" />
+ addOSGi="false" />
<!-- OpenSSL FFM - Coyote -->
<jarIt jarfile="${tomcat-coyote-ffm.jar}"
filesDir="${tomcat.classes}"
filesId="files.tomcat-coyote-ffm"
manifest="${tomcat.manifests}/tomcat-coyote-ffm.jar.manifest"
- addOSGi="true" />
+ addOSGi="false" />
<!-- WebSocket implementation JAR File -->
<jarIt jarfile="${tomcat-websocket.jar}"
filesDir="${tomcat.classes}"
filesId="files.tomcat-websocket"
meta-inf="${tomcat.manifests}/tomcat-websocket.jar"
- addOSGi="true" />
+ addOSGi="false" />
<!-- Catalina GroupCom/Tribes JAR File -->
<jarIt jarfile="${catalina-tribes.jar}"
filesDir="${tomcat.classes}"
filesId="files.catalina-tribes"
- addOSGi="true" />
+ addOSGi="false" />
<!-- Catalina Main JAR File -->
<jarIt jarfile="${catalina.jar}"
filesDir="${tomcat.classes}"
filesId="files.catalina"
- addOSGi="true" />
+ addOSGi="false" />
<!-- Catalina Cluster/HA JAR File -->
<jarIt jarfile="${catalina-ha.jar}"
filesDir="${tomcat.classes}"
filesId="files.catalina-ha"
- addOSGi="true" />
+ addOSGi="false" />
<!-- Server-Side Includes (SSI) -->
<jarIt jarfile="${catalina-ssi.jar}"
filesDir="${tomcat.classes}"
filesId="files.catalina-ssi"
- addOSGi="true" />
+ addOSGi="false" />
<!-- Catalina Ant Tasks JAR File -->
<jarIt jarfile="${catalina-ant.jar}"
@@ -1140,27 +1140,27 @@
<jarIt jarfile="${catalina-storeconfig.jar}"
filesDir="${tomcat.classes}"
filesId="files.catalina-storeconfig"
- addOSGi="true" />
+ addOSGi="false" />
<!-- Jasper EL Implementation JAR File -->
<jarIt jarfile="${jasper-el.jar}"
filesDir="${tomcat.classes}"
filesId="files.jasper-el"
meta-inf="${tomcat.manifests}/jasper-el.jar"
- addOSGi="true" />
+ addOSGi="false" />
<!-- Jasper Implementation JAR File -->
<jarIt jarfile="${jasper.jar}"
filesDir="${tomcat.classes}"
filesId="files.jasper"
meta-inf="${tomcat.manifests}/jasper.jar"
- addOSGi="true" />
+ addOSGi="false" />
<!-- Re-packaged Apache Commons DBCP 2-->
<jarIt jarfile="${tomcat-dbcp.jar}"
filesDir="${tomcat.classes}"
filesId="files.tomcat-dbcp"
- addOSGi="true" />
+ addOSGi="false" />
<!-- i18n JARs -->
<jar jarfile="${tomcat.build}/lib/tomcat-i18n-cs.jar"
@@ -1620,7 +1620,7 @@
filesId="files.tomcat-embed-core"
notice="${tomcat.manifests}/servlet-api.jar.notice"
license="${tomcat.manifests}/servlet-api.jar.license"
- addOSGi="true"
+ addOSGi="false"
addGraal="true"
graalPrefix="org.apache.tomcat.embed/tomcat-embed-core"
graalFiles="res/graal/tomcat-embed-core/native-image"
@@ -1628,7 +1628,7 @@
<jarIt jarfile="${tomcat-embed-el.jar}"
filesDir="${tomcat.classes}"
filesId="files.tomcat-embed-el"
- addOSGi="true"
+ addOSGi="false"
addGraal="true"
graalPrefix="org.apache.tomcat.embed/tomcat-embed-el"
graalFiles="res/graal/tomcat-embed-el/native-image"
@@ -1637,7 +1637,7 @@
filesDir="${tomcat.classes}"
filesId="files.tomcat-embed-jasper"
meta-inf="${tomcat.manifests}/jasper.jar"
- addOSGi="true"
+ addOSGi="false"
addGraal="true"
graalPrefix="org.apache.tomcat.embed/tomcat-embed-jasper"
graalFiles="res/graal/tomcat-embed-jasper/native-image"
@@ -1646,7 +1646,7 @@
filesDir="${tomcat.classes}"
filesId="files.tomcat-embed-websocket"
meta-inf="${tomcat.manifests}/tomcat-websocket.jar"
- addOSGi="true"
+ addOSGi="false"
addGraal="true"
graalPrefix="org.apache.tomcat.embed/tomcat-embed-websocket"
graalFiles="res/graal/tomcat-embed-websocket/native-image"

View File

@ -1,17 +0,0 @@
Manifest-Version: 1.0
Bundle-RequiredExecutionEnvironment: CDC-1.1/Foundation-1.1,J2SE-1.4
Bundle-SymbolicName: javax.servlet
Bundle-ManifestVersion: 2
Bundle-Name: %bundleName
Bundle-Localization: plugin
Bundle-Version: 3.0.0
Bundle-Vendor: %bundleProvider
Export-Package: javax.servlet;version="3.0",
javax.servlet;version="2.6",
javax.servlet.http;version="3.0",
javax.servlet.http;version="2.6",
javax.servlet.annotation;version="2.6",
javax.servlet.descriptor;version="3.0",
javax.servlet.descriptor;version="2.6",
javax.servlet.resources;version="3.0",
javax.servlet.resources;version="2.6"

View File

@ -0,0 +1,25 @@
--- tomcat/conf/catalina.policy.orig 2022-11-04 16:17:41.227506990 +0800
+++ tomcat/conf/catalina.policy 2022-11-04 16:21:51.393351415 +0800
@@ -56,6 +56,15 @@ grant codeBase "file:${java.home}/lib/ex
// permission java.security.AllPermission;
//};
+// ========== RHEL SPECIFIC CODE PERMISSIONS =======================================
+
+
+// Allowing everything in /usr/share/java allows too many unknowns to be permitted
+// Specifying the individual jars that tomcat needs to function with the security manager
+// is the safest way forward.
+grant codeBase "file:/usr/share/java/ecj/ecj.jar" {
+ permission java.security.AllPermission;
+};
// ========== CATALINA CODE PERMISSIONS =======================================
@@ -261,4 +270,4 @@ grant codeBase "file:${catalina.home}/we
//
// The permissions granted to a specific JAR
// grant codeBase "war:file:${catalina.base}/webapps/examples.war*/WEB-INF/lib/foo.jar" {
-// };
\ No newline at end of file
+// };

View File

@ -1,6 +1,6 @@
--- tomcat/conf/tomcat-users.xml~ 2008-01-28 17:41:06.000000000 -0500
+++ tomcat/conf/tomcat-users.xml 2008-03-07 19:40:07.000000000 -0500
@@ -23,4 +23,14 @@
@@ -53,4 +53,14 @@
<user username="both" password="<must-be-changed>" roles="tomcat,role1"/>
<user username="role1" password="<must-be-changed>" roles="role1"/>
-->

View File

@ -43,9 +43,8 @@ JAVA_OPTS="-Djavax.sql.DataSource.Factory=org.apache.commons.dbcp.BasicDataSourc
# Run tomcat under the Java Security Manager
SECURITY_MANAGER="false"
# Time to wait in seconds, before killing process
# TODO(stingray): does nothing, fix.
# SHUTDOWN_WAIT="30"
# SHUTDOWN_WAIT has been deprecated. To change the shutdown wait time, set
# TimeoutStopSec in tomcat.service.
# If you wish to further customize your tomcat environment,
# put your own definitions here

View File

@ -1,4 +1,7 @@
@@@TCLOG@@@/catalina.out {
# This is an example config only and is disabled by default
# If you wish to use it, you'll need to update /etc/tomcat/logging.properties
# to prevent catalina*.log from being rotated by Tomcat
@@@TCLOG@@@/catalina*.log {
copytruncate
weekly
rotate 52

View File

@ -15,8 +15,6 @@ EnvironmentFile=-/etc/sysconfig/tomcat
ExecStart=/usr/libexec/tomcat/server start
SuccessExitStatus=143
User=tomcat
Group=tomcat
[Install]
WantedBy=multi-user.target

View File

@ -1,12 +0,0 @@
Manifest-Version: 1.0
Export-Package: org.apache.tomcat;version="8.0.26"
Bundle-Vendor: %bundleProvider
Bundle-ClassPath: .
Bundle-Version: 8.0.26
Bundle-Name: %bundleName
Bundle-Localization: plugin
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.apache.tomcat
Bundle-RequiredExecutionEnvironment: J2SE-1.4,CDC-1.0/Foundation-1.0,J
2SE-1.3

View File

@ -1,12 +1,11 @@
--- tomcat/build.xml.orig 2018-08-07 10:32:04.994403913 -0400
+++ tomcat/build.xml 2018-08-07 10:32:30.874319588 -0400
@@ -2989,6 +2989,9 @@ Read the Building page on the Apache Tom
<path id="bndlib.classpath">
<fileset file="${bnd.jar}" />
<fileset file="${bndlib.jar}" />
+ <fileset file="${bndlibg.jar}" />
+ <fileset file="${bndannotation.jar}" />
+ <fileset file="${slf4j-api.jar}" />
</path>
--- tomcat/res/bnd/build-defaults.bnd.orig 2024-05-01 11:07:38.804582327 +0300
+++ tomcat/res/bnd/build-defaults.bnd 2024-05-01 11:17:08.857295279 +0300
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
<taskdef resource="aQute/bnd/ant/taskdef.properties" classpathref="bndlib.classpath" />
-Bundle-Version: ${version_cleanup;${version}}
+Bundle-Version: ${version}
Bundle-License: https://www.apache.org/licenses/LICENSE-2.0.txt
Specification-Title: Apache Tomcat

View File

@ -1,13 +0,0 @@
Manifest-Version: 1.0
Export-Package: org.apache.juli;version="8.0.26",org.apache.juli.loggi
ng;version="8.0.26"
Bundle-Vendor: %bundleProvider
Bundle-ClassPath: .
Bundle-Version: 8.0.26
Bundle-Name: %bundleName
Bundle-Localization: plugin
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.apache.juli
Bundle-RequiredExecutionEnvironment: J2SE-1.4,CDC-1.0/Foundation-1.0,J
2SE-1.3

View File

@ -13,14 +13,12 @@ After=syslog.target network.target
[Service]
Type=simple
EnvironmentFile=/etc/tomcat/tomcat.conf
Environment="NAME=%I"
EnvironmentFile=-/etc/sysconfig/tomcat@%I
Environment="NAME=%i"
EnvironmentFile=-/etc/sysconfig/tomcat@%i
ExecStart=/usr/libexec/tomcat/server start
ExecStop=/usr/libexec/tomcat/server stop
SuccessExitStatus=143
User=tomcat
Group=tomcat
[Install]
WantedBy=multi-user.target

View File

@ -1,129 +1,87 @@
%global jspspec 2.3
%global major_version 9
%global minor_version 0
%global micro_version 10
%global micro_version 96
%global packdname apache-tomcat-%{version}-src
%global servletspec 4.0
%global elspec 3.0
%global tcuid 91
%global tcuid 53
%{!?_mavendepmapfragdir: %global _mavendepmapfragdir /usr/share/maven-metadata}
%{?fc24: %global _mavendepmapfragdir /usr/share/maven-metadata}
# FHS 2.3 compliant tree structure - http://www.pathname.com/fhs/2.3/
%global basedir %{_var}/lib/%{name}
%global appdir %{basedir}/webapps
%global homedir %{_datadir}/%{name}
%global bindir %{homedir}/bin
%global confdir %{_sysconfdir}/%{name}
%global libdir %{_javadir}/%{name}
%global logdir %{_var}/log/%{name}
%global cachedir %{_var}/cache/%{name}
%global tempdir %{cachedir}/temp
%global workdir %{cachedir}/work
%global _systemddir /lib/systemd/system
Name: tomcat
Epoch: 1
Version: %{major_version}.%{minor_version}.%{micro_version}
Release: 32
Summary: Implementation of the Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies
License: ASL 2.0
URL: http://tomcat.apache.org/
Release: 4
Summary: Apache Servlet/JSP Engine, RI for Servlet %{servletspec}/JSP %{jspspec} API
Source0: https://github.com/apache/tomcat/archive/%{version}.tar.gz
License: Apache-2.0
URL: http://tomcat.apache.org/
Source0: https://archive.apache.org/dist/tomcat/tomcat-%{major_version}/v%{version}/src/%{packdname}.tar.gz
Source1: %{name}-%{major_version}.%{minor_version}.conf
Source3: %{name}-%{major_version}.%{minor_version}.sysconfig
Source4: %{name}-%{major_version}.%{minor_version}.wrapper
Source5: %{name}-%{major_version}.%{minor_version}.logrotate
Source6: %{name}-%{major_version}.%{minor_version}-digest.script
Source7: %{name}-%{major_version}.%{minor_version}-tool-wrapper.script
Source8: servlet-api-OSGi-MANIFEST.MF
Source9: jsp-api-OSGi-MANIFEST.MF
Source11: %{name}-%{major_version}.%{minor_version}.service
Source12: el-api-OSGi-MANIFEST.MF
Source13: jasper-el-OSGi-MANIFEST.MF
Source14: jasper-OSGi-MANIFEST.MF
Source15: tomcat-api-OSGi-MANIFEST.MF
Source16: tomcat-juli-OSGi-MANIFEST.MF
Source20: %{name}-%{major_version}.%{minor_version}-jsvc.service
Source21: tomcat-functions
Source30: tomcat-preamble
Source31: tomcat-server
Source32: tomcat-named.service
Source33: java-9-start-up-parameters.conf
Patch0: %{name}-%{major_version}.%{minor_version}-bootstrap-MANIFEST.MF.patch
Patch1: %{name}-%{major_version}.%{minor_version}-tomcat-users-webapp.patch
Patch2: %{name}-build.patch
Patch3: disableJavadocFailOnWarning.patch
Patch6001: CVE-2019-0199-1.patch
Patch6002: CVE-2019-0199-2.patch
Patch6003: CVE-2019-0199-3.patch
Patch6004: CVE-2019-0199-4.patch
Patch6005: CVE-2019-0199-5.patch
Patch6006: CVE-2019-0199-6.patch
Patch6007: CVE-2019-0199-7.patch
Patch6008: CVE-2019-0199-8.patch
Patch6009: CVE-2019-0199-9.patch
Patch6010: CVE-2019-0199-10.patch
Patch6011: CVE-2019-0199-11.patch
Patch6012: CVE-2018-11784.patch
Patch6013: CVE-2019-0221.patch
Patch6014: CVE-2019-10072-1.patch
Patch6015: CVE-2019-10072-2.patch
Patch6016: CVE-2019-17563.patch
Patch6017: CVE-2019-12418.patch
Patch6018: CVE-2020-1938-1.patch
Patch6019: CVE-2020-1938-2.patch
Patch6020: CVE-2020-1938-3.patch
Patch6021: CVE-2020-1938-4.patch
Patch6022: CVE-2020-1938-5.patch
Patch6023: CVE-2020-1935.patch
Patch6024: CVE-2020-9484.patch
Patch6025: CVE-2020-11996.patch
Patch6026: CVE-2020-13934.patch
Patch6027: CVE-2020-13935.patch
Patch6028: CVE-2020-13943-1.patch
Patch6029: CVE-2020-13943-2.patch
Patch6030: CVE-2020-13943-3.patch
Patch6031: CVE-2020-13943-4.patch
Patch6032: CVE-2020-17527.patch
Patch6033: CVE-2021-24122.patch
Patch6035: CVE-2021-25122-pre.patch
Patch6036: CVE-2021-25122.patch
Patch6037: CVE-2021-25329-pre1.patch
Patch6038: CVE-2021-25329-pre2.patch
Patch6039: CVE-2021-25329-pre3.patch
Patch6040: CVE-2021-25329.patch
Patch6041: CVE-2021-33037-1.patch
Patch6042: CVE-2021-33037-2.patch
Patch6043: CVE-2021-33037-3.patch
Patch6044: CVE-2021-30640-pre1.patch
Patch6045: CVE-2021-30640-pre2.patch
Patch6046: CVE-2021-30640-pre3.patch
Patch6047: CVE-2021-30640-pre4.patch
Patch6048: CVE-2021-30640-pre5.patch
Patch6049: CVE-2021-30640-1.patch
Patch6050: CVE-2021-30640-2.patch
Patch6051: CVE-2021-30640-3.patch
Patch6052: CVE-2021-30640-4.patch
Patch6053: CVE-2021-30640-5.patch
Patch6054: CVE-2021-30640-6.patch
Patch6055: CVE-2021-30640-7.patch
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
Patch3: %{name}-%{major_version}.%{minor_version}-catalina-policy.patch
Patch4: rhbz-1857043.patch
# remove bnd dependency which version is too low on rhel8
Patch6: remove-bnd-annotation.patch
Patch7: build-with-jdk-1.8.patch
Patch8: CVE-2024-52318.patch
Patch9: CVE-2024-50379-1.patch
Patch10: CVE-2024-50379-2.patch
Patch11: CVE-2024-54677-pre.patch
Patch12: CVE-2024-54677-1.patch
Patch13: CVE-2024-54677-2.patch
Patch14: CVE-2024-54677-3.patch
Patch15: CVE-2024-54677-4.patch
Patch16: CVE-2024-54677-5.patch
Patch17: CVE-2024-54677-6.patch
Patch18: CVE-2024-54677-7.patch
Patch19: CVE-2024-54677-8.patch
Patch20: CVE-2024-54677-9.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
BuildRequires: jpackage-utils >= 0:1.7.0 java-devel >= 1:1.8.0 junit javapackages-local
BuildRequires: geronimo-saaj aqute-bndlib aqute-bnd systemd-units wsdl4j geronimo-jaxrpc
BuildArch: noarch
Requires: procps jpackage-utils java-headless >= 1:1.8.0 apache-commons-daemon
Requires: tomcat-taglibs-standard >= 0:1.1 ecj libtcnative-1-0 >= 1.2.14
Requires: apache-commons-dbcp apache-commons-pool apache-commons-collections
BuildRequires: ant
BuildRequires: ecj
BuildRequires: findutils
BuildRequires: javapackages-local
BuildRequires: aqute-bnd
BuildRequires: aqute-bndlib
BuildRequires: systemd
Requires: (java-headless >= 1:1.8 or java-1.8.0-headless or java-11-headless or java-17-headless or java >= 1:1.8)
Requires: javapackages-tools
Requires: ecj
Requires(pre): shadow-utils
Requires(post): chkconfig
Requires(preun): chkconfig
Requires(postun): chkconfig
Requires(post): systemd-units
Requires(preun): systemd-units
Requires(postun): systemd-units
Requires(post): systemd
Requires(preun): systemd
Requires(postun): systemd
Provides: %{name}-log4j = %{epoch}:%{version}-%{release}
Provides: servlet = %{servletspec} servlet6 servlet3 el_api = %{elspec} jsp = %{jspspec}
@ -143,13 +101,16 @@ Obsoletes: %{name}-jsp-%{jspspec}-api < %{epoch}:%{version}-%{release}
Obsoletes: %{name}-webapps < %{epoch}:%{version}-%{release}
Obsoletes: %{name}-admin-webapps < %{epoch}:%{version}-%{release}
BuildArch: noarch
%description
The Apache Tomcat software is developed in an open and participatory environment
and released under the Apache License version 2. The Apache Tomcat project is
intended to be a collaboration of the best-of-breed developers from around the
world. We invite you to participate in this open development project
Tomcat is the servlet container that is used in the official Reference
Implementation for the Java Servlet and JavaServer Pages technologies.
The Java Servlet and JavaServer Pages specifications are developed by
Sun under the Java Community Process.
Tomcat is developed in an open and participatory environment and
released under the Apache Software License version 2.0. Tomcat is intended
to be a collaboration of the best-of-breed developers from around the world.
%package jsvc
Summary: Apache jsvc wrapper for Apache Tomcat as separate service
@ -175,124 +136,105 @@ Obsoletes: %{name}-javadoc < %{epoch}:%{version}-%{release}
Man pages and other related documents for %{name}.
%prep
%autosetup -p1 -n %{packdname}
# remove pre-built binaries and windows files
find . -type f \( -name "*.bat" -o -name "*.class" -o -name Thumbs.db -o -name "*.gz" -o \
-name "*.jar" -o -name "*.war" -o -name "*.zip" \) -delete
%autosetup -p1 -n %{name}-%{version}
# Remove webservices naming resources as it's generally unused
%{__rm} -rf java/org/apache/naming/factory/webservices
# Configure maven files
%mvn_package ":tomcat-el-api" tomcat-el-api
%mvn_alias "org.apache.tomcat:tomcat-el-api" "org.eclipse.jetty.orbit:javax.el"
%mvn_file org.apache.tomcat:tomcat-jsp-api tomcat/jsp-api
%mvn_package ":tomcat-jsp-api" tomcat-jsp-api
%mvn_alias "org.apache.tomcat:tomcat-jsp-api" "org.eclipse.jetty.orbit:javax.servlet.jsp"
%mvn_package ":tomcat-servlet-api" tomcat-servlet-api
ln -s $(build-classpath tomcat-taglibs-standard/taglibs-standard-impl) webapps/examples/WEB-INF/lib/jstl.jar
ln -s $(build-classpath tomcat-taglibs-standard/taglibs-standard-compat) webapps/examples/WEB-INF/lib/standard.jar
%build
export OPT_JAR_LIST="xalan-j2-serializer"
touch HACK
%{ant} -Dbase.path="." \
-Dbuild.compiler="modern" \
-Dcommons-collections.jar="$(build-classpath apache-commons-collections)" \
-Dcommons-daemon.jar="$(build-classpath apache-commons-daemon)" \
-Dcommons-daemon.native.src.tgz="HACK" \
-Djdt.jar="$(build-classpath ecj/ecj)" \
-Dtomcat-native.tar.gz="HACK" \
-Dtomcat-native.home="." \
-Dcommons-daemon.native.win.mgr.exe="HACK" \
-Dnsis.exe="HACK" \
-Djaxrpc-lib.jar="$(build-classpath jaxrpc)" \
-Dwsdl4j-lib.jar="$(build-classpath wsdl4j)" \
-Dsaaj-api.jar="$(build-classpath geronimo-saaj)" \
-Dbnd.jar="$(build-classpath aqute-bnd/biz.aQute.bnd)" \
-Dbndlib.jar="$(build-classpath aqute-bnd/biz.aQute.bndlib)" \
-Dbndlibg.jar="$(build-classpath aqute-bnd/aQute.libg)" \
-Dbndannotation.jar="$(build-classpath aqute-bnd/biz.aQute.bnd.annotation)" \
-Dslf4j-api.jar="$(build-classpath slf4j/slf4j-api)" \
-Dno.build.dbcp=true \
-Dversion="%{version}" \
-Dversion.build="%{micro_version}" \
-Djava.7.home=%{java_home} \
-Dexecute.validate=false \
deploy dist-prepare dist-source javadoc
rm output/build/bin/commons-daemon.jar output/build/lib/ecj.jar
pushd output/dist/src/webapps/docs/appdev/sample/src
mkdir -p ../web/WEB-INF/classes
%{javac} -cp ../../../../../../../../output/build/lib/servlet-api.jar -d ../web/WEB-INF/classes mypackage/Hello.java
pushd ../web
%{jar} cf ../../../../../../../../output/build/webapps/docs/appdev/sample/sample.war *
popd
popd
# we don't care about the tarballs and we're going to replace
# tomcat-dbcp.jar with apache-commons-{collections,dbcp,pool}-tomcat5.jar
# so just create a dummy file for later removal
touch HACK
mkdir -p META-INF
cp -p %{SOURCE8} META-INF/MANIFEST.MF
touch META-INF/MANIFEST.MF
zip output/build/lib/servlet-api.jar META-INF/MANIFEST.MF
cp -p %{SOURCE9} META-INF/MANIFEST.MF
touch META-INF/MANIFEST.MF
zip output/build/lib/jsp-api.jar META-INF/MANIFEST.MF
cp -p %{SOURCE12} META-INF/MANIFEST.MF
touch META-INF/MANIFEST.MF
zip output/build/lib/el-api.jar META-INF/MANIFEST.MF
cp -p %{SOURCE13} META-INF/MANIFEST.MF
touch META-INF/MANIFEST.MF
zip output/build/lib/jasper-el.jar META-INF/MANIFEST.MF
cp -p %{SOURCE14} META-INF/MANIFEST.MF
touch META-INF/MANIFEST.MF
zip output/build/lib/jasper.jar META-INF/MANIFEST.MF
cp -p %{SOURCE15} META-INF/MANIFEST.MF
touch META-INF/MANIFEST.MF
zip output/build/lib/tomcat-api.jar META-INF/MANIFEST.MF
cp -p %{SOURCE16} META-INF/MANIFEST.MF
touch META-INF/MANIFEST.MF
zip output/build/bin/tomcat-juli.jar META-INF/MANIFEST.MF
# who needs a build.properties file anyway
%{ant} -Dbase.path="." \
-Dbuild.compiler="modern" \
-Dcommons-daemon.jar="HACK" \
-Dcommons-daemon.native.src.tgz="HACK" \
-Djdt.jar="$(build-classpath ecj/ecj)" \
-Dtomcat-native.tar.gz="HACK" \
-Dtomcat-native.home="." \
-Dcommons-daemon.native.win.mgr.exe="HACK" \
-Dnsis.exe="HACK" \
-Djaxrpc-lib.jar="HACK" \
-Dwsdl4j-lib.jar="HACK" \
-Dbnd.jar="HACK" \
-Dversion="%{version}" \
-Dversion.build="%{micro_version}" \
deploy
# remove some jars that we'll replace with symlinks later
%{__rm} output/build/lib/ecj.jar
# Remove the example webapps per Apache Tomcat Security Considerations
# see https://tomcat.apache.org/tomcat-9.0-doc/security-howto.html
%{__rm} -rf output/build/webapps/examples
%install
install -d -m 0755 %{buildroot}%{_bindir}
install -d -m 0755 %{buildroot}%{_sbindir}
install -d -m 0755 %{buildroot}%{_javadocdir}/%{name}
install -d -m 0755 %{buildroot}%{_sysconfdir}/init.d
install -d -m 0755 %{buildroot}/lib/systemd/system
install -d -m 0755 %{buildroot}%{_systemddir}
install -d -m 0755 %{buildroot}%{_sysconfdir}/logrotate.d
install -d -m 0755 %{buildroot}%{_sysconfdir}/sysconfig
install -d -m 0755 %{buildroot}%{_var}/lib/%{name}/webapps
install -d -m 0755 %{buildroot}%{_datadir}/%{name}/bin
install -d -m 0775 %{buildroot}%{_sysconfdir}/%{name}
install -d -m 0775 %{buildroot}%{_sysconfdir}/%{name}/Catalina/localhost
install -d -m 0775 %{buildroot}%{_sysconfdir}/%{name}/conf.d
install -d -m 0755 %{buildroot}%{appdir}
install -d -m 0755 %{buildroot}%{bindir}
install -d -m 0775 %{buildroot}%{confdir}
install -d -m 0775 %{buildroot}%{confdir}/Catalina/localhost
install -d -m 0775 %{buildroot}%{confdir}/conf.d
/bin/echo "Place your custom *.conf files here. Shell expansion is supported." > %{buildroot}%{_sysconfdir}/%{name}/conf.d/README
install -d -m 0755 %{buildroot}%{_javadir}/%{name}
install -d -m 0775 %{buildroot}%{_var}/log/%{name}
/bin/touch %{buildroot}%{_var}/log/%{name}/catalina.out
install -d -m 0755 %{buildroot}%{libdir}
install -d -m 0775 %{buildroot}%{logdir}
/bin/touch %{buildroot}%{logdir}/catalina.out
install -d -m 0775 %{buildroot}%{_localstatedir}/lib/tomcats
install -d -m 0775 %{buildroot}%{_datadir}/%{name}
install -d -m 0775 %{buildroot}%{_var}/cache/%{name}/temp
install -d -m 0775 %{buildroot}%{_var}/cache/%{name}/work
install -d -m 0775 %{buildroot}%{homedir}
install -d -m 0775 %{buildroot}%{tempdir}
install -d -m 0775 %{buildroot}%{workdir}
install -d -m 0755 %{buildroot}%{_unitdir}
install -d -m 0755 %{buildroot}%{_libexecdir}/%{name}
pushd output/build
cp -a bin/*.{jar,xml} %{buildroot}%{_datadir}/%{name}/bin
cp -a conf/*.{policy,properties,xml,xsd} %{buildroot}%{_sysconfdir}/%{name}
cp -a lib/*.jar %{buildroot}%{_javadir}/%{name}
cp -a webapps/* %{buildroot}%{_var}/lib/%{name}/webapps
cp -a bin/*.{jar,xml} %{buildroot}%{bindir}
cp -a conf/*.{policy,properties,xml,xsd} %{buildroot}%{confdir}
cp -a lib/*.jar %{buildroot}%{libdir}
cp -a webapps/* %{buildroot}%{appdir}
popd
cp -a output/dist/webapps/docs/api/* %{buildroot}%{_javadocdir}/%{name}
sed -e "s|\@\@\@TCHOME\@\@\@|%{_datadir}/%{name}|g" \
-e "s|\@\@\@TCTEMP\@\@\@|%{_var}/cache/%{name}/temp|g" \
sed -e "s|\@\@\@TCHOME\@\@\@|%{homedir}|g" \
-e "s|\@\@\@TCTEMP\@\@\@|%{tempdir}|g" \
-e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE1} \
> %{buildroot}%{_sysconfdir}/%{name}/%{name}.conf
sed -e "s|\@\@\@TCHOME\@\@\@|%{_datadir}/%{name}|g" \
-e "s|\@\@\@TCTEMP\@\@\@|%{_var}/cache/%{name}/temp|g" \
> %{buildroot}%{confdir}/%{name}.conf
sed -e "s|\@\@\@TCHOME\@\@\@|%{homedir}|g" \
-e "s|\@\@\@TCTEMP\@\@\@|%{tempdir}|g" \
-e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE3} \
> %{buildroot}%{_sysconfdir}/sysconfig/%{name}
install -m 0644 %{SOURCE4} %{buildroot}%{_sbindir}/%{name}
install -m 0644 %{SOURCE11} %{buildroot}%{_unitdir}/%{name}.service
install -m 0644 %{SOURCE20} %{buildroot}%{_unitdir}/%{name}-jsvc.service
sed -e "s|\@\@\@TCLOG\@\@\@|%{_var}/log/%{name}|g" %{SOURCE5} > %{buildroot}%{_sysconfdir}/logrotate.d/%{name}
sed -e "s|\@\@\@TCHOME\@\@\@|%{_datadir}/%{name}|g" \
-e "s|\@\@\@TCTEMP\@\@\@|%{_var}/cache/%{name}/temp|g" \
install -m 0644 %{SOURCE4} \
%{buildroot}%{_sbindir}/%{name}
install -m 0644 %{SOURCE11} \
%{buildroot}%{_unitdir}/%{name}.service
install -m 0644 %{SOURCE20} \
%{buildroot}%{_unitdir}/%{name}-jsvc.service
sed -e "s|\@\@\@TCLOG\@\@\@|%{logdir}|g" %{SOURCE5} \
> %{buildroot}%{_sysconfdir}/logrotate.d/%{name}
sed -e "s|\@\@\@TCHOME\@\@\@|%{homedir}|g" \
-e "s|\@\@\@TCTEMP\@\@\@|%{tempdir}|g" \
-e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE6} \
> %{buildroot}%{_bindir}/%{name}-digest
sed -e "s|\@\@\@TCHOME\@\@\@|%{_datadir}/%{name}|g" \
-e "s|\@\@\@TCTEMP\@\@\@|%{_var}/cache/%{name}/temp|g" \
sed -e "s|\@\@\@TCHOME\@\@\@|%{homedir}|g" \
-e "s|\@\@\@TCTEMP\@\@\@|%{tempdir}|g" \
-e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE7} \
> %{buildroot}%{_bindir}/%{name}-tool-wrapper
@ -300,6 +242,7 @@ install -m 0644 %{SOURCE21} %{buildroot}%{_libexecdir}/%{name}/functions
install -m 0755 %{SOURCE30} %{buildroot}%{_libexecdir}/%{name}/preamble
install -m 0755 %{SOURCE31} %{buildroot}%{_libexecdir}/%{name}/server
install -m 0644 %{SOURCE32} %{buildroot}%{_unitdir}/%{name}@.service
install -m 0644 %{SOURCE33} %{buildroot}%{confdir}/conf.d/
sed -i \
"s,el-api.jar,%{name}-el-%{elspec}-api.jar,;
@ -317,52 +260,29 @@ pushd %{buildroot}%{_javadir}
popd
pushd output/build
%{_bindir}/build-jar-repository lib apache-commons-collections apache-commons-dbcp apache-commons-pool ecj 2>&1
%{_bindir}/build-jar-repository -p webapps/examples/WEB-INF/lib \
tomcat-taglibs-standard/taglibs-standard-impl.jar tomcat-taglibs-standard/taglibs-standard-compat.jar 2>&1
%{_bindir}/build-jar-repository lib ecj 2>&1
popd
pushd %{buildroot}%{_javadir}/%{name}
pushd %{buildroot}%{libdir}
ln -s ../../java/%{name}-jsp-%{jspspec}-api.jar .
ln -s ../../java/%{name}-servlet-%{servletspec}-api.jar .
ln -s ../../java/%{name}-el-%{elspec}-api.jar .
ln -s $(build-classpath apache-commons-collections) commons-collections.jar
ln -s $(build-classpath apache-commons-dbcp) commons-dbcp.jar
ln -s $(build-classpath apache-commons-pool) commons-pool.jar
ln -s $(build-classpath ecj/ecj) jasper-jdt.jar
cp -a %{buildroot}%{_datadir}/%{name}/bin/tomcat-juli.jar ./
popd
pushd %{buildroot}%{_datadir}/%{name}
ln -s %{_var}/lib/%{name}/webapps webapps
ln -s %{_sysconfdir}/%{name} conf
ln -s %{_javadir}/%{name} lib
ln -s %{_var}/log/%{name} logs
ln -s %{_var}/cache/%{name}/temp temp
ln -s %{_var}/cache/%{name}/work work
popd
mkdir -p %{buildroot}%{_var}/lib/%{name}/webapps/sample
pushd %{buildroot}%{_var}/lib/%{name}/webapps/sample
%{jar} xf %{buildroot}%{_var}/lib/%{name}/webapps/docs/appdev/sample/sample.war
popd
rm %{buildroot}%{_var}/lib/%{name}/webapps/docs/appdev/sample/sample.war
mkdir -p %{buildroot}%{_var}/lib/%{name}/webapps/examples/META-INF
pushd %{buildroot}%{_var}/lib/%{name}/webapps/examples/META-INF
echo '<?xml version="1.0" encoding="UTF-8"?>' > context.xml
echo '<Context>' >> context.xml
echo ' <Resources allowLinking="true" />' >> context.xml
echo '</Context>' >> context.xml
popd
pushd %{buildroot}%{_var}/lib/%{name}/webapps/examples/WEB-INF/lib
ln -s -f $(build-classpath tomcat-taglibs-standard/taglibs-standard-impl) jstl.jar
ln -s -f $(build-classpath tomcat-taglibs-standard/taglibs-standard-compat) standard.jar
# symlink to the FHS locations where we've installed things
pushd %{buildroot}%{homedir}
ln -s %{appdir} webapps
ln -s %{confdir} conf
ln -s %{libdir} lib
ln -s %{logdir} logs
ln -s %{tempdir} temp
ln -s %{workdir} work
popd
install -d -m 0755 %{buildroot}%{_mavenpomdir}
pushd output/dist/src/res/maven
pushd res/maven
for pom in *.pom; do
sed -i 's/@MAVEN.DEPLOY.VERSION@/%{version}/g' $pom
done
@ -403,17 +323,26 @@ cp -a tomcat-jaspic-api.pom %{buildroot}%{_mavenpomdir}/JPP.%{name}-jaspic-api.p
%add_maven_depmap JPP.%{name}-jaspic-api.pom %{name}/jaspic-api.jar
%pre
%{_sbindir}/groupadd -g %{tcuid} -r tomcat 2>/dev/null || :
%{_sbindir}/useradd -c "Apache Tomcat" -u %{tcuid} -g tomcat -s /sbin/nologin -r -d %{_datadir}/%{name} tomcat 2>/dev/null || :
# add the tomcat user and group
getent group tomcat >/dev/null || %{_sbindir}/groupadd -f -g %{tcuid} -r tomcat
if ! getent passwd tomcat >/dev/null ; then
if ! getent passwd %{tcuid} >/dev/null ; then
%{_sbindir}/useradd -r -u %{tcuid} -g tomcat -d %{homedir} -s /sbin/nologin -c "Apache Tomcat" tomcat
# Tomcat uses a reserved ID, so there should never be an else
fi
fi
exit 0
%post
# install but don't activate
%systemd_post %{name}.service
%{_sbindir}/update-alternatives --install %{_javadir}/servlet.jar servlet %{_javadir}/%{name}-servlet-%{servletspec}-api.jar 30000
%{_sbindir}/update-alternatives --install %{_javadir}/elspec.jar elspec %{_javadir}/%{name}-el-%{elspec}-api.jar 20300
%{_sbindir}/update-alternatives --install %{_javadir}/jsp.jar jsp %{_javadir}/%{name}-jsp-%{jspspec}-api.jar 20200
%preun
rm -rf %{_var}/cache/%{name}/work/* %{_var}/cache/%{name}/temp/*
# clean tempdir and workdir on removal or upgrade
%{__rm} -rf %{workdir}/* %{tempdir}/*
%systemd_preun %{name}.service
%postun
@ -424,23 +353,8 @@ if [ "$1" = "0" ]; then
%{_sbindir}/update-alternatives --remove jsp %{_javadir}/%{name}-jsp-%{jspspec}-api.jar
fi
%triggerun -- tomcat < 0:7.0.22-2
/usr/bin/systemd-sysv-convert -- save tomcat > /dev/null 2>&1 || :
/sbin/chkconfig --del tomcat > /dev/null 2>&1 || :
/bin/systemctl try-restart tomcat.service > /dev/null 2>&1 || :
%files
%doc LICENSE
%{_javadir}/%{name}-servlet-%{servletspec}*.jar
%dir %{_javadir}/%{name}
%{_javadir}/%{name}/*.jar
%{_javadir}/*.jar
%{_datadir}/%{name}/bin/tomcat-juli.jar
%{_mavenpomdir}/JPP*%{name}-*.pom
%{_datadir}/maven-metadata/*.xml
%{_javadir}/%{name}-el-%{elspec}-api.jar
%{_javadir}/%{name}/%{name}-el-%{elspec}-api.jar
%{_javadir}/%{name}-jsp-%{jspspec}*.jar
%defattr(0664,root,tomcat,0755)
%doc {LICENSE,NOTICE,RELEASE*}
%attr(0755,root,root) %{_bindir}/%{name}-digest
@ -454,46 +368,57 @@ fi
%attr(0755,root,root) %{_libexecdir}/%{name}/preamble
%attr(0755,root,root) %{_libexecdir}/%{name}/server
%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/sysconfig/%{name}
%attr(0755,root,tomcat) %dir %{_var}/lib/%{name}
%attr(0755,root,tomcat) %dir %{_sysconfdir}/%{name}
%attr(0755,root,tomcat) %dir %{basedir}
%attr(0755,root,tomcat) %dir %{confdir}
%defattr(0664,tomcat,root,0770)
%attr(0770,tomcat,root) %dir %{_var}/log/%{name}
%attr(0770,tomcat,root) %dir %{logdir}
%defattr(0664,root,tomcat,0770)
%attr(0770,root,tomcat) %dir %{_var}/cache/%{name}
%attr(0770,root,tomcat) %dir %{_var}/cache/%{name}/temp
%attr(0770,root,tomcat) %dir %{_var}/cache/%{name}/work
%attr(0770,root,tomcat) %dir %{cachedir}
%attr(0770,root,tomcat) %dir %{tempdir}
%attr(0770,root,tomcat) %dir %{workdir}
%defattr(0644,root,tomcat,0775)
%attr(0775,root,tomcat) %dir %{_var}/lib/%{name}/webapps
%attr(0775,root,tomcat) %dir %{_sysconfdir}/%{name}/Catalina
%attr(0775,root,tomcat) %dir %{_sysconfdir}/%{name}/Catalina/localhost
%attr(0755,root,tomcat) %dir %{_sysconfdir}/%{name}/conf.d
%{_sysconfdir}/%{name}/conf.d/README
%config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf
%config(noreplace) %{_sysconfdir}/%{name}/*.policy
%config(noreplace) %{_sysconfdir}/%{name}/*.properties
%config(noreplace) %{_sysconfdir}/%{name}/context.xml
%config(noreplace) %{_sysconfdir}/%{name}/server.xml
%attr(0640,root,tomcat) %config(noreplace) %{_sysconfdir}/%{name}/tomcat-users.xml
%attr(0664,root,tomcat) %{_sysconfdir}/%{name}/tomcat-users.xsd
%attr(0664,root,tomcat) %config(noreplace) %{_sysconfdir}/%{name}/jaspic-providers.xml
%attr(0664,root,tomcat) %{_sysconfdir}/%{name}/jaspic-providers.xsd
%config(noreplace) %{_sysconfdir}/%{name}/web.xml
%dir %{_datadir}/%{name}
%{_datadir}/%{name}/bin/bootstrap.jar
%{_datadir}/%{name}/bin/catalina-tasks.xml
%{_datadir}/%{name}/lib
%{_datadir}/%{name}/temp
%{_datadir}/%{name}/webapps
%{_datadir}/%{name}/work
%{_datadir}/%{name}/logs
%{_datadir}/%{name}/conf
%attr(0775,root,tomcat) %dir %{appdir}
%attr(0775,root,tomcat) %dir %{confdir}/Catalina
%attr(0775,root,tomcat) %dir %{confdir}/Catalina/localhost
%attr(0755,root,tomcat) %dir %{confdir}/conf.d
%{confdir}/conf.d/README
%{confdir}/conf.d/java-9-start-up-parameters.conf
%config(noreplace) %{confdir}/%{name}.conf
%config(noreplace) %{confdir}/*.policy
%config(noreplace) %{confdir}/*.properties
%config(noreplace) %{confdir}/context.xml
%config(noreplace) %{confdir}/server.xml
%attr(0640,root,tomcat) %config(noreplace) %{confdir}/tomcat-users.xml
%attr(0664,root,tomcat) %{confdir}/tomcat-users.xsd
%attr(0664,root,tomcat) %config(noreplace) %{confdir}/jaspic-providers.xml
%attr(0664,root,tomcat) %{confdir}/jaspic-providers.xsd
%config(noreplace) %{confdir}/web.xml
%dir %{homedir}
%{bindir}/catalina-tasks.xml
%{homedir}/lib
%{homedir}/temp
%{homedir}/webapps
%{homedir}/work
%{homedir}/logs
%{homedir}/conf
%defattr(0664,root,tomcat,0755)
%{_var}/lib/%{name}/webapps/host-manager
%{_var}/lib/%{name}/webapps/manager
%{appdir}/host-manager
%{appdir}/manager
%defattr(0644,tomcat,tomcat,0755)
%{_var}/lib/%{name}/webapps/ROOT
%{_var}/lib/%{name}/webapps/examples
%{_var}/lib/%{name}/webapps/sample
%{appdir}/ROOT
%dir %{libdir}
%{libdir}/*.jar
%{_javadir}/*.jar
%{bindir}/tomcat-juli.jar
%{bindir}/bootstrap.jar
%{_mavenpomdir}/JPP*%{name}-*.pom
%{_datadir}/maven-metadata/*.xml
%files jsvc
%defattr(755,root,root,0755)
@ -502,10 +427,26 @@ fi
%attr(0660,tomcat,tomcat) %verify(not size md5 mtime) %{_var}/log/%{name}/catalina.out
%files help
%{_var}/lib/%{name}/webapps/docs
%{_javadocdir}/%{name}
%{appdir}/docs
%changelog
* Wed Dec 18 2024 wangkai <13474090681@163.com> - 1:9.0.96-4
- Fix CVE-2024-50379 CVE-2024-54677
* Tue Nov 26 2024 wangkai <13474090681@163.com> - 1:9.0.96-3
- Add requires ecj for fix server error
* Tue Nov 19 2024 wangkai <13474090681@163.com> - 1:9.0.96-2
- Fix CVE-2024-52318
* Thu Nov 07 2024 chenyaqiang <chengyaqiang@huawei.com> - 1:9.0.96-1
- Update to 9.0.96
- Fix CVE-2021-43980 CVE-2022-25762 CVE-2023-44487 CVE-2023-46589
CVE-2024-23672 CVE-2024-24549 CVE-2024-34750
* 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