From 2c33c757256a305d4c7f60c189248b29d6a0efa1 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 13 Feb 2025 14:39:02 +0100 Subject: [PATCH] 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 | 50 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 993c69e226..a5e5b285ff 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -9835,6 +9835,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 (mbedtls_ssl_conf_get_endpoint(ssl->conf) == 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 a88ff3ba2c..633d9107a0 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -6183,13 +6183,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" @@ -6214,24 +6229,53 @@ 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, 1.2" \ +requires_config_disabled MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME +run_test "Authentication: hostname unset, client default, secure config, server picks cert, 1.2" \ + "$P_SRV force_version=tls12 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_disabled MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME +requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED +run_test "Authentication: hostname unset, client default, secure config, server picks cert, 1.3" \ + "$P_SRV force_version=tls13 tls13_kex_modes=ephemeral" \ + "$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, 1.2" \ "$P_SRV force_version=tls12 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" +requires_config_enabled MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED -run_test "Authentication: hostname unset, client default, server picks cert, 1.3" \ +run_test "Authentication: hostname unset, client default, historical config, server picks cert, 1.3" \ "$P_SRV force_version=tls13 tls13_kex_modes=ephemeral" \ "$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"