[tomcat7] 02/02: Fix CVE-2013-4322: Denial of service
Emmanuel Bourg
ebourg-guest at moszumanska.debian.org
Fri Feb 28 22:39:55 UTC 2014
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 <ebourg at apache.org>
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
More information about the pkg-java-commits
mailing list