diff --git a/CVE-2021-25122-pre.patch b/CVE-2021-25122-pre.patch new file mode 100644 index 0000000000000000000000000000000000000000..0d4824f81aa87553e3222b41f4a0d3e3305134da --- /dev/null +++ b/CVE-2021-25122-pre.patch @@ -0,0 +1,53 @@ +From 995115a24bb868d1204a796f5b3170f62618a6bb Mon Sep 17 00:00:00 2001 +From: wang_yue111 <648774160@qq.com> +Date: Thu, 11 Mar 2021 18:35:41 +0800 +Subject: [PATCH] SocketWrapper.upgraded is no longer used + +It used to be used to determine if the processor should be recycled. It +has been replaced by a flag on the processor. + +--- + java/org/apache/coyote/AbstractProtocol.java | 2 -- + .../apache/tomcat/util/net/SocketWrapperBase.java | 12 ++++++++++++ + 2 files changed, 12 insertions(+), 2 deletions(-) + +diff --git a/java/org/apache/coyote/AbstractProtocol.java b/java/org/apache/coyote/AbstractProtocol.java +index 09ed0a9..9f83906 100644 +--- a/java/org/apache/coyote/AbstractProtocol.java ++++ b/java/org/apache/coyote/AbstractProtocol.java +@@ -799,8 +799,6 @@ public abstract class AbstractProtocol implements ProtocolHandler, + processor, wrapper)); + } + wrapper.unRead(leftOverInput); +- // Mark the connection as upgraded +- wrapper.setUpgraded(true); + // Associate with the processor with the connection + connections.put(socket, processor); + // Initialise the upgrade handler (which may trigger +diff --git a/java/org/apache/tomcat/util/net/SocketWrapperBase.java b/java/org/apache/tomcat/util/net/SocketWrapperBase.java +index 2479d6d..f8a79db 100644 +--- a/java/org/apache/tomcat/util/net/SocketWrapperBase.java ++++ b/java/org/apache/tomcat/util/net/SocketWrapperBase.java +@@ -138,7 +138,19 @@ public abstract class SocketWrapperBase { + } + } + ++ /** ++ * @return {@code true} if the connection has been upgraded. ++ * ++ * @deprecated Unused. Will be removed in Tomcat 10. ++ */ ++ @Deprecated + public boolean isUpgraded() { return upgraded; } ++ /** ++ * @param upgraded {@code true} if the connection has been upgraded. ++ * ++ * @deprecated Unused. Will be removed in Tomcat 10. ++ */ ++ @Deprecated + public void setUpgraded(boolean upgraded) { this.upgraded = upgraded; } + public boolean isSecure() { return secure; } + public void setSecure(boolean secure) { this.secure = secure; } +-- +2.23.0 + diff --git a/CVE-2021-25122.patch b/CVE-2021-25122.patch new file mode 100644 index 0000000000000000000000000000000000000000..2fe7e548b4a5bc716ee44b425e7d777f50d09683 --- /dev/null +++ b/CVE-2021-25122.patch @@ -0,0 +1,45 @@ +From 7b8b7134813a356595eacf01fd9e8ea6b3752c8b Mon Sep 17 00:00:00 2001 +From: wang_yue111 <648774160@qq.com> +Date: Thu, 11 Mar 2021 18:42:09 +0800 +Subject: [PATCH] Simplify the code and fix an edge case for BZ 64830 + +https://bz.apache.org/bugzilla/show_bug.cgi?id=64830 +--- + java/org/apache/coyote/AbstractProtocol.java | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/java/org/apache/coyote/AbstractProtocol.java b/java/org/apache/coyote/AbstractProtocol.java +index 9f83906..b5c4d5b 100644 +--- a/java/org/apache/coyote/AbstractProtocol.java ++++ b/java/org/apache/coyote/AbstractProtocol.java +@@ -766,8 +766,10 @@ public abstract class AbstractProtocol implements ProtocolHandler, + if (state == SocketState.UPGRADING) { + // Get the HTTP upgrade handler + UpgradeToken upgradeToken = processor.getUpgradeToken(); +- // Retrieve leftover input ++ // Restore leftover input to the wrapper so the upgrade ++ // processor can process it. + ByteBuffer leftOverInput = processor.getLeftoverInput(); ++ wrapper.unRead(leftOverInput); + if (upgradeToken == null) { + // Assume direct HTTP/2 connection + UpgradeProtocol upgradeProtocol = getProtocol().getUpgradeProtocol("h2c"); +@@ -776,7 +778,6 @@ public abstract class AbstractProtocol implements ProtocolHandler, + release(processor); + // Create the upgrade processor + processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter()); +- wrapper.unRead(leftOverInput); + // Associate with the processor with the connection + connections.put(socket, processor); + } else { +@@ -798,7 +799,6 @@ public abstract class AbstractProtocol implements ProtocolHandler, + getLog().debug(sm.getString("abstractConnectionHandler.upgradeCreate", + processor, wrapper)); + } +- wrapper.unRead(leftOverInput); + // Associate with the processor with the connection + connections.put(socket, processor); + // Initialise the upgrade handler (which may trigger +-- +2.23.0 + diff --git a/CVE-2021-25329-pre1.patch b/CVE-2021-25329-pre1.patch new file mode 100644 index 0000000000000000000000000000000000000000..6345ac7815036b3ef29b78e46236a71aaae48a58 --- /dev/null +++ b/CVE-2021-25329-pre1.patch @@ -0,0 +1,24 @@ +From d63695a656f04e39bd1ad4dee0f2339b0e3b898f Mon Sep 17 00:00:00 2001 +From: Mark Thomas +Date: Wed, 3 Oct 2018 11:16:07 +0000 +Subject: [PATCH] Ensure that a canonical path is always used for the docBase + of a Context to ensure consistent behaviour. + +git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1842702 13f79535-47bb-0310-9956-ffa450edef68 +--- + java/org/apache/catalina/startup/ContextConfig.java | 2 +- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java +index be74f29168..f3935038fc 100644 +--- a/java/org/apache/catalina/startup/ContextConfig.java ++++ b/java/org/apache/catalina/startup/ContextConfig.java +@@ -579,7 +579,7 @@ protected void fixDocBase() throws IOException { + + File file = new File(docBase); + if (!file.isAbsolute()) { +- docBase = (new File(appBase, docBase)).getPath(); ++ docBase = (new File(appBase, docBase)).getCanonicalPath(); + } else { + docBase = file.getCanonicalPath(); + } diff --git a/CVE-2021-25329-pre2.patch b/CVE-2021-25329-pre2.patch new file mode 100644 index 0000000000000000000000000000000000000000..2014fc9b98d196ed4ed065603cd7827588d0b180 --- /dev/null +++ b/CVE-2021-25329-pre2.patch @@ -0,0 +1,28 @@ +From ad60947e42e666dc9c9d77315787ea9bb567e3fd Mon Sep 17 00:00:00 2001 +From: Mark Thomas +Date: Wed, 13 Mar 2019 11:18:45 +0000 +Subject: [PATCH] Always process the docBase using absolute path during + deployment + +Use absolute path to determine the Context name, deployment type, +whether the docBase is located within the appBase etc. +--- + java/org/apache/catalina/startup/ContextConfig.java | 4 ++-- + 2 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java +index ebd3d8221f..0c67af3bf4 100644 +--- a/java/org/apache/catalina/startup/ContextConfig.java ++++ b/java/org/apache/catalina/startup/ContextConfig.java +@@ -582,9 +582,9 @@ protected void fixDocBase() throws IOException { + + File file = new File(docBase); + if (!file.isAbsolute()) { +- docBase = (new File(appBase, docBase)).getCanonicalPath(); ++ docBase = (new File(appBase, docBase)).getAbsolutePath(); + } else { +- docBase = file.getCanonicalPath(); ++ docBase = file.getAbsolutePath(); + } + file = new File(docBase); + String origDocBase = docBase; diff --git a/CVE-2021-25329-pre3.patch b/CVE-2021-25329-pre3.patch new file mode 100644 index 0000000000000000000000000000000000000000..eb537081623c30cc0271c4376dce8a82aa0a486f --- /dev/null +++ b/CVE-2021-25329-pre3.patch @@ -0,0 +1,144 @@ +From 2c5066316f6b138c4130a87cae4db05d75afe150 Mon Sep 17 00:00:00 2001 +From: wang_yue111 <648774160@qq.com> +Date: Fri, 12 Mar 2021 09:44:04 +0800 +Subject: [PATCH] 2 + +--- + .../catalina/startup/ContextConfig.java | 75 ++++++++++--------- + 1 file changed, 41 insertions(+), 34 deletions(-) + +diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java +index 89eb8d3..a4210f8 100644 +--- a/java/org/apache/catalina/startup/ContextConfig.java ++++ b/java/org/apache/catalina/startup/ContextConfig.java +@@ -566,25 +566,29 @@ public class ContextConfig implements LifecycleListener { + Host host = (Host) context.getParent(); + File appBase = host.getAppBaseFile(); + +- String docBase = context.getDocBase(); +- if (docBase == null) { ++ // This could be blank, relative, absolute or canonical ++ String docBaseConfigured = context.getDocBase(); ++ // If there is no explicit docBase, derive it from the path and version ++ if (docBaseConfigured == null) { + // Trying to guess the docBase according to the path + String path = context.getPath(); + if (path == null) { + return; + } + ContextName cn = new ContextName(path, context.getWebappVersion()); +- docBase = cn.getBaseName(); ++ docBaseConfigured = cn.getBaseName(); + } + +- File file = new File(docBase); +- if (!file.isAbsolute()) { +- docBase = (new File(appBase, docBase)).getAbsolutePath(); +- } else { +- docBase = file.getAbsolutePath(); +- } +- file = new File(docBase); +- String origDocBase = docBase; ++ // Obtain the absolute docBase in String and File form ++ String docBaseAbsolute; ++ File docBaseConfiguredFile = new File(docBaseConfigured); ++ if (!docBaseConfiguredFile.isAbsolute()) { ++ docBaseAbsolute = (new File(appBase, docBaseConfigured)).getAbsolutePath(); ++ } else { ++ docBaseAbsolute = docBaseConfiguredFile.getAbsolutePath(); ++ } ++ File docBaseAbsoluteFile = new File(docBaseAbsolute); ++ String originalDocBase = docBaseAbsolute; + + ContextName cn = new ContextName(context.getPath(), context.getWebappVersion()); + String pathName = cn.getBaseName(); +@@ -597,28 +601,29 @@ public class ContextConfig implements LifecycleListener { + } + } + +- boolean docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar); +- +- if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory()) { +- URL war = UriUtil.buildJarUrl(new File(docBase)); ++ // At this point we need to determine if we have a WAR file in the ++ // appBase that needs to be expanded. Therefore we consider the absolute ++ // docBase NOT the canonical docBase. This is because some users symlink ++ // WAR files into the appBase and we want this to work correctly. ++ boolean docBaseAbsoluteInAppBase = docBaseAbsolute.startsWith(appBase.getPath() + File.separatorChar); ++ if (docBaseAbsolute.toLowerCase(Locale.ENGLISH).endsWith(".war") && !docBaseAbsoluteFile.isDirectory()) { ++ URL war = UriUtil.buildJarUrl(docBaseAbsoluteFile); + if (unpackWARs) { +- docBase = ExpandWar.expand(host, war, pathName); +- file = new File(docBase); +- docBase = file.getCanonicalPath(); ++ docBaseAbsolute = ExpandWar.expand(host, war, pathName); ++ docBaseAbsoluteFile = new File(docBaseAbsolute); + if (context instanceof StandardContext) { +- ((StandardContext) context).setOriginalDocBase(origDocBase); ++ ((StandardContext) context).setOriginalDocBase(originalDocBase); + } + } else { + ExpandWar.validate(host, war, pathName); + } + } else { +- File docDir = new File(docBase); +- File warFile = new File(docBase + ".war"); ++ File docBaseAbsoluteFileWar = new File(docBaseAbsolute + ".war"); + URL war = null; +- if (warFile.exists() && docBaseInAppBase) { +- war = UriUtil.buildJarUrl(warFile); ++ if (docBaseAbsoluteFileWar.exists() && docBaseAbsoluteInAppBase) { ++ war = UriUtil.buildJarUrl(docBaseAbsoluteFileWar); + } +- if (docDir.exists()) { ++ if (docBaseAbsoluteFile.exists()) { + if (war != null && unpackWARs) { + // Check if WAR needs to be re-expanded (e.g. if it has + // changed). Note: HostConfig.deployWar() takes care of +@@ -629,31 +634,33 @@ public class ContextConfig implements LifecycleListener { + } else { + if (war != null) { + if (unpackWARs) { +- docBase = ExpandWar.expand(host, war, pathName); +- file = new File(docBase); +- docBase = file.getCanonicalPath(); ++ docBaseAbsolute = ExpandWar.expand(host, war, pathName); ++ docBaseAbsoluteFile = new File(docBaseAbsolute); + } else { +- docBase = warFile.getCanonicalPath(); ++ docBaseAbsolute = docBaseAbsoluteFileWar.getAbsolutePath(); ++ docBaseAbsoluteFile = docBaseAbsoluteFileWar; + ExpandWar.validate(host, war, pathName); + } + } + if (context instanceof StandardContext) { +- ((StandardContext) context).setOriginalDocBase(origDocBase); ++ ((StandardContext) context).setOriginalDocBase(originalDocBase); + } + } + } + +- // Re-calculate now docBase is a canonical path +- docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar); ++ String docBaseCanonical = docBaseAbsoluteFile.getCanonicalPath(); + +- if (docBaseInAppBase) { +- docBase = docBase.substring(appBase.getPath().length()); ++ // Re-calculate now docBase is a canonical path ++ boolean docBaseCanonicalInAppBase = docBaseCanonical.startsWith(appBase.getPath() + File.separatorChar); ++ String docBase; ++ if (docBaseCanonicalInAppBase) { ++ docBase = docBaseCanonical.substring(appBase.getPath().length()); + docBase = docBase.replace(File.separatorChar, '/'); + if (docBase.startsWith("/")) { + docBase = docBase.substring(1); + } + } else { +- docBase = docBase.replace(File.separatorChar, '/'); ++ docBase = docBaseCanonical.replace(File.separatorChar, '/'); + } + + context.setDocBase(docBase); +-- +2.23.0 + diff --git a/CVE-2021-25329.patch b/CVE-2021-25329.patch new file mode 100644 index 0000000000000000000000000000000000000000..a3127fd2eb50f2f24fa30b0af99908a07adb30e2 --- /dev/null +++ b/CVE-2021-25329.patch @@ -0,0 +1,157 @@ +From 48590d3fc54100031ba9d8c4f6362afb15c6697f Mon Sep 17 00:00:00 2001 +From: wang_yue111 <648774160@qq.com> +Date: Fri, 12 Mar 2021 09:53:00 +0800 +Subject: [PATCH] Use java.nio.file.Path for consistent sub-directory +checking + +--- + .../catalina/servlets/DefaultServlet.java | 2 +- + .../apache/catalina/session/FileStore.java | 2 +- + .../catalina/startup/ContextConfig.java | 3 ++- + .../apache/catalina/startup/ExpandWar.java | 21 +++++++------------ + .../apache/catalina/startup/HostConfig.java | 3 +-- + webapps/docs/changelog.xml | 4 ++++ + 6 files changed, 16 insertions(+), 19 deletions(-) + +diff --git a/java/org/apache/catalina/servlets/DefaultServlet.java b/java/org/apache/catalina/servlets/DefaultServlet.java +index 8b453bf..5ad60ec 100644 +--- a/java/org/apache/catalina/servlets/DefaultServlet.java ++++ b/java/org/apache/catalina/servlets/DefaultServlet.java +@@ -1992,7 +1992,7 @@ public class DefaultServlet extends HttpServlet { + + // First check that the resulting path is under the provided base + try { +- if (!candidate.getCanonicalPath().startsWith(base.getCanonicalPath())) { ++ if (!candidate.getCanonicalFile().toPath().startsWith(base.getCanonicalFile().toPath())) { + return null; + } + } catch (IOException ioe) { +diff --git a/java/org/apache/catalina/session/FileStore.java b/java/org/apache/catalina/session/FileStore.java +index 0c7f728..f77b46a 100644 +--- a/java/org/apache/catalina/session/FileStore.java ++++ b/java/org/apache/catalina/session/FileStore.java +@@ -356,7 +356,7 @@ public final class FileStore extends StoreBase { + File file = new File(storageDir, filename); + + // Check the file is within the storage directory +- if (!file.getCanonicalPath().startsWith(storageDir.getCanonicalPath())) { ++ if (!file.getCanonicalFile().toPath().startsWith(storageDir.getCanonicalFile().toPath())) { + log.warn(sm.getString("fileStore.invalid", file.getPath(), id)); + return null; + } +diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java +index a4210f8..5202253 100644 +--- a/java/org/apache/catalina/startup/ContextConfig.java ++++ b/java/org/apache/catalina/startup/ContextConfig.java +@@ -651,7 +651,8 @@ public class ContextConfig implements LifecycleListener { + String docBaseCanonical = docBaseAbsoluteFile.getCanonicalPath(); + + // Re-calculate now docBase is a canonical path +- boolean docBaseCanonicalInAppBase = docBaseCanonical.startsWith(appBase.getPath() + File.separatorChar); ++ boolean docBaseCanonicalInAppBase = ++ docBaseAbsoluteFile.getCanonicalFile().toPath().startsWith(appBase.toPath()); + String docBase; + if (docBaseCanonicalInAppBase) { + docBase = docBaseCanonical.substring(appBase.getPath().length()); +diff --git a/java/org/apache/catalina/startup/ExpandWar.java b/java/org/apache/catalina/startup/ExpandWar.java +index 7fd7144..55fe1f5 100644 +--- a/java/org/apache/catalina/startup/ExpandWar.java ++++ b/java/org/apache/catalina/startup/ExpandWar.java +@@ -26,6 +26,7 @@ import java.net.JarURLConnection; + import java.net.URL; + import java.net.URLConnection; + import java.nio.channels.FileChannel; ++import java.nio.file.Path; + import java.util.Enumeration; + import java.util.jar.JarEntry; + import java.util.jar.JarFile; +@@ -116,10 +117,7 @@ public class ExpandWar { + } + + // Expand the WAR into the new document base directory +- String canonicalDocBasePrefix = docBase.getCanonicalPath(); +- if (!canonicalDocBasePrefix.endsWith(File.separator)) { +- canonicalDocBasePrefix += File.separator; +- } ++ Path canonicalDocBasePath = docBase.getCanonicalFile().toPath(); + + // Creating war tracker parent (normally META-INF) + File warTrackerParent = warTracker.getParentFile(); +@@ -134,14 +132,13 @@ public class ExpandWar { + JarEntry jarEntry = jarEntries.nextElement(); + String name = jarEntry.getName(); + File expandedFile = new File(docBase, name); +- if (!expandedFile.getCanonicalPath().startsWith( +- canonicalDocBasePrefix)) { ++ if (!expandedFile.getCanonicalFile().toPath().startsWith(canonicalDocBasePath)) { + // Trying to expand outside the docBase + // Throw an exception to stop the deployment + throw new IllegalArgumentException( + sm.getString("expandWar.illegalPath",war, name, + expandedFile.getCanonicalPath(), +- canonicalDocBasePrefix)); ++ canonicalDocBasePath)); + } + int last = name.lastIndexOf('/'); + if (last >= 0) { +@@ -217,10 +214,7 @@ public class ExpandWar { + File docBase = new File(host.getAppBaseFile(), pathname); + + // Calculate the document base directory +- String canonicalDocBasePrefix = docBase.getCanonicalPath(); +- if (!canonicalDocBasePrefix.endsWith(File.separator)) { +- canonicalDocBasePrefix += File.separator; +- } ++ Path canonicalDocBasePath = docBase.getCanonicalFile().toPath(); + JarURLConnection juc = (JarURLConnection) war.openConnection(); + juc.setUseCaches(false); + try (JarFile jarFile = juc.getJarFile()) { +@@ -229,14 +223,13 @@ public class ExpandWar { + JarEntry jarEntry = jarEntries.nextElement(); + String name = jarEntry.getName(); + File expandedFile = new File(docBase, name); +- if (!expandedFile.getCanonicalPath().startsWith( +- canonicalDocBasePrefix)) { ++ if (!expandedFile.getCanonicalFile().toPath().startsWith(canonicalDocBasePath)) { + // Entry located outside the docBase + // Throw an exception to stop the deployment + throw new IllegalArgumentException( + sm.getString("expandWar.illegalPath",war, name, + expandedFile.getCanonicalPath(), +- canonicalDocBasePrefix)); ++ canonicalDocBasePath)); + } + } + } catch (IOException e) { +diff --git a/java/org/apache/catalina/startup/HostConfig.java b/java/org/apache/catalina/startup/HostConfig.java +index a4dad6f..d7bf6a2 100644 +--- a/java/org/apache/catalina/startup/HostConfig.java ++++ b/java/org/apache/catalina/startup/HostConfig.java +@@ -597,8 +597,7 @@ public class HostConfig implements LifecycleListener { + docBase = new File(host.getAppBaseFile(), context.getDocBase()); + } + // If external docBase, register .xml as redeploy first +- if (!docBase.getCanonicalPath().startsWith( +- host.getAppBaseFile().getAbsolutePath() + File.separator)) { ++ if (!docBase.getCanonicalFile().toPath().startsWith(host.getAppBaseFile().toPath())) { + isExternal = true; + deployedApp.redeployResources.put( + contextXml.getAbsolutePath(), +diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml +index 1fc4907..bc37288 100644 +--- a/webapps/docs/changelog.xml ++++ b/webapps/docs/changelog.xml +@@ -947,6 +947,10 @@ + Update the NSIS Installer used to build the Windows installer to version + 3.03. (kkolinko) + ++ ++ Use java.nio.file.Path to test for one directory being a ++ sub-directory of another in a consistent way. (markt) ++ + + + +-- +2.23.0 + diff --git a/tomcat.spec b/tomcat.spec index bd99b0c1065f30a8cddf810bd4e0b8f4f28276d5..42d3cc5db1e90414873e20c49f5f74c0505aacd0 100644 --- a/tomcat.spec +++ b/tomcat.spec @@ -13,7 +13,7 @@ Name: tomcat Epoch: 1 Version: %{major_version}.%{minor_version}.%{micro_version} -Release: 17 +Release: 18 Summary: Implementation of the Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies License: ASL 2.0 URL: http://tomcat.apache.org/ @@ -76,6 +76,12 @@ Patch6030: CVE-2020-13943-3.patch Patch6031: CVE-2020-13943-4.patch Patch6032: CVE-2020-17527.patch Patch6033: CVE-2021-24122.patch +Patch6035: CVE-2021-25122-pre.patch +Patch6036: CVE-2021-25122.patch +Patch6037: CVE-2021-25329-pre1.patch +Patch6038: CVE-2021-25329-pre2.patch +Patch6039: CVE-2021-25329-pre3.patch +Patch6040: CVE-2021-25329.patch BuildRequires: ecj >= 1:4.6.1 findutils apache-commons-collections apache-commons-daemon BuildRequires: apache-commons-dbcp apache-commons-pool tomcat-taglibs-standard ant @@ -477,6 +483,12 @@ fi %{_javadocdir}/%{name} %changelog +* Fri Mar 12 2021 wangyue - 1:9.0.10-18 +- Type:cve +- ID: CVE-2021-25122 CVE-2021-25329 +- SUG:restart +- DESC: fix CVE-2021-25122 CVE-2021-25329 + * Thu Feb 18 2021 wangxiao - 1:9.0.10-17 - Type:cve - ID: CVE-2021-24122