Unified trust engines w/ KeyInfoSource interface, first cut at SOAP transport layer.
[shibboleth/xmltooling.git] / xmltooling / 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 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(getPKIXValidationInfoIterator(keyInfoSource));
293     while (pkix->next()) {
294         if (::validate(certEE,certChain,pkix.get())) {
295             return true;
296         }
297     }
298
299     log.error("failed to validate certificate chain using supplied PKIX information");
300     return false;
301 }
302
303 bool AbstractPKIXTrustEngine::validate(
304     XSECCryptoX509* certEE,
305     const vector<XSECCryptoX509*>& certChain,
306     const KeyInfoSource& keyInfoSource,
307     bool checkName,
308     const KeyResolver* keyResolver
309     ) const
310 {
311     if (!certEE) {
312 #ifdef _DEBUG
313         NDC ndc("validate");
314 #endif
315         Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine").error("X.509 credential was NULL, unable to perform validation");
316         return false;
317     }
318     else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
319 #ifdef _DEBUG
320         NDC ndc("validate");
321 #endif
322         Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine").error("only the OpenSSL XSEC provider is supported");
323         return false;
324     }
325
326     STACK_OF(X509)* untrusted=sk_X509_new_null();
327     for (vector<XSECCryptoX509*>::const_iterator i=certChain.begin(); i!=certChain.end(); ++i) {
328         sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
329     }
330
331     bool ret = validate(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(),untrusted,keyInfoSource,checkName,keyResolver);
332     sk_X509_free(untrusted);
333     return ret;
334 }
335
336 bool AbstractPKIXTrustEngine::validate(
337     Signature& sig,
338     const KeyInfoSource& keyInfoSource,
339     const KeyResolver* keyResolver
340     ) const
341 {
342 #ifdef _DEBUG
343     NDC ndc("validate");
344 #endif
345     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine");
346
347     // Pull the certificate chain out of the signature using an inline KeyResolver.
348     KeyResolver::ResolvedCertificates certs;
349     if (0==m_inlineResolver->resolveCertificates(&sig, certs)) {
350         log.error("unable to perform PKIX validation, signature does not contain any certificates");
351         return false;
352     }
353
354     log.debug("validating signature using certificate from within the signature");
355
356     // Find and save off a pointer to the certificate that unlocks the object.
357     // Most of the time, this will be the first one anyway.
358     XSECCryptoX509* certEE=NULL;
359     SignatureValidator keyValidator;
360     for (vector<XSECCryptoX509*>::const_iterator i=certs.v().begin(); !certEE && i!=certs.v().end(); ++i) {
361         try {
362             keyValidator.setKey((*i)->clonePublicKey());
363             keyValidator.validate(&sig);
364             log.info("signature verified with key inside signature, attempting certificate validation...");
365             certEE=(*i);
366         }
367         catch (ValidationException&) {
368             // trap failures
369         }
370     }
371     
372     if (certEE)
373         return validate(certEE,certs.v(),keyInfoSource,true,keyResolver);
374         
375     log.error("failed to verify signature with embedded certificates");
376     return false;
377 }
378
379 bool AbstractPKIXTrustEngine::validate(
380     const XMLCh* sigAlgorithm,
381     const char* sig,
382     KeyInfo* keyInfo,
383     const char* in,
384     unsigned int in_len,
385     const KeyInfoSource& keyInfoSource,
386     const KeyResolver* keyResolver
387     ) const
388 {
389 #ifdef _DEBUG
390     NDC ndc("validate");
391 #endif
392     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine");
393
394     // Pull the certificate chain out of the KeyInfo using an inline KeyResolver.
395     KeyResolver::ResolvedCertificates certs;
396     if (!keyInfo || 0==m_inlineResolver->resolveCertificates(keyInfo, certs)) {
397         log.error("unable to perform PKIX validation, KeyInfo does not contain any certificates");
398         return false;
399     }
400
401     log.debug("validating signature using certificate from within KeyInfo");
402
403     // Find and save off a pointer to the certificate that unlocks the object.
404     // Most of the time, this will be the first one anyway.
405     XSECCryptoX509* certEE=NULL;
406     SignatureValidator keyValidator;
407     for (vector<XSECCryptoX509*>::const_iterator i=certs.v().begin(); !certEE && i!=certs.v().end(); ++i) {
408         try {
409             auto_ptr<XSECCryptoKey> key((*i)->clonePublicKey());
410             if (Signature::verifyRawSignature(key.get(), sigAlgorithm, sig, in, in_len)) {
411                 log.info("signature verified with key inside signature, attempting certificate validation...");
412                 certEE=(*i);
413             }
414         }
415         catch (SignatureException&) {
416             // trap failures
417         }
418     }
419     
420     if (certEE)
421         return validate(certEE,certs.v(),keyInfoSource,true,keyResolver);
422         
423     log.error("failed to verify signature with embedded certificates");
424     return false;
425 }