[tomcat7] 01/03: Fixed CVE-2016-6816: Information Disclosure
Emmanuel Bourg
ebourg-guest at moszumanska.debian.org
Fri Dec 9 00:34:48 UTC 2016
This is an automated email from the git hooks/post-receive script.
ebourg-guest pushed a commit to branch jessie
in repository tomcat7.
commit 3e6202cdee38f7bc53822fe22d96d9a7f93f505e
Author: Emmanuel Bourg <ebourg at apache.org>
Date: Fri Dec 9 00:30:29 2016 +0100
Fixed CVE-2016-6816: Information Disclosure
---
debian/changelog | 6 +
debian/patches/CVE-2016-6816.patch | 414 +++++++++++++++++++++++++++++++++++++
debian/patches/series | 1 +
3 files changed, 421 insertions(+)
diff --git a/debian/changelog b/debian/changelog
index 70823d0..eb01359 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -4,6 +4,12 @@ tomcat7 (7.0.56-3+deb8u6) UNRELEASED; urgency=medium
package is upgraded. Thanks to Paul Szabo for the report (see #845393)
* Fixed CVE-2016-9775: Potential privilege escalation when the tomcat7
package is purged. Thanks to Paul Szabo for the report (see #845385)
+ * 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.
* CVE-2016-5018 follow-up: Applied a missing modification fixing
a ClassNotFoundException when the security manager is enabled
(Closes: #846298)
diff --git a/debian/patches/CVE-2016-6816.patch b/debian/patches/CVE-2016-6816.patch
new file mode 100644
index 0000000..85304a1
--- /dev/null
+++ b/debian/patches/CVE-2016-6816.patch
@@ -0,0 +1,414 @@
+Description: Fixes 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.
+Origin: backport, https://svn.apache.org/r1767675
+--- a/java/org/apache/coyote/http11/AbstractInputBuffer.java
++++ b/java/org/apache/coyote/http11/AbstractInputBuffer.java
+@@ -28,8 +28,6 @@
+
+ public abstract class AbstractInputBuffer<S> implements InputBuffer{
+
+- protected static final boolean[] HTTP_TOKEN_CHAR = new boolean[128];
+-
+ /**
+ * The string manager for this package.
+ */
+@@ -37,57 +35,6 @@
+ StringManager.getManager(Constants.Package);
+
+
+- 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 if (i == '\t') {
+- HTTP_TOKEN_CHAR[i] = false;
+- } else {
+- HTTP_TOKEN_CHAR[i] = true;
+- }
+- }
+- }
+-
+-
+ /**
+ * Associated Coyote request.
+ */
+--- a/java/org/apache/coyote/http11/InternalAprInputBuffer.java
++++ b/java/org/apache/coyote/http11/InternalAprInputBuffer.java
+@@ -30,6 +30,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.parser.HttpParser;
+ import org.apache.tomcat.util.net.AbstractEndpoint;
+ import org.apache.tomcat.util.net.SocketWrapper;
+
+@@ -170,15 +171,13 @@
+ 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++;
+@@ -231,6 +230,8 @@
+ } else if ((buf[pos] == Constants.QUESTION)
+ && (questionPos == -1)) {
+ questionPos = pos;
++ } else if (HttpParser.isNotRequestTarget(buf[pos])) {
++ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
+ }
+
+ pos++;
+@@ -267,7 +268,7 @@
+
+ //
+ // Reading the protocol
+- // Protocol is always US-ASCII
++ // Protocol is always "HTTP/" DIGIT "." DIGIT
+ //
+
+ while (!eol) {
+@@ -284,6 +285,8 @@
+ if (end == 0)
+ end = pos;
+ eol = true;
++ } else if (!HttpParser.isHttpProtocol(buf[pos])) {
++ throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol"));
+ }
+
+ pos++;
+@@ -382,7 +385,7 @@
+ 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);
+--- a/java/org/apache/coyote/http11/InternalInputBuffer.java
++++ b/java/org/apache/coyote/http11/InternalInputBuffer.java
+@@ -28,6 +28,7 @@
+ import org.apache.juli.logging.LogFactory;
+ import org.apache.tomcat.util.buf.ByteChunk;
+ import org.apache.tomcat.util.buf.MessageBytes;
++import org.apache.tomcat.util.http.parser.HttpParser;
+ import org.apache.tomcat.util.net.AbstractEndpoint;
+ import org.apache.tomcat.util.net.SocketWrapper;
+
+@@ -126,15 +127,13 @@
+ 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++;
+@@ -188,6 +187,8 @@
+ } else if ((buf[pos] == Constants.QUESTION)
+ && (questionPos == -1)) {
+ questionPos = pos;
++ } else if (HttpParser.isNotRequestTarget(buf[pos])) {
++ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
+ }
+
+ pos++;
+@@ -223,7 +224,7 @@
+
+ //
+ // Reading the protocol
+- // Protocol is always US-ASCII
++ // Protocol is always "HTTP/" DIGIT "." DIGIT
+ //
+
+ while (!eol) {
+@@ -240,6 +241,8 @@
+ if (end == 0)
+ end = pos;
+ eol = true;
++ } else if (!HttpParser.isHttpProtocol(buf[pos])) {
++ throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol"));
+ }
+
+ pos++;
+@@ -338,7 +341,7 @@
+ 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);
+--- a/java/org/apache/coyote/http11/InternalNioInputBuffer.java
++++ b/java/org/apache/coyote/http11/InternalNioInputBuffer.java
+@@ -25,6 +25,7 @@
+ 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.parser.HttpParser;
+ import org.apache.tomcat.util.net.AbstractEndpoint;
+ import org.apache.tomcat.util.net.NioChannel;
+ import org.apache.tomcat.util.net.NioEndpoint;
+@@ -257,14 +258,13 @@
+ 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++;
+ }
+@@ -314,6 +314,8 @@
+ } else if ((buf[pos] == Constants.QUESTION)
+ && (parsingRequestLineQPos == -1)) {
+ parsingRequestLineQPos = pos;
++ } else if (HttpParser.isNotRequestTarget(buf[pos])) {
++ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
+ }
+ pos++;
+ }
+@@ -351,7 +353,7 @@
+ if (parsingRequestLinePhase == 6) {
+ //
+ // Reading the protocol
+- // Protocol is always US-ASCII
++ // Protocol is always "HTTP/" DIGIT "." DIGIT
+ //
+ while (!parsingRequestLineEol) {
+ // Read new bytes if needed
+@@ -366,6 +368,8 @@
+ if (end == 0)
+ end = pos;
+ parsingRequestLineEol = true;
++ } else if (!HttpParser.isHttpProtocol(buf[pos])) {
++ throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol"));
+ }
+ pos++;
+ }
+@@ -558,7 +562,7 @@
+ 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;
+--- a/java/org/apache/coyote/http11/LocalStrings.properties
++++ b/java/org/apache/coyote/http11/LocalStrings.properties
+@@ -39,8 +39,10 @@
+ http11processor.sendfile.error=Error sending data using sendfile. May be caused by invalid request attributes for start/end points
+
+ iib.eof.error=Unexpected EOF read on the socket
+-iib.invalidheader=The HTTP header line [{0}] does not conform to RFC 2616 and has been ignored.
++iib.invalidheader=The HTTP header line [{0}] does not conform to RFC 7230 and has been ignored.
+ iib.invalidmethod=Invalid character (CR or LF) found in method name
++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.parseheaders.ise.error=Unexpected state: headers already parsed. Buffer not recycled?
+ iib.requestheadertoolarge.error=Request header is too large
+
+--- a/java/org/apache/tomcat/util/http/parser/HttpParser.java
++++ b/java/org/apache/tomcat/util/http/parser/HttpParser.java
+@@ -53,9 +53,14 @@
+ private static final Map<String,Integer> fieldTypes =
+ new HashMap<String,Integer>();
+
+- // Arrays used by isToken(), isHex()
+- private static final boolean isToken[] = new boolean[128];
+- private static final boolean isHex[] = new boolean[128];
++ private static final int ARRAY_SIZE = 128;
++
++ private static final boolean[] IS_CONTROL = new boolean[ARRAY_SIZE];
++ private static final boolean[] IS_SEPARATOR = new boolean[ARRAY_SIZE];
++ private static final boolean[] IS_TOKEN = new boolean[ARRAY_SIZE];
++ private static final boolean[] IS_HEX = new boolean[ARRAY_SIZE];
++ private static final boolean[] IS_NOT_REQUEST_TARGET = new boolean[ARRAY_SIZE];
++ private static final boolean[] IS_HTTP_PROTOCOL = new boolean[ARRAY_SIZE];
+
+ static {
+ // Digest field types.
+@@ -77,24 +82,43 @@
+ // RFC2617 says nc is 8LHEX. <">8LHEX<"> will also be accepted
+ fieldTypes.put("nc", FIELD_TYPE_LHEX);
+
+- // Setup the flag arrays
+- for (int i = 0; i < 128; i++) {
+- if (i < 32) {
+- isToken[i] = false;
+- } else if (i == '(' || i == ')' || i == '<' || i == '>' || i == '@' ||
+- i == ',' || i == ';' || i == ':' || i == '\\' || i == '\"' ||
+- i == '/' || i == '[' || i == ']' || i == '?' || i == '=' ||
+- i == '{' || i == '}' || i == ' ' || i == '\t') {
+- isToken[i] = false;
+- } else {
+- isToken[i] = true;
++ for (int i = 0; i < ARRAY_SIZE; i++) {
++ // Control> 0-31, 127
++ if (i < 32 || i == 127) {
++ IS_CONTROL[i] = true;
+ }
+
+- if (i >= '0' && i <= '9' || i >= 'A' && i <= 'F' ||
+- i >= 'a' && i <= 'f') {
+- isHex[i] = true;
+- } else {
+- isHex[i] = false;
++ // Separator
++ if ( i == '(' || i == ')' || i == '<' || i == '>' || i == '@' ||
++ i == ',' || i == ';' || i == ':' || i == '\\' || i == '\"' ||
++ i == '/' || i == '[' || i == ']' || i == '?' || i == '=' ||
++ i == '{' || i == '}' || i == ' ' || i == '\t') {
++ IS_SEPARATOR[i] = true;
++ }
++
++ // Token: Anything 0-127 that is not a control and not a separator
++ if (!IS_CONTROL[i] && !IS_SEPARATOR[i] && i < 128) {
++ IS_TOKEN[i] = true;
++ }
++
++ // Hex: 0-9, a-f, A-F
++ if ((i >= '0' && i <='9') || (i >= 'a' && i <= 'f') || (i >= 'A' && i <= 'F')) {
++ IS_HEX[i] = true;
++ }
++
++ // 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,19 +270,41 @@
+ 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];
++ return IS_TOKEN[c];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ return false;
+ }
+ }
+
+- private static boolean isHex(int c) {
+- // Fast for correct values, slower for incorrect ones
++ public static boolean isHex(int c) {
++ // Fast for correct values, slower for some incorrect ones
++ try {
++ return IS_HEX[c];
++ } catch (ArrayIndexOutOfBoundsException ex) {
++ return false;
++ }
++ }
++
++
++ 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 isHex[c];
++ return IS_HTTP_PROTOCOL[c];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ return false;
+ }
diff --git a/debian/patches/series b/debian/patches/series
index 063daac..18753e0 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -34,3 +34,4 @@ CVE-2016-5018.patch
CVE-2016-6794.patch
CVE-2016-6796.patch
CVE-2016-6797.patch
+CVE-2016-6816.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