X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fcpp-opensaml.git;a=blobdiff_plain;f=saml%2Fsaml2%2Fmetadata%2Fimpl%2FAbstractMetadataProvider.cpp;h=f2cca921cd4ea9d6cc59c8ee6a0d4b262b757307;hp=70360701add9855ee5051dfcd69c38a0c23fd084;hb=1462057b3b9ae7e165d34d988e30b14c213672ca;hpb=570c594bdbb64ae1f5f0b58b7559c1044d61e1f0 diff --git a/saml/saml2/metadata/impl/AbstractMetadataProvider.cpp b/saml/saml2/metadata/impl/AbstractMetadataProvider.cpp index 7036070..f2cca92 100644 --- a/saml/saml2/metadata/impl/AbstractMetadataProvider.cpp +++ b/saml/saml2/metadata/impl/AbstractMetadataProvider.cpp @@ -1,17 +1,21 @@ -/* - * Copyright 2001-2007 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 +/** + * Licensed to the University Corporation for Advanced Internet + * Development, Inc. (UCAID) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * UCAID licenses this file to you 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 + * 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. + * 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. */ /** @@ -27,73 +31,140 @@ #include "saml2/metadata/MetadataCredentialContext.h" #include "saml2/metadata/MetadataCredentialCriteria.h" +#include +#include +#include +#include #include +#include +#include +#include #include +#include +#include +#include #include using namespace opensaml::saml2md; +using namespace xmltooling::logging; using namespace xmltooling; +using namespace boost::lambda; +using namespace boost; using namespace std; using opensaml::SAMLArtifact; static const XMLCh _KeyInfoResolver[] = UNICODE_LITERAL_15(K,e,y,I,n,f,o,R,e,s,o,l,v,e,r); -static const XMLCh type[] = UNICODE_LITERAL_4(t,y,p,e); +static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e); AbstractMetadataProvider::AbstractMetadataProvider(const DOMElement* e) - : ObservableMetadataProvider(e), m_resolver(NULL), m_credentialLock(NULL) + : ObservableMetadataProvider(e), m_lastUpdate(0), m_resolver(nullptr), m_credentialLock(Mutex::create()) { - e = e ? XMLHelper::getFirstChildElement(e, _KeyInfoResolver) : NULL; + e = XMLHelper::getFirstChildElement(e, _KeyInfoResolver); if (e) { - auto_ptr_char t(e->getAttributeNS(NULL,type)); - if (t.get()) - m_resolver = XMLToolingConfig::getConfig().KeyInfoResolverManager.newPlugin(t.get(),e); - else + string t = XMLHelper::getAttrString(e, nullptr, _type); + if (!t.empty()) { + m_resolverWrapper.reset(XMLToolingConfig::getConfig().KeyInfoResolverManager.newPlugin(t.c_str(), e)); + m_resolver = m_resolverWrapper.get(); + } + else { throw UnknownExtensionException(" element found with no type attribute"); + } } - m_credentialLock = Mutex::create(); } AbstractMetadataProvider::~AbstractMetadataProvider() { for (credmap_t::iterator c = m_credentialMap.begin(); c!=m_credentialMap.end(); ++c) - for_each(c->second.begin(), c->second.end(), cleanup_pair()); - delete m_credentialLock; - delete m_resolver; + for_each(c->second.begin(), c->second.end(), xmltooling::cleanup()); } -void AbstractMetadataProvider::emitChangeEvent() +void AbstractMetadataProvider::outputStatus(ostream& os) const +{ + os << " 0) { + DateTime ts(m_lastUpdate); + ts.parseDateTime(); + auto_ptr_char timestamp(ts.getFormattedString()); + os << " lastUpdate='" << timestamp.get() << "'"; + } + + os << "/>"; +} + +void AbstractMetadataProvider::emitChangeEvent() const { for (credmap_t::iterator c = m_credentialMap.begin(); c!=m_credentialMap.end(); ++c) - for_each(c->second.begin(), c->second.end(), cleanup_pair()); + for_each(c->second.begin(), c->second.end(), xmltooling::cleanup()); m_credentialMap.clear(); ObservableMetadataProvider::emitChangeEvent(); } -void AbstractMetadataProvider::index(EntityDescriptor* site, time_t validUntil) +void AbstractMetadataProvider::emitChangeEvent(const EntityDescriptor& entity) const +{ + for (credmap_t::iterator c = m_credentialMap.begin(); c!=m_credentialMap.end(); ++c) + for_each(c->second.begin(), c->second.end(), xmltooling::cleanup()); + m_credentialMap.clear(); + ObservableMetadataProvider::emitChangeEvent(entity); +} + +void AbstractMetadataProvider::indexEntity(EntityDescriptor* site, time_t& validUntil, bool replace) const { + // If child expires later than input, reset child, otherwise lower input to match. if (validUntil < site->getValidUntilEpoch()) site->setValidUntil(validUntil); + else + validUntil = site->getValidUntilEpoch(); auto_ptr_char id(site->getEntityID()); if (id.get()) { - m_sites.insert(make_pair(id.get(),site)); + if (replace) { + // The data structure here needs work. + // We have to find all the sites stored against the replaced ID. Then we have to + // search for those sites in the entire set of sites tracked by the sources map and + // remove them from both places. + set existingSites; + pair existingRange = m_sites.equal_range(id.get()); + static pair::iterator,bool> (set::* ins)(const EntityDescriptor* const &) = + &set::insert; + for_each( + existingRange.first, existingRange.second, + lambda::bind(ins, boost::ref(existingSites), lambda::bind(&sitemap_t::value_type::second, _1)) + ); + m_sites.erase(existingRange.first, existingRange.second); + for (sitemap_t::iterator s = m_sources.begin(); s != m_sources.end();) { + if (existingSites.count(s->second) > 0) { + sitemap_t::iterator temp = s; + ++s; + m_sources.erase(temp); + } + else { + ++s; + } + } + } + m_sites.insert(sitemap_t::value_type(id.get(),site)); } // Process each IdP role. - const vector& roles=const_cast(site)->getIDPSSODescriptors(); - for (vector::const_iterator i=roles.begin(); i!=roles.end(); i++) { + const vector& roles = const_cast(site)->getIDPSSODescriptors(); + for (vector::const_iterator i = roles.begin(); i != roles.end(); i++) { // SAML 1.x? if ((*i)->hasSupport(samlconstants::SAML10_PROTOCOL_ENUM) || (*i)->hasSupport(samlconstants::SAML11_PROTOCOL_ENUM)) { // Check for SourceID extension element. - const Extensions* exts=(*i)->getExtensions(); + const Extensions* exts = (*i)->getExtensions(); if (exts && exts->hasChildren()) { - const vector& children=exts->getUnknownXMLObjects(); - for (vector::const_iterator ext=children.begin(); ext!=children.end(); ++ext) { - SourceID* sid=dynamic_cast(*ext); + const vector& children = exts->getUnknownXMLObjects(); + for (vector::const_iterator ext = children.begin(); ext != children.end(); ++ext) { + SourceID* sid = dynamic_cast(*ext); if (sid) { auto_ptr_char sourceid(sid->getID()); if (sourceid.get()) { - m_sources.insert(pair(sourceid.get(),site)); + m_sources.insert(sitemap_t::value_type(sourceid.get(),site)); break; } } @@ -101,95 +172,150 @@ void AbstractMetadataProvider::index(EntityDescriptor* site, time_t validUntil) } // Hash the ID. - m_sources.insert( - pair(SAMLConfig::getConfig().hashSHA1(id.get(), true),site) - ); + m_sources.insert(sitemap_t::value_type(SecurityHelper::doHash("SHA1", id.get(), strlen(id.get())),site)); // Load endpoints for type 0x0002 artifacts. - const vector& locs=const_cast(*i)->getArtifactResolutionServices(); - for (vector::const_iterator loc=locs.begin(); loc!=locs.end(); loc++) { + const vector& locs = const_cast(*i)->getArtifactResolutionServices(); + for (vector::const_iterator loc = locs.begin(); loc != locs.end(); loc++) { auto_ptr_char location((*loc)->getLocation()); if (location.get()) - m_sources.insert(pair(location.get(),site)); + m_sources.insert(sitemap_t::value_type(location.get(),site)); } } // SAML 2.0? if ((*i)->hasSupport(samlconstants::SAML20P_NS)) { // Hash the ID. - m_sources.insert( - pair(SAMLConfig::getConfig().hashSHA1(id.get(), true),site) - ); + m_sources.insert(sitemap_t::value_type(SecurityHelper::doHash("SHA1", id.get(), strlen(id.get())),site)); } } } -void AbstractMetadataProvider::index(EntitiesDescriptor* group, time_t validUntil) +void AbstractMetadataProvider::indexGroup(EntitiesDescriptor* group, time_t& validUntil) const { + // If child expires later than input, reset child, otherwise lower input to match. if (validUntil < group->getValidUntilEpoch()) group->setValidUntil(validUntil); + else + validUntil = group->getValidUntilEpoch(); auto_ptr_char name(group->getName()); if (name.get()) { - m_groups.insert(make_pair(name.get(),group)); + m_groups.insert(groupmap_t::value_type(name.get(),group)); } - const vector& groups=const_cast(group)->getEntitiesDescriptors(); - for (vector::const_iterator i=groups.begin(); i!=groups.end(); i++) - index(*i,group->getValidUntilEpoch()); + // Track the smallest validUntil amongst the children. + time_t minValidUntil = validUntil; - const vector& sites=const_cast(group)->getEntityDescriptors(); - for (vector::const_iterator j=sites.begin(); j!=sites.end(); j++) - index(*j,group->getValidUntilEpoch()); + const vector& groups = const_cast(group)->getEntitiesDescriptors(); + for (vector::const_iterator i = groups.begin(); i != groups.end(); i++) { + // Use the current validUntil fence for each child, but track the smallest we find. + time_t subValidUntil = validUntil; + indexGroup(*i, subValidUntil); + if (subValidUntil < minValidUntil) + minValidUntil = subValidUntil; + } + + const vector& sites = const_cast(group)->getEntityDescriptors(); + for (vector::const_iterator j = sites.begin(); j != sites.end(); j++) { + // Use the current validUntil fence for each child, but track the smallest we find. + time_t subValidUntil = validUntil; + indexEntity(*j, subValidUntil); + if (subValidUntil < minValidUntil) + minValidUntil = subValidUntil; + } + + // Pass back up the smallest child we found. + if (minValidUntil < validUntil) + validUntil = minValidUntil; } -void AbstractMetadataProvider::clearDescriptorIndex() +void AbstractMetadataProvider::index(EntityDescriptor* site, time_t validUntil, bool replace) const { - m_sources.clear(); + indexEntity(site, validUntil, replace); +} + +void AbstractMetadataProvider::index(EntitiesDescriptor* group, time_t validUntil) const +{ + indexGroup(group, validUntil); +} + +void AbstractMetadataProvider::clearDescriptorIndex(bool freeSites) +{ + if (freeSites) + for_each(m_sites.begin(), m_sites.end(), cleanup_const_pair()); m_sites.clear(); m_groups.clear(); + m_sources.clear(); } const EntitiesDescriptor* AbstractMetadataProvider::getEntitiesDescriptor(const char* name, bool strict) const { - pair range=m_groups.equal_range(name); + pair range=const_cast(m_groups).equal_range(name); - time_t now=time(NULL); + time_t now=time(nullptr); for (groupmap_t::const_iterator i=range.first; i!=range.second; i++) if (now < i->second->getValidUntilEpoch()) return i->second; - if (!strict && range.first!=range.second) - return range.first->second; - - return NULL; -} - -const EntityDescriptor* AbstractMetadataProvider::getEntityDescriptor(const char* name, bool strict) const -{ - pair range=m_sites.equal_range(name); + if (range.first != range.second) { + Category& log = Category::getInstance(SAML_LOGCAT ".MetadataProvider"); + if (strict) { + log.warn("ignored expired metadata group (%s)", range.first->first.c_str()); + } + else { + log.info("no valid metadata found, returning expired metadata group (%s)", range.first->first.c_str()); + return range.first->second; + } + } - time_t now=time(NULL); - for (sitemap_t::const_iterator i=range.first; i!=range.second; i++) - if (now < i->second->getValidUntilEpoch()) - return i->second; - - if (!strict && range.first!=range.second) - return range.first->second; - - return NULL; + return nullptr; } -const EntityDescriptor* AbstractMetadataProvider::getEntityDescriptor(const SAMLArtifact* artifact) const +pair AbstractMetadataProvider::getEntityDescriptor(const Criteria& criteria) const { - pair range=m_sources.equal_range(artifact->getSource()); - - time_t now=time(NULL); - for (sitemap_t::const_iterator i=range.first; i!=range.second; i++) - if (now < i->second->getValidUntilEpoch()) - return i->second; + pair range; + if (criteria.entityID_ascii) + range = const_cast(m_sites).equal_range(criteria.entityID_ascii); + else if (criteria.entityID_unicode) { + auto_ptr_char id(criteria.entityID_unicode); + range = const_cast(m_sites).equal_range(id.get()); + } + else if (criteria.artifact) + range = const_cast(m_sources).equal_range(criteria.artifact->getSource()); + else + return pair(nullptr,nullptr); + + pair result; + result.first = nullptr; + result.second = nullptr; + + time_t now=time(nullptr); + for (sitemap_t::const_iterator i=range.first; i!=range.second; i++) { + if (now < i->second->getValidUntilEpoch()) { + result.first = i->second; + break; + } + } + + if (!result.first && range.first!=range.second) { + Category& log = Category::getInstance(SAML_LOGCAT ".MetadataProvider"); + if (criteria.validOnly) { + log.warn("ignored expired metadata instance for (%s)", range.first->first.c_str()); + } + else { + log.info("no valid metadata found, returning expired instance for (%s)", range.first->first.c_str()); + result.first = range.first->second; + } + } - return NULL; + if (result.first && criteria.role) { + result.second = result.first->getRoleDescriptor(*criteria.role, criteria.protocol); + if (!result.second && criteria.protocol2) + result.second = result.first->getRoleDescriptor(*criteria.role, criteria.protocol2); + } + + return result; } const Credential* AbstractMetadataProvider::resolve(const CredentialCriteria* criteria) const @@ -202,9 +328,9 @@ const Credential* AbstractMetadataProvider::resolve(const CredentialCriteria* cr const credmap_t::mapped_type& creds = resolveCredentials(metacrit->getRole()); for (credmap_t::mapped_type::const_iterator c = creds.begin(); c!=creds.end(); ++c) - if (matches(*c,criteria)) - return c->second; - return NULL; + if (metacrit->matches(*(*c))) + return *c; +return nullptr; } vector::size_type AbstractMetadataProvider::resolve( @@ -218,42 +344,32 @@ vector::size_type AbstractMetadataProvider::resolve( Lock lock(m_credentialLock); const credmap_t::mapped_type& creds = resolveCredentials(metacrit->getRole()); - for (credmap_t::mapped_type::const_iterator c = creds.begin(); c!=creds.end(); ++c) - if (matches(*c,criteria)) - results.push_back(c->second); + for (credmap_t::mapped_type::const_iterator c = creds.begin(); c!=creds.end(); ++c) + if (metacrit->matches(*(*c))) + results.push_back(*c); return results.size(); } const AbstractMetadataProvider::credmap_t::mapped_type& AbstractMetadataProvider::resolveCredentials(const RoleDescriptor& role) const { credmap_t::const_iterator i = m_credentialMap.find(&role); - if (i!=m_credentialMap.end()) + if (i != m_credentialMap.end()) return i->second; const KeyInfoResolver* resolver = m_resolver ? m_resolver : XMLToolingConfig::getConfig().getKeyInfoResolver(); const vector& keys = role.getKeyDescriptors(); AbstractMetadataProvider::credmap_t::mapped_type& resolved = m_credentialMap[&role]; - for (vector::const_iterator k = keys.begin(); k!=keys.end(); ++k) { - if ((*k)->getKeyInfo()) { - auto_ptr mcc(new MetadataCredentialContext(*(*k))); - Credential* c = resolver->resolve(mcc.get()); - mcc.release(); - resolved.push_back(make_pair((*k)->getUse(), c)); + for (indirect_iterator::const_iterator> k = make_indirect_iterator(keys.begin()); + k != make_indirect_iterator(keys.end()); ++k) { + if (k->getKeyInfo()) { + auto_ptr mcc(new MetadataCredentialContext(*k)); + auto_ptr c(resolver->resolve(mcc.get())); + if (c.get()) { + mcc.release(); // this API sucks, the object is now owned by the Credential + resolved.push_back(c.get()); + c.release(); + } } } return resolved; } - -bool AbstractMetadataProvider::matches(const pair& cred, const CredentialCriteria* criteria) const -{ - if (criteria) { - // Check for a usage mismatch. - if ((criteria->getUsage()==CredentialCriteria::SIGNING_CREDENTIAL || criteria->getUsage()==CredentialCriteria::TLS_CREDENTIAL) && - XMLString::equals(cred.first,KeyDescriptor::KEYTYPE_ENCRYPTION)) - return false; - else if (criteria->getUsage()==CredentialCriteria::ENCRYPTION_CREDENTIAL && XMLString::equals(cred.first,KeyDescriptor::KEYTYPE_SIGNING)) - return false; - return cred.second->matches(*criteria); - } - return true; -}