This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-rampart.git

commit f8dcc7460487a18382320a0a553781e914fafb31
Author: Robert Lazarski <robertlazar...@gmail.com>
AuthorDate: Wed Nov 6 10:41:26 2024 -1000

    RAMPART-234 Allow custom https listeners to populate the client certificate 
chain in the message context
---
 .../java/org/apache/rampart/RampartConstants.java  |   5 +
 .../java/org/apache/rampart/util/RampartUtil.java  | 114 +++++++++++++++++----
 .../resources/org/apache/rampart/errors.properties |   5 +-
 3 files changed, 102 insertions(+), 22 deletions(-)

diff --git 
a/modules/rampart-core/src/main/java/org/apache/rampart/RampartConstants.java 
b/modules/rampart-core/src/main/java/org/apache/rampart/RampartConstants.java
index e280d746..a7741f10 100644
--- 
a/modules/rampart-core/src/main/java/org/apache/rampart/RampartConstants.java
+++ 
b/modules/rampart-core/src/main/java/org/apache/rampart/RampartConstants.java
@@ -5,6 +5,11 @@ public class RampartConstants {
        public static final String TIME_LOG = "org.apache.rampart.TIME";
        public static final String MESSAGE_LOG = "org.apache.rampart.MESSAGE";
        public static final String SEC_FAULT = "SECURITY_VALIDATION_FAILURE";
+        /**
+         * The key under which the HTTPS client certificate, determened by the 
https listener, may
+         * be populated as a property of the message context.
+         */
+        public static final String HTTPS_CLIENT_CERT_KEY = 
"https.client.cert.key";
     public static final String MERLIN_CRYPTO_IMPL = 
"org.apache.ws.security.components.crypto.Merlin";
     public static final String MERLIN_CRYPTO_IMPL_CACHE_KEY = 
"org.apache.ws.security.crypto.merlin.file";
 
diff --git 
a/modules/rampart-core/src/main/java/org/apache/rampart/util/RampartUtil.java 
b/modules/rampart-core/src/main/java/org/apache/rampart/util/RampartUtil.java
index 2ed59869..c09ca1ab 100644
--- 
a/modules/rampart-core/src/main/java/org/apache/rampart/util/RampartUtil.java
+++ 
b/modules/rampart-core/src/main/java/org/apache/rampart/util/RampartUtil.java
@@ -31,11 +31,14 @@ import org.apache.axis2.dataretrieval.DRConstants;
 import org.apache.axis2.dataretrieval.client.MexClient;
 import org.apache.axis2.description.AxisService;
 import org.apache.axis2.description.Parameter;
+import org.apache.axis2.description.TransportInDescription;
+import org.apache.axis2.engine.AxisConfiguration;
 import org.apache.axis2.mex.MexConstants;
 import org.apache.axis2.mex.MexException;
 import org.apache.axis2.mex.om.Metadata;
 import org.apache.axis2.mex.om.MetadataReference;
 import org.apache.axis2.mex.om.MetadataSection;
+import org.apache.axis2.kernel.TransportListener;
 import org.apache.axis2.kernel.http.HTTPConstants;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -58,7 +61,14 @@ import org.apache.rampart.policy.model.CryptoConfig;
 import org.apache.rampart.policy.model.KerberosConfig;
 import org.apache.rampart.policy.model.RampartConfig;
 import org.apache.ws.secpolicy.SPConstants;
-import org.apache.ws.secpolicy.model.*;
+import org.apache.ws.secpolicy.model.HttpsToken;
+import org.apache.ws.secpolicy.model.IssuedToken;
+import org.apache.ws.secpolicy.model.SecureConversationToken;
+import org.apache.ws.secpolicy.model.SupportingToken;
+import org.apache.ws.secpolicy.model.UsernameToken;
+import org.apache.ws.secpolicy.model.Wss10;
+import org.apache.ws.secpolicy.model.Wss11;
+import org.apache.ws.secpolicy.model.X509Token;
 import org.apache.wss4j.dom.message.WSSecBase;
 import org.apache.wss4j.common.crypto.CryptoType;
 import org.apache.wss4j.common.crypto.Crypto;
@@ -1814,29 +1824,91 @@ public class RampartUtil {
         return null;
     }
     
-    public static void validateTransport(RampartMessageData rmd) throws 
RampartException {
-
-        RampartPolicyData rpd = rmd.getPolicyData();
-
-        if (rpd == null) {
-            return;
-        }
-
-        if (rpd.isTransportBinding() && !rmd.isInitiator()) {
-            if (rpd.getTransportToken() instanceof HttpsToken) {
-                String incomingTransport = 
rmd.getMsgContext().getIncomingTransportName();
-                if 
(!incomingTransport.equals(org.apache.axis2.Constants.TRANSPORT_HTTPS)) {
-                    throw new RampartException("invalidTransport",
-                            new String[]{incomingTransport});
+    /**
+     * Validate transport binding policy assertions.
+     * In case an HttpsToken is required by the security policy the method 
will verify that the 
+     * HTTPS transport was used indeed. Furthermore if the assertion requires 
a client certificate
+     * being used, the method will try to obtain the client certificate chain 
first from the 
+     * message context properties directly under the key {@link 
RampartConstants#HTTPS_CLIENT_CERT_KEY}
+     * and, if the property is not available, will try to get the 
HttpsServletRequest from the 
+     * message context properties (populated there by the AxisServlet if axis2 
is running inside a servlet
+     * engine) and retrieve the https client certificate chain from its 
attributes. The client certificate
+     * chain is expected to be available under the 
<code>javax.servlet.request.X509Certificate</code>
+     * attribute of the servlet request. No further trust verification is done 
for the client
+     * certificate - the transport listener should have already verified this.
+     * 
+     * @param messageData
+     * @throws RampartException
+     */
+    public static void validateTransport(RampartMessageData messageData) 
throws RampartException {
+        
+        MessageContext msgContext = messageData.getMsgContext();
+        RampartPolicyData policyData = messageData.getPolicyData();
+        AxisConfiguration axisConf = 
msgContext.getConfigurationContext().getAxisConfiguration();
+        
+        if(policyData != null && policyData.isTransportBinding() && 
!messageData.isInitiator()){
+            if (policyData.getTransportToken() instanceof HttpsToken) {
+                try {
+                    TransportInDescription transportIn = 
msgContext.getTransportIn();
+                    if (transportIn == null) {
+                        transportIn = msgContext.getOptions().getTransportIn();
+                    }
+                    
+                    //maybe the transportIn was not populated by the receiver
+                    if (transportIn == null) {
+                        transportIn = 
axisConf.getTransportIn(msgContext.getIncomingTransportName());
+                    }
+                    
+                    if (transportIn == null) {
+                        throw new RampartException("httpsVerificationFailed");
+                    }
+                    
+                    TransportListener receiver = transportIn.getReceiver();
+                    String incomingEPR = 
receiver.getEPRsForService(msgContext.getAxisService().getName(),
+                                                                          
null)[0].getAddress();
+                    if (incomingEPR == null) {
+                        incomingEPR = msgContext.getIncomingTransportName();
+                    }
+    
+                    if 
(!incomingEPR.startsWith(org.apache.axis2.Constants.TRANSPORT_HTTPS)) {
+                        if (incomingEPR.indexOf(':') > 0) {
+                            incomingEPR = incomingEPR.substring(0, 
incomingEPR.indexOf(':'));
+                        }
+                        throw new RampartException("invalidTransport", new 
String[] { incomingEPR });
+                    }
+                } catch (AxisFault af) {
+                    String incomingTransport = 
msgContext.getIncomingTransportName();
+                    if 
(!incomingTransport.equals(org.apache.axis2.Constants.TRANSPORT_HTTPS)) {
+                        throw new RampartException("invalidTransport", new 
String[] { incomingTransport });
+                    }
                 }
-                if (((HttpsToken) 
rpd.getTransportToken()).isRequireClientCertificate()) {
 
-                    MessageContext messageContext = rmd.getMsgContext();
-                    HttpServletRequest request = ((HttpServletRequest) 
messageContext.getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST));
-                    if (request == null || 
request.getAttribute("javax.servlet.request.X509Certificate") == null) {
-                        throw new RampartException("clientAuthRequired");
+                MessageContext messageContext = messageData.getMsgContext();
+                HttpServletRequest request = ((HttpServletRequest) 
messageContext.getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST));
+                
+                // verify client certificate used
+                // try to obtain the client certificate chain directly from 
the message context
+                // and then from the servlet request
+                HttpsToken token = (HttpsToken)policyData.getTransportToken();
+                if (token.isRequireClientCertificate()) {
+                    Object certificateChainProperty = 
msgContext.getProperty(RampartConstants.HTTPS_CLIENT_CERT_KEY);
+                    if (certificateChainProperty instanceof X509Certificate[]) 
{
+                        // HTTPS client certificate chain found
+                        return;
+                    } else {
+                        Object requestProperty = 
msgContext.getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST);
+                        if (requestProperty instanceof HttpServletRequest) {
+                            Object certificateChain = 
request.getAttribute("javax.servlet.request.X509Certificate"); //$NON-NLS-1$
+                            if (certificateChain instanceof X509Certificate[]) 
{
+                                // HTTPS client certificate chain found
+                                return;
+                            }
+                        }
                     }
-                }
+                    
+                    // HTTPS client certificate chain NOT found
+                    throw new 
RampartException("httpsClientCertValidationFailed");
+                 }
 
             }
         }
diff --git 
a/modules/rampart-core/src/main/resources/org/apache/rampart/errors.properties 
b/modules/rampart-core/src/main/resources/org/apache/rampart/errors.properties
index 034b91d1..fea93359 100644
--- 
a/modules/rampart-core/src/main/resources/org/apache/rampart/errors.properties
+++ 
b/modules/rampart-core/src/main/resources/org/apache/rampart/errors.properties
@@ -101,6 +101,9 @@ bodyNotSigned = Soap Body must be signed
 unexprectedSignature = Unexpected signature
 invalidTransport = Expected transport is "https" but incoming transport found 
: \"{0}\" 
 requiredElementsMissing = Required Elements not found in the incoming message 
: {0}
+httpsVerificationFailed = Unable to verify HTTPS transport usage: incoming 
transport description is unavailable
+httpsClientCertValidationFailed = Unable to verify HTTPS client certificate 
usage: client certificate chain is not available.
+requiredElementsMissing = Required Elements not found in the incoming message 
: {0}
 repeatingNonceValue = Nonce value : {0}, already seen before for user name : 
{1}. Possibly this could be a replay attack.
 invalidNonceLifeTime = Invalid value for nonceLifeTime in rampart 
configuration file.
 invalidIssuerAddress = Invalid value for Issuer
@@ -112,4 +115,4 @@ invalidServicePrincipalNameForm = Invalid 
servicePrincipalNameForm found in Ramp
 noKerberosConfigDefined = No kerberosConfig policy assertion defined in 
rampart config.
 errorInBuildingKereberosToken = Error in building kereberos token.
 cannotLoadKrbTokenDecoderClass = Cannot load Kerberos token decoder class: {0}
-cannotCreateKrbTokenDecoderInstance = Cannot create instance of Kerberos token 
decoder : {0}
\ No newline at end of file
+cannotCreateKrbTokenDecoderInstance = Cannot create instance of Kerberos token 
decoder : {0}

Reply via email to