[tomcat8] 01/10: Fixed CVE-2016-6816: Information Disclosure
Emmanuel Bourg
ebourg-guest at moszumanska.debian.org
Wed Nov 23 19:54:11 UTC 2016
This is an automated email from the git hooks/post-receive script.
ebourg-guest pushed a commit to branch jessie
in repository tomcat8.
commit e3f8d0f8cb01ee318848b5a932c8e447dc14ad21
Author: Emmanuel Bourg <ebourg at apache.org>
Date: Tue Nov 22 23:24:08 2016 +0100
Fixed CVE-2016-6816: Information Disclosure
---
debian/changelog | 11 +
debian/patches/CVE-2016-6816.patch | 408 +++++++++++++++++++++++++++++++++++++
debian/patches/series | 1 +
3 files changed, 420 insertions(+)
diff --git a/debian/changelog b/debian/changelog
index 33c3ed1..93d26d5 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,14 @@
+tomcat8 (8.0.14-1+deb8u5) UNRELEASED; urgency=medium
+
+ * 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.
+
+ -- Emmanuel Bourg <ebourg at apache.org> Tue, 22 Nov 2016 23:21:56 +0100
+
tomcat8 (8.0.14-1+deb8u4) jessie-security; urgency=medium
* Fixed CVE-2016-0762: The Realm implementations did not process the supplied
diff --git a/debian/patches/CVE-2016-6816.patch b/debian/patches/CVE-2016-6816.patch
new file mode 100644
index 0000000..d56aa07
--- /dev/null
+++ b/debian/patches/CVE-2016-6816.patch
@@ -0,0 +1,408 @@
+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/r1767653
+--- a/java/org/apache/coyote/http11/AbstractInputBuffer.java
++++ b/java/org/apache/coyote/http11/AbstractInputBuffer.java
+@@ -29,8 +29,6 @@
+
+ public abstract class AbstractInputBuffer<S> implements InputBuffer{
+
+- protected static final boolean[] HTTP_TOKEN_CHAR = new boolean[128];
+-
+ /**
+ * The string manager for this package.
+ */
+@@ -38,57 +36,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/AbstractNioInputBuffer.java
++++ b/java/org/apache/coyote/http11/AbstractNioInputBuffer.java
+@@ -21,6 +21,7 @@
+
+ import org.apache.coyote.Request;
+ import org.apache.tomcat.util.buf.MessageBytes;
++import org.apache.tomcat.util.http.parser.HttpParser;
+
+ public abstract class AbstractNioInputBuffer<S> extends AbstractInputBuffer<S> {
+
+@@ -223,14 +224,14 @@
+ if (!fill(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++;
+ }
+@@ -280,6 +281,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++;
+ }
+@@ -316,7 +319,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
+@@ -331,6 +334,8 @@
+ if (end == 0)
+ end = pos;
+ parsingRequestLineEol = true;
++ } else if (!HttpParser.isHttpProtocol(buf[pos])) {
++ throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol"));
+ }
+ pos++;
+ }
+@@ -471,7 +476,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/InternalAprInputBuffer.java
++++ b/java/org/apache/coyote/http11/InternalAprInputBuffer.java
+@@ -32,6 +32,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;
+
+@@ -176,15 +177,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++;
+@@ -237,6 +236,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++;
+@@ -272,7 +273,7 @@
+
+ //
+ // Reading the protocol
+- // Protocol is always US-ASCII
++ // Protocol is always "HTTP/" DIGIT "." DIGIT
+ //
+
+ while (!eol) {
+@@ -289,6 +290,8 @@
+ if (end == 0)
+ end = pos;
+ eol = true;
++ } else if (!HttpParser.isHttpProtocol(buf[pos])) {
++ throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol"));
+ }
+
+ pos++;
+@@ -387,7 +390,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;
+
+@@ -137,15 +138,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++;
+@@ -199,6 +198,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++;
+@@ -233,7 +234,7 @@
+
+ //
+ // Reading the protocol
+- // Protocol is always US-ASCII
++ // Protocol is always "HTTP/" DIGIT "." DIGIT
+ //
+
+ while (!eol) {
+@@ -250,6 +251,8 @@
+ if (end == 0)
+ end = pos;
+ eol = true;
++ } else if (!HttpParser.isHttpProtocol(buf[pos])) {
++ throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol"));
+ }
+
+ pos++;
+@@ -348,7 +351,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/LocalStrings.properties
++++ b/java/org/apache/coyote/http11/LocalStrings.properties
+@@ -31,8 +31,10 @@
+ iib.eof.error=Unexpected EOF read on the socket
+ iib.failedread.apr=Read failed with APR/native error code [{0}]
+ iib.filter.npe=You may not add a null filter
+-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.readtimeout=Timeout attempting to read data from the socket
+ 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
+@@ -34,29 +34,53 @@
+ */
+ public class HttpParser {
+
+- // 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 {
+- // 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;
+ }
+ }
+ }
+@@ -79,10 +103,10 @@
+ return result.toString();
+ }
+
+- static boolean isToken(int c) {
+- // Fast for correct values, slower for incorrect ones
++ public static boolean isToken(int c) {
++ // Fast for correct values, slower for some incorrect ones
+ try {
+- return isToken[c];
++ return IS_TOKEN[c];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ return false;
+ }
+@@ -91,7 +115,29 @@
+ static boolean isHex(int c) {
+ // Fast for correct values, slower for incorrect ones
+ try {
+- return isHex[c];
++ 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 IS_HTTP_PROTOCOL[c];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ return false;
+ }
diff --git a/debian/patches/series b/debian/patches/series
index e68070d..aec9c90 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -26,3 +26,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/tomcat8.git
More information about the pkg-java-commits
mailing list