|
|
9e6f2c |
--- java/org/apache/catalina/servlets/DefaultServlet.java.orig 2017-10-13 09:41:05.734302404 -0400
|
|
|
9e6f2c |
+++ java/org/apache/catalina/servlets/DefaultServlet.java 2017-10-13 09:42:53.515701311 -0400
|
|
|
9e6f2c |
@@ -855,23 +855,6 @@
|
|
|
9e6f2c |
return;
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
|
|
|
9e6f2c |
- // If the resource is not a collection, and the resource path
|
|
|
9e6f2c |
- // ends with "/" or "\", return NOT FOUND
|
|
|
9e6f2c |
- if (cacheEntry.context == null) {
|
|
|
9e6f2c |
- if (path.endsWith("/") || (path.endsWith("\\"))) {
|
|
|
9e6f2c |
- // Check if we're included so we can return the appropriate
|
|
|
9e6f2c |
- // missing resource name in the error
|
|
|
9e6f2c |
- String requestUri = (String) request.getAttribute(
|
|
|
9e6f2c |
- RequestDispatcher.INCLUDE_REQUEST_URI);
|
|
|
9e6f2c |
- if (requestUri == null) {
|
|
|
9e6f2c |
- requestUri = request.getRequestURI();
|
|
|
9e6f2c |
- }
|
|
|
9e6f2c |
- response.sendError(HttpServletResponse.SC_NOT_FOUND,
|
|
|
9e6f2c |
- requestUri);
|
|
|
9e6f2c |
- return;
|
|
|
9e6f2c |
- }
|
|
|
9e6f2c |
- }
|
|
|
9e6f2c |
-
|
|
|
9e6f2c |
boolean isError = DispatcherType.ERROR == request.getDispatcherType();
|
|
|
9e6f2c |
|
|
|
9e6f2c |
// Check if the conditions specified in the optional If headers are
|
|
|
9e6f2c |
--- java/org/apache/naming/resources/FileDirContext.java.orig 2017-10-13 09:41:05.737302387 -0400
|
|
|
9e6f2c |
+++ java/org/apache/naming/resources/FileDirContext.java 2017-10-13 09:42:53.516701306 -0400
|
|
|
9e6f2c |
@@ -14,8 +14,6 @@
|
|
|
9e6f2c |
* See the License for the specific language governing permissions and
|
|
|
9e6f2c |
* limitations under the License.
|
|
|
9e6f2c |
*/
|
|
|
9e6f2c |
-
|
|
|
9e6f2c |
-
|
|
|
9e6f2c |
package org.apache.naming.resources;
|
|
|
9e6f2c |
|
|
|
9e6f2c |
import java.io.File;
|
|
|
9e6f2c |
@@ -75,6 +73,8 @@
|
|
|
9e6f2c |
|
|
|
9e6f2c |
/**
|
|
|
9e6f2c |
* Builds a file directory context using the given environment.
|
|
|
9e6f2c |
+ *
|
|
|
9e6f2c |
+ * @param env The environment with which to build the context
|
|
|
9e6f2c |
*/
|
|
|
9e6f2c |
public FileDirContext(Hashtable<String,Object> env) {
|
|
|
9e6f2c |
super(env);
|
|
|
9e6f2c |
@@ -95,6 +95,8 @@
|
|
|
9e6f2c |
*/
|
|
|
9e6f2c |
protected String absoluteBase = null;
|
|
|
9e6f2c |
|
|
|
9e6f2c |
+ private String canonicalBase = null;
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
|
|
|
9e6f2c |
/**
|
|
|
9e6f2c |
* Allow linking.
|
|
|
9e6f2c |
@@ -104,7 +106,6 @@
|
|
|
9e6f2c |
|
|
|
9e6f2c |
// ------------------------------------------------------------- Properties
|
|
|
9e6f2c |
|
|
|
9e6f2c |
-
|
|
|
9e6f2c |
/**
|
|
|
9e6f2c |
* Set the document root.
|
|
|
9e6f2c |
*
|
|
|
9e6f2c |
@@ -117,32 +118,41 @@
|
|
|
9e6f2c |
*/
|
|
|
9e6f2c |
@Override
|
|
|
9e6f2c |
public void setDocBase(String docBase) {
|
|
|
9e6f2c |
+ // Validate the format of the proposed document root
|
|
|
9e6f2c |
+ if (docBase == null) {
|
|
|
9e6f2c |
+ throw new IllegalArgumentException(sm.getString("resources.null"));
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
|
|
|
9e6f2c |
- // Validate the format of the proposed document root
|
|
|
9e6f2c |
- if (docBase == null)
|
|
|
9e6f2c |
- throw new IllegalArgumentException
|
|
|
9e6f2c |
- (sm.getString("resources.null"));
|
|
|
9e6f2c |
-
|
|
|
9e6f2c |
- // Calculate a File object referencing this document base directory
|
|
|
9e6f2c |
- base = new File(docBase);
|
|
|
9e6f2c |
+ // Calculate a File object referencing this document base directory
|
|
|
9e6f2c |
+ base = new File(docBase);
|
|
|
9e6f2c |
try {
|
|
|
9e6f2c |
base = base.getCanonicalFile();
|
|
|
9e6f2c |
} catch (IOException e) {
|
|
|
9e6f2c |
// Ignore
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
|
|
|
9e6f2c |
- // Validate that the document base is an existing directory
|
|
|
9e6f2c |
- if (!base.exists() || !base.isDirectory() || !base.canRead())
|
|
|
9e6f2c |
- throw new IllegalArgumentException
|
|
|
9e6f2c |
- (sm.getString("fileResources.base", docBase));
|
|
|
9e6f2c |
- this.absoluteBase = base.getAbsolutePath();
|
|
|
9e6f2c |
- super.setDocBase(docBase);
|
|
|
9e6f2c |
+ // Validate that the document base is an existing directory
|
|
|
9e6f2c |
+ if (!base.exists() || !base.isDirectory() || !base.canRead()) {
|
|
|
9e6f2c |
+ throw new IllegalArgumentException(sm.getString("fileResources.base", docBase));
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
|
|
|
9e6f2c |
+ this.absoluteBase = normalize(base.getAbsolutePath());
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ // absoluteBase also needs to be normalized. Using the canonical path is
|
|
|
9e6f2c |
+ // the simplest way of doing this.
|
|
|
9e6f2c |
+ try {
|
|
|
9e6f2c |
+ this.canonicalBase = base.getCanonicalPath();
|
|
|
9e6f2c |
+ } catch (IOException e) {
|
|
|
9e6f2c |
+ throw new IllegalArgumentException(e);
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+ super.setDocBase(docBase);
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
|
|
|
9e6f2c |
|
|
|
9e6f2c |
/**
|
|
|
9e6f2c |
* Set allow linking.
|
|
|
9e6f2c |
+ *
|
|
|
9e6f2c |
+ * @param allowLinking The new value for the attribute
|
|
|
9e6f2c |
*/
|
|
|
9e6f2c |
public void setAllowLinking(boolean allowLinking) {
|
|
|
9e6f2c |
this.allowLinking = allowLinking;
|
|
|
9e6f2c |
@@ -151,6 +161,8 @@
|
|
|
9e6f2c |
|
|
|
9e6f2c |
/**
|
|
|
9e6f2c |
* Is linking allowed.
|
|
|
9e6f2c |
+ *
|
|
|
9e6f2c |
+ * @return {@code true} is linking is allowed, otherwise {@false}
|
|
|
9e6f2c |
*/
|
|
|
9e6f2c |
public boolean getAllowLinking() {
|
|
|
9e6f2c |
return allowLinking;
|
|
|
9e6f2c |
@@ -193,7 +205,7 @@
|
|
|
9e6f2c |
@Override
|
|
|
9e6f2c |
protected Object doLookup(String name) {
|
|
|
9e6f2c |
Object result = null;
|
|
|
9e6f2c |
- File file = file(name);
|
|
|
9e6f2c |
+ File file = file(name, true);
|
|
|
9e6f2c |
|
|
|
9e6f2c |
if (file == null)
|
|
|
9e6f2c |
return null;
|
|
|
9e6f2c |
@@ -230,7 +242,7 @@
|
|
|
9e6f2c |
public void unbind(String name)
|
|
|
9e6f2c |
throws NamingException {
|
|
|
9e6f2c |
|
|
|
9e6f2c |
- File file = file(name);
|
|
|
9e6f2c |
+ File file = file(name, true);
|
|
|
9e6f2c |
|
|
|
9e6f2c |
if (file == null)
|
|
|
9e6f2c |
throw new NameNotFoundException(
|
|
|
9e6f2c |
@@ -255,22 +267,22 @@
|
|
|
9e6f2c |
* @exception NamingException if a naming exception is encountered
|
|
|
9e6f2c |
*/
|
|
|
9e6f2c |
@Override
|
|
|
9e6f2c |
- public void rename(String oldName, String newName)
|
|
|
9e6f2c |
- throws NamingException {
|
|
|
9e6f2c |
+ public void rename(String oldName, String newName) throws NamingException {
|
|
|
9e6f2c |
|
|
|
9e6f2c |
- File file = file(oldName);
|
|
|
9e6f2c |
+ File file = file(oldName, true);
|
|
|
9e6f2c |
|
|
|
9e6f2c |
- if (file == null)
|
|
|
9e6f2c |
- throw new NameNotFoundException
|
|
|
9e6f2c |
- (sm.getString("resources.notFound", oldName));
|
|
|
9e6f2c |
+ if (file == null) {
|
|
|
9e6f2c |
+ throw new NameNotFoundException(sm.getString("resources.notFound", oldName));
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
|
|
|
9e6f2c |
- File newFile = new File(base, newName);
|
|
|
9e6f2c |
+ File newFile = file(newName, false);
|
|
|
9e6f2c |
+ if (newFile == null) {
|
|
|
9e6f2c |
+ throw new NamingException(sm.getString("resources.renameFail", oldName, newName));
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
|
|
|
9e6f2c |
if (!file.renameTo(newFile)) {
|
|
|
9e6f2c |
- throw new NamingException(sm.getString("resources.renameFail",
|
|
|
9e6f2c |
- oldName, newName));
|
|
|
9e6f2c |
+ throw new NamingException(sm.getString("resources.renameFail", oldName, newName));
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
-
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
|
|
|
9e6f2c |
|
|
|
9e6f2c |
@@ -291,11 +303,11 @@
|
|
|
9e6f2c |
protected List<NamingEntry> doListBindings(String name)
|
|
|
9e6f2c |
throws NamingException {
|
|
|
9e6f2c |
|
|
|
9e6f2c |
- File file = file(name);
|
|
|
9e6f2c |
+ File file = file(name, true);
|
|
|
9e6f2c |
|
|
|
9e6f2c |
if (file == null)
|
|
|
9e6f2c |
return null;
|
|
|
9e6f2c |
-
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
return list(file);
|
|
|
9e6f2c |
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
@@ -395,7 +407,7 @@
|
|
|
9e6f2c |
throws NamingException {
|
|
|
9e6f2c |
|
|
|
9e6f2c |
// Building attribute list
|
|
|
9e6f2c |
- File file = file(name);
|
|
|
9e6f2c |
+ File file = file(name, true);
|
|
|
9e6f2c |
|
|
|
9e6f2c |
if (file == null)
|
|
|
9e6f2c |
return null;
|
|
|
9e6f2c |
@@ -463,12 +475,20 @@
|
|
|
9e6f2c |
* @exception NamingException if a naming exception is encountered
|
|
|
9e6f2c |
*/
|
|
|
9e6f2c |
@Override
|
|
|
9e6f2c |
- public void bind(String name, Object obj, Attributes attrs)
|
|
|
9e6f2c |
- throws NamingException {
|
|
|
9e6f2c |
+ public void bind(String name, Object obj, Attributes attrs) throws NamingException {
|
|
|
9e6f2c |
|
|
|
9e6f2c |
// Note: No custom attributes allowed
|
|
|
9e6f2c |
|
|
|
9e6f2c |
- File file = new File(base, name);
|
|
|
9e6f2c |
+ // bind() is meant to create a file so ensure that the path doesn't end
|
|
|
9e6f2c |
+ // in '/'
|
|
|
9e6f2c |
+ if (name.endsWith("/")) {
|
|
|
9e6f2c |
+ throw new NamingException(sm.getString("resources.bindFailed", name));
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ File file = file(name, false);
|
|
|
9e6f2c |
+ if (file == null) {
|
|
|
9e6f2c |
+ throw new NamingException(sm.getString("resources.bindFailed", name));
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
if (file.exists())
|
|
|
9e6f2c |
throw new NameAlreadyBoundException
|
|
|
9e6f2c |
(sm.getString("resources.alreadyBound", name));
|
|
|
9e6f2c |
@@ -503,7 +523,10 @@
|
|
|
9e6f2c |
// Note: No custom attributes allowed
|
|
|
9e6f2c |
// Check obj type
|
|
|
9e6f2c |
|
|
|
9e6f2c |
- File file = new File(base, name);
|
|
|
9e6f2c |
+ File file = file(name, false);
|
|
|
9e6f2c |
+ if (file == null) {
|
|
|
9e6f2c |
+ throw new NamingException(sm.getString("resources.bindFailed", name));
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
|
|
|
9e6f2c |
InputStream is = null;
|
|
|
9e6f2c |
if (obj instanceof Resource) {
|
|
|
9e6f2c |
@@ -583,13 +606,14 @@
|
|
|
9e6f2c |
public DirContext createSubcontext(String name, Attributes attrs)
|
|
|
9e6f2c |
throws NamingException {
|
|
|
9e6f2c |
|
|
|
9e6f2c |
- File file = new File(base, name);
|
|
|
9e6f2c |
+ File file = file(name, false);
|
|
|
9e6f2c |
+ if (file == null) {
|
|
|
9e6f2c |
+ throw new NamingException(sm.getString("resources.bindFailed", name));
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
if (file.exists())
|
|
|
9e6f2c |
- throw new NameAlreadyBoundException
|
|
|
9e6f2c |
- (sm.getString("resources.alreadyBound", name));
|
|
|
9e6f2c |
+ throw new NameAlreadyBoundException(sm.getString("resources.alreadyBound", name));
|
|
|
9e6f2c |
if (!file.mkdir())
|
|
|
9e6f2c |
- throw new NamingException
|
|
|
9e6f2c |
- (sm.getString("resources.bindFailed", name));
|
|
|
9e6f2c |
+ throw new NamingException(sm.getString("resources.bindFailed", name));
|
|
|
9e6f2c |
return (DirContext) lookup(name);
|
|
|
9e6f2c |
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
@@ -758,6 +782,7 @@
|
|
|
9e6f2c |
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
/**
|
|
|
9e6f2c |
* Return a File object representing the specified normalized
|
|
|
9e6f2c |
* context-relative path if it exists and is readable. Otherwise,
|
|
|
9e6f2c |
@@ -766,51 +791,133 @@
|
|
|
9e6f2c |
* @param name Normalized context-relative path (with leading '/')
|
|
|
9e6f2c |
*/
|
|
|
9e6f2c |
protected File file(String name) {
|
|
|
9e6f2c |
+ return file(name, true);
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ /**
|
|
|
9e6f2c |
+ * Return a File object representing the specified normalized
|
|
|
9e6f2c |
+ * context-relative path if it exists and is readable. Otherwise,
|
|
|
9e6f2c |
+ * return null .
|
|
|
9e6f2c |
+ *
|
|
|
9e6f2c |
+ * @param name Normalized context-relative path (with leading '/')
|
|
|
9e6f2c |
+ * @param mustExist Must the specified resource exist?
|
|
|
9e6f2c |
+ */
|
|
|
9e6f2c |
+ protected File file(String name, boolean mustExist) {
|
|
|
9e6f2c |
+ if (name.equals("/")) {
|
|
|
9e6f2c |
+ name = "";
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
|
|
|
9e6f2c |
File file = new File(base, name);
|
|
|
9e6f2c |
- if (file.exists() && file.canRead()) {
|
|
|
9e6f2c |
+ return validate(file, name, mustExist, absoluteBase, canonicalBase);
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
|
|
|
9e6f2c |
- if (allowLinking)
|
|
|
9e6f2c |
- return file;
|
|
|
9e6f2c |
-
|
|
|
9e6f2c |
- // Check that this file belongs to our root path
|
|
|
9e6f2c |
- String canPath = null;
|
|
|
9e6f2c |
- try {
|
|
|
9e6f2c |
- canPath = file.getCanonicalPath();
|
|
|
9e6f2c |
- } catch (IOException e) {
|
|
|
9e6f2c |
- // Ignore
|
|
|
9e6f2c |
- }
|
|
|
9e6f2c |
- if (canPath == null)
|
|
|
9e6f2c |
- return null;
|
|
|
9e6f2c |
|
|
|
9e6f2c |
- // Check to see if going outside of the web application root
|
|
|
9e6f2c |
- if (!canPath.startsWith(absoluteBase)) {
|
|
|
9e6f2c |
- return null;
|
|
|
9e6f2c |
- }
|
|
|
9e6f2c |
+ protected File validate(File file, String name, boolean mustExist, String absoluteBase,
|
|
|
9e6f2c |
+ String canonicalBase) {
|
|
|
9e6f2c |
|
|
|
9e6f2c |
- // Case sensitivity check - this is now always done
|
|
|
9e6f2c |
- String fileAbsPath = file.getAbsolutePath();
|
|
|
9e6f2c |
- if (fileAbsPath.endsWith("."))
|
|
|
9e6f2c |
- fileAbsPath = fileAbsPath + "/";
|
|
|
9e6f2c |
- String absPath = normalize(fileAbsPath);
|
|
|
9e6f2c |
- canPath = normalize(canPath);
|
|
|
9e6f2c |
- if ((absoluteBase.length() < absPath.length())
|
|
|
9e6f2c |
- && (absoluteBase.length() < canPath.length())) {
|
|
|
9e6f2c |
- absPath = absPath.substring(absoluteBase.length() + 1);
|
|
|
9e6f2c |
- if (absPath.equals(""))
|
|
|
9e6f2c |
- absPath = "/";
|
|
|
9e6f2c |
- canPath = canPath.substring(absoluteBase.length() + 1);
|
|
|
9e6f2c |
- if (canPath.equals(""))
|
|
|
9e6f2c |
- canPath = "/";
|
|
|
9e6f2c |
- if (!canPath.equals(absPath))
|
|
|
9e6f2c |
- return null;
|
|
|
9e6f2c |
- }
|
|
|
9e6f2c |
+ // If the requested names ends in '/', the Java File API will return a
|
|
|
9e6f2c |
+ // matching file if one exists. This isn't what we want as it is not
|
|
|
9e6f2c |
+ // consistent with the Servlet spec rules for request mapping.
|
|
|
9e6f2c |
+ if (name.endsWith("/") && file.isFile()) {
|
|
|
9e6f2c |
+ return null;
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
|
|
|
9e6f2c |
- } else {
|
|
|
9e6f2c |
+ // If the file/dir must exist but the identified file/dir can't be read
|
|
|
9e6f2c |
+ // then signal that the resource was not found
|
|
|
9e6f2c |
+ if (mustExist && !file.canRead()) {
|
|
|
9e6f2c |
+ return null;
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ // If allow linking is enabled, files are not limited to being located
|
|
|
9e6f2c |
+ // under the fileBase so all further checks are disabled.
|
|
|
9e6f2c |
+ if (allowLinking) {
|
|
|
9e6f2c |
+ return file;
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ // Additional Windows specific checks to handle known problems with
|
|
|
9e6f2c |
+ // File.getCanonicalPath()
|
|
|
9e6f2c |
+ if (JrePlatform.IS_WINDOWS && isInvalidWindowsFilename(name)) {
|
|
|
9e6f2c |
+ return null;
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ // Check that this file is located under the web application root
|
|
|
9e6f2c |
+ String canPath = null;
|
|
|
9e6f2c |
+ try {
|
|
|
9e6f2c |
+ canPath = file.getCanonicalPath();
|
|
|
9e6f2c |
+ } catch (IOException e) {
|
|
|
9e6f2c |
+ // Ignore
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+ if (canPath == null || !canPath.startsWith(canonicalBase)) {
|
|
|
9e6f2c |
+ return null;
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ // Ensure that the file is not outside the fileBase. This should not be
|
|
|
9e6f2c |
+ // possible for standard requests (the request is normalized early in
|
|
|
9e6f2c |
+ // the request processing) but might be possible for some access via the
|
|
|
9e6f2c |
+ // Servlet API (RequestDispatcher etc.) therefore these checks are
|
|
|
9e6f2c |
+ // retained as an additional safety measure. absoluteBase has been
|
|
|
9e6f2c |
+ // normalized so absPath needs to be normalized as well.
|
|
|
9e6f2c |
+ String absPath = normalize(file.getAbsolutePath());
|
|
|
9e6f2c |
+ if ((absoluteBase.length() > absPath.length())) {
|
|
|
9e6f2c |
return null;
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ // Remove the fileBase location from the start of the paths since that
|
|
|
9e6f2c |
+ // was not part of the requested path and the remaining check only
|
|
|
9e6f2c |
+ // applies to the request path
|
|
|
9e6f2c |
+ absPath = absPath.substring(absoluteBase.length());
|
|
|
9e6f2c |
+ canPath = canPath.substring(canonicalBase.length());
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ // Case sensitivity check
|
|
|
9e6f2c |
+ // The normalized requested path should be an exact match the equivalent
|
|
|
9e6f2c |
+ // canonical path. If it is not, possible reasons include:
|
|
|
9e6f2c |
+ // - case differences on case insensitive file systems
|
|
|
9e6f2c |
+ // - Windows removing a trailing ' ' or '.' from the file name
|
|
|
9e6f2c |
+ //
|
|
|
9e6f2c |
+ // In all cases, a mis-match here results in the resource not being
|
|
|
9e6f2c |
+ // found
|
|
|
9e6f2c |
+ //
|
|
|
9e6f2c |
+ // absPath is normalized so canPath needs to be normalized as well
|
|
|
9e6f2c |
+ // Can't normalize canPath earlier as canonicalBase is not normalized
|
|
|
9e6f2c |
+ if (canPath.length() > 0) {
|
|
|
9e6f2c |
+ canPath = normalize(canPath);
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+ if (!canPath.equals(absPath)) {
|
|
|
9e6f2c |
+ return null;
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
return file;
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
|
|
|
9e6f2c |
+ private boolean isInvalidWindowsFilename(String name) {
|
|
|
9e6f2c |
+ final int len = name.length();
|
|
|
9e6f2c |
+ if (len == 0) {
|
|
|
9e6f2c |
+ return false;
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+ // This consistently ~10 times faster than the equivalent regular
|
|
|
9e6f2c |
+ // expression irrespective of input length.
|
|
|
9e6f2c |
+ for (int i = 0; i < len; i++) {
|
|
|
9e6f2c |
+ char c = name.charAt(i);
|
|
|
9e6f2c |
+ if (c == '\"' || c == '<' || c == '>') {
|
|
|
9e6f2c |
+ // These characters are disallowed in Windows file names and
|
|
|
9e6f2c |
+ // there are known problems for file names with these characters
|
|
|
9e6f2c |
+ // when using File#getCanonicalPath().
|
|
|
9e6f2c |
+ // Note: There are additional characters that are disallowed in
|
|
|
9e6f2c |
+ // Windows file names but these are not known to cause
|
|
|
9e6f2c |
+ // problems when using File#getCanonicalPath().
|
|
|
9e6f2c |
+ return true;
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+ // Windows does not allow file names to end in ' ' unless specific low
|
|
|
9e6f2c |
+ // level APIs are used to create the files that bypass various checks.
|
|
|
9e6f2c |
+ // File names that end in ' ' are known to cause problems when using
|
|
|
9e6f2c |
+ // File#getCanonicalPath().
|
|
|
9e6f2c |
+ if (name.charAt(len -1) == ' ') {
|
|
|
9e6f2c |
+ return true;
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+ return false;
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
|
|
|
9e6f2c |
|
|
|
9e6f2c |
@@ -1054,10 +1161,10 @@
|
|
|
9e6f2c |
return super.getResourceType();
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
|
|
|
9e6f2c |
-
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
/**
|
|
|
9e6f2c |
* Get canonical path.
|
|
|
9e6f2c |
- *
|
|
|
9e6f2c |
+ *
|
|
|
9e6f2c |
* @return String the file's canonical path
|
|
|
9e6f2c |
*/
|
|
|
9e6f2c |
@Override
|
|
|
9e6f2c |
@@ -1071,10 +1178,6 @@
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
return canonicalPath;
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
-
|
|
|
9e6f2c |
-
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
-
|
|
|
9e6f2c |
-
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
|
|
|
9e6f2c |
--- java/org/apache/naming/resources/VirtualDirContext.java.orig 2017-10-13 09:41:05.740302370 -0400
|
|
|
9e6f2c |
+++ java/org/apache/naming/resources/VirtualDirContext.java 2017-10-13 09:42:53.517701300 -0400
|
|
|
9e6f2c |
@@ -76,7 +76,8 @@
|
|
|
9e6f2c |
* be listed twice.
|
|
|
9e6f2c |
*
|
|
|
9e6f2c |
*
|
|
|
9e6f2c |
- * @param path
|
|
|
9e6f2c |
+ * @param path The set of file system paths and virtual paths to map them to
|
|
|
9e6f2c |
+ * in the required format
|
|
|
9e6f2c |
*/
|
|
|
9e6f2c |
public void setExtraResourcePaths(String path) {
|
|
|
9e6f2c |
extraResourcePaths = path;
|
|
|
9e6f2c |
@@ -106,13 +107,13 @@
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
path = resSpec.substring(0, idx);
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
- String dir = resSpec.substring(idx + 1);
|
|
|
9e6f2c |
+ File dir = new File(resSpec.substring(idx + 1));
|
|
|
9e6f2c |
List<String> resourcePaths = mappedResourcePaths.get(path);
|
|
|
9e6f2c |
if (resourcePaths == null) {
|
|
|
9e6f2c |
resourcePaths = new ArrayList<String>();
|
|
|
9e6f2c |
mappedResourcePaths.put(path, resourcePaths);
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
- resourcePaths.add(dir);
|
|
|
9e6f2c |
+ resourcePaths.add(dir.getAbsolutePath());
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
if (mappedResourcePaths.isEmpty()) {
|
|
|
9e6f2c |
@@ -151,15 +152,17 @@
|
|
|
9e6f2c |
String resourcesDir = dirList.get(0);
|
|
|
9e6f2c |
if (name.equals(path)) {
|
|
|
9e6f2c |
File f = new File(resourcesDir);
|
|
|
9e6f2c |
- if (f.exists() && f.canRead()) {
|
|
|
9e6f2c |
+ f = validate(f, name, true, resourcesDir);
|
|
|
9e6f2c |
+ if (f != null) {
|
|
|
9e6f2c |
return new FileResourceAttributes(f);
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
path += "/";
|
|
|
9e6f2c |
if (name.startsWith(path)) {
|
|
|
9e6f2c |
String res = name.substring(path.length());
|
|
|
9e6f2c |
- File f = new File(resourcesDir + "/" + res);
|
|
|
9e6f2c |
- if (f.exists() && f.canRead()) {
|
|
|
9e6f2c |
+ File f = new File(resourcesDir, res);
|
|
|
9e6f2c |
+ f = validate(f, res, true, resourcesDir);
|
|
|
9e6f2c |
+ if (f != null) {
|
|
|
9e6f2c |
return new FileResourceAttributes(f);
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
@@ -168,9 +171,16 @@
|
|
|
9e6f2c |
throw initialException;
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
@Override
|
|
|
9e6f2c |
protected File file(String name) {
|
|
|
9e6f2c |
- File file = super.file(name);
|
|
|
9e6f2c |
+ return file(name, true);
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ @Override
|
|
|
9e6f2c |
+ protected File file(String name, boolean mustExist) {
|
|
|
9e6f2c |
+ File file = super.file(name, true);
|
|
|
9e6f2c |
if (file != null || mappedResourcePaths == null) {
|
|
|
9e6f2c |
return file;
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
@@ -185,7 +195,8 @@
|
|
|
9e6f2c |
if (name.equals(path)) {
|
|
|
9e6f2c |
for (String resourcesDir : dirList) {
|
|
|
9e6f2c |
file = new File(resourcesDir);
|
|
|
9e6f2c |
- if (file.exists() && file.canRead()) {
|
|
|
9e6f2c |
+ file = validate(file, name, true, resourcesDir);
|
|
|
9e6f2c |
+ if (file != null) {
|
|
|
9e6f2c |
return file;
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
@@ -194,7 +205,8 @@
|
|
|
9e6f2c |
String res = name.substring(path.length());
|
|
|
9e6f2c |
for (String resourcesDir : dirList) {
|
|
|
9e6f2c |
file = new File(resourcesDir, res);
|
|
|
9e6f2c |
- if (file.exists() && file.canRead()) {
|
|
|
9e6f2c |
+ file = validate(file, res, true, resourcesDir);
|
|
|
9e6f2c |
+ if (file != null) {
|
|
|
9e6f2c |
return file;
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
@@ -229,7 +241,8 @@
|
|
|
9e6f2c |
if (res != null) {
|
|
|
9e6f2c |
for (String resourcesDir : dirList) {
|
|
|
9e6f2c |
File f = new File(resourcesDir, res);
|
|
|
9e6f2c |
- if (f.exists() && f.canRead() && f.isDirectory()) {
|
|
|
9e6f2c |
+ f = validate(f, res, true, resourcesDir);
|
|
|
9e6f2c |
+ if (f != null && f.isDirectory()) {
|
|
|
9e6f2c |
List<NamingEntry> virtEntries = super.list(f);
|
|
|
9e6f2c |
for (NamingEntry entry : virtEntries) {
|
|
|
9e6f2c |
// filter duplicate
|
|
|
9e6f2c |
@@ -264,7 +277,8 @@
|
|
|
9e6f2c |
if (name.equals(path)) {
|
|
|
9e6f2c |
for (String resourcesDir : dirList) {
|
|
|
9e6f2c |
File f = new File(resourcesDir);
|
|
|
9e6f2c |
- if (f.exists() && f.canRead()) {
|
|
|
9e6f2c |
+ f = validate(f, name, true, resourcesDir);
|
|
|
9e6f2c |
+ if (f != null) {
|
|
|
9e6f2c |
if (f.isFile()) {
|
|
|
9e6f2c |
return new FileResource(f);
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
@@ -279,8 +293,9 @@
|
|
|
9e6f2c |
if (name.startsWith(path)) {
|
|
|
9e6f2c |
String res = name.substring(path.length());
|
|
|
9e6f2c |
for (String resourcesDir : dirList) {
|
|
|
9e6f2c |
- File f = new File(resourcesDir + "/" + res);
|
|
|
9e6f2c |
- if (f.exists() && f.canRead()) {
|
|
|
9e6f2c |
+ File f = new File(resourcesDir, res);
|
|
|
9e6f2c |
+ f = validate(f, res, true, resourcesDir);
|
|
|
9e6f2c |
+ if (f != null) {
|
|
|
9e6f2c |
if (f.isFile()) {
|
|
|
9e6f2c |
return new FileResource(f);
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
@@ -304,4 +319,9 @@
|
|
|
9e6f2c |
return null;
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ protected File validate(File file, String name, boolean mustExist, String absoluteBase) {
|
|
|
9e6f2c |
+ return validate(file, name, mustExist, normalize(absoluteBase), absoluteBase);
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
}
|
|
|
9e6f2c |
--- webapps/docs/changelog.xml.orig 2017-10-13 09:15:35.996884086 -0400
|
|
|
9e6f2c |
+++ webapps/docs/changelog.xml 2017-10-13 09:44:50.895046977 -0400
|
|
|
9e6f2c |
@@ -64,6 +64,14 @@
|
|
|
9e6f2c |
<bug>61101</bug>: CORS filter should set Vary header in response.
|
|
|
9e6f2c |
Submitted by Rick Riemer. (remm)
|
|
|
9e6f2c |
</fix>
|
|
|
9e6f2c |
+ <fix>
|
|
|
9e6f2c |
+ Correct regression in 7.0.80 that broke WebDAV. (markt)
|
|
|
9e6f2c |
+ </fix>
|
|
|
9e6f2c |
+ <fix>
|
|
|
9e6f2c |
+ <bug>61542</bug>: Fix CVE-2017-12617 and prevent JSPs from being
|
|
|
9e6f2c |
+ uploaded via a specially crafted request when HTTP PUT was enabled.
|
|
|
9e6f2c |
+ (markt)
|
|
|
9e6f2c |
+ </fix>
|
|
|
9e6f2c |
</changelog>
|
|
|
9e6f2c |
</subsection>
|
|
|
9e6f2c |
</section>
|
|
|
9e6f2c |
--- java/org/apache/naming/resources/JrePlatform.java.orig 2017-10-13 09:41:05.745302342 -0400
|
|
|
9e6f2c |
+++ java/org/apache/naming/resources/JrePlatform.java 2017-10-13 09:42:53.516701306 -0400
|
|
|
9e6f2c |
@@ -0,0 +1,59 @@
|
|
|
9e6f2c |
+/*
|
|
|
9e6f2c |
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
9e6f2c |
+ * contributor license agreements. See the NOTICE file distributed with
|
|
|
9e6f2c |
+ * this work for additional information regarding copyright ownership.
|
|
|
9e6f2c |
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
|
9e6f2c |
+ * (the "License"); you may not use this file except in compliance with
|
|
|
9e6f2c |
+ * the License. You may obtain a copy of the License at
|
|
|
9e6f2c |
+ *
|
|
|
9e6f2c |
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
9e6f2c |
+ *
|
|
|
9e6f2c |
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
9e6f2c |
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
9e6f2c |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
9e6f2c |
+ * See the License for the specific language governing permissions and
|
|
|
9e6f2c |
+ * limitations under the License.
|
|
|
9e6f2c |
+ */
|
|
|
9e6f2c |
+package org.apache.naming.resources;
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+import java.security.AccessController;
|
|
|
9e6f2c |
+import java.security.PrivilegedAction;
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+public class JrePlatform {
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ private static final String OS_NAME_PROPERTY = "os.name";
|
|
|
9e6f2c |
+ private static final String OS_NAME_WINDOWS_PREFIX = "Windows";
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ static {
|
|
|
9e6f2c |
+ /*
|
|
|
9e6f2c |
+ * There are a few places where a) the behaviour of the Java API depends
|
|
|
9e6f2c |
+ * on the underlying platform and b) those behavioural differences have
|
|
|
9e6f2c |
+ * an impact on Tomcat.
|
|
|
9e6f2c |
+ *
|
|
|
9e6f2c |
+ * Tomcat therefore needs to be able to determine the platform it is
|
|
|
9e6f2c |
+ * running on to account for those differences.
|
|
|
9e6f2c |
+ *
|
|
|
9e6f2c |
+ * In an ideal world this code would not exist.
|
|
|
9e6f2c |
+ */
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ // This check is derived from the check in Apache Commons Lang
|
|
|
9e6f2c |
+ String osName;
|
|
|
9e6f2c |
+ if (System.getSecurityManager() == null) {
|
|
|
9e6f2c |
+ osName = System.getProperty(OS_NAME_PROPERTY);
|
|
|
9e6f2c |
+ } else {
|
|
|
9e6f2c |
+ osName = AccessController.doPrivileged(
|
|
|
9e6f2c |
+ new PrivilegedAction<String>() {
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ @Override
|
|
|
9e6f2c |
+ public String run() {
|
|
|
9e6f2c |
+ return System.getProperty(OS_NAME_PROPERTY);
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+ });
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ IS_WINDOWS = osName.startsWith(OS_NAME_WINDOWS_PREFIX);
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ public static final boolean IS_WINDOWS;
|
|
|
9e6f2c |
+}
|
|
|
9e6f2c |
--- test/org/apache/naming/resources/TestFileDirContext.java.orig 2017-10-13 09:45:35.991795584 -0400
|
|
|
9e6f2c |
+++ test/org/apache/naming/resources/TestFileDirContext.java 2017-10-13 09:42:53.517701300 -0400
|
|
|
9e6f2c |
@@ -0,0 +1,46 @@
|
|
|
9e6f2c |
+/*
|
|
|
9e6f2c |
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
9e6f2c |
+ * contributor license agreements. See the NOTICE file distributed with
|
|
|
9e6f2c |
+ * this work for additional information regarding copyright ownership.
|
|
|
9e6f2c |
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
|
9e6f2c |
+ * (the "License"); you may not use this file except in compliance with
|
|
|
9e6f2c |
+ * the License. You may obtain a copy of the License at
|
|
|
9e6f2c |
+ *
|
|
|
9e6f2c |
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
9e6f2c |
+ *
|
|
|
9e6f2c |
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
9e6f2c |
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
9e6f2c |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
9e6f2c |
+ * See the License for the specific language governing permissions and
|
|
|
9e6f2c |
+ * limitations under the License.
|
|
|
9e6f2c |
+ */
|
|
|
9e6f2c |
+package org.apache.naming.resources;
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+import java.io.File;
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+import javax.servlet.http.HttpServletResponse;
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+import org.junit.Assert;
|
|
|
9e6f2c |
+import org.junit.Test;
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+import org.apache.catalina.startup.Tomcat;
|
|
|
9e6f2c |
+import org.apache.catalina.startup.TomcatBaseTest;
|
|
|
9e6f2c |
+import org.apache.tomcat.util.buf.ByteChunk;
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+public class TestFileDirContext extends TomcatBaseTest {
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ @Test
|
|
|
9e6f2c |
+ public void testLookupResourceWithTrailingSlash() throws Exception {
|
|
|
9e6f2c |
+ Tomcat tomcat = getTomcatInstance();
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ File appDir = new File("test/webapp-3.0");
|
|
|
9e6f2c |
+ // app dir is relative to server home
|
|
|
9e6f2c |
+ tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ tomcat.start();
|
|
|
9e6f2c |
+
|
|
|
9e6f2c |
+ int sc = getUrl("http://localhost:" + getPort() +
|
|
|
9e6f2c |
+ "/test/index.html/", new ByteChunk(), null);
|
|
|
9e6f2c |
+ Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, sc);
|
|
|
9e6f2c |
+ }
|
|
|
9e6f2c |
+}
|