From e59c5cabf4de78956a7e3d6f3fcd2d0b337e516ed854a236b4f8dd2a30acf7ac Mon Sep 17 00:00:00 2001 From: Michele Bussolotto Date: Wed, 17 Jan 2024 15:46:22 +0000 Subject: [PATCH] Accepting request 1139494 from home:mbussolotto:branches:Java:packages - change server.xml during %post instead of %posttrans - add libxslt-tools requirement - Fixed CVEs: * CVE-2023-46589: Apache Tomcat: HTTP request smuggling due to incorrect headers parsing (bsc#1217649) - Added patches: * tomcat-10-CVE-2023-46589.patch OBS-URL: https://build.opensuse.org/request/show/1139494 OBS-URL: https://build.opensuse.org/package/show/Java:packages/tomcat10?expand=0&rev=20 --- tomcat-10-CVE-2023-46589.patch | 307 +++++++++++++++++++++++++++++++++ tomcat10.changes | 15 ++ tomcat10.spec | 10 +- 3 files changed, 328 insertions(+), 4 deletions(-) create mode 100644 tomcat-10-CVE-2023-46589.patch diff --git a/tomcat-10-CVE-2023-46589.patch b/tomcat-10-CVE-2023-46589.patch new file mode 100644 index 0000000..04f256b --- /dev/null +++ b/tomcat-10-CVE-2023-46589.patch @@ -0,0 +1,307 @@ +Index: apache-tomcat-10.1.14-src/java/org/apache/catalina/connector/BadRequestException.java +=================================================================== +--- /dev/null ++++ apache-tomcat-10.1.14-src/java/org/apache/catalina/connector/BadRequestException.java +@@ -0,0 +1,68 @@ ++/* ++ * 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.connector; ++ ++import java.io.IOException; ++ ++/** ++ * Extend IOException to identify it as being caused by a bad request from a remote client. ++ */ ++public class BadRequestException extends IOException { ++ ++ private static final long serialVersionUID = 1L; ++ ++ ++ // ------------------------------------------------------------ Constructors ++ ++ /** ++ * Construct a new BadRequestException with no other information. ++ */ ++ public BadRequestException() { ++ super(); ++ } ++ ++ ++ /** ++ * Construct a new BadRequestException for the specified message. ++ * ++ * @param message Message describing this exception ++ */ ++ public BadRequestException(String message) { ++ super(message); ++ } ++ ++ ++ /** ++ * Construct a new BadRequestException for the specified throwable. ++ * ++ * @param throwable Throwable that caused this exception ++ */ ++ public BadRequestException(Throwable throwable) { ++ super(throwable); ++ } ++ ++ ++ /** ++ * Construct a new BadRequestException for the specified message and throwable. ++ * ++ * @param message Message describing this exception ++ * @param throwable Throwable that caused this exception ++ */ ++ public BadRequestException(String message, Throwable throwable) { ++ super(message, throwable); ++ } ++} +Index: apache-tomcat-10.1.14-src/java/org/apache/catalina/connector/ClientAbortException.java +=================================================================== +--- apache-tomcat-10.1.14-src.orig/java/org/apache/catalina/connector/ClientAbortException.java ++++ apache-tomcat-10.1.14-src/java/org/apache/catalina/connector/ClientAbortException.java +@@ -16,14 +16,12 @@ + */ + package org.apache.catalina.connector; + +-import java.io.IOException; +- + /** + * Extend IOException to identify it as being caused by an abort of a request by a remote client. + * + * @author Glenn L. Nielsen + */ +-public final class ClientAbortException extends IOException { ++public final class ClientAbortException extends BadRequestException { + + private static final long serialVersionUID = 1L; + +Index: apache-tomcat-10.1.14-src/java/org/apache/catalina/connector/InputBuffer.java +=================================================================== +--- apache-tomcat-10.1.14-src.orig/java/org/apache/catalina/connector/InputBuffer.java ++++ apache-tomcat-10.1.14-src/java/org/apache/catalina/connector/InputBuffer.java +@@ -29,6 +29,7 @@ import java.util.Map; + import java.util.concurrent.ConcurrentHashMap; + + import jakarta.servlet.ReadListener; ++import jakarta.servlet.RequestDispatcher; + + import org.apache.catalina.security.SecurityUtil; + import org.apache.coyote.ActionCode; +@@ -307,10 +308,24 @@ public class InputBuffer extends Reader + + try { + return coyoteRequest.doRead(this); ++ } catch (BadRequestException bre) { ++ // Set flag used by asynchronous processing to detect errors on non-container threads ++ coyoteRequest.setErrorException(bre); ++ // In synchronous processing, this exception may be swallowed by the application so set error flags here. ++ coyoteRequest.setAttribute(RequestDispatcher.ERROR_EXCEPTION, bre); ++ coyoteRequest.getResponse().setStatus(400); ++ coyoteRequest.getResponse().setError(); ++ // Make the exception visible to the application ++ throw bre; + } catch (IOException ioe) { ++ // Set flag used by asynchronous processing to detect errors on non-container threads + coyoteRequest.setErrorException(ioe); +- // An IOException on a read is almost always due to +- // the remote client aborting the request. ++ // In synchronous processing, this exception may be swallowed by the application so set error flags here. ++ coyoteRequest.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe); ++ coyoteRequest.getResponse().setStatus(400); ++ coyoteRequest.getResponse().setError(); ++ // Any other IOException on a read is almost always due to the remote client aborting the request. ++ // Make the exception visible to the application + throw new ClientAbortException(ioe); + } + } +Index: apache-tomcat-10.1.14-src/java/org/apache/catalina/core/ApplicationDispatcher.java +=================================================================== +--- apache-tomcat-10.1.14-src.orig/java/org/apache/catalina/core/ApplicationDispatcher.java ++++ apache-tomcat-10.1.14-src/java/org/apache/catalina/core/ApplicationDispatcher.java +@@ -41,7 +41,7 @@ import org.apache.catalina.AsyncDispatch + import org.apache.catalina.Context; + import org.apache.catalina.Globals; + import org.apache.catalina.Wrapper; +-import org.apache.catalina.connector.ClientAbortException; ++import org.apache.catalina.connector.BadRequestException; + import org.apache.catalina.connector.Request; + import org.apache.catalina.connector.RequestFacade; + import org.apache.catalina.connector.Response; +@@ -642,7 +642,7 @@ final class ApplicationDispatcher implem + filterChain.doFilter(request, response); + } + // Servlet Service Method is called by the FilterChain +- } catch (ClientAbortException e) { ++ } catch (BadRequestException e) { + ioException = e; + } catch (IOException e) { + wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException", wrapper.getName()), e); +@@ -653,7 +653,7 @@ final class ApplicationDispatcher implem + wrapper.unavailable(e); + } catch (ServletException e) { + Throwable rootCause = StandardWrapper.getRootCause(e); +- if (!(rootCause instanceof ClientAbortException)) { ++ if (!(rootCause instanceof BadRequestException)) { + wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException", wrapper.getName()), + rootCause); + } +Index: apache-tomcat-10.1.14-src/java/org/apache/catalina/core/StandardWrapperValve.java +=================================================================== +--- apache-tomcat-10.1.14-src.orig/java/org/apache/catalina/core/StandardWrapperValve.java ++++ apache-tomcat-10.1.14-src/java/org/apache/catalina/core/StandardWrapperValve.java +@@ -32,7 +32,7 @@ import org.apache.catalina.Container; + import org.apache.catalina.Context; + import org.apache.catalina.Globals; + import org.apache.catalina.LifecycleException; +-import org.apache.catalina.connector.ClientAbortException; ++import org.apache.catalina.connector.BadRequestException; + import org.apache.catalina.connector.Request; + import org.apache.catalina.connector.Response; + import org.apache.catalina.valves.ValveBase; +@@ -169,7 +169,7 @@ final class StandardWrapperValve extends + } + + } +- } catch (ClientAbortException | CloseNowException e) { ++ } catch (BadRequestException | CloseNowException e) { + if (container.getLogger().isDebugEnabled()) { + container.getLogger().debug( + sm.getString("standardWrapper.serviceException", wrapper.getName(), context.getName()), e); +@@ -190,7 +190,7 @@ final class StandardWrapperValve extends + // do not want to do exception(request, response, e) processing + } catch (ServletException e) { + Throwable rootCause = StandardWrapper.getRootCause(e); +- if (!(rootCause instanceof ClientAbortException)) { ++ if (!(rootCause instanceof BadRequestException)) { + container.getLogger().error(sm.getString("standardWrapper.serviceExceptionRoot", wrapper.getName(), + context.getName(), e.getMessage()), rootCause); + } +Index: apache-tomcat-10.1.14-src/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java +=================================================================== +--- apache-tomcat-10.1.14-src.orig/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java ++++ apache-tomcat-10.1.14-src/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java +@@ -428,6 +428,83 @@ public class TestChunkedInputFilter exte + } + } + ++ ++ @Test ++ public void testTrailerHeaderNameNotTokenThrowException() throws Exception { ++ doTestTrailerHeaderNameNotToken(false); ++ } ++ ++ @Test ++ public void testTrailerHeaderNameNotTokenSwallowException() throws Exception { ++ doTestTrailerHeaderNameNotToken(true); ++ } ++ ++ private void doTestTrailerHeaderNameNotToken(boolean swallowException) throws Exception { ++ ++ // Setup Tomcat instance ++ Tomcat tomcat = getTomcatInstance(); ++ ++ // No file system docBase required ++ Context ctx = tomcat.addContext("", null); ++ ++ Tomcat.addServlet(ctx, "servlet", new SwallowBodyServlet(swallowException)); ++ ctx.addServletMappingDecoded("/", "servlet"); ++ ++ tomcat.start(); ++ ++ String[] request = new String[]{ ++ "POST / HTTP/1.1" + SimpleHttpClient.CRLF + ++ "Host: localhost" + SimpleHttpClient.CRLF + ++ "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + ++ "Content-Type: application/x-www-form-urlencoded" + SimpleHttpClient.CRLF + ++ "Connection: close" + SimpleHttpClient.CRLF + ++ SimpleHttpClient.CRLF + ++ "3" + SimpleHttpClient.CRLF + ++ "a=0" + SimpleHttpClient.CRLF + ++ "4" + SimpleHttpClient.CRLF + ++ "&b=1" + SimpleHttpClient.CRLF + ++ "0" + SimpleHttpClient.CRLF + ++ "x@trailer: Test" + SimpleHttpClient.CRLF + ++ SimpleHttpClient.CRLF }; ++ ++ TrailerClient client = new TrailerClient(tomcat.getConnector().getLocalPort()); ++ client.setRequest(request); ++ ++ client.connect(); ++ client.processRequest(); ++ // Expected to fail because of invalid trailer header name ++ Assert.assertTrue(client.getResponseLine(), client.isResponse400()); ++ } ++ ++ private static class SwallowBodyServlet extends HttpServlet { ++ private static final long serialVersionUID = 1L; ++ ++ private final boolean swallowException; ++ ++ SwallowBodyServlet(boolean swallowException) { ++ this.swallowException = swallowException; ++ } ++ ++ @Override ++ protected void doPost(HttpServletRequest req, HttpServletResponse resp) ++ throws ServletException, IOException { ++ resp.setContentType("text/plain"); ++ PrintWriter pw = resp.getWriter(); ++ ++ // Read the body ++ InputStream is = req.getInputStream(); ++ try { ++ while (is.read() > -1) { ++ } ++ pw.write("OK"); ++ } catch (IOException ioe) { ++ if (!swallowException) { ++ throw ioe; ++ } ++ } ++ } ++ } ++ + private static class EchoHeaderServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + +Index: apache-tomcat-10.1.14-src/webapps/docs/changelog.xml +=================================================================== +--- apache-tomcat-10.1.14-src.orig/webapps/docs/changelog.xml ++++ apache-tomcat-10.1.14-src/webapps/docs/changelog.xml +@@ -129,6 +129,11 @@ + Improve handling of failures within recycle() methods. + (markt) + ++ ++ Ensure that an IOException during the reading of the ++ request triggers always error handling, regardless of whether the ++ application swallows the exception. (markt) ++ + + + +@@ -170,7 +175,7 @@ + + Improvements to HTTP/2 overhead protection. (markt) + +- ++d + + + diff --git a/tomcat10.changes b/tomcat10.changes index 84c6dee..28d6489 100644 --- a/tomcat10.changes +++ b/tomcat10.changes @@ -1,3 +1,18 @@ +------------------------------------------------------------------- +Wed Jan 17 15:35:51 UTC 2024 - Michele Bussolotto + +- change server.xml during %post instead of %posttrans +- add libxslt-tools requirement + +------------------------------------------------------------------- +Wed Jan 17 15:35:40 UTC 2024 - Michele Bussolotto + +- Fixed CVEs: + * CVE-2023-46589: Apache Tomcat: HTTP request smuggling due to + incorrect headers parsing (bsc#1217649) +- Added patches: + * tomcat-10-CVE-2023-46589.patch + ------------------------------------------------------------------- Tue Jan 16 09:05:32 UTC 2024 - Michele Bussolotto diff --git a/tomcat10.spec b/tomcat10.spec index 25831ac..50ab8f6 100644 --- a/tomcat10.spec +++ b/tomcat10.spec @@ -71,6 +71,7 @@ Source21: %{app_name}-functions Source30: %{app_name}-preamble Source31: %{app_name}-server Source32: %{app_name}-named.service +Source33: tomcat-10-CVE-2023-46589.patch Source100: valve.xslt Source101: allowLinking.xslt Source1000: %{app_name}-rpmlintrc @@ -111,7 +112,6 @@ BuildRequires: jakarta-taglibs-standard >= 1.1 BuildRequires: java-devel >= 11 BuildRequires: javapackages-local BuildRequires: junit -BuildRequires: libxslt-tools BuildRequires: osgi-annotation BuildRequires: osgi-compendium BuildRequires: osgi-core @@ -132,6 +132,7 @@ Requires: apache-commons-pool2 Requires: jakarta-servlet Requires: java >= %{java_version} Requires(post): %fillup_prereq +Requires(post): libxslt-tools Requires(pre): shadow Requires: libtcnative-1-0 >= 1.2.38 Requires: logrotate @@ -150,6 +151,7 @@ ATTENTION: This tomcat is built with java %{java_version}. Summary: The host manager and manager web applications for Apache Tomcat Group: Productivity/Networking/Web/Servers Requires: %{name} = %{version}-%{release} +Requires(post): libxslt-tools Conflicts: %{app_name}-admin-webapps %description admin-webapps @@ -167,6 +169,7 @@ Embeddeding support (various libraries) for Apache Tomcat. Summary: The "docs" web application for Apache Tomcat Group: Productivity/Networking/Web/Servers Requires: %{name} = %{version}-%{release} +Requires(post): libxslt-tools Conflicts: %{app_name}-docs-webapp %description docs-webapp @@ -261,6 +264,7 @@ Summary: ROOT and examples web applications for Apache Tomcat Group: Productivity/Networking/Web/Servers Requires: %{name} = %{version}-%{release} Requires: jakarta-taglibs-standard >= 1.1 +Requires(post): libxslt-tools Conflicts: %{app_name}-webapps %description webapps @@ -587,6 +591,7 @@ getent passwd tomcat >/dev/null || %{_sbindir}/useradd -c "Apache Tomcat" \ %post %service_add_post %{app_name}.service %{fillup_only %{app_name}} +xsltproc --output %{confdir}/server.xml %{confdir}/valve.xslt %{confdir}/server.xml %preun %service_del_preun %{app_name}.service @@ -696,9 +701,6 @@ if [ ! -e %{_datadir}/%{app_name}/webapps/docs ]; then ln -sf %{tomcatappdir}/docs %{_datadir}/%{app_name}/webapps/docs fi -%posttrans -xsltproc --output %{confdir}/server.xml %{confdir}/valve.xslt %{confdir}/server.xml - %files %doc {LICENSE,NOTICE,RELEASE*} %attr(0755,root,root) %{_bindir}/%{app_name}-digest