Redesign condition and profile processing based on new policy rules. Fix element...
authorScott Cantor <cantor.2@osu.edu>
Thu, 19 Mar 2009 21:22:57 +0000 (21:22 +0000)
committerScott Cantor <cantor.2@osu.edu>
Thu, 19 Mar 2009 21:22:57 +0000 (21:22 +0000)
configs/shibboleth2.xml
schemas/shibboleth-2.0-native-sp-config.xsd
shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp
shibsp/handler/AssertionConsumerService.h
shibsp/handler/impl/AssertionConsumerService.cpp
shibsp/handler/impl/SAML1Consumer.cpp
shibsp/handler/impl/SAML2Consumer.cpp
shibsp/handler/impl/SAML2Logout.cpp
shibsp/impl/XMLServiceProvider.cpp
shibsp/security/SecurityPolicy.cpp

index 281fa18..b1ae6d3 100644 (file)
     
     <!-- Each policy defines a set of rules to use to secure messages. -->
     <SecurityPolicies>
-        <!-- The predefined policy enforces replay/freshness and permits signing and client TLS. -->
+        <!--
+        The predefined policy enforces replay/freshness, standard
+        condition processing, and permits signing and client TLS.
+        -->
         <Policy id="default" validate="false">
-            <Rule type="MessageFlow" checkReplay="true" expires="60"/>
-            <Rule type="ClientCertAuth" errorFatal="true"/>
-            <Rule type="XMLSigning" errorFatal="true"/>
-            <Rule type="SimpleSigning" errorFatal="true"/>
+            <PolicyRule type="MessageFlow" checkReplay="true" expires="60"/>
+            <PolicyRule type="Conditions"/>
+            <PolicyRule type="ClientCertAuth" errorFatal="true"/>
+            <PolicyRule type="XMLSigning" errorFatal="true"/>
+            <PolicyRule type="SimpleSigning" errorFatal="true"/>
         </Policy>
     </SecurityPolicies>
 
index 29db543..5a1623d 100644 (file)
                                                <documentation>Specifies a set of SecurityPolicyRule plugins</documentation>\r
                                        </annotation>\r
                                        <complexType>\r
-                                               <sequence>\r
+                                               <choice>\r
                                                        <element name="Rule" type="conf:PluggableType" minOccurs="1" maxOccurs="unbounded"/>\r
-                                               </sequence>\r
+                            <element name="PolicyRule" type="conf:PluggableType" minOccurs="1" maxOccurs="unbounded"/>\r
+                                               </choice>\r
                                                <attribute name="id" type="conf:string" use="required"/>\r
                                                <attribute name="validate" type="boolean"/>\r
                                                <anyAttribute namespace="##any" processContents="lax"/>\r
index 367a9cf..fbecae7 100644 (file)
@@ -206,6 +206,7 @@ namespace shibsp {
         bool SAML2Query(QueryContext& ctx) const;
 
         Category& m_log;
+        string m_policyId;
         vector<AttributeDesignator*> m_SAML1Designators;
         vector<saml2::Attribute*> m_SAML2Designators;
     };
@@ -215,6 +216,7 @@ namespace shibsp {
         return new QueryResolver(e);
     }
 
+    static const XMLCh _policyId[] = UNICODE_LITERAL_8(p,o,l,i,c,y,I,d);
 };
 
 QueryResolver::QueryResolver(const DOMElement* e) : m_log(Category::getInstance(SHIBSP_LOGCAT".AttributeResolver.Query"))
