Author: markt
Date: Sun Sep 16 22:18:54 2012
New Revision: 1385387

URL: http://svn.apache.org/viewvc?rev=1385387&view=rev
Log:
Add in support for allowLinking option.
Note: this restores the case sensitivity checks on Windows closing a large 
security hole in the new resources implementation.

Modified:
    tomcat/sandbox/trunk-resources/java/org/apache/catalina/WebResourceRoot.java
    
tomcat/sandbox/trunk-resources/java/org/apache/catalina/webresources/DirResourceSet.java
    
tomcat/sandbox/trunk-resources/java/org/apache/catalina/webresources/StandardRoot.java
    
tomcat/sandbox/trunk-resources/test/org/apache/catalina/webresources/TesterWebResourceRoot.java

Modified: 
tomcat/sandbox/trunk-resources/java/org/apache/catalina/WebResourceRoot.java
URL: 
http://svn.apache.org/viewvc/tomcat/sandbox/trunk-resources/java/org/apache/catalina/WebResourceRoot.java?rev=1385387&r1=1385386&r2=1385387&view=diff
==============================================================================
--- 
tomcat/sandbox/trunk-resources/java/org/apache/catalina/WebResourceRoot.java 
(original)
+++ 
tomcat/sandbox/trunk-resources/java/org/apache/catalina/WebResourceRoot.java 
Sun Sep 16 22:18:54 2012
@@ -223,6 +223,7 @@ public interface WebResourceRoot extends
     boolean getAddWebinfClassesResources();
 
     void setAllowLinking(boolean allowLinking);
+    boolean getAllowLinking();
 
     public static enum ResourceSetType {
         PRE,

Modified: 
tomcat/sandbox/trunk-resources/java/org/apache/catalina/webresources/DirResourceSet.java
URL: 
http://svn.apache.org/viewvc/tomcat/sandbox/trunk-resources/java/org/apache/catalina/webresources/DirResourceSet.java?rev=1385387&r1=1385386&r2=1385387&view=diff
==============================================================================
--- 
tomcat/sandbox/trunk-resources/java/org/apache/catalina/webresources/DirResourceSet.java
 (original)
+++ 
tomcat/sandbox/trunk-resources/java/org/apache/catalina/webresources/DirResourceSet.java
 Sun Sep 16 22:18:54 2012
@@ -27,6 +27,7 @@ import org.apache.catalina.WebResourceRo
 import org.apache.catalina.WebResourceRoot.ResourceSetType;
 import org.apache.catalina.util.IOTools;
 import org.apache.catalina.util.ResourceSet;
+import org.apache.tomcat.util.http.RequestUtil;
 
 /**
  * Represents a {@link WebResourceSet} based on a directory.
@@ -37,6 +38,8 @@ public class DirResourceSet extends Abst
 
     private final WebResourceRoot root;
     private final File base;
+    private final String absoluteBase;
+    private final String canonicalBase;
     private final String webAppMount;
 
     /**
@@ -63,6 +66,19 @@ public class DirResourceSet extends Abst
                     "TODO-i18n: base/internalPath is not a directory");
         }
         this.base = base;
+
+        String absolutePath = base.getAbsolutePath();
+        if (absolutePath.endsWith(".")) {
+            absolutePath = absolutePath + "/";
+        }
+        this.absoluteBase = normalize(absolutePath);
+
+        try {
+            this.canonicalBase = base.getCanonicalPath();
+        } catch (IOException e) {
+            throw new IllegalArgumentException(e);
+        }
+
         this.webAppMount = webAppMount;
 
         if (root.getAddWebinfClassesResources()) {
@@ -80,7 +96,10 @@ public class DirResourceSet extends Abst
     public WebResource getResource(String path) {
         checkPath(path);
         if (path.startsWith(webAppMount)) {
-            File f = new File(base, path.substring(webAppMount.length()));
+            File f = file(path.substring(webAppMount.length()), true);
+            if (f == null) {
+                return new EmptyResource(root, path);
+            }
             if (f.isDirectory() && path.charAt(path.length() - 1) != '/') {
                 path = path += '/';
             }
@@ -94,7 +113,10 @@ public class DirResourceSet extends Abst
     public String[] list(String path) {
         checkPath(path);
         if (path.startsWith(webAppMount)) {
-            File f = new File(base, path.substring(webAppMount.length()));
+            File f = file(path.substring(webAppMount.length()), true);
+            if (f == null) {
+                return EMPTY_STRING_ARRAY;
+            }
             String[] result = f.list();
             if (result == null) {
                 return EMPTY_STRING_ARRAY;
@@ -111,19 +133,21 @@ public class DirResourceSet extends Abst
         checkPath(path);
         ResourceSet<String> result = new ResourceSet<>();
         if (path.startsWith(webAppMount)) {
-            File f = new File(base, path.substring(webAppMount.length()));
-            File[] list = f.listFiles();
-            if (list != null) {
-                for (File entry : list) {
-                    StringBuilder sb = new StringBuilder(path);
-                    if (path.charAt(path.length() - 1) != '/') {
-                        sb.append('/');
-                    }
-                    sb.append(entry.getName());
-                    if (entry.isDirectory()) {
-                        sb.append('/');
+            File f = file(path.substring(webAppMount.length()), true);
+            if (f != null) {
+                File[] list = f.listFiles();
+                if (list != null) {
+                    for (File entry : list) {
+                        StringBuilder sb = new StringBuilder(path);
+                        if (path.charAt(path.length() - 1) != '/') {
+                            sb.append('/');
+                        }
+                        sb.append(entry.getName());
+                        if (entry.isDirectory()) {
+                            sb.append('/');
+                        }
+                        result.add(sb.toString());
                     }
-                    result.add(sb.toString());
                 }
             }
         }
@@ -135,7 +159,10 @@ public class DirResourceSet extends Abst
     public boolean mkdir(String path) {
         checkPath(path);
         if (path.startsWith(webAppMount)) {
-            File f = new File(base, path.substring(webAppMount.length()));
+            File f = file(path.substring(webAppMount.length()), false);
+            if (f == null) {
+                return false;
+            }
             return f.mkdir();
         } else {
             return false;
@@ -153,7 +180,10 @@ public class DirResourceSet extends Abst
 
         File dest = null;
         if (path.startsWith(webAppMount)) {
-            dest = new File(base, path.substring(webAppMount.length()));
+            dest = file(path.substring(webAppMount.length()), false);
+            if (dest == null) {
+                return false;
+            }
         } else {
             return false;
         }
@@ -170,4 +200,67 @@ public class DirResourceSet extends Abst
 
         return true;
     }
+
+    private File file(String name, boolean mustExist) {
+
+        File file = new File(base, name);
+        if (file.exists() && file.canRead() || !mustExist) {
+
+            if (root.getAllowLinking()) {
+                return file;
+            }
+
+            // Check that this file is located under the WebResourceSet's base
+            String canPath = null;
+            try {
+                canPath = file.getCanonicalPath();
+            } catch (IOException e) {
+                // Ignore
+            }
+            if (canPath == null)
+                return null;
+
+            if (!canPath.startsWith(canonicalBase)) {
+                return null;
+            }
+
+            // Case sensitivity check
+            // Note: We know the resource is located somewhere under base at
+            //       point. The purpose of this code is to check in a case
+            //       sensitive manner, the path to the resource under base
+            //       agrees with what was requested
+            String fileAbsPath = file.getAbsolutePath();
+            if (fileAbsPath.endsWith("."))
+                fileAbsPath = fileAbsPath + "/";
+            String absPath = normalize(fileAbsPath);
+            if ((absoluteBase.length() < absPath.length())
+                && (canonicalBase.length() < canPath.length())) {
+                absPath = absPath.substring(absoluteBase.length() + 1);
+                if (absPath.equals(""))
+                    absPath = "/";
+                canPath = canPath.substring(canonicalBase.length() + 1);
+                if (canPath.equals(""))
+                    canPath = "/";
+                if (!canPath.equals(absPath))
+                    return null;
+            }
+
+        } else {
+            return null;
+        }
+        return file;
+    }
+
+    /**
+     * Return a context-relative path, beginning with a "/", that represents
+     * the canonical version of the specified path after ".." and "." elements
+     * are resolved out.  If the specified path attempts to go outside the
+     * boundaries of the current context (i.e. too many ".." path elements
+     * are present), return <code>null</code> instead.
+     *
+     * @param path Path to be normalized
+     */
+    protected String normalize(String path) {
+        return RequestUtil.normalize(path, File.separatorChar == '/');
+    }
 }

