Blame SOURCES/tomcat-7.0.76-CVE-2020-1938.patch

783d95
diff -up ./java/org/apache/coyote/ajp/AbstractAjpProcessor.java.orig ./java/org/apache/coyote/ajp/AbstractAjpProcessor.java
783d95
--- ./java/org/apache/coyote/ajp/AbstractAjpProcessor.java.orig	2020-03-03 14:24:23.418779786 -0500
783d95
+++ ./java/org/apache/coyote/ajp/AbstractAjpProcessor.java	2020-03-03 14:26:28.555466694 -0500
783d95
@@ -23,7 +23,12 @@ import java.net.InetAddress;
783d95
 import java.security.NoSuchProviderException;
783d95
 import java.security.cert.CertificateFactory;
783d95
 import java.security.cert.X509Certificate;
783d95
+import java.util.Collections;
783d95
+import java.util.HashSet;
783d95
+import java.util.Set;
783d95
 import java.util.concurrent.atomic.AtomicBoolean;
783d95
+import java.util.regex.Matcher;
783d95
+import java.util.regex.Pattern;
783d95
 
783d95
 import javax.servlet.http.HttpServletResponse;
783d95
 
783d95
@@ -80,6 +85,9 @@ public abstract class AbstractAjpProcess
783d95
     protected static final byte[] pongMessageArray;
783d95
 
783d95
 
783d95
+    private static final Set<String> javaxAttributes;
783d95
+
783d95
+
783d95
     static {
783d95
         // Allocate the end message array
783d95
         AjpMessage endMessage = new AjpMessage(16);
783d95
@@ -120,6 +128,14 @@ public abstract class AbstractAjpProcess
783d95
         pongMessageArray = new byte[pongMessage.getLen()];
783d95
         System.arraycopy(pongMessage.getBuffer(), 0, pongMessageArray,
783d95
                 0, pongMessage.getLen());
783d95
+
783d95
+        // Build the Set of javax attributes
783d95
+        Set<String> s = new HashSet<String>();
783d95
+        s.add("javax.servlet.request.cipher_suite");
783d95
+        s.add("javax.servlet.request.key_size");
783d95
+        s.add("javax.servlet.request.ssl_session");
783d95
+        s.add("javax.servlet.request.X509Certificate");
783d95
+        javaxAttributes= Collections.unmodifiableSet(s);
783d95
     }
783d95
 
783d95
 
783d95
@@ -307,9 +323,16 @@ public abstract class AbstractAjpProcess
783d95
     /**
783d95
      * Required secret.
783d95
      */
783d95
+    @Deprecated
783d95
     protected String requiredSecret = null;
783d95
+    protected String secret = null;
783d95
+    public void setSecret(String secret) {
783d95
+        this.secret = secret;
783d95
+        this.requiredSecret = secret;
783d95
+    }
783d95
+    @Deprecated
783d95
     public void setRequiredSecret(String requiredSecret) {
783d95
-        this.requiredSecret = requiredSecret;
783d95
+        setSecret(requiredSecret);
783d95
     }
783d95
 
783d95
 
783d95
@@ -326,9 +349,15 @@ public abstract class AbstractAjpProcess
783d95
     public String getClientCertProvider() { return clientCertProvider; }
783d95
     public void setClientCertProvider(String s) { this.clientCertProvider = s; }
783d95
 
783d95
-    // --------------------------------------------------------- Public Methods
783d95
+
783d95
+    private Pattern allowedRequestAttributesPatternPattern;
783d95
+    public void setAllowedRequestAttributesPatternPattern(Pattern allowedRequestAttributesPatternPattern) {
783d95
+        this.allowedRequestAttributesPatternPattern = allowedRequestAttributesPatternPattern;
783d95
+    }
783d95
 
783d95
 
