[tomcat7] 01/01: CVE-2015-5345 follow-up: Added a missing modification enabling the mapper*RedirectEnabled attributes on a context
Emmanuel Bourg
ebourg-guest at moszumanska.debian.org
Thu Dec 8 14:31: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 c942271eead27774eb72700a41cda2ced50e8e3e
Author: Emmanuel Bourg <ebourg at apache.org>
Date: Thu Dec 8 14:10:54 2016 +0100
CVE-2015-5345 follow-up: Added a missing modification enabling the mapper*RedirectEnabled attributes on a context
---
debian/changelog | 3 +
debian/patches/CVE-2015-5345.patch | 388 +++++++++++++++++++++++++++++++++----
2 files changed, 358 insertions(+), 33 deletions(-)
diff --git a/debian/changelog b/debian/changelog
index 9d3ef28..70823d0 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -9,6 +9,9 @@ tomcat7 (7.0.56-3+deb8u6) UNRELEASED; urgency=medium
(Closes: #846298)
* CVE-2016-6797 follow-up: Fixed a regression preventing some applications
from accessing the global resources (Closes: #845425)
+ * CVE-2015-5345 follow-up: Added a missing modification enabling the use of
+ the mapperContextRootRedirectEnabled and mapperDirectoryRedirectEnabled
+ attributes on a context.
* Backported a fix for a test failure in Test*NonLoginAndBasicAuthenticator
with recent JREs
* Refreshed the expired SSL certificates used by the tests
diff --git a/debian/patches/CVE-2015-5345.patch b/debian/patches/CVE-2015-5345.patch
index 3d94c39..7f768cd 100644
--- a/debian/patches/CVE-2015-5345.patch
+++ b/debian/patches/CVE-2015-5345.patch
@@ -8,21 +8,9 @@ the existence of a directory via a URL that lacks a trailing / (slash)
character.
http://svn.apache.org/viewvc?view=revision&revision=1715213
+http://svn.apache.org/viewvc?view=revision&revision=1716860
+http://svn.apache.org/viewvc?view=revision&revision=1717210
http://svn.apache.org/viewvc?view=revision&revision=1717212
----
- .../catalina/authenticator/FormAuthenticator.java | 14 ++++++++
- java/org/apache/catalina/core/StandardContext.java | 37 ++++++++++++++++++++--
- .../apache/catalina/core/mbeans-descriptors.xml | 8 +++++
- .../apache/catalina/servlets/DefaultServlet.java | 28 +++++++++++++++-
- .../apache/catalina/servlets/WebdavServlet.java | 5 +++
- .../org/apache/tomcat/util/http/mapper/Mapper.java | 21 ++++++------
- .../apache/catalina/startup/TomcatBaseTest.java | 3 +-
- webapps/docs/changelog.xml | 10 ++++++
- webapps/docs/config/context.xml | 16 ++++++++++
- 9 files changed, 126 insertions(+), 16 deletions(-)
-
-diff --git a/java/org/apache/catalina/authenticator/FormAuthenticator.java b/java/org/apache/catalina/authenticator/FormAuthenticator.java
-index a6846d0..d7e9eb5 100644
--- a/java/org/apache/catalina/authenticator/FormAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/FormAuthenticator.java
@@ -263,6 +263,20 @@ public class FormAuthenticator
@@ -125,7 +113,7 @@ diff --git a/java/org/apache/catalina/servlets/DefaultServlet.java b/java/org/ap
index 8be47b3..0ed549c 100644
--- a/java/org/apache/catalina/servlets/DefaultServlet.java
+++ b/java/org/apache/catalina/servlets/DefaultServlet.java
-@@ -373,6 +373,10 @@ public class DefaultServlet
+@@ -373,42 +373,40 @@
* @param request The servlet request we are processing
*/
protected String getRelativePath(HttpServletRequest request) {
@@ -136,7 +124,62 @@ index 8be47b3..0ed549c 100644
// IMPORTANT: DefaultServlet can be mapped to '/' or '/path/*' but always
// serves resources from the web app root with context rooted paths.
// i.e. it can not be used to mount the web app root under a sub-path
-@@ -782,7 +786,8 @@ public class DefaultServlet
+ // This method must construct a complete context rooted path, although
+ // subclasses can change this behaviour.
+
+- // Are we being processed by a RequestDispatcher.include()?
+- if (request.getAttribute(
+- RequestDispatcher.INCLUDE_REQUEST_URI) != null) {
+- String result = (String) request.getAttribute(
+- RequestDispatcher.INCLUDE_PATH_INFO);
+- if (result == null) {
+- result = (String) request.getAttribute(
+- RequestDispatcher.INCLUDE_SERVLET_PATH);
+- } else {
+- result = (String) request.getAttribute(
+- RequestDispatcher.INCLUDE_SERVLET_PATH) + result;
+- }
+- if ((result == null) || (result.equals(""))) {
+- result = "/";
+- }
+- return (result);
+- }
++ String servletPath;
++ String pathInfo;
+
+- // No, extract the desired path directly from the request
+- String result = request.getPathInfo();
+- if (result == null) {
+- result = request.getServletPath();
++ if (request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null) {
++ // For includes, get the info from the attributes
++ pathInfo = (String) request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO);
++ servletPath = (String) request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH);
+ } else {
+- result = request.getServletPath() + result;
++ pathInfo = request.getPathInfo();
++ servletPath = request.getServletPath();
++ }
++
++ StringBuilder result = new StringBuilder();
++ if (servletPath.length() > 0) {
++ result.append(servletPath);
++ }
++ if (pathInfo != null) {
++ result.append(pathInfo);
+ }
+- if ((result == null) || (result.equals(""))) {
+- result = "/";
++ if (result.length() == 0 && !allowEmptyPath) {
++ result.append('/');
+ }
+- return (result);
+
++ return result.toString();
+ }
+
+
+@@ -782,7 +780,8 @@
boolean serveContent = content;
// Identify the requested resource path
@@ -146,7 +189,7 @@ index 8be47b3..0ed549c 100644
if (debug > 0) {
if (serveContent)
log("DefaultServlet.serveResource: Serving resource '" +
-@@ -792,6 +797,12 @@ public class DefaultServlet
+@@ -792,6 +791,12 @@
path + "' headers only");
}
@@ -159,7 +202,7 @@ index 8be47b3..0ed549c 100644
CacheEntry cacheEntry = resources.lookupCache(path);
if (!cacheEntry.exists) {
-@@ -860,6 +871,11 @@ public class DefaultServlet
+@@ -860,6 +865,11 @@
if (cacheEntry.context != null) {
@@ -171,7 +214,7 @@ index 8be47b3..0ed549c 100644
// Skip directory listings if we have been configured to
// suppress them
if (!listings) {
-@@ -1067,6 +1083,16 @@ public class DefaultServlet
+@@ -1067,6 +1077,16 @@
}
@@ -188,27 +231,93 @@ index 8be47b3..0ed549c 100644
/**
* Parse the content-range header.
-diff --git a/java/org/apache/catalina/servlets/WebdavServlet.java b/java/org/apache/catalina/servlets/WebdavServlet.java
-index 6ced423..7fe6064 100644
--- a/java/org/apache/catalina/servlets/WebdavServlet.java
+++ b/java/org/apache/catalina/servlets/WebdavServlet.java
-@@ -430,6 +430,11 @@ public class WebdavServlet
+@@ -430,23 +430,29 @@
*/
@Override
protected String getRelativePath(HttpServletRequest request) {
+- // Are we being processed by a RequestDispatcher.include()?
+- if (request.getAttribute(
+- RequestDispatcher.INCLUDE_REQUEST_URI) != null) {
+- String result = (String) request.getAttribute(
+- RequestDispatcher.INCLUDE_PATH_INFO);
+- if ((result == null) || (result.equals("")))
+- result = "/";
+- return (result);
+ return getRelativePath(request, false);
+ }
+
+ @Override
+ protected String getRelativePath(HttpServletRequest request, boolean allowEmptyPath) {
- // Are we being processed by a RequestDispatcher.include()?
- if (request.getAttribute(
- RequestDispatcher.INCLUDE_REQUEST_URI) != null) {
-diff --git a/java/org/apache/tomcat/util/http/mapper/Mapper.java b/java/org/apache/tomcat/util/http/mapper/Mapper.java
-index ef6e0b1..78adabe 100644
++ String pathInfo;
++
++ if (request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null) {
++ // For includes, get the info from the attributes
++ pathInfo = (String) request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO);
++ } else {
++ pathInfo = request.getPathInfo();
+ }
+
+- // No, extract the desired path directly from the request
+- String result = request.getPathInfo();
+- if ((result == null) || (result.equals(""))) {
+- result = "/";
++ StringBuilder result = new StringBuilder();
++ if (pathInfo != null) {
++ result.append(pathInfo);
++ }
++ if (result.length() == 0) {
++ result.append('/');
+ }
+- return (result);
+
++ return result.toString();
+ }
+
+
--- a/java/org/apache/tomcat/util/http/mapper/Mapper.java
+++ b/java/org/apache/tomcat/util/http/mapper/Mapper.java
-@@ -856,20 +856,13 @@ public final class Mapper {
+@@ -273,7 +273,29 @@
+ public void addContextVersion(String hostName, Object host, String path,
+ String version, Object context, String[] welcomeResources,
+ javax.naming.Context resources, Collection<WrapperMappingInfo> wrappers) {
++ addContextVersion(hostName, host, path, version, context, welcomeResources, resources,
++ wrappers, false, false);
++ }
++
+
++ /**
++ * Add a new Context to an existing Host.
++ *
++ * @param hostName Virtual host name this context belongs to
++ * @param host Host object
++ * @param path Context path
++ * @param version Context version
++ * @param context Context object
++ * @param welcomeResources Welcome files defined for this context
++ * @param resources Static resources of the context
++ * @param wrappers Information on wrapper mappings
++ * @param mapperContextRootRedirectEnabled Mapper does context root redirects
++ * @param mapperDirectoryRedirectEnabled Mapper does directory redirects
++ */
++ public void addContextVersion(String hostName, Object host, String path,
++ String version, Object context, String[] welcomeResources,
++ javax.naming.Context resources, Collection<WrapperMappingInfo> wrappers,
++ boolean mapperContextRootRedirectEnabled, boolean mapperDirectoryRedirectEnabled) {
+ Host mappedHost = exactFind(hosts, hostName);
+ if (mappedHost == null) {
+ addHost(hostName, new String[0], host);
+@@ -294,6 +316,8 @@
+ newContextVersion.slashCount = slashCount;
+ newContextVersion.welcomeResources = welcomeResources;
+ newContextVersion.resources = resources;
++ newContextVersion.mapperContextRootRedirectEnabled = mapperContextRootRedirectEnabled;
++ newContextVersion.mapperDirectoryRedirectEnabled = mapperDirectoryRedirectEnabled;
+ if (wrappers != null) {
+ addWrappers(newContextVersion, wrappers);
+ }
+@@ -856,20 +880,13 @@
int pathOffset = path.getOffset();
int pathEnd = path.getEnd();
@@ -231,9 +340,13 @@ index ef6e0b1..78adabe 100644
path.setOffset(servletPath);
// Rule 1 -- Exact Match
-@@ -906,8 +899,10 @@ public final class Mapper {
+@@ -904,10 +921,13 @@
+ }
+ }
- if(mappingData.wrapper == null && noServletPath) {
+- if(mappingData.wrapper == null && noServletPath) {
++ if(mappingData.wrapper == null && noServletPath &&
++ contextVersion.mapperContextRootRedirectEnabled) {
// The path is empty, redirect to "/"
+ path.append('/');
+ pathEnd = path.getEnd();
@@ -243,7 +356,7 @@ index ef6e0b1..78adabe 100644
path.setEnd(pathEnd - 1);
return;
}
-@@ -1028,7 +1023,11 @@ public final class Mapper {
+@@ -1028,11 +1048,16 @@
Object file = null;
String pathStr = path.toString();
try {
@@ -256,8 +369,21 @@ index ef6e0b1..78adabe 100644
} catch(NamingException nex) {
// Swallow, since someone else handles the 404
}
-diff --git a/test/org/apache/catalina/startup/TomcatBaseTest.java b/test/org/apache/catalina/startup/TomcatBaseTest.java
-index 7ad04ee..38ffd5b 100644
+- if (file != null && file instanceof DirContext) {
++ if (file != null && file instanceof DirContext &&
++ contextVersion.mapperDirectoryRedirectEnabled) {
+ // Note: this mutates the path: do not do any processing
+ // after this (since we set the redirectPath, there
+ // shouldn't be any)
+@@ -1684,6 +1709,8 @@
+ public Wrapper[] wildcardWrappers = new Wrapper[0];
+ public Wrapper[] extensionWrappers = new Wrapper[0];
+ public int nesting = 0;
++ public boolean mapperContextRootRedirectEnabled = false;
++ public boolean mapperDirectoryRedirectEnabled = false;
+ private volatile boolean paused;
+
+ public ContextVersion() {
--- a/test/org/apache/catalina/startup/TomcatBaseTest.java
+++ b/test/org/apache/catalina/startup/TomcatBaseTest.java
@@ -231,8 +231,7 @@ public abstract class TomcatBaseTest extends LoggingBaseTest {
@@ -318,3 +444,199 @@ index 10b34f1..3679837 100644
<attribute name="override" required="false">
<p>Set to <code>true</code> to ignore any settings in both the global
or <a href="host.html">Host</a> default contexts. By default, settings
+--- a/java/org/apache/catalina/connector/MapperListener.java
++++ b/java/org/apache/catalina/connector/MapperListener.java
+@@ -384,7 +384,8 @@
+
+ mapper.addContextVersion(host.getName(), host, contextPath,
+ context.getWebappVersion(), context, welcomeFiles, resources,
+- wrappers);
++ wrappers, context.getMapperContextRootRedirectEnabled(),
++ context.getMapperDirectoryRedirectEnabled());
+
+ if(log.isDebugEnabled()) {
+ log.debug(sm.getString("mapperListener.registerContext",
+--- a/java/org/apache/catalina/Context.java
++++ b/java/org/apache/catalina/Context.java
+@@ -1614,4 +1614,44 @@
+ * method names.
+ */
+ public Map<String, String> findPreDestroyMethods();
++
++ /**
++ * If enabled, requests for a web application context root will be
++ * redirected (adding a trailing slash) by the Mapper. This is more
++ * efficient but has the side effect of confirming that the context path is
++ * valid.
++ *
++ * @param mapperContextRootRedirectEnabled Should the redirects be enabled?
++ */
++ public void setMapperContextRootRedirectEnabled(boolean mapperContextRootRedirectEnabled);
++
++ /**
++ * Determines if requests for a web application context root will be
++ * redirected (adding a trailing slash) by the Mapper. This is more
++ * efficient but has the side effect of confirming that the context path is
++ * valid.
++ *
++ * @return {@code true} if the Mapper level redirect is enabled for this
++ * Context.
++ */
++ public boolean getMapperContextRootRedirectEnabled();
++
++ /**
++ * If enabled, requests for a directory will be redirected (adding a
++ * trailing slash) by the Mapper. This is more efficient but has the
++ * side effect of confirming that the directory is valid.
++ *
++ * @param mapperDirectoryRedirectEnabled Should the redirects be enabled?
++ */
++ public void setMapperDirectoryRedirectEnabled(boolean mapperDirectoryRedirectEnabled);
++
++ /**
++ * Determines if requests for a directory will be redirected (adding a
++ * trailing slash) by the Mapper. This is more efficient but has the
++ * side effect of confirming that the directory is valid.
++ *
++ * @return {@code true} if the Mapper level redirect is enabled for this
++ * Context.
++ */
++ public boolean getMapperDirectoryRedirectEnabled();
+ }
+--- a/java/org/apache/catalina/startup/FailedContext.java
++++ b/java/org/apache/catalina/startup/FailedContext.java
+@@ -703,4 +703,20 @@
+
+ @Override
+ public String getContainerSciFilter() { return null; }
+-}
+\ No newline at end of file
++
++ @Override
++ public void setMapperContextRootRedirectEnabled(boolean mapperContextRootRedirectEnabled) {
++ // NO-OP
++ }
++
++ @Override
++ public boolean getMapperContextRootRedirectEnabled() { return false; }
++
++ @Override
++ public void setMapperDirectoryRedirectEnabled(boolean mapperDirectoryRedirectEnabled) {
++ // NO-OP
++ }
++
++ @Override
++ public boolean getMapperDirectoryRedirectEnabled() { return false; }
++}
+--- a/test/org/apache/catalina/core/TesterContext.java
++++ b/test/org/apache/catalina/core/TesterContext.java
+@@ -1219,4 +1219,20 @@
+
+ @Override
+ public String getContainerSciFilter() { return null; }
++
++ @Override
++ public void setMapperContextRootRedirectEnabled(boolean mapperContextRootRedirectEnabled) {
++ // NO-OP
++ }
++
++ @Override
++ public boolean getMapperContextRootRedirectEnabled() { return false; }
++
++ @Override
++ public void setMapperDirectoryRedirectEnabled(boolean mapperDirectoryRedirectEnabled) {
++ // NO-OP
++ }
++
++ @Override
++ public boolean getMapperDirectoryRedirectEnabled() { return false; }
+ }
+--- a/test/org/apache/tomcat/util/http/mapper/TestMapperWebapps.java
++++ b/test/org/apache/tomcat/util/http/mapper/TestMapperWebapps.java
+@@ -18,6 +18,7 @@
+
+ import java.io.File;
+ import java.io.IOException;
++import java.net.HttpURLConnection;
+ import java.util.HashMap;
+ import java.util.List;
+
+@@ -31,8 +32,11 @@
+
+ import org.apache.catalina.Context;
+ import org.apache.catalina.core.StandardContext;
++import org.apache.catalina.deploy.SecurityCollection;
++import org.apache.catalina.deploy.SecurityConstraint;
+ import org.apache.catalina.startup.Tomcat;
+ import org.apache.catalina.startup.TomcatBaseTest;
++import org.apache.catalina.valves.RemoteAddrValve;
+ import org.apache.tomcat.util.buf.ByteChunk;
+
+ /**
+@@ -224,6 +228,66 @@
+ Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc);
+ }
+
++ @Test
++ public void testRedirect() throws Exception {
++ // Disable the following of redirects for this test only
++ boolean originalValue = HttpURLConnection.getFollowRedirects();
++ HttpURLConnection.setFollowRedirects(false);
++ try {
++ Tomcat tomcat = getTomcatInstance();
++
++ // Use standard test webapp as ROOT
++ File rootDir = new File("test/webapp-3.0");
++ org.apache.catalina.Context root =
++ tomcat.addWebapp(null, "", rootDir.getAbsolutePath());
++
++ // Add a security constraint
++ SecurityConstraint constraint = new SecurityConstraint();
++ SecurityCollection collection = new SecurityCollection();
++ collection.addPattern("/welcome-files/*");
++ collection.addPattern("/welcome-files");
++ constraint.addCollection(collection);
++ constraint.addAuthRole("foo");
++ root.addConstraint(constraint);
++
++ // Also make examples available
++ File examplesDir = new File(getBuildDirectory(), "webapps/examples");
++ org.apache.catalina.Context examples = tomcat.addWebapp(
++ null, "/examples", examplesDir.getAbsolutePath());
++ // Then block access to the examples to test redirection
++ RemoteAddrValve rav = new RemoteAddrValve();
++ rav.setDeny(".*");
++ rav.setDenyStatus(404);
++ examples.getPipeline().addValve(rav);
++
++ tomcat.start();
++
++ // Redirects within a web application
++ doRedirectTest("/welcome-files", 401);
++ doRedirectTest("/welcome-files/", 401);
++
++ doRedirectTest("/jsp", 302);
++ doRedirectTest("/jsp/", 404);
++
++ doRedirectTest("/WEB-INF", 404);
++ doRedirectTest("/WEB-INF/", 404);
++
++ // Redirects between web applications
++ doRedirectTest("/examples", 404);
++ doRedirectTest("/examples/", 404);
++ } finally {
++ HttpURLConnection.setFollowRedirects(originalValue);
++ }
++ }
++
++
++ private void doRedirectTest(String path, int expected) throws IOException {
++ ByteChunk bc = new ByteChunk();
++ int rc = getUrl("http://localhost:" + getPort() + path, bc, null);
++ Assert.assertEquals(expected, rc);
++ }
++
++
+ /**
+ * Prepare a string to search in messages that contain a timestamp, when it
+ * is known that the timestamp was printed between {@code timeA} and
--
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