From: Scott Cantor Date: Mon, 21 Aug 2006 17:05:22 +0000 (+0000) Subject: SAML TrustEngine wrappers, ExplicitKeyTrustEngine plugin. X-Git-Tag: 2.0-alpha1~202 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fcpp-opensaml.git;a=commitdiff_plain;h=d70c88b320fcc61aa5f657abbf57f8c451f4ab25 SAML TrustEngine wrappers, ExplicitKeyTrustEngine plugin. --- diff --git a/.cdtproject b/.cdtproject index 00d9ac0..83ab683 100644 --- a/.cdtproject +++ b/.cdtproject @@ -60,7 +60,7 @@ - + @@ -70,13 +70,16 @@ + + - + + diff --git a/.project b/.project index 6ad4904..22f4aa1 100644 --- a/.project +++ b/.project @@ -1,6 +1,6 @@ - OpenSAML2-C + cpp-opensaml2 XMLTooling-C diff --git a/saml/Makefile.am b/saml/Makefile.am index 869eb35..9ef8991 100644 --- a/saml/Makefile.am +++ b/saml/Makefile.am @@ -6,6 +6,8 @@ libsamlincludedir = $(includedir)/saml encincludedir = $(includedir)/saml/encryption +secincludedir = $(includedir)/saml/security + sigincludedir = $(includedir)/saml/signature utilincludedir = $(includedir)/saml/util @@ -26,6 +28,11 @@ libsamlinclude_HEADERS = \ encinclude_HEADERS = \ encryption/EncryptedKeyResolver.h +secinclude_HEADERS = \ + security/MetadataKeyInfoIterator.h \ + security/TrustEngine.h \ + security/X509TrustEngine.h + siginclude_HEADERS = \ signature/ContentReference.h \ signature/SignableObject.h \ @@ -58,7 +65,6 @@ noinst_HEADERS = \ libsaml_la_SOURCES = \ SAMLArtifact.cpp \ SAMLConfig.cpp \ - encryption/EncryptedKeyResolver.cpp \ saml1/core/impl/AssertionsImpl.cpp \ saml1/core/impl/AssertionsSchemaValidators.cpp \ saml1/core/impl/ProtocolsImpl.cpp \ @@ -79,6 +85,9 @@ libsaml_la_SOURCES = \ saml2/metadata/impl/ObservableMetadataProvider.cpp \ saml2/metadata/impl/SignatureMetadataFilter.cpp \ saml2/metadata/impl/WhitelistMetadataFilter.cpp \ + encryption/EncryptedKeyResolver.cpp \ + security/impl/TrustEngine.cpp \ + security/impl/ExplicitKeyTrustEngine.cpp \ signature/ContentReference.cpp \ signature/SignatureProfileValidator.cpp \ util/SAMLConstants.cpp diff --git a/saml/SAMLConfig.cpp b/saml/SAMLConfig.cpp index 695964a..e154646 100644 --- a/saml/SAMLConfig.cpp +++ b/saml/SAMLConfig.cpp @@ -29,6 +29,7 @@ #include "saml2/core/Protocols.h" #include "saml2/metadata/Metadata.h" #include "saml2/metadata/MetadataProvider.h" +#include "security/TrustEngine.h" #include "util/SAMLConstants.h" #include @@ -86,6 +87,7 @@ bool SAMLInternalConfig::init() saml2md::registerMetadataClasses(); saml2md::registerMetadataProviders(); saml2md::registerMetadataFilters(); + registerTrustEngines(); log.info("library initialization complete"); return true; @@ -105,6 +107,7 @@ void SAMLInternalConfig::term() SAMLArtifactManager.deregisterFactories(); MetadataFilterManager.deregisterFactories(); MetadataProviderManager.deregisterFactories(); + TrustEngineManager.deregisterFactories(); XMLToolingConfig::getConfig().term(); Category::getInstance(SAML_LOGCAT".SAMLConfig").info("library shutdown complete"); diff --git a/saml/SAMLConfig.h b/saml/SAMLConfig.h index 0c2f7be..b97bda0 100644 --- a/saml/SAMLConfig.h +++ b/saml/SAMLConfig.h @@ -37,6 +37,7 @@ namespace opensaml { class SAML_API SAMLArtifact; + class SAML_API TrustEngine; namespace saml2md { class SAML_API MetadataProvider; @@ -131,6 +132,11 @@ namespace opensaml { */ xmltooling::PluginManager SAMLArtifactManager; + /** + * Manages factories for TrustEngine plugins. + */ + xmltooling::PluginManager TrustEngineManager; + protected: SAMLConfig() {} }; diff --git a/saml/saml.vcproj b/saml/saml.vcproj index 73c7317..0485e28 100644 --- a/saml/saml.vcproj +++ b/saml/saml.vcproj @@ -337,6 +337,22 @@ > + + + + + + + + + + + + + + + + + +#include + +namespace opensaml { + + /** + * Adapter between SAML metadata and TrustEngine KeyInfoIterator interface. + */ + class SAML_API MetadataKeyInfoIterator : public xmltooling::TrustEngine::KeyInfoIterator + { + const std::vector& m_keys; + std::vector::const_iterator m_iter; + + void advance() { + while (hasNext()) { + const XMLCh* use=(*m_iter)->getUse(); + if ((!use || !*use || XMLString::equals(use,saml2md::KeyDescriptor::KEYTYPE_SIGNING)) && (*m_iter)->getKeyInfo()) + return; + m_iter++; + } + } + + public: + MetadataKeyInfoIterator(const saml2md::RoleDescriptor& role) : m_keys(role.getKeyDescriptors()) { + m_iter=m_keys.begin(); + advance(); + } + + virtual ~MetadataKeyInfoIterator() {} + + /** + * Indicates whether additional KeyInfo objects are available. + * + * @return true iff another KeyInfo object can be fetched + */ + virtual bool hasNext() const { + return m_iter!=m_keys.end(); + } + + /** + * Returns the next KeyInfo object available. + * + * @return the next KeyInfo object, or NULL if none are left + */ + virtual const xmlsignature::KeyInfo* next() { + xmlsignature::KeyInfo* ret = (*m_iter)->getKeyInfo(); + m_iter++; + advance(); + return ret; + } + }; +}; + +#endif /* __saml_keyiter_h__ */ diff --git a/saml/security/TrustEngine.h b/saml/security/TrustEngine.h new file mode 100644 index 0000000..996286b --- /dev/null +++ b/saml/security/TrustEngine.h @@ -0,0 +1,86 @@ +/* + * 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. + */ + +/** + * @file saml/security/TrustEngine.h + * + * SAML-specific TrustEngine API + */ + +#ifndef __saml_trust_h__ +#define __saml_trust_h__ + +#include +#include +#include + +namespace opensaml { + + /** + * Adapts SAML metadata as a source of KeyInfo for a TrustEngine + * and adds SAML-specific signature validation. + */ + class SAML_API TrustEngine { + MAKE_NONCOPYABLE(TrustEngine); + protected: + /** + * Constructor. + * + * If a DOM is supplied, the following XML content is supported: + * + *
    + *
  • <KeyResolver> elements with a type attribute + *
