Move token validation into SAML library, first draft SAML 1 SSO handler.
authorScott Cantor <cantor.2@osu.edu>
Wed, 7 Mar 2007 20:36:08 +0000 (20:36 +0000)
committerScott Cantor <cantor.2@osu.edu>
Wed, 7 Mar 2007 20:36:08 +0000 (20:36 +0000)
15 files changed:
shibsp/Application.h
shibsp/Makefile.am
shibsp/SPConfig.cpp
shibsp/attribute/resolver/ResolutionContext.h
shibsp/attribute/resolver/impl/SimpleAttributeResolver.cpp
shibsp/handler/AbstractHandler.h
shibsp/handler/AssertionConsumerService.h [new file with mode: 0644]
shibsp/handler/Handler.h
shibsp/handler/RemotedHandler.h
shibsp/handler/impl/AbstractHandler.cpp
shibsp/handler/impl/AssertionConsumerService.cpp [new file with mode: 0644]
shibsp/handler/impl/RemotedHandler.cpp
shibsp/handler/impl/SAML1Consumer.cpp [new file with mode: 0644]
shibsp/impl/XMLServiceProvider.cpp
shibsp/shibsp.vcproj

index 89b31bb..113d743 100644 (file)
@@ -26,7 +26,6 @@
 #include <shibsp/util/PropertySet.h>
 #include <saml/saml2/metadata/MetadataProvider.h>
 #include <xmltooling/security/TrustEngine.h>
-#include <xmltooling/validation/Validator.h>
 
 namespace shibsp {
     
@@ -164,17 +163,6 @@ namespace shibsp {
          * @return set of audience values associated with the Application
          */
         virtual const std::vector<const XMLCh*>& getAudiences() const=0;
-
-        /**
-         * Returns a validator for applying verification rules to incoming SAML tokens.
-         *
-         * <p>The validator must be freed by the caller.
-         * 
-         * @param ts    timestamp against which to evaluate the token's validity, or 0 to ignore
-         * @param role  metadata role of token issuer, if known
-         * @return a validator
-         */
-        virtual xmltooling::Validator* getTokenValidator(time_t ts=0, const opensaml::saml2md::RoleDescriptor* role=NULL) const=0;
     };
 };
 
index 2d94a68..1a8af1e 100644 (file)
@@ -54,6 +54,7 @@ bindinclude_HEADERS = \
 
 handinclude_HEADERS = \
        handler/AbstractHandler.h \
+       handler/AssertionConsumerService.h \
        handler/Handler.h \
        handler/RemotedHandler.h
 
@@ -90,8 +91,10 @@ libshibsp_la_SOURCES = \
        attribute/resolver/impl/AttributeResolver.cpp \
        attribute/resolver/impl/SimpleAttributeResolver.cpp \
        binding/impl/SOAPClient.cpp \
-       handler/impl/RemotedHandler.cpp \
        handler/impl/AbstractHandler.cpp \
+       handler/impl/AssertionConsumerService.cpp \
+       handler/impl/RemotedHandler.cpp \
+       handler/impl/SAML1Consumer.cpp \
        impl/RemotedSessionCache.cpp \
        impl/StorageServiceSessionCache.cpp \
        impl/XMLAccessControl.cpp \
index baa1e40..ff635ab 100644 (file)
@@ -109,6 +109,7 @@ bool SPInternalConfig::init(const char* catalog_path)
     registerAttributeDecoders();
     registerAttributeFactories();
     registerAttributeResolvers();
+    registerHandlers();
     registerListenerServices();
     registerRequestMappers();
     registerSessionCaches();
