[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