Treat alerts as fatal errors regardless of level in TLS1.3

This commit is contained in:
Tobias Frauenschläger
2026-03-05 12:30:59 +01:00
parent 1c8d593af7
commit 11fc781d0d
3 changed files with 65 additions and 0 deletions
+15
View File
@@ -22036,6 +22036,12 @@ static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
if (level == alert_fatal) {
ssl->options.isClosed = 1; /* Don't send close_notify */
}
/* RFC 8446 Section 6.2: In TLS 1.3, all error alerts are implicitly
* fatal regardless of the AlertLevel byte. */
if (IsAtLeastTLSv1_3(ssl->version) &&
code != close_notify && code != user_canceled) {
ssl->options.isClosed = 1;
}
}
if (++ssl->options.alertCount >= WOLFSSL_ALERT_COUNT_MAX) {
@@ -23510,6 +23516,15 @@ default:
if (type == decrypt_error)
return FATAL_ERROR;
/* RFC 8446 Section 6.2: In TLS 1.3, all error alerts MUST
* be treated as fatal regardless of the AlertLevel byte.
* Only close_notify (handled above) and user_canceled
* are exempt. */
if (IsAtLeastTLSv1_3(ssl->version) &&
type != user_canceled && type != invalid_alert) {
return FATAL_ERROR;
}
/* Reset error if we got an alert level in ret */
if (ret > 0)
ret = 0;
+48
View File
@@ -3100,6 +3100,54 @@ int test_tls13_plaintext_alert(void)
return EXPECT_RESULT();
}
/* Test that TLS 1.3 warning-level alerts are treated as fatal (RFC 8446
* Section 6.2).
* A peer sending e.g. {alert_warning, handshake_failure} must still cause the
* connection to be terminated, not silently continued.
*/
int test_tls13_warning_alert_is_fatal(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT)
WOLFSSL_CTX *ctx_c = NULL;
WOLFSSL *ssl_c = NULL;
struct test_memio_ctx test_ctx;
WOLFSSL_ALERT_HISTORY h;
/* TLS record: content_type=alert(0x15), version=TLS1.2(0x0303), len=2,
* level=warning(0x01), code=handshake_failure(0x28=40) */
static const unsigned char warn_alert[] =
{ 0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x28 };
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, NULL, &ssl_c, NULL,
wolfTLSv1_3_client_method, NULL), 0);
/* Client sends ClientHello, then waits for the server response. */
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
/* Inject a warning-level handshake_failure alert as if from the server.
* RFC 8446 Section 6.2: In TLS 1.3, all error alerts MUST be treated as
* fatalregardless of the AlertLevel byte. */
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
(const char *)warn_alert, sizeof(warn_alert)), 0);
/* Expect the connection to be terminated, not silently continued. */
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WC_NO_ERR_TRACE(FATAL_ERROR));
/* The alert details should be recorded correctly. */
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
ExpectIntEQ(h.last_rx.code, handshake_failure);
ExpectIntEQ(h.last_rx.level, alert_warning);
wolfSSL_free(ssl_c);
wolfSSL_CTX_free(ctx_c);
#endif
return EXPECT_RESULT();
}
/* Test that wolfSSL_set1_sigalgs_list() is honored in TLS 1.3
*/
int test_tls13_cert_req_sigalgs(void)
+2
View File
@@ -38,6 +38,7 @@ int test_tls13_duplicate_extension(void);
int test_key_share_mismatch(void);
int test_tls13_middlebox_compat_empty_session_id(void);
int test_tls13_plaintext_alert(void);
int test_tls13_warning_alert_is_fatal(void);
int test_tls13_cert_req_sigalgs(void);
#define TEST_TLS13_DECLS \
@@ -55,6 +56,7 @@ int test_tls13_cert_req_sigalgs(void);
TEST_DECL_GROUP("tls13", test_key_share_mismatch), \
TEST_DECL_GROUP("tls13", test_tls13_middlebox_compat_empty_session_id), \
TEST_DECL_GROUP("tls13", test_tls13_plaintext_alert), \
TEST_DECL_GROUP("tls13", test_tls13_warning_alert_is_fatal), \
TEST_DECL_GROUP("tls13", test_tls13_cert_req_sigalgs)
#endif /* WOLFCRYPT_TEST_TLS13_H */