From d25eb3c0ede922ecad141bb44a7893ab7a5fb017 Mon Sep 17 00:00:00 2001 From: cantor Date: Thu, 30 Aug 2007 02:20:57 +0000 Subject: [PATCH] Rework signature validation in metadata filter. Integrate TrustEngine option into the filter for use with dynamic metadata. Migrate PKIX unit test to static engine. git-svn-id: https://svn.middleware.georgetown.edu/cpp-opensaml2/trunk@299 fb386ef7-a10c-0410-8ebf-fd3f8e989ab0 --- .../metadata/impl/BlacklistMetadataFilter.cpp | 2 +- .../metadata/impl/ChainingMetadataProvider.cpp | 2 +- .../metadata/impl/DynamicMetadataProvider.cpp | 66 ++++++------ .../metadata/impl/SignatureMetadataFilter.cpp | 117 ++++++++++++++++----- .../metadata/impl/WhitelistMetadataFilter.cpp | 2 +- saml/saml2/metadata/impl/XMLMetadataProvider.cpp | 2 +- samltest/Makefile.am | 2 +- .../data/security/FilesystemCredentialResolver.xml | 6 -- samltest/data/security/StaticPKIXTrustEngine.xml | 2 + samltest/samltest.vcproj | 16 +-- ...ustEngineTest.h => StaticPKIXTrustEngineTest.h} | 84 +++------------ 11 files changed, 154 insertions(+), 147 deletions(-) delete mode 100644 samltest/data/security/FilesystemCredentialResolver.xml create mode 100644 samltest/data/security/StaticPKIXTrustEngine.xml rename samltest/security/{AbstractPKIXTrustEngineTest.h => StaticPKIXTrustEngineTest.h} (57%) diff --git a/saml/saml2/metadata/impl/BlacklistMetadataFilter.cpp b/saml/saml2/metadata/impl/BlacklistMetadataFilter.cpp index 1a4a834..23ef33b 100644 --- a/saml/saml2/metadata/impl/BlacklistMetadataFilter.cpp +++ b/saml/saml2/metadata/impl/BlacklistMetadataFilter.cpp @@ -121,7 +121,7 @@ void BlacklistMetadataFilter::doFilter(XMLObject& xmlObject) const void BlacklistMetadataFilter::doFilter(EntitiesDescriptor& entities) const { - Category& log=Category::getInstance(SAML_LOGCAT".Metadata"); + Category& log=Category::getInstance(SAML_LOGCAT".MetadataFilter.Blacklist"); VectorOf(EntityDescriptor) v=entities.getEntityDescriptors(); for (VectorOf(EntityDescriptor)::size_type i=0; igetAttributeNS(NULL,type)); diff --git a/saml/saml2/metadata/impl/DynamicMetadataProvider.cpp b/saml/saml2/metadata/impl/DynamicMetadataProvider.cpp index a23308f..7078fac 100644 --- a/saml/saml2/metadata/impl/DynamicMetadataProvider.cpp +++ b/saml/saml2/metadata/impl/DynamicMetadataProvider.cpp @@ -24,10 +24,10 @@ #include "saml2/metadata/Metadata.h" #include "saml2/metadata/DynamicMetadataProvider.h" -#include -#include -#include -#include +#include +#include +#include +#include #include using namespace opensaml::saml2md; @@ -35,7 +35,7 @@ using namespace xmltooling::logging; using namespace xmltooling; using namespace std; -static const XMLCh validate[] = UNICODE_LITERAL_8(v,a,l,i,d,a,t,e); +static const XMLCh validate[] = UNICODE_LITERAL_8(v,a,l,i,d,a,t,e); namespace opensaml { namespace saml2md { @@ -49,8 +49,8 @@ namespace opensaml { DynamicMetadataProvider::DynamicMetadataProvider(const DOMElement* e) : AbstractMetadataProvider(e), m_lock(RWLock::create()) { - const XMLCh* flag=e ? e->getAttributeNS(NULL,validate) : NULL; - m_validate=(XMLString::equals(flag,xmlconstants::XML_TRUE) || XMLString::equals(flag,xmlconstants::XML_ONE)); + const XMLCh* flag=e ? e->getAttributeNS(NULL,validate) : NULL; + m_validate=(XMLString::equals(flag,xmlconstants::XML_TRUE) || XMLString::equals(flag,xmlconstants::XML_ONE)); } DynamicMetadataProvider::~DynamicMetadataProvider() @@ -102,16 +102,16 @@ const EntityDescriptor* DynamicMetadataProvider::getEntityDescriptor(const char* EntityDescriptor* DynamicMetadataProvider::resolve(const char* entityID) const { - try { - DOMDocument* doc=NULL; - auto_ptr_XMLCh widenit(entityID); - URLInputSource src(widenit.get()); - Wrapper4InputSource dsrc(&src,false); - if (m_validate) - doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc); - else - doc=XMLToolingConfig::getConfig().getParser().parse(dsrc); - + try { + DOMDocument* doc=NULL; + auto_ptr_XMLCh widenit(entityID); + URLInputSource src(widenit.get()); + Wrapper4InputSource dsrc(&src,false); + if (m_validate) + doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc); + else + doc=XMLToolingConfig::getConfig().getParser().parse(dsrc); + // Wrap the document for now. XercesJanitor docjanitor(doc); @@ -126,20 +126,20 @@ EntityDescriptor* DynamicMetadataProvider::resolve(const char* entityID) const "Root of metadata instance not recognized: $1", params(1,xmlObject->getElementQName().toString().c_str()) ); } - xmlObject.release(); - return entity; - } - catch (XMLException& e) { - auto_ptr_char msg(e.getMessage()); - Category::getInstance(SAML_LOGCAT".MetadataProvider.Dynamic").error( - "Xerces error while resolving entityID (%s): %s", entityID, msg.get() - ); - throw MetadataException(msg.get()); - } - catch (exception& e) { - Category::getInstance(SAML_LOGCAT".MetadataProvider.Dynamic").error( - "error while resolving entityID (%s): %s", entityID, e.what() - ); - throw; - } + xmlObject.release(); + return entity; + } + catch (XMLException& e) { + auto_ptr_char msg(e.getMessage()); + Category::getInstance(SAML_LOGCAT".MetadataProvider.Dynamic").error( + "Xerces error while resolving entityID (%s): %s", entityID, msg.get() + ); + throw MetadataException(msg.get()); + } + catch (exception& e) { + Category::getInstance(SAML_LOGCAT".MetadataProvider.Dynamic").error( + "error while resolving entityID (%s): %s", entityID, e.what() + ); + throw; + } } diff --git a/saml/saml2/metadata/impl/SignatureMetadataFilter.cpp b/saml/saml2/metadata/impl/SignatureMetadataFilter.cpp index b108799..efc82ed 100644 --- a/saml/saml2/metadata/impl/SignatureMetadataFilter.cpp +++ b/saml/saml2/metadata/impl/SignatureMetadataFilter.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -41,7 +42,22 @@ using namespace std; namespace opensaml { namespace saml2md { - + + class SAML_DLLLOCAL DummyCredentialResolver : public CredentialResolver + { + public: + DummyCredentialResolver() {} + ~DummyCredentialResolver() {} + + Lockable* lock() {return this;} + void unlock() {} + + const Credential* resolve(const CredentialCriteria* criteria=NULL) const {return NULL;} + vector::size_type resolve( + vector& results, const CredentialCriteria* criteria=NULL + ) const {return 0;} + }; + class SAML_DLLLOCAL SignatureMetadataFilter : public MetadataFilter { public: @@ -55,16 +71,11 @@ namespace opensaml { private: void doFilter(EntitiesDescriptor& entities, bool rootObject=false) const; - void verifySignature(Signature* sig) const { - if (sig) { - m_profileValidator.validate(sig); - m_sigValidator.validate(sig); - } - } + void verifySignature(Signature* sig, const XMLCh* peerName) const; CredentialResolver* m_credResolver; + SignatureTrustEngine* m_trust; SignatureProfileValidator m_profileValidator; - mutable SignatureValidator m_sigValidator; }; MetadataFilter* SAML_DLLLOCAL SignatureMetadataFilterFactory(const DOMElement* const & e) @@ -75,13 +86,14 @@ namespace opensaml { }; }; +static const XMLCh _TrustEngine[] = UNICODE_LITERAL_11(T,r,u,s,t,E,n,g,i,n,e); static const XMLCh _CredentialResolver[] = UNICODE_LITERAL_18(C,r,e,d,e,n,t,i,a,l,R,e,s,o,l,v,e,r); static const XMLCh type[] = UNICODE_LITERAL_4(t,y,p,e); static const XMLCh certificate[] = UNICODE_LITERAL_11(c,e,r,t,i,f,i,c,a,t,e); static const XMLCh Certificate[] = UNICODE_LITERAL_11(C,e,r,t,i,f,i,c,a,t,e); static const XMLCh Path[] = UNICODE_LITERAL_4(P,a,t,h); -SignatureMetadataFilter::SignatureMetadataFilter(const DOMElement* e) : m_credResolver(NULL) +SignatureMetadataFilter::SignatureMetadataFilter(const DOMElement* e) : m_credResolver(NULL), m_trust(NULL) { if (e && e->hasAttributeNS(NULL,certificate)) { // Dummy up a file resolver. @@ -95,13 +107,25 @@ SignatureMetadataFilter::SignatureMetadataFilter(const DOMElement* e) : m_credRe return; } - e = e ? XMLHelper::getFirstChildElement(e, _CredentialResolver) : NULL; - auto_ptr_char t(e ? e->getAttributeNS(NULL,type) : NULL); + DOMElement* sub = e ? XMLHelper::getFirstChildElement(e, _CredentialResolver) : NULL; + auto_ptr_char t(sub ? sub->getAttributeNS(NULL,type) : NULL); if (t.get()) { - m_credResolver = XMLToolingConfig::getConfig().CredentialResolverManager.newPlugin(t.get(),e); + m_credResolver = XMLToolingConfig::getConfig().CredentialResolverManager.newPlugin(t.get(),sub); + return; } - else - throw MetadataFilterException("Missing element, or no type attribute found"); + + sub = e ? XMLHelper::getFirstChildElement(e, _TrustEngine) : NULL; + auto_ptr_char t2(sub ? sub->getAttributeNS(NULL,type) : NULL); + if (t2.get()) { + TrustEngine* trust = XMLToolingConfig::getConfig().TrustEngineManager.newPlugin(t2.get(),sub); + if (!(m_trust = dynamic_cast(trust))) { + delete trust; + throw MetadataFilterException("TrustEngine-based SignatureMetadataFilter requires a SignatureTrustEngine plugin."); + } + return; + } + + throw MetadataFilterException("SignatureMetadataFilter configuration requires or element."); } void SignatureMetadataFilter::doFilter(XMLObject& xmlObject) const @@ -110,11 +134,6 @@ void SignatureMetadataFilter::doFilter(XMLObject& xmlObject) const NDC ndc("doFilter"); #endif - CredentialCriteria cc; - cc.setUsage(CredentialCriteria::SIGNING_CREDENTIAL); - Locker locker(m_credResolver); - m_sigValidator.setCredential(m_credResolver->resolve(&cc)); - try { EntitiesDescriptor& entities = dynamic_cast(xmlObject); doFilter(entities, true); @@ -127,7 +146,7 @@ void SignatureMetadataFilter::doFilter(XMLObject& xmlObject) const EntityDescriptor& entity = dynamic_cast(xmlObject); if (!entity.getSignature()) throw MetadataFilterException("Root metadata element was unsigned."); - verifySignature(entity.getSignature()); + verifySignature(entity.getSignature(), entity.getEntityID()); } catch (bad_cast) { } @@ -137,20 +156,20 @@ void SignatureMetadataFilter::doFilter(XMLObject& xmlObject) const void SignatureMetadataFilter::doFilter(EntitiesDescriptor& entities, bool rootObject) const { - Category& log=Category::getInstance(SAML_LOGCAT".Metadata"); + Category& log=Category::getInstance(SAML_LOGCAT".MetadataFilter.Signature"); Signature* sig = entities.getSignature(); if (!sig && rootObject) throw MetadataFilterException("Root metadata element was unsigned."); - verifySignature(sig); + verifySignature(sig, entities.getName()); VectorOf(EntityDescriptor) v=entities.getEntityDescriptors(); for (VectorOf(EntityDescriptor)::size_type i=0; igetSignature()); + verifySignature(v[i]->getSignature(), v[i]->getEntityID()); i++; } - catch (XMLToolingException& e) { + catch (exception& 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); @@ -160,13 +179,59 @@ void SignatureMetadataFilter::doFilter(EntitiesDescriptor& entities, bool rootOb VectorOf(EntitiesDescriptor) w=entities.getEntitiesDescriptors(); for (VectorOf(EntitiesDescriptor)::size_type j=0; jgetSignature()); + verifySignature(w[j]->getSignature(), w[j]->getName()); j++; } - catch (XMLToolingException& e) { + catch (exception& 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); } } } + +void SignatureMetadataFilter::verifySignature(Signature* sig, const XMLCh* peerName) const +{ + if (!sig) + return; + + m_profileValidator.validate(sig); + + // Set up criteria. + CredentialCriteria cc; + cc.setUsage(CredentialCriteria::SIGNING_CREDENTIAL); + cc.setSignature(*sig, CredentialCriteria::KEYINFO_EXTRACTION_KEY); + if (peerName) { + auto_ptr_char pname(peerName); + cc.setPeerName(pname.get()); + } + + if (m_credResolver) { + Locker locker(m_credResolver); + vector creds; + if (m_credResolver->resolve(creds,&cc)) { + SignatureValidator sigValidator; + for (vector::const_iterator i = creds.begin(); i != creds.end(); ++i) { + try { + sigValidator.setCredential(*i); + sigValidator.validate(sig); + return; // success! + } + catch (exception&) { + } + } + throw MetadataFilterException("CredentialResolver did not supply a successful verification key."); + } + else { + throw MetadataFilterException("CredentialResolver did not supply any verification keys."); + } + } + else if (m_trust) { + DummyCredentialResolver dummy; + if (m_trust->validate(*sig, dummy, &cc)) + return; + throw MetadataFilterException("TrustEngine unable to verify signature."); + } + + throw MetadataFilterException("Unable to verify signature."); +} diff --git a/saml/saml2/metadata/impl/WhitelistMetadataFilter.cpp b/saml/saml2/metadata/impl/WhitelistMetadataFilter.cpp index d351e77..f27a550 100644 --- a/saml/saml2/metadata/impl/WhitelistMetadataFilter.cpp +++ b/saml/saml2/metadata/impl/WhitelistMetadataFilter.cpp @@ -118,7 +118,7 @@ void WhitelistMetadataFilter::doFilter(XMLObject& xmlObject) const void WhitelistMetadataFilter::doFilter(EntitiesDescriptor& entities) const { - Category& log=Category::getInstance(SAML_LOGCAT".Metadata"); + Category& log=Category::getInstance(SAML_LOGCAT".MetadataFilter.Whitelist"); VectorOf(EntityDescriptor) v=entities.getEntityDescriptors(); for (VectorOf(EntityDescriptor)::size_type i=0; i - - - ../samltest/data/cert.pem - - diff --git a/samltest/data/security/StaticPKIXTrustEngine.xml b/samltest/data/security/StaticPKIXTrustEngine.xml new file mode 100644 index 0000000..47e1273 --- /dev/null +++ b/samltest/data/security/StaticPKIXTrustEngine.xml @@ -0,0 +1,2 @@ + + diff --git a/samltest/samltest.vcproj b/samltest/samltest.vcproj index 543520e..1907533 100644 --- a/samltest/samltest.vcproj +++ b/samltest/samltest.vcproj @@ -553,11 +553,11 @@ Name="security" > @@ -2449,7 +2449,7 @@ Name="security" > @@ -2487,7 +2487,7 @@ > @@ -2504,7 +2504,7 @@ > @@ -2513,7 +2513,7 @@ > diff --git a/samltest/security/AbstractPKIXTrustEngineTest.h b/samltest/security/StaticPKIXTrustEngineTest.h similarity index 57% rename from samltest/security/AbstractPKIXTrustEngineTest.h rename to samltest/security/StaticPKIXTrustEngineTest.h index f627a71..fdc44dc 100644 --- a/samltest/security/AbstractPKIXTrustEngineTest.h +++ b/samltest/security/StaticPKIXTrustEngineTest.h @@ -19,73 +19,13 @@ #include #include #include -#include -#include +#include using namespace opensaml::saml2; using namespace opensaml::saml2md; using namespace xmlsignature; -namespace { - class SampleTrustEngine : public AbstractPKIXTrustEngine { - public: - SampleTrustEngine() {} - ~SampleTrustEngine() {} - - class SampleIterator : public PKIXValidationInfoIterator { - CredentialResolver* m_resolver; - mutable vector m_crls; - bool m_done; - public: - SampleIterator() : m_resolver(NULL), m_done(false) { - string config = data_path + "security/FilesystemCredentialResolver.xml"; - ifstream in(config.c_str()); - DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(in); - XercesJanitor janitor(doc); - m_resolver = XMLToolingConfig::getConfig().CredentialResolverManager.newPlugin( - FILESYSTEM_CREDENTIAL_RESOLVER,doc->getDocumentElement() - ); - m_resolver->lock(); - } - - ~SampleIterator() { - m_resolver->unlock(); - delete m_resolver; - } - - bool next() { - if (m_done) - return false; - m_done = true; - return true; - } - - int getVerificationDepth() const { - return 0; - } - - const vector& getTrustAnchors() const { - return dynamic_cast(m_resolver->resolve())->getEntityCertificateChain(); - } - - const vector& getCRLs() const { - XSECCryptoX509CRL* crl = dynamic_cast(m_resolver->resolve())->getCRL(); - if (crl) - m_crls.push_back(crl); - return m_crls; - } - }; - - PKIXValidationInfoIterator* getPKIXValidationInfoIterator( - const CredentialResolver& credResolver, CredentialCriteria* criteria=NULL - ) const { - dynamic_cast(criteria); - return new SampleIterator(); - } - }; -}; - -class AbstractPKIXTrustEngineTest : public CxxTest::TestSuite, public SAMLObjectBaseTestCase { +class StaticPKIXTrustEngineTest : public CxxTest::TestSuite, public SAMLObjectBaseTestCase { public: void setUp() { SAMLObjectBaseTestCase::setUp(); @@ -95,7 +35,7 @@ public: SAMLObjectBaseTestCase::tearDown(); } - void testAbstractPKIXTrustEngine() { + void testStaticPKIXTrustEngine() { string config = data_path + "security/XMLMetadataProvider.xml"; ifstream in(config.c_str()); DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(in); @@ -119,15 +59,21 @@ public: } // Build trust engine. - auto_ptr trustEngine(new SampleTrustEngine()); - - // Get signed assertion. - config = data_path + "signature/SAML2Assertion.xml"; + config = data_path + "security/StaticPKIXTrustEngine.xml"; ifstream in2(config.c_str()); DOMDocument* doc2=XMLToolingConfig::getConfig().getParser().parse(in2); XercesJanitor janitor2(doc2); - auto_ptr assertion(dynamic_cast(XMLObjectBuilder::getBuilder(doc2->getDocumentElement())->buildFromDocument(doc2))); - janitor2.release(); + auto_ptr trustEngine( + XMLToolingConfig::getConfig().TrustEngineManager.newPlugin(STATIC_PKIX_TRUSTENGINE,doc2->getDocumentElement()) + ); + + // Get signed assertion. + config = data_path + "signature/SAML2Assertion.xml"; + ifstream in3(config.c_str()); + DOMDocument* doc3=XMLToolingConfig::getConfig().getParser().parse(in3); + XercesJanitor janitor3(doc3); + auto_ptr assertion(dynamic_cast(XMLObjectBuilder::getBuilder(doc3->getDocumentElement())->buildFromDocument(doc3))); + janitor3.release(); Locker locker(metadataProvider.get()); const EntityDescriptor* descriptor = metadataProvider->getEntityDescriptor("https://idp.example.org"); -- 2.1.4