This is an automated email from the git hooks/post-receive script. apo pushed a commit to branch wheezy in repository tomcat6.
commit 416b5a11d5f28589cccb7fd6970692630fc996de Author: Markus Koschany <[email protected]> Date: Fri Nov 25 22:04:20 2016 +0100 Import Debian patch 6.0.45+dfsg-1~deb7u3 --- debian/changelog | 14 +- debian/patches/CVE-2016-6816.patch | 1105 ++++++++++++++++++++++++++++++++++++ debian/patches/CVE-2016-8735.patch | 24 + debian/patches/series | 2 + 4 files changed, 1143 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 85928c9..5ecc7a3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -tomcat6 (6.0.45+dfsg-1~deb7u3) wheezy-security; urgency=high +tomcat6 (6.0.45+dfsg-1~deb7u3) UNRELEASED; urgency=high * Fixed CVE-2016-0762: The Realm implementations did not process the supplied password if the supplied user name did not exist. This made a timing attack @@ -19,6 +19,16 @@ tomcat6 (6.0.45+dfsg-1~deb7u3) wheezy-security; urgency=high web application. Therefore, it was possible for a web application to access any global JNDI resource whether an explicit ResourceLink had been configured or not. + * Fixed CVE-2016-6816: The code that parsed the HTTP request line permitted + invalid characters. This could be exploited, in conjunction with a proxy + that also permitted the invalid characters but with a different + interpretation, to inject data into the HTTP response. By manipulating the + HTTP response the attacker could poison a web-cache, perform an XSS attack + and/or obtain sensitive information from requests other then their own. + * Fixed CVE-2016-8735: The JmxRemoteLifecycleListener was not updated to take + account of Oracle's fix for CVE-2016-3427. Therefore, Tomcat installations + using this listener remained vulnerable to a similar remote code execution + vulnerability. * CVE-2016-1240 follow-up: - The previous init.d fix was vulnerable to a race condition that could be exploited to make any existing file writable by the tomcat user. @@ -28,7 +38,7 @@ tomcat6 (6.0.45+dfsg-1~deb7u3) wheezy-security; urgency=high Thanks to Paul Szabo for the report. * Hardened the init.d script, thanks to Paul Szabo - -- Markus Koschany <[email protected]> Mon, 07 Nov 2016 13:46:30 +0100 + -- Markus Koschany <[email protected]> Fri, 25 Nov 2016 22:04:20 +0100 tomcat6 (6.0.45+dfsg-1~deb7u2) wheezy-security; urgency=high diff --git a/debian/patches/CVE-2016-6816.patch b/debian/patches/CVE-2016-6816.patch new file mode 100644 index 0000000..d936b4e --- /dev/null +++ b/debian/patches/CVE-2016-6816.patch @@ -0,0 +1,1105 @@ +From: Markus Koschany <[email protected]> +Date: Fri, 25 Nov 2016 20:08:42 +0100 +Subject: CVE-2016-6816 + +Origin: http://svn.apache.org/r1767683 +--- + .../apache/coyote/http11/AbstractInputBuffer.java | 52 +--------- + .../coyote/http11/InternalAprInputBuffer.java | 77 +++++++-------- + .../apache/coyote/http11/InternalInputBuffer.java | 69 ++++++------- + .../coyote/http11/InternalNioInputBuffer.java | 110 ++++++++++----------- + .../apache/coyote/http11/LocalStrings.properties | 3 + + .../apache/tomcat/util/http/parser/HttpParser.java | 45 ++++++++- + java/org/apache/tomcat/util/res/StringManager.java | 3 + + 7 files changed, 168 insertions(+), 191 deletions(-) + +diff --git a/java/org/apache/coyote/http11/AbstractInputBuffer.java b/java/org/apache/coyote/http11/AbstractInputBuffer.java +index 05e9d34..587755f 100644 +--- a/java/org/apache/coyote/http11/AbstractInputBuffer.java ++++ b/java/org/apache/coyote/http11/AbstractInputBuffer.java +@@ -17,56 +17,8 @@ + package org.apache.coyote.http11; + + import org.apache.coyote.InputBuffer; ++import org.apache.tomcat.util.res.StringManager; + + public abstract class AbstractInputBuffer implements InputBuffer { +- +- protected static final boolean[] HTTP_TOKEN_CHAR = new boolean[128]; + +- static { +- for (int i = 0; i < 128; i++) { +- if (i < 32) { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == 127) { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == '(') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == ')') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == '<') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == '>') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == '@') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == ',') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == ';') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == ':') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == '\\') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == '\"') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == '/') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == '[') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == ']') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == '?') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == '=') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == '{') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == '}') { +- HTTP_TOKEN_CHAR[i] = false; +- } else if (i == ' ') { +- HTTP_TOKEN_CHAR[i] = false; +- } else { +- HTTP_TOKEN_CHAR[i] = true; +- } +- } +- } +-} ++ protected static final StringManager sm = StringManager.getManager(AbstractInputBuffer.class);} +diff --git a/java/org/apache/coyote/http11/InternalAprInputBuffer.java b/java/org/apache/coyote/http11/InternalAprInputBuffer.java +index f703719..a5f2804 100644 +--- a/java/org/apache/coyote/http11/InternalAprInputBuffer.java ++++ b/java/org/apache/coyote/http11/InternalAprInputBuffer.java +@@ -26,7 +26,7 @@ import org.apache.tomcat.jni.Status; + import org.apache.tomcat.util.buf.ByteChunk; + import org.apache.tomcat.util.buf.MessageBytes; + import org.apache.tomcat.util.http.MimeHeaders; +-import org.apache.tomcat.util.res.StringManager; ++import org.apache.tomcat.util.http.parser.HttpParser; + import org.apache.coyote.InputBuffer; + import org.apache.coyote.Request; + import org.apache.juli.logging.Log; +@@ -68,23 +68,12 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + + parsingHeader = true; + swallowInput = true; +- +- } +- +- +- // -------------------------------------------------------------- Variables + +- +- /** +- * The string manager for this package. +- */ +- protected static StringManager sm = +- StringManager.getManager(Constants.Package); ++ } + + + // ----------------------------------------------------- Instance Variables + +- + /** + * Associated Coyote request. + */ +@@ -196,7 +185,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + */ + public void addFilter(InputFilter filter) { + +- InputFilter[] newFilterLibrary = ++ InputFilter[] newFilterLibrary = + new InputFilter[filterLibrary.length + 1]; + for (int i = 0; i < filterLibrary.length; i++) { + newFilterLibrary[i] = filterLibrary[i]; +@@ -264,7 +253,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + + + /** +- * Recycle the input buffer. This should be called when closing the ++ * Recycle the input buffer. This should be called when closing the + * connection. + */ + public void recycle() { +@@ -289,7 +278,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + + /** + * End processing of current HTTP request. +- * Note: All bytes of the current request should have been already ++ * Note: All bytes of the current request should have been already + * consumed. This method only resets all the pointers so that we are ready + * to parse the next HTTP request. + */ +@@ -302,7 +291,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + if (lastValid - pos > 0 && pos > 0) { + System.arraycopy(buf, pos, buf, 0, lastValid - pos); + } +- ++ + // Recycle filters + for (int i = 0; i <= lastActiveFilter; i++) { + activeFilters[i].recycle(); +@@ -320,7 +309,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + + /** + * End request (consumes leftover bytes). +- * ++ * + * @throws IOException an undelying I/O error occured + */ + public void endRequest() +@@ -335,14 +324,14 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + + + /** +- * Read the request line. This function is meant to be used during the +- * HTTP request header parsing. Do NOT attempt to read the request body ++ * Read the request line. This function is meant to be used during the ++ * HTTP request header parsing. Do NOT attempt to read the request body + * using it. + * + * @throws IOException If an exception occurs during the underlying socket + * read operations, or if the given buffer is not big enough to accomodate + * the whole line. +- * @return true if data is properly fed; false if no data is available ++ * @return true if data is properly fed; false if no data is available + * immediately and thread should be freed + */ + public boolean parseRequestLine(boolean useAvailableData) +@@ -398,17 +387,19 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + throw new EOFException(sm.getString("iib.eof.error")); + } + +- // Spec says no CR or LF in method name +- if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) { +- throw new IllegalArgumentException( +- sm.getString("iib.invalidmethod")); ++ // Spec says method name is a token followed by a single SP but ++ // also be tolerant of multiple SP and/or HT. ++ if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) { + } + // Spec says single SP but it also says be tolerant of HT + if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) { + space = true; + request.method().setBytes(buf, start, pos - start); ++ } else if (!HttpParser.isToken(buf[pos])) { ++ throw new IllegalArgumentException(sm.getString("iib.invalidmethod")); + } + ++ + pos++; + + } +@@ -450,15 +441,17 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) { + space = true; + end = pos; +- } else if ((buf[pos] == Constants.CR) ++ } else if ((buf[pos] == Constants.CR) + || (buf[pos] == Constants.LF)) { + // HTTP/0.9 style request + eol = true; + space = true; + end = pos; +- } else if ((buf[pos] == Constants.QUESTION) ++ } else if ((buf[pos] == Constants.QUESTION) + && (questionPos == -1)) { + questionPos = pos; ++ } else if (HttpParser.isNotRequestTarget(buf[pos])) { ++ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget")); + } + + pos++; +@@ -467,7 +460,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + + request.unparsedURI().setBytes(buf, start, end - start); + if (questionPos >= 0) { +- request.queryString().setBytes(buf, questionPos + 1, ++ request.queryString().setBytes(buf, questionPos + 1, + end - questionPos - 1); + request.requestURI().setBytes(buf, start, questionPos - start); + } else { +@@ -495,7 +488,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + + // + // Reading the protocol +- // Protocol is always US-ASCII ++ // Protocol is always "HTTP/" DIGIT "." DIGIT + // + + while (!eol) { +@@ -512,6 +505,8 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + if (end == 0) + end = pos; + eol = true; ++ } else if (!HttpParser.isHttpProtocol(buf[pos])) { ++ throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol")); + } + + pos++; +@@ -523,7 +518,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + } else { + request.protocol().setString(""); + } +- ++ + return true; + + } +@@ -546,7 +541,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + + /** + * Parse an HTTP header. +- * ++ * + * @return false after reading a blank line (which indicates that the + * HTTP header parsing is done + */ +@@ -604,7 +599,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + if (buf[pos] == Constants.COLON) { + colon = true; + headerValue = headers.addValue(buf, start, pos - start); +- } else if (!HTTP_TOKEN_CHAR[buf[pos]]) { ++ } else if (!HttpParser.isToken(buf[pos])) { + // If a non-token header is detected, skip the line and + // ignore the header + skipLine(start); +@@ -710,14 +705,14 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + + } + +- ++ + private void skipLine(int start) throws IOException { + boolean eol = false; + int lastRealByte = start; + if (pos - 1 > start) { + lastRealByte = pos - 1; + } +- ++ + while (!eol) { + + // Read new bytes if needed +@@ -741,8 +736,8 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + lastRealByte - start + 1, "ISO-8859-1"))); + } + } +- +- ++ ++ + /** + * Available bytes (note that due to encoding, this may not correspond ) + */ +@@ -763,7 +758,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + /** + * Read some bytes. + */ +- public int doRead(ByteChunk chunk, Request req) ++ public int doRead(ByteChunk chunk, Request req) + throws IOException { + + if (lastActiveFilter == -1) +@@ -779,7 +774,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + + /** + * Fill the internal buffer using data from the undelying input stream. +- * ++ * + * @return false if at end of stream + */ + protected boolean fill() +@@ -811,7 +806,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + } else { + + if (buf.length - end < 4500) { +- // In this case, the request header was really large, so we allocate a ++ // In this case, the request header was really large, so we allocate a + // brand new one; the old one will get GCed when subsequent requests + // clear all references + buf = new byte[buf.length]; +@@ -850,14 +845,14 @@ public class InternalAprInputBuffer extends AbstractInputBuffer { + * This class is an input buffer which will read its data from an input + * stream. + */ +- protected class SocketInputBuffer ++ protected class SocketInputBuffer + implements InputBuffer { + + + /** + * Read bytes into the specified chunk. + */ +- public int doRead(ByteChunk chunk, Request req ) ++ public int doRead(ByteChunk chunk, Request req ) + throws IOException { + + if (pos >= lastValid) { +diff --git a/java/org/apache/coyote/http11/InternalInputBuffer.java b/java/org/apache/coyote/http11/InternalInputBuffer.java +index ffad9da..94f3017 100644 +--- a/java/org/apache/coyote/http11/InternalInputBuffer.java ++++ b/java/org/apache/coyote/http11/InternalInputBuffer.java +@@ -23,8 +23,7 @@ import java.io.EOFException; + import org.apache.tomcat.util.buf.ByteChunk; + import org.apache.tomcat.util.buf.MessageBytes; + import org.apache.tomcat.util.http.MimeHeaders; +-import org.apache.tomcat.util.res.StringManager; +- ++import org.apache.tomcat.util.http.parser.HttpParser; + import org.apache.coyote.InputBuffer; + import org.apache.coyote.Request; + import org.apache.juli.logging.Log; +@@ -39,7 +38,7 @@ import org.apache.juli.logging.LogFactory; + public class InternalInputBuffer extends AbstractInputBuffer { + + private static final Log log = LogFactory.getLog(InternalInputBuffer.class); +- ++ + // -------------------------------------------------------------- Constants + + +@@ -76,19 +75,8 @@ public class InternalInputBuffer extends AbstractInputBuffer { + } + + +- // -------------------------------------------------------------- Variables +- +- +- /** +- * The string manager for this package. +- */ +- protected static StringManager sm = +- StringManager.getManager(Constants.Package); +- +- + // ----------------------------------------------------- Instance Variables + +- + /** + * Associated Coyote request. + */ +@@ -201,7 +189,7 @@ public class InternalInputBuffer extends AbstractInputBuffer { + + // FIXME: Check for null ? + +- InputFilter[] newFilterLibrary = ++ InputFilter[] newFilterLibrary = + new InputFilter[filterLibrary.length + 1]; + for (int i = 0; i < filterLibrary.length; i++) { + newFilterLibrary[i] = filterLibrary[i]; +@@ -269,7 +257,7 @@ public class InternalInputBuffer extends AbstractInputBuffer { + + + /** +- * Recycle the input buffer. This should be called when closing the ++ * Recycle the input buffer. This should be called when closing the + * connection. + */ + public void recycle() { +@@ -294,7 +282,7 @@ public class InternalInputBuffer extends AbstractInputBuffer { + + /** + * End processing of current HTTP request. +- * Note: All bytes of the current request should have been already ++ * Note: All bytes of the current request should have been already + * consumed. This method only resets all the pointers so that we are ready + * to parse the next HTTP request. + */ +@@ -325,7 +313,7 @@ public class InternalInputBuffer extends AbstractInputBuffer { + + /** + * End request (consumes leftover bytes). +- * ++ * + * @throws IOException an undelying I/O error occured + */ + public void endRequest() +@@ -340,8 +328,8 @@ public class InternalInputBuffer extends AbstractInputBuffer { + + + /** +- * Read the request line. This function is meant to be used during the +- * HTTP request header parsing. Do NOT attempt to read the request body ++ * Read the request line. This function is meant to be used during the ++ * HTTP request header parsing. Do NOT attempt to read the request body + * using it. + * + * @throws IOException If an exception occurs during the underlying socket +@@ -390,17 +378,16 @@ public class InternalInputBuffer extends AbstractInputBuffer { + throw new EOFException(sm.getString("iib.eof.error")); + } + +- // Spec says no CR or LF in method name +- if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) { +- throw new IllegalArgumentException( +- sm.getString("iib.invalidmethod")); +- } +- // Spec says single SP but it also says be tolerant of HT ++ // Spec says method name is a token followed by a single SP but ++ // also be tolerant of multiple SP and/or HT. + if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) { + space = true; + request.method().setBytes(buf, start, pos - start); ++ } else if (!HttpParser.isToken(buf[pos])) { ++ throw new IllegalArgumentException(sm.getString("iib.invalidmethod")); + } + ++ + pos++; + + } +@@ -443,15 +430,17 @@ public class InternalInputBuffer extends AbstractInputBuffer { + if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) { + space = true; + end = pos; +- } else if ((buf[pos] == Constants.CR) ++ } else if ((buf[pos] == Constants.CR) + || (buf[pos] == Constants.LF)) { + // HTTP/0.9 style request + eol = true; + space = true; + end = pos; +- } else if ((buf[pos] == Constants.QUESTION) ++ } else if ((buf[pos] == Constants.QUESTION) + && (questionPos == -1)) { + questionPos = pos; ++ } else if (HttpParser.isNotRequestTarget(buf[pos])) { ++ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget")); + } + + pos++; +@@ -460,7 +449,7 @@ public class InternalInputBuffer extends AbstractInputBuffer { + + request.unparsedURI().setBytes(buf, start, end - start); + if (questionPos >= 0) { +- request.queryString().setBytes(buf, questionPos + 1, ++ request.queryString().setBytes(buf, questionPos + 1, + end - questionPos - 1); + request.requestURI().setBytes(buf, start, questionPos - start); + } else { +@@ -487,7 +476,7 @@ public class InternalInputBuffer extends AbstractInputBuffer { + + // + // Reading the protocol +- // Protocol is always US-ASCII ++ // Protocol is always "HTTP/" DIGIT "." DIGIT + // + + while (!eol) { +@@ -504,6 +493,8 @@ public class InternalInputBuffer extends AbstractInputBuffer { + if (end == 0) + end = pos; + eol = true; ++ } else if (!HttpParser.isHttpProtocol(buf[pos])) { ++ throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol")); + } + + pos++; +@@ -536,7 +527,7 @@ public class InternalInputBuffer extends AbstractInputBuffer { + + /** + * Parse an HTTP header. +- * ++ * + * @return false after reading a blank line (which indicates that the + * HTTP header parsing is done + */ +@@ -594,7 +585,7 @@ public class InternalInputBuffer extends AbstractInputBuffer { + if (buf[pos] == Constants.COLON) { + colon = true; + headerValue = headers.addValue(buf, start, pos - start); +- } else if (!HTTP_TOKEN_CHAR[buf[pos]]) { ++ } else if (!HttpParser.isToken(buf[pos])) { + // If a non-token header is detected, skip the line and + // ignore the header + skipLine(start); +@@ -708,7 +699,7 @@ public class InternalInputBuffer extends AbstractInputBuffer { + /** + * Read some bytes. + */ +- public int doRead(ByteChunk chunk, Request req) ++ public int doRead(ByteChunk chunk, Request req) + throws IOException { + + if (lastActiveFilter == -1) +@@ -727,7 +718,7 @@ public class InternalInputBuffer extends AbstractInputBuffer { + if (pos - 1 > start) { + lastRealByte = pos - 1; + } +- ++ + while (!eol) { + + // Read new bytes if needed +@@ -752,10 +743,10 @@ public class InternalInputBuffer extends AbstractInputBuffer { + } + } + +- ++ + /** + * Fill the internal buffer using data from the undelying input stream. +- * ++ * + * @return false if at end of stream + */ + protected boolean fill() +@@ -778,7 +769,7 @@ public class InternalInputBuffer extends AbstractInputBuffer { + } else { + + if (buf.length - end < 4500) { +- // In this case, the request header was really large, so we allocate a ++ // In this case, the request header was really large, so we allocate a + // brand new one; the old one will get GCed when subsequent requests + // clear all references + buf = new byte[buf.length]; +@@ -805,14 +796,14 @@ public class InternalInputBuffer extends AbstractInputBuffer { + * This class is an input buffer which will read its data from an input + * stream. + */ +- protected class InputStreamInputBuffer ++ protected class InputStreamInputBuffer + implements InputBuffer { + + + /** + * Read bytes into the specified chunk. + */ +- public int doRead(ByteChunk chunk, Request req ) ++ public int doRead(ByteChunk chunk, Request req ) + throws IOException { + + if (pos >= lastValid) { +diff --git a/java/org/apache/coyote/http11/InternalNioInputBuffer.java b/java/org/apache/coyote/http11/InternalNioInputBuffer.java +index 7289201..c050a16 100644 +--- a/java/org/apache/coyote/http11/InternalNioInputBuffer.java ++++ b/java/org/apache/coyote/http11/InternalNioInputBuffer.java +@@ -25,9 +25,9 @@ import org.apache.coyote.Request; + import org.apache.tomcat.util.buf.ByteChunk; + import org.apache.tomcat.util.buf.MessageBytes; + import org.apache.tomcat.util.http.MimeHeaders; ++import org.apache.tomcat.util.http.parser.HttpParser; + import org.apache.tomcat.util.net.NioChannel; + import org.apache.tomcat.util.net.NioSelectorPool; +-import org.apache.tomcat.util.res.StringManager; + import org.apache.tomcat.util.net.NioEndpoint; + + /** +@@ -88,7 +88,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + } + + // ----------------------------------------------------------- Constructors +- ++ + + /** + * Alternate constructor. +@@ -119,19 +119,8 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + } + + +- // -------------------------------------------------------------- Variables +- +- +- /** +- * The string manager for this package. +- */ +- protected static StringManager sm = +- StringManager.getManager(Constants.Package); +- +- + // ----------------------------------------------------- Instance Variables + +- + /** + * Associated Coyote request. + */ +@@ -193,12 +182,12 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + * Underlying socket. + */ + protected NioChannel socket; +- ++ + /** + * Selector pool, for blocking reads and blocking writes + */ + protected NioSelectorPool pool; +- ++ + + /** + * Underlying input buffer. +@@ -263,7 +252,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + buf = new byte[bufLength]; + } + } +- ++ + /** + * Get the underlying socket input stream. + */ +@@ -271,10 +260,10 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + return socket; + } + +- public void setSelectorPool(NioSelectorPool pool) { ++ public void setSelectorPool(NioSelectorPool pool) { + this.pool = pool; + } +- ++ + public NioSelectorPool getSelectorPool() { + return pool; + } +@@ -285,7 +274,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + */ + public void addFilter(InputFilter filter) { + +- InputFilter[] newFilterLibrary = ++ InputFilter[] newFilterLibrary = + new InputFilter[filterLibrary.length + 1]; + for (int i = 0; i < filterLibrary.length; i++) { + newFilterLibrary[i] = filterLibrary[i]; +@@ -357,7 +346,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + public boolean isReadable() throws IOException { + return (pos < lastValid) || (nbRead()>0); + } +- ++ + /** + * Issues a non blocking read + * @return int +@@ -368,7 +357,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + } + + /** +- * Recycle the input buffer. This should be called when closing the ++ * Recycle the input buffer. This should be called when closing the + * connection. + */ + public void recycle() { +@@ -399,7 +388,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + + /** + * End processing of current HTTP request. +- * Note: All bytes of the current request should have been already ++ * Note: All bytes of the current request should have been already + * consumed. This method only resets all the pointers so that we are ready + * to parse the next HTTP request. + */ +@@ -437,7 +426,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + + /** + * End request (consumes leftover bytes). +- * ++ * + * @throws IOException an undelying I/O error occured + */ + public void endRequest() +@@ -452,14 +441,14 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + + + /** +- * Read the request line. This function is meant to be used during the +- * HTTP request header parsing. Do NOT attempt to read the request body ++ * Read the request line. This function is meant to be used during the ++ * HTTP request header parsing. Do NOT attempt to read the request body + * using it. + * + * @throws IOException If an exception occurs during the underlying socket + * read operations, or if the given buffer is not big enough to accommodate + * the whole line. +- * @return true if data is properly fed; false if no data is available ++ * @return true if data is properly fed; false if no data is available + * immediately and thread should be freed + */ + public boolean parseRequestLine(boolean useAvailableDataOnly) +@@ -473,7 +462,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + if ( parsingRequestLinePhase == 0 ) { + byte chr = 0; + do { +- ++ + // Read new bytes if needed + if (pos >= lastValid) { + if (useAvailableDataOnly) { +@@ -510,14 +499,13 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + if (!fill(true, false)) //request line parsing + return false; + } +- // Spec says no CR or LF in method name +- if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) { +- throw new IllegalArgumentException( +- sm.getString("iib.invalidmethod")); +- } ++ // Spec says method name is a token followed by a single SP but ++ // also be tolerant of multiple SP and/or HT. + if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) { + space = true; + request.method().setBytes(buf, parsingRequestLineStart, pos - parsingRequestLineStart); ++ } else if (!HttpParser.isToken(buf[pos])) { ++ throw new IllegalArgumentException(sm.getString("iib.invalidmethod")); + } + pos++; + } +@@ -543,7 +531,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + } + if (parsingRequestLinePhase == 4) { + // Mark the current buffer position +- ++ + int end = 0; + // + // Reading the URI +@@ -558,21 +546,23 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) { + space = true; + end = pos; +- } else if ((buf[pos] == Constants.CR) ++ } else if ((buf[pos] == Constants.CR) + || (buf[pos] == Constants.LF)) { + // HTTP/0.9 style request + parsingRequestLineEol = true; + space = true; + end = pos; +- } else if ((buf[pos] == Constants.QUESTION) ++ } else if ((buf[pos] == Constants.QUESTION) + && (parsingRequestLineQPos == -1)) { + parsingRequestLineQPos = pos; ++ } else if (HttpParser.isNotRequestTarget(buf[pos])) { ++ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget")); + } + pos++; + } + request.unparsedURI().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart); + if (parsingRequestLineQPos >= 0) { +- request.queryString().setBytes(buf, parsingRequestLineQPos + 1, ++ request.queryString().setBytes(buf, parsingRequestLineQPos + 1, + end - parsingRequestLineQPos - 1); + request.requestURI().setBytes(buf, parsingRequestLineStart, parsingRequestLineQPos - parsingRequestLineStart); + } else { +@@ -601,10 +591,10 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + // Mark the current buffer position + end = 0; + } +- if (parsingRequestLinePhase == 6) { ++ if (parsingRequestLinePhase == 6) { + // + // Reading the protocol +- // Protocol is always US-ASCII ++ // Protocol is always "HTTP/" DIGIT "." DIGIT + // + while (!parsingRequestLineEol) { + // Read new bytes if needed +@@ -612,17 +602,19 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + if (!fill(true, false)) //request line parsing + return false; + } +- ++ + if (buf[pos] == Constants.CR) { + end = pos; + } else if (buf[pos] == Constants.LF) { + if (end == 0) + end = pos; + parsingRequestLineEol = true; ++ } else if (!HttpParser.isHttpProtocol(buf[pos])) { ++ throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol")); + } + pos++; + } +- ++ + if ( (end - parsingRequestLineStart) > 0) { + request.protocol().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart); + } else { +@@ -636,7 +628,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + } + throw new IllegalStateException("Invalid request line parse phase:"+parsingRequestLinePhase); + } +- ++ + private void expand(int newsize) { + if ( newsize > buf.length ) { + if (parsingHeader) { +@@ -652,7 +644,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + tmp = null; + } + } +- ++ + /** + * Perform blocking read with a timeout if desired + * @param timeout boolean - if we want to use the timeout data +@@ -673,7 +665,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + nRead = getSelectorPool().read(socket.getBufHandler().getReadBuffer(),socket,selector,att.getTimeout()); + } catch ( EOFException eof ) { + nRead = -1; +- } finally { ++ } finally { + if ( selector != null ) getSelectorPool().put(selector); + } + } else { +@@ -700,7 +692,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + public boolean parseHeaders() + throws IOException { + HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS; +- ++ + do { + status = parseHeader(); + // Checking that +@@ -729,7 +721,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + + /** + * Parse an HTTP header. +- * ++ * + * @return false after reading a blank line (which indicates that the + * HTTP header parsing is done + */ +@@ -745,7 +737,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + + // Read new bytes if needed + if (pos >= lastValid) { +- if (!fill(true,false)) {//parse header ++ if (!fill(true,false)) {//parse header + headerParsePos = HeaderParsePosition.HEADER_START; + return HeaderParseStatus.NEED_MORE_DATA; + } +@@ -770,18 +762,18 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + // Mark the current buffer position + headerData.start = pos; + headerParsePos = HeaderParsePosition.HEADER_NAME; +- } ++ } + + // + // Reading the header name + // Header name is always US-ASCII + // +- ++ + while (headerParsePos == HeaderParsePosition.HEADER_NAME) { + + // Read new bytes if needed + if (pos >= lastValid) { +- if (!fill(true,false)) { //parse header ++ if (!fill(true,false)) { //parse header + return HeaderParseStatus.NEED_MORE_DATA; + } + } +@@ -796,7 +788,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + headerData.realPos = pos; + headerData.lastSignificantChar = pos; + break; +- } else if (!HTTP_TOKEN_CHAR[chr]) { ++ } else if (!HttpParser.isToken(chr)) { + // If a non-token header is detected, skip the line and + // ignore the header + headerData.lastSignificantChar = pos; +@@ -828,7 +820,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + while (true) { + // Read new bytes if needed + if (pos >= lastValid) { +- if (!fill(true,false)) {//parse header ++ if (!fill(true,false)) {//parse header + //HEADER_VALUE_START + return HeaderParseStatus.NEED_MORE_DATA; + } +@@ -851,7 +843,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + + // Read new bytes if needed + if (pos >= lastValid) { +- if (!fill(true,false)) {//parse header ++ if (!fill(true,false)) {//parse header + //HEADER_VALUE + return HeaderParseStatus.NEED_MORE_DATA; + } +@@ -884,7 +876,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + // Read new bytes if needed + if (pos >= lastValid) { + if (!fill(true,false)) {//parse header +- ++ + //HEADER_MULTI_LINE + return HeaderParseStatus.NEED_MORE_DATA; + } +@@ -910,7 +902,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + headerData.recycle(); + return HeaderParseStatus.HAVE_MORE_HEADERS; + } +- ++ + private HeaderParseStatus skipLine() throws IOException { + headerParsePos = HeaderParsePosition.HEADER_SKIPLINE; + boolean eol = false; +@@ -945,7 +937,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + headerParsePos = HeaderParsePosition.HEADER_START; + return HeaderParseStatus.HAVE_MORE_HEADERS; + } +- ++ + private HeaderParseData headerData = new HeaderParseData(); + public static class HeaderParseData { + /** +@@ -1004,7 +996,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + /** + * Read some bytes. + */ +- public int doRead(ByteChunk chunk, Request req) ++ public int doRead(ByteChunk chunk, Request req) + throws IOException { + + if (lastActiveFilter == -1) +@@ -1019,7 +1011,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + + /** + * Fill the internal buffer using data from the undelying input stream. +- * ++ * + * @return false if at end of stream + */ + protected boolean fill(boolean timeout, boolean block) +@@ -1052,14 +1044,14 @@ public class InternalNioInputBuffer extends AbstractInputBuffer { + * This class is an input buffer which will read its data from an input + * stream. + */ +- protected class SocketInputBuffer ++ protected class SocketInputBuffer + implements InputBuffer { + + + /** + * Read bytes into the specified chunk. + */ +- public int doRead(ByteChunk chunk, Request req ) ++ public int doRead(ByteChunk chunk, Request req ) + throws IOException { + + if (pos >= lastValid) { +diff --git a/java/org/apache/coyote/http11/LocalStrings.properties b/java/org/apache/coyote/http11/LocalStrings.properties +index 542eedd..0fb5d0c 100644 +--- a/java/org/apache/coyote/http11/LocalStrings.properties ++++ b/java/org/apache/coyote/http11/LocalStrings.properties +@@ -62,5 +62,8 @@ http11processor.sendfile.error=Error sending data using sendfile. May be caused + + iib.eof.error=Unexpected EOF read on the socket + iib.requestheadertoolarge.error=Request header is too large ++iib.invalidheader=The HTTP header line [{0}] does not conform to RFC 7230 and has been ignored. ++iib.invalidRequestTarget=Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986 ++iib.invalidHttpProtocol=Invalid character found in the HTTP protocol + iib.invalidmethod=Invalid character (CR or LF) found in method name + +diff --git a/java/org/apache/tomcat/util/http/parser/HttpParser.java b/java/org/apache/tomcat/util/http/parser/HttpParser.java +index b828f71..b92d687 100644 +--- a/java/org/apache/tomcat/util/http/parser/HttpParser.java ++++ b/java/org/apache/tomcat/util/http/parser/HttpParser.java +@@ -54,8 +54,11 @@ public class HttpParser { + new HashMap<String, Integer>(); + + // Arrays used by isToken(), isHex() ++ private static final boolean[] IS_CONTROL = new boolean[128]; + private static final boolean isToken[] = new boolean[128]; + private static final boolean isHex[] = new boolean[128]; ++ private static final boolean[] IS_NOT_REQUEST_TARGET = new boolean[128]; ++ private static final boolean[] IS_HTTP_PROTOCOL = new boolean[128]; + + static { + // Digest field types. +@@ -96,6 +99,21 @@ public class HttpParser { + } else { + isHex[i] = false; + } ++ ++ // Not valid for request target. ++ // Combination of multiple rules from RFC7230 and RFC 3986. Must be ++ // ASCII, no controls plus a few additional characters excluded ++ if (IS_CONTROL[i] || i > 127 || ++ i == ' ' || i == '\"' || i == '#' || i == '<' || i == '>' || i == '\\' || ++ i == '^' || i == '`' || i == '{' || i == '|' || i == '}') { ++ IS_NOT_REQUEST_TARGET[i] = true; ++ } ++ ++ // Not valid for HTTP protocol ++ // "HTTP/" DIGIT "." DIGIT ++ if (i == 'H' || i == 'T' || i == 'P' || i == '/' || i == '.' || (i >= '0' && i <= '9')) { ++ IS_HTTP_PROTOCOL[i] = true; ++ } + } + } + +@@ -246,7 +264,7 @@ public class HttpParser { + return result.toString(); + } + +- private static boolean isToken(int c) { ++ public static boolean isToken(int c) { + // Fast for correct values, slower for incorrect ones + try { + return isToken[c]; +@@ -255,7 +273,7 @@ public class HttpParser { + } + } + +- private static boolean isHex(int c) { ++ public static boolean isHex(int c) { + // Fast for correct values, slower for incorrect ones + try { + return isHex[c]; +@@ -264,6 +282,29 @@ public class HttpParser { + } + } + ++ ++ public static boolean isNotRequestTarget(int c) { ++ // Fast for valid request target characters, slower for some incorrect ++ // ones ++ try { ++ return IS_NOT_REQUEST_TARGET[c]; ++ } catch (ArrayIndexOutOfBoundsException ex) { ++ return true; ++ } ++ } ++ ++ ++ public static boolean isHttpProtocol(int c) { ++ // Fast for valid HTTP protocol characters, slower for some incorrect ++ // ones ++ try { ++ return IS_HTTP_PROTOCOL[c]; ++ } catch (ArrayIndexOutOfBoundsException ex) { ++ return false; ++ } ++ } ++ ++ + // Skip any LWS and return the next char + private static int skipLws(StringReader input, boolean withReset) + throws IOException { +diff --git a/java/org/apache/tomcat/util/res/StringManager.java b/java/org/apache/tomcat/util/res/StringManager.java +index 67c56f0..bd0a84c 100644 +--- a/java/org/apache/tomcat/util/res/StringManager.java ++++ b/java/org/apache/tomcat/util/res/StringManager.java +@@ -179,6 +179,9 @@ public class StringManager { + private static final Map<String,Map<Locale,StringManager>> managers = + new Hashtable<String,Map<Locale,StringManager>>(); + ++ public static final StringManager getManager(Class<?> clazz) { ++ return getManager(clazz.getPackage().getName()); ++ } + /** + * Get the StringManager for a particular package. If a manager for + * a package already exists, it will be reused, else a new diff --git a/debian/patches/CVE-2016-8735.patch b/debian/patches/CVE-2016-8735.patch new file mode 100644 index 0000000..0d3a851 --- /dev/null +++ b/debian/patches/CVE-2016-8735.patch @@ -0,0 +1,24 @@ +From: Markus Koschany <[email protected]> +Date: Fri, 25 Nov 2016 20:11:08 +0100 +Subject: CVE-2016-8735 + +Origin: http://svn.apache.org/r1767684 +--- + java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java b/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java +index 7d04955..7f8ff01 100644 +--- a/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java ++++ b/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java +@@ -198,6 +198,10 @@ public class JmxRemoteLifecycleListener implements LifecycleListener { + csf = new RmiClientLocalhostSocketFactory(csf); + } + ++ env.put("jmx.remote.rmi.server.credential.types", new String[] { ++ String[].class.getName(), ++ String.class.getName() }); ++ + // Populate the env properties used to create the server + if (csf != null) { + env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, diff --git a/debian/patches/series b/debian/patches/series index 5aa3fc5..e2c4068 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -12,3 +12,5 @@ CVE-2016-6794.patch CVE-2016-6797.patch CVE-2016-5018.patch CVE-2016-6796.patch +CVE-2016-6816.patch +CVE-2016-8735.patch -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/tomcat6.git _______________________________________________ pkg-java-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-commits