783d95
+    // --------------------------------------------------------- Public Methods
783d95
+
783d95
     /**
783d95
      * Send an action to the connector.
783d95
      *
783d95
@@ -827,7 +856,7 @@ public abstract class AbstractAjpProcess
783d95
         }
783d95
 
783d95
         // Decode extra attributes
783d95
-        boolean secret = false;
783d95
+        boolean secretPresentInRequest = false;
783d95
         byte attributeCode;
783d95
         while ((attributeCode = requestHeaderMessage.getByte())
783d95
                 != Constants.SC_A_ARE_DONE) {
783d95
@@ -856,8 +885,25 @@ public abstract class AbstractAjpProcess
783d95
                     }
783d95
                 } else if(n.equals(Constants.SC_A_SSL_PROTOCOL)) {
783d95
                     request.setAttribute(SSLSupport.PROTOCOL_VERSION_KEY, v);
783d95
+                } else if (n.equals("JK_LB_ACTIVATION")) {
783d95
+                    request.setAttribute(n, v);
783d95
+                } else if (javaxAttributes.contains(n)) {
783d95
+                    request.setAttribute(n, v);
783d95
                 } else {
783d95
-                    request.setAttribute(n, v );
783d95
+                    // All 'known' attributes will be processed by the previous
783d95
+                    // blocks. Any remaining attribute is an 'arbitrary' one.
783d95
+                    if (allowedRequestAttributesPatternPattern == null) {
783d95
+                        response.setStatus(403);
783d95
+                        setErrorState(ErrorState.CLOSE_CLEAN, null);
783d95
+                    } else {
783d95
+                        Matcher m = allowedRequestAttributesPatternPattern.matcher(n);
783d95
+                        if (m.matches()) {
783d95
+                            request.setAttribute(n, v);
783d95
+                        } else {
783d95
+                            response.setStatus(403);
783d95
+                            setErrorState(ErrorState.CLOSE_CLEAN, null);
783d95
+                        }
783d95
+                    }
783d95
                 }
783d95
                 break;
783d95
 
783d95
@@ -930,9 +976,9 @@ public abstract class AbstractAjpProcess
783d95
 
783d95
             case Constants.SC_A_SECRET:
783d95
                 requestHeaderMessage.getBytes(tmpMB);
783d95
-                if (requiredSecret != null) {
783d95
-                    secret = true;
783d95
-                    if (!tmpMB.equals(requiredSecret)) {
783d95
+                if (secret != null) {
783d95
+                    secretPresentInRequest = true;
783d95
+                    if (!tmpMB.equals(secret)) {
783d95
                         response.setStatus(403);
783d95
                         setErrorState(ErrorState.CLOSE_CLEAN, null);
783d95
                     }
783d95
@@ -948,7 +994,7 @@ public abstract class AbstractAjpProcess
783d95
         }
783d95
 
783d95
         // Check if secret was submitted if required
783d95
-        if ((requiredSecret != null) && !secret) {
783d95
+        if ((secret != null) && !secretPresentInRequest) {
783d95
             response.setStatus(403);
783d95
             setErrorState(ErrorState.CLOSE_CLEAN, null);
783d95
         }
783d95
diff -up ./java/org/apache/coyote/ajp/AbstractAjpProtocol.java.orig ./java/org/apache/coyote/ajp/AbstractAjpProtocol.java
783d95
--- ./java/org/apache/coyote/ajp/AbstractAjpProtocol.java.orig	2020-03-03 14:24:23.422779776 -0500
783d95
+++ ./java/org/apache/coyote/ajp/AbstractAjpProtocol.java	2020-03-03 14:39:25.219521977 -0500
783d95
@@ -16,6 +16,8 @@
783d95
  */
783d95
 package org.apache.coyote.ajp;
783d95
 
783d95
+import java.util.regex.Pattern;
783d95
+
783d95
 import org.apache.coyote.AbstractProtocol;
783d95
 import org.apache.coyote.Processor;
783d95
 import org.apache.coyote.http11.upgrade.servlet31.HttpUpgradeHandler;
783d95
@@ -85,8 +87,61 @@ public abstract class AbstractAjpProtoco
783d95
      * Required secret.
783d95
      */
783d95
     protected String requiredSecret = null;
