Blame SOURCES/tomcat-7.0.42-CVE-2013-4322.patch

1af9a1
--- java/org/apache/coyote/http11/AbstractHttp11Processor.java.orig	2014-03-17 16:00:40.592415000 -0400
1af9a1
+++ java/org/apache/coyote/http11/AbstractHttp11Processor.java	2014-03-18 13:39:06.789696000 -0400
1af9a1
@@ -684,13 +684,14 @@
1af9a1
     /**
1af9a1
      * Initialize standard input and output filters.
1af9a1
      */
1af9a1
-    protected void initializeFilters(int maxTrailerSize) {
1af9a1
+    protected void initializeFilters(int maxTrailerSize, int maxExtensionSize) {
1af9a1
         // Create and add the identity filters.
1af9a1
         getInputBuffer().addFilter(new IdentityInputFilter());
1af9a1
         getOutputBuffer().addFilter(new IdentityOutputFilter());
1af9a1
 
1af9a1
         // Create and add the chunked filters.
1af9a1
-        getInputBuffer().addFilter(new ChunkedInputFilter(maxTrailerSize));
1af9a1
+        getInputBuffer().addFilter(
1af9a1
+                new ChunkedInputFilter(maxTrailerSize, maxExtensionSize));
1af9a1
         getOutputBuffer().addFilter(new ChunkedOutputFilter());
1af9a1
 
1af9a1
         // Create and add the void filters.
1af9a1
--- java/org/apache/coyote/http11/AbstractHttp11Protocol.java.orig	2014-03-17 16:00:57.458467000 -0400
1af9a1
+++ java/org/apache/coyote/http11/AbstractHttp11Protocol.java	2014-03-17 16:40:11.035409000 -0400
1af9a1
@@ -151,7 +151,15 @@
1af9a1
         this.maxTrailerSize = maxTrailerSize;
1af9a1
     }
1af9a1
 
1af9a1
-
1af9a1
+     /**
1af9a1
+      * Maximum size of extension information in chunked encoding
1af9a1
+      */
1af9a1
+     private int maxExtensionSize = 8192;
1af9a1
+     public int getMaxExtensionSize() { return maxExtensionSize; }
1af9a1
+     public void setMaxExtensionSize(int maxExtensionSize) {
1af9a1
+         this.maxExtensionSize = maxExtensionSize;
1af9a1
+     }
1af9a1
+ 
1af9a1
     /**
1af9a1
      * This field indicates if the protocol is treated as if it is secure. This
1af9a1
      * normally means https is being used but can be used to fake https e.g
1af9a1
--- java/org/apache/coyote/http11/Http11AprProcessor.java.orig	2014-03-17 16:01:22.889559000 -0400
1af9a1
+++ java/org/apache/coyote/http11/Http11AprProcessor.java	2014-03-17 16:43:14.716027000 -0400
1af9a1
@@ -58,7 +58,7 @@
1af9a1
 
1af9a1
 
1af9a1
     public Http11AprProcessor(int headerBufferSize, AprEndpoint endpoint,
1af9a1
-            int maxTrailerSize) {
1af9a1
+            int maxTrailerSize, int maxExtensionSize) {
1af9a1
 
1af9a1
         super(endpoint);
1af9a1
 
1af9a1
@@ -68,7 +68,7 @@
1af9a1
         outputBuffer = new InternalAprOutputBuffer(response, headerBufferSize);
1af9a1
         response.setOutputBuffer(outputBuffer);
1af9a1
 
1af9a1
-        initializeFilters(maxTrailerSize);
1af9a1
+        initializeFilters(maxTrailerSize, maxExtensionSize);
1af9a1
     }
1af9a1
 
1af9a1
 
1af9a1
--- java/org/apache/coyote/http11/Http11AprProtocol.java.orig	2014-03-17 16:10:16.268358000 -0400
1af9a1
+++ java/org/apache/coyote/http11/Http11AprProtocol.java	2014-03-17 16:50:17.428466000 -0400
1af9a1
@@ -294,7 +294,7 @@
1af9a1
         protected Http11AprProcessor createProcessor() {
1af9a1
             Http11AprProcessor processor = new Http11AprProcessor(
1af9a1
                     proto.getMaxHttpHeaderSize(), (AprEndpoint)proto.endpoint,
1af9a1
-                    proto.getMaxTrailerSize());
1af9a1
+                    proto.getMaxTrailerSize(), proto.getMaxExtensionSize());
1af9a1
             processor.setAdapter(proto.adapter);
1af9a1
             processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
1af9a1
             processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
1af9a1
--- java/org/apache/coyote/http11/Http11NioProcessor.java.orig	2014-03-17 16:02:20.016748000 -0400
1af9a1
+++ java/org/apache/coyote/http11/Http11NioProcessor.java	2014-03-17 16:51:55.623782000 -0400
1af9a1
@@ -63,7 +63,7 @@
1af9a1
 
1af9a1
 
1af9a1
     public Http11NioProcessor(int maxHttpHeaderSize, NioEndpoint endpoint,
1af9a1
-            int maxTrailerSize) {
1af9a1
+            int maxTrailerSize, int maxExtensionSize) {
1af9a1
 
1af9a1
         super(endpoint);
1af9a1
 
1af9a1
@@ -73,7 +73,7 @@
1af9a1
         outputBuffer = new InternalNioOutputBuffer(response, maxHttpHeaderSize);
1af9a1
         response.setOutputBuffer(outputBuffer);
1af9a1
 
1af9a1
-        initializeFilters(maxTrailerSize);
1af9a1
+        initializeFilters(maxTrailerSize, maxExtensionSize);
1af9a1
     }
1af9a1
 
1af9a1
 
1af9a1
--- java/org/apache/coyote/http11/Http11NioProtocol.java.orig	2014-03-17 16:07:26.027787000 -0400
1af9a1
+++ java/org/apache/coyote/http11/Http11NioProtocol.java	2014-03-17 16:53:09.198025000 -0400
1af9a1
@@ -260,7 +260,7 @@
1af9a1
         public Http11NioProcessor createProcessor() {
1af9a1
             Http11NioProcessor processor = new Http11NioProcessor(
1af9a1
                     proto.getMaxHttpHeaderSize(), (NioEndpoint)proto.endpoint,
1af9a1
-                    proto.getMaxTrailerSize());
1af9a1
+                    proto.getMaxTrailerSize(), proto.getMaxExtensionSize());
1af9a1
             processor.setAdapter(proto.adapter);
1af9a1
             processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
1af9a1
             processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
1af9a1
--- java/org/apache/coyote/http11/Http11Processor.java.orig	2014-03-17 16:07:45.099837000 -0400
1af9a1
+++ java/org/apache/coyote/http11/Http11Processor.java	2014-03-18 12:42:34.018260000 -0400
1af9a1
@@ -50,7 +50,7 @@
1af9a1
 
1af9a1
 
1af9a1
     public Http11Processor(int headerBufferSize, JIoEndpoint endpoint,
1af9a1
-            int maxTrailerSize) {
1af9a1
+            int maxTrailerSize, int maxExtensionSize) {
1af9a1
 
1af9a1
         super(endpoint);
1af9a1
         
1af9a1
@@ -60,7 +60,7 @@
1af9a1
         outputBuffer = new InternalOutputBuffer(response, headerBufferSize);
1af9a1
         response.setOutputBuffer(outputBuffer);
1af9a1
 
1af9a1
-        initializeFilters(maxTrailerSize);
1af9a1
+        initializeFilters(maxTrailerSize, maxExtensionSize);
1af9a1
     }
1af9a1
 
1af9a1
 
1af9a1
--- java/org/apache/coyote/http11/Http11Protocol.java.orig	2014-03-17 16:08:00.058113000 -0400
1af9a1
+++ java/org/apache/coyote/http11/Http11Protocol.java	2014-03-17 16:56:04.194609000 -0400
1af9a1
@@ -164,7 +164,7 @@
1af9a1
         protected Http11Processor createProcessor() {
1af9a1
             Http11Processor processor = new Http11Processor(
1af9a1
                     proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint,
1af9a1
-                    proto.getMaxTrailerSize());
1af9a1
+                    proto.getMaxTrailerSize(), proto.getMaxExtensionSize());
1af9a1
             processor.setAdapter(proto.adapter);
1af9a1
             processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
1af9a1
             processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
1af9a1
--- java/org/apache/coyote/http11/filters/ChunkedInputFilter.java.orig	2014-03-17 16:08:12.213985000 -0400
73015d
+++ java/org/apache/coyote/http11/filters/ChunkedInputFilter.java	2014-04-15 19:51:43.729201000 -0400
73015d
@@ -39,6 +39,8 @@
73015d
  */
73015d
 public class ChunkedInputFilter implements InputFilter {
73015d
 
73015d
+    private static final org.apache.juli.logging.Log log
73015d
+        = org.apache.juli.logging.LogFactory.getLog(ChunkedInputFilter.class);
73015d
 
73015d
     // -------------------------------------------------------------- Constants
73015d
 
73015d
@@ -118,9 +120,29 @@
1af9a1
      */
1af9a1
     private Request request;
1af9a1
     
1af9a1
+
1af9a1
+    /**
1af9a1
+     * Limit for extension size.
1af9a1
+     */
1af9a1
+    private final long maxExtensionSize;
1af9a1
+
1af9a1
+
1af9a1
+     /**
1af9a1
+     * Limit for trailer size.
1af9a1
+     */
1af9a1
+     private int maxTrailerSize;
1af9a1
+
1af9a1
+
1af9a1
+    /**
1af9a1
+     * Size of extensions processed for this request.
1af9a1
+     */
1af9a1
+    private long extensionSize;
1af9a1
+
1af9a1
     // ----------------------------------------------------------- Constructors
1af9a1
-    public ChunkedInputFilter(int maxTrailerSize) {
1af9a1
+    public ChunkedInputFilter(int maxTrailerSize, int maxExtensionSize) {
1af9a1
         this.trailingHeaders.setLimit(maxTrailerSize);
1af9a1
+        this.maxTrailerSize = maxTrailerSize;
1af9a1
+        this.maxExtensionSize = maxExtensionSize;
1af9a1
     }
1af9a1
 
1af9a1
     // ---------------------------------------------------- InputBuffer Methods
73015d
@@ -250,6 +272,8 @@
1af9a1
         endChunk = false;
1af9a1
         needCRLFParse = false;
1af9a1
         trailingHeaders.recycle();
1af9a1
+        trailingHeaders.setLimit(maxTrailerSize);
1af9a1
+        extensionSize = 0;
1af9a1
     }
1af9a1
 
1af9a1
 
73015d
@@ -299,7 +323,7 @@
1af9a1
         int result = 0;
1af9a1
         boolean eol = false;
1af9a1
         boolean readDigit = false;
1af9a1
-        boolean trailer = false;
1af9a1
+        boolean extension = false;
1af9a1
 
1af9a1
         while (!eol) {
1af9a1
 
73015d
@@ -309,11 +333,17 @@
73015d
             }
73015d
 
1af9a1
             if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) {
73015d
+                log.info("EOL is true");
1af9a1
                 parseCRLF(false);
1af9a1
                 eol = true;
1af9a1
-            } else if (buf[pos] == Constants.SEMI_COLON) {
1af9a1
-                trailer = true;
1af9a1
-            } else if (!trailer) { 
1af9a1
+            } else if (buf[pos] == Constants.SEMI_COLON && !extension) {
1af9a1
+                // First semi-colon marks the start of the extension. Further
1af9a1
+                // semi-colons may appear to separate multiple chunk-extensions.
1af9a1
+                // These need to be processed as part of parsing the extensions.
1af9a1
+                extension = true;
1af9a1
+                extensionSize++;
73015d
+                log.info("SEMI_COLON");
1af9a1
+            } else if (!extension) {
1af9a1
                 //don't read data after the trailer
1af9a1
                 int charValue = HexUtils.getDec(buf[pos]);
1af9a1
                 if (charValue != -1) {
73015d
@@ -323,15 +353,22 @@
73015d
                 } else {
73015d
                     //we shouldn't allow invalid, non hex characters
1af9a1
                     //in the chunked header
73015d
+                    log.info("Returning false");
1af9a1
                     return false;
1af9a1
                 }
1af9a1
-            }
1af9a1
-
1af9a1
+            } else {
1af9a1
+            // Extension 'parsing'
1af9a1
+            // Note that the chunk-extension is neither parsed nor
1af9a1
+            // validated. Currently it is simply ignored.
1af9a1
+                extensionSize++;
1af9a1
+                if (maxExtensionSize > -1 && extensionSize > maxExtensionSize) {
1af9a1
+                    throw new IOException("maxExtensionSize exceeded");
1af9a1
+                }
1af9a1
+           }
73015d
             // Parsing the CRLF increments pos