index f05bcd0..bdc0ecd 100644 (file)
@@ -32,6 +32,7 @@ namespace shibsp {
 
     class SHIBSP_API Application;
     class SHIBSP_API Session;
+    class SHIBSP_API Attribute;
 
     /**
      * A context for a resolution request.
index bff9752..268677f 100644 (file)
@@ -22,6 +22,7 @@
 \r
 #include "internal.h"\r
 #include "Application.h"\r
+#include "ServiceProvider.h"\r
 #include "SessionCache.h"\r
 #include "attribute/AttributeDecoder.h"\r
 #include "attribute/resolver/AttributeResolver.h"\r
 #include <saml/saml1/binding/SAML1SOAPClient.h>\r
 #include <saml/saml1/core/Assertions.h>\r
 #include <saml/saml1/core/Protocols.h>\r
+#include <saml/saml1/profile/AssertionValidator.h>\r
 #include <saml/saml2/binding/SAML2SOAPClient.h>\r
 #include <saml/saml2/core/Protocols.h>\r
 #include <saml/saml2/metadata/Metadata.h>\r
 #include <saml/saml2/metadata/MetadataProvider.h>\r
+#include <saml/saml2/profile/AssertionValidator.h>\r
 #include <xmltooling/util/NDC.h>\r
 #include <xmltooling/util/ReloadableXMLFile.h>\r
 #include <xmltooling/util/XMLHelper.h>\r
@@ -471,6 +474,8 @@ void SimpleResolverImpl::query(ResolutionContext& ctx, const NameIdentifier& nam
 \r
     SecurityPolicy policy;\r
     shibsp::SOAPClient soaper(ctx.getApplication(),policy);\r
+    const PropertySet* policySettings = ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);\r
+    pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");\r
 \r
     auto_ptr_XMLCh binding(samlconstants::SAML1_BINDING_SOAP);\r
     saml1p::Response* response=NULL;\r
@@ -504,44 +509,32 @@ void SimpleResolverImpl::query(ResolutionContext& ctx, const NameIdentifier& nam
         return;\r
     }\r
 \r
-    time_t now = time(NULL);\r
-    const Validator* tokval = ctx.getApplication().getTokenValidator(now, AA);\r
     const vector<saml1::Assertion*>& assertions = const_cast<const saml1p::Response*>(response)->getAssertions();\r
-    if (assertions.size()==1) {\r
-        auto_ptr<saml1p::Response> wrapper(response);\r
-        saml1::Assertion* newtoken = assertions.front();\r
-        if (!XMLString::equals(policy.getIssuer() ? policy.getIssuer()->getName() : NULL, newtoken->getIssuer())) {\r
-            log.error("assertion issued by someone other than AA, rejecting it");\r
-            return;\r
-        }\r
-        try {\r
-            tokval->validate(newtoken);\r
-        }\r
-        catch (exception& ex) {\r
-            log.error("assertion failed validation check: %s", ex.what());\r
-        }\r
-        newtoken->detach();\r
-        wrapper.release();\r
-        ctx.getResolvedAssertions().push_back(newtoken);\r
-        resolve(ctx, newtoken, attributes);\r
+    if (assertions.size()>1)\r
+        log.warn("simple resolver only supports one assertion in the query response");\r
+\r
+    auto_ptr<saml1p::Response> wrapper(response);\r
+    saml1::Assertion* newtoken = assertions.front();\r
+\r
+    if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {\r
+        log.error("assertion unsigned, rejecting it based on signedAssertions policy");\r
+        return;\r
     }\r
-    else {\r
-        auto_ptr<saml1p::Response> wrapper(response);\r
-        for (vector<saml1::Assertion*>::const_iterator a = assertions.begin(); a!=assertions.end(); ++a) {\r
-            if (!XMLString::equals(policy.getIssuer() ? policy.getIssuer()->getName() : NULL, (*a)->getIssuer())) {\r
-                log.error("assertion issued by someone other than AA, rejecting it");\r
-                continue;\r
-            }\r
-            try {\r
-                tokval->validate(*a);\r
-            }\r
-            catch (exception& ex) {\r
-                log.error("assertion failed validation check: %s", ex.what());\r
-            }\r
-            resolve(ctx, *a, attributes);\r
-            ctx.getResolvedAssertions().push_back((*a)->cloneAssertion());\r
-        }\r
+\r
+    try {\r
+        policy.evaluate(*newtoken);\r
+        if (!policy.isSecure())\r
+            throw SecurityPolicyException("Security of SAML 1.x query result not established.");\r
+        saml1::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL));\r
+        tokval.validateAssertion(*newtoken);\r
+    }\r
+    catch (exception& ex) {\r
+        log.error("assertion failed policy/validation: %s", ex.what());\r
     }\r
+    newtoken->detach();\r
+    wrapper.release();\r
+    ctx.getResolvedAssertions().push_back(newtoken);\r
+    resolve(ctx, newtoken, attributes);\r
 }\r
 \r
 void SimpleResolverImpl::query(ResolutionContext& ctx, const NameID& nameid, const vector<const char*>* attributes) const\r
@@ -564,6 +557,8 @@ void SimpleResolverImpl::query(ResolutionContext& ctx, const NameID& nameid, con
 \r
     SecurityPolicy policy;\r
     shibsp::SOAPClient soaper(ctx.getApplication(),policy);\r
+    const PropertySet* policySettings = ctx.getApplication().getServiceProvider().getPolicySettings(ctx.getApplication().getString("policyId").second);\r
+    pair<bool,bool> signedAssertions = policySettings->getBool("signedAssertions");\r
 \r
     auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP);\r
     saml2p::StatusResponseType* srt=NULL;\r
@@ -602,44 +597,32 @@ void SimpleResolverImpl::query(ResolutionContext& ctx, const NameID& nameid, con
         return;\r
     }\r
 \r
-    time_t now = time(NULL);\r
-    const Validator* tokval = ctx.getApplication().getTokenValidator(now, AA);\r
     const vector<saml2::Assertion*>& assertions = const_cast<const saml2p::Response*>(response)->getAssertions();\r
-    if (assertions.size()==1) {\r
-        auto_ptr<saml2p::Response> wrapper(response);\r
-        saml2::Assertion* newtoken = assertions.front();\r
-        if (!XMLString::equals(policy.getIssuer() ? policy.getIssuer()->getName() : NULL, newtoken->getIssuer() ? newtoken->getIssuer()->getName() : NULL)) {\r
-            log.error("assertion issued by someone other than AA, rejecting it");\r
-            return;\r
-        }\r
-        try {\r
-            tokval->validate(newtoken);\r
-        }\r
-        catch (exception& ex) {\r
-            log.error("assertion failed validation check: %s", ex.what());\r
-        }\r
-        newtoken->detach();\r
-        wrapper.release();\r
-        ctx.getResolvedAssertions().push_back(newtoken);\r
-        resolve(ctx, newtoken, attributes);\r
+    if (assertions.size()>1)\r
+        log.warn("simple resolver only supports one assertion in the query response");\r
+\r
+    auto_ptr<saml2p::Response> wrapper(response);\r
+    saml2::Assertion* newtoken = assertions.front();\r
+\r
+    if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {\r
+        log.error("assertion unsigned, rejecting it based on signedAssertions policy");\r
+        return;\r
     }\r
-    else {\r
-        auto_ptr<saml2p::Response> wrapper(response);\r
-        for (vector<saml2::Assertion*>::const_iterator a = assertions.begin(); a!=assertions.end(); ++a) {\r
-            if (!XMLString::equals(policy.getIssuer() ? policy.getIssuer()->getName() : NULL, (*a)->getIssuer() ? (*a)->getIssuer()->getName() : NULL)) {\r
-                log.error("assertion issued by someone other than AA, rejecting it");\r
-                return;\r
-            }\r
-            try {\r
-                tokval->validate(*a);\r
-            }\r
-            catch (exception& ex) {\r
-                log.error("assertion failed validation check: %s", ex.what());\r
-            }\r
-            resolve(ctx, *a, attributes);\r
-            ctx.getResolvedAssertions().push_back((*a)->cloneAssertion());\r
-        }\r
+\r
+    try {\r
+        policy.evaluate(*newtoken);\r
+        if (!policy.isSecure())\r
+            throw SecurityPolicyException("Security of SAML 2.0 query result not established.");\r
+        saml2::AssertionValidator tokval(ctx.getApplication().getAudiences(), time(NULL));\r
+        tokval.validateAssertion(*newtoken);\r
+    }\r
+    catch (exception& ex) {\r
+        log.error("assertion failed policy/validation: %s", ex.what());\r
     }\r
+    newtoken->detach();\r
+    wrapper.release();\r
+    ctx.getResolvedAssertions().push_back(newtoken);\r
+    resolve(ctx, newtoken, attributes);\r
 }\r
 \r
 void SimpleResolver::resolveAttributes(ResolutionContext& ctx, const vector<const char*>* attributes) const\r
index ba79ea5..4f1cb85 100644 (file)
@@ -25,6 +25,8 @@
 
 #include <shibsp/handler/Handler.h>
 #include <shibsp/util/DOMPropertySet.h>
+#include <log4cpp/Category.hh>
+#include <xmltooling/XMLObject.h>
 
 namespace shibsp {
 
@@ -47,11 +49,25 @@ namespace shibsp {
          * @param remapper  optional map of property rename rules for legacy property support
          */
         AbstractHandler(
-            const xercesc::DOMElement* e,
-            xercesc::DOMNodeFilter* filter=NULL,
+            const DOMElement* e,
+            log4cpp::Category& log,
+            DOMNodeFilter* filter=NULL,
             const std::map<std::string,std::string>* remapper=NULL
             );
+
+        /**
+         * Examines a protocol response message for errors and raises an annotated exception
+         * if an error is found.
+         * 
+         * <p>The base class version understands SAML 1.x and SAML 2.0 responses.
+         * 
+         * @param response      a response message of some known protocol
+         */
+        virtual void checkError(const xmltooling::XMLObject* response) const;
         
+        /** Logging object. */
+        log4cpp::Category& m_log; 
+    
     public:
         virtual ~AbstractHandler() {}
     };
