Author: markt
Date: Wed Jul 27 16:24:26 2016
New Revision: 1754306
URL: http://svn.apache.org/viewvc?rev=1754306&view=rev
Log:
Decode the path provided to the request dispatcher by default. The reasoning is:
- the servlet spec is clear (see 9.1.1) that path can include a query string;
- the query string is delimited by '?';
- '?' is a valid character in the path (if it is encoded);
- users may want to use '?'in the path;
- therefore users need to be able to provide an encoded path;
- therefore the RequestDispatcher needs to decode the path.
This change should be transparent unless an application is passing an unencoded
% to the request dispatcher which should be fairly rare.
The behaviour is configurable via a Context attribute so the previous behaviour
can easily be restored.
Added:
tomcat/tc6.0.x/trunk/test/org/apache/catalina/core/TestApplicationContext.java
- copied, changed from r1754287,
tomcat/tc7.0.x/trunk/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcher.java
Modified:
tomcat/tc6.0.x/trunk/java/org/apache/catalina/Context.java
tomcat/tc6.0.x/trunk/java/org/apache/catalina/connector/Request.java
tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/ApplicationContext.java
tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/LocalStrings.properties
tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardContext.java
tomcat/tc6.0.x/trunk/java/org/apache/catalina/util/URLEncoder.java
tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml
tomcat/tc6.0.x/trunk/webapps/docs/config/context.xml
Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/Context.java
URL:
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/Context.java?rev=1754306&r1=1754305&r2=1754306&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/Context.java (original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/Context.java Wed Jul 27
16:24:26 2016
@@ -1232,4 +1232,26 @@ public interface Context extends Contain
* Context.
*/
public boolean getMapperDirectoryRedirectEnabled();
+
+ /**
+ * Are paths used in calls to obtain a request dispatcher expected to be
+ * encoded? This affects both how Tomcat handles calls to obtain a request
+ * dispatcher as well as how Tomcat generates paths used to obtain request
+ * dispatchers internally.
+ *
+ * @param dispatchersUseEncodedPaths {@code true} to use encoded paths,
+ * otherwise {@code false}
+ */
+ public void setDispatchersUseEncodedPaths(boolean
dispatchersUseEncodedPaths);
+
+ /**
+ * Are paths used in calls to obtain a request dispatcher expected to be
+ * encoded? This applys to both how Tomcat handles calls to obtain a
request
+ * dispatcher as well as how Tomcat generates paths used to obtain request
+ * dispatchers internally.
+ *
+ * @return {@code true} if encoded paths will be used, otherwise
+ * {@code false}
+ */
+ public boolean getDispatchersUseEncodedPaths();
}
Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/connector/Request.java
URL:
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/connector/Request.java?rev=1754306&r1=1754305&r2=1754306&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/connector/Request.java
(original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/connector/Request.java Wed
Jul 27 16:24:26 2016
@@ -72,6 +72,7 @@ import org.apache.catalina.util.Enumerat
import org.apache.catalina.util.ParameterMap;
import org.apache.catalina.util.StringManager;
import org.apache.catalina.util.StringParser;
+import org.apache.catalina.util.URLEncoder;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
@@ -1344,14 +1345,22 @@ public class Request
int pos = requestPath.lastIndexOf('/');
String relative = null;
- if (pos >= 0) {
- relative = requestPath.substring(0, pos + 1) + path;
+ if (context.getDispatchersUseEncodedPaths()) {
+ if (pos >= 0) {
+ relative = URLEncoder.DEFAULT.encode(
+ requestPath.substring(0, pos + 1), "UTF-8") + path;
+ } else {
+ relative = URLEncoder.DEFAULT.encode(requestPath, "UTF-8") +
path;
+ }
} else {
- relative = requestPath + path;
+ if (pos >= 0) {
+ relative = requestPath.substring(0, pos + 1) + path;
+ } else {
+ relative = requestPath + path;
+ }
}
return (context.getServletContext().getRequestDispatcher(relative));
-
}
Modified:
tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/ApplicationContext.java
URL:
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/ApplicationContext.java?rev=1754306&r1=1754305&r2=1754306&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/ApplicationContext.java
(original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/ApplicationContext.java
Wed Jul 27 16:24:26 2016
@@ -21,8 +21,10 @@ package org.apache.catalina.core;
import java.io.File;
import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
+import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
@@ -50,6 +52,7 @@ import org.apache.catalina.util.RequestU
import org.apache.catalina.util.ResourceSet;
import org.apache.catalina.util.ServerInfo;
import org.apache.catalina.util.StringManager;
+import org.apache.catalina.util.URLEncoder;
import org.apache.naming.resources.DirContextURLStreamHandler;
import org.apache.naming.resources.Resource;
import org.apache.tomcat.util.buf.CharChunk;
@@ -406,17 +409,38 @@ public class ApplicationContext implemen
// Get query string
String queryString = null;
- int pos = path.indexOf('?');
+ String normalizedPath = path;
+ int pos = normalizedPath.indexOf('?');
if (pos >= 0) {
- queryString = path.substring(pos + 1);
- path = path.substring(0, pos);
+ queryString = normalizedPath.substring(pos + 1);
+ normalizedPath = normalizedPath.substring(0, pos);
}
- path = RequestUtil.normalize(path);
- if (path == null)
+ normalizedPath = RequestUtil.normalize(normalizedPath);
+ if (normalizedPath == null)
return (null);
- pos = path.length();
+ if (getContext().getDispatchersUseEncodedPaths()) {
+ // Decode
+ String decodedPath;
+ try {
+ decodedPath = URLDecoder.decode(normalizedPath, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ // Impossible
+ return null;
+ }
+
+ // Security check to catch attempts to encode /../ sequences
+ normalizedPath = RequestUtil.normalize(decodedPath);
+ if (!decodedPath.equals(normalizedPath)) {
+ getContext().getLogger().warn(
+ sm.getString("applicationContext.illegalDispatchPath",
path),
+ new IllegalArgumentException());
+ return null;
+ }
+ }
+
+ pos = normalizedPath.length();
// Use the thread local URI and mapping data
DispatchData dd = dispatchData.get();
@@ -439,11 +463,11 @@ public class ApplicationContext implemen
* Ignore any trailing path params (separated by ';') for mapping
* purposes
*/
- int semicolon = path.indexOf(';');
+ int semicolon = normalizedPath.indexOf(';');
if (pos >= 0 && semicolon > pos) {
semicolon = -1;
}
- uriCC.append(path, 0, semicolon > 0 ? semicolon : pos);
+ uriCC.append(normalizedPath, 0, semicolon > 0 ? semicolon : pos);
context.getMapper().map(uriMB, mappingData);
if (mappingData.wrapper == null) {
return (null);
@@ -454,7 +478,7 @@ public class ApplicationContext implemen
* RequestDispatcher's requestURI
*/
if (semicolon > 0) {
- uriCC.append(path, semicolon, pos - semicolon);
+ uriCC.append(normalizedPath, semicolon, pos - semicolon);
}
} catch (Exception e) {
// Should never happen
@@ -468,11 +492,11 @@ public class ApplicationContext implemen
mappingData.recycle();
+ String encodedUri = URLEncoder.DEFAULT.encode(uriCC.toString(),
"UTF-8");
+
// Construct a RequestDispatcher to process this request
- return new ApplicationDispatcher
- (wrapper, uriCC.toString(), wrapperPath, pathInfo,
+ return new ApplicationDispatcher(wrapper, encodedUri, wrapperPath,
pathInfo,
queryString, null);
-
}
Modified:
tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/LocalStrings.properties
URL:
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/LocalStrings.properties?rev=1754306&r1=1754305&r2=1754306&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/LocalStrings.properties
(original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/LocalStrings.properties
Wed Jul 27 16:24:26 2016
@@ -14,6 +14,7 @@
# limitations under the License.
applicationContext.attributeEvent=Exception thrown by attributes event listener
+applicationContext.illegalDispatchPath=An application attempted to obtain a
request dispatcher with an illegal path [{0}] that was rejected because it
contained an encoded directory traversal attempt
applicationContext.mapping.error=Error during mapping
applicationContext.requestDispatcher.iae=Path {0} does not start with a "/"
character
applicationContext.resourcePaths.iae=Path {0} does not start with a "/"
character
Modified:
tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardContext.java
URL:
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardContext.java?rev=1754306&r1=1754305&r2=1754306&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardContext.java
(original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardContext.java Wed
Jul 27 16:24:26 2016
@@ -794,8 +794,26 @@ public class StandardContext
boolean mapperDirectoryRedirectEnabled = false;
+ private boolean dispatchersUseEncodedPaths = true;
+
+
// ----------------------------------------------------- Context Properties
+ public void setDispatchersUseEncodedPaths(boolean
dispatchersUseEncodedPaths) {
+ this.dispatchersUseEncodedPaths = dispatchersUseEncodedPaths;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * The default value for this implementation is {@code true}.
+ */
+ public boolean getDispatchersUseEncodedPaths() {
+ return dispatchersUseEncodedPaths;
+ }
+
+
public void setMapperContextRootRedirectEnabled(boolean
mapperContextRootRedirectEnabled) {
this.mapperContextRootRedirectEnabled =
mapperContextRootRedirectEnabled;
}
Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/util/URLEncoder.java
URL:
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/util/URLEncoder.java?rev=1754306&r1=1754305&r2=1754306&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/util/URLEncoder.java
(original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/util/URLEncoder.java Wed Jul
27 16:24:26 2016
@@ -38,6 +38,16 @@ public class URLEncoder {
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F'};
+ public static final URLEncoder DEFAULT = new URLEncoder();
+ static {
+ DEFAULT.addSafeCharacter('~');
+ DEFAULT.addSafeCharacter('-');
+ DEFAULT.addSafeCharacter('_');
+ DEFAULT.addSafeCharacter('.');
+ DEFAULT.addSafeCharacter('*');
+ DEFAULT.addSafeCharacter('/');
+ }
+
//Array containing the safe characters set.
protected BitSet safeCharacters = new BitSet(256);
Copied:
tomcat/tc6.0.x/trunk/test/org/apache/catalina/core/TestApplicationContext.java
(from r1754287,
tomcat/tc7.0.x/trunk/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcher.java)
URL:
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/test/org/apache/catalina/core/TestApplicationContext.java?p2=tomcat/tc6.0.x/trunk/test/org/apache/catalina/core/TestApplicationContext.java&p1=tomcat/tc7.0.x/trunk/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcher.java&r1=1754287&r2=1754306&rev=1754306&view=diff
==============================================================================
---
tomcat/tc7.0.x/trunk/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcher.java
(original)
+++
tomcat/tc6.0.x/trunk/test/org/apache/catalina/core/TestApplicationContext.java
Wed Jul 27 16:24:26 2016
@@ -17,10 +17,7 @@
package org.apache.catalina.core;
import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collection;
-import javax.servlet.AsyncContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
@@ -29,33 +26,13 @@ import javax.servlet.http.HttpServletRes
import org.junit.Assert;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
import org.apache.catalina.Context;
-import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
-import org.apache.catalina.util.URLEncoder;
import org.apache.tomcat.util.buf.ByteChunk;
-@RunWith(value = Parameterized.class)
-public class TestApplicationContextGetRequestDispatcher extends TomcatBaseTest
{
-
- private final boolean useAsync;
-
- public TestApplicationContextGetRequestDispatcher(boolean useAsync) {
- this.useAsync = useAsync;
- }
-
- @Parameters(name = "{index}: useAsync[{0}]")
- public static Collection<Object[]> data() {
- return Arrays.asList(new Object[][]{
- {Boolean.TRUE},
- {Boolean.FALSE}
- });
- }
+public class TestApplicationContext extends TomcatBaseTest {
@Test
public void testGetRequestDispatcherNullPath01() throws Exception {
@@ -371,13 +348,7 @@ public class TestApplicationContextGetRe
// Note: This will decode the provided path
ctx.addServletMapping(targetPath, "target");
- if (useAsync) {
- Wrapper w = Tomcat.addServlet(
- ctx, "rd", new AsyncDispatcherServlet(dispatchPath,
useEncodedDispatchPaths));
- w.setAsyncSupported(true);
- } else {
- Tomcat.addServlet(ctx, "rd", new DispatcherServlet(dispatchPath));
- }
+ Tomcat.addServlet(ctx, "rd", new DispatcherServlet(dispatchPath));
// Note: This will decode the provided path
ctx.addServletMapping(startPath, "rd");
@@ -459,46 +430,4 @@ public class TestApplicationContextGetRe
}
}
}
-
-
- private static class AsyncDispatcherServlet extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
- private static final String NULL = "RD-NULL";
-
- private final String dispatchPath;
- private final boolean encodePath;
-
- public AsyncDispatcherServlet(String dispatchPath, boolean encodePath)
{
- this.dispatchPath = dispatchPath;
- this.encodePath = encodePath;
- }
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
-
- AsyncContext ac = req.startAsync();
- // Quick and dirty. Sufficient for this test but ignores lots of
- // edge cases.
- String target = null;
- if (dispatchPath != null) {
- target = req.getServletPath();
- int lastSlash = target.lastIndexOf('/');
- target = target.substring(0, lastSlash + 1);
- if (encodePath) {
- target = URLEncoder.DEFAULT.encode(target, "UTF-8");
- }
- target += dispatchPath;
- }
- try {
- ac.dispatch(target);
- } catch (UnsupportedOperationException uoe) {
- ac.complete();
- resp.setContentType("text/plain");
- resp.setCharacterEncoding("UTF-8");
- resp.getWriter().print(NULL);
- }
- }
- }
}
Modified: tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml
URL:
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml?rev=1754306&r1=1754305&r2=1754306&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml Wed Jul 27 16:24:26 2016
@@ -103,6 +103,12 @@
attempts during the lock out period will no longer reset the lock out
timer to zero. (markt)
</fix>
+ <fix>
+ By default, treat paths used to obtain a request dispatcher as encoded.
+ This behaviour can be changed per web application via the
+ <code>dispatchersUseEncodedPaths</code> attribute of the Context.
+ (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">
Modified: tomcat/tc6.0.x/trunk/webapps/docs/config/context.xml
URL:
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/config/context.xml?rev=1754306&r1=1754305&r2=1754306&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/webapps/docs/config/context.xml (original)
+++ tomcat/tc6.0.x/trunk/webapps/docs/config/context.xml Wed Jul 27 16:24:26
2016
@@ -251,6 +251,14 @@
used.</p>
</attribute>
+ <attribute name="dispatchersUseEncodedPaths" required="false">
+ <p>Controls whether paths used in calls to obtain a request dispatcher
+ ares expected to be encoded. This affects both how Tomcat handles calls
+ to obtain a request dispatcher as well as how Tomcat generates paths
+ used to obtain request dispatchers internally. If not specified, the
+ default value of <code>true</code> is used.</p>
+ </attribute>
+
<attribute name="docBase" required="true">
<p>The <em>Document Base</em> (also known as the <em>Context
Root</em>) directory for this web application, or the pathname
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]