Handle DN matching better.
[shibboleth/cpp-sp.git] / xmlproviders / XMLTrust.cpp
1 /* 
2  * The Shibboleth License, Version 1. 
3  * Copyright (c) 2002 
4  * University Corporation for Advanced Internet Development, Inc. 
5  * All rights reserved
6  * 
7  * 
8  * Redistribution and use in source and binary forms, with or without 
9  * modification, are permitted provided that the following conditions are met:
10  * 
11  * Redistributions of source code must retain the above copyright notice, this 
12  * list of conditions and the following disclaimer.
13  * 
14  * Redistributions in binary form must reproduce the above copyright notice, 
15  * this list of conditions and the following disclaimer in the documentation 
16  * and/or other materials provided with the distribution, if any, must include 
17  * the following acknowledgment: "This product includes software developed by 
18  * the University Corporation for Advanced Internet Development 
19  * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
20  * may appear in the software itself, if and wherever such third-party 
21  * acknowledgments normally appear.
22  * 
23  * Neither the name of Shibboleth nor the names of its contributors, nor 
24  * Internet2, nor the University Corporation for Advanced Internet Development, 
25  * Inc., nor UCAID may be used to endorse or promote products derived from this 
26  * software without specific prior written permission. For written permission, 
27  * please contact shibboleth@shibboleth.org
28  * 
29  * Products derived from this software may not be called Shibboleth, Internet2, 
30  * UCAID, or the University Corporation for Advanced Internet Development, nor 
31  * may Shibboleth appear in their name, without prior written permission of the 
32  * University Corporation for Advanced Internet Development.
33  * 
34  * 
35  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
36  * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
37  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
38  * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
39  * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
40  * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
41  * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
42  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
43  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
47  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49
50 /* XMLTrust.cpp - a trust implementation that uses an XML file
51
52    Scott Cantor
53    9/27/02
54
55    $History:$
56 */
57
58 #include "internal.h"
59
60 #include <sys/types.h>
61 #include <sys/stat.h>
62
63 #include <openssl/err.h>
64 #include <openssl/x509v3.h>
65 #include <openssl/x509_vfy.h>
66
67 #include <log4cpp/Category.hh>
68 #include <xercesc/framework/URLInputSource.hpp>
69 #include <xercesc/util/regx/RegularExpression.hpp>
70 #include <xsec/enc/XSECKeyInfoResolverDefault.hpp>
71
72 using namespace shibboleth;
73 using namespace saml;
74 using namespace log4cpp;
75 using namespace std;
76
77 namespace {
78
79     int logging_callback(int ok, X509_STORE_CTX* store)
80     {
81         if (!ok)
82             Category::getInstance("OpenSSL").error(X509_verify_cert_error_string(store->error));
83         return ok;
84     }
85     
86     int verify_callback(X509_STORE_CTX* ctx, void* arg)
87     {
88         Category::getInstance("OpenSSL").debug("invoking default X509 verify callback");
89         return X509_verify_cert(ctx);
90     }
91
92     class XMLTrustImpl : public ReloadableXMLFileImpl
93     {
94     public:
95         XMLTrustImpl(const char* pathname) : ReloadableXMLFileImpl(pathname), m_wildcard(NULL) { init(); }
96         XMLTrustImpl(const DOMElement* e) : ReloadableXMLFileImpl(e), m_wildcard(NULL) { init(); }
97         void init();
98         ~XMLTrustImpl();
99         
100         struct KeyAuthority
101         {
102             KeyAuthority() : m_depth(1) {}
103             ~KeyAuthority();
104             X509_STORE* getX509Store();
105             
106 #ifndef HAVE_GOOD_STL
107             vector<const XMLCh*> m_subjects;
108 #endif
109             vector<X509*> m_certs;
110             unsigned short m_depth;
111         };
112         
113         vector<DSIGKeyInfoList*> m_keybinds;
114         vector<KeyAuthority*> m_keyauths;
115         KeyAuthority* m_wildcard;
116 #ifdef HAVE_GOOD_STL
117         typedef map<xstring,KeyAuthority*> AuthMap;
118         typedef map<xstring,DSIGKeyInfoList*> BindMap;
119         AuthMap m_authMap;
120         BindMap m_bindMap;
121 #endif
122     };
123
124     class XMLTrust : public ITrust, public ReloadableXMLFile
125     {
126     public:
127         XMLTrust(const DOMElement* e) : ReloadableXMLFile(e) {}
128         ~XMLTrust() {}
129
130     bool validate(
131         const saml::Iterator<IRevocation*>& revocations,
132         const IProviderRole* role, const saml::SAMLSignedObject& token,
133         const saml::Iterator<IMetadata*>& metadatas=EMPTY(IMetadata*)
134         );
135     bool attach(const Iterator<IRevocation*>& revocations, const IProviderRole* role, void* ctx);
136
137     protected:
138         virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
139         virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
140     };
141
142 }
143
144 IPlugIn* XMLTrustFactory(const DOMElement* e)
145 {
146     XMLTrust* t=new XMLTrust(e);
147     try {
148         t->getImplementation();
149     }
150     catch (...) {
151         delete t;
152         throw;
153     }
154     return t;    
155 }
156
157
158 ReloadableXMLFileImpl* XMLTrust::newImplementation(const char* pathname, bool first) const
159 {
160     return new XMLTrustImpl(pathname);
161 }
162
163 ReloadableXMLFileImpl* XMLTrust::newImplementation(const DOMElement* e, bool first) const
164 {
165     return new XMLTrustImpl(e);
166 }
167
168 X509_STORE* XMLTrustImpl::KeyAuthority::getX509Store()
169 {
170     NDC ndc("getX509Store");
171     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrust");
172
173     // Load the cert vector into a store.
174     X509_STORE* store=X509_STORE_new();
175     if (!store) {
176         log_openssl();
177         return NULL;
178     }
179     
180     for (vector<X509*>::iterator j=m_certs.begin(); j!=m_certs.end(); j++) {
181         if (!X509_STORE_add_cert(store,X509_dup(*j))) {
182             log_openssl();
183             log.warn("failed to add cert: %s", (*j)->name);
184             continue;
185         }
186     }
187
188     return store;
189 }
190
191 XMLTrustImpl::KeyAuthority::~KeyAuthority()
192 {
193     for (vector<X509*>::iterator i=m_certs.begin(); i!=m_certs.end(); i++)
194         X509_free(*i);
195 }
196
197 class KeyInfoNodeFilter : public DOMNodeFilter
198 {
199 public:
200     short acceptNode(const DOMNode* node) const
201     {
202         // Our filter just skips any trees not rooted by ds:KeyInfo.
203         if (node->getNodeType()==DOMNode::ELEMENT_NODE) {
204             if (saml::XML::isElementNamed(static_cast<const DOMElement*>(node),saml::XML::XMLSIG_NS,L(KeyInfo)))
205                 return FILTER_ACCEPT;
206         }
207         return FILTER_REJECT;
208     }
209 };
210
211 void XMLTrustImpl::init()
212 {
213     NDC ndc("XMLTrustImpl");
214     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrustImpl");
215
216     try {
217         if (!saml::XML::isElementNamed(m_root,::XML::TRUST_NS,SHIB_L(Trust))) {
218             log.error("Construction requires a valid trust file: (trust:Trust as root element)");
219             throw TrustException("Construction requires a valid trust file: (trust:Trust as root element)");
220         }
221
222         // Loop over the KeyAuthority elements.
223         DOMNodeList* nlist=m_root->getElementsByTagNameNS(::XML::TRUST_NS,SHIB_L(KeyAuthority));
224         for (int i=0; nlist && i<nlist->getLength(); i++) {
225             KeyAuthority* ka=new KeyAuthority();
226             m_keyauths.push_back(ka);
227             
228             DOMElement* e=static_cast<DOMElement*>(nlist->item(i));
229             const XMLCh* depth=e->getAttributeNS(NULL,SHIB_L(VerifyDepth));
230             if (depth && *depth)
231                 ka->m_depth=XMLString::parseInt(depth);
232             
233             // Very rudimentary, grab up all the in-band X509Certificate elements, and flatten into one list.
234             DOMNodeList* certlist=e->getElementsByTagNameNS(saml::XML::XMLSIG_NS,L(X509Certificate));
235             for (int j=0; certlist && j<certlist->getLength(); j++) {
236                 auto_ptr_char blob(certlist->item(j)->getFirstChild()->getNodeValue());
237                 X509* x=B64_to_X509(blob.get());
238                 if (x)
239                     ka->m_certs.push_back(x);
240                 else
241                     log.warn("unable to create certificate from inline X509Certificate data");
242             }
243
244             // Now look for externally referenced objects.
245             certlist=e->getElementsByTagNameNS(saml::XML::XMLSIG_NS,SHIB_L(RetrievalMethod));
246             for (int k=0; certlist && k<certlist->getLength(); k++) {
247                 DOMElement* cert=static_cast<DOMElement*>(certlist->item(k));
248                 if (!XMLString::compareString(cert->getAttributeNS(NULL,SHIB_L(Type)),::XML::XMLSIG_RETMETHOD_RAWX509)) {
249                     // DER format
250                     auto_ptr_char fname(cert->getAttributeNS(NULL,SHIB_L(URI)));
251                     FILE* f=fopen(fname.get(),"r");
252                     if (f) {
253                         X509* x=NULL;
254                         d2i_X509_fp(f,&x);
255                         if (x) {
256                             ka->m_certs.push_back(x);
257                             continue;
258                         }
259                         else
260                             log_openssl();
261                     }
262                     log.warn("unable to create certificate from externally referenced file");
263                 }
264                 else if (!XMLString::compareString(cert->getAttributeNS(NULL,SHIB_L(Type)),::XML::SHIB_RETMETHOD_PEMX509)) {
265                     // PEM format
266                     int count=0;
267                     auto_ptr_char fname(cert->getAttributeNS(NULL,SHIB_L(URI)));
268                     FILE* f=fopen(fname.get(),"r");
269                     if (f) {
270                         X509* x=NULL;
271                         while (x=PEM_read_X509(f,NULL,NULL,NULL)) {
272                             ka->m_certs.push_back(x);
273                             count++;
274                         }
275                     }
276                     if (!count)
277                         log.warn("unable to create certificate from externally referenced file");
278                 }
279             }
280             
281             // Now map the ds:KeyName values to the list of certs.
282             bool wildcard=true;
283             DOMElement* sub=saml::XML::getFirstChildElement(e,saml::XML::XMLSIG_NS,SHIB_L(KeyName));
284             while (sub) {
285                 const XMLCh* name=sub->getFirstChild()->getNodeValue();
286                 if (name && *name) {
287                     wildcard=false;
288 #ifdef HAVE_GOOD_STL
289                     m_authMap[name]=ka;
290 #else
291                     ka->m_subjects.push_back(name);
292 #endif
293                 }
294                 sub=saml::XML::getNextSiblingElement(sub,saml::XML::XMLSIG_NS,SHIB_L(KeyName));
295             }
296             
297             // If no Subjects, this is a catch-all binding.
298             if (wildcard) {
299                 if (!m_wildcard) {
300                     log.warn("found a wildcard KeyAuthority element, make sure this is what you intend");
301                     m_wildcard=ka;
302                 }
303                 else
304                     log.warn("found multiple wildcard KeyAuthority elements, ignoring all but the first");
305             }
306         }
307
308         // Now traverse the outer ds:KeyInfo elements. Supposedly this cast just works...
309         int count=0;
310         KeyInfoNodeFilter filter;
311         XSECKeyInfoResolverDefault resolver;
312         DOMTreeWalker* walker=
313             static_cast<DOMDocumentTraversal*>(m_doc)->createTreeWalker(const_cast<DOMElement*>(m_root),DOMNodeFilter::SHOW_ELEMENT,&filter,false);
314         DOMElement* kidom=static_cast<DOMElement*>(walker->firstChild());
315         while (kidom) {
316             count++;
317             DSIGKeyInfoList* KIL = new DSIGKeyInfoList(NULL);
318             // We let XMLSec hack through anything it can. This should evolve over time, or we can
319             // plug in our own KeyResolver later...
320             DOMElement* child=saml::XML::getFirstChildElement(kidom);
321             int count2=1;
322             while (child) {
323                 if (!KIL->addXMLKeyInfo(child))
324                     log.warn("skipped unsupported ds:KeyInfo child element (%d)",count2);
325                 child=saml::XML::getNextSiblingElement(child);
326                 count2++;
327             }
328             
329             // Dry run...can we resolve to a key?
330             XSECCryptoKey* key=resolver.resolveKey(KIL);
331             if (key) {
332                 // So far so good, now look for the name binding(s).
333                 delete key;
334                 bool named=false;
335                 for (size_t index=0; index<KIL->getSize(); index++) {
336                     DSIGKeyInfo* info=KIL->item(index);
337                     const XMLCh* name=info->getKeyName();
338                     if (name && *name) {
339                         if (!named)
340                             m_keybinds.push_back(KIL);
341                         named=true;
342 #ifdef HAVE_GOOD_STL
343                         m_bindMap[name]=KIL;
344 #endif
345                     }
346                 }
347                 if (!named) {
348                     log.warn("skipping ds:KeyInfo binding (%d) that does not contain a usable key name",count);
349                     delete KIL;
350                 }
351             }
352             else {
353                 log.warn("skipping ds:KeyInfo binding (%d) that does not resolve to a key",count);
354                 delete KIL;
355             }
356             kidom=static_cast<DOMElement*>(walker->nextSibling());
357         }
358         walker->release();    // This just cleans up aggressively, but there's no leak if we don't.
359     }
360     catch (SAMLException& e) {
361         log.errorStream() << "Error while parsing trust configuration: " << e.what() << CategoryStream::ENDLINE;
362         for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
363             delete (*i);
364         for (vector<DSIGKeyInfoList*>::iterator j=m_keybinds.begin(); j!=m_keybinds.end(); j++)
365             delete (*j);
366         throw;
367     }
368     catch (...) {
369         log.error("Unexpected error while parsing trust configuration");
370         for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
371             delete (*i);
372         for (vector<DSIGKeyInfoList*>::iterator j=m_keybinds.begin(); j!=m_keybinds.end(); j++)
373             delete (*j);
374         throw;
375     }
376 }
377
378 XMLTrustImpl::~XMLTrustImpl()
379 {
380     for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
381         delete (*i);
382     for (vector<DSIGKeyInfoList*>::iterator j=m_keybinds.begin(); j!=m_keybinds.end(); j++)
383         delete (*j);
384 }
385
386 bool XMLTrust::attach(const Iterator<IRevocation*>& revocations, const IProviderRole* role, void* ctx)
387 {
388     lock();
389     try {
390         saml::NDC ndc("attach");
391         Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrust");
392         XMLTrustImpl* impl=dynamic_cast<XMLTrustImpl*>(getImplementation());
393     
394         // Build a list of the names to match. We include any named KeyDescriptors, and the provider ID and its groups.
395         vector<const XMLCh*> names;
396         Iterator<const IKeyDescriptor*> kdlist=role->getKeyDescriptors();
397         while (kdlist.hasNext()) {
398             const IKeyDescriptor* kd=kdlist.next();
399             if (kd->getUse()!=IKeyDescriptor::signing)
400                 continue;
401             DSIGKeyInfoList* kilist=kd->getKeyInfo();
402             for (size_t s=0; kilist && s<kilist->getSize(); s++) {
403                 const XMLCh* n=kilist->item(s)->getKeyName();
404                 if (n)
405                     names.push_back(n);
406             }
407         }
408         names.push_back(role->getProvider()->getId());
409         Iterator<const XMLCh*> groups=role->getProvider()->getGroups();
410         while (groups.hasNext())
411             names.push_back(groups.next());
412     
413         // Now check each name.
414         XMLTrustImpl::KeyAuthority* kauth=NULL;
415         for (vector<const XMLCh*>::const_iterator name=names.begin(); !kauth && name!=names.end(); name++) {
416     #ifdef HAVE_GOOD_STL
417             XMLTrustImpl::AuthMap::const_iterator c=impl->m_authMap.find(*name);
418             if (c!=impl->m_authMap.end()) {
419                 kauth=c->second;
420                 if (log.isDebugEnabled()) {
421                     auto_ptr_char temp(*name);
422                     log.debug("KeyAuthority match on %s",temp.get());
423                 }
424             }
425     #else
426             // Without a decent STL, we trade-off the transcoding by doing a linear search.
427             for (vector<XMLTrustImpl::KeyAuthority*>::const_iterator keyauths=impl->m_keyauths.begin(); !kauth && keyauths!=impl->m_keyauths.end(); keyauths++) {
428                 for (vector<const XMLCh*>::const_iterator subs=(*keyauths)->m_subjects.begin(); !kauth && subs!=(*keyauths)->m_subjects.end(); subs++) {
429                     if (!XMLString::compareString(*name,*subs)) {
430                         kauth=*keyauths;
431                         if (log.isDebugEnabled()) {
432                             auto_ptr_char temp(*name);
433                             log.debug("KeyAuthority match on %s",temp.get());
434                         }
435                     }
436                 }
437             }
438     #endif
439         }
440     
441         if (!kauth) {
442             if (impl->m_wildcard) {
443                log.warn("applying wildcard KeyAuthority, use with caution!");
444                 kauth=impl->m_wildcard;
445             }
446             else {
447                 unlock();
448                 log.warn("no KeyAuthority found to validate SSL connection, leaving it alone");
449                 return false;
450             }
451         }
452     
453         // If we have a match, use the associated keyauth unless we already did...
454         X509_STORE* store=kauth->getX509Store();
455         if (store) {
456       
457             // Add any relevant CRLs.
458             log.debug("obtaining CRLs for this provider/role");
459             Revocation rev(revocations);
460             Iterator<void*> crls=rev.getRevocationLists(role->getProvider(),role);
461             while (crls.hasNext()) {
462                 if (!X509_STORE_add_crl(store,X509_CRL_dup(reinterpret_cast<X509_CRL*>(crls.next())))) {
463                     log_openssl();
464                     log.warn("failed to add CRL");
465                 }
466             }
467         
468             // Apply store to this context.
469             SSL_CTX_set_verify(reinterpret_cast<SSL_CTX*>(ctx),SSL_VERIFY_PEER,logging_callback);
470 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
471             SSL_CTX_set_cert_verify_callback(reinterpret_cast<SSL_CTX*>(ctx),verify_callback,NULL);
472 #else
473             SSL_CTX_set_cert_verify_callback(reinterpret_cast<SSL_CTX*>(ctx),reinterpret_cast<int (*)()>(verify_callback),NULL);
474 #endif
475             SSL_CTX_set_cert_store(reinterpret_cast<SSL_CTX*>(ctx),store);
476             SSL_CTX_set_verify_depth(reinterpret_cast<SSL_CTX*>(ctx),kauth->m_depth);
477         }
478     }
479     catch (...) {
480         unlock();
481         throw;
482     }
483     unlock();
484     return true;
485 }
486
487 bool XMLTrust::validate(
488     const saml::Iterator<IRevocation*>& revocations,
489     const IProviderRole* role, const saml::SAMLSignedObject& token,
490     const saml::Iterator<IMetadata*>& metadatas
491     )
492 {
493     lock();
494     try {
495         NDC ndc("validate");
496         Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrust");
497         XMLTrustImpl* impl=dynamic_cast<XMLTrustImpl*>(getImplementation());
498     
499         // This is where we're going to hide all the juicy SAML trust bits. If we botch it
500         // we can just plug in a new version, hopefully.
501     
502         Metadata metadata(metadatas);   // With luck we won't need this.
503     
504         // Did the caller tell us about the signer?
505         const IProvider* provider=(role ? role->getProvider() : NULL);
506         if (!provider) {
507             log.debug("no role descriptor passed in, trying to map token to provider");
508             
509             // The first step is to identify the provider responsible for signing the token.
510             // We can't narrow it down to role, because we don't know why the token is being validated.
511             
512             // If it's an assertion, this isn't terribly hard, but we need to hack in support for both
513             // Issuer and NameQualifier as a provider ID. Issuer will be the main one going forward.
514             // 1.0/1.1 origins will be sending a hostname as Issuer, but this won't hit the metadata lookup
515             // and we'll fall back to NameQualifier. Non-Shib SAML origins generally would be based on Issuer.
516             
517             // Responses allow us to try and locate a provider by checking the assertion(s) inside.
518             // Technically somebody could enclose somebody else's assertions, but if the use case is
519             // that advanced, we're probably into SAML 2.0 and we'll have Issuer up top.
520             
521             // Requests...umm, pretty much out of luck. We'll apply our own hack if there's an
522             // attribute query, and use Resource.
523             
524             if (typeid(token)==typeid(SAMLResponse)) {
525                 Iterator<SAMLAssertion*> assertions=dynamic_cast<const SAMLResponse&>(token).getAssertions();
526                 while (!provider && assertions.hasNext()) {
527                     SAMLAssertion* assertion=assertions.next();
528                     provider=metadata.lookup(assertion->getIssuer());
529                     if (!provider) {
530                         Iterator<SAMLStatement*> statements=assertion->getStatements();
531                         while (!provider && statements.hasNext()) {
532                             SAMLSubjectStatement* statement=dynamic_cast<SAMLSubjectStatement*>(statements.next());
533                             if (statement && statement->getSubject()->getNameIdentifier()->getNameQualifier())
534                                 provider=metadata.lookup(statement->getSubject()->getNameIdentifier()->getNameQualifier());
535                         }
536                     }
537                 }
538             }
539             else if (typeid(token)==typeid(SAMLAssertion)) {
540                 provider=metadata.lookup(dynamic_cast<const SAMLAssertion&>(token).getIssuer());
541                 if (!provider) {
542                     Iterator<SAMLStatement*> statements=dynamic_cast<const SAMLAssertion&>(token).getStatements();
543                     while (!provider && statements.hasNext()) {
544                         SAMLSubjectStatement* statement=dynamic_cast<SAMLSubjectStatement*>(statements.next());
545                         if (statement && statement->getSubject()->getNameIdentifier()->getNameQualifier())
546                             provider=metadata.lookup(statement->getSubject()->getNameIdentifier()->getNameQualifier());
547                     }
548                 }
549             }
550             else if (typeid(token)==typeid(SAMLRequest)) {
551                 const SAMLQuery* q=dynamic_cast<const SAMLRequest&>(token).getQuery();
552                 if (q && dynamic_cast<const SAMLAttributeQuery*>(q))
553                     provider=metadata.lookup(dynamic_cast<const SAMLAttributeQuery*>(q)->getResource());
554             }
555             
556             // If we still don't have a provider, there's no likely basis for trust,
557             // but a wildcard KeyAuthority might apply.
558             if (log.isInfoEnabled() && provider) {
559                 auto_ptr_char temp(provider->getId());
560                 log.info("mapped signed token to provider: %s", temp.get());
561             }
562             else if (!provider)
563                 log.warn("unable to map signed token to provider, only wildcarded trust will apply");
564         }
565         
566         vector<const XMLCh*> names;
567         XSECKeyInfoResolverDefault keyResolver;
568         
569         // First, try to resolve a KeyDescriptor from the role into an actual key.
570         // That's the simplest case. Failing that, remember any key names we run across.
571         
572         if (role) {
573             log.debug("checking for key descriptors that resolve directly");
574             Iterator<const IKeyDescriptor*> kd_i=role->getKeyDescriptors();
575             while (kd_i.hasNext()) {
576                 const IKeyDescriptor* kd=kd_i.next();
577                 if (kd->getUse()!=IKeyDescriptor::signing)
578                     continue;
579                 DSIGKeyInfoList* KIL=kd->getKeyInfo();
580                 if (!KIL)
581                     continue;
582                 XSECCryptoKey* key=keyResolver.resolveKey(KIL);
583                 if (key) {
584                     log.debug("found an inline key, trying it...");
585                     try {
586                         token.verify(key);
587                         unlock();
588                         log.info("token verified with inline key, nothing more to verify");
589                         return true;
590                     }
591                     catch (SAMLException& e) {
592                         log.debug("inline key failed: %s", e.what());
593                     }
594                 }
595                 else {
596                     for (size_t s=0; s<KIL->getSize(); s++) {
597                         const XMLCh* n=KIL->item(s)->getKeyName();
598                         if (n)
599                             names.push_back(n);
600                     }
601                 }
602             }
603         }
604         
605         // Push the provider ID on the key name list. We don't push provider groups in, since
606         // matching groups to a key makes no sense.
607         if (provider)
608             names.push_back(provider->getId());
609         
610         // No keys inline in metadata. Now we try and find a key inline in trust.
611         log.debug("checking for keys in trust file");
612         DSIGKeyInfoList* KIL=NULL;
613         for (vector<const XMLCh*>::const_iterator name=names.begin(); !KIL && name!=names.end(); name++) {
614     #ifdef HAVE_GOOD_STL
615             XMLTrustImpl::BindMap::const_iterator c=impl->m_bindMap.find(*name);
616             if (c!=impl->m_bindMap.end()) {
617                 KIL=c->second;
618                 if (log.isDebugEnabled()) {
619                     auto_ptr_char temp(*name);
620                     log.debug("KeyInfo match on %s",temp.get());
621                 }
622             }
623     #else
624             // Without a decent STL, we trade-off the transcoding by doing a linear search.
625             for (vector<DSIGKeyInfoList*>::const_iterator keybinds=impl->m_keybinds.begin(); !KIL && keybinds!=impl->m_keybinds.end(); keybinds++) {
626                 for (size_t s=0; !KIL && s<(*keybinds)->getSize(); s++) {
627                     if (!XMLString::compareString(*name,(*keybinds)->item(s)->getKeyName())) {
628                         KIL=*keybinds;
629                         if (log.isDebugEnabled()) {
630                             auto_ptr_char temp(*name);
631                             log.debug("KeyInfo match on %s",temp.get());
632                         }
633                     }
634                 }
635             }
636     #endif
637         }
638         
639         if (KIL) {
640             // Any inline KeyInfo should ostensible resolve to a key we can try.
641             XSECCryptoKey* key=keyResolver.resolveKey(KIL);
642             if (key) {
643                 log.debug("resolved key, trying it...");
644                 try {
645                     token.verify(key);
646                     unlock();
647                     log.info("token verified with KeyInfo, nothing more to verify");
648                     return true;
649                 }
650                 catch (SAMLException& e) {
651                     log.debug("inline key failed: %s", e.what());
652                 }
653             }
654             else
655                 log.warn("KeyInfo in trust provider did not resolve to a key");
656         }
657         
658         // Direct key verification hasn't worked. Now we have to switch over to KeyAuthority-based
659         // validation. The actual verification key has to be inside the token.
660         log.debug("verifying signature using key inside token...");
661         try {
662             token.verify();
663             log.info("verified with key inside token, entering validation stage");
664         }
665         catch (SAMLException& e) {
666             unlock();
667             log.debug("verification using key inside token failed: %s", e.what());
668             return false;
669         }
670         
671         // Before we do the cryptogprahy, check that the EE certificate "name" matches
672         // one of the acceptable key "names" for the signer. Without this, we have a gaping
673         // hole in the validation.
674         log.debug("matching token's certificate subject against valid key names...");
675         vector<const XMLCh*> certs;
676         for (unsigned int i=0; i<token.getX509CertificateCount(); i++)
677             certs.push_back(token.getX509Certificate(i));
678     
679         // Decode the EE cert.
680         auto_ptr_char EE(certs[0]);
681         X509* x=B64_to_X509(EE.get());
682         if (!x) {
683             unlock();
684             log.error("unable to decode X.509 signing certificate");
685             return false;
686         }
687         
688         // Transcode the possible key "names" to UTF-8. For some simple cases, this should
689         // handle UTF-8 encoded DNs in certificates.
690         vector<string> keynames;
691         Iterator<const XMLCh*> iname(names);
692         while (iname.hasNext()) {
693             auto_ptr<char> kn(toUTF8(iname.next()));
694             keynames.push_back(kn.get());
695         }
696         
697         bool match=false;
698         char buf[256];
699         X509_NAME* subject=X509_get_subject_name(x);
700         if (subject) {
701             // One way is a direct match to the subject DN.
702             // Seems that the way to do the compare is to write the X509_NAME into a BIO.
703             BIO* b = BIO_new(BIO_s_mem());
704             BIO* b2 = BIO_new(BIO_s_mem());
705             BIO_set_mem_eof_return(b, 0);
706             BIO_set_mem_eof_return(b2, 0);
707             // The flags give us LDAP order instead of X.500, with a comma/space separator.
708             int len=X509_NAME_print_ex(b,subject,0,XN_FLAG_RFC2253|XN_FLAG_SEP_CPLUS_SPC);
709             string subjectstr,subjectstr2;
710             BIO_flush(b);
711             while ((len = BIO_read(b, buf, 255)) > 0) {
712                 buf[len] = '\0';
713                 subjectstr+=buf;
714             }
715             log.infoStream() << "certificate subject: " << subjectstr << CategoryStream::ENDLINE;
716             len=X509_NAME_print_ex(b2,subject,0,XN_FLAG_RFC2253);
717             BIO_flush(b2);
718             while ((len = BIO_read(b2, buf, 255)) > 0) {
719                 buf[len] = '\0';
720                 subjectstr2+=buf;
721             }
722             
723             // Check each keyname.
724             for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
725 #ifdef HAVE_STRCASECMP
726                 if (!strcasecmp(n->c_str(),subjectstr.c_str()) || !strcasecmp(n->c_str(),subjectstr2.c_str())) {
727 #else
728                 if (!stricmp(n->c_str(),subjectstr.c_str()) || !stricmp(n->c_str(),subjectstr2.c_str())) {
729 #endif
730                     log.info("matched full subject DN to a key name");
731                     match=true;
732                     break;
733                 }
734             }
735             BIO_free(b);
736             BIO_free(b2);
737             
738             if (!match) {
739                 log.debug("unable to match DN, trying TLS-style hostname match");
740                 memset(buf,0,sizeof(buf));
741                 if (X509_NAME_get_text_by_NID(subject,NID_commonName,buf,255)>0) {
742                     for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
743     #ifdef HAVE_STRCASECMP
744                         if (!strcasecmp(buf,n->c_str())) {
745     #else
746                         if (!stricmp(buf,n->c_str())) {
747     #endif
748                             log.info("matched subject CN to a key name");
749                             match=true;
750                             break;
751                         }
752                     }
753                 }
754                 else
755                     log.warn("no common name in certificate subject");
756                 
757                 if (!match) {
758                     log.debug("unable to match CN, trying DNS subjectAltName");     // I seriously doubt this works.
759                     int extcount=X509_get_ext_count(x);
760                     for (int c=0; c<extcount; c++) {
761                         X509_EXTENSION* ext=X509_get_ext(x,c);
762                         const char* extstr=OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
763                         if (!strcmp(extstr,"subjectAltName")) {
764                             X509V3_EXT_METHOD* meth=X509V3_EXT_get(ext);
765                             if (!meth || !meth->d2i || !meth->i2v || !ext->value->data) // had to add all these to prevent crashing
766                                 break;
767                             unsigned char* data=ext->value->data;
768                             STACK_OF(CONF_VALUE)* val=meth->i2v(meth,meth->d2i(NULL,&data,ext->value->length),NULL);
769                             for (int j=0; j<sk_CONF_VALUE_num(val); j++) {
770                                 CONF_VALUE* nval=sk_CONF_VALUE_value(val,j);
771                                 if (!strcmp(nval->name,"DNS") || !strcmp(nval->name,"URI")) {
772                                     for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
773     #ifdef HAVE_STRCASECMP
774                                         if (!strcasecmp(nval->value,n->c_str())) {
775     #else
776                                         if (!stricmp(nval->value,n->c_str())) {
777     #endif
778                                             log.info("matched DNS subjectAltName to a key name");
779                                             match=true;
780                                             break;
781                                         }
782                                     }
783                                 }
784                             }
785                         }
786                     }
787                 }
788             }
789         }
790         else
791             log.error("certificate has no subject?!");
792     
793         X509_free(x);
794     
795         if (!match) {
796             unlock();
797             log.error("cannot match certificate subject against provider's key names");
798             return false;
799         }
800     
801         // We're ready for the final stage.
802         log.debug("final step, certificate path validation...");
803     
804         // Push any provider groups on the name match list.
805         if (provider) {
806             Iterator<const XMLCh*> groups=provider->getGroups();
807             while (groups.hasNext())
808                 names.push_back(groups.next());
809         }
810     
811         // Now we hunt the list for a KeyAuthority that matches one of the names.
812         XMLTrustImpl::KeyAuthority* kauth=NULL;
813         for (vector<const XMLCh*>::const_iterator name2=names.begin(); !kauth && name2!=names.end(); name2++) {
814 #ifdef HAVE_GOOD_STL
815             XMLTrustImpl::AuthMap::const_iterator c=impl->m_authMap.find(*name2);
816             if (c!=impl->m_authMap.end()) {
817                 kauth=c->second;
818                 if (log.isDebugEnabled()) {
819                     auto_ptr_char temp(*name2);
820                     log.debug("KeyAuthority match on %s",temp.get());
821                 }
822             }
823 #else
824             // Without a decent STL, we trade-off the transcoding by doing a linear search.
825             for (vector<XMLTrustImpl::KeyAuthority*>::const_iterator keyauths=impl->m_keyauths.begin(); !kauth && keyauths!=impl->m_keyauths.end(); keyauths++) {
826                 for (vector<const XMLCh*>::const_iterator subs=(*keyauths)->m_subjects.begin(); !kauth && subs!=(*keyauths)->m_subjects.end(); subs++) {
827                     if (!XMLString::compareString(*name2,*subs)) {
828                         kauth=*keyauths;
829                         if (log.isDebugEnabled()) {
830                             auto_ptr_char temp(*name2);
831                             log.debug("KeyAuthority match on %s",temp.get());
832                         }
833                     }
834                 }
835             }
836 #endif
837         }
838     
839         if (!kauth) {
840             if (impl->m_wildcard) {
841                log.warn("applying wildcard KeyAuthority, use with caution!");
842                 kauth=impl->m_wildcard;
843             }
844             else {
845                 unlock();
846                 log.warn("no KeyAuthority found to validate the token, leaving untrusted");
847                 return false;
848             }
849         }
850     
851         log.debug("building untrusted certificate chain from signature");
852         STACK_OF(X509)* chain=sk_X509_new_null();
853         Iterator<const XMLCh*> icerts(certs);
854         while (icerts.hasNext()) {
855             auto_ptr_char xbuf(icerts.next());
856             X509* x=B64_to_X509(xbuf.get());
857             if (!x) {
858                 unlock();
859                 log.error("unable to parse certificate in signature");
860                 sk_X509_pop_free(chain,X509_free);
861                 return false;
862             }
863             sk_X509_push(chain,x);
864         }
865     
866         X509_STORE* store=kauth->getX509Store();
867         if (!store) {
868             unlock();
869             log.error("unable to load X509_STORE from KeyAuthority object");
870             sk_X509_pop_free(chain,X509_free);
871             return false;
872         }
873         
874         X509_STORE_CTX* ctx=X509_STORE_CTX_new();
875         if (!ctx) {
876             log_openssl();
877             unlock();
878             log.error("unable to create X509_STORE_CTX");
879             X509_STORE_free(store);
880             sk_X509_pop_free(chain,X509_free);
881             return false;
882         }
883     
884 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
885         if (X509_STORE_CTX_init(ctx,store,sk_X509_value(chain,0),chain)!=1) {
886             log_openssl();
887             unlock();
888             log.error("unable to initialize X509_STORE_CTX");
889             X509_STORE_CTX_free(ctx);
890             X509_STORE_free(store);
891             sk_X509_pop_free(chain,X509_free);
892             return false;
893         }
894 #else
895         X509_STORE_CTX_init(ctx,store,sk_X509_value(chain,0),chain);
896 #endif
897         if (kauth->m_depth)
898             X509_STORE_CTX_set_depth(ctx,kauth->m_depth);
899     
900         // Add any relevant CRLs.
901         log.debug("obtaining CRLs for this provider/role");
902         Revocation rev(revocations);
903         Iterator<void*> crls=rev.getRevocationLists(provider,role);
904         while (crls.hasNext()) {
905             if (!X509_STORE_add_crl(store,X509_CRL_dup(reinterpret_cast<X509_CRL*>(crls.next())))) {
906                 log_openssl();
907                 log.warn("failed to add CRL");
908             }
909         }
910     
911         int result=X509_verify_cert(ctx);
912         sk_X509_pop_free(chain,X509_free);
913         X509_STORE_CTX_free(ctx);
914         X509_STORE_free(store);
915         unlock();
916         
917         if (result==1) {
918             log.info("successfully validated certificate chain, token signature trusted");
919             return true;
920         }
921         
922         log.error("failed to validate certificate chain, token signature untrusted");
923         return false;
924     }
925     catch (...) {
926         unlock();
927         throw;
928     }       
929 }