ec2c8bf93db7c1eb2bad5e442c2da59f0f3a9ee8
[shibboleth/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 verify_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     class XMLTrustImpl : public ReloadableXMLFileImpl
87     {
88     public:
89         XMLTrustImpl(const char* pathname) : ReloadableXMLFileImpl(pathname) { init(); }
90         XMLTrustImpl(const DOMElement* e) : ReloadableXMLFileImpl(e) { init(); }
91         void init();
92         ~XMLTrustImpl();
93         
94         struct KeyAuthority
95         {
96             KeyAuthority() : m_depth(0) {}
97             ~KeyAuthority();
98             X509_STORE* getX509Store();
99             
100 #ifndef HAVE_GOOD_STL
101             vector<const XMLCh*> m_subjects;
102 #endif
103             vector<X509*> m_certs;
104             unsigned short m_depth;
105         };
106         
107         vector<DSIGKeyInfoList*> m_keybinds;
108         vector<KeyAuthority*> m_keyauths;
109         KeyAuthority* m_wildcard;
110 #ifdef HAVE_GOOD_STL
111         typedef map<xstring,KeyAuthority*> AuthMap;
112         typedef map<xstring,DSIGKeyInfoList*> BindMap;
113         AuthMap m_authMap;
114         BindMap m_bindMap;
115 #endif
116     };
117
118     class XMLTrust : public ITrust, public ReloadableXMLFile
119     {
120     public:
121         XMLTrust(const DOMElement* e) : ReloadableXMLFile(e) {}
122         ~XMLTrust() {}
123
124     bool validate(
125         const saml::Iterator<IRevocation*>& revocations,
126         const IProviderRole* role, const saml::SAMLSignedObject& token,
127         const saml::Iterator<IMetadata*>& metadatas=EMPTY(IMetadata*)
128         ) const;
129     bool attach(const Iterator<IRevocation*>& revocations, const IProviderRole* role, void* ctx) const;
130
131     protected:
132         virtual ReloadableXMLFileImpl* newImplementation(const char* pathname) const;
133         virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e) const;
134     };
135
136 }
137
138 extern "C" ITrust* XMLTrustFactory(const DOMElement* e)
139 {
140     XMLTrust* t=new XMLTrust(e);
141     try {
142         t->getImplementation();
143     }
144     catch (...) {
145         delete t;
146         throw;
147     }
148     return t;    
149 }
150
151
152 ReloadableXMLFileImpl* XMLTrust::newImplementation(const char* pathname) const
153 {
154     return new XMLTrustImpl(pathname);
155 }
156
157 ReloadableXMLFileImpl* XMLTrust::newImplementation(const DOMElement* e) const
158 {
159     return new XMLTrustImpl(e);
160 }
161
162 X509_STORE* XMLTrustImpl::KeyAuthority::getX509Store()
163 {
164     NDC ndc("getX509Store");
165     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrust");
166
167     // Load the cert vector into a store.
168     X509_STORE* store=X509_STORE_new();
169     if (!store) {
170         log_openssl();
171         return NULL;
172     }
173     
174     X509_STORE_set_verify_cb_func(store,verify_callback);
175
176     for (vector<X509*>::iterator j=m_certs.begin(); j!=m_certs.end(); j++) {
177         if (!X509_STORE_add_cert(store,X509_dup(*j))) {
178             log_openssl();
179             log.warn("failed to add cert: %s", (*j)->name);
180             continue;
181         }
182     }
183
184     return store;
185 }
186
187 XMLTrustImpl::KeyAuthority::~KeyAuthority()
188 {
189     for (vector<X509*>::iterator i=m_certs.begin(); i!=m_certs.end(); i++)
190         X509_free(*i);
191 }
192
193 class KeyInfoNodeFilter : public DOMNodeFilter
194 {
195 public:
196     short acceptNode(const DOMNode* node) const
197     {
198         // Our filter just skips any trees not rooted by ds:KeyInfo.
199         if (node->getNodeType()==DOMNode::ELEMENT_NODE) {
200             if (saml::XML::isElementNamed(static_cast<const DOMElement*>(node),saml::XML::XMLSIG_NS,L(KeyInfo)))
201                 return FILTER_ACCEPT;
202         }
203         return FILTER_REJECT;
204     }
205 };
206
207 void XMLTrustImpl::init()
208 {
209     NDC ndc("XMLTrustImpl");
210     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrustImpl");
211
212     try {
213         if (!saml::XML::isElementNamed(m_root,::XML::TRUST_NS,SHIB_L(Trust))) {
214             log.error("Construction requires a valid trust file: (trust:Trust as root element)");
215             throw TrustException("Construction requires a valid trust file: (trust:Trust as root element)");
216         }
217
218         // Loop over the KeyAuthority elements.
219         DOMNodeList* nlist=m_root->getElementsByTagNameNS(::XML::TRUST_NS,SHIB_L(KeyAuthority));
220         for (int i=0; nlist && i<nlist->getLength(); i++) {
221             KeyAuthority* ka=new KeyAuthority();
222             m_keyauths.push_back(ka);
223             
224             DOMElement* e=static_cast<DOMElement*>(nlist->item(i));
225             const XMLCh* depth=e->getAttributeNS(NULL,SHIB_L(VerifyDepth));
226             if (depth && *depth)
227                 ka->m_depth=XMLString::parseInt(depth);
228             
229             // Very rudimentary, grab up all the in-band X509Certificate elements, and flatten into one list.
230             DOMNodeList* certlist=e->getElementsByTagNameNS(saml::XML::XMLSIG_NS,L(X509Certificate));
231             for (int j=0; certlist && j<certlist->getLength(); j++) {
232                 auto_ptr_char blob(certlist->item(j)->getFirstChild()->getNodeValue());
233                 X509* x=B64_to_X509(blob.get());
234                 if (x)
235                     ka->m_certs.push_back(x);
236                 else
237                     log.warn("unable to create certificate from inline X509Certificate data");
238             }
239
240             // Now look for externally referenced objects.
241             certlist=e->getElementsByTagNameNS(saml::XML::XMLSIG_NS,SHIB_L(RetrievalMethod));
242             for (int k=0; certlist && k<certlist->getLength(); k++) {
243                 DOMElement* cert=static_cast<DOMElement*>(certlist->item(k));
244                 if (!XMLString::compareString(cert->getAttributeNS(NULL,SHIB_L(Type)),::XML::XMLSIG_RETMETHOD_RAWX509)) {
245                     // DER format
246                     auto_ptr_char fname(cert->getAttributeNS(NULL,SHIB_L(URI)));
247                     FILE* f=fopen(fname.get(),"r");
248                     if (f) {
249                         X509* x=NULL;
250                         d2i_X509_fp(f,&x);
251                         if (x) {
252                             ka->m_certs.push_back(x);
253                             continue;
254                         }
255                         else
256                             log_openssl();
257                     }
258                     log.warn("unable to create certificate from externally referenced file");
259                 }
260                 else if (!XMLString::compareString(cert->getAttributeNS(NULL,SHIB_L(Type)),::XML::SHIB_RETMETHOD_PEMX509)) {
261                     // PEM format
262                     int count=0;
263                     auto_ptr_char fname(cert->getAttributeNS(NULL,SHIB_L(URI)));
264                     FILE* f=fopen(fname.get(),"r");
265                     if (f) {
266                         X509* x=NULL;
267                         while (x=PEM_read_X509(f,NULL,NULL,NULL)) {
268                             ka->m_certs.push_back(x);
269                             count++;
270                         }
271                     }
272                     if (!count)
273                         log.warn("unable to create certificate from externally referenced file");
274                 }
275             }
276             
277             // Now map the ds:KeyName values to the list of certs.
278             bool wildcard=true;
279             DOMElement* sub=saml::XML::getFirstChildElement(e,saml::XML::XMLSIG_NS,SHIB_L(KeyName));
280             while (sub) {
281                 const XMLCh* name=sub->getFirstChild()->getNodeValue();
282                 if (name && *name) {
283                     wildcard=false;
284 #ifdef HAVE_GOOD_STL
285                     m_authMap[name]=ka;
286 #else
287                     ka->m_subjects.push_back(name);
288 #endif
289                 }
290                 sub=saml::XML::getNextSiblingElement(sub,saml::XML::XMLSIG_NS,SHIB_L(KeyName));
291             }
292             
293             // If no Subjects, this is a catch-all binding.
294             if (wildcard) {
295                 if (!m_wildcard)
296                     m_wildcard=ka;
297                 else
298                     log.warn("found multiple wildcard KeyAuthority elements, ignoring all but the first");
299             }
300         }
301
302         // Now traverse the outer ds:KeyInfo elements. Supposedly this cast just works...
303         int count=0;
304         KeyInfoNodeFilter filter;
305         XSECKeyInfoResolverDefault resolver;
306         DOMTreeWalker* walker=
307             static_cast<DOMDocumentTraversal*>(m_doc)->createTreeWalker(const_cast<DOMElement*>(m_root),DOMNodeFilter::SHOW_ELEMENT,&filter,false);
308         DOMElement* kidom=static_cast<DOMElement*>(walker->firstChild());
309         while (kidom) {
310             count++;
311             DSIGKeyInfoList* KIL = new DSIGKeyInfoList(NULL);
312             // We let XMLSec hack through anything it can. This should evolve over time, or we can
313             // plug in our own KeyResolver later...
314             DOMElement* child=saml::XML::getFirstChildElement(kidom);
315             int count2=1;
316             while (child) {
317                 if (!KIL->addXMLKeyInfo(child))
318                     log.warn("skipped unsupported ds:KeyInfo child element (%d)",count2);
319                 child=saml::XML::getNextSiblingElement(child);
320                 count2++;
321             }
322             
323             // Dry run...can we resolve to a key?
324             XSECCryptoKey* key=resolver.resolveKey(KIL);
325             if (key) {
326                 // So far so good, now look for the name binding(s).
327                 delete key;
328                 bool named=false;
329                 for (size_t index=0; index<KIL->getSize(); index++) {
330                     DSIGKeyInfo* info=KIL->item(index);
331                     const XMLCh* name=info->getKeyName();
332                     if (name && *name) {
333                         if (!named)
334                             m_keybinds.push_back(KIL);
335                         named=true;
336 #ifdef HAVE_GOOD_STL
337                         m_bindMap[name]=KIL;
338 #endif
339                     }
340                 }
341                 if (!named) {
342                     log.warn("skipping ds:KeyInfo binding (%d) that does not contain a usable key name",count);
343                     delete KIL;
344                 }
345             }
346             else {
347                 log.warn("skipping ds:KeyInfo binding (%d) that does not resolve to a key",count);
348                 delete KIL;
349             }
350             kidom=static_cast<DOMElement*>(walker->nextSibling());
351         }
352         walker->release();    // This just cleans up aggressively, but there's no leak if we don't.
353     }
354     catch (SAMLException& e) {
355         log.errorStream() << "Error while parsing trust configuration: " << e.what() << CategoryStream::ENDLINE;
356         for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
357             delete (*i);
358         for (vector<DSIGKeyInfoList*>::iterator j=m_keybinds.begin(); j!=m_keybinds.end(); j++)
359             delete (*j);
360         throw;
361     }
362     catch (...) {
363         log.error("Unexpected error while parsing trust configuration");
364         for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
365             delete (*i);
366         for (vector<DSIGKeyInfoList*>::iterator j=m_keybinds.begin(); j!=m_keybinds.end(); j++)
367             delete (*j);
368         throw;
369     }
370 }
371
372 XMLTrustImpl::~XMLTrustImpl()
373 {
374     for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
375         delete (*i);
376     for (vector<DSIGKeyInfoList*>::iterator j=m_keybinds.begin(); j!=m_keybinds.end(); j++)
377         delete (*j);
378 }
379
380 bool XMLTrust::attach(const Iterator<IRevocation*>& revocations, const IProviderRole* role, void* ctx) const
381 {
382     saml::NDC ndc("attach");
383     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrust");
384     XMLTrustImpl* impl=dynamic_cast<XMLTrustImpl*>(getImplementation());
385
386     // Build a list of the names to match. We include any named KeyDescriptors, and the provider ID and its groups.
387     vector<const XMLCh*> names;
388     Iterator<const IKeyDescriptor*> kdlist=role->getKeyDescriptors();
389     while (kdlist.hasNext()) {
390         const IKeyDescriptor* kd=kdlist.next();
391         if (kd->getUse()!=IKeyDescriptor::signing)
392             continue;
393         DSIGKeyInfoList* kilist=kd->getKeyInfo();
394         for (size_t s=0; kilist && s<kilist->getSize(); s++) {
395             const XMLCh* n=kilist->item(s)->getKeyName();
396             if (n)
397                 names.push_back(n);
398         }
399     }
400     names.push_back(role->getProvider()->getId());
401     Iterator<const XMLCh*> groups=role->getProvider()->getGroups();
402     while (groups.hasNext())
403         names.push_back(groups.next());
404
405     // Now check each name.
406     XMLTrustImpl::KeyAuthority* kauth=NULL;
407     for (vector<const XMLCh*>::const_iterator name=names.begin(); name!=names.end(); name++) {
408 #ifdef HAVE_GOOD_STL
409         XMLTrustImpl::AuthMap::const_iterator c=impl->m_authMap.find(*name);
410         if (c!=impl->m_authMap.end()) {
411             kauth=c->second;
412             if (log.isDebugEnabled()) {
413                 auto_ptr_char temp(*name);
414                 log.debug("KeyAuthority match on %s",temp.get());
415             }
416         }
417 #else
418         // Without a decent STL, we trade-off the transcoding by doing a linear search.
419         for (vector<XMLTrustImpl::KeyAuthority*>::const_iterator keyauths=impl->m_keyauths.begin(); keyauths!=impl->m_keyauths.end(); keyauths++) {
420             for (vector<const XMLCh*>::const_iterator subs=keyauths->m_subjects.begin(); subs!=keyauths->m_subjects.end(); subs++) {
421                 if (!XMLString::compareString(*name,*subs)) {
422                     kauth=*keyauths;
423                     if (log.isDebugEnabled()) {
424                         auto_ptr_char temp(*name);
425                         log.debug("KeyAuthority match on %s",temp.get());
426                     }
427                 }
428             }
429         }
430 #endif
431     }
432
433     if (!kauth) {
434         if (impl->m_wildcard) {
435            log.warn("applying wildcard KeyAuthority, use with caution!");
436             kauth=impl->m_wildcard;
437         }
438         else {
439             log.error("no KeyAuthority found to validate SSL connection, leaving it alone");
440             return false;
441         }
442     }
443
444     // If we have a match, use the associated keyauth.
445     X509_STORE* store=kauth->getX509Store();
446     if (store) {
447       
448         // Add any relevant CRLs.
449         log.debug("obtaining CRLs for this provider/role");
450         Revocation rev(revocations);
451         Iterator<void*> crls=rev.getRevocationLists(role->getProvider(),role);
452         while (crls.hasNext()) {
453             if (!X509_STORE_add_crl(store,X509_CRL_dup(reinterpret_cast<X509_CRL*>(crls.next())))) {
454                 log_openssl();
455                 log.warn("failed to add CRL");
456             }
457         }
458         
459         // Apply store to this context.
460         SSL_CTX_set_cert_store(reinterpret_cast<SSL_CTX*>(ctx),store);
461         SSL_CTX_set_verify_depth(reinterpret_cast<SSL_CTX*>(ctx),kauth->m_depth);
462         
463     }
464     return true;
465 }
466
467 bool XMLTrust::validate(
468     const saml::Iterator<IRevocation*>& revocations,
469     const IProviderRole* role, const saml::SAMLSignedObject& token,
470     const saml::Iterator<IMetadata*>& metadatas
471     ) const
472 {
473     NDC ndc("validate");
474     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrust");
475     XMLTrustImpl* impl=dynamic_cast<XMLTrustImpl*>(getImplementation());
476
477     // This is where we're going to hide all the juicy SAML trust bits. If we botch it
478     // we can just plug in a new version, hopefully.
479
480     Metadata metadata(metadatas);   // With luck we won't need this.
481
482     // Did the caller tell us about the signer?
483     const IProvider* provider=(role ? role->getProvider() : NULL);
484     if (!provider) {
485         log.debug("no role descriptor passed in, trying to map token to provider");
486         
487         // The first step is to identify the provider responsible for signing the token.
488         // We can't narrow it down to role, because we don't know why the token is being validated.
489         
490         // If it's an assertion, this isn't terribly hard, but we need to hack in support for both
491         // Issuer and NameQualifier as a provider ID. Issuer will be the main one going forward.
492         // 1.0/1.1 origins will be sending a hostname as Issuer, but this won't hit the metadata lookup
493         // and we'll fall back to NameQualifier. Non-Shib SAML origins generally would be based on Issuer.
494         
495         // Responses allow us to try and locate a provider by checking the assertion(s) inside.
496         // Technically somebody could enclose somebody else's assertions, but if the use case is
497         // that advanced, we're probably into SAML 2.0 and we'll have Issuer up top.
498         
499         // Requests...umm, pretty much out of luck. We'll apply our own hack if there's an
500         // attribute query, and use Resource.
501         
502         if (typeid(token)==typeid(SAMLResponse)) {
503             Iterator<SAMLAssertion*> assertions=dynamic_cast<const SAMLResponse&>(token).getAssertions();
504             while (!provider && assertions.hasNext()) {
505                 SAMLAssertion* assertion=assertions.next();
506                 provider=metadata.lookup(assertion->getIssuer());
507                 if (!provider) {
508                     Iterator<SAMLStatement*> statements=assertion->getStatements();
509                     while (!provider && statements.hasNext()) {
510                         SAMLSubjectStatement* statement=dynamic_cast<SAMLSubjectStatement*>(statements.next());
511                         if (statement && statement->getSubject()->getNameQualifier())
512                             provider=metadata.lookup(statement->getSubject()->getNameQualifier());
513                     }
514                 }
515             }
516         }
517         else if (typeid(token)==typeid(SAMLAssertion)) {
518             provider=metadata.lookup(dynamic_cast<const SAMLAssertion&>(token).getIssuer());
519             if (!provider) {
520                 Iterator<SAMLStatement*> statements=dynamic_cast<const SAMLAssertion&>(token).getStatements();
521                 while (!provider && statements.hasNext()) {
522                     SAMLSubjectStatement* statement=dynamic_cast<SAMLSubjectStatement*>(statements.next());
523                     if (statement && statement->getSubject()->getNameQualifier())
524                         provider=metadata.lookup(statement->getSubject()->getNameQualifier());
525                 }
526             }
527         }
528         else if (typeid(token)==typeid(SAMLRequest)) {
529             const SAMLQuery* q=dynamic_cast<const SAMLRequest&>(token).getQuery();
530             if (q && dynamic_cast<const SAMLAttributeQuery*>(q))
531                 provider=metadata.lookup(dynamic_cast<const SAMLAttributeQuery*>(q)->getResource());
532         }
533         
534         // If we still don't have a provider, there's no likely basis for trust,
535         // but a wildcard KeyAuthority might apply.
536         if (log.isInfoEnabled() && provider) {
537             auto_ptr_char temp(provider->getId());
538             log.info("mapped signed token to provider: %s", temp.get());
539         }
540         else if (!provider)
541             log.warn("unable to map signed token to provider, only wildcarded trust will apply");
542     }
543     
544     vector<const XMLCh*> names;
545     XSECKeyInfoResolverDefault keyResolver;
546     
547     // First, try to resolve a KeyDescriptor from the role into an actual key.
548     // That's the simplest case. Failing that, remember any key names we run across.
549     
550     if (role) {
551         log.debug("checking for key descriptors that resolve directly");
552         Iterator<const IKeyDescriptor*> kd_i=role->getKeyDescriptors();
553         while (kd_i.hasNext()) {
554             const IKeyDescriptor* kd=kd_i.next();
555             if (kd->getUse()!=IKeyDescriptor::signing)
556                 continue;
557             DSIGKeyInfoList* KIL=kd->getKeyInfo();
558             if (!KIL)
559                 continue;
560             XSECCryptoKey* key=keyResolver.resolveKey(KIL);
561             if (key) {
562                 log.debug("found an inline key, trying it...");
563                 try {
564                     token.verify(key);
565                     log.info("token verified with inline key, nothing more to verify");
566                     return true;
567                 }
568                 catch (SAMLException& e) {
569                     log.debug("inline key failed: %s", e.what());
570                 }
571             }
572             else {
573                 for (size_t s=0; s<KIL->getSize(); s++) {
574                     const XMLCh* n=KIL->item(s)->getKeyName();
575                     if (n)
576                         names.push_back(n);
577                 }
578             }
579         }
580     }
581     
582     // Push the provider ID on the key name list. We don't push provider groups in, since
583     // matching groups to a key makes no sense.
584     if (provider)
585         names.push_back(provider->getId());
586     
587     // No keys inline in metadata. Now we try and find a key inline in trust.
588     log.debug("checking for keys in trust file");
589     DSIGKeyInfoList* KIL=NULL;
590     for (vector<const XMLCh*>::const_iterator name=names.begin(); name!=names.end(); name++) {
591 #ifdef HAVE_GOOD_STL
592         XMLTrustImpl::BindMap::const_iterator c=impl->m_bindMap.find(*name);
593         if (c!=impl->m_bindMap.end()) {
594             KIL=c->second;
595             if (log.isDebugEnabled()) {
596                 auto_ptr_char temp(*name);
597                 log.debug("KeyInfo match on %s",temp.get());
598             }
599         }
600 #else
601         // Without a decent STL, we trade-off the transcoding by doing a linear search.
602         for (vector<XMLTrustImpl::DSIGKeyInfoList*>::const_iterator keybinds=impl->m_keybinds.begin(); keybinds!=impl->m_keybinds.end(); keybinds++) {
603             for (size_t s=0; s<(*keybinds)->getSize(); s++) {
604                 if (!XMLString::compareString(*name,(*keybinds)->item(s)->getKeyName())) {
605                     KIL=*keybinds;
606                     if (log.isDebugEnabled()) {
607                         auto_ptr_char temp(*name);
608                         log.debug("KeyInfo match on %s",temp.get());
609                     }
610                 }
611             }
612         }
613 #endif
614     }
615     
616     if (KIL) {
617         // Any inline KeyInfo should ostensible resolve to a key we can try.
618         XSECCryptoKey* key=keyResolver.resolveKey(KIL);
619         if (key) {
620             log.debug("resolved key, trying it...");
621             try {
622                 token.verify(key);
623                 log.info("token verified with KeyInfo, nothing more to verify");
624                 return true;
625             }
626             catch (SAMLException& e) {
627                 log.debug("inline key failed: %s", e.what());
628             }
629         }
630         else
631             log.warn("KeyInfo in trust provider did not resolve to a key");
632     }
633     
634     // Direct key verification hasn't worked. Now we have to switch over to KeyAuthority-based
635     // validation. The actual verification key has to be inside the token.
636     log.debug("verifying signature using key inside token...");
637     try {
638         token.verify();
639         log.info("verified with key inside token, entering validation stage");
640     }
641     catch (SAMLException& e) {
642         log.debug("verification using key inside token failed: %s", e.what());
643         return false;
644     }
645     
646     // Before we do the cryptogprahy, check that the EE certificate "name" matches
647     // one of the acceptable key "names" for the signer. Without this, we have a gaping
648     // hole in the validation.
649     log.debug("matching token's certificate subject against valid key names...");
650     vector<const XMLCh*> certs;
651     for (unsigned int i=0; i<token.getX509CertificateCount(); i++)
652         certs.push_back(token.getX509Certificate(i));
653
654     // Decode the EE cert.
655     auto_ptr_char EE(certs[0]);
656     X509* x=B64_to_X509(EE.get());
657     if (!x) {
658         log.error("unable to decode X.509 signing certificate");
659         return false;
660     }
661     
662     // Transcode the possible key "names" to UTF-8. For some simple cases, this should
663     // handle UTF-8 encoded DNs in certificates.
664     vector<string> keynames;
665     Iterator<const XMLCh*> iname(names);
666     while (iname.hasNext()) {
667         auto_ptr<char> kn(toUTF8(iname.next()));
668         keynames.push_back(kn.get());
669     }
670     
671     bool match=false;
672     char buf[256];
673     X509_NAME* subject=X509_get_subject_name(x);
674     if (subject) {
675         // The best way is a direct match to the subject DN. We should encourage this.
676         // Seems that the way to do the compare is to write the X509_NAME into a BIO.
677         // Believe this will give us RFC 2253 / LDAP syntax...
678         BIO* b = BIO_new(BIO_s_mem());
679         if (b) {
680             BIO_set_mem_eof_return(b, 0);
681             // The DN_REV flag gives us LDAP order instead of X.500
682             int len=X509_NAME_print_ex(b,subject,0,XN_FLAG_SEP_COMMA_PLUS|XN_FLAG_DN_REV);
683             if (len) {
684                 BIO_flush(b);
685                 string subjectstr;
686                 while ((len = BIO_read(b, buf, 255)) > 0) {
687                     buf[len] = '\0';
688                     subjectstr+=buf;
689                 }
690                 log.infoStream() << "certificate subject: " << subjectstr << CategoryStream::ENDLINE;
691                 // Check each keyname.
692                 for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
693 #ifdef HAVE_STRCASECMP
694                     if (!strcasecmp(n->c_str(),subjectstr.c_str())) {
695 #else
696                     if (!stricmp(n->c_str(),subjectstr.c_str())) {
697 #endif
698                         log.info("matched full subject DN to a key name");
699                         match=true;
700                         break;
701                     }
702                 }
703             }
704             else
705                 log.error("certificate has no subject?!");
706             BIO_free(b);
707         }
708         else
709             log.error("unable to obtain memory BIO from OpenSSL");
710         
711         if (!match) {
712             log.debug("unable to match DN, trying TLS-style hostname match");
713             memset(buf,0,sizeof(buf));
714             if (X509_NAME_get_text_by_NID(subject,NID_commonName,buf,255)>0) {
715                 for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
716 #ifdef HAVE_STRCASECMP
717                     if (!strcasecmp(buf,n->c_str())) {
718 #else
719                     if (!stricmp(buf,n->c_str())) {
720 #endif
721                         log.info("matched subject CN to a key name");
722                         match=true;
723                         break;
724                     }
725                 }
726             }
727             else
728                 log.warn("no common name in certificate subject");
729             
730             if (!match) {
731                 log.debug("unable to match CN, trying DNS subjectAltName");
732                 int extcount=X509_get_ext_count(x);
733                 for (int c=0; c<extcount; c++) {
734                     X509_EXTENSION* ext=X509_get_ext(x,c);
735                     const char* extstr=OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
736                     if (!strcmp(extstr,"subjectAltName")) {
737                         X509V3_EXT_METHOD* meth=X509V3_EXT_get(ext);
738                         if (!meth || !meth->d2i || !meth->i2v || !ext->value->data) // had to add all these to prevent crashing
739                             break;
740                         unsigned char* data=ext->value->data;
741                         STACK_OF(CONF_VALUE)* val=meth->i2v(meth,meth->d2i(NULL,&data,ext->value->length),NULL);
742                         for (int j=0; j<sk_CONF_VALUE_num(val); j++) {
743                             CONF_VALUE* nval=sk_CONF_VALUE_value(val,j);
744                             if (!strcmp(nval->name,"DNS")) {
745                                 for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
746 #ifdef HAVE_STRCASECMP
747                                     if (!strcasecmp(nval->value,n->c_str())) {
748 #else
749                                     if (!stricmp(nval->value,n->c_str())) {
750 #endif
751                                         log.info("matched DNS subjectAltName to a key name");
752                                         match=true;
753                                         break;
754                                     }
755                                 }
756                             }
757                         }
758                     }
759                 }
760             }
761         }
762     }
763     else
764         log.error("certificate has no subject?!");
765
766     X509_free(x);
767
768     if (!match) {
769         log.error("cannot match certificate subject against provider's key names");
770         return false;
771     }
772
773     // We're ready for the final stage.
774     log.debug("final step, certificate path validation...");
775
776     // Push any provider groups on the name match list.
777     if (provider) {
778         Iterator<const XMLCh*> groups=provider->getGroups();
779         while (groups.hasNext())
780             names.push_back(groups.next());
781     }
782
783     // Now we hunt the list for a KeyAuthority that matches one of the names.
784     XMLTrustImpl::KeyAuthority* kauth=NULL;
785     for (vector<const XMLCh*>::const_iterator name2=names.begin(); name2!=names.end(); name2++) {
786 #ifdef HAVE_GOOD_STL
787         XMLTrustImpl::AuthMap::const_iterator c=impl->m_authMap.find(*name2);
788         if (c!=impl->m_authMap.end()) {
789             kauth=c->second;
790             if (log.isDebugEnabled()) {
791                 auto_ptr_char temp(*name2);
792                 log.debug("KeyAuthority match on %s",temp.get());
793             }
794         }
795 #else
796         // Without a decent STL, we trade-off the transcoding by doing a linear search.
797         for (vector<XMLTrustImpl::KeyAuthority*>::const_iterator keyauths=impl->m_keyauths.begin(); keyauths!=impl->m_keyauths.end(); keyauths++) {
798             for (vector<const XMLCh*>::const_iterator subs=keyauths->m_subjects.begin(); subs!=keyauths->m_subjects.end(); subs++) {
799                 if (!XMLString::compareString(*name2,*subs)) {
800                     kauth=*keyauths;
801                     if (log.isDebugEnabled()) {
802                         auto_ptr_char temp(*name2);
803                         log.debug("KeyAuthority match on %s",temp.get());
804                     }
805                 }
806             }
807         }
808 #endif
809     }
810
811     if (!kauth) {
812         if (impl->m_wildcard) {
813            log.warn("applying wildcard KeyAuthority, use with caution!");
814             kauth=impl->m_wildcard;
815         }
816         else {
817             log.error("no KeyAuthority found to validate the token, leaving untrusted");
818             return false;
819         }
820     }
821
822     log.debug("building untrusted certificate chain from signature");
823     STACK_OF(X509)* chain=sk_X509_new_null();
824     Iterator<const XMLCh*> icerts(certs);
825     while (icerts.hasNext()) {
826         auto_ptr_char xbuf(icerts.next());
827         X509* x=B64_to_X509(xbuf.get());
828         if (!x) {
829             log.error("unable to parse certificate in signature");
830             sk_X509_pop_free(chain,X509_free);
831             return false;
832         }
833         sk_X509_push(chain,x);
834     }
835
836     X509_STORE* store=kauth->getX509Store();
837     if (!store) {
838         log.error("unable to load X509_STORE from KeyAuthority object");
839         sk_X509_pop_free(chain,X509_free);
840         return false;
841     }
842     
843     X509_STORE_CTX* ctx=X509_STORE_CTX_new();
844     if (!ctx) {
845         log_openssl();
846         log.error("unable to create X509_STORE_CTX");
847         sk_X509_pop_free(chain,X509_free);
848         return false;
849     }
850
851 #if (OPENSSL_VERSION_NUMBER > 0x009070000L)
852     if (X509_STORE_CTX_init(ctx,store,sk_X509_value(chain,0),chain)!=1) {
853         log_openssl();
854         log.error("unable to initialize X509_STORE_CTX");
855         sk_X509_pop_free(chain,X509_free);
856         return false;
857     }
858 #else
859     X509_STORE_CTX_init(ctx,store,sk_X509_value(chain,0),chain);
860 #endif
861     if (kauth->m_depth)
862         X509_STORE_CTX_set_depth(ctx,kauth->m_depth);
863
864     // Add any relevant CRLs.
865     log.debug("obtaining CRLs for this provider/role");
866     Revocation rev(revocations);
867     Iterator<void*> crls=rev.getRevocationLists(provider,role);
868     while (crls.hasNext()) {
869         if (!X509_STORE_add_crl(store,X509_CRL_dup(reinterpret_cast<X509_CRL*>(crls.next())))) {
870             log_openssl();
871             log.warn("failed to add CRL");
872         }
873     }
874
875     int result=X509_verify_cert(ctx);
876     sk_X509_pop_free(chain,X509_free);
877     X509_STORE_CTX_free(ctx);
878
879     if (result==1) {
880         log.info("successfully validated certificate chain, token signature trusted");
881         return true;
882     }
883     
884     log.error("failed to validate certificate chain, token signature untrusted");
885     return false;
886 }