This is an automated email from the git hooks/post-receive script. ebourg-guest pushed a commit to branch wheezy in repository tomcat7.
commit b5930b1e6bf69c11442dac142b1b1dd519b6494a Author: Emmanuel Bourg <[email protected]> Date: Fri Feb 28 23:37:10 2014 +0100 Fix CVE-2013-4322: Denial of service --- debian/changelog | 7 +- debian/patches/0024-CVE-2013-4322.patch | 347 ++++++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 352 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index b3a3655..2ccff36 100644 --- a/debian/changelog +++ b/debian/changelog @@ -11,9 +11,10 @@ tomcat7 (7.0.28-4+deb7u1) wheezy-security; urgency=high * Fix CVE-2013-2071: A runtime exception in AsyncListener.onComplete() prevents the request from being recycled. This may expose elements of a previous request to a current request. - * Fix CVE-2012-3544: When processing a request submitted using the chunked - transfer encoding, Tomcat ignored but did not limit any extensions that - were included. This allows a client to perform a limited denial of service + * Fix CVE-2012-3544 and CVE-2013-4322: When processing a request submitted + using the chunked transfer encoding, Tomcat ignored but did not limit any + extensions that were included. This allows a client to perform a limited + denial of service. by streaming an unlimited amount of data to the server. * Fix CVE-2013-4286: Reject requests with multiple content-length headers or with a content-length header when chunked encoding is being used. diff --git a/debian/patches/0024-CVE-2013-4322.patch b/debian/patches/0024-CVE-2013-4322.patch new file mode 100644 index 0000000..55252a2 --- /dev/null +++ b/debian/patches/0024-CVE-2013-4322.patch @@ -0,0 +1,347 @@ +Description: Fix for CVE-2013-4322: Add support for limiting the size of chunk + extensions when using chunked encoding +Origin: backport from Tomcat 7.0.50, http://svn.apache.org/r1521864 and http://svn.apache.org/r1549523 +--- a/java/org/apache/coyote/http11/AbstractHttp11Processor.java ++++ b/java/org/apache/coyote/http11/AbstractHttp11Processor.java +@@ -681,13 +681,14 @@ + /** + * Initialize standard input and output filters. + */ +- protected void initializeFilters(int maxTrailerSize) { ++ protected void initializeFilters(int maxTrailerSize, int maxExtensionSize) { + // Create and add the identity filters. + getInputBuffer().addFilter(new IdentityInputFilter()); + getOutputBuffer().addFilter(new IdentityOutputFilter()); + + // Create and add the chunked filters. +- getInputBuffer().addFilter(new ChunkedInputFilter(maxTrailerSize)); ++ getInputBuffer().addFilter( ++ new ChunkedInputFilter(maxTrailerSize, maxExtensionSize)); + 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 +@@ -153,6 +153,16 @@ + + + /** ++ * Maximum size of extension information in chunked encoding ++ */ ++ private int maxExtensionSize = 8192; ++ public int getMaxExtensionSize() { return maxExtensionSize; } ++ public void setMaxExtensionSize(int maxExtensionSize) { ++ this.maxExtensionSize = maxExtensionSize; ++ } ++ ++ ++ /** + * 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 maxTrailerSize, int maxExtensionSize) { + + super(endpoint); + +@@ -68,7 +68,7 @@ + outputBuffer = new InternalAprOutputBuffer(response, headerBufferSize); + response.setOutputBuffer(outputBuffer); + +- initializeFilters(maxTrailerSize); ++ initializeFilters(maxTrailerSize, maxExtensionSize); + } + + +--- a/java/org/apache/coyote/http11/Http11AprProtocol.java ++++ b/java/org/apache/coyote/http11/Http11AprProtocol.java +@@ -258,7 +258,7 @@ + protected Http11AprProcessor createProcessor() { + Http11AprProcessor processor = new Http11AprProcessor( + proto.getMaxHttpHeaderSize(), (AprEndpoint)proto.endpoint, +- proto.getMaxTrailerSize()); ++ proto.getMaxTrailerSize(), proto.getMaxExtensionSize()); + 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 maxTrailerSize, int maxExtensionSize) { + + super(endpoint); + +@@ -73,7 +73,7 @@ + outputBuffer = new InternalNioOutputBuffer(response, maxHttpHeaderSize); + response.setOutputBuffer(outputBuffer); + +- initializeFilters(maxTrailerSize); ++ initializeFilters(maxTrailerSize, maxExtensionSize); + } + + +--- a/java/org/apache/coyote/http11/Http11NioProtocol.java ++++ b/java/org/apache/coyote/http11/Http11NioProtocol.java +@@ -260,7 +260,7 @@ + public Http11NioProcessor createProcessor() { + Http11NioProcessor processor = new Http11NioProcessor( + proto.getMaxHttpHeaderSize(), (NioEndpoint)proto.endpoint, +- proto.getMaxTrailerSize()); ++ proto.getMaxTrailerSize(), proto.getMaxExtensionSize()); + 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 maxTrailerSize, int maxExtensionSize) { + + super(endpoint); + +@@ -60,7 +60,7 @@ + outputBuffer = new InternalOutputBuffer(response, headerBufferSize); + response.setOutputBuffer(outputBuffer); + +- initializeFilters(maxTrailerSize); ++ initializeFilters(maxTrailerSize, maxExtensionSize); + } + + +--- a/java/org/apache/coyote/http11/Http11Protocol.java ++++ b/java/org/apache/coyote/http11/Http11Protocol.java +@@ -164,7 +164,7 @@ + protected Http11Processor createProcessor() { + Http11Processor processor = new Http11Processor( + proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint, +- proto.getMaxTrailerSize()); ++ proto.getMaxTrailerSize(),proto.getMaxExtensionSize()); + 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 +@@ -118,9 +118,29 @@ + */ + private Request request; + ++ ++ /** ++ * Limit for extension size. ++ */ ++ private final long maxExtensionSize; ++ ++ ++ /** ++ * Limit for trailer size. ++ */ ++ private int maxTrailerSize; ++ ++ /** ++ * Size of extensions processed for this request. ++ */ ++ private long extensionSize; ++ ++ + // ----------------------------------------------------------- Constructors +- public ChunkedInputFilter(int maxTrailerSize) { ++ public ChunkedInputFilter(int maxTrailerSize, int maxExtensionSize) { + this.trailingHeaders.setLimit(maxTrailerSize); ++ this.maxExtensionSize = maxExtensionSize; ++ this.maxTrailerSize = maxTrailerSize; + } + + // ---------------------------------------------------- InputBuffer Methods +@@ -247,6 +267,8 @@ + endChunk = false; + needCRLFParse = false; + trailingHeaders.recycle(); ++ trailingHeaders.setLimit(maxTrailerSize); ++ extensionSize = 0; + } + + +@@ -294,7 +316,7 @@ + int result = 0; + boolean eol = false; + boolean readDigit = false; +- boolean trailer = false; ++ boolean extension = false; + + while (!eol) { + +@@ -306,9 +328,13 @@ + if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) { + parseCRLF(false); + eol = true; +- } else if (buf[pos] == Constants.SEMI_COLON) { +- trailer = true; +- } else if (!trailer) { ++ } else if (buf[pos] == Constants.SEMI_COLON && !extension) { ++ // First semi-colon marks the start of the extension. Further ++ // semi-colons may appear to separate multiple chunk-extensions. ++ // These need to be processed as part of parsing the extensions. ++ extension = true; ++ extensionSize++; ++ } else if (!extension) { + //don't read data after the trailer + if (HexUtils.getDec(buf[pos]) != -1) { + readDigit = true; +@@ -319,6 +345,14 @@ + //in the chunked header + return false; + } ++ } else { ++ // Extension 'parsing' ++ // Note that the chunk-extension is neither parsed nor ++ // validated. Currently it is simply ignored. ++ extensionSize++; ++ if (maxExtensionSize > -1 && extensionSize > maxExtensionSize) { ++ throw new IOException("maxExtensionSize exceeded"); ++ } + } + + // Parsing the CRLF increments pos +@@ -483,6 +517,13 @@ + chr = buf[pos]; + if ((chr == Constants.SP) || (chr == Constants.HT)) { + pos++; ++ // If we swallow whitespace, make sure it counts towards the ++ // limit placed on trailing header size ++ int newlimit = trailingHeaders.getLimit() -1; ++ if (trailingHeaders.getEnd() > newlimit) { ++ throw new IOException("Exceeded maxTrailerSize"); ++ } ++ trailingHeaders.setLimit(newlimit); + } else { + space = false; + } +--- a/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java ++++ b/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java +@@ -38,6 +38,8 @@ + + public class TestChunkedInputFilter extends TomcatBaseTest { + ++ private static final int EXT_SIZE_LIMIT = 10; ++ + @Test + public void testTrailingHeaders() throws Exception { + // Setup Tomcat instance +@@ -120,6 +122,76 @@ + assertTrue(client.isResponse500()); + } + ++ ++ @Test ++ public void testExtensionSizeLimitOneBelow() throws Exception { ++ doTestExtensionSizeLimit(EXT_SIZE_LIMIT - 1, true); ++ } ++ ++ ++ @Test ++ public void testExtensionSizeLimitExact() throws Exception { ++ doTestExtensionSizeLimit(EXT_SIZE_LIMIT, true); ++ } ++ ++ ++ @Test ++ public void testExtensionSizeLimitOneOver() throws Exception { ++ doTestExtensionSizeLimit(EXT_SIZE_LIMIT + 1, false); ++ } ++ ++ ++ private void doTestExtensionSizeLimit(int len, boolean ok) throws Exception { ++ // Setup Tomcat instance ++ Tomcat tomcat = getTomcatInstance(); ++ ++ tomcat.getConnector().setProperty( ++ "maxExtensionSize", Integer.toString(EXT_SIZE_LIMIT)); ++ ++ // Must have a real docBase - just use temp ++ Context ctx = ++ tomcat.addContext("", System.getProperty("java.io.tmpdir")); ++ ++ Tomcat.addServlet(ctx, "servlet", new EchoHeaderServlet()); ++ ctx.addServletMapping("/", "servlet"); ++ ++ tomcat.start(); ++ ++ String extName = ";foo="; ++ StringBuilder extValue = new StringBuilder(len); ++ for (int i = 0; i < (len - extName.length()); i++) { ++ extValue.append("x"); ++ } ++ ++ String[] request = new String[]{ ++ "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF + ++ "Host: any" + SimpleHttpClient.CRLF + ++ "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + ++ "Content-Type: application/x-www-form-urlencoded" + ++ SimpleHttpClient.CRLF + ++ "Connection: close" + SimpleHttpClient.CRLF + ++ SimpleHttpClient.CRLF + ++ "3" + extName + extValue.toString() + SimpleHttpClient.CRLF + ++ "a=0" + SimpleHttpClient.CRLF + ++ "4" + SimpleHttpClient.CRLF + ++ "&b=1" + SimpleHttpClient.CRLF + ++ "0" + SimpleHttpClient.CRLF + ++ SimpleHttpClient.CRLF }; ++ ++ TrailerClient client = ++ new TrailerClient(tomcat.getConnector().getLocalPort()); ++ client.setRequest(request); ++ ++ client.connect(); ++ client.processRequest(); ++ ++ if (ok) { ++ assertTrue(client.isResponse200()); ++ } else { ++ assertTrue(client.isResponse500()); ++ } ++ } ++ + @Test + public void testNoTrailingHeaders() throws Exception { + // Setup Tomcat instance +--- a/webapps/docs/changelog.xml ++++ b/webapps/docs/changelog.xml +@@ -258,6 +258,10 @@ + <bug>53373</bug>: Allow whitespace around delimiters in <Context> + aliases for readability. (schultz) + </fix> ++ <fix> ++ Add support for limiting the size of chunk extensions when using chunked ++ encoding. (markt) ++ </fix> + </changelog> + </subsection> + <subsection name="Coyote"> +--- a/webapps/docs/config/http.xml ++++ b/webapps/docs/config/http.xml +@@ -398,6 +398,12 @@ + and connections are not counted.</p> + </attribute> + ++ <attribute name="maxExtensionSize" required="false"> ++ <p>Limits the total length of chunk extensions in chunked HTTP requests. ++ If the value is <code>-1</code>, no limit will be imposed. If not ++ specified, the default value of <code>8192</code> will be used.</p> ++ </attribute> ++ + <attribute name="maxHttpHeaderSize" required="false"> + <p>The maximum size of the request and response HTTP header, specified + in bytes. If not specified, this attribute is set to 8192 (8 KB).</p> diff --git a/debian/patches/series b/debian/patches/series index 8c41b6a..8d52425 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -19,3 +19,4 @@ cve-2012-3439-tests.patch 0021-CVE-2012-3544.patch 0022-update-test-certificates.patch 0023-CVE-2013-4286.patch +0024-CVE-2013-4322.patch -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/tomcat7.git _______________________________________________ pkg-java-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-commits

