diff --git a/src/ocsp.c b/src/ocsp.c index 2d49b0dcbd2..c5b1f0ed102 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -772,12 +772,50 @@ const char *wolfSSL_OCSP_cert_status_str(long s) int wolfSSL_OCSP_check_validity(WOLFSSL_ASN1_TIME* thisupd, WOLFSSL_ASN1_TIME* nextupd, long sec, long maxsec) { +#ifndef NO_ASN_TIME + int ret = WOLFSSL_SUCCESS; + time_t now = wc_Time(0); + time_t cmp; + + /* Same semantics as OpenSSL's OCSP_check_validity: sec is the allowed + * clock skew and maxsec, when non-negative, the maximum age of thisUpdate. + * wolfSSL_X509_cmp_time() returns 0 for a NULL or malformed time. */ + if (sec < 0) + sec = 0; + + cmp = now + sec; + if (wolfSSL_X509_cmp_time(thisupd, &cmp) != -1) { + WOLFSSL_MSG("OCSP thisUpdate missing, malformed or in the future"); + ret = WOLFSSL_FAILURE; + } + else if (maxsec >= 0) { + cmp = now - maxsec; + if (wolfSSL_X509_cmp_time(thisupd, &cmp) != 1) { + WOLFSSL_MSG("OCSP thisUpdate is too old"); + ret = WOLFSSL_FAILURE; + } + } + + if ((ret == WOLFSSL_SUCCESS) && (nextupd != NULL)) { + cmp = now - sec; + if (wolfSSL_X509_cmp_time(nextupd, &cmp) != 1) { + WOLFSSL_MSG("OCSP nextUpdate has passed or is malformed"); + ret = WOLFSSL_FAILURE; + } + else if (wolfSSL_ASN1_TIME_compare(nextupd, thisupd) < 0) { + WOLFSSL_MSG("OCSP nextUpdate precedes thisUpdate"); + ret = WOLFSSL_FAILURE; + } + } + + return ret; +#else (void)thisupd; (void)nextupd; (void)sec; (void)maxsec; - /* Dates validated in DecodeSingleResponse. */ return WOLFSSL_SUCCESS; +#endif /* !NO_ASN_TIME */ } void wolfSSL_OCSP_CERTID_free(WOLFSSL_OCSP_CERTID* certId) diff --git a/tests/api.c b/tests/api.c index 1d60e540cda..0e2747575fa 100644 --- a/tests/api.c +++ b/tests/api.c @@ -48027,6 +48027,7 @@ TEST_CASE testCases[] = { TEST_DECL_GROUP("ocsp", test_ocsp_responder_keyhash_binding), TEST_DECL_GROUP("ocsp", test_ocsp_response_parsing), TEST_DECL_GROUP("ocsp", test_ocsp_certid_enc_dec), + TEST_DECL_GROUP("ocsp", test_ocsp_check_validity), TEST_DECL_GROUP("ocsp", test_ocsp_certid_dup), TEST_DECL_GROUP("ocsp", test_ocsp_resp_find_status_serial_prefix), TEST_DECL_GROUP("ocsp", test_ocsp_resp_times), diff --git a/tests/api/test_ocsp.c b/tests/api/test_ocsp.c index 8d61ef50e9f..8afa237e079 100644 --- a/tests/api/test_ocsp.c +++ b/tests/api/test_ocsp.c @@ -6937,3 +6937,124 @@ int test_wolfIO_DecodeUrl_host_bounds(void) return TEST_SKIPPED; } #endif /* HAVE_HTTP_CLIENT */ + +/* thisUpdate must be present, no more than sec seconds in the future and no + * older than maxsec; nextUpdate is optional but when present must be valid + * and not have passed. Issue 10718. */ +#if defined(HAVE_OCSP) && (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) && \ + !defined(NO_ASN_TIME) && !defined(USER_TIME) && !defined(TIME_OVERRIDES) +int test_ocsp_check_validity(void) +{ + EXPECT_DECLS; + time_t now = wc_Time(0); + WOLFSSL_ASN1_TIME thisUpd; + WOLFSSL_ASN1_TIME nextUpd; + const long DAY = 24L * 60 * 60; + + /* Fresh thisUpdate, no nextUpdate. */ + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&thisUpd, now, 0, -60)); + ExpectIntEQ(wolfSSL_OCSP_check_validity(&thisUpd, NULL, 300, 300), + WOLFSSL_SUCCESS); + + /* thisUpdate older than maxsec (replayed response). */ + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&thisUpd, now, 0, -DAY)); + ExpectIntEQ(wolfSSL_OCSP_check_validity(&thisUpd, NULL, 300, 300), + WOLFSSL_FAILURE); + + /* thisUpdate in the future beyond the skew. */ + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&thisUpd, now, 0, DAY)); + ExpectIntEQ(wolfSSL_OCSP_check_validity(&thisUpd, NULL, 300, 300), + WOLFSSL_FAILURE); + + /* thisUpdate in the future within the skew. */ + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&thisUpd, now, 0, 100)); + ExpectIntEQ(wolfSSL_OCSP_check_validity(&thisUpd, NULL, 300, 300), + WOLFSSL_SUCCESS); + + /* NULL thisUpdate. */ + ExpectIntEQ(wolfSSL_OCSP_check_validity(NULL, NULL, 300, 300), + WOLFSSL_FAILURE); + + /* Zero-length thisUpdate. */ + XMEMSET(&thisUpd, 0, sizeof(thisUpd)); + ExpectIntEQ(wolfSSL_OCSP_check_validity(&thisUpd, NULL, 300, 300), + WOLFSSL_FAILURE); + + /* Expired nextUpdate. */ + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&thisUpd, now, 0, -60)); + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&nextUpd, now, 0, -DAY)); + ExpectIntEQ(wolfSSL_OCSP_check_validity(&thisUpd, &nextUpd, 300, 300), + WOLFSSL_FAILURE); + + /* nextUpdate in the past within the skew. */ + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&thisUpd, now, 0, -120)); + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&nextUpd, now, 0, -100)); + ExpectIntEQ(wolfSSL_OCSP_check_validity(&thisUpd, &nextUpd, 300, DAY), + WOLFSSL_SUCCESS); + + /* nextUpdate in the future. */ + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&thisUpd, now, 0, -60)); + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&nextUpd, now, 0, DAY)); + ExpectIntEQ(wolfSSL_OCSP_check_validity(&thisUpd, &nextUpd, 300, 300), + WOLFSSL_SUCCESS); + + /* nextUpdate before thisUpdate. */ + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&thisUpd, now, 0, -60)); + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&nextUpd, now, 0, -120)); + ExpectIntEQ(wolfSSL_OCSP_check_validity(&thisUpd, &nextUpd, 300, 300), + WOLFSSL_FAILURE); + + /* Zero-length nextUpdate. */ + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&thisUpd, now, 0, -60)); + XMEMSET(&nextUpd, 0, sizeof(nextUpd)); + ExpectIntEQ(wolfSSL_OCSP_check_validity(&thisUpd, &nextUpd, 300, DAY), + WOLFSSL_FAILURE); + + /* Negative maxsec disables the age check. */ + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&thisUpd, now, 0, -DAY)); + ExpectIntEQ(wolfSSL_OCSP_check_validity(&thisUpd, NULL, 300, -1), + WOLFSSL_SUCCESS); + + /* thisUpdate within a larger maxsec. */ + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&thisUpd, now, 0, -DAY)); + ExpectIntEQ(wolfSSL_OCSP_check_validity(&thisUpd, NULL, 300, 2 * DAY), + WOLFSSL_SUCCESS); + +#ifdef WOLFSSL_OCSP_PARSE_STATUS + /* Through wolfSSL_OCSP_single_get0_status, which returns NULL for an + * absent nextUpdate. */ + { + WOLFSSL_OCSP_SINGLERESP single; + CertStatus certStatus; + WOLFSSL_ASN1_TIME* gThis = NULL; + WOLFSSL_ASN1_TIME* gNext = NULL; + + XMEMSET(&single, 0, sizeof(single)); + XMEMSET(&certStatus, 0, sizeof(certStatus)); + certStatus.status = CERT_GOOD; + single.status = &certStatus; + + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&certStatus.thisDateParsed, now, 0, + -60)); + ExpectIntEQ(wolfSSL_OCSP_single_get0_status(&single, NULL, NULL, + &gThis, &gNext), CERT_GOOD); + ExpectIntEQ(wolfSSL_OCSP_check_validity(gThis, gNext, 300, DAY), + WOLFSSL_SUCCESS); + + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&certStatus.thisDateParsed, now, 0, + -DAY)); + ExpectIntEQ(wolfSSL_OCSP_single_get0_status(&single, NULL, NULL, + &gThis, &gNext), CERT_GOOD); + ExpectIntEQ(wolfSSL_OCSP_check_validity(gThis, gNext, 300, 300), + WOLFSSL_FAILURE); + } +#endif /* WOLFSSL_OCSP_PARSE_STATUS */ + + return EXPECT_RESULT(); +} +#else +int test_ocsp_check_validity(void) +{ + return TEST_SKIPPED; +} +#endif diff --git a/tests/api/test_ocsp.h b/tests/api/test_ocsp.h index 0c0c6eb2d27..618b8f58e2c 100644 --- a/tests/api/test_ocsp.h +++ b/tests/api/test_ocsp.h @@ -23,6 +23,7 @@ #define WOLFSSL_TEST_OCSP_H int test_ocsp_certid_enc_dec(void); +int test_ocsp_check_validity(void); int test_ocsp_certid_dup(void); int test_ocsp_resp_find_status_serial_prefix(void); int test_ocsp_resp_times(void); diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 46090474893..d6f8f2fbca2 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -37349,8 +37349,10 @@ static int DecodeSingleResponse(const byte* source, word32* ioIndex, DECL_ASNGETDATA(dataASN, singleResponseASN_Length); int ret = 0; CertStatus* cs = NULL; - word32 thisDateLen; - word32 nextDateLen; + /* Maximum lengths for the date buffers. Initialized here so MSVC does not + * warn (C4701) about the WOLFSSL_OCSP_PARSE_STATUS uses below. */ + word32 thisDateLen = MAX_DATE_SIZE; + word32 nextDateLen = MAX_DATE_SIZE; word32 certIdSeqIdx; (void)wrapperSz; @@ -37361,10 +37363,6 @@ static int DecodeSingleResponse(const byte* source, word32* ioIndex, if (ret == 0) { cs = single->status; - /* Set maximum lengths for data. */ - thisDateLen = MAX_DATE_SIZE; - nextDateLen = MAX_DATE_SIZE; - /* Set OID type, buffers to hold data and variables to hold size. */ GetASN_Buffer(&dataASN[SINGLERESPONSEASN_IDX_THISUPDATE_GT], cs->thisDate, &thisDateLen); diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 6564f4aa52a..e6ca017db5a 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -4454,9 +4454,11 @@ #endif #if defined(HAVE_OCSP) && !defined(WOLFCRYPT_ONLY) && \ - (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || \ - defined(WOLFSSL_HAPROXY) || defined(HAVE_LIGHTY) || \ - defined(WOLFSSL_APACHE_HTTPD)) + (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) || \ + defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || \ + defined(HAVE_LIGHTY) || defined(WOLFSSL_APACHE_HTTPD)) + /* OPENSSL_EXTRA: the compat getters must return the parsed + * thisUpdate/nextUpdate for wolfSSL_OCSP_check_validity. */ #undef WOLFSSL_OCSP_PARSE_STATUS #define WOLFSSL_OCSP_PARSE_STATUS #endif