diff --git a/shibsp/handler/AssertionConsumerService.h b/shibsp/handler/AssertionConsumerService.h
new file mode 100644 (file)
index 0000000..5466012
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ *  Copyright 2001-2007 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 shibsp/handler/AssertionConsumerService.h
+ * 
+ * Base class for handlers that create sessions by consuming SSO protocol responses. 
+ */
+
+#ifndef __shibsp_acshandler_h__
+#define __shibsp_acshandler_h__
+
+#include <shibsp/handler/AbstractHandler.h>
+#include <shibsp/handler/RemotedHandler.h>
+#include <saml/binding/MessageDecoder.h>
+#include <saml/saml2/metadata/Metadata.h>
+
+namespace shibsp {
+
+    class SHIBSP_API ResolutionContext;
+
+#if defined (_MSC_VER)
+    #pragma warning( push )
+    #pragma warning( disable : 4250 )
+#endif
+
+    /**
+     * Base class for handlers that create sessions by consuming SSO protocol responses.
+     */
+    class SHIBSP_API AssertionConsumerService : public AbstractHandler, public RemotedHandler 
+    {
+    public:
+        virtual ~AssertionConsumerService();
+
+        std::pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
+        void receive(DDF& in, std::ostream& out);
+
+    protected:
+        AssertionConsumerService(const DOMElement* e, log4cpp::Category& log);
+        
+        /**
+         * Implement protocol-specific handling of the incoming decoded message.
+         * 
+         * <p>The result of implementing the protocol should be an exception or
+         * the key to a newly created session.
+         * 
+         * @param application   reference to application receiving message
+         * @param httpRequest   client request that included message
+         * @param policy        the SecurityPolicy in effect, after having evaluated the message
+         * @param settings      policy configuration settings in effect
+         * @param xmlObject     a protocol-specific message object
+         * @return  the key to the newly created session
+         */
+        virtual std::string implementProtocol(
+            const Application& application,
+            const opensaml::HTTPRequest& httpRequest,
+            opensaml::SecurityPolicy& policy,
+            const PropertySet* settings,
+            const xmltooling::XMLObject& xmlObject
+            ) const=0;
+            
+        /**
+         * Enforce address checking requirements.
+         * 
+         * @param application   reference to application receiving message
+         * @param httpRequest   client request that initiated session
+         * @param issuedTo      address for which security assertion was issued
+         */
+        void checkAddress(
+            const Application& application, const opensaml::HTTPRequest& httpRequest, const char* issuedTo
+            ) const;
+        
+        /**
+         * Attempt SSO-initiated attribute resolution using the supplied information.
+         * 
+         * <p>The caller must free the returned context handle.
+         * 
+         * @param application   reference to application receiving message
+         * @param httpRequest   client request that initiated session
+         * @param issuer        source of SSO tokens
+         * @param nameid        identifier of principal
+         * @param tokens        tokens to resolve, if any
+         */
+        ResolutionContext* resolveAttributes(
+            const Application& application,
+            const opensaml::HTTPRequest& httpRequest,
+            const opensaml::saml2md::EntityDescriptor* issuer,
+            const opensaml::saml2::NameID& nameid,
+            const std::vector<const opensaml::Assertion*>* tokens=NULL
+            ) const;
+        
+    private:
+        std::string processMessage(
+            const Application& application,
+            const opensaml::HTTPRequest& httpRequest,
+            std::string& providerId,
+            std::string& relayState
+            ) const;
+            
+        std::pair<bool,long> sendRedirect(
+            SPRequest& request, const char* key, const char* providerId, const char* relayState
+            ) const;
+        
+        void maintainHistory(SPRequest& request, const char* providerId, const char* cookieProps) const;
+                
+        opensaml::MessageDecoder* m_decoder;
+        xmltooling::auto_ptr_char m_configNS;
+        xmltooling::QName m_role;
+    };
+
+#if defined (_MSC_VER)
+    #pragma warning( pop )
+#endif
+
+};
+
+#endif /* __shibsp_acshandler_h__ */
index c26c3c8..94ccefa 100644 (file)
@@ -52,6 +52,9 @@ namespace shibsp {
          */
         virtual std::pair<bool,long> run(SPRequest& request, bool isHandler=true) const=0;
     };
+    
+    /** Registers Handler implementations. */
+    void SHIBSP_API registerHandlers();
 };
 
 #endif /* __shibsp_handler_h__ */
index 3006842..9420bb5 100644 (file)
@@ -35,22 +35,21 @@ namespace shibsp {
     class SHIBSP_API RemotedHandler : public virtual Handler, public Remoted 
     {
     public:
-        virtual ~RemotedHandler() {}
+        virtual ~RemotedHandler();
 
     protected:
-        RemotedHandler() {}
+        RemotedHandler();
 
         /**
-         * Wraps a request by annotating an outgoing data flow with the data needed
+         * Wraps a request by creating an outgoing data flow with the data needed
          * to remote the request information.
          *
          * @param request   an SPRequest to remote
-         * @param in        the dataflow object to annotate
          * @param headers   array of request headers to copy to remote request
          * @param certs     true iff client certificates should be available for the remote request
          * @return  the input dataflow object
          */
-        const DDF& wrap(const SPRequest& request, DDF& in, const std::vector<std::string>& headers, bool certs=false) const;
+        DDF wrap(const SPRequest& request, const std::vector<std::string>* headers=NULL, bool certs=false) const;
         
         /**
          * Unwraps a response by examining an incoming data flow to determine
@@ -77,6 +76,12 @@ namespace shibsp {
          * @return  a call-specific response object, to be freed by the caller 
          */
         opensaml::HTTPResponse* getResponse(DDF& out) const;
+        
+        /** Message address for remote half. */
+        std::string m_address;
+        
+    private:
+        static unsigned int m_counter;
     };
 };
 
index 9fc7cf3..823751c 100644 (file)
  */
 
 #include "internal.h"
+#include "exceptions.h"
 #include "handler/AbstractHandler.h"
 
