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 <[email protected]> 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 _______________________________________________ pkg-java-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-commits

