This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push: new 427732ef8e Automate protection for CVE-2024-56337 427732ef8e is described below commit 427732ef8ee7f707a32734a13a926a2d070b8ba9 Author: Mark Thomas <ma...@apache.org> AuthorDate: Thu Jan 16 16:37:06 2025 +0000 Automate protection for CVE-2024-56337 If there is a potentially vulnerable web application (Java < 21, WebResourceSet configured for read/write, case-insensitive file system) ensure that the JVM behaves as if sun.io.useCanonCaches is false. If that cannot be ensured, prevent the potentially vulnerable web applications from starting. --- bin/catalina.bat | 7 +++ bin/catalina.sh | 7 +++ .../catalina/webresources/DirResourceSet.java | 29 +++++++++ .../catalina/webresources/LocalStrings.properties | 2 + .../org/apache/tomcat/util/compat/Jre21Compat.java | 14 +++++ java/org/apache/tomcat/util/compat/JreCompat.java | 69 +++++++++++++++++++++- .../tomcat/util/compat/LocalStrings.properties | 3 + webapps/docs/changelog.xml | 9 +++ 8 files changed, 139 insertions(+), 1 deletion(-) diff --git a/bin/catalina.bat b/bin/catalina.bat index 05704d6115..9fc3da0369 100755 --- a/bin/catalina.bat +++ b/bin/catalina.bat @@ -219,6 +219,13 @@ rem Register custom URL handlers rem Do this here so custom URL handles (specifically 'war:...') can be used in the security policy set "JAVA_OPTS=%JAVA_OPTS% -Djava.protocol.handler.pkgs=org.apache.catalina.webresources" +rem Disable the global canonical file name cache to protect against CVE-2024-56337 +rem Note: The cache is disabled by default in Java 12 to 20 inclusive +rem The cache is removed in Java 21 onwards +rem Need to set this here as java.io.FileSystem caches this in a static field during class +rem initialisation so it needs to be set before any file system access +set "JAVA_OPTS=%JAVA_OPTS% -Dsun.io.useCanonCaches=false" + rem Check for the deprecated LOGGING_CONFIG rem Only use it if CATALINA_LOGGING_CONFIG is not set and LOGGING_CONFIG starts with "-D..." if not "%LOGGING_CONFIG:~0,2%"=="-D" goto noLoggingDeprecation diff --git a/bin/catalina.sh b/bin/catalina.sh index 3a2ffdafa0..8d07001214 100755 --- a/bin/catalina.sh +++ b/bin/catalina.sh @@ -268,6 +268,13 @@ JAVA_OPTS="$JAVA_OPTS $JSSE_OPTS" # Do this here so custom URL handles (specifically 'war:...') can be used in the security policy JAVA_OPTS="$JAVA_OPTS -Djava.protocol.handler.pkgs=org.apache.catalina.webresources" +# Disable the global canonical file name cache to protect against CVE-2024-56337 +# Note: The cache is disabled by default in Java 12 to 20 inclusive +# The cache is removed in Java 21 onwards +# Need to set this here as java.io.FileSystem caches this in a static field during class +# initialisation so it needs to be set before any file system access +JAVA_OPTS="$JAVA_OPTS -Dsun.io.useCanonCaches=false" + # Check for the deprecated LOGGING_CONFIG # Only use it if CATALINA_LOGGING_CONFIG is not set and LOGGING_CONFIG starts with "-D..." if [ -z "$CATALINA_LOGGING_CONFIG" ]; then diff --git a/java/org/apache/catalina/webresources/DirResourceSet.java b/java/org/apache/catalina/webresources/DirResourceSet.java index 512adf8b8a..4f39e87378 100644 --- a/java/org/apache/catalina/webresources/DirResourceSet.java +++ b/java/org/apache/catalina/webresources/DirResourceSet.java @@ -36,6 +36,7 @@ import org.apache.catalina.WebResourceRoot.ResourceSetType; import org.apache.catalina.util.ResourceSet; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.compat.JreCompat; import org.apache.tomcat.util.http.RequestUtil; /** @@ -335,6 +336,34 @@ public class DirResourceSet extends AbstractFileResourceSet implements WebResour } } } + // Check for exposure to CVE-2024-56337 + if (caseSensitive) { + // CVE-2024-56337 (nor CVE-2024-50379) is not exploitable on a case sensitive file system + return; + } + if (isReadOnly()) { + // CVE-2024-56337 (nor CVE-2024-50379) is not exploitable on a read-only ResourceSet + return; + } + if (JreCompat.getInstance().isCanonCachesDisabled()) { + // CVE-2024-56337 (nor CVE-2024-50379) is not exploitable if the canonical file name cache is disabled + return; + } + // This ResourceSet may be exposed to CVE-2024-56337. + if (JreCompat.getInstance().disableCanonCaches()) { + /* + * The canonical file name cache was enabled and is now disabled. + */ + log.warn(sm.getString("dirResourceSet.canonCaches.disabled", getFileBase(), + getRoot().getContext().getName())); + } else { + /* + * The canonical file name cache could not be disabled (or Tomcat cannot confirm it has been disabled). This + * ResourceSet may be exposed to CVE-2024-56337. + */ + throw new IllegalStateException(sm.getString("dirResourceSet.canonCaches.enabled", getFileBase(), + getRoot().getContext().getName())); + } } diff --git a/java/org/apache/catalina/webresources/LocalStrings.properties b/java/org/apache/catalina/webresources/LocalStrings.properties index 20bcdf5330..2590428e09 100644 --- a/java/org/apache/catalina/webresources/LocalStrings.properties +++ b/java/org/apache/catalina/webresources/LocalStrings.properties @@ -36,6 +36,8 @@ cachedResource.invalidURL=Unable to create an instance of CachedResourceURLStrea classpathUrlStreamHandler.notFound=Unable to load the resource [{0}] using the thread context class loader or the current class''s class loader +dirResourceSet.canonCaches.disabled=Disabled the global canonical file name cache to protect against CVE-2024-56337 when starting the WebResourceSet at [{0}] which is part of the web application [{1}] +dirResourceSet.canonCaches.enabled=Unable to disable the global canonical file name cache or confirm that it is disabled when starting the WebResourceSet at [{0}] which is part of the web application [{1}]. The WebResourceSet may be exposed to CVE-2024-56337. dirResourceSet.isCaseSensitive.fail=Error trying to determine if file system at [{0}] is case sensitive so assuming it is not case sensitive dirResourceSet.manifestFail=Failed to read manifest from [{0}] dirResourceSet.notDirectory=The directory specified by base and internal path [{0}]{1}[{2}] does not exist. diff --git a/java/org/apache/tomcat/util/compat/Jre21Compat.java b/java/org/apache/tomcat/util/compat/Jre21Compat.java index c534ea4e2d..ee01f3e28e 100644 --- a/java/org/apache/tomcat/util/compat/Jre21Compat.java +++ b/java/org/apache/tomcat/util/compat/Jre21Compat.java @@ -104,4 +104,18 @@ public class Jre21Compat extends Jre19Compat { throw new CompletionException(e); } } + + + @Override + public boolean isCanonCachesDisabled() { + // The cache has been removed in Java 21 + return true; + } + + + @Override + public boolean disableCanonCaches() { + // The cache has been removed in Java 21 + return true; + } } diff --git a/java/org/apache/tomcat/util/compat/JreCompat.java b/java/org/apache/tomcat/util/compat/JreCompat.java index 66b11684c7..135b7bc21c 100644 --- a/java/org/apache/tomcat/util/compat/JreCompat.java +++ b/java/org/apache/tomcat/util/compat/JreCompat.java @@ -37,6 +37,8 @@ import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import javax.security.auth.Subject; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.res.StringManager; /** @@ -45,6 +47,9 @@ import org.apache.tomcat.util.res.StringManager; */ public class JreCompat { + private static final Log log = LogFactory.getLog(Jre21Compat.class); + private static final StringManager sm = StringManager.getManager(JreCompat.class); + private static final int RUNTIME_MAJOR_VERSION = 8; private static final JreCompat instance; @@ -55,8 +60,8 @@ public class JreCompat { private static final boolean jre19Available; private static final boolean jre21Available; private static final boolean jre22Available; - private static final StringManager sm = StringManager.getManager(JreCompat.class); + private static final Field useCanonCachesField; protected static final Method setApplicationProtocolsMethod; protected static final Method getApplicationProtocolMethod; @@ -119,6 +124,21 @@ public class JreCompat { } jre11Available = instance.jarFileRuntimeMajorVersion() >= 11; + Field f1 = null; + try { + Class<?> clazz = Class.forName("java.io.FileSystem"); + f1 = clazz.getDeclaredField("useCanonCaches"); + f1.setAccessible(true); + } catch (ReflectiveOperationException | IllegalArgumentException e) { + /* + * Log at debug level as this will only be an issue if the field needs to be accessed and most + * configurations will not need to do so. Appropriate warnings will be logged if an attempt is made to use + * the field when it could not be found/accessed. + */ + log.debug(sm.getString("jreCompat.useCanonCaches.init"), e); + } + useCanonCachesField = f1; + Method m1 = null; Method m2 = null; try { @@ -482,4 +502,51 @@ public class JreCompat { throw new CompletionException(e); } } + + + /* + * The behaviour of the canonical file cache varies by Java version. + * + * The cache was removed in Java 21 so these methods and the associated code can be removed once the minimum Java + * version is 21. + * + * For 12 <= Java <= 20, the cache was present but disabled by default. Since the user may have changed the default + * Tomcat has to assume the cache is enabled unless proven otherwise. + * + * For Java < 12, the cache was enabled by default. Tomcat assumes the cache is enabled unless proven otherwise. + */ + public boolean isCanonCachesDisabled() { + if (useCanonCachesField == null) { + // No need to log a warning. The warning will be logged when trying to disable the cache. + return false; + } + boolean result = false; + try { + result = !((Boolean) useCanonCachesField.get(null)).booleanValue(); + } catch (ReflectiveOperationException e) { + // No need to log a warning. The warning will be logged when trying to disable the cache. + } + return result; + } + + + /** + * Disable the global canonical file cache. + * + * @return {@code true} if the global canonical file cache was already disabled prior to this call or was disabled + * as a result of this call, otherwise {@code false} + */ + public boolean disableCanonCaches() { + if (useCanonCachesField == null) { + log.warn(sm.getString("jreCompat.useCanonCaches.none")); + return false; + } + try { + useCanonCachesField.set(null, Boolean.TRUE); + } catch (ReflectiveOperationException | IllegalArgumentException e) { + log.warn(sm.getString("jreCompat.useCanonCaches.failed"), e); + return false; + } + return true; + } } diff --git a/java/org/apache/tomcat/util/compat/LocalStrings.properties b/java/org/apache/tomcat/util/compat/LocalStrings.properties index ad858297e9..4b33b7a4aa 100644 --- a/java/org/apache/tomcat/util/compat/LocalStrings.properties +++ b/java/org/apache/tomcat/util/compat/LocalStrings.properties @@ -37,3 +37,6 @@ jreCompat.noApplicationProtocol=Java Runtime does not support SSLEngine.getAppli jreCompat.noApplicationProtocols=Java Runtime does not support SSLParameters.setApplicationProtocols(). You must use Java 9 to use this feature. jreCompat.noUnixDomainSocket=Java Runtime does not support Unix domain sockets. You must use Java 16 or later to use this feature. jreCompat.noVirtualThreads=Java Runtime does not support virtual threads. You must use Java 21 or later to use this feature. +jreCompat.useCanonCaches.failed=Failed to set the java.io.FileSystem.useCanonCaches static field +jreCompat.useCanonCaches.init=Unable to create a reference to the java.io.FileSystem.useCanonCaches static field +jreCompat.useCanonCaches.none=No reference to the java.io.FileSystem.useCanonCaches static field available. Enable debug logging for more information. diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 4c54b39903..1861714fcc 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -140,6 +140,15 @@ Improve checks for <code>WEB-INF</code> and <code>META-INF</code> in the WebDAV servlet. Based on a patch submitted by Chenjp. (remm) </fix> + <add> + Add a check to ensure that, if one or more web applications are + potentially vulnerable to CVE-2024-56337, the JVM has been configured to + protect against the vulnerability and to configure the JVM correctly if + not. Where one or more web applications are potentially vulnerable to + CVE-2004-56337 and the JVM cannot be correctly configured or it cannot + be confirmed that the JVM has been correctly configured, prevent the + impacted web applications from starting. (markt) + </add> </changelog> </subsection> <subsection name="Coyote"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org