+#include <saml/saml1/core/Protocols.h>
+#include <saml/saml2/core/Protocols.h>
+#include <saml/util/SAMLConstants.h>
+
 using namespace shibsp;
+using namespace samlconstants;
+using namespace opensaml;
+using namespace xmltooling;
 using namespace xercesc;
 using namespace std;
 
+namespace shibsp {
+    SHIBSP_DLLLOCAL PluginManager<Handler,const DOMElement*>::Factory SAML1ConsumerFactory;
+};
+
+void SHIBSP_API shibsp::registerHandlers()
+{
+    SPConfig& conf=SPConfig::getConfig();
+    conf.AssertionConsumerServiceManager.registerFactory(SAML1_PROFILE_BROWSER_ARTIFACT, SAML1ConsumerFactory);
+    conf.AssertionConsumerServiceManager.registerFactory(SAML1_PROFILE_BROWSER_POST, SAML1ConsumerFactory);
+}
+
 AbstractHandler::AbstractHandler(
-    const DOMElement* e, DOMNodeFilter* filter, const map<string,string>* remapper
-    ) {
-    load(e,log4cpp::Category::getInstance(SHIBSP_LOGCAT".Handler"),filter,remapper);
+    const DOMElement* e, log4cpp::Category& log, DOMNodeFilter* filter, const map<string,string>* remapper
+    ) : m_log(log) {
+    load(e,log,filter,remapper);
+}
+
+void AbstractHandler::checkError(const XMLObject* response) const
+{
+    const saml2p::StatusResponseType* r2 = dynamic_cast<const saml2p::StatusResponseType*>(response);
+    if (r2) {
+        const saml2p::Status* status = r2->getStatus();
+        if (status) {
+            const saml2p::StatusCode* sc = status->getStatusCode();
+            const XMLCh* code = sc ? sc->getValue() : NULL;
+            if (code && !XMLString::equals(code,saml2p::StatusCode::SUCCESS)) {
+                FatalProfileException ex("SAML Response message contained an error.");
+                auto_ptr_char c1(code);
+                ex.addProperty("code", c1.get());
+                if (sc->getStatusCode()) {
+                    code = sc->getStatusCode()->getValue();
+                    auto_ptr_char c2(code);
+                    ex.addProperty("code2", c2.get());
+                }
+                if (status->getStatusMessage()) {
+                    auto_ptr_char msg(status->getStatusMessage()->getMessage());
+                    ex.addProperty("message", msg.get());
+                }
+            }
+        }
+    }
+
+    const saml1p::Response* r1 = dynamic_cast<const saml1p::Response*>(response);
+    if (r1) {
+        const saml1p::Status* status = r1->getStatus();
+        if (status) {
+            const saml1p::StatusCode* sc = status->getStatusCode();
+            const QName* code = sc ? sc->getValue() : NULL;
+            if (code && *code != saml1p::StatusCode::SUCCESS) {
+                FatalProfileException ex("SAML Response message contained an error.");
+                ex.addProperty("code", code->toString().c_str());
+                if (sc->getStatusCode()) {
+                    code = sc->getStatusCode()->getValue();
+                    if (code)
+                        ex.addProperty("code2", code->toString().c_str());
+                }
+                if (status->getStatusMessage()) {
+                    auto_ptr_char msg(status->getStatusMessage()->getMessage());
+                    ex.addProperty("message", msg.get());
+                }
+            }
+        }
+    }
 }
