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 <ng@gsmk.de>
This commit is contained in:
Nico Geyso
2025-11-18 10:33:36 +01:00
parent abb0b22954
commit d589854611
6 changed files with 86 additions and 0 deletions

View File

@@ -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().

View File

@@ -1715,6 +1715,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.
*/
@@ -4911,6 +4918,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
*

View File

@@ -4931,6 +4931,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;
}
@@ -5015,6 +5017,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;

View File

@@ -1296,6 +1296,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 */

View File

@@ -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

View File

@@ -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 */