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>&apos;/&apos;</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

Reply via email to