diff --git a/shibsp/handler/impl/AssertionConsumerService.cpp b/shibsp/handler/impl/AssertionConsumerService.cpp
new file mode 100644 (file)
index 0000000..2be942f
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ *  Copyright 2001-2007 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.
+ */
+
+/**
+ * AssertionConsumerService.cpp
+ * 
+ * Base class for handlers that create sessions by consuming SSO protocol responses. 
+ */
+
+#include "internal.h"
+#include "Application.h"
+#include "exceptions.h"
+#include "ServiceProvider.h"
+#include "attribute/resolver/AttributeResolver.h"
+#include "attribute/resolver/ResolutionContext.h"
+#include "handler/AssertionConsumerService.h"
+#include "util/SPConstants.h"
+
+#include <saml/SAMLConfig.h>
+#include <saml/binding/URLEncoder.h>
+#include <saml/saml1/core/Assertions.h>
+#include <saml/util/CommonDomainCookie.h>
+
+using namespace shibspconstants;
+using namespace samlconstants;
+using namespace shibsp;
+using namespace opensaml;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+AssertionConsumerService::AssertionConsumerService(const DOMElement* e, Category& log)
+    : AbstractHandler(e, log), m_configNS(SHIB2SPCONFIG_NS),
+        m_role(samlconstants::SAML20MD_NS, opensaml::saml2md::IDPSSODescriptor::LOCAL_NAME)
+{
+    if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess))
+        m_decoder = SAMLConfig::getConfig().MessageDecoderManager.newPlugin(getString("Binding").second,e);
+}
+
+AssertionConsumerService::~AssertionConsumerService()
+{
+    delete m_decoder;
+}
+
+pair<bool,long> AssertionConsumerService::run(SPRequest& request, bool isHandler) const
+{
+    SPConfig& conf = SPConfig::getConfig();
+    if (conf.isEnabled(SPConfig::OutOfProcess)) {
+        string relayState, providerId;
+        string key = processMessage(request.getApplication(), request, providerId, relayState);
+        return sendRedirect(request, key.c_str(), providerId.c_str(), relayState.c_str());
+    }
+    else {
+        DDF in = wrap(request);
+        DDFJanitor jin(in);
+        in.addmember("application_id").string(request.getApplication().getId());
+        DDF out=request.getServiceProvider().getListenerService()->send(in);
+        DDFJanitor jout(out);
+        if (!out["key"].isstring())
+            throw FatalProfileException("Remote processing of SAML 1.x Browser profile did not return a usable session key.");
+        return sendRedirect(request, out["key"].string(), out["provider_id"].string(), out["RelayState"].string());
+    }
+}
+
+void AssertionConsumerService::receive(DDF& in, ostream& out)
+{
+    // Find application.
+    const char* aid=in["application_id"].string();
+    const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL;
+    if (!app) {
+        // Something's horribly wrong.
+        m_log.error("couldn't find application (%s) for new session", aid ? aid : "(missing)");
+        throw ConfigurationException("Unable to locate application for new session, deleted?");
+    }
+    
+    // Unpack the request.
+    auto_ptr<HTTPRequest> http(getRequest(in));
+    
+    // Do the work.
+    string relayState, providerId;
+    string key = processMessage(*app, *http.get(), providerId, relayState);
+    
+    // Repack for return to caller.
+    DDF ret=DDF(NULL).structure();
+    DDFJanitor jret(ret);
+    ret.addmember("key").string(key.c_str());
+    if (!providerId.empty())
+        ret.addmember("provider_id").string(providerId.c_str());
+    if (!relayState.empty())
+        ret.addmember("RelayState").string(relayState.c_str());
+    out << ret;
+}
+
+string AssertionConsumerService::processMessage(
+    const Application& application, const HTTPRequest& httpRequest, string& providerId, string& relayState
+    ) const
+{
+    // Locate policy key.
+    pair<bool,const char*> policyId = getString("policyId", m_configNS.get());  // namespace-qualified if inside handler element
+    if (!policyId.first)
+        policyId = application.getString("policyId");   // unqualified in Application(s) element
+        
+    // Access policy properties.
+    const PropertySet* settings = application.getServiceProvider().getPolicySettings(policyId.second);
+    pair<bool,bool> validate = settings->getBool("validate");
+
+    // Lock metadata for use by policy.
+    Locker metadataLocker(application.getMetadataProvider());
+
+    // Create the policy.
+    SecurityPolicy policy(
+        application.getServiceProvider().getPolicyRules(policyId.second), 
+        application.getMetadataProvider(),
+        &m_role,
+        application.getTrustEngine(),
+        validate.first && validate.second
+        );
+    
+    // Decode the message and process it in a protocol-specific way.
+    auto_ptr<XMLObject> msg(m_decoder->decode(relayState, httpRequest, policy));
+    string key = implementProtocol(application, httpRequest, policy, settings, *msg.get());
+
+    auto_ptr_char issuer(policy.getIssuer() ? policy.getIssuer()->getName() : NULL);
+    if (issuer.get())
+        providerId = issuer.get();
+    
+    return key;
+}
+
+pair<bool,long> AssertionConsumerService::sendRedirect(
+    SPRequest& request, const char* key, const char* providerId, const char* relayState
+    ) const
+{
+    string s,k(key);
+    
+    if (relayState && !strcmp(relayState,"default")) {
+        pair<bool,const char*> homeURL=request.getApplication().getString("homeURL");
+        relayState=homeURL.first ? homeURL.second : "/";
+    }
+    else if (!relayState || !strcmp(relayState,"cookie")) {
+        // Pull the value from the "relay state" cookie.
+        pair<string,const char*> relay_cookie = request.getApplication().getCookieNameProps("_shibstate_");
+        relayState = request.getCookie(relay_cookie.first.c_str());
+        if (!relayState || !*relayState) {
+            // No apparent relay state value to use, so fall back on the default.
+            pair<bool,const char*> homeURL=request.getApplication().getString("homeURL");
+            relayState=homeURL.first ? homeURL.second : "/";
+        }
+        else {
+            char* rscopy=strdup(relayState);
+            SAMLConfig::getConfig().getURLEncoder()->decode(rscopy);
+            s=rscopy;
+            free(rscopy);
+            relayState=s.c_str();
+        }
+        request.setCookie(relay_cookie.first.c_str(),relay_cookie.second);
+    }
+
+    // We've got a good session, so set the session cookie.
+    pair<string,const char*> shib_cookie=request.getApplication().getCookieNameProps("_shibsession_");
+    k += shib_cookie.second;
+    request.setCookie(shib_cookie.first.c_str(), k.c_str());
+
+    // History cookie.
+    maintainHistory(request, providerId, shib_cookie.second);
+
+    // Now redirect to the target.
+    return make_pair(true, request.sendRedirect(relayState));
+}
+
+void AssertionConsumerService::checkAddress(
+    const Application& application, const HTTPRequest& httpRequest, const char* issuedTo
+    ) const
+{
+    const PropertySet* props=application.getPropertySet("Sessions");
+    pair<bool,bool> checkAddress = props ? props->getBool("checkAddress") : make_pair(false,true);
+    if (!checkAddress.first)
+        checkAddress.second=true;
+
+    if (checkAddress.second) {
+        m_log.debug("checking client address");
+        if (httpRequest.getRemoteAddr() != issuedTo) {
+            throw FatalProfileException(
+               "Your client's current address ($client_addr) differs from the one used when you authenticated "
+                "to your identity provider. To correct this problem, you may need to bypass a proxy server. "
+                "Please contact your local support staff or help desk for assistance.",
+                namedparams(1,"client_addr",httpRequest.getRemoteAddr().c_str())
+                );
+        }
+    }
+}
+
+ResolutionContext* AssertionConsumerService::resolveAttributes(
+    const Application& application,
+    const HTTPRequest& httpRequest,
+    const saml2md::EntityDescriptor* issuer,
+    const saml2::NameID& nameid,
+    const vector<const Assertion*>* tokens
+    ) const
+{
+    AttributeResolver* resolver = application.getAttributeResolver();
+    if (!resolver) {
+        m_log.info("no AttributeResolver available, skipping resolution");
+        return NULL;
+    }
+    
+    try {
+        m_log.debug("resolving attributes...");
+        auto_ptr<ResolutionContext> ctx(
+            resolver->createResolutionContext(application, httpRequest.getRemoteAddr().c_str(), issuer, nameid, tokens)
+            );
+        resolver->resolveAttributes(*ctx.get());
+        return ctx.release();
+    }
+    catch (exception& ex) {
+        m_log.error("attribute resolution failed: %s", ex.what());
+    }
+    
+    return NULL;
+}
+
+void AssertionConsumerService::maintainHistory(SPRequest& request, const char* providerId, const char* cookieProps) const
+{
+    if (!providerId)
+        return;
+        
+    const PropertySet* sessionProps=request.getApplication().getPropertySet("Sessions");
+    pair<bool,bool> idpHistory=sessionProps->getBool("idpHistory");
+    if (!idpHistory.first || idpHistory.second) {
+        // Set an IdP history cookie locally (essentially just a CDC).
+        CommonDomainCookie cdc(request.getCookie(CommonDomainCookie::CDCName));
+
+        // Either leave in memory or set an expiration.
+        pair<bool,unsigned int> days=sessionProps->getUnsignedInt("idpHistoryDays");
+        if (!days.first || days.second==0) {
+            string c = string(cdc.set(providerId)) + cookieProps;
+            request.setCookie(CommonDomainCookie::CDCName, c.c_str());
+        }
+        else {
+            time_t now=time(NULL) + (days.second * 24 * 60 * 60);
+#ifdef HAVE_GMTIME_R
+            struct tm res;
+            struct tm* ptime=gmtime_r(&now,&res);
+#else
+            struct tm* ptime=gmtime(&now);
+#endif
+            char timebuf[64];
+            strftime(timebuf,64,"%a, %d %b %Y %H:%M:%S GMT",ptime);
+            string c = string(cdc.set(providerId)) + cookieProps + "; expires=" + timebuf;
+            request.setCookie(CommonDomainCookie::CDCName, c.c_str());
+        }
+    }
+}
index 0728b19..385fa6c 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 #include "internal.h"
+#include "ServiceProvider.h"
 #include "handler/RemotedHandler.h"
 
 #include <algorithm>