783d95
+    private String secret = null;
783d95
+    /**
783d95
+     * Set the secret that must be included with every request.
783d95
+     *
783d95
+     * @param secret The required secret
783d95
+     */
783d95
+    public void setSecret(String secret) {
783d95
+        this.secret = secret;
783d95
+        this.requiredSecret = secret;
783d95
+    }
783d95
+    protected String getSecret() {
783d95
+        return secret;
783d95
+    }
783d95
+    /**
783d95
+     * Set the required secret that must be included with every request.
783d95
+     *
783d95
+     * @param requiredSecret The required secret
783d95
+     *
783d95
+     * @deprecated Replaced by {@link #setSecret(String)}.
783d95
+     *             Will be removed in Tomcat 11 onwards
783d95
+     */
783d95
+    @Deprecated
783d95
     public void setRequiredSecret(String requiredSecret) {
783d95
-        this.requiredSecret = requiredSecret;
783d95
+        setSecret(requiredSecret);
783d95
+    }
783d95
+    /**
783d95
+     * @return The current secret
783d95
+     *
783d95
+     * @deprecated Replaced by {@link #getSecret()}.
783d95
+     *             Will be removed in Tomcat 11 onwards
783d95
+     */
783d95
+    @Deprecated
783d95
+    protected String getRequiredSecret() {
783d95
+        return getSecret();
783d95
+    }
783d95
+
783d95
+
783d95
+    private boolean secretRequired = false;
783d95
+    public void setSecretRequired(boolean secretRequired) {
783d95
+        this.secretRequired = secretRequired;
783d95
+    }
783d95
+    public boolean getSecretRequired() {
783d95
+        return secretRequired;
783d95
+    }
783d95
+
783d95
+
783d95
+    private Pattern allowedRequestAttributesPatternPattern;
783d95
+    public void setAllowedRequestAttributesPattern(String allowedRequestAttributesPattern) {
783d95
+        this.allowedRequestAttributesPatternPattern = Pattern.compile(allowedRequestAttributesPattern);
783d95
+    }
783d95
+    public String getAllowedRequestAttributesPattern() {
783d95
+        return allowedRequestAttributesPatternPattern.pattern();
783d95
+    }
783d95
+    protected Pattern getAllowedRequestAttributesPatternPattern() {
783d95
+        return allowedRequestAttributesPatternPattern;
783d95
     }
783d95
 
783d95
 
783d95
@@ -136,4 +191,16 @@ public abstract class AbstractAjpProtoco
783d95
             return null;
783d95
         }
783d95
     }
783d95
+
783d95
+
783d95
+    @Override
783d95
+    public void start() throws Exception {
783d95
+        if (getSecretRequired()) {
783d95
+            String secret = getSecret();
783d95
+            if (secret == null || secret.length() == 0) {
783d95
+                throw new IllegalArgumentException(sm.getString("ajpprotocol.nosecret"));
783d95
+            }
783d95
+        }
783d95
+        super.start();
783d95
+    }
783d95
 }
783d95
diff -up ./java/org/apache/coyote/ajp/AjpAprProtocol.java.orig ./java/org/apache/coyote/ajp/AjpAprProtocol.java
783d95
--- ./java/org/apache/coyote/ajp/AjpAprProtocol.java.orig	2020-03-03 14:24:23.426779766 -0500
783d95
+++ ./java/org/apache/coyote/ajp/AjpAprProtocol.java	2020-03-03 14:26:28.556466692 -0500
783d95
@@ -148,10 +148,11 @@ public class AjpAprProtocol extends Abst
783d95
             processor.setAjpFlush(proto.getAjpFlush());
783d95
             processor.setTomcatAuthentication(proto.tomcatAuthentication);
783d95
             processor.setTomcatAuthorization(proto.getTomcatAuthorization());
783d95
-            processor.setRequiredSecret(proto.requiredSecret);
783d95
+            processor.setSecret(proto.getSecret());
783d95
             processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
783d95
             processor.setClientCertProvider(proto.getClientCertProvider());
783d95
             processor.setMaxCookieCount(proto.getMaxCookieCount());
783d95
+            processor.setAllowedRequestAttributesPatternPattern(proto.getAllowedRequestAttributesPatternPattern());
783d95
             register(processor);
783d95
             return processor;
783d95
         }
783d95
diff -up ./java/org/apache/coyote/ajp/AjpNioProtocol.java.orig ./java/org/apache/coyote/ajp/AjpNioProtocol.java
783d95
--- ./java/org/apache/coyote/ajp/AjpNioProtocol.java.orig	2020-03-03 14:24:23.430779756 -0500
783d95
+++ ./java/org/apache/coyote/ajp/AjpNioProtocol.java	2020-03-03 14:26:28.556466692 -0500
783d95
@@ -178,10 +178,11 @@ public class AjpNioProtocol extends Abst
783d95
             processor.setAjpFlush(proto.getAjpFlush());
783d95
             processor.setTomcatAuthentication(proto.tomcatAuthentication);
