From 88d152f2d423e79c8aa289608ec2b0216a3353dd Mon Sep 17 00:00:00 2001 From: pmeloni Date: Wed, 5 Aug 2026 19:33:39 +0200 Subject: [PATCH] - Resolves: RHEL-192806 HTTP/2 request headers not validated (CVE-2026-41293) - Resolves: RHEL-219561 Security constraint bypass via improper URL encoding in rewrite valve (CVE-2026-59083) - Resolves: RHEL-219581 Insufficient documentation for EncryptInterceptor may lead to insecure configurations (CVE-2026-59084) - Resolves: RHEL-238206 tomcat: Information disclosure due to HTTP Authentication Header exposure during WebSocket authentication (CVE-2026-42498) - Resolves: RHEL-238213 tomcat: Improper Handling of Case Sensitivity in LockOutRealm (CVE-2026-43513) - Resolves: RHEL-238260 tomcat: Improper Authorization allows security bypass (CVE-2026-43515) - Resolves: RHEL-238294 tomcat: Authentication bypass via digest authentication (CVE-2026-43512) - Resolves: RHEL-239018 tomcat: Apache Tomcat: Authentication bypass via missing critical step in JNDIRealm GSSAPI configuration (CVE-2026-55957) --- .gitignore | 1 + JmxRemoteLifecycleListener.patch | 40 --- remove-bnd-annotation.patch | 69 ++--- rhbz-1857043.patch | 71 +++-- rhel-168577.patch | 494 ------------------------------- sources | 2 +- tomcat.spec | 46 ++- 7 files changed, 104 insertions(+), 619 deletions(-) delete mode 100644 JmxRemoteLifecycleListener.patch delete mode 100644 rhel-168577.patch diff --git a/.gitignore b/.gitignore index a10b81a..fba0536 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ /tomcat-9.0.87.redhat-00011-src.zip /tomcat-9.0.87.redhat-00012-src.zip /tomcat-9.0.87.redhat-00013-src.zip +/apache-tomcat-9.0.120-src.tar.gz diff --git a/JmxRemoteLifecycleListener.patch b/JmxRemoteLifecycleListener.patch deleted file mode 100644 index 3145a54..0000000 --- a/JmxRemoteLifecycleListener.patch +++ /dev/null @@ -1,40 +0,0 @@ -diff --git a/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java b/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java -index f62f8d1..db19960 100644 ---- a/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java -+++ b/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java -@@ -611,34 +611,28 @@ public class JmxRemoteLifecycleListener extends SSLHostConfig implements Lifecyc - * Better to use the internal API than re-invent the wheel. - */ - @SuppressWarnings("restriction") -- private static class JmxRegistry extends sun.rmi.registry.RegistryImpl { -+ private static class JmxRegistry { - private static final long serialVersionUID = -3772054804656428217L; - private final String jmxName; - private final Remote jmxServer; - public JmxRegistry(int port, RMIClientSocketFactory csf, - RMIServerSocketFactory ssf, String jmxName, Remote jmxServer) throws RemoteException { -- super(port, csf, ssf); - this.jmxName = jmxName; - this.jmxServer = jmxServer; - } -- @Override - public Remote lookup(String name) - throws RemoteException, NotBoundException { - return (jmxName.equals(name)) ? jmxServer : null; - } -- @Override - public void bind(String name, Remote obj) - throws RemoteException, AlreadyBoundException, AccessException { - } -- @Override - public void unbind(String name) - throws RemoteException, NotBoundException, AccessException { - } -- @Override - public void rebind(String name, Remote obj) - throws RemoteException, AccessException { - } -- @Override - public String[] list() throws RemoteException { - return new String[] { jmxName }; - } diff --git a/remove-bnd-annotation.patch b/remove-bnd-annotation.patch index d16e2e0..e51ae60 100644 --- a/remove-bnd-annotation.patch +++ b/remove-bnd-annotation.patch @@ -1,74 +1,69 @@ diff --git a/build.xml b/build.xml -index 1a4b255..ea50aeb 100644 --- a/build.xml +++ b/build.xml -@@ -3296,7 +3296,7 @@ asf.ldap.username=${release.asfusername} - +@@ -3755,7 +3755,7 @@ + + > - + diff --git a/java/org/apache/el/ExpressionFactoryImpl.java b/java/org/apache/el/ExpressionFactoryImpl.java -index 3a6690a..03a2afe 100644 --- a/java/org/apache/el/ExpressionFactoryImpl.java +++ b/java/org/apache/el/ExpressionFactoryImpl.java -@@ -34,7 +34,7 @@ import org.apache.el.util.MessageFactory; - * - * @author Jacob Hookom [jacob@hookom.net] +@@ -31,7 +31,7 @@ + /** + * Implementation of {@link javax.el.ExpressionFactory}. */ --@aQute.bnd.annotation.spi.ServiceProvider(value=ExpressionFactory.class) -+//@aQute.bnd.annotation.spi.ServiceProvider(value=ExpressionFactory.class) +-@aQute.bnd.annotation.spi.ServiceProvider(value = ExpressionFactory.class) ++//@aQute.bnd.annotation.spi.ServiceProvider(value = ExpressionFactory.class) public class ExpressionFactoryImpl extends ExpressionFactory { - - static { + + /** diff --git a/java/org/apache/juli/logging/LogFactory.java b/java/org/apache/juli/logging/LogFactory.java -index bfc4238..acf989a 100644 --- a/java/org/apache/juli/logging/LogFactory.java +++ b/java/org/apache/juli/logging/LogFactory.java -@@ -21,7 +21,7 @@ import java.nio.file.FileSystems; +@@ -21,7 +21,7 @@ import java.util.ServiceLoader; import java.util.logging.LogManager; - + -import aQute.bnd.annotation.spi.ServiceConsumer; +//import aQute.bnd.annotation.spi.ServiceConsumer; - + /** - * This is a modified LogFactory that uses a simple {@link ServiceLoader} based -@@ -63,7 +63,7 @@ import aQute.bnd.annotation.spi.ServiceConsumer; - * @author Costin Manolache - * @author Richard A. Sitze + * This is a modified LogFactory that uses a simple {@link ServiceLoader} based discovery mechanism with a default of +@@ -50,7 +50,7 @@ + * IMPLEMENTATION NOTE - This implementation is heavily based on the SAXParserFactory and + * DocumentBuilderFactory implementations (corresponding to the JAXP pluggability APIs) found in Apache Xerces. */ --@ServiceConsumer(value=Log.class) -+//@ServiceConsumer(value=Log.class) +-@ServiceConsumer(value = Log.class) ++//@ServiceConsumer(value = Log.class) public class LogFactory { - + private static final LogFactory singleton = new LogFactory(); diff --git a/java/org/apache/tomcat/websocket/WsContainerProvider.java b/java/org/apache/tomcat/websocket/WsContainerProvider.java -index 4b0577c..e383290 100644 --- a/java/org/apache/tomcat/websocket/WsContainerProvider.java +++ b/java/org/apache/tomcat/websocket/WsContainerProvider.java -@@ -19,7 +19,7 @@ package org.apache.tomcat.websocket; - import javax.websocket.ContainerProvider; - import javax.websocket.WebSocketContainer; - +@@ -22,7 +22,7 @@ + /** + * WebSocket container provider for Tomcat. + */ -@aQute.bnd.annotation.spi.ServiceProvider(value = ContainerProvider.class) +//@aQute.bnd.annotation.spi.ServiceProvider(value = ContainerProvider.class) public class WsContainerProvider extends ContainerProvider { - - @Override + + /** diff --git a/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java b/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java -index 00f492e..fe5c34d 100644 --- a/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java +++ b/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java -@@ -26,7 +26,7 @@ import javax.websocket.HandshakeResponse; - import javax.websocket.server.HandshakeRequest; - import javax.websocket.server.ServerEndpointConfig; - +@@ -30,7 +30,7 @@ + * Default implementation of {@link ServerEndpointConfig.Configurator} for WebSocket server endpoints. + */ + @SuppressWarnings("javadoc") // Eclipse bug - the Javadoc is fine -@aQute.bnd.annotation.spi.ServiceProvider(value = ServerEndpointConfig.Configurator.class) +//@aQute.bnd.annotation.spi.ServiceProvider(value = ServerEndpointConfig.Configurator.class) public class DefaultServerEndpointConfigurator extends ServerEndpointConfig.Configurator { - - @Override + + /** diff --git a/rhbz-1857043.patch b/rhbz-1857043.patch index dd74c3b..53cf897 100644 --- a/rhbz-1857043.patch +++ b/rhbz-1857043.patch @@ -1,22 +1,21 @@ -diff -up ./build.xml.orig ./build.xml ---- build.xml.orig 2021-07-07 10:53:55.493742841 +0800 -+++ build.xml 2021-07-07 11:09:43.107968515 +0800 -@@ -1020,7 +1020,7 @@ +--- build.xml.orig 2026-02-11 15:17:18.947314996 -0500 ++++ build.xml 2026-02-11 15:17:23.675329041 -0500 +@@ -1116,7 +1116,7 @@ filesDir="${tomcat.classes}" filesId="files.annotations-api" manifest="${tomcat.manifests}/annotations-api.jar.manifest" - addOSGi="true" /> + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + + ++ addOSGi="false" /> + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + + addOSGi="false" /> - + clusterValveClass; -+ static { -+ Class clazz = null; -+ try { -+ clazz = Class.forName("org.apache.catalina.ha.ClusterValve"); -+ } catch (ClassNotFoundException e) { -+ // Expected when clustering JARs are not present -+ } -+ clusterValveClass = clazz; -+ } -+ - /** - * Store the specified Engine properties. - * -@@ -72,7 +82,7 @@ public class StandardEngineSF extends StoreFactoryBase { - if(valves != null && valves.length > 0 ) { - List engineValves = new ArrayList<>() ; - for (Valve valve : valves) { -- if (!(valve instanceof ClusterValve)) { -+ if (clusterValveClass == null || !clusterValveClass.isInstance(valve)) { - engineValves.add(valve); - } - } -diff --git a/java/org/apache/catalina/storeconfig/StandardHostSF.java b/java/org/apache/catalina/storeconfig/StandardHostSF.java -index 54d2aeed42..5c511a5256 100644 ---- a/java/org/apache/catalina/storeconfig/StandardHostSF.java -+++ b/java/org/apache/catalina/storeconfig/StandardHostSF.java -@@ -26,13 +26,23 @@ import org.apache.catalina.LifecycleListener; - import org.apache.catalina.Realm; - import org.apache.catalina.Valve; - import org.apache.catalina.core.StandardHost; --import org.apache.catalina.ha.ClusterValve; - - /** - * Store server.xml Element Host - */ - public class StandardHostSF extends StoreFactoryBase { - -+ private static final Class clusterValveClass; -+ static { -+ Class clazz = null; -+ try { -+ clazz = Class.forName("org.apache.catalina.ha.ClusterValve"); -+ } catch (ClassNotFoundException e) { -+ // Expected when clustering JARs are not present -+ } -+ clusterValveClass = clazz; -+ } -+ - /** - * Store the specified Host properties and children - * (Listener,Alias,Realm,Valve,Cluster, Context) -@@ -78,7 +88,7 @@ public class StandardHostSF extends StoreFactoryBase { - if(valves != null && valves.length > 0 ) { - List hostValves = new ArrayList<>() ; - for (Valve valve : valves) { -- if (!(valve instanceof ClusterValve)) { -+ if (clusterValveClass == null || !clusterValveClass.isInstance(valve)) { - hostValves.add(valve); - } - } -diff --git a/java/org/apache/catalina/storeconfig/StoreRegistry.java b/java/org/apache/catalina/storeconfig/StoreRegistry.java -index 4865171d14..1c1b21112e 100644 ---- a/java/org/apache/catalina/storeconfig/StoreRegistry.java -+++ b/java/org/apache/catalina/storeconfig/StoreRegistry.java -@@ -16,7 +16,9 @@ - */ - package org.apache.catalina.storeconfig; - -+import java.util.ArrayList; - import java.util.HashMap; -+import java.util.List; - import java.util.Map; - - import javax.naming.directory.DirContext; -@@ -28,17 +30,6 @@ import org.apache.catalina.Realm; - import org.apache.catalina.Valve; - import org.apache.catalina.WebResourceRoot; - import org.apache.catalina.WebResourceSet; --import org.apache.catalina.ha.CatalinaCluster; --import org.apache.catalina.ha.ClusterDeployer; --import org.apache.catalina.ha.ClusterListener; --import org.apache.catalina.tribes.Channel; --import org.apache.catalina.tribes.ChannelInterceptor; --import org.apache.catalina.tribes.ChannelReceiver; --import org.apache.catalina.tribes.ChannelSender; --import org.apache.catalina.tribes.Member; --import org.apache.catalina.tribes.MembershipService; --import org.apache.catalina.tribes.MessageListener; --import org.apache.catalina.tribes.transport.DataSender; - import org.apache.coyote.UpgradeProtocol; - import org.apache.juli.logging.Log; - import org.apache.juli.logging.LogFactory; -@@ -49,10 +40,16 @@ import org.apache.tomcat.util.res.StringManager; - * Central StoreRegistry for all server.xml elements - */ - public class StoreRegistry { -- private static Log log = LogFactory.getLog(StoreRegistry.class); -- private static StringManager sm = StringManager.getManager(StoreRegistry.class); -+ /** -+ * Constructs a new StoreRegistry with default settings. -+ */ -+ public StoreRegistry() { -+ } - -- private Map descriptors = new HashMap<>(); -+ private static final Log log = LogFactory.getLog(StoreRegistry.class); -+ private static final StringManager sm = StringManager.getManager(StoreRegistry.class); -+ -+ private final Map descriptors = new HashMap<>(); - - private String encoding = "UTF-8"; - -@@ -61,49 +58,116 @@ public class StoreRegistry { - private String version; - - // Access Information -- private static Class interfaces[] = { CatalinaCluster.class, -- ChannelSender.class, ChannelReceiver.class, Channel.class, -- MembershipService.class, ClusterDeployer.class, Realm.class, -- Manager.class, DirContext.class, LifecycleListener.class, -- Valve.class, ClusterListener.class, MessageListener.class, -- DataSender.class, ChannelInterceptor.class, Member.class, -- WebResourceRoot.class, WebResourceSet.class, -- CredentialHandler.class, UpgradeProtocol.class, -- CookieProcessor.class }; -+ // Lazily initialized to gracefully handle optional features like clustering -+ private static volatile Class[] interfaces = null; -+ -+ /** -+ * Initialize the interfaces array with all available classes. -+ * Uses dynamic loading for optional classes (e.g., clustering) to avoid -+ * ClassNotFoundException when those JARs are not present. This approach -+ * is consistent with how Catalina.addClusterRuleSet() handles clustering. -+ */ -+ private static Class[] getInterfaces() { -+ if (interfaces == null) { -+ synchronized (StoreRegistry.class) { -+ if (interfaces == null) { -+ // Required interfaces - always present -+ List> list = new ArrayList<>(); -+ list.add(Realm.class); -+ list.add(Manager.class); -+ list.add(DirContext.class); -+ list.add(LifecycleListener.class); -+ list.add(Valve.class); -+ list.add(WebResourceRoot.class); -+ list.add(WebResourceSet.class); -+ list.add(CredentialHandler.class); -+ list.add(UpgradeProtocol.class); -+ list.add(CookieProcessor.class); -+ -+ // Optional clustering interfaces - load dynamically to support -+ // deployments where clustering JARs may not be present -+ tryAddClass(list, "org.apache.catalina.ha.CatalinaCluster"); -+ tryAddClass(list, "org.apache.catalina.tribes.ChannelSender"); -+ tryAddClass(list, "org.apache.catalina.tribes.ChannelReceiver"); -+ tryAddClass(list, "org.apache.catalina.tribes.Channel"); -+ tryAddClass(list, "org.apache.catalina.tribes.MembershipService"); -+ tryAddClass(list, "org.apache.catalina.ha.ClusterDeployer"); -+ tryAddClass(list, "org.apache.catalina.ha.ClusterListener"); -+ tryAddClass(list, "org.apache.catalina.tribes.MessageListener"); -+ tryAddClass(list, "org.apache.catalina.tribes.transport.DataSender"); -+ tryAddClass(list, "org.apache.catalina.tribes.ChannelInterceptor"); -+ tryAddClass(list, "org.apache.catalina.tribes.Member"); -+ -+ interfaces = list.toArray(new Class[0]); -+ -+ if (log.isDebugEnabled()) { -+ log.debug(sm.getString("registry.interfacesLoaded", Integer.valueOf(interfaces.length))); -+ } -+ } -+ } -+ } -+ return interfaces; -+ } -+ -+ /** -+ * Try to load a class by name and add it to the list if successful. -+ * Logs at TRACE level if the class is not available. -+ */ -+ private static void tryAddClass(List> list, String className) { -+ try { -+ Class clazz = Class.forName(className, false, StoreRegistry.class.getClassLoader()); -+ list.add(clazz); -+ if (log.isTraceEnabled()) { -+ log.trace(sm.getString("registry.optionalClassLoaded", className)); -+ } -+ } catch (ClassNotFoundException | NoClassDefFoundError e) { -+ if (log.isTraceEnabled()) { -+ log.trace(sm.getString("registry.optionalClassNotFound", className)); -+ } -+ } -+ } - - /** -- * @return the name -+ * Returns the name of this registry. -+ * -+ * @return the registry name - */ - public String getName() { - return name; - } - - /** -- * @param name The name to set. -+ * Sets the name of this registry. -+ * -+ * @param name the registry name - */ - public void setName(String name) { - this.name = name; - } - - /** -- * @return the version -+ * Returns the version of this registry. -+ * -+ * @return the registry version - */ - public String getVersion() { - return version; - } - - /** -- * @param version The version to set -+ * Sets the version of this registry. -+ * -+ * @param version the registry version - */ - public void setVersion(String version) { - this.version = version; - } - - /** -- * Find a description for id. Handle interface search when no direct match -- * found. -+ * Find a description for id. Handle interface search when no direct match found. - * - * @param id The class name -+ * - * @return the description - */ - public StoreDescription findDescription(String id) { -@@ -120,17 +184,17 @@ public class StoreRegistry { - } - if (aClass != null) { - desc = descriptors.get(aClass.getName()); -- for (int i = 0; desc == null && i < interfaces.length; i++) { -- if (interfaces[i].isAssignableFrom(aClass)) { -- desc = descriptors.get(interfaces[i].getName()); -+ Class[] availableInterfaces = getInterfaces(); -+ for (int i = 0; desc == null && i < availableInterfaces.length; i++) { -+ if (availableInterfaces[i].isAssignableFrom(aClass)) { -+ desc = descriptors.get(availableInterfaces[i].getName()); - } - } - } - } - if (log.isDebugEnabled()) { - if (desc != null) { -- log.trace("find descriptor " + id + "#" + desc.getTag() + "#" -- + desc.getStoreFactoryClass()); -+ log.trace("find descriptor " + id + "#" + desc.getTag() + "#" + desc.getStoreFactoryClass()); - } else { - log.debug(sm.getString("registry.noDescriptor", id)); - } -@@ -142,6 +206,7 @@ public class StoreRegistry { - * Find Description by class. - * - * @param aClass The class -+ * - * @return the description - */ - public StoreDescription findDescription(Class aClass) { -@@ -152,6 +217,7 @@ public class StoreRegistry { - * Find factory from class name. - * - * @param aClassName The class name -+ * - * @return the factory - */ - public IStoreFactory findStoreFactory(String aClassName) { -@@ -168,6 +234,7 @@ public class StoreRegistry { - * Find factory from class. - * - * @param aClass The class -+ * - * @return the factory - */ - public IStoreFactory findStoreFactory(Class aClass) { -@@ -186,8 +253,7 @@ public class StoreRegistry { - } - descriptors.put(key, desc); - if (log.isTraceEnabled()) { -- log.trace("register store descriptor " + key + "#" + desc.getTag() -- + "#" + desc.getTagClass()); -+ log.trace("register store descriptor " + key + "#" + desc.getTag() + "#" + desc.getTagClass()); - } - } - -@@ -195,11 +261,12 @@ public class StoreRegistry { - * Unregister a description. - * - * @param desc The description -+ * - * @return the description, or null if it was not registered - */ - public StoreDescription unregisterDescription(StoreDescription desc) { - String key = desc.getId(); -- if (key == null || "".equals(key)) { -+ if (key == null || key.isEmpty()) { - key = desc.getTagClass(); - } - return descriptors.remove(key); -@@ -208,7 +275,9 @@ public class StoreRegistry { - // Attributes - - /** -- * @return the encoding -+ * Returns the character encoding used when writing configuration files. -+ * -+ * @return the character encoding - */ - public String getEncoding() { - return encoding; -@@ -216,6 +285,7 @@ public class StoreRegistry { - - /** - * Set the encoding to use when writing the configuration files. -+ * - * @param string The encoding - */ - public void setEncoding(String string) { -diff --git a/test/org/apache/catalina/storeconfig/TestStoreRegistry.java b/test/org/apache/catalina/storeconfig/TestStoreRegistry.java -new file mode 100644 -index 0000000000..e6869d3642 ---- /dev/null -+++ b/test/org/apache/catalina/storeconfig/TestStoreRegistry.java -@@ -0,0 +1,104 @@ -+/* -+ * 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.storeconfig; -+ -+import java.lang.reflect.Method; -+ -+import org.junit.Assert; -+import org.junit.Test; -+ -+import org.apache.catalina.Manager; -+import org.apache.catalina.Realm; -+import org.apache.catalina.Valve; -+ -+/** -+ * Test StoreRegistry behavior, particularly dynamic loading of optional classes like clustering. -+ * -+ * Verifies StoreRegistry uses the same dynamic loading pattern. -+ */ -+public class TestStoreRegistry { -+ -+ /** -+ * Test that clustering classes are dynamically loaded like other Tomcat components. -+ * -+ * StoreRegistry should initialize successfully whether clustering is available or not. -+ * This matches the pattern used in Catalina.addClusterRuleSet(). -+ */ -+ @Test -+ public void testClusteringClassesOptional() throws Exception { -+ // Verify StoreRegistry initializes successfully with dynamic class loading -+ StoreRegistry registry = new StoreRegistry(); -+ Assert.assertNotNull("Registry should initialize with dynamic loading", registry); -+ -+ // Trigger lazy loading of interfaces array -+ Method getInterfacesMethod = StoreRegistry.class.getDeclaredMethod("getInterfaces"); -+ getInterfacesMethod.setAccessible(true); -+ -+ Class[] interfaces = (Class[]) getInterfacesMethod.invoke(null); -+ Assert.assertNotNull("Interfaces should load dynamically", interfaces); -+ -+ // Test passes if we get here without ClassNotFoundException. -+ // The actual number of interfaces loaded depends on whether clustering is available, -+ // but we should always have at least the core 10 interfaces. -+ Assert.assertTrue("Should have at least 10 core interfaces", -+ interfaces.length >= 10); -+ -+ // Verify required core interfaces are always present -+ boolean hasRealm = false; -+ boolean hasManager = false; -+ boolean hasValve = false; -+ -+ for (Class iface : interfaces) { -+ if (iface.equals(Realm.class)) { -+ hasRealm = true; -+ } -+ if (iface.equals(Manager.class)) { -+ hasManager = true; -+ } -+ if (iface.equals(Valve.class)) { -+ hasValve = true; -+ } -+ } -+ -+ Assert.assertTrue("Should contain Realm interface", hasRealm); -+ Assert.assertTrue("Should contain Manager interface", hasManager); -+ Assert.assertTrue("Should contain Valve interface", hasValve); -+ } -+ -+ /** -+ * Test that findDescription works with interface inheritance and -+ * dynamically loaded interfaces. -+ */ -+ @Test -+ public void testFindDescriptionWithDynamicInterfaces() throws Exception { -+ StoreRegistry registry = new StoreRegistry(); -+ -+ // Register a description for the Valve interface -+ StoreDescription valveDesc = new StoreDescription(); -+ valveDesc.setId(Valve.class.getName()); -+ valveDesc.setTag("Valve"); -+ valveDesc.setTagClass(Valve.class.getName()); -+ registry.registerDescription(valveDesc); -+ -+ // AccessLogValve implements Valve interface - should find via dynamic interface matching -+ String accessLogValveClass = "org.apache.catalina.valves.AccessLogValve"; -+ StoreDescription foundDesc = registry.findDescription(accessLogValveClass); -+ -+ Assert.assertNotNull("Should find description via interface matching", foundDesc); -+ Assert.assertEquals("Should match Valve descriptor", "Valve", foundDesc.getTag()); -+ } -+} -diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml -index 012b607f18..17f639d669 100644 ---- a/webapps/docs/changelog.xml -+++ b/webapps/docs/changelog.xml -@@ -138,6 +138,13 @@ - The rewrite valve should not do a rewrite if the output is identical - to the input. (remm) - -+ -+ Update StoreRegistry to dynamically load optional clustering -+ classes rather than statically referencing them. This matches the pattern -+ used in Catalina.addClusterRuleSet() and prevents -+ NoClassDefFoundError when StoreConfigLifecycleListener -+ is configured but clustering classes are not available. (csutherl) -+ - - Add a new valveSkip (or VS) rule flag to the - rewrite valve to allow skipping over the next valve in the Catalina diff --git a/sources b/sources index d789131..0f8a42a 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (tomcat-9.0.87.redhat-00013-src.zip) = 1049d86d4bbdfd251a3f0cc72840cb6535a5637a76becaec8cb9c6532430dabaefd007af285fa1ac8d6a2a70f1d0378f6a1e908f7e7c5aff7c2bbedcd521cc9d +SHA512 (apache-tomcat-9.0.120-src.tar.gz) = db8230bb08b1c33ea710b71fbba1de3d0c84c6416a784921333c03ef6526c1b022b0b2ad18686b734babba538500da795e46bf5bbbd789d8a7a251ffb3665a59 diff --git a/tomcat.spec b/tomcat.spec index 9526e94..bb3961a 100644 --- a/tomcat.spec +++ b/tomcat.spec @@ -31,8 +31,8 @@ %global jspspec 2.3 %global major_version 9 %global minor_version 0 -%global micro_version 87 -%global packdname %{name}-%{major_version}.%{minor_version}.%{micro_version}.redhat-00013-src +%global micro_version 120 +%global packdname apache-%{name}-%{major_version}.%{minor_version}.%{micro_version}-src %global servletspec 4.0 %global elspec 3.0 %global tcuid 53 @@ -56,12 +56,12 @@ Name: tomcat Epoch: 1 Version: %{major_version}.%{minor_version}.%{micro_version} -Release: 2%{?dist} +Release: 1%{?dist} Summary: Apache Servlet/JSP Engine, RI for Servlet %{servletspec}/JSP %{jspspec} API License: ASL 2.0 URL: http://tomcat.apache.org/ -Source0: %{packdname}.zip +Source0: %{packdname}.tar.gz Source1: %{name}-%{major_version}.%{minor_version}.conf Source3: %{name}-%{major_version}.%{minor_version}.sysconfig Source4: %{name}-%{major_version}.%{minor_version}.wrapper @@ -82,19 +82,20 @@ Patch3: %{name}-%{major_version}.%{minor_version}-catalina-policy.patch Patch4: rhbz-1857043.patch # remove bnd dependency which version is too low on rhel8 Patch6: remove-bnd-annotation.patch -Patch7: JmxRemoteLifecycleListener.patch -Patch8: rhel-168577.patch + + BuildArch: noarch BuildRequires: ant -BuildRequires: ecj +BuildRequires: ecj >= 1:4.10 BuildRequires: findutils +BuildRequires: java-17-openjdk-devel BuildRequires: javapackages-local BuildRequires: aqute-bnd BuildRequires: aqute-bndlib BuildRequires: systemd -Requires: (java-headless >= 1:1.8 or java-1.8.0-headless or java-11-headless or java-17-headless or java >= 1:1.8) +Requires: (java-headless >= 1:1.8 or java-1.8.0-headless or java-11-headless or java-17-headless or java-21-headless or java >= 1:1.8) Requires: javapackages-tools Requires: %{name}-lib = %{epoch}:%{version}-%{release} %if 0%{?fedora} || 0%{?rhel} > 7 @@ -185,7 +186,7 @@ Requires: %{name} = %{epoch}:%{version}-%{release} The ROOT web application for Apache Tomcat. %prep -%setup -q -n apache-%{packdname} +%setup -q -n %{packdname} # remove pre-built binaries and windows files find . -type f \( -name "*.bat" -o -name "*.class" -o -name Thumbs.db -o -name "*.gz" -o \ -name "*.jar" -o -name "*.war" -o -name "*.zip" \) -delete @@ -196,8 +197,6 @@ find . -type f \( -name "*.bat" -o -name "*.class" -o -name Thumbs.db -o -name " %patch -P3 -p0 %patch -P4 -p0 %patch -P6 -p1 -%patch -P7 -p1 -%patch -P8 -p1 # Remove webservices naming resources as it's generally unused %{__rm} -rf java/org/apache/naming/factory/webservices @@ -219,8 +218,12 @@ export OPT_JAR_LIST="xalan-j2-serializer" # so just create a dummy file for later removal touch HACK +# Adding JAVA_HOME to always compile with java-17 instead of autodetecting +export JAVA_HOME=%{_jvmdir}/java-17-openjdk +export PATH=$JAVA_HOME/bin:$PATH + # who needs a build.properties file anyway -%{ant} -Dbase.path="." \ +ant -Dbase.path="." \ -Dbuild.compiler="modern" \ -Dcommons-daemon.jar="HACK" \ -Dcommons-daemon.native.src.tgz="HACK" \ @@ -238,6 +241,9 @@ touch HACK # remove some jars that we'll replace with symlinks later %{__rm} output/build/lib/ecj.jar +# Cleanup commons-daemon.jar that somehow appeared since last build, but is unnecessary +%{__rm} -rf output/build/bin/commons-daemon.jar + # Remove the example webapps per Apache Tomcat Security Considerations # see https://tomcat.apache.org/tomcat-9.0-doc/security-howto.html %{__rm} -rf output/build/webapps/examples @@ -403,7 +409,6 @@ popd %mvn_artifact res/maven/tomcat-websocket-api.pom ${RPM_BUILD_ROOT}%{libdir}/websocket-api.jar %mvn_artifact res/maven/tomcat-websocket.pom ${RPM_BUILD_ROOT}%{libdir}/tomcat-websocket.jar %mvn_artifact res/maven/tomcat.pom - %mvn_install %pre @@ -439,7 +444,7 @@ exit 0 %systemd_preun %{name}.service %postun -%systemd_postun_with_restart %{name}.service +%systemd_postun_with_restart %{name}.service %postun jsp-%{jspspec}-api if [ "$1" = "0" ]; then @@ -459,7 +464,7 @@ if [ "$1" = "0" ]; then %{_javadir}/%{name}-el-%{elspec}-api.jar fi -%files +%files %defattr(0664,root,tomcat,0755) %doc {LICENSE,NOTICE,RELEASE*} %attr(0755,root,root) %{_bindir}/%{name}-digest @@ -558,7 +563,18 @@ fi %{appdir}/ROOT + %changelog +* Fri Jul 31 2026 Pietro Meloni - 1:9.0.120-1 +- Resolves: RHEL-192806 HTTP/2 request headers not validated (CVE-2026-41293) +- Resolves: RHEL-219561 Security constraint bypass via improper URL encoding in rewrite valve (CVE-2026-59083) +- Resolves: RHEL-219581 Insufficient documentation for EncryptInterceptor may lead to insecure configurations (CVE-2026-59084) +- Resolves: RHEL-238206 tomcat: Information disclosure due to HTTP Authentication Header exposure during WebSocket authentication (CVE-2026-42498) +- Resolves: RHEL-238213 tomcat: Improper Handling of Case Sensitivity in LockOutRealm (CVE-2026-43513) +- Resolves: RHEL-238260 tomcat: Improper Authorization allows security bypass (CVE-2026-43515) +- Resolves: RHEL-238294 tomcat: Authentication bypass via digest authentication (CVE-2026-43512) +- Resolves: RHEL-239018 tomcat: Apache Tomcat: Authentication bypass via missing critical step in JNDIRealm GSSAPI configuration (CVE-2026-55957) + * Thu Jun 4 2026 Pietro Meloni - 1:9.0.87-2 - Resolves: RHEL-183993 Remove tomcat clustering JAR from RPM builds