Modified: tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/parser/HttpParser.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/parser/HttpParser.java?rev=1830313&r1=1830312&r2=1830313&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/parser/HttpParser.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/parser/HttpParser.java Fri Apr 27 08:45:09 2018 @@ -68,7 +68,6 @@ public class HttpParser { 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]; private static final boolean[] IS_ALPHA = new boolean[ARRAY_SIZE]; private static final boolean[] IS_NUMERIC = new boolean[ARRAY_SIZE]; @@ -76,8 +75,10 @@ public class HttpParser { private static final boolean[] IS_UNRESERVED = new boolean[ARRAY_SIZE]; private static final boolean[] IS_SUBDELIM = new boolean[ARRAY_SIZE]; private static final boolean[] IS_USERINFO = new boolean[ARRAY_SIZE]; - private static final boolean[] IS_ABSOLUTEPATH = new boolean[ARRAY_SIZE]; - private static final boolean[] IS_QUERY = new boolean[ARRAY_SIZE]; + private static final boolean[] IS_RELAXABLE = new boolean[ARRAY_SIZE]; + + private static final HttpParser DEFAULT; + static { // Digest field types. @@ -99,19 +100,6 @@ public class HttpParser { // RFC2617 says nc is 8LHEX. <">8LHEX<"> will also be accepted fieldTypes.put("nc", FIELD_TYPE_LHEX); - String prop = System.getProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow"); - if (prop != null) { - for (int i = 0; i < prop.length(); i++) { - char c = prop.charAt(i); - if (c == '{' || c == '}' || c == '|') { - REQUEST_TARGET_ALLOW[c] = true; - } else { - log.warn(sm.getString("http.invalidRequestTargetCharacter", - Character.valueOf(c))); - } - } - } - for (int i = 0; i < ARRAY_SIZE; i++) { // Control> 0-31, 127 if (i < 32 || i == 127) { @@ -136,17 +124,6 @@ public class HttpParser { 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 == '}') { - if (!REQUEST_TARGET_ALLOW[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')) { @@ -165,8 +142,8 @@ public class HttpParser { IS_UNRESERVED[i] = true; } - if (i == '!' || i == '$' || i == '&' || i == '\'' || i == '(' || i == ')' || i == '*' || i == '+' || - i == ',' || i == ';' || i == '=') { + if (i == '!' || i == '$' || i == '&' || i == '\'' || i == '(' || i == ')' || i == '*' || + i == '+' || i == ',' || i == ';' || i == '=') { IS_SUBDELIM[i] = true; } @@ -175,6 +152,50 @@ public class HttpParser { IS_USERINFO[i] = true; } + // The characters that are normally not permitted for which the + // restrictions may be relaxed when used in the path and/or query + // string + if (i == '\"' || i == '<' || i == '>' || i == '[' || i == '\\' || i == ']' || + i == '^' || i == '`' || i == '{' || i == '|' || i == '}') { + IS_RELAXABLE[i] = true; + } + } + + String prop = System.getProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow"); + if (prop != null) { + for (int i = 0; i < prop.length(); i++) { + char c = prop.charAt(i); + if (c == '{' || c == '}' || c == '|') { + REQUEST_TARGET_ALLOW[c] = true; + } else { + log.warn(sm.getString("http.invalidRequestTargetCharacter", + Character.valueOf(c))); + } + } + } + + DEFAULT = new HttpParser(null, null); + } + + + private final boolean[] IS_NOT_REQUEST_TARGET = new boolean[ARRAY_SIZE]; + private final boolean[] IS_ABSOLUTEPATH_RELAXED = new boolean[ARRAY_SIZE]; + private final boolean[] IS_QUERY_RELAXED = new boolean[ARRAY_SIZE]; + + + public HttpParser(String relaxedPathChars, String relaxedQueryChars) { + for (int i = 0; i < ARRAY_SIZE; i++) { + // 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 == '}') { + if (!REQUEST_TARGET_ALLOW[i]) { + IS_NOT_REQUEST_TARGET[i] = true; + } + } + /* * absolute-path = 1*( "/" segment ) * segment = *pchar @@ -182,8 +203,8 @@ public class HttpParser { * * Note pchar allows everything userinfo allows plus "@" */ - if (IS_USERINFO[i] || i == '@' || i == '/') { - IS_ABSOLUTEPATH[i] = true; + if (IS_USERINFO[i] || i == '@' || i == '/' || REQUEST_TARGET_ALLOW[i]) { + IS_ABSOLUTEPATH_RELAXED[i] = true; } /* @@ -191,10 +212,13 @@ public class HttpParser { * * Note query allows everything absolute-path allows plus "?" */ - if (IS_ABSOLUTEPATH[i] || i == '?') { - IS_QUERY[i] = true; + if (IS_ABSOLUTEPATH_RELAXED[i] || i == '?' || REQUEST_TARGET_ALLOW[i]) { + IS_QUERY_RELAXED[i] = true; } } + + relax(IS_ABSOLUTEPATH_RELAXED, relaxedPathChars); + relax(IS_QUERY_RELAXED, relaxedQueryChars); } /** @@ -327,6 +351,39 @@ public class HttpParser { } + public boolean isNotRequestTargetRelaxed(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 boolean isAbsolutePathRelaxed(int c) { + // Fast for valid user info characters, slower for some incorrect + // ones + try { + return IS_ABSOLUTEPATH_RELAXED[c]; + } catch (ArrayIndexOutOfBoundsException ex) { + return false; + } + } + + + public boolean isQueryRelaxed(int c) { + // Fast for valid user info characters, slower for some incorrect + // ones + try { + return IS_QUERY_RELAXED[c]; + } catch (ArrayIndexOutOfBoundsException ex) { + return false; + } + } + + public static String unquote(String input) { if (input == null || input.length() < 2) { return input; @@ -379,13 +436,7 @@ public class HttpParser { 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; - } + return DEFAULT.isNotRequestTargetRelaxed(c); } @@ -433,25 +484,24 @@ public class HttpParser { } - public static boolean isAbsolutePath(int c) { + private static boolean isRelaxable(int c) { // Fast for valid user info characters, slower for some incorrect // ones try { - return IS_ABSOLUTEPATH[c]; + return IS_RELAXABLE[c]; } catch (ArrayIndexOutOfBoundsException ex) { return false; } } + public static boolean isAbsolutePath(int c) { + return DEFAULT.isAbsolutePathRelaxed(c); + } + + public static boolean isQuery(int c) { - // Fast for valid user info characters, slower for some incorrect - // ones - try { - return IS_QUERY[c]; - } catch (ArrayIndexOutOfBoundsException ex) { - return false; - } + return DEFAULT.isQueryRelaxed(c); } @@ -905,12 +955,27 @@ public class HttpParser { } } + + private void relax(boolean[] flags, String relaxedChars) { + if (relaxedChars != null && relaxedChars.length() > 0) { + char[] chars = relaxedChars.toCharArray(); + for (char c : chars) { + if (isRelaxable(c)) { + flags[c] = true; + IS_NOT_REQUEST_TARGET[c] = false; + } + } + } + } + + private enum AllowsEnd { NEVER, FIRST, ALWAYS } + private enum DomainParseState { NEW( true, false, false, AllowsEnd.NEVER, AllowsEnd.NEVER, " at the start of"), ALL_ALPHA( true, true, true, AllowsEnd.ALWAYS, AllowsEnd.ALWAYS, " after a letter in"),
Modified: tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml?rev=1830313&r1=1830312&r2=1830313&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml (original) +++ tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml Fri Apr 27 08:45:09 2018 @@ -111,6 +111,12 @@ contains multiple <code>Host</code> headers is rejected with a 400 response. (markt) </fix> + <add> + <bug>62273</bug>: Implement configuration options to work-around + specification non-compliant user agents (including all the major + browsers) that do not correctly %nn encode URI paths and query strings + as required by RFC 7230 and RFC 3986. (markt) + </add> </changelog> </subsection> <subsection name="Jasper"> Modified: tomcat/tc7.0.x/trunk/webapps/docs/config/http.xml URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/config/http.xml?rev=1830313&r1=1830312&r2=1830313&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/docs/config/http.xml (original) +++ tomcat/tc7.0.x/trunk/webapps/docs/config/http.xml Fri Apr 27 08:45:09 2018 @@ -532,6 +532,32 @@ illegal header will be ignored.</p> </attribute> + <attribute name="relaxedPathChars" required="false"> + <p>The <a href="https://tools.ietf.org/rfc/rfc7230.txt">HTTP/1.1 + specification</a> requires that certain characters are %nn encoded when + used in URI paths. Unfortunately, many user agents including all the major + browsers are not compliant with this specification and use these + characters in unencoded form. To prevent Tomcat rejecting such requests, + this attribute may be used to specify the additional characters to allow. + If not specified, no addtional characters will be allowed. The value may + be any combination of the following characters: + <code>" < > [ \ ] ^ ` { | }</code> . Any other characters + present in the value will be ignored.</p> + </attribute> + + <attribute name="relaxedQueryChars" required="false"> + <p>The <a href="https://tools.ietf.org/rfc/rfc7230.txt">HTTP/1.1 + specification</a> requires that certain characters are %nn encoded when + used in URI query strings. Unfortunately, many user agents including all + the major browsers are not compliant with this specification and use these + characters in unencoded form. To prevent Tomcat rejecting such requests, + this attribute may be used to specify the additional characters to allow. + If not specified, no addtional characters will be allowed. The value may + be any combination of the following characters: + <code>" < > [ \ ] ^ ` { | }</code> . Any other characters + present in the value will be ignored.</p> + </attribute> + <attribute name="restrictedUserAgents" required="false"> <p>The value is a regular expression (using <code>java.util.regex</code>) matching the <code>user-agent</code> header of HTTP clients for which Modified: tomcat/tc7.0.x/trunk/webapps/docs/config/systemprops.xml URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/config/systemprops.xml?rev=1830313&r1=1830312&r2=1830313&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/docs/config/systemprops.xml (original) +++ tomcat/tc7.0.x/trunk/webapps/docs/config/systemprops.xml Fri Apr 27 08:45:09 2018 @@ -716,11 +716,15 @@ </property> <property name="tomcat.util.http.parser.HttpParser. requestTargetAllow"> + <p><i>This system property is deprecated. Use the + <code>relaxedPathChars</code> and <code>relaxedQueryChars</code> + attributes of the Connector instead. These attributes permit a wider range + of characters to be configured as valid.</i></p> <p>A string comprised of characters the server should allow even when they are not encoded. These characters would normally result in a 400 status.</p> <p>The acceptable characters for this property are: <code>|</code>, <code>{</code> , and <code>}</code></p> - <p><strong>WARNING</strong>: Use of this option will expose the server to CVE-2016-6816. + <p><strong>WARNING</strong>: Use of this option may expose the server to CVE-2016-6816. </p> <p>If not specified, the default value of <code>null</code> will be used.</p> </property> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org