Moved property set classes to new library.
[shibboleth/cpp-sp.git] / xmlproviders / XMLTrust.cpp
1 /*
2  *  Copyright 2001-2005 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 <algorithm>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30
31 #include <openssl/err.h>
32 #include <openssl/x509v3.h>
33 #include <openssl/x509_vfy.h>
34
35 #include <log4cpp/Category.hh>
36 #include <xercesc/framework/URLInputSource.hpp>
37 #include <xercesc/util/regx/RegularExpression.hpp>
38 #include <xsec/enc/XSECCryptoException.hpp>
39 #include <xsec/enc/XSECKeyInfoResolverDefault.hpp>
40
41 using namespace shibboleth;
42 using namespace saml;
43 using namespace log4cpp;
44 using namespace std;
45
46 namespace {
47     class XMLTrustImpl : public ReloadableXMLFileImpl
48     {
49     public:
50         XMLTrustImpl(const char* pathname) : ReloadableXMLFileImpl(pathname), m_wildcard(NULL) { init(); }
51         XMLTrustImpl(const DOMElement* e) : ReloadableXMLFileImpl(e), m_wildcard(NULL) { init(); }
52         void init();
53         ~XMLTrustImpl();
54         
55         struct KeyAuthority
56         {
57             KeyAuthority() : m_depth(1) {}
58             ~KeyAuthority();
59             X509_STORE* getX509Store();
60             
61 #ifndef HAVE_GOOD_STL
62             vector<const XMLCh*> m_subjects;
63 #endif
64             vector<X509*> m_certs;
65             vector<X509_CRL*> m_crls;
66             unsigned short m_depth;
67         };
68         
69         vector<DSIGKeyInfoList*> m_keybinds;
70         vector<KeyAuthority*> m_keyauths;
71         KeyAuthority* m_wildcard;
72 #ifdef HAVE_GOOD_STL
73         typedef map<xstring,KeyAuthority*> AuthMap;
74         typedef map<xstring,DSIGKeyInfoList*> BindMap;
75         AuthMap m_authMap;
76         BindMap m_bindMap;
77 #endif
78     };
79
80     class XMLTrust : public ITrust, public ReloadableXMLFile
81     {
82     public:
83         XMLTrust(const DOMElement* e);
84         ~XMLTrust();
85
86     bool validate(void* certEE, const Iterator<void*>& certChain, const IRoleDescriptor* role, bool checkName=true);
87     bool validate(const saml::SAMLSignedObject& token, const IRoleDescriptor* role, ITrust* certValidator=NULL);
88
89     protected:
90         virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
91         virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
92
93         vector<KeyInfoResolver*> m_resolvers;
94         ITrust* m_delegate;
95     };
96 }
97
98 IPlugIn* XMLTrustFactory(const DOMElement* e)
99 {
100     auto_ptr<XMLTrust> t(new XMLTrust(e));
101     t->getImplementation();
102     return t.release();
103 }
104
105
106 ReloadableXMLFileImpl* XMLTrust::newImplementation(const char* pathname, bool first) const
107 {
108     return new XMLTrustImpl(pathname);
109 }
110
111 ReloadableXMLFileImpl* XMLTrust::newImplementation(const DOMElement* e, bool first) const
112 {
113     return new XMLTrustImpl(e);
114 }
115
116 X509_STORE* XMLTrustImpl::KeyAuthority::getX509Store()
117 {
118 #ifdef _DEBUG
119     NDC ndc("getX509Store");
120 #endif
121     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Trust");
122
123     // Load the cert vector into a store.
124     X509_STORE* store=X509_STORE_new();
125     if (!store) {
126         log_openssl();
127         return NULL;
128     }
129 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
130     X509_STORE_set_flags(store,X509_V_FLAG_CRL_CHECK_ALL);
131 #endif
132
133     for (vector<X509*>::iterator j=m_certs.begin(); j!=m_certs.end(); j++) {
134         if (!X509_STORE_add_cert(store,*j)) {
135             log_openssl();
136             log.warn("failed to add cert: %s", (*j)->name);
137             continue;
138         }
139     }
140
141     for (vector<X509_CRL*>::iterator k=m_crls.begin(); k!=m_crls.end(); k++) {
142         if (!X509_STORE_add_crl(store,*k)) {
143             log_openssl();
144             log.warn("failed to add CRL");
145             continue;
146         }
147     }
148
149     return store;
150 }
151
152 XMLTrustImpl::KeyAuthority::~KeyAuthority()
153 {
154     for_each(m_certs.begin(),m_certs.end(),X509_free);
155     for_each(m_crls.begin(),m_crls.end(),X509_CRL_free);
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 (unsigned int 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 (unsigned int 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 (unsigned int 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 (unsigned int 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=resolver.resolveKey(KIL);
302             if (key) {
303                 // So far so good, now look for the name binding(s).
304                 delete key;
305                 bool named=false;
306                 for (size_t index=0; index<KIL->getSize(); index++) {
307                     DSIGKeyInfo* info=KIL->item(index);
308                     const XMLCh* name=info->getKeyName();
309                     if (name && *name) {
310                         if (!named)
311                             m_keybinds.push_back(KIL);
312                         named=true;
313 #ifdef HAVE_GOOD_STL
314                         m_bindMap[name]=KIL;
315 #endif
316                     }
317                 }
318                 if (!named) {
319                     log.warn("skipping ds:KeyInfo binding (%d) that does not contain a usable key name",count);
320                     delete KIL;
321                 }
322             }
323             else {
324                 log.warn("skipping ds:KeyInfo binding (%d) that does not resolve to a key",count);
325                 delete KIL;
326             }
327             kidom=static_cast<DOMElement*>(walker->nextSibling());
328         }
329         walker->release();    // This just cleans up aggressively, but there's no leak if we don't.
330     }
331     catch (SAMLException& e) {
332         log.errorStream() << "Error while parsing trust configuration: " << e.what() << CategoryStream::ENDLINE;
333         this->~XMLTrustImpl();
334         throw;
335     }
336 #ifndef _DEBUG
337     catch (...) {
338         log.error("Unexpected error while parsing trust configuration");
339         this->~XMLTrustImpl();
340         throw;
341     }
342 #endif
343 }
344
345 XMLTrustImpl::~XMLTrustImpl()
346 {
347     for_each(m_keyauths.begin(),m_keyauths.end(),xmltooling::cleanup<KeyAuthority>());
348     for_each(m_keybinds.begin(),m_keybinds.end(),xmltooling::cleanup<DSIGKeyInfoList>());
349 }
350
351 XMLTrust::XMLTrust(const DOMElement* e) : ReloadableXMLFile(e), m_delegate(NULL)
352 {
353     static const XMLCh resolver[] =
354     { chLatin_K, chLatin_e, chLatin_y, chLatin_I, chLatin_n, chLatin_f, chLatin_o,
355       chLatin_R, chLatin_e, chLatin_s, chLatin_o, chLatin_l, chLatin_v, chLatin_e, chLatin_r, chNull
356     };
357
358     static const XMLCh _type[] =
359     { chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull };
360
361     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Trust");
362
363     // Find any KeyResolver plugins.
364     DOMElement* child=saml::XML::getFirstChildElement(e);
365     while (child) {
366         if (!XMLString::compareString(resolver,child->getLocalName()) && child->hasAttributeNS(NULL,_type)) {
367             try {
368                 auto_ptr_char temp(child->getAttributeNS(NULL,_type));
369                 m_resolvers.push_back(KeyInfoResolver::getInstance(temp.get(),child));
370             }
371             catch (SAMLException& ex) {
372                 log.error("caught SAML exception building KeyInfoResolver plugin: %s",ex.what());
373             }
374 #ifndef _DEBUG
375             catch (...) {
376                 log.error("caught unknown exception building KeyInfoResolver plugin");
377             }
378 #endif
379         }
380         child=saml::XML::getNextSiblingElement(child);
381     }
382     m_resolvers.push_back(KeyInfoResolver::getInstance(e));
383
384     try {
385         IPlugIn* plugin=SAMLConfig::getConfig().getPlugMgr().newPlugin(
386             "edu.internet2.middleware.shibboleth.common.provider.ShibbolethTrust",e
387             );
388         m_delegate=dynamic_cast<ITrust*>(plugin);
389         if (!m_delegate) {
390             delete plugin;
391             log.error("plugin was not a trust provider");
392             throw UnsupportedExtensionException("Legacy trust provider requires Shibboleth trust provider in order to function.");
393         }
394     }
395     catch (SAMLException& ex) {
396         log.error("caught SAML exception building embedded trust provider: %s", ex.what());
397         throw;
398     }
399 }
400
401 XMLTrust::~XMLTrust()
402 {
403     delete m_delegate;
404     for_each(m_resolvers.begin(),m_resolvers.end(),xmltooling::cleanup<KeyInfoResolver>());
405 }
406
407 static int error_callback(int ok, X509_STORE_CTX* ctx)
408 {
409     if (!ok)
410         Category::getInstance("OpenSSL").error("path validation failure: %s", X509_verify_cert_error_string(ctx->error));
411     return ok;
412 }
413
414 bool XMLTrust::validate(void* certEE, const Iterator<void*>& certChain, const IRoleDescriptor* role, bool checkName)
415 {
416     // The delegated trust plugin handles path validation with metadata extensions.
417     // We only take over if the legacy format has to kick in.
418     if (m_delegate->validate(certEE,certChain,role,checkName))
419         return true;
420
421 #ifdef _DEBUG
422     saml::NDC ndc("validate");
423 #endif
424     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Trust");
425
426     if (checkName) {
427         // Before we do the cryptogprahy, check that the EE certificate "name" matches
428         // one of the acceptable key "names" for the signer.
429         vector<string> keynames;
430         
431         // Build a list of acceptable names. Transcode the possible key "names" to UTF-8.
432         // For some simple cases, this should handle UTF-8 encoded DNs in certificates.
433         Iterator<const IKeyDescriptor*> kd_i=role->getKeyDescriptors();
434         while (kd_i.hasNext()) {
435             const IKeyDescriptor* kd=kd_i.next();
436             if (kd->getUse()!=IKeyDescriptor::signing)
437                 continue;
438             DSIGKeyInfoList* KIL=kd->getKeyInfo();
439             if (!KIL)
440                 continue;
441             for (size_t s=0; s<KIL->getSize(); s++) {
442                 const XMLCh* n=KIL->item(s)->getKeyName();
443                 if (n) {
444                     auto_ptr<char> kn(toUTF8(n));
445                     keynames.push_back(kn.get());
446                 }
447             }
448         }
449         auto_ptr<char> kn(toUTF8(role->getEntityDescriptor()->getId()));
450         keynames.push_back(kn.get());
451         
452         char buf[256];
453         X509* x=(X509*)certEE;
454         X509_NAME* subject=X509_get_subject_name(x);
455         if (subject) {
456             // One way is a direct match to the subject DN.
457             // Seems that the way to do the compare is to write the X509_NAME into a BIO.
458             BIO* b = BIO_new(BIO_s_mem());
459             BIO* b2 = BIO_new(BIO_s_mem());
460             BIO_set_mem_eof_return(b, 0);
461             BIO_set_mem_eof_return(b2, 0);
462             // The flags give us LDAP order instead of X.500, with a comma separator.
463             int len=X509_NAME_print_ex(b,subject,0,XN_FLAG_RFC2253);
464             string subjectstr,subjectstr2;
465             BIO_flush(b);
466             while ((len = BIO_read(b, buf, 255)) > 0) {
467                 buf[len] = '\0';
468                 subjectstr+=buf;
469             }
470             log.infoStream() << "certificate subject: " << subjectstr << CategoryStream::ENDLINE;
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             while ((len = BIO_read(b2, buf, 255)) > 0) {
475                 buf[len] = '\0';
476                 subjectstr2+=buf;
477             }
478             
479             // Check each keyname.
480             for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
481 #ifdef HAVE_STRCASECMP
482                 if (!strcasecmp(n->c_str(),subjectstr.c_str()) || !strcasecmp(n->c_str(),subjectstr2.c_str())) {
483 #else
484                 if (!_stricmp(n->c_str(),subjectstr.c_str()) || !_stricmp(n->c_str(),subjectstr2.c_str())) {
485 #endif
486                     log.info("matched full subject DN to a key name (%s)", n->c_str());
487                     checkName=false;
488                     break;
489                 }
490             }
491             BIO_free(b);
492             BIO_free(b2);
493
494             if (checkName) {
495                 log.debug("unable to match DN, trying TLS subjectAltName match");
496                 STACK_OF(GENERAL_NAME)* altnames=(STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
497                 if (altnames) {
498                     int numalts = sk_GENERAL_NAME_num(altnames);
499                     for (int an=0; !checkName && an<numalts; an++) {
500                         const GENERAL_NAME* check = sk_GENERAL_NAME_value(altnames, an);
501                         if (check->type==GEN_DNS || check->type==GEN_URI) {
502                             const char* altptr = (char*)ASN1_STRING_data(check->d.ia5);
503                             const int altlen = ASN1_STRING_length(check->d.ia5);
504                             
505                             for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
506 #ifdef HAVE_STRCASECMP
507                                 if (!strncasecmp(altptr,n->c_str(),altlen)) {
508 #else
509                                 if (!_strnicmp(altptr,n->c_str(),altlen)) {
510 #endif
511                                     log.info("matched DNS/URI subjectAltName to a key name (%s)", n->c_str());
512                                     checkName=false;
513                                     break;
514                                 }
515                             }
516                         }
517                     }
518                     GENERAL_NAMES_free(altnames);
519                 }
520                 
521                 if (checkName) {
522                     log.debug("unable to match subjectAltName, trying TLS CN match");
523                     memset(buf,0,sizeof(buf));
524                     if (X509_NAME_get_text_by_NID(subject,NID_commonName,buf,255)>0) {
525                         for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
526 #ifdef HAVE_STRCASECMP
527                             if (!strcasecmp(buf,n->c_str())) {
528 #else
529                             if (!_stricmp(buf,n->c_str())) {
530 #endif
531                                 log.info("matched subject CN to a key name (%s)", n->c_str());
532                                 checkName=false;
533                                 break;
534                             }
535                         }
536                     }
537                     else
538                         log.warn("no common name in certificate subject");
539                 }
540             }
541         }
542         else
543             log.error("certificate has no subject?!");
544     }
545
546     if (checkName) {
547         log.error("cannot match certificate subject against acceptable key names based on KeyDescriptors");
548         return false;
549     }
550
551     lock();
552     try {
553         XMLTrustImpl* impl=dynamic_cast<XMLTrustImpl*>(getImplementation());
554     
555         // Build a list of the names to match. We include any named KeyDescriptors, and the provider ID and its groups.
556         vector<const XMLCh*> names;
557         Iterator<const IKeyDescriptor*> kdlist=role->getKeyDescriptors();
558         while (kdlist.hasNext()) {
559             const IKeyDescriptor* kd=kdlist.next();
560             if (kd->getUse()==IKeyDescriptor::encryption)
561                 continue;
562             DSIGKeyInfoList* kilist=kd->getKeyInfo();
563             for (size_t s=0; kilist && s<kilist->getSize(); s++) {
564                 const XMLCh* n=kilist->item(s)->getKeyName();
565                 if (n)
566                     names.push_back(n);
567             }
568         }
569         names.push_back(role->getEntityDescriptor()->getId());
570         const IEntitiesDescriptor* group=role->getEntityDescriptor()->getEntitiesDescriptor();
571         while (group) {
572             if (group->getName())
573                 names.push_back(group->getName());
574             group=group->getEntitiesDescriptor();
575         }
576     
577         // Now check each name.
578         XMLTrustImpl::KeyAuthority* kauth=NULL;
579         for (vector<const XMLCh*>::const_iterator name=names.begin(); !kauth && name!=names.end(); name++) {
580 #ifdef HAVE_GOOD_STL
581             XMLTrustImpl::AuthMap::const_iterator c=impl->m_authMap.find(*name);
582             if (c!=impl->m_authMap.end()) {
583                 kauth=c->second;
584                 if (log.isInfoEnabled()) {
585                     auto_ptr_char temp(*name);
586                     log.info("KeyAuthority match on %s",temp.get());
587                 }
588             }
589 #else
590             // Without a decent STL, we trade-off the transcoding by doing a linear search.
591             for (vector<XMLTrustImpl::KeyAuthority*>::const_iterator keyauths=impl->m_keyauths.begin(); !kauth && keyauths!=impl->m_keyauths.end(); keyauths++) {
592                 for (vector<const XMLCh*>::const_iterator subs=(*keyauths)->m_subjects.begin(); !kauth && subs!=(*keyauths)->m_subjects.end(); subs++) {
593                     if (!XMLString::compareString(*name,*subs)) {
594                         kauth=*keyauths;
595                         if (log.isInfoEnabled()) {
596                             auto_ptr_char temp(*name);
597                             log.info("KeyAuthority match on %s",temp.get());
598                         }
599                     }
600                 }
601             }
602 #endif
603         }
604     
605         if (!kauth) {
606             if (impl->m_wildcard) {
607                log.warn("applying wildcard KeyAuthority, use with caution!");
608                 kauth=impl->m_wildcard;
609             }
610             else {
611                 unlock();
612                 log.warn("no KeyAuthority found to validate SSL connection, leaving it alone");
613                 return false;
614             }
615         }
616     
617         log.debug("performing certificate path validation...");
618
619         // If we have a match, use the associated keyauth.
620         X509_STORE* store=kauth->getX509Store();
621         if (store) {
622             STACK_OF(X509)* untrusted=sk_X509_new_null();
623             certChain.reset();
624             while (certChain.hasNext())
625                 sk_X509_push(untrusted,(X509*)certChain.next());
626
627             // This contains the state of the validate operation.
628             X509_STORE_CTX ctx;
629
630             // AFAICT, EE and untrusted are passed in but not owned by the ctx.
631 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
632             if (X509_STORE_CTX_init(&ctx,store,(X509*)certEE,untrusted)!=1) {
633                 log_openssl();
634                 log.error("unable to initialize X509_STORE_CTX");
635                 X509_STORE_free(store);
636                 sk_X509_free(untrusted);
637                 unlock();
638                 return false;
639             }
640 #else
641             X509_STORE_CTX_init(&ctx,store,(X509*)certEE,untrusted);
642 #endif
643             X509_STORE_CTX_set_depth(&ctx,100);    // handle depth below
644             X509_STORE_CTX_set_verify_cb(&ctx,error_callback);
645             
646             int ret=X509_verify_cert(&ctx);
647             if (ret==1) {
648                 // Now see if the depth was acceptable by counting the number of intermediates.
649                 int depth=sk_X509_num(ctx.chain)-2;
650                 if (kauth->m_depth < depth) {
651                     log.error(
652                         "certificate chain was too long (%d intermediates, only %d allowed)",
653                         (depth==-1) ? 0 : depth,
654                         kauth->m_depth
655                         );
656                     ret=0;
657                 }
658             }
659             
660             // Clean up...
661             X509_STORE_CTX_cleanup(&ctx);
662             X509_STORE_free(store);
663
664             if (ret==1) {
665                 log.info("successfully validated certificate chain");
666                 unlock();
667                 return true;
668             }
669         }
670     }
671     catch (...) {
672         unlock();
673         throw;
674     }
675     unlock();
676     return false;
677 }
678
679 bool XMLTrust::validate(const saml::SAMLSignedObject& token, const IRoleDescriptor* role, ITrust* certValidator)
680 {
681     // The delegated trust plugin handles metadata keys and use of metadata extensions.
682     // If it fails to find an inline key in metadata, then it will branch off to the
683     // extended version and verify the token using the certificates inside it. At that
684     // point, control will pass to the other virtual function above and we can handle
685     // legacy KeyAuthority rules that way.
686     if (m_delegate->validate(token,role,certValidator ? certValidator : this))
687         return true;
688
689 #ifdef _DEBUG
690     saml::NDC ndc("validate");
691 #endif
692     Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Trust");
693
694     lock();
695     try {
696         XMLTrustImpl* impl=dynamic_cast<XMLTrustImpl*>(getImplementation());
697
698         // If we actually make it this far, the only case we're handling directly
699         // is an inline key in the old trust file format. Build a list of key names
700         // which will be used to find matching rules.
701         vector<const XMLCh*> names;
702         
703         // Build a list of acceptable names. Transcode the possible key "names" to UTF-8.
704         // For some simple cases, this should handle UTF-8 encoded DNs in certificates.
705         Iterator<const IKeyDescriptor*> kd_i=role->getKeyDescriptors();
706         while (kd_i.hasNext()) {
707             const IKeyDescriptor* kd=kd_i.next();
708             if (kd->getUse()!=IKeyDescriptor::signing)
709                 continue;
710             DSIGKeyInfoList* KIL=kd->getKeyInfo();
711             if (!KIL)
712                 continue;
713             for (size_t s=0; s<KIL->getSize(); s++) {
714                 const XMLCh* n=KIL->item(s)->getKeyName();
715                 if (n)
716                     names.push_back(n);
717             }
718         }
719         names.push_back(role->getEntityDescriptor()->getId());
720
721         log.debug("checking for keys in trust file");
722         DSIGKeyInfoList* KIL=NULL;
723         for (vector<const XMLCh*>::const_iterator name=names.begin(); !KIL && name!=names.end(); name++) {
724 #ifdef HAVE_GOOD_STL
725             XMLTrustImpl::BindMap::const_iterator c=impl->m_bindMap.find(*name);
726             if (c!=impl->m_bindMap.end()) {
727                 KIL=c->second;
728                 if (log.isInfoEnabled()) {
729                     auto_ptr_char temp(*name);
730                     log.info("KeyInfo match on %s",temp.get());
731                 }
732             }
733 #else
734             // Without a decent STL, we trade-off the transcoding by doing a linear search.
735             for (vector<DSIGKeyInfoList*>::const_iterator keybinds=impl->m_keybinds.begin(); !KIL && keybinds!=impl->m_keybinds.end(); keybinds++) {
736                 for (size_t s=0; !KIL && s<(*keybinds)->getSize(); s++) {
737                     if (!XMLString::compareString(*name,(*keybinds)->item(s)->getKeyName())) {
738                         KIL=*keybinds;
739                         if (log.isInfoEnabled()) {
740                             auto_ptr_char temp(*name);
741                             log.info("KeyInfo match on %s",temp.get());
742                         }
743                     }
744                 }
745             }
746 #endif
747         }
748         
749         if (KIL) {
750             // Any inline KeyInfo should ostensibly resolve to a key we can try.
751             Iterator<KeyInfoResolver*> resolvers(m_resolvers);
752             while (resolvers.hasNext()) {
753                 XSECCryptoKey* key=((XSECKeyInfoResolver*)*resolvers.next())->resolveKey(KIL);
754                 if (key) {
755                     log.debug("resolved key, trying it...");
756                     try {
757                         token.verify(key);
758                         unlock();
759                         log.info("token verified with KeyInfo, nothing more to verify");
760                         return true;
761                     }
762                     catch (SAMLException& e) {
763                         unlock();
764                         log.warn("verification with inline key failed: %s", e.what());
765                         return false;
766                     }
767                 }
768             }
769             log.warn("KeyInfo in trust provider did not resolve to a key");
770         }
771     }
772     catch (...) {
773         unlock();
774         throw;
775     }       
776
777     unlock();
778     return false;
779 }