Update to 9.0.96

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

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

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

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

View File

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

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-01-28 17:41:06.000000000 -0500
+++ tomcat/conf/tomcat-users.xml 2008-03-07 19:40:07.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="both" password="<must-be-changed>" roles="tomcat,role1"/>
<user username="role1" password="<must-be-changed>" roles="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 # Run tomcat under the Java Security Manager
SECURITY_MANAGER="false" SECURITY_MANAGER="false"
# Time to wait in seconds, before killing process # SHUTDOWN_WAIT has been deprecated. To change the shutdown wait time, set
# TODO(stingray): does nothing, fix. # TimeoutStopSec in tomcat.service.
# SHUTDOWN_WAIT="30"
# If you wish to further customize your tomcat environment, # If you wish to further customize your tomcat environment,
# put your own definitions here # 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 copytruncate
weekly weekly
rotate 52 rotate 52

View File

@ -15,8 +15,6 @@ EnvironmentFile=-/etc/sysconfig/tomcat
ExecStart=/usr/libexec/tomcat/server start ExecStart=/usr/libexec/tomcat/server start
SuccessExitStatus=143 SuccessExitStatus=143
User=tomcat User=tomcat
Group=tomcat
[Install] [Install]
WantedBy=multi-user.target 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/res/bnd/build-defaults.bnd.orig 2024-05-01 11:07:38.804582327 +0300
+++ tomcat/build.xml 2018-08-07 10:32:30.874319588 -0400 +++ tomcat/res/bnd/build-defaults.bnd 2024-05-01 11:17:08.857295279 +0300
@@ -2989,6 +2989,9 @@ Read the Building page on the Apache Tom @@ -13,7 +13,7 @@
<path id="bndlib.classpath"> # See the License for the specific language governing permissions and
<fileset file="${bnd.jar}" /> # limitations under the License.
<fileset file="${bndlib.jar}" />
+ <fileset file="${bndlibg.jar}" />
+ <fileset file="${bndannotation.jar}" />
+ <fileset file="${slf4j-api.jar}" />
</path>
<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] [Service]
Type=simple Type=simple
EnvironmentFile=/etc/tomcat/tomcat.conf EnvironmentFile=/etc/tomcat/tomcat.conf
Environment="NAME=%I" Environment="NAME=%i"
EnvironmentFile=-/etc/sysconfig/tomcat@%I EnvironmentFile=-/etc/sysconfig/tomcat@%i
ExecStart=/usr/libexec/tomcat/server start ExecStart=/usr/libexec/tomcat/server start
ExecStop=/usr/libexec/tomcat/server stop ExecStop=/usr/libexec/tomcat/server stop
SuccessExitStatus=143 SuccessExitStatus=143
User=tomcat User=tomcat
Group=tomcat
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@ -1,133 +1,74 @@
%global jspspec 2.3 %global jspspec 2.3
%global major_version 9 %global major_version 9
%global minor_version 0 %global minor_version 0
%global micro_version 10 %global micro_version 96
%global packdname apache-tomcat-%{version}-src %global packdname apache-tomcat-%{version}-src
%global servletspec 4.0 %global servletspec 4.0
%global elspec 3.0 %global elspec 3.0
%global tcuid 91 %global tcuid 53
%{!?_mavendepmapfragdir: %global _mavendepmapfragdir /usr/share/maven-metadata} # FHS 2.3 compliant tree structure - http://www.pathname.com/fhs/2.3/
%{?fc24: %global _mavendepmapfragdir /usr/share/maven-metadata} %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 Name: tomcat
Epoch: 1 Epoch: 1
Version: %{major_version}.%{minor_version}.%{micro_version} Version: %{major_version}.%{minor_version}.%{micro_version}
Release: 33 Release: 1
Summary: Implementation of the Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies Summary: Apache Servlet/JSP Engine, RI for Servlet %{servletspec}/JSP %{jspspec} API
License: ASL 2.0
URL: http://tomcat.apache.org/
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 Source1: %{name}-%{major_version}.%{minor_version}.conf
Source3: %{name}-%{major_version}.%{minor_version}.sysconfig Source3: %{name}-%{major_version}.%{minor_version}.sysconfig
Source4: %{name}-%{major_version}.%{minor_version}.wrapper Source4: %{name}-%{major_version}.%{minor_version}.wrapper
Source5: %{name}-%{major_version}.%{minor_version}.logrotate Source5: %{name}-%{major_version}.%{minor_version}.logrotate
Source6: %{name}-%{major_version}.%{minor_version}-digest.script Source6: %{name}-%{major_version}.%{minor_version}-digest.script
Source7: %{name}-%{major_version}.%{minor_version}-tool-wrapper.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 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 Source20: %{name}-%{major_version}.%{minor_version}-jsvc.service
Source21: tomcat-functions Source21: tomcat-functions
Source30: tomcat-preamble Source30: tomcat-preamble
Source31: tomcat-server Source31: tomcat-server
Source32: tomcat-named.service Source32: tomcat-named.service
Source33: java-9-start-up-parameters.conf
Patch0: %{name}-%{major_version}.%{minor_version}-bootstrap-MANIFEST.MF.patch Patch0: %{name}-%{major_version}.%{minor_version}-bootstrap-MANIFEST.MF.patch
Patch1: %{name}-%{major_version}.%{minor_version}-tomcat-users-webapp.patch Patch1: %{name}-%{major_version}.%{minor_version}-tomcat-users-webapp.patch
Patch2: %{name}-build.patch Patch2: %{name}-build.patch
Patch3: disableJavadocFailOnWarning.patch Patch3: %{name}-%{major_version}.%{minor_version}-catalina-policy.patch
Patch6001: CVE-2019-0199-1.patch Patch4: rhbz-1857043.patch
Patch6002: CVE-2019-0199-2.patch # remove bnd dependency which version is too low on rhel8
Patch6003: CVE-2019-0199-3.patch Patch6: remove-bnd-annotation.patch
Patch6004: CVE-2019-0199-4.patch Patch7: build-with-jdk-1.8.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
Patch6075: CVE-2024-21733.patch
Patch6076: CVE-2023-24998.patch
Patch6077: CVE-2023-28709.patch
Patch6078: CVE-2023-42795.patch
BuildRequires: ecj >= 1:4.6.1 findutils apache-commons-collections apache-commons-daemon BuildArch: noarch
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
Requires: procps jpackage-utils java-headless >= 1:1.8.0 apache-commons-daemon BuildRequires: ant
Requires: tomcat-taglibs-standard >= 0:1.1 ecj libtcnative-1-0 >= 1.2.14 BuildRequires: ecj
Requires: apache-commons-dbcp apache-commons-pool apache-commons-collections 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: %{name}-lib = %{epoch}:%{version}-%{release}
Requires(pre): shadow-utils Requires(pre): shadow-utils
Requires(post): chkconfig Requires(post): systemd
Requires(preun): chkconfig Requires(preun): systemd
Requires(postun): chkconfig Requires(postun): systemd
Requires(post): systemd-units
Requires(preun): systemd-units
Requires(postun): systemd-units
Provides: %{name}-log4j = %{epoch}:%{version}-%{release} Provides: %{name}-log4j = %{epoch}:%{version}-%{release}
Provides: servlet = %{servletspec} servlet6 servlet3 el_api = %{elspec} jsp = %{jspspec} Provides: servlet = %{servletspec} servlet6 servlet3 el_api = %{elspec} jsp = %{jspspec}
@ -147,13 +88,16 @@ Obsoletes: %{name}-jsp-%{jspspec}-api < %{epoch}:%{version}-%{release}
Obsoletes: %{name}-webapps < %{epoch}:%{version}-%{release} Obsoletes: %{name}-webapps < %{epoch}:%{version}-%{release}
Obsoletes: %{name}-admin-webapps < %{epoch}:%{version}-%{release} Obsoletes: %{name}-admin-webapps < %{epoch}:%{version}-%{release}
BuildArch: noarch
%description %description
The Apache Tomcat software is developed in an open and participatory environment Tomcat is the servlet container that is used in the official Reference
and released under the Apache License version 2. The Apache Tomcat project is Implementation for the Java Servlet and JavaServer Pages technologies.
intended to be a collaboration of the best-of-breed developers from around the The Java Servlet and JavaServer Pages specifications are developed by
world. We invite you to participate in this open development project 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 %package jsvc
Summary: Apache jsvc wrapper for Apache Tomcat as separate service Summary: Apache jsvc wrapper for Apache Tomcat as separate service
@ -179,124 +123,105 @@ Obsoletes: %{name}-javadoc < %{epoch}:%{version}-%{release}
Man pages and other related documents for %{name}. Man pages and other related documents for %{name}.
%prep %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 \ 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 -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 %build
export OPT_JAR_LIST="xalan-j2-serializer" export OPT_JAR_LIST="xalan-j2-serializer"
touch HACK # we don't care about the tarballs and we're going to replace
%{ant} -Dbase.path="." \ # tomcat-dbcp.jar with apache-commons-{collections,dbcp,pool}-tomcat5.jar
-Dbuild.compiler="modern" \ # so just create a dummy file for later removal
-Dcommons-collections.jar="$(build-classpath apache-commons-collections)" \ touch HACK
-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
mkdir -p META-INF # who needs a build.properties file anyway
cp -p %{SOURCE8} META-INF/MANIFEST.MF %{ant} -Dbase.path="." \
touch META-INF/MANIFEST.MF -Dbuild.compiler="modern" \
zip output/build/lib/servlet-api.jar META-INF/MANIFEST.MF -Dcommons-daemon.jar="HACK" \
cp -p %{SOURCE9} META-INF/MANIFEST.MF -Dcommons-daemon.native.src.tgz="HACK" \
touch META-INF/MANIFEST.MF -Djdt.jar="$(build-classpath ecj/ecj)" \
zip output/build/lib/jsp-api.jar META-INF/MANIFEST.MF -Dtomcat-native.tar.gz="HACK" \
cp -p %{SOURCE12} META-INF/MANIFEST.MF -Dtomcat-native.home="." \
touch META-INF/MANIFEST.MF -Dcommons-daemon.native.win.mgr.exe="HACK" \
zip output/build/lib/el-api.jar META-INF/MANIFEST.MF -Dnsis.exe="HACK" \
cp -p %{SOURCE13} META-INF/MANIFEST.MF -Djaxrpc-lib.jar="HACK" \
touch META-INF/MANIFEST.MF -Dwsdl4j-lib.jar="HACK" \
zip output/build/lib/jasper-el.jar META-INF/MANIFEST.MF -Dbnd.jar="HACK" \
cp -p %{SOURCE14} META-INF/MANIFEST.MF -Dversion="%{version}" \
touch META-INF/MANIFEST.MF -Dversion.build="%{micro_version}" \
zip output/build/lib/jasper.jar META-INF/MANIFEST.MF deploy
cp -p %{SOURCE15} META-INF/MANIFEST.MF
touch META-INF/MANIFEST.MF # remove some jars that we'll replace with symlinks later
zip output/build/lib/tomcat-api.jar META-INF/MANIFEST.MF %{__rm} output/build/lib/ecj.jar
cp -p %{SOURCE16} META-INF/MANIFEST.MF # Remove the example webapps per Apache Tomcat Security Considerations
touch META-INF/MANIFEST.MF # see https://tomcat.apache.org/tomcat-9.0-doc/security-howto.html
zip output/build/bin/tomcat-juli.jar META-INF/MANIFEST.MF %{__rm} -rf output/build/webapps/examples
%install %install
install -d -m 0755 %{buildroot}%{_bindir} install -d -m 0755 %{buildroot}%{_bindir}
install -d -m 0755 %{buildroot}%{_sbindir} 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}%{_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}/logrotate.d
install -d -m 0755 %{buildroot}%{_sysconfdir}/sysconfig install -d -m 0755 %{buildroot}%{_sysconfdir}/sysconfig
install -d -m 0755 %{buildroot}%{_var}/lib/%{name}/webapps install -d -m 0755 %{buildroot}%{appdir}
install -d -m 0755 %{buildroot}%{_datadir}/%{name}/bin install -d -m 0755 %{buildroot}%{bindir}
install -d -m 0775 %{buildroot}%{_sysconfdir}/%{name} install -d -m 0775 %{buildroot}%{confdir}
install -d -m 0775 %{buildroot}%{_sysconfdir}/%{name}/Catalina/localhost install -d -m 0775 %{buildroot}%{confdir}/Catalina/localhost
install -d -m 0775 %{buildroot}%{_sysconfdir}/%{name}/conf.d 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 /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 0755 %{buildroot}%{libdir}
install -d -m 0775 %{buildroot}%{_var}/log/%{name} install -d -m 0775 %{buildroot}%{logdir}
/bin/touch %{buildroot}%{_var}/log/%{name}/catalina.out /bin/touch %{buildroot}%{logdir}/catalina.out
install -d -m 0775 %{buildroot}%{_localstatedir}/lib/tomcats install -d -m 0775 %{buildroot}%{_localstatedir}/lib/tomcats
install -d -m 0775 %{buildroot}%{_datadir}/%{name} install -d -m 0775 %{buildroot}%{homedir}
install -d -m 0775 %{buildroot}%{_var}/cache/%{name}/temp install -d -m 0775 %{buildroot}%{tempdir}
install -d -m 0775 %{buildroot}%{_var}/cache/%{name}/work install -d -m 0775 %{buildroot}%{workdir}
install -d -m 0755 %{buildroot}%{_unitdir} install -d -m 0755 %{buildroot}%{_unitdir}
install -d -m 0755 %{buildroot}%{_libexecdir}/%{name} install -d -m 0755 %{buildroot}%{_libexecdir}/%{name}
pushd output/build pushd output/build
cp -a bin/*.{jar,xml} %{buildroot}%{_datadir}/%{name}/bin cp -a bin/*.{jar,xml} %{buildroot}%{bindir}
cp -a conf/*.{policy,properties,xml,xsd} %{buildroot}%{_sysconfdir}/%{name} cp -a conf/*.{policy,properties,xml,xsd} %{buildroot}%{confdir}
cp -a lib/*.jar %{buildroot}%{_javadir}/%{name} cp -a lib/*.jar %{buildroot}%{libdir}
cp -a webapps/* %{buildroot}%{_var}/lib/%{name}/webapps cp -a webapps/* %{buildroot}%{appdir}
popd popd
cp -a output/dist/webapps/docs/api/* %{buildroot}%{_javadocdir}/%{name} sed -e "s|\@\@\@TCHOME\@\@\@|%{homedir}|g" \
-e "s|\@\@\@TCTEMP\@\@\@|%{tempdir}|g" \
sed -e "s|\@\@\@TCHOME\@\@\@|%{_datadir}/%{name}|g" \
-e "s|\@\@\@TCTEMP\@\@\@|%{_var}/cache/%{name}/temp|g" \
-e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE1} \ -e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE1} \
> %{buildroot}%{_sysconfdir}/%{name}/%{name}.conf > %{buildroot}%{confdir}/%{name}.conf
sed -e "s|\@\@\@TCHOME\@\@\@|%{_datadir}/%{name}|g" \ sed -e "s|\@\@\@TCHOME\@\@\@|%{homedir}|g" \
-e "s|\@\@\@TCTEMP\@\@\@|%{_var}/cache/%{name}/temp|g" \ -e "s|\@\@\@TCTEMP\@\@\@|%{tempdir}|g" \
-e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE3} \ -e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE3} \
> %{buildroot}%{_sysconfdir}/sysconfig/%{name} > %{buildroot}%{_sysconfdir}/sysconfig/%{name}
install -m 0644 %{SOURCE4} %{buildroot}%{_sbindir}/%{name} install -m 0644 %{SOURCE4} \
install -m 0644 %{SOURCE11} %{buildroot}%{_unitdir}/%{name}.service %{buildroot}%{_sbindir}/%{name}
install -m 0644 %{SOURCE20} %{buildroot}%{_unitdir}/%{name}-jsvc.service install -m 0644 %{SOURCE11} \
sed -e "s|\@\@\@TCLOG\@\@\@|%{_var}/log/%{name}|g" %{SOURCE5} > %{buildroot}%{_sysconfdir}/logrotate.d/%{name} %{buildroot}%{_unitdir}/%{name}.service
sed -e "s|\@\@\@TCHOME\@\@\@|%{_datadir}/%{name}|g" \ install -m 0644 %{SOURCE20} \
-e "s|\@\@\@TCTEMP\@\@\@|%{_var}/cache/%{name}/temp|g" \ %{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} \ -e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE6} \
> %{buildroot}%{_bindir}/%{name}-digest > %{buildroot}%{_bindir}/%{name}-digest
sed -e "s|\@\@\@TCHOME\@\@\@|%{_datadir}/%{name}|g" \ sed -e "s|\@\@\@TCHOME\@\@\@|%{homedir}|g" \
-e "s|\@\@\@TCTEMP\@\@\@|%{_var}/cache/%{name}/temp|g" \ -e "s|\@\@\@TCTEMP\@\@\@|%{tempdir}|g" \
-e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE7} \ -e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE7} \
> %{buildroot}%{_bindir}/%{name}-tool-wrapper > %{buildroot}%{_bindir}/%{name}-tool-wrapper
@ -304,6 +229,7 @@ install -m 0644 %{SOURCE21} %{buildroot}%{_libexecdir}/%{name}/functions
install -m 0755 %{SOURCE30} %{buildroot}%{_libexecdir}/%{name}/preamble install -m 0755 %{SOURCE30} %{buildroot}%{_libexecdir}/%{name}/preamble
install -m 0755 %{SOURCE31} %{buildroot}%{_libexecdir}/%{name}/server install -m 0755 %{SOURCE31} %{buildroot}%{_libexecdir}/%{name}/server
install -m 0644 %{SOURCE32} %{buildroot}%{_unitdir}/%{name}@.service install -m 0644 %{SOURCE32} %{buildroot}%{_unitdir}/%{name}@.service
install -m 0644 %{SOURCE33} %{buildroot}%{confdir}/conf.d/
sed -i \ sed -i \
"s,el-api.jar,%{name}-el-%{elspec}-api.jar,; "s,el-api.jar,%{name}-el-%{elspec}-api.jar,;
@ -321,52 +247,29 @@ pushd %{buildroot}%{_javadir}
popd popd
pushd output/build pushd output/build
%{_bindir}/build-jar-repository lib apache-commons-collections apache-commons-dbcp apache-commons-pool ecj 2>&1 %{_bindir}/build-jar-repository lib 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
popd popd
pushd %{buildroot}%{_javadir}/%{name} pushd %{buildroot}%{libdir}
ln -s ../../java/%{name}-jsp-%{jspspec}-api.jar . ln -s ../../java/%{name}-jsp-%{jspspec}-api.jar .
ln -s ../../java/%{name}-servlet-%{servletspec}-api.jar . ln -s ../../java/%{name}-servlet-%{servletspec}-api.jar .
ln -s ../../java/%{name}-el-%{elspec}-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 ln -s $(build-classpath ecj/ecj) jasper-jdt.jar
cp -a %{buildroot}%{_datadir}/%{name}/bin/tomcat-juli.jar ./ cp -a %{buildroot}%{_datadir}/%{name}/bin/tomcat-juli.jar ./
popd popd
pushd %{buildroot}%{_datadir}/%{name} # symlink to the FHS locations where we've installed things
ln -s %{_var}/lib/%{name}/webapps webapps pushd %{buildroot}%{homedir}
ln -s %{_sysconfdir}/%{name} conf ln -s %{appdir} webapps
ln -s %{_javadir}/%{name} lib ln -s %{confdir} conf
ln -s %{_var}/log/%{name} logs ln -s %{libdir} lib
ln -s %{_var}/cache/%{name}/temp temp ln -s %{logdir} logs
ln -s %{_var}/cache/%{name}/work work ln -s %{tempdir} temp
popd ln -s %{workdir} work
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
popd popd
install -d -m 0755 %{buildroot}%{_mavenpomdir} install -d -m 0755 %{buildroot}%{_mavenpomdir}
pushd output/dist/src/res/maven pushd res/maven
for pom in *.pom; do for pom in *.pom; do
sed -i 's/@MAVEN.DEPLOY.VERSION@/%{version}/g' $pom sed -i 's/@MAVEN.DEPLOY.VERSION@/%{version}/g' $pom
done done
@ -407,17 +310,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 %add_maven_depmap JPP.%{name}-jaspic-api.pom %{name}/jaspic-api.jar
%pre %pre
%{_sbindir}/groupadd -g %{tcuid} -r tomcat 2>/dev/null || : # add the tomcat user and group
%{_sbindir}/useradd -c "Apache Tomcat" -u %{tcuid} -g tomcat -s /sbin/nologin -r -d %{_datadir}/%{name} tomcat 2>/dev/null || : 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 %post
# install but don't activate
%systemd_post %{name}.service %systemd_post %{name}.service
%{_sbindir}/update-alternatives --install %{_javadir}/servlet.jar servlet %{_javadir}/%{name}-servlet-%{servletspec}-api.jar 30000 %{_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}/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 %{_sbindir}/update-alternatives --install %{_javadir}/jsp.jar jsp %{_javadir}/%{name}-jsp-%{jspspec}-api.jar 20200
%preun %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 %systemd_preun %{name}.service
%postun %postun
@ -428,23 +340,8 @@ if [ "$1" = "0" ]; then
%{_sbindir}/update-alternatives --remove jsp %{_javadir}/%{name}-jsp-%{jspspec}-api.jar %{_sbindir}/update-alternatives --remove jsp %{_javadir}/%{name}-jsp-%{jspspec}-api.jar
fi 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 %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) %defattr(0664,root,tomcat,0755)
%doc {LICENSE,NOTICE,RELEASE*} %doc {LICENSE,NOTICE,RELEASE*}
%attr(0755,root,root) %{_bindir}/%{name}-digest %attr(0755,root,root) %{_bindir}/%{name}-digest
@ -458,46 +355,57 @@ fi
%attr(0755,root,root) %{_libexecdir}/%{name}/preamble %attr(0755,root,root) %{_libexecdir}/%{name}/preamble
%attr(0755,root,root) %{_libexecdir}/%{name}/server %attr(0755,root,root) %{_libexecdir}/%{name}/server
%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/sysconfig/%{name} %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/sysconfig/%{name}
%attr(0755,root,tomcat) %dir %{_var}/lib/%{name} %attr(0755,root,tomcat) %dir %{basedir}
%attr(0755,root,tomcat) %dir %{_sysconfdir}/%{name} %attr(0755,root,tomcat) %dir %{confdir}
%defattr(0664,tomcat,root,0770) %defattr(0664,tomcat,root,0770)
%attr(0770,tomcat,root) %dir %{_var}/log/%{name} %attr(0770,tomcat,root) %dir %{logdir}
%defattr(0664,root,tomcat,0770) %defattr(0664,root,tomcat,0770)
%attr(0770,root,tomcat) %dir %{_var}/cache/%{name} %attr(0770,root,tomcat) %dir %{cachedir}
%attr(0770,root,tomcat) %dir %{_var}/cache/%{name}/temp %attr(0770,root,tomcat) %dir %{tempdir}
%attr(0770,root,tomcat) %dir %{_var}/cache/%{name}/work %attr(0770,root,tomcat) %dir %{workdir}
%defattr(0644,root,tomcat,0775) %defattr(0644,root,tomcat,0775)
%attr(0775,root,tomcat) %dir %{_var}/lib/%{name}/webapps %attr(0775,root,tomcat) %dir %{appdir}
%attr(0775,root,tomcat) %dir %{_sysconfdir}/%{name}/Catalina %attr(0775,root,tomcat) %dir %{confdir}/Catalina
%attr(0775,root,tomcat) %dir %{_sysconfdir}/%{name}/Catalina/localhost %attr(0775,root,tomcat) %dir %{confdir}/Catalina/localhost
%attr(0755,root,tomcat) %dir %{_sysconfdir}/%{name}/conf.d %attr(0755,root,tomcat) %dir %{confdir}/conf.d
%{_sysconfdir}/%{name}/conf.d/README %{confdir}/conf.d/README
%config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf %{confdir}/conf.d/java-9-start-up-parameters.conf
%config(noreplace) %{_sysconfdir}/%{name}/*.policy %config(noreplace) %{confdir}/%{name}.conf
%config(noreplace) %{_sysconfdir}/%{name}/*.properties %config(noreplace) %{confdir}/*.policy
%config(noreplace) %{_sysconfdir}/%{name}/context.xml %config(noreplace) %{confdir}/*.properties
%config(noreplace) %{_sysconfdir}/%{name}/server.xml %config(noreplace) %{confdir}/context.xml
%attr(0640,root,tomcat) %config(noreplace) %{_sysconfdir}/%{name}/tomcat-users.xml %config(noreplace) %{confdir}/server.xml
%attr(0664,root,tomcat) %{_sysconfdir}/%{name}/tomcat-users.xsd %attr(0640,root,tomcat) %config(noreplace) %{confdir}/tomcat-users.xml
%attr(0664,root,tomcat) %config(noreplace) %{_sysconfdir}/%{name}/jaspic-providers.xml %attr(0664,root,tomcat) %{confdir}/tomcat-users.xsd
%attr(0664,root,tomcat) %{_sysconfdir}/%{name}/jaspic-providers.xsd %attr(0664,root,tomcat) %config(noreplace) %{confdir}/jaspic-providers.xml
%config(noreplace) %{_sysconfdir}/%{name}/web.xml %attr(0664,root,tomcat) %{confdir}/jaspic-providers.xsd
%dir %{_datadir}/%{name} %config(noreplace) %{confdir}/web.xml
%{_datadir}/%{name}/bin/bootstrap.jar
%{_datadir}/%{name}/bin/catalina-tasks.xml %dir %{homedir}
%{_datadir}/%{name}/lib %{bindir}/catalina-tasks.xml
%{_datadir}/%{name}/temp %{homedir}/lib
%{_datadir}/%{name}/webapps %{homedir}/temp
%{_datadir}/%{name}/work %{homedir}/webapps
%{_datadir}/%{name}/logs %{homedir}/work
%{_datadir}/%{name}/conf %{homedir}/logs
%{homedir}/conf
%defattr(0664,root,tomcat,0755) %defattr(0664,root,tomcat,0755)
%{_var}/lib/%{name}/webapps/host-manager %{appdir}/host-manager
%{_var}/lib/%{name}/webapps/manager %{appdir}/manager
%defattr(0644,tomcat,tomcat,0755) %defattr(0644,tomcat,tomcat,0755)
%{_var}/lib/%{name}/webapps/ROOT %{appdir}/ROOT
%{_var}/lib/%{name}/webapps/examples
%{_var}/lib/%{name}/webapps/sample %dir %{libdir}
%{libdir}/*.jar
%{_javadir}/*.jar
%{bindir}/tomcat-juli.jar
%{bindir}/bootstrap.jar
%{_mavenpomdir}/JPP*%{name}-*.pom
%{_datadir}/maven-metadata/*.xml
%files jsvc %files jsvc
%defattr(755,root,root,0755) %defattr(755,root,root,0755)
@ -506,10 +414,14 @@ fi
%attr(0660,tomcat,tomcat) %verify(not size md5 mtime) %{_var}/log/%{name}/catalina.out %attr(0660,tomcat,tomcat) %verify(not size md5 mtime) %{_var}/log/%{name}/catalina.out
%files help %files help
%{_var}/lib/%{name}/webapps/docs %{appdir}/docs
%{_javadocdir}/%{name}
%changelog %changelog
* 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 * 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 - Fix CVE-2024-21733,CVE-2023-24998,CVE-2023-28709,CVE-2023-42795