+ * + * XML namespaces are ignored in the processing of this content. + * + * @param e DOM to supply configuration for provider + */ + TrustEngine(const DOMElement* e=NULL) {} + + public: + virtual ~TrustEngine() {} + + /** + * Determines whether a signed SAML object is correct and valid with respect + * to the information known about the issuer. + * + * A custom KeyResolver can be supplied from outside the TrustEngine. + * Alternatively, one may be specified to the plugin constructor. + * A non-caching, inline resolver will be used as a fallback. + * + * @param sig reference to a signature object to validate + * @param role metadata role supplying key information + * @param keyResolver optional externally supplied KeyResolver, or NULL + */ + virtual bool validate( + xmlsignature::Signature& sig, + saml2md::RoleDescriptor& role, + const xmlsignature::KeyResolver* keyResolver=NULL + )=0; + }; + + + /** + * Registers TrustEngine classes into the runtime. + */ + void SAML_API registerTrustEngines(); + + /** TrustEngine based on explicit key information resolved from metadata. */ + #define EXPLICIT_KEY_SAMLTRUSTENGINE "org.opensaml.security.ExplicitKeyTrustEngine" +}; + +#endif /* __saml_trust_h__ */ diff --git a/saml/security/X509TrustEngine.h b/saml/security/X509TrustEngine.h new file mode 100644 index 0000000..1bc8bb6 --- /dev/null +++ b/saml/security/X509TrustEngine.h @@ -0,0 +1,78 @@ +/* + * 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. + */ + +/** + * @file saml/security/X509TrustEngine.h + * + * Extended TrustEngine interface that adds validation of X.509 credentials. + */ + +#ifndef __saml_x509trust_h__ +#define __saml_x509trust_h__ + +#include + +namespace opensaml { + + /** + * Extended TrustEngine interface that adds validation of X.509 credentials. + */ + class SAML_API X509TrustEngine : public TrustEngine { + protected: + /** + * Constructor. + * + * If a DOM is supplied, the following XML content is supported: + * + *
    + *
  • <KeyResolver> elements with a type attribute + *
