[tomcat6] 01/01: Import Debian patch 6.0.45+dfsg-1~deb7u3
Markus Koschany
apo at moszumanska.debian.org
Fri Nov 25 23:38:43 UTC 2016
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 <apo at debian.org>
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 <apo at debian.org> Mon, 07 Nov 2016 13:46:30 +0100
+ -- Markus Koschany <apo at debian.org> 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 <apo at debian.org>
+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 <apo at debian.org>
+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
More information about the pkg-java-commits
mailing list