File tomcat-8.0.53-CVE-2019-17569.patch of Package tomcat.37363
Index: apache-tomcat-8.0.53-src/java/org/apache/coyote/http11/AbstractHttp11Processor.java
===================================================================
--- apache-tomcat-8.0.53-src.orig/java/org/apache/coyote/http11/AbstractHttp11Processor.java
+++ apache-tomcat-8.0.53-src/java/org/apache/coyote/http11/AbstractHttp11Processor.java
@@ -19,6 +19,8 @@ package org.apache.coyote.http11;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.StringTokenizer;
@@ -50,6 +52,7 @@ import org.apache.tomcat.util.buf.Messag
import org.apache.tomcat.util.http.FastHttpDateFormat;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.http.parser.HttpParser;
+import org.apache.tomcat.util.http.parser.TokenList;
import org.apache.tomcat.util.log.UserDataHelper;
import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
@@ -1276,7 +1279,7 @@ public abstract class AbstractHttp11Proc
/**
* After reading the request headers, we have to setup the request filters.
*/
- protected void prepareRequest() {
+ protected void prepareRequest() throws IOException{
http11 = true;
http09 = false;
@@ -1506,19 +1509,16 @@ public abstract class AbstractHttp11Proc
transferEncodingValueMB = headers.getValue("transfer-encoding");
}
if (transferEncodingValueMB != null) {
- String transferEncodingValue = transferEncodingValueMB.toString();
- // Parse the comma separated list. "identity" codings are ignored
- int startPos = 0;
- int commaPos = transferEncodingValue.indexOf(',');
- String encodingName = null;
- while (commaPos != -1) {
- encodingName = transferEncodingValue.substring(startPos, commaPos);
- addInputFilter(inputFilters, encodingName);
- startPos = commaPos + 1;
- commaPos = transferEncodingValue.indexOf(',', startPos);
+ List<String> encodingNames = new ArrayList<String>();
+ if (TokenList.parseTokenList(headers.values("transfer-encoding"), encodingNames)) {
+ for (String encodingName : encodingNames) {
+ // "identity" codings are ignored
+ addInputFilter(inputFilters, encodingName);
+ }
+ } else {
+ // Invalid transfer encoding
+ badRequest("http11processor.request.invalidTransferEncoding");
}
- encodingName = transferEncodingValue.substring(startPos);
- addInputFilter(inputFilters, encodingName);
}
// Parse content-length header
@@ -1554,6 +1554,13 @@ public abstract class AbstractHttp11Proc
}
}
+ private void badRequest(String errorKey) {
+ response.setStatus(400);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ if (getLog().isDebugEnabled()) {
+ getLog().debug(sm.getString(errorKey));
+ }
+ }
/**
* Connector implementation specific request preparation. Ideally, this will
Index: apache-tomcat-8.0.53-src/java/org/apache/coyote/http11/LocalStrings.properties
===================================================================
--- apache-tomcat-8.0.53-src.orig/java/org/apache/coyote/http11/LocalStrings.properties
+++ apache-tomcat-8.0.53-src/java/org/apache/coyote/http11/LocalStrings.properties
@@ -18,7 +18,8 @@ http11processor.header.parse=Error parsi
http11processor.neverused=This method should never be used
http11processor.request.inconsistentHosts=The host specified in the request line is not consistent with the host header
http11processor.request.invalidScheme=The HTTP request contained an absolute URI with an invalid scheme
-http11processor.request.invalidUri==The HTTP request contained an invalid URI
+http11processor.request.invalidTransferEncoding=The HTTP request contained an invalid Transfer-Encoding header
+http11processor.request.invalidUri=The HTTP request contained an invalid URI
http11processor.request.invalidUserInfo=The HTTP request contained an absolute URI with an invalid userinfo
http11processor.request.multipleHosts=The request contained multiple host headers
http11processor.request.noHostHeader=The HTTP/1.1 request did not provide a host header
Index: apache-tomcat-8.0.53-src/java/org/apache/tomcat/util/http/parser/TokenList.java
===================================================================
--- /dev/null
+++ apache-tomcat-8.0.53-src/java/org/apache/tomcat/util/http/parser/TokenList.java
@@ -0,0 +1,113 @@
+/*
+ * 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.parser;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Locale;
+
+public class TokenList {
+
+ private TokenList() {
+ // Utility class. Hide default constructor.
+ }
+
+
+ /**
+ * Parses an enumeration of header values of the form 1#token, forcing all
+ * parsed values to lower case.
+ *
+ * @param inputs The headers to parse
+ * @param collection The Collection (usually a list of a set) to which the
+ * parsed tokens should be added
+ *
+ * @return {@code} true if the header values were parsed cleanly, otherwise
+ * {@code false} (e.g. if a non-token value was encountered)
+ *
+ * @throws IOException If an I/O error occurs reading the header
+ */
+ public static boolean parseTokenList(Enumeration<String> inputs, Collection<String> collection) throws IOException {
+ boolean result = true;
+ while (inputs.hasMoreElements()) {
+ String nextHeaderValue = inputs.nextElement();
+ if (nextHeaderValue != null) {
+ if (!TokenList.parseTokenList(new StringReader(nextHeaderValue), collection)) {
+ result = false;
+ }
+ }
+ }
+ return result;
+ }
+
+
+ /**
+ * Parses a header of the form 1#token, forcing all parsed values to lower
+ * case. This is typically used when header values are case-insensitive.
+ *
+ * @param input The header to parse
+ * @param collection The Collection (usually a list of a set) to which the
+ * parsed tokens should be added
+ *
+ * @return {@code} true if the header was parsed cleanly, otherwise
+ * {@code false} (e.g. if a non-token value was encountered)
+ *
+ * @throws IOException If an I/O error occurs reading the header
+ */
+ public static boolean parseTokenList(Reader input, Collection<String> collection) throws IOException {
+ boolean invalid = false;
+ boolean valid = false;
+
+ do {
+ String fieldName = HttpParser.readToken(input);
+ if (fieldName == null) {
+ // Invalid field-name, skip to the next one
+ invalid = true;
+ HttpParser.skipUntil(input, 0, ',');
+ continue;
+ }
+
+ if (fieldName.length() == 0) {
+ // No more data to read
+ break;
+ }
+
+ SkipResult skipResult = HttpParser.skipConstant(input, ",");
+ if (skipResult == SkipResult.EOF) {
+ // EOF
+ valid = true;
+ collection.add(fieldName.toLowerCase(Locale.ENGLISH));
+ break;
+ } else if (skipResult == SkipResult.FOUND) {
+ valid = true;
+ collection.add(fieldName.toLowerCase(Locale.ENGLISH));
+ continue;
+ } else {
+ // Not a token - ignore it
+ invalid = true;
+ HttpParser.skipUntil(input, 0, ',');
+ continue;
+ }
+ } while (true);
+
+ // Only return true if at least one valid token was read and no invalid
+ // entries were found
+ return valid && !invalid;
+ }
+}