143 lines
5.8 KiB
Diff
143 lines
5.8 KiB
Diff
From 64fa5b99442589ef0bf2a7fcd71ad2bc68b35fad Mon Sep 17 00:00:00 2001
|
|
From: Mark Thomas <markt@apache.org>
|
|
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<String> 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<String> 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.</p>
|
|
</attribute>
|
|
|
|
+ <attribute name="allowedArbitraryRequestAttributes" required="false">
|
|
+ <p>The AJP protocol passes some information from the reverse proxy to the
|
|
+ AJP connector using request attributes. These attributes are:</p>
|
|
+ <ul>
|
|
+ <li>javax.servlet.request.cipher_suite</li>
|
|
+ <li>javax.servlet.request.key_size</li>
|
|
+ <li>javax.servlet.request.ssl_session</li>
|
|
+ <li>javax.servlet.request.X509Certificate</li>
|
|
+ <li>AJP_LOCAL_ADDR</li>
|
|
+ <li>AJP_REMOTE_PORT</li>
|
|
+ <li>AJP_SSL_PROTOCOL</li>
|
|
+ <li>JK_LB_ACTIVATION</li>
|
|
+ </ul>
|
|
+ <p>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 <code>null</code>.</p>
|
|
+ </attribute>
|
|
+
|
|
<attribute name="bindOnInit" required="false">
|
|
<p>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
|