diff --git a/src/internal.c b/src/internal.c index c66dfc4a35..c1373ba092 100644 --- a/src/internal.c +++ b/src/internal.c @@ -8730,6 +8730,12 @@ void FreeHandshakeResources(WOLFSSL* ssl) * !WOLFSSL_POST_HANDSHAKE_AUTH */ #endif /* HAVE_TLS_EXTENSIONS && !NO_TLS */ +#ifdef OPENSSL_EXTRA + if (ssl->ocspResp != NULL) + XFREE(ssl->ocspResp, NULL, 0); + ssl->ocspResp = NULL; +#endif + #ifdef WOLFSSL_STATIC_MEMORY /* when done with handshake decrement current handshake count */ if (ssl->heap != NULL) { @@ -24158,6 +24164,43 @@ static int BuildCertificateStatus(WOLFSSL* ssl, byte type, buffer* status, return ret; } #endif + +#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \ +(defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || defined(OPENSSL_EXTRA)) +static int BuildCertificateStatusWithStatusCB(WOLFSSL* ssl) +{ + WOLFSSL_OCSP *ocsp; + void *ioCtx = NULL; + buffer response; + int ret; + + ocsp = SSL_CM(ssl)->ocsp_stapling; + if (ocsp == NULL || ocsp->statusCb == NULL) + return BAD_FUNC_ARG; + ioCtx = (ssl && ssl->ocspIOCtx != NULL) ? + ssl->ocspIOCtx : ocsp->cm->ocspIOCtx; + XMEMSET(&response, 0, sizeof(response)); + WOLFSSL_MSG("Calling ocsp->statusCb"); + ret = ocsp->statusCb(ssl, ioCtx); + switch (ret) { + case SSL_TLSEXT_ERR_OK: + response.buffer = ssl->ocspResp; + response.length = ssl->ocspRespSz; + ret = BuildCertificateStatus(ssl, WOLFSSL_CSR_OCSP, &response, 1); + break; + case SSL_TLSEXT_ERR_NOACK: + /* No OCSP response to send */ + ret = 0; + break; + case SSL_TLSEXT_ERR_ALERT_FATAL: + /* fall through */ + default: + ret = WOLFSSL_FATAL_ERROR; + break; + } + return ret; +} +#endif /* HAVE_CERTIFICATE_STATUS_REQUEST && (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || defined(OPENSSL_EXTRA)) */ #endif /* NO_WOLFSSL_SERVER */ /* handle generation of certificate_status (22) */ @@ -24179,6 +24222,14 @@ int SendCertificateStatus(WOLFSSL* ssl) status_type = status_type ? status_type : ssl->status_request_v2; #endif +#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \ +(defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || defined(OPENSSL_EXTRA)) + if (ssl != NULL && SSL_CM(ssl) != NULL && SSL_CM(ssl)->ocsp_stapling != NULL && SSL_CM(ssl)->ocsp_stapling->statusCb != NULL) { + if (ssl->status_request == WOLFSSL_CSR_OCSP) + return BuildCertificateStatusWithStatusCB(ssl); + } +#endif + switch (status_type) { #ifndef NO_WOLFSSL_SERVER diff --git a/src/ocsp.c b/src/ocsp.c index 41c038fd12..418544113f 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -480,31 +480,6 @@ int CheckOcspRequest(WOLFSSL_OCSP* ocsp, OcspRequest* ocspRequest, ioCtx = (ssl && ssl->ocspIOCtx != NULL) ? ssl->ocspIOCtx : ocsp->cm->ocspIOCtx; -#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) - if (ocsp->statusCb != NULL && ssl != NULL) { - WOLFSSL_MSG("Calling ocsp->statusCb"); - ret = ocsp->statusCb(ssl, ioCtx); - switch (ret) { - case SSL_TLSEXT_ERR_OK: - ret = wolfSSL_get_ocsp_response(ssl, &response); - ret = CheckOcspResponse(ocsp, response, ret, responseBuffer, - status, entry, NULL, heap); - XFREE(response, NULL, DYNAMIC_TYPE_OPENSSL); - break; - case SSL_TLSEXT_ERR_NOACK: - ret = OCSP_LOOKUP_FAIL; - break; - case SSL_TLSEXT_ERR_ALERT_FATAL: - default: - WOLFSSL_LEAVE("CheckOcspRequest", ocsp->error); - ret = WOLFSSL_FATAL_ERROR; - break; - } - WOLFSSL_LEAVE("CheckOcspRequest", ret); - return ret; - } -#endif - if (ocsp->cm->ocspUseOverrideURL) { url = ocsp->cm->ocspOverrideURL; if (url != NULL && url[0] != '\0') diff --git a/src/ssl.c b/src/ssl.c index e4aecb0e84..33b874d064 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -16410,6 +16410,8 @@ long wolfSSL_set_tlsext_status_ocsp_resp(WOLFSSL *s, unsigned char *resp, if (s == NULL) return WOLFSSL_FAILURE; + if (s->ocspResp) + XFREE(s->ocspResp, NULL, 0); s->ocspResp = resp; s->ocspRespSz = len; diff --git a/src/tls.c b/src/tls.c index 039096967e..4733983f9b 100644 --- a/src/tls.c +++ b/src/tls.c @@ -3215,13 +3215,80 @@ static word16 TLSX_CSR_GetSize(CertificateStatusRequest* csr, byte isRequest) } #endif #if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER) - if (!isRequest && csr->ssl->options.tls1_3) + if (!isRequest && csr->ssl->options.tls1_3) { +#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || defined(OPENSSL_EXTRA) + if (csr->ssl != NULL && SSL_CM(csr->ssl) != NULL && SSL_CM(csr->ssl)->ocsp_stapling != NULL + && SSL_CM(csr->ssl)->ocsp_stapling->statusCb != NULL) { + return OPAQUE8_LEN + OPAQUE24_LEN + csr->ssl->ocspRespSz; + } +#endif /* OPENSSL_ALL || WOLFSSL_NGINX || WOLFSSL_HAPROXY || OPENSSL_EXTRA */ return OPAQUE8_LEN + OPAQUE24_LEN + csr->response.length; + } #endif return size; } +#if (defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER)) && (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || defined(OPENSSL_EXTRA)) +static int TLSX_CSR_SetResponseWithStatusCB(WOLFSSL *ssl) +{ + void *ioCtx = NULL; + WOLFSSL_OCSP *ocsp; + int ret; + + if (ssl == NULL || SSL_CM(ssl) == NULL) + return BAD_FUNC_ARG; + ocsp = SSL_CM(ssl)->ocsp_stapling; + if (ocsp == NULL || ocsp->statusCb == NULL) + return BAD_FUNC_ARG; + ioCtx = (ssl->ocspIOCtx != NULL) ? ssl->ocspIOCtx : ocsp->cm->ocspIOCtx; + ret = ocsp->statusCb(ssl, ioCtx); + switch (ret) { + case SSL_TLSEXT_ERR_OK: + if (ssl->ocspRespSz > 0) { + /* ack the extension, status cb provided the response in ssl->ocspResp */ + TLSX_SetResponse(ssl, TLSX_STATUS_REQUEST); + ssl->status_request = WOLFSSL_CSR_OCSP; + } + ret = 0; + break; + case SSL_TLSEXT_ERR_NOACK: + /* suppressing as not critical */ + ret = 0; + break; + case SSL_TLSEXT_ERR_ALERT_FATAL: + default: + ret = WOLFSSL_FATAL_ERROR; + break; + } + return ret; +} + +static int TLSX_CSR_WriteWithStatusCB(CertificateStatusRequest* csr, byte* output) +{ + WOLFSSL *ssl = csr->ssl; + WOLFSSL_OCSP *ocsp; + word16 offset = 0; + byte *response; + int respSz; + + response = ssl->ocspResp; + respSz = ssl->ocspRespSz; + if (ssl == NULL || SSL_CM(ssl) == NULL) + return BAD_FUNC_ARG; + ocsp = SSL_CM(ssl)->ocsp_stapling; + if (ocsp == NULL || ocsp->statusCb == NULL) + return BAD_FUNC_ARG; + if (respSz == 0) + return BAD_FUNC_ARG; + output[offset++] = WOLFSSL_CSR_OCSP; + c32to24(respSz, output + offset); + offset += OPAQUE24_LEN; + XMEMCPY(output + offset, response, respSz); + return offset + respSz; +} +#endif /* (TLS13 && !NO_WOLFSLL_SERVER) && (OPENSSL_ALL || WOLFSSL_NGINX || WOLFSSL_HAPROXY || OPENSSL_EXTRA) */ + static int TLSX_CSR_Write(CertificateStatusRequest* csr, byte* output, byte isRequest) { @@ -3268,6 +3335,12 @@ static int TLSX_CSR_Write(CertificateStatusRequest* csr, byte* output, #endif #if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER) if (!isRequest && csr->ssl->options.tls1_3) { +#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || defined(OPENSSL_EXTRA) + if (csr->ssl != NULL && SSL_CM(csr->ssl) != NULL && SSL_CM(csr->ssl)->ocsp_stapling != NULL + && SSL_CM(csr->ssl)->ocsp_stapling->statusCb != NULL) { + return TLSX_CSR_WriteWithStatusCB(csr, output); + } +#endif /* OPENSSL_ALL || WOLFSSL_NGINX || WOLFSSL_HAPROXY || defined(OPENSSL_EXTRA) */ word16 offset = 0; output[offset++] = csr->status_type; c32to24(csr->response.length, output + offset); @@ -3445,6 +3518,12 @@ static int TLSX_CSR_Parse(WOLFSSL* ssl, const byte* input, word16 length, #if defined(WOLFSSL_TLS13) if (ssl->options.tls1_3) { +#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || defined(OPENSSL_EXTRA) + if (ssl != NULL && SSL_CM(ssl) != NULL && SSL_CM(ssl)->ocsp_stapling != NULL + && SSL_CM(ssl)->ocsp_stapling->statusCb != NULL) { + return TLSX_CSR_SetResponseWithStatusCB(ssl); + } +#endif /* OPENSSL_ALL || WOLFSSL_NGINX || WOLFSSL_HAPROXY || defined(OPENSSL_EXTRA) */ if (ssl->buffers.certificate == NULL) { WOLFSSL_MSG("Certificate buffer not set!"); return BUFFER_ERROR; @@ -3894,6 +3973,13 @@ static int TLSX_CSR2_Parse(WOLFSSL* ssl, const byte* input, word16 length, continue; } +#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || defined(OPENSSL_EXTRA) + /* OpenSSL status CB supports only CERTIFICATE STATUS REQ V1 */ + if (ssl != NULL && SSL_CM(ssl) != NULL && SSL_CM(ssl)->ocsp_stapling != NULL && + SSL_CM(ssl)->ocsp_stapling->statusCb != NULL) { + return 0; + } +#endif /* if using status_request and already sending it, remove it * and prefer to use the v2 version */ #ifdef HAVE_CERTIFICATE_STATUS_REQUEST diff --git a/tests/api.c b/tests/api.c index 4821345cff..a955caec0a 100644 --- a/tests/api.c +++ b/tests/api.c @@ -95693,6 +95693,190 @@ static int test_ocsp_callback_fails(void) defined(HAVE_CERTIFICATE_STATUS_REQUEST) */ +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(HAVE_OCSP) && \ + defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \ + !defined(WOLFSSL_NO_TLS12) && \ + (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) + +struct _test_ocsp_status_callback_ctx { + byte *ocsp_resp; + int ocsp_resp_sz; + int invoked; +}; + +static int test_ocsp_status_callback_cb(WOLFSSL *ssl, void *ctx) +{ + struct _test_ocsp_status_callback_ctx *_ctx = (struct _test_ocsp_status_callback_ctx*)ctx; + + _ctx->invoked++; + SSL_set_tlsext_status_ocsp_resp(ssl, _ctx->ocsp_resp, _ctx->ocsp_resp_sz); + return SSL_TLSEXT_ERR_OK; +} + +static int test_ocsp_status_callback_cb_noack(WOLFSSL *ssl, void *ctx) +{ + struct _test_ocsp_status_callback_ctx *_ctx = (struct _test_ocsp_status_callback_ctx*)ctx; + (void)ssl; + + _ctx->invoked++; + return SSL_TLSEXT_ERR_NOACK; +} + +static int test_ocsp_status_callback_cb_err(WOLFSSL *ssl, void *ctx) +{ + struct _test_ocsp_status_callback_ctx *_ctx = (struct _test_ocsp_status_callback_ctx*)ctx; + (void)ssl; + + _ctx->invoked++; + return SSL_TLSEXT_ERR_ALERT_FATAL; +} + +static int test_ocsp_status_callback_test_setup(struct _test_ocsp_status_callback_ctx *cb_ctx, + struct test_ssl_memio_ctx *test_ctx, method_provider cm, method_provider sm) +{ + int ret; + + cb_ctx->invoked = 0; + XMEMSET(test_ctx, 0, sizeof(*test_ctx)); + test_ctx->c_cb.caPemFile = "./certs/ocsp/root-ca-cert.pem"; + test_ctx->s_cb.certPemFile = "./certs/ocsp/server1-cert.pem"; + test_ctx->s_cb.keyPemFile = "./certs/ocsp/server1-key.pem"; + test_ctx->c_cb.method = cm; + test_ctx->s_cb.method = sm; + ret = test_ssl_memio_setup(test_ctx); + wolfSSL_set_verify(test_ctx->c_ssl, WOLFSSL_VERIFY_DEFAULT, NULL); + return ret; +} + +static int test_ocsp_status_callback(void) +{ + struct test_params { + method_provider c_method; + method_provider s_method; + }; + + const char* responseFile = "./certs/ocsp/test-leaf-response.der"; + struct _test_ocsp_status_callback_ctx cb_ctx; + struct test_ssl_memio_ctx test_ctx; + int enable_client_ocsp; + int enable_must_staple; + byte data[4096]; + unsigned int i; + EXPECT_DECLS; + + struct test_params params[] = { + { wolfTLSv1_2_client_method, wolfTLSv1_2_server_method }, +#if defined(WOLFSSL_TLS13) + { wolfTLSv1_3_client_method, wolfTLSv1_3_server_method }, +#endif +#if defined(WOLFSSL_DTLS) + { wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method }, +#endif +#if defined(WOLFSSL_DTLS13) + { wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method }, +#endif + }; + + XMEMSET(&cb_ctx, 0, sizeof(cb_ctx)); + XFILE f = XBADFILE; + f = XFOPEN(responseFile, "rb"); + if (f == XBADFILE) + return -1; + cb_ctx.ocsp_resp_sz = (word32)XFREAD(data, 1, 4096, f); + if (f != XBADFILE) { + XFCLOSE(f); + f = XBADFILE; + } + cb_ctx.ocsp_resp = data; + + for (i = 0; i < sizeof(params)/sizeof(params[0]); i++) { + for (enable_client_ocsp = 0; enable_client_ocsp <= 1; enable_client_ocsp++) { + ExpectIntEQ(test_ocsp_status_callback_test_setup(&cb_ctx, + &test_ctx, params[i].c_method, params[i].s_method), TEST_SUCCESS); + ExpectIntEQ(SSL_CTX_set_tlsext_status_cb(test_ctx.s_ctx, test_ocsp_status_callback_cb), SSL_SUCCESS); + ExpectIntEQ(SSL_CTX_set_tlsext_status_arg(test_ctx.s_ctx, (void*)&cb_ctx), SSL_SUCCESS); + if (enable_client_ocsp) { + ExpectIntEQ(wolfSSL_UseOCSPStapling(test_ctx.c_ssl, WOLFSSL_CSR_OCSP, 0), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.c_ctx), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_EnableOCSPMustStaple(test_ctx.c_ctx), WOLFSSL_SUCCESS); + } + ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS); + ExpectIntEQ(cb_ctx.invoked, enable_client_ocsp ? 1 : 0); + test_ssl_memio_cleanup(&test_ctx); + if (!EXPECT_SUCCESS()) + return EXPECT_RESULT(); + } + } +#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) + /* test client sending both OCSPv1 and OCSPv2/MultiOCSP */ + /* StatusCb only supports OCSPv1 */ + ExpectIntEQ(test_ocsp_status_callback_test_setup(&cb_ctx, &test_ctx, wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), TEST_SUCCESS); + ExpectIntEQ(SSL_CTX_set_tlsext_status_cb(test_ctx.s_ctx, test_ocsp_status_callback_cb), SSL_SUCCESS); + ExpectIntEQ(SSL_CTX_set_tlsext_status_arg(test_ctx.s_ctx, (void*)&cb_ctx), SSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.c_ctx), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_EnableOCSPMustStaple(test_ctx.c_ctx), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseOCSPStapling(test_ctx.c_ssl, WOLFSSL_CSR_OCSP, 0), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseOCSPStaplingV2(test_ctx.c_ssl, WOLFSSL_CSR2_OCSP_MULTI, 0), WOLFSSL_SUCCESS); + wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_DEFAULT, NULL); + ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS); + ExpectIntEQ(cb_ctx.invoked, 1); + test_ssl_memio_cleanup(&test_ctx); + + if (!EXPECT_SUCCESS()) + return EXPECT_RESULT(); +#endif /* defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) */ + /* test cb returning NO_ACK, not acking the OCSP */ + for (i = 0; i < sizeof(params)/sizeof(params[0]); i++) { + for (enable_must_staple = 0; enable_must_staple <= 1; enable_must_staple++) { + ExpectIntEQ(test_ocsp_status_callback_test_setup(&cb_ctx, &test_ctx, params[i].c_method, params[i].s_method), TEST_SUCCESS); + ExpectIntEQ(SSL_CTX_set_tlsext_status_cb(test_ctx.s_ctx, test_ocsp_status_callback_cb_noack), SSL_SUCCESS); + ExpectIntEQ(SSL_CTX_set_tlsext_status_arg(test_ctx.s_ctx, (void*)&cb_ctx), SSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.c_ctx), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseOCSPStapling(test_ctx.c_ssl, WOLFSSL_CSR_OCSP, 0), WOLFSSL_SUCCESS); + if (enable_must_staple) + ExpectIntEQ(wolfSSL_CTX_EnableOCSPMustStaple(test_ctx.c_ctx), WOLFSSL_SUCCESS); + wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_DEFAULT, NULL); + ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), enable_must_staple ? TEST_FAIL : TEST_SUCCESS); + ExpectIntEQ(cb_ctx.invoked, 1); + test_ssl_memio_cleanup(&test_ctx); + if (!EXPECT_SUCCESS()) + return EXPECT_RESULT(); + } + } + + /* test cb returning err aborting handshake */ + for (i = 0; i < sizeof(params)/sizeof(params[0]); i++) { + for (enable_client_ocsp = 0; enable_client_ocsp <= 1; enable_client_ocsp++) { + ExpectIntEQ(test_ocsp_status_callback_test_setup(&cb_ctx, &test_ctx, params[i].c_method, params[i].s_method), TEST_SUCCESS); + ExpectIntEQ(SSL_CTX_set_tlsext_status_cb(test_ctx.s_ctx, test_ocsp_status_callback_cb_err), SSL_SUCCESS); + ExpectIntEQ(SSL_CTX_set_tlsext_status_arg(test_ctx.s_ctx, (void*)&cb_ctx), SSL_SUCCESS); + if (enable_client_ocsp) + ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.c_ctx), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseOCSPStapling(test_ctx.c_ssl, WOLFSSL_CSR_OCSP, 0), WOLFSSL_SUCCESS); + wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_DEFAULT, NULL); + ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), enable_client_ocsp ? TEST_FAIL : TEST_SUCCESS); + ExpectIntEQ(cb_ctx.invoked, enable_client_ocsp ? 1: 0); + test_ssl_memio_cleanup(&test_ctx); + if (!EXPECT_SUCCESS()) + return EXPECT_RESULT(); + } + } + + return EXPECT_RESULT(); +} + +#else +static int test_ocsp_status_callback(void) +{ + return TEST_SKIPPED; +} +#endif /* defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(HAVE_OCSP) && \ + defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \ + !defined(WOLFSSL_NO_TLS12) && \ + (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) */ + /*----------------------------------------------------------------------------* | Main *----------------------------------------------------------------------------*/ @@ -96945,6 +97129,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_UseOCSPStaplingV2), TEST_DECL(test_self_signed_stapling), TEST_DECL(test_ocsp_callback_fails), + TEST_DECL(test_ocsp_status_callback), /* Multicast */ TEST_DECL(test_wolfSSL_mcast),