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