783d95
             processor.setTomcatAuthorization(proto.getTomcatAuthorization());
783d95
-            processor.setRequiredSecret(proto.requiredSecret);
783d95
+            processor.setSecret(proto.getSecret());
783d95
             processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
783d95
             processor.setClientCertProvider(proto.getClientCertProvider());
783d95
             processor.setMaxCookieCount(proto.getMaxCookieCount());
783d95
+            processor.setAllowedRequestAttributesPatternPattern(proto.getAllowedRequestAttributesPatternPattern());
783d95
             register(processor);
783d95
             return processor;
783d95
         }
783d95
diff -up ./java/org/apache/coyote/ajp/AjpProtocol.java.orig ./java/org/apache/coyote/ajp/AjpProtocol.java
783d95
--- ./java/org/apache/coyote/ajp/AjpProtocol.java.orig	2020-03-03 14:24:23.435779743 -0500
783d95
+++ ./java/org/apache/coyote/ajp/AjpProtocol.java	2020-03-03 14:26:28.556466692 -0500
783d95
@@ -136,10 +136,11 @@ public class AjpProtocol extends Abstrac
783d95
             processor.setAjpFlush(proto.getAjpFlush());
783d95
             processor.setTomcatAuthentication(proto.tomcatAuthentication);
783d95
             processor.setTomcatAuthorization(proto.getTomcatAuthorization());
783d95
-            processor.setRequiredSecret(proto.requiredSecret);
783d95
+            processor.setSecret(proto.getSecret());
783d95
             processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
783d95
             processor.setClientCertProvider(proto.getClientCertProvider());
783d95
             processor.setMaxCookieCount(proto.getMaxCookieCount());
783d95
+            processor.setAllowedRequestAttributesPatternPattern(proto.getAllowedRequestAttributesPatternPattern());
783d95
             register(processor);
783d95
             return processor;
783d95
         }
783d95
diff -up ./java/org/apache/coyote/ajp/LocalStrings.properties.orig ./java/org/apache/coyote/ajp/LocalStrings.properties
783d95
--- ./java/org/apache/coyote/ajp/LocalStrings.properties.orig	2020-03-03 14:24:23.439779733 -0500
783d95
+++ ./java/org/apache/coyote/ajp/LocalStrings.properties	2020-03-03 14:26:13.057505470 -0500
783d95
@@ -14,6 +14,7 @@
783d95
 # limitations under the License.
783d95
 ajpprotocol.endpoint.starterror=Error starting endpoint
783d95
 ajpprotocol.init=Initializing Coyote AJP/1.3 on {0}
783d95
+ajpprotocol.nosecret=The AJP Connector is configured with secretRequired="true" but the secret attribute is either null or "". This combination is not valid.
783d95
 ajpprotocol.start=Starting Coyote AJP/1.3 on {0}
783d95
 ajpprotocol.failedwrite=Socket write failed
783d95
 ajpprotocol.request.register=Error registering request processor in JMX
783d95
diff -up ./test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java.orig ./test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java
783d95
--- ./test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java.orig	2020-03-03 14:24:23.442779726 -0500
783d95
+++ ./test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java	2020-03-03 14:26:28.556466692 -0500
783d95
@@ -30,14 +30,26 @@ import javax.servlet.http.HttpServletReq
783d95
 import javax.servlet.http.HttpServletResponse;
783d95
 
783d95
 import org.junit.Assert;
783d95
+import org.junit.Before;
783d95
 import org.junit.Test;
783d95
 
783d95
 import org.apache.catalina.Context;
783d95
+import org.apache.catalina.connector.Connector;
783d95
 import org.apache.catalina.startup.Tomcat;
783d95
 import org.apache.catalina.startup.TomcatBaseTest;
783d95
 
