F-4144: honor WOLFSSL_OP_NO_RENEGOTIATION

The documented 'reject peer-initiated renegotiation' option was accepted and
stored but never consulted. Now DoHelloRequest replies with a no_renegotiation
warning instead of starting SCR when the bit is set (client side), and the
server refuses a renegotiation ClientHello with a no_renegotiation warning
instead of resetting handshake state.
This commit is contained in:
Juliusz Sosinowicz
2026-06-03 00:29:19 +02:00
parent 6e1ca6bc70
commit 1173a365fe
4 changed files with 164 additions and 0 deletions
+21
View File
@@ -18051,6 +18051,15 @@ static int DoHelloRequest(WOLFSSL* ssl, word32 size)
}
#ifdef HAVE_SECURE_RENEGOTIATION
else if (ssl->secure_renegotiation && ssl->secure_renegotiation->enabled) {
/* WOLFSSL_OP_NO_RENEGOTIATION: caller opted into rejecting
* peer-initiated renegotiation. Respond with a no_renegotiation
* warning alert instead of starting a secure renegotiation. */
if (ssl->options.mask & WOLFSSL_OP_NO_RENEGOTIATION) {
WOLFSSL_MSG("Rejecting HelloRequest: WOLFSSL_OP_NO_RENEGOTIATION");
WOLFSSL_LEAVE("DoHelloRequest", 0);
WOLFSSL_END(WC_FUNC_HELLO_REQUEST_DO);
return SendAlert(ssl, alert_warning, no_renegotiation);
}
ssl->secure_renegotiation->startScr = 1;
WOLFSSL_LEAVE("DoHelloRequest", 0);
WOLFSSL_END(WC_FUNC_HELLO_REQUEST_DO);
@@ -18783,6 +18792,17 @@ int DoHandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx,
ssl->secure_renegotiation &&
ssl->secure_renegotiation->enabled)
{
/* WOLFSSL_OP_NO_RENEGOTIATION: caller opted into rejecting
* peer-initiated renegotiation. RFC 5246 7.2.2: no_renegotiation is a
* warning-level alert, so refuse the renegotiation but keep the
* established connection rather than aborting it. Skip the ClientHello
* body and leave handshake state untouched, mirroring the client-side
* HelloRequest refusal in DoHelloRequest(). */
if (ssl->options.mask & WOLFSSL_OP_NO_RENEGOTIATION) {
WOLFSSL_MSG("Refusing renegotiation: WOLFSSL_OP_NO_RENEGOTIATION");
*inOutIdx = expectedIdx;
return SendAlert(ssl, alert_warning, no_renegotiation);
}
WOLFSSL_MSG("Reset handshake state");
XMEMSET(&ssl->msgsReceived, 0, sizeof(MsgsReceived));
ssl->options.serverState = NULL_STATE;
@@ -23240,6 +23260,7 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
/* see if sending SSLv2 client hello */
if ( ssl->options.side == WOLFSSL_SERVER_END &&
ssl->options.clientState == NULL_STATE &&
!ssl->options.handShakeDone &&
ssl->buffers.inputBuffer.buffer[ssl->buffers.inputBuffer.idx]
!= handshake &&
/* change_cipher_spec here is an error but we want to handle
+2
View File
@@ -35075,6 +35075,8 @@ TEST_CASE testCases[] = {
TEST_DECL(test_tls12_chacha20_poly1305_bad_tag),
TEST_DECL(test_tls13_null_cipher_bad_hmac),
TEST_DECL(test_scr_verify_data_mismatch),
TEST_DECL(test_scr_no_renegotiation_option),
TEST_DECL(test_helloRequest_no_renegotiation_option),
TEST_DECL(test_tls13_hrr_cipher_suite_mismatch),
TEST_DECL(test_tls13_ticket_age_out_of_window),
TEST_DECL(test_wolfSSL_DisableExtendedMasterSecret),
+139
View File
@@ -350,6 +350,145 @@ int test_scr_verify_data_mismatch(void)
return EXPECT_RESULT();
}
/* F-4144: WOLFSSL_OP_NO_RENEGOTIATION on the server must refuse a
* client-initiated renegotiation with a no_renegotiation *warning* while
* keeping the established connection alive, rather than aborting it. */
int test_scr_no_renegotiation_option(void)
{
EXPECT_DECLS;
#if defined(HAVE_SECURE_RENEGOTIATION) && !defined(WOLFSSL_NO_TLS12) && \
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
struct test_memio_ctx test_ctx;
WOLFSSL_CTX *ctx_c = NULL;
WOLFSSL_CTX *ctx_s = NULL;
WOLFSSL *ssl_c = NULL;
WOLFSSL *ssl_s = NULL;
WOLFSSL_ALERT_HISTORY history;
byte readBuf[16];
int ret = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR);
int i;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
XMEMSET(&history, 0, sizeof(history));
test_ctx.c_ciphers = test_ctx.s_ciphers = "ECDHE-RSA-AES128-GCM-SHA256";
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
&ssl_s, wolfTLSv1_2_client_method,
wolfTLSv1_2_server_method), 0);
ExpectIntEQ(wolfSSL_CTX_UseSecureRenegotiation(ctx_c), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_UseSecureRenegotiation(ctx_s), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_c), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_s), WOLFSSL_SUCCESS);
/* Server opts into rejecting peer-initiated renegotiation. */
wolfSSL_set_options(ssl_s, WOLFSSL_OP_NO_RENEGOTIATION);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
/* Client initiates renegotiation: it sends a ClientHello and waits for a
* ServerHello that never comes. */
ExpectIntLT(wolfSSL_Rehandshake(ssl_c), 0);
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
/* Server processes the renegotiation ClientHello. It must refuse without
* aborting: the read returns WANT_READ (connection still alive), not a
* SECURE_RENEGOTIATION_E fatal error. */
ExpectIntLT(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 0);
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
/* The refusal was a warning-level no_renegotiation alert. */
ExpectIntEQ(wolfSSL_get_alert_history(ssl_s, &history), WOLFSSL_SUCCESS);
ExpectIntEQ(history.last_tx.level, alert_warning);
ExpectIntEQ(history.last_tx.code, no_renegotiation);
/* The connection is still active and passes data: the server sends
* application data which the client receives and decrypts correctly, even
* though the client's renegotiation attempt was refused. The client
* surfaces the data once it has processed the no_renegotiation warning. */
ExpectIntEQ(wolfSSL_write(ssl_s, "hello", 5), 5);
for (i = 0; i < 10 && ret != 5; i++)
ret = wolfSSL_read(ssl_c, readBuf, sizeof(readBuf));
ExpectIntEQ(ret, 5);
ExpectIntEQ(XMEMCMP(readBuf, "hello", 5), 0);
wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}
/* F-4144: WOLFSSL_OP_NO_RENEGOTIATION on the client must refuse a
* server-initiated renegotiation (HelloRequest) with a no_renegotiation
* *warning* while keeping the established connection alive, rather than
* starting a secure renegotiation. */
int test_helloRequest_no_renegotiation_option(void)
{
EXPECT_DECLS;
#if defined(HAVE_SECURE_RENEGOTIATION) && !defined(WOLFSSL_NO_TLS12) && \
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
struct test_memio_ctx test_ctx;
WOLFSSL_CTX *ctx_c = NULL;
WOLFSSL_CTX *ctx_s = NULL;
WOLFSSL *ssl_c = NULL;
WOLFSSL *ssl_s = NULL;
WOLFSSL_ALERT_HISTORY history;
byte readBuf[16];
int ret = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR);
int i;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
XMEMSET(&history, 0, sizeof(history));
test_ctx.c_ciphers = test_ctx.s_ciphers = "ECDHE-RSA-AES128-GCM-SHA256";
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
&ssl_s, wolfTLSv1_2_client_method,
wolfTLSv1_2_server_method), 0);
ExpectIntEQ(wolfSSL_CTX_UseSecureRenegotiation(ctx_c), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_UseSecureRenegotiation(ctx_s), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_c), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_s), WOLFSSL_SUCCESS);
/* Client opts into rejecting peer-initiated renegotiation. */
wolfSSL_set_options(ssl_c, WOLFSSL_OP_NO_RENEGOTIATION);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
/* Server asks the client to renegotiate by sending a HelloRequest, then
* waits for the ClientHello that never comes. */
ExpectIntLT(wolfSSL_Rehandshake(ssl_s), 0);
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
/* Client processes the HelloRequest. It must refuse without starting a
* renegotiation: the read returns WANT_READ (connection still alive). */
ExpectIntLT(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), 0);
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
/* The refusal was a warning-level no_renegotiation alert. */
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &history), WOLFSSL_SUCCESS);
ExpectIntEQ(history.last_tx.level, alert_warning);
ExpectIntEQ(history.last_tx.code, no_renegotiation);
/* The connection is still active and passes data: the client sends
* application data which the server receives and decrypts correctly, even
* though its renegotiation request was refused. */
ExpectIntEQ(wolfSSL_write(ssl_c, "hello", 5), 5);
for (i = 0; i < 10 && ret != 5; i++)
ret = wolfSSL_read(ssl_s, readBuf, sizeof(readBuf));
ExpectIntEQ(ret, 5);
ExpectIntEQ(XMEMCMP(readBuf, "hello", 5), 0);
wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}
/* F-2126: DoTls13ClientHello must reject a second ClientHello whose
* cipher suite does not match the server's HelloRetryRequest. The
* client offers two suites in CH1 and only a different one in CH2. */
+2
View File
@@ -27,6 +27,8 @@ int test_tls_ems_resumption_downgrade(void);
int test_tls12_chacha20_poly1305_bad_tag(void);
int test_tls13_null_cipher_bad_hmac(void);
int test_scr_verify_data_mismatch(void);
int test_scr_no_renegotiation_option(void);
int test_helloRequest_no_renegotiation_option(void);
int test_tls13_hrr_cipher_suite_mismatch(void);
int test_tls13_ticket_age_out_of_window(void);
int test_wolfSSL_DisableExtendedMasterSecret(void);