Fixes to constants, allow sequences of condition rules, new policy rule for Delegatio...
authorScott Cantor <cantor.2@osu.edu>
Sun, 5 Apr 2009 19:16:46 +0000 (19:16 +0000)
committerScott Cantor <cantor.2@osu.edu>
Sun, 5 Apr 2009 19:16:46 +0000 (19:16 +0000)
saml/Makefile.am
saml/binding/SecurityPolicyRule.h
saml/binding/impl/SecurityPolicy.cpp
saml/profile/impl/ConditionsRule.cpp
saml/saml.vcproj
saml/saml2/core/impl/Assertions20Impl.cpp
saml/saml2/core/impl/Assertions20SchemaValidators.cpp
saml/saml2/profile/impl/DelegationRestrictionRule.cpp [new file with mode: 0644]
saml/util/SAMLConstants.cpp

index 4d859d6..050e4df 100644 (file)
@@ -170,6 +170,7 @@ libsaml_la_SOURCES = \
        saml2/profile/impl/Assertion20Validator.cpp \
        saml2/profile/impl/BrowserSSOProfile20Validator.cpp \
     saml2/profile/impl/BearerConfirmationRule.cpp \
+    saml2/profile/impl/DelegationRestrictionRule.cpp \
        saml2/profile/impl/SAML2AssertionPolicy.cpp \
        encryption/EncryptedKeyResolver.cpp \
        signature/ContentReference.cpp \
