2.0 SOAP Encoder
[shibboleth/cpp-opensaml.git] / saml / security / impl / AbstractPKIXTrustEngine.cpp
1 /*
2  *  Copyright 2006 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 /**
18  * AbstractPKIXTrustEngine.cpp
19  * 
20  * A trust engine that uses X.509 trust anchors and CRLs associated with a role
21  * to perform PKIX validation of signatures and certificates.
22  */
23
24 #include "internal.h"
25 #include "security/AbstractPKIXTrustEngine.h"
26 #include "signature/SignatureProfileValidator.h"
27
28 #include <openssl/x509_vfy.h>
29 #include <openssl/x509v3.h>
30 #include <xmltooling/security/OpenSSLCryptoX509CRL.h>
31 #include <xmltooling/signature/SignatureValidator.h>
32 #include <xmltooling/util/NDC.h>
33 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
34 #include <log4cpp/Category.hh>
35
36 using namespace opensaml::saml2md;
37 using namespace opensaml;
38 using namespace xmlsignature;
39 using namespace xmltooling;
40 using namespace log4cpp;
41 using namespace std;
42
43 AbstractPKIXTrustEngine::AbstractPKIXTrustEngine(const DOMElement* e) : X509TrustEngine(e), m_inlineResolver(NULL)
44 {
45     m_inlineResolver = XMLToolingConfig::getConfig().KeyResolverManager.newPlugin(INLINE_KEY_RESOLVER,NULL);
46 }
47
48 AbstractPKIXTrustEngine::~AbstractPKIXTrustEngine()
49 {
50     delete m_inlineResolver;
51 }
52
53 namespace {
54     static int SAML_DLLLOCAL error_callback(int ok, X509_STORE_CTX* ctx)
55     {
56         if (!ok)
57             Category::getInstance("OpenSSL").error("path validation failure: %s", X509_verify_cert_error_string(ctx->error));
58         return ok;
59     }
60
61     static bool SAML_DLLLOCAL validate(
62         X509* EE, STACK_OF(X509)* untrusted, AbstractPKIXTrustEngine::PKIXValidationInfoIterator* pkixInfo
63         )
64     {
65         Category& log=Category::getInstance(SAML_LOGCAT".TrustEngine");
66     
67         // First we build a stack of CA certs. These objects are all referenced in place.
68         log.debug("building CA list from PKIX Validation information");
69     
70         // We need this for CRL support.
71         X509_STORE* store=X509_STORE_new();
72         if (!store) {
73             log_openssl();
74             return false;
75         }
76     #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
77         X509_STORE_set_flags(store,X509_V_FLAG_CRL_CHECK_ALL);
78     #endif
79     
80         STACK_OF(X509)* CAstack = sk_X509_new_null();
81         
82         // This contains the state of the validate operation.
83         X509_STORE_CTX ctx;
84         
85         const vector<XSECCryptoX509*>& CAcerts = pkixInfo->getTrustAnchors();
86         for (vector<XSECCryptoX509*>::const_iterator i=CAcerts.begin(); i!=CAcerts.end(); ++i) {
87             if ((*i)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL) {
88                 sk_X509_push(CAstack,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
89             }
90         }
91
92         const vector<XSECCryptoX509CRL*>& crls = pkixInfo->getCRLs();
93         for (vector<XSECCryptoX509CRL*>::const_iterator j=crls.begin(); j!=crls.end(); ++j) {
94             if ((*j)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL) {
95                 // owned by store
96                 X509_STORE_add_crl(
97                     store,
98                     X509_CRL_dup(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL())
99                     );
100             }
101         }
102      
103         // AFAICT, EE and untrusted are passed in but not owned by the ctx.
104     #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
105         if (X509_STORE_CTX_init(&ctx,store,EE,untrusted)!=1) {
106             log_openssl();
107             log.error("unable to initialize X509_STORE_CTX");
108             sk_X509_free(CAstack);
109             X509_STORE_free(store);
110             return false;
111         }
112     #else
113         X509_STORE_CTX_init(&ctx,store,EE,untrusted);
114     #endif
115     
116         // Seems to be most efficient to just pass in the CA stack.
117         X509_STORE_CTX_trusted_stack(&ctx,CAstack);
118         X509_STORE_CTX_set_depth(&ctx,100);    // we check the depth down below
119         X509_STORE_CTX_set_verify_cb(&ctx,error_callback);
120         
121         int ret=X509_verify_cert(&ctx);
122         if (ret==1) {
123             // Now see if the depth was acceptable by counting the number of intermediates.
124             int depth=sk_X509_num(ctx.chain)-2;
125             if (pkixInfo->getVerificationDepth() < depth) {
126                 log.error(
127                     "certificate chain was too long (%d intermediates, only %d allowed)",
128                     (depth==-1) ? 0 : depth,
129                     pkixInfo->getVerificationDepth()
130                     );
131                 ret=0;
132             }
133         }
134         
135         // Clean up...
136         X509_STORE_CTX_cleanup(&ctx);
137         X509_STORE_free(store);
138         sk_X509_free(CAstack);
139     
140         if (ret==1) {
141             log.info("successfully validated certificate chain");
142             return true;
143         }
144         
145         return false;
146     }
147 };
148
149 bool AbstractPKIXTrustEngine::checkEntityNames(XSECCryptoX509* certEE, const RoleDescriptor& role) const
150 {
151     Category& log=Category::getInstance(SAML_LOGCAT".TrustEngine");
152     
153     // Build a list of acceptable names. Transcode the possible key "names" to UTF-8.
154     // For some simple cases, this should handle UTF-8 encoded DNs in certificates.
155     vector<string> keynames;
156     const vector<KeyDescriptor*>& keydescs=role.getKeyDescriptors();
157     for (vector<KeyDescriptor*>::const_iterator kd_i=keydescs.begin(); kd_i!=keydescs.end(); ++kd_i) {
158         const XMLCh* use=(*kd_i)->getUse();
159         const KeyInfo* keyInfo = (*kd_i)->getKeyInfo();
160         if (keyInfo && use && XMLString::equals(use,KeyDescriptor::KEYTYPE_ENCRYPTION))
161             continue;
162         const vector<KeyName*>& knames=keyInfo->getKeyNames();
163         for (vector<KeyName*>::const_iterator kn_i=knames.begin(); kn_i!=knames.end(); ++kn_i) {
164             const XMLCh* n=(*kn_i)->getName();
165             if (n && *n) {
166                 char* kn=toUTF8(n);
167                 keynames.push_back(kn);
168                 delete[] kn;
169             }
170         }
171     }
172     
173     EntityDescriptor* parent=dynamic_cast<EntityDescriptor*>(role.getParent());
174     if (parent) {
175         const XMLCh* eid=parent->getEntityID();
176         if (eid && *eid) {
177             char* kn=toUTF8(eid);
178             keynames.push_back(kn);
179             delete[] kn;
180         }
181     }
182     
183     char buf[256];
184     X509* x=static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509();
185     X509_NAME* subject=X509_get_subject_name(x);
186     if (subject) {
187         // One way is a direct match to the subject DN.
188         // Seems that the way to do the compare is to write the X509_NAME into a BIO.
189         BIO* b = BIO_new(BIO_s_mem());
190         BIO* b2 = BIO_new(BIO_s_mem());
191         BIO_set_mem_eof_return(b, 0);
192         BIO_set_mem_eof_return(b2, 0);
193         // The flags give us LDAP order instead of X.500, with a comma separator.
194         int len=X509_NAME_print_ex(b,subject,0,XN_FLAG_RFC2253);
195         string subjectstr,subjectstr2;
196         BIO_flush(b);
197         while ((len = BIO_read(b, buf, 255)) > 0) {
198             buf[len] = '\0';
199             subjectstr+=buf;
200         }
201         log.infoStream() << "certificate subject: " << subjectstr << CategoryStream::ENDLINE;
202         // The flags give us LDAP order instead of X.500, with a comma plus space separator.
203         len=X509_NAME_print_ex(b2,subject,0,XN_FLAG_RFC2253 + XN_FLAG_SEP_CPLUS_SPC - XN_FLAG_SEP_COMMA_PLUS);
204         BIO_flush(b2);
205         while ((len = BIO_read(b2, buf, 255)) > 0) {
206             buf[len] = '\0';
207             subjectstr2+=buf;
208         }
209         
210         // Check each keyname.
211         for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
212 #ifdef HAVE_STRCASECMP
213             if (!strcasecmp(n->c_str(),subjectstr.c_str()) || !strcasecmp(n->c_str(),subjectstr2.c_str())) {
214 #else
215             if (!stricmp(n->c_str(),subjectstr.c_str()) || !stricmp(n->c_str(),subjectstr2.c_str())) {
216 #endif
217                 log.info("matched full subject DN to a key name (%s)", n->c_str());
218                 BIO_free(b);
219                 BIO_free(b2);
220                 return true;
221             }
222         }
223         BIO_free(b);
224         BIO_free(b2);
225
226         log.debug("unable to match DN, trying TLS subjectAltName match");
227         STACK_OF(GENERAL_NAME)* altnames=(STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
228         if (altnames) {
229             int numalts = sk_GENERAL_NAME_num(altnames);
230             for (int an=0; an<numalts; an++) {
231                 const GENERAL_NAME* check = sk_GENERAL_NAME_value(altnames, an);
232                 if (check->type==GEN_DNS || check->type==GEN_URI) {
233                     const char* altptr = (char*)ASN1_STRING_data(check->d.ia5);
234                     const int altlen = ASN1_STRING_length(check->d.ia5);
235                     
236                     for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
237 #ifdef HAVE_STRCASECMP
238                         if ((check->type==GEN_DNS && !strncasecmp(altptr,n->c_str(),altlen))
239 #else
240                         if ((check->type==GEN_DNS && !strnicmp(altptr,n->c_str(),altlen))
241 #endif
242                                 || (check->type==GEN_URI && !strncmp(altptr,n->c_str(),altlen))) {
243                             log.info("matched DNS/URI subjectAltName to a key name (%s)", n->c_str());
244                             GENERAL_NAMES_free(altnames);
245                             return true;
246                         }
247                     }
248                 }
249             }
250         }
251         GENERAL_NAMES_free(altnames);
252             
253         log.debug("unable to match subjectAltName, trying TLS CN match");
254         memset(buf,0,sizeof(buf));
255         if (X509_NAME_get_text_by_NID(subject,NID_commonName,buf,255)>0) {
256             for (vector<string>::const_iterator n=keynames.begin(); n!=keynames.end(); n++) {
257 #ifdef HAVE_STRCASECMP
258                 if (!strcasecmp(buf,n->c_str())) {
259 #else
260                 if (!stricmp(buf,n->c_str())) {
261 #endif
262                     log.info("matched subject CN to a key name (%s)", n->c_str());
263                     return true;
264                 }
265             }
266         }
267         else
268             log.warn("no common name in certificate subject");
269     }
270     else
271         log.error("certificate has no subject?!");
272     
273     return false;
274 }
275
276 bool AbstractPKIXTrustEngine::validate(
277     XSECCryptoX509* certEE,
278     const vector<XSECCryptoX509*>& certChain,
279     const RoleDescriptor& role,
280     bool checkName,
281     const KeyResolver* keyResolver
282     ) const
283 {
284 #ifdef _DEBUG
285     NDC ndc("validate");
286 #endif
287     Category& log=Category::getInstance(SAML_LOGCAT".TrustEngine");
288
289     if (!certEE) {
290         log.error("X.509 credential was NULL, unable to perform validation");
291         return false;
292     }
293
294     if (checkName) {
295         log.debug("checking that the entity certificate name is acceptable");
296         if (!checkEntityNames(certEE,role)) {
297             log.error("entity certificate name was not acceptable");
298             return false;
299         }
300     }
301     
302     log.debug("performing certificate path validation...");
303
304     STACK_OF(X509)* untrusted=sk_X509_new_null();
305     for (vector<XSECCryptoX509*>::const_iterator i=certChain.begin(); i!=certChain.end(); ++i) {
306         sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
307     }
308     
309     auto_ptr<PKIXValidationInfoIterator> pkix(getPKIXValidationInfoIterator(role));
310     while (pkix->next()) {
311         if (::validate(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(),untrusted,pkix.get())) {
312             sk_X509_free(untrusted);
313             return true;
314         }
315     }
316
317     sk_X509_free(untrusted);
318     log.error("failed to validate certificate chain using supplied PKIX information");
319     return false;
320 }
321
322 bool AbstractPKIXTrustEngine::validate(Signature& sig, const RoleDescriptor& role, const KeyResolver* keyResolver) const
323 {
324 #ifdef _DEBUG
325     NDC ndc("validate");
326 #endif
327     Category& log=Category::getInstance(SAML_LOGCAT".TrustEngine");
328
329     log.debug("attempting to validate signature profile");
330     SignatureProfileValidator sigValidator;
331     try {
332         sigValidator.validate(&sig);
333         log.debug("signature profile validated");
334     }
335     catch (ValidationException& e) {
336         if (log.isDebugEnabled()) {
337             log.debug("signature profile failed to validate: %s", e.what());
338         }
339         return false;
340     }
341
342     // Pull the certificate chain out of the signature using an inline KeyResolver.
343     KeyResolver::ResolvedCertificates certs;
344     if (0==m_inlineResolver->resolveCertificates(&sig, certs)) {
345         log.error("unable to perform PKIX validation, signature does not contain any certificates");
346         return false;
347     }
348
349     log.debug("validating signature using certificate from within the signature");
350
351     // Find and save off a pointer to the certificate that unlocks the object.
352     // Most of the time, this will be the first one anyway.
353     XSECCryptoX509* certEE=NULL;
354     SignatureValidator keyValidator;
355     for (vector<XSECCryptoX509*>::const_iterator i=certs.v().begin(); !certEE && i!=certs.v().end(); ++i) {
356         try {
357             keyValidator.setKey((*i)->clonePublicKey());
358             keyValidator.validate(&sig);
359             log.info("signature verified with key inside signature, attempting certificate validation...");
360             certEE=(*i);
361         }
362         catch (ValidationException&) {
363             // trap failures
364         }
365     }
366     
367     if (certEE)
368         return validate(certEE,certs.v(),role,true,keyResolver);
369         
370     log.error("failed to verify signature with embedded certificates");
371     return false;
372 }
373
374 bool AbstractPKIXTrustEngine::validate(
375     const XMLCh* sigAlgorithm,
376     const char* sig,
377     KeyInfo* keyInfo,
378     const char* in,
379     unsigned int in_len,
380     const RoleDescriptor& role,
381     const KeyResolver* keyResolver
382     ) const
383 {
384 #ifdef _DEBUG
385     NDC ndc("validate");
386 #endif
387     Category& log=Category::getInstance(SAML_LOGCAT".TrustEngine");
388
389     // Pull the certificate chain out of the KeyInfo using an inline KeyResolver.
390     KeyResolver::ResolvedCertificates certs;
391     if (!keyInfo || 0==m_inlineResolver->resolveCertificates(keyInfo, certs)) {
392         log.error("unable to perform PKIX validation, KeyInfo does not contain any certificates");
393         return false;
394     }
395
396     log.debug("validating signature using certificate from within KeyInfo");
397
398     // Find and save off a pointer to the certificate that unlocks the object.
399     // Most of the time, this will be the first one anyway.
400     XSECCryptoX509* certEE=NULL;
401     SignatureValidator keyValidator;
402     for (vector<XSECCryptoX509*>::const_iterator i=certs.v().begin(); !certEE && i!=certs.v().end(); ++i) {
403         try {
404             auto_ptr<XSECCryptoKey> key((*i)->clonePublicKey());
405             if (Signature::verifyRawSignature(key.get(), sigAlgorithm, sig, in, in_len)) {
406                 log.info("signature verified with key inside signature, attempting certificate validation...");
407                 certEE=(*i);
408             }
409         }
410         catch (SignatureException&) {
411             // trap failures
412         }
413     }
414     
415     if (certEE)
416         return validate(certEE,certs.v(),role,true,keyResolver);
417         
418     log.error("failed to verify signature with embedded certificates");
419     return false;
420 }