@@ -201,10 +202,35 @@ long RemotedResponse::sendRedirect(const char* url)
 }
 
 
-const DDF& RemotedHandler::wrap(const SPRequest& request, DDF& in, const vector<string>& headers, bool certs) const
+unsigned int RemotedHandler::m_counter = 0;
+
+RemotedHandler::RemotedHandler()
+{
+    m_address += ('A' + (m_counter++));
+    m_address += "::run::RemotedHandler";
+
+    SPConfig& conf = SPConfig::getConfig();
+    if (conf.isEnabled(SPConfig::OutOfProcess)) {
+        ListenerService* listener = conf.getServiceProvider()->getListenerService(false);
+        if (listener)
+            listener->regListener(m_address.c_str(),this);
+        else
+            Category::getInstance(SHIBSP_LOGCAT".Handler").info("no ListenerService available, handler remoting disabled");
+    }
+}
+
+RemotedHandler::~RemotedHandler()
 {
-    if (!in.isstruct())
-        in.structure();
+    SPConfig& conf = SPConfig::getConfig();
+    ListenerService* listener=conf.getServiceProvider()->getListenerService(false);
+    if (listener && conf.isEnabled(SPConfig::OutOfProcess))
+        listener->unregListener(m_address.c_str(),this);
+    m_counter--;
+}
+
+DDF RemotedHandler::wrap(const SPRequest& request, const vector<string>* headers, bool certs) const
+{
+    DDF in = DDF(m_address.c_str()).structure();
     in.addmember("scheme").string(request.getScheme());
     in.addmember("hostname").string(request.getHostname());
     in.addmember("port").integer(request.getPort());
@@ -218,21 +244,23 @@ const DDF& RemotedHandler::wrap(const SPRequest& request, DDF& in, const vector<
     in.addmember("url").string(request.getRequestURL());
     in.addmember("query").string(request.getQueryString());
 
-    string hdr;
-    DDF hin = in.addmember("headers").structure();
-    for (vector<string>::const_iterator h = headers.begin(); h!=headers.end(); ++h) {
-        hdr = request.getHeader(h->c_str());
-        if (!hdr.empty())
-            hin.addmember(h->c_str()).string(hdr.c_str());
+    if (headers) {
+        string hdr;
+        DDF hin = in.addmember("headers").structure();
+        for (vector<string>::const_iterator h = headers->begin(); h!=headers->end(); ++h) {
+            hdr = request.getHeader(h->c_str());
+            if (!hdr.empty())
+                hin.addmember(h->c_str()).string(hdr.c_str());
+        }
     }
 
     if (certs) {
         const vector<XSECCryptoX509*>& xvec = request.getClientCertificates();
         if (!xvec.empty()) {
-            hin = in.addmember("certificates").list();
+            DDF clist = in.addmember("certificates").list();
             for (vector<XSECCryptoX509*>::const_iterator x = xvec.begin(); x!=xvec.end(); ++x) {
                 DDF x509 = DDF(NULL).string((*x)->getDEREncodingSB().rawCharBuffer());
-                hin.add(x509);
+                clist.add(x509);
             }
         }
     }
diff --git a/shibsp/handler/impl/SAML1Consumer.cpp b/shibsp/handler/impl/SAML1Consumer.cpp
new file mode 100644 (file)
index 0000000..b9d45fa
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ *  Copyright 2001-2007 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.
+ */
+
+/**
+ * SAML1Consumer.cpp
+ * 
+ * SAML 1.x assertion consumer service 
+ */
+
+#include "internal.h"
+#include "Application.h"
+#include "exceptions.h"
+#include "ServiceProvider.h"
+#include "SessionCache.h"
+#include "attribute/resolver/ResolutionContext.h"
+#include "handler/AssertionConsumerService.h"
+
+#include <saml/saml1/core/Assertions.h>
+#include <saml/saml1/core/Protocols.h>
+#include <saml/saml1/profile/BrowserSSOProfileValidator.h>
+#include <saml/saml2/metadata/Metadata.h>
+
+using namespace shibsp;
+using namespace opensaml::saml1;
+using namespace opensaml::saml1p;
+using namespace opensaml;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+using saml2::NameID;
+using saml2::NameIDBuilder;
+using saml2md::EntityDescriptor;
+
+namespace shibsp {
+
+#if defined (_MSC_VER)
+    #pragma warning( push )
+    #pragma warning( disable : 4250 )
+#endif
+    
+    class SHIBSP_DLLLOCAL SAML1Consumer : public AssertionConsumerService
+    {
+    public:
+        SAML1Consumer(const DOMElement* e) : AssertionConsumerService(e, Category::getInstance(SHIBSP_LOGCAT".SAML1")) {}
+        virtual ~SAML1Consumer() {}
+        
+    private:
+        string implementProtocol(
+            const Application& application,
+            const HTTPRequest& httpRequest,
+            SecurityPolicy& policy,
+            const PropertySet* settings,
+            const XMLObject& xmlObject
+            ) const;
+    };
+
+#if defined (_MSC_VER)
+    #pragma warning( pop )
+#endif
+
+    Handler* SHIBSP_DLLLOCAL SAML1ConsumerFactory(const DOMElement* const & e)
+    {
+        return new SAML1Consumer(e);
+    }
+    
+};
+
+string SAML1Consumer::implementProtocol(
+    const Application& application,
+    const HTTPRequest& httpRequest,
+    SecurityPolicy& policy,
+    const PropertySet* settings,
+    const XMLObject& xmlObject
+    ) const
+{
+    // Implementation of SAML 1.x SSO profile(s).
+    m_log.debug("processing message against SAML 1.x SSO profile");
+
+    // With the binding aspects now moved out to the MessageDecoder,
+    // the focus here is on the assertion content. For SAML 1.x,
+    // all the security comes from the protocol layer, and signing
+    // the assertion isn't sufficient. So we can check the policy
+    // object now and bail if it's not a secure message.
+    if (!policy.isSecure())
+        throw SecurityPolicyException("Security of SAML 1.x SSO response not established.");
+
+    // Check for errors...this will throw if it's not a successful message.
+    checkError(&xmlObject);
+
+    const Response* response = dynamic_cast<const Response*>(&xmlObject);
+    if (!response)
+        throw FatalProfileException("Incoming message was not a samlp:Response.");
+
+    const vector<saml1::Assertion*>& assertions = response->getAssertions();
+    if (assertions.empty())
+        throw FatalProfileException("Incoming message contained no SAML assertions.");
+
+    // Maintain list of "legit" tokens to feed to SP subsystems.
+    const AuthenticationStatement* ssoStatement=NULL;
+    vector<const opensaml::Assertion*> tokens;
+
+    // Profile validator.
+    time_t now = time(NULL);
+    BrowserSSOProfileValidator ssoValidator(application.getAudiences(), now);
+
+    // With this flag on, we ignore any unsigned assertions.
+    pair<bool,bool> flag = settings->getBool("signedAssertions");
+    for (vector<saml1::Assertion*>::const_iterator a = assertions.begin(); a!=assertions.end(); ++a) {
+        // Skip unsigned assertion?
+        if (!(*a)->getSignature() && flag.first && flag.second) {
+            m_log.warn("found unsigned assertion in SAML response, ignoring it per signedAssertions policy");
+            continue;
+        }
+
+        try {
+            // Run the policy over the assertion. Handles issuer consistency, replay, freshness,
+            // and signature verification, assuming the relevant rules are configured.
+            policy.evaluate(*(*a));
+
+            // Now do profile and core semantic validation to ensure we can use it for SSO.
+            ssoValidator.validateAssertion(*(*a));
+
+            // Track it as a valid token.
+            tokens.push_back(*a);
+
+            // Save off the first valid SSO statement.
+            if (!ssoStatement && !(*a)->getAuthenticationStatements().empty())
+                ssoStatement = (*a)->getAuthenticationStatements().front();
+        }
+        catch (exception& ex) {
+            m_log.warn("profile validation error in assertion: %s", ex.what());
+        }
+    }
+
+    if (!ssoStatement)
+        throw FatalProfileException("A valid authentication statement was not found in the incoming message.");
+
+    // Address checking.
+    SubjectLocality* locality = ssoStatement->getSubjectLocality();
+    if (locality && locality->getIPAddress()) {
+        auto_ptr_char ip(locality->getIPAddress());
+        checkAddress(application, httpRequest, ip.get());
+    }
+
+    m_log.debug("SSO profile processing completed successfully");
+
+    // We've successfully "accepted" at least one SSO token, along with any additional valid tokens.
+    // To complete processing, we need to resolve attributes and then create the session.
+
+    // First, normalize the SAML 1.x NameIdentifier...
+    auto_ptr<NameID> nameid(NameIDBuilder::buildNameID());
+    NameIdentifier* n = ssoStatement->getSubject()->getNameIdentifier();
+    if (n) {
+        nameid->setName(n->getName());
+        nameid->setFormat(n->getFormat());
+        nameid->setNameQualifier(n->getNameQualifier());
+    }
+
+    const EntityDescriptor* issuerMetadata = dynamic_cast<const EntityDescriptor*>(policy.getIssuerMetadata()->getParent());
+    auto_ptr<ResolutionContext> ctx(
+        resolveAttributes(application, httpRequest, issuerMetadata, *nameid.get(), &tokens)
+        );
+
+    // Copy over any new tokens, but leave them in the context for cleanup.
+    tokens.insert(tokens.end(), ctx->getResolvedAssertions().begin(), ctx->getResolvedAssertions().end());
+
+    // Now we have to extract the authentication details for session setup.
+
+    // Session expiration for SAML 1.x is purely SP-driven, and the method is mapped to a ctx class.
+    const PropertySet* sessionProps = application.getPropertySet("Sessions");
+    pair<bool,unsigned int> lifetime = sessionProps ? sessionProps->getUnsignedInt("lifetime") : make_pair(true,28800);
+    if (!lifetime.first)
+        lifetime.second = 28800;
+    auto_ptr_char authnInstant(
+        ssoStatement->getAuthenticationInstant() ? ssoStatement->getAuthenticationInstant()->getRawData() : NULL
+        );
+    auto_ptr_char authnMethod(ssoStatement->getAuthenticationMethod());
+
+    vector<shibsp::Attribute*>& attrs = ctx->getResolvedAttributes();
+    string key = application.getServiceProvider().getSessionCache()->insert(
+        lifetime.second ? now + lifetime.second : 0,
+        application,
+        httpRequest.getRemoteAddr().c_str(),
+        issuerMetadata,
+        *nameid.get(),
+        authnInstant.get(),
+        NULL,
+        authnMethod.get(),
+        NULL,
+        &tokens,
+        &attrs
+        );
+    attrs.clear();  // Attributes are owned by cache now.
+    return key;
+}
index b419c2e..b7d6f39 100644 (file)
@@ -66,18 +66,6 @@ namespace {
     #pragma warning( disable : 4250 )\r
 #endif\r
 \r
-    class SHIBSP_DLLLOCAL TokenValidator : public Validator\r
-    {\r
-    public:\r
-        TokenValidator(const Application& app, time_t ts=0, const RoleDescriptor* role=NULL) : m_app(app), m_ts(ts), m_role(role) {}\r
-        void validate(const XMLObject*) const;\r
-\r
-    private:\r
-        const Application& m_app;\r
-        time_t m_ts;\r
-        const RoleDescriptor* m_role;\r
-    };\r
-\r
     static vector<const Handler*> g_noHandlers;\r
 \r
     // Application configuration wrapper\r
@@ -122,9 +110,6 @@ namespace {
         const vector<const XMLCh*>& getAudiences() const {\r
             return (m_audiences.empty() && m_base) ? m_base->getAudiences() : m_audiences;\r
         }\r
-        Validator* getTokenValidator(time_t ts=0, const saml2md::RoleDescriptor* role=NULL) const {\r
-            return new TokenValidator(*this, ts, role);\r
-        }\r
 \r
         // Provides filter to exclude special config elements.\r
         short acceptNode(const DOMNode* node) const;\r
@@ -358,122 +343,6 @@ namespace shibsp {
     }\r
 };\r
 \r
-void TokenValidator::validate(const XMLObject* xmlObject) const\r
-{\r
-#ifdef _DEBUG\r
-    xmltooling::NDC ndc("validate");\r
-#endif\r
-    Category& log=Category::getInstance(SHIBSP_LOGCAT".Application");\r
-\r
-    const opensaml::Assertion* root = NULL;\r
-    const saml2::Assertion* token2 = dynamic_cast<const saml2::Assertion*>(xmlObject);\r
-    if (token2) {\r
-        const saml2::Conditions* conds = token2->getConditions();\r
-        // First verify the time conditions, using the specified timestamp, if non-zero.\r
-        if (m_ts>0 && conds) {\r
-            unsigned int skew = XMLToolingConfig::getConfig().clock_skew_secs;\r
-            time_t t=conds->getNotBeforeEpoch();\r
-            if (m_ts+skew < t)\r
-                throw ValidationException("Assertion is not yet valid.");\r
-            t=conds->getNotOnOrAfterEpoch();\r
-            if (t <= m_ts-skew)\r
-                throw ValidationException("Assertion is no longer valid.");\r
-        }\r
-\r
-        // Now we process conditions. Only audience restrictions at the moment.\r
-        const vector<saml2::Condition*>& convec = conds->getConditions();\r
-        for (vector<saml2::Condition*>::const_iterator c = convec.begin(); c!=convec.end(); ++c) {\r
-            const saml2::AudienceRestriction* ac=dynamic_cast<const saml2::AudienceRestriction*>(*c);\r
-            if (!ac) {\r
-                log.error("unrecognized Condition in assertion (%s)",\r
-                    (*c)->getSchemaType() ? (*c)->getSchemaType()->toString().c_str() : (*c)->getElementQName().toString().c_str());\r
-                throw ValidationException("Assertion contains an unrecognized condition.");\r
-            }\r
-\r
-            bool found = false;\r
-            const vector<saml2::Audience*>& auds1 = ac->getAudiences();\r
-            const vector<const XMLCh*>& auds2 = m_app.getAudiences();\r
-            for (vector<saml2::Audience*>::const_iterator a = auds1.begin(); !found && a!=auds1.end(); ++a) {\r
-                for (vector<const XMLCh*>::const_iterator a2 = auds2.begin(); !found && a2!=auds2.end(); ++a2) {\r
-                    found = XMLString::equals((*a)->getAudienceURI(), *a2);\r
-                }\r
-            }\r
-\r
-            if (!found) {\r
-                ostringstream os;\r
-                os << *ac;\r
-                log.error("unacceptable AudienceRestriction in assertion (%s)", os.str().c_str());\r
-                throw ValidationException("Assertion contains an unacceptable AudienceRestriction.");\r
-            }\r
-        }\r
-\r
-        root = token2;\r
-    }\r
-    else {\r
-        const saml1::Assertion* token1 = dynamic_cast<const saml1::Assertion*>(xmlObject);\r
-        if (token1) {\r
-            const saml1::Conditions* conds = token1->getConditions();\r
-            // First verify the time conditions, using the specified timestamp, if non-zero.\r
-            if (m_ts>0 && conds) {\r
-                unsigned int skew = XMLToolingConfig::getConfig().clock_skew_secs;\r
-                time_t t=conds->getNotBeforeEpoch();\r
-                if (m_ts+skew < t)\r
-                    throw ValidationException("Assertion is not yet valid.");\r
-                t=conds->getNotOnOrAfterEpoch();\r
-                if (t <= m_ts-skew)\r
-                    throw ValidationException("Assertion is no longer valid.");\r
-            }\r
-\r
-            // Now we process conditions. Only audience restrictions at the moment.\r
-            const vector<saml1::Condition*>& convec = conds->getConditions();\r
-            for (vector<saml1::Condition*>::const_iterator c = convec.begin(); c!=convec.end(); ++c) {\r
-                const saml1::AudienceRestrictionCondition* ac=dynamic_cast<const saml1::AudienceRestrictionCondition*>(*c);\r
-                if (!ac) {\r
-                    log.error("unrecognized Condition in assertion (%s)",\r
-                        (*c)->getSchemaType() ? (*c)->getSchemaType()->toString().c_str() : (*c)->getElementQName().toString().c_str());\r
-                    throw ValidationException("Assertion contains an unrecognized condition.");\r
-                }\r
-\r
-                bool found = false;\r
-                const vector<saml1::Audience*>& auds1 = ac->getAudiences();\r
-                const vector<const XMLCh*>& auds2 = m_app.getAudiences();\r
-                for (vector<saml1::Audience*>::const_iterator a = auds1.begin(); !found && a!=auds1.end(); ++a) {\r
-                    for (vector<const XMLCh*>::const_iterator a2 = auds2.begin(); !found && a2!=auds2.end(); ++a2) {\r
-                        found = XMLString::equals((*a)->getAudienceURI(), *a2);\r
-                    }\r
-                }\r
-\r
-                if (!found) {\r
-                    ostringstream os;\r
-                    os << *ac;\r
-                    log.error("unacceptable AudienceRestrictionCondition in assertion (%s)", os.str().c_str());\r
-                    throw ValidationException("Assertion contains an unacceptable AudienceRestrictionCondition.");\r
-                }\r
-            }\r
-\r
-            root = token1;\r
-        }\r
-        else {\r
-            throw ValidationException("Unknown object type passed to token validator.");\r
-        }\r
-    }\r
-\r
-    if (!m_role || !m_app.getTrustEngine()) {\r
-        log.warn("no issuer role or TrustEngine provided, so no signature validation performed");\r
-        return;\r
-    }\r
-\r
-    const PropertySet* policy=m_app.getServiceProvider().getPolicySettings(m_app.getString("policyId").second);\r
-    pair<bool,bool> signedAssertions=policy ? policy->getBool("signedAssertions") : make_pair(false,false);\r
-\r
-    if (root->getSignature()) {\r
-        if (!m_app.getTrustEngine()->validate(*(root->getSignature()),*m_role))\r
-            throw ValidationException("Assertion signature did not validate.");\r
-    }\r
-    else if (signedAssertions.first && signedAssertions.second)\r
-        throw ValidationException("Assertion was unsigned, violating policy.");\r
-}\r
-\r
 XMLApplication::XMLApplication(\r
     const ServiceProvider* sp,\r
     const DOMElement* e,\r
index 3b0a311..c230b57 100644 (file)
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\handler\impl\AssertionConsumerService.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\handler\impl\RemotedHandler.cpp"\r
                                                >\r
                                        </File>\r
+                                       <File\r
+                                               RelativePath=".\handler\impl\SAML1Consumer.cpp"\r
+                                               >\r
+                                       </File>\r
                                </Filter>\r
                        </Filter>\r
                </Filter>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\handler\AssertionConsumerService.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\handler\Handler.h"\r
                                        >\r
                                </File>\r