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 <[email protected]> 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 _______________________________________________ pkg-java-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-commits

