/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.cxf.ws.security.wss4j; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import javax.xml.soap.SOAPException; import javax.xml.stream.XMLStreamException; import org.w3c.dom.Element; import org.apache.cxf.binding.soap.SoapMessage; import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.message.MessageUtils; import org.apache.cxf.rt.security.utils.SecurityUtils; import org.apache.cxf.ws.policy.AssertionInfo; import org.apache.cxf.ws.policy.AssertionInfoMap; import org.apache.cxf.ws.security.SecurityConstants; import org.apache.cxf.ws.security.policy.PolicyUtils; import org.apache.cxf.ws.security.wss4j.policyvalidators.PolicyValidatorParameters; import org.apache.cxf.ws.security.wss4j.policyvalidators.SecurityPolicyValidator; import org.apache.cxf.ws.security.wss4j.policyvalidators.ValidatorUtils; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.PasswordEncryptor; import org.apache.wss4j.common.ext.WSSecurityException; import org.apache.wss4j.dom.WSConstants; import org.apache.wss4j.dom.WSDataRef; import org.apache.wss4j.dom.engine.WSSecurityEngineResult; import org.apache.wss4j.dom.handler.RequestData; import org.apache.wss4j.dom.handler.WSHandlerConstants; import org.apache.wss4j.dom.handler.WSHandlerResult; import org.apache.wss4j.dom.message.token.Timestamp; import org.apache.wss4j.policy.SP12Constants; import org.apache.wss4j.policy.SP13Constants; import org.apache.wss4j.policy.SPConstants; import org.apache.wss4j.policy.model.AlgorithmSuite; import org.apache.wss4j.policy.model.UsernameToken; import org.apache.wss4j.policy.model.UsernameToken.PasswordType; import org.apache.wss4j.policy.model.Wss11; /** * */ public class PolicyBasedWSS4JInInterceptor extends WSS4JInInterceptor { /** * */ public PolicyBasedWSS4JInInterceptor() { super(true); } public void handleMessage(SoapMessage msg) throws Fault { AssertionInfoMap aim = msg.get(AssertionInfoMap.class); boolean enableStax = MessageUtils.isTrue(msg.getContextualProperty(SecurityConstants.ENABLE_STREAMING_SECURITY)); if (aim != null && !enableStax) { super.handleMessage(msg); } } private void handleWSS11(AssertionInfoMap aim, SoapMessage message) { if (isRequestor(message)) { message.put(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, "false"); Collection<AssertionInfo> ais = PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.WSS11); if (!ais.isEmpty()) { for (AssertionInfo ai : ais) { Wss11 wss11 = (Wss11)ai.getAssertion(); if (wss11.isRequireSignatureConfirmation()) { message.put(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, "true"); break; } } } } } private String addToAction(String action, String val, boolean pre) { if (action.contains(val)) { return action; } if (pre) { return val + " " + action; } return action + " " + val; } private String checkAsymmetricBinding( AssertionInfoMap aim, String action, SoapMessage message, RequestData data ) throws WSSecurityException { AssertionInfo ai = PolicyUtils.getFirstAssertionByLocalname(aim, SPConstants.ASYMMETRIC_BINDING); if (ai == null) { return action; } action = addToAction(action, "Signature", true); action = addToAction(action, "Encrypt", true); Object s = SecurityUtils.getSecurityPropertyValue(SecurityConstants.SIGNATURE_CRYPTO, message); if (s == null) { s = SecurityUtils.getSecurityPropertyValue(SecurityConstants.SIGNATURE_PROPERTIES, message); } Object e = SecurityUtils.getSecurityPropertyValue(SecurityConstants.ENCRYPT_CRYPTO, message); if (e == null) { e = SecurityUtils.getSecurityPropertyValue(SecurityConstants.ENCRYPT_PROPERTIES, message); } Crypto encrCrypto = getEncryptionCrypto(e, message, data); Crypto signCrypto = null; if (e != null && e.equals(s)) { signCrypto = encrCrypto; } else { signCrypto = getSignatureCrypto(s, message, data); } final String signCryptoRefId = signCrypto != null ? "RefId-" + signCrypto.hashCode() : null; if (signCrypto != null) { message.put(WSHandlerConstants.DEC_PROP_REF_ID, signCryptoRefId); message.put(signCryptoRefId, signCrypto); } if (encrCrypto != null) { final String encCryptoRefId = "RefId-" + encrCrypto.hashCode(); message.put(WSHandlerConstants.SIG_VER_PROP_REF_ID, encCryptoRefId); message.put(encCryptoRefId, (Crypto)encrCrypto); } else if (signCrypto != null) { message.put(WSHandlerConstants.SIG_VER_PROP_REF_ID, signCryptoRefId); message.put(signCryptoRefId, (Crypto)signCrypto); } return action; } private String checkDefaultBinding( AssertionInfoMap aim, String action, SoapMessage message, RequestData data ) throws WSSecurityException { action = addToAction(action, "Signature", true); action = addToAction(action, "Encrypt", true); Object s = SecurityUtils.getSecurityPropertyValue(SecurityConstants.SIGNATURE_CRYPTO, message); if (s == null) { s = SecurityUtils.getSecurityPropertyValue(SecurityConstants.SIGNATURE_PROPERTIES, message); } Object e = SecurityUtils.getSecurityPropertyValue(SecurityConstants.ENCRYPT_CRYPTO, message); if (e == null) { e = SecurityUtils.getSecurityPropertyValue(SecurityConstants.ENCRYPT_PROPERTIES, message); } Crypto encrCrypto = getEncryptionCrypto(e, message, data); Crypto signCrypto = null; if (e != null && e.equals(s)) { signCrypto = encrCrypto; } else { signCrypto = getSignatureCrypto(s, message, data); } final String signCryptoRefId = signCrypto != null ? "RefId-" + signCrypto.hashCode() : null; if (signCrypto != null) { message.put(WSHandlerConstants.DEC_PROP_REF_ID, signCryptoRefId); message.put(signCryptoRefId, signCrypto); } if (encrCrypto != null) { final String encCryptoRefId = "RefId-" + encrCrypto.hashCode(); message.put(WSHandlerConstants.SIG_VER_PROP_REF_ID, encCryptoRefId); message.put(encCryptoRefId, (Crypto)encrCrypto); } else if (signCrypto != null) { message.put(WSHandlerConstants.SIG_VER_PROP_REF_ID, signCryptoRefId); message.put(signCryptoRefId, (Crypto)signCrypto); } return action; } /** * Is a Nonce Cache required, i.e. are we expecting a UsernameToken */ @Override protected boolean isNonceCacheRequired(List<Integer> actions, SoapMessage msg) { AssertionInfoMap aim = msg.get(AssertionInfoMap.class); if (aim != null) { AssertionInfo ai = PolicyUtils.getFirstAssertionByLocalname(aim, SPConstants.USERNAME_TOKEN); if (ai != null) { return true; } } return false; } /** * Is a Timestamp cache required, i.e. are we expecting a Timestamp */ @Override protected boolean isTimestampCacheRequired(List<Integer> actions, SoapMessage msg) { AssertionInfoMap aim = msg.get(AssertionInfoMap.class); if (aim != null) { AssertionInfo ai = PolicyUtils.getFirstAssertionByLocalname(aim, SPConstants.INCLUDE_TIMESTAMP); if (ai != null) { return true; } } return false; } /** * Is a SAML Cache required, i.e. are we expecting a SAML Token */ @Override protected boolean isSamlCacheRequired(List<Integer> actions, SoapMessage msg) { AssertionInfoMap aim = msg.get(AssertionInfoMap.class); if (aim != null) { AssertionInfo ai = PolicyUtils.getFirstAssertionByLocalname(aim, SPConstants.SAML_TOKEN); if (ai != null) { return true; } } return false; } private void checkUsernameToken( AssertionInfoMap aim, SoapMessage message ) throws WSSecurityException { Collection<AssertionInfo> ais = PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.USERNAME_TOKEN); if (!ais.isEmpty()) { for (AssertionInfo ai : ais) { UsernameToken policy = (UsernameToken)ai.getAssertion(); if (policy.getPasswordType() == PasswordType.NoPassword) { message.put(WSHandlerConstants.ALLOW_USERNAMETOKEN_NOPASSWORD, "true"); } } } } private String checkSymmetricBinding( AssertionInfoMap aim, String action, SoapMessage message, RequestData data ) throws WSSecurityException { AssertionInfo ai = PolicyUtils.getFirstAssertionByLocalname(aim, SPConstants.SYMMETRIC_BINDING); if (ai == null) { return action; } action = addToAction(action, "Signature", true); action = addToAction(action, "Encrypt", true); Object s = SecurityUtils.getSecurityPropertyValue(SecurityConstants.SIGNATURE_CRYPTO, message); if (s == null) { s = SecurityUtils.getSecurityPropertyValue(SecurityConstants.SIGNATURE_PROPERTIES, message); } Object e = SecurityUtils.getSecurityPropertyValue(SecurityConstants.ENCRYPT_CRYPTO, message); if (e == null) { e = SecurityUtils.getSecurityPropertyValue(SecurityConstants.ENCRYPT_PROPERTIES, message); } Crypto encrCrypto = getEncryptionCrypto(e, message, data); Crypto signCrypto = null; if (e != null && e.equals(s)) { signCrypto = encrCrypto; } else { signCrypto = getSignatureCrypto(s, message, data); } if (isRequestor(message)) { Crypto crypto = encrCrypto; if (crypto == null) { crypto = signCrypto; } if (crypto != null) { final String refId = "RefId-" + crypto.hashCode(); message.put(WSHandlerConstants.SIG_VER_PROP_REF_ID, refId); message.put(refId, crypto); } crypto = signCrypto; if (crypto == null) { crypto = encrCrypto; } if (crypto != null) { final String refId = "RefId-" + crypto.hashCode(); message.put(WSHandlerConstants.DEC_PROP_REF_ID, refId); message.put(refId, crypto); } } else { Crypto crypto = signCrypto; if (crypto == null) { crypto = encrCrypto; } if (crypto != null) { final String refId = "RefId-" + crypto.hashCode(); message.put(WSHandlerConstants.SIG_VER_PROP_REF_ID, refId); message.put(refId, crypto); } crypto = encrCrypto; if (crypto == null) { crypto = signCrypto; } if (crypto != null) { final String refId = "RefId-" + crypto.hashCode(); message.put(WSHandlerConstants.DEC_PROP_REF_ID, refId); message.put(refId, crypto); } } return action; } private Crypto getEncryptionCrypto(Object e, SoapMessage message, RequestData requestData) throws WSSecurityException { PasswordEncryptor passwordEncryptor = getPasswordEncryptor(message, requestData); return WSS4JUtils.getEncryptionCrypto(e, message, passwordEncryptor); } private PasswordEncryptor getPasswordEncryptor(SoapMessage soapMessage, RequestData requestData) { PasswordEncryptor passwordEncryptor = (PasswordEncryptor)soapMessage.getContextualProperty( SecurityConstants.PASSWORD_ENCRYPTOR_INSTANCE ); if (passwordEncryptor != null) { return passwordEncryptor; } return super.getPasswordEncryptor(requestData); } private Crypto getSignatureCrypto(Object s, SoapMessage message, RequestData requestData) throws WSSecurityException { PasswordEncryptor passwordEncryptor = getPasswordEncryptor(message, requestData); return WSS4JUtils.getSignatureCrypto(s, message, passwordEncryptor); } /** * Set a WSS4J AlgorithmSuite object on the RequestData context, to restrict the * algorithms that are allowed for encryption, signature, etc. */ protected void setAlgorithmSuites(SoapMessage message, RequestData data) throws WSSecurityException { AlgorithmSuiteTranslater translater = new AlgorithmSuiteTranslater(); translater.translateAlgorithmSuites(message.get(AssertionInfoMap.class), data); // Allow for setting non-standard signature algorithms boolean asymmAlgSet = false; String asymSignatureAlgorithm = (String)message.getContextualProperty(SecurityConstants.ASYMMETRIC_SIGNATURE_ALGORITHM); if (asymSignatureAlgorithm != null && data.getAlgorithmSuite() != null) { data.getAlgorithmSuite().getSignatureMethods().clear(); data.getAlgorithmSuite().getSignatureMethods().add(asymSignatureAlgorithm); asymmAlgSet = true; } String symSignatureAlgorithm = (String)message.getContextualProperty(SecurityConstants.SYMMETRIC_SIGNATURE_ALGORITHM); if (symSignatureAlgorithm != null && data.getAlgorithmSuite() != null) { if (!asymmAlgSet) { data.getAlgorithmSuite().getSignatureMethods().clear(); } data.getAlgorithmSuite().getSignatureMethods().add(symSignatureAlgorithm); } } protected void computeAction(SoapMessage message, RequestData data) throws WSSecurityException { String action = getString(WSHandlerConstants.ACTION, message); if (action == null) { action = ""; } AssertionInfoMap aim = message.get(AssertionInfoMap.class); if (aim != null) { //things that DO impact setup handleWSS11(aim, message); action = checkAsymmetricBinding(aim, action, message, data); action = checkSymmetricBinding(aim, action, message, data); Collection<AssertionInfo> ais = aim.get(SP12Constants.TRANSPORT_BINDING); if ("".equals(action) || (ais != null && !ais.isEmpty())) { action = checkDefaultBinding(aim, action, message, data); } // Allow for setting non-standard asymmetric signature algorithms String asymSignatureAlgorithm = (String)message.getContextualProperty(SecurityConstants.ASYMMETRIC_SIGNATURE_ALGORITHM); String symSignatureAlgorithm = (String)message.getContextualProperty(SecurityConstants.SYMMETRIC_SIGNATURE_ALGORITHM); if (asymSignatureAlgorithm != null || symSignatureAlgorithm != null) { Collection<AssertionInfo> algorithmSuites = PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.ALGORITHM_SUITE); if (algorithmSuites != null && !algorithmSuites.isEmpty()) { for (AssertionInfo algorithmSuite : algorithmSuites) { AlgorithmSuite algSuite = (AlgorithmSuite)algorithmSuite.getAssertion(); if (asymSignatureAlgorithm != null) { algSuite.setAsymmetricSignature(asymSignatureAlgorithm); } if (symSignatureAlgorithm != null) { algSuite.setSymmetricSignature(symSignatureAlgorithm); } } } } checkUsernameToken(aim, message); // stuff we can default to asserted and un-assert if a condition isn't met PolicyUtils.assertPolicy(aim, SPConstants.KEY_VALUE_TOKEN); PolicyUtils.assertPolicy(aim, SPConstants.RSA_KEY_VALUE); // WSS10 ais = PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.WSS10); if (!ais.isEmpty()) { for (AssertionInfo ai : ais) { ai.setAsserted(true); } PolicyUtils.assertPolicy(aim, SPConstants.MUST_SUPPORT_REF_KEY_IDENTIFIER); PolicyUtils.assertPolicy(aim, SPConstants.MUST_SUPPORT_REF_ISSUER_SERIAL); PolicyUtils.assertPolicy(aim, SPConstants.MUST_SUPPORT_REF_EXTERNAL_URI); PolicyUtils.assertPolicy(aim, SPConstants.MUST_SUPPORT_REF_EMBEDDED_TOKEN); } // Trust 1.0 ais = PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.TRUST_10); boolean trust10Asserted = false; if (!ais.isEmpty()) { for (AssertionInfo ai : ais) { ai.setAsserted(true); } PolicyUtils.assertPolicy(aim, SPConstants.MUST_SUPPORT_CLIENT_CHALLENGE); PolicyUtils.assertPolicy(aim, SPConstants.MUST_SUPPORT_SERVER_CHALLENGE); PolicyUtils.assertPolicy(aim, SPConstants.REQUIRE_CLIENT_ENTROPY); PolicyUtils.assertPolicy(aim, SPConstants.REQUIRE_SERVER_ENTROPY); PolicyUtils.assertPolicy(aim, SPConstants.MUST_SUPPORT_ISSUED_TOKENS); trust10Asserted = true; } // Trust 1.3 ais = PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.TRUST_13); if (!ais.isEmpty()) { for (AssertionInfo ai : ais) { ai.setAsserted(true); } PolicyUtils.assertPolicy(aim, SP12Constants.REQUIRE_REQUEST_SECURITY_TOKEN_COLLECTION); PolicyUtils.assertPolicy(aim, SP12Constants.REQUIRE_APPLIES_TO); PolicyUtils.assertPolicy(aim, SP13Constants.SCOPE_POLICY_15); PolicyUtils.assertPolicy(aim, SP13Constants.MUST_SUPPORT_INTERACTIVE_CHALLENGE); if (!trust10Asserted) { PolicyUtils.assertPolicy(aim, SPConstants.MUST_SUPPORT_CLIENT_CHALLENGE); PolicyUtils.assertPolicy(aim, SPConstants.MUST_SUPPORT_SERVER_CHALLENGE); PolicyUtils.assertPolicy(aim, SPConstants.REQUIRE_CLIENT_ENTROPY); PolicyUtils.assertPolicy(aim, SPConstants.REQUIRE_SERVER_ENTROPY); PolicyUtils.assertPolicy(aim, SPConstants.MUST_SUPPORT_ISSUED_TOKENS); } } message.put(WSHandlerConstants.ACTION, action.trim()); } } @Override protected void doResults( SoapMessage msg, String actor, Element soapHeader, Element soapBody, WSHandlerResult results, boolean utWithCallbacks ) throws SOAPException, XMLStreamException, WSSecurityException { // // Pre-fetch various results // List<WSSecurityEngineResult> signedResults = new ArrayList<>(); if (results.getActionResults().containsKey(WSConstants.SIGN)) { signedResults.addAll(results.getActionResults().get(WSConstants.SIGN)); } if (results.getActionResults().containsKey(WSConstants.UT_SIGN)) { signedResults.addAll(results.getActionResults().get(WSConstants.UT_SIGN)); } if (results.getActionResults().containsKey(WSConstants.ST_SIGNED)) { signedResults.addAll(results.getActionResults().get(WSConstants.ST_SIGNED)); } Collection<WSDataRef> signed = new HashSet<>(); for (WSSecurityEngineResult result : signedResults) { List<WSDataRef> sl = CastUtils.cast((List<?>)result.get(WSSecurityEngineResult.TAG_DATA_REF_URIS)); if (sl != null) { for (WSDataRef r : sl) { signed.add(r); } } } List<WSSecurityEngineResult> encryptResults = results.getActionResults().get(WSConstants.ENCR); Collection<WSDataRef> encrypted = new HashSet<>(); if (encryptResults != null) { for (WSSecurityEngineResult result : encryptResults) { List<WSDataRef> sl = CastUtils.cast((List<?>)result.get(WSSecurityEngineResult.TAG_DATA_REF_URIS)); if (sl != null) { for (WSDataRef r : sl) { encrypted.add(r); } } } } CryptoCoverageUtil.reconcileEncryptedSignedRefs(signed, encrypted); // // Check policies // PolicyValidatorParameters parameters = new PolicyValidatorParameters(); AssertionInfoMap aim = msg.get(AssertionInfoMap.class); parameters.setAssertionInfoMap(aim); parameters.setMessage(msg); parameters.setSoapBody(soapBody); parameters.setSoapHeader(soapHeader); parameters.setResults(results); parameters.setSignedResults(signedResults); parameters.setEncryptedResults(encryptResults); parameters.setUtWithCallbacks(utWithCallbacks); parameters.setSigned(signed); parameters.setEncrypted(encrypted); List<WSSecurityEngineResult> utResults = new ArrayList<>(); if (results.getActionResults().containsKey(WSConstants.UT)) { utResults.addAll(results.getActionResults().get(WSConstants.UT)); } if (results.getActionResults().containsKey(WSConstants.UT_NOPASSWORD)) { utResults.addAll(results.getActionResults().get(WSConstants.UT_NOPASSWORD)); } parameters.setUsernameTokenResults(utResults); List<WSSecurityEngineResult> samlResults = new ArrayList<>(); if (results.getActionResults().containsKey(WSConstants.ST_SIGNED)) { samlResults.addAll(results.getActionResults().get(WSConstants.ST_SIGNED)); } if (results.getActionResults().containsKey(WSConstants.ST_UNSIGNED)) { samlResults.addAll(results.getActionResults().get(WSConstants.ST_UNSIGNED)); } parameters.setSamlResults(samlResults); // Store the timestamp element WSSecurityEngineResult tsResult = null; if (results.getActionResults().containsKey(WSConstants.TS)) { tsResult = results.getActionResults().get(WSConstants.TS).get(0); } Element timestamp = null; if (tsResult != null) { Timestamp ts = (Timestamp)tsResult.get(WSSecurityEngineResult.TAG_TIMESTAMP); timestamp = ts.getElement(); } parameters.setTimestampElement(timestamp); // Validate security policies Map<QName, SecurityPolicyValidator> validators = ValidatorUtils.getSecurityPolicyValidators(msg); for (Map.Entry<QName, Collection<AssertionInfo>> entry : aim.entrySet()) { // Check to see if we have a security policy + if we can validate it if (validators.containsKey(entry.getKey())) { validators.get(entry.getKey()).validatePolicies(parameters, entry.getValue()); } } super.doResults(msg, actor, soapHeader, soapBody, results, utWithCallbacks); } }