From eac2e4daef9515614c273a2f2f7ea7fb4ea67538 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 13 Feb 2025 21:23:22 +0100 Subject: [PATCH 01/30] Automate MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK dependency Signed-off-by: Gilles Peskine --- tests/ssl-opt.sh | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 4bb9d1a2c8..784391eb36 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -372,6 +372,11 @@ detect_required_features() { requires_config_enabled MBEDTLS_SSL_FALLBACK_SCSV;; esac + case " $1 " in + *\ ca_callback=1\ *) + requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK;; + esac + unset tmp } @@ -1646,7 +1651,6 @@ run_test "Default (compression enabled)" \ -S "error" \ -C "error" -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "CA callback on client" \ "$P_SRV debug_level=3" \ "$P_CLI ca_callback=1 debug_level=3 " \ @@ -1655,7 +1659,6 @@ run_test "CA callback on client" \ -S "error" \ -C "error" -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK requires_config_enabled MBEDTLS_X509_CRT_PARSE_C requires_config_enabled MBEDTLS_ECDSA_C requires_config_enabled MBEDTLS_SHA256_C @@ -4999,7 +5002,6 @@ run_test "Authentication: send CA list in CertificateRequest, client self sig # Tests for auth_mode, using CA callback, these are duplicated from the authentication tests # When updating these tests, modify the matching authentication tests accordingly -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "Authentication, CA callback: server badcert, client required" \ "$P_SRV crt_file=data_files/server5-badsign.crt \ key_file=data_files/server5.key" \ @@ -5011,7 +5013,6 @@ run_test "Authentication, CA callback: server badcert, client required" \ -c "! mbedtls_ssl_handshake returned" \ -c "X509 - Certificate verification failed" -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "Authentication, CA callback: server badcert, client optional" \ "$P_SRV crt_file=data_files/server5-badsign.crt \ key_file=data_files/server5.key" \ @@ -5031,7 +5032,6 @@ run_test "Authentication, CA callback: server badcert, client optional" \ # different means to have the server ignoring the client's supported curve list. requires_config_enabled MBEDTLS_ECP_C -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "Authentication, CA callback: server ECDH p256v1, client required, p256v1 unsupported" \ "$P_SRV debug_level=1 key_file=data_files/server5.key \ crt_file=data_files/server5.ku-ka.crt" \ @@ -5043,7 +5043,6 @@ run_test "Authentication, CA callback: server ECDH p256v1, client required, p -C "bad server certificate (ECDH curve)" # Expect failure at earlier verification stage requires_config_enabled MBEDTLS_ECP_C -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "Authentication, CA callback: server ECDH p256v1, client optional, p256v1 unsupported" \ "$P_SRV debug_level=1 key_file=data_files/server5.key \ crt_file=data_files/server5.ku-ka.crt" \ @@ -5054,7 +5053,6 @@ run_test "Authentication, CA callback: server ECDH p256v1, client optional, p -c "! Certificate verification flags"\ -c "bad server certificate (ECDH curve)" # Expect failure only at ECDH params check -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "Authentication, CA callback: client SHA256, server required" \ "$P_SRV ca_callback=1 debug_level=3 auth_mode=required" \ "$P_CLI debug_level=3 crt_file=data_files/server6.crt \ @@ -5065,7 +5063,6 @@ run_test "Authentication, CA callback: client SHA256, server required" \ -c "Supported Signature Algorithm found: 4," \ -c "Supported Signature Algorithm found: 5," -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "Authentication, CA callback: client SHA384, server required" \ "$P_SRV ca_callback=1 debug_level=3 auth_mode=required" \ "$P_CLI debug_level=3 crt_file=data_files/server6.crt \ @@ -5076,7 +5073,6 @@ run_test "Authentication, CA callback: client SHA384, server required" \ -c "Supported Signature Algorithm found: 4," \ -c "Supported Signature Algorithm found: 5," -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "Authentication, CA callback: client badcert, server required" \ "$P_SRV ca_callback=1 debug_level=3 auth_mode=required" \ "$P_CLI debug_level=3 crt_file=data_files/server5-badsign.crt \ @@ -5099,7 +5095,6 @@ run_test "Authentication, CA callback: client badcert, server required" \ # detect that its write end of the connection is closed and abort # before reading the alert message. -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "Authentication, CA callback: client cert not trusted, server required" \ "$P_SRV ca_callback=1 debug_level=3 auth_mode=required" \ "$P_CLI debug_level=3 crt_file=data_files/server5-selfsigned.crt \ @@ -5118,7 +5113,6 @@ run_test "Authentication, CA callback: client cert not trusted, server requir -c "! mbedtls_ssl_handshake returned" \ -s "X509 - Certificate verification failed" -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "Authentication, CA callback: client badcert, server optional" \ "$P_SRV ca_callback=1 debug_level=3 auth_mode=optional" \ "$P_CLI debug_level=3 crt_file=data_files/server5-badsign.crt \ @@ -5139,7 +5133,6 @@ run_test "Authentication, CA callback: client badcert, server optional" \ requires_config_value_equals "MBEDTLS_X509_MAX_INTERMEDIATE_CA" $MAX_IM_CA requires_full_size_output_buffer -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "Authentication, CA callback: server max_int chain, client default" \ "$P_SRV crt_file=data_files/dir-maxpath/c09.pem \ key_file=data_files/dir-maxpath/09.key" \ @@ -5150,7 +5143,6 @@ run_test "Authentication, CA callback: server max_int chain, client default" requires_config_value_equals "MBEDTLS_X509_MAX_INTERMEDIATE_CA" $MAX_IM_CA requires_full_size_output_buffer -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "Authentication, CA callback: server max_int+1 chain, client default" \ "$P_SRV crt_file=data_files/dir-maxpath/c10.pem \ key_file=data_files/dir-maxpath/10.key" \ @@ -5161,7 +5153,6 @@ run_test "Authentication, CA callback: server max_int+1 chain, client default requires_config_value_equals "MBEDTLS_X509_MAX_INTERMEDIATE_CA" $MAX_IM_CA requires_full_size_output_buffer -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "Authentication, CA callback: server max_int+1 chain, client optional" \ "$P_SRV crt_file=data_files/dir-maxpath/c10.pem \ key_file=data_files/dir-maxpath/10.key" \ @@ -5173,7 +5164,6 @@ run_test "Authentication, CA callback: server max_int+1 chain, client optiona requires_config_value_equals "MBEDTLS_X509_MAX_INTERMEDIATE_CA" $MAX_IM_CA requires_full_size_output_buffer -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "Authentication, CA callback: client max_int+1 chain, server optional" \ "$P_SRV ca_callback=1 debug_level=3 ca_file=data_files/dir-maxpath/00.crt auth_mode=optional" \ "$P_CLI crt_file=data_files/dir-maxpath/c10.pem \ @@ -5184,7 +5174,6 @@ run_test "Authentication, CA callback: client max_int+1 chain, server optiona requires_config_value_equals "MBEDTLS_X509_MAX_INTERMEDIATE_CA" $MAX_IM_CA requires_full_size_output_buffer -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "Authentication, CA callback: client max_int+1 chain, server required" \ "$P_SRV ca_callback=1 debug_level=3 ca_file=data_files/dir-maxpath/00.crt auth_mode=required" \ "$P_CLI crt_file=data_files/dir-maxpath/c10.pem \ @@ -5195,7 +5184,6 @@ run_test "Authentication, CA callback: client max_int+1 chain, server require requires_config_value_equals "MBEDTLS_X509_MAX_INTERMEDIATE_CA" $MAX_IM_CA requires_full_size_output_buffer -requires_config_enabled MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK run_test "Authentication, CA callback: client max_int chain, server required" \ "$P_SRV ca_callback=1 debug_level=3 ca_file=data_files/dir-maxpath/00.crt auth_mode=required" \ "$P_CLI crt_file=data_files/dir-maxpath/c09.pem \ From 724a7bbd92b7972d96e4da66f1c5e2e290d73d0a Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 12 Feb 2025 21:50:53 +0100 Subject: [PATCH 02/30] mbedtls_ssl_set_hostname tests: baseline Test the current behavior. Signed-off-by: Gilles Peskine --- programs/ssl/ssl_client2.c | 39 +++++++++-- tests/ssl-opt.sh | 139 +++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+), 4 deletions(-) diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index d01202fb68..7e6b052163 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -64,6 +64,7 @@ int main(void) #define DFL_ARC4 -1 #define DFL_SHA1 -1 #define DFL_AUTH_MODE -1 +#define DFL_SET_HOSTNAME 1 #define DFL_MFL_CODE MBEDTLS_SSL_MAX_FRAG_LEN_NONE #define DFL_TRUNC_HMAC -1 #define DFL_RECSPLIT -1 @@ -380,6 +381,9 @@ int main(void) #define USAGE2 \ " auth_mode=%%s default: (library default: none)\n" \ " options: none, optional, required\n" \ + " set_hostname=%%s call mbedtls_ssl_set_hostname()?" \ + " options: no, server_name, NULL\n" \ + " default: server_name (but ignored if certs disabled)\n" \ USAGE_IO \ USAGE_KEY_OPAQUE \ USAGE_CA_CALLBACK \ @@ -473,6 +477,8 @@ struct options { int arc4; /* flag for arc4 suites support */ int allow_sha1; /* flag for SHA-1 support */ int auth_mode; /* verify mode for connection */ + int set_hostname; /* call mbedtls_ssl_set_hostname()? */ + /* 0=no, 1=yes, -1=NULL */ unsigned char mfl_code; /* code for maximum fragment length */ int trunc_hmac; /* negotiate truncated hmac or not */ int recsplit; /* enable record splitting? */ @@ -796,6 +802,7 @@ int main(int argc, char *argv[]) opt.arc4 = DFL_ARC4; opt.allow_sha1 = DFL_SHA1; opt.auth_mode = DFL_AUTH_MODE; + opt.set_hostname = DFL_SET_HOSTNAME; opt.mfl_code = DFL_MFL_CODE; opt.trunc_hmac = DFL_TRUNC_HMAC; opt.recsplit = DFL_RECSPLIT; @@ -1148,6 +1155,16 @@ usage: } else { goto usage; } + } else if (strcmp(p, "set_hostname") == 0) { + if (strcmp(q, "no") == 0) { + opt.set_hostname = 0; + } else if (strcmp(q, "server_name") == 0) { + opt.set_hostname = 1; + } else if (strcmp(q, "NULL") == 0) { + opt.set_hostname = -1; + } else { + goto usage; + } } else if (strcmp(p, "max_frag_len") == 0) { if (strcmp(q, "512") == 0) { opt.mfl_code = MBEDTLS_SSL_MAX_FRAG_LEN_512; @@ -1893,10 +1910,24 @@ usage: } #if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) - if ((ret = mbedtls_ssl_set_hostname(&ssl, opt.server_name)) != 0) { - mbedtls_printf(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", - ret); - goto exit; + switch (opt.set_hostname) { + case -1: + if ((ret = mbedtls_ssl_set_hostname(&ssl, NULL)) != 0) { + mbedtls_printf(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", + ret); + goto exit; + } + break; + case 0: + /* Skip the call */ + break; + default: + if ((ret = mbedtls_ssl_set_hostname(&ssl, opt.server_name)) != 0) { + mbedtls_printf(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", + ret); + goto exit; + } + break; } #endif diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 784391eb36..0d25ab4e18 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -4650,6 +4650,145 @@ run_test "Authentication: server goodcert, client required, no trusted CA" \ -c "! mbedtls_ssl_handshake returned" \ -c "SSL - No CA Chain is set, but required to operate" +# The next few tests check what happens if the server has a valid certificate +# that does not match its name (impersonation). + +run_test "Authentication: hostname match, client required" \ + "$P_SRV" \ + "$P_CLI auth_mode=required server_name=localhost debug_level=1" \ + 0 \ + -C "does not match with the expected CN" \ + -C "x509_verify_cert() returned -" \ + -C "! mbedtls_ssl_handshake returned" \ + -C "X509 - Certificate verification failed" + +run_test "Authentication: hostname mismatch (wrong), client required" \ + "$P_SRV" \ + "$P_CLI auth_mode=required server_name=wrong-name debug_level=1" \ + 1 \ + -c "does not match with the expected CN" \ + -c "x509_verify_cert() returned -" \ + -c "! mbedtls_ssl_handshake returned" \ + -c "X509 - Certificate verification failed" + +run_test "Authentication: hostname mismatch (empty), client required" \ + "$P_SRV" \ + "$P_CLI auth_mode=required server_name= debug_level=1" \ + 1 \ + -c "does not match with the expected CN" \ + -c "x509_verify_cert() returned -" \ + -c "! mbedtls_ssl_handshake returned" \ + -c "X509 - Certificate verification failed" + +run_test "Authentication: hostname mismatch (truncated), client required" \ + "$P_SRV" \ + "$P_CLI auth_mode=required server_name=localhos debug_level=1" \ + 1 \ + -c "does not match with the expected CN" \ + -c "x509_verify_cert() returned -" \ + -c "! mbedtls_ssl_handshake returned" \ + -c "X509 - Certificate verification failed" + +run_test "Authentication: hostname mismatch (last char), client required" \ + "$P_SRV" \ + "$P_CLI auth_mode=required server_name=localhoss debug_level=1" \ + 1 \ + -c "does not match with the expected CN" \ + -c "x509_verify_cert() returned -" \ + -c "! mbedtls_ssl_handshake returned" \ + -c "X509 - Certificate verification failed" + +run_test "Authentication: hostname mismatch (trailing), client required" \ + "$P_SRV" \ + "$P_CLI auth_mode=required server_name=localhostt debug_level=1" \ + 1 \ + -c "does not match with the expected CN" \ + -c "x509_verify_cert() returned -" \ + -c "! mbedtls_ssl_handshake returned" \ + -c "X509 - Certificate verification failed" + +run_test "Authentication: hostname mismatch, client optional" \ + "$P_SRV" \ + "$P_CLI auth_mode=optional server_name=wrong-name debug_level=1" \ + 0 \ + -c "does not match with the expected CN" \ + -c "x509_verify_cert() returned -" \ + -C "X509 - Certificate verification failed" + +run_test "Authentication: hostname mismatch, client none" \ + "$P_SRV" \ + "$P_CLI auth_mode=none server_name=wrong-name debug_level=1" \ + 0 \ + -C "does not match with the expected CN" \ + -C "x509_verify_cert() returned -" \ + -C "X509 - Certificate verification failed" + +run_test "Authentication: hostname null, client required" \ + "$P_SRV" \ + "$P_CLI auth_mode=required set_hostname=NULL debug_level=1" \ + 0 \ + -C "does not match with the expected CN" \ + -C "x509_verify_cert() returned -" \ + -C "! mbedtls_ssl_handshake returned" \ + -C "X509 - Certificate verification failed" + +run_test "Authentication: hostname null, client optional" \ + "$P_SRV" \ + "$P_CLI auth_mode=optional set_hostname=NULL debug_level=1" \ + 0 \ + -C "does not match with the expected CN" \ + -C "x509_verify_cert() returned -" \ + -C "X509 - Certificate verification failed" + +run_test "Authentication: hostname null, client none" \ + "$P_SRV" \ + "$P_CLI auth_mode=none set_hostname=NULL debug_level=1" \ + 0 \ + -C "does not match with the expected CN" \ + -C "x509_verify_cert() returned -" \ + -C "X509 - Certificate verification failed" + +run_test "Authentication: hostname unset, client required" \ + "$P_SRV" \ + "$P_CLI auth_mode=required set_hostname=no debug_level=1" \ + 0 \ + -C "does not match with the expected CN" \ + -C "x509_verify_cert() returned -" \ + -C "! mbedtls_ssl_handshake returned" \ + -C "X509 - Certificate verification failed" + +run_test "Authentication: hostname unset, client optional" \ + "$P_SRV" \ + "$P_CLI auth_mode=optional set_hostname=no debug_level=1" \ + 0 \ + -C "does not match with the expected CN" \ + -C "x509_verify_cert() returned -" \ + -C "X509 - Certificate verification failed" + +run_test "Authentication: hostname unset, client none" \ + "$P_SRV" \ + "$P_CLI auth_mode=none set_hostname=no debug_level=1" \ + 0 \ + -C "does not match with the expected CN" \ + -C "x509_verify_cert() returned -" \ + -C "X509 - Certificate verification failed" + +run_test "Authentication: hostname unset, client default, server picks cert" \ + "$P_SRV force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-CCM-8" \ + "$P_CLI psk=73776f726466697368 psk_identity=foo set_hostname=no debug_level=1" \ + 0 \ + -C "does not match with the expected CN" \ + -C "x509_verify_cert() returned -" \ + -C "X509 - Certificate verification failed" + +run_test "Authentication: hostname unset, client default, server picks PSK" \ + "$P_SRV force_ciphersuite=TLS-PSK-WITH-AES-128-CCM-8 psk=73776f726466697368 psk_identity=foo" \ + "$P_CLI psk=73776f726466697368 psk_identity=foo set_hostname=no debug_level=1" \ + 0 \ + -C "does not match with the expected CN" \ + -C "x509_verify_cert() returned -" \ + -C "X509 - Certificate verification failed" + # The purpose of the next two tests is to test the client's behaviour when receiving a server # certificate with an unsupported elliptic curve. This should usually not happen because # the client informs the server about the supported curves - it does, though, in the From ff25715cb4bcc3c787d42eadc0fc2466c008be4f Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 20 Feb 2025 13:57:51 +0100 Subject: [PATCH 03/30] Move mbedtls_ssl_set_hostname up Refactoring to prepare for "Access ssl->hostname through abstractions". Signed-off-by: Gilles Peskine --- library/ssl_tls.c | 86 +++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 6149434104..c3e005b188 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -38,6 +38,49 @@ #include "mbedtls/oid.h" #endif +#if defined(MBEDTLS_X509_CRT_PARSE_C) +int mbedtls_ssl_set_hostname(mbedtls_ssl_context *ssl, const char *hostname) +{ + /* Initialize to suppress unnecessary compiler warning */ + size_t hostname_len = 0; + + /* Check if new hostname is valid before + * making any change to current one */ + if (hostname != NULL) { + hostname_len = strlen(hostname); + + if (hostname_len > MBEDTLS_SSL_MAX_HOST_NAME_LEN) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + } + + /* Now it's clear that we will overwrite the old hostname, + * so we can free it safely */ + + if (ssl->hostname != NULL) { + mbedtls_platform_zeroize(ssl->hostname, strlen(ssl->hostname)); + mbedtls_free(ssl->hostname); + } + + /* Passing NULL as hostname shall clear the old one */ + + if (hostname == NULL) { + ssl->hostname = NULL; + } else { + ssl->hostname = mbedtls_calloc(1, hostname_len + 1); + if (ssl->hostname == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + memcpy(ssl->hostname, hostname, hostname_len); + + ssl->hostname[hostname_len] = '\0'; + } + + return 0; +} +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + #if defined(MBEDTLS_SSL_PROTO_DTLS) #if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) @@ -4617,49 +4660,6 @@ void mbedtls_ssl_conf_curves(mbedtls_ssl_config *conf, } #endif /* MBEDTLS_ECP_C */ -#if defined(MBEDTLS_X509_CRT_PARSE_C) -int mbedtls_ssl_set_hostname(mbedtls_ssl_context *ssl, const char *hostname) -{ - /* Initialize to suppress unnecessary compiler warning */ - size_t hostname_len = 0; - - /* Check if new hostname is valid before - * making any change to current one */ - if (hostname != NULL) { - hostname_len = strlen(hostname); - - if (hostname_len > MBEDTLS_SSL_MAX_HOST_NAME_LEN) { - return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; - } - } - - /* Now it's clear that we will overwrite the old hostname, - * so we can free it safely */ - - if (ssl->hostname != NULL) { - mbedtls_platform_zeroize(ssl->hostname, strlen(ssl->hostname)); - mbedtls_free(ssl->hostname); - } - - /* Passing NULL as hostname shall clear the old one */ - - if (hostname == NULL) { - ssl->hostname = NULL; - } else { - ssl->hostname = mbedtls_calloc(1, hostname_len + 1); - if (ssl->hostname == NULL) { - return MBEDTLS_ERR_SSL_ALLOC_FAILED; - } - - memcpy(ssl->hostname, hostname, hostname_len); - - ssl->hostname[hostname_len] = '\0'; - } - - return 0; -} -#endif /* MBEDTLS_X509_CRT_PARSE_C */ - #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) void mbedtls_ssl_conf_sni(mbedtls_ssl_config *conf, int (*f_sni)(void *, mbedtls_ssl_context *, From 3a2f75d8d59bc0c3f5a15bc13efa22fef0b8c55e Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 12 Feb 2025 23:28:48 +0100 Subject: [PATCH 04/30] Access ssl->hostname through abstractions New abstractions to access ssl->hostname: mbedtls_ssl_has_set_hostname_been_called() (only implemented approximatively for now), mbedtls_ssl_get_hostname_pointer(), mbedtls_ssl_free_hostname(). Only access ssl->hostname directly in these functions and in mbedtls_ssl_set_hostname(). Use these abstractions to access the hostname with the opportunity for extra checks in mbedtls_ssl_verify_certificate(). No behavior change except for a new log message. Signed-off-by: Gilles Peskine --- include/mbedtls/ssl_internal.h | 12 ++++++ library/ssl_cli.c | 9 ++-- library/ssl_tls.c | 79 ++++++++++++++++++++++++++++------ 3 files changed, 84 insertions(+), 16 deletions(-) diff --git a/include/mbedtls/ssl_internal.h b/include/mbedtls/ssl_internal.h index 3a40b4ba2f..fdc1719c5a 100644 --- a/include/mbedtls/ssl_internal.h +++ b/include/mbedtls/ssl_internal.h @@ -1214,6 +1214,18 @@ static inline size_t mbedtls_ssl_hs_hdr_len(const mbedtls_ssl_context *ssl) return 4; } +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) +/** Get the host name from the SSL context. + * + * \param[in] ssl SSL context + * + * \return The \p hostname pointer from the SSL context. + * \c NULL if mbedtls_ssl_set_hostname() has never been called on + * \p ssl or if it was last called with \p NULL. + */ +const char *mbedtls_ssl_get_hostname_pointer(const mbedtls_ssl_context *ssl); +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + #if defined(MBEDTLS_SSL_PROTO_DTLS) void mbedtls_ssl_send_flight_completed(mbedtls_ssl_context *ssl); void mbedtls_ssl_recv_flight_completed(mbedtls_ssl_context *ssl); diff --git a/library/ssl_cli.c b/library/ssl_cli.c index 4fde783d3e..2854e00fcb 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -83,19 +83,20 @@ static int ssl_write_hostname_ext(mbedtls_ssl_context *ssl, size_t *olen) { unsigned char *p = buf; + const char *hostname = mbedtls_ssl_get_hostname_pointer(ssl); size_t hostname_len; *olen = 0; - if (ssl->hostname == NULL) { + if (hostname == NULL) { return 0; } MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, adding server name extension: %s", - ssl->hostname)); + hostname)); - hostname_len = strlen(ssl->hostname); + hostname_len = strlen(hostname); MBEDTLS_SSL_CHK_BUF_PTR(p, end, hostname_len + 9); @@ -139,7 +140,7 @@ static int ssl_write_hostname_ext(mbedtls_ssl_context *ssl, MBEDTLS_PUT_UINT16_BE(hostname_len, p, 0); p += 2; - memcpy(p, ssl->hostname, hostname_len); + memcpy(p, hostname, hostname_len); *olen = hostname_len + 9; diff --git a/library/ssl_tls.c b/library/ssl_tls.c index c3e005b188..01acd1dbc6 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -39,6 +39,47 @@ #endif #if defined(MBEDTLS_X509_CRT_PARSE_C) + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) +/** Whether mbedtls_ssl_set_hostname() has been called. + * + * \param[in] ssl SSL context + * + * \return \c 1 if mbedtls_ssl_set_hostname() has been called on \p ssl + * (including `mbedtls_ssl_set_hostname(ssl, NULL)`), + * otherwise \c 0. + */ +static int mbedtls_ssl_has_set_hostname_been_called( + const mbedtls_ssl_context *ssl) +{ + /* We can't tell the difference between the case where + * mbedtls_ssl_set_hostname() has not been called at all, and + * the case where it was last called with NULL. For the time + * being, we assume the latter, i.e. we behave as if there had + * been an implicit call to mbedtls_ssl_set_hostname(ssl, NULL). */ + return ssl->hostname != NULL; +} +#endif + +/* Micro-optimization: don't export this function if it isn't needed outside + * of this source file. */ +#if !defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) +static +#endif +const char *mbedtls_ssl_get_hostname_pointer(const mbedtls_ssl_context *ssl) +{ + return ssl->hostname; +} + +static void mbedtls_ssl_free_hostname(mbedtls_ssl_context *ssl) +{ + if (ssl->hostname != NULL) { + mbedtls_platform_zeroize(ssl->hostname, strlen(ssl->hostname)); + mbedtls_free(ssl->hostname); + } + ssl->hostname = NULL; +} + int mbedtls_ssl_set_hostname(mbedtls_ssl_context *ssl, const char *hostname) { /* Initialize to suppress unnecessary compiler warning */ @@ -56,11 +97,7 @@ int mbedtls_ssl_set_hostname(mbedtls_ssl_context *ssl, const char *hostname) /* Now it's clear that we will overwrite the old hostname, * so we can free it safely */ - - if (ssl->hostname != NULL) { - mbedtls_platform_zeroize(ssl->hostname, strlen(ssl->hostname)); - mbedtls_free(ssl->hostname); - } + mbedtls_ssl_free_hostname(ssl); /* Passing NULL as hostname shall clear the old one */ @@ -2564,13 +2601,27 @@ static int ssl_parse_certificate_coordinate(mbedtls_ssl_context *ssl, return SSL_CERTIFICATE_EXPECTED; } +static int get_hostname_for_verification(mbedtls_ssl_context *ssl, + const char **hostname) +{ + if (!mbedtls_ssl_has_set_hostname_been_called(ssl)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Certificate verification without having set hostname")); + } + + *hostname = mbedtls_ssl_get_hostname_pointer(ssl); + if (*hostname == NULL) { + MBEDTLS_SSL_DEBUG_MSG(2, ("Certificate verification without CN verification")); + } + + return 0; +} + MBEDTLS_CHECK_RETURN_CRITICAL static int ssl_parse_certificate_verify(mbedtls_ssl_context *ssl, int authmode, mbedtls_x509_crt *chain, void *rs_ctx) { - int ret = 0; const mbedtls_ssl_ciphersuite_t *ciphersuite_info = ssl->handshake->ciphersuite_info; int have_ca_chain = 0; @@ -2592,6 +2643,13 @@ static int ssl_parse_certificate_verify(mbedtls_ssl_context *ssl, p_vrfy = ssl->conf->p_vrfy; } + const char *hostname = ""; + int ret = get_hostname_for_verification(ssl, &hostname); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "get_hostname_for_verification", ret); + return ret; + } + /* * Main check: verify certificate */ @@ -2606,7 +2664,7 @@ static int ssl_parse_certificate_verify(mbedtls_ssl_context *ssl, ssl->conf->f_ca_cb, ssl->conf->p_ca_cb, ssl->conf->cert_profile, - ssl->hostname, + hostname, &ssl->session_negotiate->verify_result, f_vrfy, p_vrfy); } else @@ -2634,7 +2692,7 @@ static int ssl_parse_certificate_verify(mbedtls_ssl_context *ssl, chain, ca_chain, ca_crl, ssl->conf->cert_profile, - ssl->hostname, + hostname, &ssl->session_negotiate->verify_result, f_vrfy, p_vrfy, rs_ctx); } @@ -6816,10 +6874,7 @@ void mbedtls_ssl_free(mbedtls_ssl_context *ssl) } #if defined(MBEDTLS_X509_CRT_PARSE_C) - if (ssl->hostname != NULL) { - mbedtls_platform_zeroize(ssl->hostname, strlen(ssl->hostname)); - mbedtls_free(ssl->hostname); - } + mbedtls_ssl_free_hostname(ssl); #endif #if defined(MBEDTLS_SSL_HW_RECORD_ACCEL) From 520ceb27266ab021b1689183b5bc81f0088c220c Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 12 Feb 2025 23:35:54 +0100 Subject: [PATCH 05/30] Update the documentation of ssl->hostname Signed-off-by: Gilles Peskine --- include/mbedtls/ssl.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 9cdf3a3ebb..a18edbbca0 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -1403,8 +1403,14 @@ struct mbedtls_ssl_context { * User settings */ #if defined(MBEDTLS_X509_CRT_PARSE_C) - char *hostname; /*!< expected peer CN for verification - (and SNI if available) */ + /** Expected peer CN for verification. + * + * Also used on clients for SNI. + * + * If this is \p NULL, the peer name verification is skipped, and + * the server_name extension is not sent. + */ + char *hostname; #endif /* MBEDTLS_X509_CRT_PARSE_C */ #if defined(MBEDTLS_SSL_ALPN) From f33c45f84d52c49d985063e41f15998e84133e90 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 12 Feb 2025 23:53:25 +0100 Subject: [PATCH 06/30] Keep track of whether mbedtls_ssl_set_hostname() has been called Use a special marker as ssl->hostname if mbedtls_ssl_set_hostname() has been called with NULL. If mbedtls_ssl_set_hostname() has never been called, the field is NULL, as before. No behavior change apart from now emitting a different log message depending on whether mbedtls_ssl_set_hostname() has been called with NULL or not at all. Signed-off-by: Gilles Peskine --- include/mbedtls/ssl.h | 4 ++++ library/ssl_tls.c | 29 ++++++++++++++++++++--------- tests/ssl-opt.sh | 42 +++++++++++++++++++++++++++++++----------- 3 files changed, 55 insertions(+), 20 deletions(-) diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index a18edbbca0..447a293535 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -1409,6 +1409,10 @@ struct mbedtls_ssl_context { * * If this is \p NULL, the peer name verification is skipped, and * the server_name extension is not sent. + * + * This can be a special value to indicate that mbedtls_ssl_set_hostname() + * has been called with \p NULL, as opposed to never having been called. + * See mbedtls_ssl_get_hostname_pointer(). */ char *hostname; #endif /* MBEDTLS_X509_CRT_PARSE_C */ diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 01acd1dbc6..7526a1e96a 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -40,6 +40,12 @@ #if defined(MBEDTLS_X509_CRT_PARSE_C) +/* A magic value for `ssl->hostname` indicating that + * mbedtls_ssl_set_hostname() has been called with `NULL`. + * If mbedtls_ssl_set_hostname() has never been called on `ssl`, then + * `ssl->hostname == NULL`. */ +static const char *const ssl_hostname_skip_cn_verification = ""; + #if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) /** Whether mbedtls_ssl_set_hostname() has been called. * @@ -52,11 +58,6 @@ static int mbedtls_ssl_has_set_hostname_been_called( const mbedtls_ssl_context *ssl) { - /* We can't tell the difference between the case where - * mbedtls_ssl_set_hostname() has not been called at all, and - * the case where it was last called with NULL. For the time - * being, we assume the latter, i.e. we behave as if there had - * been an implicit call to mbedtls_ssl_set_hostname(ssl, NULL). */ return ssl->hostname != NULL; } #endif @@ -68,12 +69,16 @@ static #endif const char *mbedtls_ssl_get_hostname_pointer(const mbedtls_ssl_context *ssl) { + if (ssl->hostname == ssl_hostname_skip_cn_verification) { + return NULL; + } return ssl->hostname; } static void mbedtls_ssl_free_hostname(mbedtls_ssl_context *ssl) { - if (ssl->hostname != NULL) { + if (ssl->hostname != NULL && + ssl->hostname != ssl_hostname_skip_cn_verification) { mbedtls_platform_zeroize(ssl->hostname, strlen(ssl->hostname)); mbedtls_free(ssl->hostname); } @@ -99,13 +104,19 @@ int mbedtls_ssl_set_hostname(mbedtls_ssl_context *ssl, const char *hostname) * so we can free it safely */ mbedtls_ssl_free_hostname(ssl); - /* Passing NULL as hostname shall clear the old one */ - if (hostname == NULL) { - ssl->hostname = NULL; + /* Passing NULL as hostname clears the old one, but leaves a + * special marker to indicate that mbedtls_ssl_set_hostname() + * has been called. */ + /* ssl->hostname should be const, but isn't. We won't actually + * write to the buffer, so it's ok to cast away the const. */ + ssl->hostname = (char *) ssl_hostname_skip_cn_verification; } else { ssl->hostname = mbedtls_calloc(1, hostname_len + 1); if (ssl->hostname == NULL) { + /* mbedtls_ssl_set_hostname() has been called, but unsuccessfully. + * Leave ssl->hostname in the same state as if the function had + * not been called, i.e. a null pointer. */ return MBEDTLS_ERR_SSL_ALLOC_FAILED; } diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 0d25ab4e18..576bc0710b 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -4655,9 +4655,11 @@ run_test "Authentication: server goodcert, client required, no trusted CA" \ run_test "Authentication: hostname match, client required" \ "$P_SRV" \ - "$P_CLI auth_mode=required server_name=localhost debug_level=1" \ + "$P_CLI auth_mode=required server_name=localhost debug_level=2" \ 0 \ -C "does not match with the expected CN" \ + -C "Certificate verification without having set hostname" \ + -C "Certificate verification without CN verification" \ -C "x509_verify_cert() returned -" \ -C "! mbedtls_ssl_handshake returned" \ -C "X509 - Certificate verification failed" @@ -4709,7 +4711,7 @@ run_test "Authentication: hostname mismatch (trailing), client required" \ run_test "Authentication: hostname mismatch, client optional" \ "$P_SRV" \ - "$P_CLI auth_mode=optional server_name=wrong-name debug_level=1" \ + "$P_CLI auth_mode=optional server_name=wrong-name debug_level=2" \ 0 \ -c "does not match with the expected CN" \ -c "x509_verify_cert() returned -" \ @@ -4717,75 +4719,93 @@ run_test "Authentication: hostname mismatch, client optional" \ run_test "Authentication: hostname mismatch, client none" \ "$P_SRV" \ - "$P_CLI auth_mode=none server_name=wrong-name debug_level=1" \ + "$P_CLI auth_mode=none server_name=wrong-name debug_level=2" \ 0 \ -C "does not match with the expected CN" \ + -C "Certificate verification without having set hostname" \ + -C "Certificate verification without CN verification" \ -C "x509_verify_cert() returned -" \ -C "X509 - Certificate verification failed" run_test "Authentication: hostname null, client required" \ "$P_SRV" \ - "$P_CLI auth_mode=required set_hostname=NULL debug_level=1" \ + "$P_CLI auth_mode=required set_hostname=NULL debug_level=2" \ 0 \ -C "does not match with the expected CN" \ + -C "Certificate verification without having set hostname" \ + -c "Certificate verification without CN verification" \ -C "x509_verify_cert() returned -" \ -C "! mbedtls_ssl_handshake returned" \ -C "X509 - Certificate verification failed" run_test "Authentication: hostname null, client optional" \ "$P_SRV" \ - "$P_CLI auth_mode=optional set_hostname=NULL debug_level=1" \ + "$P_CLI auth_mode=optional set_hostname=NULL debug_level=2" \ 0 \ -C "does not match with the expected CN" \ + -C "Certificate verification without having set hostname" \ + -c "Certificate verification without CN verification" \ -C "x509_verify_cert() returned -" \ -C "X509 - Certificate verification failed" run_test "Authentication: hostname null, client none" \ "$P_SRV" \ - "$P_CLI auth_mode=none set_hostname=NULL debug_level=1" \ + "$P_CLI auth_mode=none set_hostname=NULL debug_level=2" \ 0 \ -C "does not match with the expected CN" \ + -C "Certificate verification without having set hostname" \ + -C "Certificate verification without CN verification" \ -C "x509_verify_cert() returned -" \ -C "X509 - Certificate verification failed" run_test "Authentication: hostname unset, client required" \ "$P_SRV" \ - "$P_CLI auth_mode=required set_hostname=no debug_level=1" \ + "$P_CLI auth_mode=required set_hostname=no debug_level=2" \ 0 \ -C "does not match with the expected CN" \ + -c "Certificate verification without having set hostname" \ + -c "Certificate verification without CN verification" \ -C "x509_verify_cert() returned -" \ -C "! mbedtls_ssl_handshake returned" \ -C "X509 - Certificate verification failed" run_test "Authentication: hostname unset, client optional" \ "$P_SRV" \ - "$P_CLI auth_mode=optional set_hostname=no debug_level=1" \ + "$P_CLI auth_mode=optional set_hostname=no debug_level=2" \ 0 \ -C "does not match with the expected CN" \ + -c "Certificate verification without having set hostname" \ + -c "Certificate verification without CN verification" \ -C "x509_verify_cert() returned -" \ -C "X509 - Certificate verification failed" run_test "Authentication: hostname unset, client none" \ "$P_SRV" \ - "$P_CLI auth_mode=none set_hostname=no debug_level=1" \ + "$P_CLI auth_mode=none set_hostname=no debug_level=2" \ 0 \ -C "does not match with the expected CN" \ + -C "Certificate verification without having set hostname" \ + -C "Certificate verification without CN verification" \ -C "x509_verify_cert() returned -" \ -C "X509 - Certificate verification failed" run_test "Authentication: hostname unset, client default, server picks cert" \ "$P_SRV force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-CCM-8" \ - "$P_CLI psk=73776f726466697368 psk_identity=foo set_hostname=no debug_level=1" \ + "$P_CLI psk=73776f726466697368 psk_identity=foo set_hostname=no debug_level=2" \ 0 \ -C "does not match with the expected CN" \ + -c "Certificate verification without having set hostname" \ + -c "Certificate verification without CN verification" \ -C "x509_verify_cert() returned -" \ -C "X509 - Certificate verification failed" run_test "Authentication: hostname unset, client default, server picks PSK" \ "$P_SRV force_ciphersuite=TLS-PSK-WITH-AES-128-CCM-8 psk=73776f726466697368 psk_identity=foo" \ - "$P_CLI psk=73776f726466697368 psk_identity=foo set_hostname=no debug_level=1" \ + "$P_CLI psk=73776f726466697368 psk_identity=foo set_hostname=no debug_level=2" \ 0 \ -C "does not match with the expected CN" \ + -C "Certificate verification without having set hostname" \ + -C "Certificate verification without CN verification" \ -C "x509_verify_cert() returned -" \ -C "X509 - Certificate verification failed" From 63f958df772f763dfd5b6d0506f9cac9ed34c34e Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 13 Feb 2025 13:46:03 +0100 Subject: [PATCH 07/30] Create error code for mbedtls_ssl_set_hostname not called Signed-off-by: Gilles Peskine --- include/mbedtls/error.h | 2 +- include/mbedtls/ssl.h | 32 ++++++++++++++++++++++++++++++++ library/error.c | 2 ++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/include/mbedtls/error.h b/include/mbedtls/error.h index 7a183733ee..fd0ac76321 100644 --- a/include/mbedtls/error.h +++ b/include/mbedtls/error.h @@ -92,7 +92,7 @@ * ECP 4 10 (Started from top) * MD 5 5 * HKDF 5 1 (Started from top) - * SSL 5 2 (Started from 0x5F00) + * SSL 5 3 (Started from 0x5F00) * CIPHER 6 8 (Started from 0x6080) * SSL 6 24 (Started from top, plus 0x6000) * SSL 7 32 diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 447a293535..4b3a67c48d 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -183,6 +183,38 @@ #define MBEDTLS_ERR_SSL_BAD_CONFIG -0x5E80 /** Cache entry not found */ #define MBEDTLS_ERR_SSL_CACHE_ENTRY_NOT_FOUND -0x5E00 +/** Attempt to verify a certificate without an expected hostname. + * This is usually insecure. + * + * In TLS clients, when a client authenticates a server through its + * certificate, the client normally checks three things: + * - the certificate chain must be valid; + * - the chain must start from a trusted CA; + * - the certificate must cover the server name that is expected by the client. + * + * Omitting any of these checks is generally insecure, and can allow a + * malicious server to impersonate a legitimate server. + * + * The third check may be safely skipped in some unusual scenarios, + * such as networks where eavesdropping is a risk but not active attacks, + * or a private PKI where the client equally trusts all servers that are + * accredited by the root CA. + * + * You should call mbedtls_ssl_set_hostname() with the expected server name + * before starting a TLS handshake on a client (unless the client is + * set up to only use PSK-based authentication, which does not rely on the + * host name). If you have determined that server name verification is not + * required for security in your scenario, call mbedtls_ssl_set_hostname() + * with \p NULL as the server name. + * + * This error is raised if all of the following conditions are met: + * + * - A TLS client is configured with the authentication mode + * #MBEDTLS_SSL_VERIFY_REQUIRED (default). + * - Certificate authentication is enabled. + * - The client does not call mbedtls_ssl_set_hostname(). + */ +#define MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME -0x5D80 /* * Various constants diff --git a/library/error.c b/library/error.c index cb7ad57e45..dc7590461c 100644 --- a/library/error.c +++ b/library/error.c @@ -508,6 +508,8 @@ const char *mbedtls_high_level_strerr(int error_code) return( "SSL - Invalid value in SSL config" ); case -(MBEDTLS_ERR_SSL_CACHE_ENTRY_NOT_FOUND): return( "SSL - Cache entry not found" ); + case -(MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME): + return( "SSL - Attempt to verify a certificate without an expected hostname. This is usually insecure. In TLS clients, when a client authenticates a server through its certificate, the client normally checks three things: - the certificate chain must be valid; - the chain must start from a trusted CA; - the certificate must cover the server name that is expected by the client. Omitting any of these checks is generally insecure, and can allow a malicious server to impersonate a legitimate server. The third check may be safely skipped in some unusual scenarios, such as networks where eavesdropping is a risk but not active attacks, or a private PKI where the client equally trusts all servers that are accredited by the root CA. You should call mbedtls_ssl_set_hostname() with the expected server name before starting a TLS handshake on a client (unless the client is set up to only use PSK-based authentication, which does not rely on the host name). If you have determined that server name verification is not required for security in your scenario, call mbedtls_ssl_set_hostname() with \\p NULL as the server name. This error is raised if all of the following conditions are met: - A TLS client is configured with the authentication mode #MBEDTLS_SSL_VERIFY_REQUIRED (default). - Certificate authentication is enabled. - The client does not call mbedtls_ssl_set_hostname()" ); #endif /* MBEDTLS_SSL_TLS_C */ #if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C) From ae59c5232274df36b1b58dd52eb74cb153829a1a Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 13 Feb 2025 13:46:58 +0100 Subject: [PATCH 08/30] Create configuration option to bypass the mbedtls_ssl_set_hostname check Update generated files. Signed-off-by: Gilles Peskine --- include/mbedtls/config.h | 40 +++++++++++++++++++ include/mbedtls/ssl.h | 3 ++ library/error.c | 2 +- library/version_features.c | 3 ++ programs/test/query_config.c | 12 ++++++ scripts/config.py | 1 + .../test_suite_config.mbedtls_boolean.data | 8 ++++ 7 files changed, 68 insertions(+), 1 deletion(-) diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h index 84af7f767e..9590dd3cac 100644 --- a/include/mbedtls/config.h +++ b/include/mbedtls/config.h @@ -1713,6 +1713,46 @@ */ //#define MBEDTLS_SSL_ASYNC_PRIVATE +/** \def MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME + * + * In TLS clients, when a client authenticates a server through its + * certificate, the client normally checks three things: + * - the certificate chain must be valid; + * - the chain must start from a trusted CA; + * - the certificate must cover the server name that is expected by the client. + * + * Omitting any of these checks is generally insecure, and can allow a + * malicious server to impersonate a legitimate server. + * + * The third check may be safely skipped in some unusual scenarios, + * such as networks where eavesdropping is a risk but not active attacks, + * or a private PKI where the client equally trusts all servers that are + * accredited by the root CA. + * + * You should call mbedtls_ssl_set_hostname() with the expected server name + * before starting a TLS handshake on a client (unless the client is + * set up to only use PSK-based authentication, which does not rely on the + * host name). This configuration option controls what happens if a TLS client + * is configured with the authentication mode #MBEDTLS_SSL_VERIFY_REQUIRED + * (default), certificate authentication is enabled and the client does not + * call mbedtls_ssl_set_hostname(): + * + * - If this option is unset (default), the connection attempt is aborted + * with the error #MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME. + * - If this option is set, the TLS library does not check the server name + * that the certificate is valid for. This is the historical behavior + * of Mbed TLS, but may be insecure as explained above. + * + * Enable this option for strict backward compatibility if you have + * determined that it is secure in the scenario where you are using + * Mbed TLS. + * + * \deprecated This option exists only for backward compatibility and will + * be removed in the next major version of Mbed TLS. + * + */ +//#define MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME + /** * \def MBEDTLS_SSL_CONTEXT_SERIALIZATION * diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 4b3a67c48d..541b879331 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -213,6 +213,9 @@ * #MBEDTLS_SSL_VERIFY_REQUIRED (default). * - Certificate authentication is enabled. * - The client does not call mbedtls_ssl_set_hostname(). + * - The configuration option + * #MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME + * is not enabled. */ #define MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME -0x5D80 diff --git a/library/error.c b/library/error.c index dc7590461c..a0667e177d 100644 --- a/library/error.c +++ b/library/error.c @@ -509,7 +509,7 @@ const char *mbedtls_high_level_strerr(int error_code) case -(MBEDTLS_ERR_SSL_CACHE_ENTRY_NOT_FOUND): return( "SSL - Cache entry not found" ); case -(MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME): - return( "SSL - Attempt to verify a certificate without an expected hostname. This is usually insecure. In TLS clients, when a client authenticates a server through its certificate, the client normally checks three things: - the certificate chain must be valid; - the chain must start from a trusted CA; - the certificate must cover the server name that is expected by the client. Omitting any of these checks is generally insecure, and can allow a malicious server to impersonate a legitimate server. The third check may be safely skipped in some unusual scenarios, such as networks where eavesdropping is a risk but not active attacks, or a private PKI where the client equally trusts all servers that are accredited by the root CA. You should call mbedtls_ssl_set_hostname() with the expected server name before starting a TLS handshake on a client (unless the client is set up to only use PSK-based authentication, which does not rely on the host name). If you have determined that server name verification is not required for security in your scenario, call mbedtls_ssl_set_hostname() with \\p NULL as the server name. This error is raised if all of the following conditions are met: - A TLS client is configured with the authentication mode #MBEDTLS_SSL_VERIFY_REQUIRED (default). - Certificate authentication is enabled. - The client does not call mbedtls_ssl_set_hostname()" ); + return( "SSL - Attempt to verify a certificate without an expected hostname. This is usually insecure. In TLS clients, when a client authenticates a server through its certificate, the client normally checks three things: - the certificate chain must be valid; - the chain must start from a trusted CA; - the certificate must cover the server name that is expected by the client. Omitting any of these checks is generally insecure, and can allow a malicious server to impersonate a legitimate server. The third check may be safely skipped in some unusual scenarios, such as networks where eavesdropping is a risk but not active attacks, or a private PKI where the client equally trusts all servers that are accredited by the root CA. You should call mbedtls_ssl_set_hostname() with the expected server name before starting a TLS handshake on a client (unless the client is set up to only use PSK-based authentication, which does not rely on the host name). If you have determined that server name verification is not required for security in your scenario, call mbedtls_ssl_set_hostname() with \\p NULL as the server name. This error is raised if all of the following conditions are met: - A TLS client is configured with the authentication mode #MBEDTLS_SSL_VERIFY_REQUIRED (default). - Certificate authentication is enabled. - The client does not call mbedtls_ssl_set_hostname(). - The configuration option #MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME is not enabled" ); #endif /* MBEDTLS_SSL_TLS_C */ #if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C) diff --git a/library/version_features.c b/library/version_features.c index 6f663b12a7..f5734c4577 100644 --- a/library/version_features.c +++ b/library/version_features.c @@ -486,6 +486,9 @@ static const char * const features[] = { #if defined(MBEDTLS_SSL_ASYNC_PRIVATE) "MBEDTLS_SSL_ASYNC_PRIVATE", #endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ +#if defined(MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME) + "MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME", +#endif /* MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME */ #if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) "MBEDTLS_SSL_CONTEXT_SERIALIZATION", #endif /* MBEDTLS_SSL_CONTEXT_SERIALIZATION */ diff --git a/programs/test/query_config.c b/programs/test/query_config.c index 5d9886bcd9..d09b7f69d4 100644 --- a/programs/test/query_config.c +++ b/programs/test/query_config.c @@ -1376,6 +1376,14 @@ int query_config(const char *config) } #endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ +#if defined(MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME) + if( strcmp( "MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME", config ) == 0 ) + { + MACRO_EXPANSION_TO_STR( MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME ); + return( 0 ); + } +#endif /* MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME */ + #if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) if( strcmp( "MBEDTLS_SSL_CONTEXT_SERIALIZATION", config ) == 0 ) { @@ -3506,6 +3514,10 @@ void list_config(void) OUTPUT_MACRO_NAME_VALUE(MBEDTLS_SSL_ASYNC_PRIVATE); #endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ +#if defined(MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME) + OUTPUT_MACRO_NAME_VALUE(MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME); +#endif /* MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME */ + #if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) OUTPUT_MACRO_NAME_VALUE(MBEDTLS_SSL_CONTEXT_SERIALIZATION); #endif /* MBEDTLS_SSL_CONTEXT_SERIALIZATION */ diff --git a/scripts/config.py b/scripts/config.py index 766b5af9c1..ef72db324e 100755 --- a/scripts/config.py +++ b/scripts/config.py @@ -326,6 +326,7 @@ def crypto_adapter(adapter): DEPRECATED = frozenset([ 'MBEDTLS_SSL_PROTO_SSL3', + 'MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME', 'MBEDTLS_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO', ]) diff --git a/tests/suites/test_suite_config.mbedtls_boolean.data b/tests/suites/test_suite_config.mbedtls_boolean.data index 1a1d13f370..718bd280bd 100644 --- a/tests/suites/test_suite_config.mbedtls_boolean.data +++ b/tests/suites/test_suite_config.mbedtls_boolean.data @@ -1264,6 +1264,14 @@ Config: !MBEDTLS_SSL_CBC_RECORD_SPLITTING depends_on:!MBEDTLS_SSL_CBC_RECORD_SPLITTING:MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C pass: +Config: MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME +depends_on:MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME:MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C +pass: + +Config: !MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME +depends_on:!MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME:MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C +pass: + Config: MBEDTLS_SSL_CLI_C depends_on:MBEDTLS_SSL_CLI_C pass: From 551756df7c5d76736883db37b8d43284bc622bb2 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 13 Feb 2025 14:39:02 +0100 Subject: [PATCH 09/30] Require calling mbedtls_ssl_set_hostname() for security In a TLS client, when using certificate authentication, the client should check that the certificate is valid for the server name that the client expects. Otherwise, in most scenarios, a malicious server can impersonate another server. Normally, the application code should call mbedtls_ssl_set_hostname(). However, it's easy to forget. So raise an error if mandatory certificate authentication is in effect and mbedtls_ssl_set_hostname() has not been called. Raise the new error code MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME, for easy identification. But don't raise the error if the backward compatibility option MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME is enabled. Signed-off-by: Gilles Peskine --- library/ssl_tls.c | 6 ++++++ tests/ssl-opt.sh | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 7526a1e96a..91b4ae9473 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2617,6 +2617,12 @@ static int get_hostname_for_verification(mbedtls_ssl_context *ssl, { if (!mbedtls_ssl_has_set_hostname_been_called(ssl)) { MBEDTLS_SSL_DEBUG_MSG(1, ("Certificate verification without having set hostname")); +#if !defined(MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT && + ssl->conf->authmode == MBEDTLS_SSL_VERIFY_REQUIRED) { + return MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME; + } +#endif } *hostname = mbedtls_ssl_get_hostname_pointer(ssl); diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 576bc0710b..cc77c26bd8 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -4758,13 +4758,28 @@ run_test "Authentication: hostname null, client none" \ -C "x509_verify_cert() returned -" \ -C "X509 - Certificate verification failed" -run_test "Authentication: hostname unset, client required" \ +requires_config_disabled MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME +run_test "Authentication: hostname unset, client required, secure config" \ + "$P_SRV" \ + "$P_CLI auth_mode=required set_hostname=no debug_level=2" \ + 1 \ + -C "does not match with the expected CN" \ + -c "Certificate verification without having set hostname" \ + -C "Certificate verification without CN verification" \ + -c "get_hostname_for_verification() returned -" \ + -C "x509_verify_cert() returned -" \ + -c "! mbedtls_ssl_handshake returned" \ + -C "X509 - Certificate verification failed" + +requires_config_enabled MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME +run_test "Authentication: hostname unset, client required, historical config" \ "$P_SRV" \ "$P_CLI auth_mode=required set_hostname=no debug_level=2" \ 0 \ -C "does not match with the expected CN" \ -c "Certificate verification without having set hostname" \ -c "Certificate verification without CN verification" \ + -C "get_hostname_for_verification() returned -" \ -C "x509_verify_cert() returned -" \ -C "! mbedtls_ssl_handshake returned" \ -C "X509 - Certificate verification failed" @@ -4789,13 +4804,27 @@ run_test "Authentication: hostname unset, client none" \ -C "x509_verify_cert() returned -" \ -C "X509 - Certificate verification failed" -run_test "Authentication: hostname unset, client default, server picks cert" \ +requires_config_disabled MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME +run_test "Authentication: hostname unset, client default, secure config, server picks cert" \ + "$P_SRV force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-CCM-8" \ + "$P_CLI psk=73776f726466697368 psk_identity=foo set_hostname=no debug_level=2" \ + 1 \ + -C "does not match with the expected CN" \ + -c "Certificate verification without having set hostname" \ + -C "Certificate verification without CN verification" \ + -c "get_hostname_for_verification() returned -" \ + -C "x509_verify_cert() returned -" \ + -C "X509 - Certificate verification failed" + +requires_config_enabled MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME +run_test "Authentication: hostname unset, client default, historical config, server picks cert" \ "$P_SRV force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-CCM-8" \ "$P_CLI psk=73776f726466697368 psk_identity=foo set_hostname=no debug_level=2" \ 0 \ -C "does not match with the expected CN" \ -c "Certificate verification without having set hostname" \ -c "Certificate verification without CN verification" \ + -C "get_hostname_for_verification() returned -" \ -C "x509_verify_cert() returned -" \ -C "X509 - Certificate verification failed" From 18b52ce40c3f0beae806a1f38401fbd7f5c3f3d7 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 13 Feb 2025 17:28:49 +0100 Subject: [PATCH 10/30] Call mbedtls_ssl_set_hostname in the generic endpoint setup in unit tests Signed-off-by: Gilles Peskine --- tests/src/test_helpers/ssl_helpers.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/src/test_helpers/ssl_helpers.c b/tests/src/test_helpers/ssl_helpers.c index 9afc6333b4..fcdc632bbf 100644 --- a/tests/src/test_helpers/ssl_helpers.c +++ b/tests/src/test_helpers/ssl_helpers.c @@ -721,6 +721,10 @@ int mbedtls_test_ssl_endpoint_init( ret = mbedtls_ssl_setup(&(ep->ssl), &(ep->conf)); TEST_ASSERT(ret == 0); + if (MBEDTLS_SSL_IS_CLIENT == endpoint_type) { + ret = mbedtls_ssl_set_hostname(&(ep->ssl), "localhost"); + } + #if defined(MBEDTLS_SSL_PROTO_DTLS) && defined(MBEDTLS_SSL_SRV_C) if (endpoint_type == MBEDTLS_SSL_IS_SERVER && dtls_context != NULL) { mbedtls_ssl_conf_dtls_cookies(&(ep->conf), NULL, NULL, NULL); From f086b8f0f1706a38ad5a5e597c5e95afd634e62b Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 13 Feb 2025 21:46:00 +0100 Subject: [PATCH 11/30] mbedtls_ssl_set_hostname tests: add tests with CA callback Signed-off-by: Gilles Peskine --- tests/ssl-opt.sh | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index cc77c26bd8..afe15df7fa 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -4664,6 +4664,18 @@ run_test "Authentication: hostname match, client required" \ -C "! mbedtls_ssl_handshake returned" \ -C "X509 - Certificate verification failed" +run_test "Authentication: hostname match, client required, CA callback" \ + "$P_SRV" \ + "$P_CLI auth_mode=required server_name=localhost debug_level=3 ca_callback=1" \ + 0 \ + -C "does not match with the expected CN" \ + -C "Certificate verification without having set hostname" \ + -C "Certificate verification without CN verification" \ + -c "use CA callback for X.509 CRT verification" \ + -C "x509_verify_cert() returned -" \ + -C "! mbedtls_ssl_handshake returned" \ + -C "X509 - Certificate verification failed" + run_test "Authentication: hostname mismatch (wrong), client required" \ "$P_SRV" \ "$P_CLI auth_mode=required server_name=wrong-name debug_level=1" \ @@ -4784,6 +4796,34 @@ run_test "Authentication: hostname unset, client required, historical config" \ -C "! mbedtls_ssl_handshake returned" \ -C "X509 - Certificate verification failed" +requires_config_disabled MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME +run_test "Authentication: hostname unset, client required, secure config, CA callback" \ + "$P_SRV" \ + "$P_CLI auth_mode=required set_hostname=no debug_level=3 ca_callback=1" \ + 1 \ + -C "does not match with the expected CN" \ + -c "Certificate verification without having set hostname" \ + -C "Certificate verification without CN verification" \ + -c "get_hostname_for_verification() returned -" \ + -C "use CA callback for X.509 CRT verification" \ + -C "x509_verify_cert() returned -" \ + -c "! mbedtls_ssl_handshake returned" \ + -C "X509 - Certificate verification failed" + +requires_config_enabled MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME +run_test "Authentication: hostname unset, client required, historical config, CA callback" \ + "$P_SRV" \ + "$P_CLI auth_mode=required set_hostname=no debug_level=3 ca_callback=1" \ + 0 \ + -C "does not match with the expected CN" \ + -c "Certificate verification without having set hostname" \ + -c "Certificate verification without CN verification" \ + -C "get_hostname_for_verification() returned -" \ + -c "use CA callback for X.509 CRT verification" \ + -C "x509_verify_cert() returned -" \ + -C "! mbedtls_ssl_handshake returned" \ + -C "X509 - Certificate verification failed" + run_test "Authentication: hostname unset, client optional" \ "$P_SRV" \ "$P_CLI auth_mode=optional set_hostname=no debug_level=2" \ From 130949322532507398974d7014129d13a69c5bb6 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 17 Feb 2025 17:42:35 +0100 Subject: [PATCH 12/30] Run part of ssl-opt.sh in full_no_deprecated In particular, run the test case "Authentication: hostname unset, client required, secure config, CA callback" Signed-off-by: Gilles Peskine --- tests/scripts/all.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh index 4c2d0cc297..c3b6bf9a5a 100755 --- a/tests/scripts/all.sh +++ b/tests/scripts/all.sh @@ -1780,6 +1780,9 @@ component_test_full_no_deprecated () { msg "test: make, full_no_deprecated config" # ~ 5s make test + + msg "test: ssl-opt.sh authentication, full_no_deprecated config" # ~ 10s + tests/ssl-opt.sh -f 'Default\|Authentication' } component_test_full_no_deprecated_deprecated_warning () { From 1ecf2c7d57a888f072aa3a5a2dec24ef6cbfcc83 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 17 Feb 2025 17:41:54 +0100 Subject: [PATCH 13/30] Add a note about calling mbedtls_ssl_set_hostname to mbedtls_ssl_setup Signed-off-by: Gilles Peskine --- include/mbedtls/ssl.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 541b879331..e837f4ceb4 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -1580,6 +1580,14 @@ void mbedtls_ssl_init(mbedtls_ssl_context *ssl); * Calling mbedtls_ssl_setup again is not supported, even * if no session is active. * + * \warning After setting up a client context, if certificate-based + * authentication is enabled, you should call + * mbedtls_ssl_set_hostname() to specifiy the expected + * name of the server. Without this, in most scenarios, + * the TLS connection is insecure. See + * #MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME + * for more information. + * * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto * subsystem must have been initialized by calling * psa_crypto_init() before calling this function. From 8f964d4677009acf943bfdcaf90cfb98b4031730 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 17 Feb 2025 17:49:20 +0100 Subject: [PATCH 14/30] Changelog entries for requiring mbedls_ssl_set_hostname() in TLS clients Signed-off-by: Gilles Peskine --- ChangeLog.d/mbedtls_ssl_set_hostname.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 ChangeLog.d/mbedtls_ssl_set_hostname.txt diff --git a/ChangeLog.d/mbedtls_ssl_set_hostname.txt b/ChangeLog.d/mbedtls_ssl_set_hostname.txt new file mode 100644 index 0000000000..cb61ed18e8 --- /dev/null +++ b/ChangeLog.d/mbedtls_ssl_set_hostname.txt @@ -0,0 +1,18 @@ +Default behavior changes + * In TLS clients, if mbedtls_ssl_set_hostname() has not been called, + mbedtls_ssl_handshake() now fails with + MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME + if certificate-based authentication of the server is attempted. + This is because authenticating a server without knowing what name + to expect is usually insecure. To restore the old behavior, either + call mbedtls_ssl_set_hostname() with NULL as the hostname, or + enable the new compile-time option + MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME. + +Security + * Note that TLS clients should generally call mbedtls_ssl_set_hostname() + if they use certificate authentication (i.e. not pre-shared keys). + Otherwise, in many scenarios, the server could be impersonated. + The library will now prevent the handshake and return + MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME + if mbedtls_ssl_set_hostname() has not been called. From 7d193acf01c559a69359fd47f718f723c28d948a Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 20 Feb 2025 17:39:52 +0100 Subject: [PATCH 15/30] Expand and rectify the documentation of mbedtls_ssl_context::hostname Signed-off-by: Gilles Peskine --- include/mbedtls/ssl.h | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index e837f4ceb4..541fcc8e6f 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -1442,12 +1442,27 @@ struct mbedtls_ssl_context { * * Also used on clients for SNI. * - * If this is \p NULL, the peer name verification is skipped, and - * the server_name extension is not sent. + * The value of this field can be: + * - \p NULL in a newly initialized or reset context. + * - A heap-allocated copy of the last value passed to + * mbedtls_ssl_set_hostname(), if the last call had a non-null + * \p hostname argument. + * - A special value to indicate that mbedtls_ssl_set_hostname() + * was called with \p NULL (as opposed to never having been called). + * See `mbedtls_ssl_get_hostname_pointer()` in `ssl_tls.c`. * - * This can be a special value to indicate that mbedtls_ssl_set_hostname() - * has been called with \p NULL, as opposed to never having been called. - * See mbedtls_ssl_get_hostname_pointer(). + * If this field contains the value \p NULL and the configuration option + * #MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME + * is unset, on a TLS client, attempting to verify a server certificate + * results in the error + * #MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME. + * + * If this field contains the special value described above, or if + * the value is \p NULL and the configuration option + * #MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME + * is set, then the peer name verification is skipped, which may be + * insecure, especially on a client. Furthermore, on a client, the + * server_name extension is not sent. */ char *hostname; #endif /* MBEDTLS_X509_CRT_PARSE_C */ From 6310e98fa54fffb28b646875938c810cad1790fe Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 20 Feb 2025 17:44:48 +0100 Subject: [PATCH 16/30] Expose mbedtls_ssl_get_hostname_pointer() In 2.28, the `hostname` field of `mbedtls_ssl_context` is part of the public API. We've slightly changed its meaning in order to fix a security issue. Document the new function mbedtls_ssl_get_hostname_pointer() which returns what used to be the value of this field. Signed-off-by: Gilles Peskine --- ChangeLog.d/mbedtls_ssl_set_hostname.txt | 3 +++ include/mbedtls/ssl.h | 5 ++++- include/mbedtls/ssl_internal.h | 2 -- library/ssl_tls.c | 5 ----- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ChangeLog.d/mbedtls_ssl_set_hostname.txt b/ChangeLog.d/mbedtls_ssl_set_hostname.txt index cb61ed18e8..bd15d3a8bd 100644 --- a/ChangeLog.d/mbedtls_ssl_set_hostname.txt +++ b/ChangeLog.d/mbedtls_ssl_set_hostname.txt @@ -8,6 +8,9 @@ Default behavior changes call mbedtls_ssl_set_hostname() with NULL as the hostname, or enable the new compile-time option MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME. + The content of ssl->hostname after mbedtls_ssl_set_hostname(ssl, NULL) + has changed, see the documentation of the hostname field in the + mbedtls_ssl_context struct type for details. Security * Note that TLS clients should generally call mbedtls_ssl_set_hostname() diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 541fcc8e6f..f89f470341 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -1449,7 +1449,10 @@ struct mbedtls_ssl_context { * \p hostname argument. * - A special value to indicate that mbedtls_ssl_set_hostname() * was called with \p NULL (as opposed to never having been called). - * See `mbedtls_ssl_get_hostname_pointer()` in `ssl_tls.c`. + * + * If you need to obtain the value passed to + * mbedtls_ssl_set_hostname() even if it may have been called with + * \p NULL, call mbedtls_ssl_get_hostname_pointer(). * * If this field contains the value \p NULL and the configuration option * #MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME diff --git a/include/mbedtls/ssl_internal.h b/include/mbedtls/ssl_internal.h index fdc1719c5a..e3873690f7 100644 --- a/include/mbedtls/ssl_internal.h +++ b/include/mbedtls/ssl_internal.h @@ -1214,7 +1214,6 @@ static inline size_t mbedtls_ssl_hs_hdr_len(const mbedtls_ssl_context *ssl) return 4; } -#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) /** Get the host name from the SSL context. * * \param[in] ssl SSL context @@ -1224,7 +1223,6 @@ static inline size_t mbedtls_ssl_hs_hdr_len(const mbedtls_ssl_context *ssl) * \p ssl or if it was last called with \p NULL. */ const char *mbedtls_ssl_get_hostname_pointer(const mbedtls_ssl_context *ssl); -#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ #if defined(MBEDTLS_SSL_PROTO_DTLS) void mbedtls_ssl_send_flight_completed(mbedtls_ssl_context *ssl); diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 91b4ae9473..65d5b964d3 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -62,11 +62,6 @@ static int mbedtls_ssl_has_set_hostname_been_called( } #endif -/* Micro-optimization: don't export this function if it isn't needed outside - * of this source file. */ -#if !defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) -static -#endif const char *mbedtls_ssl_get_hostname_pointer(const mbedtls_ssl_context *ssl) { if (ssl->hostname == ssl_hostname_skip_cn_verification) { From 538553fa7bffbd9d50d2e9d677774d11677db5c9 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 20 Feb 2025 19:12:04 +0100 Subject: [PATCH 17/30] Improve documentation of mbedtls_ssl_set_hostname Signed-off-by: Gilles Peskine --- include/mbedtls/ssl.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index f89f470341..2722f6e0a5 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -3178,16 +3178,19 @@ void mbedtls_ssl_conf_sig_hashes(mbedtls_ssl_config *conf, #if defined(MBEDTLS_X509_CRT_PARSE_C) /** * \brief Set or reset the hostname to check against the received - * server certificate. It sets the ServerName TLS extension, - * too, if that extension is enabled. (client-side only) + * peer certificate. On a client, this also sets the + * ServerName TLS extension, if that extension is enabled. + * On a TLS 1.3 client, this also sets the server name in + * the session resumption ticket, if that feature is enabled. * * \param ssl SSL context - * \param hostname the server hostname, may be NULL to clear hostname - - * \note Maximum hostname length MBEDTLS_SSL_MAX_HOST_NAME_LEN. + * \param hostname The server hostname. This may be \c NULL to clear + * the hostname. * - * \return 0 if successful, MBEDTLS_ERR_SSL_ALLOC_FAILED on - * allocation failure, MBEDTLS_ERR_SSL_BAD_INPUT_DATA on + * \note Maximum hostname length #MBEDTLS_SSL_MAX_HOST_NAME_LEN. + * + * \return 0 if successful, #MBEDTLS_ERR_SSL_ALLOC_FAILED on + * allocation failure, #MBEDTLS_ERR_SSL_BAD_INPUT_DATA on * too long input hostname. * * Hostname set to the one provided on success (cleared From 2cc9dcbbcc373b851efc6b66d0e3b83ecaf31194 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 20 Feb 2025 19:12:16 +0100 Subject: [PATCH 18/30] Document the need to call mbedtls_ssl_set_hostname Signed-off-by: Gilles Peskine --- include/mbedtls/ssl.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 2722f6e0a5..8f9d38efe7 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -3189,6 +3189,16 @@ void mbedtls_ssl_conf_sig_hashes(mbedtls_ssl_config *conf, * * \note Maximum hostname length #MBEDTLS_SSL_MAX_HOST_NAME_LEN. * + * \note If the hostname is \c NULL on a client, then the server + * is not authenticated: it only needs to have a valid + * certificate, not a certificate matching its name. + * Therefore you should always call this function on a client, + * unless the connection is set up to only allow + * pre-shared keys, or in scenarios where server + * impersonation is not a concern. See the documentation of + * #MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME + * for more details. + * * \return 0 if successful, #MBEDTLS_ERR_SSL_ALLOC_FAILED on * allocation failure, #MBEDTLS_ERR_SSL_BAD_INPUT_DATA on * too long input hostname. From 466ebe8ced5719ec9bd25cc34e90a716e3965d3a Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 6 Mar 2025 12:41:39 +0100 Subject: [PATCH 19/30] Zeroize temporary heap buffers used in PSA operations Signed-off-by: Gilles Peskine --- ChangeLog.d/psa-zeroize.txt | 2 ++ library/psa_crypto.c | 21 +++++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 ChangeLog.d/psa-zeroize.txt diff --git a/ChangeLog.d/psa-zeroize.txt b/ChangeLog.d/psa-zeroize.txt new file mode 100644 index 0000000000..e597302dc6 --- /dev/null +++ b/ChangeLog.d/psa-zeroize.txt @@ -0,0 +1,2 @@ +Security + * Zeroize temporary heap buffers used in PSA operations. diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 21d8420163..755465ef40 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -6171,16 +6171,22 @@ psa_status_t psa_crypto_local_input_alloc(const uint8_t *input, size_t input_len return PSA_SUCCESS; error: - mbedtls_free(local_input->buffer); - local_input->buffer = NULL; + if (local_input->buffer != NULL) { + mbedtls_platform_zeroize(local_input->buffer, local_input->length); + mbedtls_free(local_input->buffer); + local_input->buffer = NULL; + } local_input->length = 0; return status; } void psa_crypto_local_input_free(psa_crypto_local_input_t *local_input) { - mbedtls_free(local_input->buffer); - local_input->buffer = NULL; + if (local_input->buffer != NULL) { + mbedtls_platform_zeroize(local_input->buffer, local_input->length); + mbedtls_free(local_input->buffer); + local_input->buffer = NULL; + } local_input->length = 0; } @@ -6223,8 +6229,11 @@ psa_status_t psa_crypto_local_output_free(psa_crypto_local_output_t *local_outpu return status; } - mbedtls_free(local_output->buffer); - local_output->buffer = NULL; + if (local_output->buffer != NULL) { + mbedtls_platform_zeroize(local_output->buffer, local_output->length); + mbedtls_free(local_output->buffer); + local_output->buffer = NULL; + } local_output->length = 0; return PSA_SUCCESS; From 68014b2b80491318eb7e08b1d690e7ebe27d249f Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 10 Mar 2025 14:10:11 +0000 Subject: [PATCH 20/30] Return and propagate errors in calc_finished() Allow calc_finished to return an error code and propagate that back to the original function. If an error is returned by a PSA function, propagate it upwards instead of continuing, so that we do not fail to properly check the finished message. Signed-off-by: David Horstmann --- include/mbedtls/ssl_internal.h | 2 +- library/ssl_tls.c | 39 ++++++++++++++++++++++------------ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/include/mbedtls/ssl_internal.h b/include/mbedtls/ssl_internal.h index e3873690f7..21f11ed971 100644 --- a/include/mbedtls/ssl_internal.h +++ b/include/mbedtls/ssl_internal.h @@ -467,7 +467,7 @@ struct mbedtls_ssl_handshake_params { void (*update_checksum)(mbedtls_ssl_context *, const unsigned char *, size_t); void (*calc_verify)(const mbedtls_ssl_context *, unsigned char *, size_t *); - void (*calc_finished)(mbedtls_ssl_context *, unsigned char *, int); + int (*calc_finished)(mbedtls_ssl_context *, unsigned char *, int); mbedtls_ssl_tls_prf_cb *tls_prf; #if defined(MBEDTLS_DHM_C) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 65d5b964d3..d183078a0b 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -892,25 +892,25 @@ static void ssl_update_checksum_md5sha1(mbedtls_ssl_context *, const unsigned ch #if defined(MBEDTLS_SSL_PROTO_SSL3) static void ssl_calc_verify_ssl(const mbedtls_ssl_context *, unsigned char *, size_t *); -static void ssl_calc_finished_ssl(mbedtls_ssl_context *, unsigned char *, int); +static int ssl_calc_finished_ssl(mbedtls_ssl_context *, unsigned char *, int); #endif #if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) static void ssl_calc_verify_tls(const mbedtls_ssl_context *, unsigned char *, size_t *); -static void ssl_calc_finished_tls(mbedtls_ssl_context *, unsigned char *, int); +static int ssl_calc_finished_tls(mbedtls_ssl_context *, unsigned char *, int); #endif #if defined(MBEDTLS_SSL_PROTO_TLS1_2) #if defined(MBEDTLS_SHA256_C) static void ssl_update_checksum_sha256(mbedtls_ssl_context *, const unsigned char *, size_t); static void ssl_calc_verify_tls_sha256(const mbedtls_ssl_context *, unsigned char *, size_t *); -static void ssl_calc_finished_tls_sha256(mbedtls_ssl_context *, unsigned char *, int); +static int ssl_calc_finished_tls_sha256(mbedtls_ssl_context *, unsigned char *, int); #endif #if defined(MBEDTLS_SHA512_C) && !defined(MBEDTLS_SHA512_NO_SHA384) static void ssl_update_checksum_sha384(mbedtls_ssl_context *, const unsigned char *, size_t); static void ssl_calc_verify_tls_sha384(const mbedtls_ssl_context *, unsigned char *, size_t *); -static void ssl_calc_finished_tls_sha384(mbedtls_ssl_context *, unsigned char *, int); +static int ssl_calc_finished_tls_sha384(mbedtls_ssl_context *, unsigned char *, int); #endif #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ @@ -3136,7 +3136,7 @@ static void ssl_update_checksum_sha384(mbedtls_ssl_context *ssl, #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ #if defined(MBEDTLS_SSL_PROTO_SSL3) -static void ssl_calc_finished_ssl( +static int ssl_calc_finished_ssl( mbedtls_ssl_context *ssl, unsigned char *buf, int from) { const char *sender; @@ -3218,11 +3218,13 @@ static void ssl_calc_finished_ssl( mbedtls_platform_zeroize(sha1sum, sizeof(sha1sum)); MBEDTLS_SSL_DEBUG_MSG(2, ("<= calc finished")); + + return 0; } #endif /* MBEDTLS_SSL_PROTO_SSL3 */ #if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) -static void ssl_calc_finished_tls( +static int ssl_calc_finished_tls( mbedtls_ssl_context *ssl, unsigned char *buf, int from) { int len = 12; @@ -3278,12 +3280,14 @@ static void ssl_calc_finished_tls( mbedtls_platform_zeroize(padbuf, sizeof(padbuf)); MBEDTLS_SSL_DEBUG_MSG(2, ("<= calc finished")); + + return 0; } #endif /* MBEDTLS_SSL_PROTO_TLS1 || MBEDTLS_SSL_PROTO_TLS1_1 */ #if defined(MBEDTLS_SSL_PROTO_TLS1_2) #if defined(MBEDTLS_SHA256_C) -static void ssl_calc_finished_tls_sha256( +static int ssl_calc_finished_tls_sha256( mbedtls_ssl_context *ssl, unsigned char *buf, int from) { int len = 12; @@ -3314,13 +3318,13 @@ static void ssl_calc_finished_tls_sha256( status = psa_hash_clone(&ssl->handshake->fin_sha256_psa, &sha256_psa); if (status != PSA_SUCCESS) { MBEDTLS_SSL_DEBUG_MSG(2, ("PSA hash clone failed")); - return; + return status; } status = psa_hash_finish(&sha256_psa, padbuf, sizeof(padbuf), &hash_size); if (status != PSA_SUCCESS) { MBEDTLS_SSL_DEBUG_MSG(2, ("PSA hash finish failed")); - return; + return status; } MBEDTLS_SSL_DEBUG_BUF(3, "PSA calculated padbuf", padbuf, 32); #else @@ -3354,12 +3358,14 @@ static void ssl_calc_finished_tls_sha256( mbedtls_platform_zeroize(padbuf, sizeof(padbuf)); MBEDTLS_SSL_DEBUG_MSG(2, ("<= calc finished")); + + return 0; } #endif /* MBEDTLS_SHA256_C */ #if defined(MBEDTLS_SHA512_C) && !defined(MBEDTLS_SHA512_NO_SHA384) -static void ssl_calc_finished_tls_sha384( +static int ssl_calc_finished_tls_sha384( mbedtls_ssl_context *ssl, unsigned char *buf, int from) { int len = 12; @@ -3390,13 +3396,13 @@ static void ssl_calc_finished_tls_sha384( status = psa_hash_clone(&ssl->handshake->fin_sha384_psa, &sha384_psa); if (status != PSA_SUCCESS) { MBEDTLS_SSL_DEBUG_MSG(2, ("PSA hash clone failed")); - return; + return status; } status = psa_hash_finish(&sha384_psa, padbuf, sizeof(padbuf), &hash_size); if (status != PSA_SUCCESS) { MBEDTLS_SSL_DEBUG_MSG(2, ("PSA hash finish failed")); - return; + return status; } MBEDTLS_SSL_DEBUG_BUF(3, "PSA calculated padbuf", padbuf, 48); #else @@ -3441,6 +3447,8 @@ static void ssl_calc_finished_tls_sha384( mbedtls_platform_zeroize(padbuf, sizeof(padbuf)); MBEDTLS_SSL_DEBUG_MSG(2, ("<= calc finished")); + + return 0; } #endif /* MBEDTLS_SHA512_C && !MBEDTLS_SHA512_NO_SHA384 */ #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ @@ -3535,7 +3543,12 @@ int mbedtls_ssl_write_finished(mbedtls_ssl_context *ssl) mbedtls_ssl_update_out_pointers(ssl, ssl->transform_negotiate); - ssl->handshake->calc_finished(ssl, ssl->out_msg + 4, ssl->conf->endpoint); + ret = ssl->handshake->calc_finished(ssl, ssl->out_msg + 4, + ssl->conf->endpoint); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "calc_finished", ret); + return ret; + } /* * RFC 5246 7.4.9 (Page 63) says 12 is the default length and ciphersuites From b81920dc8f33fb8cce75ec0ff889d7b90a7ba741 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 11 Mar 2025 15:52:48 +0000 Subject: [PATCH 21/30] Add changelog entry for TLS 1.2 Finished fix Signed-off-by: David Horstmann --- ChangeLog.d/tls12-check-finished-calc.txt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ChangeLog.d/tls12-check-finished-calc.txt diff --git a/ChangeLog.d/tls12-check-finished-calc.txt b/ChangeLog.d/tls12-check-finished-calc.txt new file mode 100644 index 0000000000..cd52d32ffd --- /dev/null +++ b/ChangeLog.d/tls12-check-finished-calc.txt @@ -0,0 +1,6 @@ +Security + * Fix a vulnerability in the TLS 1.2 handshake. If memory allocation failed + or there was a cryptographic hardware failure when calculating the + Finished message, it could be calculated incorrectly. This would break + the security guarantees of the TLS handshake. + CVE-2025-27810 From 78302e263c61cef7109e39316a4c10fa098573b8 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 11 Mar 2025 15:55:46 +0000 Subject: [PATCH 22/30] Add MBEDTLS_CHECK_RETURN_CRITICAL annotation Ensure that the compiler will warn us if we do not check the return of calc_verify in future. Signed-off-by: David Horstmann --- include/mbedtls/ssl_internal.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/mbedtls/ssl_internal.h b/include/mbedtls/ssl_internal.h index 21f11ed971..17bb631b59 100644 --- a/include/mbedtls/ssl_internal.h +++ b/include/mbedtls/ssl_internal.h @@ -467,6 +467,7 @@ struct mbedtls_ssl_handshake_params { void (*update_checksum)(mbedtls_ssl_context *, const unsigned char *, size_t); void (*calc_verify)(const mbedtls_ssl_context *, unsigned char *, size_t *); + MBEDTLS_CHECK_RETURN_CRITICAL int (*calc_finished)(mbedtls_ssl_context *, unsigned char *, int); mbedtls_ssl_tls_prf_cb *tls_prf; From 2b85729d23625e3183bdba74cf0e93864eeffba6 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 11 Mar 2025 18:13:02 +0000 Subject: [PATCH 23/30] Add checking to missed case of calc_finished() Signed-off-by: David Horstmann --- library/ssl_tls.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index d183078a0b..976c38f67e 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -3677,7 +3677,11 @@ int mbedtls_ssl_parse_finished(mbedtls_ssl_context *ssl) #endif hash_len = 12; - ssl->handshake->calc_finished(ssl, buf, ssl->conf->endpoint ^ 1); + ret = ssl->handshake->calc_finished(ssl, buf, ssl->conf->endpoint ^ 1); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "calc_finished", ret); + goto exit; + } if ((ret = mbedtls_ssl_read_record(ssl, 1)) != 0) { MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record", ret); From 09072663a79792d69d61522133d5ef33c0787df0 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 12 Mar 2025 12:03:13 +0000 Subject: [PATCH 24/30] Convert PSA errors to Mbed TLS MD errors Factor out a static function to perform error conversion and use it for the calc_verify() functions along with the place where it is currently used. Signed-off-by: David Horstmann --- library/ssl_tls.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 976c38f67e..bc6dc2740f 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -627,6 +627,21 @@ exit: #if defined(MBEDTLS_SSL_PROTO_TLS1_2) #if defined(MBEDTLS_USE_PSA_CRYPTO) +static int mbedtls_ssl_md_error_from_psa(psa_status_t status) +{ + switch (status) { + case PSA_ERROR_NOT_SUPPORTED: + return MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE; + case PSA_ERROR_BAD_STATE: /* Intentional fallthrough */ + case PSA_ERROR_BUFFER_TOO_SMALL: + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + case PSA_ERROR_INSUFFICIENT_MEMORY: + return MBEDTLS_ERR_MD_ALLOC_FAILED; + default: + return MBEDTLS_ERR_MD_HW_ACCEL_FAILED; + } +} + static psa_status_t setup_psa_key_derivation(psa_key_derivation_operation_t *derivation, psa_key_id_t key, psa_algorithm_t alg, @@ -3318,13 +3333,13 @@ static int ssl_calc_finished_tls_sha256( status = psa_hash_clone(&ssl->handshake->fin_sha256_psa, &sha256_psa); if (status != PSA_SUCCESS) { MBEDTLS_SSL_DEBUG_MSG(2, ("PSA hash clone failed")); - return status; + return mbedtls_ssl_md_error_from_psa(status); } status = psa_hash_finish(&sha256_psa, padbuf, sizeof(padbuf), &hash_size); if (status != PSA_SUCCESS) { MBEDTLS_SSL_DEBUG_MSG(2, ("PSA hash finish failed")); - return status; + return mbedtls_ssl_md_error_from_psa(status); } MBEDTLS_SSL_DEBUG_BUF(3, "PSA calculated padbuf", padbuf, 32); #else @@ -3396,13 +3411,13 @@ static int ssl_calc_finished_tls_sha384( status = psa_hash_clone(&ssl->handshake->fin_sha384_psa, &sha384_psa); if (status != PSA_SUCCESS) { MBEDTLS_SSL_DEBUG_MSG(2, ("PSA hash clone failed")); - return status; + return mbedtls_ssl_md_error_from_psa(status); } status = psa_hash_finish(&sha384_psa, padbuf, sizeof(padbuf), &hash_size); if (status != PSA_SUCCESS) { MBEDTLS_SSL_DEBUG_MSG(2, ("PSA hash finish failed")); - return status; + return mbedtls_ssl_md_error_from_psa(status); } MBEDTLS_SSL_DEBUG_BUF(3, "PSA calculated padbuf", padbuf, 48); #else @@ -7643,17 +7658,8 @@ exit: if (status != PSA_SUCCESS) { mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR); - switch (status) { - case PSA_ERROR_NOT_SUPPORTED: - return MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE; - case PSA_ERROR_BAD_STATE: /* Intentional fallthrough */ - case PSA_ERROR_BUFFER_TOO_SMALL: - return MBEDTLS_ERR_MD_BAD_INPUT_DATA; - case PSA_ERROR_INSUFFICIENT_MEMORY: - return MBEDTLS_ERR_MD_ALLOC_FAILED; - default: - return MBEDTLS_ERR_MD_HW_ACCEL_FAILED; - } + + return mbedtls_ssl_md_error_from_psa(status); } return 0; } From 226daac16841d83d72f8c320ffe5a3b0b956fdbc Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 12 Mar 2025 13:58:01 +0000 Subject: [PATCH 25/30] Declare conversion function even without 1.2 In 2.28 we may only enable TLS 1.0 or 1.1 in which case this function is still needed. Signed-off-by: David Horstmann --- library/ssl_tls.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index bc6dc2740f..73f9c7f471 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -624,9 +624,7 @@ exit: } #endif /* MBEDTLS_SSL_PROTO_TLS1) || MBEDTLS_SSL_PROTO_TLS1_1 */ -#if defined(MBEDTLS_SSL_PROTO_TLS1_2) #if defined(MBEDTLS_USE_PSA_CRYPTO) - static int mbedtls_ssl_md_error_from_psa(psa_status_t status) { switch (status) { @@ -641,6 +639,10 @@ static int mbedtls_ssl_md_error_from_psa(psa_status_t status) return MBEDTLS_ERR_MD_HW_ACCEL_FAILED; } } +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) +#if defined(MBEDTLS_USE_PSA_CRYPTO) static psa_status_t setup_psa_key_derivation(psa_key_derivation_operation_t *derivation, psa_key_id_t key, From 43bb98f55b639af3231a6b6edc6d46c8639ae32f Mon Sep 17 00:00:00 2001 From: Minos Galanakis Date: Tue, 18 Mar 2025 16:44:06 +0000 Subject: [PATCH 26/30] Changelog: Added CVE. Signed-off-by: Minos Galanakis --- ChangeLog.d/mbedtls_ssl_set_hostname.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog.d/mbedtls_ssl_set_hostname.txt b/ChangeLog.d/mbedtls_ssl_set_hostname.txt index bd15d3a8bd..913c185068 100644 --- a/ChangeLog.d/mbedtls_ssl_set_hostname.txt +++ b/ChangeLog.d/mbedtls_ssl_set_hostname.txt @@ -19,3 +19,4 @@ Security The library will now prevent the handshake and return MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME if mbedtls_ssl_set_hostname() has not been called. + CVE-2025-27809 From 1b94fc6344933d4d108bb11c5febda2f28038998 Mon Sep 17 00:00:00 2001 From: Minos Galanakis Date: Tue, 18 Mar 2025 16:51:19 +0000 Subject: [PATCH 27/30] Assemble Changelog Signed-off-by: Minos Galanakis --- ChangeLog | 51 +++++++++++++++++++ ChangeLog.d/fix-aesni-asm-clobbers.txt | 5 -- ChangeLog.d/fix-compilation-with-djgpp.txt | 2 - .../fix-key-derive-bad-state-error.txt | 3 -- .../fix-msvc-version-guard-format-zu.txt | 9 ---- ChangeLog.d/mbedtls_ssl_set_hostname.txt | 22 -------- ChangeLog.d/psa-zeroize.txt | 2 - .../replace-close-with-mbedtls_net_close.txt | 4 -- ChangeLog.d/tls12-check-finished-calc.txt | 6 --- 9 files changed, 51 insertions(+), 53 deletions(-) delete mode 100644 ChangeLog.d/fix-aesni-asm-clobbers.txt delete mode 100644 ChangeLog.d/fix-compilation-with-djgpp.txt delete mode 100644 ChangeLog.d/fix-key-derive-bad-state-error.txt delete mode 100644 ChangeLog.d/fix-msvc-version-guard-format-zu.txt delete mode 100644 ChangeLog.d/mbedtls_ssl_set_hostname.txt delete mode 100644 ChangeLog.d/psa-zeroize.txt delete mode 100644 ChangeLog.d/replace-close-with-mbedtls_net_close.txt delete mode 100644 ChangeLog.d/tls12-check-finished-calc.txt diff --git a/ChangeLog b/ChangeLog index d36ff4e4a8..07db747588 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,56 @@ Mbed TLS ChangeLog (Sorted per branch, date) += Mbed TLS x.x.x branch released xxxx-xx-xx + +Default behavior changes + * In TLS clients, if mbedtls_ssl_set_hostname() has not been called, + mbedtls_ssl_handshake() now fails with + MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME + if certificate-based authentication of the server is attempted. + This is because authenticating a server without knowing what name + to expect is usually insecure. To restore the old behavior, either + call mbedtls_ssl_set_hostname() with NULL as the hostname, or + enable the new compile-time option + MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME. + The content of ssl->hostname after mbedtls_ssl_set_hostname(ssl, NULL) + has changed, see the documentation of the hostname field in the + mbedtls_ssl_context struct type for details. + +Security + * Note that TLS clients should generally call mbedtls_ssl_set_hostname() + if they use certificate authentication (i.e. not pre-shared keys). + Otherwise, in many scenarios, the server could be impersonated. + The library will now prevent the handshake and return + MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME + if mbedtls_ssl_set_hostname() has not been called. + CVE-2025-27809 + * Zeroize temporary heap buffers used in PSA operations. + * Fix a vulnerability in the TLS 1.2 handshake. If memory allocation failed + or there was a cryptographic hardware failure when calculating the + Finished message, it could be calculated incorrectly. This would break + the security guarantees of the TLS handshake. + CVE-2025-27810 + +Bugfix + * Use 'mbedtls_net_close' instead of 'close' in 'mbedtls_net_bind' + and 'mbedtls_net_connect' to prevent possible double close fd + problems. Fixes #9711. + * Fix compilation on MS-DOS DJGPP. Fixes #9813. + * Fix missing constraints on the AES-NI inline assembly which is used on + GCC-like compilers when building AES for generic x86_64 targets. This + may have resulted in incorrect code with some compilers, depending on + optimizations. Fixes #9819. + * Fix issue where psa_key_derivation_input_integer() is not detecting + bad state after an operation has been aborted. + * Fix definition of MBEDTLS_PRINTF_SIZET to prevent runtime crashes that + occurred whenever SSL debugging was enabled on a copy of Mbed TLS built + with Visual Studio 2013 or MinGW. + Fixes #10017. + * Remove Everest Visual Studio 2010 compatibility headers, which could + shadow standard CRT headers inttypes.h and stdbool.h with incomplete + implementatios if placed on the include path, eg. when building Mbed TLS + with the .sln file shipped with the project. + = Mbed TLS 2.28.9 branch released 2024-08-30 Security diff --git a/ChangeLog.d/fix-aesni-asm-clobbers.txt b/ChangeLog.d/fix-aesni-asm-clobbers.txt deleted file mode 100644 index 538f0c5115..0000000000 --- a/ChangeLog.d/fix-aesni-asm-clobbers.txt +++ /dev/null @@ -1,5 +0,0 @@ -Bugfix - * Fix missing constraints on the AES-NI inline assembly which is used on - GCC-like compilers when building AES for generic x86_64 targets. This - may have resulted in incorrect code with some compilers, depending on - optimizations. Fixes #9819. diff --git a/ChangeLog.d/fix-compilation-with-djgpp.txt b/ChangeLog.d/fix-compilation-with-djgpp.txt deleted file mode 100644 index 5b79fb69de..0000000000 --- a/ChangeLog.d/fix-compilation-with-djgpp.txt +++ /dev/null @@ -1,2 +0,0 @@ -Bugfix - * Fix compilation on MS-DOS DJGPP. Fixes #9813. diff --git a/ChangeLog.d/fix-key-derive-bad-state-error.txt b/ChangeLog.d/fix-key-derive-bad-state-error.txt deleted file mode 100644 index 0bccf77682..0000000000 --- a/ChangeLog.d/fix-key-derive-bad-state-error.txt +++ /dev/null @@ -1,3 +0,0 @@ -Bugfix - * Fix issue where psa_key_derivation_input_integer() is not detecting - bad state after an operation has been aborted. diff --git a/ChangeLog.d/fix-msvc-version-guard-format-zu.txt b/ChangeLog.d/fix-msvc-version-guard-format-zu.txt deleted file mode 100644 index 2713f6c9f4..0000000000 --- a/ChangeLog.d/fix-msvc-version-guard-format-zu.txt +++ /dev/null @@ -1,9 +0,0 @@ -Bugfix - * Fix definition of MBEDTLS_PRINTF_SIZET to prevent runtime crashes that - occurred whenever SSL debugging was enabled on a copy of Mbed TLS built - with Visual Studio 2013 or MinGW. - Fixes #10017. - * Remove Everest Visual Studio 2010 compatibility headers, which could - shadow standard CRT headers inttypes.h and stdbool.h with incomplete - implementatios if placed on the include path, eg. when building Mbed TLS - with the .sln file shipped with the project. diff --git a/ChangeLog.d/mbedtls_ssl_set_hostname.txt b/ChangeLog.d/mbedtls_ssl_set_hostname.txt deleted file mode 100644 index 913c185068..0000000000 --- a/ChangeLog.d/mbedtls_ssl_set_hostname.txt +++ /dev/null @@ -1,22 +0,0 @@ -Default behavior changes - * In TLS clients, if mbedtls_ssl_set_hostname() has not been called, - mbedtls_ssl_handshake() now fails with - MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME - if certificate-based authentication of the server is attempted. - This is because authenticating a server without knowing what name - to expect is usually insecure. To restore the old behavior, either - call mbedtls_ssl_set_hostname() with NULL as the hostname, or - enable the new compile-time option - MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME. - The content of ssl->hostname after mbedtls_ssl_set_hostname(ssl, NULL) - has changed, see the documentation of the hostname field in the - mbedtls_ssl_context struct type for details. - -Security - * Note that TLS clients should generally call mbedtls_ssl_set_hostname() - if they use certificate authentication (i.e. not pre-shared keys). - Otherwise, in many scenarios, the server could be impersonated. - The library will now prevent the handshake and return - MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME - if mbedtls_ssl_set_hostname() has not been called. - CVE-2025-27809 diff --git a/ChangeLog.d/psa-zeroize.txt b/ChangeLog.d/psa-zeroize.txt deleted file mode 100644 index e597302dc6..0000000000 --- a/ChangeLog.d/psa-zeroize.txt +++ /dev/null @@ -1,2 +0,0 @@ -Security - * Zeroize temporary heap buffers used in PSA operations. diff --git a/ChangeLog.d/replace-close-with-mbedtls_net_close.txt b/ChangeLog.d/replace-close-with-mbedtls_net_close.txt deleted file mode 100644 index 213cf55b40..0000000000 --- a/ChangeLog.d/replace-close-with-mbedtls_net_close.txt +++ /dev/null @@ -1,4 +0,0 @@ -Bugfix - * Use 'mbedtls_net_close' instead of 'close' in 'mbedtls_net_bind' - and 'mbedtls_net_connect' to prevent possible double close fd - problems. Fixes #9711. diff --git a/ChangeLog.d/tls12-check-finished-calc.txt b/ChangeLog.d/tls12-check-finished-calc.txt deleted file mode 100644 index cd52d32ffd..0000000000 --- a/ChangeLog.d/tls12-check-finished-calc.txt +++ /dev/null @@ -1,6 +0,0 @@ -Security - * Fix a vulnerability in the TLS 1.2 handshake. If memory allocation failed - or there was a cryptographic hardware failure when calculating the - Finished message, it could be calculated incorrectly. This would break - the security guarantees of the TLS handshake. - CVE-2025-27810 From 92e298ed2a349aaf2929ce43046e5c8610241e32 Mon Sep 17 00:00:00 2001 From: Minos Galanakis Date: Tue, 18 Mar 2025 17:17:34 +0000 Subject: [PATCH 28/30] Version Bump for 2.28.10 ./scripts/bump_version.sh --version 2.28.10 Signed-off-by: Minos Galanakis --- doxygen/input/doc_mainpage.h | 2 +- doxygen/mbedtls.doxyfile | 2 +- include/mbedtls/version.h | 8 ++++---- library/CMakeLists.txt | 6 +++--- pkgconfig/CMakeLists.txt | 2 +- tests/suites/test_suite_version.data | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/doxygen/input/doc_mainpage.h b/doxygen/input/doc_mainpage.h index d7c64637f5..1a8482a9aa 100644 --- a/doxygen/input/doc_mainpage.h +++ b/doxygen/input/doc_mainpage.h @@ -10,7 +10,7 @@ */ /** - * @mainpage Mbed TLS v2.28.9 API Documentation + * @mainpage Mbed TLS v2.28.10 API Documentation * * This documentation describes the internal structure of Mbed TLS. It was * automatically generated from specially formatted comment blocks in diff --git a/doxygen/mbedtls.doxyfile b/doxygen/mbedtls.doxyfile index b75597d10e..6943686c77 100644 --- a/doxygen/mbedtls.doxyfile +++ b/doxygen/mbedtls.doxyfile @@ -1,4 +1,4 @@ -PROJECT_NAME = "Mbed TLS v2.28.9" +PROJECT_NAME = "Mbed TLS v2.28.10" OUTPUT_DIRECTORY = ../apidoc/ FULL_PATH_NAMES = NO OPTIMIZE_OUTPUT_FOR_C = YES diff --git a/include/mbedtls/version.h b/include/mbedtls/version.h index 66998bf560..4d30fcfb54 100644 --- a/include/mbedtls/version.h +++ b/include/mbedtls/version.h @@ -26,16 +26,16 @@ */ #define MBEDTLS_VERSION_MAJOR 2 #define MBEDTLS_VERSION_MINOR 28 -#define MBEDTLS_VERSION_PATCH 9 +#define MBEDTLS_VERSION_PATCH 10 /** * The single version number has the following structure: * MMNNPP00 * Major version | Minor version | Patch version */ -#define MBEDTLS_VERSION_NUMBER 0x021C0900 -#define MBEDTLS_VERSION_STRING "2.28.9" -#define MBEDTLS_VERSION_STRING_FULL "Mbed TLS 2.28.9" +#define MBEDTLS_VERSION_NUMBER 0x021C0A00 +#define MBEDTLS_VERSION_STRING "2.28.10" +#define MBEDTLS_VERSION_STRING_FULL "Mbed TLS 2.28.10" #if defined(MBEDTLS_VERSION_C) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 8374979eca..b66b3c8faa 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -206,15 +206,15 @@ endif(USE_STATIC_MBEDTLS_LIBRARY) if(USE_SHARED_MBEDTLS_LIBRARY) set(CMAKE_LIBRARY_PATH ${CMAKE_CURRENT_BINARY_DIR}) add_library(${mbedcrypto_target} SHARED ${src_crypto}) - set_target_properties(${mbedcrypto_target} PROPERTIES VERSION 2.28.9 SOVERSION 7) + set_target_properties(${mbedcrypto_target} PROPERTIES VERSION 2.28.10 SOVERSION 7) target_link_libraries(${mbedcrypto_target} PUBLIC ${libs}) add_library(${mbedx509_target} SHARED ${src_x509}) - set_target_properties(${mbedx509_target} PROPERTIES VERSION 2.28.9 SOVERSION 1) + set_target_properties(${mbedx509_target} PROPERTIES VERSION 2.28.10 SOVERSION 1) target_link_libraries(${mbedx509_target} PUBLIC ${libs} ${mbedcrypto_target}) add_library(${mbedtls_target} SHARED ${src_tls}) - set_target_properties(${mbedtls_target} PROPERTIES VERSION 2.28.9 SOVERSION 14) + set_target_properties(${mbedtls_target} PROPERTIES VERSION 2.28.10 SOVERSION 14) target_link_libraries(${mbedtls_target} PUBLIC ${libs} ${mbedx509_target}) endif(USE_SHARED_MBEDTLS_LIBRARY) diff --git a/pkgconfig/CMakeLists.txt b/pkgconfig/CMakeLists.txt index 158f9a84bd..96a20f974d 100644 --- a/pkgconfig/CMakeLists.txt +++ b/pkgconfig/CMakeLists.txt @@ -9,7 +9,7 @@ if(NOT DISABLE_PACKAGE_CONFIG_AND_INSTALL) set(PKGCONFIG_PROJECT_HOMEPAGE_URL "https://www.trustedfirmware.org/projects/mbed-tls/") # Following the conventsion for DESCRIPTION and HOMEPAGE_URL, VERSION wasn't added until 3.0 and depends on policy CMP0048 - set(PKGCONFIG_VERSION 2.28.9) + set(PKGCONFIG_VERSION 2.28.10) configure_file(mbedcrypto.pc.in mbedcrypto.pc @ONLY) install(FILES diff --git a/tests/suites/test_suite_version.data b/tests/suites/test_suite_version.data index fa1999af2f..c5d8d7cf49 100644 --- a/tests/suites/test_suite_version.data +++ b/tests/suites/test_suite_version.data @@ -1,8 +1,8 @@ Check compile time library version -check_compiletime_version:"2.28.9" +check_compiletime_version:"2.28.10" Check runtime library version -check_runtime_version:"2.28.9" +check_runtime_version:"2.28.10" Check for MBEDTLS_VERSION_C check_feature:"MBEDTLS_VERSION_C":0 From 78a2a31a726ef56e270e3cf78dbdd0e234b424de Mon Sep 17 00:00:00 2001 From: Minos Galanakis Date: Tue, 18 Mar 2025 17:23:31 +0000 Subject: [PATCH 29/30] Finalise ChangeLog Signed-off-by: Minos Galanakis --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 07db747588..181db4009d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ Mbed TLS ChangeLog (Sorted per branch, date) -= Mbed TLS x.x.x branch released xxxx-xx-xx += Mbed TLS 2.28.10 branch released 2025-03-24 Default behavior changes * In TLS clients, if mbedtls_ssl_set_hostname() has not been called, From 71a228202c5ec7100bcbfe87d05da83ff3e3d1c4 Mon Sep 17 00:00:00 2001 From: Minos Galanakis Date: Tue, 18 Mar 2025 18:56:18 +0000 Subject: [PATCH 30/30] Update BRANCHES.md Signed-off-by: Minos Galanakis --- BRANCHES.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/BRANCHES.md b/BRANCHES.md index 9d5d779345..93bcbbd52e 100644 --- a/BRANCHES.md +++ b/BRANCHES.md @@ -107,9 +107,10 @@ The following branches are currently maintained: - [`development`](https://github.com/Mbed-TLS/mbedtls/) - [`mbedtls-3.6`](https://github.com/Mbed-TLS/mbedtls/tree/mbedtls-3.6) maintained until March 2027, see - . -- [`mbedtls-2.28`](https://github.com/Mbed-TLS/mbedtls/tree/mbedtls-2.28) - maintained until the end of 2024, see - . + . + +> Note: [**`mbedtls-2.28.10`**](https://github.com/Mbed-TLS/mbedtls/releases/tag/v2.28.10) +is the last release of the 2.28 LTS and won't receive bug fixes or security fixes anymore. +Users are advised to upgrade to a maintained version. Users are urged to always use the latest version of a maintained branch.