Modified: 
tomcat/sandbox/trunk-resources/java/org/apache/catalina/webresources/StandardRoot.java
URL: 
http://svn.apache.org/viewvc/tomcat/sandbox/trunk-resources/java/org/apache/catalina/webresources/StandardRoot.java?rev=1385387&r1=1385386&r2=1385387&view=diff
==============================================================================
--- 
tomcat/sandbox/trunk-resources/java/org/apache/catalina/webresources/StandardRoot.java
 (original)
+++ 
tomcat/sandbox/trunk-resources/java/org/apache/catalina/webresources/StandardRoot.java
 Sun Sep 16 22:18:54 2012
@@ -48,6 +48,7 @@ public class StandardRoot extends Lifecy
             StringManager.getManager(Constants.Package);
 
     private final Context context;
+    private boolean allowLinking = false;
     private ArrayList<WebResourceSet> preResources = new ArrayList<>();
     private WebResourceSet main;
     private ArrayList<WebResourceSet> jarResources = new ArrayList<>();
@@ -182,7 +183,11 @@ public class StandardRoot extends Lifecy
         String[] resources = list(path);
         WebResource[] result = new WebResource[resources.length];
         for (int i = 0; i < resources.length; i++) {
-            result[i] = getResource(path + "/" + resources[i]);
+            if (path.charAt(path.length() - 1) == '/') {
+                result[i] = getResource(path + resources[i]);
+            } else {
+                result[i] = getResource(path + '/' + resources[i]);
+            }
         }
         return result;
     }
@@ -237,7 +242,12 @@ public class StandardRoot extends Lifecy
 
     @Override
     public void setAllowLinking(boolean allowLinking) {
-        // TODO Implement this feature
+        this.allowLinking = allowLinking;
+    }
+
+    @Override
+    public boolean getAllowLinking() {
+        return allowLinking;
     }
 
     @Override

Modified: 
tomcat/sandbox/trunk-resources/test/org/apache/catalina/webresources/TesterWebResourceRoot.java
URL: 
http://svn.apache.org/viewvc/tomcat/sandbox/trunk-resources/test/org/apache/catalina/webresources/TesterWebResourceRoot.java?rev=1385387&r1=1385386&r2=1385387&view=diff
==============================================================================
--- 
tomcat/sandbox/trunk-resources/test/org/apache/catalina/webresources/TesterWebResourceRoot.java
 (original)
+++ 
tomcat/sandbox/trunk-resources/test/org/apache/catalina/webresources/TesterWebResourceRoot.java
 Sun Sep 16 22:18:54 2012
@@ -138,4 +138,9 @@ public class TesterWebResourceRoot imple
     public void setAllowLinking(boolean allowLinking) {
         // NO-OP
     }
+
+    @Override
+    public boolean getAllowLinking() {
+        return false;
+    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to