Index: tomcat6-6.0.53/apache-tomcat-6.0.53-src/java/org/apache/catalina/servlets/DefaultServlet.java
===================================================================
--- tomcat6-6.0.53.orig/apache-tomcat-6.0.53-src/java/org/apache/catalina/servlets/DefaultServlet.java
+++ tomcat6-6.0.53/apache-tomcat-6.0.53-src/java/org/apache/catalina/servlets/DefaultServlet.java
@@ -801,23 +801,6 @@ public class DefaultServlet
             return;
         }
 
-        // If the resource is not a collection, and the resource path
-        // ends with "/" or "\", return NOT FOUND
-        if (cacheEntry.context == null) {
-            if (path.endsWith("/") || (path.endsWith("\\"))) {
-                // Check if we're included so we can return the appropriate
-                // missing resource name in the error
-                String requestUri = (String) request.getAttribute(
-                                            Globals.INCLUDE_REQUEST_URI_ATTR);
-                if (requestUri == null) {
-                    requestUri = request.getRequestURI();
-                }
-                response.sendError(HttpServletResponse.SC_NOT_FOUND,
-                                   requestUri);
-                return;
-            }
-        }
-
         boolean isError = Integer.valueOf(ApplicationFilterFactory.ERROR).equals(request.getAttribute(Globals.DISPATCHER_TYPE_ATTR));
 
         // Check if the conditions specified in the optional If headers are
Index: tomcat6-6.0.53/apache-tomcat-6.0.53-src/java/org/apache/naming/resources/FileDirContext.java
===================================================================
--- tomcat6-6.0.53.orig/apache-tomcat-6.0.53-src/java/org/apache/naming/resources/FileDirContext.java
+++ tomcat6-6.0.53/apache-tomcat-6.0.53-src/java/org/apache/naming/resources/FileDirContext.java
@@ -29,6 +29,7 @@ import java.util.Date;
 import java.util.Hashtable;
 
 import javax.naming.NameAlreadyBoundException;
+import javax.naming.NameNotFoundException;
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
 import javax.naming.OperationNotSupportedException;
