[tomcat7] 06/06: Fixed CVE-2014-0230: Add a new limit for the amount of data Tomcat will swallow for an aborted upload

Emmanuel Bourg ebourg-guest at moszumanska.debian.org
Mon Jan 11 10:16:30 UTC 2016


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 <ebourg at apache.org>
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 <ebourg at apache.org>  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



More information about the pkg-java-commits mailing list