From 73514022d6a44deb8da9be57c8ed9d7e823d3b6b Mon Sep 17 00:00:00 2001 From: cantor Date: Thu, 27 May 2010 21:59:52 +0000 Subject: [PATCH] https://bugs.internet2.edu/jira/browse/SSPCPP-293 git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/branches/REL_2@3258 cb58f699-b61c-0410-a6fe-9272a202ed29 --- adfs/adfs.cpp | 4 +- schemas/shibboleth-2.0-native-sp-config.xsd | 57 +++-- shibsp/Makefile.am | 4 +- shibsp/SPConfig.cpp | 3 + shibsp/SPConfig.h | 6 + shibsp/ServiceProvider.cpp | 9 + shibsp/ServiceProvider.h | 11 + shibsp/handler/AssertionConsumerService.h | 5 +- shibsp/handler/impl/AssertionConsumerService.cpp | 13 +- shibsp/handler/impl/SAML1Consumer.cpp | 4 +- shibsp/handler/impl/SAML2Consumer.cpp | 4 +- shibsp/impl/XMLSecurityPolicyProvider.cpp | 295 +++++++++++++++++++++++ shibsp/impl/XMLServiceProvider.cpp | 183 +++++--------- shibsp/security/SecurityPolicyProvider.h | 116 +++++++++ shibsp/shibsp.vcxproj | 2 + shibsp/shibsp.vcxproj.filters | 6 + 16 files changed, 562 insertions(+), 160 deletions(-) create mode 100644 shibsp/impl/XMLSecurityPolicyProvider.cpp create mode 100644 shibsp/security/SecurityPolicyProvider.h diff --git a/adfs/adfs.cpp b/adfs/adfs.cpp index 0c9f242..c957c19 100644 --- a/adfs/adfs.cpp +++ b/adfs/adfs.cpp @@ -179,7 +179,7 @@ namespace { const HTTPRequest& httpRequest, HTTPResponse& httpResponse, SecurityPolicy& policy, - const PropertySet* settings, + const PropertySet*, const XMLObject& xmlObject ) const; #endif @@ -595,7 +595,7 @@ void ADFSConsumer::implementProtocol( const HTTPRequest& httpRequest, HTTPResponse& httpResponse, SecurityPolicy& policy, - const PropertySet* settings, + const PropertySet*, const XMLObject& xmlObject ) const { diff --git a/schemas/shibboleth-2.0-native-sp-config.xsd b/schemas/shibboleth-2.0-native-sp-config.xsd index 02a4140..2e89fe9 100644 --- a/schemas/shibboleth-2.0-native-sp-config.xsd +++ b/schemas/shibboleth-2.0-native-sp-config.xsd @@ -604,31 +604,33 @@ - - - Container for specifying sets of policy rules to apply to incoming messages - - - - - Specifies a set of SecurityPolicyRule plugins - - - - - - - - - - - - - - - - - + + + + Container for specifying sets of policy rules to apply to incoming messages + + + + + Specifies a set of SecurityPolicyRule plugins + + + + + + + + + + + + + + + + + + @@ -676,7 +678,10 @@ - + + + + diff --git a/shibsp/Makefile.am b/shibsp/Makefile.am index 334f513..7184a59 100644 --- a/shibsp/Makefile.am +++ b/shibsp/Makefile.am @@ -87,7 +87,8 @@ reminclude_HEADERS = \ secinclude_HEADERS = \ security/PKIXTrustEngine.h \ - security/SecurityPolicy.h + security/SecurityPolicy.h \ + security/SecurityPolicyProvider.h utilinclude_HEADERS = \ util/CGIParser.h \ @@ -197,6 +198,7 @@ libshibsp_la_SOURCES = \ attribute/resolver/impl/XMLAttributeExtractor.cpp \ binding/impl/ArtifactResolver.cpp \ binding/impl/SOAPClient.cpp \ + impl/XMLSecurityPolicyProvider.cpp \ metadata/DynamicMetadataProvider.cpp \ metadata/MetadataExtImpl.cpp \ metadata/MetadataExtSchemaValidators.cpp \ diff --git a/shibsp/SPConfig.cpp b/shibsp/SPConfig.cpp index 7a4585b..f89ee17 100644 --- a/shibsp/SPConfig.cpp +++ b/shibsp/SPConfig.cpp @@ -55,6 +55,7 @@ # include "binding/ArtifactResolver.h" # include "metadata/MetadataExt.h" # include "security/PKIXTrustEngine.h" +# include "security/SecurityPolicyProvider.h" # include #endif @@ -251,6 +252,7 @@ bool SPConfig::init(const char* catalog_path, const char* inst_prefix) registerAttributeFilters(); registerMatchFunctors(); } + registerSecurityPolicyProviders(); #endif if (isEnabled(Listener)) @@ -301,6 +303,7 @@ void SPConfig::term() Attribute::deregisterFactories(); #ifndef SHIBSP_LITE + SecurityPolicyProviderManager.deregisterFactories(); if (isEnabled(AttributeResolution)) { MatchFunctorManager.deregisterFactories(); AttributeFilterManager.deregisterFactories(); diff --git a/shibsp/SPConfig.h b/shibsp/SPConfig.h index e8afff6..ed3ddb5 100644 --- a/shibsp/SPConfig.h +++ b/shibsp/SPConfig.h @@ -55,6 +55,7 @@ namespace shibsp { class SHIBSP_API AttributeResolver; class SHIBSP_API FilterPolicyContext; class SHIBSP_API MatchFunctor; + class SHIBSP_API SecurityPolicyProvider; #endif #if defined (_MSC_VER) @@ -212,6 +213,11 @@ namespace shibsp { * Manages factories for MatchFunctor plugins. */ xmltooling::PluginManager< MatchFunctor,xmltooling::QName,std::pair > MatchFunctorManager; + + /** + * Manages factories for SecurityPolicyProvider plugins. + */ + xmltooling::PluginManager SecurityPolicyProviderManager; #endif /** diff --git a/shibsp/ServiceProvider.cpp b/shibsp/ServiceProvider.cpp index e166771..9c2487b 100644 --- a/shibsp/ServiceProvider.cpp +++ b/shibsp/ServiceProvider.cpp @@ -168,6 +168,15 @@ ServiceProvider::~ServiceProvider() { } +#ifndef SHIBSP_LITE +SecurityPolicyProvider* ServiceProvider::getSecurityPolicyProvider(bool required) const +{ + if (required) + throw ConfigurationException("No SecurityPolicyProvider available."); + return NULL; +} +#endif + pair ServiceProvider::doAuthentication(SPRequest& request, bool handler) const { #ifdef _DEBUG diff --git a/shibsp/ServiceProvider.h b/shibsp/ServiceProvider.h index 2a4e4b7..15658fe 100644 --- a/shibsp/ServiceProvider.h +++ b/shibsp/ServiceProvider.h @@ -48,6 +48,7 @@ namespace shibsp { class SHIBSP_API SPRequest; class SHIBSP_API TemplateParameters; #ifndef SHIBSP_LITE + class SHIBSP_API SecurityPolicyProvider; class SHIBSP_API TransactionLog; #endif @@ -109,6 +110,15 @@ namespace shibsp { #ifndef SHIBSP_LITE /** + * Returns a SecurityPolicyProvider instance. + * + * @param required true iff an exception should be thrown if no SecurityPolicyProvider is available + * @return a SecurityPolicyProvider + */ + virtual SecurityPolicyProvider* getSecurityPolicyProvider(bool required=true) const; + + /** + * @deprecated * Returns the security policy settings for an identified policy. * * @param id identifies the policy to return @@ -117,6 +127,7 @@ namespace shibsp { virtual const PropertySet* getPolicySettings(const char* id) const=0; /** + * @deprecated * Returns the security policy rules for an identified policy. * * @param id identifies the policy to return diff --git a/shibsp/handler/AssertionConsumerService.h b/shibsp/handler/AssertionConsumerService.h index bbdafd4..a7618fb 100644 --- a/shibsp/handler/AssertionConsumerService.h +++ b/shibsp/handler/AssertionConsumerService.h @@ -94,6 +94,7 @@ namespace shibsp { void generateMetadata(opensaml::saml2md::SPSSODescriptor& role, const char* handlerURL) const; /** + * @deprecated * Returns a SecurityPolicy instance to use for an incoming request. * *

