[cpp-opensaml] branch master updated: CPPOST-112 - Clean up message correlation in relevant policy rules

Scott Cantor cantor.2 at osu.edu
Tue Feb 11 12:59:08 EST 2020


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch master
in repository cpp-opensaml.

View the commit online:
http://git.shibboleth.net/view/?p=cpp-opensaml.git;a=commit;h=eae759ea5c32951115f4248f4bc6040d7e28635f

The following commit(s) were added to refs/heads/master by this push:
       new  eae759e   CPPOST-112 - Clean up message correlation in relevant policy rules
eae759e is described below

commit eae759ea5c32951115f4248f4bc6040d7e28635f
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Feb 11 12:57:50 2020 -0500

    CPPOST-112 - Clean up message correlation in relevant policy rules
    
    https://issues.shibboleth.net/jira/browse/CPPOST-112
    
    Initial implementation of toggleable correlation support.
---
 Projects/vc15/saml/saml.vcxproj                    |  4 +-
 Projects/vc15/saml/saml.vcxproj.filters            |  6 ++
 saml/Makefile.am                                   |  2 +
 saml/binding/MessageDecoder.h                      | 33 ++++++++--
 saml/binding/SecurityPolicy.h                      | 18 +++++-
 saml/binding/impl/MessageDecoder.cpp               | 21 ++++++
 saml/binding/impl/MessageFlowRule.cpp              | 21 ++++++
 saml/binding/impl/SecurityPolicy.cpp               | 12 ++++
 saml/saml1/binding/impl/SAML1ArtifactDecoder.cpp   |  2 +
 saml/saml1/binding/impl/SAML1MessageDecoder.cpp    |  2 +
 saml/saml1/binding/impl/SAML1POSTDecoder.cpp       |  2 +
 saml/saml1/binding/impl/SAML1SOAPClient.cpp        |  9 ++-
 saml/saml1/binding/impl/SAML1SOAPDecoder.cpp       |  2 +
 saml/saml2/binding/SAML2MessageDecoder.h           | 21 ++++++
 ...SAML2MessageDecoder.h => SAML2MessageEncoder.h} | 38 +++++++----
 saml/saml2/binding/impl/SAML2ArtifactDecoder.cpp   |  4 ++
 saml/saml2/binding/impl/SAML2ArtifactEncoder.cpp   | 13 ++--
 saml/saml2/binding/impl/SAML2ECPDecoder.cpp        | 35 ++++++----
 saml/saml2/binding/impl/SAML2ECPEncoder.cpp        | 11 ++--
 saml/saml2/binding/impl/SAML2MessageDecoder.cpp    | 39 ++++++++++++
 saml/saml2/binding/impl/SAML2MessageEncoder.cpp    | 74 ++++++++++++++++++++++
 saml/saml2/binding/impl/SAML2POSTDecoder.cpp       |  4 ++
 saml/saml2/binding/impl/SAML2POSTEncoder.cpp       | 11 ++--
 saml/saml2/binding/impl/SAML2RedirectDecoder.cpp   |  4 ++
 saml/saml2/binding/impl/SAML2RedirectEncoder.cpp   | 14 ++--
 saml/saml2/binding/impl/SAML2SOAPClient.cpp        |  6 +-
 saml/saml2/binding/impl/SAML2SOAPDecoder.cpp       |  2 +
 saml/saml2/binding/impl/SAML2SOAPEncoder.cpp       |  8 +--
 saml/saml2/profile/impl/BearerConfirmationRule.cpp | 15 ++++-
 samltest/saml1/binding/SAML1ArtifactTest.h         |  2 +-
 samltest/saml1/binding/SAML1POSTTest.h             |  2 +-
 samltest/saml2/binding/SAML2ArtifactTest.h         |  2 +-
 samltest/saml2/binding/SAML2POSTTest.h             |  2 +-
 samltest/saml2/binding/SAML2RedirectTest.h         |  2 +-
 34 files changed, 356 insertions(+), 87 deletions(-)

diff --git a/Projects/vc15/saml/saml.vcxproj b/Projects/vc15/saml/saml.vcxproj
index f3713a5..5b4cc9d 100644
--- a/Projects/vc15/saml/saml.vcxproj
+++ b/Projects/vc15/saml/saml.vcxproj
@@ -191,6 +191,7 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="..\..\..\saml\saml2\binding\impl\SAML2MessageEncoder.cpp" />
     <ClCompile Include="..\..\..\saml\saml2\metadata\impl\AbstractDynamicMetadataProvider.cpp" />
     <ClCompile Include="..\..\..\saml\saml2\metadata\impl\DiscoverableMetadataProvider.cpp" />
     <ClCompile Include="..\..\..\saml\saml2\metadata\impl\EntityAttributesEntityMatcher.cpp" />
@@ -297,6 +298,7 @@
     <ClInclude Include="..\..\..\saml\exceptions.h" />
     <ClInclude Include="..\..\..\saml\internal.h" />
     <ClInclude Include="..\..\..\saml\RootObject.h" />
+    <ClInclude Include="..\..\..\saml\saml2\binding\SAML2MessageEncoder.h" />
     <ClInclude Include="..\..\..\saml\saml2\metadata\AbstractDynamicMetadataProvider.h" />
     <ClInclude Include="..\..\..\saml\saml2\metadata\DiscoverableMetadataProvider.h" />
     <ClInclude Include="..\..\..\saml\saml2\metadata\EntityMatcher.h" />
@@ -346,4 +348,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
+</Project>
\ No newline at end of file
diff --git a/Projects/vc15/saml/saml.vcxproj.filters b/Projects/vc15/saml/saml.vcxproj.filters
index 4598f4b..bc13cb4 100644
--- a/Projects/vc15/saml/saml.vcxproj.filters
+++ b/Projects/vc15/saml/saml.vcxproj.filters
@@ -363,6 +363,9 @@
     <ClCompile Include="..\..\..\saml\saml2\metadata\impl\LocalDynamicMetadataProvider.cpp">
       <Filter>Source Files\saml2\metadata\impl</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\saml\saml2\binding\impl\SAML2MessageEncoder.cpp">
+      <Filter>Source Files\saml2\binding\impl</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\..\saml\Assertion.h">
@@ -497,6 +500,9 @@
     <ClInclude Include="..\..\..\saml\saml2\metadata\AbstractDynamicMetadataProvider.h">
       <Filter>Header Files\saml2\metadata</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\saml\saml2\binding\SAML2MessageEncoder.h">
+      <Filter>Header Files\saml2\binding</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\..\..\saml\saml.rc">
diff --git a/saml/Makefile.am b/saml/Makefile.am
index 251a314..48b003f 100644
--- a/saml/Makefile.am
+++ b/saml/Makefile.am
@@ -73,6 +73,7 @@ saml2bindinclude_HEADERS = \
 	saml2/binding/SAML2Artifact.h \
 	saml2/binding/SAML2ArtifactType0004.h \
 	saml2/binding/SAML2MessageDecoder.h \
+	saml2/binding/SAML2MessageEncoder.h \
 	saml2/binding/SAML2SOAPClient.h
 
 saml2mdinclude_HEADERS = \
@@ -161,6 +162,7 @@ libsaml_la_SOURCES = \
 	saml2/binding/impl/SAML2ECPDecoder.cpp \
 	saml2/binding/impl/SAML2ECPEncoder.cpp \
 	saml2/binding/impl/SAML2MessageDecoder.cpp \
+	saml2/binding/impl/SAML2MessageEncoder.cpp \
 	saml2/binding/impl/SAML2POSTDecoder.cpp \
 	saml2/binding/impl/SAML2POSTEncoder.cpp \
 	saml2/binding/impl/SAML2RedirectDecoder.cpp \
diff --git a/saml/binding/MessageDecoder.h b/saml/binding/MessageDecoder.h
index d93c7c9..c7f5186 100644
--- a/saml/binding/MessageDecoder.h
+++ b/saml/binding/MessageDecoder.h
@@ -34,6 +34,7 @@
 
 namespace xmltooling {
     class XMLTOOL_API GenericRequest;
+    class XMLTOOL_API GenericResponse;
     class XMLTOOL_API XMLObject;
 };
 
@@ -150,13 +151,9 @@ namespace opensaml {
         void setArtifactResolver(const ArtifactResolver* artifactResolver);
 
         /**
-         * Decodes a transport request into a SAML protocol message, and evaluates it
-         * against a supplied SecurityPolicy. If the transport request does not contain
-         * the information necessary to decode the request, nullptr will be returned.
-         * Errors during the decoding process will be raised as exceptions.
+         * @Deprecated
          *
-         * <p>Artifact-based bindings require an ArtifactResolver be set to
-         * turn an artifact into the corresponding message.
+         * The future version is the variant below that also takes a response object.
          *
          * @param relayState        will be set to RelayState/TARGET value accompanying message
          * @param genericRequest    reference to interface for accessing transport request to decode
@@ -167,7 +164,29 @@ namespace opensaml {
             std::string& relayState,
             const xmltooling::GenericRequest& genericRequest,
             SecurityPolicy& policy
-            ) const=0;
+            ) const;
+
+        /**
+        * Decodes a transport request into a SAML protocol message, and evaluates it
+        * against a supplied SecurityPolicy. If the transport request does not contain
+        * the information necessary to decode the request, nullptr will be returned.
+        * Errors during the decoding process will be raised as exceptions.
+        *
+        * <p>Artifact-based bindings require an ArtifactResolver be set to
+        * turn an artifact into the corresponding message.</p>
+        *
+        * @param relayState        will be set to RelayState/TARGET value accompanying message
+        * @param genericRequest    reference to interface for accessing transport request to decode
+        * @param genericResponse   optional interface for accessing transport response
+        * @param policy            reference to policy containing rules, MetadataProvider, TrustEngine, etc.
+        * @return  the decoded message, or nullptr if the decoder did not recognize the request content
+        */
+        virtual xmltooling::XMLObject* decode(
+            std::string& relayState,
+            const xmltooling::GenericRequest& genericRequest,
+            xmltooling::GenericResponse* genericResponse,
+            SecurityPolicy& policy
+        ) const;
 
     protected:
         MessageDecoder();
diff --git a/saml/binding/SecurityPolicy.h b/saml/binding/SecurityPolicy.h
index 47b29ec..b7ecc18 100644
--- a/saml/binding/SecurityPolicy.h
+++ b/saml/binding/SecurityPolicy.h
@@ -159,6 +159,14 @@ namespace opensaml {
         const XMLCh* getCorrelationID() const;
 
         /**
+        * Returns the message identifier to which the message being evaluated
+        * claims to be a response.
+        *
+        * @return correlatable message identifier
+        */
+        const XMLCh* getInResponseTo() const;
+
+        /**
          * Gets a mutable array of installed policy rules.
          *
          * <p>If adding rules, their lifetime must be at least as long as the policy object.
@@ -234,6 +242,14 @@ namespace opensaml {
         void setCorrelationID(const XMLCh* correlationID);
 
         /**
+        * Sets the message identifier to which the message being evaluated
+        * was responding (i.e., the value to be compared to the correlation ID).
+        *
+        * @param id correlatable message identifier
+        */
+        void setInResponseTo(const XMLCh* id);
+
+        /**
          * Evaluates the policy against the given request and message,
          * possibly populating message information in the policy object.
          *
@@ -418,7 +434,7 @@ namespace opensaml {
 
         // contextual information
         mutable time_t m_ts;
-        xmltooling::xstring m_correlationID;
+        xmltooling::xstring m_correlationID, m_inResponseTo;
         std::vector<xmltooling::xstring> m_audiences;
     };
 
diff --git a/saml/binding/impl/MessageDecoder.cpp b/saml/binding/impl/MessageDecoder.cpp
index 6e4bcc8..d2d60d9 100644
--- a/saml/binding/impl/MessageDecoder.cpp
+++ b/saml/binding/impl/MessageDecoder.cpp
@@ -25,6 +25,7 @@
  */
 
 #include "internal.h"
+#include "exceptions.h"
 #include "binding/MessageDecoder.h"
 #include "saml2/metadata/EndpointManager.h"
 #include "saml2/metadata/Metadata.h"
@@ -115,3 +116,23 @@ bool MessageDecoder::ArtifactResolver::isSupported(const SSODescriptorType& ssoD
 
     return false;
 }
+
+XMLObject* MessageDecoder::decode(
+    std::string& relayState,
+    const xmltooling::GenericRequest& genericRequest,
+    SecurityPolicy& policy
+    ) const
+{
+    // Default just calls new version.
+    return decode(relayState, genericRequest, nullptr, policy);
+}
+
+XMLObject* MessageDecoder::decode(
+    std::string& relayState,
+    const xmltooling::GenericRequest& genericRequest,
+    xmltooling::GenericResponse* genericResponse,
+    SecurityPolicy& policy
+    ) const
+{
+    throw BindingException("Unimplemented decode method");
+}
diff --git a/saml/binding/impl/MessageFlowRule.cpp b/saml/binding/impl/MessageFlowRule.cpp
index d9a4b96..ac98e94 100644
--- a/saml/binding/impl/MessageFlowRule.cpp
+++ b/saml/binding/impl/MessageFlowRule.cpp
@@ -54,6 +54,7 @@ namespace opensaml {
 
     private:
         bool m_checkReplay;
+        bool m_correlation;
         time_t m_expires;
     };
 
@@ -64,10 +65,12 @@ namespace opensaml {
 };
 
 static const XMLCh checkReplay[] = UNICODE_LITERAL_11(c,h,e,c,k,R,e,p,l,a,y);
+static const XMLCh checkCorrelation[] = UNICODE_LITERAL_16(c,h,e,c,k,C,o,r,r,e,l,a,t,i,o,n);
 static const XMLCh expires[] = UNICODE_LITERAL_7(e,x,p,i,r,e,s);
 
 MessageFlowRule::MessageFlowRule(const DOMElement* e)
     : m_checkReplay(XMLHelper::getAttrBool(e, true, checkReplay)),
+        m_correlation(XMLHelper::getAttrBool(e, false, checkCorrelation)),
         m_expires(XMLHelper::getAttrInt(e, XMLToolingConfig::getConfig().clock_skew_secs, expires))
 {
 }
@@ -96,6 +99,24 @@ bool MessageFlowRule::evaluate(const XMLObject& message, const GenericRequest* r
         }
     }
 
+    if (m_correlation) {
+        if (policy.getCorrelationID() && *(policy.getCorrelationID())) {
+            if (!XMLString::equals(policy.getCorrelationID(), policy.getInResponseTo())) {
+                log.warn("Response correlation ID did not match request ID");
+                auto_ptr_char requestID(policy.getCorrelationID());
+                throw SecurityPolicyException("Rejecting non-correlated response to request ID ($1).",
+                    params(1, requestID.get()));
+            }
+        }
+        else if (policy.getInResponseTo() && *(policy.getInResponseTo())) {
+            log.warn("Response correlation failed due to lack of request ID to compare against");
+            throw SecurityPolicyException("Response correlation failed with lack of correlation ID");
+        }
+    }
+    else {
+        log.debug("ignoring InResponseTo, correlation checking is disabled");
+    }
+
     // Check replay.
     if (m_checkReplay) {
         const XMLCh* id = policy.getMessageID();
diff --git a/saml/binding/impl/SecurityPolicy.cpp b/saml/binding/impl/SecurityPolicy.cpp
index e772a62..0cb436f 100644
--- a/saml/binding/impl/SecurityPolicy.cpp
+++ b/saml/binding/impl/SecurityPolicy.cpp
@@ -163,6 +163,11 @@ const XMLCh* SecurityPolicy::getCorrelationID() const
     return m_correlationID.c_str();
 }
 
+const XMLCh* SecurityPolicy::getInResponseTo() const
+{
+    return m_inResponseTo.c_str();
+}
+
 vector<const SecurityPolicyRule*>& SecurityPolicy::getRules()
 {
     return m_rules;
@@ -212,6 +217,13 @@ void SecurityPolicy::setCorrelationID(const XMLCh* correlationID)
         m_correlationID = correlationID;
 }
 
+void SecurityPolicy::setInResponseTo(const XMLCh* id)
+{
+    m_inResponseTo.erase();
+    if (id)
+        m_inResponseTo = id;
+}
+
 void SecurityPolicy::evaluate(const XMLObject& message, const GenericRequest* request)
 {
     for_each(
diff --git a/saml/saml1/binding/impl/SAML1ArtifactDecoder.cpp b/saml/saml1/binding/impl/SAML1ArtifactDecoder.cpp
index bb2a579..d131668 100644
--- a/saml/saml1/binding/impl/SAML1ArtifactDecoder.cpp
+++ b/saml/saml1/binding/impl/SAML1ArtifactDecoder.cpp
@@ -59,6 +59,7 @@ namespace opensaml {
             xmltooling::XMLObject* decode(
                 std::string& relayState,
                 const GenericRequest& genericRequest,
+                GenericResponse* genericResponse,
                 SecurityPolicy& policy
                 ) const;
         };
@@ -73,6 +74,7 @@ namespace opensaml {
 XMLObject* SAML1ArtifactDecoder::decode(
     string& relayState,
     const GenericRequest& genericRequest,
+    GenericResponse* genericResponse,
     SecurityPolicy& policy
     ) const
 {
diff --git a/saml/saml1/binding/impl/SAML1MessageDecoder.cpp b/saml/saml1/binding/impl/SAML1MessageDecoder.cpp
index bf5c673..4a37db9 100644
--- a/saml/saml1/binding/impl/SAML1MessageDecoder.cpp
+++ b/saml/saml1/binding/impl/SAML1MessageDecoder.cpp
@@ -89,6 +89,8 @@ void SAML1MessageDecoder::extractMessageDetails(
         return;
     }
 
+    policy.setInResponseTo(response->getInResponseTo());
+
     log.debug("extracting issuer from SAML 1.x Response");
     const vector<saml1::Assertion*>& assertions = response->getAssertions();
     if (assertions.empty()) {
diff --git a/saml/saml1/binding/impl/SAML1POSTDecoder.cpp b/saml/saml1/binding/impl/SAML1POSTDecoder.cpp
index d32a02d..83a17eb 100644
--- a/saml/saml1/binding/impl/SAML1POSTDecoder.cpp
+++ b/saml/saml1/binding/impl/SAML1POSTDecoder.cpp
@@ -62,6 +62,7 @@ namespace opensaml {
             xmltooling::XMLObject* decode(
                 std::string& relayState,
                 const GenericRequest& genericRequest,
+                GenericResponse* genericResponse,
                 SecurityPolicy& policy
                 ) const;
         };                
@@ -76,6 +77,7 @@ namespace opensaml {
 XMLObject* SAML1POSTDecoder::decode(
     string& relayState,
     const GenericRequest& genericRequest,
+    GenericResponse* genericResponse,
     SecurityPolicy& policy
     ) const
 {
diff --git a/saml/saml1/binding/impl/SAML1SOAPClient.cpp b/saml/saml1/binding/impl/SAML1SOAPClient.cpp
index e436b47..d57fe00 100644
--- a/saml/saml1/binding/impl/SAML1SOAPClient.cpp
+++ b/saml/saml1/binding/impl/SAML1SOAPClient.cpp
@@ -74,10 +74,6 @@ Response* SAML1SOAPClient::receiveSAML()
             Response* response = dynamic_cast<Response*>(body->getUnknownXMLObjects().front());
             if (response) {
 
-                // Check InResponseTo.
-                if (m_correlate && response->getInResponseTo() && !XMLString::equals(m_correlate, response->getInResponseTo()))
-                    throw SecurityPolicyException("InResponseTo attribute did not correlate with the Request ID.");
-                
                 m_soaper.getPolicy().reset(true);
 
                 // Extract Response details and run policy against it.
@@ -85,8 +81,11 @@ Response* SAML1SOAPClient::receiveSAML()
                 // alternate issuers at that layer.
                 m_soaper.getPolicy().setMessageID(response->getResponseID());
                 m_soaper.getPolicy().setIssueInstant(response->getIssueInstantEpoch());
+                m_soaper.getPolicy().setInResponseTo(response->getInResponseTo());
+                m_soaper.getPolicy().setCorrelationID(m_correlate);
+
                 m_soaper.getPolicy().evaluate(*response);
-                
+
                 // Check Status.
                 Status* status = response->getStatus();
                 if (status) {
diff --git a/saml/saml1/binding/impl/SAML1SOAPDecoder.cpp b/saml/saml1/binding/impl/SAML1SOAPDecoder.cpp
index 2dbba34..8aaf548 100644
--- a/saml/saml1/binding/impl/SAML1SOAPDecoder.cpp
+++ b/saml/saml1/binding/impl/SAML1SOAPDecoder.cpp
@@ -60,6 +60,7 @@ namespace opensaml {
             xmltooling::XMLObject* decode(
                 std::string& relayState,
                 const GenericRequest& genericRequest,
+                GenericResponse* genericResponse,
                 SecurityPolicy& policy
                 ) const;
         };                
@@ -74,6 +75,7 @@ namespace opensaml {
 XMLObject* SAML1SOAPDecoder::decode(
     string& relayState,
     const GenericRequest& genericRequest,
+    GenericResponse* genericResponse,
     SecurityPolicy& policy
     ) const
 {
diff --git a/saml/saml2/binding/SAML2MessageDecoder.h b/saml/saml2/binding/SAML2MessageDecoder.h
index 6248468..a659b15 100644
--- a/saml/saml2/binding/SAML2MessageDecoder.h
+++ b/saml/saml2/binding/SAML2MessageDecoder.h
@@ -29,6 +29,11 @@
 
 #include <saml/binding/MessageDecoder.h>
 
+namespace xmltooling {
+    class HTTPRequest;
+    class HTTPResponse;
+}
+
 namespace opensaml {
     namespace saml2p {
         
@@ -41,6 +46,22 @@ namespace opensaml {
             SAML2MessageDecoder();
             virtual ~SAML2MessageDecoder();
 
+            /**
+            * If relay state is provided, the previous request ID is extracted from a correlation cookie
+            * and supplied to the policy.
+            *
+            * @param request HTTP request
+            * @param response optional HTTP response
+            * @param relayState the RelayState token
+            * @param policy the SecurityPolicy to attach the ID to
+            */
+            void extractCorrelationID(
+                const xmltooling::HTTPRequest& request,
+                xmltooling::HTTPResponse* response,
+                const std::string& relayState,
+                SecurityPolicy& policy
+                ) const;
+
         public:
             const XMLCh* getProtocolFamily() const;
             void extractMessageDetails (
diff --git a/saml/saml2/binding/SAML2MessageDecoder.h b/saml/saml2/binding/SAML2MessageEncoder.h
similarity index 56%
copy from saml/saml2/binding/SAML2MessageDecoder.h
copy to saml/saml2/binding/SAML2MessageEncoder.h
index 6248468..8b153b5 100644
--- a/saml/saml2/binding/SAML2MessageDecoder.h
+++ b/saml/saml2/binding/SAML2MessageEncoder.h
@@ -24,33 +24,43 @@
  * Base class for SAML 2.0 MessageDecoders.
  */
 
-#ifndef __saml2_msgdecoder_h__
-#define __saml2_msgdecoder_h__
+#ifndef __saml2_msgencoder_h__
+#define __saml2_msgencoder_h__
 
-#include <saml/binding/MessageDecoder.h>
+#include <saml/binding/MessageEncoder.h>
+#include <saml/saml2/core/Protocols.h>
+
+namespace xmltooling {
+    class HTTPResponse;
+}
 
 namespace opensaml {
     namespace saml2p {
         
         /**
-         *  Base class for SAML 2.0 MessageDecoders.
+         *  Base class for SAML 2.0 MessageEncoders.
          */
-        class SAML_API SAML2MessageDecoder : public MessageDecoder
+        class SAML_API SAML2MessageEncoder : public MessageEncoder
         {
         protected:
-            SAML2MessageDecoder();
-            virtual ~SAML2MessageDecoder();
+            SAML2MessageEncoder();
+            virtual ~SAML2MessageEncoder();
+
+            /**
+             * If the message is a request and relay state is provided, the request ID is preserved in a correlation cookie.
+             *
+             * @param response HTTP response
+             * @param message the SAML message
+             * @param relayState the RelayState token
+             */
+            void preserveCorrelationID(
+                xmltooling::HTTPResponse& response, const RequestAbstractType& message, const char* relayState
+                ) const;
 
         public:
             const XMLCh* getProtocolFamily() const;
-            void extractMessageDetails (
-                const xmltooling::XMLObject& message,
-                const xmltooling::GenericRequest& genericRequest,
-                const XMLCh* protocol,
-                SecurityPolicy& policy
-                ) const;
         };
     };
 };
 
-#endif /* __saml2_msgdecoder_h__ */
+#endif /* __saml2_msgencoder_h__ */
diff --git a/saml/saml2/binding/impl/SAML2ArtifactDecoder.cpp b/saml/saml2/binding/impl/SAML2ArtifactDecoder.cpp
index dd35c99..724c01e 100644
--- a/saml/saml2/binding/impl/SAML2ArtifactDecoder.cpp
+++ b/saml/saml2/binding/impl/SAML2ArtifactDecoder.cpp
@@ -37,6 +37,7 @@
 #include <xmltooling/logging.h>
 #include <xmltooling/XMLToolingConfig.h>
 #include <xmltooling/io/HTTPRequest.h>
+#include <xmltooling/io/HTTPResponse.h>
 #include <xmltooling/util/NDC.h>
 #include <xmltooling/util/ReplayCache.h>
 
@@ -60,6 +61,7 @@ namespace opensaml {
             xmltooling::XMLObject* decode(
                 std::string& relayState,
                 const GenericRequest& genericRequest,
+                GenericResponse* genericResponse,
                 SecurityPolicy& policy
                 ) const;
         };
@@ -74,6 +76,7 @@ namespace opensaml {
 XMLObject* SAML2ArtifactDecoder::decode(
     string& relayState,
     const GenericRequest& genericRequest,
+    GenericResponse* genericResponse,
     SecurityPolicy& policy
     ) const
 {
@@ -169,6 +172,7 @@ XMLObject* SAML2ArtifactDecoder::decode(
         throw BindingException("ArtifactResponse message did not contain a protocol message.");
     }
     extractMessageDetails(*payload, genericRequest, samlconstants::SAML20P_NS, policy);
+    extractCorrelationID(*httpRequest, dynamic_cast<HTTPResponse*>(genericResponse), relayState, policy);
     policy.evaluate(*payload, &genericRequest);
 
     // Return the payload only.
diff --git a/saml/saml2/binding/impl/SAML2ArtifactEncoder.cpp b/saml/saml2/binding/impl/SAML2ArtifactEncoder.cpp
index caa095d..4b26087 100644
--- a/saml/saml2/binding/impl/SAML2ArtifactEncoder.cpp
+++ b/saml/saml2/binding/impl/SAML2ArtifactEncoder.cpp
@@ -27,8 +27,8 @@
 #include "internal.h"
 #include "exceptions.h"
 #include "binding/ArtifactMap.h"
-#include "binding/MessageEncoder.h"
 #include "saml2/binding/SAML2Artifact.h"
+#include "saml2/binding/SAML2MessageEncoder.h"
 #include "saml2/core/Protocols.h"
 #include "saml2/metadata/Metadata.h"
 #include "signature/ContentReference.h"
@@ -56,16 +56,12 @@ using boost::scoped_ptr;
 
 namespace opensaml {
     namespace saml2p {              
-        class SAML_DLLLOCAL SAML2ArtifactEncoder : public MessageEncoder
+        class SAML_DLLLOCAL SAML2ArtifactEncoder : public SAML2MessageEncoder
         {
         public:
             SAML2ArtifactEncoder(const DOMElement* e);
             virtual ~SAML2ArtifactEncoder() {}
 
-            const XMLCh* getProtocolFamily() const {
-                return samlconstants::SAML20P_NS;
-            }
-
             long encode(
                 GenericResponse& genericResponse,
                 XMLObject* xmlObject,
@@ -159,7 +155,10 @@ long SAML2ArtifactEncoder::encode(
 
     StatusResponseType* response = nullptr;
     RequestAbstractType* request = dynamic_cast<RequestAbstractType*>(xmlObject);
-    if (!request) {
+    if (request) {
+        preserveCorrelationID(*httpResponse, *request, relayState);
+    }
+    else {
         response = dynamic_cast<StatusResponseType*>(xmlObject);
         if (!response)
             throw BindingException("XML content for SAML 2.0 HTTP-Artifact Encoder must be a SAML 2.0 protocol message.");
diff --git a/saml/saml2/binding/impl/SAML2ECPDecoder.cpp b/saml/saml2/binding/impl/SAML2ECPDecoder.cpp
index f57e593..610ac2f 100644
--- a/saml/saml2/binding/impl/SAML2ECPDecoder.cpp
+++ b/saml/saml2/binding/impl/SAML2ECPDecoder.cpp
@@ -33,6 +33,7 @@
 #include <xmltooling/logging.h>
 #include <xmltooling/XMLToolingConfig.h>
 #include <xmltooling/io/HTTPRequest.h>
+#include <xmltooling/io/HTTPResponse.h>
 #include <xmltooling/soap/SOAP.h>
 #include <xmltooling/util/NDC.h>
 #include <xmltooling/util/ParserPool.h>
@@ -57,6 +58,7 @@ namespace opensaml {
             xmltooling::XMLObject* decode(
                 std::string& relayState,
                 const GenericRequest& genericRequest,
+                GenericResponse* genericResponse,
                 SecurityPolicy& policy
                 ) const;
         };                
@@ -71,6 +73,7 @@ namespace opensaml {
 XMLObject* SAML2ECPDecoder::decode(
     string& relayState,
     const GenericRequest& genericRequest,
+    GenericResponse* genericResponse,
     SecurityPolicy& policy
     ) const
 {
@@ -112,11 +115,29 @@ XMLObject* SAML2ECPDecoder::decode(
     if (body && body->hasChildren()) {
         Response* response = dynamic_cast<Response*>(body->getUnknownXMLObjects().front());
         if (response) {
+
+            // Check for RelayState header.
+            if (env->getHeader()) {
+                static const XMLCh RelayState[] = UNICODE_LITERAL_10(R,e,l,a,y,S,t,a,t,e);
+                const vector<XMLObject*>& blocks = const_cast<const Header*>(env->getHeader())->getUnknownXMLObjects();
+                vector<XMLObject*>::const_iterator h =
+                    find_if(blocks.begin(), blocks.end(), hasQName(xmltooling::QName(samlconstants::SAML20ECP_NS, RelayState)));
+                const ElementProxy* ep = dynamic_cast<const ElementProxy*>(h != blocks.end() ? *h : nullptr);
+                if (ep) {
+                    auto_ptr_char rs(ep->getTextContent());
+                    if (rs.get())
+                        relayState = rs.get();
+                }
+            }
+
             // Run through the policy at two layers.
             extractMessageDetails(*env, genericRequest, samlconstants::SAML20P_NS, policy);
             policy.evaluate(*env, &genericRequest);
             policy.reset(true);
             extractMessageDetails(*response, genericRequest, samlconstants::SAML20P_NS, policy);
+            if (httpRequest) {
+                extractCorrelationID(*httpRequest, dynamic_cast<HTTPResponse*>(genericResponse), relayState, policy);
+            }
             policy.evaluate(*response, &genericRequest);
 
             // Check destination URL if this is HTTP.
@@ -133,20 +154,6 @@ XMLObject* SAML2ECPDecoder::decode(
                     throw BindingException("SAML message delivered with PAOS to incorrect server URL.");
                 }
             }
-
-            // Check for RelayState header.
-            if (env->getHeader()) {
-                static const XMLCh RelayState[] = UNICODE_LITERAL_10(R,e,l,a,y,S,t,a,t,e);
-                const vector<XMLObject*>& blocks = const_cast<const Header*>(env->getHeader())->getUnknownXMLObjects();
-                vector<XMLObject*>::const_iterator h =
-                    find_if(blocks.begin(), blocks.end(), hasQName(xmltooling::QName(samlconstants::SAML20ECP_NS, RelayState)));
-                const ElementProxy* ep = dynamic_cast<const ElementProxy*>(h != blocks.end() ? *h : nullptr);
-                if (ep) {
-                    auto_ptr_char rs(ep->getTextContent());
-                    if (rs.get())
-                        relayState = rs.get();
-                }
-            }
             
             xmlObject.release();
             body->detach(); // frees Envelope
diff --git a/saml/saml2/binding/impl/SAML2ECPEncoder.cpp b/saml/saml2/binding/impl/SAML2ECPEncoder.cpp
index 9842605..1f3741a 100644
--- a/saml/saml2/binding/impl/SAML2ECPEncoder.cpp
+++ b/saml/saml2/binding/impl/SAML2ECPEncoder.cpp
@@ -26,9 +26,9 @@
 
 #include "internal.h"
 #include "exceptions.h"
-#include "binding/MessageEncoder.h"
 #include "signature/ContentReference.h"
 #include "saml1/core/Protocols.h"
+#include "saml2/binding/SAML2MessageEncoder.h"
 #include "saml2/core/Protocols.h"
 
 #include <sstream>
@@ -57,16 +57,12 @@ namespace opensaml {
         
         static const XMLCh ProviderName[] = UNICODE_LITERAL_12(P, r, o, v, i, d, e, r, N, a, m, e);
 
-        class SAML_DLLLOCAL SAML2ECPEncoder : public MessageEncoder
+        class SAML_DLLLOCAL SAML2ECPEncoder : public SAML2MessageEncoder
         {
         public:
             SAML2ECPEncoder(const DOMElement* e);
             virtual ~SAML2ECPEncoder() {}
 
-            const XMLCh* getProtocolFamily() const {
-                return samlconstants::SAML20P_NS;
-            }
-
             long encode(
                 GenericResponse& genericResponse,
                 XMLObject* xmlObject,
@@ -154,6 +150,9 @@ long SAML2ECPEncoder::encode(
         httpResponse->setResponseHeader("Expires", "01-Jan-1997 12:00:00 GMT");
         httpResponse->setResponseHeader("Cache-Control", "no-cache, no-store, must-revalidate, private");
         httpResponse->setResponseHeader("Pragma", "no-cache");
+        if (request) {
+            preserveCorrelationID(*httpResponse, *request, relayState);
+        }
     }
 
     // Wrap it in a SOAP envelope.
diff --git a/saml/saml2/binding/impl/SAML2MessageDecoder.cpp b/saml/saml2/binding/impl/SAML2MessageDecoder.cpp
index d2e0a82..18e25d9 100644
--- a/saml/saml2/binding/impl/SAML2MessageDecoder.cpp
+++ b/saml/saml2/binding/impl/SAML2MessageDecoder.cpp
@@ -33,6 +33,10 @@
 #include "util/SAMLConstants.h"
 
 #include <xmltooling/logging.h>
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/io/HTTPRequest.h>
+#include <xmltooling/io/HTTPResponse.h>
+#include <xmltooling/util/URLEncoder.h>
 
 using namespace opensaml::saml2md;
 using namespace opensaml::saml2p;
@@ -55,6 +59,36 @@ const XMLCh* SAML2MessageDecoder::getProtocolFamily() const
     return samlconstants::SAML20P_NS;
 }
 
+void SAML2MessageDecoder::extractCorrelationID(
+    const HTTPRequest& request, HTTPResponse* response, const string& relayState, SecurityPolicy& policy
+    ) const
+{
+    Category& log = Category::getInstance(SAML_LOGCAT ".MessageDecoder.SAML2");
+
+    if (!relayState.empty()) {
+        string cookie_name = string("_opensaml_req_").append(
+            XMLToolingConfig::getConfig().getURLEncoder()->encode(relayState.c_str()));
+        const char* cookie = request.getCookie(cookie_name.c_str());
+        if (cookie && *cookie) {
+            log.debug("recovered request/response correlation value (%s)", cookie);
+            char* dup = strdup(cookie);
+            XMLToolingConfig::getConfig().getURLEncoder()->decode(dup);
+            auto_ptr_XMLCh corrID(dup);
+            free(dup);
+            policy.setCorrelationID(corrID.get());
+            if (response) {
+                response->setCookie(cookie_name.c_str(), nullptr, 0, HTTPResponse::SAMESITE_NONE);
+            }
+        }
+        else {
+            log.debug("no request/response correlation cookie found");
+        }
+    }
+    else {
+        log.debug("no RelayState, unable to search for request/response correlation cookie");
+    }
+}
+
 void SAML2MessageDecoder::extractMessageDetails(
     const XMLObject& message, const GenericRequest& request, const XMLCh* protocol, SecurityPolicy& policy
     ) const
@@ -71,6 +105,11 @@ void SAML2MessageDecoder::extractMessageDetails(
         policy.setMessageID(samlRoot.getID());
         policy.setIssueInstant(samlRoot.getIssueInstantEpoch());
 
+        const saml2p::StatusResponseType* statusResponse = dynamic_cast<const saml2p::StatusResponseType*>(&message);
+        if (statusResponse) {
+            policy.setInResponseTo(statusResponse->getInResponseTo());
+        }
+
         log.debug("extracting issuer from SAML 2.0 protocol message");
         const Issuer* issuer = samlRoot.getIssuer();
         if (issuer) {
diff --git a/saml/saml2/binding/impl/SAML2MessageEncoder.cpp b/saml/saml2/binding/impl/SAML2MessageEncoder.cpp
new file mode 100644
index 0000000..673dff9
--- /dev/null
+++ b/saml/saml2/binding/impl/SAML2MessageEncoder.cpp
@@ -0,0 +1,74 @@
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * UCAID 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.
+ */
+
+/**
+ * SAML2MessageEncoder.cpp
+ *
+ * Base class for SAML 2.0 MessageEncoders.
+ */
+
+#include "internal.h"
+#include "saml2/binding/SAML2MessageEncoder.h"
+#include "saml2/core/Protocols.h"
+#include "util/SAMLConstants.h"
+
+#include <xmltooling/logging.h>
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/io/HTTPResponse.h>
+#include <xmltooling/util/URLEncoder.h>
+
+using namespace opensaml::saml2p;
+using namespace opensaml::saml2;
+using namespace opensaml;
+using namespace xmltooling::logging;
+using namespace xmltooling;
+using namespace std;
+
+SAML2MessageEncoder::SAML2MessageEncoder()
+{
+}
+
+SAML2MessageEncoder::~SAML2MessageEncoder()
+{
+}
+
+const XMLCh* SAML2MessageEncoder::getProtocolFamily() const
+{
+    return samlconstants::SAML20P_NS;
+}
+
+void SAML2MessageEncoder::preserveCorrelationID(HTTPResponse& response, const RequestAbstractType& message, const char* relayState) const
+{
+    Category& log = Category::getInstance(SAML_LOGCAT ".MessageEncoder.SAML2");
+
+    if (relayState && *relayState) {
+        string cookie_name = string("_opensaml_req_").append(
+            XMLToolingConfig::getConfig().getURLEncoder()->encode(relayState));
+        auto_ptr_char id(message.getID());
+        log.debug("tracking request (%s) against RelayState token (%s)", id.get(), relayState);
+        response.setCookie(cookie_name.c_str(),
+            XMLToolingConfig::getConfig().getURLEncoder()->encode(id.get()).c_str(),
+            0,
+            HTTPResponse::SAMESITE_NONE);
+    }
+    else {
+        log.debug("no relay state, request/response correlation is disabled");
+    }
+}
diff --git a/saml/saml2/binding/impl/SAML2POSTDecoder.cpp b/saml/saml2/binding/impl/SAML2POSTDecoder.cpp
index 1b7900e..534b8ff 100644
--- a/saml/saml2/binding/impl/SAML2POSTDecoder.cpp
+++ b/saml/saml2/binding/impl/SAML2POSTDecoder.cpp
@@ -38,6 +38,7 @@
 #include <xmltooling/logging.h>
 #include <xmltooling/XMLToolingConfig.h>
 #include <xmltooling/io/HTTPRequest.h>
+#include <xmltooling/io/HTTPResponse.h>
 #include <xmltooling/util/NDC.h>
 #include <xmltooling/util/ParserPool.h>
 #include <xmltooling/validation/ValidatorSuite.h>
@@ -61,6 +62,7 @@ namespace opensaml {
             xmltooling::XMLObject* decode(
                 std::string& relayState,
                 const GenericRequest& genericRequest,
+                GenericResponse* genericResponse,
                 SecurityPolicy& policy
                 ) const;
         };                
@@ -75,6 +77,7 @@ namespace opensaml {
 XMLObject* SAML2POSTDecoder::decode(
     std::string& relayState,
     const GenericRequest& genericRequest,
+    GenericResponse* genericResponse,
     SecurityPolicy& policy
     ) const
 {
@@ -133,6 +136,7 @@ XMLObject* SAML2POSTDecoder::decode(
 
     // Run through the policy.
     extractMessageDetails(*root, genericRequest, samlconstants::SAML20P_NS, policy);
+    extractCorrelationID(*httpRequest, dynamic_cast<HTTPResponse*>(genericResponse), relayState, policy);
     policy.evaluate(*root, &genericRequest);
     
     // Check destination URL.
diff --git a/saml/saml2/binding/impl/SAML2POSTEncoder.cpp b/saml/saml2/binding/impl/SAML2POSTEncoder.cpp
index 453865a..ed84214 100644
--- a/saml/saml2/binding/impl/SAML2POSTEncoder.cpp
+++ b/saml/saml2/binding/impl/SAML2POSTEncoder.cpp
@@ -26,8 +26,8 @@
 
 #include "internal.h"
 #include "exceptions.h"
-#include "binding/MessageEncoder.h"
 #include "signature/ContentReference.h"
+#include "saml2/binding/SAML2MessageEncoder.h"
 #include "saml2/core/Protocols.h"
 
 #include <fstream>
@@ -56,16 +56,12 @@ using boost::scoped_ptr;
 
 namespace opensaml {
     namespace saml2p {              
-        class SAML_DLLLOCAL SAML2POSTEncoder : public MessageEncoder
+        class SAML_DLLLOCAL SAML2POSTEncoder : public SAML2MessageEncoder
         {
         public:
             SAML2POSTEncoder(const DOMElement* e, bool simple=false);
             virtual ~SAML2POSTEncoder() {}
 
-            const XMLCh* getProtocolFamily() const {
-                return samlconstants::SAML20P_NS;
-            }
-
             long encode(
                 GenericResponse& genericResponse,
                 XMLObject* xmlObject,
@@ -249,6 +245,9 @@ long SAML2POSTEncoder::encode(
         httpResponse->setResponseHeader("Expires", "01-Jan-1997 12:00:00 GMT");
         httpResponse->setResponseHeader("Cache-Control", "no-cache, no-store, must-revalidate, private");
         httpResponse->setResponseHeader("Pragma", "no-cache");
+        if (request) {
+            preserveCorrelationID(*httpResponse, *request, relayState);
+        }
     }
     long ret = genericResponse.sendResponse(s);
 
diff --git a/saml/saml2/binding/impl/SAML2RedirectDecoder.cpp b/saml/saml2/binding/impl/SAML2RedirectDecoder.cpp
index f58c819..ca46e57 100644
--- a/saml/saml2/binding/impl/SAML2RedirectDecoder.cpp
+++ b/saml/saml2/binding/impl/SAML2RedirectDecoder.cpp
@@ -36,6 +36,7 @@
 #include <xmltooling/logging.h>
 #include <xmltooling/XMLToolingConfig.h>
 #include <xmltooling/io/HTTPRequest.h>
+#include <xmltooling/io/HTTPResponse.h>
 #include <xmltooling/util/NDC.h>
 #include <xmltooling/util/ParserPool.h>
 #include <xmltooling/util/XMLHelper.h>
@@ -61,6 +62,7 @@ namespace opensaml {
             xmltooling::XMLObject* decode(
                 std::string& relayState,
                 const GenericRequest& genericRequest,
+                GenericResponse* genericResponse,
                 SecurityPolicy& policy
                 ) const;
         };
@@ -75,6 +77,7 @@ namespace opensaml {
 XMLObject* SAML2RedirectDecoder::decode(
     string& relayState,
     const GenericRequest& genericRequest,
+    GenericResponse* genericResponse,
     SecurityPolicy& policy
     ) const
 {
@@ -144,6 +147,7 @@ XMLObject* SAML2RedirectDecoder::decode(
 
     // Run through the policy.
     extractMessageDetails(*root, genericRequest, samlconstants::SAML20P_NS, policy);
+    extractCorrelationID(*httpRequest, dynamic_cast<HTTPResponse*>(genericResponse), relayState, policy);
     policy.evaluate(*root, &genericRequest);
 
     // Check destination URL.
diff --git a/saml/saml2/binding/impl/SAML2RedirectEncoder.cpp b/saml/saml2/binding/impl/SAML2RedirectEncoder.cpp
index ea98c7b..2e867d3 100644
--- a/saml/saml2/binding/impl/SAML2RedirectEncoder.cpp
+++ b/saml/saml2/binding/impl/SAML2RedirectEncoder.cpp
@@ -26,8 +26,7 @@
 
 #include "internal.h"
 #include "exceptions.h"
-#include "binding/MessageEncoder.h"
-#include "saml2/core/Protocols.h"
+#include "saml2/binding/SAML2MessageEncoder.h"
 
 #include <fstream>
 #include <sstream>
@@ -52,7 +51,7 @@ using namespace std;
 
 namespace opensaml {
     namespace saml2p {              
-        class SAML_DLLLOCAL SAML2RedirectEncoder : public MessageEncoder
+        class SAML_DLLLOCAL SAML2RedirectEncoder : public SAML2MessageEncoder
         {
         public:
             SAML2RedirectEncoder() {}
@@ -62,10 +61,6 @@ namespace opensaml {
                 return true;
             }
 
-            const XMLCh* getProtocolFamily() const {
-                return samlconstants::SAML20P_NS;
-            }
-
             long encode(
                 GenericResponse& genericResponse,
                 XMLObject* xmlObject,
@@ -112,7 +107,10 @@ long SAML2RedirectEncoder::encode(
     
     StatusResponseType* response = nullptr;
     RequestAbstractType* request = dynamic_cast<RequestAbstractType*>(xmlObject);
-    if (!request) {
+    if (request) {
+        preserveCorrelationID(*httpResponse, *request, relayState);
+    }
+    else {
         response = dynamic_cast<StatusResponseType*>(xmlObject);
         if (!response)
             throw BindingException("XML content for SAML 2.0 HTTP-Redirect Encoder must be a SAML 2.0 protocol message.");
diff --git a/saml/saml2/binding/impl/SAML2SOAPClient.cpp b/saml/saml2/binding/impl/SAML2SOAPClient.cpp
index 36898c2..da45caf 100644
--- a/saml/saml2/binding/impl/SAML2SOAPClient.cpp
+++ b/saml/saml2/binding/impl/SAML2SOAPClient.cpp
@@ -76,16 +76,14 @@ StatusResponseType* SAML2SOAPClient::receiveSAML()
             // Check for SAML Response.
             StatusResponseType* response = dynamic_cast<StatusResponseType*>(body->getUnknownXMLObjects().front());
             if (response) {
-                // Check InResponseTo.
-                if (m_correlate && response->getInResponseTo() && !XMLString::equals(m_correlate, response->getInResponseTo()))
-                    throw SecurityPolicyException("InResponseTo attribute did not correlate with the Request ID.");
-
                 SecurityPolicy& policy = m_soaper.getPolicy();
                 policy.reset(true);
 
                 // Extract Response details.
                 policy.setMessageID(response->getID());
                 policy.setIssueInstant(response->getIssueInstantEpoch());
+                policy.setInResponseTo(response->getInResponseTo());
+                policy.setCorrelationID(m_correlate);
 
                 // Extract and re-verify Issuer if present.
                 const Issuer* issuer = response->getIssuer();
diff --git a/saml/saml2/binding/impl/SAML2SOAPDecoder.cpp b/saml/saml2/binding/impl/SAML2SOAPDecoder.cpp
index b16744f..812342b 100644
--- a/saml/saml2/binding/impl/SAML2SOAPDecoder.cpp
+++ b/saml/saml2/binding/impl/SAML2SOAPDecoder.cpp
@@ -60,6 +60,7 @@ namespace opensaml {
             xmltooling::XMLObject* decode(
                 std::string& relayState,
                 const GenericRequest& genericRequest,
+                GenericResponse* genericResponse,
                 SecurityPolicy& policy
                 ) const;
         };                
@@ -74,6 +75,7 @@ namespace opensaml {
 XMLObject* SAML2SOAPDecoder::decode(
     string& relayState,
     const GenericRequest& genericRequest,
+    GenericResponse* genericResponse,
     SecurityPolicy& policy
     ) const
 {
diff --git a/saml/saml2/binding/impl/SAML2SOAPEncoder.cpp b/saml/saml2/binding/impl/SAML2SOAPEncoder.cpp
index ac9f1e9..a8785b0 100644
--- a/saml/saml2/binding/impl/SAML2SOAPEncoder.cpp
+++ b/saml/saml2/binding/impl/SAML2SOAPEncoder.cpp
@@ -26,8 +26,8 @@
 
 #include "internal.h"
 #include "exceptions.h"
-#include "binding/MessageEncoder.h"
 #include "signature/ContentReference.h"
+#include "saml2/binding/SAML2MessageEncoder.h"
 #include "saml2/core/Protocols.h"
 
 #include <sstream>
@@ -48,7 +48,7 @@ using namespace std;
 
 namespace opensaml {
     namespace saml2p {              
-        class SAML_DLLLOCAL SAML2SOAPEncoder : public MessageEncoder
+        class SAML_DLLLOCAL SAML2SOAPEncoder : public SAML2MessageEncoder
         {
         public:
             SAML2SOAPEncoder() {}
@@ -58,10 +58,6 @@ namespace opensaml {
                 return false;
             }
 
-            const XMLCh* getProtocolFamily() const {
-                return samlconstants::SAML20P_NS;
-            }
-
             long encode(
                 GenericResponse& genericResponse,
                 XMLObject* xmlObject,
diff --git a/saml/saml2/profile/impl/BearerConfirmationRule.cpp b/saml/saml2/profile/impl/BearerConfirmationRule.cpp
index 8406348..745fa0e 100644
--- a/saml/saml2/profile/impl/BearerConfirmationRule.cpp
+++ b/saml/saml2/profile/impl/BearerConfirmationRule.cpp
@@ -107,12 +107,21 @@ bool BearerConfirmationRule::evaluate(const XMLObject& message, const GenericReq
                     }
                 }
 
-                if (m_correlation && policy.getCorrelationID() && *(policy.getCorrelationID())) {
-                    if (!XMLString::equals(policy.getCorrelationID(), data ? data->getInResponseTo() : nullptr)) {
-                        msg = "bearer confirmation failed with request correlation mismatch";
+                if (m_correlation) {
+                    if (policy.getCorrelationID() && *(policy.getCorrelationID())) {
+                        if (!XMLString::equals(policy.getCorrelationID(), data ? data->getInResponseTo() : nullptr)) {
+                            msg = "bearer confirmation failed with request correlation mismatch";
+                            continue;
+                        }
+                    }
+                    else if (data && data->getInResponseTo() && *(data->getInResponseTo())) {
+                        msg = "bearer confirmation issued in response to request failed with lack of correlation ID";
                         continue;
                     }
                 }
+                else {
+                    log.debug("ignoring InResponseTo, correlation checking is disabled");
+                }
 
                 if (m_validity) {
                     if (!data || !data->getNotOnOrAfter()) {
diff --git a/samltest/saml1/binding/SAML1ArtifactTest.h b/samltest/saml1/binding/SAML1ArtifactTest.h
index 1a7dc54..1d4d09d 100644
--- a/samltest/saml1/binding/SAML1ArtifactTest.h
+++ b/samltest/saml1/binding/SAML1ArtifactTest.h
@@ -86,7 +86,7 @@ public:
                 SAMLConfig::getConfig().MessageDecoderManager.newPlugin(samlconstants::SAML1_PROFILE_BROWSER_ARTIFACT, nullptr, false)
                 );
             decoder->setArtifactResolver(this);
-            scoped_ptr<Response> response(dynamic_cast<Response*>(decoder->decode(relayState,*this,policy)));
+            scoped_ptr<Response> response(dynamic_cast<Response*>(decoder->decode(relayState, *this, this, policy)));
             
             // Test the results.
             TSM_ASSERT_EQUALS("TARGET was not the expected result.", relayState, "state");
diff --git a/samltest/saml1/binding/SAML1POSTTest.h b/samltest/saml1/binding/SAML1POSTTest.h
index 8747cda..b4ac07e 100644
--- a/samltest/saml1/binding/SAML1POSTTest.h
+++ b/samltest/saml1/binding/SAML1POSTTest.h
@@ -93,7 +93,7 @@ public:
             scoped_ptr<MessageDecoder> decoder(
                 SAMLConfig::getConfig().MessageDecoderManager.newPlugin(samlconstants::SAML1_PROFILE_BROWSER_POST, nullptr, false)
                 );
-            scoped_ptr<Response> response(dynamic_cast<Response*>(decoder->decode(relayState,*this,policy)));
+            scoped_ptr<Response> response(dynamic_cast<Response*>(decoder->decode(relayState, *this, this, policy)));
             
             // Test the results.
             TSM_ASSERT_EQUALS("TARGET was not the expected result.", relayState, "state");
diff --git a/samltest/saml2/binding/SAML2ArtifactTest.h b/samltest/saml2/binding/SAML2ArtifactTest.h
index 1c6cd72..f71bcfd 100644
--- a/samltest/saml2/binding/SAML2ArtifactTest.h
+++ b/samltest/saml2/binding/SAML2ArtifactTest.h
@@ -87,7 +87,7 @@ public:
                 SAMLConfig::getConfig().MessageDecoderManager.newPlugin(samlconstants::SAML20_BINDING_HTTP_ARTIFACT, nullptr, false)
                 );
             decoder->setArtifactResolver(this);
-            scoped_ptr<Response> response(dynamic_cast<Response*>(decoder->decode(relayState,*this,policy)));
+            scoped_ptr<Response> response(dynamic_cast<Response*>(decoder->decode(relayState, *this, this, policy)));
             
             // Test the results.
             TSM_ASSERT_EQUALS("RelayState was not the expected result.", relayState, "state");
diff --git a/samltest/saml2/binding/SAML2POSTTest.h b/samltest/saml2/binding/SAML2POSTTest.h
index e6780e0..332795b 100644
--- a/samltest/saml2/binding/SAML2POSTTest.h
+++ b/samltest/saml2/binding/SAML2POSTTest.h
@@ -92,7 +92,7 @@ public:
             scoped_ptr<MessageDecoder> decoder(
                 SAMLConfig::getConfig().MessageDecoderManager.newPlugin(samlconstants::SAML20_BINDING_HTTP_POST, nullptr, false)
                 );
-            scoped_ptr<Response> response(dynamic_cast<Response*>(decoder->decode(relayState,*this,policy)));
+            scoped_ptr<Response> response(dynamic_cast<Response*>(decoder->decode(relayState, *this, this, policy)));
             
             // Test the results.
             TSM_ASSERT_EQUALS("RelayState was not the expected result.", relayState, "state");
diff --git a/samltest/saml2/binding/SAML2RedirectTest.h b/samltest/saml2/binding/SAML2RedirectTest.h
index 85bad24..c7f9f80 100644
--- a/samltest/saml2/binding/SAML2RedirectTest.h
+++ b/samltest/saml2/binding/SAML2RedirectTest.h
@@ -82,7 +82,7 @@ public:
             scoped_ptr<MessageDecoder> decoder(
                 SAMLConfig::getConfig().MessageDecoderManager.newPlugin(samlconstants::SAML20_BINDING_HTTP_REDIRECT, nullptr, false)
                 );
-            scoped_ptr<Response> response(dynamic_cast<Response*>(decoder->decode(relayState,*this,policy)));
+            scoped_ptr<Response> response(dynamic_cast<Response*>(decoder->decode(relayState, *this, this, policy)));
             
             // Test the results.
             TSM_ASSERT_EQUALS("RelayState was not the expected result.", relayState, "state");

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list