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