Allows handlers to customize the type of policy object their policy rules might require. @@ -121,7 +122,7 @@ namespace shibsp { * @param httpRequest client request that included message * @param httpResponse response to client * @param policy the SecurityPolicy in effect, after having evaluated the message - * @param settings policy configuration settings in effect + * @param reserved ignore this parameter * @param xmlObject a protocol-specific message object */ virtual void implementProtocol( @@ -129,7 +130,7 @@ namespace shibsp { const xmltooling::HTTPRequest& httpRequest, xmltooling::HTTPResponse& httpResponse, opensaml::SecurityPolicy& policy, - const PropertySet* settings, + const PropertySet* reserved, const xmltooling::XMLObject& xmlObject ) const=0; diff --git a/shibsp/handler/impl/AssertionConsumerService.cpp b/shibsp/handler/impl/AssertionConsumerService.cpp index 1c1160b..b35cd5c 100644 --- a/shibsp/handler/impl/AssertionConsumerService.cpp +++ b/shibsp/handler/impl/AssertionConsumerService.cpp @@ -38,6 +38,7 @@ # include "attribute/resolver/ResolutionContext.h" # include "metadata/MetadataProviderCriteria.h" # include "security/SecurityPolicy.h" +# include "security/SecurityPolicyProvider.h" # include # include # include @@ -143,16 +144,12 @@ pair AssertionConsumerService::processMessage( if (!policyId.first) policyId = application.getString("policyId"); // unqualified in Application(s) element - // Access policy properties. - const PropertySet* settings = application.getServiceProvider().getPolicySettings(policyId.second); - pair validate = settings->getBool("validate"); - // Lock metadata for use by policy. Locker metadataLocker(application.getMetadataProvider()); // Create the policy. auto_ptr policy( - createSecurityPolicy(application, &m_role, validate.first && validate.second, policyId.second) + application.getServiceProvider().getSecurityPolicyProvider()->createSecurityPolicy(application, &m_role, policyId.second) ); string relayState; @@ -164,7 +161,7 @@ pair AssertionConsumerService::processMessage( DDF postData = recoverPostData(application, httpRequest, httpResponse, relayState.c_str()); DDFJanitor postjan(postData); recoverRelayState(application, httpRequest, httpResponse, relayState); - implementProtocol(application, httpRequest, httpResponse, *(policy.get()), settings, *msg.get()); + implementProtocol(application, httpRequest, httpResponse, *(policy.get()), NULL, *msg.get()); auto_ptr_char issuer(policy->getIssuer() ? policy->getIssuer()->getName() : nullptr); @@ -187,8 +184,8 @@ pair AssertionConsumerService::processMessage( // Check for isPassive error condition. const char* sc2 = ex.getProperty("statusCode2"); if (sc2 && !strcmp(sc2, "urn:oasis:names:tc:SAML:2.0:status:NoPassive")) { - validate = getBool("ignoreNoPassive", m_configNS.get()); // namespace-qualified if inside handler element - if (validate.first && validate.second && !relayState.empty()) { + pair ignore = getBool("ignoreNoPassive", m_configNS.get()); // namespace-qualified if inside handler element + if (ignore.first && ignore.second && !relayState.empty()) { m_log.debug("ignoring SAML status of NoPassive and redirecting to resource..."); return make_pair(true, httpResponse.sendRedirect(relayState.c_str())); } diff --git a/shibsp/handler/impl/SAML1Consumer.cpp b/shibsp/handler/impl/SAML1Consumer.cpp index b098aab..78cd49a 100644 --- a/shibsp/handler/impl/SAML1Consumer.cpp +++ b/shibsp/handler/impl/SAML1Consumer.cpp @@ -91,7 +91,7 @@ namespace shibsp { const HTTPRequest& httpRequest, HTTPResponse& httpResponse, SecurityPolicy& policy, - const PropertySet* settings, + const PropertySet*, const XMLObject& xmlObject ) const; @@ -130,7 +130,7 @@ void SAML1Consumer::implementProtocol( const HTTPRequest& httpRequest, HTTPResponse& httpResponse, SecurityPolicy& policy, - const PropertySet* settings, + const PropertySet*, const XMLObject& xmlObject ) const { diff --git a/shibsp/handler/impl/SAML2Consumer.cpp b/shibsp/handler/impl/SAML2Consumer.cpp index c22f4a2..e67297e 100644 --- a/shibsp/handler/impl/SAML2Consumer.cpp +++ b/shibsp/handler/impl/SAML2Consumer.cpp @@ -87,7 +87,7 @@ namespace shibsp { const HTTPRequest& httpRequest, HTTPResponse& httpResponse, SecurityPolicy& policy, - const PropertySet* settings, + const PropertySet*, const XMLObject& xmlObject ) const; @@ -125,7 +125,7 @@ void SAML2Consumer::implementProtocol( const HTTPRequest& httpRequest, HTTPResponse& httpResponse, SecurityPolicy& policy, - const PropertySet* settings, + const PropertySet*, const XMLObject& xmlObject ) const { diff --git a/shibsp/impl/XMLSecurityPolicyProvider.cpp b/shibsp/impl/XMLSecurityPolicyProvider.cpp new file mode 100644 index 0000000..5f0fdb0 --- /dev/null +++ b/shibsp/impl/XMLSecurityPolicyProvider.cpp @@ -0,0 +1,295 @@ +/* + * Copyright 2010 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. + */ + +/** + * XMLSecurityPolicyProvider.cpp + * + * XML-based security policy provider. + */ + +#include "internal.h" +#include "exceptions.h" +#include "Application.h" +#include "security/SecurityPolicy.h" +#include "security/SecurityPolicyProvider.h" +#include "util/DOMPropertySet.h" +#include "util/SPConstants.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using shibspconstants::SHIB2SPCONFIG_NS; +using opensaml::SAMLConfig; +using opensaml::SecurityPolicyRule; +using namespace shibsp; +using namespace xmltooling; +using namespace std; + +namespace shibsp { + +#if defined (_MSC_VER) + #pragma warning( push ) + #pragma warning( disable : 4250 ) +#endif + + class SHIBSP_DLLLOCAL XMLSecurityPolicyProviderImpl + { + public: + XMLSecurityPolicyProviderImpl(const DOMElement* e, Category& log); + ~XMLSecurityPolicyProviderImpl() { + for (map< string,pair > >::iterator i = m_policyMap.begin(); i != m_policyMap.end(); ++i) { + delete i->second.first; + for_each(i->second.second.begin(), i->second.second.end(), xmltooling::cleanup()); + } + if (m_document) + m_document->release(); + } + + void setDocument(DOMDocument* doc) { + m_document = doc; + } + + private: + DOMDocument* m_document; + vector m_whitelist,m_blacklist; + map< string,pair< PropertySet*,vector > > m_policyMap; + + friend class SHIBSP_DLLLOCAL XMLSecurityPolicyProvider; + }; + + class XMLSecurityPolicyProvider : public SecurityPolicyProvider, public ReloadableXMLFile + { + public: + XMLSecurityPolicyProvider(const DOMElement* e) + : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".SecurityPolicyProvider.XML")), m_impl(nullptr) { + background_load(); // guarantees an exception or the policy is loaded + } + + ~XMLSecurityPolicyProvider() { + shutdown(); + delete m_impl; + } + + const PropertySet* getPolicySettings(const char* id) const { + map > >::const_iterator i = m_impl->m_policyMap.find(id); + if (i != m_impl->m_policyMap.end()) + return i->second.first; + throw ConfigurationException("Security Policy ($1) not found, check element.", params(1,id)); + } + + const vector& getPolicyRules(const char* id) const { + map > >::const_iterator i = m_impl->m_policyMap.find(id); + if (i != m_impl->m_policyMap.end()) + return i->second.second; + throw ConfigurationException("Security Policy ($1) not found, check element.", params(1,id)); + } + const vector& getAlgorithmBlacklist() const { + return m_impl->m_blacklist; + } + const vector& getAlgorithmWhitelist() const { + return m_impl->m_whitelist; + } + + protected: + pair load(bool backup); + pair background_load(); + + private: + XMLSecurityPolicyProviderImpl* m_impl; + }; + +#if defined (_MSC_VER) + #pragma warning( pop ) +#endif + + SecurityPolicyProvider* SHIBSP_DLLLOCAL XMLSecurityPolicyProviderFactory(const DOMElement* const & e) + { + return new XMLSecurityPolicyProvider(e); + } + + class SHIBSP_DLLLOCAL PolicyNodeFilter : public DOMNodeFilter + { + public: +#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE + short +#else + FilterAction +#endif + acceptNode(const DOMNode* node) const { + return FILTER_REJECT; + } + }; + + static const XMLCh _id[] = UNICODE_LITERAL_2(i,d); + static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e); + static const XMLCh AlgorithmBlacklist[] = UNICODE_LITERAL_18(A,l,g,o,r,i,t,h,m,B,l,a,c,k,l,i,s,t); + static const XMLCh AlgorithmWhitelist[] = UNICODE_LITERAL_18(A,l,g,o,r,i,t,h,m,W,h,i,t,e,l,i,s,t); + static const XMLCh Policy[] = UNICODE_LITERAL_6(P,o,l,i,c,y); + static const XMLCh PolicyRule[] = UNICODE_LITERAL_10(P,o,l,i,c,y,R,u,l,e); + static const XMLCh Rule[] = UNICODE_LITERAL_4(R,u,l,e); + static const XMLCh SecurityPolicies[] = UNICODE_LITERAL_16(S,e,c,u,r,i,t,y,P,o,l,i,c,i,e,s); +} + +void SHIBSP_API shibsp::registerSecurityPolicyProviders() +{ + SPConfig::getConfig().SecurityPolicyProviderManager.registerFactory(XML_SECURITYPOLICY_PROVIDER, XMLSecurityPolicyProviderFactory); +} + +SecurityPolicyProvider::SecurityPolicyProvider() +{ +} + +SecurityPolicyProvider::~SecurityPolicyProvider() +{ +} + +opensaml::SecurityPolicy* SecurityPolicyProvider::createSecurityPolicy( + const Application& application, const xmltooling::QName* role, const char* policyId + ) const +{ + pair validate = getPolicySettings(policyId ? policyId : application.getString("policyId").second)->getBool("validate"); + return new SecurityPolicy(application, role, (validate.first && validate.second), policyId); +} + +XMLSecurityPolicyProviderImpl::XMLSecurityPolicyProviderImpl(const DOMElement* e, Category& log) : m_document(nullptr) +{ +#ifdef _DEBUG + xmltooling::NDC ndc("XMLSecurityPolicyProviderImpl"); +#endif + + if (!XMLHelper::isNodeNamed(e, SHIB2SPCONFIG_NS, SecurityPolicies)) + throw ConfigurationException("XML SecurityPolicyProvider requires conf:SecurityPolicies at root of configuration."); + + const XMLCh* algs = nullptr; + const DOMElement* alglist = XMLHelper::getLastChildElement(e, AlgorithmBlacklist); + if (alglist && alglist->hasChildNodes()) { + algs = alglist->getFirstChild()->getNodeValue(); + } + else if ((alglist = XMLHelper::getLastChildElement(e, AlgorithmWhitelist)) && alglist->hasChildNodes()) { + algs = alglist->getFirstChild()->getNodeValue(); + } + if (algs) { + const XMLCh* token; + XMLStringTokenizer tokenizer(algs); + while (tokenizer.hasMoreTokens()) { + token = tokenizer.nextToken(); + if (token) { + if (XMLString::equals(alglist->getLocalName(), AlgorithmBlacklist)) + m_blacklist.push_back(token); + else + m_whitelist.push_back(token); + } + } + } + + PolicyNodeFilter filter; + SAMLConfig& samlConf = SAMLConfig::getConfig(); + e = XMLHelper::getFirstChildElement(e, Policy); + while (e) { + auto_ptr_char id(e->getAttributeNS(nullptr, _id)); + pair< PropertySet*,vector >& rules = m_policyMap[id.get()]; + rules.first = nullptr; + auto_ptr settings(new DOMPropertySet()); + settings->load(e, nullptr, &filter); + rules.first = settings.release(); + + // Process PolicyRule elements. + const DOMElement* rule = XMLHelper::getFirstChildElement(e, PolicyRule); + while (rule) { + auto_ptr_char type(rule->getAttributeNS(nullptr, _type)); + try { + rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(type.get(), rule)); + } + catch (exception& ex) { + log.crit("error instantiating policy rule (%s) in policy (%s): %s", type.get(), id.get(), ex.what()); + } + rule = XMLHelper::getNextSiblingElement(rule, PolicyRule); + } + + if (rules.second.size() == 0) { + // Process Rule elements. + log.warn("detected legacy Policy configuration, please convert to new PolicyRule syntax"); + rule = XMLHelper::getFirstChildElement(e, Rule); + while (rule) { + auto_ptr_char type(rule->getAttributeNS(nullptr, _type)); + try { + rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(type.get(), rule)); + } + catch (exception& ex) { + log.crit("error instantiating policy rule (%s) in policy (%s): %s", type.get(), id.get(), ex.what()); + } + rule = XMLHelper::getNextSiblingElement(rule, Rule); + } + + // Manually add a basic Conditions rule. + log.info("installing a default Conditions rule in policy (%s) for compatibility with legacy configuration", id.get()); + rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(CONDITIONS_POLICY_RULE, nullptr)); + } + + e = XMLHelper::getNextSiblingElement(e, Policy); + } +} + +pair XMLSecurityPolicyProvider::load(bool backup) +{ + // Load from source using base class. + pair raw = ReloadableXMLFile::load(backup); + + // If we own it, wrap it. + XercesJanitor docjanitor(raw.first ? raw.second->getOwnerDocument() : nullptr); + + XMLSecurityPolicyProviderImpl* impl = new XMLSecurityPolicyProviderImpl(raw.second, m_log); + + // If we held the document, transfer it to the impl. If we didn't, it's a no-op. + impl->setDocument(docjanitor.release()); + + // Perform the swap inside a lock. + if (m_lock) + m_lock->wrlock(); + SharedLock locker(m_lock, false); + delete m_impl; + m_impl = impl; + + + return make_pair(false,(DOMElement*)nullptr); +} + +pair XMLSecurityPolicyProvider::background_load() +{ + try { + return load(false); + } + catch (long& ex) { + if (ex == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED) + m_log.info("remote resource (%s) unchanged", m_source.c_str()); + if (!m_loaded && !m_backing.empty()) + return load(true); + throw; + } + catch (exception&) { + if (!m_loaded && !m_backing.empty()) + return load(true); + throw; + } +} diff --git a/shibsp/impl/XMLServiceProvider.cpp b/shibsp/impl/XMLServiceProvider.cpp index d052a9e..231039f 100644 --- a/shibsp/impl/XMLServiceProvider.cpp +++ b/shibsp/impl/XMLServiceProvider.cpp @@ -58,11 +58,11 @@ # include "attribute/resolver/AttributeExtractor.h" # include "attribute/resolver/AttributeResolver.h" # include "security/PKIXTrustEngine.h" +# include "security/SecurityPolicyProvider.h" # include # include # include # include -# include # include # include # include @@ -74,7 +74,6 @@ # include # include # include -# include # include using namespace opensaml::saml2; using namespace opensaml::saml2p; @@ -254,7 +253,7 @@ namespace { RequestMapper* m_requestMapper; map m_appmap; #ifndef SHIBSP_LITE - map< string,pair< PropertySet*,vector > > m_policyMap; + SecurityPolicyProvider* m_policy; vector< pair< string, pair > > m_transportOptions; #endif @@ -309,6 +308,21 @@ namespace { #endif } +#ifndef SHIBSP_LITE + // Lockable + Lockable* lock() { + ReloadableXMLFile::lock(); + if (m_impl->m_policy) + m_impl->m_policy->lock(); + return this; + } + void unlock() { + if (m_impl->m_policy) + m_impl->m_policy->unlock(); + ReloadableXMLFile::unlock(); + } +#endif + // PropertySet const PropertySet* getParent() const { return m_impl->getParent(); } void setParent(const PropertySet* parent) {return m_impl->setParent(parent);} @@ -366,18 +380,18 @@ namespace { } #ifndef SHIBSP_LITE + SecurityPolicyProvider* getSecurityPolicyProvider(bool required=true) const { + if (required && !m_impl->m_policy) + throw ConfigurationException("No SecurityPolicyProvider available."); + return m_impl->m_policy; + } + const PropertySet* getPolicySettings(const char* id) const { - map > >::const_iterator i = m_impl->m_policyMap.find(id); - if (i!=m_impl->m_policyMap.end()) - return i->second.first; - throw ConfigurationException("Security Policy ($1) not found, check element.", params(1,id)); + return getSecurityPolicyProvider()->getPolicySettings(id); } const vector& getPolicyRules(const char* id) const { - map > >::const_iterator i = m_impl->m_policyMap.find(id); - if (i!=m_impl->m_policyMap.end()) - return i->second.second; - throw ConfigurationException("Security Policy ($1) not found, check element.", params(1,id)); + return getSecurityPolicyProvider()->getPolicyRules(id); } bool setTransportOptions(SOAPTransport& transport) const { @@ -411,8 +425,6 @@ namespace { #pragma warning( pop ) #endif - static const XMLCh AlgorithmBlacklist[] = UNICODE_LITERAL_18(A,l,g,o,r,i,t,h,m,B,l,a,c,k,l,i,s,t); - static const XMLCh AlgorithmWhitelist[] = UNICODE_LITERAL_18(A,l,g,o,r,i,t,h,m,W,h,i,t,e,l,i,s,t); static const XMLCh ApplicationOverride[] = UNICODE_LITERAL_19(A,p,p,l,i,c,a,t,i,o,n,O,v,e,r,r,i,d,e); static const XMLCh ApplicationDefaults[] = UNICODE_LITERAL_19(A,p,p,l,i,c,a,t,i,o,n,D,e,f,a,u,l,t,s); static const XMLCh _ArtifactMap[] = UNICODE_LITERAL_11(A,r,t,i,f,a,c,t,M,a,p); @@ -441,14 +453,12 @@ namespace { static const XMLCh _option[] = UNICODE_LITERAL_6(o,p,t,i,o,n); static const XMLCh OutOfProcess[] = UNICODE_LITERAL_12(O,u,t,O,f,P,r,o,c,e,s,s); static const XMLCh _path[] = UNICODE_LITERAL_4(p,a,t,h); - static const XMLCh Policy[] = UNICODE_LITERAL_6(P,o,l,i,c,y); - static const XMLCh PolicyRule[] = UNICODE_LITERAL_10(P,o,l,i,c,y,R,u,l,e); static const XMLCh _provider[] = UNICODE_LITERAL_8(p,r,o,v,i,d,e,r); static const XMLCh RelyingParty[] = UNICODE_LITERAL_12(R,e,l,y,i,n,g,P,a,r,t,y); static const XMLCh _ReplayCache[] = UNICODE_LITERAL_11(R,e,p,l,a,y,C,a,c,h,e); static const XMLCh _RequestMapper[] = UNICODE_LITERAL_13(R,e,q,u,e,s,t,M,a,p,p,e,r); - static const XMLCh Rule[] = UNICODE_LITERAL_4(R,u,l,e); static const XMLCh SecurityPolicies[] = UNICODE_LITERAL_16(S,e,c,u,r,i,t,y,P,o,l,i,c,i,e,s); + static const XMLCh SecurityPolicyProvider[] = UNICODE_LITERAL_22(S,e,c,u,r,i,t,y,P,o,l,i,c,y,P,r,o,v,i,d,e,r); static const XMLCh _SessionCache[] = UNICODE_LITERAL_12(S,e,s,s,i,o,n,C,a,c,h,e); static const XMLCh _SessionInitiator[] = UNICODE_LITERAL_16(S,e,s,s,i,o,n,I,n,i,t,i,a,t,o,r); static const XMLCh _SingleLogoutService[] = UNICODE_LITERAL_19(S,i,n,g,l,e,L,o,g,o,u,t,S,e,r,v,i,c,e); @@ -459,21 +469,6 @@ namespace { static const XMLCh _TrustEngine[] = UNICODE_LITERAL_11(T,r,u,s,t,E,n,g,i,n,e); static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e); static const XMLCh UnixListener[] = UNICODE_LITERAL_12(U,n,i,x,L,i,s,t,e,n,e,r); - -#ifndef SHIBSP_LITE - class SHIBSP_DLLLOCAL PolicyNodeFilter : public DOMNodeFilter - { - public: -#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE - short -#else - FilterAction -#endif - acceptNode(const DOMNode* node) const { - return FILTER_REJECT; - } - }; -#endif }; namespace shibsp { @@ -1225,6 +1220,7 @@ XMLConfigImpl::acceptNode(const DOMNode* node) const XMLString::equals(name,_RequestMapper) || XMLString::equals(name,_ReplayCache) || XMLString::equals(name,SecurityPolicies) || + XMLString::equals(name,SecurityPolicyProvider) || XMLString::equals(name,_SessionCache) || XMLString::equals(name,Site) || XMLString::equals(name,_StorageService) || @@ -1266,7 +1262,11 @@ void XMLConfigImpl::doExtensions(const DOMElement* e, const char* label, Categor } XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* outer, Category& log) - : m_requestMapper(nullptr), m_outer(outer), m_document(nullptr) + : m_requestMapper(nullptr), +#ifndef SHIBSP_LITE + m_policy(nullptr), +#endif + m_outer(outer), m_document(nullptr) { #ifdef _DEBUG xmltooling::NDC ndc("XMLConfigImpl"); @@ -1320,7 +1320,7 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o // First load any property sets. load(e,nullptr,this); - const DOMElement* child; + DOMElement* child; string plugtype; // Much of the processing can only occur on the first instantiation. @@ -1456,44 +1456,11 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o throw ConfigurationException("Can't build SessionCache, missing conf:SessionCache element?"); } } - -#ifndef SHIBSP_LITE - child = XMLHelper::getLastChildElement(e, SecurityPolicies); - if (child) { - const XMLCh* algs = nullptr; - const DOMElement* alglist = XMLHelper::getLastChildElement(child, AlgorithmBlacklist); - if (alglist && alglist->hasChildNodes()) { - algs = alglist->getFirstChild()->getNodeValue(); - } - else if ((alglist = XMLHelper::getLastChildElement(child, AlgorithmWhitelist)) && alglist->hasChildNodes()) { - algs = alglist->getFirstChild()->getNodeValue(); - } - if (algs) { -#ifdef SHIBSP_XMLSEC_WHITELISTING - const XMLCh* token; - XMLStringTokenizer tokenizer(algs); - while (tokenizer.hasMoreTokens()) { - token = tokenizer.nextToken(); - if (token) { - if (XMLString::equals(alglist->getLocalName(), AlgorithmBlacklist)) - XSECPlatformUtils::blacklistAlgorithm(token); - else - XSECPlatformUtils::whitelistAlgorithm(token); - } - } -#else - log.fatal("XML-Security-C library prior to 1.6.0 does not support algorithm white/blacklists"); - throw ConfigurationException("XML-Security-C library prior to 1.6.0 does not support algorithm white/blacklists."); -#endif - } - } -#endif } // end of first-time-only stuff // Back to the fully dynamic stuff...next up is the RequestMapper. if (conf.isEnabled(SPConfig::RequestMapping)) { - child=XMLHelper::getFirstChildElement(e,_RequestMapper); - if (child) { + if (child = XMLHelper::getFirstChildElement(e,_RequestMapper)) { auto_ptr_char type(child->getAttributeNS(nullptr,_type)); log.info("building RequestMapper of type %s...",type.get()); m_requestMapper=conf.RequestMapperManager.newPlugin(type.get(),child); @@ -1506,53 +1473,38 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o #ifndef SHIBSP_LITE // Load security policies. - child = XMLHelper::getLastChildElement(e,SecurityPolicies); - if (child) { - PolicyNodeFilter filter; - child = XMLHelper::getFirstChildElement(child,Policy); - while (child) { - auto_ptr_char id(child->getAttributeNS(nullptr,_id)); - pair< PropertySet*,vector >& rules = m_policyMap[id.get()]; - rules.first = nullptr; - auto_ptr settings(new DOMPropertySet()); - settings->load(child, nullptr, &filter); - rules.first = settings.release(); - - // Process PolicyRule elements. - const DOMElement* rule = XMLHelper::getFirstChildElement(child,PolicyRule); - while (rule) { - auto_ptr_char type(rule->getAttributeNS(nullptr,_type)); - try { - rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(type.get(),rule)); - } - catch (exception& ex) { - log.crit("error instantiating policy rule (%s) in policy (%s): %s", type.get(), id.get(), ex.what()); - } - rule = XMLHelper::getNextSiblingElement(rule,PolicyRule); - } - - if (rules.second.size() == 0) { - // Process Rule elements. - log.warn("detected legacy Policy configuration, please convert to new PolicyRule syntax"); - rule = XMLHelper::getFirstChildElement(child,Rule); - while (rule) { - auto_ptr_char type(rule->getAttributeNS(nullptr,_type)); - try { - rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(type.get(),rule)); - } - catch (exception& ex) { - log.crit("error instantiating policy rule (%s) in policy (%s): %s", type.get(), id.get(), ex.what()); - } - rule = XMLHelper::getNextSiblingElement(rule,Rule); - } - - // Manually add a basic Conditions rule. - log.info("installing a default Conditions rule in policy (%s) for compatibility with legacy configuration", id.get()); - rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(CONDITIONS_POLICY_RULE, nullptr)); - } + if (child = XMLHelper::getLastChildElement(e, SecurityPolicyProvider)) { + auto_ptr_char type(child->getAttributeNS(nullptr, _type)); + log.info("building SecurityPolicyProvider of type %s...", type.get()); + m_policy = conf.SecurityPolicyProviderManager.newPlugin(type.get(), child); + } + else if (child = XMLHelper::getLastChildElement(e, SecurityPolicies)) { + // For backward compatibility, wrap in a plugin element. + DOMElement* polwrapper = e->getOwnerDocument()->createElementNS(nullptr, SecurityPolicyProvider); + polwrapper->appendChild(child); + log.info("building SecurityPolicyProvider of type %s...", XML_SECURITYPOLICY_PROVIDER); + m_policy = conf.SecurityPolicyProviderManager.newPlugin(XML_SECURITYPOLICY_PROVIDER, polwrapper); + } + else { + log.fatal("can't build SecurityPolicyProvider, missing conf:SecurityPolicyProvider element?"); + throw ConfigurationException("Can't build SecurityPolicyProvider, missing conf:SecurityPolicyProvider element?"); + } - child = XMLHelper::getNextSiblingElement(child,Policy); + if (first) { +#ifdef SHIBSP_XMLSEC_WHITELISTING + vector::const_iterator alg; + if (!m_policy->getAlgorithmBlacklist().empty()) { + for (alg = m_policy->getAlgorithmBlacklist().begin(); alg != m_policy->getAlgorithmBlacklist().end(); ++alg) + XSECPlatformUtils::blacklistAlgorithm(alg->c_str()); + } + else if (!m_policy->getAlgorithmWhitelist().empty()) { + for (alg = m_policy->getAlgorithmWhitelist().begin(); alg != m_policy->getAlgorithmWhitelist().end(); ++alg) + XSECPlatformUtils::whitelistAlgorithm(alg->c_str()); } +#else + log.fatal("XML-Security-C library prior to 1.6.0 does not support algorithm white/blacklists"); + throw ConfigurationException("XML-Security-C library prior to 1.6.0 does not support algorithm white/blacklists."); +#endif } // Process TransportOption elements. @@ -1609,11 +1561,8 @@ void XMLConfigImpl::cleanup() for_each(m_appmap.begin(),m_appmap.end(),cleanup_pair()); m_appmap.clear(); #ifndef SHIBSP_LITE - for (map< string,pair > >::iterator i=m_policyMap.begin(); i!=m_policyMap.end(); ++i) { - delete i->second.first; - for_each(i->second.second.begin(), i->second.second.end(), xmltooling::cleanup()); - } - m_policyMap.clear(); + delete m_policy; + m_policy = nullptr; #endif delete m_requestMapper; m_requestMapper = nullptr; diff --git a/shibsp/security/SecurityPolicyProvider.h b/shibsp/security/SecurityPolicyProvider.h new file mode 100644 index 0000000..32734f9 --- /dev/null +++ b/shibsp/security/SecurityPolicyProvider.h @@ -0,0 +1,116 @@ +/* + * Copyright 2010 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 shibsp/security/SecurityPolicyProvider.h + * + * Interface to a source of security policy settings and rules. + */ + +#ifndef __shibsp_policyfactory_h__ +#define __shibsp_policyfactory_h__ + +#ifndef SHIBSP_LITE + +#include + +#include +#include +#include + +namespace xmltooling { + class XMLTOOL_API QName; +}; + +namespace opensaml { + class SAML_API SecurityPolicy; + class SAML_API SecurityPolicyRule; +}; + +namespace shibsp { + + class SHIBSP_API Application; + class SHIBSP_API PropertySet; + + /** + * Interface to a source of security policy settings and rules. + */ + class SHIBSP_API SecurityPolicyProvider : public virtual xmltooling::Lockable + { + MAKE_NONCOPYABLE(SecurityPolicyProvider); + protected: + SecurityPolicyProvider(); + public: + virtual ~SecurityPolicyProvider(); + + /** + * Returns the security policy settings for an identified policy. + * + * @param id identifies the policy to return + * @return a PropertySet + */ + virtual const PropertySet* getPolicySettings(const char* id) const=0; + + /** + * Returns the security policy rules for an identified policy. + * + * @param id identifies the policy to return + * @return an array of policy rules + */ + virtual const std::vector& getPolicyRules(const char* id) const=0; + + /** + * Returns a set of XML Signature/Encryption algorithm identifiers to block. + * + * @return an array of algorithm URIs to block + */ + virtual const std::vector& getAlgorithmBlacklist() const=0; + + /** + * Returns a set of XML Signature/Encryption algorithm identifiers to permit. + * + * @return an array of algorithm URIs to permit + */ + virtual const std::vector& getAlgorithmWhitelist() const=0; + + /** + * Returns a SecurityPolicy applicable to an application and/or policy identifier. + * + *

The caller MUST lock the application's MetadataProvider for the life + * of the returned object. + * + * @param application reference to application applying policy + * @param role identifies the role (generally IdP or SP) of the policy peer + * @param policyId identifies policy, defaults to the application's default + * @return a new policy instance, which the caller is responsible for freeing + */ + virtual opensaml::SecurityPolicy* createSecurityPolicy( + const Application& application, const xmltooling::QName* role, const char* policyId=nullptr + ) const; + }; + + /** + * Registers SecurityPolicyProvider classes into the runtime. + */ + void SHIBSP_API registerSecurityPolicyProviders(); + + /** SecurityPolicyProvider based on an XML configuration format. */ + #define XML_SECURITYPOLICY_PROVIDER "XML" +}; + +#endif + +#endif /* __shibsp_policyfactory_h__ */ diff --git a/shibsp/shibsp.vcxproj b/shibsp/shibsp.vcxproj index 990b70f..c8906f5 100644 --- a/shibsp/shibsp.vcxproj +++ b/shibsp/shibsp.vcxproj @@ -181,6 +181,7 @@ + @@ -283,6 +284,7 @@ + diff --git a/shibsp/shibsp.vcxproj.filters b/shibsp/shibsp.vcxproj.filters index 0c89b89..47af548 100644 --- a/shibsp/shibsp.vcxproj.filters +++ b/shibsp/shibsp.vcxproj.filters @@ -366,6 +366,9 @@ Source Files\handler\impl + + Source Files\impl + @@ -518,6 +521,9 @@ Header Files\metadata + + Header Files\security + -- 2.1.4