From: Scott Cantor Date: Sun, 13 Aug 2006 23:02:54 +0000 (+0000) Subject: Add signature-checking metadata filter. X-Git-Tag: 2.0-alpha1~204 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fcpp-opensaml.git;a=commitdiff_plain;h=a1fc8349ebc40c9dae9f50ca264efad71071bda8 Add signature-checking metadata filter. --- diff --git a/saml/Makefile.am b/saml/Makefile.am index 36e8611..869eb35 100644 --- a/saml/Makefile.am +++ b/saml/Makefile.am @@ -77,6 +77,7 @@ libsaml_la_SOURCES = \ saml2/metadata/impl/MetadataProvider.cpp \ saml2/metadata/impl/MetadataSchemaValidators.cpp \ saml2/metadata/impl/ObservableMetadataProvider.cpp \ + saml2/metadata/impl/SignatureMetadataFilter.cpp \ saml2/metadata/impl/WhitelistMetadataFilter.cpp \ signature/ContentReference.cpp \ signature/SignatureProfileValidator.cpp \ diff --git a/saml/saml.vcproj b/saml/saml.vcproj index ce4d609..73c7317 100644 --- a/saml/saml.vcproj +++ b/saml/saml.vcproj @@ -319,6 +319,10 @@ > + + diff --git a/saml/saml2/metadata/MetadataFilter.h b/saml/saml2/metadata/MetadataFilter.h index a6dc24f..d383c56 100644 --- a/saml/saml2/metadata/MetadataFilter.h +++ b/saml/saml2/metadata/MetadataFilter.h @@ -70,6 +70,9 @@ namespace opensaml { /** MetadataFilter that deletes all but whitelisted entities. */ #define WHITELIST_METADATA_FILTER "org.opensaml.saml2.metadata.provider.WhitelistMetadataFilter" + + /** MetadataFilter that verifies signatures and filters out any that don't pass. */ + #define SIGNATURE_METADATA_FILTER "org.opensaml.saml2.metadata.provider.SignatureMetadataFilter" DECL_XMLTOOLING_EXCEPTION(MetadataFilterException,SAML_EXCEPTIONAPI(SAML_API),opensaml::saml2md,xmltooling::XMLToolingException,Exceptions related to metadata filtering); }; diff --git a/saml/saml2/metadata/MetadataProvider.h b/saml/saml2/metadata/MetadataProvider.h index f773e41..583c1b2 100644 --- a/saml/saml2/metadata/MetadataProvider.h +++ b/saml/saml2/metadata/MetadataProvider.h @@ -57,10 +57,11 @@ namespace opensaml { * * * diff --git a/saml/saml2/metadata/impl/MetadataProvider.cpp b/saml/saml2/metadata/impl/MetadataProvider.cpp index 3e88ff3..151d6a8 100644 --- a/saml/saml2/metadata/impl/MetadataProvider.cpp +++ b/saml/saml2/metadata/impl/MetadataProvider.cpp @@ -38,6 +38,7 @@ namespace opensaml { SAML_DLLLOCAL PluginManager::Factory FilesystemMetadataProviderFactory; SAML_DLLLOCAL PluginManager::Factory BlacklistMetadataFilterFactory; SAML_DLLLOCAL PluginManager::Factory WhitelistMetadataFilterFactory; + SAML_DLLLOCAL PluginManager::Factory SignatureMetadataFilterFactory; }; }; @@ -53,10 +54,12 @@ void SAML_API opensaml::saml2md::registerMetadataFilters() { SAMLConfig::getConfig().MetadataFilterManager.registerFactory(BLACKLIST_METADATA_FILTER, BlacklistMetadataFilterFactory); SAMLConfig::getConfig().MetadataFilterManager.registerFactory(WHITELIST_METADATA_FILTER, WhitelistMetadataFilterFactory); + SAMLConfig::getConfig().MetadataFilterManager.registerFactory(SIGNATURE_METADATA_FILTER, SignatureMetadataFilterFactory); } static const XMLCh Blacklist[] = UNICODE_LITERAL_23(B,l,a,c,k,l,i,s,t,M,e,t,a,d,a,t,a,F,i,l,t,e,r); static const XMLCh Whitelist[] = UNICODE_LITERAL_23(W,h,i,t,e,l,i,s,t,M,e,t,a,d,a,t,a,F,i,l,t,e,r); +static const XMLCh SigFilter[] = UNICODE_LITERAL_23(S,i,g,n,a,t,u,r,e,M,e,t,a,d,a,t,a,F,i,l,t,e,r); static const XMLCh Exclude[] = UNICODE_LITERAL_7(E,x,c,l,u,d,e); static const XMLCh Include[] = UNICODE_LITERAL_7(I,n,c,l,u,d,e); static const XMLCh GenericKeyResolver[] = UNICODE_LITERAL_11(K,e,y,R,e,s,o,l,v,e,r); @@ -78,11 +81,18 @@ MetadataProvider::MetadataProvider(const DOMElement* e) : m_resolver(NULL) auto_ptr_char t(child->getAttributeNS(NULL,type)); if (t.get()) m_resolver = XMLToolingConfig::getConfig().KeyResolverManager.newPlugin(t.get(),child); + else + throw UnknownExtensionException(" element found with no type attribute"); } else if (XMLString::equals(child->getLocalName(),GenericMetadataFilter)) { auto_ptr_char t(child->getAttributeNS(NULL,type)); if (t.get()) m_filters.push_back(conf.MetadataFilterManager.newPlugin(t.get(),child)); + else + throw UnknownExtensionException(" element found with no type attribute"); + } + else if (XMLString::equals(child->getLocalName(),SigFilter)) { + m_filters.push_back(conf.MetadataFilterManager.newPlugin(SIGNATURE_METADATA_FILTER,child)); } else if (XMLString::equals(child->getLocalName(),Whitelist)) { m_filters.push_back(conf.MetadataFilterManager.newPlugin(WHITELIST_METADATA_FILTER,child)); diff --git a/saml/saml2/metadata/impl/SignatureMetadataFilter.cpp b/saml/saml2/metadata/impl/SignatureMetadataFilter.cpp new file mode 100644 index 0000000..64c43f8 --- /dev/null +++ b/saml/saml2/metadata/impl/SignatureMetadataFilter.cpp @@ -0,0 +1,150 @@ +/* + * Copyright 2001-2006 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. + */ + +/** + * BlacklistMetadataFilter.cpp + * + * Removes blacklisted entities from a metadata instance + */ + +#include "internal.h" +#include "saml2/metadata/MetadataFilter.h" +#include "signature/SignatureProfileValidator.h" + +#include + +#include +#include + +using namespace opensaml::saml2md; +using namespace opensaml; +using namespace xmlsignature; +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +namespace opensaml { + namespace saml2md { + + class SAML_DLLLOCAL SignatureMetadataFilter : public MetadataFilter + { + public: + SignatureMetadataFilter(const DOMElement* e); + ~SignatureMetadataFilter() { + delete m_sigValidator; + } + + const char* getId() const { return SIGNATURE_METADATA_FILTER; } + void doFilter(XMLObject& xmlObject) const; + + private: + void doFilter(EntitiesDescriptor& entities, bool rootObject=false) const; + void verifySignature(Signature* sig) const { + if (sig) { + m_profileValidator.validate(sig); + m_sigValidator->validate(sig); + } + } + + SignatureProfileValidator m_profileValidator; + SignatureValidator* m_sigValidator; + }; + + MetadataFilter* SAML_DLLLOCAL SignatureMetadataFilterFactory(const DOMElement* const & e) + { + return new SignatureMetadataFilter(e); + } + + }; +}; + +static const XMLCh GenericKeyResolver[] = UNICODE_LITERAL_11(K,e,y,R,e,s,o,l,v,e,r); +static const XMLCh type[] = UNICODE_LITERAL_4(t,y,p,e); + +SignatureMetadataFilter::SignatureMetadataFilter(const DOMElement* e) : m_sigValidator(NULL) +{ + e = XMLHelper::getFirstChildElement(e, GenericKeyResolver); + auto_ptr_char t(e ? e->getAttributeNS(NULL,type) : NULL); + if (t.get()) { + auto_ptr kr(XMLToolingConfig::getConfig().KeyResolverManager.newPlugin(t.get(),e)); + m_sigValidator = new SignatureValidator(kr.get()); + kr.release(); + } + else + throw MetadataFilterException("missing element, or no type attribute found"); +} + +void SignatureMetadataFilter::doFilter(XMLObject& xmlObject) const +{ +#ifdef _DEBUG + NDC ndc("doFilter"); +#endif + + try { + EntitiesDescriptor& entities = dynamic_cast(xmlObject); + doFilter(entities, true); + return; + } + catch (bad_cast) { + } + + try { + EntityDescriptor& entity = dynamic_cast(xmlObject); + if (!entity.getSignature()) + throw MetadataFilterException("Root metadata element was unsigned."); + verifySignature(entity.getSignature()); + } + catch (bad_cast) { + } + + throw MetadataFilterException("SignatureMetadataFilter was given an improper metadata instance to filter."); +} + +void SignatureMetadataFilter::doFilter(EntitiesDescriptor& entities, bool rootObject) const +{ + Category& log=Category::getInstance(SAML_LOGCAT".Metadata"); + + Signature* sig = entities.getSignature(); + if (!sig && rootObject) + throw MetadataFilterException("Root metadata element was unsigned."); + verifySignature(sig); + + VectorOf(EntityDescriptor) v=entities.getEntityDescriptors(); + for (VectorOf(EntityDescriptor)::size_type i=0; igetSignature()); + i++; + } + catch (XMLToolingException& e) { + auto_ptr_char id(v[i]->getEntityID()); + log.info("filtering out entity (%s) after failed signature check: ", id.get(), e.what()); + v.erase(v.begin() + i); + } + } + + VectorOf(EntitiesDescriptor) w=entities.getEntitiesDescriptors(); + for (VectorOf(EntitiesDescriptor)::size_type j=0; jgetSignature()); + j++; + } + catch (XMLToolingException& e) { + auto_ptr_char name(w[j]->getName()); + log.info("filtering out group (%s) after failed signature check: ", name.get(), e.what()); + w.erase(w.begin() + j); + } + } +} diff --git a/samltest/data/saml2/metadata/FilesystemMetadataProvider.xml b/samltest/data/saml2/metadata/FilesystemMetadataProvider.xml index 05ef707..403ad46 100644 --- a/samltest/data/saml2/metadata/FilesystemMetadataProvider.xml +++ b/samltest/data/saml2/metadata/FilesystemMetadataProvider.xml @@ -1,2 +1,6 @@ - + + + + + diff --git a/samltest/internal.h b/samltest/internal.h index 7e82e01..4575bc4 100644 --- a/samltest/internal.h +++ b/samltest/internal.h @@ -72,8 +72,16 @@ protected: } } - void assertEquals(const char* failMessage, DOMDocument* expectedDOM, XMLObject* xmlObject) { - DOMElement* generatedDOM = xmlObject->marshall(); + void assertEquals(const char* failMessage, DOMDocument* expectedDOM, XMLObject* xmlObject, bool canMarshall=true) { + DOMElement* generatedDOM = xmlObject->getDOM(); + if (!generatedDOM) { + if (!canMarshall) { + TSM_ASSERT("DOM not available", false); + } + else { + generatedDOM = xmlObject->marshall(); + } + } if (!generatedDOM->isEqualNode(expectedDOM->getDocumentElement())) { string buf; XMLHelper::serialize(generatedDOM, buf); @@ -85,8 +93,8 @@ protected: } } - void assertEquals(DOMDocument* expectedDOM, XMLObject* xmlObject) { - assertEquals("Marshalled DOM was not the same as the expected DOM", expectedDOM, xmlObject); + void assertEquals(DOMDocument* expectedDOM, XMLObject* xmlObject, bool canMarshall=true) { + assertEquals("Marshalled DOM was not the same as the expected DOM", expectedDOM, xmlObject, canMarshall); delete xmlObject; } diff --git a/samltest/saml2/metadata/FilesystemMetadataProviderTest.h b/samltest/saml2/metadata/FilesystemMetadataProviderTest.h index a9c1f83..73b18b0 100644 --- a/samltest/saml2/metadata/FilesystemMetadataProviderTest.h +++ b/samltest/saml2/metadata/FilesystemMetadataProviderTest.h @@ -58,9 +58,14 @@ public: auto_ptr metadataProvider( SAMLConfig::getConfig().MetadataProviderManager.newPlugin(FILESYSTEM_METADATA_PROVIDER,doc->getDocumentElement()) ); - metadataProvider->init(); + try { + metadataProvider->init(); + } + catch (XMLToolingException& ex) { + TS_TRACE(ex.what()); + throw; + } - Locker locker(metadataProvider.get()); const EntityDescriptor* descriptor = metadataProvider->getEntityDescriptor(entityID); TSM_ASSERT("Retrieved entity descriptor was null", descriptor!=NULL); @@ -91,8 +96,13 @@ public: auto_ptr metadataProvider( SAMLConfig::getConfig().MetadataProviderManager.newPlugin(FILESYSTEM_METADATA_PROVIDER,doc->getDocumentElement()) ); - metadataProvider->init(); - + try { + metadataProvider->init(); + } + catch (XMLToolingException& ex) { + TS_TRACE(ex.what()); + throw; + } Locker locker(metadataProvider.get()); const EntityDescriptor* descriptor = metadataProvider->getEntityDescriptor(entityID); @@ -116,8 +126,13 @@ public: auto_ptr metadataProvider( SAMLConfig::getConfig().MetadataProviderManager.newPlugin(FILESYSTEM_METADATA_PROVIDER,doc->getDocumentElement()) ); - metadataProvider->init(); - + try { + metadataProvider->init(); + } + catch (XMLToolingException& ex) { + TS_TRACE(ex.what()); + throw; + } Locker locker(metadataProvider.get()); const EntityDescriptor* descriptor = metadataProvider->getEntityDescriptor(entityID2);