This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/10.1.x by this push:
new 6b00658e88 Automate protection for CVE-2024-56337
6b00658e88 is described below
commit 6b00658e8817d3747dc2c46fa7d8e8ba2724649c
Author: Mark Thomas <[email protected]>
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 | 70 +++++++++++++++++++++-
.../tomcat/util/compat/LocalStrings.properties | 3 +
webapps/docs/changelog.xml | 9 +++
8 files changed, 140 insertions(+), 1 deletion(-)
diff --git a/bin/catalina.bat b/bin/catalina.bat
index 9c55ae940e..5e1a3e8006 100755
--- a/bin/catalina.bat
+++ b/bin/catalina.bat
@@ -207,6 +207,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"
+
if not "%CATALINA_LOGGING_CONFIG%" == "" goto noJuliConfig
set CATALINA_LOGGING_CONFIG=-Dnop
if not exist "%CATALINA_BASE%\conf\logging.properties" goto noJuliConfig
diff --git a/bin/catalina.sh b/bin/catalina.sh
index 32f87ffb6f..959a637389 100755
--- a/bin/catalina.sh
+++ b/bin/catalina.sh
@@ -255,6 +255,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"
+
# Set juli LogManager config file if it is present and an override has not
been issued
if [ -z "$CATALINA_LOGGING_CONFIG" ]; then
if [ -r "$CATALINA_BASE"/conf/logging.properties ]; 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 4e4666d95e..0aae54fe95 100644
--- a/java/org/apache/tomcat/util/compat/JreCompat.java
+++ b/java/org/apache/tomcat/util/compat/JreCompat.java
@@ -26,6 +26,8 @@ import java.util.concurrent.CompletionException;
import javax.security.auth.Subject;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -34,13 +36,17 @@ import org.apache.tomcat.util.res.StringManager;
*/
public class JreCompat {
+ private static final Log log = LogFactory.getLog(JreCompat.class);
+ private static final StringManager sm =
StringManager.getManager(JreCompat.class);
+
private static final JreCompat instance;
private static final boolean graalAvailable;
private static final boolean jre16Available;
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;
static {
boolean result = false;
@@ -87,6 +93,21 @@ public class JreCompat {
jre19Available = false;
jre16Available = false;
}
+
+ 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;
}
@@ -261,4 +282,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.FALSE);
+ } 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 ed61f81ee9..738e2ae4ac 100644
--- a/java/org/apache/tomcat/util/compat/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/compat/LocalStrings.properties
@@ -31,3 +31,6 @@ jre22Compat.unexpected=Failed to create references to Java 22
classes and method
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 3423b7a64c..a40d83ec54 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -145,6 +145,15 @@
<code>CrawlerSessionManagerValve</code>. Submitted by Brian Matzon.
(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: [email protected]
For additional commands, e-mail: [email protected]