5c686b71810f9ad785d7cbceaa8419e737b2d6f6
[shibboleth/cpp-sp.git] / shib / ShibbolethTrust.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 /* ShibbolethTrust.cpp - a trust implementation that relies solely on standard SAML metadata
18
19    Scott Cantor
20    4/10/05
21
22    $History:$
23 */
24
25 #include "internal.h"
26
27 #include <openssl/err.h>
28 #include <openssl/x509_vfy.h>
29 #include <openssl/x509v3.h>
30 #include <xsec/dsig/DSIGKeyInfoX509.hpp>
31 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
32
33 using namespace shibboleth;
34 using namespace saml;
35 using namespace log4cpp;
36 using namespace std;
37
38 namespace {
39     void log_openssl()
40     {
41         const char* file;
42         const char* data;
43         int flags,line;
44     
45         unsigned long code=ERR_get_error_line_data(&file,&line,&data,&flags);
46         while (code) {
47             Category& log=Category::getInstance("OpenSSL");
48             log.errorStream() << "error code: " << code << " in " << file << ", line " << line << CategoryStream::ENDLINE;
49             if (data && (flags & ERR_TXT_STRING))
50                 log.errorStream() << "error data: " << data << CategoryStream::ENDLINE;
51             code=ERR_get_error_line_data(&file,&line,&data,&flags);
52         }
53     }
54     
55     X509* B64_to_X509(const char* buf)
56     {
57         BIO* bmem = BIO_new_mem_buf((void*)buf,-1);
58         BIO* b64 = BIO_new(BIO_f_base64());
59         b64 = BIO_push(b64, bmem);
60         X509* x=NULL;
61         d2i_X509_bio(b64,&x);
62         if (!x)
63             log_openssl();
64         BIO_free_all(b64);
65         return x;
66     }
67     
68     X509_CRL* B64_to_CRL(const char* buf)
69     {
70         BIO* bmem = BIO_new_mem_buf((void*)buf,-1);
71         BIO* b64 = BIO_new(BIO_f_base64());
72         b64 = BIO_push(b64, bmem);
73         X509_CRL* x=NULL;
74         d2i_X509_CRL_bio(b64,&x);
75         if (!x)
76             log_openssl();
77         BIO_free_all(b64);
78         return x;
79     }
80
81     class ShibbolethTrust : public BasicTrust
82     {
83     public:
84         ShibbolethTrust(const DOMElement* e);
85         ~ShibbolethTrust();
86
87         bool validate(void* certEE, const Iterator<void*>& certChain, const IRoleDescriptor* role, bool checkName=true);
88         bool validate(const saml::SAMLSignedObject& token, const IRoleDescriptor* role, ITrust* certValidator=NULL);
89         
90     private:
91         bool validate(X509* EE, STACK_OF(X509)* untrusted, const IKeyAuthority* rule);
92
93         vector<IMetadata*> m_metas;
94     };
95 }
96
97 IPlugIn* ShibbolethTrustFactory(const DOMElement* e)
98 {
99     return new ShibbolethTrust(e);
100 }
101
102 ShibbolethTrust::ShibbolethTrust(const DOMElement* e) : BasicTrust(e)
103 {
104     static const XMLCh MetadataProvider[] =
105     { chLatin_M, chLatin_e, chLatin_t, chLatin_a, chLatin_d, chLatin_a, chLatin_t, chLatin_a,
106       chLatin_P, chLatin_r, chLatin_o, chLatin_v, chLatin_i, chLatin_d, chLatin_e, chLatin_r, chNull
107     };
108     static const XMLCh _type[] = { chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull };
109
110 #ifdef _DEBUG
111     saml::NDC ndc("ShibbolethTrust");
112 #endif
113     Category& log=Category::getInstance(SHIB_LOGCAT".Trust.Shibboleth");
114
115     // Check for embedded trust metadata.
116     e=saml::XML::getFirstChildElement(e);
117     while (e) {
118         if (!XMLString::compareString(e->getLocalName(),MetadataProvider) && e->hasAttributeNS(NULL,_type)) {
119             auto_ptr_char type(e->getAttributeNS(NULL,_type));
120             log.info("trust provider building embedded metadata provider of type %s...",type.get());
121             try {
122                 IPlugIn* plugin=SAMLConfig::getConfig().getPlugMgr().newPlugin(type.get(),e);
123                 IMetadata* md=dynamic_cast<IMetadata*>(plugin);
124                 if (md)
125                     m_metas.push_back(md);
126                 else {
127                     delete plugin;
128                     log.error("plugin was not a metadata provider");
129                 }
130             }
131             catch (SAMLException& ex) {
132                 log.error("caught SAML exception building embedded metadata provider: %s", ex.what());
133             }
134 #ifndef _DEBUG
135             catch (...) {
136                 log.error("caught unknown exception building embedded metadata provider");
137             }
138 #endif
139         }
140         e=saml::XML::getNextSiblingElement(e);
141     }
142 }
143
144 ShibbolethTrust::~ShibbolethTrust()
145 {
146     for (vector<IMetadata*>::iterator i=m_metas.begin(); i!=m_metas.end(); i++)
147         delete *i;
148 }
149
150 static int error_callback(int ok, X509_STORE_CTX* ctx)
151 {
152     if (!ok)
153         Category::getInstance("OpenSSL").error("path validation failure: %s", X509_verify_cert_error_string(ctx->error));
154     return ok;
155 }
156
157 bool ShibbolethTrust::validate(X509* EE, STACK_OF(X509)* untrusted, const IKeyAuthority* rule)
158 {
159     Category& log=Category::getInstance(SHIB_LOGCAT".Trust.Shibboleth");
160
161     // First we build a stack of CA certs. These objects are all referenced in place.
162     log.debug("building CA list from KeyAuthority extension");
163
164     // We need this for CRL support.
165     X509_STORE* store=X509_STORE_new();
166     if (!store) {
167         log_openssl();
168         return false;
169     }
170 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
171     X509_STORE_set_flags(store,X509_V_FLAG_CRL_CHECK_ALL);
172 #endif
173
174     STACK_OF(X509)* CAstack = sk_X509_new_null();
175     
176     // This contains the state of the validate operation.
177     X509_STORE_CTX ctx;
178         
179     Iterator<DSIGKeyInfoList*> iKIL=rule->getKeyInfos();
180     while (iKIL.hasNext()) {
181         DSIGKeyInfoList* KIL=iKIL.next();
182         
183         // Try and locate a certificate.
184         Iterator<KeyInfoResolver*> resolvers(m_resolvers);
185         while (resolvers.hasNext()) {
186             XSECCryptoX509* cert=resolvers.next()->resolveCert(KIL);
187             if (cert && cert->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL) {
188                 sk_X509_push(CAstack,static_cast<OpenSSLCryptoX509*>(cert)->getOpenSSLX509());
189                 break;
190             }
191         }
192         
193         // Try and locate one or more CRLs.
194         for (size_t s=0; s<KIL->getSize(); s++) {
195             DSIGKeyInfo* KI=KIL->item(s);
196             if (KI->getKeyInfoType()==DSIGKeyInfo::KEYINFO_X509) {
197                 const XMLCh* raw=static_cast<DSIGKeyInfoX509*>(KI)->getX509CRL();
198                 if (raw) {
199                     auto_ptr_char blob(raw);
200                     X509_CRL* crl=B64_to_CRL(blob.get());
201                     if (crl)
202                         X509_STORE_add_crl(store,crl);  // owned by store
203                     else
204                         log.error("unable to create CRL from X509CRL data");
205                 }
206             }
207         }
208     }
209  
210     // AFAICT, EE and untrusted are passed in but not owned by the ctx.
211 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
212     if (X509_STORE_CTX_init(&ctx,store,EE,untrusted)!=1) {
213         log_openssl();
214         log.error("unable to initialize X509_STORE_CTX");
215         sk_X509_free(CAstack);
216         X509_STORE_free(store);
217         return false;
218     }
219 #else
220     X509_STORE_CTX_init(&ctx,store,EE,untrusted);
221 #endif
222
223     // Seems to be most efficient to just pass in the CA stack.
224     X509_STORE_CTX_trusted_stack(&ctx,CAstack);
225     X509_STORE_CTX_set_depth(&ctx,100);    // we check the depth down below
226     X509_STORE_CTX_set_verify_cb(&ctx,error_callback);
227     
228     int ret=X509_verify_cert(&ctx);
229     if (ret==1) {
230         // Now see if the depth was acceptable by counting the number of intermediates.
231         int depth=sk_X509_num(ctx.chain)-2;
232         if (rule->getVerifyDepth() < depth) {
233             log.error(
234                 "certificate chain was too long (%d intermediates, only %d allowed)",
235                 (depth==-1) ? 0 : depth,
236                 rule->getVerifyDepth()
237                 );
238             ret=0;
239         }
240     }
241     
242     // Clean up...
243     X509_STORE_CTX_cleanup(&ctx);
244     X509_STORE_free(store);
245     sk_X509_free(CAstack);
246
247     if (ret==1) {
248         log.info("successfully validated certificate chain");
249         return true;
250     }
251     
252     return false;
253 }
254
255 bool ShibbolethTrust::validate(void* certEE, const Iterator<void*>& certChain, const IRoleDescriptor* role, bool checkName)
256 {
257     if (BasicTrust::validate(certEE,certChain,role))
258         return true;
259         
260 #ifdef _DEBUG
261     saml::NDC ndc("validate");
262 #endif
263     Category& log=Category::getInstance(SHIB_LOGCAT".Trust.Shibboleth");
264
265     if (!certEE)
266         return false;
267
268     // The extended trust implementation supports metadata extensions to validate
269     // signing certificates found inside the signature.
270
271     if (checkName) {
272         // Before we do the cryptogprahy, check that the EE certificate "name" matches
273         // one of the acceptable key "names" for the signer.
274         vector<string> keynames;
275         
276         // Build a list of acceptable names. Transcode the possible key "names" to UTF-8.
277         // For some simple cases, this should handle UTF-8 encoded DNs in certificates.
278         Iterator<const IKeyDescriptor*> kd_i=role->getKeyDescriptors();
279         while (kd_i.hasNext()) {
280             const IKeyDescriptor* kd=kd_i.next();
281             if (kd->getUse()!=IKeyDescriptor::signing)
282                 continue;
283             DSIGKeyInfoList* KIL=kd->getKeyInfo();
284             if (!KIL)
285                 continue;
286             for (size_t s=0; s<KIL->getSize(); s++) {
287                 const XMLCh* n=KIL->item(s)->getKeyName();
288                 if (n) {
289                     auto_ptr<char> kn(toUTF8(n));
290                     keynames.push_back(kn.get());
291                 }
292             }
293         }
294         auto_ptr<char> kn(toUTF8(role->getEntityDescriptor()->getId()));
295         keynames.push_back(kn.get());
296         
297         char buf[256];
298         X509* x=(X509*)certEE;
299         X509_NAME* subject=X509_get_subject_name(x);
300         if (subject) {
301             // One way is a direct match to the subject DN.
302             // Seems that the way to do the compare is to write the X509_NAME into a BIO.
303             BIO* b = BIO_new(BIO_s_mem());
304             BIO* b2 = BIO_new(BIO_s_mem());
305             BIO_set_mem_eof_return(b, 0);
306             BIO_set_mem_eof_return(b2, 0);
307             // The flags give us LDAP order instead of X.500, with a comma separator.
308             int len=X509_NAME_print_ex(b,subject,0,XN_FLAG_RFC2253);
309             string subjectstr,subjectstr2;
310             BIO_flush(b);
311             while ((len = BIO_read(b, buf, 255)) > 0) {
312                 buf[len] = '\0';
313                 subjectstr+=buf;
314             }
315             log.infoStream() << "certificate subject: " << subjectstr << CategoryStream::ENDLINE;
316             // The flags give us LDAP order instead of X.500, with a comma plus space separator.
317             len=X509_NAME_print_ex(b2,subject,0,XN_FLAG_RFC2253 + XN_FLAG_SEP_CPLUS_SPC - XN_FLAG_SEP_COMMA_PLUS);
318             BIO_flush(b2);
319             while ((len = BIO_read(b2, buf, 255)) > 0) {
320                 buf[len] = '\0';
321                 subjectstr2+=buf;
322             }
323             
324             // Check each keyname.
325             for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
326 #ifdef HAVE_STRCASECMP
327                 if (!strcasecmp(n->c_str(),subjectstr.c_str()) || !strcasecmp(n->c_str(),subjectstr2.c_str())) {
328 #else
329                 if (!stricmp(n->c_str(),subjectstr.c_str()) || !stricmp(n->c_str(),subjectstr2.c_str())) {
330 #endif
331                     log.info("matched full subject DN to a key name (%s)", n->c_str());
332                     checkName=false;
333                     break;
334                 }
335             }
336             BIO_free(b);
337             BIO_free(b2);
338
339             if (checkName) {
340                 log.debug("unable to match DN, trying TLS subjectAltName match");
341                 STACK_OF(GENERAL_NAME)* altnames=(STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
342                 if (altnames) {
343                     int numalts = sk_GENERAL_NAME_num(altnames);
344                     for (int an=0; checkName && an<numalts; an++) {
345                         const GENERAL_NAME* check = sk_GENERAL_NAME_value(altnames, an);
346                         if (check->type==GEN_DNS || check->type==GEN_URI) {
347                             const char* altptr = (char*)ASN1_STRING_data(check->d.ia5);
348                             const int altlen = ASN1_STRING_length(check->d.ia5);
349                             
350                             for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
351 #ifdef HAVE_STRCASECMP
352                                 if ((check->type==GEN_DNS && !strncasecmp(altptr,n->c_str(),altlen))
353 #else
354                                 if ((check->type==GEN_DNS && !strnicmp(altptr,n->c_str(),altlen))
355 #endif
356                                         || (check->type==GEN_URI && !strncmp(altptr,n->c_str(),altlen))) {
357                                     log.info("matched DNS/URI subjectAltName to a key name (%s)", n->c_str());
358                                     checkName=false;
359                                     break;
360                                 }
361                             }
362                         }
363                     }
364                     GENERAL_NAMES_free(altnames);
365                 }
366                 
367                 if (checkName) {
368                     log.debug("unable to match subjectAltName, trying TLS CN match");
369                     memset(buf,0,sizeof(buf));
370                     if (X509_NAME_get_text_by_NID(subject,NID_commonName,buf,255)>0) {
371                         for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
372 #ifdef HAVE_STRCASECMP
373                             if (!strcasecmp(buf,n->c_str())) {
374 #else
375                             if (!stricmp(buf,n->c_str())) {
376 #endif
377                                 log.info("matched subject CN to a key name (%s)", n->c_str());
378                                 checkName=false;
379                                 break;
380                             }
381                         }
382                     }
383                     else
384                         log.warn("no common name in certificate subject");
385                 }
386             }
387         }
388         else
389             log.error("certificate has no subject?!");
390     }
391
392     if (checkName) {
393         log.error("cannot match certificate subject against acceptable key names based on KeyDescriptors");
394         return false;
395     }
396     
397     log.debug("performing certificate path validation...");
398
399     STACK_OF(X509)* untrusted=sk_X509_new_null();
400     certChain.reset();
401     while (certChain.hasNext())
402         sk_X509_push(untrusted,(X509*)certChain.next());
403
404     // Check for entity-level KeyAuthorities.
405     const IExtendedEntityDescriptor* entity=dynamic_cast<const IExtendedEntityDescriptor*>(role->getEntityDescriptor());
406     if (entity) {
407         Iterator<const IKeyAuthority*> kauths=entity->getKeyAuthorities();
408         while (kauths.hasNext())
409             if (validate((X509*)certEE,untrusted,kauths.next())) {
410                 sk_X509_free(untrusted);
411                 return true;
412             }
413     }
414
415     // Now repeat using any embedded metadata.
416     Iterator<IMetadata*> metas(m_metas);
417     while (metas.hasNext()) {
418         IMetadata* m=metas.next();
419         Locker locker(m);
420         const IEntityDescriptor* ed=m->lookup(role->getEntityDescriptor()->getId());
421         if (!ed)
422             continue;
423
424         // Check for entity-level KeyAuthorities.
425         entity=dynamic_cast<const IExtendedEntityDescriptor*>(ed);
426         if (entity) {
427             Iterator<const IKeyAuthority*> kauths=entity->getKeyAuthorities();
428             while (kauths.hasNext())
429                 if (validate((X509*)certEE,untrusted,kauths.next())) {
430                     sk_X509_free(untrusted);
431                     return true;
432                 }
433         }
434     }
435     
436     const IEntitiesDescriptor* group=role->getEntityDescriptor()->getEntitiesDescriptor();
437     while (group) {
438         const IExtendedEntitiesDescriptor* egroup=dynamic_cast<const IExtendedEntitiesDescriptor*>(group);
439         if (egroup) {
440             Iterator<const IKeyAuthority*> kauths=egroup->getKeyAuthorities();
441             while (kauths.hasNext())
442                 if (validate((X509*)certEE,untrusted,kauths.next())) {
443                     sk_X509_free(untrusted);
444                     return true;
445                 }
446         }
447         
448         // Now repeat using any embedded metadata.
449         Iterator<IMetadata*> metas(m_metas);
450         while (metas.hasNext()) {
451             IMetadata* m=metas.next();
452             Locker locker(m);
453             const IEntitiesDescriptor* g=m->lookupGroup(group->getName());
454             if (!g)
455                 continue;
456     
457             // Check for group-level KeyAuthorities.
458             egroup=dynamic_cast<const IExtendedEntitiesDescriptor*>(g);
459             if (egroup) {
460                 Iterator<const IKeyAuthority*> kauths=egroup->getKeyAuthorities();
461                 while (kauths.hasNext())
462                     if (validate((X509*)certEE,untrusted,kauths.next())) {
463                         sk_X509_free(untrusted);
464                         return true;
465                     }
466             }
467         }
468         
469         group=group->getEntitiesDescriptor();
470     }
471     
472     log.debug("failed to validate certificate chain using KeyAuthority extensions");
473     return false;
474 }
475
476 bool ShibbolethTrust::validate(const saml::SAMLSignedObject& token, const IRoleDescriptor* role, ITrust* certValidator)
477 {
478     if (BasicTrust::validate(token,role))
479         return true;
480
481 #ifdef _DEBUG
482     saml::NDC ndc("validate");
483 #endif
484     Category& log=Category::getInstance(SHIB_LOGCAT".Trust.Shibboleth");
485
486     // The extended trust implementation supports metadata extensions to validate
487     // signing certificates found inside the signature.
488  
489     // Get the certificate chain out of the object in portable form.
490     vector<XSECCryptoX509*> certs;
491     for (unsigned int i=0; i<token.getX509CertificateCount(); i++) {
492         auto_ptr_char cert(token.getX509Certificate(i));
493         auto_ptr<XSECCryptoX509> x(XSECPlatformUtils::g_cryptoProvider->X509());
494         try {
495             x->loadX509Base64Bin(cert.get(),strlen(cert.get()));
496             certs.push_back(x.release());
497         }
498         catch (...) {
499             log.error("unable to load certificate from signature, skipping it");
500         }
501     }
502
503     log.debug("validating signature using certificate from within the signature");
504
505     // Native representations.
506     X509* certEE=NULL;
507     vector<void*> chain;
508     
509     // Find and save off a pointer to the certificate that unlocks the object.
510     // Most of the time, this will be the first one anyway.
511     Iterator<XSECCryptoX509*> iter(certs);
512     while (iter.hasNext()) {
513         try {
514             XSECCryptoX509* c=iter.next();
515             chain.push_back(static_cast<OpenSSLCryptoX509*>(c)->getOpenSSLX509());
516             if (!certEE) {
517                 token.verify(*c);
518                 log.info("signature verified with key inside signature, attempting certificate validation...");
519                 certEE=static_cast<OpenSSLCryptoX509*>(c)->getOpenSSLX509();
520             }
521         }
522         catch (...) {
523             // trap failures
524         }
525     }
526     
527     bool ret=false;
528     if (certEE)
529         ret=(certValidator) ? certValidator->validate(certEE,chain,role) : this->validate(certEE,chain,role);
530     else
531         log.debug("failed to verify signature with embedded certificates");
532     
533     for (vector<XSECCryptoX509*>::iterator j=certs.begin(); j!=certs.end(); j++)
534         delete *j;
535
536     return ret;
537 }