diff --git a/include/violite.h b/include/violite.h index a6ccd1a607d5..17f8e444616b 100644 --- a/include/violite.h +++ b/include/violite.h @@ -253,6 +253,7 @@ const char *sslGetErrString(enum enum_ssl_init_error err); struct st_VioSSLFd { SSL_CTX *ssl_context; + const char *hostname; }; int sslaccept(struct st_VioSSLFd *, MYSQL_VIO, long timeout, @@ -264,7 +265,7 @@ struct st_VioSSLFd *new_VioSSLConnectorFd( const char *key_file, const char *cert_file, const char *ca_file, const char *ca_path, const char *cipher, const char *ciphersuites, enum enum_ssl_init_error *error, const char *crl_file, const char *crl_path, - const long ssl_ctx_flags, const char *server_host); + const long ssl_ctx_flags, const char *server_host, int verify, bool verify_identity); long process_tls_version(const char *tls_version); diff --git a/plugin/x/client/xconnection_impl.cc b/plugin/x/client/xconnection_impl.cc index c1686c6d5e2e..23263092ba1c 100644 --- a/plugin/x/client/xconnection_impl.cc +++ b/plugin/x/client/xconnection_impl.cc @@ -675,8 +675,10 @@ XError Connection_impl::activate_tls() { auto ssl_ctx_flags = process_tls_version( details::null_when_empty(m_context->m_ssl_config.m_tls_version)); + const int verify = m_context->m_ssl_config.m_mode >= Ssl_config::Mode::Ssl_verify_ca ? SSL_VERIFY_PEER : SSL_VERIFY_NONE; const bool verify_identity = Ssl_config::Mode::Ssl_verify_identity == m_context->m_ssl_config.m_mode; + m_vioSslFd = new_VioSSLConnectorFd( details::null_when_empty(m_context->m_ssl_config.m_key), details::null_when_empty(m_context->m_ssl_config.m_cert), @@ -686,7 +688,7 @@ XError Connection_impl::activate_tls() { &m_ssl_init_error, details::null_when_empty(m_context->m_ssl_config.m_crl), details::null_when_empty(m_context->m_ssl_config.m_crl_path), - ssl_ctx_flags, verify_identity ? m_hostname.c_str() : nullptr); + ssl_ctx_flags, m_hostname.c_str(), verify, verify_identity); if (nullptr == m_vioSslFd) return get_ssl_init_error(m_ssl_init_error); diff --git a/sql-common/client.cc b/sql-common/client.cc index b8050f2b1a7a..e8c7392289a4 100644 --- a/sql-common/client.cc +++ b/sql-common/client.cc @@ -3352,9 +3352,12 @@ const char *STDCALL mysql_get_ssl_cipher(MYSQL *mysql MY_ATTRIBUTE((unused))) { return nullptr; } +#if OPENSSL_VERSION_NUMBER < 0x10002000L /* Check the server's (subject) Common Name against the - hostname we connected to + hostname we connected to. This function is only used + when OpenSSL is older than 1.0.2 since versions since + 1.0.2 provide an implementation for hostname validation. SYNOPSIS ssl_verify_server_cert() @@ -3375,13 +3378,11 @@ static int ssl_verify_server_cert(Vio *vio, const char *server_hostname, X509 *server_cert = nullptr; int ret_validation = 1; -#if !(OPENSSL_VERSION_NUMBER >= 0x10002000L) int cn_loc = -1; char *cn = NULL; ASN1_STRING *cn_asn1 = NULL; X509_NAME_ENTRY *cn_entry = NULL; X509_NAME *subject = NULL; -#endif DBUG_TRACE; DBUG_PRINT("enter", ("server_hostname: %s", server_hostname)); @@ -3411,16 +3412,6 @@ static int ssl_verify_server_cert(Vio *vio, const char *server_hostname, are what we expect. */ - /* Use OpenSSL certificate matching functions instead of our own if we - have OpenSSL. The X509_check_* functions return 1 on success. - */ -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - /* - For OpenSSL 1.0.2 and up we already set certificate verification - parameters in the new_VioSSLFd() to perform automatic checks. - */ - ret_validation = 0; -#else /* OPENSSL_VERSION_NUMBER < 0x10002000L */ /* OpenSSL prior to 1.0.2 do not support X509_check_host() function. Use deprecated X509_get_subject_name() instead. @@ -3460,7 +3451,6 @@ static int ssl_verify_server_cert(Vio *vio, const char *server_hostname, /* Success */ ret_validation = 0; } -#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ *errptr = "SSL certificate validation success"; @@ -3468,6 +3458,7 @@ static int ssl_verify_server_cert(Vio *vio, const char *server_hostname, if (server_cert != nullptr) X509_free(server_cert); return ret_validation; } +#endif /* OPENSSL_VERSION_NUMBER < 0x10002000L */ /* Note that the mysql argument must be initialized with mysql_init() @@ -4161,20 +4152,6 @@ static int cli_establish_ssl(MYSQL *mysql) { goto error; } - /* - If the ssl_mode is VERIFY_CA or VERIFY_IDENTITY, make sure that the - connection doesn't succeed without providing the CA certificate. - */ - if (mysql->options.extension && - mysql->options.extension->ssl_mode > SSL_MODE_REQUIRED && - !(mysql->options.ssl_ca || mysql->options.ssl_capath)) { - set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, - ER_CLIENT(CR_SSL_CONNECTION_ERROR), - "CA certificate is required if ssl-mode " - "is VERIFY_CA or VERIFY_IDENTITY"); - goto error; - } - /* Attempt SSL connection if ssl_mode != SSL_MODE_DISABLED and the server supports SSL. Fallback on unencrypted connection otherwise. @@ -4186,11 +4163,21 @@ static int cli_establish_ssl(MYSQL *mysql) { struct st_mysql_options *options = &mysql->options; struct st_VioSSLFd *ssl_fd; enum enum_ssl_init_error ssl_init_error = SSL_INITERR_NOERROR; +#if OPENSSL_VERSION_NUMBER < 0x10002000L const char *cert_error; +#endif unsigned long ssl_error; char buff[33], *end; - const bool verify_identity = - mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT; + int verify; + bool verify_identity; + + if (options->extension && options->extension->ssl_mode >= SSL_MODE_VERIFY_CA) { + verify = SSL_VERIFY_PEER; + verify_identity = options->extension->ssl_mode == SSL_MODE_VERIFY_IDENTITY; + } else { + verify = SSL_VERIFY_NONE; + verify_identity = false; + } /* check if server supports compression else turn off client capability */ if (!(mysql->server_capabilities & CLIENT_ZSTD_COMPRESSION_ALGORITHM)) @@ -4227,7 +4214,7 @@ static int cli_establish_ssl(MYSQL *mysql) { options->extension ? options->extension->ssl_crl : nullptr, options->extension ? options->extension->ssl_crlpath : nullptr, options->extension ? options->extension->ssl_ctx_flags : 0, - verify_identity ? mysql->host : nullptr))) { + mysql->host, verify, verify_identity))) { set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, ER_CLIENT(CR_SSL_CONNECTION_ERROR), sslGetErrString(ssl_init_error)); @@ -4249,6 +4236,11 @@ static int cli_establish_ssl(MYSQL *mysql) { } DBUG_PRINT("info", ("IO layer change done!")); + /* OpenSSL 1.0.2 and up will use their own verification function. + * This is set up in new_VioSSLConnectorFd and adding here it here + * again is superfluous. + */ +#if OPENSSL_VERSION_NUMBER < 0x10002000L /* Verify server cert */ if (verify_identity && ssl_verify_server_cert(net->vio, mysql->host, &cert_error)) { @@ -4256,6 +4248,7 @@ static int cli_establish_ssl(MYSQL *mysql) { ER_CLIENT(CR_SSL_CONNECTION_ERROR), cert_error); goto error; } +#endif MYSQL_TRACE(SSL_CONNECTED, mysql, ()); MYSQL_TRACE_STAGE(mysql, AUTHENTICATE); @@ -4305,21 +4298,6 @@ static net_async_status cli_establish_ssl_nonblocking(MYSQL *mysql, int *res) { goto error; } - /* - If the ssl_mode is VERIFY_CA or VERIFY_IDENTITY, make sure - that the connection doesn't succeed without providing the - CA certificate. - */ - if (mysql->options.extension && - mysql->options.extension->ssl_mode > SSL_MODE_REQUIRED && - !(mysql->options.ssl_ca || mysql->options.ssl_capath)) { - set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, - ER_CLIENT(CR_SSL_CONNECTION_ERROR), - "CA certificate is required if ssl-mode " - "is VERIFY_CA or VERIFY_IDENTITY"); - goto error; - } - /* Attempt SSL connection if ssl_mode != SSL_MODE_DISABLED and the server supports SSL. Fallback on unencrypted @@ -4366,11 +4344,21 @@ static net_async_status cli_establish_ssl_nonblocking(MYSQL *mysql, int *res) { struct st_mysql_options *options = &mysql->options; struct st_VioSSLFd *ssl_fd; enum enum_ssl_init_error ssl_init_error; +#if OPENSSL_VERSION_NUMBER < 0x10002000L const char *cert_error; +#endif unsigned long ssl_error; size_t ret; - const bool verify_identity = - mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT; + int verify; + bool verify_identity; + + if (options->extension && options->extension->ssl_mode >= SSL_MODE_VERIFY_CA) { + verify = SSL_VERIFY_PEER; + verify_identity = options->extension->ssl_mode == SSL_MODE_VERIFY_IDENTITY; + } else { + verify = SSL_VERIFY_NONE; + verify_identity = false; + } MYSQL_TRACE_STAGE(mysql, SSL_NEGOTIATION); @@ -4385,7 +4373,7 @@ static net_async_status cli_establish_ssl_nonblocking(MYSQL *mysql, int *res) { options->extension ? options->extension->ssl_crl : nullptr, options->extension ? options->extension->ssl_crlpath : nullptr, options->extension ? options->extension->ssl_ctx_flags : 0, - verify_identity ? mysql->host : nullptr))) { + mysql->host, verify, verify_identity))) { set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, ER_CLIENT(CR_SSL_CONNECTION_ERROR), @@ -4427,6 +4415,11 @@ static net_async_status cli_establish_ssl_nonblocking(MYSQL *mysql, int *res) { /* sslconnect creates a new vio, so update it. */ vio_set_blocking_flag(net->vio, !ctx->non_blocking); + /* OpenSSL 1.0.2 and up will use their own verification function. + * This is set up in new_VioSSLConnectorFd and adding here it here + * again is superfluous. + */ +#if OPENSSL_VERSION_NUMBER < 0x10002000L /* Verify server cert */ if (verify_identity && ssl_verify_server_cert(net->vio, mysql->host, &cert_error)) { @@ -4434,7 +4427,7 @@ static net_async_status cli_establish_ssl_nonblocking(MYSQL *mysql, int *res) { ER_CLIENT(CR_SSL_CONNECTION_ERROR), cert_error); goto error; } - +#endif MYSQL_TRACE(SSL_CONNECTED, mysql, ()); MYSQL_TRACE_STAGE(mysql, AUTHENTICATE); } diff --git a/vio/test-ssl.cc b/vio/test-ssl.cc index 6d5eb2192403..f107ff73ca45 100644 --- a/vio/test-ssl.cc +++ b/vio/test-ssl.cc @@ -88,7 +88,8 @@ int main(int argc, char **argv) { ssl_acceptor = new_VioSSLAcceptorFd(server_key, server_cert, ca_file, ca_path, cipher); ssl_connector = new_VioSSLConnectorFd(client_key, client_cert, ca_file, - ca_path, cipher, &ssl_init_error); + ca_path, cipher, NULL, &ssl_init_error, + NULL, NULL, 0, NULL, SSL_VERIFY_NONE, false); client_vio = (Vio *)my_malloc(sizeof(Vio), MYF(0)); client_vio->sd = sv[0]; diff --git a/vio/test-sslclient.cc b/vio/test-sslclient.cc index 26a7a6c1e2a2..7052974f1495 100644 --- a/vio/test-sslclient.cc +++ b/vio/test-sslclient.cc @@ -63,7 +63,9 @@ int main(int argc MY_ATTRIBUTE((unused)), char **argv) { if (ca_path != 0) printf("CApath : %s\n", ca_path); ssl_connector = new_VioSSLConnectorFd(client_key, client_cert, ca_file, - ca_path, cipher, &ssl_init_error); + ca_path, cipher, NULL, &ssl_init_error, + NULL, NULL, 0, NULL, SSL_VERIFY_NONE, false); + if (!ssl_connector) { fatal_error("client:new_VioSSLConnectorFd failed"); } diff --git a/vio/viossl.cc b/vio/viossl.cc index c63bbd8989ea..d31f1a519033 100644 --- a/vio/viossl.cc +++ b/vio/viossl.cc @@ -630,6 +630,11 @@ static int ssl_do(struct st_VioSSLFd *ptr, Vio *vio, long timeout, DBUG_PRINT("info", ("ssl: %p timeout: %ld", ssl, timeout)); SSL_clear(ssl); SSL_SESSION_set_timeout(SSL_get_session(ssl), timeout); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (ptr->hostname) { + SSL_set_tlsext_host_name(ssl, const_cast(ptr->hostname)); + } +#endif SSL_set_fd(ssl, sd); #if defined(SSL_OP_NO_COMPRESSION) SSL_set_options(ssl, SSL_OP_NO_COMPRESSION); /* OpenSSL >= 1.0 only */ diff --git a/vio/viosslfactories.cc b/vio/viosslfactories.cc index c25117bd0fbb..85e6c18ead24 100644 --- a/vio/viosslfactories.cc +++ b/vio/viosslfactories.cc @@ -580,7 +580,8 @@ static struct st_VioSSLFd *new_VioSSLFd( const char *ca_path, const char *cipher, const char *ciphersuites MY_ATTRIBUTE((unused)), bool is_client, enum enum_ssl_init_error *error, const char *crl_file, const char *crl_path, - const long ssl_ctx_flags, const char *server_host MY_ATTRIBUTE((unused))) { + const long ssl_ctx_flags, const char *server_host, + bool verify_identity MY_ATTRIBUTE((unused))) { DH *dh; struct st_VioSSLFd *ssl_fd; long ssl_ctx_options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; @@ -633,6 +634,10 @@ static struct st_VioSSLFd *new_VioSSLFd( return nullptr; } + if (server_host) { + ssl_fd->hostname = my_strdup(PSI_NOT_INSTRUMENTED, server_host, MYF(0)); + } + #ifdef HAVE_TLSv13 /* Set OpenSSL TLS v1.3 ciphersuites. @@ -758,7 +763,7 @@ static struct st_VioSSLFd *new_VioSSLFd( If server_host parameter is set it contains either IP address or server's hostname. Pass it to the lib to perform automatic checks. */ - if (server_host) { + if (verify_identity && server_host) { X509_VERIFY_PARAM *param = SSL_CTX_get0_param(ssl_fd->ssl_context); assert(is_client); /* @@ -784,6 +789,7 @@ static struct st_VioSSLFd *new_VioSSLFd( error: DBUG_PRINT("error", ("%s", sslGetErrString(*error))); report_errors(); + my_free(const_cast(ssl_fd->hostname)); SSL_CTX_free(ssl_fd->ssl_context); my_free(ssl_fd); return nullptr; @@ -795,19 +801,12 @@ struct st_VioSSLFd *new_VioSSLConnectorFd( const char *key_file, const char *cert_file, const char *ca_file, const char *ca_path, const char *cipher, const char *ciphersuites, enum enum_ssl_init_error *error, const char *crl_file, const char *crl_path, - const long ssl_ctx_flags, const char *server_host) { + const long ssl_ctx_flags, const char *server_host, int verify, bool verify_identity) { struct st_VioSSLFd *ssl_fd; - int verify = SSL_VERIFY_PEER; - - /* - Turn off verification of servers certificate if both - ca_file and ca_path is set to NULL - */ - if (ca_file == nullptr && ca_path == nullptr) verify = SSL_VERIFY_NONE; if (!(ssl_fd = new_VioSSLFd(key_file, cert_file, ca_file, ca_path, cipher, ciphersuites, true, error, crl_file, crl_path, - ssl_ctx_flags, server_host))) { + ssl_ctx_flags, server_host, verify_identity))) { return nullptr; } @@ -828,7 +827,7 @@ struct st_VioSSLFd *new_VioSSLAcceptorFd( int verify = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; if (!(ssl_fd = new_VioSSLFd(key_file, cert_file, ca_file, ca_path, cipher, ciphersuites, false, error, crl_file, crl_path, - ssl_ctx_flags, nullptr))) { + ssl_ctx_flags, nullptr, false))) { return nullptr; } /* Init the the VioSSLFd as a "acceptor" ie. the server side */ @@ -849,6 +848,7 @@ struct st_VioSSLFd *new_VioSSLAcceptorFd( } void free_vio_ssl_acceptor_fd(struct st_VioSSLFd *fd) { + my_free(const_cast(fd->hostname)); SSL_CTX_free(fd->ssl_context); my_free(fd); } diff --git a/vio/viotest-ssl.cc b/vio/viotest-ssl.cc index fd9ed079039d..3a7be8ba6849 100644 --- a/vio/viotest-ssl.cc +++ b/vio/viotest-ssl.cc @@ -90,7 +90,8 @@ int main(int argc, char **argv) { ssl_acceptor = new_VioSSLAcceptorFd(server_key, server_cert, ca_file, ca_path); ssl_connector = new_VioSSLConnectorFd(client_key, client_cert, ca_file, - ca_path, &ssl_init_error); + ca_path, NULL, &ssl_init_error, NULL, + NULL, 0, SSL_VERIFY_NONE, false); client_vio = (Vio *)my_malloc(sizeof(Vio), MYF(0)); client_vio->sd = sv[0];