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