diff --git a/SOURCES/tomcat-7.0.76-CVE-2020-1938.patch b/SOURCES/tomcat-7.0.76-CVE-2020-1938.patch
new file mode 100644
index 0000000..0201bb7
--- /dev/null
+++ b/SOURCES/tomcat-7.0.76-CVE-2020-1938.patch
@@ -0,0 +1,393 @@
+diff -up ./java/org/apache/coyote/ajp/AbstractAjpProcessor.java.orig ./java/org/apache/coyote/ajp/AbstractAjpProcessor.java
+--- ./java/org/apache/coyote/ajp/AbstractAjpProcessor.java.orig 2020-03-03 14:24:23.418779786 -0500
++++ ./java/org/apache/coyote/ajp/AbstractAjpProcessor.java 2020-03-03 14:26:28.555466694 -0500
+@@ -23,7 +23,12 @@ import java.net.InetAddress;
+ import java.security.NoSuchProviderException;
+ import java.security.cert.CertificateFactory;
+ import java.security.cert.X509Certificate;
++import java.util.Collections;
++import java.util.HashSet;
++import java.util.Set;
+ import java.util.concurrent.atomic.AtomicBoolean;
++import java.util.regex.Matcher;
++import java.util.regex.Pattern;
+
+ import javax.servlet.http.HttpServletResponse;
+
+@@ -80,6 +85,9 @@ public abstract class AbstractAjpProcess
+ protected static final byte[] pongMessageArray;
+
+
++ private static final Set javaxAttributes;
++
++
+ static {
+ // Allocate the end message array
+ AjpMessage endMessage = new AjpMessage(16);
+@@ -120,6 +128,14 @@ public abstract class AbstractAjpProcess
+ pongMessageArray = new byte[pongMessage.getLen()];
+ System.arraycopy(pongMessage.getBuffer(), 0, pongMessageArray,
+ 0, pongMessage.getLen());
++
++ // Build the Set of javax attributes
++ Set s = new HashSet();
++ s.add("javax.servlet.request.cipher_suite");
++ s.add("javax.servlet.request.key_size");
++ s.add("javax.servlet.request.ssl_session");
++ s.add("javax.servlet.request.X509Certificate");
++ javaxAttributes= Collections.unmodifiableSet(s);
+ }
+
+
+@@ -307,9 +323,16 @@ public abstract class AbstractAjpProcess
+ /**
+ * Required secret.
+ */
++ @Deprecated
+ protected String requiredSecret = null;
++ protected String secret = null;
++ public void setSecret(String secret) {
++ this.secret = secret;
++ this.requiredSecret = secret;
++ }
++ @Deprecated
+ public void setRequiredSecret(String requiredSecret) {
+- this.requiredSecret = requiredSecret;
++ setSecret(requiredSecret);
+ }
+
+
+@@ -326,9 +349,15 @@ public abstract class AbstractAjpProcess
+ public String getClientCertProvider() { return clientCertProvider; }
+ public void setClientCertProvider(String s) { this.clientCertProvider = s; }
+
+- // --------------------------------------------------------- Public Methods
++
++ private Pattern allowedRequestAttributesPatternPattern;
++ public void setAllowedRequestAttributesPatternPattern(Pattern allowedRequestAttributesPatternPattern) {
++ this.allowedRequestAttributesPatternPattern = allowedRequestAttributesPatternPattern;
++ }
+
+
++ // --------------------------------------------------------- Public Methods
++
+ /**
+ * Send an action to the connector.
+ *
+@@ -827,7 +856,7 @@ public abstract class AbstractAjpProcess
+ }
+
+ // Decode extra attributes
+- boolean secret = false;
++ boolean secretPresentInRequest = false;
+ byte attributeCode;
+ while ((attributeCode = requestHeaderMessage.getByte())
+ != Constants.SC_A_ARE_DONE) {
+@@ -856,8 +885,25 @@ public abstract class AbstractAjpProcess
+ }
+ } else if(n.equals(Constants.SC_A_SSL_PROTOCOL)) {
+ request.setAttribute(SSLSupport.PROTOCOL_VERSION_KEY, v);
++ } else if (n.equals("JK_LB_ACTIVATION")) {
++ request.setAttribute(n, v);
++ } else if (javaxAttributes.contains(n)) {
++ request.setAttribute(n, v);
+ } else {
+- request.setAttribute(n, v );
++ // All 'known' attributes will be processed by the previous
++ // blocks. Any remaining attribute is an 'arbitrary' one.
++ if (allowedRequestAttributesPatternPattern == null) {
++ response.setStatus(403);
++ setErrorState(ErrorState.CLOSE_CLEAN, null);
++ } else {
++ Matcher m = allowedRequestAttributesPatternPattern.matcher(n);
++ if (m.matches()) {
++ request.setAttribute(n, v);
++ } else {
++ response.setStatus(403);
++ setErrorState(ErrorState.CLOSE_CLEAN, null);
++ }
++ }
+ }
+ break;
+
+@@ -930,9 +976,9 @@ public abstract class AbstractAjpProcess
+
+ case Constants.SC_A_SECRET:
+ requestHeaderMessage.getBytes(tmpMB);
+- if (requiredSecret != null) {
+- secret = true;
+- if (!tmpMB.equals(requiredSecret)) {
++ if (secret != null) {
++ secretPresentInRequest = true;
++ if (!tmpMB.equals(secret)) {
+ response.setStatus(403);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ }
+@@ -948,7 +994,7 @@ public abstract class AbstractAjpProcess
+ }
+
+ // Check if secret was submitted if required
+- if ((requiredSecret != null) && !secret) {
++ if ((secret != null) && !secretPresentInRequest) {
+ response.setStatus(403);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ }
+diff -up ./java/org/apache/coyote/ajp/AbstractAjpProtocol.java.orig ./java/org/apache/coyote/ajp/AbstractAjpProtocol.java
+--- ./java/org/apache/coyote/ajp/AbstractAjpProtocol.java.orig 2020-03-03 14:24:23.422779776 -0500
++++ ./java/org/apache/coyote/ajp/AbstractAjpProtocol.java 2020-03-03 14:39:25.219521977 -0500
+@@ -16,6 +16,8 @@
+ */
+ package org.apache.coyote.ajp;
+
++import java.util.regex.Pattern;
++
+ import org.apache.coyote.AbstractProtocol;
+ import org.apache.coyote.Processor;
+ import org.apache.coyote.http11.upgrade.servlet31.HttpUpgradeHandler;
+@@ -85,8 +87,61 @@ public abstract class AbstractAjpProtoco
+ * Required secret.
+ */
+ protected String requiredSecret = null;
++ private String secret = null;
++ /**
++ * Set the secret that must be included with every request.
++ *
++ * @param secret The required secret
++ */
++ public void setSecret(String secret) {
++ this.secret = secret;
++ this.requiredSecret = secret;
++ }
++ protected String getSecret() {
++ return secret;
++ }
++ /**
++ * Set the required secret that must be included with every request.
++ *
++ * @param requiredSecret The required secret
++ *
++ * @deprecated Replaced by {@link #setSecret(String)}.
++ * Will be removed in Tomcat 11 onwards
++ */
++ @Deprecated
+ public void setRequiredSecret(String requiredSecret) {
+- this.requiredSecret = requiredSecret;
++ setSecret(requiredSecret);
++ }
++ /**
++ * @return The current secret
++ *
++ * @deprecated Replaced by {@link #getSecret()}.
++ * Will be removed in Tomcat 11 onwards
++ */
++ @Deprecated
++ protected String getRequiredSecret() {
++ return getSecret();
++ }
++
++
++ private boolean secretRequired = false;
++ public void setSecretRequired(boolean secretRequired) {
++ this.secretRequired = secretRequired;
++ }
++ public boolean getSecretRequired() {
++ return secretRequired;
++ }
++
++
++ private Pattern allowedRequestAttributesPatternPattern;
++ public void setAllowedRequestAttributesPattern(String allowedRequestAttributesPattern) {
++ this.allowedRequestAttributesPatternPattern = Pattern.compile(allowedRequestAttributesPattern);
++ }
++ public String getAllowedRequestAttributesPattern() {
++ return allowedRequestAttributesPatternPattern.pattern();
++ }
++ protected Pattern getAllowedRequestAttributesPatternPattern() {
++ return allowedRequestAttributesPatternPattern;
+ }
+
+
+@@ -136,4 +191,16 @@ public abstract class AbstractAjpProtoco
+ return null;
+ }
+ }
++
++
++ @Override
++ public void start() throws Exception {
++ if (getSecretRequired()) {
++ String secret = getSecret();
++ if (secret == null || secret.length() == 0) {
++ throw new IllegalArgumentException(sm.getString("ajpprotocol.nosecret"));
++ }
++ }
++ super.start();
++ }
+ }
+diff -up ./java/org/apache/coyote/ajp/AjpAprProtocol.java.orig ./java/org/apache/coyote/ajp/AjpAprProtocol.java
+--- ./java/org/apache/coyote/ajp/AjpAprProtocol.java.orig 2020-03-03 14:24:23.426779766 -0500
++++ ./java/org/apache/coyote/ajp/AjpAprProtocol.java 2020-03-03 14:26:28.556466692 -0500
+@@ -148,10 +148,11 @@ public class AjpAprProtocol extends Abst
+ processor.setAjpFlush(proto.getAjpFlush());
+ processor.setTomcatAuthentication(proto.tomcatAuthentication);
+ processor.setTomcatAuthorization(proto.getTomcatAuthorization());
+- processor.setRequiredSecret(proto.requiredSecret);
++ processor.setSecret(proto.getSecret());
+ processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
+ processor.setClientCertProvider(proto.getClientCertProvider());
+ processor.setMaxCookieCount(proto.getMaxCookieCount());
++ processor.setAllowedRequestAttributesPatternPattern(proto.getAllowedRequestAttributesPatternPattern());
+ register(processor);
+ return processor;
+ }
+diff -up ./java/org/apache/coyote/ajp/AjpNioProtocol.java.orig ./java/org/apache/coyote/ajp/AjpNioProtocol.java
+--- ./java/org/apache/coyote/ajp/AjpNioProtocol.java.orig 2020-03-03 14:24:23.430779756 -0500
++++ ./java/org/apache/coyote/ajp/AjpNioProtocol.java 2020-03-03 14:26:28.556466692 -0500
+@@ -178,10 +178,11 @@ public class AjpNioProtocol extends Abst
+ processor.setAjpFlush(proto.getAjpFlush());
+ processor.setTomcatAuthentication(proto.tomcatAuthentication);
+ processor.setTomcatAuthorization(proto.getTomcatAuthorization());
+- processor.setRequiredSecret(proto.requiredSecret);
++ processor.setSecret(proto.getSecret());
+ processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
+ processor.setClientCertProvider(proto.getClientCertProvider());
+ processor.setMaxCookieCount(proto.getMaxCookieCount());
++ processor.setAllowedRequestAttributesPatternPattern(proto.getAllowedRequestAttributesPatternPattern());
+ register(processor);
+ return processor;
+ }
+diff -up ./java/org/apache/coyote/ajp/AjpProtocol.java.orig ./java/org/apache/coyote/ajp/AjpProtocol.java
+--- ./java/org/apache/coyote/ajp/AjpProtocol.java.orig 2020-03-03 14:24:23.435779743 -0500
++++ ./java/org/apache/coyote/ajp/AjpProtocol.java 2020-03-03 14:26:28.556466692 -0500
+@@ -136,10 +136,11 @@ public class AjpProtocol extends Abstrac
+ processor.setAjpFlush(proto.getAjpFlush());
+ processor.setTomcatAuthentication(proto.tomcatAuthentication);
+ processor.setTomcatAuthorization(proto.getTomcatAuthorization());
+- processor.setRequiredSecret(proto.requiredSecret);
++ processor.setSecret(proto.getSecret());
+ processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
+ processor.setClientCertProvider(proto.getClientCertProvider());
+ processor.setMaxCookieCount(proto.getMaxCookieCount());
++ processor.setAllowedRequestAttributesPatternPattern(proto.getAllowedRequestAttributesPatternPattern());
+ register(processor);
+ return processor;
+ }
+diff -up ./java/org/apache/coyote/ajp/LocalStrings.properties.orig ./java/org/apache/coyote/ajp/LocalStrings.properties
+--- ./java/org/apache/coyote/ajp/LocalStrings.properties.orig 2020-03-03 14:24:23.439779733 -0500
++++ ./java/org/apache/coyote/ajp/LocalStrings.properties 2020-03-03 14:26:13.057505470 -0500
+@@ -14,6 +14,7 @@
+ # limitations under the License.
+ ajpprotocol.endpoint.starterror=Error starting endpoint
+ ajpprotocol.init=Initializing Coyote AJP/1.3 on {0}
++ajpprotocol.nosecret=The AJP Connector is configured with secretRequired="true" but the secret attribute is either null or "". This combination is not valid.
+ ajpprotocol.start=Starting Coyote AJP/1.3 on {0}
+ ajpprotocol.failedwrite=Socket write failed
+ ajpprotocol.request.register=Error registering request processor in JMX
+diff -up ./test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java.orig ./test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java
+--- ./test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java.orig 2020-03-03 14:24:23.442779726 -0500
++++ ./test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java 2020-03-03 14:26:28.556466692 -0500
+@@ -30,14 +30,26 @@ import javax.servlet.http.HttpServletReq
+ import javax.servlet.http.HttpServletResponse;
+
+ import org.junit.Assert;
++import org.junit.Before;
+ import org.junit.Test;
+
+ import org.apache.catalina.Context;
++import org.apache.catalina.connector.Connector;
+ import org.apache.catalina.startup.Tomcat;
+ import org.apache.catalina.startup.TomcatBaseTest;
+
+ public class TestAbstractAjpProcessor extends TomcatBaseTest {
+
++ @Before
++ @Override
++ public void setUp() throws Exception {
++ super.setUp();
++
++ Connector c = getTomcatInstance().getConnector();
++ c.setProperty("secretRequired", "false");
++ c.setProperty("allowedRequestAttributesPattern", "MYATTRIBUTE.*");
++ }
++
+ @Override
+ protected String getProtocol() {
+ /*
+diff -up ./webapps/docs/changelog.xml.orig ./webapps/docs/changelog.xml
+--- ./webapps/docs/changelog.xml.orig 2020-03-03 14:24:23.446779716 -0500
++++ ./webapps/docs/changelog.xml 2020-03-03 14:41:14.526247697 -0500
+@@ -57,6 +57,25 @@
+ They eventually become mixed with the numbered issues. (I.e., numbered
+ issues do not "pop up" wrt. others).
+ -->
++
++
++
++
++ Rename the requiredSecret
attribute of the AJP/1.3
++ Connector to secret
and add a new attribute
++ secretRequired
that defaults to true
. When
++ secretRequired
is true
the AJP/1.3 Connector
++ will not start unless the secret
attribute is configured to
++ a non-null, non-zero length String. (markt)
++
++
++ Add a new attribute, allowedRequestAttributesPattern
to
++ the AJP/1.3 Connector. Requests with unreconised attributes will be
++ blocked with a 403. (markt)
++
++
++
++
+
+
+
+diff -up ./webapps/docs/config/ajp.xml.orig ./webapps/docs/config/ajp.xml
+--- ./webapps/docs/config/ajp.xml.orig 2020-03-03 14:24:23.448779711 -0500
++++ ./webapps/docs/config/ajp.xml 2020-03-03 14:39:44.506473588 -0500
+@@ -312,6 +312,25 @@
+ interface.
+
+
++
++ The AJP protocol passes some information from the reverse proxy to the
++ AJP connector using request attributes. These attributes are:
++
++ - javax.servlet.request.cipher_suite
++ - javax.servlet.request.key_size
++ - javax.servlet.request.ssl_session
++ - javax.servlet.request.X509Certificate
++ - AJP_LOCAL_ADDR
++ - AJP_REMOTE_PORT
++ - AJP_SSL_PROTOCOL
++ - JK_LB_ACTIVATION
++
++ The AJP protocol supports the passing of arbitrary request attributes.
++ Requests containing arbitrary request attributes will be rejected with a
++ 403 response unless the entire attribute name matches this regular
++ expression. If not specified, the default value is null
.
++
++
+
+ Controls when the socket used by the connector is bound. By default it
+ is bound when the connector is initiated and unbound when the connector is
+@@ -436,8 +455,18 @@
+ expected concurrent requests (synchronous and asynchronous).
+
+
+-
++
+ Only requests from workers with this secret keyword will be accepted.
++ The default value is null
. This attrbute must be specified
++ with a non-null, non-zero length value unless
++ secretRequired is explicitly configured to be
++ false
.
++
++
++
++ If this attribute is true
, the AJP Connector will only
++ start if the secret attribute is configured with a
++ non-null, non-zero length value. The default value is false
.
+
+
+
diff --git a/SPECS/tomcat.spec b/SPECS/tomcat.spec
index a4e5ad6..0581db7 100644
--- a/SPECS/tomcat.spec
+++ b/SPECS/tomcat.spec
@@ -54,7 +54,7 @@
Name: tomcat
Epoch: 0
Version: %{major_version}.%{minor_version}.%{micro_version}
-Release: 10%{?dist}
+Release: 11%{?dist}
Summary: Apache Servlet/JSP Engine, RI for Servlet %{servletspec}/JSP %{jspspec} API
Group: System Environment/Daemons
@@ -101,6 +101,7 @@ Patch13: %{name}-7.0.76-CVE-2018-8014.patch
Patch14: %{name}-7.0.76-CVE-2018-8034.patch
Patch15: %{name}-7.0.76-rhbz-1588703.patch
Patch16: %{name}-7.0.76-rhbz-1455483.patch
+Patch17: %{name}-7.0.76-CVE-2020-1938.patch
BuildArch: noarch
@@ -262,6 +263,7 @@ find . -type f \( -name "*.bat" -o -name "*.class" -o -name Thumbs.db -o -name "
%patch14 -p0
%patch15 -p0
%patch16 -p0
+%patch17 -p0
%{__ln_s} $(build-classpath jakarta-taglibs-core) webapps/examples/WEB-INF/lib/jstl.jar
%{__ln_s} $(build-classpath jakarta-taglibs-standard) webapps/examples/WEB-INF/lib/standard.jar
@@ -706,6 +708,9 @@ fi
%attr(0644,root,root) %{_unitdir}/%{name}-jsvc.service
%changelog
+* Tue Mar 03 2020 Coty Sutherland 0:7.0.76-11
+- Resolves: rhbz#1806801 CVE-2020-1938 tomcat: Apache Tomcat AJP File Read/Inclusion Vulnerability
+
* Tue Sep 03 2019 Coty Sutherland 0:7.0.76-10
- Resolves: rhbz#1748541 Bump tomcat release number