This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/11.0.x by this push:
     new bda730151a Automate protection for CVE-2024-56337
bda730151a is described below

commit bda730151a77a959e851104fe35e8c40ae6045d5
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  | 70 +++++++++++++++++++++-
 .../tomcat/util/compat/LocalStrings.properties     |  4 ++
 webapps/docs/changelog.xml                         |  9 +++
 8 files changed, 141 insertions(+), 1 deletion(-)

diff --git a/bin/catalina.bat b/bin/catalina.bat
index 78a7eb9d9f..88bf844c14 100755
--- a/bin/catalina.bat
+++ b/bin/catalina.bat
@@ -203,6 +203,13 @@ set "JSSE_OPTS=-Djdk.tls.ephemeralDHKeySize=2048"
 :gotJsseOpts
 set "JAVA_OPTS=%JAVA_OPTS% %JSSE_OPTS%"
 
+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 ee679ad0c6..c5a996cb65 100755
--- a/bin/catalina.sh
+++ b/bin/catalina.sh
@@ -251,6 +251,13 @@ if [ -z "$JSSE_OPTS" ] ; then
 fi
 JAVA_OPTS="$JAVA_OPTS $JSSE_OPTS"
 
+# 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 001ce82546..a948c038cc 100644
--- a/java/org/apache/tomcat/util/compat/JreCompat.java
+++ b/java/org/apache/tomcat/util/compat/JreCompat.java
@@ -23,6 +23,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;
 
 /**
@@ -31,12 +33,16 @@ 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 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;
@@ -73,6 +79,21 @@ public class JreCompat {
             jre21Available = false;
             jre19Available = 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;
     }
 
 
@@ -209,4 +230,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 621d5d3e98..fb62719b75 100644
--- a/java/org/apache/tomcat/util/compat/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/compat/LocalStrings.properties
@@ -20,3 +20,7 @@ jre18Compat.javaPre18=Method not found so assuming code is 
running on a pre-Java
 
 jre22Compat.javaPre22=Class not found so assuming code is running on a 
pre-Java 22 JVM
 jre22Compat.unexpected=Failed to create references to Java 22 classes and 
methods
+
+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 92358645a9..8ace88bf23 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: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to