783d95
 public class TestAbstractAjpProcessor extends TomcatBaseTest {
783d95
 
783d95
+    @Before
783d95
+    @Override
783d95
+    public void setUp() throws Exception {
783d95
+        super.setUp();
783d95
+
783d95
+        Connector c = getTomcatInstance().getConnector();
783d95
+        c.setProperty("secretRequired", "false");
783d95
+        c.setProperty("allowedRequestAttributesPattern", "MYATTRIBUTE.*");
783d95
+    }
783d95
+
783d95
     @Override
783d95
     protected String getProtocol() {
783d95
         /*
783d95
diff -up ./webapps/docs/changelog.xml.orig ./webapps/docs/changelog.xml
783d95
--- ./webapps/docs/changelog.xml.orig	2020-03-03 14:24:23.446779716 -0500
783d95
+++ ./webapps/docs/changelog.xml	2020-03-03 14:41:14.526247697 -0500
783d95
@@ -57,6 +57,25 @@
783d95
   They eventually become mixed with the numbered issues. (I.e., numbered
783d95
   issues do not "pop up" wrt. others).
783d95
 -->
783d95
+<section name="Tomcat 7.0.76-11 (csutherl)">
783d95
+  <subsection name="Coyote">
783d95
+    <changelog>
783d95
+      <add>
783d95
+        Rename the requiredSecret attribute of the AJP/1.3
783d95
+        Connector to secret and add a new attribute
783d95
+        secretRequired that defaults to true. When
783d95
+        secretRequired is true the AJP/1.3 Connector
783d95
+        will not start unless the secret attribute is configured to
783d95
+        a non-null, non-zero length String. (markt)
783d95
+      </add>
783d95
+      <add>
783d95
+        Add a new attribute, allowedRequestAttributesPattern to
783d95
+        the AJP/1.3 Connector. Requests with unreconised attributes will be
783d95
+        blocked with a 403. (markt)
783d95
+      </add>
783d95
+    </changelog>
783d95
+  </subsection>
783d95
+</section>
783d95
 <section name="Tomcat 7.0.76-9 (csutherl)">
783d95
   <subsection name="Catalina">
783d95
     <changelog>
783d95
diff -up ./webapps/docs/config/ajp.xml.orig ./webapps/docs/config/ajp.xml
783d95
--- ./webapps/docs/config/ajp.xml.orig	2020-03-03 14:24:23.448779711 -0500
783d95
+++ ./webapps/docs/config/ajp.xml	2020-03-03 14:39:44.506473588 -0500
783d95
@@ -312,6 +312,25 @@
783d95
       interface.

783d95
     </attribute>
783d95
 
783d95
+    <attribute name="allowedRequestAttributesPattern" required="false">
783d95
+      

The AJP protocol passes some information from the reverse proxy to the

783d95
+      AJP connector using request attributes. These attributes are:

783d95
+      
    783d95
    +        
  • javax.servlet.request.cipher_suite
  • 783d95
    +        
  • javax.servlet.request.key_size
  • 783d95
    +        
  • javax.servlet.request.ssl_session
  • 783d95
    +        
  • javax.servlet.request.X509Certificate
  • 783d95
    +        
  • AJP_LOCAL_ADDR
  • 783d95
    +        
  • AJP_REMOTE_PORT
  • 783d95
    +        
  • AJP_SSL_PROTOCOL
  • 783d95
    +        
  • JK_LB_ACTIVATION
  • 783d95
    +      
    783d95
    +      

    The AJP protocol supports the passing of arbitrary request attributes.

    783d95
    +      Requests containing arbitrary request attributes will be rejected with a
    783d95
    +      403 response unless the entire attribute name matches this regular
    783d95
    +      expression. If not specified, the default value is null.

    783d95
    +    </attribute>
    783d95
    +
    783d95
         <attribute name="bindOnInit" required="false">
    783d95
           

    Controls when the socket used by the connector is bound. By default it

    783d95
           is bound when the connector is initiated and unbound when the connector is
    783d95
    @@ -436,8 +455,18 @@
    783d95
           expected concurrent requests (synchronous and asynchronous).

    783d95
         </attribute>
    783d95
     
    783d95
    -    <attribute name="requiredSecret" required="false">
    783d95
    +    <attribute name="secret" required="false">
    783d95
           

    Only requests from workers with this secret keyword will be accepted.

    783d95
    +      The default value is null. This attrbute must be specified
    783d95
    +      with a non-null, non-zero length value unless
    783d95
    +      secretRequired is explicitly configured to be
    783d95
    +      false.

    783d95
    +    </attribute>
    783d95
    +
    783d95
    +    <attribute name="secretRequired" required="false">
    783d95
    +      

    If this attribute is true, the AJP Connector will only

    783d95
    +      start if the secret attribute is configured with a
    783d95
    +      non-null, non-zero length value. The default value is false.
    783d95
           

    783d95
         </attribute>
    783d95