diff --git a/src/compat/os_cert.cpp b/src/compat/os_cert.cpp index 91b309d9..1bf84c33 100644 --- a/src/compat/os_cert.cpp +++ b/src/compat/os_cert.cpp @@ -17,7 +17,6 @@ */ #include "os_cert.h" - #include "logger.h" #ifdef _WIN32 @@ -32,96 +31,6 @@ #include #endif /*TARGET_OS_OSX*/ -#if EMBEDDED_ASN1_TIME_PARSE -#undef X509_NAME -#undef OCSP_REQUEST -#undef OCSP_RESPONSE -#define GENTIME_LENGTH 15 -#define UTCTIME_LENGTH 13 -#define ATOI2(ar) ((ar) += 2, ((ar)[-2] - '0') * 10 + ((ar)[-1] - '0')) -extern "C" { -// Taken from libressl/src/crypto/asn1/a_time_tm.c -int -ASN1_time_parse(const char* bytes, size_t len, struct tm* tm, int mode) -{ - size_t i; - int type = 0; - struct tm ltm; - struct tm* lt; - const char* p; - - if (bytes == NULL) - return (-1); - - /* Constrain to valid lengths. */ - if (len != UTCTIME_LENGTH && len != GENTIME_LENGTH) - return (-1); - - lt = tm; - if (lt == NULL) { - memset(<m, 0, sizeof(ltm)); - lt = <m; - } - - /* Timezone is required and must be GMT (Zulu). */ - if (bytes[len - 1] != 'Z') - return (-1); - - /* Make sure everything else is digits. */ - for (i = 0; i < len - 1; i++) { - if (isdigit((unsigned char) bytes[i])) - continue; - return (-1); - } - - /* - * Validate and convert the time - */ - p = bytes; - switch (len) { - case GENTIME_LENGTH: - if (mode == V_ASN1_UTCTIME) - return (-1); - lt->tm_year = (ATOI2(p) * 100) - 1900; /* cc */ - type = V_ASN1_GENERALIZEDTIME; - /* FALLTHROUGH */ - case UTCTIME_LENGTH: - if (type == 0) { - if (mode == V_ASN1_GENERALIZEDTIME) - return (-1); - type = V_ASN1_UTCTIME; - } - lt->tm_year += ATOI2(p); /* yy */ - if (type == V_ASN1_UTCTIME) { - if (lt->tm_year < 50) - lt->tm_year += 100; - } - lt->tm_mon = ATOI2(p) - 1; /* mm */ - if (lt->tm_mon < 0 || lt->tm_mon > 11) - return (-1); - lt->tm_mday = ATOI2(p); /* dd */ - if (lt->tm_mday < 1 || lt->tm_mday > 31) - return (-1); - lt->tm_hour = ATOI2(p); /* HH */ - if (lt->tm_hour < 0 || lt->tm_hour > 23) - return (-1); - lt->tm_min = ATOI2(p); /* MM */ - if (lt->tm_min < 0 || lt->tm_min > 59) - return (-1); - lt->tm_sec = ATOI2(p); /* SS */ - /* Leap second 60 is not accepted. Reconsider later? */ - if (lt->tm_sec < 0 || lt->tm_sec > 59) - return (-1); - break; - default: - return (-1); - } - - return type; -} -} -#endif /* EMBEDDED_ASN1_TIME_PARSE */ - namespace dht { namespace http { diff --git a/src/compat/os_cert.h b/src/compat/os_cert.h index c45198fd..54052d62 100644 --- a/src/compat/os_cert.h +++ b/src/compat/os_cert.h @@ -24,36 +24,6 @@ #include -#if (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER > 0x20501000L)) -#define EMBEDDED_ASN1_TIME_PARSE 0 -#else -#define EMBEDDED_ASN1_TIME_PARSE 1 -#endif - -#if EMBEDDED_ASN1_TIME_PARSE -#define V_ASN1_UTCTIME 23 -#define V_ASN1_GENERALIZEDTIME 24 - -extern "C" { -/* - * Parse an RFC 5280 format ASN.1 time string. - * - * mode must be: - * 0 if we expect to parse a time as specified in RFC 5280 for an X509 object. - * V_ASN1_UTCTIME if we wish to parse an RFC5280 format UTC time. - * V_ASN1_GENERALIZEDTIME if we wish to parse an RFC5280 format Generalized time. - * - * Returns: - * -1 if the string was invalid. - * V_ASN1_UTCTIME if the string validated as a UTC time string. - * V_ASN1_GENERALIZEDTIME if the string validated as a Generalized time string. - * - * Fills in *tm with the corresponding time if tm is non NULL. - */ -int ASN1_time_parse(const char* bytes, size_t len, struct tm* tm, int mode); -} -#endif - namespace dht { namespace http { diff --git a/src/dht_proxy_client.cpp b/src/dht_proxy_client.cpp index d054423a..9ff0a779 100644 --- a/src/dht_proxy_client.cpp +++ b/src/dht_proxy_client.cpp @@ -26,7 +26,6 @@ #include #include - namespace dht { struct DhtProxyClient::InfoState { diff --git a/src/http.cpp b/src/http.cpp index 6b7d4aad..2b16aed1 100644 --- a/src/http.cpp +++ b/src/http.cpp @@ -225,20 +225,25 @@ Connection::is_ssl() const return ssl_ctx_ ? true : false; } -static time_t -parse_ocsp_time(ASN1_GENERALIZEDTIME* gt) -{ - struct tm tm; - time_t rv = -1; +std::string asn1ToString(ASN1_GENERALIZEDTIME* time) { + if (!time) { + return "(null)"; + } + BIO* memBio = BIO_new(BIO_s_mem()); + if (!memBio) { + throw std::runtime_error("Failed to create BIO"); + } - if (gt == nullptr) - return -1; - // RFC 6960 specifies that all times in OCSP must be GENERALIZEDTIME - if (ASN1_time_parse((const char*)gt->data, gt->length, &tm, V_ASN1_GENERALIZEDTIME) == -1) - return -1; - if ((rv = timegm(&tm)) == -1) - return -1; - return rv; + if (ASN1_GENERALIZEDTIME_print(memBio, time) <= 0) { + BIO_free(memBio); + throw std::runtime_error("Failed to print ASN1_GENERALIZEDTIME"); + } + + char* bioData; + long bioLength = BIO_get_mem_data(memBio, &bioData); + std::string result(bioData, bioLength); + BIO_free(memBio); + return result; } static inline X509* @@ -342,36 +347,32 @@ ocspRequestFromCert(STACK_OF(X509)* fullchain, const std::shared_ptr& lo bool ocspValidateResponse(const OscpRequestInfo& info, STACK_OF(X509)* fullchain, const std::string& response, X509_STORE *store, const std::shared_ptr& logger) { - ASN1_GENERALIZEDTIME *revtime = nullptr, *thisupd = nullptr, *nextupd = nullptr; - const uint8_t* p = (const uint8_t*)response.data(); - int status, cert_status=0, crl_reason=0; - time_t now, rev_t = -1, this_t, next_t; - X509* cert = cert_from_chain(fullchain); if (cert == nullptr) { if (logger) - logger->e("No certificate found"); + logger->error("ocsp: no certificate found"); return false; } X509* issuer = issuer_from_chain(fullchain); if (issuer == nullptr) { if (logger) - logger->e("Unable to find issuer for cert"); + logger->error("ocsp: unable to find issuer for cert"); return false; } OCSP_CERTID *cidr; if ((cidr = OCSP_cert_to_id(nullptr, cert, issuer)) == nullptr) { if (logger) - logger->e("Unable to get issuer cert/CID"); + logger->error("ocsp: unable to get issuer cert/CID"); return false; } std::unique_ptr cid(cidr, &OCSP_CERTID_free); + const uint8_t* resp_data = (const uint8_t*)response.data(); OCSP_RESPONSE *r; - if ((r = d2i_OCSP_RESPONSE(nullptr, &p, response.size())) == nullptr) { + if ((r = d2i_OCSP_RESPONSE(nullptr, &resp_data, response.size())) == nullptr) { if (logger) - logger->e("OCSP response unserializable"); + logger->error("OCSP response unserializable"); return false; } std::unique_ptr resp(r, &OCSP_RESPONSE_free); @@ -379,99 +380,59 @@ ocspValidateResponse(const OscpRequestInfo& info, STACK_OF(X509)* fullchain, con OCSP_BASICRESP *brespr; if ((brespr = OCSP_response_get1_basic(resp.get())) == nullptr) { if (logger) - logger->e("Failed to load OCSP response"); + logger->error("Failed to load OCSP response"); return false; } std::unique_ptr bresp(brespr, &OCSP_BASICRESP_free); if (OCSP_basic_verify(bresp.get(), fullchain, store, OCSP_TRUSTOTHER) != 1) { if (logger) - logger->w("OCSP verify failed"); + logger->warn("OCSP verify failed"); return false; } - printf("OCSP response signature validated\n"); - status = OCSP_response_status(resp.get()); + int status = OCSP_response_status(resp.get()); if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { if (logger) - logger->w("OCSP Failure: code %d (%s)", status, OCSP_response_status_str(status)); + logger->warn("OCSP Failure: code {:d} ({:s})", status, OCSP_response_status_str(status)); return false; } // Check the nonce if we sent one if (OCSP_check_nonce(info.req.get(), bresp.get()) <= 0) { if (logger) - logger->w("No OCSP nonce, or mismatch"); + logger->warn("No OCSP nonce, or mismatch"); return false; } + ASN1_GENERALIZEDTIME *revtime = nullptr, *thisupd = nullptr, *nextupd = nullptr; + int cert_status=0, crl_reason=0; if (OCSP_resp_find_status(bresp.get(), cid.get(), &cert_status, &crl_reason, &revtime, &thisupd, &nextupd) != 1) { if (logger) - logger->w("OCSP verify failed: no result for cert"); + logger->warn("OCSP verify failed: no result for cert"); return false; } - if (revtime && (rev_t = parse_ocsp_time(revtime)) == -1) { - if (logger) - logger->w("Unable to parse revocation time in OCSP reply"); - return false; - } // Belt and suspenders, Treat it as revoked if there is either // a revocation time, or status revoked. - if (rev_t != -1 || cert_status == V_OCSP_CERTSTATUS_REVOKED) { - if (logger) - logger->w("Invalid OCSP reply: certificate is revoked"); - if (rev_t != -1) { - if (logger) - logger->w("Certificate revoked at: %s", ctime(&rev_t)); + if (revtime || cert_status == V_OCSP_CERTSTATUS_REVOKED) { + if (logger) { + logger->warn("OCSP verify failed: certificate revoked since {}", asn1ToString(revtime)); } return false; } - if ((this_t = parse_ocsp_time(thisupd)) == -1) { - if (logger) - logger->w("unable to parse this update time in OCSP reply"); - return false; - } - if ((next_t = parse_ocsp_time(nextupd)) == -1) { - if (logger) - logger->w("unable to parse next update time in OCSP reply"); - return false; - } - // Don't allow this update to precede next update - if (this_t >= next_t) { + if (OCSP_check_validity(thisupd, nextupd, 1, -1) == 0) { if (logger) - logger->w("Invalid OCSP reply: this update >= next update"); - return false; - } - - now = time(nullptr); - // Check that this update is not more than JITTER seconds in the future. - if (this_t > now + JITTER_SEC) { - if (logger) - logger->e("Invalid OCSP reply: this update is in the future (%s)", ctime(&this_t)); - return false; - } - - // Check that this update is not more than MAXSEC in the past. - if (this_t < now - MAXAGE_SEC) { - if (logger) - logger->e("Invalid OCSP reply: this update is too old (%s)", ctime(&this_t)); - return false; - } - - // Check that next update is still valid - if (next_t < now - JITTER_SEC) { - if (logger) - logger->w("Invalid OCSP reply: reply has expired (%s)", ctime(&next_t)); + logger->warn("OCSP reply is expired or not yet valid"); return false; } if (logger) { - logger->d("OCSP response validated"); - logger->d(" This Update: %s", ctime(&this_t)); - logger->d(" Next Update: %s", ctime(&next_t)); + logger->debug("OCSP response validated"); + logger->debug(" This Update: {}", asn1ToString(thisupd)); + logger->debug(" Next Update: {}", asn1ToString(nextupd)); } return true; }