From 4b841ab1978950cfbd0955acd81a469e61af109a Mon Sep 17 00:00:00 2001 From: Nico Geyso Date: Tue, 18 Nov 2025 10:33:36 +0100 Subject: [PATCH 1/4] mbedtls_ssl_get_alert(): getter for fatal alerts Even though the TLS RFCs do not mandate libraries to expose *Error Alerts* (as defined in RFC8446 6.2 for TLS 1.3 and in RFC5246 7.2.2 for TLS 1.2) to the user, there are use cases when it is handy to get the actual last received fatal error instead of a generic one. For instance this enables the user to differ between received fatal errors in case `mbedtls_ssl_handshake()`, `mbedtls_ssl_handshake_step()` or `mbedtls_ssl_read()` returned `MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE`. This changesets stores the last incoming fatal alert in `mbedtls_ssl_context` and provides `mbedtls_ssl_get_alert()` as a getter for retrieving it. Another option would be to provide a callback mechanisms for all kinds of alerts (not only fatals) but for simplicity I discarded this option. Signed-off-by: Nico Geyso --- ChangeLog.d/alert-getter.txt | 6 ++++ include/mbedtls/ssl.h | 23 +++++++++++++++ library/ssl_msg.c | 10 +++++++ library/ssl_tls.c | 2 ++ tests/suites/test_suite_ssl.data | 3 ++ tests/suites/test_suite_ssl.function | 42 ++++++++++++++++++++++++++++ 6 files changed, 86 insertions(+) create mode 100644 ChangeLog.d/alert-getter.txt diff --git a/ChangeLog.d/alert-getter.txt b/ChangeLog.d/alert-getter.txt new file mode 100644 index 000000000000..2b6afd225d17 --- /dev/null +++ b/ChangeLog.d/alert-getter.txt @@ -0,0 +1,6 @@ +Features + * Add the function mbedtls_ssl_get_alert() which returns the + last received fatal error alert type for a more generic + MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE return value from + mbedtls_ssl_handshake(), mbedtls_ssl_handshake_step() or + mbedtls_ssl_read(). diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 02e527cdf5c7..08a18a84c5b1 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -1722,6 +1722,13 @@ struct mbedtls_ssl_context { int MBEDTLS_PRIVATE(keep_current_message); /*!< drop or reuse current message on next call to record layer? */ + unsigned char MBEDTLS_PRIVATE(in_alert_recv); /*!< Determines if a fatal alert has + been received. Values: + - \c 0 , no fatal alert received. + - \c 1 , a fatal alert has been received */ + unsigned char MBEDTLS_PRIVATE(in_alert_type); /*!< Type of fatal alert if in_alert_recv + != 0 */ + /* The following three variables indicate if and, if yes, * what kind of alert is pending to be sent. */ @@ -4918,6 +4925,22 @@ int mbedtls_ssl_write(mbedtls_ssl_context *ssl, const unsigned char *buf, size_t int mbedtls_ssl_send_alert_message(mbedtls_ssl_context *ssl, unsigned char level, unsigned char message); + +/** + * \brief Get the received fatal alert + * + * \param ssl SSL context + * + * \return The alert description type (MBEDTLS_SSL_ALERT_MSG_*) if a fatal + * alert has been received or MBEDTLS_ERR_SSL_BAD_INPUT_DATA + * + * \note This function can be used in case mbedtls_ssl_handshake(), + * mbedtls_ssl_handshake_step() or mbedtls_ssl_read() returned + * MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE to get the actual alert + * description type. + */ +int mbedtls_ssl_get_alert(mbedtls_ssl_context *ssl); + /** * \brief Notify the peer that the connection is being closed * diff --git a/library/ssl_msg.c b/library/ssl_msg.c index e1198fa62793..f584489dcecf 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -4932,6 +4932,8 @@ int mbedtls_ssl_handle_message_type(mbedtls_ssl_context *ssl) if (ssl->in_msg[0] == MBEDTLS_SSL_ALERT_LEVEL_FATAL) { MBEDTLS_SSL_DEBUG_MSG(1, ("is a fatal alert message (msg %d)", ssl->in_msg[1])); + ssl->in_alert_recv = 1; + ssl->in_alert_type = ssl->in_msg[1]; return MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE; } @@ -5016,6 +5018,14 @@ int mbedtls_ssl_send_alert_message(mbedtls_ssl_context *ssl, return 0; } +int mbedtls_ssl_get_alert(mbedtls_ssl_context *ssl) +{ + if (ssl == NULL || ssl->in_alert_recv != 1) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + return ssl->in_alert_type; +} + int mbedtls_ssl_write_change_cipher_spec(mbedtls_ssl_context *ssl) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 550f79de295a..dab7654e30d8 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -1297,6 +1297,8 @@ void mbedtls_ssl_session_reset_msg_layer(mbedtls_ssl_context *ssl, memset(ssl->in_buf, 0, in_buf_len); } + ssl->in_alert_recv = 0; + ssl->send_alert = 0; /* Reset outgoing message writing */ diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data index fa61b0f435c1..b05de38509d8 100644 --- a/tests/suites/test_suite_ssl.data +++ b/tests/suites/test_suite_ssl.data @@ -3364,3 +3364,6 @@ ssl_tls_exporter_rejects_bad_parameters:MBEDTLS_SSL_VERSION_TLS1_3:24:250:10 TLS 1.3 Keying Material Exporter: Handshake not done depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_TEST_AT_LEAST_ONE_TLS1_3_CIPHERSUITE:MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED:PSA_WANT_ALG_RSA_PKCS1V15_SIGN:MBEDTLS_X509_RSASSA_PSS_SUPPORT ssl_tls_exporter_too_early:MBEDTLS_SSL_VERSION_TLS1_3:1:MBEDTLS_SSL_SERVER_CERTIFICATE + +TLS fatal alert getter +ssl_get_alert_after_fatal diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index 5b6500898e2a..f3f3c1976a70 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -5936,3 +5936,45 @@ exit: MD_OR_USE_PSA_DONE(); } /* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ +void ssl_get_alert_after_fatal(void) +{ + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + + /* prepapre ssl context to test on*/ + mbedtls_ssl_init(&ssl); + mbedtls_ssl_config_init(&conf); + TEST_EQUAL(mbedtls_ssl_config_defaults(&conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT), 0); + + mbedtls_ssl_conf_rng(&conf, mbedtls_test_random, NULL); + MD_OR_USE_PSA_INIT(); + TEST_ASSERT(mbedtls_ssl_setup(&ssl, &conf) == 0); + + /* No alert has been received yet */ + TEST_ASSERT(mbedtls_ssl_get_alert(&ssl) == MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + + // prepare input message buffer with fatal alert + ssl.in_msglen = 2; + ssl.in_msgtype = MBEDTLS_SSL_MSG_ALERT; + ssl.in_msg[0] = MBEDTLS_SSL_ALERT_LEVEL_FATAL; + ssl.in_msg[1] = MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE; + + /* import prepared fatal alert and test getter */ + TEST_ASSERT(mbedtls_ssl_handle_message_type(&ssl) == MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE ); + TEST_ASSERT(mbedtls_ssl_get_alert(&ssl) == MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + + /* Reset the session and check that no alert is present*/ + mbedtls_ssl_session_reset_msg_layer( &ssl, 0 ); + TEST_ASSERT(mbedtls_ssl_get_alert(&ssl) == MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + +exit: + mbedtls_ssl_free(&ssl); + mbedtls_ssl_config_free(&conf); + USE_PSA_DONE(); +} +/* END_CASE */ From 09abb98770ba3b94cb1079ac40f67c6686af58a0 Mon Sep 17 00:00:00 2001 From: Nico Geyso Date: Wed, 19 Nov 2025 18:17:46 +0100 Subject: [PATCH 2/4] test ssl_get_alert_after_fatal - remove mbedtls_ssl_conf_rng Signed-off-by: Nico Geyso --- tests/suites/test_suite_ssl.function | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index f3f3c1976a70..1a0d091b288a 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -5946,13 +5946,13 @@ void ssl_get_alert_after_fatal(void) /* prepapre ssl context to test on*/ mbedtls_ssl_init(&ssl); mbedtls_ssl_config_init(&conf); + MD_OR_USE_PSA_INIT(); + TEST_EQUAL(mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT), 0); - mbedtls_ssl_conf_rng(&conf, mbedtls_test_random, NULL); - MD_OR_USE_PSA_INIT(); TEST_ASSERT(mbedtls_ssl_setup(&ssl, &conf) == 0); /* No alert has been received yet */ From a423d766334aa131ca5d294196e1109c079e4747 Mon Sep 17 00:00:00 2001 From: Nico Geyso Date: Wed, 19 Nov 2025 19:10:11 +0100 Subject: [PATCH 3/4] Fix C code style issues Signed-off-by: Nico Geyso --- include/mbedtls/ssl.h | 4 ++-- tests/suites/test_suite_ssl.function | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 08a18a84c5b1..f04a14ea94e5 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -1724,8 +1724,8 @@ struct mbedtls_ssl_context { unsigned char MBEDTLS_PRIVATE(in_alert_recv); /*!< Determines if a fatal alert has been received. Values: - - \c 0 , no fatal alert received. - - \c 1 , a fatal alert has been received */ + - \c 0 , no fatal alert received. + - \c 1 , a fatal alert has been received */ unsigned char MBEDTLS_PRIVATE(in_alert_type); /*!< Type of fatal alert if in_alert_recv != 0 */ diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index 1a0d091b288a..d4eeb760f8dc 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -5965,11 +5965,11 @@ void ssl_get_alert_after_fatal(void) ssl.in_msg[1] = MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE; /* import prepared fatal alert and test getter */ - TEST_ASSERT(mbedtls_ssl_handle_message_type(&ssl) == MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE ); + TEST_ASSERT(mbedtls_ssl_handle_message_type(&ssl) == MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE); TEST_ASSERT(mbedtls_ssl_get_alert(&ssl) == MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); /* Reset the session and check that no alert is present*/ - mbedtls_ssl_session_reset_msg_layer( &ssl, 0 ); + mbedtls_ssl_session_reset_msg_layer( &ssl, 0); TEST_ASSERT(mbedtls_ssl_get_alert(&ssl) == MBEDTLS_ERR_SSL_BAD_INPUT_DATA); exit: From 6b9f810c0ea5a51578546d23708157533e46d8a5 Mon Sep 17 00:00:00 2001 From: Nico Geyso Date: Wed, 19 Nov 2025 23:09:11 +0100 Subject: [PATCH 4/4] Fix more C code style issues Signed-off-by: Nico Geyso --- include/mbedtls/ssl.h | 4 ++-- tests/suites/test_suite_ssl.function | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index f04a14ea94e5..c9a22007c393 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -1723,11 +1723,11 @@ struct mbedtls_ssl_context { on next call to record layer? */ unsigned char MBEDTLS_PRIVATE(in_alert_recv); /*!< Determines if a fatal alert has - been received. Values: + been received. Values: - \c 0 , no fatal alert received. - \c 1 , a fatal alert has been received */ unsigned char MBEDTLS_PRIVATE(in_alert_type); /*!< Type of fatal alert if in_alert_recv - != 0 */ + != 0 */ /* The following three variables indicate if and, if yes, * what kind of alert is pending to be sent. diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index d4eeb760f8dc..d4ff7fea579a 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -5969,7 +5969,7 @@ void ssl_get_alert_after_fatal(void) TEST_ASSERT(mbedtls_ssl_get_alert(&ssl) == MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); /* Reset the session and check that no alert is present*/ - mbedtls_ssl_session_reset_msg_layer( &ssl, 0); + mbedtls_ssl_session_reset_msg_layer(&ssl, 0); TEST_ASSERT(mbedtls_ssl_get_alert(&ssl) == MBEDTLS_ERR_SSL_BAD_INPUT_DATA); exit: