Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ChangeLog.d/alert-getter.txt
Original file line number Diff line number Diff line change
@@ -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().
23 changes: 23 additions & 0 deletions include/mbedtls/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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
*
Expand Down
10 changes: 10 additions & 0 deletions library/ssl_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how I feel about it. The change is small to warrant a new discrete allert type for when there is no allert, but on the other side, a valid ssl context containing no allert is technically not a bad input, whereas it is a bad input for mbedtls_ssl_get_alert.

An option would be to return MBEDTLS_SSL_ALERT_MSG_CLOSE_NOTIFY when ssl->in_alert_recv != 1 but I also does not feel right error as well.

@ronald-cron-arm wdyt?

}
return ssl->in_alert_type;
}

int mbedtls_ssl_write_change_cipher_spec(mbedtls_ssl_context *ssl)
{
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
Expand Down
3 changes: 3 additions & 0 deletions library/ssl_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,9 @@ 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->in_alert_type = 0;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra whitespace here and we are missing clearing the ssl->in_alert_type

Copy link
Author

@ng-gsmk ng-gsmk Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry but I can't follow what you mean with the extra whitespace? Is there an extra whitespace in the changeset missing or one that should be removed?

Regarding the extra clearing: I followed the way how send alert was implemented (there is no clearing for the type for a reset neither) but added it now with 33bd8f8.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With regards to the whitespace I meant that instead of doing

ssl->in_alert_recv = 0;
ssl->in_alert_type = 0;

ssl->send_alert = 0;

We could have

ssl->in_alert_recv = 0;
ssl->in_alert_type = 0;
ssl->send_alert = 0;

But do not push a change just for that, wait for the second reviewer's comments.

Also same applies with rebase, I would recommend to do it after both reviewers have approved a pr, so you do not have to redo it every time the base branch has created a conflict ;)

Thanks for getting on it so quickly.

ssl->send_alert = 0;

/* Reset outgoing message writing */
Expand Down
3 changes: 3 additions & 0 deletions tests/suites/test_suite_ssl.data
Original file line number Diff line number Diff line change
Expand Up @@ -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
45 changes: 45 additions & 0 deletions tests/suites/test_suite_ssl.function
Original file line number Diff line number Diff line change
Expand Up @@ -5936,3 +5936,48 @@ 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);
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);

TEST_ASSERT(mbedtls_ssl_setup(&ssl, &conf) == 0);

/* Invalid ssl context */
TEST_ASSERT(mbedtls_ssl_get_alert(NULL) == MBEDTLS_ERR_SSL_BAD_INPUT_DATA);

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