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