Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -16499,6 +16499,8 @@ static int ProcessPeerCertLeafRevocation(WOLFSSL* ssl, ProcPeerCertArgs* args,
return 1;
}
#endif
if (ret == WC_NO_ERR_TRACE(OCSP_NO_URL))
ret = OcspNoUrlPolicy(SSL_CM(ssl));
doLookup = (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN));
if (ret != 0) {
WOLFSSL_MSG("\tOCSP Lookup not ok");
Expand Down Expand Up @@ -17485,6 +17487,8 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
goto exit_ppc;
}
#endif
if (ret == WC_NO_ERR_TRACE(OCSP_NO_URL))
ret = OcspNoUrlPolicy(SSL_CM(ssl));
if (ret != 0) {
WOLFSSL_ERROR_VERBOSE(ret);
WOLFSSL_MSG("\tOCSP Lookup not ok");
Expand Down Expand Up @@ -26232,9 +26236,12 @@ int CreateOcspResponse(WOLFSSL* ssl, OcspRequest** ocspRequest,
ssl->heap);

/* Suppressing soft-fail responder errors. OCSP_CERT_REVOKED is an
* explicit positive assertion of revocation and must not be ignored. */
* explicit positive assertion of revocation and must not be ignored.
* OCSP_NO_URL just means there is no responder to staple from;
* stapling stays best-effort. */
if (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) ||
ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL)) {
ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL) ||
ret == WC_NO_ERR_TRACE(OCSP_NO_URL)) {
ret = 0;
}
}
Expand Down Expand Up @@ -27221,9 +27228,12 @@ int SendCertificateStatus(WOLFSSL* ssl)
/* Suppressing soft-fail responder errors.
* OCSP_CERT_REVOKED is an explicit positive
* assertion of revocation and must not be
* ignored. */
* ignored. OCSP_NO_URL just means there is no
* responder to staple from; stapling stays
* best-effort. */
if (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) ||
ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL)) {
ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL) ||
ret == WC_NO_ERR_TRACE(OCSP_NO_URL)) {
ret = 0;
}

Expand Down Expand Up @@ -27252,9 +27262,12 @@ int SendCertificateStatus(WOLFSSL* ssl)

/* Suppressing soft-fail responder errors.
* OCSP_CERT_REVOKED is an explicit positive assertion of
* revocation and must not be ignored. */
* revocation and must not be ignored. OCSP_NO_URL just
* means there is no responder to staple from; stapling
* stays best-effort. */
if (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) ||
ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL)) {
ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL) ||
ret == WC_NO_ERR_TRACE(OCSP_NO_URL)) {
ret = 0;
}
}
Expand Down Expand Up @@ -28690,6 +28703,9 @@ const char* wolfSSL_ERR_reason_error_string(unsigned long e)
case OCSP_NEED_URL:
return "OCSP need URL";

case OCSP_NO_URL:
return "Cert has no OCSP responder URL";

case OCSP_CERT_UNKNOWN:
return "OCSP Cert unknown";

Expand Down Expand Up @@ -41452,6 +41468,8 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
* Run OCSP if the CertManager has it enabled. */
if (ret == 0 && SSL_CM(ssl)->ocspEnabled) {
ret = CheckCertOCSP_ex(SSL_CM(ssl)->ocsp, dCert, ssl);
if (ret == WC_NO_ERR_TRACE(OCSP_NO_URL))
ret = OcspNoUrlPolicy(SSL_CM(ssl));
}
#endif
#ifdef HAVE_CRL
Expand Down
38 changes: 28 additions & 10 deletions src/ocsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -535,16 +535,12 @@ int CheckOcspRequest(WOLFSSL_OCSP* ocsp, OcspRequest* ocspRequest,
urlSz = ocspRequest->urlSz;
}
else {
/* No AIA URL and no override. ocspCheckAll asks for strict chain
* checking, so fail closed - but only on the client verification
* instance (cm->ocsp); stapling (cm->ocsp_stapling) shares the cm
* flag and must stay best-effort. */
if (ocsp->cm->ocspCheckAll && ocsp == ocsp->cm->ocsp) {
WOLFSSL_MSG("Cert has no OCSP URL and ocspCheckAll is set");
return OCSP_NEED_URL;
}
WOLFSSL_MSG("Cert has no OCSP URL, assuming CERT_GOOD");
return 0;
/* Cert advertises no OCSP responder and no override URL is set, so
* OCSP has no opinion on this cert. Report that distinctly from a
* failed lookup; the caller owns the policy decision. Callers wanting
* the historical soft-fail run this through OcspNoUrlPolicy(). */
WOLFSSL_MSG("Cert has no OCSP URL");
return OCSP_NO_URL;
Comment thread
ejohnstown marked this conversation as resolved.
}

