https://issues.shibboleth.net/jira/browse/SSPCPP-228
[shibboleth/cpp-sp.git] / xmlproviders / XMLTrust.cpp
1 /*
2  *  Copyright 2001-2009 Internet2
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /* XMLTrust.cpp - a trust implementation that uses an XML file
18
19    Scott Cantor
20    9/27/02
21
22    $History:$
23 */
24
25 #include "internal.h"
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29
30 #include <openssl/err.h>
31 #include <openssl/x509v3.h>
32 #include <openssl/x509_vfy.h>
33
34 #include <xercesc/framework/URLInputSource.hpp>
35 #include <xercesc/util/regx/RegularExpression.hpp>
36 #include <xsec/enc/XSECCryptoException.hpp>
37 #include <xsec/enc/XSECKeyInfoResolverDefault.hpp>
38
39 using namespace xmlproviders::logging;
40 using namespace shibboleth;
41 using namespace saml;
42 using namespace std;
43
44 namespace {
45     class XMLTrustImpl : public ReloadableXMLFileImpl
46     {
47     public:
48         XMLTrustImpl(const char* pathname) : ReloadableXMLFileImpl(pathname), m_wildcard(NULL) { init(); }
49         XMLTrustImpl(const DOMElement* e) : ReloadableXMLFileImpl(e), m_wildcard(NULL) { init(); }
50         void init();
51         ~XMLTrustImpl();
52         
53         struct KeyAuthority
54         {
55             KeyAuthority() : m_depth(1) {}
56             ~KeyAuthority();
57             X509_STORE* getX509Store();
58             
59 #ifndef HAVE_GOOD_STL
60             vector<const XMLCh*> m_subjects;
61 #endif
62             vector<X509*> m_certs;
63             vector<X509_CRL*> m_crls;
64             unsigned short m_depth;
65         };
66         
67         vector<DSIGKeyInfoList*> m_keybinds;
68         vector<KeyAuthority*> m_keyauths;
69         KeyAuthority* m_wildcard;
70 #ifdef HAVE_GOOD_STL
71         typedef map<xstring,KeyAuthority*> AuthMap;
72         typedef map<xstring,DSIGKeyInfoList*> BindMap;
73         AuthMap m_authMap;
74         BindMap m_bindMap;
75 #endif
76     };
77
78     class XMLTrust : public ITrust, public ReloadableXMLFile
79     {
80     public:
81         XMLTrust(const DOMElement* e);
82         ~XMLTrust();
83
84     bool validate(void* certEE, const Iterator<void*>& certChain, const IRoleDescriptor* role, bool checkName=true);
85     bool validate(const saml::SAMLSignedObject& token, const IRoleDescriptor* role, ITrust* certValidator=NULL);
86
87     protected:
88         virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
89         virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
90
91         vector<KeyInfoResolver*> m_resolvers;
92         ITrust* m_delegate;
93     };
94 }
95
96 IPlugIn* XMLTrustFactory(const DOMElement* e)
97 {
98     auto_ptr<XMLTrust> t(new XMLTrust(e));
99     t->getImplementation();
100     return t.release();
101 }
102
103
104 ReloadableXMLFileImpl* XMLTrust::newImplementation(const char* pathname, bool first) const
105 {
106     return new XMLTrustImpl(pathname);
107 }
108
109 ReloadableXMLFileImpl* XMLTrust::newImplementation(const DOMElement* e, bool first) const
110 {
111     return new XMLTrustImpl(e);
112 }
113
114 X509_STORE* XMLTrustImpl::KeyAuthority::getX509Store()
115 {
116 #ifdef _DEBUG
117     NDC ndc("getX509Store");
118 #endif
119     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Trust");
120
121     // Load the cert vector into a store.
122     X509_STORE* store=X509_STORE_new();
123     if (!store) {
124         log_openssl();
125         return NULL;
126     }
127 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
128     X509_STORE_set_flags(store,X509_V_FLAG_CRL_CHECK_ALL);
129 #endif
130
131     for (vector<X509*>::iterator j=m_certs.begin(); j!=m_certs.end(); j++) {
132         if (!X509_STORE_add_cert(store,*j)) {
133             log_openssl();
134             log.warn("failed to add cert: %s", (*j)->name);
135             continue;
136         }
137     }
138
139     for (vector<X509_CRL*>::iterator k=m_crls.begin(); k!=m_crls.end(); k++) {
140         if (!X509_STORE_add_crl(store,*k)) {
141             log_openssl();
142             log.warn("failed to add CRL");
143             continue;
144         }
145     }
146
147     return store;
148 }
149
150 XMLTrustImpl::KeyAuthority::~KeyAuthority()
151 {
152     for (vector<X509*>::iterator i=m_certs.begin(); i!=m_certs.end(); i++)
153         X509_free(*i);
154     for (vector<X509_CRL*>::iterator j=m_crls.begin(); j!=m_crls.end(); j++)
155         X509_CRL_free(*j);
156 }
157
158 class KeyInfoNodeFilter : public DOMNodeFilter
159 {
160 public:
161     short acceptNode(const DOMNode* node) const
162     {
163         // Our filter just skips any trees not rooted by ds:KeyInfo.
164         if (node->getNodeType()==DOMNode::ELEMENT_NODE) {
165             if (saml::XML::isElementNamed(static_cast<const DOMElement*>(node),saml::XML::XMLSIG_NS,L(KeyInfo)))
166                 return FILTER_ACCEPT;
167         }
168         return FILTER_REJECT;
169     }
170 };
171
172 void XMLTrustImpl::init()
173 {
174 #ifdef _DEBUG
175     saml::NDC ndc("init");
176 #endif
177     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Trust");
178
179     try {
180         if (!saml::XML::isElementNamed(m_root,::XML::TRUST_NS,SHIB_L(Trust))) {
181             log.error("Construction requires a valid trust file: (trust:Trust as root element)");
182             throw TrustException("Construction requires a valid trust file: (trust:Trust as root element)");
183         }
184
185         // Loop over the KeyAuthority elements.
186         DOMNodeList* nlist=m_root->getElementsByTagNameNS(::XML::TRUST_NS,SHIB_L(KeyAuthority));
187         for (XMLSize_t i=0; nlist && i<nlist->getLength(); i++) {
188             auto_ptr<KeyAuthority> ka(new KeyAuthority());
189             
190             const DOMElement* e=static_cast<DOMElement*>(nlist->item(i));
191             const XMLCh* depth=e->getAttributeNS(NULL,SHIB_L(VerifyDepth));
192             if (depth && *depth)
193                 ka->m_depth=XMLString::parseInt(depth);
194             
195             const DOMElement* k_child=saml::XML::getLastChildElement(e,saml::XML::XMLSIG_NS,L(KeyInfo));
196             if (!k_child) {
197                 log.error("ignoring KeyAuthority element with no ds:KeyInfo");
198                 continue;
199             }
200             const DOMElement* badkeyname=saml::XML::getFirstChildElement(k_child,saml::XML::XMLSIG_NS,SHIB_L(KeyName));
201             if (badkeyname) {
202                 log.error("ignoring KeyAuthority element with embedded ds:KeyName, these must appear only outside of ds:KeyInfo");
203                 continue;
204             }
205             
206             // Very rudimentary, grab up all the in-band X509Certificate elements, and flatten into one list.
207             DOMNodeList* certlist=k_child->getElementsByTagNameNS(saml::XML::XMLSIG_NS,L(X509Certificate));
208             for (XMLSize_t j=0; certlist && j<certlist->getLength(); j++) {
209                 auto_ptr_char blob(certlist->item(j)->getFirstChild()->getNodeValue());
210                 X509* x=B64_to_X509(blob.get());
211                 if (x)
212                     ka->m_certs.push_back(x);
213                 else
214                     log.error("unable to create certificate from inline X509Certificate data");
215             }
216
217             // Now look for externally referenced objects.
218             certlist=k_child->getElementsByTagNameNS(saml::XML::XMLSIG_NS,SHIB_L(RetrievalMethod));
219             for (XMLSize_t k=0; certlist && k<certlist->getLength(); k++) {
220                 DOMElement* cert=static_cast<DOMElement*>(certlist->item(k));
221                 if (!XMLString::compareString(cert->getAttributeNS(NULL,SHIB_L(Type)),::XML::XMLSIG_RETMETHOD_RAWX509)) {
222                     // DER format
223                     auto_ptr_char fname(cert->getAttributeNS(NULL,SHIB_L(URI)));
224                     FILE* f=fopen(fname.get(),"r");
225                     if (f) {
226                         X509* x=NULL;
227                         d2i_X509_fp(f,&x);
228                         if (x) {
229                             ka->m_certs.push_back(x);
230                             continue;
231                         }
232                         else
233                             log_openssl();
234                     }
235                     log.error("unable to create certificate from externally referenced file");
236                 }
237             }
238
239             // Very rudimentary, grab up all the in-band X509CRL elements, and flatten into one list.
240             certlist=k_child->getElementsByTagNameNS(saml::XML::XMLSIG_NS,SHIB_L(X509CRL));
241             for (XMLSize_t r=0; certlist && r<certlist->getLength(); r++) {
242                 auto_ptr_char blob(certlist->item(r)->getFirstChild()->getNodeValue());
243                 X509_CRL* x=B64_to_CRL(blob.get());
244                 if (x)
245                     ka->m_crls.push_back(x);
246                 else
247                     log.warn("unable to create CRL from inline X509CRL data");
248             }
249
250             KeyAuthority* ka2=ka.release();
251             m_keyauths.push_back(ka2);
252             
253             // Now map the ds:KeyName values to the list of certs.
254             bool wildcard=true;
255             DOMElement* sub=saml::XML::getFirstChildElement(e,saml::XML::XMLSIG_NS,SHIB_L(KeyName));
256             while (sub) {
257                 const XMLCh* name=sub->getFirstChild()->getNodeValue();
258                 if (name && *name) {
259                     wildcard=false;
260 #ifdef HAVE_GOOD_STL
261                     m_authMap[name]=ka2;
262 #else
263                     ka2->m_subjects.push_back(name);
264 #endif
265                 }
266                 sub=saml::XML::getNextSiblingElement(sub,saml::XML::XMLSIG_NS,SHIB_L(KeyName));
267             }
268             
269             // If no Subjects, this is a catch-all binding.
270             if (wildcard) {
271                 if (!m_wildcard) {
272                     log.warn("found a wildcard KeyAuthority element, make sure this is what you intend");
273                     m_wildcard=ka2;
274                 }
275                 else
276                     log.warn("found multiple wildcard KeyAuthority elements, ignoring all but the first");
277             }
278         }
279
280         // Now traverse the outer ds:KeyInfo elements. Supposedly this cast just works...
281         int count=0;
282         KeyInfoNodeFilter filter;
283         XSECKeyInfoResolverDefault resolver;
284         DOMTreeWalker* walker=
285             static_cast<DOMDocumentTraversal*>(m_doc)->createTreeWalker(const_cast<DOMElement*>(m_root),DOMNodeFilter::SHOW_ELEMENT,&filter,false);
286         DOMElement* kidom=static_cast<DOMElement*>(walker->firstChild());
287         while (kidom) {
288             count++;
289             DSIGKeyInfoList* KIL = new DSIGKeyInfoList(NULL);
290             // We let XMLSec hack through anything it can. This should evolve over time, or we can
291             // plug in our own KeyResolver later...
292             try {
293                 if (!KIL->loadListFromXML(kidom))
294                     log.error("skipping ds:KeyInfo element (%d) containing unsupported children",count);
295             }
296             catch (XSECCryptoException& xe) {
297                 log.error("unable to process ds:KeyInfo element (%d): %s",count,xe.getMsg());
298             }
299             
300             // Dry run...can we resolve to a key?
301             XSECCryptoKey* key=NULL;
302             try {
303                 key = resolver.resolveKey(KIL);
304             }
305             catch (XSECCryptoException& xe) {
306                 log.error("unable to resolver key from ds:KeyInfo element (%d): %s", count, xe.getMsg());
307             }
308             if (key) {
309                 // So far so good, now look for the name binding(s).
310                 delete key;
311                 bool named=false;
312                 for (size_t index=0; index<KIL->getSize(); index++) {
313                     DSIGKeyInfo* info=KIL->item(index);
314                     const XMLCh* name=info->getKeyName();
315                     if (name && *name) {
316                         if (!named)
317                             m_keybinds.push_back(KIL);
318                         named=true;
319 #ifdef HAVE_GOOD_STL
320                         m_bindMap[name]=KIL;
321 #endif
322                     }
323                 }
324                 if (!named) {
325                     log.warn("skipping ds:KeyInfo binding (%d) that does not contain a usable key name",count);
326                     delete KIL;
327                 }
328             }
329             else {
330                 log.warn("skipping ds:KeyInfo binding (%d) that does not resolve to a key",count);
331                 delete KIL;
332             }
333             kidom=static_cast<DOMElement*>(walker->nextSibling());
334         }
335         walker->release();    // This just cleans up aggressively, but there's no leak if we don't.
336     }
337     catch (SAMLException& e) {
338         log.errorStream() << "Error while parsing trust configuration: " << e.what() << xmlproviders::logging::eol;
339         this->~XMLTrustImpl();
340         throw;
341     }
342 #ifndef _DEBUG
343     catch (...) {
344         log.error("Unexpected error while parsing trust configuration");
345         this->~XMLTrustImpl();
346         throw;
347     }
348 #endif
349 }
350
351 XMLTrustImpl::~XMLTrustImpl()
352 {
353     for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
354         delete (*i);
355     for (vector<DSIGKeyInfoList*>::iterator j=m_keybinds.begin(); j!=m_keybinds.end(); j++)
356         delete (*j);
357 }
358
359 XMLTrust::XMLTrust(const DOMElement* e) : ReloadableXMLFile(e), m_delegate(NULL)
360 {
361     static const XMLCh resolver[] =
362     { chLatin_K, chLatin_e, chLatin_y, chLatin_I, chLatin_n, chLatin_f, chLatin_o,
363       chLatin_R, chLatin_e, chLatin_s, chLatin_o, chLatin_l, chLatin_v, chLatin_e, chLatin_r, chNull
364     };
365
366     static const XMLCh _type[] =
367     { chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull };
368
369     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Trust");
370
371     // Find any KeyResolver plugins.
372     DOMElement* child=saml::XML::getFirstChildElement(e);
373     while (child) {
374         if (!XMLString::compareString(resolver,child->getLocalName()) && child->hasAttributeNS(NULL,_type)) {
375             try {
376                 auto_ptr_char temp(child->getAttributeNS(NULL,_type));
377                 m_resolvers.push_back(KeyInfoResolver::getInstance(temp.get(),child));
378             }
379             catch (SAMLException& ex) {
380                 log.error("caught SAML exception building KeyInfoResolver plugin: %s",ex.what());
381             }
382 #ifndef _DEBUG
383             catch (...) {
384                 log.error("caught unknown exception building KeyInfoResolver plugin");
385             }
386 #endif
387         }
388         child=saml::XML::getNextSiblingElement(child);
389     }
390     m_resolvers.push_back(KeyInfoResolver::getInstance(e));
391
392     try {
393         IPlugIn* plugin=SAMLConfig::getConfig().getPlugMgr().newPlugin(
394             "edu.internet2.middleware.shibboleth.common.provider.ShibbolethTrust",e
395             );
396         m_delegate=dynamic_cast<ITrust*>(plugin);
397         if (!m_delegate) {
398             delete plugin;
399             log.error("plugin was not a trust provider");
400             throw UnsupportedExtensionException("Legacy trust provider requires Shibboleth trust provider in order to function.");
401         }
402     }
403     catch (SAMLException& ex) {
404         log.error("caught SAML exception building embedded trust provider: %s", ex.what());
405         throw;
406     }
407 }
408
409 XMLTrust::~XMLTrust()
410 {
411     delete m_delegate;
412     for (vector<KeyInfoResolver*>::iterator i=m_resolvers.begin(); i!=m_resolvers.end(); i++)
413         delete *i;
414 }
415
416 static int error_callback(int ok, X509_STORE_CTX* ctx)
417 {
418     if (!ok)
419         Category::getInstance("OpenSSL").error("path validation failure: %s", X509_verify_cert_error_string(ctx->error));
420     return ok;
421 }
422
423 bool XMLTrust::validate(void* certEE, const Iterator<void*>& certChain, const IRoleDescriptor* role, bool checkName)
424 {
425     // The delegated trust plugin handles path validation with metadata extensions.
426     // We only take over if the legacy format has to kick in.
427     if (m_delegate->validate(certEE,certChain,role,checkName))
428         return true;
429
430 #ifdef _DEBUG
431     saml::NDC ndc("validate");
432 #endif
433     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Trust");
434
435     if (checkName) {
436         // Before we do the cryptogprahy, check that the EE certificate "name" matches
437         // one of the acceptable key "names" for the signer.
438         vector<string> keynames;
439         
440         // Build a list of acceptable names. Transcode the possible key "names" to UTF-8.
441         // For some simple cases, this should handle UTF-8 encoded DNs in certificates.
442         Iterator<const IKeyDescriptor*> kd_i=role->getKeyDescriptors();
443         while (kd_i.hasNext()) {
444             const IKeyDescriptor* kd=kd_i.next();
445             if (kd->getUse()!=IKeyDescriptor::signing)
446                 continue;
447             DSIGKeyInfoList* KIL=kd->getKeyInfo();
448             if (!KIL)
449                 continue;
450             for (size_t s=0; s<KIL->getSize(); s++) {
451                 const XMLCh* n=KIL->item(s)->getKeyName();
452                 if (n) {
453                     auto_ptr<char> kn(toUTF8(n));
454                     keynames.push_back(kn.get());
455                 }
456             }
457         }
458         auto_ptr<char> kn(toUTF8(role->getEntityDescriptor()->getId()));
459         keynames.push_back(kn.get());
460         
461         X509* x=(X509*)certEE;
462         X509_NAME* subject=X509_get_subject_name(x);
463         if (subject) {
464             // One way is a direct match to the subject DN.
465             // Seems that the way to do the compare is to write the X509_NAME into a BIO.
466             BIO* b = BIO_new(BIO_s_mem());
467             BIO* b2 = BIO_new(BIO_s_mem());
468             // The flags give us LDAP order instead of X.500, with a comma separator.
469             int len=X509_NAME_print_ex(b,subject,0,XN_FLAG_RFC2253);
470             BIO_flush(b);
471             // The flags give us LDAP order instead of X.500, with a comma plus space separator.
472             len=X509_NAME_print_ex(b2,subject,0,XN_FLAG_RFC2253 + XN_FLAG_SEP_CPLUS_SPC - XN_FLAG_SEP_COMMA_PLUS);
473             BIO_flush(b2);
474
475             BUF_MEM* bptr=NULL;
476             BUF_MEM* bptr2=NULL;
477             BIO_get_mem_ptr(b, &bptr);
478             BIO_get_mem_ptr(b2, &bptr2);
479
480             if (bptr && bptr->length > 0 && log.isDebugEnabled()) {
481                 string subjectstr(bptr->data, bptr->length);
482                 log.debug("certificate subject: %s", subjectstr.c_str());
483             }
484             
485             // Check each keyname.
486             for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
487 #ifdef HAVE_STRCASECMP
488             if ((n->length() == bptr->length && !strncasecmp(n->c_str(), bptr->data, bptr->length)) ||
489                 (n->length() == bptr2->length && !strncasecmp(n->c_str(), bptr2->data, bptr2->length))) {
490 #else
491             if ((n->length() == bptr->length && !strnicmp(n->c_str(), bptr->data, bptr->length)) ||
492                 (n->length() == bptr2->length && !strnicmp(n->c_str(), bptr2->data, bptr2->length))) {
493 #endif
494                     log.debug("matched full subject DN to a key name (%s)", n->c_str());
495                     checkName=false;
496                     break;
497                 }
498             }
499             BIO_free(b);
500             BIO_free(b2);
501
502             if (checkName) {
503                 log.debug("unable to match DN, trying TLS subjectAltName match");
504                 STACK_OF(GENERAL_NAME)* altnames=(STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
505                 if (altnames) {
506                     int numalts = sk_GENERAL_NAME_num(altnames);
507                     for (int an=0; checkName && an<numalts; an++) {
508                         const GENERAL_NAME* check = sk_GENERAL_NAME_value(altnames, an);
509                         if (check->type==GEN_DNS || check->type==GEN_URI) {
510                             const char* altptr = (char*)ASN1_STRING_data(check->d.ia5);
511                             const int altlen = ASN1_STRING_length(check->d.ia5);
512                             for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
513 #ifdef HAVE_STRCASECMP
514                                 if ((check->type==GEN_DNS && n->length()==altlen && !strncasecmp(altptr,n->c_str(),altlen))
515 #else
516                                 if ((check->type==GEN_DNS && n->length()==altlen && !strnicmp(altptr,n->c_str(),altlen))
517 #endif
518                                         || (check->type==GEN_URI && n->length()==altlen && !strncmp(altptr,n->c_str(),altlen))) {
519                                     log.debug("matched DNS/URI subjectAltName to a key name (%s)", n->c_str());
520                                     checkName=false;
521                                     break;
522                                 }
523                             }
524                         }
525                     }
526                     GENERAL_NAMES_free(altnames);
527                 }
528                 
529                 if (checkName) {
530                     log.debug("unable to match subjectAltName, trying TLS CN match");
531
532                     // Fetch the last CN RDN.
533                     char* peer_CN = NULL;
534                     int j,i = -1;
535                     while ((j=X509_NAME_get_index_by_NID(subject, NID_commonName, i)) >= 0)
536                         i = j;
537                     if (i >= 0) {
538                         ASN1_STRING* tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject, i));
539                         // Copied in from libcurl.
540                         /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input
541                            is already UTF-8 encoded. We check for this case and copy the raw
542                            string manually to avoid the problem. */
543                         if(tmp && ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
544                             j = ASN1_STRING_length(tmp);
545                             if(j >= 0) {
546                                 peer_CN = (char*)OPENSSL_malloc(j + 1);
547                                 memcpy(peer_CN, ASN1_STRING_data(tmp), j);
548                                 peer_CN[j] = '\0';
549                             }
550                         }
551                         else /* not a UTF8 name */ {
552                             j = ASN1_STRING_to_UTF8(reinterpret_cast<unsigned char**>(&peer_CN), tmp);
553                         }
554
555                         for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
556 #ifdef HAVE_STRCASECMP
557                             if (n->length() == j && !strncasecmp(peer_CN, n->c_str(), j)) {
558 #else
559                             if (n->length() == j && !strnicmp(peer_CN, n->c_str(), j)) {
560 #endif
561                                 log.debug("matched subject CN to a key name (%s)", n->c_str());
562                                 checkName=false;
563                                 break;
564                             }
565                         }
566                         if(peer_CN)
567                             OPENSSL_free(peer_CN);
568                     }
569                     else {
570                         log.warn("no common name in certificate subject");
571                     }
572                 }
573             }
574         }
575         else {
576             log.error("certificate has no subject?!");
577         }
578     }
579
580     if (checkName) {
581         log.error("cannot match certificate subject against acceptable key names based on KeyDescriptors");
582         return false;
583     }
584
585     lock();
586     try {
587         XMLTrustImpl* impl=dynamic_cast<XMLTrustImpl*>(getImplementation());
588     
589         // Build a list of the names to match. We include any named KeyDescriptors, and the provider ID and its groups.
590         vector<const XMLCh*> names;
591         Iterator<const IKeyDescriptor*> kdlist=role->getKeyDescriptors();
592         while (kdlist.hasNext()) {
593             const IKeyDescriptor* kd=kdlist.next();
594             if (kd->getUse()==IKeyDescriptor::encryption)
595                 continue;
596             DSIGKeyInfoList* kilist=kd->getKeyInfo();
597             for (size_t s=0; kilist && s<kilist->getSize(); s++) {
598                 const XMLCh* n=kilist->item(s)->getKeyName();
599                 if (n)
600                     names.push_back(n);
601             }
602         }
603         names.push_back(role->getEntityDescriptor()->getId());
604         const IEntitiesDescriptor* group=role->getEntityDescriptor()->getEntitiesDescriptor();
605         while (group) {
606             if (group->getName())
607                 names.push_back(group->getName());
608             group=group->getEntitiesDescriptor();
609         }
610     
611         // Now check each name.
612         XMLTrustImpl::KeyAuthority* kauth=NULL;
613         for (vector<const XMLCh*>::const_iterator name=names.begin(); !kauth && name!=names.end(); name++) {
614 #ifdef HAVE_GOOD_STL
615             XMLTrustImpl::AuthMap::const_iterator c=impl->m_authMap.find(*name);
616             if (c!=impl->m_authMap.end()) {
617                 kauth=c->second;
618                 if (log.isInfoEnabled()) {
619                     auto_ptr_char temp(*name);
620                     log.info("KeyAuthority 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<XMLTrustImpl::KeyAuthority*>::const_iterator keyauths=impl->m_keyauths.begin(); !kauth && keyauths!=impl->m_keyauths.end(); keyauths++) {
626                 for (vector<const XMLCh*>::const_iterator subs=(*keyauths)->m_subjects.begin(); !kauth && subs!=(*keyauths)->m_subjects.end(); subs++) {
627                     if (!XMLString::compareString(*name,*subs)) {
628                         kauth=*keyauths;
629                         if (log.isInfoEnabled()) {
630                             auto_ptr_char temp(*name);
631                             log.debug("KeyAuthority match on %s",temp.get());
632                         }
633                     }
634                 }
635             }
636 #endif
637         }
638     
639         if (!kauth) {
640             if (impl->m_wildcard) {
641                log.warn("applying wildcard KeyAuthority, use with caution!");
642                 kauth=impl->m_wildcard;
643             }
644             else {
645                 unlock();
646                 log.warn("no KeyAuthority found to validate SSL connection, leaving it alone");
647                 return false;
648             }
649         }
650     
651         log.debug("performing certificate path validation...");
652
653         // If we have a match, use the associated keyauth.
654         X509_STORE* store=kauth->getX509Store();
655         if (store) {
656             STACK_OF(X509)* untrusted=sk_X509_new_null();
657             certChain.reset();
658             while (certChain.hasNext())
659                 sk_X509_push(untrusted,(X509*)certChain.next());
660
661             // This contains the state of the validate operation.
662             X509_STORE_CTX ctx;
663
664             // AFAICT, EE and untrusted are passed in but not owned by the ctx.
665 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
666             if (X509_STORE_CTX_init(&ctx,store,(X509*)certEE,untrusted)!=1) {
667                 log_openssl();
668                 log.error("unable to initialize X509_STORE_CTX");
669                 X509_STORE_free(store);
670                 sk_X509_free(untrusted);
671                 unlock();
672                 return false;
673             }
674 #else
675             X509_STORE_CTX_init(&ctx,store,(X509*)certEE,untrusted);
676 #endif
677             X509_STORE_CTX_set_depth(&ctx,100);    // handle depth below
678             X509_STORE_CTX_set_verify_cb(&ctx,error_callback);
679             
680             int ret=X509_verify_cert(&ctx);
681             if (ret==1) {
682                 // Now see if the depth was acceptable by counting the number of intermediates.
683                 int depth=sk_X509_num(ctx.chain)-2;
684                 if (kauth->m_depth < depth) {
685                     log.error(
686                         "certificate chain was too long (%d intermediates, only %d allowed)",
687                         (depth==-1) ? 0 : depth,
688                         kauth->m_depth
689                         );
690                     ret=0;
691                 }
692             }
693             
694             // Clean up...
695             X509_STORE_CTX_cleanup(&ctx);
696             X509_STORE_free(store);
697
698             if (ret==1) {
699                 log.debug("successfully validated certificate chain");
700                 unlock();
701                 return true;
702             }
703         }
704     }
705     catch (...) {
706         unlock();
707         throw;
708     }
709     unlock();
710     return false;
711 }
712
713 bool XMLTrust::validate(const saml::SAMLSignedObject& token, const IRoleDescriptor* role, ITrust* certValidator)
714 {
715     // The delegated trust plugin handles metadata keys and use of metadata extensions.
716     // If it fails to find an inline key in metadata, then it will branch off to the
717     // extended version and verify the token using the certificates inside it. At that
718     // point, control will pass to the other virtual function above and we can handle
719     // legacy KeyAuthority rules that way.
720     if (m_delegate->validate(token,role,certValidator ? certValidator : this))
721         return true;
722
723 #ifdef _DEBUG
724     saml::NDC ndc("validate");
725 #endif
726     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Trust");
727
728     lock();
729     try {
730         XMLTrustImpl* impl=dynamic_cast<XMLTrustImpl*>(getImplementation());
731
732         // If we actually make it this far, the only case we're handling directly
733         // is an inline key in the old trust file format. Build a list of key names
734         // which will be used to find matching rules.
735         vector<const XMLCh*> names;
736         
737         // Build a list of acceptable names. Transcode the possible key "names" to UTF-8.
738         // For some simple cases, this should handle UTF-8 encoded DNs in certificates.
739         Iterator<const IKeyDescriptor*> kd_i=role->getKeyDescriptors();
740         while (kd_i.hasNext()) {
741             const IKeyDescriptor* kd=kd_i.next();
742             if (kd->getUse()!=IKeyDescriptor::signing)
743                 continue;
744             DSIGKeyInfoList* KIL=kd->getKeyInfo();
745             if (!KIL)
746                 continue;
747             for (size_t s=0; s<KIL->getSize(); s++) {
748                 const XMLCh* n=KIL->item(s)->getKeyName();
749                 if (n)
750                     names.push_back(n);
751             }
752         }
753         names.push_back(role->getEntityDescriptor()->getId());
754
755         log.debug("checking for keys in trust file");
756         DSIGKeyInfoList* KIL=NULL;
757         for (vector<const XMLCh*>::const_iterator name=names.begin(); !KIL && name!=names.end(); name++) {
758 #ifdef HAVE_GOOD_STL
759             XMLTrustImpl::BindMap::const_iterator c=impl->m_bindMap.find(*name);
760             if (c!=impl->m_bindMap.end()) {
761                 KIL=c->second;
762                 if (log.isInfoEnabled()) {
763                     auto_ptr_char temp(*name);
764                     log.info("KeyInfo match on %s",temp.get());
765                 }
766             }
767 #else
768             // Without a decent STL, we trade-off the transcoding by doing a linear search.
769             for (vector<DSIGKeyInfoList*>::const_iterator keybinds=impl->m_keybinds.begin(); !KIL && keybinds!=impl->m_keybinds.end(); keybinds++) {
770                 for (size_t s=0; !KIL && s<(*keybinds)->getSize(); s++) {
771                     if (!XMLString::compareString(*name,(*keybinds)->item(s)->getKeyName())) {
772                         KIL=*keybinds;
773                         if (log.isInfoEnabled()) {
774                             auto_ptr_char temp(*name);
775                             log.debug("KeyInfo match on %s",temp.get());
776                         }
777                     }
778                 }
779             }
780 #endif
781         }
782         
783         if (KIL) {
784             // Any inline KeyInfo should ostensibly resolve to a key we can try.
785             Iterator<KeyInfoResolver*> resolvers(m_resolvers);
786             while (resolvers.hasNext()) {
787                 XSECCryptoKey* key=NULL;
788                 try {
789                     key=((XSECKeyInfoResolver*)*resolvers.next())->resolveKey(KIL);
790                 }
791                 catch (XSECCryptoException& xe) {
792                     log.error("unable to resolver ds:KeyInfo element into key: %s", xe.getMsg());
793                 }
794                 if (key) {
795                     log.debug("resolved key, trying it...");
796                     try {
797                         token.verify(key);
798                         unlock();
799                         log.debug("token verified with KeyInfo, nothing more to verify");
800                         return true;
801                     }
802                     catch (SAMLException& e) {
803                         unlock();
804                         log.warn("verification with inline key failed: %s", e.what());
805                         return false;
806                     }
807                 }
808             }
809             log.warn("KeyInfo in trust provider did not resolve to a key");
810         }
811     }
812     catch (...) {
813         unlock();
814         throw;
815     }       
816
817     unlock();
818     return false;
819 }