Fix some tabs in the last patch.
[shibboleth/cpp-xmltooling.git] / xmltooling / security / impl / PKIXPathValidator.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  * PKIXPathValidator.cpp
23  * 
24  * A path validator based on PKIX support in OpenSSL.
25  */
26
27 #include "internal.h"
28 #include "logging.h"
29 #include "security/OpenSSLPathValidator.h"
30 #include "security/OpenSSLCryptoX509CRL.h"
31 #include "security/PKIXPathValidatorParams.h"
32 #include "security/SecurityHelper.h"
33 #include "util/NDC.h"
34 #include "util/PathResolver.h"
35 #include "util/Threads.h"
36 #include "util/XMLHelper.h"
37
38 #include <memory>
39 #include <algorithm>
40 #include <fstream>
41 #include <openssl/x509_vfy.h>
42 #include <openssl/x509v3.h>
43 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
44 #include <xercesc/util/XMLUniDefs.hpp>
45
46 using namespace xmltooling::logging;
47 using namespace xmltooling;
48 using namespace std;
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(
56                 "path validation failure at depth(%d): %s", ctx->error_depth, X509_verify_cert_error_string(ctx->error)
57                 );
58         }
59         return ok;
60     }
61
62     static string XMLTOOL_DLLLOCAL X509_NAME_to_string(X509_NAME* n)
63     {
64         string s;
65         BIO* b = BIO_new(BIO_s_mem());
66         X509_NAME_print_ex(b,n,0,XN_FLAG_RFC2253);
67         BIO_flush(b);
68         BUF_MEM* bptr=nullptr;
69         BIO_get_mem_ptr(b, &bptr);
70         if (bptr && bptr->length > 0) {
71             s.append(bptr->data, bptr->length);
72         }
73         BIO_free(b);
74         return s;
75     }
76
77     static time_t XMLTOOL_DLLLOCAL getCRLTime(const ASN1_TIME *a)
78     {
79         struct tm t;
80         memset(&t, 0, sizeof(t));
81         // RFC 5280, sections 5.1.2.4 and 5.1.2.5 require thisUpdate and nextUpdate
82         // to be encoded as UTCTime until 2049, and RFC 5280 section 4.1.2.5.1
83         // further restricts the format to "YYMMDDHHMMSSZ" ("even where the number
84         // of seconds is zero").
85         // As long as OpenSSL doesn't provide any API to convert ASN1_TIME values
86         // time_t, we therefore have to parse it ourselves, unfortunately.
87         if (sscanf((const char*)a->data, "%2d%2d%2d%2d%2d%2dZ",
88             &t.tm_year, &t.tm_mon, &t.tm_mday,
89             &t.tm_hour, &t.tm_min, &t.tm_sec) == 6) {
90             if (t.tm_year <= 50) {
91                 // RFC 5280, section 4.1.2.5.1
92                 t.tm_year += 100;
93             }
94             t.tm_mon--;
95 #if defined(HAVE_TIMEGM)
96             return timegm(&t);
97 #else
98             // Windows, and hopefully most others...?
99             return mktime(&t) - timezone;
100 #endif
101         }
102         return (time_t)-1;
103     }
104
105     static const XMLCh minRefreshDelay[] =      UNICODE_LITERAL_15(m,i,n,R,e,f,r,e,s,h,D,e,l,a,y);
106     static const XMLCh minSecondsRemaining[] =  UNICODE_LITERAL_19(m,i,n,S,e,c,o,n,d,s,R,e,m,a,i,n,i,n,g);
107     static const XMLCh minPercentRemaining[] =  UNICODE_LITERAL_19(m,i,n,P,e,r,c,e,n,t,R,e,m,a,i,n,i,n,g);
108 };
109
110 namespace xmltooling {
111
112     class XMLTOOL_DLLLOCAL PKIXPathValidator : public OpenSSLPathValidator
113     {
114     public:
115         PKIXPathValidator(const xercesc::DOMElement* e)
116             : m_log(Category::getInstance(XMLTOOLING_LOGCAT ".PathValidator.PKIX")),
117               m_lock(XMLToolingConfig::getConfig().getNamedMutex(XMLTOOLING_LOGCAT ".PathValidator.PKIX")),
118               m_minRefreshDelay(XMLHelper::getAttrInt(e, 60, minRefreshDelay)),
119               m_minSecondsRemaining(XMLHelper::getAttrInt(e, 86400, minSecondsRemaining)),
120               m_minPercentRemaining(XMLHelper::getAttrInt(e, 10, minPercentRemaining)) {
121         }
122
123         virtual ~PKIXPathValidator() {}
124
125         bool validate(
126             XSECCryptoX509* certEE, const vector<XSECCryptoX509*>& certChain, const PathValidatorParams& params
127             ) const;
128         bool validate(
129             X509* certEE, STACK_OF(X509)* certChain, const PathValidatorParams& params
130             ) const;
131
132     private:
133         XSECCryptoX509CRL* getRemoteCRLs(const char* cdpuri) const;
134         bool isFreshCRL(XSECCryptoX509CRL *c, Category* log=nullptr) const;
135
136         Category& m_log;
137         Mutex& m_lock;
138         time_t m_minRefreshDelay,m_minSecondsRemaining;
139         unsigned short m_minPercentRemaining;
140
141         static map<string,time_t> m_crlUpdateMap;
142     };
143
144     PathValidator* XMLTOOL_DLLLOCAL PKIXPathValidatorFactory(const xercesc::DOMElement* const & e)
145     {
146         return new PKIXPathValidator(e);
147     }
148
149 };
150
151 map<string,time_t> PKIXPathValidator::m_crlUpdateMap;
152
153 void XMLTOOL_API xmltooling::registerPathValidators()
154 {
155     XMLToolingConfig& conf=XMLToolingConfig::getConfig();
156     conf.PathValidatorManager.registerFactory(PKIX_PATHVALIDATOR, PKIXPathValidatorFactory);
157 }
158
159 PathValidator::PathValidator()
160 {
161 }
162
163 PathValidator::~PathValidator()
164 {
165 }
166
167 PathValidator::PathValidatorParams::PathValidatorParams()
168 {
169 }
170
171 PathValidator::PathValidatorParams::~PathValidatorParams()
172 {
173 }
174
175 PKIXPathValidatorParams::PKIXPathValidatorParams()
176 {
177 }
178
179 PKIXPathValidatorParams::~PKIXPathValidatorParams()
180 {
181 }
182
183 OpenSSLPathValidator::OpenSSLPathValidator()
184 {
185 }
186
187 OpenSSLPathValidator::~OpenSSLPathValidator()
188 {
189 }
190
191 bool PKIXPathValidator::validate(
192     XSECCryptoX509* certEE, const vector<XSECCryptoX509*>& certChain, const PathValidatorParams& params
193     ) const
194 {
195     if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
196         m_log.error("only the OpenSSL XSEC provider is supported");
197         return false;
198     }
199
200     STACK_OF(X509)* untrusted=sk_X509_new_null();
201     for (vector<XSECCryptoX509*>::const_iterator i=certChain.begin(); i!=certChain.end(); ++i)
202         sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
203
204     bool ret = validate(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(), untrusted, params);
205     sk_X509_free(untrusted);
206     return ret;
207 }
208
209 bool PKIXPathValidator::validate(X509* EE, STACK_OF(X509)* untrusted, const PathValidatorParams& params) const
210 {
211 #ifdef _DEBUG
212     NDC ndc("validate");
213 #endif
214
215     const PKIXPathValidatorParams* pkixParams = dynamic_cast<const PKIXPathValidatorParams*>(&params);
216     if (!pkixParams) {
217         m_log.error("input parameters were of incorrect type");
218         return false;
219     }
220
221     // First we build a stack of CA certs. These objects are all referenced in place.
222     m_log.debug("supplying PKIX Validation information");
223
224     // We need this for CRL support.
225     X509_STORE* store=X509_STORE_new();
226     if (!store) {
227         log_openssl();
228         return false;
229     }
230
231     // PKIX policy checking (cf. RFCs 3280/5280 section 6)
232     if (pkixParams->isPolicyMappingInhibited() || pkixParams->isAnyPolicyInhibited() || (!pkixParams->getPolicies().empty())) {
233 #if (OPENSSL_VERSION_NUMBER < 0x00908000L)
234         m_log.error("PKIX policy checking option is configured, but OpenSSL version is less than 0.9.8");
235         X509_STORE_free(store);
236         return false;
237 #else
238         unsigned long pflags = 0;
239         X509_VERIFY_PARAM *vpm = X509_VERIFY_PARAM_new();
240         if (!vpm) {
241             log_openssl();
242             X509_STORE_free(store);
243             return false;
244         }
245
246         // populate the "user-initial-policy-set" input variable
247         const set<string>& policies = pkixParams->getPolicies();
248         if (!policies.empty()) {
249             for (set<string>::const_iterator o=policies.begin(); o!=policies.end(); o++) {
250                 ASN1_OBJECT *oid = OBJ_txt2obj(o->c_str(), 1);
251                 if (oid && X509_VERIFY_PARAM_add0_policy(vpm, oid)) {
252                     m_log.debug("OID (%s) added to set of acceptable policies", o->c_str());
253                 }
254                 else {
255                     log_openssl();
256                     m_log.error("unable to parse/configure policy OID value (%s)", o->c_str());
257                     if (oid)
258                         ASN1_OBJECT_free(oid);
259                     X509_VERIFY_PARAM_free(vpm);
260                     X509_STORE_free(store);
261                     return false;
262                 }
263             }
264             // when the user has supplied at least one policy OID, he obviously wants to check
265             // for an explicit policy ("initial-explicit-policy")
266             pflags |= X509_V_FLAG_EXPLICIT_POLICY;
267         }
268
269         // "initial-policy-mapping-inhibit" input variable
270         if (pkixParams->isPolicyMappingInhibited())
271             pflags |= X509_V_FLAG_INHIBIT_MAP;
272         // "initial-any-policy-inhibit" input variable
273         if (pkixParams->isAnyPolicyInhibited())
274             pflags |= X509_V_FLAG_INHIBIT_ANY;
275
276         if (!X509_VERIFY_PARAM_set_flags(vpm, pflags) || !X509_STORE_set1_param(store, vpm)) {
277             log_openssl();
278             m_log.error("unable to set PKIX policy checking parameters");
279             X509_VERIFY_PARAM_free(vpm);
280             X509_STORE_free(store);
281             return false;
282         }
283
284         X509_VERIFY_PARAM_free(vpm);
285 #endif
286     }
287
288     // This contains the state of the validate operation.
289     int count=0;
290     X509_STORE_CTX ctx;
291
292     // AFAICT, EE and untrusted are passed in but not owned by the ctx.
293 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
294     if (X509_STORE_CTX_init(&ctx,store,EE,untrusted) != 1) {
295         log_openssl();
296         m_log.error("unable to initialize X509_STORE_CTX");
297         X509_STORE_free(store);
298         return false;
299     }
300 #else
301     X509_STORE_CTX_init(&ctx,store,EE,untrusted);
302 #endif
303
304     STACK_OF(X509)* CAstack = sk_X509_new_null();
305     const vector<XSECCryptoX509*>& CAcerts = pkixParams->getTrustAnchors();
306     for (vector<XSECCryptoX509*>::const_iterator i=CAcerts.begin(); i!=CAcerts.end(); ++i) {
307         if ((*i)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL) {
308             sk_X509_push(CAstack,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
309             ++count;
310         }
311     }
312     m_log.debug("supplied (%d) CA certificate(s)", count);
313
314     // Seems to be most efficient to just pass in the CA stack.
315     X509_STORE_CTX_trusted_stack(&ctx,CAstack);
316     X509_STORE_CTX_set_depth(&ctx,100);    // we check the depth down below
317     X509_STORE_CTX_set_verify_cb(&ctx,error_callback);
318
319     // Do a first pass verify. If CRLs aren't used, this is the only pass.
320     int ret = X509_verify_cert(&ctx);
321     if (ret == 1) {
322         // Now see if the depth was acceptable by counting the number of intermediates.
323         int depth=sk_X509_num(ctx.chain)-2;
324         if (pkixParams->getVerificationDepth() < depth) {
325             m_log.error(
326                 "certificate chain was too long (%d intermediates, only %d allowed)",
327                 (depth==-1) ? 0 : depth,
328                 pkixParams->getVerificationDepth()
329                 );
330             ret = 0;
331         }
332     }
333
334     // If the first pass succeeded, check to see if we need a second with CRLs.
335     if (ret == 1 && pkixParams->getRevocationChecking() != PKIXPathValidatorParams::REVOCATION_OFF) {
336 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
337         // After the first X509_verify_cert call, the ctx can no longer be used
338         // (subsequent calls will fail with OpenSSL 1.0.1p / 1.0.2d or later).
339         X509_STORE_CTX_cleanup(&ctx);
340
341         // When we add CRLs, we have to be sure the nextUpdate hasn't passed, because OpenSSL won't accept
342         // the CRL in that case. If we end up not adding a CRL for a particular link in the chain, the
343         // validation will fail (if the fullChain option was set).
344         set<string> crlissuers;
345         time_t now = time(nullptr);
346
347         // Pull CRLs from external CDP first, since an attacker is likely to stick an old but valid CRL into
348         // the signature.
349         for (int i = 0; i < sk_X509_num(untrusted); ++i) {
350             X509 *cert = sk_X509_value(untrusted, i);
351             string crlissuer(X509_NAME_to_string(X509_get_issuer_name(cert)));
352             if (crlissuers.count(crlissuer)) {
353                // We already have a CRL for this cert, so skip CRLDP processing for this one.
354                continue;
355             }
356             bool foundUsableCDP = false;
357             STACK_OF(DIST_POINT)* dps = (STACK_OF(DIST_POINT)*)X509_get_ext_d2i(cert, NID_crl_distribution_points, nullptr, nullptr);
358             for (int ii = 0; !foundUsableCDP && ii < sk_DIST_POINT_num(dps); ++ii) {
359                 DIST_POINT* dp = sk_DIST_POINT_value(dps, ii);
360                 if (!dp->distpoint || dp->distpoint->type != 0)
361                     continue;
362                 for (int iii = 0; !foundUsableCDP && iii < sk_GENERAL_NAME_num(dp->distpoint->name.fullname); ++iii) {
363                     GENERAL_NAME* gen = sk_GENERAL_NAME_value(dp->distpoint->name.fullname, iii);
364                     // Only consider URIs, and stop after the first one we find.
365                     if (gen->type == GEN_URI) {
366                         const char* cdpuri = (const char*)gen->d.ia5->data;
367                         auto_ptr<XSECCryptoX509CRL> crl(getRemoteCRLs(cdpuri));
368                         if (crl.get() && crl->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL &&
369                             (isFreshCRL(crl.get()) || (ii == sk_DIST_POINT_num(dps)-1 && iii == sk_GENERAL_NAME_num(dp->distpoint->name.fullname)-1))) {
370                             // owned by store
371                             X509_STORE_add_crl(store, X509_CRL_dup(static_cast<OpenSSLCryptoX509CRL*>(crl.get())->getOpenSSLX509CRL()));
372                             m_log.debug("added CRL issued by (%s)", crlissuer.c_str());
373                             crlissuers.insert(crlissuer);
374                             foundUsableCDP = true;
375                         }
376                     }
377                 }
378             }
379             sk_DIST_POINT_free(dps);
380         }
381
382         // Pick up any valid CRLs inline.
383         const vector<XSECCryptoX509CRL*>& crls = pkixParams->getCRLs();
384         for (vector<XSECCryptoX509CRL*>::const_iterator j=crls.begin(); j!=crls.end(); ++j) {
385             if ((*j)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL &&
386                 (X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()), &now) > 0)) {
387                 string crlissuer(X509_NAME_to_string(X509_CRL_get_issuer(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL())));
388                 if (crlissuer.empty() || crlissuers.count(crlissuer)) {
389                    // We already have a CRL for this cert, so skip this one.
390                    continue;
391                 }
392                 m_log.debug("added CRL issued by (%s)", crlissuer.c_str());
393                 crlissuers.insert(crlissuer);
394                 // owned by store
395                 X509_STORE_add_crl(store, X509_CRL_dup(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()));
396             }
397         }
398
399         // Do a second pass verify with CRLs in place. Reinitialize ctx, see
400         // https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=aae41f8c54257d9fa6904d3a9aa09c5db6cefd0d
401 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
402         if (X509_STORE_CTX_init(&ctx,store,EE,untrusted) != 1) {
403             log_openssl();
404             m_log.error("unable to initialize X509_STORE_CTX");
405             ret = 0;
406         }
407 #else
408         X509_STORE_CTX_init(&ctx,store,EE,untrusted);
409 #endif
410         if (ret != 0) {
411             X509_STORE_CTX_trusted_stack(&ctx,CAstack);
412             X509_STORE_CTX_set_depth(&ctx,100);  // already checked above
413             X509_STORE_CTX_set_verify_cb(&ctx,error_callback);
414             if (pkixParams->getRevocationChecking() == PKIXPathValidatorParams::REVOCATION_FULLCHAIN)
415                 X509_STORE_CTX_set_flags(&ctx, X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
416             else
417                 X509_STORE_CTX_set_flags(&ctx, X509_V_FLAG_CRL_CHECK);
418             ret = X509_verify_cert(&ctx);
419         }
420 #else
421         m_log.warn("CRL checking is enabled, but OpenSSL version is too old");
422         ret = 0;
423 #endif
424     }
425
426     if (ret == 1) {
427         m_log.debug("successfully validated certificate chain");
428     }
429 #if defined(X509_V_ERR_NO_EXPLICIT_POLICY) && (OPENSSL_VERSION_NUMBER < 0x10000000L)
430     else if (X509_STORE_CTX_get_error(&ctx) == X509_V_ERR_NO_EXPLICIT_POLICY && !pkixParams->isPolicyMappingInhibited()) {
431         m_log.warn("policy mapping requires OpenSSL 1.0.0 or later");
432     }
433 #endif
434
435     // Clean up...
436     X509_STORE_CTX_cleanup(&ctx);
437     X509_STORE_free(store);
438     sk_X509_free(CAstack);
439
440     return (ret == 1);
441 }
442
443 XSECCryptoX509CRL* PKIXPathValidator::getRemoteCRLs(const char* cdpuri) const
444 {
445     // This is a filesystem-based CRL cache using a shared lock across all instances
446     // of this class.
447
448     // The filenames for the CRL cache are based on a hash of the CRL location.
449     string cdpfile = SecurityHelper::doHash("SHA1", cdpuri, strlen(cdpuri)) + ".crl";
450     XMLToolingConfig::getConfig().getPathResolver()->resolve(cdpfile, PathResolver::XMLTOOLING_CACHE_FILE);
451     string cdpstaging = cdpfile + ".tmp";
452
453     time_t now = time(nullptr);
454     vector<XSECCryptoX509CRL*> crls;
455
456     try {
457         // While holding the lock, check for a cached copy of the CRL, and remove "expired" ones.
458         Lock glock(m_lock);
459 #ifdef WIN32
460         struct _stat stat_buf;
461         if (_stat(cdpfile.c_str(), &stat_buf) == 0) {
462 #else
463         struct stat stat_buf;
464         if (stat(cdpfile.c_str(), &stat_buf) == 0) {
465 #endif
466             SecurityHelper::loadCRLsFromFile(crls, cdpfile.c_str());
467             if (crls.empty() || crls.front()->getProviderName() != DSIGConstants::s_unicodeStrPROVOpenSSL ||
468                 X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(crls.front())->getOpenSSLX509CRL()), &now) < 0) {
469                 for_each(crls.begin(), crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
470                 crls.clear();
471                 remove(cdpfile.c_str());    // may as well delete the local copy
472                 m_crlUpdateMap.erase(cdpuri);
473                 m_log.info("deleting cached CRL from %s with nextUpdate field in the past", cdpuri);
474             }
475         }
476     }
477     catch (exception& ex) {
478         m_log.error("exception loading cached copy of CRL from %s: %s", cdpuri, ex.what());
479     }
480
481     if (crls.empty() || !isFreshCRL(crls.front(), &m_log)) {
482         bool updateTimestamp = true;
483         try {
484             // If we get here, the cached copy didn't exist yet, or it's time to refresh.
485             // To limit the rate of unsuccessful attempts when a CRLDP is unreachable,
486             // we remember the timestamp of the last attempt (both successful/unsuccessful).
487             time_t ts = 0;
488             m_lock.lock();
489             map<string,time_t>::const_iterator tsit = m_crlUpdateMap.find(cdpuri);
490             if (tsit != m_crlUpdateMap.end())
491                 ts = tsit->second;
492             m_lock.unlock();
493
494             if (difftime(now, ts) > m_minRefreshDelay) {
495                 SOAPTransport::Address addr("AbstractPKIXTrustEngine", cdpuri, cdpuri);
496                 string scheme(addr.m_endpoint, strchr(addr.m_endpoint,':') - addr.m_endpoint);
497                 auto_ptr<SOAPTransport> soap(XMLToolingConfig::getConfig().SOAPTransportManager.newPlugin(scheme.c_str(), addr));
498                 soap->send();
499                 istream& msg = soap->receive();
500                 Lock glock(m_lock);
501                 ofstream out(cdpstaging.c_str(), fstream::trunc|fstream::binary);
502                 out << msg.rdbuf();
503                 out.close();
504                 SecurityHelper::loadCRLsFromFile(crls, cdpstaging.c_str());
505                 if (crls.empty() || crls.front()->getProviderName() != DSIGConstants::s_unicodeStrPROVOpenSSL ||
506                     X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(crls.front())->getOpenSSLX509CRL()), &now) < 0) {
507                     // The "new" CRL wasn't usable, so get rid of it.
508                     for_each(crls.begin(), crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
509                     crls.clear();
510                     remove(cdpstaging.c_str());
511                     m_log.error("ignoring CRL retrieved from %s with nextUpdate field in the past", cdpuri);
512                 }
513                 else {
514                     // "Commit" the new CRL. Note that we might add a CRL which doesn't pass
515                     // isFreshCRL, but that's preferrable over adding none at all.
516                     m_log.info("CRL refreshed from %s", cdpuri);
517                     remove(cdpfile.c_str());
518                     if (rename(cdpstaging.c_str(), cdpfile.c_str()) != 0)
519                         m_log.error("unable to rename CRL staging file");
520                 }
521             }
522             else {
523                 updateTimestamp = false;    // don't update if we're within the backoff window
524             }
525         }
526         catch (exception& ex) {
527             m_log.error("exception downloading/caching CRL from %s: %s", cdpuri, ex.what());
528         }
529
530         if (updateTimestamp) {
531             Lock glock(m_lock);
532             m_crlUpdateMap[cdpuri] = now;
533         }
534     }
535
536     if (crls.empty())
537         return nullptr;
538     for_each(crls.begin() + 1, crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
539     return crls.front();
540 }
541
542 bool PKIXPathValidator::isFreshCRL(XSECCryptoX509CRL *c, Category* log) const
543 {
544     if (c) {
545         const X509_CRL* crl = static_cast<OpenSSLCryptoX509CRL*>(c)->getOpenSSLX509CRL();
546         time_t thisUpdate = getCRLTime(X509_CRL_get_lastUpdate(crl));
547         time_t nextUpdate = getCRLTime(X509_CRL_get_nextUpdate(crl));
548         time_t now = time(nullptr);
549
550         if (thisUpdate < 0 || nextUpdate < 0) {
551             // we failed to parse at least one of the fields (they were not encoded
552             // as required by RFC 5280, actually)
553             time_t exp = now + m_minSecondsRemaining;
554             if (log) {
555                 log->warn("isFreshCRL (issuer '%s'): improperly encoded thisUpdate or nextUpdate field - falling back to simple time comparison",
556                           (X509_NAME_to_string(X509_CRL_get_issuer(crl))).c_str());
557             }
558             return (X509_cmp_time(X509_CRL_get_nextUpdate(crl), &exp) > 0) ? true : false;
559         }
560         else {
561             if (log && log->isDebugEnabled()) {
562                 log->debug("isFreshCRL (issuer '%s'): %.0f seconds until nextUpdate (%3.2f%% elapsed since thisUpdate)",
563                           (X509_NAME_to_string(X509_CRL_get_issuer(crl))).c_str(),
564                           difftime(nextUpdate, now), (difftime(now, thisUpdate) * 100) / difftime(nextUpdate, thisUpdate));
565             }
566
567             // consider it recent enough if there are at least MIN_SECS_REMAINING
568             // to the nextUpdate, and at least MIN_PERCENT_REMAINING of its
569             // overall "validity" are remaining to the nextUpdate
570             return (now + m_minSecondsRemaining < nextUpdate) &&
571                     ((difftime(nextUpdate, now) * 100) / difftime(nextUpdate, thisUpdate) > m_minPercentRemaining);
572         }
573     }
574     return false;
575 }