request = (byte*)XMALLOC((size_t)requestSz, ocsp->cm->heap, DYNAMIC_TYPE_OCSP);
Expand Down Expand Up @@ -581,6 +577,28 @@ int CheckOcspRequest(WOLFSSL_OCSP* ocsp, OcspRequest* ocspRequest,
return ret;
}

/* Map OCSP_NO_URL - "this cert advertises no OCSP responder" - to a
* verification result.
*
* WOLFSSL_OCSP_CHECKALL selects which certificates get checked, not how hard
* to fail when one cannot be checked, so it deliberately has no say here.
* Refuse the cert only when the user explicitly asked for that with
* WOLFSSL_OCSP_FAIL_IF_NOT_SUPPORTED.
*
* Returns OCSP_NEED_URL to refuse the cert, or 0 for the historical
* soft-fail. */
int OcspNoUrlPolicy(WOLFSSL_CERT_MANAGER* cm)
{
if (cm != NULL && cm->ocspFailIfNotSupported) {
WOLFSSL_MSG("Cert has no OCSP URL and OCSP is required for every cert");
WOLFSSL_ERROR_VERBOSE(OCSP_NEED_URL);
return OCSP_NEED_URL;
}

WOLFSSL_MSG("Cert has no OCSP URL, assuming CERT_GOOD");
return 0;
}

/* Enforce https://www.rfc-editor.org/rfc/rfc6960#section-4.2.2.2. Both halves
* of CertID (issuerNameHash and issuerKeyHash) must match; name-only matching
* would authorize a same-DN / different-key CA. issuerKeyHash may be NULL when
Expand Down
14 changes: 12 additions & 2 deletions src/ssl_certman.c
Original file line number Diff line number Diff line change
Expand Up @@ -2137,7 +2137,8 @@ int wolfSSL_CertManagerLoadCRLFile(WOLFSSL_CERT_MANAGER* cm, const char* file,
* @param [in] cm Certificate manager.
* @param [in] options Options for using OCSP. Valid flags:
* WOLFSSL_OCSP_URL_OVERRIDE, WOLFSSL_OCSP_NO_NONCE,
* WOLFSSL_OCSP_CHECKALL.
* WOLFSSL_OCSP_CHECKALL,
* WOLFSSL_OCSP_FAIL_IF_NOT_SUPPORTED.
* @return WOLFSSL_SUCCESS on success.
* @return 0 when initializing the OCSP object fails.
* @return BAD_FUNC_ARG when cm is NULL.
Expand Down Expand Up @@ -2201,6 +2202,10 @@ int wolfSSL_CertManagerEnableOCSP(WOLFSSL_CERT_MANAGER* cm, int options)
if (options & WOLFSSL_OCSP_CHECKALL) {
cm->ocspCheckAll = 1;
}
/* Fail closed on certs that advertise no OCSP responder if requested. */
if (options & WOLFSSL_OCSP_FAIL_IF_NOT_SUPPORTED) {
cm->ocspFailIfNotSupported = 1;
}
#ifndef WOLFSSL_USER_IO
/* Set built-in OCSP lookup. */
cm->ocspIOCb = EmbedOcspLookup;
Expand Down Expand Up @@ -2446,7 +2451,12 @@ int wolfSSL_CertManagerCheckOCSP(WOLFSSL_CERT_MANAGER* cm,
}
/* Do OCSP checks with decoded certificate. */
else if ((ret = CheckCertOCSP(cm->ocsp, cert)) != 0) {
WOLFSSL_MSG("CheckCertOCSP failed");
/* Apply the caller's policy for a cert that advertises no
* responder rather than reporting a lookup failure. */
if (ret == WC_NO_ERR_TRACE(OCSP_NO_URL))
ret = OcspNoUrlPolicy(cm);
if (ret != 0)
WOLFSSL_MSG("CheckCertOCSP failed");
}

