From 7d1737c8b5c58b1f052d1d6b6d4f509a68dc84cc Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Wed, 22 Feb 2012 23:04:45 +0000 Subject: [PATCH] https://issues.shibboleth.net/jira/browse/SSPCPP-342 --- .../metadata/impl/WhitelistMetadataFilter.cpp | 184 +++++++++++++++------ 1 file changed, 137 insertions(+), 47 deletions(-) diff --git a/saml/saml2/metadata/impl/WhitelistMetadataFilter.cpp b/saml/saml2/metadata/impl/WhitelistMetadataFilter.cpp index f671edc..5c38639 100644 --- a/saml/saml2/metadata/impl/WhitelistMetadataFilter.cpp +++ b/saml/saml2/metadata/impl/WhitelistMetadataFilter.cpp @@ -28,20 +28,23 @@ #include "saml2/metadata/Metadata.h" #include "saml2/metadata/MetadataFilter.h" -#include +#include +#include +#include +#include #include #include -#include +using namespace opensaml::saml2; using namespace opensaml::saml2md; using namespace xmltooling::logging; using namespace xmltooling; +using namespace boost::lambda; using namespace boost; using namespace std; namespace opensaml { namespace saml2md { - class SAML_DLLLOCAL WhitelistMetadataFilter : public MetadataFilter { public: @@ -52,15 +55,13 @@ namespace opensaml { void doFilter(XMLObject& xmlObject) const; private: - void doFilter(EntitiesDescriptor& entities) const; - - bool found(const XMLCh* id) const { - if (!id) - return false; - return m_set.count(id)==1; - } + void filterGroup(EntitiesDescriptor&) const; + bool included(const EntityDescriptor&) const; + bool matches(const EntityAttributes*, const Attribute*) const; - set m_set; + set m_entities; + bool m_trimTags; + vector< boost::shared_ptr > m_tags; }; MetadataFilter* SAML_DLLLOCAL WhitelistMetadataFilterFactory(const DOMElement* const & e) @@ -68,57 +69,55 @@ namespace opensaml { return new WhitelistMetadataFilter(e); } + static const XMLCh Include[] = UNICODE_LITERAL_7(I,n,c,l,u,d,e); + static const XMLCh trimTags[] = UNICODE_LITERAL_8(t,r,i,m,T,a,g,s); }; }; -static const XMLCh Include[] = UNICODE_LITERAL_7(I,n,c,l,u,d,e); WhitelistMetadataFilter::WhitelistMetadataFilter(const DOMElement* e) + : m_trimTags(XMLHelper::getAttrBool(e, false, trimTags)) { - e = XMLHelper::getFirstChildElement(e); - while (e) { - if (XMLString::equals(e->getLocalName(), Include) && e->hasChildNodes()) { - m_set.insert(e->getFirstChild()->getTextContent()); + DOMElement* child = XMLHelper::getFirstChildElement(e); + while (child) { + if (XMLString::equals(child->getLocalName(), Include) && child->hasChildNodes()) { + m_entities.insert(child->getFirstChild()->getTextContent()); + } + else if (XMLHelper::isNodeNamed(child, samlconstants::SAML20_NS, Attribute::LOCAL_NAME)) { + boost::shared_ptr obj(AttributeBuilder::buildOneFromElement(child)); + m_tags.push_back(boost::shared_dynamic_cast(obj)); } - e = XMLHelper::getNextSiblingElement(e); + child = XMLHelper::getNextSiblingElement(child); } } void WhitelistMetadataFilter::doFilter(XMLObject& xmlObject) const { -#ifdef _DEBUG - NDC ndc("doFilter"); -#endif - - try { - doFilter(dynamic_cast(xmlObject)); - return; - } - catch (bad_cast&) { - } - - try { - EntityDescriptor& entity = dynamic_cast(xmlObject); - if (!found(entity.getEntityID())) - throw MetadataFilterException(WHITELIST_METADATA_FILTER" MetadataFilter instructed to filter the root/only entity in the metadata."); - return; + EntitiesDescriptor* group = dynamic_cast(&xmlObject); + if (group) { + filterGroup(*group); } - catch (bad_cast&) { + else { + EntityDescriptor* entity = dynamic_cast(&xmlObject); + if (entity) { + if (!included(*entity)) + throw MetadataFilterException(WHITELIST_METADATA_FILTER" MetadataFilter instructed to filter the root/only entity in the metadata."); + } + else { + throw MetadataFilterException(WHITELIST_METADATA_FILTER" MetadataFilter was given an improper metadata instance to filter."); + } } - - throw MetadataFilterException(WHITELIST_METADATA_FILTER" MetadataFilter was given an improper metadata instance to filter."); } -void WhitelistMetadataFilter::doFilter(EntitiesDescriptor& entities) const +void WhitelistMetadataFilter::filterGroup(EntitiesDescriptor& entities) const { Category& log=Category::getInstance(SAML_LOGCAT".MetadataFilter."WHITELIST_METADATA_FILTER); - VectorOf(EntityDescriptor) v=entities.getEntityDescriptors(); - for (VectorOf(EntityDescriptor)::size_type i=0; igetEntityID(); - if (!found(id)) { - auto_ptr_char id2(id); - log.info("filtering out non-whitelisted entity (%s)", id2.get()); + VectorOf(EntityDescriptor) v = entities.getEntityDescriptors(); + for (VectorOf(EntityDescriptor)::size_type i = 0; i < v.size(); ) { + if (!included(*v[i])) { + auto_ptr_char id(v[i]->getEntityID()); + log.info("filtering out non-whitelisted entity (%s)", id.get()); v.erase(v.begin() + i); } else { @@ -126,11 +125,102 @@ void WhitelistMetadataFilter::doFilter(EntitiesDescriptor& entities) const } } - const vector& groups=const_cast(entities).getEntitiesDescriptors(); + const vector& groups = const_cast(entities).getEntitiesDescriptors(); for_each( make_indirect_iterator(groups.begin()), make_indirect_iterator(groups.end()), - boost::bind( - static_cast(&WhitelistMetadataFilter::doFilter), this, _1 - ) + lambda::bind(&WhitelistMetadataFilter::filterGroup, this, _1) ); } + +bool WhitelistMetadataFilter::included(const EntityDescriptor& entity) const +{ + // Check for entityID. + if (entity.getEntityID() && !m_entities.empty() && m_entities.count(entity.getEntityID()) == 1) + return true; + + // Check for a tag match in the EntityAttributes extension of the entity and its parent(s). + if (!m_tags.empty()) { + const Extensions* exts = entity.getExtensions(); + if (exts) { + const vector& children = exts->getUnknownXMLObjects(); + const XMLObject* xo = find_if(children, ll_dynamic_cast(_1) != ((EntityAttributes*)nullptr)); + if (xo) { + // If we find a matching tag, we win. Each tag is treated in OR fashion. + if (find_if(m_tags.begin(), m_tags.end(), + lambda::bind(&WhitelistMetadataFilter::matches, this, dynamic_cast(xo), + lambda::bind(&boost::shared_ptr::get, _1))) != m_tags.end()) { + return true; + } + } + } + + const EntitiesDescriptor* group = dynamic_cast(entity.getParent()); + while (group) { + exts = group->getExtensions(); + if (exts) { + const vector& children = exts->getUnknownXMLObjects(); + const XMLObject* xo = find_if(children, ll_dynamic_cast(_1) != ((EntityAttributes*)nullptr)); + if (xo) { + // If we find a matching tag, we win. Each tag is treated in OR fashion. + if (find_if(m_tags.begin(), m_tags.end(), + lambda::bind(&WhitelistMetadataFilter::matches, this, dynamic_cast(xo), + lambda::bind(&boost::shared_ptr::get, _1))) != m_tags.end()) { + return true; + } + } + } + group = dynamic_cast(group->getParent()); + } + } + return false; +} + +bool WhitelistMetadataFilter::matches(const EntityAttributes* ea, const Attribute* tag) const +{ + const vector& attrs = ea->getAttributes(); + const vector& tagvals = tag->getAttributeValues(); + if (!attrs.empty() && !tagvals.empty()) { + // Track whether we've found every tag value. + vector flags(tagvals.size()); + + // Check each attribute/tag in the candidate. + for (indirect_iterator::const_iterator> a = make_indirect_iterator(attrs.begin()); + a != make_indirect_iterator(attrs.end()); ++a) { + // Compare Name and NameFormat for a matching tag. + if (XMLString::equals(a->getName(), tag->getName()) && + (!tag->getNameFormat() || XMLString::equals(tag->getNameFormat(), Attribute::UNSPECIFIED) || + XMLString::equals(tag->getNameFormat(), a->getNameFormat()))) { + // Check each tag value's simple content for a match. + for (vector::size_type tagindex = 0; tagindex < tagvals.size(); ++tagindex) { + const XMLObject* tagval = tagvals[tagindex]; + const XMLCh* tagvalstr = (tagval->getDOM()) ? tagval->getDOM()->getTextContent() : tagval->getTextContent(); + const vector& cvals = const_cast(*a).getAttributeValues(); + for (indirect_iterator::const_iterator> cval = make_indirect_iterator(cvals.begin()); + cval != make_indirect_iterator(cvals.end()); ++cval) { + const XMLCh* cvalstr = cval->getDOM() ? cval->getDOM()->getTextContent() : cval->getTextContent(); + if (tagvalstr && cvalstr) { + if (XMLString::equals(tagvalstr, cvalstr)) { + flags[tagindex] = true; + break; + } + else if (m_trimTags) { + XMLCh* dup = XMLString::replicate(cvalstr); + XMLString::trim(dup); + if (XMLString::equals(tagvalstr, dup)) { + XMLString::release(&dup); + flags[tagindex] = true; + break; + } + XMLString::release(&dup); + } + } + } + } + } + } + + if (find(flags.begin(), flags.end(), false) == flags.end()) + return true; + } + return false; +} -- 2.1.4