[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