5d7a598fee3930110b9fe721330a97ffe54b05b3
[shibboleth/cpp-xmltooling.git] / xmltooling / security / impl / AbstractPKIXTrustEngine.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * AbstractPKIXTrustEngine.cpp
23  * 
24  * A trust engine that uses X.509 trust anchors and CRLs associated with a KeyInfoSource
25  * to perform PKIX validation of signatures and certificates.
26  */
27
28 #include "internal.h"
29 #include "logging.h"
30 #include "security/AbstractPKIXTrustEngine.h"
31 #include "signature/KeyInfo.h"
32 #include "signature/Signature.h"
33 #include "security/CredentialCriteria.h"
34 #include "security/CredentialResolver.h"
35 #include "security/KeyInfoResolver.h"
36 #include "security/OpenSSLCryptoX509CRL.h"
37 #include "security/SecurityHelper.h"
38 #include "security/X509Credential.h"
39 #include "signature/SignatureValidator.h"
40 #include "util/NDC.h"
41 #include "util/PathResolver.h"
42
43 #include <fstream>
44 #include <openssl/x509_vfy.h>
45 #include <openssl/x509v3.h>
46 #include <xercesc/util/XMLUniDefs.hpp>
47 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
48
49 using namespace xmlsignature;
50 using namespace xmltooling::logging;
51 using namespace xmltooling;
52 using namespace std;
53
54
55 namespace {
56     static int XMLTOOL_DLLLOCAL error_callback(int ok, X509_STORE_CTX* ctx)
57     {
58         if (!ok)
59             Category::getInstance("OpenSSL").error("path validation failure: %s", X509_verify_cert_error_string(ctx->error));
60         return ok;
61     }
62
63     static string XMLTOOL_DLLLOCAL X509_NAME_to_string(X509_NAME* n)
64     {
65         string s;
66         BIO* b = BIO_new(BIO_s_mem());
67         X509_NAME_print_ex(b,n,0,XN_FLAG_RFC2253);
68         BIO_flush(b);
69         BUF_MEM* bptr=nullptr;
70         BIO_get_mem_ptr(b, &bptr);
71         if (bptr && bptr->length > 0) {
72             s.append(bptr->data, bptr->length);
73         }
74         BIO_free(b);
75         return s;
76     }
77
78     static time_t XMLTOOL_DLLLOCAL getCRLTime(const ASN1_TIME *a)
79     {
80         struct tm t;
81         memset(&t, 0, sizeof(t));
82         // RFC 5280, sections 5.1.2.4 and 5.1.2.5 require thisUpdate and nextUpdate
83         // to be encoded as UTCTime until 2049, and RFC 5280 section 4.1.2.5.1
84         // further restricts the format to "YYMMDDHHMMSSZ" ("even where the number
85         // of seconds is zero").
86         // As long as OpenSSL doesn't provide any API to convert ASN1_TIME values
87         // time_t, we therefore have to parse it ourselves, unfortunately.
88         if (sscanf((const char*)a->data, "%2d%2d%2d%2d%2d%2dZ",
89             &t.tm_year, &t.tm_mon, &t.tm_mday,
90             &t.tm_hour, &t.tm_min, &t.tm_sec) == 6) {
91             if (t.tm_year <= 50) {
92                 // RFC 5280, section 4.1.2.5.1
93                 t.tm_year += 100;
94             }
95             t.tm_mon--;
96 #if defined(HAVE_TIMEGM)
97             return timegm(&t);
98 #else
99             // Windows, and hopefully most others...?
100             return mktime(&t) - timezone;
101 #endif
102         }
103         return (time_t)-1;
104     }
105
106     static bool XMLTOOL_DLLLOCAL isFreshCRL(XSECCryptoX509CRL *c, Category* log=nullptr)
107     {
108         // eventually, these should be made configurable
109         #define MIN_SECS_REMAINING 86400
110         #define MIN_PERCENT_REMAINING 10
111         if (c) {
112             const X509_CRL* crl = static_cast<OpenSSLCryptoX509CRL*>(c)->getOpenSSLX509CRL();
113             time_t thisUpdate = getCRLTime(X509_CRL_get_lastUpdate(crl));
114             time_t nextUpdate = getCRLTime(X509_CRL_get_nextUpdate(crl));
115             time_t now = time(nullptr);
116
117             if (thisUpdate < 0 || nextUpdate < 0) {
118                 // we failed to parse at least one of the fields (they were not encoded
119                 // as required by RFC 5280, actually)
120                 time_t exp = now + MIN_SECS_REMAINING;
121                 if (log) {
122                     log->warn("isFreshCRL (issuer '%s'): improperly encoded thisUpdate or nextUpdate field - falling back to simple time comparison",
123                               (X509_NAME_to_string(X509_CRL_get_issuer(crl))).c_str());
124                 }
125                 return (X509_cmp_time(X509_CRL_get_nextUpdate(crl), &exp) > 0) ? true : false;
126             }
127             else {
128                 if (log && log->isDebugEnabled()) {
129                     log->debug("isFreshCRL (issuer '%s'): %.0f seconds until nextUpdate (%3.2f%% elapsed since thisUpdate)",
130                               (X509_NAME_to_string(X509_CRL_get_issuer(crl))).c_str(),
131                               difftime(nextUpdate, now), (difftime(now, thisUpdate) * 100) / difftime(nextUpdate, thisUpdate));
132                 }
133
134                 // consider it recent enough if there are at least MIN_SECS_REMAINING
135                 // to the nextUpdate, and at least MIN_PERCENT_REMAINING of its
136                 // overall "validity" are remaining to the nextUpdate
137                 return (now + MIN_SECS_REMAINING < nextUpdate) &&
138                         ((difftime(nextUpdate, now) * 100) / difftime(nextUpdate, thisUpdate) > MIN_PERCENT_REMAINING);
139             }
140         }
141         return false;
142     }
143
144     static XSECCryptoX509CRL* XMLTOOL_DLLLOCAL getRemoteCRLs(const char* cdpuri, Category& log) {
145         // This is a temporary CRL cache implementation to avoid breaking binary compatibility
146         // for the library. Caching can't rely on any member objects within the TrustEngine,
147         // including locks, so we're using the global library lock for the time being.
148         // All other state is kept in the file system.
149
150         // minimum number of seconds between re-attempting a download from one particular CRLDP
151         #define MIN_RETRY_WAIT 60
152
153         // The filenames for the CRL cache are based on a hash of the CRL location.
154         string cdpfile = SecurityHelper::doHash("SHA1", cdpuri, strlen(cdpuri)) + ".crl";
155         XMLToolingConfig::getConfig().getPathResolver()->resolve(cdpfile, PathResolver::XMLTOOLING_RUN_FILE);
156         string cdpstaging = cdpfile + ".tmp";
157         string tsfile = cdpfile + ".ts";
158
159         time_t now = time(nullptr);
160         vector<XSECCryptoX509CRL*> crls;
161
162         try {
163             // While holding the lock, check for a cached copy of the CRL, and remove "expired" ones.
164             Locker glock(&XMLToolingConfig::getConfig());
165 #ifdef WIN32
166             struct _stat stat_buf;
167             if (_stat(cdpfile.c_str(), &stat_buf) == 0) {
168 #else
169             struct stat stat_buf;
170             if (stat(cdpfile.c_str(), &stat_buf) == 0) {
171 #endif
172                 SecurityHelper::loadCRLsFromFile(crls, cdpfile.c_str());
173                 if (crls.empty() || crls.front()->getProviderName() != DSIGConstants::s_unicodeStrPROVOpenSSL ||
174                     X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(crls.front())->getOpenSSLX509CRL()), &now) < 0) {
175                     for_each(crls.begin(), crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
176                     crls.clear();
177                     remove(cdpfile.c_str());    // may as well delete the local copy
178                     remove(tsfile.c_str());
179                     log.info("deleting cached CRL from %s with nextUpdate field in the past", cdpuri);
180                 }
181             }
182         }
183         catch (exception& ex) {
184             log.error("exception loading cached copy of CRL from %s: %s", cdpuri, ex.what());
185         }
186
187         if (crls.empty() || !isFreshCRL(crls.front(), &log)) {
188             bool updateTimestamp = true;
189             try {
190                 // If we get here, the cached copy didn't exist yet, or it's time to refresh.
191                 // To limit the rate of unsuccessful attempts when a CRLDP is unreachable,
192                 // we remember the timestamp of the last attempt (both successful/unsuccessful).
193                 // We store this in the file system because of the binary compatibility issue.
194                 time_t ts = 0;
195                 try {
196                     Locker glock(&XMLToolingConfig::getConfig());
197                     ifstream tssrc(tsfile.c_str());
198                     if (tssrc)
199                         tssrc >> ts;
200                 }
201                 catch (exception&) {
202                     ts = 0;
203                 }
204
205                 if (difftime(now, ts) > MIN_RETRY_WAIT) {
206                     SOAPTransport::Address addr("AbstractPKIXTrustEngine", cdpuri, cdpuri);
207                     string scheme(addr.m_endpoint, strchr(addr.m_endpoint,':') - addr.m_endpoint);
208                     auto_ptr<SOAPTransport> soap(XMLToolingConfig::getConfig().SOAPTransportManager.newPlugin(scheme.c_str(), addr));
209                     soap->send();
210                     istream& msg = soap->receive();
211                     Locker glock(&XMLToolingConfig::getConfig());
212                     ofstream out(cdpstaging.c_str(), fstream::trunc|fstream::binary);
213                     out << msg.rdbuf();
214                     out.close();
215                     SecurityHelper::loadCRLsFromFile(crls, cdpstaging.c_str());
216                     if (crls.empty() || crls.front()->getProviderName() != DSIGConstants::s_unicodeStrPROVOpenSSL ||
217                         X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(crls.front())->getOpenSSLX509CRL()), &now) < 0) {
218                         // The "new" CRL wasn't usable, so get rid of it.
219                         for_each(crls.begin(), crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
220                         crls.clear();
221                         remove(cdpstaging.c_str());
222                         log.error("ignoring CRL retrieved from %s with nextUpdate field in the past", cdpuri);
223                     }
224                     else {
225                         // "Commit" the new CRL. Note that we might add a CRL which doesn't pass
226                         // isFreshCRL, but that's preferrable over adding none at all.
227                         log.info("CRL refreshed from %s", cdpuri);
228                         remove(cdpfile.c_str());
229                         if (rename(cdpstaging.c_str(), cdpfile.c_str()) != 0)
230                             log.error("unable to rename CRL staging file");
231                     }
232                 }
233                 else {
234                     updateTimestamp = false;    // don't update if we're within the backoff window
235                 }
236             }
237             catch (exception& ex) {
238                 log.error("exception downloading/caching CRL from %s: %s", cdpuri, ex.what());
239             }
240
241             if (updateTimestamp) {
242                 // update the timestamp file
243                 Locker glock(&XMLToolingConfig::getConfig());
244                 ofstream tssink(tsfile.c_str(), fstream::trunc);
245                 tssink << now;
246                 tssink.close();
247             }
248         }
249
250         if (crls.empty())
251             return nullptr;
252         for_each(crls.begin() + 1, crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
253         return crls.front();
254     }
255
256     static bool XMLTOOL_DLLLOCAL validate(
257         X509* EE,
258         STACK_OF(X509)* untrusted,
259         AbstractPKIXTrustEngine::PKIXValidationInfoIterator* pkixInfo,
260         bool useCRL,
261         bool fullCRLChain,
262         const vector<XSECCryptoX509CRL*>* inlineCRLs=nullptr,
263         bool policyMappingInhibit=false,
264         bool anyPolicyInhibit=false,
265         const set<string>* policyOIDs=nullptr
266         )
267     {
268         Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine");
269     
270         // First we build a stack of CA certs. These objects are all referenced in place.
271         log.debug("supplying PKIX Validation information");
272     
273         // We need this for CRL support.
274         X509_STORE* store=X509_STORE_new();
275         if (!store) {
276             log_openssl();
277             return false;
278         }
279
280         // PKIX policy checking (cf. RFCs 3280/5280 section 6)
281         if (policyMappingInhibit || anyPolicyInhibit || (policyOIDs && !policyOIDs->empty())) {
282 #if (OPENSSL_VERSION_NUMBER < 0x00908000L)
283             log.error("PKIX policy checking option is configured, but OpenSSL version is less than 0.9.8");
284             X509_STORE_free(store);
285             return false;
286 #else
287             unsigned long pflags = 0;
288             X509_VERIFY_PARAM *vpm = X509_VERIFY_PARAM_new();
289             if (!vpm) {
290                 log_openssl();
291                 X509_STORE_free(store);
292                 return false;
293             }
294
295             // populate the "user-initial-policy-set" input variable
296             if (policyOIDs && !policyOIDs->empty()) {
297                 for (set<string>::const_iterator o=policyOIDs->begin(); o!=policyOIDs->end(); o++) {
298                     ASN1_OBJECT *oid = OBJ_txt2obj(o->c_str(), 1);
299                     if (oid && X509_VERIFY_PARAM_add0_policy(vpm, oid)) {
300                         log.debug("OID (%s) added to set of acceptable policies", o->c_str());
301                     }
302                     else {
303                         log_openssl();
304                         log.error("unable to parse/configure policy OID value (%s)", o->c_str());
305                         if (oid)
306                             ASN1_OBJECT_free(oid);
307                         X509_VERIFY_PARAM_free(vpm);
308                         X509_STORE_free(store);
309                         return false;
310                     }
311                 }
312                 // when the user has supplied at least one policy OID, he obviously wants to check
313                 // for an explicit policy ("initial-explicit-policy")
314                 pflags |= X509_V_FLAG_EXPLICIT_POLICY;
315             }
316
317             // "initial-policy-mapping-inhibit" input variable
318             if (policyMappingInhibit)
319                 pflags |= X509_V_FLAG_INHIBIT_MAP;
320             // "initial-any-policy-inhibit" input variable
321             if (anyPolicyInhibit)
322                 pflags |= X509_V_FLAG_INHIBIT_ANY;
323
324             if (!X509_VERIFY_PARAM_set_flags(vpm, pflags) || !X509_STORE_set1_param(store, vpm)) {
325                 log_openssl();
326                 log.error("unable to set PKIX policy checking parameters");
327                 X509_VERIFY_PARAM_free(vpm);
328                 X509_STORE_free(store);
329                 return false;
330             }
331
332             X509_VERIFY_PARAM_free(vpm);
333 #endif
334         }
335     
336         // This contains the state of the validate operation.
337         int count=0;
338         X509_STORE_CTX ctx;
339
340         // AFAICT, EE and untrusted are passed in but not owned by the ctx.
341 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
342         if (X509_STORE_CTX_init(&ctx,store,EE,untrusted)!=1) {
343             log_openssl();
344             log.error("unable to initialize X509_STORE_CTX");
345             X509_STORE_free(store);
346             return false;
347         }
348 #else
349         X509_STORE_CTX_init(&ctx,store,EE,untrusted);
350 #endif
351
352         STACK_OF(X509)* CAstack = sk_X509_new_null();
353         const vector<XSECCryptoX509*>& CAcerts = pkixInfo->getTrustAnchors();
354         for (vector<XSECCryptoX509*>::const_iterator i=CAcerts.begin(); i!=CAcerts.end(); ++i) {
355             if ((*i)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL) {
356                 sk_X509_push(CAstack,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
357                 ++count;
358             }
359         }
360         log.debug("supplied (%d) CA certificate(s)", count);
361
362         // Seems to be most efficient to just pass in the CA stack.
363         X509_STORE_CTX_trusted_stack(&ctx,CAstack);
364         X509_STORE_CTX_set_depth(&ctx,100);    // we check the depth down below
365         X509_STORE_CTX_set_verify_cb(&ctx,error_callback);
366
367         // Do a first pass verify. If CRLs aren't used, this is the only pass.
368         int ret=X509_verify_cert(&ctx);
369         if (ret==1) {
370             // Now see if the depth was acceptable by counting the number of intermediates.
371             int depth=sk_X509_num(ctx.chain)-2;
372             if (pkixInfo->getVerificationDepth() < depth) {
373                 log.error(
374                     "certificate chain was too long (%d intermediates, only %d allowed)",
375                     (depth==-1) ? 0 : depth,
376                     pkixInfo->getVerificationDepth()
377                     );
378                 ret=0;
379             }
380         }
381
382         if (useCRL) {
383 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
384             // When we add CRLs, we have to be sure the nextUpdate hasn't passed, because OpenSSL won't accept
385             // the CRL in that case. If we end up not adding a CRL for a particular link in the chain, the
386             // validation will fail (if the fullChain option was set).
387             set<string> crlissuers;
388             time_t now = time(nullptr);
389             if (inlineCRLs) {
390                 for (vector<XSECCryptoX509CRL*>::const_iterator j=inlineCRLs->begin(); j!=inlineCRLs->end(); ++j) {
391                     if ((*j)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL &&
392                         (X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()), &now) > 0)) {
393                         // owned by store
394                         X509_STORE_add_crl(store, X509_CRL_dup(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()));
395                         string crlissuer(X509_NAME_to_string(X509_CRL_get_issuer(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL())));
396                         if (!crlissuer.empty()) {
397                             log.debug("added inline CRL issued by (%s)", crlissuer.c_str());
398                             crlissuers.insert(crlissuer);
399                         }
400                     }
401                 }
402             }
403             const vector<XSECCryptoX509CRL*>& crls = pkixInfo->getCRLs();
404             for (vector<XSECCryptoX509CRL*>::const_iterator j=crls.begin(); j!=crls.end(); ++j) {
405                 if ((*j)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL &&
406                     (X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()), &now) > 0)) {
407                     // owned by store
408                     X509_STORE_add_crl(store, X509_CRL_dup(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()));
409                     string crlissuer(X509_NAME_to_string(X509_CRL_get_issuer(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL())));
410                     if (!crlissuer.empty()) {
411                         log.debug("added CRL issued by (%s)", crlissuer.c_str());
412                         crlissuers.insert(crlissuer);
413                     }
414                 }
415             }
416
417             for (int i = 0; i < sk_X509_num(untrusted); ++i) {
418                 X509 *cert = sk_X509_value(untrusted, i);
419                 string crlissuer(X509_NAME_to_string(X509_get_issuer_name(cert)));
420                 if (crlissuers.count(crlissuer)) {
421                    // We already have a CRL for this cert, so skip CRLDP processing for this one.
422                    continue;
423                 }
424
425                 bool foundUsableCDP = false;
426                 STACK_OF(DIST_POINT)* dps = (STACK_OF(DIST_POINT)*)X509_get_ext_d2i(cert, NID_crl_distribution_points, nullptr, nullptr);
427                 for (int ii = 0; !foundUsableCDP && ii < sk_DIST_POINT_num(dps); ++ii) {
428                     DIST_POINT* dp = sk_DIST_POINT_value(dps, ii);
429                     if (!dp->distpoint || dp->distpoint->type != 0)
430                         continue;
431                     for (int iii = 0; !foundUsableCDP && iii < sk_GENERAL_NAME_num(dp->distpoint->name.fullname); ++iii) {
432                         GENERAL_NAME* gen = sk_GENERAL_NAME_value(dp->distpoint->name.fullname, iii);
433                         // Only consider HTTP URIs, and stop after the first one we find.
434 #ifdef HAVE_STRCASECMP
435                         if (gen->type == GEN_URI && (!strncasecmp((const char*)gen->d.ia5->data, "http:", 5))) {
436 #else
437                         if (gen->type == GEN_URI && (!strnicmp((const char*)gen->d.ia5->data, "http:", 5))) {
438 #endif
439                             const char* cdpuri = (const char*)gen->d.ia5->data;
440                             auto_ptr<XSECCryptoX509CRL> crl(getRemoteCRLs(cdpuri, log));
441                             if (crl.get() && crl->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL &&
442                                 (isFreshCRL(crl.get()) || (ii == sk_DIST_POINT_num(dps)-1 && iii == sk_GENERAL_NAME_num(dp->distpoint->name.fullname)-1))) {
443                                 // owned by store
444                                 X509_STORE_add_crl(store, X509_CRL_dup(static_cast<OpenSSLCryptoX509CRL*>(crl.get())->getOpenSSLX509CRL()));
445                                 log.debug("added CRL issued by (%s)", crlissuer.c_str());
446                                 crlissuers.insert(crlissuer);
447                                 foundUsableCDP = true;
448                             }
449                         }
450                     }
451                 }
452                 sk_DIST_POINT_free(dps);
453             }
454
455             // Do a second pass verify with CRLs in place.
456             X509_STORE_CTX_set_flags(&ctx, fullCRLChain ? (X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL) : (X509_V_FLAG_CRL_CHECK));
457             ret=X509_verify_cert(&ctx);
458 #else
459             log.warn("CRL checking is enabled, but OpenSSL version is too old");
460             ret = 0;
461 #endif
462         }
463
464         // Clean up...
465         X509_STORE_CTX_cleanup(&ctx);
466         X509_STORE_free(store);
467         sk_X509_free(CAstack);
468     
469         if (ret==1) {
470             log.debug("successfully validated certificate chain");
471             return true;
472         }
473         
474         return false;
475     }
476
477     static XMLCh fullCRLChain[] =                   UNICODE_LITERAL_12(f,u,l,l,C,R,L,C,h,a,i,n);
478     static XMLCh checkRevocation[] =        UNICODE_LITERAL_15(c,h,e,c,k,R,e,v,o,c,a,t,i,o,n);
479     static XMLCh policyMappingInhibit[] =   UNICODE_LITERAL_20(p,o,l,i,c,y,M,a,p,p,i,n,g,I,n,h,i,b,i,t);
480     static XMLCh anyPolicyInhibit[] =       UNICODE_LITERAL_16(a,n,y,P,o,l,i,c,y,I,n,h,i,b,i,t);
481     static XMLCh PolicyOID[] =                      UNICODE_LITERAL_9(P,o,l,i,c,y,O,I,D);
482     static XMLCh TrustedName[] =                    UNICODE_LITERAL_11(T,r,u,s,t,e,d,N,a,m,e);
483 };
484
485 AbstractPKIXTrustEngine::PKIXValidationInfoIterator::PKIXValidationInfoIterator()
486 {
487 }
488
489 AbstractPKIXTrustEngine::PKIXValidationInfoIterator::~PKIXValidationInfoIterator()
490 {
491 }
492
493 AbstractPKIXTrustEngine::AbstractPKIXTrustEngine(const xercesc::DOMElement* e)
494         : TrustEngine(e),
495                 m_checkRevocation(XMLHelper::getAttrString(e, nullptr, checkRevocation)),
496                 m_fullCRLChain(XMLHelper::getAttrBool(e, false, fullCRLChain)),
497                 m_policyMappingInhibit(XMLHelper::getAttrBool(e, false, policyMappingInhibit)),
498                 m_anyPolicyInhibit(XMLHelper::getAttrBool(e, false, anyPolicyInhibit))
499 {
500     if (m_fullCRLChain) {
501         Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX").warn(
502             "fullCRLChain option is deprecated, setting checkRevocation to \"fullChain\""
503             );
504         m_checkRevocation = "fullChain";
505     }
506     else if (m_checkRevocation == "fullChain") {
507         m_fullCRLChain = true; // in case anything's using this
508     }
509
510     xercesc::DOMElement* c = XMLHelper::getFirstChildElement(e);
511     while (c) {
512         if (c->hasChildNodes()) {
513             auto_ptr_char v(c->getTextContent());
514             if (v.get() && *v.get()) {
515                 if (XMLString::equals(c->getLocalName(), PolicyOID))
516                     m_policyOIDs.insert(v.get());
517                 else if (XMLString::equals(c->getLocalName(), TrustedName))
518                     m_trustedNames.insert(v.get());
519             }
520         }
521         c = XMLHelper::getNextSiblingElement(c);
522     }
523 }
524
525 AbstractPKIXTrustEngine::~AbstractPKIXTrustEngine()
526 {
527 }
528
529 bool AbstractPKIXTrustEngine::checkEntityNames(
530     X509* certEE, const CredentialResolver& credResolver, const CredentialCriteria& criteria
531     ) const
532 {
533     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX");
534
535     // We resolve to a set of trusted credentials.
536     vector<const Credential*> creds;
537     credResolver.resolve(creds,&criteria);
538
539     // Build a list of acceptable names.
540     set<string> trustednames = m_trustedNames;
541     if (log.isDebugEnabled()) {
542         for (set<string>::const_iterator n=m_trustedNames.begin(); n!=m_trustedNames.end(); n++) {
543             log.debug("adding to list of trusted names (%s)", n->c_str());
544         }
545     }
546     if (criteria.getPeerName()) {
547         trustednames.insert(criteria.getPeerName());
548         log.debug("adding to list of trusted names (%s)", criteria.getPeerName());
549     }
550     for (vector<const Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) {
551         trustednames.insert((*cred)->getKeyNames().begin(), (*cred)->getKeyNames().end());
552         if (log.isDebugEnabled()) {
553             for (set<string>::const_iterator n=(*cred)->getKeyNames().begin(); n!=(*cred)->getKeyNames().end(); n++) {
554                 log.debug("adding to list of trusted names (%s)", n->c_str());
555             }
556         }
557     }
558
559     X509_NAME* subject=X509_get_subject_name(certEE);
560     if (subject) {
561         // One way is a direct match to the subject DN.
562         // Seems that the way to do the compare is to write the X509_NAME into a BIO.
563         BIO* b = BIO_new(BIO_s_mem());
564         BIO* b2 = BIO_new(BIO_s_mem());
565         // The flags give us LDAP order instead of X.500, with a comma separator.
566         X509_NAME_print_ex(b,subject,0,XN_FLAG_RFC2253);
567         BIO_flush(b);
568         // The flags give us LDAP order instead of X.500, with a comma plus space separator.
569         X509_NAME_print_ex(b2,subject,0,XN_FLAG_RFC2253 + XN_FLAG_SEP_CPLUS_SPC - XN_FLAG_SEP_COMMA_PLUS);
570         BIO_flush(b2);
571
572         BUF_MEM* bptr=nullptr;
573         BUF_MEM* bptr2=nullptr;
574         BIO_get_mem_ptr(b, &bptr);
575         BIO_get_mem_ptr(b2, &bptr2);
576
577         if (bptr && bptr->length > 0 && log.isDebugEnabled()) {
578             string subjectstr(bptr->data, bptr->length);
579             log.debug("certificate subject: %s", subjectstr.c_str());
580         }
581         
582         // Check each keyname.
583         for (set<string>::const_iterator n=trustednames.begin(); bptr && bptr2 && n!=trustednames.end(); n++) {
584 #ifdef HAVE_STRCASECMP
585             if ((n->length() == bptr->length && !strncasecmp(n->c_str(), bptr->data, bptr->length)) ||
586                 (n->length() == bptr2->length && !strncasecmp(n->c_str(), bptr2->data, bptr2->length))) {
587 #else
588             if ((n->length() == bptr->length && !strnicmp(n->c_str(), bptr->data, bptr->length)) ||
589                 (n->length() == bptr2->length && !strnicmp(n->c_str(), bptr2->data, bptr2->length))) {
590 #endif
591                 log.debug("matched full subject DN to a key name (%s)", n->c_str());
592                 BIO_free(b);
593                 BIO_free(b2);
594                 return true;
595             }
596         }
597         BIO_free(b);
598         BIO_free(b2);
599
600         log.debug("unable to match DN, trying TLS subjectAltName match");
601         STACK_OF(GENERAL_NAME)* altnames=(STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(certEE, NID_subject_alt_name, nullptr, nullptr);
602         if (altnames) {
603             int numalts = sk_GENERAL_NAME_num(altnames);
604             for (int an=0; an<numalts; an++) {
605                 const GENERAL_NAME* check = sk_GENERAL_NAME_value(altnames, an);
606                 if (check->type==GEN_DNS || check->type==GEN_URI) {
607                     const char* altptr = (char*)ASN1_STRING_data(check->d.ia5);
608                     const int altlen = ASN1_STRING_length(check->d.ia5);
609                     for (set<string>::const_iterator n=trustednames.begin(); n!=trustednames.end(); n++) {
610 #ifdef HAVE_STRCASECMP
611                         if ((check->type==GEN_DNS && n->length()==altlen && !strncasecmp(altptr,n->c_str(),altlen))
612 #else
613                         if ((check->type==GEN_DNS && n->length()==altlen && !strnicmp(altptr,n->c_str(),altlen))
614 #endif
615                                 || (check->type==GEN_URI && n->length()==altlen && !strncmp(altptr,n->c_str(),altlen))) {
616                             log.debug("matched DNS/URI subjectAltName to a key name (%s)", n->c_str());
617                             GENERAL_NAMES_free(altnames);
618                             return true;
619                         }
620                     }
621                 }
622             }
623         }
624         GENERAL_NAMES_free(altnames);
625             
626         log.debug("unable to match subjectAltName, trying TLS CN match");
627
628         // Fetch the last CN RDN.
629         char* peer_CN = nullptr;
630         int j,i = -1;
631         while ((j=X509_NAME_get_index_by_NID(subject, NID_commonName, i)) >= 0)
632             i = j;
633         if (i >= 0) {
634             ASN1_STRING* tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject, i));
635             // Copied in from libcurl.
636             /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input
637                is already UTF-8 encoded. We check for this case and copy the raw
638                string manually to avoid the problem. */
639             if(tmp && ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
640                 j = ASN1_STRING_length(tmp);
641                 if(j >= 0) {
642                     peer_CN = (char*)OPENSSL_malloc(j + 1);
643                     memcpy(peer_CN, ASN1_STRING_data(tmp), j);
644                     peer_CN[j] = '\0';
645                 }
646             }
647             else /* not a UTF8 name */ {
648                 j = ASN1_STRING_to_UTF8(reinterpret_cast<unsigned char**>(&peer_CN), tmp);
649             }
650
651             for (set<string>::const_iterator n=trustednames.begin(); n!=trustednames.end(); n++) {
652 #ifdef HAVE_STRCASECMP
653                 if (n->length() == j && !strncasecmp(peer_CN, n->c_str(), j)) {
654 #else
655                 if (n->length() == j && !strnicmp(peer_CN, n->c_str(), j)) {
656 #endif
657                     log.debug("matched subject CN to a key name (%s)", n->c_str());
658                     if(peer_CN)
659                         OPENSSL_free(peer_CN);
660                     return true;
661                 }
662             }
663             if(peer_CN)
664                 OPENSSL_free(peer_CN);
665         }
666         else {
667             log.warn("no common name in certificate subject");
668         }
669     }
670     else {
671         log.error("certificate has no subject?!");
672     }
673     
674     return false;
675 }
676
677 bool AbstractPKIXTrustEngine::validateWithCRLs(
678     X509* certEE,
679     STACK_OF(X509)* certChain,
680     const CredentialResolver& credResolver,
681     CredentialCriteria* criteria,
682     const std::vector<XSECCryptoX509CRL*>* inlineCRLs
683     ) const
684 {
685 #ifdef _DEBUG
686     NDC ndc("validateWithCRLs");
687 #endif
688     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX");
689
690     if (!certEE) {
691         log.error("X.509 credential was NULL, unable to perform validation");
692         return false;
693     }
694
695     if (criteria && criteria->getPeerName() && *(criteria->getPeerName())) {
696         log.debug("checking that the certificate name is acceptable");
697         if (criteria && criteria->getUsage()==Credential::UNSPECIFIED_CREDENTIAL)
698             criteria->setUsage(Credential::SIGNING_CREDENTIAL);
699         if (!checkEntityNames(certEE,credResolver,*criteria)) {
700             log.error("certificate name was not acceptable");
701             return false;
702         }
703     }
704     else if (!m_trustedNames.empty()) {
705         log.debug("checking that the certificate name is acceptable");
706         CredentialCriteria cc;
707         cc.setUsage(Credential::SIGNING_CREDENTIAL);
708         if (!checkEntityNames(certEE,credResolver,cc)) {
709             log.error("certificate name was not acceptable");
710             return false;
711         }
712     }
713     
714     log.debug("performing certificate path validation...");
715
716     auto_ptr<PKIXValidationInfoIterator> pkix(getPKIXValidationInfoIterator(credResolver, criteria));
717     while (pkix->next()) {
718         if (::validate(
719                                 certEE,
720                                 certChain,
721                                 pkix.get(),
722                                 (m_checkRevocation=="entityOnly" || m_checkRevocation=="fullChain"),
723                                 (m_checkRevocation=="fullChain"),
724                                 (m_checkRevocation=="entityOnly" || m_checkRevocation=="fullChain") ? inlineCRLs : nullptr,
725                                 m_policyMappingInhibit,
726                                 m_anyPolicyInhibit,
727                                 &m_policyOIDs
728                                 )) {
729             return true;
730         }
731     }
732
733     log.debug("failed to validate certificate chain using supplied PKIX information");
734     return false;
735 }
736
737 bool AbstractPKIXTrustEngine::validate(
738     X509* certEE,
739     STACK_OF(X509)* certChain,
740     const CredentialResolver& credResolver,
741     CredentialCriteria* criteria
742     ) const
743 {
744     return validateWithCRLs(certEE,certChain,credResolver,criteria);
745 }
746
747 bool AbstractPKIXTrustEngine::validate(
748     XSECCryptoX509* certEE,
749     const vector<XSECCryptoX509*>& certChain,
750     const CredentialResolver& credResolver,
751     CredentialCriteria* criteria
752     ) const
753 {
754 #ifdef _DEBUG
755         NDC ndc("validate");
756 #endif
757     if (!certEE) {
758         Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX").error("X.509 credential was NULL, unable to perform validation");
759         return false;
760     }
761     else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
762         Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX").error("only the OpenSSL XSEC provider is supported");
763         return false;
764     }
765
766     STACK_OF(X509)* untrusted=sk_X509_new_null();
767     for (vector<XSECCryptoX509*>::const_iterator i=certChain.begin(); i!=certChain.end(); ++i)
768         sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
769
770     bool ret = validate(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(), untrusted, credResolver, criteria);
771     sk_X509_free(untrusted);
772     return ret;
773 }
774
775 bool AbstractPKIXTrustEngine::validate(
776     Signature& sig,
777     const CredentialResolver& credResolver,
778     CredentialCriteria* criteria
779     ) const
780 {
781 #ifdef _DEBUG
782     NDC ndc("validate");
783 #endif
784     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX");
785
786     const KeyInfoResolver* inlineResolver = m_keyInfoResolver;
787     if (!inlineResolver)
788         inlineResolver = XMLToolingConfig::getConfig().getKeyInfoResolver();
789     if (!inlineResolver) {
790         log.error("unable to perform PKIX validation, no KeyInfoResolver available");
791         return false;
792     }
793
794     // Pull the certificate chain out of the signature.
795     X509Credential* x509cred;
796     auto_ptr<Credential> cred(inlineResolver->resolve(&sig,X509Credential::RESOLVE_CERTS|X509Credential::RESOLVE_CRLS));
797     if (!cred.get() || !(x509cred=dynamic_cast<X509Credential*>(cred.get()))) {
798         log.error("unable to perform PKIX validation, signature does not contain any certificates");
799         return false;
800     }
801     const vector<XSECCryptoX509*>& certs = x509cred->getEntityCertificateChain();
802     if (certs.empty()) {
803         log.error("unable to perform PKIX validation, signature does not contain any certificates");
804         return false;
805     }
806
807     log.debug("validating signature using certificate from within the signature");
808
809     // Find and save off a pointer to the certificate that unlocks the object.
810     // Most of the time, this will be the first one anyway.
811     XSECCryptoX509* certEE=nullptr;
812     SignatureValidator keyValidator;
813     for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); !certEE && i!=certs.end(); ++i) {
814         try {
815             auto_ptr<XSECCryptoKey> key((*i)->clonePublicKey());
816             keyValidator.setKey(key.get());
817             keyValidator.validate(&sig);
818             log.debug("signature verified with key inside signature, attempting certificate validation...");
819             certEE=(*i);
820         }
821         catch (ValidationException& ex) {
822             log.debug(ex.what());
823         }
824     }
825     
826     if (!certEE) {
827         log.debug("failed to verify signature with embedded certificates");
828         return false;
829     }
830     else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
831         log.error("only the OpenSSL XSEC provider is supported");
832         return false;
833     }
834
835     STACK_OF(X509)* untrusted=sk_X509_new_null();
836     for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); i!=certs.end(); ++i)
837         sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
838     const vector<XSECCryptoX509CRL*>& crls = x509cred->getCRLs();
839     bool ret = validateWithCRLs(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(), untrusted, credResolver, criteria, &crls);
840     sk_X509_free(untrusted);
841     return ret;
842 }
843
844 bool AbstractPKIXTrustEngine::validate(
845     const XMLCh* sigAlgorithm,
846     const char* sig,
847     KeyInfo* keyInfo,
848     const char* in,
849     unsigned int in_len,
850     const CredentialResolver& credResolver,
851     CredentialCriteria* criteria
852     ) const
853 {
854 #ifdef _DEBUG
855     NDC ndc("validate");
856 #endif
857     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX");
858
859     if (!keyInfo) {
860         log.error("unable to perform PKIX validation, KeyInfo not present");
861         return false;
862     }
863
864     const KeyInfoResolver* inlineResolver = m_keyInfoResolver;
865     if (!inlineResolver)
866         inlineResolver = XMLToolingConfig::getConfig().getKeyInfoResolver();
867     if (!inlineResolver) {
868         log.error("unable to perform PKIX validation, no KeyInfoResolver available");
869         return false;
870     }
871
872     // Pull the certificate chain out of the signature.
873     X509Credential* x509cred;
874     auto_ptr<Credential> cred(inlineResolver->resolve(keyInfo,X509Credential::RESOLVE_CERTS));
875     if (!cred.get() || !(x509cred=dynamic_cast<X509Credential*>(cred.get()))) {
876         log.error("unable to perform PKIX validation, KeyInfo does not contain any certificates");
877         return false;
878     }
879     const vector<XSECCryptoX509*>& certs = x509cred->getEntityCertificateChain();
880     if (certs.empty()) {
881         log.error("unable to perform PKIX validation, KeyInfo does not contain any certificates");
882         return false;
883     }
884
885     log.debug("validating signature using certificate from within KeyInfo");
886
887     // Find and save off a pointer to the certificate that unlocks the object.
888     // Most of the time, this will be the first one anyway.
889     XSECCryptoX509* certEE=nullptr;
890     for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); !certEE && i!=certs.end(); ++i) {
891         try {
892             auto_ptr<XSECCryptoKey> key((*i)->clonePublicKey());
893             if (Signature::verifyRawSignature(key.get(), sigAlgorithm, sig, in, in_len)) {
894                 log.debug("signature verified with key inside signature, attempting certificate validation...");
895                 certEE=(*i);
896             }
897         }
898         catch (SignatureException& ex) {
899             log.debug(ex.what());
900         }
901     }
902
903     if (!certEE) {
904         log.debug("failed to verify signature with embedded certificates");
905         return false;
906     }
907     else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
908         log.error("only the OpenSSL XSEC provider is supported");
909         return false;
910     }
911
912     STACK_OF(X509)* untrusted=sk_X509_new_null();
913     for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); i!=certs.end(); ++i)
914         sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
915     const vector<XSECCryptoX509CRL*>& crls = x509cred->getCRLs();
916     bool ret = validateWithCRLs(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(), untrusted, credResolver, criteria, &crls);
917     sk_X509_free(untrusted);
918     return ret;
919 }