All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.rampart.RampartEngine Maven / Gradle / Ivy

There is a newer version: 1.7.1
Show newest version
/*
 * Copyright 2004,2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.rampart;

import org.apache.axiom.om.OMElement;
import org.apache.axiom.soap.SOAP11Constants;
import org.apache.axiom.soap.SOAP12Constants;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFault;
import org.apache.axiom.soap.SOAPFaultCode;
import org.apache.axiom.soap.SOAPFaultSubCode;
import org.apache.axiom.soap.SOAPFaultValue;
import org.apache.axiom.soap.SOAPHeader;
import org.apache.axiom.soap.SOAPHeaderBlock;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.rahas.RahasConstants;
import org.apache.rahas.Token;
import org.apache.rahas.TokenStorage;
import org.apache.rahas.TrustUtil;
import org.apache.rahas.impl.util.SAML2KeyInfo;
import org.apache.rahas.impl.util.SAML2Utils;
import org.apache.rampart.policy.RampartPolicyData;
import org.apache.rampart.util.Axis2Util;
import org.apache.rampart.util.RampartUtil;
import org.apache.ws.secpolicy.WSSPolicyException;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.WSSecurityEngine;
import org.apache.ws.security.WSSecurityEngineResult;
import org.apache.ws.security.WSSecurityException;
import org.apache.ws.security.WSUsernameTokenPrincipal;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.saml.SAMLKeyInfo;
import org.apache.ws.security.saml.SAMLUtil;
import org.opensaml.SAMLAssertion;
import org.opensaml.saml2.core.Assertion;
import org.opensaml.saml2.core.Conditions;
import org.opensaml.saml2.core.Subject;
import org.opensaml.saml2.core.SubjectConfirmationData;

import javax.xml.namespace.QName;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Vector;

public class RampartEngine {

	private static Log log = LogFactory.getLog(RampartEngine.class);
	private static Log tlog = LogFactory.getLog(RampartConstants.TIME_LOG);
    private static ServiceNonceCache serviceNonceCache = new ServiceNonceCache();

	public Vector process(MessageContext msgCtx) throws WSSPolicyException,
	RampartException, WSSecurityException, AxisFault {

		boolean doDebug = log.isDebugEnabled();
		boolean dotDebug = tlog.isDebugEnabled();
		
		log.debug("Enter process(MessageContext msgCtx)");

		RampartMessageData rmd = new RampartMessageData(msgCtx, false);

		RampartPolicyData rpd = rmd.getPolicyData();
		
		msgCtx.setProperty(RampartMessageData.RAMPART_POLICY_DATA, rpd);

        RampartUtil.validateTransport(rmd);

        // If there is no policy information return immediately
        if (rpd == null) {
           return null;
        }


        //TODO these checks have to be done before the convertion to avoid unnecessary convertion to LLOM -> DOOM
        // If the message is a security fault or no security
        // header required by the policy
        if(isSecurityFault(rmd) || !RampartUtil.isSecHeaderRequired(rpd,rmd.isInitiator(),true)) {
			SOAPEnvelope env = Axis2Util.getSOAPEnvelopeFromDOMDocument(rmd.getDocument(), true);

			//Convert back to llom since the inflow cannot use llom
			msgCtx.setEnvelope(env);
			Axis2Util.useDOOM(false);
            log.debug("Return process MessageContext msgCtx)");
            return null;
		}


		Vector results = null;

		WSSecurityEngine engine = new WSSecurityEngine();

		ValidatorData data = new ValidatorData(rmd);

		SOAPHeader header = rmd.getMsgContext().getEnvelope().getHeader();
		if(header == null) {
		    throw new RampartException("missingSOAPHeader");
		}
		
                ArrayList headerBlocks = header.getHeaderBlocksWithNSURI(WSConstants.WSSE_NS);
		SOAPHeaderBlock secHeader = null;
		//Issue is axiom - a returned collection must not be null
		if(headerBlocks != null) {
    		Iterator headerBlocksIterator = headerBlocks.iterator();
    		while (headerBlocksIterator.hasNext()) {
    			SOAPHeaderBlock elem = (SOAPHeaderBlock) headerBlocksIterator.next();
    			if(elem.getLocalName().equals(WSConstants.WSSE_LN)) {
    				secHeader = elem;
    				break;
    			}
    		}
		}
		
		if(secHeader == null) {
		    throw new RampartException("missingSecurityHeader");
		}
		
		long t0=0, t1=0, t2=0, t3=0;
		if(dotDebug){
			t0 = System.currentTimeMillis();
		}

		String actorValue = secHeader.getAttributeValue(new QName(rmd
				.getSoapConstants().getEnvelopeURI(), "actor"));

		Crypto signatureCrypto = RampartUtil.getSignatureCrypto(rpd.getRampartConfig(), 
        		msgCtx.getAxisService().getClassLoader());
        TokenCallbackHandler tokenCallbackHandler = new TokenCallbackHandler(rmd.getTokenStorage(), RampartUtil.getPasswordCB(rmd));
        if(rpd.isSymmetricBinding()) {
			//Here we have to create the CB handler to get the tokens from the 
			//token storage
			log.debug("Processing security header using SymetricBinding");
			results = engine.processSecurityHeader(rmd.getDocument(),
					actorValue, 
					tokenCallbackHandler,
					signatureCrypto, 
					        RampartUtil.getEncryptionCrypto(rpd.getRampartConfig(), 
					                msgCtx.getAxisService().getClassLoader()));

            // Remove encryption tokens if this is the initiator and if initiator is receiving a message

            if (rmd.isInitiator() && (msgCtx.getFLOW() == MessageContext.IN_FLOW ||
                    msgCtx.getFLOW() == MessageContext.IN_FAULT_FLOW)) {
                tokenCallbackHandler.removeEncryptedToken();
            }

		} else {
			log.debug("Processing security header in normal path");
			results = engine.processSecurityHeader(rmd.getDocument(),
					actorValue, 
					tokenCallbackHandler,
					signatureCrypto, 
							RampartUtil.getEncryptionCrypto(rpd.getRampartConfig(), 
									msgCtx.getAxisService().getClassLoader()));
		}

		if(dotDebug){
			t1 = System.currentTimeMillis();
		}

                //Store symm tokens
                //Pick the first SAML token
                //TODO : This is a hack , MUST FIX
                //get the sec context id from the req msg ctx 
		
		//Store username in MessageContext property

        for (int j = 0; j < results.size(); j++) {
            WSSecurityEngineResult wser = (WSSecurityEngineResult) results.get(j);
            final Integer actInt =
                    (Integer) wser.get(WSSecurityEngineResult.TAG_ACTION);
            if (WSConstants.ST_UNSIGNED == actInt.intValue()) {

                // If this is a SAML2.0 assertion
                if (wser.get(WSSecurityEngineResult.TAG_SAML_ASSERTION) instanceof Assertion) {
                    final Assertion assertion = (Assertion) wser.get(
                            WSSecurityEngineResult.TAG_SAML_ASSERTION);

                    // if the subject confirmation method is Bearer, do not try to get the KeyInfo
                    if(TrustUtil.getSAML2SubjectConfirmationMethod(assertion).equals(
                            RahasConstants.SAML20_SUBJECT_CONFIRMATION_BEARER)){
                        break;
                    }

                    String id = assertion.getID();
                    Subject subject = assertion.getSubject();

                    Date dateOfCreation = null;
                    Date dateOfExpiration = null;

                    //Read the validity period from the 'Conditions' element, else read it from SC Data
                    if (assertion.getConditions() != null) {
                        Conditions conditions = assertion.getConditions();
                        if (conditions.getNotBefore() != null) {
                            dateOfCreation = conditions.getNotBefore().toDate();
                        }
                        if (conditions.getNotOnOrAfter() != null) {
                            dateOfExpiration = conditions.getNotOnOrAfter().toDate();
                        }
                    } else {
                        SubjectConfirmationData scData = subject.getSubjectConfirmations()
                                .get(0).getSubjectConfirmationData();
                        if (scData.getNotBefore() != null) {
                            dateOfCreation = scData.getNotBefore().toDate();
                        }
                        if (scData.getNotOnOrAfter() != null) {
                            dateOfExpiration = scData.getNotOnOrAfter().toDate();
                        }
                    }

                    // TODO : SAML2KeyInfo element needs to be moved to WSS4J.
                    SAML2KeyInfo saml2KeyInfo = SAML2Utils.
                            getSAML2KeyInfo(assertion, signatureCrypto, tokenCallbackHandler);

                    //Store the token
                    try {
                        TokenStorage store = rmd.getTokenStorage();
                        if (store.getToken(id) == null) {
                            Token token = new Token(id, (OMElement) SAML2Utils.getElementFromAssertion(assertion), dateOfCreation, dateOfExpiration);
                            token.setSecret(saml2KeyInfo.getSecret());
                            store.add(token);
                        }
                    } catch (Exception e) {
                        throw new RampartException(
                                "errorInAddingTokenIntoStore", e);
                    }

                }
                //if this is a SAML1.1 assertion
                else {
                    final SAMLAssertion assertion = ((SAMLAssertion) wser.get(
                            WSSecurityEngineResult.TAG_SAML_ASSERTION));

                    // if the subject confirmation method is Bearer, do not try to get the KeyInfo
                    if(RahasConstants.SAML11_SUBJECT_CONFIRMATION_BEARER.equals(
                            TrustUtil.getSAML11SubjectConfirmationMethod(assertion))){
                        break;
                    }

                    String id = assertion.getId();
                    Date created = assertion.getNotBefore();
                    Date expires = assertion.getNotOnOrAfter();
                    SAMLKeyInfo samlKi = SAMLUtil.getSAMLKeyInfo(assertion,
                            signatureCrypto, tokenCallbackHandler);
                    try {
                        TokenStorage store = rmd.getTokenStorage();
                        if (store.getToken(id) == null) {
                            Token token = new Token(id, (OMElement) assertion.toDOM(), created, expires);
                            token.setSecret(samlKi.getSecret());
                            store.add(token);
                        }
                    } catch (Exception e) {
                        throw new RampartException(
                                "errorInAddingTokenIntoStore", e);
                    }

                }
            } else if (WSConstants.UT == actInt.intValue()) {

		        WSUsernameTokenPrincipal userNameTokenPrincipal = (WSUsernameTokenPrincipal)wser.get(WSSecurityEngineResult.TAG_PRINCIPAL);

                String username = userNameTokenPrincipal.getName();
                msgCtx.setProperty(RampartMessageData.USERNAME, username);
                
                if (userNameTokenPrincipal.getNonce() != null) {
                    // Check whether this is a replay attack. To verify that we need to check whether nonce value
                    // is a repeating one
                    int nonceLifeTimeInSeconds = 0;

                    if (rpd.getRampartConfig() != null) {
                        
                        String stringLifeTime = rpd.getRampartConfig().getNonceLifeTime();

                        try {
                            nonceLifeTimeInSeconds = Integer.parseInt(stringLifeTime);

                        } catch (NumberFormatException e) {
                            log.error("Invalid value for nonceLifeTime in rampart configuration file.", e);
                            throw new RampartException(
                                        "invalidNonceLifeTime", e);

                        }
                    }

                    String serviceEndpointName = msgCtx.getAxisService().getEndpointName();

                    boolean valueRepeating = serviceNonceCache.isNonceRepeatingForService(serviceEndpointName, username, userNameTokenPrincipal.getNonce());

                    if (valueRepeating){
                        throw new RampartException("repeatingNonceValue", new Object[]{ userNameTokenPrincipal.getNonce(), username} );
                    }

                    serviceNonceCache.addNonceForService(serviceEndpointName, username, userNameTokenPrincipal.getNonce(), nonceLifeTimeInSeconds);
                }
            } else if (WSConstants.SIGN == actInt.intValue()) {
                X509Certificate cert = (X509Certificate) wser.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
                msgCtx.setProperty(RampartMessageData.X509_CERT, cert);
            }

        }

		SOAPEnvelope env = Axis2Util.getSOAPEnvelopeFromDOMDocument(rmd.getDocument(), true);

		if(dotDebug){
			t2 = System.currentTimeMillis();
		}

		//Convert back to llom since the inflow cannot use DOOM
		msgCtx.setEnvelope(env);
		Axis2Util.useDOOM(false);
				
		PolicyValidatorCallbackHandler validator = RampartUtil.getPolicyValidatorCB(msgCtx, rpd);
		
		validator.validate(data, results);

		if(dotDebug){
			t3 = System.currentTimeMillis();
			tlog.debug("processHeader by WSSecurityEngine took : " + (t1 - t0) +
					", DOOM conversion took :" + (t2 - t1) +
					", PolicyBasedResultsValidattor took " + (t3 - t2));
		}

		log.debug("Return process(MessageContext msgCtx)");
		return results;
	}
	
	// Check whether this a soap fault because of failure in processing the security header 
	//and if so, we don't expect the security header
	//
	//

	
	private boolean isSecurityFault(RampartMessageData rmd) {

		SOAPEnvelope soapEnvelope = rmd.getMsgContext().getEnvelope();
		SOAPFault soapFault = soapEnvelope.getBody().getFault();

		// This is not a soap fault
		if (soapFault == null) {
			return false;
		}

		String soapVersionURI = rmd.getMsgContext().getEnvelope().getNamespace().getNamespaceURI();
		SOAPFaultCode faultCode = soapFault.getCode();
		if(faultCode == null){
			//If no fault code is given, then it can't be security fault
			return false;
		}
		
		if (soapVersionURI.equals(SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI)) {
			// This is a fault processing the security header
			if (faultCode.getTextAsQName().getNamespaceURI().equals(WSConstants.WSSE_NS)) {
				return true;
			}
		} else if (soapVersionURI.equals(SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI)) {
			// TODO AXIOM API returns only one fault sub code, there can be many
			SOAPFaultSubCode faultSubCode = faultCode.getSubCode();
			if (faultSubCode != null) {
				SOAPFaultValue faultSubCodeValue = faultSubCode.getValue();

				// This is a fault processing the security header
				if (faultSubCodeValue != null && faultSubCodeValue.getTextAsQName().
						getNamespaceURI().equals(WSConstants.WSSE_NS)) {
					return true;
				}
			}
		}

		return false;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy