[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