@@ -76,6 +77,8 @@ public class FileDirContext extends Base
 
     /**
      * Builds a file directory context using the given environment.
+     *
+     * @param env The environment with which to build the context
      */
     public FileDirContext(Hashtable env) {
         super(env);
@@ -96,7 +99,8 @@ public class FileDirContext extends Base
      */
     protected String absoluteBase = null;
 
-
+    private String canonicalBase = null;
+ 
     /**
      * Case sensitivity.
      */
@@ -111,7 +115,6 @@ public class FileDirContext extends Base
 
     // ------------------------------------------------------------- Properties
 
-
     /**
      * Set the document root.
      *
@@ -125,24 +128,32 @@ public class FileDirContext extends Base
     public void setDocBase(String docBase) {
 
     // Validate the format of the proposed document root
-    if (docBase == null)
-        throw new IllegalArgumentException
-        (sm.getString("resources.null"));
+    if (docBase == null) {
+        throw new IllegalArgumentException(sm.getString("resources.null"));
+    }
 
     // Calculate a File object referencing this document base directory
     base = new File(docBase);
-        try {
-            base = base.getCanonicalFile();
-        } catch (IOException e) {
+    try {
+        base = base.getCanonicalFile();
+    } catch (IOException e) {
             // Ignore
-        }
+    }
 
     // Validate that the document base is an existing directory
-    if (!base.exists() || !base.isDirectory() || !base.canRead())
-        throw new IllegalArgumentException
-        (sm.getString("fileResources.base", docBase));
-        this.absoluteBase = base.getAbsolutePath();
-        super.setDocBase(docBase);
+    if (!base.exists() || !base.isDirectory() || !base.canRead()) {
+        throw new IllegalArgumentException (sm.getString("fileResources.base", docBase));
+    }
+    this.absoluteBase = base.getAbsolutePath();
+
+    // absoluteBase also needs to be normalized. Using the canonical path is
+    // the simplest way of doing this.
+   try {
+       this.canonicalBase = base.getCanonicalPath();
+    } catch (IOException e) {
+            throw new IllegalArgumentException(e);
+    }
+    super.setDocBase(docBase);
 
     }
 
@@ -165,6 +176,8 @@ public class FileDirContext extends Base
 
     /**
      * Set allow linking.
+     *
+     * @param allowLinking The new value for the attribute
      */
     public void setAllowLinking(boolean allowLinking) {
         this.allowLinking = allowLinking;
@@ -173,6 +186,8 @@ public class FileDirContext extends Base
 
     /**
      * Is linking allowed.
+     *
+     * @return {@code true} is linking is allowed, otherwise {@false}
      */
     public boolean getAllowLinking() {
         return allowLinking;
@@ -203,7 +218,7 @@ public class FileDirContext extends Base
     public Object lookup(String name)
         throws NamingException {
         Object result = null;
-        File file = file(name);
+        File file = file(name, true);
 
         if (file == null)
             throw new NamingException
@@ -241,7 +256,7 @@ public class FileDirContext extends Base
     public void unbind(String name)
         throws NamingException {
 
-        File file = file(name);
+        File file = file(name, true);
 
         if (file == null)
             throw new NamingException
@@ -265,16 +280,18 @@ public class FileDirContext extends Base
      * @exception NameAlreadyBoundException if newName is already bound
      * @exception NamingException if a naming exception is encountered
      */
-    public void rename(String oldName, String newName)
-        throws NamingException {
+    public void rename(String oldName, String newName) throws NamingException {
 
-        File file = file(oldName);
+        File file = file(oldName, true);
 
         if (file == null)
             throw new NamingException
                 (sm.getString("resources.notFound", oldName));
 
-        File newFile = new File(base, newName);
+        File newFile = file(newName, false);
+        if (newFile == null) {
+            throw new NamingException(sm.getString("resources.renameFail", oldName, newName));
+        }
 
         file.renameTo(newFile);
 
@@ -297,7 +314,7 @@ public class FileDirContext extends Base
     public NamingEnumeration list(String name)
         throws NamingException {
 
-        File file = file(name);
+        File file = file(name, true);
 
         if (file == null)
             throw new NamingException
@@ -426,7 +443,7 @@ public class FileDirContext extends Base
         throws NamingException {
 
         // Building attribute list
-        File file = file(name);
+        File file = file(name, true);
 
         if (file == null)
             throw new NamingException
@@ -492,12 +509,20 @@ public class FileDirContext extends Base
      * of the binding are not supplied
      * @exception NamingException if a naming exception is encountered
      */
-    public void bind(String name, Object obj, Attributes attrs)
-        throws NamingException {
+    public void bind(String name, Object obj, Attributes attrs) throws NamingException {
 
         // Note: No custom attributes allowed
 
-        File file = new File(base, name);
+        // bind() is meant to create a file so ensure that the path doesn't end
+        // in '/'
+        if (name.endsWith("/")) {
+            throw new NamingException(sm.getString("resources.bindFailed", name));
+        }
+
+        File file = file(name, false);
+        if (file == null) {
+            throw new NamingException(sm.getString("resources.bindFailed", name));
+        }
         if (file.exists())
             throw new NameAlreadyBoundException
                 (sm.getString("resources.alreadyBound", name));
@@ -531,7 +556,10 @@ public class FileDirContext extends Base
         // Note: No custom attributes allowed
         // Check obj type
 
-        File file = new File(base, name);
+        File file = file(name, false);
+        if (file == null) {
+            throw new NamingException(sm.getString("resources.bindFailed", name));
+        }
 
         InputStream is = null;
         if (obj instanceof Resource) {
@@ -609,13 +637,14 @@ public class FileDirContext extends Base
     public DirContext createSubcontext(String name, Attributes attrs)
         throws NamingException {
 
-        File file = new File(base, name);
+        File file = file(name, false);
+        if (file == null) {
+            throw new NamingException(sm.getString("resources.bindFailed", name));
+        }
         if (file.exists())
-            throw new NameAlreadyBoundException
-                (sm.getString("resources.alreadyBound", name));
+            throw new NameAlreadyBoundException(sm.getString("resources.alreadyBound", name));
         if (!file.mkdir())
-            throw new NamingException
-                (sm.getString("resources.bindFailed", name));
+            throw new NamingException(sm.getString("resources.bindFailed", name));
         return (DirContext) lookup(name);
 
     }
@@ -785,56 +814,129 @@ public class FileDirContext extends Base
      * @param name Normalized context-relative path (with leading '/')
      */
     protected File file(String name) {
+        return file(name, true);
+    }
 
+    /**
+     * Return a File object representing the specified normalized
+     * context-relative path if it exists and is readable.  Otherwise,
+     * return <code>null</code>.
+     *
+     * @param name      Normalized context-relative path (with leading '/')
+     * @param mustExist Must the specified resource exist?
+     */
+    protected File file(String name, boolean mustExist) {
+        if (name.equals("/")) {
+            name = "";
+        }
         File file = new File(base, name);
-        if (file.exists() && file.canRead()) {
+        return validate(file, name, mustExist, absoluteBase, canonicalBase);
+    }
 
-        	if (allowLinking)
-        		return file;
-        	
-            // Check that this file belongs to our root path
-            String canPath = null;
-            try {
-                canPath = file.getCanonicalPath();
-            } catch (IOException e) {
-            }
-            if (canPath == null)
-                return null;
+    protected File validate(File file, String name, boolean mustExist, String absoluteBase,
+                String canonicalBase) {
 
-            // Check to see if going outside of the web application root
-            if (!canPath.startsWith(absoluteBase)) {
-                return null;
-            }
+        // If the requested names ends in '/', the Java File API will return a
+        // matching file if one exists. This isn't what we want as it is not
+        // consistent with the Servlet spec rules for request mapping.
+        if (name.endsWith("/") && file.isFile()) {
+            return null;
+        }
+        // If the file/dir must exist but the identified file/dir can't be read
+        // then signal that the resource was not found
+        if (mustExist && !file.canRead()) {
+            return null;
+        }
 
-            // Case sensitivity check
-            if (caseSensitive) {
-                String fileAbsPath = file.getAbsolutePath();
-                if (fileAbsPath.endsWith("."))
-                    fileAbsPath = fileAbsPath + "/";
-                String absPath = normalize(fileAbsPath);
-                if (canPath != null)
-                    canPath = normalize(canPath);
-                if ((absoluteBase.length() < absPath.length())
-                    && (absoluteBase.length() < canPath.length())) {
-                    absPath = absPath.substring(absoluteBase.length() + 1);
-                    if ((canPath == null) || (absPath == null))
-                        return null;
-                    if (absPath.equals(""))
-                        absPath = "/";
-                    canPath = canPath.substring(absoluteBase.length() + 1);
-                    if (canPath.equals(""))
-                        canPath = "/";
-                    if (!canPath.equals(absPath))
-                        return null;
-                }
-            }
 
-        } else {
+        // If allow linking is enabled, files are not limited to being located
+        // under the fileBase so all further checks are disabled.
+        if (allowLinking) {
+            return file;
+        }
+
+        // Additional Windows specific checks to handle known problems with
+        // File.getCanonicalPath()
+        if (JrePlatform.IS_WINDOWS && isInvalidWindowsFilename(name)) {
+            return null;
+        }
+
+        // Check that this file is located under the web application root
+        String canPath = null;
+        try {
+            canPath = file.getCanonicalPath();
+        } catch (IOException e) {
+            // Ignore
+        }
+        if (canPath == null || !canPath.startsWith(canonicalBase)) {
+            return null;
+        }
+        // Ensure that the file is not outside the fileBase. This should not be
+        // possible for standard requests (the request is normalized early in
+        // the request processing) but might be possible for some access via the
+        // Servlet API (RequestDispatcher etc.) therefore these checks are
+        // retained as an additional safety measure. absoluteBase has been
+        // normalized so absPath needs to be normalized as well.
+        String absPath = normalize(file.getAbsolutePath());
+        if ((absoluteBase.length() > absPath.length())) {
             return null;
         }
+
+        // Remove the fileBase location from the start of the paths since that
+        // was not part of the requested path and the remaining check only
+        // applies to the request path
+        absPath = absPath.substring(absoluteBase.length());
+        canPath = canPath.substring(canonicalBase.length());
+
+        // Case sensitivity check
+        // The normalized requested path should be an exact match the equivalent
+        // canonical path. If it is not, possible reasons include:
+        // - case differences on case insensitive file systems
+        // - Windows removing a trailing ' ' or '.' from the file name
+        //
+        // In all cases, a mis-match here results in the resource not being
+        // found
+        //
+        // absPath is normalized so canPath needs to be normalized as well
+        // Can't normalize canPath earlier as canonicalBase is not normalized
+        if (canPath.length() > 0) {
+            canPath = normalize(canPath);
+        }
+        if (!canPath.equals(absPath)) {
+            return null;
+        }
+
         return file;
 
     }
+    private boolean isInvalidWindowsFilename(String name) {
+        final int len = name.length();
+        if (len == 0) {
+            return false;
+        }
+        // This consistently ~10 times faster than the equivalent regular
+        // expression irrespective of input length.
+        for (int i = 0; i < len; i++) {
+            char c = name.charAt(i);
+            if (c == '\"' || c == '<' || c == '>') {
+                // These characters are disallowed in Windows file names and
+                // there are known problems for file names with these characters
+                // when using File#getCanonicalPath().
+                // Note: There are additional characters that are disallowed in
+                //       Windows file names but these are not known to cause
+                //       problems when using File#getCanonicalPath().
+                return true;
+            }
+        }
+        // Windows does not allow file names to end in ' ' unless specific low
+        // level APIs are used to create the files that bypass various checks.
+        // File names that end in ' ' are known to cause problems when using
+        // File#getCanonicalPath().
+        if (name.charAt(len -1) == ' ') {
+            return true;
+        }
+        return false;
+    }
 
 
     /**
@@ -1093,10 +1195,6 @@ public class FileDirContext extends Base
             }
             return canonicalPath;
         }
-        
-
     }
-
-
 }
 
Index: tomcat6-6.0.53/apache-tomcat-6.0.53-src/java/org/apache/naming/resources/JrePlatform.java
===================================================================
--- /dev/null
+++ tomcat6-6.0.53/apache-tomcat-6.0.53-src/java/org/apache/naming/resources/JrePlatform.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.naming.resources;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+public class JrePlatform {
+
+    private static final String OS_NAME_PROPERTY = "os.name";
+    private static final String OS_NAME_WINDOWS_PREFIX = "Windows";
+
+    static {
+        /*
+         * There are a few places where a) the behaviour of the Java API depends
+         * on the underlying platform and b) those behavioural differences have
+         * an impact on Tomcat.
+         *
+         * Tomcat therefore needs to be able to determine the platform it is
+         * running on to account for those differences.
+         *
+         * In an ideal world this code would not exist.
+         */
+
+        // This check is derived from the check in Apache Commons Lang
+        String osName;
+        if (System.getSecurityManager() == null) {
+            osName = System.getProperty(OS_NAME_PROPERTY);
+        } else {
+            osName = AccessController.doPrivileged(
+                    new PrivilegedAction<String>() {
+
+                    // @Override // Override doesn't work on java5...
+                    public String run() {
+                        return System.getProperty(OS_NAME_PROPERTY);
+                    }
+                });
+        }
+
+        IS_WINDOWS = osName.startsWith(OS_NAME_WINDOWS_PREFIX);
+    }
+
+
+    public static final boolean IS_WINDOWS;
+}
Index: tomcat6-6.0.53/apache-tomcat-6.0.53-src/java/org/apache/naming/resources/VirtualDirContext.java
===================================================================
--- tomcat6-6.0.53.orig/apache-tomcat-6.0.53-src/java/org/apache/naming/resources/VirtualDirContext.java
+++ tomcat6-6.0.53/apache-tomcat-6.0.53-src/java/org/apache/naming/resources/VirtualDirContext.java
@@ -223,4 +223,7 @@ public class VirtualDirContext extends F
         return virtual;
     }
 
+    protected File validate(File file, String name, boolean mustExist, String absoluteBase) {
+        return validate(file, name, mustExist, normalize(absoluteBase), absoluteBase);
+    }
 }
Index: tomcat6-6.0.53/apache-tomcat-6.0.53-src/webapps/docs/changelog.xml
===================================================================
--- tomcat6-6.0.53.orig/apache-tomcat-6.0.53-src/webapps/docs/changelog.xml
+++ tomcat6-6.0.53/apache-tomcat-6.0.53-src/webapps/docs/changelog.xml
@@ -6861,6 +6861,15 @@
       </fix>
     </changelog>
   </subsection>
+  <subsection name="Catalina">
+    <changelog>
+      <fix>
+        <bug>61542</bug>: Fix CVE-2017-12617 and prevent JSPs from being
+        uploaded via a specially crafted request when HTTP PUT was enabled.
+        (markt)
+      </fix>
+    </changelog>
+  </subsection>
  </section>
 <section name="Tomcat 6.0.10 (remm)" rtext="released 2007-02-28">
   <subsection name="Catalina">
