From d41afe91ee59daf6b5f5037cecac52900fe2ccb2 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Fri, 31 Jan 2020 20:41:54 +0100 Subject: a lot of more OpenSAML3 refactoring staff This version is also NOT stable! --- .../modules/pvp2/impl/binding/RedirectBinding.java | 376 +++++++++++++-------- 1 file changed, 240 insertions(+), 136 deletions(-) (limited to 'eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/RedirectBinding.java') 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 559ee3b8..5f74053d 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 @@ -26,211 +26,315 @@ import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.modules.pvp2.PvpConstants; import at.gv.egiz.eaaf.modules.pvp2.api.binding.IDecoder; 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.IRefreshableMetadataProvider; +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.initialize.EaafDefaultSaml2Bootstrap; -import at.gv.egiz.eaaf.modules.pvp2.impl.validation.TrustEngineFactory; -import at.gv.egiz.eaaf.modules.pvp2.impl.verification.PvpAuthRequestSignedRole; +import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.EaafHttpRedirectDeflateDecoder; +import at.gv.egiz.eaaf.modules.pvp2.impl.verification.PvpSamlMessageHandlerChain; -import org.apache.commons.lang3.StringUtils; -import org.opensaml.common.binding.BasicSAMLMessageContext; +import org.opensaml.messaging.context.MessageContext; import org.opensaml.messaging.decoder.MessageDecodingException; -import org.opensaml.messaging.encoder.MessageEncodingException; +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; +import org.opensaml.saml.common.binding.security.impl.MessageLifetimeSecurityHandler; +import org.opensaml.saml.common.messaging.context.SAMLBindingContext; import org.opensaml.saml.common.xml.SAMLConstants; -import org.opensaml.saml.saml2.binding.decoding.impl.HTTPRedirectDeflateDecoder; 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.opensaml.saml.saml2.metadata.IDPSSODescriptor; -import org.opensaml.saml.saml2.metadata.SPSSODescriptor; -import org.opensaml.saml.saml2.metadata.SingleSignOnService; -import org.opensaml.saml.saml2.metadata.impl.SingleSignOnServiceBuilder; -import org.opensaml.saml2.binding.security.SAML2HTTPRedirectDeflateSignatureRule; -import org.opensaml.saml2.metadata.provider.MetadataProvider; -import org.opensaml.security.credential.Credential; -import org.opensaml.ws.security.SecurityPolicyResolver; -import org.opensaml.ws.security.provider.BasicSecurityPolicy; -import org.opensaml.ws.security.provider.StaticSecurityPolicyResolver; -import org.opensaml.ws.transport.http.HttpServletRequestAdapter; -import org.opensaml.ws.transport.http.HttpServletResponseAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; +import net.shibboleth.utilities.java.support.component.ComponentInitializationException; import net.shibboleth.utilities.java.support.net.URIComparator; -import net.shibboleth.utilities.java.support.xml.BasicParserPool; -@Service("PVPRedirectBinding") -public class RedirectBinding implements IDecoder, IEncoder { +public class RedirectBinding extends AbstractBinding implements IDecoder, IEncoder { private static final Logger log = LoggerFactory.getLogger(RedirectBinding.class); @Override public void encodeRequest(final HttpServletRequest req, final HttpServletResponse resp, final RequestAbstractType request, final String targetLocation, final String relayState, - final Credential credentials, final IRequest pendingReq) - throws MessageEncodingException, SecurityException { - - // load default PVP security configurations - EaafDefaultSaml2Bootstrap.initializeDefaultPvpConfiguration(); - - log.debug("create SAML RedirectBinding response"); - - final HTTPRedirectDeflateEncoder encoder = new HTTPRedirectDeflateEncoder(); - final HttpServletResponseAdapter responseAdapter = new HttpServletResponseAdapter(resp, true); - final BasicSAMLMessageContext context = - new BasicSAMLMessageContext<>(); - final SingleSignOnService service = new SingleSignOnServiceBuilder().buildObject(); - service.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI); - service.setLocation(targetLocation); - context.setOutboundSAMLMessageSigningCredential(credentials); - context.setPeerEntityEndpoint(service); - context.setOutboundSAMLMessage(request); - context.setOutboundMessageTransport(responseAdapter); - context.setRelayState(relayState); - - encoder.encode(context); + final EaafX509Credential credentials, final IRequest pendingReq) + throws Pvp2Exception { + + try { + log.debug("create SAML RedirectBinding response"); + final HTTPRedirectDeflateEncoder encoder = new HTTPRedirectDeflateEncoder(); + encoder.setHttpServletResponse(resp); + + final MessageContext messageContext = buildBasicMessageContext(encoder, request); + + // set endpoint url + messageContext.addSubcontext(injectEndpointInfos(request, targetLocation)); + + // inject signing context + messageContext.addSubcontext(injectSigningInfos(credentials)); + + // set relayState of exists + SAMLBindingSupport.setRelayState(messageContext, relayState); + + // encode message + encoder.initialize(); + encoder.encode(); + + } catch (final Exception e) { + log.warn("Can not encode SAML2 Redirect-Binding request", e); + throw new SamlBindingException("internal.pvp.95", + new Object[] { PvpConstants.REDIRECT, "encoding", e.getMessage() }, + e); + + } + } @Override - public void encodeRespone(final HttpServletRequest req, final HttpServletResponse resp, + public void encodeResponse(final HttpServletRequest req, final HttpServletResponse resp, final StatusResponseType response, final String targetLocation, final String relayState, - final Credential credentials, final IRequest pendingReq) - throws MessageEncodingException, SecurityException { + final EaafX509Credential credentials, final IRequest pendingReq) + throws Pvp2Exception { + + try { + log.debug("create SAML RedirectBinding response"); + + final HTTPRedirectDeflateEncoder encoder = new HTTPRedirectDeflateEncoder(); + encoder.setHttpServletResponse(resp); + + final MessageContext messageContext = buildBasicMessageContext(encoder, response); + + // set endpoint url + messageContext.addSubcontext(injectEndpointInfos(response, targetLocation)); - // load default PVP security configurations - EaafDefaultSaml2Bootstrap.initializeDefaultPvpConfiguration(); + // inject signing context + messageContext.addSubcontext(injectSigningInfos(credentials)); - log.debug("create SAML RedirectBinding response"); + // set relayState of exists + SAMLBindingSupport.setRelayState(messageContext, relayState); - final HTTPRedirectDeflateEncoder encoder = new HTTPRedirectDeflateEncoder(); - final HttpServletResponseAdapter responseAdapter = new HttpServletResponseAdapter(resp, true); - final BasicSAMLMessageContext context = - new BasicSAMLMessageContext<>(); - final SingleSignOnService service = new SingleSignOnServiceBuilder().buildObject(); - service.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI); - service.setLocation(targetLocation); - context.setOutboundSAMLMessageSigningCredential(credentials); - context.setPeerEntityEndpoint(service); - context.setOutboundSAMLMessage(response); - context.setOutboundMessageTransport(responseAdapter); - context.setRelayState(relayState); + // encode message + encoder.initialize(); + encoder.encode(); - encoder.encode(context); + } catch (final Exception e) { + log.warn("Can not encode SAML2 Redirect-Binding request", e); + throw new SamlBindingException("internal.pvp.95", + new Object[] { PvpConstants.REDIRECT, "encoding", e.getMessage() }, + e); + + } } @Override public InboundMessageInterface decode(final HttpServletRequest req, - final HttpServletResponse resp, final MetadataProvider metadataProvider, + final HttpServletResponse resp, final IPvp2MetadataProvider metadataProvider, final boolean isSpEndPoint, final URIComparator comparator) - throws MessageDecodingException, SecurityException { - - final HTTPRedirectDeflateDecoder decode = new HTTPRedirectDeflateDecoder(new BasicParserPool()); - - final BasicSAMLMessageContext messageContext = - new BasicSAMLMessageContext<>(); - messageContext.setInboundMessageTransport(new HttpServletRequestAdapter(req)); + throws Pvp2Exception { - // set metadata descriptor type - if (isSpEndPoint) { - messageContext.setPeerEntityRole(IDPSSODescriptor.DEFAULT_ELEMENT_NAME); - decode.setURIComparator(comparator); + // TODO: implement one flat decoder to get SAML2 request/response parametes as + // same as in SAML2HTTPRedirectDeflateSignatureSecurityHandler + final EaafHttpRedirectDeflateDecoder decode = new EaafHttpRedirectDeflateDecoder(); + decode.setHttpServletRequest(req); - } else { - messageContext.setPeerEntityRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME); - decode.setURIComparator(comparator); - } + // decode request + try { + decode.initialize(); + decode.decode(); - 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); + } catch (MessageDecodingException | ComponentInitializationException e) { + throw new SamlBindingException("internal.pvp.95", + new Object[] { PvpConstants.REDIRECT, "decoding", e.getMessage() }, + e); } - try { - decode.decode(messageContext); - - // check signature - signatureRule.evaluate(messageContext); + final MessageContext messageContext = decode.getMessageContext(); - } catch (final SecurityException e) { - if (StringUtils.isEmpty(messageContext.getInboundMessageIssuer())) { - throw e; + final SAMLBindingContext bindingContext = messageContext.getSubcontext(SAMLBindingContext.class, true); + if (!bindingContext.hasBindingSignature()) { + log.info("SAML Redirect-Binding message contains no signature. Message will be rejected"); + throw new InvalidPvpRequestException("internal.pvp.02", null); - } + } - 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); + //inject informations into message context that are required for further processing + injectInboundMessageContexts(messageContext, 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); - // check signature - signatureRule.evaluate(messageContext); - } - log.trace("Second PVP2X message validation finished"); + try { + messageValidatorChain.initialize(); + messageValidatorChain.invoke(messageContext); - } else { - throw e; + } 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.getInboundMessage() instanceof RequestAbstractType) { + + if (messageContext.getMessage() instanceof RequestAbstractType) { final RequestAbstractType inboundMessage = - (RequestAbstractType) messageContext.getInboundMessage(); + (RequestAbstractType) messageContext.getMessage(); msg = new PvpSProfileRequest(inboundMessage, getSaml2BindingName()); + msg.setEntityID(inboundMessage.getIssuer().getValue()); - } else if (messageContext.getInboundMessage() instanceof StatusResponseType) { + } else if (messageContext.getMessage() instanceof StatusResponseType) { final StatusResponseType inboundMessage = - (StatusResponseType) messageContext.getInboundMessage(); + (StatusResponseType) messageContext.getMessage(); msg = new PvpSProfileResponse(inboundMessage); + msg.setEntityID(inboundMessage.getIssuer().getValue()); } 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()); + 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 public boolean handleDecode(final String action, final HttpServletRequest req) { - return action.equals(PvpConstants.REDIRECT) || action.equals(PvpConstants.SINGLELOGOUT) + return (action.equals(PvpConstants.REDIRECT) || action.equals(PvpConstants.SINGLELOGOUT)) && req.getMethod().equals("GET"); } -- cgit v1.2.3