Hi, I've prepared a tomcat6 package with the patches I've recently submitted to the BTS concerning CVE-2014-0227 and CVE-2014-0230. I haven't found a test-case for the CVEs and I have been unable to test the vulnerability by myself. Although, I have tested the basic tomcat6 functionnalities. The packages are available at the repository: deb https://people.debian.org/~santiago/debian santiago-squeeze-lts/ Regards, Santiago
diff -Nru tomcat6-6.0.41/debian/changelog tomcat6-6.0.41/debian/changelog
--- tomcat6-6.0.41/debian/changelog 2015-01-18 22:39:59.000000000 +0100
+++ tomcat6-6.0.41/debian/changelog 2015-05-15 10:39:02.000000000 +0200
@@ -1,3 +1,13 @@
+tomcat6 (6.0.41-2+squeeze7~2) santiago-squeeze-lts; urgency=medium
+
+ * Security upload by the Debian LTS team.
+ * This upload fixes the following issues:
+ - CVE-2014-0227: HTTP request smuggling or DoS by streaming malformed data
+ - CVE-2014-0230: non-persistent DoS attack by feeding data aborting an
+ upload
+
+ -- Santiago Ruano Rincón <santiago@riseup.net> Fri, 15 May 2015 10:38:49 +0200
+
tomcat6 (6.0.41-2+squeeze6) squeeze-lts; urgency=medium
* Security upload by the Debian LTS team.
diff -Nru tomcat6-6.0.41/debian/patches/CVE-2014-0227.patch tomcat6-6.0.41/debian/patches/CVE-2014-0227.patch
--- tomcat6-6.0.41/debian/patches/CVE-2014-0227.patch 1970-01-01 01:00:00.000000000 +0100
+++ tomcat6-6.0.41/debian/patches/CVE-2014-0227.patch 2015-05-14 15:44:49.000000000 +0200
@@ -0,0 +1,425 @@
+Description: Improvements to ChunkedInputFilter
+ - Clean-up
+ - i18n for ChunkedInputFilter error message
+ - Add error flag to allow subsequent attempts at reading after an error to
+ fail fast
+ Fixes CVE-2014-0227
+Origin: https://svn.apache.org/viewvc?view=revision&revision=1603628
+
+Index: tomcat6-6.0.41/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
+===================================================================
+--- tomcat6-6.0.41.orig/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
++++ tomcat6-6.0.41/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
+@@ -14,7 +14,6 @@
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-
+ package org.apache.coyote.http11.filters;
+
+ import java.io.EOFException;
+@@ -29,6 +28,7 @@ import org.apache.coyote.http11.Constant
+ import org.apache.coyote.http11.InputFilter;
+ import org.apache.tomcat.util.buf.MessageBytes;
+ import org.apache.tomcat.util.http.MimeHeaders;
++import org.apache.tomcat.util.res.StringManager;
+
+ /**
+ * Chunked input filter. Parses chunked data according to
+@@ -39,9 +39,11 @@ import org.apache.tomcat.util.http.MimeH
+ */
+ public class ChunkedInputFilter implements InputFilter {
+
++ private static final StringManager sm = StringManager.getManager(
++ ChunkedInputFilter.class.getPackage().getName());
+
+- // -------------------------------------------------------------- Constants
+
++ // -------------------------------------------------------------- Constants
+
+ protected static final String ENCODING_NAME = "chunked";
+ protected static final ByteChunk ENCODING = new ByteChunk();
+@@ -49,7 +51,6 @@ public class ChunkedInputFilter implemen
+
+ // ----------------------------------------------------- Static Initializer
+
+-
+ static {
+ ENCODING.setBytes(ENCODING_NAME.getBytes(), 0, ENCODING_NAME.length());
+ }
+@@ -57,7 +58,6 @@ public class ChunkedInputFilter implemen
+
+ // ----------------------------------------------------- Instance Variables
+
+-
+ /**
+ * Next buffer in the pipeline.
+ */
+@@ -120,6 +120,11 @@ public class ChunkedInputFilter implemen
+
+
+ /**
++ * Flag that indicates if an error has occurred.
++ */
++ private boolean error;
++
++ /**
+ * Flag set to true if the next call to doRead() must parse a CRLF pair
+ * before doing anything else.
+ */
+@@ -130,13 +135,10 @@ public class ChunkedInputFilter implemen
+ * Request being parsed.
+ */
+ private Request request;
+-
+- // ------------------------------------------------------------- Properties
+
+
+ // ---------------------------------------------------- InputBuffer Methods
+
+-
+ /**
+ * Read bytes.
+ *
+@@ -146,11 +148,12 @@ public class ChunkedInputFilter implemen
+ * whichever is greater. If the filter does not do request body length
+ * control, the returned value should be -1.
+ */
+- public int doRead(ByteChunk chunk, Request req)
+- throws IOException {
+-
+- if (endChunk)
++ public int doRead(ByteChunk chunk, Request req) throws IOException {
++ if (endChunk) {
+ return -1;
++ }
++
++ checkError();
+
+ if(needCRLFParse) {
+ needCRLFParse = false;
+@@ -159,7 +162,7 @@ public class ChunkedInputFilter implemen
+
+ if (remaining <= 0) {
+ if (!parseChunkHeader()) {
+- throw new IOException("Invalid chunk header");
++ throwIOException(sm.getString("chunkedInputFilter.invalidHeader"));
+ }
+ if (endChunk) {
+ parseEndChunk();
+@@ -171,8 +174,7 @@ public class ChunkedInputFilter implemen
+
+ if (pos >= lastValid) {
+ if (readBytes() < 0) {
+- throw new IOException(
+- "Unexpected end of stream whilst reading request body");
++ throwIOException(sm.getString("chunkedInputFilter.eos"));
+ }
+ }
+
+@@ -197,13 +199,11 @@ public class ChunkedInputFilter implemen
+ }
+
+ return result;
+-
+ }
+
+
+ // ---------------------------------------------------- InputFilter Methods
+
+-
+ /**
+ * Read the content length from the request.
+ */
+@@ -215,16 +215,13 @@ public class ChunkedInputFilter implemen
+ /**
+ * End the current request.
+ */
+- public long end()
+- throws IOException {
+-
++ public long end() throws IOException {
+ // Consume extra bytes : parse the stream until the end chunk is found
+ while (doRead(readChunk, null) >= 0) {
+ }
+
+ // Return the number of extra bytes which were consumed
+- return (lastValid - pos);
+-
++ return lastValid - pos;
+ }
+
+
+@@ -232,7 +229,7 @@ public class ChunkedInputFilter implemen
+ * Amount of bytes still available in a buffer.
+ */
+ public int available() {
+- return (lastValid - pos);
++ return lastValid - pos;
+ }
+
+
+@@ -258,6 +255,7 @@ public class ChunkedInputFilter implemen
+ trailingHeaders.setLimit(org.apache.coyote.Constants.MAX_TRAILER_SIZE);
+ }
+ extensionSize = 0;
++ error = false;
+ }
+
+
+@@ -272,12 +270,10 @@ public class ChunkedInputFilter implemen
+
+ // ------------------------------------------------------ Protected Methods
+
+-
+ /**
+ * Read bytes from the previous buffer.
+ */
+- protected int readBytes()
+- throws IOException {
++ protected int readBytes() throws IOException {
+
+ int nRead = buffer.doRead(readChunk, null);
+ pos = readChunk.getStart();
+@@ -285,7 +281,6 @@ public class ChunkedInputFilter implemen
+ buf = readChunk.getBytes();
+
+ return nRead;
+-
+ }
+
+
+@@ -298,8 +293,7 @@ public class ChunkedInputFilter implemen
+ * we should not parse F23IAMGONNAMESSTHISUP34CRLF as a valid header
+ * according to spec
+ */
+- protected boolean parseChunkHeader()
+- throws IOException {
++ protected boolean parseChunkHeader() throws IOException {
+
+ int result = 0;
+ boolean eol = false;
+@@ -340,7 +334,7 @@ public class ChunkedInputFilter implemen
+ extensionSize++;
+ if (org.apache.coyote.Constants.MAX_EXTENSION_SIZE > -1 &&
+ extensionSize > org.apache.coyote.Constants.MAX_EXTENSION_SIZE) {
+- throw new IOException("maxExtensionSize exceeded");
++ throwIOException(sm.getString("chunkedInputFilter.maxExtension"));
+ }
+ }
+
+@@ -348,21 +342,22 @@ public class ChunkedInputFilter implemen
+ if (!eol) {
+ pos++;
+ }
+-
+ }
+
+- if (readDigit == 0 || result < 0)
++ if (readDigit == 0 || result < 0) {
+ return false;
++ }
+
+- if (result == 0)
++ if (result == 0) {
+ endChunk = true;
++ }
+
+ remaining = result;
+- if (remaining < 0)
++ if (remaining < 0) {
+ return false;
++ }
+
+ return true;
+-
+ }
+
+
+@@ -389,26 +384,27 @@ public class ChunkedInputFilter implemen
+ boolean crfound = false;
+
+ while (!eol) {
+-
+ if (pos >= lastValid) {
+- if (readBytes() <= 0)
+- throw new IOException("Invalid CRLF");
++ if (readBytes() <= 0) {
++ throwIOException(sm.getString("chunkedInputFilter.invalidCrlfNoData"));
++ }
+ }
+
+ if (buf[pos] == Constants.CR) {
+- if (crfound) throw new IOException("Invalid CRLF, two CR characters encountered.");
++ if (crfound) {
++ throwIOException(sm.getString("chunkedInputFilter.invalidCrlfCRCR"));
++ }
+ crfound = true;
+ } else if (buf[pos] == Constants.LF) {
+ if (!tolerant && !crfound) {
+- throw new IOException("Invalid CRLF, no CR character encountered.");
++ throwIOException(sm.getString("chunkedInputFilter.invalidCrlfNoCR"));
+ }
+ eol = true;
+ } else {
+- throw new IOException("Invalid CRLF");
++ throwIOException(sm.getString("chunkedInputFilter.invalidCrlf"));
+ }
+
+ pos++;
+-
+ }
+ }
+
+@@ -417,7 +413,6 @@ public class ChunkedInputFilter implemen
+ * Parse end chunk data.
+ */
+ protected boolean parseEndChunk() throws IOException {
+-
+ // Handle optional trailer headers
+ while (parseHeader()) {
+ // Loop until we run out of headers
+@@ -434,8 +429,9 @@ public class ChunkedInputFilter implemen
+
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+- if (readBytes() <0)
+- throw new EOFException("Unexpected end of stream whilst reading trailer headers for chunked request");
++ if (readBytes() <0) {
++ throwEOFException(sm.getString("chunkedInputFilter.eosTrailer"));
++ }
+ }
+
+ chr = buf[pos];
+@@ -459,8 +455,9 @@ public class ChunkedInputFilter implemen
+
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+- if (readBytes() <0)
+- throw new EOFException("Unexpected end of stream whilst reading trailer headers for chunked request");
++ if (readBytes() <0) {
++ throwEOFException(sm.getString("chunkedInputFilter.eosTrailer"));
++ }
+ }
+
+ chr = buf[pos];
+@@ -500,8 +497,9 @@ public class ChunkedInputFilter implemen
+
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+- if (readBytes() <0)
+- throw new EOFException("Unexpected end of stream whilst reading trailer headers for chunked request");
++ if (readBytes() <0) {
++ throwEOFException(sm.getString("chunkedInputFilter.eosTrailer"));
++ }
+ }
+
+ chr = buf[pos];
+@@ -512,7 +510,7 @@ public class ChunkedInputFilter implemen
+ if (trailingHeaders.getLimit() != -1) {
+ int newlimit = trailingHeaders.getLimit() -1;
+ if (trailingHeaders.getEnd() > newlimit) {
+- throw new IOException("Exceeded maxTrailerSize");
++ throwIOException(sm.getString("chunkedInputFilter.maxTrailer"));
+ }
+ trailingHeaders.setLimit(newlimit);
+ }
+@@ -527,8 +525,9 @@ public class ChunkedInputFilter implemen
+
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+- if (readBytes() <0)
+- throw new EOFException("Unexpected end of stream whilst reading trailer headers for chunked request");
++ if (readBytes() <0) {
++ throwEOFException(sm.getString("chunkedInputFilter.eosTrailer"));
++ }
+ }
+
+ chr = buf[pos];
+@@ -552,8 +551,9 @@ public class ChunkedInputFilter implemen
+
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+- if (readBytes() <0)
+- throw new EOFException("Unexpected end of stream whilst reading trailer headers for chunked request");
++ if (readBytes() <0) {
++ throwEOFException(sm.getString("chunkedInputFilter.eosTrailer"));
++ }
+ }
+
+ chr = buf[pos];
+@@ -574,4 +574,23 @@ public class ChunkedInputFilter implemen
+
+ return true;
+ }
++
++
++ private void throwIOException(String msg) throws IOException {
++ error = true;
++ throw new IOException(msg);
++ }
++
++
++ private void throwEOFException(String msg) throws IOException {
++ error = true;
++ throw new EOFException(msg);
++ }
++
++
++ private void checkError() throws IOException {
++ if (error) {
++ throw new IOException(sm.getString("chunkedInputFilter.error"));
++ }
++ }
+ }
+Index: tomcat6-6.0.41/java/org/apache/coyote/http11/filters/LocalStrings.properties
+===================================================================
+--- /dev/null
++++ tomcat6-6.0.41/java/org/apache/coyote/http11/filters/LocalStrings.properties
+@@ -0,0 +1,25 @@
++# Licensed to the Apache Software Foundation (ASF) under one or more
++# contributor license agreements. See the NOTICE file distributed with
++# this work for additional information regarding copyright ownership.
++# The ASF licenses this file to You under the Apache License, Version 2.0
++# (the "License"); you may not use this file except in compliance with
++# the License. You may obtain a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS,
++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++# See the License for the specific language governing permissions and
++# limitations under the License.
++
++chunkedInputFilter.error=No data available due to previous error
++chunkedInputFilter.eos=Unexpected end of stream while reading request body
++chunkedInputFilter.eosTrailer=Unexpected end of stream while reading trailer headers
++chunkedInputFilter.invalidCrlf=Invalid end of line sequence (character other than CR or LF found)
++chunkedInputFilter.invalidCrlfCRCR=Invalid end of line sequence (CRCR)
++chunkedInputFilter.invalidCrlfNoCR=Invalid end of line sequence (No CR before LF)
++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
+Index: tomcat6-6.0.41/webapps/docs/changelog.xml
+===================================================================
+--- tomcat6-6.0.41.orig/webapps/docs/changelog.xml
++++ tomcat6-6.0.41/webapps/docs/changelog.xml
+@@ -272,6 +272,15 @@
+ </fix>
+ </changelog>
+ </subsection>
++ <subsection name="Coyote">
++ <changelog>
++ <fix>
++ Various improvements to ChunkedInputFilter including clean-up, i18n for
++ error messages and adding an error flag to allow subsequent attempts at
++ reading after an error to fail fast. (markt)
++ </fix>
++ </changelog>
++ </subsection>
+ <subsection name="Jasper">
+ <changelog>
+ <fix>
diff -Nru tomcat6-6.0.41/debian/patches/CVE-2014-0230.patch tomcat6-6.0.41/debian/patches/CVE-2014-0230.patch
--- tomcat6-6.0.41/debian/patches/CVE-2014-0230.patch 1970-01-01 01:00:00.000000000 +0100
+++ tomcat6-6.0.41/debian/patches/CVE-2014-0230.patch 2015-05-14 15:48:21.000000000 +0200
@@ -0,0 +1,148 @@
+Description: Add support for maxSwallowSize
+ Fixes CVE-2014-0230
+Origin: https://svn.apache.org/viewvc?view=revision&revision=1659537
+
+Index: tomcat6-6.0.41/java/org/apache/coyote/http11/filters/IdentityInputFilter.java
+===================================================================
+--- tomcat6-6.0.41.orig/java/org/apache/coyote/http11/filters/IdentityInputFilter.java
++++ tomcat6-6.0.41/java/org/apache/coyote/http11/filters/IdentityInputFilter.java
+@@ -20,7 +20,7 @@ package org.apache.coyote.http11.filters
+ import java.io.IOException;
+
+ import org.apache.tomcat.util.buf.ByteChunk;
+-
++import org.apache.tomcat.util.res.StringManager;
+ import org.apache.coyote.InputBuffer;
+ import org.apache.coyote.Request;
+ import org.apache.coyote.http11.InputFilter;
+@@ -32,9 +32,11 @@ import org.apache.coyote.http11.InputFil
+ */
+ public class IdentityInputFilter implements InputFilter {
+
++ private static final StringManager sm = StringManager.getManager(
++ IdentityInputFilter.class.getPackage().getName());
+
+- // -------------------------------------------------------------- Constants
+
++ // -------------------------------------------------------------- Constants
+
+ protected static final String ENCODING_NAME = "identity";
+ protected static final ByteChunk ENCODING = new ByteChunk();
+@@ -150,17 +152,25 @@ public class IdentityInputFilter impleme
+ }
+
+
+- /**
+- * End the current request.
+- */
+- public long end()
+- throws IOException {
++ public long end() throws IOException {
++
++ final int maxSwallowSize = org.apache.coyote.Constants.MAX_SWALLOW_SIZE;
++ 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;
+ }
+Index: tomcat6-6.0.41/java/org/apache/coyote/http11/filters/LocalStrings.properties
+===================================================================
+--- tomcat6-6.0.41.orig/java/org/apache/coyote/http11/filters/LocalStrings.properties
++++ tomcat6-6.0.41/java/org/apache/coyote/http11/filters/LocalStrings.properties
+@@ -22,4 +22,6 @@ chunkedInputFilter.invalidCrlfNoCR=Inval
+ 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
+Index: tomcat6-6.0.41/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
+===================================================================
+--- tomcat6-6.0.41.orig/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
++++ tomcat6-6.0.41/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
+@@ -216,8 +216,15 @@ public class ChunkedInputFilter implemen
+ * End the current request.
+ */
+ public long end() throws IOException {
++ int maxSwallowSize = org.apache.coyote.Constants.MAX_SWALLOW_SIZE;
++ long swallowed = 0;
++ int read = 0;
+ // Consume extra bytes : parse the stream until the end chunk is found
+- while (doRead(readChunk, null) >= 0) {
++ 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
+Index: tomcat6-6.0.41/java/org/apache/coyote/Constants.java
+===================================================================
+--- tomcat6-6.0.41.orig/java/org/apache/coyote/Constants.java
++++ tomcat6-6.0.41/java/org/apache/coyote/Constants.java
+@@ -85,4 +85,13 @@ public final class Constants {
+ Integer.parseInt(System.getProperty(
+ "org.apache.coyote.MAX_EXTENSION_SIZE",
+ "8192"));
++
++ /**
++ * Limit on the length of request body Tomcat will swallow if it is not
++ * read during normal request processing. Defaults to 2MB.
++ */
++ public static final int MAX_SWALLOW_SIZE =
++ Integer.parseInt(System.getProperty(
++ "org.apache.coyote.MAX_SWALLOW_SIZE",
++ "2097152"));
+ }
+Index: tomcat6-6.0.41/webapps/docs/changelog.xml
+===================================================================
+--- tomcat6-6.0.41.orig/webapps/docs/changelog.xml
++++ tomcat6-6.0.41/webapps/docs/changelog.xml
+@@ -51,6 +51,11 @@
+ attributes with empty string value in custom tags. Based on a patch
+ provided by Hariprasad Manchi. (violetagg/kkolinko)
+ </fix>
++ <fix>
++ When applying the <code>maxSwallowSize</code> limit to a connection read
++ that many bytes first before closing the connection to give the client a
++ chance to read the reponse. (markt)
++ </fix>
+ </changelog>
+ </subsection>
+ </section>
+Index: tomcat6-6.0.41/webapps/docs/config/systemprops.xml
+===================================================================
+--- tomcat6-6.0.41.orig/webapps/docs/config/systemprops.xml
++++ tomcat6-6.0.41/webapps/docs/config/systemprops.xml
+@@ -440,6 +440,14 @@
+ <p>If not specified, the default value of <code>8192</code> will be used.</p>
+ </property>
+
++ <property name="org.apache.coyote.MAX_SWALLOW_SIZE">
++ <p>Limits the length of a request body Tomcat will swallow if it is not
++ read during normal request processing. If the value is <code>-1</code>, no
++ limit will be imposed.</p>
++ <p>If not specified, the default value of <code>2097152</code> (2MB) will
++ be used.</p>
++ </property>
++
+ <property name="catalina.useNaming">
+ <p>If this is <code>false</code> it will override the
+ <code>useNaming</code> attribute for all <a href="context.html">
diff -Nru tomcat6-6.0.41/debian/patches/series tomcat6-6.0.41/debian/patches/series
--- tomcat6-6.0.41/debian/patches/series 2015-01-18 22:39:58.000000000 +0100
+++ tomcat6-6.0.41/debian/patches/series 2015-05-14 14:11:17.000000000 +0200
@@ -8,3 +8,5 @@
0008-add-OSGI-headers-to-jsp-api.patch
0010-Use-java.security.policy-file-in-catalina.sh.patch
0011-Fix-for-NoSuchElementException-when-an-attribute-has.patch
+CVE-2014-0227.patch
+CVE-2014-0230.patch
Attachment:
signature.asc
Description: Digital signature