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

1af9a1
--- java/org/apache/coyote/ajp/AbstractAjpProcessor.java.orig	2014-03-14 17:13:46.228345000 -0400
1af9a1
+++ java/org/apache/coyote/ajp/AbstractAjpProcessor.java	2014-03-18 13:54:13.570758000 -0400
1af9a1
@@ -25,6 +25,8 @@
1af9a1
 import java.security.cert.X509Certificate;
1af9a1
 import java.util.concurrent.atomic.AtomicBoolean;
1af9a1
 
1af9a1
+import javax.servlet.http.HttpServletResponse;
1af9a1
+
1af9a1
 import org.apache.coyote.AbstractProcessor;
1af9a1
 import org.apache.coyote.ActionCode;
1af9a1
 import org.apache.coyote.AsyncContextCallback;
1af9a1
@@ -651,7 +653,7 @@
1af9a1
 
1af9a1
         // Set this every time in case limit has been changed via JMX
1af9a1
         headers.setLimit(endpoint.getMaxHeaderCount());
1af9a1
-
1af9a1
+        boolean contentLengthSet = false;
1af9a1
         int hCount = requestHeaderMessage.getInt();
1af9a1
         for(int i = 0 ; i < hCount ; i++) {
1af9a1
             String hName = null;
1af9a1
@@ -686,10 +688,15 @@
1af9a1
 
1af9a1
             if (hId == Constants.SC_REQ_CONTENT_LENGTH ||
1af9a1
                     (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
1af9a1
-                // just read the content-length header, so set it
1af9a1
                 long cl = vMB.getLong();
1af9a1
-                if(cl < Integer.MAX_VALUE)
1af9a1
-                    request.setContentLength( (int)cl );
1af9a1
+                if (contentLengthSet) {
1af9a1
+                    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
1af9a1
+                    error = true;
1af9a1
+                } else {
1af9a1
+                    contentLengthSet = true;
1af9a1
+                    // Set the content-length header for the request
1af9a1
+                   request.setContentLength((int)cl);
1af9a1
+                }
1af9a1
             } else if (hId == Constants.SC_REQ_CONTENT_TYPE ||
1af9a1
                     (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
1af9a1
                 // just read the content-type header, so set it
1af9a1
--- java/org/apache/coyote/http11/AbstractHttp11Processor.java.orig	2014-03-14 17:13:46.514347000 -0400
1af9a1
+++ java/org/apache/coyote/http11/AbstractHttp11Processor.java	2014-03-14 17:13:46.353345000 -0400
1af9a1
@@ -1277,10 +1277,20 @@
1af9a1
 
1af9a1
         // Parse content-length header
1af9a1
         long contentLength = request.getContentLengthLong();
1af9a1
-        if (contentLength >= 0 && !contentDelimitation) {
1af9a1
-            getInputBuffer().addActiveFilter
1af9a1
-                (inputFilters[Constants.IDENTITY_FILTER]);
1af9a1
-            contentDelimitation = true;
1af9a1
+        if (contentLength >= 0) {
1af9a1
+            if (contentDelimitation) {
1af9a1
+                // contentDelimitation being true at this point indicates that
1af9a1
+                // chunked encoding is being used but chunked encoding should
1af9a1
+                // not be used with a content length. RFC 2616, section 4.4,
1af9a1
+                // bullet 3 states Content-Length must be ignored in this case -
1af9a1
+                // so remove it.
1af9a1
+                headers.removeHeader("content-length");
1af9a1
+                request.setContentLength(-1);
1af9a1
+            } else {
1af9a1
+                getInputBuffer().addActiveFilter
1af9a1
+                        (inputFilters[Constants.IDENTITY_FILTER]);
1af9a1
+               contentDelimitation = true;
1af9a1
+           }
1af9a1
         }
1af9a1
 
1af9a1
         MessageBytes valueMB = headers.getValue("host");
1af9a1
--- test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java.orig	2014-03-14 17:13:52.878367000 -0400
1af9a1
+++ test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java	2014-03-14 17:21:43.278956000 -0400
1af9a1
@@ -90,6 +90,61 @@
1af9a1
         ajpClient.disconnect();
1af9a1
     }
1af9a1
 
1af9a1
+    @Test
1af9a1
+    public void testPost() throws Exception {
1af9a1
+        doTestPost(false, HttpServletResponse.SC_OK);
1af9a1
+    }
1af9a1
+
1af9a1
+    public void testPostMultipleContentLength() throws Exception {
1af9a1
+        // Multiple content lengths
1af9a1
+        doTestPost(true, HttpServletResponse.SC_BAD_REQUEST);
1af9a1
+    }
1af9a1
+
1af9a1
+
1af9a1
+    public void doTestPost(boolean multipleCL, int expectedStatus) 
1af9a1
+        throws Exception {
1af9a1
+        Tomcat tomcat = getTomcatInstance();
1af9a1
+       // Use the normal Tomcat ROOT context
1af9a1
+       File root = new File("test/webapp-3.0");
1af9a1
+       tomcat.addWebapp("", root.getAbsolutePath());
1af9a1
+       tomcat.start();
1af9a1
+       SimpleAjpClient ajpClient = new SimpleAjpClient();
1af9a1
+       ajpClient.setPort(getPort());
1af9a1
+       ajpClient.connect();
1af9a1
+
1af9a1
+       validateCpong(ajpClient.cping());
1af9a1
+
1af9a1
+       TesterAjpMessage forwardMessage =
1af9a1
+                   ajpClient.createForwardMessage("/echo-params.jsp", 4);
1af9a1
+       forwardMessage.addHeader(0xA008, "9");
1af9a1
+       if (multipleCL) {
1af9a1
+          forwardMessage.addHeader(0xA008, "99");
1af9a1
+       }
1af9a1
+       forwardMessage.addHeader(0xA007, "application/x-www-form-urlencoded");
1af9a1
+       forwardMessage.end();
1af9a1
+       TesterAjpMessage bodyMessage =
1af9a1
+                   ajpClient.createBodyMessage("test=data".getBytes());
1af9a1
+
1af9a1
+       TesterAjpMessage responseHeaders =
1af9a1
+                   ajpClient.sendMessage(forwardMessage, bodyMessage);
1af9a1
+
1af9a1
+       validateResponseHeaders(responseHeaders, expectedStatus);
1af9a1
+       if (expectedStatus == HttpServletResponse.SC_OK) {
1af9a1
+           // Expect 3 messages: headers, body, end for a valid request
1af9a1
+           TesterAjpMessage responseBody = ajpClient.readMessage();
1af9a1
+           validateResponseBody(responseBody, "test - data");
1af9a1
+           validateResponseEnd(ajpClient.readMessage(), true);
1af9a1
+           // Double check the connection is still open
1af9a1
+           validateCpong(ajpClient.cping());
1af9a1
+       } else {
1af9a1
+           // Expect 2 messages: headers, end for an invalid request
1af9a1
+           validateResponseEnd(ajpClient.readMessage(), false);
1af9a1
+       }
1af9a1
+
1af9a1
+       ajpClient.disconnect();
1af9a1
+                                                                                    }
1af9a1
+
1af9a1
+
1af9a1
     /**
1af9a1
      * Process response header packet and checks the status. Any other data is
1af9a1
      * ignored.
1af9a1
--- test/org/apache/coyote/http11/TestAbstractHttp11Processor.java.orig	2014-03-14 17:13:52.946367000 -0400
1af9a1
+++ test/org/apache/coyote/http11/TestAbstractHttp11Processor.java	2014-03-14 17:13:52.925368000 -0400
1af9a1
@@ -87,7 +87,7 @@
1af9a1
             "Transfer-encoding: buffered" + SimpleHttpClient.CRLF +
1af9a1
             "Content-Length: 9" + SimpleHttpClient.CRLF +
1af9a1
             "Content-Type: application/x-www-form-urlencoded" +
1af9a1
-                    SimpleHttpClient.CRLF +
1af9a1
+            SimpleHttpClient.CRLF +
1af9a1
             SimpleHttpClient.CRLF +
1af9a1
             "test=data";
1af9a1
 
1af9a1
@@ -99,6 +99,54 @@
1af9a1
         assertTrue(client.isResponse501());
1af9a1
     }
1af9a1
 
1af9a1
+     @Test
1af9a1
+     public void testWithTEChunked() throws Exception {
1af9a1
+         doTestWithTEChunked(false);
1af9a1
+     }
1af9a1
+     
1af9a1
+     @Test
1af9a1
+     public void testWithTEChunkedWithCL() throws Exception {
1af9a1
+        // Should be ignored
1af9a1
+        doTestWithTEChunked(true);
1af9a1
+     }
1af9a1
+     
1af9a1
+     private void doTestWithTEChunked(boolean withCL)
1af9a1
+             throws Exception {
1af9a1
+     
1af9a1
+        Tomcat tomcat = getTomcatInstance();
1af9a1
+    
1af9a1
+        // Use the normal Tomcat ROOT context
1af9a1
+        File root = new File("test/webapp-3.0");
1af9a1
+        tomcat.addWebapp("", root.getAbsolutePath());
1af9a1
+        
1af9a1
+        tomcat.start();
1af9a1
+        
1af9a1
+        String request =
1af9a1
+                 "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF +
1af9a1
+                 "Host: any" + SimpleHttpClient.CRLF +
1af9a1
+                 (withCL ? "Content-length: 1" + 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
+                 "9" + SimpleHttpClient.CRLF +
1af9a1
+                 "test=data" + SimpleHttpClient.CRLF +
1af9a1
+                 "0" + SimpleHttpClient.CRLF +
1af9a1
+                 SimpleHttpClient.CRLF;
1af9a1
+                
1af9a1
+        Client client = new Client(tomcat.getConnector().getLocalPort());
1af9a1
+        client.setRequest(new String[] {request});
1af9a1
+       
1af9a1
+        client.connect();
1af9a1
+        client.processRequest();
1af9a1
+        assertTrue(client.isResponse200());
1af9a1
+        assertTrue(client.getResponseBody().contains("test - data"));
1af9a1
+    }
1af9a1
+    
1af9a1
+    
1af9a1
+
1af9a1
+
1af9a1
 
1af9a1
     @Test
1af9a1
     public void testWithTEIdentity() throws Exception {