@@ -223,6 +225,12 @@ QueryResolver::QueryResolver(const DOMElement* e) : m_log(Category::getInstance(
     xmltooling::NDC ndc("QueryResolver");
 #endif
 
+    const XMLCh* pid = e ? e->getAttributeNS(NULL, _policyId) : NULL;
+    if (pid && *pid) {
+        auto_ptr_char temp(pid);
+        m_policyId = temp.get();
+    }
+
     DOMElement* child = XMLHelper::getFirstChildElement(e);
     while (child) {
         try {
@@ -266,7 +274,16 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const
 
     const Application& application = ctx.getApplication();
     const PropertySet* relyingParty = application.getRelyingParty(ctx.getEntityDescriptor());
-    shibsp::SecurityPolicy policy(application);
+
+    // Locate policy key.
+    const char* policyId = m_policyId.empty() ? application.getString("policyId").second : m_policyId.c_str();
+
+    // Access policy properties.
+    const PropertySet* settings = application.getServiceProvider().getPolicySettings(policyId);
+    pair<bool,bool> validate = settings->getBool("validate");
+
+    shibsp::SecurityPolicy policy(application, NULL, validate.first && validate.second, policyId);
+    policy.getAudiences().push_back(relyingParty->getXMLString("entityID").second);
     MetadataCredentialCriteria mcc(*AA);
     shibsp::SOAPClient soaper(policy);
 
@@ -396,14 +413,23 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const
     }
 
     const Application& application = ctx.getApplication();
-    shibsp::SecurityPolicy policy(application);
-    MetadataCredentialCriteria mcc(*AA);
-    shibsp::SOAPClient soaper(policy);
-
     const PropertySet* relyingParty = application.getRelyingParty(ctx.getEntityDescriptor());
+
+    // Locate policy key.
+    const char* policyId = m_policyId.empty() ? application.getString("policyId").second : m_policyId.c_str();
+
+    // Access policy properties.
+    const PropertySet* settings = application.getServiceProvider().getPolicySettings(policyId);
+    pair<bool,bool> validate = settings->getBool("validate");
+
     pair<bool,bool> signedAssertions = relyingParty->getBool("requireSignedAssertions");
     pair<bool,const char*> encryption = relyingParty->getString("encryption");
 
+    shibsp::SecurityPolicy policy(application, NULL, validate.first && validate.second, policyId);
+    policy.getAudiences().push_back(relyingParty->getXMLString("entityID").second);
+    MetadataCredentialCriteria mcc(*AA);
+    shibsp::SOAPClient soaper(policy);
+
     auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP);
     saml2p::StatusResponseType* srt=NULL;
     const vector<AttributeService*>& endpoints=AA->getAttributeServices();
index a7289fe..752e86c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2001-2007 Internet2
+ *  Copyright 2001-2009 Internet2
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -76,6 +76,23 @@ namespace shibsp {
         void generateMetadata(opensaml::saml2md::SPSSODescriptor& role, const char* handlerURL) const;
         
         /**
+         * Returns a SecurityPolicy instance to use for an incoming request.
+         *
+         * <p>Allows handlers to customize the type of policy object their policy rules might require.
+         * <p>The caller <strong>MUST</strong> lock the application's MetadataProvider for the life
+         * of the returned object.
+         *
+         * @param application   reference to application receiving message
+         * @param role          identifies the role (generally IdP or SP) of the policy peer
+         * @param validate      true iff XML parsing should be done with validation
+         * @param policyId      identifies policy rules to auto-attach, defaults to the application's set
+         * @return  a new policy instance, which the caller is responsible for freeing
+         */
+        virtual opensaml::SecurityPolicy* createSecurityPolicy(
+            const Application& application, const xmltooling::QName* role, bool validate, const char* policyId
+            ) const;
+
+        /**
          * Implement protocol-specific handling of the incoming decoded message.
          * 
          * <p>The result of implementing the protocol should be an exception or
index 99a21da..ead66b3 100644 (file)
@@ -147,20 +147,22 @@ pair<bool,long> AssertionConsumerService::processMessage(
     Locker metadataLocker(application.getMetadataProvider());
 
     // Create the policy.
-    shibsp::SecurityPolicy policy(application, &m_role, validate.first && validate.second, policyId.second);
+    auto_ptr<opensaml::SecurityPolicy> policy(
+        createSecurityPolicy(application, &m_role, validate.first && validate.second, policyId.second)
+        );
 
     string relayState;
     try {
         // Decode the message and process it in a protocol-specific way.
-        auto_ptr<XMLObject> msg(m_decoder->decode(relayState, httpRequest, policy));
+        auto_ptr<XMLObject> msg(m_decoder->decode(relayState, httpRequest, *(policy.get())));
         if (!msg.get())
             throw BindingException("Failed to decode an SSO protocol response.");
         DDF postData = recoverPostData(application, httpRequest, httpResponse, relayState.c_str());
         DDFJanitor postjan(postData);
         recoverRelayState(application, httpRequest, httpResponse, relayState);
-        implementProtocol(application, httpRequest, httpResponse, policy, settings, *msg.get());
+        implementProtocol(application, httpRequest, httpResponse, *(policy.get()), settings, *msg.get());
 
-        auto_ptr_char issuer(policy.getIssuer() ? policy.getIssuer()->getName() : NULL);
+        auto_ptr_char issuer(policy->getIssuer() ? policy->getIssuer()->getName() : NULL);
 
         // History cookie.
         if (issuer.get() && *issuer.get())
@@ -221,7 +223,8 @@ void AssertionConsumerService::checkAddress(const Application& application, cons
 
 #ifndef SHIBSP_LITE
 
-void AssertionConsumerService::generateMetadata(SPSSODescriptor& role, const char* handlerURL) const {
+void AssertionConsumerService::generateMetadata(SPSSODescriptor& role, const char* handlerURL) const
+{
     const char* loc = getString("Location").second;
     string hurl(handlerURL);
     if (*loc != '/')
@@ -244,6 +247,13 @@ void AssertionConsumerService::generateMetadata(SPSSODescriptor& role, const cha
     role.getAssertionConsumerServices().push_back(ep);
 }
 
+opensaml::SecurityPolicy* AssertionConsumerService::createSecurityPolicy(
+    const Application& application, const xmltooling::QName* role, bool validate, const char* policyId
+    ) const
+{
+    return new SecurityPolicy(application, role, validate, policyId);
+}
+
 class SHIBSP_DLLLOCAL DummyContext : public ResolutionContext
 {
 public:
index d1c46b8..30c059d 100644 (file)
@@ -1,6 +1,6 @@
 /*
- *  Copyright 2001-2007 Internet2
- * 
+ *  Copyright 2001-2009 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
@@ -16,8 +16,8 @@
 
 /**
  * SAML1Consumer.cpp
- * 
- * SAML 1.x assertion consumer service 
+ *
+ * SAML 1.x assertion consumer service
  */
 
 #include "internal.h"
@@ -29,9 +29,9 @@
 # include "ServiceProvider.h"
 # include "SessionCache.h"
 # include "attribute/resolver/ResolutionContext.h"
+# include <saml/SAMLConfig.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 opensaml::saml1;
 using namespace opensaml::saml1p;
@@ -55,18 +55,25 @@ namespace shibsp {
     #pragma warning( push )
     #pragma warning( disable : 4250 )
 #endif
-    
+
     class SHIBSP_DLLLOCAL SAML1Consumer : public AssertionConsumerService
     {
     public:
         SAML1Consumer(const DOMElement* e, const char* appId)
-                : AssertionConsumerService(e, appId, Category::getInstance(SHIBSP_LOGCAT".SSO.SAML1")) {
+            : AssertionConsumerService(e, appId, Category::getInstance(SHIBSP_LOGCAT".SSO.SAML1")) {
 #ifndef SHIBSP_LITE
+            m_ssoRule = NULL;
             m_post = XMLString::equals(getString("Binding").second, samlconstants::SAML1_PROFILE_BROWSER_POST);
+            if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess))
+                m_ssoRule = SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(SAML1BROWSERSSO_POLICY_RULE, e);
 #endif
         }
-        virtual ~SAML1Consumer() {}
-        
+        virtual ~SAML1Consumer() {
+#ifndef SHIBSP_LITE
+            delete m_ssoRule;
+#endif
+        }
+
 #ifndef SHIBSP_LITE
         void generateMetadata(SPSSODescriptor& role, const char* handlerURL) const {
             AssertionConsumerService::generateMetadata(role, handlerURL);
@@ -83,7 +90,9 @@ namespace shibsp {
             const PropertySet* settings,
             const XMLObject& xmlObject
             ) const;
+
         bool m_post;
+        SecurityPolicyRule* m_ssoRule;
 #endif
     };
 
@@ -95,7 +104,19 @@ namespace shibsp {
     {
         return new SAML1Consumer(p.first, p.second);
     }
-    
+
+#ifndef SHIBSP_LITE
+    class SHIBSP_DLLLOCAL _rulenamed : std::unary_function<const SecurityPolicyRule*,bool>
+    {
+    public:
+        _rulenamed(const char* name) : m_name(name) {}
+        bool operator()(const SecurityPolicyRule* rule) const {
+            return rule ? !strcmp(m_name, rule->getType()) : false;
+        }
+    private:
+        const char* m_name;
+    };
+#endif
 };
 
 #ifndef SHIBSP_LITE
@@ -125,7 +146,7 @@ void SAML1Consumer::implementProtocol(
             throw MetadataException("Security of SAML 1.x SSO POST response not established.");
         throw SecurityPolicyException("Security of SAML 1.x SSO POST response not established.");
     }
-        
+
     // Remember whether we already established trust.
     bool alreadySecured = policy.isAuthenticated();
 
@@ -158,10 +179,14 @@ void SAML1Consumer::implementProtocol(
     // Saves off error messages potentially helpful for users.
     string contextualError;
 
-    // Profile validator.
-    time_t now = time(NULL);
-    BrowserSSOProfileValidator ssoValidator(application.getRelyingParty(entity)->getXMLString("entityID").second, application.getAudiences(), now);
+    // Ensure the BrowserSSO rule is in the policy set.
+    if (find_if(policy.getRules(), _rulenamed(SAML1BROWSERSSO_POLICY_RULE)) == NULL)
+        policy.getRules().push_back(m_ssoRule);
+
+    // Populate recipient as audience.
+    policy.getAudiences().push_back(application.getRelyingParty(entity)->getXMLString("entityID").second);
 
+    time_t now = time(NULL);
     for (vector<saml1::Assertion*>::const_iterator a = assertions.begin(); a!=assertions.end(); ++a) {
         try {
             // Skip unsigned assertion?
@@ -178,16 +203,14 @@ void SAML1Consumer::implementProtocol(
                 );
 
             // Run the policy over the assertion. Handles replay, freshness, and
-            // signature verification, assuming the relevant rules are configured.
+            // signature verification, assuming the relevant rules are configured,
+            // along with condition and profile enforcement.
             policy.evaluate(*(*a));
-            
+
             // If no security is in place now, we kick it.
             if (!alreadySecured && !policy.isAuthenticated())
                 throw SecurityPolicyException("Unable to establish security of incoming assertion.");
 
-            // 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);
 
index ee586f6..8dc51e5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2001-2007 Internet2
+ *  Copyright 2001-2009 Internet2
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 # include "ServiceProvider.h"
 # include "SessionCache.h"
 # include "attribute/resolver/ResolutionContext.h"
+# include <saml/SAMLConfig.h>
 # include <saml/saml2/core/Protocols.h>
-# include <saml/saml2/profile/BrowserSSOProfileValidator.h>
 # include <saml/saml2/metadata/Metadata.h>
 # include <saml/saml2/metadata/MetadataCredentialCriteria.h>
+# include <saml/saml2/profile/SAML2AssertionPolicy.h>
 using namespace opensaml::saml2;
 using namespace opensaml::saml2p;
 using namespace opensaml::saml2md;
@@ -58,8 +59,17 @@ namespace shibsp {
     public:
         SAML2Consumer(const DOMElement* e, const char* appId)
             : AssertionConsumerService(e, appId, Category::getInstance(SHIBSP_LOGCAT".SSO.SAML2")) {
+#ifndef SHIBSP_LITE
+            m_ssoRule = NULL;
+            if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess))
+                m_ssoRule = SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(BEARER_POLICY_RULE, e);
+#endif
+        }
+        virtual ~SAML2Consumer() {
+#ifndef SHIBSP_LITE
+            delete m_ssoRule;
+#endif
         }
-        virtual ~SAML2Consumer() {}
 
 #ifndef SHIBSP_LITE
         void generateMetadata(SPSSODescriptor& role, const char* handlerURL) const {
@@ -76,6 +86,8 @@ namespace shibsp {
             const PropertySet* settings,
             const XMLObject& xmlObject
             ) const;
+
+        SecurityPolicyRule* m_ssoRule;
 #endif
     };
 
@@ -88,6 +100,18 @@ namespace shibsp {
         return new SAML2Consumer(p.first, p.second);
     }
 
+#ifndef SHIBSP_LITE
+    class SHIBSP_DLLLOCAL _rulenamed : std::unary_function<const SecurityPolicyRule*,bool>
+    {
+    public:
+        _rulenamed(const char* name) : m_name(name) {}
+        bool operator()(const SecurityPolicyRule* rule) const {
+            return rule ? !strcmp(m_name, rule->getType()) : false;
+        }
+    private:
+        const char* m_name;
+    };
+#endif
 };
 
 #ifndef SHIBSP_LITE
@@ -140,9 +164,6 @@ void SAML2Consumer::implementProtocol(
         flag = application.getRelyingParty(entity)->getBool("requireSignedAssertions");
     }
 
-    time_t now = time(NULL);
-    string dest = httpRequest.getRequestURL();
-
     // authnskew allows rejection of SSO if AuthnInstant is too old.
     const PropertySet* sessionProps = application.getPropertySet("Sessions");
     pair<bool,unsigned int> authnskew = sessionProps ? sessionProps->getUnsignedInt("maxTimeSinceAuthn") : pair<bool,unsigned int>(false,0);
@@ -150,6 +171,14 @@ void SAML2Consumer::implementProtocol(
     // Saves off error messages potentially helpful for users.
     string contextualError;
 
+    // Ensure the Bearer rule is in the policy set.
+    if (find_if(policy.getRules(), _rulenamed(BEARER_POLICY_RULE)) == NULL)
+        policy.getRules().push_back(m_ssoRule);
+
+    // Populate recipient as audience.
+    policy.getAudiences().push_back(application.getRelyingParty(entity)->getXMLString("entityID").second);
+
+    time_t now = time(NULL);
     for (vector<saml2::Assertion*>::const_iterator a = assertions.begin(); a!=assertions.end(); ++a) {
         try {
             // Skip unsigned assertion?
@@ -164,7 +193,8 @@ void SAML2Consumer::implementProtocol(
             extractMessageDetails(*(*a), samlconstants::SAML20P_NS, policy);
 
             // Run the policy over the assertion. Handles replay, freshness, and
-            // signature verification, assuming the relevant rules are configured.
+            // signature verification, assuming the relevant rules are configured,
+            // along with condition and profile enforcement.
             policy.evaluate(*(*a));
 
             // If no security is in place now, we kick it.
@@ -179,14 +209,14 @@ void SAML2Consumer::implementProtocol(
                     throw SecurityPolicyException("The incoming assertion was unsigned, violating local security policy.");
             }
 
-            // Now do profile and core semantic validation to ensure we can use it for SSO.
-            BrowserSSOProfileValidator ssoValidator(
-                application.getRelyingParty(entity)->getXMLString("entityID").second, application.getAudiences(), now, dest.substr(0,dest.find('?')).c_str()
-                );
-            ssoValidator.validateAssertion(*(*a));
-
             // Address checking.
-            checkAddress(application, httpRequest, ssoValidator.getAddress());
+            SubjectConfirmationData* subcondata = dynamic_cast<SubjectConfirmationData*>(
+                dynamic_cast<SAML2AssertionPolicy&>(policy).getSubjectConfirmation()->getSubjectConfirmationData()
+                );
+            if (subcondata && subcondata->getAddress()) {
+                auto_ptr_char boundip(subcondata->getAddress());
+                checkAddress(application, httpRequest, boundip.get());
+            }
 
             // Track it as a valid token.
             tokens.push_back(*a);
@@ -249,7 +279,8 @@ void SAML2Consumer::implementProtocol(
             extractMessageDetails(*decrypted, samlconstants::SAML20P_NS, policy);
 
             // Run the policy over the assertion. Handles replay, freshness, and
-            // signature verification, assuming the relevant rules are configured.
+            // signature verification, assuming the relevant rules are configured,
+            // along with condition and profile enforcement.
             // We have to marshall the object first to ensure signatures can be checked.
             if (!decrypted->getDOM())
                 decrypted->marshall();
@@ -267,14 +298,14 @@ void SAML2Consumer::implementProtocol(
                     throw SecurityPolicyException("The decrypted assertion was unsigned, violating local security policy.");
             }
 
-            // Now do profile and core semantic validation to ensure we can use it for SSO.
-            BrowserSSOProfileValidator ssoValidator(
-                application.getRelyingParty(entity)->getXMLString("entityID").second, application.getAudiences(), now, dest.substr(0,dest.find('?')).c_str()
-                );
-            ssoValidator.validateAssertion(*decrypted);
-
             // Address checking.
-            checkAddress(application, httpRequest, ssoValidator.getAddress());
+            SubjectConfirmationData* subcondata = dynamic_cast<SubjectConfirmationData*>(
+                dynamic_cast<SAML2AssertionPolicy&>(policy).getSubjectConfirmation()->getSubjectConfirmationData()
+                );
+            if (subcondata && subcondata->getAddress()) {
+                auto_ptr_char boundip(subcondata->getAddress());
+                checkAddress(application, httpRequest, boundip.get());
+            }
 
             // Track it as a valid token.
             tokens.push_back(decrypted);
index f7f5c0b..c843897 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2001-2007 Internet2
+ *  Copyright 2001-2009 Internet2
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -441,6 +441,7 @@ pair<bool,long> SAML2Logout::doRequest(const Application& application, const HTT
             if (cacheex) {
                 time_t expires = logoutRequest->getNotOnOrAfter() ? logoutRequest->getNotOnOrAfterEpoch() : 0;
                 cacheex->logout(application, entity, *nameid, &indexes, expires, sessions);
+                m_log.debug("session cache returned %d sessions bound to NameID in logout request", sessions.size());
 
                 // Now we actually terminate everything except for the active session,
                 // if this is front-channel, for notification purposes.
index 26f36af..cc412c6 100644 (file)
@@ -430,6 +430,7 @@ namespace {
     static const XMLCh OutOfProcess[] =         UNICODE_LITERAL_12(O,u,t,O,f,P,r,o,c,e,s,s);
     static const XMLCh _path[] =                UNICODE_LITERAL_4(p,a,t,h);
     static const XMLCh Policy[] =               UNICODE_LITERAL_6(P,o,l,i,c,y);
+    static const XMLCh PolicyRule[] =           UNICODE_LITERAL_10(P,o,l,i,c,y,R,u,l,e);
     static const XMLCh _provider[] =            UNICODE_LITERAL_8(p,r,o,v,i,d,e,r);
     static const XMLCh RelyingParty[] =         UNICODE_LITERAL_12(R,e,l,y,i,n,g,P,a,r,t,y);
     static const XMLCh _ReplayCache[] =         UNICODE_LITERAL_11(R,e,p,l,a,y,C,a,c,h,e);
@@ -766,7 +767,7 @@ XMLApplication::XMLApplication(
 
 #ifndef SHIBSP_LITE
         nlist=e->getElementsByTagNameNS(samlconstants::SAML20_NS,Audience::LOCAL_NAME);
-        if (nlist) {
+        if (nlist && nlist->getLength()) {
             log.warn("use of <saml:Audience> elements outside of a Security Policy Rule is deprecated");
             for (XMLSize_t i=0; i<nlist->getLength(); i++)
                 if (nlist->item(i)->getParentNode()->isSameNode(e) && nlist->item(i)->hasChildNodes())
@@ -1487,8 +1488,8 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o
                 settings->load(child, NULL, &filter);
                 rules.first = settings.release();
 
-                // Process Rule elements.
-                const DOMElement* rule = XMLHelper::getFirstChildElement(child,Rule);
+                // Process PolicyRule elements.
+                const DOMElement* rule = XMLHelper::getFirstChildElement(child,PolicyRule);
                 while (rule) {
                     auto_ptr_char type(rule->getAttributeNS(NULL,_type));
                     try {
@@ -1497,7 +1498,27 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o
                     catch (exception& ex) {
                         log.crit("error instantiating policy rule (%s) in policy (%s): %s", type.get(), id.get(), ex.what());
                     }
-                    rule = XMLHelper::getNextSiblingElement(rule,Rule);
+                    rule = XMLHelper::getNextSiblingElement(rule,PolicyRule);
+                }
+
+                if (rules.second.size() == 0) {
+                    // Process Rule elements.
+                    log.warn("detected legacy Policy configuration, please convert to new PolicyRule syntax");
+                    rule = XMLHelper::getFirstChildElement(child,Rule);
+                    while (rule) {
+                        auto_ptr_char type(rule->getAttributeNS(NULL,_type));
+                        try {
+                            rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(type.get(),rule));
+                        }
+                        catch (exception& ex) {
+                            log.crit("error instantiating policy rule (%s) in policy (%s): %s", type.get(), id.get(), ex.what());
+                        }
+                        rule = XMLHelper::getNextSiblingElement(rule,Rule);
+                    }
+
+                    // Manually add a basic Conditions rule.
+                    log.info("installing a default Conditions rule in policy (%s) for compatibility with legacy configuration", id.get());
+                    rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(CONDITIONS_POLICY_RULE, NULL));
                 }
 
                 child = XMLHelper::getNextSiblingElement(child,Policy);
index a3847db..8317554 100644 (file)
 #include "metadata/MetadataProviderCriteria.h"
 #include "security/SecurityPolicy.h"
 
-using namespace opensaml::saml2;
 using namespace shibsp;
+using namespace opensaml::saml2;
+using namespace std;
 
 SecurityPolicy::SecurityPolicy(const Application& application, const xmltooling::QName* role, bool validate, const char* policyId)
-    : opensaml::SecurityPolicy(application.getMetadataProvider(), role, application.getTrustEngine(), validate), m_application(application) {
-
-    const std::vector<const opensaml::SecurityPolicyRule*>& rules =
+        : opensaml::SecurityPolicy(application.getMetadataProvider(), role, application.getTrustEngine(), validate), m_application(application) {
+    const vector<const opensaml::SecurityPolicyRule*>& rules =
         application.getServiceProvider().getPolicyRules(policyId ? policyId : application.getString("policyId").second);
     getRules().assign(rules.begin(), rules.end());
+
+    // Populate audiences.
+    if (application.getAudiences()) {
+        for (vector<const XMLCh*>::const_iterator a = application.getAudiences()->begin(); a != application.getAudiences()->end(); ++a)
+            getAudiences().push_back(*a);
+    }
 }
 
 opensaml::saml2md::MetadataProvider::Criteria& SecurityPolicy::getMetadataProviderCriteria() const