2 * The Shibboleth License, Version 1.
4 * University Corporation for Advanced Internet Development, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
14 * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution, if any, must include
17 * the following acknowledgment: "This product includes software developed by
18 * the University Corporation for Advanced Internet Development
19 * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
20 * may appear in the software itself, if and wherever such third-party
21 * acknowledgments normally appear.
23 * Neither the name of Shibboleth nor the names of its contributors, nor
24 * Internet2, nor the University Corporation for Advanced Internet Development,
25 * Inc., nor UCAID may be used to endorse or promote products derived from this
26 * software without specific prior written permission. For written permission,
27 * please contact shibboleth@shibboleth.org
29 * Products derived from this software may not be called Shibboleth, Internet2,
30 * UCAID, or the University Corporation for Advanced Internet Development, nor
31 * may Shibboleth appear in their name, without prior written permission of the
32 * University Corporation for Advanced Internet Development.
35 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36 * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
38 * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
39 * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
40 * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
41 * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
42 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50 /* XMLTrust.cpp - a trust implementation that uses an XML file
60 #include <sys/types.h>
63 #include <openssl/err.h>
64 #include <openssl/x509v3.h>
65 #include <openssl/x509_vfy.h>
67 #include <log4cpp/Category.hh>
68 #include <xercesc/framework/URLInputSource.hpp>
69 #include <xercesc/util/regx/RegularExpression.hpp>
70 #include <xsec/enc/XSECCryptoException.hpp>
71 #include <xsec/enc/XSECKeyInfoResolverDefault.hpp>
73 using namespace shibboleth;
75 using namespace log4cpp;
80 int logging_callback(int ok, X509_STORE_CTX* store)
83 Category::getInstance("OpenSSL").error("verify_callback error: %s", X509_verify_cert_error_string(store->error));
87 int verify_callback(X509_STORE_CTX* ctx, void* arg)
89 Category::getInstance("OpenSSL").debug("invoking default X509 verify callback");
90 return X509_verify_cert(ctx);
93 class XMLTrustImpl : public ReloadableXMLFileImpl
96 XMLTrustImpl(const char* pathname) : ReloadableXMLFileImpl(pathname), m_wildcard(NULL) { init(); }
97 XMLTrustImpl(const DOMElement* e) : ReloadableXMLFileImpl(e), m_wildcard(NULL) { init(); }
103 KeyAuthority() : m_depth(1) {}
105 X509_STORE* getX509Store();
107 #ifndef HAVE_GOOD_STL
108 vector<const XMLCh*> m_subjects;
110 vector<X509*> m_certs;
111 unsigned short m_depth;
114 vector<DSIGKeyInfoList*> m_keybinds;
115 vector<KeyAuthority*> m_keyauths;
116 KeyAuthority* m_wildcard;
118 typedef map<xstring,KeyAuthority*> AuthMap;
119 typedef map<xstring,DSIGKeyInfoList*> BindMap;
125 class XMLTrust : public ITrust, public ReloadableXMLFile
128 XMLTrust(const DOMElement* e) : ReloadableXMLFile(e) {}
132 const saml::Iterator<IRevocation*>& revocations,
133 const IRoleDescriptor* role, const saml::SAMLSignedObject& token,
134 const saml::Iterator<IMetadata*>& metadatas=EMPTY(IMetadata*)
136 bool attach(const Iterator<IRevocation*>& revocations, const IRoleDescriptor* role, void* ctx);
139 virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
140 virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
145 IPlugIn* XMLTrustFactory(const DOMElement* e)
147 XMLTrust* t=new XMLTrust(e);
149 t->getImplementation();
159 ReloadableXMLFileImpl* XMLTrust::newImplementation(const char* pathname, bool first) const
161 return new XMLTrustImpl(pathname);
164 ReloadableXMLFileImpl* XMLTrust::newImplementation(const DOMElement* e, bool first) const
166 return new XMLTrustImpl(e);
169 X509_STORE* XMLTrustImpl::KeyAuthority::getX509Store()
171 NDC ndc("getX509Store");
172 Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrust");
174 // Load the cert vector into a store.
175 X509_STORE* store=X509_STORE_new();
181 for (vector<X509*>::iterator j=m_certs.begin(); j!=m_certs.end(); j++) {
182 if (!X509_STORE_add_cert(store,X509_dup(*j))) {
184 log.warn("failed to add cert: %s", (*j)->name);
192 XMLTrustImpl::KeyAuthority::~KeyAuthority()
194 for (vector<X509*>::iterator i=m_certs.begin(); i!=m_certs.end(); i++)
198 class KeyInfoNodeFilter : public DOMNodeFilter
201 short acceptNode(const DOMNode* node) const
203 // Our filter just skips any trees not rooted by ds:KeyInfo.
204 if (node->getNodeType()==DOMNode::ELEMENT_NODE) {
205 if (saml::XML::isElementNamed(static_cast<const DOMElement*>(node),saml::XML::XMLSIG_NS,L(KeyInfo)))
206 return FILTER_ACCEPT;
208 return FILTER_REJECT;
212 void XMLTrustImpl::init()
214 NDC ndc("XMLTrustImpl");
215 Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrustImpl");
218 if (!saml::XML::isElementNamed(m_root,::XML::TRUST_NS,SHIB_L(Trust))) {
219 log.error("Construction requires a valid trust file: (trust:Trust as root element)");
220 throw TrustException("Construction requires a valid trust file: (trust:Trust as root element)");
223 // Loop over the KeyAuthority elements.
224 DOMNodeList* nlist=m_root->getElementsByTagNameNS(::XML::TRUST_NS,SHIB_L(KeyAuthority));
225 for (int i=0; nlist && i<nlist->getLength(); i++) {
226 auto_ptr<KeyAuthority> ka(new KeyAuthority());
228 const DOMElement* e=static_cast<DOMElement*>(nlist->item(i));
229 const XMLCh* depth=e->getAttributeNS(NULL,SHIB_L(VerifyDepth));
231 ka->m_depth=XMLString::parseInt(depth);
233 const DOMElement* k_child=saml::XML::getLastChildElement(e,saml::XML::XMLSIG_NS,L(KeyInfo));
235 log.error("ignoring KeyAuthority element with no ds:KeyInfo");
238 const DOMElement* badkeyname=saml::XML::getFirstChildElement(k_child,saml::XML::XMLSIG_NS,SHIB_L(KeyName));
240 log.error("ignoring KeyAuthority element with embedded ds:KeyName, these must appear only outside of ds:KeyInfo");
244 // Very rudimentary, grab up all the in-band X509Certificate elements, and flatten into one list.
245 DOMNodeList* certlist=k_child->getElementsByTagNameNS(saml::XML::XMLSIG_NS,L(X509Certificate));
246 for (int j=0; certlist && j<certlist->getLength(); j++) {
247 auto_ptr_char blob(certlist->item(j)->getFirstChild()->getNodeValue());
248 X509* x=B64_to_X509(blob.get());
250 ka->m_certs.push_back(x);
252 log.error("unable to create certificate from inline X509Certificate data");
255 // Now look for externally referenced objects.
256 certlist=k_child->getElementsByTagNameNS(saml::XML::XMLSIG_NS,SHIB_L(RetrievalMethod));
257 for (int k=0; certlist && k<certlist->getLength(); k++) {
258 DOMElement* cert=static_cast<DOMElement*>(certlist->item(k));
259 if (!XMLString::compareString(cert->getAttributeNS(NULL,SHIB_L(Type)),::XML::XMLSIG_RETMETHOD_RAWX509)) {
261 auto_ptr_char fname(cert->getAttributeNS(NULL,SHIB_L(URI)));
262 FILE* f=fopen(fname.get(),"r");
267 ka->m_certs.push_back(x);
273 log.error("unable to create certificate from externally referenced file");
275 else if (!XMLString::compareString(cert->getAttributeNS(NULL,SHIB_L(Type)),::XML::SHIB_RETMETHOD_PEMX509)) {
278 auto_ptr_char fname(cert->getAttributeNS(NULL,SHIB_L(URI)));
279 FILE* f=fopen(fname.get(),"r");
282 while (x=PEM_read_X509(f,NULL,NULL,NULL)) {
283 ka->m_certs.push_back(x);
288 log.error("unable to create certificate from externally referenced file");
292 KeyAuthority* ka2=ka.release();
293 m_keyauths.push_back(ka2);
295 // Now map the ds:KeyName values to the list of certs.
297 DOMElement* sub=saml::XML::getFirstChildElement(e,saml::XML::XMLSIG_NS,SHIB_L(KeyName));
299 const XMLCh* name=sub->getFirstChild()->getNodeValue();
305 ka2->m_subjects.push_back(name);
308 sub=saml::XML::getNextSiblingElement(sub,saml::XML::XMLSIG_NS,SHIB_L(KeyName));
311 // If no Subjects, this is a catch-all binding.
314 log.warn("found a wildcard KeyAuthority element, make sure this is what you intend");
318 log.warn("found multiple wildcard KeyAuthority elements, ignoring all but the first");
322 // Now traverse the outer ds:KeyInfo elements. Supposedly this cast just works...
324 KeyInfoNodeFilter filter;
325 XSECKeyInfoResolverDefault resolver;
326 DOMTreeWalker* walker=
327 static_cast<DOMDocumentTraversal*>(m_doc)->createTreeWalker(const_cast<DOMElement*>(m_root),DOMNodeFilter::SHOW_ELEMENT,&filter,false);
328 DOMElement* kidom=static_cast<DOMElement*>(walker->firstChild());
331 DSIGKeyInfoList* KIL = new DSIGKeyInfoList(NULL);
332 // We let XMLSec hack through anything it can. This should evolve over time, or we can
333 // plug in our own KeyResolver later...
334 DOMElement* child=saml::XML::getFirstChildElement(kidom);
338 if (!KIL->addXMLKeyInfo(child))
339 log.warn("skipped unsupported ds:KeyInfo child element (%d)",count2);
341 catch (XSECCryptoException& xe) {
342 log.error("unable to resolve ds:KeyInfo child element (%d) to usable key: %s",count2,xe.getMsg());
344 child=saml::XML::getNextSiblingElement(child);
348 // Dry run...can we resolve to a key?
349 XSECCryptoKey* key=resolver.resolveKey(KIL);
351 // So far so good, now look for the name binding(s).
354 for (size_t index=0; index<KIL->getSize(); index++) {
355 DSIGKeyInfo* info=KIL->item(index);
356 const XMLCh* name=info->getKeyName();
359 m_keybinds.push_back(KIL);
367 log.warn("skipping ds:KeyInfo binding (%d) that does not contain a usable key name",count);
372 log.warn("skipping ds:KeyInfo binding (%d) that does not resolve to a key",count);
375 kidom=static_cast<DOMElement*>(walker->nextSibling());
377 walker->release(); // This just cleans up aggressively, but there's no leak if we don't.
379 catch (SAMLException& e) {
380 log.errorStream() << "Error while parsing trust configuration: " << e.what() << CategoryStream::ENDLINE;
381 for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
383 for (vector<DSIGKeyInfoList*>::iterator j=m_keybinds.begin(); j!=m_keybinds.end(); j++)
388 log.error("Unexpected error while parsing trust configuration");
389 for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
391 for (vector<DSIGKeyInfoList*>::iterator j=m_keybinds.begin(); j!=m_keybinds.end(); j++)
397 XMLTrustImpl::~XMLTrustImpl()
399 for (vector<KeyAuthority*>::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++)
401 for (vector<DSIGKeyInfoList*>::iterator j=m_keybinds.begin(); j!=m_keybinds.end(); j++)
405 bool XMLTrust::attach(const Iterator<IRevocation*>& revocations, const IRoleDescriptor* role, void* ctx)
409 saml::NDC ndc("attach");
410 Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrust");
411 XMLTrustImpl* impl=dynamic_cast<XMLTrustImpl*>(getImplementation());
413 // Build a list of the names to match. We include any named KeyDescriptors, and the provider ID and its groups.
414 vector<const XMLCh*> names;
415 Iterator<const IKeyDescriptor*> kdlist=role->getKeyDescriptors();
416 while (kdlist.hasNext()) {
417 const IKeyDescriptor* kd=kdlist.next();
418 if (kd->getUse()==IKeyDescriptor::encryption)
420 DSIGKeyInfoList* kilist=kd->getKeyInfo();
421 for (size_t s=0; kilist && s<kilist->getSize(); s++) {
422 const XMLCh* n=kilist->item(s)->getKeyName();
427 names.push_back(role->getEntityDescriptor()->getId());
428 const IEntitiesDescriptor* group=role->getEntityDescriptor()->getEntitiesDescriptor();
430 names.push_back(group->getName());
431 group=group->getEntitiesDescriptor();
434 // Now check each name.
435 XMLTrustImpl::KeyAuthority* kauth=NULL;
436 for (vector<const XMLCh*>::const_iterator name=names.begin(); !kauth && name!=names.end(); name++) {
438 XMLTrustImpl::AuthMap::const_iterator c=impl->m_authMap.find(*name);
439 if (c!=impl->m_authMap.end()) {
441 if (log.isInfoEnabled()) {
442 auto_ptr_char temp(*name);
443 log.info("KeyAuthority match on %s",temp.get());
447 // Without a decent STL, we trade-off the transcoding by doing a linear search.
448 for (vector<XMLTrustImpl::KeyAuthority*>::const_iterator keyauths=impl->m_keyauths.begin(); !kauth && keyauths!=impl->m_keyauths.end(); keyauths++) {
449 for (vector<const XMLCh*>::const_iterator subs=(*keyauths)->m_subjects.begin(); !kauth && subs!=(*keyauths)->m_subjects.end(); subs++) {
450 if (!XMLString::compareString(*name,*subs)) {
452 if (log.isInfoEnabled()) {
453 auto_ptr_char temp(*name);
454 log.info("KeyAuthority match on %s",temp.get());
463 if (impl->m_wildcard) {
464 log.warn("applying wildcard KeyAuthority, use with caution!");
465 kauth=impl->m_wildcard;
469 log.warn("no KeyAuthority found to validate SSL connection, leaving it alone");
474 // If we have a match, use the associated keyauth unless we already did...
475 X509_STORE* store=kauth->getX509Store();
478 // Add any relevant CRLs.
479 log.debug("obtaining CRLs for this provider/role");
480 Revocation rev(revocations);
481 Iterator<void*> crls=rev.getRevocationLists(role->getEntityDescriptor(),role);
482 while (crls.hasNext()) {
483 if (!X509_STORE_add_crl(store,X509_CRL_dup(reinterpret_cast<X509_CRL*>(crls.next())))) {
485 log.warn("failed to add CRL");
489 // Apply store to this context.
490 SSL_CTX_set_verify(reinterpret_cast<SSL_CTX*>(ctx),SSL_VERIFY_PEER,logging_callback);
491 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
492 SSL_CTX_set_cert_verify_callback(reinterpret_cast<SSL_CTX*>(ctx),verify_callback,NULL);
494 SSL_CTX_set_cert_verify_callback(reinterpret_cast<SSL_CTX*>(ctx),reinterpret_cast<int (*)()>(verify_callback),NULL);
496 SSL_CTX_set_cert_store(reinterpret_cast<SSL_CTX*>(ctx),store);
497 SSL_CTX_set_verify_depth(reinterpret_cast<SSL_CTX*>(ctx),kauth->m_depth);
508 bool XMLTrust::validate(
509 const saml::Iterator<IRevocation*>& revocations,
510 const IRoleDescriptor* role, const saml::SAMLSignedObject& token,
511 const saml::Iterator<IMetadata*>& metadatas
517 Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".XMLTrust");
518 XMLTrustImpl* impl=dynamic_cast<XMLTrustImpl*>(getImplementation());
520 // This is where we're going to hide all the juicy SAML trust bits. If we botch it
521 // we can just plug in a new version, hopefully.
523 Metadata metadata(metadatas); // With luck we won't need this.
525 // Did the caller tell us about the signer?
526 const IEntityDescriptor* provider=(role ? role->getEntityDescriptor() : NULL);
528 log.debug("no role descriptor passed in, trying to map token to provider");
530 // The first step is to identify the provider responsible for signing the token.
531 // We can't narrow it down to role, because we don't know why the token is being validated.
533 // If it's an assertion, this isn't terribly hard, but we need to hack in support for both
534 // Issuer and NameQualifier as a provider ID. Issuer will be the main one going forward.
535 // 1.0/1.1 origins will be sending a hostname as Issuer, but this won't hit the metadata lookup
536 // and we'll fall back to NameQualifier. Non-Shib SAML origins generally would be based on Issuer.
538 // Responses allow us to try and locate a provider by checking the assertion(s) inside.
539 // Technically somebody could enclose somebody else's assertions, but if the use case is
540 // that advanced, we're probably into SAML 2.0 and we'll have Issuer up top.
542 // Requests...umm, pretty much out of luck. We'll apply our own hack if there's an
543 // attribute query, and use Resource.
545 if (typeid(token)==typeid(SAMLResponse)) {
546 Iterator<SAMLAssertion*> assertions=dynamic_cast<const SAMLResponse&>(token).getAssertions();
547 while (!provider && assertions.hasNext()) {
548 SAMLAssertion* assertion=assertions.next();
549 provider=metadata.lookup(assertion->getIssuer());
551 Iterator<SAMLStatement*> statements=assertion->getStatements();
552 while (!provider && statements.hasNext()) {
553 SAMLSubjectStatement* statement=dynamic_cast<SAMLSubjectStatement*>(statements.next());
554 if (statement && statement->getSubject()->getNameIdentifier()->getNameQualifier())
555 provider=metadata.lookup(statement->getSubject()->getNameIdentifier()->getNameQualifier());
560 else if (typeid(token)==typeid(SAMLAssertion)) {
561 provider=metadata.lookup(dynamic_cast<const SAMLAssertion&>(token).getIssuer());
563 Iterator<SAMLStatement*> statements=dynamic_cast<const SAMLAssertion&>(token).getStatements();
564 while (!provider && statements.hasNext()) {
565 SAMLSubjectStatement* statement=dynamic_cast<SAMLSubjectStatement*>(statements.next());
566 if (statement && statement->getSubject()->getNameIdentifier()->getNameQualifier())
567 provider=metadata.lookup(statement->getSubject()->getNameIdentifier()->getNameQualifier());
571 else if (typeid(token)==typeid(SAMLRequest)) {
572 const SAMLQuery* q=dynamic_cast<const SAMLRequest&>(token).getQuery();
573 if (q && dynamic_cast<const SAMLAttributeQuery*>(q))
574 provider=metadata.lookup(dynamic_cast<const SAMLAttributeQuery*>(q)->getResource());
577 // If we still don't have a provider, there's no likely basis for trust,
578 // but a wildcard KeyAuthority might apply.
579 if (log.isInfoEnabled() && provider) {
580 auto_ptr_char temp(provider->getId());
581 log.info("mapped signed token to provider: %s", temp.get());
584 log.warn("unable to map signed token to provider, only wildcarded trust will apply");
587 vector<const XMLCh*> names;
588 XSECKeyInfoResolverDefault keyResolver;
590 // First, try to resolve a KeyDescriptor from the role into an actual key.
591 // That's the simplest case. Failing that, remember any key names we run across.
594 log.debug("checking for key descriptors that resolve directly");
595 Iterator<const IKeyDescriptor*> kd_i=role->getKeyDescriptors();
596 while (kd_i.hasNext()) {
597 const IKeyDescriptor* kd=kd_i.next();
598 if (kd->getUse()!=IKeyDescriptor::signing)
600 DSIGKeyInfoList* KIL=kd->getKeyInfo();
603 XSECCryptoKey* key=keyResolver.resolveKey(KIL);
605 log.debug("found an inline key descriptor, trying it...");
609 log.info("token verified with inline key descriptor, nothing more to verify");
612 catch (SAMLException& e) {
613 log.warn("inline key descriptor failed: %s", e.what());
617 for (size_t s=0; s<KIL->getSize(); s++) {
618 const XMLCh* n=KIL->item(s)->getKeyName();
626 // Push the provider ID on the key name list. We don't push provider groups in, since
627 // matching groups to a key makes no sense.
629 names.push_back(provider->getId());
631 // No keys inline in metadata. Now we try and find a key inline in trust.
632 log.debug("checking for keys in trust file");
633 DSIGKeyInfoList* KIL=NULL;
634 for (vector<const XMLCh*>::const_iterator name=names.begin(); !KIL && name!=names.end(); name++) {
636 XMLTrustImpl::BindMap::const_iterator c=impl->m_bindMap.find(*name);
637 if (c!=impl->m_bindMap.end()) {
639 if (log.isInfoEnabled()) {
640 auto_ptr_char temp(*name);
641 log.info("KeyInfo match on %s",temp.get());
645 // Without a decent STL, we trade-off the transcoding by doing a linear search.
646 for (vector<DSIGKeyInfoList*>::const_iterator keybinds=impl->m_keybinds.begin(); !KIL && keybinds!=impl->m_keybinds.end(); keybinds++) {
647 for (size_t s=0; !KIL && s<(*keybinds)->getSize(); s++) {
648 if (!XMLString::compareString(*name,(*keybinds)->item(s)->getKeyName())) {
650 if (log.isInfoEnabled()) {
651 auto_ptr_char temp(*name);
652 log.info("KeyInfo match on %s",temp.get());
661 // Any inline KeyInfo should ostensible resolve to a key we can try.
662 XSECCryptoKey* key=keyResolver.resolveKey(KIL);
664 log.debug("resolved key, trying it...");
668 log.info("token verified with KeyInfo, nothing more to verify");
671 catch (SAMLException& e) {
672 log.warn("inline key failed: %s", e.what());
676 log.warn("KeyInfo in trust provider did not resolve to a key");
679 // Direct key verification hasn't worked. Now we have to switch over to KeyAuthority-based
680 // validation. The actual verification key has to be inside the token.
681 log.debug("verifying signature using key inside token...");
684 log.info("verified with key inside token, entering validation stage");
686 catch (SAMLException& e) {
688 log.warn("verification using key inside token failed: %s", e.what());
692 // Before we do the cryptogprahy, check that the EE certificate "name" matches
693 // one of the acceptable key "names" for the signer. Without this, we have a gaping
694 // hole in the validation.
695 log.debug("matching token's certificate subject against valid key names...");
696 vector<const XMLCh*> certs;
697 for (unsigned int i=0; i<token.getX509CertificateCount(); i++)
698 certs.push_back(token.getX509Certificate(i));
700 // Decode the EE cert.
701 auto_ptr_char EE(certs[0]);
702 X509* x=B64_to_X509(EE.get());
705 log.error("unable to decode X.509 EE certificate");
709 // Transcode the possible key "names" to UTF-8. For some simple cases, this should
710 // handle UTF-8 encoded DNs in certificates.
711 vector<string> keynames;
712 Iterator<const XMLCh*> iname(names);
713 while (iname.hasNext()) {
714 auto_ptr<char> kn(toUTF8(iname.next()));
715 keynames.push_back(kn.get());
718 const char* match=NULL; // This will be set to the first matching key name.
720 X509_NAME* subject=X509_get_subject_name(x);
722 // One way is a direct match to the subject DN.
723 // Seems that the way to do the compare is to write the X509_NAME into a BIO.
724 BIO* b = BIO_new(BIO_s_mem());
725 BIO* b2 = BIO_new(BIO_s_mem());
726 BIO_set_mem_eof_return(b, 0);
727 BIO_set_mem_eof_return(b2, 0);
728 // The flags give us LDAP order instead of X.500, with a comma separator.
729 int len=X509_NAME_print_ex(b,subject,0,XN_FLAG_RFC2253);
730 string subjectstr,subjectstr2;
732 while ((len = BIO_read(b, buf, 255)) > 0) {
736 log.infoStream() << "certificate subject: " << subjectstr << CategoryStream::ENDLINE;
737 // The flags give us LDAP order instead of X.500, with a comma plus space separator.
738 len=X509_NAME_print_ex(b2,subject,0,XN_FLAG_RFC2253 + XN_FLAG_SEP_CPLUS_SPC - XN_FLAG_SEP_COMMA_PLUS);
740 while ((len = BIO_read(b2, buf, 255)) > 0) {
745 // Check each keyname.
746 for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
747 #ifdef HAVE_STRCASECMP
748 if (!strcasecmp(n->c_str(),subjectstr.c_str()) || !strcasecmp(n->c_str(),subjectstr2.c_str())) {
750 if (!stricmp(n->c_str(),subjectstr.c_str()) || !stricmp(n->c_str(),subjectstr2.c_str())) {
752 log.info("matched full subject DN to a key name (%s)", n->c_str());
761 log.debug("unable to match DN, trying TLS subjectAltName match");
762 STACK_OF(GENERAL_NAME)* altnames=(STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
764 int numalts = sk_GENERAL_NAME_num(altnames);
765 for (int an=0; !match && an<numalts; an++) {
766 const GENERAL_NAME* check = sk_GENERAL_NAME_value(altnames, an);
767 if (check->type==GEN_DNS || check->type==GEN_URI) {
768 const char* altptr = (char*)ASN1_STRING_data(check->d.ia5);
769 const int altlen = ASN1_STRING_length(check->d.ia5);
771 for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
772 #ifdef HAVE_STRCASECMP
773 if (!strncasecmp(altptr,n->c_str(),altlen)) {
775 if (!strnicmp(altptr,n->c_str(),altlen)) {
777 log.info("matched DNS/URI subjectAltName to a key name (%s)", n->c_str());
784 GENERAL_NAMES_free(altnames);
788 log.debug("unable to match subjectAltName, trying TLS CN match");
789 memset(buf,0,sizeof(buf));
790 if (X509_NAME_get_text_by_NID(subject,NID_commonName,buf,255)>0) {
791 for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
792 #ifdef HAVE_STRCASECMP
793 if (!strcasecmp(buf,n->c_str())) {
795 if (!stricmp(buf,n->c_str())) {
797 log.info("matched subject CN to a key name (%s)", n->c_str());
804 log.warn("no common name in certificate subject");
809 log.error("certificate has no subject?!");
815 log.error("cannot match certificate subject against provider's key names");
819 // We're ready for the final stage.
820 log.debug("final step, certificate path validation...");
822 // Build a new list of key authority matching names, the actual key name, providerId, and any groups.
823 auto_ptr<XMLCh> kn2(fromUTF8(match));
824 vector<const XMLCh*> authnames;
825 authnames.push_back(kn2.get());
827 authnames.push_back(provider->getId());
828 const IEntitiesDescriptor* group=provider->getEntitiesDescriptor();
830 authnames.push_back(group->getName());
831 group=group->getEntitiesDescriptor();
835 // Now we hunt the list for a KeyAuthority that matches one of the authority matching names.
836 XMLTrustImpl::KeyAuthority* kauth=NULL;
837 for (vector<const XMLCh*>::const_iterator name2=authnames.begin(); !kauth && name2!=authnames.end(); name2++) {
839 XMLTrustImpl::AuthMap::const_iterator c=impl->m_authMap.find(*name2);
840 if (c!=impl->m_authMap.end()) {
842 if (log.isInfoEnabled()) {
843 auto_ptr_char temp(*name2);
844 log.info("KeyAuthority match on %s",temp.get());
848 // Without a decent STL, we trade-off the transcoding by doing a linear search.
849 for (vector<XMLTrustImpl::KeyAuthority*>::const_iterator keyauths=impl->m_keyauths.begin(); !kauth && keyauths!=impl->m_keyauths.end(); keyauths++) {
850 for (vector<const XMLCh*>::const_iterator subs=(*keyauths)->m_subjects.begin(); !kauth && subs!=(*keyauths)->m_subjects.end(); subs++) {
851 if (!XMLString::compareString(*name2,*subs)) {
853 if (log.isInfoEnabled()) {
854 auto_ptr_char temp(*name2);
855 log.info("KeyAuthority match on %s",temp.get());
864 if (impl->m_wildcard) {
865 log.warn("applying wildcard KeyAuthority, use with caution!");
866 kauth=impl->m_wildcard;
870 log.warn("no KeyAuthority found to validate the token, leaving untrusted");
875 log.debug("building untrusted certificate chain from signature");
876 STACK_OF(X509)* chain=sk_X509_new_null();
877 Iterator<const XMLCh*> icerts(certs);
878 while (icerts.hasNext()) {
879 auto_ptr_char xbuf(icerts.next());
880 X509* x=B64_to_X509(xbuf.get());
883 log.error("unable to parse certificate in signature");
884 sk_X509_pop_free(chain,X509_free);
887 sk_X509_push(chain,x);
890 X509_STORE* store=kauth->getX509Store();
893 log.error("unable to load X509_STORE from KeyAuthority object");
894 sk_X509_pop_free(chain,X509_free);
898 X509_STORE_CTX* ctx=X509_STORE_CTX_new();
902 log.error("unable to create X509_STORE_CTX");
903 X509_STORE_free(store);
904 sk_X509_pop_free(chain,X509_free);
908 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
909 if (X509_STORE_CTX_init(ctx,store,sk_X509_value(chain,0),chain)!=1) {
912 log.error("unable to initialize X509_STORE_CTX");
913 X509_STORE_CTX_free(ctx);
914 X509_STORE_free(store);
915 sk_X509_pop_free(chain,X509_free);
919 X509_STORE_CTX_init(ctx,store,sk_X509_value(chain,0),chain);
922 X509_STORE_CTX_set_depth(ctx,kauth->m_depth);
924 // Add any relevant CRLs.
925 log.debug("obtaining CRLs for this provider/role");
926 Revocation rev(revocations);
927 Iterator<void*> crls=rev.getRevocationLists(provider,role);
928 while (crls.hasNext()) {
929 if (!X509_STORE_add_crl(store,X509_CRL_dup(reinterpret_cast<X509_CRL*>(crls.next())))) {
931 log.warn("failed to add CRL");
935 int result=X509_verify_cert(ctx);
936 sk_X509_pop_free(chain,X509_free);
937 X509_STORE_CTX_free(ctx);
938 X509_STORE_free(store);
942 log.info("successfully validated certificate chain, token signature trusted");
946 log.error("failed to validate certificate chain, token signature untrusted");