|
|
79b4cc |
--- java/org/apache/coyote/http11/filters/ChunkedInputFilter.java.orig 2014-06-16 18:41:33.642851000 -0400
|
|
|
79b4cc |
+++ java/org/apache/coyote/http11/filters/ChunkedInputFilter.java 2014-06-16 19:36:36.796994000 -0400
|
|
|
79b4cc |
@@ -29,6 +29,7 @@
|
|
|
79b4cc |
import org.apache.tomcat.util.buf.HexUtils;
|
|
|
79b4cc |
import org.apache.tomcat.util.buf.MessageBytes;
|
|
|
79b4cc |
import org.apache.tomcat.util.http.MimeHeaders;
|
|
|
79b4cc |
+import org.apache.tomcat.util.res.StringManager;
|
|
|
79b4cc |
|
|
|
79b4cc |
/**
|
|
|
79b4cc |
* Chunked input filter. Parses chunked data according to
|
|
|
79b4cc |
@@ -39,6 +40,9 @@
|
|
|
79b4cc |
*/
|
|
|
79b4cc |
public class ChunkedInputFilter implements InputFilter {
|
|
|
79b4cc |
|
|
|
79b4cc |
+ private static final StringManager sm = StringManager.getManager(
|
|
|
79b4cc |
+ ChunkedInputFilter.class.getPackage().getName());
|
|
|
79b4cc |
+
|
|
|
79b4cc |
private static final org.apache.juli.logging.Log log
|
|
|
79b4cc |
= org.apache.juli.logging.LogFactory.getLog(ChunkedInputFilter.class);
|
|
|
79b4cc |
|
|
|
79b4cc |
@@ -138,6 +142,11 @@
|
|
|
79b4cc |
*/
|
|
|
79b4cc |
private long extensionSize;
|
|
|
79b4cc |
|
|
|
79b4cc |
+ /**
|
|
|
79b4cc |
+ * Flat that indicates an error has occured
|
|
|
79b4cc |
+ */
|
|
|
79b4cc |
+ private boolean error;
|
|
|
79b4cc |
+
|
|
|
79b4cc |
// ----------------------------------------------------------- Constructors
|
|
|
79b4cc |
public ChunkedInputFilter(int maxTrailerSize, int maxExtensionSize) {
|
|
|
79b4cc |
this.trailingHeaders.setLimit(maxTrailerSize);
|
|
|
79b4cc |
@@ -161,6 +170,8 @@
|
|
|
79b4cc |
public int doRead(ByteChunk chunk, Request req)
|
|
|
79b4cc |
throws IOException {
|
|
|
79b4cc |
|
|
|
79b4cc |
+ checkError();
|
|
|
79b4cc |
+
|
|
|
79b4cc |
if (endChunk)
|
|
|
79b4cc |
return -1;
|
|
|
79b4cc |
|
|
|
79b4cc |
@@ -171,7 +182,8 @@
|
|
|
79b4cc |
|
|
|
79b4cc |
if (remaining <= 0) {
|
|
|
79b4cc |
if (!parseChunkHeader()) {
|
|
|
79b4cc |
- throw new IOException("Invalid chunk header");
|
|
|
79b4cc |
+ throwIOException(sm.getString(
|
|
|
79b4cc |
+ "chunkedInputFilter.invalidHeader"));
|
|
|
79b4cc |
}
|
|
|
79b4cc |
if (endChunk) {
|
|
|
79b4cc |
parseEndChunk();
|
|
|
79b4cc |
@@ -183,9 +195,9 @@
|
|
|
79b4cc |
|
|
|
79b4cc |
if (pos >= lastValid) {
|
|
|
79b4cc |
if (readBytes() < 0) {
|
|
|
79b4cc |
- throw new IOException(
|
|
|
79b4cc |
- "Unexpected end of stream whilst reading request body");
|
|
|
79b4cc |
+ throwIOException(sm.getString("chunkedInputFilter.eos"));
|
|
|
79b4cc |
}
|
|
|
79b4cc |
+
|
|
|
79b4cc |
}
|
|
|
79b4cc |
|
|
|
79b4cc |
if (remaining > (lastValid - pos)) {
|
|
|
79b4cc |
@@ -232,6 +244,8 @@
|
|
|
79b4cc |
public long end()
|
|
|
79b4cc |
throws IOException {
|
|
|
79b4cc |
|
|
|
79b4cc |
+ checkError();
|
|
|
79b4cc |
+
|
|
|
79b4cc |
// Consume extra bytes : parse the stream until the end chunk is found
|
|
|
79b4cc |
while (doRead(readChunk, null) >= 0) {
|
|
|
79b4cc |
// NOOP: Just consume the input
|
|
|
79b4cc |
@@ -274,6 +288,7 @@
|
|
|
79b4cc |
trailingHeaders.recycle();
|
|
|
79b4cc |
trailingHeaders.setLimit(maxTrailerSize);
|
|
|
79b4cc |
extensionSize = 0;
|
|
|
79b4cc |
+ error = false;
|
|
|
79b4cc |
}
|
|
|
79b4cc |
|
|
|
79b4cc |
|
|
|
79b4cc |
@@ -286,6 +301,22 @@
|
|
|
79b4cc |
return ENCODING;
|
|
|
79b4cc |
}
|
|
|
79b4cc |
|
|
|
79b4cc |
+ private void throwIOException(String msg) throws IOException {
|
|
|
79b4cc |
+ error = true;
|
|
|
79b4cc |
+ throw new IOException(msg);
|
|
|
79b4cc |
+ }
|
|
|
79b4cc |
+
|
|
|
79b4cc |
+ private void throwEOFException(String msg) throws IOException {
|
|
|
79b4cc |
+ error = true;
|
|
|
79b4cc |
+ throw new IOException(msg);
|
|
|
79b4cc |
+ }
|
|
|
79b4cc |
+
|
|
|
79b4cc |
+ private void checkError() throws IOException {
|
|
|
79b4cc |
+ if (error) {
|
|
|
79b4cc |
+ throw new IOException(
|
|
|
79b4cc |
+ sm.getString("chunkedInputFilter.error"));
|
|
|
79b4cc |
+ }
|
|
|
79b4cc |
+ }
|
|
|
79b4cc |
|
|
|
79b4cc |
// ------------------------------------------------------ Protected Methods
|
|
|
79b4cc |
|
|
|
79b4cc |
@@ -322,7 +353,7 @@
|
|
|
79b4cc |
|
|
|
79b4cc |
int result = 0;
|
|
|
79b4cc |
boolean eol = false;
|
|
|
79b4cc |
- boolean readDigit = false;
|
|
|
79b4cc |
+ int readDigit = 0;
|
|
|
79b4cc |
boolean extension = false;
|
|
|
79b4cc |
|
|
|
79b4cc |
while (!eol) {
|
|
|
79b4cc |
@@ -346,10 +377,9 @@
|
|
|
79b4cc |
} else if (!extension) {
|
|
|
79b4cc |
//don't read data after the trailer
|
|
|
79b4cc |
int charValue = HexUtils.getDec(buf[pos]);
|
|
|
79b4cc |
- if (charValue != -1) {
|
|
|
79b4cc |
- readDigit = true;
|
|
|
79b4cc |
- result *= 16;
|
|
|
79b4cc |
- result += charValue;
|
|
|
79b4cc |
+ if (charValue != -1 && readDigit < 8) {
|
|
|
79b4cc |
+ readDigit++;
|
|
|
79b4cc |
+ result = (result << 4) | charValue;
|
|
|
79b4cc |
} else {
|
|
|
79b4cc |
//we shouldn't allow invalid, non hex characters
|
|
|
79b4cc |
//in the chunked header
|
|
|
79b4cc |
@@ -362,7 +392,8 @@
|
|
|
79b4cc |
// validated. Currently it is simply ignored.
|
|
|
79b4cc |
extensionSize++;
|
|
|
79b4cc |
if (maxExtensionSize > -1 && extensionSize > maxExtensionSize) {
|
|
|
79b4cc |
- throw new IOException("maxExtensionSize exceeded");
|
|
|
79b4cc |
+ throwIOException(sm.getString(
|
|
|
79b4cc |
+ "chunkedInputFilter.maxExtension"));
|
|
|
79b4cc |
}
|
|
|
79b4cc |
}
|
|
|
79b4cc |
// Parsing the CRLF increments pos
|
|
|
79b4cc |
@@ -371,7 +402,7 @@
|
|
|
79b4cc |
}
|
|
|
79b4cc |
}
|
|
|
79b4cc |
|
|
|
79b4cc |
- if (!readDigit)
|
|
|
79b4cc |
+ if (readDigit == 0 || result < 0)
|
|
|
79b4cc |
return false;
|
|
|
79b4cc |
|
|
|
79b4cc |
if (result == 0)
|
|
|
79b4cc |
@@ -411,20 +442,27 @@
|
|
|
79b4cc |
while (!eol) {
|
|
|
79b4cc |
|
|
|
79b4cc |
if (pos >= lastValid) {
|
|
|
79b4cc |
- if (readBytes() <= 0)
|
|
|
79b4cc |
- throw new IOException("Invalid CRLF");
|
|
|
79b4cc |
+ if (readBytes() <= 0) {
|
|
|
79b4cc |
+ throwIOException(sm.getString(
|
|
|
79b4cc |
+ "chunkedInputFilter.invaldCrlfNoData"));
|
|
|
79b4cc |
+ }
|
|
|
79b4cc |
}
|
|
|
79b4cc |
|
|
|
79b4cc |
if (buf[pos] == Constants.CR) {
|
|
|
79b4cc |
- if (crfound) throw new IOException("Invalid CRLF, two CR characters encountered.");
|
|
|
79b4cc |
+ if (crfound) {
|
|
|
79b4cc |
+ throwIOException(sm.getString(
|
|
|
79b4cc |
+ "chunkedInputFilter.invaldCrlfCRCR"));
|
|
|
79b4cc |
+ }
|
|
|
79b4cc |
crfound = true;
|
|
|
79b4cc |
} else if (buf[pos] == Constants.LF) {
|
|
|
79b4cc |
if (!tolerant && !crfound) {
|
|
|
79b4cc |
- throw new IOException("Invalid CRLF, no CR character encountered.");
|
|
|
79b4cc |
+ throwIOException(sm.getString(
|
|
|
79b4cc |
+ "chunkedInputFilter.invalidCrlfNoCR"));
|
|
|
79b4cc |
}
|
|
|
79b4cc |
eol = true;
|
|
|
79b4cc |
} else {
|
|
|
79b4cc |
- throw new IOException("Invalid CRLF");
|
|
|
79b4cc |
+ throwIOException(
|
|
|
79b4cc |
+ sm.getString("chunkedInputFilter.invalidCrlf"));
|
|
|
79b4cc |
}
|
|
|
79b4cc |
|
|
|
79b4cc |
pos++;
|
|
|
79b4cc |
@@ -453,8 +491,10 @@
|
|
|
79b4cc |
|
|
|
79b4cc |
// Read new bytes if needed
|
|
|
79b4cc |
if (pos >= lastValid) {
|
|
|
79b4cc |
- if (readBytes() <0)
|
|
|
79b4cc |
- throw new EOFException("Unexpected end of stream whilst reading trailer headers for chunked request");
|
|
|
79b4cc |
+ if (readBytes() <0) {
|
|
|
79b4cc |
+ throwEOFException(
|
|
|
79b4cc |
+ sm.getString("chunkedInputFilter.eosTrailer"));
|
|
|
79b4cc |
+ }
|
|
|
79b4cc |
}
|
|
|
79b4cc |
|
|
|
79b4cc |
chr = buf[pos];
|
|
|
79b4cc |
@@ -478,8 +518,10 @@
|
|
|
79b4cc |
|
|
|
79b4cc |
// Read new bytes if needed
|
|
|
79b4cc |
if (pos >= lastValid) {
|
|
|
79b4cc |
- if (readBytes() <0)
|
|
|
79b4cc |
- throw new EOFException("Unexpected end of stream whilst reading trailer headers for chunked request");
|
|
|
79b4cc |
+ if (readBytes() <0) {
|
|
|
79b4cc |
+ throwEOFException(sm.getString(
|
|
|
79b4cc |
+ "chunkedInputFilter.eosTrailer"));
|
|
|
79b4cc |
+ }
|
|
|
79b4cc |
}
|
|
|
79b4cc |
|
|
|
79b4cc |
chr = buf[pos];
|
|
|
79b4cc |
@@ -530,7 +572,8 @@
|
|
|
79b4cc |
// limit placed on trailing header size
|
|
|
79b4cc |
int newlimit = trailingHeaders.getLimit() -1;
|
|
|
79b4cc |
if (trailingHeaders.getEnd() > newlimit) {
|
|
|
79b4cc |
- throw new IOException("Exceeded maxTrailerSize");
|
|
|
79b4cc |
+ throw new IOException(
|
|
|
79b4cc |
+ sm.getString("chunkedInputFilter.maxTrailer"));
|
|
|
79b4cc |
}
|
|
|
79b4cc |
trailingHeaders.setLimit(newlimit);
|
|
|
79b4cc |
} else {
|
|
|
79b4cc |
@@ -542,8 +585,11 @@
|
|
|
79b4cc |
|
|
|
79b4cc |
// Read new bytes if needed
|
|
|
79b4cc |
if (pos >= lastValid) {
|
|
|
79b4cc |
- if (readBytes() <0)
|
|
|
79b4cc |
- throw new EOFException("Unexpected end of stream whilst reading trailer headers for chunked request");
|
|
|
79b4cc |
+ if (readBytes() <0) {
|
|
|
79b4cc |
+ throwEOFException(
|
|
|
79b4cc |
+ sm.getString("chunkedInputFilter.eosTrailer"));
|
|
|
79b4cc |
+ }
|
|
|
79b4cc |
+
|
|
|
79b4cc |
}
|
|
|
79b4cc |
|
|
|
79b4cc |
chr = buf[pos];
|
|
|
79b4cc |
@@ -567,8 +613,10 @@
|
|
|
79b4cc |
|
|
|
79b4cc |
// Read new bytes if needed
|
|
|
79b4cc |
if (pos >= lastValid) {
|
|
|
79b4cc |
- if (readBytes() <0)
|
|
|
79b4cc |
- throw new EOFException("Unexpected end of stream whilst reading trailer headers for chunked request");
|
|
|
79b4cc |
+ if (readBytes() <0) {
|
|
|
79b4cc |
+ throwEOFException(sm.getString(
|
|
|
79b4cc |
+ "chunkedInputFilter.eosTrailer"));
|
|
|
79b4cc |
+ }
|
|
|
79b4cc |
}
|
|
|
79b4cc |
|
|
|
79b4cc |
chr = buf[pos];
|
|
|
79b4cc |
--- java/org/apache/coyote/http11/filters/LocalStrings.properties.orig 2014-06-16 18:41:33.647850000 -0400
|
|
|
79b4cc |
+++ java/org/apache/coyote/http11/filters/LocalStrings.properties 2014-06-16 19:22:22.740111000 -0400
|
|
|
79b4cc |
@@ -0,0 +1,25 @@
|
|
|
79b4cc |
+# Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
79b4cc |
+# contributor license agreements. See the NOTICE file distributed with
|
|
|
79b4cc |
+# this work for additional information regarding copyright ownership.
|
|
|
79b4cc |
+# The ASF licenses this file to You under the Apache License, Version 2.0
|
|
|
79b4cc |
+# (the "License"); you may not use this file except in compliance with
|
|
|
79b4cc |
+# the License. You may obtain a copy of the License at
|
|
|
79b4cc |
+#
|
|
|
79b4cc |
+# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
79b4cc |
+#
|
|
|
79b4cc |
+# Unless required by applicable law or agreed to in writing, software
|
|
|
79b4cc |
+# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
79b4cc |
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
79b4cc |
+# See the License for the specific language governing permissions and
|
|
|
79b4cc |
+# limitations under the License.
|
|
|
79b4cc |
+
|
|
|
79b4cc |
+chunkedInputFilter.error=No data available due to previous error
|
|
|
79b4cc |
+chunkedInputFilter.eos=Unexpected end of stream while reading request body
|
|
|
79b4cc |
+chunkedInputFilter.eosTrailer=Unexpected end of stream while reading trailer headers
|
|
|
79b4cc |
+chunkedInputFilter.invalidCrlf=Invalid end of line sequence (character other than CR or LF found)
|
|
|
79b4cc |
+chunkedInputFilter.invalidCrlfCRCR=Invalid end of line sequence (CRCR)
|
|
|
79b4cc |
+chunkedInputFilter.invalidCrlfNoCR=Invalid end of line sequence (No CR before LF)
|
|
|
79b4cc |
+chunkedInputFilter.invalidCrlfNoData=Invalid end of line sequence (no data available to read)
|
|
|
79b4cc |
+chunkedInputFilter.invalidHeader=Invalid chunk header
|
|
|
79b4cc |
+chunkedInputFilter.maxExtension=maxExtensionSize exceeded
|
|
|
79b4cc |
+chunkedInputFilter.maxTrailer=maxTrailerSize exceeded
|
|
|
79b4cc |
--- webapps/docs/changelog.xml.orig 2014-06-16 18:41:33.658857000 -0400
|
|
|
79b4cc |
+++ webapps/docs/changelog.xml 2014-06-16 19:37:59.354278000 -0400
|
|
|
79b4cc |
@@ -336,6 +336,12 @@
|
|
|
79b4cc |
<subsection name="Coyote">
|
|
|
79b4cc |
<changelog>
|
|
|
79b4cc |
<fix>
|
|
|
79b4cc |
+ Resolves: CVE-2014-0075. Improve processing of chunk size from
|
|
|
79b4cc |
+ chunked headers. Avoid overflow and use a bit of shift instead
|
|
|
79b4cc |
+ of multiplication as it is marginally faster. (mark/kkolinko)
|
|
|
79b4cc |
+ Patch applied by Red Hat Jun 16 2014
|
|
|
79b4cc |
+ </fix>
|
|
|
79b4cc |
+ <fix>
|
|
|
79b4cc |
<bug>54947</bug>: Fix the HTTP NIO connector that incorrectly rejected a
|
|
|
79b4cc |
request if the CRLF terminating the request line was split across
|
|
|
79b4cc |
multiple packets. Patch by Konstantin Preißer. (markt)
|