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"