From 64fa5b99442589ef0bf2a7fcd71ad2bc68b35fad Mon Sep 17 00:00:00 2001 From: Mark Thomas Date: Tue, 21 Jan 2020 15:04:12 +0000 Subject: [PATCH] Add new AJP attribute allowedArbitraryRequestAttributes Requests with unrecognised attributes will be blocked with a 403 --- .../coyote/ajp/AbstractAjpProtocol.java | 13 +++++++ java/org/apache/coyote/ajp/AjpProcessor.java | 36 ++++++++++++++++++- webapps/docs/config/ajp.xml | 19 ++++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) diff --git a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java index 81da7da6d1..a2f5e2844a 100644 --- a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java +++ b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java @@ -17,6 +17,7 @@ package org.apache.coyote.ajp; import java.net.InetAddress; +import java.util.regex.Pattern; import org.apache.coyote.AbstractProtocol; import org.apache.coyote.Processor; @@ -188,6 +189,18 @@ public boolean getSecretRequired() { } + private Pattern allowedArbitraryRequestAttributesPattern; + public void setAllowedArbitraryRequestAttributes(String allowedArbitraryRequestAttributes) { + this.allowedArbitraryRequestAttributesPattern = Pattern.compile(allowedArbitraryRequestAttributes); + } + public String getAllowedArbitraryRequestAttributes() { + return allowedArbitraryRequestAttributesPattern.pattern(); + } + protected Pattern getAllowedArbitraryRequestAttributesPattern() { + return allowedArbitraryRequestAttributesPattern; + } + + /** * AJP packet size. */ diff --git a/java/org/apache/coyote/ajp/AjpProcessor.java b/java/org/apache/coyote/ajp/AjpProcessor.java index d466de2c09..f3d783f546 100644 --- a/java/org/apache/coyote/ajp/AjpProcessor.java +++ b/java/org/apache/coyote/ajp/AjpProcessor.java @@ -25,6 +25,11 @@ 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.regex.Matcher; +import java.util.regex.Pattern; import javax.servlet.http.HttpServletResponse; @@ -78,6 +83,9 @@ private static final byte[] pongMessageArray; + private static final Set javaxAttributes; + + static { // Allocate the end message array AjpMessage endMessage = new AjpMessage(16); @@ -118,6 +126,14 @@ 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); } @@ -728,8 +744,26 @@ private void prepareRequest() { } } 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. + Pattern pattern = protocol.getAllowedArbitraryRequestAttributesPattern(); + if (pattern == null) { + response.setStatus(403); + setErrorState(ErrorState.CLOSE_CLEAN, null); + } else { + Matcher m = pattern.matcher(n); + if (m.matches()) { + request.setAttribute(n, v); + } else { + response.setStatus(403); + setErrorState(ErrorState.CLOSE_CLEAN, null); + } + } } break; diff --git a/webapps/docs/config/ajp.xml b/webapps/docs/config/ajp.xml index 3999a13e66..69348a18e2 100644 --- a/webapps/docs/config/ajp.xml +++ b/webapps/docs/config/ajp.xml @@ -311,6 +311,25 @@ port. By default, the loopback address will be used.

+ +

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