Moved property set classes to new library.
[shibboleth/cpp-sp.git] / xmlproviders / XMLTrust.cpp
index ec2c8bf..ef697da 100644 (file)
@@ -1,50 +1,17 @@
-/* 
- * The Shibboleth License, Version 1. 
- * Copyright (c) 2002 
- * University Corporation for Advanced Internet Development, Inc. 
- * All rights reserved
+/*
+ *  Copyright 2001-2005 Internet2
  * 
- * 
- * Redistribution and use in source and binary forms, with or without 
- * modification, are permitted provided that the following conditions are met:
- * 
- * Redistributions of source code must retain the above copyright notice, this 
- * list of conditions and the following disclaimer.
- * 
- * Redistributions in binary form must reproduce the above copyright notice, 
- * this list of conditions and the following disclaimer in the documentation 
- * and/or other materials provided with the distribution, if any, must include 
- * the following acknowledgment: "This product includes software developed by 
- * the University Corporation for Advanced Internet Development 
- * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
- * may appear in the software itself, if and wherever such third-party 
- * acknowledgments normally appear.
- * 
- * Neither the name of Shibboleth nor the names of its contributors, nor 
- * Internet2, nor the University Corporation for Advanced Internet Development, 
- * Inc., nor UCAID may be used to endorse or promote products derived from this 
- * software without specific prior written permission. For written permission, 
- * please contact shibboleth@shibboleth.org
- * 
- * Products derived from this software may not be called Shibboleth, Internet2, 
- * UCAID, or the University Corporation for Advanced Internet Development, nor 
- * may Shibboleth appear in their name, without prior written permission of the 
- * University Corporation for Advanced Internet Development.
- * 
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
- * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
- * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
- * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
- * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
- * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 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.
  */
 
 /* XMLTrust.cpp - a trust implementation that uses an XML file
@@ -57,6 +24,7 @@
 
 #include "internal.h"
 
+#include <algorithm>
 #include <sys/types.h>
 #include <sys/stat.h>
 
@@ -67,6 +35,7 @@
 #include <log4cpp/Category.hh>
 #include <xercesc/framework/URLInputSource.hpp>
 #include <xercesc/util/regx/RegularExpression.hpp>
+#include <xsec/enc/XSECCryptoException.hpp>
 #include <xsec/enc/XSECKeyInfoResolverDefault.hpp>
 
 using namespace shibboleth;
@@ -75,25 +44,17 @@ using namespace log4cpp;
 using namespace std;
 
 namespace {
-
-    int verify_callback(int ok, X509_STORE_CTX* store)
-    {
-        if (!ok)
-            Category::getInstance("OpenSSL").error(X509_verify_cert_error_string(store->error));
-        return ok;
-    }
-    
     class XMLTrustImpl : public ReloadableXMLFileImpl
     {
     public:
-        XMLTrustImpl(const char* pathname) : ReloadableXMLFileImpl(pathname) { init(); }
-        XMLTrustImpl(const DOMElement* e) : ReloadableXMLFileImpl(e) { init(); }
+        XMLTrustImpl(const char* pathname) : ReloadableXMLFileImpl(pathname), m_wildcard(NULL) { init(); }
+        XMLTrustImpl(const DOMElement* e) : ReloadableXMLFileImpl(e), m_wildcard(NULL) { init(); }
         void init();
         ~XMLTrustImpl();
         
         struct KeyAuthority
         {
-            KeyAuthority() : m_depth(0) {}
+            KeyAuthority() : m_depth(1) {}
             ~KeyAuthority();
             X509_STORE* getX509Store();
             
@@ -101,6 +62,7 @@ namespace {
             vector<const XMLCh*> m_subjects;
 #endif
             vector<X509*> m_certs;
+            vector<X509_CRL*> m_crls;
             unsigned short m_depth;
         };
         
@@ -118,51 +80,45 @@ namespace {
     class XMLTrust : public ITrust, public ReloadableXMLFile
     {
     public:
-        XMLTrust(const DOMElement* e) : ReloadableXMLFile(e) {}
-        ~XMLTrust() {}
+        XMLTrust(const DOMElement* e);
+        ~XMLTrust();
 
-    bool validate(
-        const saml::Iterator<IRevocation*>& revocations,
-        const IProviderRole* role, const saml::SAMLSignedObject& token,
-        const saml::Iterator<IMetadata*>& metadatas=EMPTY(IMetadata*)
-        ) const;
-    bool attach(const Iterator<IRevocation*>& revocations, const IProviderRole* role, void* ctx) const;
+    bool validate(void* certEE, const Iterator<void*>& certChain, const IRoleDescriptor* role, bool checkName=true);
+    bool validate(const saml::SAMLSignedObject& token, const IRoleDescriptor* role, ITrust* certValidator=NULL);
 
     protected:
-        virtual ReloadableXMLFileImpl* newImplementation(const char* pathname) const;
-        virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e) const;
-    };
+        virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
+        virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
 
+        vector<KeyInfoResolver*> m_resolvers;
+        ITrust* m_delegate;
+    };
 }
 
-extern "C" ITrust* XMLTrustFactory(const DOMElement* e)
+IPlugIn* XMLTrustFactory(const DOMElement* e)
 {
-    XMLTrust* t=new XMLTrust(e);
-    try {
-        t->getImplementation();
-    }
-    catch (...) {
-        delete t;
-        throw;
-    }
-    return t;    
+    auto_ptr<XMLTrust> t(new XMLTrust(e));
+    t->getImplementation();
+    return t.release();
 }
 
 
-ReloadableXMLFileImpl* XMLTrust::newImplementation(const char* pathname) const
+ReloadableXMLFileImpl* XMLTrust::newImplementation(const char* pathname, bool first) const
 {
     return new XMLTrustImpl(pathname);
 }
 
-ReloadableXMLFileImpl* XMLTrust::newImplementation(const DOMElement* e) const
+ReloadableXMLFileImpl* XMLTrust::newImplementation(const DOMElement* e, bool first) const
 {
     return new XMLTrustImpl(e);
 }
 
 X509_STORE* XMLTrustImpl::KeyAuthority::getX509Store()
 {
+#ifdef _DEBUG
     NDC ndc("getX509Store");
-    Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrust");
+#endif
+    Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Trust");
 
     // Load the cert vector into a store.
     X509_STORE* store=X509_STORE_new();
@@ -170,24 +126,33 @@ X509_STORE* XMLTrustImpl::KeyAuthority::getX509Store()
         log_openssl();
         return NULL;
     }
-    
-    X509_STORE_set_verify_cb_func(store,verify_callback);
+#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+    X509_STORE_set_flags(store,X509_V_FLAG_CRL_CHECK_ALL);
+#endif
 
     for (vector<X509*>::iterator j=m_certs.begin(); j!=m_certs.end(); j++) {
-        if (!X509_STORE_add_cert(store,X509_dup(*j))) {
+        if (!X509_STORE_add_cert(store,*j)) {
             log_openssl();
             log.warn("failed to add cert: %s", (*j)->name);
             continue;
         }
     }
 
+    for (vector<X509_CRL*>::iterator k=m_crls.begin(); k!=m_crls.end(); k++) {
+        if (!X509_STORE_add_crl(store,*k)) {
+            log_openssl();
+            log.warn("failed to add CRL");
+            continue;
+        }
+    }
+
     return store;
 }
 
 XMLTrustImpl::KeyAuthority::~KeyAuthority()
 {
-    for (vector<X509*>::iterator i=m_certs.begin(); i!=m_certs.end(); i++)
-        X509_free(*i);
+    for_each(m_certs.begin(),m_certs.end(),X509_free);
+    for_each(m_crls.begin(),m_crls.end(),X509_CRL_free);
 }
 
 class KeyInfoNodeFilter : public DOMNodeFilter
@@ -206,8 +171,10 @@ public:
 
 void XMLTrustImpl::init()
 {
-    NDC ndc("XMLTrustImpl");
-    Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrustImpl");
+#ifdef _DEBUG
+    saml::NDC ndc("init");
+#endif
+    Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Trust");
 
     try {
         if (!saml::XML::isElementNamed(m_root,::XML::TRUST_NS,SHIB_L(Trust))) {
@@ -217,29 +184,39 @@ void XMLTrustImpl::init()
 
         // Loop over the KeyAuthority elements.
         DOMNodeList* nlist=m_root->getElementsByTagNameNS(::XML::TRUST_NS,SHIB_L(KeyAuthority));
-        for (int i=0; nlist && i<nlist->getLength(); i++) {
-            KeyAuthority* ka=new KeyAuthority();
-            m_keyauths.push_back(ka);
+        for (unsigned int i=0; nlist && i<nlist->getLength(); i++) {
+            auto_ptr<KeyAuthority> ka(new KeyAuthority());
             
-            DOMElement* e=static_cast<DOMElement*>(nlist->item(i));
+            const DOMElement* e=static_cast<DOMElement*>(nlist->item(i));
             const XMLCh* depth=e->getAttributeNS(NULL,SHIB_L(VerifyDepth));
             if (depth && *depth)
                 ka->m_depth=XMLString::parseInt(depth);
             
+            const DOMElement* k_child=saml::XML::getLastChildElement(e,saml::XML::XMLSIG_NS,L(KeyInfo));
+            if (!k_child) {
+                log.error("ignoring KeyAuthority element with no ds:KeyInfo");
+                continue;
+            }
+            const DOMElement* badkeyname=saml::XML::getFirstChildElement(k_child,saml::XML::XMLSIG_NS,SHIB_L(KeyName));
+            if (badkeyname) {
+                log.error("ignoring KeyAuthority element with embedded ds:KeyName, these must appear only outside of ds:KeyInfo");
+                continue;
+            }
+            
             // Very rudimentary, grab up all the in-band X509Certificate elements, and flatten into one list.
-            DOMNodeList* certlist=e->getElementsByTagNameNS(saml::XML::XMLSIG_NS,L(X509Certificate));
-            for (int j=0; certlist && j<certlist->getLength(); j++) {
+            DOMNodeList* certlist=k_child->getElementsByTagNameNS(saml::XML::XMLSIG_NS,L(X509Certificate));
+            for (unsigned int j=0; certlist && j<certlist->getLength(); j++) {
                 auto_ptr_char blob(certlist->item(j)->getFirstChild()->getNodeValue());
                 X509* x=B64_to_X509(blob.get());
                 if (x)
                     ka->m_certs.push_back(x);
                 else
-                    log.warn("unable to create certificate from inline X509Certificate data");
+                    log.error("unable to create certificate from inline X509Certificate data");
             }
 
             // Now look for externally referenced objects.
-            certlist=e->getElementsByTagNameNS(saml::XML::XMLSIG_NS,SHIB_L(RetrievalMethod));
-            for (int k=0; certlist && k<certlist->getLength(); k++) {
+            certlist=k_child->getElementsByTagNameNS(saml::XML::XMLSIG_NS,SHIB_L(RetrievalMethod));
+            for (unsigned int k=0; certlist && k<certlist->getLength(); k++) {
                 DOMElement* cert=static_cast<DOMElement*>(certlist->item(k));
                 if (!XMLString::compareString(cert->getAttributeNS(NULL,SHIB_L(Type)),::XML::XMLSIG_RETMETHOD_RAWX509)) {
                     // DER format
@@ -255,24 +232,23 @@ void XMLTrustImpl::init()
                         else
                             log_openssl();
                     }
-                    log.warn("unable to create certificate from externally referenced file");
-                }
-                else if (!XMLString::compareString(cert->getAttributeNS(NULL,SHIB_L(Type)),::XML::SHIB_RETMETHOD_PEMX509)) {
-                    // PEM format
-                    int count=0;
-                    auto_ptr_char fname(cert->getAttributeNS(NULL,SHIB_L(URI)));
-                    FILE* f=fopen(fname.get(),"r");
-                    if (f) {
-                        X509* x=NULL;
-                        while (x=PEM_read_X509(f,NULL,NULL,NULL)) {
-                            ka->m_certs.push_back(x);
-                            count++;
-                        }
-                    }
-                    if (!count)
-                        log.warn("unable to create certificate from externally referenced file");
+                    log.error("unable to create certificate from externally referenced file");
                 }
             }
+
+            // Very rudimentary, grab up all the in-band X509CRL elements, and flatten into one list.
+            certlist=k_child->getElementsByTagNameNS(saml::XML::XMLSIG_NS,SHIB_L(X509CRL));
+            for (unsigned int r=0; certlist && r<certlist->getLength(); r++) {
+                auto_ptr_char blob(certlist->item(r)->getFirstChild()->getNodeValue());
+                X509_CRL* x=B64_to_CRL(blob.get());
+                if (x)
+                    ka->m_crls.push_back(x);
+                else
+                    log.warn("unable to create CRL from inline X509CRL data");
+            }
+
+            KeyAuthority* ka2=ka.release();
+            m_keyauths.push_back(ka2);
             
             // Now map the ds:KeyName values to the list of certs.
             bool wildcard=true;
@@ -282,9 +258,9 @@ void XMLTrustImpl::init()
                 if (name && *name) {
                     wildcard=false;
 #ifdef HAVE_GOOD_STL
-                    m_authMap[name]=ka;
+                    m_authMap[name]=ka2;
 #else
-                    ka->m_subjects.push_back(name);
+                    ka2->m_subjects.push_back(name);
 #endif
                 }
                 sub=saml::XML::getNextSiblingElement(sub,saml::XML::XMLSIG_NS,SHIB_L(KeyName));
@@ -292,8 +268,10 @@ void XMLTrustImpl::init()
             
             // If no Subjects, this is a catch-all binding.
             if (wildcard) {
-                if (!m_wildcard)
-                    m_wildcard=ka;
+                if (!m_wildcard) {
+                    log.warn("found a wildcard KeyAuthority element, make sure this is what you intend");
+                    m_wildcard=ka2;
+                }
                 else
                     log.warn("found multiple wildcard KeyAuthority elements, ignoring all but the first");
             }
@@ -311,13 +289,12 @@ void XMLTrustImpl::init()
             DSIGKeyInfoList* KIL = new DSIGKeyInfoList(NULL);
             // We let XMLSec hack through anything it can. This should evolve over time, or we can
             // plug in our own KeyResolver later...
-            DOMElement* child=saml::XML::getFirstChildElement(kidom);
-            int count2=1;
-            while (child) {
-                if (!KIL->addXMLKeyInfo(child))
-                    log.warn("skipped unsupported ds:KeyInfo child element (%d)",count2);
-                child=saml::XML::getNextSiblingElement(child);
-                count2++;
+            try {
+                if (!KIL->loadListFromXML(kidom))
+                    log.error("skipping ds:KeyInfo element (%d) containing unsupported children",count);
+            }
+            catch (XSECCryptoException& xe) {
+                log.error("unable to process ds:KeyInfo element (%d): %s",count,xe.getMsg());
             }
             
             // Dry run...can we resolve to a key?
@@ -353,202 +330,106 @@ void XMLTrustImpl::init()
     }
     catch (SAMLException& e) {
         log.errorStream() << "Error while parsing trust configuration: " << e.what() << CategoryStream::ENDLINE;
-        for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
-            delete (*i);
-        for (vector<DSIGKeyInfoList*>::iterator j=m_keybinds.begin(); j!=m_keybinds.end(); j++)
-            delete (*j);
+        this->~XMLTrustImpl();
         throw;
     }
+#ifndef _DEBUG
     catch (...) {
         log.error("Unexpected error while parsing trust configuration");
-        for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
-            delete (*i);
-        for (vector<DSIGKeyInfoList*>::iterator j=m_keybinds.begin(); j!=m_keybinds.end(); j++)
-            delete (*j);
+        this->~XMLTrustImpl();
         throw;
     }
+#endif
 }
 
 XMLTrustImpl::~XMLTrustImpl()
 {
-    for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
-        delete (*i);
-    for (vector<DSIGKeyInfoList*>::iterator j=m_keybinds.begin(); j!=m_keybinds.end(); j++)
-        delete (*j);
+    for_each(m_keyauths.begin(),m_keyauths.end(),xmltooling::cleanup<KeyAuthority>());
+    for_each(m_keybinds.begin(),m_keybinds.end(),xmltooling::cleanup<DSIGKeyInfoList>());
 }
 
-bool XMLTrust::attach(const Iterator<IRevocation*>& revocations, const IProviderRole* role, void* ctx) const
+XMLTrust::XMLTrust(const DOMElement* e) : ReloadableXMLFile(e), m_delegate(NULL)
 {
-    saml::NDC ndc("attach");
-    Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrust");
-    XMLTrustImpl* impl=dynamic_cast<XMLTrustImpl*>(getImplementation());
-
-    // Build a list of the names to match. We include any named KeyDescriptors, and the provider ID and its groups.
-    vector<const XMLCh*> names;
-    Iterator<const IKeyDescriptor*> kdlist=role->getKeyDescriptors();
-    while (kdlist.hasNext()) {
-        const IKeyDescriptor* kd=kdlist.next();
-        if (kd->getUse()!=IKeyDescriptor::signing)
-            continue;
-        DSIGKeyInfoList* kilist=kd->getKeyInfo();
-        for (size_t s=0; kilist && s<kilist->getSize(); s++) {
-            const XMLCh* n=kilist->item(s)->getKeyName();
-            if (n)
-                names.push_back(n);
-        }
-    }
-    names.push_back(role->getProvider()->getId());
-    Iterator<const XMLCh*> groups=role->getProvider()->getGroups();
-    while (groups.hasNext())
-        names.push_back(groups.next());
-
-    // Now check each name.
-    XMLTrustImpl::KeyAuthority* kauth=NULL;
-    for (vector<const XMLCh*>::const_iterator name=names.begin(); name!=names.end(); name++) {
-#ifdef HAVE_GOOD_STL
-        XMLTrustImpl::AuthMap::const_iterator c=impl->m_authMap.find(*name);
-        if (c!=impl->m_authMap.end()) {
-            kauth=c->second;
-            if (log.isDebugEnabled()) {
-                auto_ptr_char temp(*name);
-                log.debug("KeyAuthority match on %s",temp.get());
+    static const XMLCh resolver[] =
+    { chLatin_K, chLatin_e, chLatin_y, chLatin_I, chLatin_n, chLatin_f, chLatin_o,
+      chLatin_R, chLatin_e, chLatin_s, chLatin_o, chLatin_l, chLatin_v, chLatin_e, chLatin_r, chNull
+    };
+
+    static const XMLCh _type[] =
+    { chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull };
+
+    Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Trust");
+
+    // Find any KeyResolver plugins.
+    DOMElement* child=saml::XML::getFirstChildElement(e);
+    while (child) {
+        if (!XMLString::compareString(resolver,child->getLocalName()) && child->hasAttributeNS(NULL,_type)) {
+            try {
+                auto_ptr_char temp(child->getAttributeNS(NULL,_type));
+                m_resolvers.push_back(KeyInfoResolver::getInstance(temp.get(),child));
             }
-        }
-#else
-        // Without a decent STL, we trade-off the transcoding by doing a linear search.
-        for (vector<XMLTrustImpl::KeyAuthority*>::const_iterator keyauths=impl->m_keyauths.begin(); keyauths!=impl->m_keyauths.end(); keyauths++) {
-            for (vector<const XMLCh*>::const_iterator subs=keyauths->m_subjects.begin(); subs!=keyauths->m_subjects.end(); subs++) {
-                if (!XMLString::compareString(*name,*subs)) {
-                    kauth=*keyauths;
-                    if (log.isDebugEnabled()) {
-                        auto_ptr_char temp(*name);
-                        log.debug("KeyAuthority match on %s",temp.get());
-                    }
-                }
+            catch (SAMLException& ex) {
+                log.error("caught SAML exception building KeyInfoResolver plugin: %s",ex.what());
+            }
+#ifndef _DEBUG
+            catch (...) {
+                log.error("caught unknown exception building KeyInfoResolver plugin");
             }
-        }
 #endif
-    }
-
-    if (!kauth) {
-        if (impl->m_wildcard) {
-           log.warn("applying wildcard KeyAuthority, use with caution!");
-            kauth=impl->m_wildcard;
-        }
-        else {
-            log.error("no KeyAuthority found to validate SSL connection, leaving it alone");
-            return false;
         }
+        child=saml::XML::getNextSiblingElement(child);
     }
+    m_resolvers.push_back(KeyInfoResolver::getInstance(e));
 
-    // If we have a match, use the associated keyauth.
-    X509_STORE* store=kauth->getX509Store();
-    if (store) {
-      
-        // Add any relevant CRLs.
-        log.debug("obtaining CRLs for this provider/role");
-        Revocation rev(revocations);
-        Iterator<void*> crls=rev.getRevocationLists(role->getProvider(),role);
-        while (crls.hasNext()) {
-            if (!X509_STORE_add_crl(store,X509_CRL_dup(reinterpret_cast<X509_CRL*>(crls.next())))) {
-                log_openssl();
-                log.warn("failed to add CRL");
-            }
+    try {
+        IPlugIn* plugin=SAMLConfig::getConfig().getPlugMgr().newPlugin(
+            "edu.internet2.middleware.shibboleth.common.provider.ShibbolethTrust",e
+            );
+        m_delegate=dynamic_cast<ITrust*>(plugin);
+        if (!m_delegate) {
+            delete plugin;
+            log.error("plugin was not a trust provider");
+            throw UnsupportedExtensionException("Legacy trust provider requires Shibboleth trust provider in order to function.");
         }
-        
-        // Apply store to this context.
-        SSL_CTX_set_cert_store(reinterpret_cast<SSL_CTX*>(ctx),store);
-        SSL_CTX_set_verify_depth(reinterpret_cast<SSL_CTX*>(ctx),kauth->m_depth);
-        
     }
-    return true;
+    catch (SAMLException& ex) {
+        log.error("caught SAML exception building embedded trust provider: %s", ex.what());
+        throw;
+    }
 }
 
-bool XMLTrust::validate(
-    const saml::Iterator<IRevocation*>& revocations,
-    const IProviderRole* role, const saml::SAMLSignedObject& token,
-    const saml::Iterator<IMetadata*>& metadatas
-    ) const
+XMLTrust::~XMLTrust()
 {
-    NDC ndc("validate");
-    Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrust");
-    XMLTrustImpl* impl=dynamic_cast<XMLTrustImpl*>(getImplementation());
+    delete m_delegate;
+    for_each(m_resolvers.begin(),m_resolvers.end(),xmltooling::cleanup<KeyInfoResolver>());
+}
 
-    // This is where we're going to hide all the juicy SAML trust bits. If we botch it
-    // we can just plug in a new version, hopefully.
+static int error_callback(int ok, X509_STORE_CTX* ctx)
+{
+    if (!ok)
+        Category::getInstance("OpenSSL").error("path validation failure: %s", X509_verify_cert_error_string(ctx->error));
+    return ok;
+}
 
-    Metadata metadata(metadatas);   // With luck we won't need this.
+bool XMLTrust::validate(void* certEE, const Iterator<void*>& certChain, const IRoleDescriptor* role, bool checkName)
+{
+    // The delegated trust plugin handles path validation with metadata extensions.
+    // We only take over if the legacy format has to kick in.
+    if (m_delegate->validate(certEE,certChain,role,checkName))
+        return true;
 
-    // Did the caller tell us about the signer?
-    const IProvider* provider=(role ? role->getProvider() : NULL);
-    if (!provider) {
-        log.debug("no role descriptor passed in, trying to map token to provider");
-        
-        // The first step is to identify the provider responsible for signing the token.
-        // We can't narrow it down to role, because we don't know why the token is being validated.
-        
-        // If it's an assertion, this isn't terribly hard, but we need to hack in support for both
-        // Issuer and NameQualifier as a provider ID. Issuer will be the main one going forward.
-        // 1.0/1.1 origins will be sending a hostname as Issuer, but this won't hit the metadata lookup
-        // and we'll fall back to NameQualifier. Non-Shib SAML origins generally would be based on Issuer.
-        
-        // Responses allow us to try and locate a provider by checking the assertion(s) inside.
-        // Technically somebody could enclose somebody else's assertions, but if the use case is
-        // that advanced, we're probably into SAML 2.0 and we'll have Issuer up top.
-        
-        // Requests...umm, pretty much out of luck. We'll apply our own hack if there's an
-        // attribute query, and use Resource.
-        
-        if (typeid(token)==typeid(SAMLResponse)) {
-            Iterator<SAMLAssertion*> assertions=dynamic_cast<const SAMLResponse&>(token).getAssertions();
-            while (!provider && assertions.hasNext()) {
-                SAMLAssertion* assertion=assertions.next();
-                provider=metadata.lookup(assertion->getIssuer());
-                if (!provider) {
-                    Iterator<SAMLStatement*> statements=assertion->getStatements();
-                    while (!provider && statements.hasNext()) {
-                        SAMLSubjectStatement* statement=dynamic_cast<SAMLSubjectStatement*>(statements.next());
-                        if (statement && statement->getSubject()->getNameQualifier())
-                            provider=metadata.lookup(statement->getSubject()->getNameQualifier());
-                    }
-                }
-            }
-        }
-        else if (typeid(token)==typeid(SAMLAssertion)) {
-            provider=metadata.lookup(dynamic_cast<const SAMLAssertion&>(token).getIssuer());
-            if (!provider) {
-                Iterator<SAMLStatement*> statements=dynamic_cast<const SAMLAssertion&>(token).getStatements();
-                while (!provider && statements.hasNext()) {
-                    SAMLSubjectStatement* statement=dynamic_cast<SAMLSubjectStatement*>(statements.next());
-                    if (statement && statement->getSubject()->getNameQualifier())
-                        provider=metadata.lookup(statement->getSubject()->getNameQualifier());
-                }
-            }
-        }
-        else if (typeid(token)==typeid(SAMLRequest)) {
-            const SAMLQuery* q=dynamic_cast<const SAMLRequest&>(token).getQuery();
-            if (q && dynamic_cast<const SAMLAttributeQuery*>(q))
-                provider=metadata.lookup(dynamic_cast<const SAMLAttributeQuery*>(q)->getResource());
-        }
+#ifdef _DEBUG
+    saml::NDC ndc("validate");
+#endif
+    Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Trust");
+
+    if (checkName) {
+        // Before we do the cryptogprahy, check that the EE certificate "name" matches
+        // one of the acceptable key "names" for the signer.
+        vector<string> keynames;
         
-        // If we still don't have a provider, there's no likely basis for trust,
-        // but a wildcard KeyAuthority might apply.
-        if (log.isInfoEnabled() && provider) {
-            auto_ptr_char temp(provider->getId());
-            log.info("mapped signed token to provider: %s", temp.get());
-        }
-        else if (!provider)
-            log.warn("unable to map signed token to provider, only wildcarded trust will apply");
-    }
-    
-    vector<const XMLCh*> names;
-    XSECKeyInfoResolverDefault keyResolver;
-    
-    // First, try to resolve a KeyDescriptor from the role into an actual key.
-    // That's the simplest case. Failing that, remember any key names we run across.
-    
-    if (role) {
-        log.debug("checking for key descriptors that resolve directly");
+        // Build a list of acceptable names. Transcode the possible key "names" to UTF-8.
+        // For some simple cases, this should handle UTF-8 encoded DNs in certificates.
         Iterator<const IKeyDescriptor*> kd_i=role->getKeyDescriptors();
         while (kd_i.hasNext()) {
             const IKeyDescriptor* kd=kd_i.next();
@@ -557,330 +438,342 @@ bool XMLTrust::validate(
             DSIGKeyInfoList* KIL=kd->getKeyInfo();
             if (!KIL)
                 continue;
-            XSECCryptoKey* key=keyResolver.resolveKey(KIL);
-            if (key) {
-                log.debug("found an inline key, trying it...");
-                try {
-                    token.verify(key);
-                    log.info("token verified with inline key, nothing more to verify");
-                    return true;
-                }
-                catch (SAMLException& e) {
-                    log.debug("inline key failed: %s", e.what());
-                }
-            }
-            else {
-                for (size_t s=0; s<KIL->getSize(); s++) {
-                    const XMLCh* n=KIL->item(s)->getKeyName();
-                    if (n)
-                        names.push_back(n);
-                }
-            }
-        }
-    }
-    
-    // Push the provider ID on the key name list. We don't push provider groups in, since
-    // matching groups to a key makes no sense.
-    if (provider)
-        names.push_back(provider->getId());
-    
-    // No keys inline in metadata. Now we try and find a key inline in trust.
-    log.debug("checking for keys in trust file");
-    DSIGKeyInfoList* KIL=NULL;
-    for (vector<const XMLCh*>::const_iterator name=names.begin(); name!=names.end(); name++) {
-#ifdef HAVE_GOOD_STL
-        XMLTrustImpl::BindMap::const_iterator c=impl->m_bindMap.find(*name);
-        if (c!=impl->m_bindMap.end()) {
-            KIL=c->second;
-            if (log.isDebugEnabled()) {
-                auto_ptr_char temp(*name);
-                log.debug("KeyInfo match on %s",temp.get());
-            }
-        }
-#else
-        // Without a decent STL, we trade-off the transcoding by doing a linear search.
-        for (vector<XMLTrustImpl::DSIGKeyInfoList*>::const_iterator keybinds=impl->m_keybinds.begin(); keybinds!=impl->m_keybinds.end(); keybinds++) {
-            for (size_t s=0; s<(*keybinds)->getSize(); s++) {
-                if (!XMLString::compareString(*name,(*keybinds)->item(s)->getKeyName())) {
-                    KIL=*keybinds;
-                    if (log.isDebugEnabled()) {
-                        auto_ptr_char temp(*name);
-                        log.debug("KeyInfo match on %s",temp.get());
-                    }
+            for (size_t s=0; s<KIL->getSize(); s++) {
+                const XMLCh* n=KIL->item(s)->getKeyName();
+                if (n) {
+                    auto_ptr<char> kn(toUTF8(n));
+                    keynames.push_back(kn.get());
                 }
             }
         }
-#endif
-    }
-    
-    if (KIL) {
-        // Any inline KeyInfo should ostensible resolve to a key we can try.
-        XSECCryptoKey* key=keyResolver.resolveKey(KIL);
-        if (key) {
-            log.debug("resolved key, trying it...");
-            try {
-                token.verify(key);
-                log.info("token verified with KeyInfo, nothing more to verify");
-                return true;
-            }
-            catch (SAMLException& e) {
-                log.debug("inline key failed: %s", e.what());
-            }
-        }
-        else
-            log.warn("KeyInfo in trust provider did not resolve to a key");
-    }
-    
-    // Direct key verification hasn't worked. Now we have to switch over to KeyAuthority-based
-    // validation. The actual verification key has to be inside the token.
-    log.debug("verifying signature using key inside token...");
-    try {
-        token.verify();
-        log.info("verified with key inside token, entering validation stage");
-    }
-    catch (SAMLException& e) {
-        log.debug("verification using key inside token failed: %s", e.what());
-        return false;
-    }
-    
-    // Before we do the cryptogprahy, check that the EE certificate "name" matches
-    // one of the acceptable key "names" for the signer. Without this, we have a gaping
-    // hole in the validation.
-    log.debug("matching token's certificate subject against valid key names...");
-    vector<const XMLCh*> certs;
-    for (unsigned int i=0; i<token.getX509CertificateCount(); i++)
-        certs.push_back(token.getX509Certificate(i));
-
-    // Decode the EE cert.
-    auto_ptr_char EE(certs[0]);
-    X509* x=B64_to_X509(EE.get());
-    if (!x) {
-        log.error("unable to decode X.509 signing certificate");
-        return false;
-    }
-    
-    // Transcode the possible key "names" to UTF-8. For some simple cases, this should
-    // handle UTF-8 encoded DNs in certificates.
-    vector<string> keynames;
-    Iterator<const XMLCh*> iname(names);
-    while (iname.hasNext()) {
-        auto_ptr<char> kn(toUTF8(iname.next()));
+        auto_ptr<char> kn(toUTF8(role->getEntityDescriptor()->getId()));
         keynames.push_back(kn.get());
-    }
-    
-    bool match=false;
-    char buf[256];
-    X509_NAME* subject=X509_get_subject_name(x);
-    if (subject) {
-        // The best way is a direct match to the subject DN. We should encourage this.
-        // Seems that the way to do the compare is to write the X509_NAME into a BIO.
-        // Believe this will give us RFC 2253 / LDAP syntax...
-        BIO* b = BIO_new(BIO_s_mem());
-        if (b) {
+        
+        char buf[256];
+        X509* x=(X509*)certEE;
+        X509_NAME* subject=X509_get_subject_name(x);
+        if (subject) {
+            // One way is a direct match to the subject DN.
+            // Seems that the way to do the compare is to write the X509_NAME into a BIO.
+            BIO* b = BIO_new(BIO_s_mem());
+            BIO* b2 = BIO_new(BIO_s_mem());
             BIO_set_mem_eof_return(b, 0);
-            // The DN_REV flag gives us LDAP order instead of X.500
-            int len=X509_NAME_print_ex(b,subject,0,XN_FLAG_SEP_COMMA_PLUS|XN_FLAG_DN_REV);
-            if (len) {
-                BIO_flush(b);
-                string subjectstr;
-                while ((len = BIO_read(b, buf, 255)) > 0) {
-                    buf[len] = '\0';
-                    subjectstr+=buf;
-                }
-                log.infoStream() << "certificate subject: " << subjectstr << CategoryStream::ENDLINE;
-                // Check each keyname.
-                for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
+            BIO_set_mem_eof_return(b2, 0);
+            // The flags give us LDAP order instead of X.500, with a comma separator.
+            int len=X509_NAME_print_ex(b,subject,0,XN_FLAG_RFC2253);
+            string subjectstr,subjectstr2;
+            BIO_flush(b);
+            while ((len = BIO_read(b, buf, 255)) > 0) {
+                buf[len] = '\0';
+                subjectstr+=buf;
+            }
+            log.infoStream() << "certificate subject: " << subjectstr << CategoryStream::ENDLINE;
+            // The flags give us LDAP order instead of X.500, with a comma plus space separator.
+            len=X509_NAME_print_ex(b2,subject,0,XN_FLAG_RFC2253 + XN_FLAG_SEP_CPLUS_SPC - XN_FLAG_SEP_COMMA_PLUS);
+            BIO_flush(b2);
+            while ((len = BIO_read(b2, buf, 255)) > 0) {
+                buf[len] = '\0';
+                subjectstr2+=buf;
+            }
+            
+            // Check each keyname.
+            for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
 #ifdef HAVE_STRCASECMP
-                    if (!strcasecmp(n->c_str(),subjectstr.c_str())) {
+                if (!strcasecmp(n->c_str(),subjectstr.c_str()) || !strcasecmp(n->c_str(),subjectstr2.c_str())) {
 #else
-                    if (!stricmp(n->c_str(),subjectstr.c_str())) {
+                if (!_stricmp(n->c_str(),subjectstr.c_str()) || !_stricmp(n->c_str(),subjectstr2.c_str())) {
 #endif
-                        log.info("matched full subject DN to a key name");
-                        match=true;
-                        break;
-                    }
+                    log.info("matched full subject DN to a key name (%s)", n->c_str());
+                    checkName=false;
+                    break;
                 }
             }
-            else
-                log.error("certificate has no subject?!");
             BIO_free(b);
-        }
-        else
-            log.error("unable to obtain memory BIO from OpenSSL");
-        
-        if (!match) {
-            log.debug("unable to match DN, trying TLS-style hostname match");
-            memset(buf,0,sizeof(buf));
-            if (X509_NAME_get_text_by_NID(subject,NID_commonName,buf,255)>0) {
-                for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
+            BIO_free(b2);
+
+            if (checkName) {
+                log.debug("unable to match DN, trying TLS subjectAltName match");
+                STACK_OF(GENERAL_NAME)* altnames=(STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
+                if (altnames) {
+                    int numalts = sk_GENERAL_NAME_num(altnames);
+                    for (int an=0; !checkName && an<numalts; an++) {
+                        const GENERAL_NAME* check = sk_GENERAL_NAME_value(altnames, an);
+                        if (check->type==GEN_DNS || check->type==GEN_URI) {
+                            const char* altptr = (char*)ASN1_STRING_data(check->d.ia5);
+                            const int altlen = ASN1_STRING_length(check->d.ia5);
+                            
+                            for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
 #ifdef HAVE_STRCASECMP
-                    if (!strcasecmp(buf,n->c_str())) {
+                                if (!strncasecmp(altptr,n->c_str(),altlen)) {
 #else
-                    if (!stricmp(buf,n->c_str())) {
+                                if (!_strnicmp(altptr,n->c_str(),altlen)) {
 #endif
-                        log.info("matched subject CN to a key name");
-                        match=true;
-                        break;
+                                    log.info("matched DNS/URI subjectAltName to a key name (%s)", n->c_str());
+                                    checkName=false;
+                                    break;
+                                }
+                            }
+                        }
                     }
+                    GENERAL_NAMES_free(altnames);
                 }
-            }
-            else
-                log.warn("no common name in certificate subject");
-            
-            if (!match) {
-                log.debug("unable to match CN, trying DNS subjectAltName");
-                int extcount=X509_get_ext_count(x);
-                for (int c=0; c<extcount; c++) {
-                    X509_EXTENSION* ext=X509_get_ext(x,c);
-                    const char* extstr=OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
-                    if (!strcmp(extstr,"subjectAltName")) {
-                        X509V3_EXT_METHOD* meth=X509V3_EXT_get(ext);
-                        if (!meth || !meth->d2i || !meth->i2v || !ext->value->data) // had to add all these to prevent crashing
-                            break;
-                        unsigned char* data=ext->value->data;
-                        STACK_OF(CONF_VALUE)* val=meth->i2v(meth,meth->d2i(NULL,&data,ext->value->length),NULL);
-                        for (int j=0; j<sk_CONF_VALUE_num(val); j++) {
-                            CONF_VALUE* nval=sk_CONF_VALUE_value(val,j);
-                            if (!strcmp(nval->name,"DNS")) {
-                                for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
+                
+                if (checkName) {
+                    log.debug("unable to match subjectAltName, trying TLS CN match");
+                    memset(buf,0,sizeof(buf));
+                    if (X509_NAME_get_text_by_NID(subject,NID_commonName,buf,255)>0) {
+                        for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
 #ifdef HAVE_STRCASECMP
-                                    if (!strcasecmp(nval->value,n->c_str())) {
+                            if (!strcasecmp(buf,n->c_str())) {
 #else
-                                    if (!stricmp(nval->value,n->c_str())) {
+                            if (!_stricmp(buf,n->c_str())) {
 #endif
-                                        log.info("matched DNS subjectAltName to a key name");
-                                        match=true;
-                                        break;
-                                    }
-                                }
+                                log.info("matched subject CN to a key name (%s)", n->c_str());
+                                checkName=false;
+                                break;
                             }
                         }
                     }
+                    else
+                        log.warn("no common name in certificate subject");
                 }
             }
         }
+        else
+            log.error("certificate has no subject?!");
     }
-    else
-        log.error("certificate has no subject?!");
 
-    X509_free(x);
-
-    if (!match) {
-        log.error("cannot match certificate subject against provider's key names");
+    if (checkName) {
+        log.error("cannot match certificate subject against acceptable key names based on KeyDescriptors");
         return false;
     }
 
-    // We're ready for the final stage.
-    log.debug("final step, certificate path validation...");
-
-    // Push any provider groups on the name match list.
-    if (provider) {
-        Iterator<const XMLCh*> groups=provider->getGroups();
-        while (groups.hasNext())
-            names.push_back(groups.next());
-    }
-
-    // Now we hunt the list for a KeyAuthority that matches one of the names.
-    XMLTrustImpl::KeyAuthority* kauth=NULL;
-    for (vector<const XMLCh*>::const_iterator name2=names.begin(); name2!=names.end(); name2++) {
-#ifdef HAVE_GOOD_STL
-        XMLTrustImpl::AuthMap::const_iterator c=impl->m_authMap.find(*name2);
-        if (c!=impl->m_authMap.end()) {
-            kauth=c->second;
-            if (log.isDebugEnabled()) {
-                auto_ptr_char temp(*name2);
-                log.debug("KeyAuthority match on %s",temp.get());
+    lock();
+    try {
+        XMLTrustImpl* impl=dynamic_cast<XMLTrustImpl*>(getImplementation());
+    
+        // Build a list of the names to match. We include any named KeyDescriptors, and the provider ID and its groups.
+        vector<const XMLCh*> names;
+        Iterator<const IKeyDescriptor*> kdlist=role->getKeyDescriptors();
+        while (kdlist.hasNext()) {
+            const IKeyDescriptor* kd=kdlist.next();
+            if (kd->getUse()==IKeyDescriptor::encryption)
+                continue;
+            DSIGKeyInfoList* kilist=kd->getKeyInfo();
+            for (size_t s=0; kilist && s<kilist->getSize(); s++) {
+                const XMLCh* n=kilist->item(s)->getKeyName();
+                if (n)
+                    names.push_back(n);
             }
         }
+        names.push_back(role->getEntityDescriptor()->getId());
+        const IEntitiesDescriptor* group=role->getEntityDescriptor()->getEntitiesDescriptor();
+        while (group) {
+            if (group->getName())
+                names.push_back(group->getName());
+            group=group->getEntitiesDescriptor();
+        }
+    
+        // Now check each name.
+        XMLTrustImpl::KeyAuthority* kauth=NULL;
+        for (vector<const XMLCh*>::const_iterator name=names.begin(); !kauth && name!=names.end(); name++) {
+#ifdef HAVE_GOOD_STL
+            XMLTrustImpl::AuthMap::const_iterator c=impl->m_authMap.find(*name);
+            if (c!=impl->m_authMap.end()) {
+                kauth=c->second;
+                if (log.isInfoEnabled()) {
+                    auto_ptr_char temp(*name);
+                    log.info("KeyAuthority match on %s",temp.get());
+                }
+            }
 #else
-        // Without a decent STL, we trade-off the transcoding by doing a linear search.
-        for (vector<XMLTrustImpl::KeyAuthority*>::const_iterator keyauths=impl->m_keyauths.begin(); keyauths!=impl->m_keyauths.end(); keyauths++) {
-            for (vector<const XMLCh*>::const_iterator subs=keyauths->m_subjects.begin(); subs!=keyauths->m_subjects.end(); subs++) {
-                if (!XMLString::compareString(*name2,*subs)) {
-                    kauth=*keyauths;
-                    if (log.isDebugEnabled()) {
-                        auto_ptr_char temp(*name2);
-                        log.debug("KeyAuthority match on %s",temp.get());
+            // Without a decent STL, we trade-off the transcoding by doing a linear search.
+            for (vector<XMLTrustImpl::KeyAuthority*>::const_iterator keyauths=impl->m_keyauths.begin(); !kauth && keyauths!=impl->m_keyauths.end(); keyauths++) {
+                for (vector<const XMLCh*>::const_iterator subs=(*keyauths)->m_subjects.begin(); !kauth && subs!=(*keyauths)->m_subjects.end(); subs++) {
+                    if (!XMLString::compareString(*name,*subs)) {
+                        kauth=*keyauths;
+                        if (log.isInfoEnabled()) {
+                            auto_ptr_char temp(*name);
+                            log.info("KeyAuthority match on %s",temp.get());
+                        }
                     }
                 }
             }
-        }
 #endif
-    }
-
-    if (!kauth) {
-        if (impl->m_wildcard) {
-           log.warn("applying wildcard KeyAuthority, use with caution!");
-            kauth=impl->m_wildcard;
         }
-        else {
-            log.error("no KeyAuthority found to validate the token, leaving untrusted");
-            return false;
+    
+        if (!kauth) {
+            if (impl->m_wildcard) {
+               log.warn("applying wildcard KeyAuthority, use with caution!");
+                kauth=impl->m_wildcard;
+            }
+            else {
+                unlock();
+                log.warn("no KeyAuthority found to validate SSL connection, leaving it alone");
+                return false;
+            }
         }
-    }
+    
+        log.debug("performing certificate path validation...");
+
+        // If we have a match, use the associated keyauth.
+        X509_STORE* store=kauth->getX509Store();
+        if (store) {
+            STACK_OF(X509)* untrusted=sk_X509_new_null();
+            certChain.reset();
+            while (certChain.hasNext())
+                sk_X509_push(untrusted,(X509*)certChain.next());
+
+            // This contains the state of the validate operation.
+            X509_STORE_CTX ctx;
+
+            // AFAICT, EE and untrusted are passed in but not owned by the ctx.
+#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+            if (X509_STORE_CTX_init(&ctx,store,(X509*)certEE,untrusted)!=1) {
+                log_openssl();
+                log.error("unable to initialize X509_STORE_CTX");
+                X509_STORE_free(store);
+                sk_X509_free(untrusted);
+                unlock();
+                return false;
+            }
+#else
+            X509_STORE_CTX_init(&ctx,store,(X509*)certEE,untrusted);
+#endif
+            X509_STORE_CTX_set_depth(&ctx,100);    // handle depth below
+            X509_STORE_CTX_set_verify_cb(&ctx,error_callback);
+            
+            int ret=X509_verify_cert(&ctx);
+            if (ret==1) {
+                // Now see if the depth was acceptable by counting the number of intermediates.
+                int depth=sk_X509_num(ctx.chain)-2;
+                if (kauth->m_depth < depth) {
+                    log.error(
+                        "certificate chain was too long (%d intermediates, only %d allowed)",
+                        (depth==-1) ? 0 : depth,
+                        kauth->m_depth
+                        );
+                    ret=0;
+                }
+            }
+            
+            // Clean up...
+            X509_STORE_CTX_cleanup(&ctx);
+            X509_STORE_free(store);
 
-    log.debug("building untrusted certificate chain from signature");
-    STACK_OF(X509)* chain=sk_X509_new_null();
-    Iterator<const XMLCh*> icerts(certs);
-    while (icerts.hasNext()) {
-        auto_ptr_char xbuf(icerts.next());
-        X509* x=B64_to_X509(xbuf.get());
-        if (!x) {
-            log.error("unable to parse certificate in signature");
-            sk_X509_pop_free(chain,X509_free);
-            return false;
+            if (ret==1) {
+                log.info("successfully validated certificate chain");
+                unlock();
+                return true;
+            }
         }
-        sk_X509_push(chain,x);
     }
-
-    X509_STORE* store=kauth->getX509Store();
-    if (!store) {
-        log.error("unable to load X509_STORE from KeyAuthority object");
-        sk_X509_pop_free(chain,X509_free);
-        return false;
-    }
-    
-    X509_STORE_CTX* ctx=X509_STORE_CTX_new();
-    if (!ctx) {
-        log_openssl();
-        log.error("unable to create X509_STORE_CTX");
-        sk_X509_pop_free(chain,X509_free);
-        return false;
+    catch (...) {
+        unlock();
+        throw;
     }
+    unlock();
+    return false;
+}
 
-#if (OPENSSL_VERSION_NUMBER > 0x009070000L)
-    if (X509_STORE_CTX_init(ctx,store,sk_X509_value(chain,0),chain)!=1) {
-        log_openssl();
-        log.error("unable to initialize X509_STORE_CTX");
-        sk_X509_pop_free(chain,X509_free);
-        return false;
-    }
+bool XMLTrust::validate(const saml::SAMLSignedObject& token, const IRoleDescriptor* role, ITrust* certValidator)
+{
+    // The delegated trust plugin handles metadata keys and use of metadata extensions.
+    // If it fails to find an inline key in metadata, then it will branch off to the
+    // extended version and verify the token using the certificates inside it. At that
+    // point, control will pass to the other virtual function above and we can handle
+    // legacy KeyAuthority rules that way.
+    if (m_delegate->validate(token,role,certValidator ? certValidator : this))
+        return true;
+
+#ifdef _DEBUG
+    saml::NDC ndc("validate");
+#endif
+    Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Trust");
+
+    lock();
+    try {
+        XMLTrustImpl* impl=dynamic_cast<XMLTrustImpl*>(getImplementation());
+
+        // If we actually make it this far, the only case we're handling directly
+        // is an inline key in the old trust file format. Build a list of key names
+        // which will be used to find matching rules.
+        vector<const XMLCh*> names;
+        
+        // Build a list of acceptable names. Transcode the possible key "names" to UTF-8.
+        // For some simple cases, this should handle UTF-8 encoded DNs in certificates.
+        Iterator<const IKeyDescriptor*> kd_i=role->getKeyDescriptors();
+        while (kd_i.hasNext()) {
+            const IKeyDescriptor* kd=kd_i.next();
+            if (kd->getUse()!=IKeyDescriptor::signing)
+                continue;
+            DSIGKeyInfoList* KIL=kd->getKeyInfo();
+            if (!KIL)
+                continue;
+            for (size_t s=0; s<KIL->getSize(); s++) {
+                const XMLCh* n=KIL->item(s)->getKeyName();
+                if (n)
+                    names.push_back(n);
+            }
+        }
+        names.push_back(role->getEntityDescriptor()->getId());
+
+        log.debug("checking for keys in trust file");
+        DSIGKeyInfoList* KIL=NULL;
+        for (vector<const XMLCh*>::const_iterator name=names.begin(); !KIL && name!=names.end(); name++) {
+#ifdef HAVE_GOOD_STL
+            XMLTrustImpl::BindMap::const_iterator c=impl->m_bindMap.find(*name);
+            if (c!=impl->m_bindMap.end()) {
+                KIL=c->second;
+                if (log.isInfoEnabled()) {
+                    auto_ptr_char temp(*name);
+                    log.info("KeyInfo match on %s",temp.get());
+                }
+            }
 #else
-    X509_STORE_CTX_init(ctx,store,sk_X509_value(chain,0),chain);
+            // Without a decent STL, we trade-off the transcoding by doing a linear search.
+            for (vector<DSIGKeyInfoList*>::const_iterator keybinds=impl->m_keybinds.begin(); !KIL && keybinds!=impl->m_keybinds.end(); keybinds++) {
+                for (size_t s=0; !KIL && s<(*keybinds)->getSize(); s++) {
+                    if (!XMLString::compareString(*name,(*keybinds)->item(s)->getKeyName())) {
+                        KIL=*keybinds;
+                        if (log.isInfoEnabled()) {
+                            auto_ptr_char temp(*name);
+                            log.info("KeyInfo match on %s",temp.get());
+                        }
+                    }
+                }
+            }
 #endif
-    if (kauth->m_depth)
-        X509_STORE_CTX_set_depth(ctx,kauth->m_depth);
-
-    // Add any relevant CRLs.
-    log.debug("obtaining CRLs for this provider/role");
-    Revocation rev(revocations);
-    Iterator<void*> crls=rev.getRevocationLists(provider,role);
-    while (crls.hasNext()) {
-        if (!X509_STORE_add_crl(store,X509_CRL_dup(reinterpret_cast<X509_CRL*>(crls.next())))) {
-            log_openssl();
-            log.warn("failed to add CRL");
+        }
+        
+        if (KIL) {
+            // Any inline KeyInfo should ostensibly resolve to a key we can try.
+            Iterator<KeyInfoResolver*> resolvers(m_resolvers);
+            while (resolvers.hasNext()) {
+                XSECCryptoKey* key=((XSECKeyInfoResolver*)*resolvers.next())->resolveKey(KIL);
+                if (key) {
+                    log.debug("resolved key, trying it...");
+                    try {
+                        token.verify(key);
+                        unlock();
+                        log.info("token verified with KeyInfo, nothing more to verify");
+                        return true;
+                    }
+                    catch (SAMLException& e) {
+                        unlock();
+                        log.warn("verification with inline key failed: %s", e.what());
+                        return false;
+                    }
+                }
+            }
+            log.warn("KeyInfo in trust provider did not resolve to a key");
         }
     }
+    catch (...) {
+        unlock();
+        throw;
+    }       
 
-    int result=X509_verify_cert(ctx);
-    sk_X509_pop_free(chain,X509_free);
-    X509_STORE_CTX_free(ctx);
-
-    if (result==1) {
-        log.info("successfully validated certificate chain, token signature trusted");
-        return true;
-    }
-    
-    log.error("failed to validate certificate chain, token signature untrusted");
+    unlock();
     return false;
 }