Starting to refactor session cache, eliminated IConfig class.
[shibboleth/cpp-sp.git] / shib-target / shib-ccache.cpp
index c58fd78..bcbfa9d 100644 (file)
 # include <unistd.h>
 #endif
 
-#include <log4cpp/Category.hh>
-#include <shibsp/SPConfig.h>
-
 #include <ctime>
 #include <algorithm>
 #include <sstream>
 #include <stdexcept>
+#include <shibsp/SPConfig.h>
 
 #ifdef HAVE_LIBDMALLOCXX
 #include <dmalloc.h>
 
 using namespace shibsp;
 using namespace shibtarget;
-using namespace shibboleth;
 using namespace saml;
+using namespace opensaml::saml2md;
 using namespace xmltooling;
 using namespace log4cpp;
 using namespace std;
+using xmlsignature::CredentialResolver;
 
 static const XMLCh cleanupInterval[] =
 { chLatin_c, chLatin_l, chLatin_e, chLatin_a, chLatin_n, chLatin_u, chLatin_p,
@@ -176,7 +175,7 @@ public:
 
     string insert(
         const IApplication* application,
-        const IEntityDescriptor* source,
+        const RoleDescriptor* role,
         const char* client_addr,
         const SAMLSubject* subject,
         const char* authnContext,
@@ -200,7 +199,7 @@ StubCache::StubCache(const DOMElement* e) : m_log(&Category::getInstance(SHIBT_L
 
 string StubCache::insert(
     const IApplication* application,
-    const IEntityDescriptor* source,
+    const RoleDescriptor* role,
     const char* client_addr,
     const SAMLSubject* subject,
     const char* authnContext,
@@ -212,7 +211,7 @@ string StubCache::insert(
     in.structure();
     in.addmember("application_id").string(application->getId());
     in.addmember("client_address").string(client_addr);
-    auto_ptr_char provid(source->getId());
+    xmltooling::auto_ptr_char provid(dynamic_cast<EntityDescriptor*>(role->getParent())->getEntityID());
     in.addmember("provider_id").string(provid.get());
     in.addmember("major_version").integer(1);
     in.addmember("minor_version").integer(tokens->getMinorVersion());
@@ -225,10 +224,10 @@ string StubCache::insert(
     os << *tokens;
     in.addmember("tokens.unfiltered").string(os.str().c_str());
 
-    out=ShibTargetConfig::getConfig().getINI()->getListener()->send(in);
+    out=SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);
     if (out["key"].isstring())
         return out["key"].string();
-    throw InvalidSessionException("A remoted cache insertion operation did not return a usable session key.");
+    throw opensaml::RetryableProfileException("A remoted cache insertion operation did not return a usable session key.");
 }
 
 ISessionCacheEntry* StubCache::find(const char* key, const IApplication* application, const char* client_addr)
@@ -241,7 +240,7 @@ ISessionCacheEntry* StubCache::find(const char* key, const IApplication* applica
     in.addmember("client_address").string(client_addr);
     
     try {
-        out=ShibTargetConfig::getConfig().getINI()->getListener()->send(in);
+        out=SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);
         if (!out.isstruct()) {
             out.destroy();
             return NULL;
@@ -265,7 +264,7 @@ void StubCache::remove(const char* key, const IApplication* application, const c
     in.addmember("application_id").string(application->getId());
     in.addmember("client_address").string(client_addr);
     
-    ShibTargetConfig::getConfig().getINI()->getListener()->send(in);
+    SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);
 }
 
 /*
@@ -280,7 +279,7 @@ public:
         MemorySessionCache* cache,
         const char* key,
         const IApplication* application,
-        const IEntityDescriptor* source,
+        const RoleDescriptor* role,
         const char* client_addr,
         const SAMLSubject* subject,
         const char* authnContext,
@@ -290,7 +289,7 @@ public:
         MemorySessionCache* cache,
         const char* key,
         const IApplication* application,
-        const IEntityDescriptor* source,
+        const RoleDescriptor* role,
         const char* client_addr,
         const char* subject,
         const char* authnContext,
@@ -306,7 +305,7 @@ public:
     void unlock() { m_lock->unlock(); }
     
     HRESULT isValid(const IApplication* application, const char* client_addr) const;
-    void populate(const IApplication* application, const IEntityDescriptor* source, bool initial=false) const;
+    void populate(const IApplication* application, const EntityDescriptor* source, bool initial=false) const;
     bool checkApplication(const IApplication* application) { return (m_obj["application_id"]==application->getId()); }
     time_t created() const { return m_sessionCreated; }
     time_t lastAccess() const { return m_lastAccess; }
@@ -315,8 +314,8 @@ public:
 private:
     bool hasAttributes(const SAMLResponse& r) const;
     time_t calculateExpiration(const SAMLResponse& r) const;
-    pair<SAMLResponse*,SAMLResponse*> getNewResponse(const IApplication* application, const IEntityDescriptor* source) const;
-    SAMLResponse* filter(const SAMLResponse* r, const IApplication* application, const IEntityDescriptor* source) const;
+    pair<SAMLResponse*,SAMLResponse*> getNewResponse(const IApplication* application, const EntityDescriptor* source) const;
+    SAMLResponse* filter(const SAMLResponse* r, const IApplication* application, const RoleDescriptor* role) const;
   
     time_t m_sessionCreated;
     mutable time_t m_responseExpiration, m_lastAccess, m_lastRetry;
@@ -338,7 +337,7 @@ public:
 
     string insert(
         const IApplication* application,
-        const IEntityDescriptor* source,
+        const RoleDescriptor* role,
         const char* client_addr,
         const SAMLSubject* subject,
         const char* authnContext,
@@ -379,7 +378,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
     MemorySessionCache* cache,
     const char* key,
     const IApplication* application,
-    const IEntityDescriptor* source,
+    const RoleDescriptor* role,
     const char* client_addr,
     const SAMLSubject* subject,
     const char* authnContext,
@@ -393,7 +392,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
     m_obj.addmember("key").string(key);
     m_obj.addmember("client_address").string(client_addr);
     m_obj.addmember("application_id").string(application->getId());
-    auto_ptr_char pid(source->getId());
+    xmltooling::auto_ptr_char pid(dynamic_cast<EntityDescriptor*>(role->getParent())->getEntityID());
     m_obj.addmember("provider_id").string(pid.get());
     m_obj.addmember("major_version").integer(1);
     m_obj.addmember("minor_version").integer(tokens->getMinorVersion());
@@ -413,7 +412,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
 
     if (hasAttributes(*tokens)) {
         // Filter attributes in the response.
-        auto_ptr<SAMLResponse> filtered(filter(tokens, application, source));
+        auto_ptr<SAMLResponse> filtered(filter(tokens, application, role));
         
         // Calculate expiration.
         m_responseExpiration=calculateExpiration(*(filtered.get()));
@@ -440,7 +439,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
     }
 
     // Transaction Logging
-    auto_ptr_char hname(subject->getNameIdentifier()->getName());
+    xmltooling::auto_ptr_char hname(subject->getNameIdentifier()->getName());
     STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
     stc.getTransactionLog().infoStream() <<
         "New session (ID: " <<
@@ -461,7 +460,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
     MemorySessionCache* cache,
     const char* key,
     const IApplication* application,
-    const IEntityDescriptor* source,
+    const RoleDescriptor* role,
     const char* client_addr,
     const char* subject,
     const char* authnContext,
@@ -484,7 +483,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
     m_obj.addmember("key").string(key);
     m_obj.addmember("client_address").string(client_addr);
     m_obj.addmember("application_id").string(application->getId());
-    auto_ptr_char pid(source->getId());
+    xmltooling::auto_ptr_char pid(dynamic_cast<EntityDescriptor*>(role->getParent())->getEntityID());
     m_obj.addmember("provider_id").string(pid.get());
     m_obj.addmember("subject").string(subject);
     m_obj.addmember("authn_context").string(authnContext);
@@ -493,7 +492,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
     m_obj.addmember("minor_version").integer(minorVersion);
 
     if (hasAttributes(*(unfiltered.get()))) {
-        auto_ptr<SAMLResponse> filtered(filter(unfiltered.get(), application, source));
+        auto_ptr<SAMLResponse> filtered(filter(unfiltered.get(), application, role));
     
         // Calculate expiration.
         m_responseExpiration=calculateExpiration(*(filtered.get()));
@@ -528,7 +527,7 @@ MemorySessionCacheEntry::~MemorySessionCacheEntry()
 HRESULT MemorySessionCacheEntry::isValid(const IApplication* app, const char* client_addr) const
 {
 #ifdef _DEBUG
-    saml::NDC ndc("isValid");
+    xmltooling::NDC ndc("isValid");
 #endif
 
     // Obtain validation rules from application settings.
@@ -640,10 +639,10 @@ time_t MemorySessionCacheEntry::calculateExpiration(const SAMLResponse& r) const
     return expiration;
 }
 
-void MemorySessionCacheEntry::populate(const IApplication* application, const IEntityDescriptor* source, bool initial) const
+void MemorySessionCacheEntry::populate(const IApplication* application, const EntityDescriptor* source, bool initial) const
 {
 #ifdef _DEBUG
-    saml::NDC ndc("populate");
+    xmltooling::NDC ndc("populate");
 #endif
 
     // Do we have any attribute data cached?
@@ -659,10 +658,23 @@ void MemorySessionCacheEntry::populate(const IApplication* application, const IE
             if (FAILED(hr))
                 m_log->error("cache store failed to return updated tokens");
             else if (hr==NOERROR && tokensFromSink!=m_obj["tokens.unfiltered"].string()) {
+
+                // Bah...find role again.
+                const RoleDescriptor* role=source->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+                if (!role)
+                    role=source->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+                if (!role)
+                    role=source->getIDPSSODescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+                if (!role)
+                    role=source->getIDPSSODescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+                if (!role) {
+                    throw MetadataException("Unable to locate attribute-issuing role in metadata.");
+                }
+
                 // The tokens in the sink were different.
                 istringstream is(tokensFromSink);
                 auto_ptr<SAMLResponse> respFromSink(new SAMLResponse(is,m_obj["minor_version"].integer()));
-                auto_ptr<SAMLResponse> filteredFromSink(filter(respFromSink.get(),application,source));
+                auto_ptr<SAMLResponse> filteredFromSink(filter(respFromSink.get(),application,role));
                 time_t expFromSink=calculateExpiration(*(filteredFromSink.get()));
                 
                 // Recheck to see if the new tokens are valid.
@@ -756,10 +768,10 @@ void MemorySessionCacheEntry::populate(const IApplication* application, const IE
             stc.releaseTransactionLog();
         }
     }
-    catch (SAMLException&) {
+    catch (exception&) {
         if (m_cache->m_propagateErrors)
             throw;
-        m_log->warn("suppressed SAML exception caught while trying to fetch attributes");
+        m_log->warn("suppressed exception caught while trying to fetch attributes");
     }
 #ifndef _DEBUG
     catch (...) {
@@ -771,11 +783,11 @@ void MemorySessionCacheEntry::populate(const IApplication* application, const IE
 }
 
 pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse(
-    const IApplication* application, const IEntityDescriptor* source
+    const IApplication* application, const EntityDescriptor* source
     ) const
 {
 #ifdef _DEBUG
-    saml::NDC ndc("getNewResponse");
+    xmltooling::NDC ndc("getNewResponse");
 #endif
 
     // The retryInterval determines how often to poll an AA that might be down.
@@ -804,12 +816,12 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse(
     pair<bool,const XMLCh*> providerID=application->getXMLString("providerId");
     if (!providerID.first) {
         m_log->crit("unable to determine ProviderID for application, not set?");
-        throw SAMLException("Unable to determine ProviderID for application, not set?");
+        throw ConfigurationException("Unable to determine ProviderID for application, not set?");
     }
 
     // Try to locate an AA role.
-    const IAttributeAuthorityDescriptor* AA=source->getAttributeAuthorityDescriptor(
-        m_obj["minor_version"].integer()==1 ? saml::XML::SAML11_PROTOCOL_ENUM : saml::XML::SAML10_PROTOCOL_ENUM
+    const AttributeAuthorityDescriptor* AA=source->getAttributeAuthorityDescriptor(
+        m_obj["minor_version"].integer()==1 ? samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM
         );
     if (!AA) {
         m_log->warn("unable to locate metadata for identity provider's Attribute Authority");
@@ -843,8 +855,7 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse(
         // Build a SAML Request....
         SAMLAttributeQuery* q=new SAMLAttributeQuery(
             new SAMLSubject(nameid),
-            providerID.second,
-            application->getAttributeDesignators().clone()
+            providerID.second
             );
         auto_ptr<SAMLRequest> req(new SAMLRequest(q));
         req->setMinorVersion(m_obj["minor_version"].integer());
@@ -852,10 +863,11 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse(
         // Sign it?
         if (signRequest.first && signRequest.second && signingCred.first) {
             if (req->getMinorVersion()==1) {
-                Credentials creds(ShibTargetConfig::getConfig().getINI()->getCredentialsProviders());
-                const ICredResolver* cr=creds.lookup(signingCred.second);
-                if (cr)
+                CredentialResolver* cr=SPConfig::getConfig().getServiceProvider()->getCredentialResolver(signingCred.second);
+                if (cr) {
+                    xmltooling::Locker locker(cr);
                     req->sign(cr->getKey(),cr->getCertificates(),signatureAlg.second,digestAlg.second);
+                }
                 else
                     m_log->error("unable to sign attribute query, specified credential (%s) was not found",signingCred.second);
             }
@@ -867,42 +879,31 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse(
 
         // Call context object
         ShibHTTPHook::ShibHTTPHookCallContext ctx(credUse,AA);
-        Trust t(application->getTrustProviders());
         
         // Use metadata to locate endpoints.
-        Iterator<const IEndpoint*> endpoints=AA->getAttributeServiceManager()->getEndpoints();
-        while (!response && endpoints.hasNext()) {
-            const IEndpoint* ep=endpoints.next();
+        const vector<AttributeService*>& endpoints=AA->getAttributeServices();
+        for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) {
             try {
                 // Get a binding object for this protocol.
-                const SAMLBinding* binding = application->getBinding(ep->getBinding());
+                const SAMLBinding* binding = application->getBinding((*ep)->getBinding());
                 if (!binding) {
-                    auto_ptr_char prot(ep->getBinding());
+                    xmltooling::auto_ptr_char prot((*ep)->getBinding());
                     m_log->warn("skipping binding on unsupported protocol (%s)", prot.get());
                     continue;
                 }
                 static const XMLCh https[] = {chLatin_h, chLatin_t, chLatin_t, chLatin_p, chLatin_s, chColon, chNull};
-                auto_ptr<SAMLResponse> r(binding->send(ep->getLocation(), *(req.get()), &ctx));
+                auto_ptr<SAMLResponse> r(binding->send((*ep)->getLocation(), *(req.get()), &ctx));
                 if (r->isSigned()) {
-                    if (!t.validate(*r,AA))
-                        throw TrustException("Unable to verify signed response message.");
+                    // TODO: trust stuff will be changing anyway...
+                    //if (!t.validate(*r,AA))
+                    //    throw TrustException("Unable to verify signed response message.");
                 }
-                else if (!ctx.isAuthenticated() || XMLString::compareNString(ep->getLocation(),https,6))
-                    throw TrustException("Response message was unauthenticated.");
+                else if (!ctx.isAuthenticated() || XMLString::compareNString((*ep)->getLocation(),https,6))
+                    throw XMLSecurityException("Response message was unauthenticated.");
                 response = r.release();
             }
-            catch (SAMLException& e) {
-                m_log->error("caught SAML exception during SAML attribute query: %s", e.what());
-                // Check for shib:InvalidHandle error and propagate it out.
-                Iterator<saml::QName> codes=e.getCodes();
-                if (codes.size()>1) {
-                    const saml::QName& code=codes[1];
-                    if (!XMLString::compareString(code.getNamespaceURI(),shibboleth::Constants::SHIB_NS) &&
-                        !XMLString::compareString(code.getLocalName(), shibboleth::Constants::InvalidHandle)) {
-                        codes.reset();
-                        throw InvalidHandleException(e.what(),saml::params(),codes);
-                    }
-                }
+            catch (exception& e) {
+                m_log->error("caught exception during SAML attribute query: %s", e.what());
             }
         }
 
@@ -910,7 +911,7 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse(
             if (signedResponse.first && signedResponse.second && !response->isSigned()) {
                 delete response;
                 m_log->error("unsigned response obtained, but we were told it must be signed.");
-                throw TrustException("Unable to obtain a signed response message.");
+                throw XMLSecurityException("Unable to obtain a signed response message.");
             }
             
             // Iterate over the tokens and apply basic validation.
@@ -918,8 +919,8 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse(
             Iterator<SAMLAssertion*> assertions=response->getAssertions();
             for (unsigned int a=0; a<assertions.size();) {
                 // Discard any assertions not issued by the right entity.
-                if (XMLString::compareString(source->getId(),assertions[a]->getIssuer())) {
-                    auto_ptr_char bad(assertions[a]->getIssuer());
+                if (XMLString::compareString(source->getEntityID(),assertions[a]->getIssuer())) {
+                    xmltooling::auto_ptr_char bad(assertions[a]->getIssuer());
                     m_log->warn("discarding assertion not issued by (%s), instead by (%s)",m_obj["provider_id"].string(),bad.get());
                     response->removeAssertion(a);
                     continue;
@@ -927,22 +928,22 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse(
 
                 // Validate the token.
                 try {
-                    application->validateToken(assertions[a],now,AA,application->getTrustProviders());
+                    application->validateToken(assertions[a],now,AA,application->getTrustEngine());
                     a++;
                 }
-                catch (SAMLException&) {
+                catch (exception&) {
                     m_log->warn("assertion failed to validate, removing it from response");
                     response->removeAssertion(a);
                 }
             }
 
             // Run it through the filter.
-            return make_pair(response,filter(response,application,source));
+            return make_pair(response,filter(response,application,AA));
         }
     }
-    catch (SAMLException& e) {
-        m_log->error("caught SAML exception during query to AA: %s", e.what());
-        annotateException(&e,AA);
+    catch (exception& e) {
+        m_log->error("caught exception during query to AA: %s", e.what());
+        throw;
     }
     
     m_log->error("no response obtained");
@@ -950,11 +951,11 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse(
 }
 
 SAMLResponse* MemorySessionCacheEntry::filter(
-    const SAMLResponse* r, const IApplication* application, const IEntityDescriptor* source
+    const SAMLResponse* r, const IApplication* application, const RoleDescriptor* role
     ) const
 {
 #ifdef _DEBUG
-    saml::NDC ndc("filter");
+    xmltooling::NDC ndc("filter");
 #endif
 
     // Make a copy of the original and process that against the AAP.
@@ -965,11 +966,11 @@ SAMLResponse* MemorySessionCacheEntry::filter(
     for (unsigned long j=0; j < copies.size();) {
         try {
             // Finally, filter the content.
-            AAP::apply(application->getAAPProviders(),*(copies[j]),source);
+            shibboleth::AAP::apply(application->getAAPProviders(),*(copies[j]),role);
             j++;
 
         }
-        catch (SAMLException&) {
+        catch (exception&) {
             m_log->info("no statements remain after AAP, removing assertion");
             copy->removeAssertion(j);
         }
@@ -997,7 +998,7 @@ SAMLResponse* MemorySessionCacheEntry::filter(
                 Iterator<SAMLAttribute*> attrs=state ? state->getAttributes() : EMPTY(SAMLAttribute*);
                 while (attrs.hasNext()) {
                     SAMLAttribute* attr=attrs.next();
-                    auto_ptr_char attrname(attr->getName());
+                    xmltooling::auto_ptr_char attrname(attr->getName());
                     tran.infoStream() << "\t" << attrname.get() << " (" << attr->getValues().size() << " values)";
                 }
             }
@@ -1061,7 +1062,7 @@ MemorySessionCache::MemorySessionCache(const DOMElement* e)
     SAMLConfig::getConfig().conn_timeout = m_AAConnectTimeout;
 
     // Register for remoted messages.
-    ListenerService* listener=ShibTargetConfig::getConfig().getINI()->getListener();
+    ListenerService* listener=SPConfig::getConfig().getServiceProvider()->getListenerService(false);
     if (listener && SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
         restoreInsert=listener->regListener("SessionCache::insert",this);
         restoreFind=listener->regListener("SessionCache::find",this);
@@ -1083,7 +1084,7 @@ MemorySessionCache::~MemorySessionCache()
     cleanup_thread->join(NULL);
 
     // Unregister remoted messages.
-    ListenerService* listener=ShibTargetConfig::getConfig().getINI()->getListener();
+    ListenerService* listener=SPConfig::getConfig().getServiceProvider()->getListenerService(false);
     if (listener && SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
         listener->unregListener("SessionCache::insert",this,restoreInsert);
         listener->unregListener("SessionCache::find",this,restoreFind);
@@ -1149,16 +1150,17 @@ bool MemorySessionCache::setBackingStore(ISessionCacheStore* store)
 DDF MemorySessionCache::receive(const DDF& in)
 {
 #ifdef _DEBUG
-    saml::NDC ndc("receive");
+    xmltooling::NDC ndc("receive");
 #endif
 
     // Find application.
+    xmltooling::Locker confLocker(SPConfig::getConfig().getServiceProvider());
     const char* aid=in["application_id"].string();
-    const IApplication* app=aid ? ShibTargetConfig::getConfig().getINI()->getApplication(aid) : NULL;
+    const IApplication* app=aid ? dynamic_cast<const IApplication*>(SPConfig::getConfig().getServiceProvider()->getApplication(aid)) : NULL;
     if (!app) {
         // Something's horribly wrong.
         m_log->error("couldn't find application (%s) for session", aid ? aid : "(missing)");
-        throw SAMLException("Unable to locate application for session, deleted?");
+        throw ConfigurationException("Unable to locate application for session, deleted?");
     }
 
     if (!strcmp(in.name(),"SessionCache::find")) {
@@ -1177,7 +1179,7 @@ DDF MemorySessionCache::receive(const DDF& in)
             entry->unlock();
             return dup;
         }
-        catch (SAMLException&) {
+        catch (exception&) {
             remove(key,app,client_address);
             throw;
         }
@@ -1203,13 +1205,26 @@ DDF MemorySessionCache::receive(const DDF& in)
             throw SAMLException("Required parameters missing in call to SessionCache::insert");
         int minor=in["minor_version"].integer();
         
-        // Locate role descriptor to use in filtering.
-        Metadata m(app->getMetadataProviders());
-        const IEntityDescriptor* site=m.lookup(provider_id);
+        // Locate entity descriptor to use in filtering.
+        MetadataProvider* m=app->getMetadataProvider();
+        xmltooling::Locker locker(m);
+        const EntityDescriptor* site=m->getEntityDescriptor(provider_id);
         if (!site) {
             m_log->error("unable to locate issuing identity provider's metadata");
             throw MetadataException("Unable to locate identity provider's metadata.");
         }
+        const RoleDescriptor* role=site->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+        if (!role)
+            role=site->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+        if (!role)
+            role=site->getIDPSSODescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+        if (!role)
+            role=site->getIDPSSODescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+        if (!role) {
+            m_log->error("unable to locate attribute-issuing role in identity provider's metadata");
+            throw MetadataException("Unable to locate attribute-issuing role in identity provider's metadata.");
+        }
+
         // Deserialize XML for insert method.
         istringstream subis(subject);
         auto_ptr<SAMLSubject> pSubject(new SAMLSubject(subis));
@@ -1217,19 +1232,19 @@ DDF MemorySessionCache::receive(const DDF& in)
         auto_ptr<SAMLResponse> pTokens(new SAMLResponse(tokis,minor));
         
         // Insert the data and return the cache key.
-        string key=insert(app,site,client_address,pSubject.get(),authn_context,pTokens.get());
+        string key=insert(app,role,client_address,pSubject.get(),authn_context,pTokens.get());
         
         DDF out(NULL);
         out.structure();
         out.addmember("key").string(key.c_str());
         return out;
     }
-    throw ListenerException("Unsupported operation ($1)",saml::params(1,in.name()));
+    throw ListenerException("Unsupported operation ($1)",xmltooling::params(1,in.name()));
 }
 
 string MemorySessionCache::insert(
     const IApplication* application,
-    const IEntityDescriptor* source,
+    const RoleDescriptor* role,
     const char* client_addr,
     const SAMLSubject* subject,
     const char* authnContext,
@@ -1237,11 +1252,11 @@ string MemorySessionCache::insert(
     )
 {
 #ifdef _DEBUG
-    saml::NDC ndc("insert");
+    xmltooling::NDC ndc("insert");
 #endif
 
     SAMLIdentifier id;
-    auto_ptr_char key(id);
+    xmltooling::auto_ptr_char key(id);
 
     if (m_log->isDebugEnabled())
         m_log->debug("creating new cache entry for application %s: \"%s\"", application->getId(), key.get());
@@ -1251,20 +1266,20 @@ string MemorySessionCache::insert(
             this,
             key.get(),
             application,
-            source,
+            role,
             client_addr,
             subject,
             authnContext,
             tokens
             )
         );
-    entry->populate(application,source,true);
+    entry->populate(application,dynamic_cast<EntityDescriptor*>(role->getParent()),true);
 
     if (m_sink) {
         HRESULT hr=m_sink->onCreate(key.get(),application,entry.get(),1,tokens->getMinorVersion(),entry->created());
         if (FAILED(hr)) {
             m_log->error("cache store returned failure while storing new entry");
-            throw SAMLException(hr,"Unable to record new session in cache store.");
+            throw IOException("Unable to record new session in cache store.");
         }
     }
 
@@ -1278,7 +1293,7 @@ string MemorySessionCache::insert(
 ISessionCacheEntry* MemorySessionCache::find(const char* key, const IApplication* application, const char* client_addr)
 {
 #ifdef _DEBUG
-    saml::NDC ndc("find");
+    xmltooling::NDC ndc("find");
 #endif
 
     m_log->debug("searching memory cache for key (%s)", key);
@@ -1302,7 +1317,7 @@ ISessionCacheEntry* MemorySessionCache::find(const char* key, const IApplication
             m_log->error("cache store returned failure during search");
             return NULL;
         }
-        const IApplication* eapp=ShibTargetConfig::getConfig().getINI()->getApplication(appid.c_str());
+        const IApplication* eapp=dynamic_cast<const IApplication*>(SPConfig::getConfig().getServiceProvider()->getApplication(appid.c_str()));
         if (!eapp) {
             // Something's horribly wrong.
             m_log->error("couldn't find application (%s) for session", appid.c_str());
@@ -1313,20 +1328,35 @@ ISessionCacheEntry* MemorySessionCache::find(const char* key, const IApplication
         if (m_log->isDebugEnabled())
             m_log->debug("loading cache entry (ID: %s) back into memory for application (%s)", key, appid.c_str());
 
-        // Locate role descriptor to use in filtering.
-        Metadata m(eapp->getMetadataProviders());
-        const IEntityDescriptor* site=m.lookup(pid.c_str());
+        // Locate role to use in filtering.
+        MetadataProvider* m=eapp->getMetadataProvider();
+        xmltooling::Locker locker(m);
+        const EntityDescriptor* site=m->getEntityDescriptor(pid.c_str());
         if (!site) {
             m_log->error("unable to locate issuing identity provider's metadata");
             if (FAILED(m_sink->onDelete(key)))
                 m_log->error("cache store returned failure during delete");
             return NULL;
         }
+        const RoleDescriptor* role=site->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+        if (!role)
+            role=site->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+        if (!role)
+            role=site->getIDPSSODescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+        if (!role)
+            role=site->getIDPSSODescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+        if (!role) {
+            m_log->error("unable to locate attribute-issuing role in identity provider's metadata");
+            if (FAILED(m_sink->onDelete(key)))
+                m_log->error("cache store returned failure during delete");
+            return NULL;
+        }
+
         MemorySessionCacheEntry* entry = new MemorySessionCacheEntry(
             this,
             key,
             eapp,
-            site,
+            role,
             addr.c_str(),
             sub.c_str(),
             ac.c_str(),
@@ -1364,25 +1394,25 @@ ISessionCacheEntry* MemorySessionCache::find(const char* key, const IApplication
     try {
         HRESULT hr=i->second->isValid(application, client_addr);
         if (FAILED(hr)) {
-            Metadata m(application->getMetadataProviders());
+            MetadataProvider* m=application->getMetadataProvider();
+            xmltooling::Locker locker(m);
             switch (hr) {
                 case SESSION_E_EXPIRED: {
-                    InvalidSessionException ex(SESSION_E_EXPIRED, "Your session has expired, and you must re-authenticate.");
-                    annotateException(&ex,m.lookup(i->second->getProviderId())); // throws it
+                    opensaml::RetryableProfileException ex("Your session has expired, and you must re-authenticate.");
+                    annotateException(&ex,m->getEntityDescriptor(i->second->getProviderId(),false)); // throws it
                 }
                 
                 case SESSION_E_ADDRESSMISMATCH: {
-                    InvalidSessionException ex(
-                        SESSION_E_ADDRESSMISMATCH,
+                    opensaml::RetryableProfileException ex(
                         "Your IP address ($1) does not match the address recorded at the time the session was established.",
-                        saml::params(1,client_addr)
+                        xmltooling::params(1,client_addr)
                         );
-                    annotateException(&ex,m.lookup(i->second->getProviderId())); // throws it
+                    annotateException(&ex,m->getEntityDescriptor(i->second->getProviderId(),false)); // throws it
                 }
                 
                 default: {
-                    InvalidSessionException ex(hr, "Your session is invalid.");
-                    annotateException(&ex,m.lookup(i->second->getProviderId())); // throws it
+                    opensaml::RetryableProfileException ex("Your session is invalid.");
+                    annotateException(&ex,m->getEntityDescriptor(i->second->getProviderId(),false)); // throws it
                 }
             }
         }
@@ -1398,8 +1428,9 @@ ISessionCacheEntry* MemorySessionCache::find(const char* key, const IApplication
 
     try {
         // Make sure the entry has valid tokens.
-        Metadata m(application->getMetadataProviders());
-        i->second->populate(application,m.lookup(i->second->getProviderId()));
+        MetadataProvider* m=application->getMetadataProvider();
+        xmltooling::Locker locker(m);
+        i->second->populate(application,m->getEntityDescriptor(i->second->getProviderId()));
     }
     catch (...) {
         i->second->unlock();
@@ -1412,7 +1443,7 @@ ISessionCacheEntry* MemorySessionCache::find(const char* key, const IApplication
 void MemorySessionCache::remove(const char* key, const IApplication* application, const char* client_addr)
 {
 #ifdef _DEBUG
-    saml::NDC ndc("remove");
+    xmltooling::NDC ndc("remove");
 #endif
 
     m_log->debug("removing cache entry with key (%s)", key);
@@ -1453,7 +1484,7 @@ void MemorySessionCache::remove(const char* key, const IApplication* application
 void MemorySessionCache::dormant(const char* key)
 {
 #ifdef _DEBUG
-    saml::NDC ndc("dormant");
+    xmltooling::NDC ndc("dormant");
 #endif
 
     m_log->debug("purging old cache entry with key (%s)", key);
@@ -1490,7 +1521,7 @@ void MemorySessionCache::dormant(const char* key)
 void MemorySessionCache::cleanup()
 {
 #ifdef _DEBUG
-    saml::NDC ndc("cleanup()");
+    xmltooling::NDC ndc("cleanup()");
 #endif
 
     int rerun_timer = 0;
@@ -1574,7 +1605,7 @@ void* MemorySessionCache::cleanup_fcn(void* cache_p)
     return NULL;
 }
 
-IPlugIn* MemoryCacheFactory(const DOMElement* e)
+SessionCache* MemoryCacheFactory(const DOMElement* const & e)
 {
     // If this is a long-lived process, we return the "real" cache.
     if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess))