|
|
6f268b |
diff -up java/org/apache/coyote/http11/AbstractHttp11Processor.java.orig java/org/apache/coyote/http11/AbstractHttp11Processor.java
|
|
|
6f268b |
--- java/org/apache/coyote/http11/AbstractHttp11Processor.java.orig 2017-03-09 08:51:40.000000000 -0500
|
|
|
6f268b |
+++ java/org/apache/coyote/http11/AbstractHttp11Processor.java 2019-03-05 14:58:20.285295932 -0500
|
|
|
6f268b |
@@ -48,6 +48,7 @@ import org.apache.tomcat.util.buf.HexUti
|
|
|
6f268b |
import org.apache.tomcat.util.buf.MessageBytes;
|
|
|
6f268b |
import org.apache.tomcat.util.http.FastHttpDateFormat;
|
|
|
6f268b |
import org.apache.tomcat.util.http.MimeHeaders;
|
|
|
6f268b |
+import org.apache.tomcat.util.http.parser.HttpParser;
|
|
|
6f268b |
import org.apache.tomcat.util.log.UserDataHelper;
|
|
|
6f268b |
import org.apache.tomcat.util.net.AbstractEndpoint;
|
|
|
6f268b |
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
|
|
|
6f268b |
@@ -262,6 +263,9 @@ public abstract class AbstractHttp11Proc
|
|
|
6f268b |
protected org.apache.coyote.http11.upgrade.UpgradeInbound upgradeInbound = null;
|
|
|
6f268b |
|
|
|
6f268b |
|
|
|
6f268b |
+ protected HttpParser httpParser;
|
|
|
6f268b |
+
|
|
|
6f268b |
+
|
|
|
6f268b |
/**
|
|
|
6f268b |
* Instance of the new protocol to use after the HTTP connection has been
|
|
|
6f268b |
* upgraded using the Servlet 3.1 based upgrade process.
|
|
|
6f268b |
@@ -1301,33 +1305,62 @@ public abstract class AbstractHttp11Proc
|
|
|
6f268b |
}
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
- // Check for a full URI (including protocol://host:port/)
|
|
|
6f268b |
+ // Check for an absolute-URI less the query string which has already
|
|
|
6f268b |
+ // been removed during the parsing of the request line
|
|
|
6f268b |
ByteChunk uriBC = request.requestURI().getByteChunk();
|
|
|
6f268b |
+ byte[] uriB = uriBC.getBytes();
|
|
|
6f268b |
if (uriBC.startsWithIgnoreCase("http", 0)) {
|
|
|
6f268b |
|
|
|
6f268b |
- int pos = uriBC.indexOf("://", 0, 3, 4);
|
|
|
6f268b |
- int uriBCStart = uriBC.getStart();
|
|
|
6f268b |
- int slashPos = -1;
|
|
|
6f268b |
- if (pos != -1) {
|
|
|
6f268b |
- byte[] uriB = uriBC.getBytes();
|
|
|
6f268b |
- slashPos = uriBC.indexOf('/', pos + 3);
|
|
|
6f268b |
+ int pos = 4;
|
|
|
6f268b |
+ // Check for https
|
|
|
6f268b |
+ if (uriBC.startsWithIgnoreCase("s", pos)) {
|
|
|
6f268b |
+ pos++;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ // Next 3 characters must be "://"
|
|
|
6f268b |
+ if (uriBC.startsWith("://", pos)) {
|
|
|
6f268b |
+ int uriBCStart = uriBC.getStart();
|
|
|
6f268b |
+
|
|
|
6f268b |
+ // '/' does not appear in the authority so use the first
|
|
|
6f268b |
+ // instance to split the authority and the path segments
|
|
|
6f268b |
+ int slashPos = uriBC.indexOf('/', pos);
|
|
|
6f268b |
+ // '@' in the authority delimits the userinfo
|
|
|
6f268b |
+
|
|
|
6f268b |
if (slashPos == -1) {
|
|
|
6f268b |
slashPos = uriBC.getLength();
|
|
|
6f268b |
- // Set URI as "/"
|
|
|
6f268b |
- request.requestURI().setBytes
|
|
|
6f268b |
- (uriB, uriBCStart + pos + 1, 1);
|
|
|
6f268b |
+ // Set URI as "/". Use 6 as it will always be a '/'.
|
|
|
6f268b |
+ // 01234567
|
|
|
6f268b |
+ // http://
|
|
|
6f268b |
+ // https://
|
|
|
6f268b |
+ request.requestURI().setBytes(uriB, uriBCStart + 6, 1);
|
|
|
6f268b |
} else {
|
|
|
6f268b |
- request.requestURI().setBytes
|
|
|
6f268b |
- (uriB, uriBCStart + slashPos,
|
|
|
6f268b |
- uriBC.getLength() - slashPos);
|
|
|
6f268b |
+ request.requestURI().setBytes(uriB, uriBCStart + slashPos, uriBC.getLength() - slashPos);
|
|
|
6f268b |
}
|
|
|
6f268b |
MessageBytes hostMB = headers.setValue("host");
|
|
|
6f268b |
hostMB.setBytes(uriB, uriBCStart + pos + 3,
|
|
|
6f268b |
slashPos - pos - 3);
|
|
|
6f268b |
+ } else {
|
|
|
6f268b |
+ response.setStatus(400);
|
|
|
6f268b |
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
|
|
|
6f268b |
+ if (getLog().isDebugEnabled()) {
|
|
|
6f268b |
+ getLog().debug(sm.getString("http11processor.request.invalidScheme"));
|
|
|
6f268b |
+ }
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
+ // Validate the characters in the URI. %nn decoding will be checked at
|
|
|
6f268b |
+ // the point of decoding.
|
|
|
6f268b |
+ for (int i = uriBC.getStart(); i < uriBC.getEnd(); i++) {
|
|
|
6f268b |
+ if (!httpParser.isAbsolutePathRelaxed(uriB[i])) {
|
|
|
6f268b |
+ response.setStatus(400);
|
|
|
6f268b |
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
|
|
|
6f268b |
+ if (getLog().isDebugEnabled()) {
|
|
|
6f268b |
+ getLog().debug(sm.getString("http11processor.request.invalidUri"));
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ break;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
// Input filter setup
|
|
|
6f268b |
InputFilter[] inputFilters = getInputBuffer().getFilters();
|
|
|
6f268b |
|
|
|
6f268b |
@@ -1364,8 +1397,7 @@ public abstract class AbstractHttp11Proc
|
|
|
6f268b |
headers.removeHeader("content-length");
|
|
|
6f268b |
request.setContentLength(-1);
|
|
|
6f268b |
} else {
|
|
|
6f268b |
- getInputBuffer().addActiveFilter
|
|
|
6f268b |
- (inputFilters[Constants.IDENTITY_FILTER]);
|
|
|
6f268b |
+ getInputBuffer().addActiveFilter(inputFilters[Constants.IDENTITY_FILTER]);
|
|
|
6f268b |
contentDelimitation = true;
|
|
|
6f268b |
}
|
|
|
6f268b |
}
|
|
|
6f268b |
@@ -1383,14 +1415,14 @@ public abstract class AbstractHttp11Proc
|
|
|
6f268b |
}
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
+ // Validate host name and extract port if present
|
|
|
6f268b |
parseHost(valueMB);
|
|
|
6f268b |
|
|
|
6f268b |
if (!contentDelimitation) {
|
|
|
6f268b |
// If there's no content length
|
|
|
6f268b |
// (broken HTTP/1.0 or HTTP/1.1), assume
|
|
|
6f268b |
// the client is not broken and didn't send a body
|
|
|
6f268b |
- getInputBuffer().addActiveFilter
|
|
|
6f268b |
- (inputFilters[Constants.VOID_FILTER]);
|
|
|
6f268b |
+ getInputBuffer().addActiveFilter(inputFilters[Constants.VOID_FILTER]);
|
|
|
6f268b |
contentDelimitation = true;
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
diff -up java/org/apache/coyote/http11/AbstractHttp11Protocol.java.orig java/org/apache/coyote/http11/AbstractHttp11Protocol.java
|
|
|
6f268b |
--- java/org/apache/coyote/http11/AbstractHttp11Protocol.java.orig 2019-03-05 12:14:08.096279991 -0500
|
|
|
6f268b |
+++ java/org/apache/coyote/http11/AbstractHttp11Protocol.java 2019-03-05 14:03:32.186921274 -0500
|
|
|
6f268b |
@@ -45,6 +45,23 @@ public abstract class AbstractHttp11Prot
|
|
|
6f268b |
// ------------------------------------------------ HTTP specific properties
|
|
|
6f268b |
// ------------------------------------------ managed in the ProtocolHandler
|
|
|
6f268b |
|
|
|
6f268b |
+ private String relaxedPathChars = null;
|
|
|
6f268b |
+ public String getRelaxedPathChars() {
|
|
|
6f268b |
+ return relaxedPathChars;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ public void setRelaxedPathChars(String relaxedPathChars) {
|
|
|
6f268b |
+ this.relaxedPathChars = relaxedPathChars;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+
|
|
|
6f268b |
+ private String relaxedQueryChars = null;
|
|
|
6f268b |
+ public String getRelaxedQueryChars() {
|
|
|
6f268b |
+ return relaxedQueryChars;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ public void setRelaxedQueryChars(String relaxedQueryChars) {
|
|
|
6f268b |
+ this.relaxedQueryChars = relaxedQueryChars;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
private int socketBuffer = 9000;
|
|
|
6f268b |
public int getSocketBuffer() { return socketBuffer; }
|
|
|
6f268b |
public void setSocketBuffer(int socketBuffer) {
|
|
|
6f268b |
diff -up java/org/apache/coyote/http11/AbstractInputBuffer.java.orig java/org/apache/coyote/http11/AbstractInputBuffer.java
|
|
|
6f268b |
--- java/org/apache/coyote/http11/AbstractInputBuffer.java.orig 2017-03-09 08:51:40.000000000 -0500
|
|
|
6f268b |
+++ java/org/apache/coyote/http11/AbstractInputBuffer.java 2019-03-05 12:14:08.096279991 -0500
|
|
|
6f268b |
@@ -22,6 +22,7 @@ import org.apache.coyote.InputBuffer;
|
|
|
6f268b |
import org.apache.coyote.Request;
|
|
|
6f268b |
import org.apache.tomcat.util.buf.ByteChunk;
|
|
|
6f268b |
import org.apache.tomcat.util.http.MimeHeaders;
|
|
|
6f268b |
+import org.apache.tomcat.util.http.parser.HttpParser;
|
|
|
6f268b |
import org.apache.tomcat.util.net.AbstractEndpoint;
|
|
|
6f268b |
import org.apache.tomcat.util.net.SocketWrapper;
|
|
|
6f268b |
import org.apache.tomcat.util.res.StringManager;
|
|
|
6f268b |
@@ -108,6 +109,9 @@ public abstract class AbstractInputBuffe
|
|
|
6f268b |
protected int lastActiveFilter;
|
|
|
6f268b |
|
|
|
6f268b |
|
|
|
6f268b |
+ protected HttpParser httpParser;
|
|
|
6f268b |
+
|
|
|
6f268b |
+
|
|
|
6f268b |
// ------------------------------------------------------------- Properties
|
|
|
6f268b |
|
|
|
6f268b |
|
|
|
6f268b |
diff -up java/org/apache/coyote/http11/Http11AprProcessor.java.orig java/org/apache/coyote/http11/Http11AprProcessor.java
|
|
|
6f268b |
--- java/org/apache/coyote/http11/Http11AprProcessor.java.orig 2019-03-05 12:13:47.032344988 -0500
|
|
|
6f268b |
+++ java/org/apache/coyote/http11/Http11AprProcessor.java 2019-03-05 14:58:20.298295897 -0500
|
|
|
6f268b |
@@ -35,6 +35,7 @@ import org.apache.tomcat.jni.SSLSocket;
|
|
|
6f268b |
import org.apache.tomcat.jni.Sockaddr;
|
|
|
6f268b |
import org.apache.tomcat.jni.Socket;
|
|
|
6f268b |
import org.apache.tomcat.util.ExceptionUtils;
|
|
|
6f268b |
+import org.apache.tomcat.util.http.parser.HttpParser;
|
|
|
6f268b |
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
|
|
|
6f268b |
import org.apache.tomcat.util.net.AprEndpoint;
|
|
|
6f268b |
import org.apache.tomcat.util.net.SSLSupport;
|
|
|
6f268b |
@@ -61,11 +62,15 @@ public class Http11AprProcessor extends
|
|
|
6f268b |
|
|
|
6f268b |
|
|
|
6f268b |
public Http11AprProcessor(int headerBufferSize, AprEndpoint endpoint, int maxTrailerSize,
|
|
|
6f268b |
- Set<String> allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) {
|
|
|
6f268b |
+ Set<String> allowedTrailerHeaders,
|
|
|
6f268b |
+ int maxExtensionSize, int maxSwallowSize, String relaxedPathChars,
|
|
|
6f268b |
+ String relaxedQueryChars) {
|
|
|
6f268b |
|
|
|
6f268b |
super(endpoint);
|
|
|
6f268b |
|
|
|
6f268b |
- inputBuffer = new InternalAprInputBuffer(request, headerBufferSize);
|
|
|
6f268b |
+ httpParser = new HttpParser(relaxedPathChars, relaxedQueryChars);
|
|
|
6f268b |
+
|
|
|
6f268b |
+ inputBuffer = new InternalAprInputBuffer(request, headerBufferSize, httpParser);
|
|
|
6f268b |
request.setInputBuffer(inputBuffer);
|
|
|
6f268b |
|
|
|
6f268b |
outputBuffer = new InternalAprOutputBuffer(response, headerBufferSize);
|
|
|
6f268b |
diff -up java/org/apache/coyote/http11/Http11AprProtocol.java.orig java/org/apache/coyote/http11/Http11AprProtocol.java
|
|
|
6f268b |
--- java/org/apache/coyote/http11/Http11AprProtocol.java.orig 2019-03-05 12:14:08.097279988 -0500
|
|
|
6f268b |
+++ java/org/apache/coyote/http11/Http11AprProtocol.java 2019-03-05 13:59:45.131631454 -0500
|
|
|
6f268b |
@@ -301,7 +301,9 @@ public class Http11AprProtocol extends A
|
|
|
6f268b |
Http11AprProcessor processor = new Http11AprProcessor(
|
|
|
6f268b |
proto.getMaxHttpHeaderSize(), (AprEndpoint)proto.endpoint,
|
|
|
6f268b |
proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(),
|
|
|
6f268b |
- proto.getMaxExtensionSize(), proto.getMaxSwallowSize());
|
|
|
6f268b |
+ proto.getMaxExtensionSize(),
|
|
|
6f268b |
+ proto.getMaxSwallowSize(), proto.getRelaxedPathChars(),
|
|
|
6f268b |
+ proto.getRelaxedQueryChars());
|
|
|
6f268b |
processor.setAdapter(proto.adapter);
|
|
|
6f268b |
processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
|
|
|
6f268b |
processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
|
|
|
6f268b |
diff -up java/org/apache/coyote/http11/Http11NioProcessor.java.orig java/org/apache/coyote/http11/Http11NioProcessor.java
|
|
|
6f268b |
--- java/org/apache/coyote/http11/Http11NioProcessor.java.orig 2019-03-05 12:13:47.033344985 -0500
|
|
|
6f268b |
+++ java/org/apache/coyote/http11/Http11NioProcessor.java 2019-03-05 13:04:00.335042387 -0500
|
|
|
6f268b |
@@ -31,6 +31,7 @@ import org.apache.coyote.http11.filters.
|
|
|
6f268b |
import org.apache.juli.logging.Log;
|
|
|
6f268b |
import org.apache.juli.logging.LogFactory;
|
|
|
6f268b |
import org.apache.tomcat.util.ExceptionUtils;
|
|
|
6f268b |
+import org.apache.tomcat.util.http.parser.HttpParser;
|
|
|
6f268b |
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
|
|
|
6f268b |
import org.apache.tomcat.util.net.NioChannel;
|
|
|
6f268b |
import org.apache.tomcat.util.net.NioEndpoint;
|
|
|
6f268b |
@@ -66,11 +67,15 @@ public class Http11NioProcessor extends
|
|
|
6f268b |
|
|
|
6f268b |
|
|
|
6f268b |
public Http11NioProcessor(int maxHttpHeaderSize, NioEndpoint endpoint, int maxTrailerSize,
|
|
|
6f268b |
- Set<String> allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) {
|
|
|
6f268b |
+ Set<String> allowedTrailerHeaders,
|
|
|
6f268b |
+ int maxExtensionSize, int maxSwallowSize, String relaxedPathChars,
|
|
|
6f268b |
+ String relaxedQueryChars) {
|
|
|
6f268b |
|
|
|
6f268b |
super(endpoint);
|
|
|
6f268b |
|
|
|
6f268b |
- inputBuffer = new InternalNioInputBuffer(request, maxHttpHeaderSize);
|
|
|
6f268b |
+ httpParser = new HttpParser(relaxedPathChars, relaxedQueryChars);
|
|
|
6f268b |
+
|
|
|
6f268b |
+ inputBuffer = new InternalNioInputBuffer(request, maxHttpHeaderSize, httpParser);
|
|
|
6f268b |
request.setInputBuffer(inputBuffer);
|
|
|
6f268b |
|
|
|
6f268b |
outputBuffer = new InternalNioOutputBuffer(response, maxHttpHeaderSize);
|
|
|
6f268b |
diff -up java/org/apache/coyote/http11/Http11NioProtocol.java.orig java/org/apache/coyote/http11/Http11NioProtocol.java
|
|
|
6f268b |
--- java/org/apache/coyote/http11/Http11NioProtocol.java.orig 2019-03-05 12:14:08.098279985 -0500
|
|
|
6f268b |
+++ java/org/apache/coyote/http11/Http11NioProtocol.java 2019-03-05 14:00:15.034537932 -0500
|
|
|
6f268b |
@@ -266,7 +266,9 @@ public class Http11NioProtocol extends A
|
|
|
6f268b |
Http11NioProcessor processor = new Http11NioProcessor(
|
|
|
6f268b |
proto.getMaxHttpHeaderSize(), (NioEndpoint)proto.endpoint,
|
|
|
6f268b |
proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(),
|
|
|
6f268b |
- proto.getMaxExtensionSize(), proto.getMaxSwallowSize());
|
|
|
6f268b |
+ proto.getMaxExtensionSize(),
|
|
|
6f268b |
+ proto.getMaxSwallowSize(), proto.getRelaxedPathChars(),
|
|
|
6f268b |
+ proto.getRelaxedQueryChars());
|
|
|
6f268b |
processor.setAdapter(proto.adapter);
|
|
|
6f268b |
processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
|
|
|
6f268b |
processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
|
|
|
6f268b |
diff -up java/org/apache/coyote/http11/Http11Processor.java.orig java/org/apache/coyote/http11/Http11Processor.java
|
|
|
6f268b |
--- java/org/apache/coyote/http11/Http11Processor.java.orig 2017-03-09 08:51:40.000000000 -0500
|
|
|
6f268b |
+++ java/org/apache/coyote/http11/Http11Processor.java 2019-03-05 14:58:20.306295875 -0500
|
|
|
6f268b |
@@ -26,6 +26,7 @@ import org.apache.coyote.ActionCode;
|
|
|
6f268b |
import org.apache.coyote.http11.filters.BufferedInputFilter;
|
|
|
6f268b |
import org.apache.juli.logging.Log;
|
|
|
6f268b |
import org.apache.juli.logging.LogFactory;
|
|
|
6f268b |
+import org.apache.tomcat.util.http.parser.HttpParser;
|
|
|
6f268b |
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
|
|
|
6f268b |
import org.apache.tomcat.util.net.JIoEndpoint;
|
|
|
6f268b |
import org.apache.tomcat.util.net.SSLSupport;
|
|
|
6f268b |
@@ -51,11 +52,16 @@ public class Http11Processor extends Abs
|
|
|
6f268b |
|
|
|
6f268b |
|
|
|
6f268b |
public Http11Processor(int headerBufferSize, JIoEndpoint endpoint, int maxTrailerSize,
|
|
|
6f268b |
- Set<String> allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) {
|
|
|
6f268b |
+ Set<String> allowedTrailerHeaders,
|
|
|
6f268b |
+ int maxExtensionSize, int maxSwallowSize, String relaxedPathChars,
|
|
|
6f268b |
+ String relaxedQueryChars) {
|
|
|
6f268b |
|
|
|
6f268b |
super(endpoint);
|
|
|
6f268b |
+
|
|
|
6f268b |
+ httpParser = new HttpParser(relaxedPathChars, relaxedQueryChars);
|
|
|
6f268b |
+
|
|
|
6f268b |
+ inputBuffer = new InternalInputBuffer(request, headerBufferSize, httpParser);
|
|
|
6f268b |
|
|
|
6f268b |
- inputBuffer = new InternalInputBuffer(request, headerBufferSize);
|
|
|
6f268b |
request.setInputBuffer(inputBuffer);
|
|
|
6f268b |
|
|
|
6f268b |
outputBuffer = new InternalOutputBuffer(response, headerBufferSize);
|
|
|
6f268b |
diff -up java/org/apache/coyote/http11/Http11Protocol.java.orig java/org/apache/coyote/http11/Http11Protocol.java
|
|
|
6f268b |
--- java/org/apache/coyote/http11/Http11Protocol.java.orig 2019-03-05 12:14:08.099279982 -0500
|
|
|
6f268b |
+++ java/org/apache/coyote/http11/Http11Protocol.java 2019-03-05 13:02:36.769301263 -0500
|
|
|
6f268b |
@@ -165,7 +165,9 @@ public class Http11Protocol extends Abst
|
|
|
6f268b |
Http11Processor processor = new Http11Processor(
|
|
|
6f268b |
proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint,
|
|
|
6f268b |
proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(),
|
|
|
6f268b |
- proto.getMaxExtensionSize(), proto.getMaxSwallowSize());
|
|
|
6f268b |
+ proto.getMaxExtensionSize(),
|
|
|
6f268b |
+ proto.getMaxSwallowSize(), proto.getRelaxedPathChars(),
|
|
|
6f268b |
+ proto.getRelaxedQueryChars());
|
|
|
6f268b |
processor.setAdapter(proto.adapter);
|
|
|
6f268b |
processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
|
|
|
6f268b |
processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
|
|
|
6f268b |
diff -up java/org/apache/coyote/http11/InternalAprInputBuffer.java.orig java/org/apache/coyote/http11/InternalAprInputBuffer.java
|
|
|
6f268b |
--- java/org/apache/coyote/http11/InternalAprInputBuffer.java.orig 2017-03-09 08:51:40.000000000 -0500
|
|
|
6f268b |
+++ java/org/apache/coyote/http11/InternalAprInputBuffer.java 2019-03-05 14:58:20.312295859 -0500
|
|
|
6f268b |
@@ -51,7 +51,8 @@ public class InternalAprInputBuffer exte
|
|
|
6f268b |
/**
|
|
|
6f268b |
* Alternate constructor.
|
|
|
6f268b |
*/
|
|
|
6f268b |
- public InternalAprInputBuffer(Request request, int headerBufferSize) {
|
|
|
6f268b |
+ public InternalAprInputBuffer(Request request, int headerBufferSize,
|
|
|
6f268b |
+ HttpParser httpParser) {
|
|
|
6f268b |
|
|
|
6f268b |
this.request = request;
|
|
|
6f268b |
headers = request.getMimeHeaders();
|
|
|
6f268b |
@@ -63,6 +64,8 @@ public class InternalAprInputBuffer exte
|
|
|
6f268b |
bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
+ this.httpParser = httpParser;
|
|
|
6f268b |
+
|
|
|
6f268b |
inputStreamInputBuffer = new SocketInputBuffer();
|
|
|
6f268b |
|
|
|
6f268b |
filterLibrary = new InputFilter[0];
|
|
|
6f268b |
@@ -231,7 +234,13 @@ public class InternalAprInputBuffer exte
|
|
|
6f268b |
end = pos;
|
|
|
6f268b |
} else if ((buf[pos] == Constants.QUESTION) && (questionPos == -1)) {
|
|
|
6f268b |
questionPos = pos;
|
|
|
6f268b |
- } else if (HttpParser.isNotRequestTarget(buf[pos])) {
|
|
|
6f268b |
+ } else if (questionPos != -1 && !httpParser.isQueryRelaxed(buf[pos])) {
|
|
|
6f268b |
+ // %nn decoding will be checked at the point of decoding
|
|
|
6f268b |
+ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
|
|
|
6f268b |
+ } else if (httpParser.isNotRequestTargetRelaxed(buf[pos])) {
|
|
|
6f268b |
+ // This is a general check that aims to catch problems early
|
|
|
6f268b |
+ // Detailed checking of each part of the request target will
|
|
|
6f268b |
+ // happen in AbstractHttp11Processor#prepareRequest()
|
|
|
6f268b |
throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
diff -up java/org/apache/coyote/http11/InternalInputBuffer.java.orig java/org/apache/coyote/http11/InternalInputBuffer.java
|
|
|
6f268b |
--- java/org/apache/coyote/http11/InternalInputBuffer.java.orig 2017-03-09 08:51:40.000000000 -0500
|
|
|
6f268b |
+++ java/org/apache/coyote/http11/InternalInputBuffer.java 2019-03-05 14:58:21.215293426 -0500
|
|
|
6f268b |
@@ -52,13 +52,16 @@ public class InternalInputBuffer extends
|
|
|
6f268b |
/**
|
|
|
6f268b |
* Default constructor.
|
|
|
6f268b |
*/
|
|
|
6f268b |
- public InternalInputBuffer(Request request, int headerBufferSize) {
|
|
|
6f268b |
+ public InternalInputBuffer(Request request, int headerBufferSize,
|
|
|
6f268b |
+ HttpParser httpParser) {
|
|
|
6f268b |
|
|
|
6f268b |
this.request = request;
|
|
|
6f268b |
headers = request.getMimeHeaders();
|
|
|
6f268b |
|
|
|
6f268b |
buf = new byte[headerBufferSize];
|
|
|
6f268b |
|
|
|
6f268b |
+ this.httpParser = httpParser;
|
|
|
6f268b |
+
|
|
|
6f268b |
inputStreamInputBuffer = new InputStreamInputBuffer();
|
|
|
6f268b |
|
|
|
6f268b |
filterLibrary = new InputFilter[0];
|
|
|
6f268b |
@@ -185,7 +188,13 @@ public class InternalInputBuffer extends
|
|
|
6f268b |
end = pos;
|
|
|
6f268b |
} else if ((buf[pos] == Constants.QUESTION) && (questionPos == -1)) {
|
|
|
6f268b |
questionPos = pos;
|
|
|
6f268b |
- } else if (HttpParser.isNotRequestTarget(buf[pos])) {
|
|
|
6f268b |
+ } else if (questionPos != -1 && !httpParser.isQueryRelaxed(buf[pos])) {
|
|
|
6f268b |
+ // %nn decoding will be checked at the point of decoding
|
|
|
6f268b |
+ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
|
|
|
6f268b |
+ } else if (httpParser.isNotRequestTargetRelaxed(buf[pos])) {
|
|
|
6f268b |
+ // This is a general check that aims to catch problems early
|
|
|
6f268b |
+ // Detailed checking of each part of the request target will
|
|
|
6f268b |
+ // happen in AbstractHttp11Processor#prepareRequest()
|
|
|
6f268b |
throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
diff -up java/org/apache/coyote/http11/InternalNioInputBuffer.java.orig java/org/apache/coyote/http11/InternalNioInputBuffer.java
|
|
|
6f268b |
--- java/org/apache/coyote/http11/InternalNioInputBuffer.java.orig 2017-03-09 08:51:40.000000000 -0500
|
|
|
6f268b |
+++ java/org/apache/coyote/http11/InternalNioInputBuffer.java 2019-03-05 14:58:20.272295967 -0500
|
|
|
6f268b |
@@ -98,12 +98,14 @@ public class InternalNioInputBuffer exte
|
|
|
6f268b |
/**
|
|
|
6f268b |
* Alternate constructor.
|
|
|
6f268b |
*/
|
|
|
6f268b |
- public InternalNioInputBuffer(Request request, int headerBufferSize) {
|
|
|
6f268b |
+ public InternalNioInputBuffer(Request request, int headerBufferSize,
|
|
|
6f268b |
+ HttpParser httpParser) {
|
|
|
6f268b |
|
|
|
6f268b |
this.request = request;
|
|
|
6f268b |
headers = request.getMimeHeaders();
|
|
|
6f268b |
|
|
|
6f268b |
this.headerBufferSize = headerBufferSize;
|
|
|
6f268b |
+ this.httpParser = httpParser;
|
|
|
6f268b |
|
|
|
6f268b |
inputStreamInputBuffer = new SocketInputBuffer();
|
|
|
6f268b |
|
|
|
6f268b |
@@ -313,7 +315,13 @@ public class InternalNioInputBuffer exte
|
|
|
6f268b |
end = pos;
|
|
|
6f268b |
} else if ((buf[pos] == Constants.QUESTION) && (parsingRequestLineQPos == -1)) {
|
|
|
6f268b |
parsingRequestLineQPos = pos;
|
|
|
6f268b |
- } else if (HttpParser.isNotRequestTarget(buf[pos])) {
|
|
|
6f268b |
+ } else if (parsingRequestLineQPos != -1 && !httpParser.isQueryRelaxed(buf[pos])) {
|
|
|
6f268b |
+ // %nn decoding will be checked at the point of decoding
|
|
|
6f268b |
+ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
|
|
|
6f268b |
+ } else if (httpParser.isNotRequestTargetRelaxed(buf[pos])) {
|
|
|
6f268b |
+ // This is a general check that aims to catch problems early
|
|
|
6f268b |
+ // Detailed checking of each part of the request target will
|
|
|
6f268b |
+ // happen in AbstractHttp11Processor#prepareRequest()
|
|
|
6f268b |
throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
|
|
|
6f268b |
}
|
|
|
6f268b |
pos++;
|
|
|
6f268b |
diff -up java/org/apache/coyote/http11/LocalStrings.properties.orig java/org/apache/coyote/http11/LocalStrings.properties
|
|
|
6f268b |
--- java/org/apache/coyote/http11/LocalStrings.properties.orig 2019-03-05 12:14:08.092280004 -0500
|
|
|
6f268b |
+++ java/org/apache/coyote/http11/LocalStrings.properties 2019-03-05 12:23:45.474498387 -0500
|
|
|
6f268b |
@@ -27,6 +27,9 @@ http11processor.filter.unknown=Unknown f
|
|
|
6f268b |
http11processor.filter.error=Error intializing filter {0}
|
|
|
6f268b |
http11processor.header.parse=Error parsing HTTP request header
|
|
|
6f268b |
http11processor.neverused=This method should never be used
|
|
|
6f268b |
+http11processor.request.invalidScheme=The HTTP request contained an absolute URI with an invalid scheme
|
|
|
6f268b |
+http11processor.request.invalidUri==The HTTP request contained an invalid URI
|
|
|
6f268b |
+http11processor.request.invalidUserInfo=The HTTP request contained an absolute URI with an invalid userinfo
|
|
|
6f268b |
http11processor.request.prepare=Error preparing request
|
|
|
6f268b |
http11processor.request.process=Error processing request
|
|
|
6f268b |
http11processor.request.finish=Error finishing request
|
|
|
6f268b |
diff -up java/org/apache/tomcat/util/buf/ByteChunk.java.orig java/org/apache/tomcat/util/buf/ByteChunk.java
|
|
|
6f268b |
--- java/org/apache/tomcat/util/buf/ByteChunk.java.orig 2017-03-09 08:51:41.000000000 -0500
|
|
|
6f268b |
+++ java/org/apache/tomcat/util/buf/ByteChunk.java 2019-03-05 12:16:53.404769901 -0500
|
|
|
6f268b |
@@ -668,7 +668,8 @@ public final class ByteChunk implements
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
/**
|
|
|
6f268b |
- * Returns true if the message bytes starts with the specified string.
|
|
|
6f268b |
+ * Returns true if the buffer starts with the specified string when tested
|
|
|
6f268b |
+ * in a case sensitive manner.
|
|
|
6f268b |
* @param s the string
|
|
|
6f268b |
* @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
|
|
|
6f268b |
*/
|
|
|
6f268b |
@@ -717,6 +718,31 @@ public final class ByteChunk implements
|
|
|
6f268b |
* @param s the string
|
|
|
6f268b |
* @param pos The position
|
|
|
6f268b |
*/
|
|
|
6f268b |
+ public boolean startsWith(String s, int pos) {
|
|
|
6f268b |
+ byte[] b = buff;
|
|
|
6f268b |
+ int len = s.length();
|
|
|
6f268b |
+ if (b == null || len + pos > end - start) {
|
|
|
6f268b |
+ return false;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ int off = start + pos;
|
|
|
6f268b |
+ for (int i = 0; i < len; i++) {
|
|
|
6f268b |
+ if (b[off++] != s.charAt(i)) {
|
|
|
6f268b |
+ return false;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ return true;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+
|
|
|
6f268b |
+ /**
|
|
|
6f268b |
+ * Returns true if the buffer starts with the specified string when tested
|
|
|
6f268b |
+ * in a case insensitive manner.
|
|
|
6f268b |
+ *
|
|
|
6f268b |
+ * @param s the string
|
|
|
6f268b |
+ * @param pos The position
|
|
|
6f268b |
+ *
|
|
|
6f268b |
+ * @return true if the start matches
|
|
|
6f268b |
+ */
|
|
|
6f268b |
public boolean startsWithIgnoreCase(String s, int pos) {
|
|
|
6f268b |
byte[] b = buff;
|
|
|
6f268b |
int len = s.length();
|
|
|
6f268b |
diff -up java/org/apache/tomcat/util/http/parser/HttpParser.java.orig java/org/apache/tomcat/util/http/parser/HttpParser.java
|
|
|
6f268b |
--- java/org/apache/tomcat/util/http/parser/HttpParser.java.orig 2017-03-09 08:51:41.000000000 -0500
|
|
|
6f268b |
+++ java/org/apache/tomcat/util/http/parser/HttpParser.java 2019-03-05 14:58:20.291295916 -0500
|
|
|
6f268b |
@@ -67,9 +67,17 @@ public class HttpParser {
|
|
|
6f268b |
private static final boolean[] IS_SEPARATOR = new boolean[ARRAY_SIZE];
|
|
|
6f268b |
private static final boolean[] IS_TOKEN = new boolean[ARRAY_SIZE];
|
|
|
6f268b |
private static final boolean[] IS_HEX = new boolean[ARRAY_SIZE];
|
|
|
6f268b |
- private static final boolean[] IS_NOT_REQUEST_TARGET = new boolean[ARRAY_SIZE];
|
|
|
6f268b |
private static final boolean[] IS_HTTP_PROTOCOL = new boolean[ARRAY_SIZE];
|
|
|
6f268b |
+ private static final boolean[] IS_ALPHA = new boolean[ARRAY_SIZE];
|
|
|
6f268b |
+ private static final boolean[] IS_NUMERIC = new boolean[ARRAY_SIZE];
|
|
|
6f268b |
private static final boolean[] REQUEST_TARGET_ALLOW = new boolean[ARRAY_SIZE];
|
|
|
6f268b |
+ private static final boolean[] IS_UNRESERVED = new boolean[ARRAY_SIZE];
|
|
|
6f268b |
+ private static final boolean[] IS_SUBDELIM = new boolean[ARRAY_SIZE];
|
|
|
6f268b |
+ private static final boolean[] IS_USERINFO = new boolean[ARRAY_SIZE];
|
|
|
6f268b |
+ private static final boolean[] IS_RELAXABLE = new boolean[ARRAY_SIZE];
|
|
|
6f268b |
+
|
|
|
6f268b |
+ private static final HttpParser DEFAULT;
|
|
|
6f268b |
+
|
|
|
6f268b |
|
|
|
6f268b |
static {
|
|
|
6f268b |
// Digest field types.
|
|
|
6f268b |
@@ -91,19 +99,6 @@ public class HttpParser {
|
|
|
6f268b |
// RFC2617 says nc is 8LHEX. <">8LHEX<"> will also be accepted
|
|
|
6f268b |
fieldTypes.put("nc", FIELD_TYPE_LHEX);
|
|
|
6f268b |
|
|
|
6f268b |
- String prop = System.getProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow");
|
|
|
6f268b |
- if (prop != null) {
|
|
|
6f268b |
- for (int i = 0; i < prop.length(); i++) {
|
|
|
6f268b |
- char c = prop.charAt(i);
|
|
|
6f268b |
- if (c == '{' || c == '}' || c == '|') {
|
|
|
6f268b |
- REQUEST_TARGET_ALLOW[c] = true;
|
|
|
6f268b |
- } else {
|
|
|
6f268b |
- log.warn(sm.getString("httpparser.invalidRequestTargetCharacter",
|
|
|
6f268b |
- Character.valueOf(c)));
|
|
|
6f268b |
- }
|
|
|
6f268b |
- }
|
|
|
6f268b |
- }
|
|
|
6f268b |
-
|
|
|
6f268b |
for (int i = 0; i < ARRAY_SIZE; i++) {
|
|
|
6f268b |
// Control> 0-31, 127
|
|
|
6f268b |
if (i < 32 || i == 127) {
|
|
|
6f268b |
@@ -128,6 +123,67 @@ public class HttpParser {
|
|
|
6f268b |
IS_HEX[i] = true;
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
+ // Not valid for HTTP protocol
|
|
|
6f268b |
+ // "HTTP/" DIGIT "." DIGIT
|
|
|
6f268b |
+ if (i == 'H' || i == 'T' || i == 'P' || i == '/' || i == '.' || (i >= '0' && i <= '9')) {
|
|
|
6f268b |
+ IS_HTTP_PROTOCOL[i] = true;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+ if (i >= '0' && i <= '9') {
|
|
|
6f268b |
+ IS_NUMERIC[i] = true;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+ if (i >= 'a' && i <= 'z' || i >= 'A' && i <= 'Z') {
|
|
|
6f268b |
+ IS_ALPHA[i] = true;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+ if (IS_ALPHA[i] || IS_NUMERIC[i] || i == '-' || i == '.' || i == '_' || i == '~') {
|
|
|
6f268b |
+ IS_UNRESERVED[i] = true;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+ if (i == '!' || i == '$' || i == '&' || i == '\'' || i == '(' || i == ')' || i == '*' ||
|
|
|
6f268b |
+ i == '+' || i == ',' || i == ';' || i == '=') {
|
|
|
6f268b |
+ IS_SUBDELIM[i] = true;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+ // userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
|
|
|
6f268b |
+ if (IS_UNRESERVED[i] || i == '%' || IS_SUBDELIM[i] || i == ':') {
|
|
|
6f268b |
+ IS_USERINFO[i] = true;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+ // The characters that are normally not permitted for which the
|
|
|
6f268b |
+ // restrictions may be relaxed when used in the path and/or query
|
|
|
6f268b |
+ // string
|
|
|
6f268b |
+ if (i == '\"' || i == '<' || i == '>' || i == '[' || i == '\\' || i == ']' ||
|
|
|
6f268b |
+ i == '^' || i == '`' || i == '{' || i == '|' || i == '}') {
|
|
|
6f268b |
+ IS_RELAXABLE[i] = true;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+ String prop = System.getProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow");
|
|
|
6f268b |
+ if (prop != null) {
|
|
|
6f268b |
+ for (int i = 0; i < prop.length(); i++) {
|
|
|
6f268b |
+ char c = prop.charAt(i);
|
|
|
6f268b |
+ if (c == '{' || c == '}' || c == '|') {
|
|
|
6f268b |
+ REQUEST_TARGET_ALLOW[c] = true;
|
|
|
6f268b |
+ } else {
|
|
|
6f268b |
+ log.warn(sm.getString("http.invalidRequestTargetCharacter",
|
|
|
6f268b |
+ Character.valueOf(c)));
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+ DEFAULT = new HttpParser(null, null);
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+
|
|
|
6f268b |
+ private final boolean[] IS_NOT_REQUEST_TARGET = new boolean[ARRAY_SIZE];
|
|
|
6f268b |
+ private final boolean[] IS_ABSOLUTEPATH_RELAXED = new boolean[ARRAY_SIZE];
|
|
|
6f268b |
+ private final boolean[] IS_QUERY_RELAXED = new boolean[ARRAY_SIZE];
|
|
|
6f268b |
+
|
|
|
6f268b |
+
|
|
|
6f268b |
+ public HttpParser(String relaxedPathChars, String relaxedQueryChars) {
|
|
|
6f268b |
+ for (int i = 0; i < ARRAY_SIZE; i++) {
|
|
|
6f268b |
// Not valid for request target.
|
|
|
6f268b |
// Combination of multiple rules from RFC7230 and RFC 3986. Must be
|
|
|
6f268b |
// ASCII, no controls plus a few additional characters excluded
|
|
|
6f268b |
@@ -139,12 +195,29 @@ public class HttpParser {
|
|
|
6f268b |
}
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
- // Not valid for HTTP protocol
|
|
|
6f268b |
- // "HTTP/" DIGIT "." DIGIT
|
|
|
6f268b |
- if (i == 'H' || i == 'T' || i == 'P' || i == '/' || i == '.' || (i >= '0' && i <= '9')) {
|
|
|
6f268b |
- IS_HTTP_PROTOCOL[i] = true;
|
|
|
6f268b |
+ /*
|
|
|
6f268b |
+ * absolute-path = 1*( "/" segment )
|
|
|
6f268b |
+ * segment = *pchar
|
|
|
6f268b |
+ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
|
|
6f268b |
+ *
|
|
|
6f268b |
+ * Note pchar allows everything userinfo allows plus "@"
|
|
|
6f268b |
+ */
|
|
|
6f268b |
+ if (IS_USERINFO[i] || i == '@' || i == '/' || REQUEST_TARGET_ALLOW[i]) {
|
|
|
6f268b |
+ IS_ABSOLUTEPATH_RELAXED[i] = true;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+ /*
|
|
|
6f268b |
+ * query = *( pchar / "/" / "?" )
|
|
|
6f268b |
+ *
|
|
|
6f268b |
+ * Note query allows everything absolute-path allows plus "?"
|
|
|
6f268b |
+ */
|
|
|
6f268b |
+ if (IS_ABSOLUTEPATH_RELAXED[i] || i == '?' || REQUEST_TARGET_ALLOW[i]) {
|
|
|
6f268b |
+ IS_QUERY_RELAXED[i] = true;
|
|
|
6f268b |
}
|
|
|
6f268b |
}
|
|
|
6f268b |
+
|
|
|
6f268b |
+ relax(IS_ABSOLUTEPATH_RELAXED, relaxedPathChars);
|
|
|
6f268b |
+ relax(IS_QUERY_RELAXED, relaxedQueryChars);
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
/**
|
|
|
6f268b |
@@ -277,6 +350,39 @@ public class HttpParser {
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
|
|
|
6f268b |
+ public boolean isNotRequestTargetRelaxed(int c) {
|
|
|
6f268b |
+ // Fast for valid request target characters, slower for some incorrect
|
|
|
6f268b |
+ // ones
|
|
|
6f268b |
+ try {
|
|
|
6f268b |
+ return IS_NOT_REQUEST_TARGET[c];
|
|
|
6f268b |
+ } catch (ArrayIndexOutOfBoundsException ex) {
|
|
|
6f268b |
+ return true;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+
|
|
|
6f268b |
+ public boolean isAbsolutePathRelaxed(int c) {
|
|
|
6f268b |
+ // Fast for valid user info characters, slower for some incorrect
|
|
|
6f268b |
+ // ones
|
|
|
6f268b |
+ try {
|
|
|
6f268b |
+ return IS_ABSOLUTEPATH_RELAXED[c];
|
|
|
6f268b |
+ } catch (ArrayIndexOutOfBoundsException ex) {
|
|
|
6f268b |
+ return false;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+
|
|
|
6f268b |
+ public boolean isQueryRelaxed(int c) {
|
|
|
6f268b |
+ // Fast for valid user info characters, slower for some incorrect
|
|
|
6f268b |
+ // ones
|
|
|
6f268b |
+ try {
|
|
|
6f268b |
+ return IS_QUERY_RELAXED[c];
|
|
|
6f268b |
+ } catch (ArrayIndexOutOfBoundsException ex) {
|
|
|
6f268b |
+ return false;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+
|
|
|
6f268b |
public static String unquote(String input) {
|
|
|
6f268b |
if (input == null || input.length() < 2) {
|
|
|
6f268b |
return input;
|
|
|
6f268b |
@@ -329,27 +435,53 @@ public class HttpParser {
|
|
|
6f268b |
|
|
|
6f268b |
|
|
|
6f268b |
public static boolean isNotRequestTarget(int c) {
|
|
|
6f268b |
- // Fast for valid request target characters, slower for some incorrect
|
|
|
6f268b |
+ return DEFAULT.isNotRequestTargetRelaxed(c);
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+
|
|
|
6f268b |
+ public static boolean isHttpProtocol(int c) {
|
|
|
6f268b |
+ // Fast for valid HTTP protocol characters, slower for some incorrect
|
|
|
6f268b |
// ones
|
|
|
6f268b |
try {
|
|
|
6f268b |
- return IS_NOT_REQUEST_TARGET[c];
|
|
|
6f268b |
+ return IS_HTTP_PROTOCOL[c];
|
|
|
6f268b |
} catch (ArrayIndexOutOfBoundsException ex) {
|
|
|
6f268b |
- return true;
|
|
|
6f268b |
+ return false;
|
|
|
6f268b |
}
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
|
|
|
6f268b |
- public static boolean isHttpProtocol(int c) {
|
|
|
6f268b |
- // Fast for valid HTTP protocol characters, slower for some incorrect
|
|
|
6f268b |
+ public static boolean isUserInfo(int c) {
|
|
|
6f268b |
+ // Fast for valid user info characters, slower for some incorrect
|
|
|
6f268b |
// ones
|
|
|
6f268b |
try {
|
|
|
6f268b |
- return IS_HTTP_PROTOCOL[c];
|
|
|
6f268b |
+ return IS_USERINFO[c];
|
|
|
6f268b |
+ } catch (ArrayIndexOutOfBoundsException ex) {
|
|
|
6f268b |
+ return false;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+
|
|
|
6f268b |
+ private static boolean isRelaxable(int c) {
|
|
|
6f268b |
+ // Fast for valid user info characters, slower for some incorrect
|
|
|
6f268b |
+ // ones
|
|
|
6f268b |
+ try {
|
|
|
6f268b |
+ return IS_RELAXABLE[c];
|
|
|
6f268b |
} catch (ArrayIndexOutOfBoundsException ex) {
|
|
|
6f268b |
return false;
|
|
|
6f268b |
}
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
|
|
|
6f268b |
+ public static boolean isAbsolutePath(int c) {
|
|
|
6f268b |
+ return DEFAULT.isAbsolutePathRelaxed(c);
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+
|
|
|
6f268b |
+ public static boolean isQuery(int c) {
|
|
|
6f268b |
+ return DEFAULT.isQueryRelaxed(c);
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
+
|
|
|
6f268b |
// Skip any LWS and return the next char
|
|
|
6f268b |
private static int skipLws(StringReader input, boolean withReset)
|
|
|
6f268b |
throws IOException {
|
|
|
6f268b |
@@ -579,6 +711,18 @@ public class HttpParser {
|
|
|
6f268b |
}
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
+ private void relax(boolean[] flags, String relaxedChars) {
|
|
|
6f268b |
+ if (relaxedChars != null && relaxedChars.length() > 0) {
|
|
|
6f268b |
+ char[] chars = relaxedChars.toCharArray();
|
|
|
6f268b |
+ for (char c : chars) {
|
|
|
6f268b |
+ if (isRelaxable(c)) {
|
|
|
6f268b |
+ flags[c] = true;
|
|
|
6f268b |
+ IS_NOT_REQUEST_TARGET[c] = false;
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
private static enum SkipConstantResult {
|
|
|
6f268b |
FOUND,
|
|
|
6f268b |
NOT_FOUND,
|
|
|
6f268b |
diff -up conf/catalina.properties.orig conf/catalina.properties
|
|
|
6f268b |
--- conf/catalina.properties.orig 2019-03-05 12:13:51.934329862 -0500
|
|
|
6f268b |
+++ conf/catalina.properties 2019-03-05 12:14:08.094279997 -0500
|
|
|
6f268b |
@@ -132,6 +132,9 @@ tomcat.util.buf.StringCache.byte.enabled
|
|
|
6f268b |
#tomcat.util.buf.StringCache.trainThreshold=500000
|
|
|
6f268b |
#tomcat.util.buf.StringCache.cacheSize=5000
|
|
|
6f268b |
|
|
|
6f268b |
+# This system property is deprecated. Use the relaxedPathChars relaxedQueryChars
|
|
|
6f268b |
+# attributes of the Connector instead. These attributes permit a wider range of
|
|
|
6f268b |
+# characters to be configured as valid.
|
|
|
6f268b |
# Allow for changes to HTTP request validation
|
|
|
6f268b |
-# WARNING: Using this option will expose the server to CVE-2016-6816
|
|
|
6f268b |
+# WARNING: Using this option may expose the server to CVE-2016-6816
|
|
|
6f268b |
#tomcat.util.http.parser.HttpParser.requestTargetAllow=|
|
|
|
6f268b |
diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml
|
|
|
6f268b |
--- webapps/docs/changelog.xml.orig 2019-03-05 12:13:47.068344877 -0500
|
|
|
6f268b |
+++ webapps/docs/changelog.xml 2019-03-05 12:14:08.103279970 -0500
|
|
|
6f268b |
@@ -108,6 +108,12 @@
|
|
|
6f268b |
Improve handing of overflow in the UTF-8 decoder with supplementary
|
|
|
6f268b |
characters. (markt)
|
|
|
6f268b |
</fix>
|
|
|
6f268b |
+ <add>
|
|
|
6f268b |
+ <bug>62273</bug>: Implement configuration options to work-around
|
|
|
6f268b |
+ specification non-compliant user agents (including all the major
|
|
|
6f268b |
+ browsers) that do not correctly %nn encode URI paths and query strings
|
|
|
6f268b |
+ as required by RFC 7230 and RFC 3986. (markt)
|
|
|
6f268b |
+ </add>
|
|
|
6f268b |
</changelog>
|
|
|
6f268b |
</subsection>
|
|
|
6f268b |
</section>
|
|
|
6f268b |
diff -up webapps/docs/config/http.xml.orig webapps/docs/config/http.xml
|
|
|
6f268b |
--- webapps/docs/config/http.xml.orig 2017-03-09 08:51:43.000000000 -0500
|
|
|
6f268b |
+++ webapps/docs/config/http.xml 2019-03-05 12:14:08.103279970 -0500
|
|
|
6f268b |
@@ -516,6 +516,32 @@
|
|
|
6f268b |
expected concurrent requests (synchronous and asynchronous).
|
|
|
6f268b |
</attribute>
|
|
|
6f268b |
|
|
|
6f268b |
+ <attribute name="relaxedPathChars" required="false">
|
|
|
6f268b |
+ The HTTP/1.1
|
|
|
6f268b |
+ specification requires that certain characters are %nn encoded when
|
|
|
6f268b |
+ used in URI paths. Unfortunately, many user agents including all the major
|
|
|
6f268b |
+ browsers are not compliant with this specification and use these
|
|
|
6f268b |
+ characters in unencoded form. To prevent Tomcat rejecting such requests,
|
|
|
6f268b |
+ this attribute may be used to specify the additional characters to allow.
|
|
|
6f268b |
+ If not specified, no addtional characters will be allowed. The value may
|
|
|
6f268b |
+ be any combination of the following characters:
|
|
|
6f268b |
+ " < > [ \ ] ^ ` { | } . Any other characters
|
|
|
6f268b |
+ present in the value will be ignored.
|
|
|
6f268b |
+ </attribute>
|
|
|
6f268b |
+
|
|
|
6f268b |
+ <attribute name="relaxedQueryChars" required="false">
|
|
|
6f268b |
+ The HTTP/1.1
|
|
|
6f268b |
+ specification requires that certain characters are %nn encoded when
|
|
|
6f268b |
+ used in URI query strings. Unfortunately, many user agents including all
|
|
|
6f268b |
+ the major browsers are not compliant with this specification and use these
|
|
|
6f268b |
+ characters in unencoded form. To prevent Tomcat rejecting such requests,
|
|
|
6f268b |
+ this attribute may be used to specify the additional characters to allow.
|
|
|
6f268b |
+ If not specified, no addtional characters will be allowed. The value may
|
|
|
6f268b |
+ be any combination of the following characters:
|
|
|
6f268b |
+ " < > [ \ ] ^ ` { | } . Any other characters
|
|
|
6f268b |
+ present in the value will be ignored.
|
|
|
6f268b |
+ </attribute>
|
|
|
6f268b |
+
|
|
|
6f268b |
<attribute name="restrictedUserAgents" required="false">
|
|
|
6f268b |
The value is a regular expression (using java.util.regex )
|
|
|
6f268b |
matching the user-agent header of HTTP clients for which
|
|
|
6f268b |
diff -up webapps/docs/config/systemprops.xml.orig webapps/docs/config/systemprops.xml
|
|
|
6f268b |
--- webapps/docs/config/systemprops.xml.orig 2019-03-05 12:14:08.104279967 -0500
|
|
|
6f268b |
+++ webapps/docs/config/systemprops.xml 2019-03-05 12:16:02.075928285 -0500
|
|
|
6f268b |
@@ -709,11 +709,15 @@
|
|
|
6f268b |
</property>
|
|
|
6f268b |
|
|
|
6f268b |
<property name="tomcat.util.http.parser.HttpParser.requestTargetAllow">
|
|
|
6f268b |
+ This system property is deprecated. Use the
|
|
|
6f268b |
+ relaxedPathChars and relaxedQueryChars
|
|
|
6f268b |
+ attributes of the Connector instead. These attributes permit a wider range
|
|
|
6f268b |
+ of characters to be configured as valid.
|
|
|
6f268b |
A string comprised of characters the server should allow even when they are not encoded.
|
|
|
6f268b |
These characters would normally result in a 400 status.
|
|
|
6f268b |
The acceptable characters for this property are: | , {
|
|
|
6f268b |
, and }
|
|
|
6f268b |
- WARNING: Use of this option will expose the server to CVE-2016-6816.
|
|
|
6f268b |
+ WARNING: Use of this option may expose the server to CVE-2016-6816.
|
|
|
6f268b |
|
|
|
6f268b |
If not specified, the default value of null will be used.
|
|
|
6f268b |
</property>
|
|
|
6f268b |
diff -up test/org/apache/catalina/core/TestApplicationContext.java.orig test/org/apache/catalina/core/TestApplicationContext.java
|
|
|
6f268b |
--- test/org/apache/catalina/core/TestApplicationContext.java.orig 2019-03-05 12:13:51.981329717 -0500
|
|
|
6f268b |
+++ test/org/apache/catalina/core/TestApplicationContext.java 2019-03-05 12:14:08.094279997 -0500
|
|
|
6f268b |
@@ -77,7 +77,7 @@ public class TestApplicationContext exte
|
|
|
6f268b |
|
|
|
6f268b |
ByteChunk res = new ByteChunk();
|
|
|
6f268b |
int rc = getUrl("http://localhost:" + getPort() +
|
|
|
6f268b |
- "/test/bug5nnnn/bug53467].jsp", res, null);
|
|
|
6f268b |
+ "/test/bug5nnnn/bug53467%5D.jsp", res, null);
|
|
|
6f268b |
|
|
|
6f268b |
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
|
|
|
6f268b |
Assert.assertTrue(res.toString().contains("OK "));
|