Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP5:GA
tomcat.5927
tomcat-8.0.43-CVE-2017-12617.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File tomcat-8.0.43-CVE-2017-12617.patch of Package tomcat.5927
Index: java/org/apache/tomcat/util/compat/JrePlatform.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- java/org/apache/tomcat/util/compat/JrePlatform.java (revision ) +++ java/org/apache/tomcat/util/compat/JrePlatform.java (revision ) @@ -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.tomcat.util.compat; + +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 + public String run() { + return System.getProperty(OS_NAME_PROPERTY); + } + }); + } + + IS_WINDOWS = osName.startsWith(OS_NAME_WINDOWS_PREFIX); + } + + + public static final boolean IS_WINDOWS; +} Index: test/org/apache/catalina/webresources/TestAbstractFileResourceSetPerformance.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- test/org/apache/catalina/webresources/TestAbstractFileResourceSetPerformance.java (revision ) +++ test/org/apache/catalina/webresources/TestAbstractFileResourceSetPerformance.java (revision ) @@ -0,0 +1,90 @@ +/* + * 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.catalina.webresources; + +import java.util.regex.Pattern; + +import org.junit.Test; + +public class TestAbstractFileResourceSetPerformance { + + private static final Pattern UNSAFE_WINDOWS_FILENAME_PATTERN = Pattern.compile(" $|[\"<>]"); + + private static final int LOOPS = 10_000_000; + + /* + * Checking individual characters is about 10 times faster on markt's dev + * PC for typical length file names. The file names need to get to ~65 + * characters before the Pattern matching is faster. + */ + @Test + public void testFileNameFiltering() { + + long start = System.nanoTime(); + for (int i = 0; i < LOOPS; i++) { + UNSAFE_WINDOWS_FILENAME_PATTERN.matcher("testfile.jsp ").find(); + } + long end = System.nanoTime(); + System.out.println("Regular expression took " + (end - start) + "ns or " + + (end-start)/LOOPS + "ns per iteration"); + + start = System.nanoTime(); + for (int i = 0; i < LOOPS; i++) { + checkForBadCharsArray("testfile.jsp "); + } + end = System.nanoTime(); + System.out.println("char[] check took " + (end - start) + "ns or " + + (end-start)/LOOPS + "ns per iteration"); + + start = System.nanoTime(); + for (int i = 0; i < LOOPS; i++) { + checkForBadCharsAt("testfile.jsp "); + } + end = System.nanoTime(); + System.out.println("charAt() check took " + (end - start) + "ns or " + + (end-start)/LOOPS + "ns per iteration"); + + } + + private boolean checkForBadCharsArray(String filename) { + char[] chars = filename.toCharArray(); + for (char c : chars) { + if (c == '\"' || c == '<' || c == '>') { + return false; + } + } + if (chars[chars.length -1] == ' ') { + return false; + } + return true; + } + + + private boolean checkForBadCharsAt(String filename) { + final int len = filename.length(); + for (int i = 0; i < len; i++) { + char c = filename.charAt(i); + if (c == '\"' || c == '<' || c == '>') { + return false; + } + } + if (filename.charAt(len - 1) == ' ') { + return false; + } + return true; + } +} Index: java/org/apache/catalina/webresources/AbstractFileResourceSet.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- java/org/apache/catalina/webresources/AbstractFileResourceSet.java (date 1490711901000) +++ java/org/apache/catalina/webresources/AbstractFileResourceSet.java (revision ) @@ -22,6 +22,7 @@ import java.net.URL; import org.apache.catalina.LifecycleException; +import org.apache.tomcat.util.compat.JrePlatform; import org.apache.tomcat.util.http.RequestUtil; public abstract class AbstractFileResourceSet extends AbstractResourceSet { @@ -57,53 +58,113 @@ name = ""; } File file = new File(fileBase, name); - if (!mustExist || file.canRead()) { - if (getRoot().getAllowLinking()) { - return file; - } + // 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; + } + + // If allow linking is enabled, files are not limited to being located + // under the fileBase so all further checks are disabled. + if (getRoot().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; + // 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 WebResourceSet's base + String canPath = null; + try { + canPath = file.getCanonicalPath(); + } catch (IOException e) { + // Ignore + } + if (canPath == null || !canPath.startsWith(canonicalBase)) { + return null; + } - if (!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, HTTP/2 push 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; + } - // 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; - } + // 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; + } - } else { - 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; + } + + /** * Return a context-relative path, beginning with a "/", that represents * the canonical version of the specified path after ".." and "." elements @@ -114,7 +175,7 @@ * @param path Path to be normalized */ private String normalize(String path) { - return RequestUtil.normalize(path, File.separatorChar == '/'); + return RequestUtil.normalize(path, File.separatorChar == '\\'); } @Override @@ -144,11 +205,7 @@ fileBase = new File(getBase(), getInternalPath()); checkType(fileBase); - String absolutePath = fileBase.getAbsolutePath(); - if (absolutePath.endsWith(".")) { - absolutePath = absolutePath + '/'; - } - this.absoluteBase = normalize(absolutePath); + this.absoluteBase = normalize(fileBase.getAbsolutePath()); try { this.canonicalBase = fileBase.getCanonicalPath();
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor