Allow message-only policy rules, basic SAML SOAP client.
authorScott Cantor <cantor.2@osu.edu>
Thu, 23 Nov 2006 04:22:59 +0000 (04:22 +0000)
committerScott Cantor <cantor.2@osu.edu>
Thu, 23 Nov 2006 04:22:59 +0000 (04:22 +0000)
22 files changed:
saml/Makefile.am
saml/binding/ClientCertAuthRule.h
saml/binding/MessageFlowRule.h
saml/binding/SOAPClient.h [new file with mode: 0644]
saml/binding/SecurityPolicy.h
saml/binding/SecurityPolicyRule.h
saml/binding/SimpleSigningRule.h
saml/binding/XMLSigningRule.h
saml/binding/impl/ClientCertAuthRule.cpp
saml/binding/impl/MessageFlowRule.cpp
saml/binding/impl/SOAPClient.cpp [new file with mode: 0644]
saml/binding/impl/SecurityPolicy.cpp
saml/binding/impl/SimpleSigningRule.cpp
saml/binding/impl/XMLSigningRule.cpp
saml/saml.vcproj
saml/saml1/binding/impl/SAML1ArtifactDecoder.cpp
saml/saml1/binding/impl/SAML1POSTDecoder.cpp
saml/saml1/binding/impl/SAML1SOAPDecoder.cpp
saml/saml2/binding/impl/SAML2ArtifactDecoder.cpp
saml/saml2/binding/impl/SAML2POSTDecoder.cpp
saml/saml2/binding/impl/SAML2RedirectDecoder.cpp
saml/saml2/binding/impl/SAML2SOAPDecoder.cpp

index 5a7e285..a779fe7 100644 (file)
@@ -45,6 +45,7 @@ samlbindinclude_HEADERS = \
        binding/SecurityPolicy.h \
        binding/SecurityPolicyRule.h \
        binding/SimpleSigningRule.h \
+       binding/SOAPClient.h \
        binding/URLEncoder.h \
        binding/XMLSigningRule.h
 
@@ -112,6 +113,7 @@ libsaml_la_SOURCES = \
        binding/impl/SAMLArtifact.cpp \
        binding/impl/SecurityPolicy.cpp \
        binding/impl/SimpleSigningRule.cpp \
+       binding/impl/SOAPClient.cpp \
        binding/impl/URLEncoder.cpp \
        binding/impl/XMLSigningRule.cpp \
        saml1/core/impl/AssertionsImpl.cpp \
index 0b90bd7..edd9b99 100644 (file)
@@ -34,8 +34,8 @@ namespace opensaml {
         virtual ~ClientCertAuthRule() {}
         
         std::pair<saml2::Issuer*,const saml2md::RoleDescriptor*> evaluate(
-            const GenericRequest& request,
             const xmltooling::XMLObject& message,
+            const GenericRequest* request,
             const saml2md::MetadataProvider* metadataProvider,
             const xmltooling::QName* role,
             const xmltooling::TrustEngine* trustEngine
index 9e261c5..885b82a 100644 (file)
@@ -37,8 +37,8 @@ namespace opensaml {
         virtual ~MessageFlowRule() {}
         
         std::pair<saml2::Issuer*,const saml2md::RoleDescriptor*> evaluate(
-            const GenericRequest& request,
             const xmltooling::XMLObject& message,
+            const GenericRequest* request,
             const saml2md::MetadataProvider* metadataProvider,
             const xmltooling::QName* role,
             const xmltooling::TrustEngine* trustEngine
diff --git a/saml/binding/SOAPClient.h b/saml/binding/SOAPClient.h
new file mode 100644 (file)
index 0000000..fcc89bd
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ *  Copyright 2001-2006 Internet2
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file saml/binding/SOAPClient.h
+ * 
+ * Specialized SOAPClient for SAML SOAP bindings.
+ */
+
+#ifndef __saml_soap11client_h__
+#define __saml_soap11client_h__
+
+#include <saml/binding/SecurityPolicy.h>
+#include <xmltooling/soap/SOAPClient.h>
+
+namespace opensaml {
+
+    /**
+     * Specialized SOAPClient for SAML SOAP bindings.
+     */
+    class SAML_API SOAPClient : soap11::SOAPClient
+    {
+    public:
+        /**
+         * Creates a SOAP client instance with a particular SecurityPolicy.
+         * 
+         * @param policy    reference to SecurityPolicy to apply
+         */
+        SOAPClient(SecurityPolicy& policy) : m_policy(policy), m_force(true) {}
+        
+        virtual ~SOAPClient() {}
+
+        /**
+         * Controls whether to force transport/peer authentication via an X509TrustEngine.
+         * 
+         * <p>Only makes sense if an X509TrustEngine is supplied by the SecurityPolicy. 
+         * 
+         * @param force  true iff the client should refuse to communicate without this protection
+         */
+        void forceTransportAuthentication(bool force=true) {
+            m_force = force;
+        }
+        
+        /**
+         * Override prepares the SecurityPolicy by clearing Issuer identity, in case the policy
+         * is reused.
+         * 
+         * @param env       SOAP envelope to send
+         * @param peer      peer to send message to, expressed in TrustEngine terms
+         * @param endpoint  URL of endpoint to recieve message
+         */
+        void send(const soap11::Envelope* env, const xmltooling::KeyInfoSource& peer, const char* endpoint);
+        
+        /**
+         * Override applies SecurityPolicy to envelope before returning it.
+         * 
+         * @return response envelope after SecurityPolicy has been applied
+         */
+        soap11::Envelope* receive();
+
+    protected:
+        /**
+         * Override prepares transport by assigning an X509TrustEngine to it, if one is
+         * attached to the policy.
+         * 
+         * @param transport reference to transport layer
+         */
+        void prepareTransport(const xmltooling::SOAPTransport& transport);
+        
+        /** Reference to security policy to apply. */
+        SecurityPolicy& m_policy;
+        
+        /** Flag controlling whether transport/peer authn is mandatory. */
+        bool m_force;
+    
+    private:
+        const saml2md::RoleDescriptor* m_peer;
+    };
+
+};
+
+#endif /* __saml_soap11client_h__ */
index 8994179..96fdffe 100644 (file)
@@ -151,16 +151,15 @@ namespace opensaml {
         }
 
         /**
-         * Evaluates the rule against the given request and message,
+         * Evaluates the policy against the given request and message,
          * possibly populating issuer information in the policy object.
          * 
-         * @param request           the protocol request
          * @param message           the incoming message
-         * @return the identity of the message issuer, in one or more of two forms, or NULL
+         * @param request           the protocol request
          * 
-         * @throws BindingException thrown if the request/message do not meet the requirements of this rule
+         * @throws BindingException thrown if the request/message do not meet the requirements of this policy
          */
-        void evaluate(const GenericRequest& request, const xmltooling::XMLObject& message);
+        void evaluate(const xmltooling::XMLObject& message, const GenericRequest* request=NULL);
 
         /**
          * Gets the issuer of the message as determined by the registered policies.
index 517d4b4..ed31d7c 100644 (file)
@@ -55,8 +55,8 @@ namespace opensaml {
          * Evaluates the rule against the given request and message. If an Issuer is
          * returned, the caller is responsible for freeing the Issuer object.
          * 
-         * @param request           the protocol request
          * @param message           the incoming message
+         * @param request           the protocol request
          * @param metadataProvider  locked MetadataProvider instance to authenticate the message
          * @param role              identifies the role (generally IdP or SP) of the peer who issued the message 
          * @param trustEngine       TrustEngine to authenticate the message
@@ -66,8 +66,8 @@ namespace opensaml {
          * @throws BindingException thrown if the request/message do not meet the requirements of this rule
          */
         virtual std::pair<saml2::Issuer*,const saml2md::RoleDescriptor*> evaluate(
-            const GenericRequest& request,
             const xmltooling::XMLObject& message,
+            const GenericRequest* request,
             const saml2md::MetadataProvider* metadataProvider,
             const xmltooling::QName* role,
             const xmltooling::TrustEngine* trustEngine
index 0219d9c..bceac07 100644 (file)
@@ -38,8 +38,8 @@ namespace opensaml {
         virtual ~SimpleSigningRule() {}
         
         std::pair<saml2::Issuer*,const saml2md::RoleDescriptor*> evaluate(
-            const GenericRequest& request,
             const xmltooling::XMLObject& message,
+            const GenericRequest* request,
             const saml2md::MetadataProvider* metadataProvider,
             const xmltooling::QName* role,
             const xmltooling::TrustEngine* trustEngine
index ed499db..d2c3580 100644 (file)
@@ -37,8 +37,8 @@ namespace opensaml {
         virtual ~XMLSigningRule() {}
         
         std::pair<saml2::Issuer*,const saml2md::RoleDescriptor*> evaluate(
-            const GenericRequest& request,
             const xmltooling::XMLObject& message,
+            const GenericRequest* request,
             const saml2md::MetadataProvider* metadataProvider,
             const xmltooling::QName* role,
             const xmltooling::TrustEngine* trustEngine
index 307a1d7..f13e25e 100644 (file)
@@ -45,8 +45,8 @@ namespace opensaml {
 };
 
 pair<saml2::Issuer*,const RoleDescriptor*> ClientCertAuthRule::evaluate(
-    const GenericRequest& request,
     const XMLObject& message,
+    const GenericRequest* request,
     const MetadataProvider* metadataProvider,
     const QName* role,
     const TrustEngine* trustEngine
@@ -55,7 +55,11 @@ pair<saml2::Issuer*,const RoleDescriptor*> ClientCertAuthRule::evaluate(
     Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.ClientCertAuth");
     log.debug("evaluating client certificate authentication policy");
     
-    pair<saml2::Issuer*,const RoleDescriptor*> ret = pair<saml2::Issuer*,const RoleDescriptor*>(NULL,NULL);  
+    pair<saml2::Issuer*,const RoleDescriptor*> ret = pair<saml2::Issuer*,const RoleDescriptor*>(NULL,NULL);
+    if (!request) {
+        log.debug("ignoring message, no protocol request available");
+        return ret;
+    }  
     
     const X509TrustEngine* x509trust;
     if (!metadataProvider || !role || !(x509trust=dynamic_cast<const X509TrustEngine*>(trustEngine))) {
@@ -63,7 +67,7 @@ pair<saml2::Issuer*,const RoleDescriptor*> ClientCertAuthRule::evaluate(
         return ret;
     }
     
-    const std::vector<XSECCryptoX509*>& chain = request.getClientCertificates();
+    const std::vector<XSECCryptoX509*>& chain = request->getClientCertificates();
     if (chain.empty()) {
         log.debug("ignoring message, no client certificates in request");
         return ret;
index 9db7c4e..f7412c2 100644 (file)
@@ -59,8 +59,8 @@ MessageFlowRule::MessageFlowRule(const DOMElement* e)
 }
 
 pair<saml2::Issuer*,const saml2md::RoleDescriptor*> MessageFlowRule::evaluate(
-    const GenericRequest& request,
     const XMLObject& message,
+    const GenericRequest* request,
     const saml2md::MetadataProvider* metadataProvider,
     const QName* role,
     const TrustEngine* trustEngine
diff --git a/saml/binding/impl/SOAPClient.cpp b/saml/binding/impl/SOAPClient.cpp
new file mode 100644 (file)
index 0000000..45693dc
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *  Copyright 2001-2006 Internet2
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * SOAPClient.cpp
+ * 
+ * Implements SOAP 1.1 messaging over a transport.
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "binding/SOAPClient.h"
+#include "saml2/metadata/Metadata.h"
+#include "saml2/metadata/MetadataProvider.h"
+
+#include <xmltooling/security/X509TrustEngine.h>
+#include <xmltooling/soap/SOAP.h>
+
+using namespace opensaml::saml2;
+using namespace opensaml::saml2md;
+using namespace opensaml;
+using namespace xmltooling;
+using namespace std;
+
+void SOAPClient::send(const soap11::Envelope* env, const KeyInfoSource& peer, const char* endpoint)
+{
+    // Clear policy.
+    m_policy.setIssuer(NULL);
+    m_policy.setIssuerMetadata(NULL);
+    
+    m_peer = dynamic_cast<const RoleDescriptor*>(&peer);
+    
+    soap11::SOAPClient::send(env, peer, endpoint);
+}
+
+void SOAPClient::prepareTransport(const xmltooling::SOAPTransport& transport)
+{
+    const X509TrustEngine* engine = dynamic_cast<const X509TrustEngine*>(m_policy.getTrustEngine());
+    if (engine) {
+        const MetadataProvider* metadata = m_policy.getMetadataProvider();
+        if (!transport.setTrustEngine(engine, m_force, metadata ? metadata->getKeyResolver() : NULL))
+            throw BindingException("Unable to install X509TrustEngine into SOAPTransport.");
+    }
+}
+
+soap11::Envelope* SOAPClient::receive()
+{
+    auto_ptr<soap11::Envelope> env(soap11::SOAPClient::receive());
+    if (env.get()) {
+        if (m_peer && m_transport->isSecure()) {
+            // Set issuer based on peer identity.
+            EntityDescriptor* parent = dynamic_cast<EntityDescriptor*>(m_peer->getParent());
+            if (parent) {
+                Issuer* issuer = IssuerBuilder::buildIssuer();
+                issuer->setName(parent->getEntityID());
+                m_policy.setIssuer(issuer);
+                m_policy.setIssuerMetadata(m_peer);
+            }
+        }
+        m_policy.evaluate(*(env.get()));
+    }
+    return env.release();
+}
index b2157ce..350057e 100644 (file)
@@ -58,12 +58,12 @@ SecurityPolicy::~SecurityPolicy()
     delete m_issuer;
 }
 
-void SecurityPolicy::evaluate(const GenericRequest& request, const XMLObject& message)
+void SecurityPolicy::evaluate(const XMLObject& message, const GenericRequest* request)
 {
     for (vector<const SecurityPolicyRule*>::const_iterator i=m_rules.begin(); i!=m_rules.end(); ++i) {
 
         // Run the rule...
-        pair<Issuer*,const RoleDescriptor*> ident = (*i)->evaluate(request,message,m_metadata,&m_role,m_trust);
+        pair<Issuer*,const RoleDescriptor*> ident = (*i)->evaluate(message,request,m_metadata,&m_role,m_trust);
 
         // Make sure returned issuer doesn't conflict.
          
index 3a09638..7f1ea7f 100644 (file)
@@ -68,8 +68,8 @@ namespace opensaml {
 
 
 pair<saml2::Issuer*,const RoleDescriptor*> SimpleSigningRule::evaluate(
-    const GenericRequest& request,
     const XMLObject& message,
+    const GenericRequest* request,
     const MetadataProvider* metadataProvider,
     const QName* role,
     const TrustEngine* trustEngine
@@ -80,18 +80,24 @@ pair<saml2::Issuer*,const RoleDescriptor*> SimpleSigningRule::evaluate(
     
     pair<saml2::Issuer*,const RoleDescriptor*> ret = pair<saml2::Issuer*,const RoleDescriptor*>(NULL,NULL);  
     
+    const HTTPRequest* httpRequest = dynamic_cast<const HTTPRequest*>(request);
+    if (!request || !httpRequest) {
+        log.debug("ignoring message, no HTTP protocol request available");
+        return ret;
+    }
+
     if (!metadataProvider || !role || !trustEngine) {
         log.debug("ignoring message, no metadata supplied");
         return ret;
     }
     
-    const char* signature = request.getParameter("Signature");
+    const char* signature = request->getParameter("Signature");
     if (!signature) {
         log.debug("ignoring unsigned message");
         return ret;
     }
     
-    const char* sigAlgorithm = request.getParameter("SigAlg");
+    const char* sigAlgorithm = request->getParameter("SigAlg");
     if (!sigAlgorithm) {
         log.error("SigAlg parameter not found, no way to verify the signature");
         return ret;
@@ -125,12 +131,11 @@ pair<saml2::Issuer*,const RoleDescriptor*> SimpleSigningRule::evaluate(
 
         string input;
         const char* pch;
-        const HTTPRequest& httpRequest = dynamic_cast<const HTTPRequest&>(request);
-        if (!strcmp(httpRequest.getMethod(), "GET")) {
+        if (!strcmp(httpRequest->getMethod(), "GET")) {
             // We have to construct a string containing the signature input by accessing the
             // request directly. We can't use the decoded parameters because we need the raw
             // data and URL-encoding isn't canonical.
-            pch = httpRequest.getQueryString();
+            pch = httpRequest->getQueryString();
             if (!appendParameter(input, pch, "SAMLRequest="))
                 appendParameter(input, pch, "SAMLResponse=");
             appendParameter(input, pch, "RelayState=");
@@ -139,14 +144,14 @@ pair<saml2::Issuer*,const RoleDescriptor*> SimpleSigningRule::evaluate(
         else {
             // With POST, the input string is concatenated from the decoded form controls.
             // GET should be this way too, but I messed up the spec, sorry.
-            pch = httpRequest.getParameter("SAMLRequest");
+            pch = httpRequest->getParameter("SAMLRequest");
             if (pch)
                 input = string("SAMLRequest=") + pch;
             else {
-                pch = httpRequest.getParameter("SAMLResponse");
+                pch = httpRequest->getParameter("SAMLResponse");
                 input = string("SAMLResponse=") + pch;
             }
-            pch = httpRequest.getParameter("RelayState");
+            pch = httpRequest->getParameter("RelayState");
             if (pch)
                 input = input + "&RelayState=" + pch;
             input = input + "&SigAlg=" + sigAlgorithm;
@@ -154,7 +159,7 @@ pair<saml2::Issuer*,const RoleDescriptor*> SimpleSigningRule::evaluate(
 
         // Check for KeyInfo, but defensively (we might be able to run without it).
         KeyInfo* keyInfo=NULL;
-        pch = request.getParameter("KeyInfo");
+        pch = request->getParameter("KeyInfo");
         if (pch) {
             try {
                 istringstream kstrm(pch);
index 6cc736d..56c2250 100644 (file)
@@ -48,8 +48,8 @@ namespace opensaml {
 };
 
 pair<saml2::Issuer*,const RoleDescriptor*> XMLSigningRule::evaluate(
-    const GenericRequest& request,
     const XMLObject& message,
+    const GenericRequest* request,
     const MetadataProvider* metadataProvider,
     const QName* role,
     const TrustEngine* trustEngine
index 8f121e1..4682f2b 100644 (file)
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\binding\impl\SOAPClient.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\binding\impl\URLEncoder.cpp"\r
                                                >\r
                                        </File>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\binding\SOAPClient.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\binding\URLEncoder.h"\r
                                        >\r
                                </File>\r
index 0f467f0..bc0f0e6 100644 (file)
@@ -140,7 +140,7 @@ XMLObject* SAML1ArtifactDecoder::decode(
             m_artifactResolver->resolve(artifacts, dynamic_cast<const IDPSSODescriptor&>(*roledesc), policy)
             );
         
-        policy.evaluate(genericRequest, *(response.get()));
+        policy.evaluate(*(response.get()), &genericRequest);
         
         for_each(artifacts.begin(), artifacts.end(), xmltooling::cleanup<SAMLArtifact>());
         return response.release();
index f3018ef..fd3258a 100644 (file)
@@ -114,7 +114,7 @@ XMLObject* SAML1POSTDecoder::decode(
         }
         
         // Run through the policy.
-        policy.evaluate(genericRequest, *response);
+        policy.evaluate(*response, &genericRequest);
     }
     catch (XMLToolingException& ex) {
         // This is just to maximize the likelihood of attaching a source to the message for support purposes.
index ffd191f..10fd72f 100644 (file)
@@ -91,8 +91,8 @@ XMLObject* SAML1SOAPDecoder::decode(
         Request* request = dynamic_cast<Request*>(body->getXMLObjects().front());
         if (request) {
             // Run through the policy at two layers.
-            policy.evaluate(genericRequest, *env);
-            policy.evaluate(genericRequest, *request);
+            policy.evaluate(*env, &genericRequest);
+            policy.evaluate(*request, &genericRequest);
             xmlObject.release();
             body->detach(); // frees Envelope
             request->detach();   // frees Body
index 48fcceb..c309162 100644 (file)
@@ -138,11 +138,11 @@ XMLObject* SAML2ArtifactDecoder::decode(
             m_artifactResolver->resolve(*(artifact2.get()), dynamic_cast<const SSODescriptorType&>(*roledesc), policy)
             );
         
-        policy.evaluate(genericRequest, *(response.get()));
+        policy.evaluate(*(response.get()), &genericRequest);
 
         // Extract payload and check that message.
         XMLObject* payload = response->getPayload();
-        policy.evaluate(genericRequest, *payload);
+        policy.evaluate(*payload, &genericRequest);
 
         // Return the payload only.
         response.release();
index ebb2ec2..8b953e3 100644 (file)
@@ -128,7 +128,7 @@ XMLObject* SAML2POSTDecoder::decode(
         }
         
         // Run through the policy.
-        policy.evaluate(genericRequest, *root);
+        policy.evaluate(*root, &genericRequest);
     }
     catch (XMLToolingException& ex) {
         // This is just to maximize the likelihood of attaching a source to the message for support purposes.
@@ -165,6 +165,5 @@ XMLObject* SAML2POSTDecoder::decode(
         annotateException(&ex,provider);  // throws it
     }
 
-    xmlObject.release();
-    return root;
+    return xmlObject.release();
 }
index 5c1dc39..d2abd41 100644 (file)
@@ -143,7 +143,7 @@ XMLObject* SAML2RedirectDecoder::decode(
         }
 
         // Run through the policy.
-        policy.evaluate(genericRequest, *root);
+        policy.evaluate(*root, &genericRequest);
     }
     catch (XMLToolingException& ex) {
         // This is just to maximize the likelihood of attaching a source to the message for support purposes.
@@ -170,6 +170,5 @@ XMLObject* SAML2RedirectDecoder::decode(
         annotateException(&ex,provider);  // throws it
     }
 
-    xmlObject.release();
-    return root;
+    return xmlObject.release();
 }
index 8ff3738..092e7f4 100644 (file)
@@ -91,8 +91,8 @@ XMLObject* SAML2SOAPDecoder::decode(
         RequestAbstractType* request = dynamic_cast<RequestAbstractType*>(body->getXMLObjects().front());
         if (request) {
             // Run through the policy at two layers.
-            policy.evaluate(genericRequest, *env);
-            policy.evaluate(genericRequest, *request);
+            policy.evaluate(*env, &genericRequest);
+            policy.evaluate(*request, &genericRequest);
             xmlObject.release();
             body->detach(); // frees Envelope
             request->detach();   // frees Body