This is an automated email from the git hooks/post-receive script. ebourg-guest pushed a commit to branch wheezy in repository tomcat7.
commit 8e55a425b5f15895cfcd6e48589bc9f40078eaf4 Author: Emmanuel Bourg <[email protected]> Date: Fri Jan 8 12:55:06 2016 +0100 Fixed CVE-2014-0230: Add a new limit for the amount of data Tomcat will swallow for an aborted upload --- debian/changelog | 4 + debian/patches/CVE-2014-0230.patch | 406 +++++++++++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 411 insertions(+) diff --git a/debian/changelog b/debian/changelog index 061daa3..4263c4e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -17,6 +17,10 @@ tomcat7 (7.0.28-4+deb7u3) wheezy-security; urgency=high subsequent attempts at reading after an error to fail fast. This prevents remote attackers from conducting HTTP request smuggling attacks or causing a denial of service by streaming data with malformed chunked requests. + * Fixed CVE-2014-0230: Add a new limit for the amount of data Tomcat will + swallow for an aborted upload. This prevents remote attackers from causing + a denial of service (thread consumption) via a series of aborted upload + attempts. -- Emmanuel Bourg <[email protected]> Mon, 04 Jan 2016 12:03:34 +0100 diff --git a/debian/patches/CVE-2014-0230.patch b/debian/patches/CVE-2014-0230.patch new file mode 100644 index 0000000..7eb135b --- /dev/null +++ b/debian/patches/CVE-2014-0230.patch @@ -0,0 +1,406 @@ +Description: CVE-2014-0230: Add a new limit, defaulting to 2MB and configurable via JMX, + for the amount of data Tomcat will swallow for an aborted upload. This prevents remote + attackers from causing a denial of service (thread consumption) via a series of aborted + upload attempts. +Origin: backport, https://svn.apache.org/r1603781 + https://svn.apache.org/r1603811 + https://svn.apache.org/r1609176 + https://svn.apache.org/r1659295 +--- a/java/org/apache/coyote/http11/AbstractHttp11Processor.java ++++ b/java/org/apache/coyote/http11/AbstractHttp11Processor.java +@@ -681,14 +681,15 @@ + /** + * Initialize standard input and output filters. + */ +- protected void initializeFilters(int maxTrailerSize, int maxExtensionSize) { ++ protected void initializeFilters(int maxTrailerSize, int maxExtensionSize, ++ int maxSwallowSize) { + // Create and add the identity filters. +- getInputBuffer().addFilter(new IdentityInputFilter()); ++ getInputBuffer().addFilter(new IdentityInputFilter(maxSwallowSize)); + getOutputBuffer().addFilter(new IdentityOutputFilter()); + + // Create and add the chunked filters. + getInputBuffer().addFilter( +- new ChunkedInputFilter(maxTrailerSize, maxExtensionSize)); ++ new ChunkedInputFilter(maxTrailerSize, maxExtensionSize, maxSwallowSize)); + getOutputBuffer().addFilter(new ChunkedOutputFilter()); + + // Create and add the void filters. +--- a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java ++++ b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java +@@ -163,6 +163,16 @@ + + + /** ++ * Maximum amount of request body to swallow. ++ */ ++ private int maxSwallowSize = 2 * 1024 * 1024; ++ public int getMaxSwallowSize() { return maxSwallowSize; } ++ public void setMaxSwallowSize(int maxSwallowSize) { ++ this.maxSwallowSize = maxSwallowSize; ++ } ++ ++ ++ /** + * This field indicates if the protocol is treated as if it is secure. This + * normally means https is being used but can be used to fake https e.g + * behind a reverse proxy. +--- a/java/org/apache/coyote/http11/Http11AprProcessor.java ++++ b/java/org/apache/coyote/http11/Http11AprProcessor.java +@@ -58,7 +58,7 @@ + + + public Http11AprProcessor(int headerBufferSize, AprEndpoint endpoint, +- int maxTrailerSize, int maxExtensionSize) { ++ int maxTrailerSize, int maxExtensionSize, int maxSwallowSize) { + + super(endpoint); + +@@ -68,7 +68,7 @@ + outputBuffer = new InternalAprOutputBuffer(response, headerBufferSize); + response.setOutputBuffer(outputBuffer); + +- initializeFilters(maxTrailerSize, maxExtensionSize); ++ initializeFilters(maxTrailerSize, maxExtensionSize, maxSwallowSize); + } + + +--- a/java/org/apache/coyote/http11/Http11AprProtocol.java ++++ b/java/org/apache/coyote/http11/Http11AprProtocol.java +@@ -258,7 +258,8 @@ + protected Http11AprProcessor createProcessor() { + Http11AprProcessor processor = new Http11AprProcessor( + proto.getMaxHttpHeaderSize(), (AprEndpoint)proto.endpoint, +- proto.getMaxTrailerSize(), proto.getMaxExtensionSize()); ++ proto.getMaxTrailerSize(), proto.getMaxExtensionSize(), ++ proto.getMaxSwallowSize()); + processor.setAdapter(proto.adapter); + processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests()); + processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); +--- a/java/org/apache/coyote/http11/Http11NioProcessor.java ++++ b/java/org/apache/coyote/http11/Http11NioProcessor.java +@@ -63,7 +63,7 @@ + + + public Http11NioProcessor(int maxHttpHeaderSize, NioEndpoint endpoint, +- int maxTrailerSize, int maxExtensionSize) { ++ int maxTrailerSize, int maxExtensionSize, int maxSwallowSize) { + + super(endpoint); + +@@ -73,7 +73,7 @@ + outputBuffer = new InternalNioOutputBuffer(response, maxHttpHeaderSize); + response.setOutputBuffer(outputBuffer); + +- initializeFilters(maxTrailerSize, maxExtensionSize); ++ initializeFilters(maxTrailerSize, maxExtensionSize, maxSwallowSize); + } + + +--- a/java/org/apache/coyote/http11/Http11NioProtocol.java ++++ b/java/org/apache/coyote/http11/Http11NioProtocol.java +@@ -260,7 +260,8 @@ + public Http11NioProcessor createProcessor() { + Http11NioProcessor processor = new Http11NioProcessor( + proto.getMaxHttpHeaderSize(), (NioEndpoint)proto.endpoint, +- proto.getMaxTrailerSize(), proto.getMaxExtensionSize()); ++ proto.getMaxTrailerSize(), proto.getMaxExtensionSize(), ++ proto.getMaxSwallowSize()); + processor.setAdapter(proto.adapter); + processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests()); + processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); +--- a/java/org/apache/coyote/http11/Http11Processor.java ++++ b/java/org/apache/coyote/http11/Http11Processor.java +@@ -50,7 +50,7 @@ + + + public Http11Processor(int headerBufferSize, JIoEndpoint endpoint, +- int maxTrailerSize, int maxExtensionSize) { ++ int maxTrailerSize, int maxExtensionSize, int maxSwallowSize) { + + super(endpoint); + +@@ -60,7 +60,7 @@ + outputBuffer = new InternalOutputBuffer(response, headerBufferSize); + response.setOutputBuffer(outputBuffer); + +- initializeFilters(maxTrailerSize, maxExtensionSize); ++ initializeFilters(maxTrailerSize, maxExtensionSize, maxSwallowSize); + } + + +--- a/java/org/apache/coyote/http11/Http11Protocol.java ++++ b/java/org/apache/coyote/http11/Http11Protocol.java +@@ -164,7 +164,8 @@ + protected Http11Processor createProcessor() { + Http11Processor processor = new Http11Processor( + proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint, +- proto.getMaxTrailerSize(),proto.getMaxExtensionSize()); ++ proto.getMaxTrailerSize(),proto.getMaxExtensionSize(), ++ proto.getMaxSwallowSize()); + processor.setAdapter(proto.adapter); + processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests()); + processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); +--- a/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java ++++ b/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java +@@ -138,6 +138,9 @@ + */ + private long extensionSize; + ++ private final int maxSwallowSize; ++ ++ + /** + * Flag that indicates if an error has occurred. + */ +@@ -145,10 +148,11 @@ + + + // ----------------------------------------------------------- Constructors +- public ChunkedInputFilter(int maxTrailerSize, int maxExtensionSize) { ++ public ChunkedInputFilter(int maxTrailerSize, int maxExtensionSize, int maxSwallowSize) { + this.trailingHeaders.setLimit(maxTrailerSize); + this.maxExtensionSize = maxExtensionSize; + this.maxTrailerSize = maxTrailerSize; ++ this.maxSwallowSize = maxSwallowSize; + } + + // ---------------------------------------------------- InputBuffer Methods +@@ -239,9 +243,14 @@ + public long end() + throws IOException { + ++ long swallowed = 0; ++ int read = 0; + // Consume extra bytes : parse the stream until the end chunk is found +- while (doRead(readChunk, null) >= 0) { +- // NOOP: Just consume the input ++ while ((read = doRead(readChunk, null)) >= 0) { ++ swallowed += read; ++ if (maxSwallowSize > -1 && swallowed > maxSwallowSize) { ++ throwIOException(sm.getString("inputFilter.maxSwallow")); ++ } + } + + // Return the number of extra bytes which were consumed +--- a/java/org/apache/coyote/http11/filters/IdentityInputFilter.java ++++ b/java/org/apache/coyote/http11/filters/IdentityInputFilter.java +@@ -24,6 +24,7 @@ + import org.apache.coyote.Request; + import org.apache.coyote.http11.InputFilter; + import org.apache.tomcat.util.buf.ByteChunk; ++import org.apache.tomcat.util.res.StringManager; + + /** + * Identity input filter. +@@ -32,6 +33,9 @@ + */ + public class IdentityInputFilter implements InputFilter { + ++ private static final StringManager sm = StringManager.getManager( ++ IdentityInputFilter.class.getPackage().getName()); ++ + + // -------------------------------------------------------------- Constants + +@@ -75,6 +79,7 @@ + */ + protected ByteChunk endChunk = new ByteChunk(); + ++ private final int maxSwallowSize; + + // ------------------------------------------------------------- Properties + +@@ -95,6 +100,13 @@ + } + + ++ // ------------------------------------------------------------ Constructor ++ ++ public IdentityInputFilter(int maxSwallowSize) { ++ this.maxSwallowSize = maxSwallowSize; ++ } ++ ++ + // ---------------------------------------------------- InputBuffer Methods + + +@@ -160,11 +172,21 @@ + public long end() + throws IOException { + ++ final boolean maxSwallowSizeExceeded = (maxSwallowSize > -1 && remaining > maxSwallowSize); ++ long swallowed = 0; ++ + // Consume extra bytes. + while (remaining > 0) { + int nread = buffer.doRead(endChunk, null); + if (nread > 0 ) { ++ swallowed += nread; + remaining = remaining - nread; ++ if (maxSwallowSizeExceeded && swallowed > maxSwallowSize) { ++ // Note: We do not fail early so the client has a chance to ++ // read the response before the connection is closed. See: ++ // http://httpd.apache.org/docs/2.0/misc/fin_wait_2.html#appendix ++ throw new IOException(sm.getString("inputFilter.maxSwallow")); ++ } + } else { // errors are handled higher up. + remaining = 0; + } +--- a/java/org/apache/coyote/http11/filters/LocalStrings.properties ++++ b/java/org/apache/coyote/http11/filters/LocalStrings.properties +@@ -22,4 +22,6 @@ + chunkedInputFilter.invalidCrlfNoData=Invalid end of line sequence (no data available to read) + chunkedInputFilter.invalidHeader=Invalid chunk header + chunkedInputFilter.maxExtension=maxExtensionSize exceeded +-chunkedInputFilter.maxTrailer=maxTrailerSize exceeded +\ No newline at end of file ++chunkedInputFilter.maxTrailer=maxTrailerSize exceeded ++ ++inputFilter.maxSwallow=maxSwallowSize exceeded +\ No newline at end of file +--- a/test/org/apache/catalina/core/TestSwallowAbortedUploads.java ++++ b/test/org/apache/catalina/core/TestSwallowAbortedUploads.java +@@ -16,8 +16,13 @@ + */ + package org.apache.catalina.core; + ++import java.io.BufferedReader; + import java.io.IOException; ++import java.io.InputStreamReader; ++import java.io.OutputStreamWriter; + import java.io.PrintWriter; ++import java.io.Writer; ++import java.net.Socket; + import java.util.Arrays; + import java.util.Collection; + +@@ -32,6 +37,7 @@ + import static org.junit.Assert.assertNull; + import static org.junit.Assert.assertTrue; + ++import org.junit.Assert; + import org.junit.Test; + + import org.apache.catalina.Context; +@@ -113,7 +119,7 @@ + Exception ex = doAbortedUploadTest(client, true, true); + assertNull("Limited upload with swallow enabled generates client exception", + ex); +- assertTrue("Limited upload with swallow enabled returns error status code", ++ assertTrue("Limited upload with swallow enabled returns non-500 status code", + client.isResponse500()); + client.reset(); + } +@@ -410,4 +416,78 @@ + } + } + ++ ++ @Test ++ public void testChunkedPUTLimit() throws Exception { ++ doTestChunkedPUT(true); ++ } ++ ++ ++ @Test ++ public void testChunkedPUTNoLimit() throws Exception { ++ doTestChunkedPUT(false); ++ } ++ ++ ++ public void doTestChunkedPUT(boolean limit) throws Exception { ++ ++ Tomcat tomcat = getTomcatInstance(); ++ tomcat.addContext("", TEMP_DIR); ++ // No need for target to exist. ++ ++ if (!limit) { ++ tomcat.getConnector().setAttribute("maxSwallowSize", "-1"); ++ } ++ ++ tomcat.start(); ++ ++ Exception writeEx = null; ++ Exception readEx = null; ++ String responseLine = null; ++ Socket conn = null; ++ ++ try { ++ conn = new Socket("localhost", getPort()); ++ Writer writer = new OutputStreamWriter( ++ conn.getOutputStream(), "US-ASCII"); ++ writer.write("PUT /does-not-exist HTTP/1.1\r\n"); ++ writer.write("Host: any\r\n"); ++ writer.write("Transfer-encoding: chunked\r\n"); ++ writer.write("\r\n"); ++ ++ // Smarter than the typical client. Attempts to read the response ++ // even if the request is not fully written. ++ try { ++ // Write (or try to write) 16MB ++ for (int i = 0; i < 1024 * 1024; i++) { ++ writer.write("10\r\n"); ++ writer.write("0123456789ABCDEF\r\n"); ++ } ++ } catch (Exception e) { ++ writeEx = e; ++ } ++ ++ try { ++ BufferedReader reader = new BufferedReader(new InputStreamReader( ++ conn.getInputStream(), "US-ASCII")); ++ ++ responseLine = reader.readLine(); ++ } catch (IOException e) { ++ readEx = e; ++ } ++ } finally { ++ if (conn != null) { ++ conn.close(); ++ } ++ } ++ ++ if (limit) { ++ Assert.assertNotNull(writeEx); ++ } else { ++ Assert.assertNull(writeEx); ++ Assert.assertNull(readEx); ++ Assert.assertNotNull(responseLine); ++ Assert.assertTrue(responseLine.contains("404")); ++ } ++ } + } +--- a/webapps/docs/config/http.xml ++++ b/webapps/docs/config/http.xml +@@ -418,6 +418,16 @@ + If not specified, this attribute is set to 100.</p> + </attribute> + ++ <attribute name="maxSwallowSize" required="false"> ++ <p>The maximum number of request body bytes (excluding transfer encoding ++ overhead) that will be swallowed by Tomcat for an aborted upload. An ++ aborted upload is when Tomcat knows that the request body is going to be ++ ignored but the client still sends it. If Tomcat does not swallow the body ++ the client is unlikely to see the response. If not specified the default ++ of 2097152 (2 megabytes) will be used. A value of less than zero indicates ++ that no limit should be enforced.</p> ++ </attribute> ++ + <attribute name="maxThreads" required="false"> + <p>The maximum number of request processing threads to be created + by this <strong>Connector</strong>, which therefore determines the +--- a/java/org/apache/catalina/connector/mbeans-descriptors.xml ++++ b/java/org/apache/catalina/connector/mbeans-descriptors.xml +@@ -97,6 +97,10 @@ + description="Maximum size of a POST which will be saved by the container during authentication" + type="int"/> + ++ <attribute name="maxSwallowSize" ++ description="The maximum number of request body bytes to be swallowed by Tomcat for an aborted upload" ++ type="int"/> ++ + <!-- Common --> + <attribute name="maxThreads" + description="The maximum number of request processing threads to be created" diff --git a/debian/patches/series b/debian/patches/series index 9a2673f..16f918d 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -26,3 +26,4 @@ CVE-2014-0099.patch CVE-2013-4444.patch CVE-2014-0075.patch CVE-2014-0227.patch +CVE-2014-0230.patch -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/tomcat7.git _______________________________________________ pkg-java-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-commits