index f4b746d..8653f98 100644 (file)
@@ -52,12 +52,13 @@ namespace opensaml {
         /**
          * Evaluates the rule against the given request and message.
          *
-         * <p>An exception will be raised if the message is invalid according to
+         * <p>An exception will be raised if the message is fatally invalid according to
          * a policy rule.
          *
          * <p>The return value is used to indicate whether a message was ignored or
          * successfully processed. A false value signals that the rule wasn't successful
-         * but was also not unsuccessful, because the rule was inapplicable to the message.
+         * because the rule was inapplicable to the message, but allows other rules to
+         * return an alternate result.
          *
          * @param message   the incoming message
          * @param request   the protocol request
@@ -82,6 +83,11 @@ namespace opensaml {
     #define AUDIENCE_POLICY_RULE        "Audience"
 
     /**
+     * SecurityPolicyRule for evaluation of SAML DelegationRestriction Conditions.
+     */
+    #define DELEGATION_POLICY_RULE        "Delegation"
+
+    /**
      * SecurityPolicyRule for TLS client certificate authentication.
      *
      * Evaluates client certificates against the issuer's metadata.
index 98875f7..f473153 100644 (file)
@@ -47,6 +47,7 @@ namespace opensaml {
 
     namespace saml2 {
         SAML_DLLLOCAL PluginManager<SecurityPolicyRule,string,const DOMElement*>::Factory BearerConfirmationRuleFactory;
+        SAML_DLLLOCAL PluginManager<SecurityPolicyRule,string,const DOMElement*>::Factory DelegationRestrictionRuleFactory;
     }
 };
 
@@ -63,6 +64,7 @@ void SAML_API opensaml::registerSecurityPolicyRules()
     conf.SecurityPolicyRuleManager.registerFactory(XMLSIGNING_POLICY_RULE, XMLSigningRuleFactory);
     conf.SecurityPolicyRuleManager.registerFactory(SAML1BROWSERSSO_POLICY_RULE, saml1::BrowserSSORuleFactory);
     conf.SecurityPolicyRuleManager.registerFactory(BEARER_POLICY_RULE, saml2::BearerConfirmationRuleFactory);
+    conf.SecurityPolicyRuleManager.registerFactory(DELEGATION_POLICY_RULE, saml2::DelegationRestrictionRuleFactory);
 }
 
 SecurityPolicy::IssuerMatchingPolicy SecurityPolicy::m_defaultMatching;
index 416a5b8..9f417d8 100644 (file)
@@ -63,12 +63,12 @@ namespace opensaml {
     static const XMLCh type[] =     UNICODE_LITERAL_4(t,y,p,e);
 
     const char config[] =
-        "<PolicyRule type=\"Conditions\" xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\">"\r
-            "<PolicyRule type=\"Audience\"/>"\r
-            "<PolicyRule type=\"Ignore\">saml:DoNotCacheCondition</PolicyRule>"\r
-            "<PolicyRule type=\"Ignore\">saml2:OneTimeUse</PolicyRule>"\r
-            "<PolicyRule type=\"Ignore\">saml2:ProxyRestriction</PolicyRule>"\r
-        "</PolicyRule>";\r
+        "<PolicyRule type=\"Conditions\" xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\">"
+            "<PolicyRule type=\"Audience\"/>"
+            "<PolicyRule type=\"Ignore\">saml:DoNotCacheCondition</PolicyRule>"
+            "<PolicyRule type=\"Ignore\">saml2:OneTimeUse</PolicyRule>"
+            "<PolicyRule type=\"Ignore\">saml2:ProxyRestriction</PolicyRule>"
+        "</PolicyRule>";
 };
 
 ConditionsRule::ConditionsRule(const DOMElement* e) : m_doc(NULL)
@@ -121,47 +121,43 @@ bool ConditionsRule::evaluate(const XMLObject& message, const GenericRequest* re
         bool valid;
 
         const vector<saml2::AudienceRestriction*>& acvec = conds->getAudienceRestrictions();
-        for (vector<saml2::AudienceRestriction*>::const_iterator ac = acvec.begin(); ac!=acvec.end(); ++ac) {
+        for (vector<saml2::AudienceRestriction*>::const_iterator ac = acvec.begin(); ac != acvec.end(); ++ac) {
             valid = false;
-            for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); r != m_rules.end(); ++r) {
-                if ((*r)->evaluate(*(*ac), request, policy))
-                    valid = true;
-            }
+            for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
+                valid = (*r)->evaluate(*(*ac), request, policy);
             if (!valid)
-                throw SecurityPolicyException("AudienceRestriction was not understood by policy.");
+                throw SecurityPolicyException("AudienceRestriction condition not successfully validated by policy.");
         }
 
         const vector<saml2::OneTimeUse*>& otvec = conds->getOneTimeUses();
         for (vector<saml2::OneTimeUse*>::const_iterator ot = otvec.begin(); ot!=otvec.end(); ++ot) {
             valid = false;
-            for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); r != m_rules.end(); ++r) {
-                if ((*r)->evaluate(*(*ot), request, policy))
-                    valid = true;
-            }
+            for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
+                valid = (*r)->evaluate(*(*ot), request, policy);
             if (!valid)
-                throw SecurityPolicyException("OneTimeUse was not understood by policy.");
+                throw SecurityPolicyException("OneTimeUse condition not successfully validated by policy.");
         }
 
         const vector<saml2::ProxyRestriction*> pvec = conds->getProxyRestrictions();
-        for (vector<saml2::ProxyRestriction*>::const_iterator p = pvec.begin(); p!=pvec.end(); ++p) {
+        for (vector<saml2::ProxyRestriction*>::const_iterator p = pvec.begin(); p != pvec.end(); ++p) {
             valid = false;
-            for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); r != m_rules.end(); ++r) {
-                if ((*r)->evaluate(*(*p), request, policy))
-                    valid = true;
-            }
+            for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
+                valid = (*r)->evaluate(*(*p), request, policy);
             if (!valid)
-                throw SecurityPolicyException("ProxyRestriction was not understood by policy.");
+                throw SecurityPolicyException("ProxyRestriction condition not successfully validated by policy.");
         }
 
         const vector<saml2::Condition*>& convec = conds->getConditions();
-        for (vector<saml2::Condition*>::const_iterator c = convec.begin(); c!=convec.end(); ++c) {
+        for (vector<saml2::Condition*>::const_iterator c = convec.begin(); c != convec.end(); ++c) {
             valid = false;
-            for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); r != m_rules.end(); ++r) {
-                if ((*r)->evaluate(*(*c), request, policy))
-                    valid = true;
+            for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
+                valid = (*r)->evaluate(*(*c), request, policy);
+            if (!valid) {
+                throw SecurityPolicyException(
+                    "Extension condition ($1) not successfully validated by policy.",
+                    params(1,((*c)->getSchemaType() ? (*c)->getSchemaType()->toString().c_str() : "Unknown Type"))
+                    );
             }
-            if (!valid)
-                throw SecurityPolicyException("Condition ($1) was not understood by policy.", params(1,(*c)->getElementQName().toString().c_str()));
         }
 
         return true;
@@ -188,36 +184,34 @@ bool ConditionsRule::evaluate(const XMLObject& message, const GenericRequest* re
         bool valid;
 
         const vector<saml1::AudienceRestrictionCondition*>& acvec = conds->getAudienceRestrictionConditions();
-        for (vector<saml1::AudienceRestrictionCondition*>::const_iterator ac = acvec.begin(); ac!=acvec.end(); ++ac) {
+        for (vector<saml1::AudienceRestrictionCondition*>::const_iterator ac = acvec.begin(); ac != acvec.end(); ++ac) {
             valid = false;
-            for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); r != m_rules.end(); ++r) {
-                if ((*r)->evaluate(*(*ac), request, policy))
-                    valid = true;
-            }
+            for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
+                valid = (*r)->evaluate(*(*ac), request, policy);
             if (!valid)
-                throw SecurityPolicyException("AudienceRestrictionCondition was not understood by policy.");
+                throw SecurityPolicyException("AudienceRestrictionCondition not successfully validated by policy.");
         }
 
         const vector<saml1::DoNotCacheCondition*>& dncvec = conds->getDoNotCacheConditions();
-        for (vector<saml1::DoNotCacheCondition*>::const_iterator dnc = dncvec.begin(); dnc!=dncvec.end(); ++dnc) {
+        for (vector<saml1::DoNotCacheCondition*>::const_iterator dnc = dncvec.begin(); dnc != dncvec.end(); ++dnc) {
             valid = false;
-            for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); r != m_rules.end(); ++r) {
-                if ((*r)->evaluate(*(*dnc), request, policy))
-                    valid = true;
-            }
+            for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
+                valid = (*r)->evaluate(*(*dnc), request, policy);
             if (!valid)
-                throw SecurityPolicyException("DoNotCacheCondition was not understood by policy.");
+                throw SecurityPolicyException("DoNotCacheCondition not successfully validated by policy.");
         }
 
         const vector<saml1::Condition*>& convec = conds->getConditions();
-        for (vector<saml1::Condition*>::const_iterator c = convec.begin(); c!=convec.end(); ++c) {
+        for (vector<saml1::Condition*>::const_iterator c = convec.begin(); c != convec.end(); ++c) {
             valid = false;
-            for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); r != m_rules.end(); ++r) {
-                if ((*r)->evaluate(*(*c), request, policy))
-                    valid = true;
+            for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
+                valid = (*r)->evaluate(*(*c), request, policy);
+            if (!valid) {
+                throw SecurityPolicyException(
+                    "Extension condition ($1) not successfully validated by policy.",
+                    params(1,((*c)->getSchemaType() ? (*c)->getSchemaType()->toString().c_str() : (*c)->getElementQName().toString().c_str()))
+                    );
             }
-            if (!valid)
-                throw SecurityPolicyException("Condition ($1) was not understood by policy.", params(1,(*c)->getElementQName().toString().c_str()));
         }
 
         return true;
index 58783f5..08f1dc3 100644 (file)
                                                        >\r
                                                </File>\r
                                                <File\r
+                                                       RelativePath=".\saml2\profile\impl\DelegationRestrictionRule.cpp"\r
+                                                       >\r
+                                               </File>\r
+                                               <File\r
                                                        RelativePath=".\saml2\profile\impl\SAML2AssertionPolicy.cpp"\r
                                                        >\r
                                                </File>\r
index d5a9b5e..c9deb78 100644 (file)
@@ -1732,7 +1732,7 @@ const XMLCh Delegate::TYPE_NAME[] =                 UNICODE_LITERAL_12(D,e,l,e,g
 const XMLCh Delegate::CONFIRMATIONMETHOD_ATTRIB_NAME[] = UNICODE_LITERAL_18(C,o,n,f,i,r,m,a,t,i,o,n,M,e,t,h,o,d);
 const XMLCh Delegate::DELEGATIONINSTANT_ATTRIB_NAME[] = UNICODE_LITERAL_17(D,e,l,e,g,a,t,i,o,n,I,n,s,t,a,n,t);
 const XMLCh DelegationRestrictionType::LOCAL_NAME[] = UNICODE_LITERAL_9(C,o,n,d,i,t,i,o,n);
-const XMLCh DelegationRestrictionType::TYPE_NAME[] =UNICODE_LITERAL_21(D,e,l,e,g,a,t,i,o,n,R,e,s,t,r,i,c,t,i,o,n);
+const XMLCh DelegationRestrictionType::TYPE_NAME[] =UNICODE_LITERAL_25(D,e,l,e,g,a,t,i,o,n,R,e,s,t,r,i,c,t,i,o,n,T,y,p,e);
 const XMLCh EncryptedAssertion::LOCAL_NAME[] =      UNICODE_LITERAL_18(E,n,c,r,y,p,t,e,d,A,s,s,e,r,t,i,o,n);
 const XMLCh EncryptedAttribute::LOCAL_NAME[] =      UNICODE_LITERAL_18(E,n,c,r,y,p,t,e,d,A,t,t,r,i,b,u,t,e);
 const XMLCh EncryptedElementType::LOCAL_NAME[] =    {chNull};
index 70b02e5..3b60320 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.
diff --git a/saml/saml2/profile/impl/DelegationRestrictionRule.cpp b/saml/saml2/profile/impl/DelegationRestrictionRule.cpp
new file mode 100644 (file)
index 0000000..5bc3bea
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ *  Copyright 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
+ *
+ *     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.
+ */
+
+/**
+ * DelegationRestrictionRule.cpp
+ *
+ * SAML DelegationRestriction SecurityPolicyRule
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "binding/SecurityPolicyRule.h"
+#include "saml2/core/Assertions.h"
+#include "util/SAMLConstants.h"
+
+#include <xmltooling/logging.h>
+
+using namespace opensaml::saml2;
+using namespace opensaml;
+using namespace xmltooling::logging;
+using namespace xmltooling;
+using namespace std;
+
+namespace opensaml {
+    namespace saml2 {
+        class SAML_DLLLOCAL DelegationRestrictionRule : public SecurityPolicyRule
+        {
+        public:
+            DelegationRestrictionRule(const DOMElement* e);
+
+            virtual ~DelegationRestrictionRule() {
+                for_each(m_delegates.begin(), m_delegates.end(), xmltooling::cleanup<Delegate>());
+            }
+            const char* getType() const {
+                return DELEGATION_POLICY_RULE;
+            }
+            bool evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const;
+
+        private:
+            vector<Delegate*> m_delegates;
+            enum {
+                MATCH_ANY,
+                MATCH_NEWEST,
+                MATCH_OLDEST
+            } m_match;
+        };
+
+        SecurityPolicyRule* SAML_DLLLOCAL DelegationRestrictionRuleFactory(const DOMElement* const & e)
+        {
+            return new DelegationRestrictionRule(e);
+        }
+
+        class SAML_DLLLOCAL _isSameDelegate : public binary_function<const Delegate*,const Delegate*,bool>,
+            public unary_function<const Delegate*,bool>
+        {
+            const Delegate* m_operand;
+            bool isSameFormat(const XMLCh* f1, const XMLCh* f2) const {
+                if (!f1 || !*f1)
+                    f1 = NameIDType::UNSPECIFIED;
+                if (!f2 || !*f2)
+                    f2 = NameIDType::UNSPECIFIED;
+                return XMLString::equals(f1, f2);
+            }
+            bool matches(const NameID* n1, const NameID* n2) const {
+                return (isSameFormat(n1->getFormat(), n2->getFormat()) &&
+                        XMLString::equals(n1->getName(), n2->getName()) &&
+                        XMLString::equals(n1->getNameQualifier(), n2->getNameQualifier()) &&
+                        XMLString::equals(n1->getSPNameQualifier(), n2->getSPNameQualifier()));
+            }
+        public:
+            _isSameDelegate() : m_operand(NULL) {}
+            _isSameDelegate(const Delegate* d) : m_operand(d) {}
+
+            // d1 is the input from the message, d2 is from the policy
+            bool operator()(const Delegate* d1, const Delegate* d2) const {
+                if (!d1->getNameID()) {
+                    Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.DelegationRestriction").error(
+                        "rule doesn't support evaluation of BaseID or EncryptedID in a Delegate"
+                        );
+                    return false;
+                }
+                if (!d2->getConfirmationMethod() || XMLString::equals(d1->getConfirmationMethod(), d2->getConfirmationMethod())) {
+                    return matches(d1->getNameID(), d2->getNameID());
+                }
+                return false;
+            }
+
+            // d is from the policy
+            bool operator()(const Delegate* d) const {
+                return this->operator()(m_operand, d);
+            }
+        };
+
+        static XMLCh match[] =  UNICODE_LITERAL_5(m,a,t,c,h);
+        static XMLCh any[] =    UNICODE_LITERAL_8(a,n,y,O,r,d,e,r);
+        static XMLCh newest[] = UNICODE_LITERAL_6(n,e,w,e,s,t);
+        static XMLCh oldest[] = UNICODE_LITERAL_6(o,l,d,e,s,t);
+
+    }
+};
+
+DelegationRestrictionRule::DelegationRestrictionRule(const DOMElement* e) : m_match(MATCH_ANY)
+{
+    if (e) {
+        const XMLCh* m = e->getAttributeNS(NULL, match);
+        if (XMLString::equals(m, newest))
+            m_match = MATCH_NEWEST;
+        else if (XMLString::equals(m, oldest))
+            m_match = MATCH_OLDEST;
+        else if (m && *m && !XMLString::equals(m, any))
+            throw SecurityPolicyException("Invalid value for \"match\" attribute in Delegation rule.");
+
+        try {
+            DOMElement* d = XMLHelper::getFirstChildElement(e, samlconstants::SAML20_DELEGATION_CONDITION_NS, Delegate::LOCAL_NAME);
+            while (d) {
+                auto_ptr<XMLObject> wrapper(XMLObjectBuilder::buildOneFromElement(d));
+                Delegate* down = dynamic_cast<Delegate*>(wrapper.get());
+                if (down) {
+                    m_delegates.push_back(down);
+                    wrapper.release();
+                }
+                d = XMLHelper::getNextSiblingElement(d, samlconstants::SAML20_DELEGATION_CONDITION_NS, Delegate::LOCAL_NAME);
+            }
+        }
+        catch (exception&) {
+            for_each(m_delegates.begin(), m_delegates.end(), xmltooling::cleanup<Delegate>());
+            throw;
+        }
+    }
+}
+
+bool DelegationRestrictionRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const
+{
+    const DelegationRestrictionType* drt=dynamic_cast<const DelegationRestrictionType*>(&message);
+    if (!drt)
+        return false;
+
+    // If we have no embedded Delegates, the condition evaluates to true.
+    if (m_delegates.empty())
+        return true;
+
+    const vector<Delegate*>& dels = drt->getDelegates();
+    if (m_match == MATCH_ANY) {
+        // Each Delegate in the condition MUST match an embedded Delegate.
+        for (vector<Delegate*>::const_iterator d1 = dels.begin(); d1 != dels.end(); ++d1) {
+            if (find_if(m_delegates.begin(), m_delegates.end(), _isSameDelegate(*d1)) == m_delegates.end())
+                return false;
+        }
+    }
+    else if (m_match == MATCH_OLDEST) {
+        return (search(dels.begin(), dels.end(), m_delegates.begin(), m_delegates.end(), _isSameDelegate()) == dels.begin());
+    }
+    else if (m_match == MATCH_NEWEST) {
+        return (search(dels.rbegin(), dels.rend(), m_delegates.begin(), m_delegates.end(), _isSameDelegate()) == dels.rbegin());
+    }
+
+    return true;
+}
index aa8fe70..2f3b332 100644 (file)
@@ -207,7 +207,7 @@ const XMLCh samlconstants::SAML20MD_ENTITY_ATTRIBUTE_PREFIX[] = UNICODE_LITERAL_
 const XMLCh samlconstants::SAML20_DELEGATION_CONDITION_NS[] = // urn:oasis:names:tc:SAML:2.0:conditions:delegation
 { chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_o, chLatin_a, chLatin_s, chLatin_i, chLatin_s, chColon,
   chLatin_n, chLatin_a, chLatin_m, chLatin_e, chLatin_s, chColon, chLatin_t, chLatin_c, chColon,
-  chLatin_S, chLatin_A, chLatin_M, chLatin_L, chColon, chDigit_2, chDigit_0, chColon,
+  chLatin_S, chLatin_A, chLatin_M, chLatin_L, chColon, chDigit_2, chPeriod, chDigit_0, chColon,
   chLatin_c, chLatin_o, chLatin_n, chLatin_d, chLatin_i, chLatin_t, chLatin_i, chLatin_o, chLatin_n, chLatin_s, chColon,
   chLatin_d, chLatin_e, chLatin_l, chLatin_e, chLatin_g, chLatin_a, chLatin_t, chLatin_i, chLatin_o, chLatin_n, chNull
 };