From e7610325ee2f1d1f4e97e1e7a9b212e692836b5a Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Tue, 4 Feb 2020 17:37:34 +0100 Subject: first stable version that uses OpenSAML 3.x --- .../modules/pvp2/impl/binding/AbstractBinding.java | 127 ++++++++++- .../modules/pvp2/impl/binding/PostBinding.java | 147 +++++++------ .../modules/pvp2/impl/binding/RedirectBinding.java | 234 +++++---------------- .../modules/pvp2/impl/binding/SoapBinding.java | 104 +++++---- 4 files changed, 287 insertions(+), 325 deletions(-) (limited to 'eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding') diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/AbstractBinding.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/AbstractBinding.java index ae108c35..3543d85a 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/AbstractBinding.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/AbstractBinding.java @@ -1,15 +1,29 @@ package at.gv.egiz.eaaf.modules.pvp2.impl.binding; +import javax.xml.namespace.QName; + import at.gv.egiz.eaaf.core.api.idp.IConfiguration; import at.gv.egiz.eaaf.modules.pvp2.api.credential.EaafX509Credential; +import at.gv.egiz.eaaf.modules.pvp2.api.message.InboundMessageInterface; import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; +import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2Exception; +import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2InternalErrorException; +import at.gv.egiz.eaaf.modules.pvp2.exception.SamlBindingException; +import at.gv.egiz.eaaf.modules.pvp2.exception.SamlMessageValidationException; import at.gv.egiz.eaaf.modules.pvp2.exception.SamlSigningException; +import at.gv.egiz.eaaf.modules.pvp2.impl.message.InboundMessage; +import at.gv.egiz.eaaf.modules.pvp2.impl.message.PvpSProfileRequest; +import at.gv.egiz.eaaf.modules.pvp2.impl.message.PvpSProfileResponse; import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils; import at.gv.egiz.eaaf.modules.pvp2.impl.validation.TrustEngineFactory; +import at.gv.egiz.eaaf.modules.pvp2.impl.verification.PvpSamlMessageHandlerChain; import org.opensaml.core.config.ConfigurationService; import org.opensaml.messaging.context.BaseContext; import org.opensaml.messaging.context.MessageContext; +import org.opensaml.messaging.decoder.MessageDecodingException; +import org.opensaml.messaging.decoder.servlet.HttpServletRequestMessageDecoder; +import org.opensaml.messaging.handler.MessageHandlerException; import org.opensaml.saml.common.SAMLObject; import org.opensaml.saml.common.SignableSAMLObject; import org.opensaml.saml.common.binding.SAMLBindingSupport; @@ -19,6 +33,8 @@ import org.opensaml.saml.common.messaging.context.SAMLMessageInfoContext; import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext; import org.opensaml.saml.common.messaging.context.SAMLProtocolContext; import org.opensaml.saml.common.xml.SAMLConstants; +import org.opensaml.saml.saml2.core.RequestAbstractType; +import org.opensaml.saml.saml2.core.StatusResponseType; import org.opensaml.saml.saml2.metadata.SingleSignOnService; import org.opensaml.saml.saml2.metadata.impl.SingleSignOnServiceBuilder; import org.opensaml.xmlsec.SignatureSigningParameters; @@ -28,21 +44,62 @@ import org.opensaml.xmlsec.context.SecurityParametersContext; import org.opensaml.xmlsec.signature.support.SignatureConstants; import org.springframework.beans.factory.annotation.Autowired; +import com.google.common.base.Optional; +import com.google.common.base.Predicates; +import com.google.common.base.Throwables; +import com.google.common.collect.FluentIterable; +import lombok.extern.slf4j.Slf4j; +import net.shibboleth.utilities.java.support.component.ComponentInitializationException; + /** * Abstract Binding implements common code for SAML2 binding implementations. * * @author tlenz * */ +@Slf4j public abstract class AbstractBinding { - @Autowired protected IConfiguration basicConfig; + @Autowired + protected IConfiguration basicConfig; public abstract String getSaml2BindingName(); + protected MessageContext internalMessageDecode( + HttpServletRequestMessageDecoder decoder, + String binding) throws Pvp2Exception { + try { + decoder.initialize(); + decoder.decode(); + + } catch (final ComponentInitializationException e) { + log.warn("Internal initialization error. Reason: {}", e.getMessage()); + throw new Pvp2InternalErrorException(e); + + } catch (final MessageDecodingException e) { + final Optional pvpException = FluentIterable.from( + Throwables.getCausalChain(e)).filter( + Predicates.instanceOf(Pvp2Exception.class)).first(); + + if (pvpException.isPresent()) { + throw (Pvp2Exception) pvpException.get(); + + } else { + throw new SamlBindingException("internal.pvp.95", + new Object[] { binding, "decoding", e.getMessage() }, + e); + + } + + } + + return decoder.getMessageContext(); + + } + protected MessageContext buildBasicMessageContext( SAMLMessageEncoder encoder, SignableSAMLObject response) { - final MessageContext messageContext = new MessageContext(); + final MessageContext messageContext = new MessageContext<>(); messageContext.setMessage(response); encoder.setMessageContext(messageContext); return messageContext; @@ -63,7 +120,7 @@ public abstract class AbstractBinding { signingParams.setSignatureReferenceDigestMethod( Saml2Utils.getDigestAlgorithm(signingParams.getSignatureAlgorithm())); - signingParams.setKeyInfoGenerator(Saml2Utils.getKeyInfoGenerator(credentials, false)); + signingParams.setKeyInfoGenerator(Saml2Utils.getKeyInfoGenerator(credentials, true)); return securityParamContext; @@ -83,16 +140,16 @@ public abstract class AbstractBinding { } protected void injectInboundMessageContexts(MessageContext messageContext, - IPvp2MetadataProvider metadataProvider) { - messageContext.addSubcontext(new SAMLPeerEntityContext()); + IPvp2MetadataProvider metadataProvider, QName peerEntityRole) throws Pvp2InternalErrorException { + final SAMLPeerEntityContext peerEntityContext = new SAMLPeerEntityContext(); + peerEntityContext.setRole(peerEntityRole); + messageContext.addSubcontext(peerEntityContext); messageContext.addSubcontext(new SAMLMessageInfoContext()); - final SAMLProtocolContext protocolContext = new SAMLProtocolContext(); protocolContext.setProtocol(SAMLConstants.SAML20P_NS); messageContext.addSubcontext(protocolContext); - final SecurityParametersContext securityParameterContext = new SecurityParametersContext(); final SignatureValidationParameters sigValParameters = new SignatureValidationParameters(); securityParameterContext.setSignatureValidationParameters(sigValParameters); @@ -100,9 +157,63 @@ public abstract class AbstractBinding { sigValParameters.setBlacklistedAlgorithms( ConfigurationService.get(SignatureValidationConfiguration.class) - .getBlacklistedAlgorithms()); + .getBlacklistedAlgorithms()); sigValParameters.setSignatureTrustEngine( TrustEngineFactory.getSignatureKnownKeysTrustEngine(metadataProvider)); } + + protected void performMessageValidation(PvpSamlMessageHandlerChain messageValidatorChain, + MessageContext messageContext) throws Pvp2Exception { + try { + messageValidatorChain.initialize(); + messageValidatorChain.invoke(messageContext); + + } catch (final ComponentInitializationException e) { + log.warn("Internal initialization error. Reason: {}", e.getMessage()); + throw new Pvp2InternalErrorException(e); + + } catch (final MessageHandlerException e) { + log.info("SAML message validation error. Reason: {}", e.getMessage()); + final Optional pvpException = FluentIterable.from( + Throwables.getCausalChain(e)).filter( + Predicates.instanceOf(Pvp2Exception.class)).first(); + + if (pvpException.isPresent()) { + throw (Pvp2Exception) pvpException.get(); + + } else { + throw new SamlMessageValidationException("internal.pvp.11", + new Object[] { e.getMessage() }, e); + + } + } + } + + protected InboundMessageInterface performMessageDecodePostProcessing( + MessageContext messageContext, boolean isVerified) { + InboundMessage msg = null; + if (messageContext.getMessage() instanceof RequestAbstractType) { + final RequestAbstractType inboundMessage = + (RequestAbstractType) messageContext.getMessage(); + msg = new PvpSProfileRequest(inboundMessage, getSaml2BindingName()); + msg.setEntityID(inboundMessage.getIssuer().getValue()); + + } else if (messageContext.getMessage() instanceof StatusResponseType) { + final StatusResponseType inboundMessage = + (StatusResponseType) messageContext.getMessage(); + msg = new PvpSProfileResponse(inboundMessage); + msg.setEntityID(inboundMessage.getIssuer().getValue()); + + } else { + // create empty container if request type is unknown + msg = new InboundMessage(); + + } + + msg.setVerified(isVerified); + msg.setRelayState(SAMLBindingSupport.getRelayState(messageContext)); + + return msg; + } } diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/PostBinding.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/PostBinding.java index 6f39392d..c679de20 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/PostBinding.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/PostBinding.java @@ -21,6 +21,7 @@ package at.gv.egiz.eaaf.modules.pvp2.impl.binding; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.xml.namespace.QName; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.gui.IGuiBuilderConfigurationFactory; @@ -36,16 +37,17 @@ import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; import at.gv.egiz.eaaf.modules.pvp2.exception.InvalidPvpRequestException; import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2Exception; import at.gv.egiz.eaaf.modules.pvp2.exception.SamlBindingException; -import at.gv.egiz.eaaf.modules.pvp2.impl.message.InboundMessage; -import at.gv.egiz.eaaf.modules.pvp2.impl.message.PvpSProfileRequest; -import at.gv.egiz.eaaf.modules.pvp2.impl.message.PvpSProfileResponse; import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.EaafHttpPostDecoder; import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.HttpPostEncoderWithOwnTemplate; +import at.gv.egiz.eaaf.modules.pvp2.impl.verification.EaafSamlProtocolMessageXmlSignatureSecurityHandler; +import at.gv.egiz.eaaf.modules.pvp2.impl.verification.PvpSamlMessageHandlerChain; import org.opensaml.messaging.context.MessageContext; -import org.opensaml.messaging.decoder.MessageDecodingException; import org.opensaml.saml.common.SAMLObject; import org.opensaml.saml.common.binding.SAMLBindingSupport; +import org.opensaml.saml.common.binding.impl.CheckMessageVersionHandler; +import org.opensaml.saml.common.binding.security.impl.MessageLifetimeSecurityHandler; +import org.opensaml.saml.common.binding.security.impl.ReceivedEndpointSecurityHandler; import org.opensaml.saml.common.messaging.SAMLMessageSecuritySupport; import org.opensaml.saml.common.xml.SAMLConstants; import org.opensaml.saml.saml2.core.RequestAbstractType; @@ -53,7 +55,6 @@ import org.opensaml.saml.saml2.core.StatusResponseType; import org.springframework.beans.factory.annotation.Autowired; import lombok.extern.slf4j.Slf4j; -import net.shibboleth.utilities.java.support.component.ComponentInitializationException; import net.shibboleth.utilities.java.support.net.URIComparator; @Slf4j @@ -78,35 +79,34 @@ public class PostBinding extends AbstractBinding implements IDecoder, IEncoder { guiConfigFactory.getSpSpecificSaml2PostConfiguration(pendingReq, "pvp_postbinding_template.html", authConfig.getConfigurationRootDirectory()); - final HttpPostEncoderWithOwnTemplate encoder - = new HttpPostEncoderWithOwnTemplate(guiConfig, guiBuilder); + final HttpPostEncoderWithOwnTemplate encoder = new HttpPostEncoderWithOwnTemplate(guiConfig, + guiBuilder); encoder.setHttpServletResponse(httpResp); - //inject message context + // inject message context final MessageContext messageContext = buildBasicMessageContext(encoder, request); - //inject signing context + // inject signing context messageContext.addSubcontext(injectSigningInfos(credentials)); - //set endpoint url + // set endpoint url messageContext.addSubcontext(injectEndpointInfos(request, targetLocation)); - - //set relayState of exists + // set relayState of exists SAMLBindingSupport.setRelayState(messageContext, relayState); - //sign SAML2 message + // sign SAML2 message SAMLMessageSecuritySupport.signMessage(messageContext); - //encode message + // encode message encoder.initialize(); encoder.encode(); } catch (final Exception e) { log.warn("Can not encode SAML2 Post-Binding request", e); throw new SamlBindingException("internal.pvp.95", - new Object[] {PvpConstants.POST, "encoding", e.getMessage()}, + new Object[] { PvpConstants.POST, "encoding", e.getMessage() }, e); } @@ -116,7 +116,7 @@ public class PostBinding extends AbstractBinding implements IDecoder, IEncoder { public void encodeResponse(final HttpServletRequest httpReq, final HttpServletResponse httpResp, final StatusResponseType response, final String targetLocation, final String relayState, final EaafX509Credential credentials, final IRequest pendingReq) - throws Pvp2Exception { + throws Pvp2Exception { try { log.debug("create SAML POSTBinding response"); @@ -130,99 +130,62 @@ public class PostBinding extends AbstractBinding implements IDecoder, IEncoder { encoder.setHttpServletResponse(httpResp); - //inject message context + // inject message context final MessageContext messageContext = buildBasicMessageContext(encoder, response); - //inject signing context + // inject signing context messageContext.addSubcontext(injectSigningInfos(credentials)); - //set endpoint url + // set endpoint url messageContext.addSubcontext(injectEndpointInfos(response, targetLocation)); - //set relayState of exists + // set relayState of exists SAMLBindingSupport.setRelayState(messageContext, relayState); - //sign SAML2 message + // sign SAML2 message SAMLMessageSecuritySupport.signMessage(messageContext); - //encode message + // encode message encoder.initialize(); encoder.encode(); } catch (final Exception e) { log.warn("Can not encode SAML2 Post-Binding response", e); throw new SamlBindingException("internal.pvp.95", - new Object[] {PvpConstants.POST, "encoding", e.getMessage()}, + new Object[] { PvpConstants.POST, "encoding", e.getMessage() }, e); } } - - - - @Override public InboundMessageInterface decode(final HttpServletRequest req, final HttpServletResponse resp, final IPvp2MetadataProvider metadataProvider, - final boolean isSpEndPoint, final URIComparator comparator) + QName peerEntityRole, final URIComparator comparator) throws Pvp2Exception { - //TODO: check, if we should re-implement HTTPPostDecoder to collect the last http parameter!!! - final EaafHttpPostDecoder decode = new EaafHttpPostDecoder(); - decode.setHttpServletRequest(req); - - //decode request - try { - decode.initialize(); - decode.decode(); - - } catch (MessageDecodingException | ComponentInitializationException e) { - throw new SamlBindingException("internal.pvp.95", - new Object[] {PvpConstants.POST, "decoding", e.getMessage()}, - e); - } - - final MessageContext messageContext = decode.getMessageContext(); + final EaafHttpPostDecoder decode = new EaafHttpPostDecoder(req); + final MessageContext messageContext = internalMessageDecode(decode, PvpConstants.POST); - if (SAMLBindingSupport.isSigningCapableBinding(messageContext)) { + // check if PVP2 AuthnRequest is signed + if (!SAMLBindingSupport.isMessageSigned(messageContext)) { log.info("SAML Post-Binding message contains no signature. Message will be rejected"); throw new InvalidPvpRequestException("internal.pvp.02", null); } - //inject informations into message context that are required for further processing - injectInboundMessageContexts(messageContext, metadataProvider); + // inject informations into message context that are required for further + // processing + injectInboundMessageContexts(messageContext, metadataProvider, peerEntityRole); + final PvpSamlMessageHandlerChain messageValidatorChain = + buildMessageValidationChain(req, comparator, metadataProvider); + log.trace("Message validation (Signature, ...) on binding-level starts ... "); + performMessageValidation(messageValidatorChain, messageContext); - //TODO: add sig validation!!! + log.trace("Message validation successful"); + return performMessageDecodePostProcessing(messageContext, true); - - - InboundMessage msg = null; - - if (messageContext.getMessage() instanceof RequestAbstractType) { - final RequestAbstractType inboundMessage = - (RequestAbstractType) messageContext.getMessage(); - msg = new PvpSProfileRequest(inboundMessage, getSaml2BindingName()); - msg.setEntityID(inboundMessage.getIssuer().getValue()); - - } else if (messageContext.getMessage() instanceof StatusResponseType) { - final StatusResponseType inboundMessage = - (StatusResponseType) messageContext.getMessage(); - msg = new PvpSProfileResponse(inboundMessage); - msg.setEntityID(inboundMessage.getIssuer().getValue()); - - } else { - // create empty container if request type is unknown - msg = new InboundMessage(); - - } - - msg.setVerified(false); - msg.setRelayState(SAMLBindingSupport.getRelayState(messageContext)); - - return msg; } @Override @@ -234,5 +197,41 @@ public class PostBinding extends AbstractBinding implements IDecoder, IEncoder { @Override public String getSaml2BindingName() { return SAMLConstants.SAML2_POST_BINDING_URI; + + } + + private PvpSamlMessageHandlerChain buildMessageValidationChain(HttpServletRequest req, + URIComparator comparator, IPvp2MetadataProvider metadataProvider) { + final PvpSamlMessageHandlerChain messageValidatorChain = new PvpSamlMessageHandlerChain(); + + final ReceivedEndpointSecurityHandler endpointSecurityHandler = new ReceivedEndpointSecurityHandler(); + endpointSecurityHandler.setHttpServletRequest(req); + endpointSecurityHandler.setURIComparator(comparator); + + messageValidatorChain.addHandler(new CheckMessageVersionHandler()); + messageValidatorChain.addHandler(endpointSecurityHandler); + messageValidatorChain.addHandler( + new EaafSamlProtocolMessageXmlSignatureSecurityHandler(metadataProvider)); + messageValidatorChain.addHandler(new MessageLifetimeSecurityHandler()); + + /* + * TODO: maybe we add it in a later version Because: - AuthnRequest replay + * should not be a problem on IDP side - Response replay will be not possible, + * because EAAF PVP implements countermeasure based on one-time tokens for each + * request + * + */ + // final MessageReplaySecurityHandler replaySecurityHandler = new + // MessageReplaySecurityHandler(); + // final StorageService replayCacheStorage = null; + // final ReplayCache replayCache = new ReplayCache(); + // replayCache.setId("Message replay cache"); + // replayCache.setStrict(true); + // replayCache.setStorage(replayCacheStorage); + // replaySecurityHandler.setReplayCache(replayCache ); + // messageValidatorChain.addHandler(replaySecurityHandler); + + return messageValidatorChain; + } } diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/RedirectBinding.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/RedirectBinding.java index 5f74053d..f62f8a11 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/RedirectBinding.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/RedirectBinding.java @@ -21,6 +21,7 @@ package at.gv.egiz.eaaf.modules.pvp2.impl.binding; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.xml.namespace.QName; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.modules.pvp2.PvpConstants; @@ -32,15 +33,11 @@ import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; import at.gv.egiz.eaaf.modules.pvp2.exception.InvalidPvpRequestException; import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2Exception; import at.gv.egiz.eaaf.modules.pvp2.exception.SamlBindingException; -import at.gv.egiz.eaaf.modules.pvp2.impl.message.InboundMessage; -import at.gv.egiz.eaaf.modules.pvp2.impl.message.PvpSProfileRequest; -import at.gv.egiz.eaaf.modules.pvp2.impl.message.PvpSProfileResponse; import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.EaafHttpRedirectDeflateDecoder; +import at.gv.egiz.eaaf.modules.pvp2.impl.verification.EaafSaml2HttpRedirectDeflateSignatureSecurityHandler; import at.gv.egiz.eaaf.modules.pvp2.impl.verification.PvpSamlMessageHandlerChain; import org.opensaml.messaging.context.MessageContext; -import org.opensaml.messaging.decoder.MessageDecodingException; -import org.opensaml.messaging.handler.MessageHandlerException; import org.opensaml.saml.common.SAMLObject; import org.opensaml.saml.common.binding.SAMLBindingSupport; import org.opensaml.saml.common.binding.impl.CheckMessageVersionHandler; @@ -48,13 +45,11 @@ import org.opensaml.saml.common.binding.security.impl.MessageLifetimeSecurityHan import org.opensaml.saml.common.messaging.context.SAMLBindingContext; import org.opensaml.saml.common.xml.SAMLConstants; import org.opensaml.saml.saml2.binding.encoding.impl.HTTPRedirectDeflateEncoder; -import org.opensaml.saml.saml2.binding.security.impl.SAML2HTTPRedirectDeflateSignatureSecurityHandler; import org.opensaml.saml.saml2.core.RequestAbstractType; import org.opensaml.saml.saml2.core.StatusResponseType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import net.shibboleth.utilities.java.support.component.ComponentInitializationException; import net.shibboleth.utilities.java.support.net.URIComparator; public class RedirectBinding extends AbstractBinding implements IDecoder, IEncoder { @@ -137,26 +132,11 @@ public class RedirectBinding extends AbstractBinding implements IDecoder, IEncod @Override public InboundMessageInterface decode(final HttpServletRequest req, final HttpServletResponse resp, final IPvp2MetadataProvider metadataProvider, - final boolean isSpEndPoint, final URIComparator comparator) + QName peerEntityRole, final URIComparator comparator) throws Pvp2Exception { - // TODO: implement one flat decoder to get SAML2 request/response parametes as - // same as in SAML2HTTPRedirectDeflateSignatureSecurityHandler - final EaafHttpRedirectDeflateDecoder decode = new EaafHttpRedirectDeflateDecoder(); - decode.setHttpServletRequest(req); - - // decode request - try { - decode.initialize(); - decode.decode(); - - } catch (MessageDecodingException | ComponentInitializationException e) { - throw new SamlBindingException("internal.pvp.95", - new Object[] { PvpConstants.REDIRECT, "decoding", e.getMessage() }, - e); - } - - final MessageContext messageContext = decode.getMessageContext(); + final EaafHttpRedirectDeflateDecoder decode = new EaafHttpRedirectDeflateDecoder(req); + final MessageContext messageContext = internalMessageDecode(decode, PvpConstants.REDIRECT); final SAMLBindingContext bindingContext = messageContext.getSubcontext(SAMLBindingContext.class, true); if (!bindingContext.hasBindingSignature()) { @@ -165,171 +145,18 @@ public class RedirectBinding extends AbstractBinding implements IDecoder, IEncod } - //inject informations into message context that are required for further processing - injectInboundMessageContexts(messageContext, metadataProvider); + // inject informations into message context that are required for further + // processing + injectInboundMessageContexts(messageContext, metadataProvider, peerEntityRole); + final PvpSamlMessageHandlerChain messageValidatorChain = + buildMessageValidationChain(req, metadataProvider); - log.trace("Signature validation on binding-level starts ... "); - final PvpSamlMessageHandlerChain messageValidatorChain = new PvpSamlMessageHandlerChain(); - final SAML2HTTPRedirectDeflateSignatureSecurityHandler redirectBindingSignaturHandler = - new SAML2HTTPRedirectDeflateSignatureSecurityHandler(); - redirectBindingSignaturHandler.setHttpServletRequest(req); - - messageValidatorChain.addHandler(new CheckMessageVersionHandler()); - messageValidatorChain.addHandler(redirectBindingSignaturHandler); - messageValidatorChain.addHandler(new MessageLifetimeSecurityHandler()); - - - /*TODO: maybe we add it in a later version - * Because: - * - AuthnRequest replay should not be a problem on IDP side - * - Response replay will be not possible, because EAAF PVP implements - * countermeasure based on one-time tokens for each request - * - */ - //final MessageReplaySecurityHandler replaySecurityHandler = new MessageReplaySecurityHandler(); - //final StorageService replayCacheStorage = null; - //final ReplayCache replayCache = new ReplayCache(); - //replayCache.setId("Message replay cache"); - //replayCache.setStrict(true); - //replayCache.setStorage(replayCacheStorage); - //replaySecurityHandler.setReplayCache(replayCache ); - //messageValidatorChain.addHandler(replaySecurityHandler); + log.trace("Message validation (Signature, ...) on binding-level starts ... "); + performMessageValidation(messageValidatorChain, messageContext); + log.trace("Message validation successful"); + return performMessageDecodePostProcessing(messageContext, true); - try { - messageValidatorChain.initialize(); - messageValidatorChain.invoke(messageContext); - - } catch (final ComponentInitializationException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - - } catch (final MessageHandlerException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - InboundMessage msg = null; - - if (messageContext.getMessage() instanceof RequestAbstractType) { - final RequestAbstractType inboundMessage = - (RequestAbstractType) messageContext.getMessage(); - msg = new PvpSProfileRequest(inboundMessage, getSaml2BindingName()); - msg.setEntityID(inboundMessage.getIssuer().getValue()); - - } else if (messageContext.getMessage() instanceof StatusResponseType) { - final StatusResponseType inboundMessage = - (StatusResponseType) messageContext.getMessage(); - msg = new PvpSProfileResponse(inboundMessage); - msg.setEntityID(inboundMessage.getIssuer().getValue()); - - } else { - // create empty container if request type is unknown - msg = new InboundMessage(); - - } - - msg.setVerified(false); - msg.setRelayState(SAMLBindingSupport.getRelayState(messageContext)); - - return msg; - -// final BasicSAMLMessageContext messageContext = -// new BasicSAMLMessageContext<>(); -// messageContext.setInboundMessageTransport(new HttpServletRequestAdapter(req)); -// -// // set metadata descriptor type -// if (isSpEndPoint) { -// messageContext.setPeerEntityRole(IDPSSODescriptor.DEFAULT_ELEMENT_NAME); -// decode.setURIComparator(comparator); -// -// } else { -// messageContext.setPeerEntityRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME); -// decode.setURIComparator(comparator); -// } -// -// messageContext.setMetadataProvider(metadataProvider); -// -// final SAML2HTTPRedirectDeflateSignatureRule signatureRule = -// new SAML2HTTPRedirectDeflateSignatureRule( -// TrustEngineFactory.getSignatureKnownKeysTrustEngine(metadataProvider)); -// final PvpAuthRequestSignedRole signedRole = new PvpAuthRequestSignedRole(); -// final BasicSecurityPolicy policy = new BasicSecurityPolicy(); -// policy.getPolicyRules().add(signedRole); -// policy.getPolicyRules().add(signatureRule); -// final SecurityPolicyResolver resolver = new StaticSecurityPolicyResolver(policy); -// messageContext.setSecurityPolicyResolver(resolver); -// -// // set metadata descriptor type -// if (isSpEndPoint) { -// messageContext.setPeerEntityRole(IDPSSODescriptor.DEFAULT_ELEMENT_NAME); -// } else { -// messageContext.setPeerEntityRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME); -// } -// -// SAML2AuthnRequestsSignedSecurityHandler -// -// try { -// decode.decode(messageContext); -// -// // check signature -// signatureRule.evaluate(messageContext); -// -// } catch (final SecurityException e) { -// if (StringUtils.isEmpty(messageContext.getInboundMessageIssuer())) { -// throw e; -// -// } -// -// if (metadataProvider instanceof IRefreshableMetadataProvider) { -// log.debug("PVP2X message validation FAILED. Reload metadata for entityID: " -// + messageContext.getInboundMessageIssuer()); -// if (!((IRefreshableMetadataProvider) metadataProvider) -// .refreshMetadataProvider(messageContext.getInboundMessageIssuer())) { -// throw e; -// } else { -// log.trace("PVP2X metadata reload finished. Check validate message again."); -// decode.decode(messageContext); -// -// // check signature -// signatureRule.evaluate(messageContext); -// -// } -// log.trace("Second PVP2X message validation finished"); -// -// } else { -// throw e; -// -// } -// } -// -// InboundMessage msg = null; -// if (messageContext.getInboundMessage() instanceof RequestAbstractType) { -// final RequestAbstractType inboundMessage = -// (RequestAbstractType) messageContext.getInboundMessage(); -// msg = new PvpSProfileRequest(inboundMessage, getSaml2BindingName()); -// -// } else if (messageContext.getInboundMessage() instanceof StatusResponseType) { -// final StatusResponseType inboundMessage = -// (StatusResponseType) messageContext.getInboundMessage(); -// msg = new PvpSProfileResponse(inboundMessage); -// -// } else { -// // create empty container if request type is unknown -// msg = new InboundMessage(); -// } -// -// if (messageContext.getPeerEntityMetadata() != null) { -// msg.setEntityID(messageContext.getPeerEntityMetadata().getEntityID()); -// } else { -// log.info( -// "No Metadata found for OA with EntityID " + messageContext.getInboundMessageIssuer()); -// } -// -// msg.setVerified(true); -// msg.setRelayState(messageContext.getRelayState()); -// -// return msg; } @Override @@ -341,5 +168,38 @@ public class RedirectBinding extends AbstractBinding implements IDecoder, IEncod @Override public String getSaml2BindingName() { return SAMLConstants.SAML2_REDIRECT_BINDING_URI; + + } + + private PvpSamlMessageHandlerChain buildMessageValidationChain(HttpServletRequest req, + IPvp2MetadataProvider metadataProvider) { + final PvpSamlMessageHandlerChain messageValidatorChain = new PvpSamlMessageHandlerChain(); + final EaafSaml2HttpRedirectDeflateSignatureSecurityHandler redirectBindingSignaturHandler = + new EaafSaml2HttpRedirectDeflateSignatureSecurityHandler(metadataProvider); + redirectBindingSignaturHandler.setHttpServletRequest(req); + + messageValidatorChain.addHandler(new CheckMessageVersionHandler()); + messageValidatorChain.addHandler(redirectBindingSignaturHandler); + messageValidatorChain.addHandler(new MessageLifetimeSecurityHandler()); + + /* + * TODO: maybe we add it in a later version Because: - AuthnRequest replay + * should not be a problem on IDP side - Response replay will be not possible, + * because EAAF PVP implements countermeasure based on one-time tokens for each + * request + * + */ + // final MessageReplaySecurityHandler replaySecurityHandler = new + // MessageReplaySecurityHandler(); + // final StorageService replayCacheStorage = null; + // final ReplayCache replayCache = new ReplayCache(); + // replayCache.setId("Message replay cache"); + // replayCache.setStrict(true); + // replayCache.setStorage(replayCacheStorage); + // replaySecurityHandler.setReplayCache(replayCache ); + // messageValidatorChain.addHandler(replaySecurityHandler); + + return messageValidatorChain; + } } diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/SoapBinding.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/SoapBinding.java index e0df2d2a..49e93f0a 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/SoapBinding.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/SoapBinding.java @@ -21,6 +21,7 @@ package at.gv.egiz.eaaf.modules.pvp2.impl.binding; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.xml.namespace.QName; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.modules.pvp2.PvpConstants; @@ -29,27 +30,29 @@ import at.gv.egiz.eaaf.modules.pvp2.api.binding.IEncoder; import at.gv.egiz.eaaf.modules.pvp2.api.credential.EaafX509Credential; import at.gv.egiz.eaaf.modules.pvp2.api.message.InboundMessageInterface; import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; -import at.gv.egiz.eaaf.modules.pvp2.exception.AttributQueryException; +import at.gv.egiz.eaaf.modules.pvp2.exception.InvalidPvpRequestException; import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2Exception; +import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2InternalErrorException; import at.gv.egiz.eaaf.modules.pvp2.exception.SamlBindingException; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils; import at.gv.egiz.eaaf.modules.pvp2.impl.verification.EaafMessageContextInitializationHandler; +import at.gv.egiz.eaaf.modules.pvp2.impl.verification.EaafSamlProtocolMessageXmlSignatureSecurityHandler; import at.gv.egiz.eaaf.modules.pvp2.impl.verification.PvpSamlMessageHandlerChain; import org.opensaml.messaging.context.MessageContext; -import org.opensaml.messaging.decoder.MessageDecodingException; import org.opensaml.saml.common.SAMLObject; import org.opensaml.saml.common.binding.SAMLBindingSupport; import org.opensaml.saml.common.binding.impl.CheckMessageVersionHandler; import org.opensaml.saml.common.binding.impl.SAMLProtocolAndRoleHandler; import org.opensaml.saml.common.binding.impl.SAMLSOAPDecoderBodyHandler; import org.opensaml.saml.common.binding.security.impl.MessageLifetimeSecurityHandler; -import org.opensaml.saml.common.binding.security.impl.SAMLProtocolMessageXMLSignatureSecurityHandler; import org.opensaml.saml.common.messaging.SAMLMessageSecuritySupport; import org.opensaml.saml.common.xml.SAMLConstants; import org.opensaml.saml.saml2.binding.decoding.impl.HTTPSOAP11Decoder; import org.opensaml.saml.saml2.binding.encoding.impl.HTTPSOAP11Encoder; import org.opensaml.saml.saml2.core.RequestAbstractType; import org.opensaml.saml.saml2.core.StatusResponseType; +import org.opensaml.soap.messaging.context.SOAP11Context; import lombok.extern.slf4j.Slf4j; import net.shibboleth.utilities.java.support.component.ComponentInitializationException; @@ -61,72 +64,55 @@ public class SoapBinding extends AbstractBinding implements IDecoder, IEncoder { @Override public InboundMessageInterface decode(final HttpServletRequest req, final HttpServletResponse resp, final IPvp2MetadataProvider metadataProvider, - final boolean isSpEndPoint, final URIComparator comparator) + QName peerEntityRole, final URIComparator comparator) throws Pvp2Exception { - try { - final HTTPSOAP11Decoder soapDecoder = new HTTPSOAP11Decoder(); + final HTTPSOAP11Decoder soapDecoder = new HTTPSOAP11Decoder(); + soapDecoder.setHttpServletRequest(req); + + injectMessageHandlerChain(soapDecoder, metadataProvider, peerEntityRole); + + final MessageContext messageContext = + internalMessageDecode(soapDecoder, PvpConstants.SOAP); + + // check if PVP2 AuthnRequest is signed + if (!SAMLBindingSupport.isMessageSigned(messageContext)) { + log.info("SAML Post-Binding message contains no signature. Message will be rejected"); + throw new InvalidPvpRequestException("internal.pvp.02", null); + + } + + return performMessageDecodePostProcessing(messageContext, true); + } + private void injectMessageHandlerChain(HTTPSOAP11Decoder soapDecoder, + IPvp2MetadataProvider metadataProvider, QName peerEntityRole) throws Pvp2InternalErrorException { + try { final PvpSamlMessageHandlerChain messageValidatorChain = new PvpSamlMessageHandlerChain(); - soapDecoder.setBodyHandler(messageValidatorChain); + messageValidatorChain.addHandler(new EaafMessageContextInitializationHandler(metadataProvider)); + messageValidatorChain.addHandler(new SAMLSOAPDecoderBodyHandler()); - final SAMLProtocolMessageXMLSignatureSecurityHandler msgSignatureValidationHandler = - new SAMLProtocolMessageXMLSignatureSecurityHandler(); + final SAMLProtocolAndRoleHandler samlProtocolHandler = new SAMLProtocolAndRoleHandler(); + samlProtocolHandler.setProtocol(SAMLConstants.SAML20P_NS); + samlProtocolHandler.setRole(peerEntityRole); + messageValidatorChain.addHandler(samlProtocolHandler); - messageValidatorChain.addHandler(new EaafMessageContextInitializationHandler()); messageValidatorChain.addHandler(new CheckMessageVersionHandler()); - messageValidatorChain.addHandler(new SAMLProtocolAndRoleHandler()); - messageValidatorChain.addHandler(msgSignatureValidationHandler); + messageValidatorChain.addHandler( + new EaafSamlProtocolMessageXmlSignatureSecurityHandler(metadataProvider)); messageValidatorChain.addHandler(new MessageLifetimeSecurityHandler()); - messageValidatorChain.addHandler(new SAMLSOAPDecoderBodyHandler()); - // decode message - soapDecoder.initialize(); - soapDecoder.decode(); + messageValidatorChain.initialize(); - final MessageContext messageContext = soapDecoder.getMessageContext(); - messageContext.getMessage(); + soapDecoder.setBodyHandler(messageValidatorChain); + + } catch (final ComponentInitializationException e) { + log.warn("Internal initialization error. Reason: {}", e.getMessage()); + throw new Pvp2InternalErrorException(e); - } catch (MessageDecodingException | ComponentInitializationException e) { - throw new SamlBindingException("internal.pvp.95", - new Object[] { PvpConstants.POST, "decoding", e.getMessage() }, - e); } -// final Envelope inboundMessage = (Envelope) messageContext.getMessage(); -// -// if (inboundMessage.getBody() != null) { -// final List xmlElemList = inboundMessage.getBody().getUnknownXMLObjects(); -// -// if (!xmlElemList.isEmpty()) { -// final SignableXMLObject attrReq = (SignableXMLObject) xmlElemList.get(0); -// final PvpSProfileRequest request = new PvpSProfileRequest(attrReq, getSaml2BindingName()); -// -// if (messageContext.getPeerEntityMetadata() != null) { -// request.setEntityID(messageContext.getPeerEntityMetadata().getEntityID()); -// -// } else if (attrReq instanceof RequestAbstractType) { -// final RequestAbstractType attributeRequest = (RequestAbstractType) attrReq; -// try { -// if (StringUtils.isNotEmpty(attributeRequest.getIssuer().getValue()) -// && metadataProvider.getRole(attributeRequest.getIssuer().getValue(), -// SPSSODescriptor.DEFAULT_ELEMENT_NAME) != null) { -// request.setEntityID(attributeRequest.getIssuer().getValue()); -// } -// -// } catch (final Exception e) { -// log.warn("No Metadata found with EntityID " + attributeRequest.getIssuer().getValue()); -// } -// } -// -// request.setVerified(false); -// return request; -// -// } -// } - - log.error("Receive empty PVP 2.1 attributequery request."); - throw new AttributQueryException("Receive empty PVP 2.1 attributequery request.", null); + } @Override @@ -157,6 +143,11 @@ public class SoapBinding extends AbstractBinding implements IDecoder, IEncoder { // inject message context final MessageContext messageContext = buildBasicMessageContext(encoder, response); + //inject SOAP enveloped + final SOAP11Context soap11Context = new SOAP11Context(); + soap11Context.setEnvelope(Saml2Utils.buildSoap11Envelope(response)); + messageContext.addSubcontext(soap11Context); + // inject signing context messageContext.addSubcontext(injectSigningInfos(credentials)); @@ -172,6 +163,7 @@ public class SoapBinding extends AbstractBinding implements IDecoder, IEncoder { // encode message encoder.initialize(); encoder.encode(); + } catch (final Exception e) { log.warn("Can not encode SAML2 SOAP-Binding response", e); throw new SamlBindingException("internal.pvp.95", -- cgit v1.2.3