Blame SOURCES/tomcat-7.0.42-CVE-2014-0075.patch

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)