From 7e35c8e03d4e018a5a26cc4c3536ef2166340931 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Sun, 5 Apr 2009 19:16:46 +0000 Subject: [PATCH] Fixes to constants, allow sequences of condition rules, new policy rule for DelegationRestriction. --- saml/Makefile.am | 1 + saml/binding/SecurityPolicyRule.h | 10 +- saml/binding/impl/SecurityPolicy.cpp | 2 + saml/profile/impl/ConditionsRule.cpp | 88 +++++------ saml/saml.vcproj | 4 + saml/saml2/core/impl/Assertions20Impl.cpp | 2 +- .../core/impl/Assertions20SchemaValidators.cpp | 2 +- .../profile/impl/DelegationRestrictionRule.cpp | 171 +++++++++++++++++++++ saml/util/SAMLConstants.cpp | 2 +- 9 files changed, 230 insertions(+), 52 deletions(-) create mode 100644 saml/saml2/profile/impl/DelegationRestrictionRule.cpp diff --git a/saml/Makefile.am b/saml/Makefile.am index 4d859d6..050e4df 100644 --- a/saml/Makefile.am +++ b/saml/Makefile.am @@ -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 \ diff --git a/saml/binding/SecurityPolicyRule.h b/saml/binding/SecurityPolicyRule.h index f4b746d..8653f98 100644 --- a/saml/binding/SecurityPolicyRule.h +++ b/saml/binding/SecurityPolicyRule.h @@ -52,12 +52,13 @@ namespace opensaml { /** * Evaluates the rule against the given request and message. * - *

An exception will be raised if the message is invalid according to + *

An exception will be raised if the message is fatally invalid according to * a policy rule. * *

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. diff --git a/saml/binding/impl/SecurityPolicy.cpp b/saml/binding/impl/SecurityPolicy.cpp index 98875f7..f473153 100644 --- a/saml/binding/impl/SecurityPolicy.cpp +++ b/saml/binding/impl/SecurityPolicy.cpp @@ -47,6 +47,7 @@ namespace opensaml { namespace saml2 { SAML_DLLLOCAL PluginManager::Factory BearerConfirmationRuleFactory; + SAML_DLLLOCAL PluginManager::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; diff --git a/saml/profile/impl/ConditionsRule.cpp b/saml/profile/impl/ConditionsRule.cpp index 416a5b8..9f417d8 100644 --- a/saml/profile/impl/ConditionsRule.cpp +++ b/saml/profile/impl/ConditionsRule.cpp @@ -63,12 +63,12 @@ namespace opensaml { static const XMLCh type[] = UNICODE_LITERAL_4(t,y,p,e); const char config[] = - "" - "" - "saml:DoNotCacheCondition" - "saml2:OneTimeUse" - "saml2:ProxyRestriction" - ""; + "" + "" + "saml:DoNotCacheCondition" + "saml2:OneTimeUse" + "saml2:ProxyRestriction" + ""; }; 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& acvec = conds->getAudienceRestrictions(); - for (vector::const_iterator ac = acvec.begin(); ac!=acvec.end(); ++ac) { + for (vector::const_iterator ac = acvec.begin(); ac != acvec.end(); ++ac) { valid = false; - for (vector::const_iterator r = m_rules.begin(); r != m_rules.end(); ++r) { - if ((*r)->evaluate(*(*ac), request, policy)) - valid = true; - } + for (vector::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& otvec = conds->getOneTimeUses(); for (vector::const_iterator ot = otvec.begin(); ot!=otvec.end(); ++ot) { valid = false; - for (vector::const_iterator r = m_rules.begin(); r != m_rules.end(); ++r) { - if ((*r)->evaluate(*(*ot), request, policy)) - valid = true; - } + for (vector::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 pvec = conds->getProxyRestrictions(); - for (vector::const_iterator p = pvec.begin(); p!=pvec.end(); ++p) { + for (vector::const_iterator p = pvec.begin(); p != pvec.end(); ++p) { valid = false; - for (vector::const_iterator r = m_rules.begin(); r != m_rules.end(); ++r) { - if ((*r)->evaluate(*(*p), request, policy)) - valid = true; - } + for (vector::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& convec = conds->getConditions(); - for (vector::const_iterator c = convec.begin(); c!=convec.end(); ++c) { + for (vector::const_iterator c = convec.begin(); c != convec.end(); ++c) { valid = false; - for (vector::const_iterator r = m_rules.begin(); r != m_rules.end(); ++r) { - if ((*r)->evaluate(*(*c), request, policy)) - valid = true; + for (vector::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& acvec = conds->getAudienceRestrictionConditions(); - for (vector::const_iterator ac = acvec.begin(); ac!=acvec.end(); ++ac) { + for (vector::const_iterator ac = acvec.begin(); ac != acvec.end(); ++ac) { valid = false; - for (vector::const_iterator r = m_rules.begin(); r != m_rules.end(); ++r) { - if ((*r)->evaluate(*(*ac), request, policy)) - valid = true; - } + for (vector::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& dncvec = conds->getDoNotCacheConditions(); - for (vector::const_iterator dnc = dncvec.begin(); dnc!=dncvec.end(); ++dnc) { + for (vector::const_iterator dnc = dncvec.begin(); dnc != dncvec.end(); ++dnc) { valid = false; - for (vector::const_iterator r = m_rules.begin(); r != m_rules.end(); ++r) { - if ((*r)->evaluate(*(*dnc), request, policy)) - valid = true; - } + for (vector::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& convec = conds->getConditions(); - for (vector::const_iterator c = convec.begin(); c!=convec.end(); ++c) { + for (vector::const_iterator c = convec.begin(); c != convec.end(); ++c) { valid = false; - for (vector::const_iterator r = m_rules.begin(); r != m_rules.end(); ++r) { - if ((*r)->evaluate(*(*c), request, policy)) - valid = true; + for (vector::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; diff --git a/saml/saml.vcproj b/saml/saml.vcproj index 58783f5..08f1dc3 100644 --- a/saml/saml.vcproj +++ b/saml/saml.vcproj @@ -712,6 +712,10 @@ > + + diff --git a/saml/saml2/core/impl/Assertions20Impl.cpp b/saml/saml2/core/impl/Assertions20Impl.cpp index d5a9b5e..c9deb78 100644 --- a/saml/saml2/core/impl/Assertions20Impl.cpp +++ b/saml/saml2/core/impl/Assertions20Impl.cpp @@ -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}; diff --git a/saml/saml2/core/impl/Assertions20SchemaValidators.cpp b/saml/saml2/core/impl/Assertions20SchemaValidators.cpp index 70b02e5..3b60320 100644 --- a/saml/saml2/core/impl/Assertions20SchemaValidators.cpp +++ b/saml/saml2/core/impl/Assertions20SchemaValidators.cpp @@ -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 index 0000000..5bc3bea --- /dev/null +++ b/saml/saml2/profile/impl/DelegationRestrictionRule.cpp @@ -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 + +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()); + } + const char* getType() const { + return DELEGATION_POLICY_RULE; + } + bool evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const; + + private: + vector 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, + public unary_function + { + 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 wrapper(XMLObjectBuilder::buildOneFromElement(d)); + Delegate* down = dynamic_cast(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()); + throw; + } + } +} + +bool DelegationRestrictionRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const +{ + const DelegationRestrictionType* drt=dynamic_cast(&message); + if (!drt) + return false; + + // If we have no embedded Delegates, the condition evaluates to true. + if (m_delegates.empty()) + return true; + + const vector& dels = drt->getDelegates(); + if (m_match == MATCH_ANY) { + // Each Delegate in the condition MUST match an embedded Delegate. + for (vector::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; +} diff --git a/saml/util/SAMLConstants.cpp b/saml/util/SAMLConstants.cpp index aa8fe70..2f3b332 100644 --- a/saml/util/SAMLConstants.cpp +++ b/saml/util/SAMLConstants.cpp @@ -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 }; -- 2.1.4