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