Author: markt Date: Fri Dec 12 16:10:15 2014 New Revision: 1644954 URL: http://svn.apache.org/r1644954 Log: Ensure that the result of calling HttpServletRequest.getContextPath() is neither decoded nor normalized as required by the Servlet specification.
Modified: tomcat/tc8.0.x/trunk/ (props changed) tomcat/tc8.0.x/trunk/java/org/apache/catalina/connector/LocalStrings.properties tomcat/tc8.0.x/trunk/java/org/apache/catalina/connector/Request.java tomcat/tc8.0.x/trunk/java/org/apache/tomcat/util/buf/UDecoder.java tomcat/tc8.0.x/trunk/test/org/apache/catalina/connector/TestRequest.java tomcat/tc8.0.x/trunk/webapps/docs/changelog.xml Propchange: tomcat/tc8.0.x/trunk/ ------------------------------------------------------------------------------ --- svn:mergeinfo (original) +++ svn:mergeinfo Fri Dec 12 16:10:15 2014 @@ -1 +1 @@ -/tomcat/trunk:1636524,1637156,1637176,1637188,1637331,1637684,1637695,1638720-1638725,1639653,1640010,1640084,1640088,1640275,1640322,1640347,1640361,1640365,1640403,1640410,1640652,1640655-1640658,1640688,1640700-1640883,1640903,1640976,1640978,1641000,1641026,1641038-1641039,1641051-1641052,1641058,1641064,1641300,1641369,1641374,1641380,1641486,1641634,1641656-1641692,1641704,1641707-1641718,1641720-1641722,1641735,1641981,1642233,1642280,1642554,1642564,1642595,1642606,1642668,1642679,1642697,1642699,1643002,1643045,1643054-1643055,1643066,1643121,1643128,1643206,1643209-1643210,1643216,1643249,1643270,1643283,1643309-1643310,1643323,1643365-1643366,1643370-1643371,1643465,1643474,1643570,1643634,1643649,1643651,1643654,1643675,1643731,1643733-1643734,1643761,1643766,1643814,1643937,1643963,1644017,1644169,1644201-1644203,1644321,1644323,1644516,1644523,1644815 +/tomcat/trunk:1636524,1637156,1637176,1637188,1637331,1637684,1637695,1638720-1638725,1639653,1640010,1640083-1640084,1640088,1640275,1640322,1640347,1640361,1640365,1640403,1640410,1640652,1640655-1640658,1640688,1640700-1640883,1640903,1640976,1640978,1641000,1641026,1641038-1641039,1641051-1641052,1641058,1641064,1641300,1641369,1641374,1641380,1641486,1641634,1641656-1641692,1641704,1641707-1641718,1641720-1641722,1641735,1641981,1642233,1642280,1642554,1642564,1642595,1642606,1642668,1642679,1642697,1642699,1642766,1643002,1643045,1643054-1643055,1643066,1643121,1643128,1643206,1643209-1643210,1643216,1643249,1643270,1643283,1643309-1643310,1643323,1643365-1643366,1643370-1643371,1643465,1643474,1643570,1643634,1643649,1643651,1643654,1643675,1643731,1643733-1643734,1643761,1643766,1643814,1643937,1643963,1644017,1644169,1644201-1644203,1644321,1644323,1644516,1644523,1644529,1644535,1644815 Modified: tomcat/tc8.0.x/trunk/java/org/apache/catalina/connector/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/tc8.0.x/trunk/java/org/apache/catalina/connector/LocalStrings.properties?rev=1644954&r1=1644953&r2=1644954&view=diff ============================================================================== --- tomcat/tc8.0.x/trunk/java/org/apache/catalina/connector/LocalStrings.properties (original) +++ tomcat/tc8.0.x/trunk/java/org/apache/catalina/connector/LocalStrings.properties Fri Dec 12 16:10:15 2014 @@ -34,6 +34,7 @@ coyoteConnector.parseBodyMethodNoTrace=T coyoteOutputStream.nbNotready=In non-blocking mode you may not write to the ServletOutputStream until the previous write has completed and isReady() returns true +coyoteRequest.getContextPath.ise=Unable to find match between the canonical context path [{0}] and the URI presented by the user agent [{1}] coyoteRequest.getInputStream.ise=getReader() has already been called for this request coyoteRequest.getReader.ise=getInputStream() has already been called for this request coyoteRequest.gssLifetimeFail=Failed to obtain remaining lifetime for user principal [{0}] Modified: tomcat/tc8.0.x/trunk/java/org/apache/catalina/connector/Request.java URL: http://svn.apache.org/viewvc/tomcat/tc8.0.x/trunk/java/org/apache/catalina/connector/Request.java?rev=1644954&r1=1644953&r2=1644954&view=diff ============================================================================== --- tomcat/tc8.0.x/trunk/java/org/apache/catalina/connector/Request.java (original) +++ tomcat/tc8.0.x/trunk/java/org/apache/catalina/connector/Request.java Fri Dec 12 16:10:15 2014 @@ -84,6 +84,7 @@ import org.apache.tomcat.util.ExceptionU import org.apache.tomcat.util.buf.B2CConverter; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.buf.MessageBytes; +import org.apache.tomcat.util.buf.UDecoder; import org.apache.tomcat.util.http.CookieProcessor; import org.apache.tomcat.util.http.FastHttpDateFormat; import org.apache.tomcat.util.http.Parameters; @@ -1901,24 +1902,80 @@ public class Request /** * Return the portion of the request URI used to select the Context - * of the Request. + * of the Request. The value returned is not decoded which also implies it + * is not normalised. */ @Override public String getContextPath() { + String canonicalContextPath = getServletContext().getContextPath(); String uri = getRequestURI(); + char[] uriChars = uri.toCharArray(); int lastSlash = mappingData.contextSlashCount; + // Special case handling for the root context + if (lastSlash == 0) { + return ""; + } int pos = 0; + // Need at least the number of slashes in the context path while (lastSlash > 0) { - pos = uri.indexOf('/', pos + 1); + pos = nextSlash(uriChars, pos + 1); if (pos == -1) { - return uri; + break; } lastSlash--; } - return uri.substring(0, pos); + // Now allow for normalization and/or encoding. Essentially, keep + // extending the candidate path up to the next slash until the decoded + // and normalized candidate path is the same as the canonical path. + String candidate; + if (pos == -1) { + candidate = uri; + } else { + candidate = uri.substring(0, pos); + } + candidate = UDecoder.URLDecode(candidate, connector.getURIEncoding()); + candidate = org.apache.tomcat.util.http.RequestUtil.normalize(candidate); + boolean match = canonicalContextPath.equals(candidate); + while (!match && pos != -1) { + pos = nextSlash(uriChars, pos + 1); + if (pos == -1) { + candidate = uri; + } else { + candidate = uri.substring(0, pos); + } + candidate = UDecoder.URLDecode(candidate, connector.getURIEncoding()); + candidate = org.apache.tomcat.util.http.RequestUtil.normalize(candidate); + match = canonicalContextPath.equals(candidate); + } + if (match) { + if (pos == -1) { + return uri; + } else { + return uri.substring(0, pos); + } + } else { + // Should never happen + throw new IllegalStateException(sm.getString( + "coyoteRequest.getContextPath.ise", canonicalContextPath, uri)); + } } + private int nextSlash(char[] uri, int startPos) { + int len = uri.length; + int pos = startPos; + while (pos < len) { + if (uri[pos] == '/') { + return pos; + } else if (UDecoder.ALLOW_ENCODED_SLASH && uri[pos] == '%' && pos + 2 < len && + uri[pos+1] == '2' && (uri[pos + 2] == 'f' || uri[pos + 2] == 'F')) { + return pos; + } + pos++; + } + return -1; + } + /** * Return the set of Cookies received with this Request. Triggers parsing of * the Cookie HTTP headers followed by conversion to Cookie objects if this Modified: tomcat/tc8.0.x/trunk/java/org/apache/tomcat/util/buf/UDecoder.java URL: http://svn.apache.org/viewvc/tomcat/tc8.0.x/trunk/java/org/apache/tomcat/util/buf/UDecoder.java?rev=1644954&r1=1644953&r2=1644954&view=diff ============================================================================== --- tomcat/tc8.0.x/trunk/java/org/apache/tomcat/util/buf/UDecoder.java (original) +++ tomcat/tc8.0.x/trunk/java/org/apache/tomcat/util/buf/UDecoder.java Fri Dec 12 16:10:15 2014 @@ -40,7 +40,7 @@ public final class UDecoder { private static final Log log = LogFactory.getLog(UDecoder.class); - private static final boolean ALLOW_ENCODED_SLASH = + public static final boolean ALLOW_ENCODED_SLASH = Boolean.valueOf(System.getProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "false")).booleanValue(); private static class DecodeException extends CharConversionException { Modified: tomcat/tc8.0.x/trunk/test/org/apache/catalina/connector/TestRequest.java URL: http://svn.apache.org/viewvc/tomcat/tc8.0.x/trunk/test/org/apache/catalina/connector/TestRequest.java?rev=1644954&r1=1644953&r2=1644954&view=diff ============================================================================== --- tomcat/tc8.0.x/trunk/test/org/apache/catalina/connector/TestRequest.java (original) +++ tomcat/tc8.0.x/trunk/test/org/apache/catalina/connector/TestRequest.java Fri Dec 12 16:10:15 2014 @@ -41,6 +41,7 @@ import static org.junit.Assert.assertTru import static org.junit.Assert.fail; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; @@ -61,6 +62,12 @@ import org.apache.tomcat.util.descriptor */ public class TestRequest extends TomcatBaseTest { + @BeforeClass + public static void setup() { + // Some of these tests need this and it used statically so set it once + System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true"); + } + /** * Test case for https://issues.apache.org/bugzilla/show_bug.cgi?id=37794 * POST parameters are not returned from a call to @@ -762,6 +769,36 @@ public class TestRequest extends TomcatB doBug56501("/pa_th/abc", "/pa_th/abc/xxx", "/pa_th/abc"); } + @Test + public void testBug57215a() throws Exception { + doBug56501("/path", "//path", "//path"); + } + + @Test + public void testBug57215b() throws Exception { + doBug56501("/path", "//path/", "//path"); + } + + @Test + public void testBug57215c() throws Exception { + doBug56501("/path", "/%2Fpath", "/%2Fpath"); + } + + @Test + public void testBug57215d() throws Exception { + doBug56501("/path", "/%2Fpath%2F", "/%2Fpath"); + } + + @Test + public void testBug57215e() throws Exception { + doBug56501("/path", "/foo/../path", "/foo/../path"); + } + + @Test + public void testBug57215f() throws Exception { + doBug56501("/path", "/foo/..%2fpath", "/foo/..%2fpath"); + } + private void doBug56501(String deployPath, String requestPath, String expected) throws Exception { Modified: tomcat/tc8.0.x/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/tc8.0.x/trunk/webapps/docs/changelog.xml?rev=1644954&r1=1644953&r2=1644954&view=diff ============================================================================== --- tomcat/tc8.0.x/trunk/webapps/docs/changelog.xml (original) +++ tomcat/tc8.0.x/trunk/webapps/docs/changelog.xml Fri Dec 12 16:10:15 2014 @@ -71,6 +71,11 @@ a patch provided by Jason McIntosh. (violetagg) </fix> <fix> + <bug>57215</bug>: Ensure that the result of calling + <code>HttpServletRequest.getContextPath()</code> is neither decoded nor + normalized as required by the Servlet specification. (markt) + </fix> + <fix> <bug>57216</bug>: Improve handling of invalid context paths. A context path should either be an empty string or start with a <code>'/'</code> and do not end with a --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org