73015d
             if (!eol) {
73015d
                 pos++;
73015d
             }
73015d
-
1af9a1
         }
1af9a1
 
1af9a1
         if (!readDigit)
73015d
@@ -489,12 +526,17 @@
1af9a1
                 chr = buf[pos];
1af9a1
                 if ((chr == Constants.SP) || (chr == Constants.HT)) {
1af9a1
                     pos++;
1af9a1
+                    // If we swallow whitespace, make sure it counts towards the
1af9a1
+                    // limit placed on trailing header size
1af9a1
+                    int newlimit = trailingHeaders.getLimit() -1;
1af9a1
+                    if (trailingHeaders.getEnd() > newlimit) {
1af9a1
+                        throw new IOException("Exceeded maxTrailerSize");
1af9a1
+                    }
1af9a1
+                    trailingHeaders.setLimit(newlimit);
1af9a1
                 } else {
1af9a1
                     space = false;
1af9a1
                 }
1af9a1
-    
1af9a1
             }
1af9a1
-    
1af9a1
             // Reading bytes until the end of the line
1af9a1
             while (!eol) {
1af9a1
     
1af9a1
--- test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java.orig	2014-03-17 16:08:33.031999000 -0400
1af9a1
+++ test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java	2014-03-17 17:40:23.853592000 -0400
1af9a1
@@ -41,6 +41,7 @@
1af9a1
 public class TestChunkedInputFilter extends TomcatBaseTest {
1af9a1
 
1af9a1
     private static final String LF = "\n";
1af9a1
+    private static final int EXT_SIZE_LIMIT = 10;
1af9a1
 
1af9a1
     @Test
1af9a1
     public void testChunkHeaderCRLF() throws Exception {
1af9a1
@@ -202,6 +203,79 @@
1af9a1
         assertTrue(client.isResponse500());
1af9a1
     }
1af9a1
 
1af9a1
+
1af9a1
+    @Test
1af9a1
+    public void testExtensionSizeLimitOneBelow() throws Exception {
1af9a1
+        doTestExtensionSizeLimit(EXT_SIZE_LIMIT - 1, true);
1af9a1
+    }
1af9a1
+
1af9a1
+
1af9a1
+    @Test
1af9a1
+    public void testExtensionSizeLimitExact() throws Exception {
1af9a1
+        doTestExtensionSizeLimit(EXT_SIZE_LIMIT, true);
1af9a1
+    }
1af9a1
+
1af9a1
+
1af9a1
+    @Test
1af9a1
+    public void testExtensionSizeLimitOneOver() throws Exception {
1af9a1
+        doTestExtensionSizeLimit(EXT_SIZE_LIMIT + 1, false);
1af9a1
+    }
1af9a1
+
1af9a1
+    private void doTestExtensionSizeLimit(int len, boolean ok) 
1af9a1
+        throws Exception {
1af9a1
+        // Setup Tomcat instance
1af9a1
+        Tomcat tomcat = getTomcatInstance();
1af9a1
+
1af9a1
+        tomcat.getConnector().setProperty(
1af9a1
+                "maxExtensionSize", Integer.toString(EXT_SIZE_LIMIT));
1af9a1
+
1af9a1
+        // Must have a real docBase - just use temp
1af9a1
+        Context ctx =
1af9a1
+            tomcat.addContext("", System.getProperty("java.io.tmpdir"));
1af9a1
+
1af9a1
+        Tomcat.addServlet(ctx, "servlet", new EchoHeaderServlet());
1af9a1
+        ctx.addServletMapping("/", "servlet");
1af9a1
+
1af9a1
+        tomcat.start();
1af9a1
+
1af9a1
+        String extName = ";foo=";
1af9a1
+        StringBuilder extValue = new StringBuilder(len);
1af9a1
+        for (int i = 0; i < (len - extName.length()); i++) {
1af9a1
+            extValue.append("x");
1af9a1
+        }
1af9a1
+
1af9a1
+        String[] request = new String[]{
1af9a1
+            "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF +
1af9a1
+            "Host: any" + SimpleHttpClient.CRLF +
1af9a1
+            "Transfer-encoding: chunked" + SimpleHttpClient.CRLF +
1af9a1
+            "Content-Type: application/x-www-form-urlencoded" +
1af9a1
+                    SimpleHttpClient.CRLF +
1af9a1
+            "Connection: close" + SimpleHttpClient.CRLF +
1af9a1
+            SimpleHttpClient.CRLF +
1af9a1
+            "3" + extName + extValue.toString() + SimpleHttpClient.CRLF +
1af9a1
+            "a=0" + SimpleHttpClient.CRLF +
1af9a1
+            "4" + SimpleHttpClient.CRLF +
1af9a1
+            "&b=1" + SimpleHttpClient.CRLF +
1af9a1
+            "0" + SimpleHttpClient.CRLF +
1af9a1
+            SimpleHttpClient.CRLF };
1af9a1
+
1af9a1
+        TrailerClient client =
1af9a1
+            new TrailerClient(tomcat.getConnector().getLocalPort());
1af9a1
+        client.setRequest(request);
1af9a1
+
1af9a1
+        client.connect();
1af9a1
+        client.processRequest();
1af9a1
+
1af9a1
+        if (ok) {
1af9a1
+           assertTrue(client.isResponse200());
1af9a1
+        } else {
1af9a1
+           assertTrue(client.isResponse500());
1af9a1
+        }
1af9a1
+    }
1af9a1
+
1af9a1
+    
1af9a1
+
1af9a1
+
1af9a1
     @Test
1af9a1
     public void testNoTrailingHeaders() throws Exception {
1af9a1
         // Setup Tomcat instance
1af9a1
--- webapps/docs/changelog.xml.orig	2014-03-17 16:08:46.095050000 -0400
1af9a1
+++ webapps/docs/changelog.xml	2014-03-17 17:44:14.163385000 -0400
1af9a1
@@ -394,6 +394,11 @@
1af9a1
 <section name="Tomcat 7.0.40 (markt)" rtext="2013-05-09">
1af9a1
   <subsection name="Catalina">
1af9a1
     <changelog>
1af9a1
+      <fix>
1af9a1
+        Add support for limiting the size of chunk extensions when using chunked
1af9a1
+        encoding. (markt)
1af9a1
+        CVE-2013-4322 patch applied by Red Hat.
1af9a1
+      </fix>
1af9a1
       <update>
1af9a1
         Update Tomcat's internal copy of Commons FileUpload to FileUpload 1.3.
1af9a1
         (markt)
1af9a1
--- webapps/docs/config/http.xml.orig	2014-03-17 16:08:59.013101000 -0400
1af9a1
+++ webapps/docs/config/http.xml	2014-03-17 18:10:13.965639000 -0400
1af9a1
@@ -399,6 +399,12 @@
1af9a1
       and connections are not counted.

1af9a1
     </attribute>
1af9a1
 
1af9a1
+    <attribute name="maxExtensionSize" required="false">
1af9a1
+      

Limits the total length of chunk extensions in chunked HTTP requests.

1af9a1
+      If the value is -1, no limit will be imposed. If not
1af9a1
+      specified, the default value of 8192 will be used.

1af9a1
+    </attribute>
1af9a1
+
1af9a1
     <attribute name="maxHttpHeaderSize" required="false">
1af9a1
       

The maximum size of the request and response HTTP header, specified

1af9a1
       in bytes. If not specified, this attribute is set to 8192 (8 KB).