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

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

637de5
     </attribute>
637de5
 
637de5
+    <attribute name="allowedRequestAttributesPattern" required="false">
637de5
+      

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

637de5
+      AJP connector using request attributes. These attributes are:

637de5
+      
    637de5
    +        
  • javax.servlet.request.cipher_suite
  • 637de5
    +        
  • javax.servlet.request.key_size
  • 637de5
    +        
  • javax.servlet.request.ssl_session
  • 637de5
    +        
  • javax.servlet.request.X509Certificate
  • 637de5
    +        
  • AJP_LOCAL_ADDR
  • 637de5
    +        
  • AJP_REMOTE_PORT
  • 637de5
    +        
  • AJP_SSL_PROTOCOL
  • 637de5
    +        
  • JK_LB_ACTIVATION
  • 637de5
    +      
    637de5
    +      

    The AJP protocol supports the passing of arbitrary request attributes.

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

    637de5
    +    </attribute>
    637de5
    +
    637de5
         <attribute name="bindOnInit" required="false">
    637de5
           

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

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

    637de5
         </attribute>
    637de5
     
    637de5
    -    <attribute name="requiredSecret" required="false">
    637de5
    +    <attribute name="secret" required="false">
    637de5
           

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

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

    637de5
    +    </attribute>
    637de5
    +
    637de5
    +    <attribute name="secretRequired" required="false">
    637de5
    +      

    If this attribute is true, the AJP Connector will only

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

    637de5
         </attribute>
    637de5