/* Dispose of dynamically allocated memory. */
Expand Down
26 changes: 22 additions & 4 deletions src/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -3730,9 +3730,11 @@ int ProcessChainOCSPRequest(WOLFSSL* ssl)
request, &csr->responses[i], ssl->heap);
/* Suppressing soft-fail responder errors. OCSP_CERT_REVOKED
* is an explicit positive assertion of revocation and must
* not be ignored. */
* not be ignored. OCSP_NO_URL just means there is no
* responder to staple from; stapling stays best-effort. */
if (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) ||
ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL)) {
ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL) ||
ret == WC_NO_ERR_TRACE(OCSP_NO_URL)) {
ret = 0;
}
i++;
Expand Down Expand Up @@ -4007,9 +4009,17 @@ int TLSX_CSR_ForceRequest(WOLFSSL* ssl)
switch (csr->status_type) {
case WOLFSSL_CSR_OCSP:
if (SSL_CM(ssl)->ocspEnabled) {
int ret;
csr->request.ocsp[0].ssl = ssl;
return CheckOcspRequest(SSL_CM(ssl)->ocsp,
ret = CheckOcspRequest(SSL_CM(ssl)->ocsp,
&csr->request.ocsp[0], NULL, NULL);
/* This is the client's fallback leaf lookup on the
* verification instance, so honor the no-responder policy
* just like the non-stapling leaf path. Default stays
* best-effort; FAIL_IF_NOT_SUPPORTED makes it fail closed. */
if (ret == WC_NO_ERR_TRACE(OCSP_NO_URL))
ret = OcspNoUrlPolicy(SSL_CM(ssl));
return ret;
}
else {
WOLFSSL_ERROR_VERBOSE(OCSP_LOOKUP_FAIL);
Expand Down Expand Up @@ -4542,9 +4552,17 @@ int TLSX_CSR2_ForceRequest(WOLFSSL* ssl)

case WOLFSSL_CSR2_OCSP_MULTI:
if (SSL_CM(ssl)->ocspEnabled && csr2->requests >= 1) {
int ret;
csr2->request.ocsp[csr2->requests-1].ssl = ssl;
return CheckOcspRequest(SSL_CM(ssl)->ocsp,
ret = CheckOcspRequest(SSL_CM(ssl)->ocsp,
&csr2->request.ocsp[csr2->requests-1], NULL, NULL);
/* This is the client's fallback leaf lookup on the
* verification instance, so honor the no-responder policy
* just like the non-stapling leaf path. Default stays
* best-effort; FAIL_IF_NOT_SUPPORTED makes it fail closed. */
if (ret == WC_NO_ERR_TRACE(OCSP_NO_URL))
ret = OcspNoUrlPolicy(SSL_CM(ssl));
return ret;
}
else {
WOLFSSL_ERROR_VERBOSE(OCSP_LOOKUP_FAIL);
Expand Down
1 change: 1 addition & 0 deletions tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -37734,6 +37734,7 @@ TEST_CASE testCases[] = {
TEST_DECL(test_ocsp_status_request_v2_multi_revoked_single),
TEST_DECL(test_ocsp_cert_unknown_crl_fallback),
TEST_DECL(test_ocsp_cert_unknown_crl_fallback_nonleaf),
TEST_DECL(test_ocsp_no_url_policy),
TEST_DECL(test_tls13_nonblock_ocsp_low_mfl),
TEST_DECL(test_ocsp_responder),
TEST_DECL(test_wolfIO_DecodeUrl_crlf_reject),
Expand Down
68 changes: 68 additions & 0 deletions tests/api/test_ocsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,74 @@ int test_ocsp_cert_unknown_crl_fallback_nonleaf(void)
}
#endif /* HAVE_OCSP && HAVE_CRL && HAVE_SSL_MEMIO_TESTS_DEPENDENCIES */

#if defined(HAVE_OCSP) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES)

/* The default memio chain (certs/server-cert.pem under certs/ca-cert.pem)
* carries no AIA extension, so no cert in it advertises an OCSP responder
* and no override URL is configured. */

static int test_ocsp_no_url_checkall_ctx_ready(WOLFSSL_CTX* ctx)
{
EXPECT_DECLS;

wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL);
/* CHECKALL selects scope only, so it must not fail a chain that
* advertises no responder. */
ExpectIntEQ(wolfSSL_CTX_EnableOCSP(ctx, WOLFSSL_OCSP_CHECKALL),
WOLFSSL_SUCCESS);

return EXPECT_RESULT();
}

static int test_ocsp_no_url_fail_closed_ctx_ready(WOLFSSL_CTX* ctx)
{
EXPECT_DECLS;

wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL);
ExpectIntEQ(wolfSSL_CTX_EnableOCSP(ctx, WOLFSSL_OCSP_CHECKALL |
Comment thread
ejohnstown marked this conversation as resolved.
WOLFSSL_OCSP_FAIL_IF_NOT_SUPPORTED),
WOLFSSL_SUCCESS);

return EXPECT_RESULT();
}

int test_ocsp_no_url_policy(void)
{
EXPECT_DECLS;
struct test_ssl_memio_ctx test_ctx;

/* WOLFSSL_OCSP_CHECKALL on its own soft-fails a cert that advertises no
* responder, as it has since the flag was introduced. Regression test:
* failing closed here breaks any chain whose CA publishes no OCSP URI,
* which is common on the public web. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
test_ctx.c_cb.ctx_ready = test_ocsp_no_url_checkall_ctx_ready;
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
TEST_SUCCESS);
test_ssl_memio_cleanup(&test_ctx);

/* Opting in with WOLFSSL_OCSP_FAIL_IF_NOT_SUPPORTED refuses the same
* chain with OCSP_NEED_URL. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
test_ctx.c_cb.ctx_ready = test_ocsp_no_url_fail_closed_ctx_ready;
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
TEST_SUCCESS);
ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, 0),
WC_NO_ERR_TRACE(OCSP_NEED_URL));
test_ssl_memio_cleanup(&test_ctx);

return EXPECT_RESULT();
}

#else
int test_ocsp_no_url_policy(void)
{
return TEST_SKIPPED;
}
#endif /* HAVE_OCSP && HAVE_SSL_MEMIO_TESTS_DEPENDENCIES */

#if defined(HAVE_OCSP) && defined(WOLFSSL_TLS13) && \
defined(WOLFSSL_NONBLOCK_OCSP) && defined(HAVE_MAX_FRAGMENT) && \
defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \
Expand Down
1 change: 1 addition & 0 deletions tests/api/test_ocsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ int test_ocsp_tls_cert_cb(void);
int test_ocsp_status_request_v2_multi_revoked_single(void);
int test_ocsp_cert_unknown_crl_fallback(void);
int test_ocsp_cert_unknown_crl_fallback_nonleaf(void);
int test_ocsp_no_url_policy(void);
int test_tls13_nonblock_ocsp_low_mfl(void);
int test_ocsp_responder(void);
int test_ocsp_ancestor_responder_rejected(void);
Expand Down
5 changes: 4 additions & 1 deletion wolfssl/error-ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,10 @@ enum wolfSSL_ErrorCodes {
RPK_UNTRUSTED_E = -521, /* RFC 7250 Raw Public Key not trusted
* out of band */

WOLFSSL_LAST_E = -521
OCSP_NO_URL = -522, /* Cert advertises no OCSP responder
* and no override URL is set */

WOLFSSL_LAST_E = -522

/* codes -1000 to -1999 are reserved for wolfCrypt. */
};
Expand Down
2 changes: 2 additions & 0 deletions wolfssl/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -2668,6 +2668,8 @@ struct WOLFSSL_CERT_MANAGER {
byte crlCheckAll:1; /* always leaf, but all ? */
byte ocspEnabled:1; /* is OCSP on ? */
byte ocspCheckAll:1; /* always leaf, but all ? */
byte ocspFailIfNotSupported:1; /* refuse a cert that advertises
* no OCSP responder ? */
byte ocspSendNonce:1; /* send the OCSP nonce ? */
byte ocspUseOverrideURL:1; /* ignore cert responder, override */
byte ocspStaplingEnabled:1; /* is OCSP Stapling on ? */
Expand Down
1 change: 1 addition & 0 deletions wolfssl/ocsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ WOLFSSL_LOCAL int CheckCertOCSP_ex(WOLFSSL_OCSP* ocsp, DecodedCert* cert,
WOLFSSL_LOCAL int CheckOcspRequest(WOLFSSL_OCSP* ocsp,
OcspRequest* ocspRequest, WOLFSSL_BUFFER_INFO* responseBuffer,
void* heap);
WOLFSSL_LOCAL int OcspNoUrlPolicy(WOLFSSL_CERT_MANAGER* cm);
WOLFSSL_LOCAL int CheckOcspResponse(WOLFSSL_OCSP *ocsp, byte *response, int responseSz,
WOLFSSL_BUFFER_INFO *responseBuffer, CertStatus *status,
OcspEntry *entry, OcspRequest *ocspRequest,
Expand Down
6 changes: 6 additions & 0 deletions wolfssl/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2948,7 +2948,13 @@ enum {
enum {
WOLFSSL_OCSP_URL_OVERRIDE = 1,
WOLFSSL_OCSP_NO_NONCE = 2,
/* Check every cert in the chain, not just the leaf. Selects scope only;
* see WOLFSSL_OCSP_FAIL_IF_NOT_SUPPORTED for failure policy. */
WOLFSSL_OCSP_CHECKALL = 4,
/* Refuse a cert that advertises no OCSP responder, instead of the default
* soft-fail. Independent of WOLFSSL_OCSP_CHECKALL, which decides which
* certs are checked rather than how hard to fail. */
WOLFSSL_OCSP_FAIL_IF_NOT_SUPPORTED = 8,

WOLFSSL_CRL_CHECKALL = 1,
WOLFSSL_CRL_CHECK = 2
Expand Down
Loading