- if (!ok)
- Category::getInstance("OpenSSL").error("path validation failure: %s", X509_verify_cert_error_string(ctx->error));
- return ok;
- }
-
- static string XMLTOOL_DLLLOCAL X509_NAME_to_string(X509_NAME* n)
- {
- string s;
- BIO* b = BIO_new(BIO_s_mem());
- X509_NAME_print_ex(b,n,0,XN_FLAG_RFC2253);
- BIO_flush(b);
- BUF_MEM* bptr=nullptr;
- BIO_get_mem_ptr(b, &bptr);
- if (bptr && bptr->length > 0) {
- s.append(bptr->data, bptr->length);
- }
- BIO_free(b);
- return s;
- }
-
- static time_t XMLTOOL_DLLLOCAL getCRLTime(const ASN1_TIME *a)
- {
- struct tm t;
- memset(&t, 0, sizeof(t));
- // RFC 5280, sections 5.1.2.4 and 5.1.2.5 require thisUpdate and nextUpdate
- // to be encoded as UTCTime until 2049, and RFC 5280 section 4.1.2.5.1
- // further restricts the format to "YYMMDDHHMMSSZ" ("even where the number
- // of seconds is zero").
- // As long as OpenSSL doesn't provide any API to convert ASN1_TIME values
- // time_t, we therefore have to parse it ourselves, unfortunately.
- if (sscanf((const char*)a->data, "%2d%2d%2d%2d%2d%2dZ",
- &t.tm_year, &t.tm_mon, &t.tm_mday,
- &t.tm_hour, &t.tm_min, &t.tm_sec) == 6) {
- if (t.tm_year <= 50) {
- // RFC 5280, section 4.1.2.5.1
- t.tm_year += 100;
- }
- t.tm_mon--;
-#if defined(HAVE_TIMEGM)
- return timegm(&t);
-#else
- // Windows, and hopefully most others...?
- return mktime(&t) - timezone;
-#endif
- }
- return (time_t)-1;
- }
-
- static bool XMLTOOL_DLLLOCAL isFreshCRL(XSECCryptoX509CRL *c, Category* log=nullptr)
- {
- // eventually, these should be made configurable
- #define MIN_SECS_REMAINING 86400
- #define MIN_PERCENT_REMAINING 10
- if (c) {
- const X509_CRL* crl = static_cast<OpenSSLCryptoX509CRL*>(c)->getOpenSSLX509CRL();
- time_t thisUpdate = getCRLTime(X509_CRL_get_lastUpdate(crl));
- time_t nextUpdate = getCRLTime(X509_CRL_get_nextUpdate(crl));
- time_t now = time(nullptr);
-
- if (thisUpdate < 0 || nextUpdate < 0) {
- // we failed to parse at least one of the fields (they were not encoded
- // as required by RFC 5280, actually)
- time_t exp = now + MIN_SECS_REMAINING;
- if (log) {
- log->warn("isFreshCRL (issuer '%s'): improperly encoded thisUpdate or nextUpdate field - falling back to simple time comparison",
- (X509_NAME_to_string(X509_CRL_get_issuer(crl))).c_str());
- }
- return (X509_cmp_time(X509_CRL_get_nextUpdate(crl), &exp) > 0) ? true : false;
- }
- else {
- if (log && log->isDebugEnabled()) {
- log->debug("isFreshCRL (issuer '%s'): %.0f seconds until nextUpdate (%3.2f%% elapsed since thisUpdate)",
- (X509_NAME_to_string(X509_CRL_get_issuer(crl))).c_str(),
- difftime(nextUpdate, now), (difftime(now, thisUpdate) * 100) / difftime(nextUpdate, thisUpdate));
- }
-
- // consider it recent enough if there are at least MIN_SECS_REMAINING
- // to the nextUpdate, and at least MIN_PERCENT_REMAINING of its
- // overall "validity" are remaining to the nextUpdate
- return (now + MIN_SECS_REMAINING < nextUpdate) &&
- ((difftime(nextUpdate, now) * 100) / difftime(nextUpdate, thisUpdate) > MIN_PERCENT_REMAINING);
- }
- }
- return false;
- }
-
- static XSECCryptoX509CRL* XMLTOOL_DLLLOCAL getRemoteCRLs(const char* cdpuri, Category& log) {
- // This is a temporary CRL cache implementation to avoid breaking binary compatibility
- // for the library. Caching can't rely on any member objects within the TrustEngine,
- // including locks, so we're using the global library lock for the time being.
- // All other state is kept in the file system.
-
- // minimum number of seconds between re-attempting a download from one particular CRLDP
- #define MIN_RETRY_WAIT 60
-
- // The filenames for the CRL cache are based on a hash of the CRL location.
- string cdpfile = SecurityHelper::doHash("SHA1", cdpuri, strlen(cdpuri)) + ".crl";
- XMLToolingConfig::getConfig().getPathResolver()->resolve(cdpfile, PathResolver::XMLTOOLING_RUN_FILE);
- string cdpstaging = cdpfile + ".tmp";
- string tsfile = cdpfile + ".ts";
-
- time_t now = time(nullptr);
- vector<XSECCryptoX509CRL*> crls;
-
- try {
- // While holding the lock, check for a cached copy of the CRL, and remove "expired" ones.
- Locker glock(&XMLToolingConfig::getConfig());
-#ifdef WIN32
- struct _stat stat_buf;
- if (_stat(cdpfile.c_str(), &stat_buf) == 0) {
-#else
- struct stat stat_buf;
- if (stat(cdpfile.c_str(), &stat_buf) == 0) {
-#endif
- SecurityHelper::loadCRLsFromFile(crls, cdpfile.c_str());
- if (crls.empty() || crls.front()->getProviderName() != DSIGConstants::s_unicodeStrPROVOpenSSL ||
- X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(crls.front())->getOpenSSLX509CRL()), &now) < 0) {
- for_each(crls.begin(), crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
- crls.clear();
- remove(cdpfile.c_str()); // may as well delete the local copy
- remove(tsfile.c_str());
- log.info("deleting cached CRL from %s with nextUpdate field in the past", cdpuri);
- }