+ * + * XML namespaces are ignored in the processing of this content. + * + * @param e DOM to supply configuration for provider + */ + X509TrustEngine(const DOMElement* e=NULL) : TrustEngine(e) {} + + public: + virtual ~X509TrustEngine() {} + + /** + * Determines whether an X.509 credential is valid with respect + * to the information known about the peer. + * + * A custom KeyResolver can be supplied from outside the TrustEngine. + * Alternatively, one may be specified to the plugin constructor. + * A non-caching, inline resolver will be used as a fallback. + * + * @param certEE end-entity certificate to validate + * @param certChain the complete set of certificates presented for validation (includes certEE) + * @param role metadata role supplying key information + * @param checkName true iff certificate subject/name checking has NOT already occurred + * @param keyResolver optional externally supplied KeyResolver, or NULL + */ + virtual bool validate( + XSECCryptoX509* certEE, + const std::vector& certChain, + saml2md::RoleDescriptor& role, + bool checkName=true, + const xmlsignature::KeyResolver* keyResolver=NULL + )=0; + }; + +}; + +#endif /* __saml_x509trust_h__ */ diff --git a/saml/security/impl/ExplicitKeyTrustEngine.cpp b/saml/security/impl/ExplicitKeyTrustEngine.cpp new file mode 100644 index 0000000..4219a90 --- /dev/null +++ b/saml/security/impl/ExplicitKeyTrustEngine.cpp @@ -0,0 +1,118 @@ +/* + * Copyright 2001-2005 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. + */ + +/** + * ExplicitKeyTrustEngine.cpp + * + * TrustEngine based on explicit knowledge of peer key information. + */ + +#include "internal.h" +#include "exceptions.h" +#include "security/MetadataKeyInfoIterator.h" +#include "security/X509TrustEngine.h" +#include "signature/SignatureProfileValidator.h" + +#include +#include +#include + +using namespace opensaml::saml2md; +using namespace opensaml; +using namespace xmlsignature; +using namespace log4cpp; +using namespace std; + +namespace opensaml { + class SAML_DLLLOCAL ExplicitKeyTrustEngine : public X509TrustEngine + { + public: + ExplicitKeyTrustEngine(const DOMElement* e) : X509TrustEngine(e), m_engine(NULL) { + auto_ptr engine( + xmltooling::XMLToolingConfig::getConfig().TrustEngineManager.newPlugin(EXPLICIT_KEY_TRUSTENGINE, e) + ); + if (m_engine=dynamic_cast(engine.get())) + engine.release(); + else + throw xmltooling::UnknownExtensionException("Embedded trust engine does not support required interface."); + } + + virtual ~ExplicitKeyTrustEngine() { + delete m_engine; + } + + virtual bool validate( + Signature& sig, + RoleDescriptor& role, + const KeyResolver* keyResolver=NULL + ); + virtual bool validate( + XSECCryptoX509* certEE, + const vector& certChain, + RoleDescriptor& role, + bool checkName=true, + const KeyResolver* keyResolver=NULL + ); + + private: + xmltooling::X509TrustEngine* m_engine; + }; + + TrustEngine* SAML_DLLLOCAL ExplicitKeyTrustEngineFactory(const DOMElement* const & e) + { + return new ExplicitKeyTrustEngine(e); + } +}; + +bool ExplicitKeyTrustEngine::validate( + Signature& sig, + RoleDescriptor& role, + const KeyResolver* keyResolver + ) +{ +#ifdef _DEBUG + xmltooling::NDC ndc("validate"); +#endif + Category& log=Category::getInstance(SAML_LOGCAT".TrustEngine"); + + log.debug("attempting to validate signature profile"); + SignatureProfileValidator sigValidator; + try { + sigValidator.validate(&sig); + log.debug("signature profile validated"); + } + catch (xmltooling::ValidationException& e) { + if (log.isDebugEnabled()) { + log.debug("signature profile failed to validate: %s", e.what()); + } + return false; + } + + MetadataKeyInfoIterator keys(role); + return static_cast(m_engine)->validate(sig,keys,keyResolver); +} + +bool ExplicitKeyTrustEngine::validate( + XSECCryptoX509* certEE, + const vector& certChain, + RoleDescriptor& role, + bool checkName, + const KeyResolver* keyResolver + ) +{ + MetadataKeyInfoIterator keys(role); + return m_engine->validate(certEE,certChain,keys,checkName,keyResolver); +} diff --git a/saml/security/impl/TrustEngine.cpp b/saml/security/impl/TrustEngine.cpp new file mode 100644 index 0000000..f5084c2 --- /dev/null +++ b/saml/security/impl/TrustEngine.cpp @@ -0,0 +1,38 @@ +/* + * 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. + */ + +/** + * TrustEngine.cpp + * + * Registration of factories for built-in engines + */ + +#include "internal.h" +#include "security/TrustEngine.h" + +using namespace opensaml; +using namespace xmltooling; +using namespace std; + +namespace opensaml { + SAML_DLLLOCAL PluginManager::Factory ExplicitKeyTrustEngineFactory; +}; + +void SAML_API opensaml::registerTrustEngines() +{ + SAMLConfig& conf=SAMLConfig::getConfig(); + conf.TrustEngineManager.registerFactory(EXPLICIT_KEY_SAMLTRUSTENGINE, ExplicitKeyTrustEngineFactory); +} diff --git a/samltest/Makefile.am b/samltest/Makefile.am index f76a967..73e4212 100644 --- a/samltest/Makefile.am +++ b/samltest/Makefile.am @@ -17,6 +17,7 @@ samltest_h = \ signature/SAML1RequestTest.h \ signature/SAML1ResponseTest.h \ signature/SAML2AssertionTest.h \ + security/ExplicitKeyTrustEngineTest.h \ saml1/core/impl/ActionTest.h \ saml1/core/impl/AdviceTest.h \ saml1/core/impl/AssertionIDReferenceTest.h \ diff --git a/samltest/data/security/FilesystemMetadataProvider.xml b/samltest/data/security/FilesystemMetadataProvider.xml new file mode 100644 index 0000000..9f4af74 --- /dev/null +++ b/samltest/data/security/FilesystemMetadataProvider.xml @@ -0,0 +1,2 @@ + + diff --git a/samltest/data/security/example-metadata.xml b/samltest/data/security/example-metadata.xml new file mode 100644 index 0000000..5c88e41 --- /dev/null +++ b/samltest/data/security/example-metadata.xml @@ -0,0 +1,33 @@ + + + + + + + + MIICjzCCAfigAwIBAgIJAKk8t1hYcMkhMA0GCSqGSIb3DQEBBAUAMDoxCzAJBgNV + BAYTAlVTMRIwEAYDVQQKEwlJbnRlcm5ldDIxFzAVBgNVBAMTDnNwLmV4YW1wbGUu + b3JnMB4XDTA1MDYyMDE1NDgzNFoXDTMyMTEwNTE1NDgzNFowOjELMAkGA1UEBhMC + VVMxEjAQBgNVBAoTCUludGVybmV0MjEXMBUGA1UEAxMOc3AuZXhhbXBsZS5vcmcw + gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANlZ1L1mKzYbUVKiMQLhZlfGDyYa + /jjCiaXP0WhLNgvJpOTeajvsrApYNnFX5MLNzuC3NeQIjXUNLN2Yo2MCSthBIOL5 + qE5dka4z9W9zytoflW1LmJ8vXpx8Ay/meG4z//J5iCpYVEquA0xl28HUIlownZUF + 7w7bx0cF/02qrR23AgMBAAGjgZwwgZkwHQYDVR0OBBYEFJZiO1qsyAyc3HwMlL9p + JpN6fbGwMGoGA1UdIwRjMGGAFJZiO1qsyAyc3HwMlL9pJpN6fbGwoT6kPDA6MQsw + CQYDVQQGEwJVUzESMBAGA1UEChMJSW50ZXJuZXQyMRcwFQYDVQQDEw5zcC5leGFt + cGxlLm9yZ4IJAKk8t1hYcMkhMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQAD + gYEAMFq/UeSQyngE0GpZueyD2UW0M358uhseYOgGEIfm+qXIFQF6MYwNoX7WFzhC + LJZ2E6mEvZZFHCHUtl7mGDvsRwgZ85YCtRbvleEpqfgNQToto9pLYe+X6vvH9Z6p + gmYsTmak+kxO93JprrOd9xp8aZPMEprL7VCdrhbZEfyYER0= + + + + + + + + + diff --git a/samltest/samltest.vcproj b/samltest/samltest.vcproj index e3eb662..ba7d52f 100644 --- a/samltest/samltest.vcproj +++ b/samltest/samltest.vcproj @@ -522,6 +522,14 @@
+ + + + @@ -549,7 +557,7 @@ > @@ -562,7 +570,7 @@ > @@ -571,7 +579,7 @@ > @@ -584,7 +592,7 @@ > @@ -593,7 +601,7 @@ > @@ -606,7 +614,7 @@ > @@ -615,7 +623,7 @@ > @@ -2284,6 +2292,32 @@ + + + + + + + + + + +#include +#include +#include + +#include "c:\cvs\cpp-opensaml2\samltest\security\ExplicitKeyTrustEngineTest.h" + +static ExplicitKeyTrustEngineTest suite_ExplicitKeyTrustEngineTest; + +static CxxTest::List Tests_ExplicitKeyTrustEngineTest = { 0, 0 }; +CxxTest::StaticSuiteDescription suiteDescription_ExplicitKeyTrustEngineTest( "c:\\cvs\\cpp-opensaml2\\samltest\\security\\ExplicitKeyTrustEngineTest.h", 25, "ExplicitKeyTrustEngineTest", suite_ExplicitKeyTrustEngineTest, Tests_ExplicitKeyTrustEngineTest ); + +static class TestDescription_ExplicitKeyTrustEngineTest_testExplicitKeyTrustEngine : public CxxTest::RealTestDescription { +public: + TestDescription_ExplicitKeyTrustEngineTest_testExplicitKeyTrustEngine() : CxxTest::RealTestDescription( Tests_ExplicitKeyTrustEngineTest, suiteDescription_ExplicitKeyTrustEngineTest, 35, "testExplicitKeyTrustEngine" ) {} + void runTest() { suite_ExplicitKeyTrustEngineTest.testExplicitKeyTrustEngine(); } +} testDescription_ExplicitKeyTrustEngineTest_testExplicitKeyTrustEngine; + diff --git a/samltest/security/ExplicitKeyTrustEngineTest.h b/samltest/security/ExplicitKeyTrustEngineTest.h new file mode 100644 index 0000000..511de41 --- /dev/null +++ b/samltest/security/ExplicitKeyTrustEngineTest.h @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#include "internal.h" +#include +#include + +using namespace opensaml::saml2; +using namespace opensaml::saml2md; +using namespace xmlsignature; + +class ExplicitKeyTrustEngineTest : public CxxTest::TestSuite, public SAMLObjectBaseTestCase { +public: + void setUp() { + SAMLObjectBaseTestCase::setUp(); + } + + void tearDown() { + SAMLObjectBaseTestCase::tearDown(); + } + + void testExplicitKeyTrustEngine() { + string config = data_path + "security/FilesystemMetadataProvider.xml"; + ifstream in(config.c_str()); + DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(in); + XercesJanitor janitor(doc); + + auto_ptr_XMLCh path("path"); + string s = data_path + "security/example-metadata.xml"; + auto_ptr_XMLCh file(s.c_str()); + doc->getDocumentElement()->setAttributeNS(NULL,path.get(),file.get()); + + // Build metadata provider. + auto_ptr metadataProvider( + SAMLConfig::getConfig().MetadataProviderManager.newPlugin(FILESYSTEM_METADATA_PROVIDER,doc->getDocumentElement()) + ); + try { + metadataProvider->init(); + } + catch (XMLToolingException& ex) { + TS_TRACE(ex.what()); + throw; + } + + // Build trust engine. + auto_ptr trustEngine( + SAMLConfig::getConfig().TrustEngineManager.newPlugin(EXPLICIT_KEY_SAMLTRUSTENGINE, NULL) + ); + + // Get signed assertion. + config = data_path + "signature/SAML2Assertion.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(); + + Locker locker(metadataProvider.get()); + const EntityDescriptor* descriptor = metadataProvider->getEntityDescriptor("https://idp.example.org"); + TSM_ASSERT("Retrieved entity descriptor was null", descriptor!=NULL); + + Signature* sig=assertion->getSignature(); + TSM_ASSERT("Signature not present", sig!=NULL); + + RoleDescriptor* role=descriptor->getIDPSSODescriptors().front(); + TSM_ASSERT("Role not present", role!=NULL); + + TSM_ASSERT("Signature failed to validate.", trustEngine->validate(*sig, *role, metadataProvider->getKeyResolver())); + } +};