commit 9436a98df9c8a0ae0b1ab3922a5c68f8b8c83b87 Author: Georg Richter Date: Fri Nov 23 09:08:14 2018 +0100 MDEV-14101: Provide option to select TLS version. Both client and server now supports the option --tls-version. Valid arguments are TLSv1_0,TLSv1_1,TLSv1_2,TLSv1_3 or a combination of them, e.g. --tls_version=TLSv1_3 --tls_version=TLSv1_2,TLSv1_3 diff --git a/client/client_priv.h b/client/client_priv.h index ffdf0fc4..c20da4dd 100644 --- a/client/client_priv.h +++ b/client/client_priv.h @@ -99,6 +99,7 @@ enum options_client OPT_REPORT_PROGRESS, OPT_SKIP_ANNOTATE_ROWS_EVENTS, OPT_SSL_CRL, OPT_SSL_CRLPATH, + OPT_TLS_VERSION, OPT_PRINT_ROW_COUNT, OPT_PRINT_ROW_EVENT_POSITIONS, OPT_MAX_CLIENT_OPTION /* should be always the last */ }; diff --git a/client/mysql.cc b/client/mysql.cc index 4e94d092..2c69be24 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1361,6 +1361,7 @@ static bool do_connect(MYSQL *mysql, const char *host, const char *user, opt_ssl_capath, opt_ssl_cipher); mysql_options(mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl); mysql_options(mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath); + mysql_options(mysql, MARIADB_OPT_TLS_VERSION, (void *)opt_tls_version); } mysql_options(mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (char*)&opt_ssl_verify_server_cert); diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc index be81deb2..a0bc387b 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -353,6 +353,7 @@ int main(int argc,char *argv[]) opt_ssl_capath, opt_ssl_cipher); mysql_options(&mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl); mysql_options(&mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath); + mysql_options(&mysql, MARIADB_OPT_TLS_VERSION, (void *)opt_tls_version); } mysql_options(&mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (char*)&opt_ssl_verify_server_cert); diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index c111730d..c61a7116 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -2112,6 +2112,7 @@ static Exit_status safe_connect() opt_ssl_capath, opt_ssl_cipher); mysql_options(mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl); mysql_options(mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath); + mysql_options(mysql, MARIADB_OPT_TLS_VERSION, (void *)opt_tls_version); } mysql_options(mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (char*)&opt_ssl_verify_server_cert); diff --git a/client/mysqldump.c b/client/mysqldump.c index fd720514..f68002e8 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -1705,6 +1705,7 @@ static int connect_to_db(char *host, char *user,char *passwd) opt_ssl_capath, opt_ssl_cipher); mysql_options(&mysql_connection, MYSQL_OPT_SSL_CRL, opt_ssl_crl); mysql_options(&mysql_connection, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath); + mysql_options(mysql, MARIADB_OPT_TLS_VERSION, (void *)opt_tls_version); } mysql_options(&mysql_connection,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (char*)&opt_ssl_verify_server_cert); diff --git a/client/mysqlimport.c b/client/mysqlimport.c index 2e4e8550..33c82faf 100644 --- a/client/mysqlimport.c +++ b/client/mysqlimport.c @@ -443,6 +443,7 @@ static MYSQL *db_connect(char *host, char *database, opt_ssl_capath, opt_ssl_cipher); mysql_options(mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl); mysql_options(mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath); + mysql_options(mysql, MARIADB_OPT_TLS_VERSION, (void *)opt_tls_version); } mysql_options(mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (char*)&opt_ssl_verify_server_cert); diff --git a/client/mysqlshow.c b/client/mysqlshow.c index d7c4dbec..1bcf89ee 100644 --- a/client/mysqlshow.c +++ b/client/mysqlshow.c @@ -122,6 +122,7 @@ int main(int argc, char **argv) opt_ssl_capath, opt_ssl_cipher); mysql_options(&mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl); mysql_options(&mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath); + mysql_options(&mysql, MARIADB_OPT_TLS_VERSION, (void *)opt_tls_version); } mysql_options(&mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (char*)&opt_ssl_verify_server_cert); diff --git a/client/mysqltest.cc b/client/mysqltest.cc index f8c4f84a..08bd5763 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -6118,6 +6118,7 @@ void do_connect(struct st_command *command) opt_ssl_capath, ssl_cipher ? ssl_cipher : opt_ssl_cipher); mysql_options(con_slot->mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl); mysql_options(con_slot->mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath); + mysql_options(con_slot->mysql, MARIADB_OPT_TLS_VERSION, (void *)opt_tls_version); #if MYSQL_VERSION_ID >= 50000 /* Turn on ssl_verify_server_cert only if host is "localhost" */ opt_ssl_verify_server_cert= !strcmp(ds_host.str, "localhost"); diff --git a/include/sslopt-longopts.h b/include/sslopt-longopts.h index e605d013..a0e54ec7 100644 --- a/include/sslopt-longopts.h +++ b/include/sslopt-longopts.h @@ -46,6 +46,11 @@ "Certificate revocation list path (implies --ssl).", &opt_ssl_crlpath, &opt_ssl_crlpath, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"tls-version", OPT_TLS_VERSION, + "TLS protocol version for secure connection.", + &opt_tls_version, &opt_tls_version, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, + #ifdef MYSQL_CLIENT {"ssl-verify-server-cert", OPT_SSL_VERIFY_SERVER_CERT, "Verify server's \"Common Name\" in its cert against hostname used " diff --git a/include/sslopt-vars.h b/include/sslopt-vars.h index 8e669760..9f0d9ecc 100644 --- a/include/sslopt-vars.h +++ b/include/sslopt-vars.h @@ -30,6 +30,7 @@ SSL_STATIC char *opt_ssl_cipher = 0; SSL_STATIC char *opt_ssl_key = 0; SSL_STATIC char *opt_ssl_crl = 0; SSL_STATIC char *opt_ssl_crlpath = 0; +SSL_STATIC char *opt_tls_version = 0; #ifdef MYSQL_CLIENT SSL_STATIC my_bool opt_ssl_verify_server_cert= 0; #endif diff --git a/include/violite.h b/include/violite.h index 74b53f41..d45daa23 100644 --- a/include/violite.h +++ b/include/violite.h @@ -148,7 +148,8 @@ enum enum_ssl_init_error { SSL_INITERR_NOERROR= 0, SSL_INITERR_CERT, SSL_INITERR_KEY, SSL_INITERR_NOMATCH, SSL_INITERR_BAD_PATHS, SSL_INITERR_CIPHERS, - SSL_INITERR_MEMFAIL, SSL_INITERR_DH, SSL_INITERR_LASTERR + SSL_INITERR_MEMFAIL, SSL_INITERR_DH, SSL_INITERR_PROTOCOL, + SSL_INITERR_LASTERR }; const char* sslGetErrString(enum enum_ssl_init_error err); @@ -167,9 +168,10 @@ struct st_VioSSLFd const char *crl_file, const char *crl_path); struct st_VioSSLFd *new_VioSSLAcceptorFd(const char *key_file, const char *cert_file, - const char *ca_file,const char *ca_path, - const char *cipher, enum enum_ssl_init_error *error, - const char *crl_file, const char *crl_path); + const char *ca_file,const char *ca_path, + const char *cipher, enum enum_ssl_init_error *error, + const char *crl_file, const char *crl_path, + const char *tls_version); void free_vio_ssl_acceptor_fd(struct st_VioSSLFd *fd); #endif /* HAVE_OPENSSL */ diff --git a/mysql-test/main/mdev14101.combinations b/mysql-test/main/mdev14101.combinations new file mode 100644 index 00000000..64760072 --- /dev/null +++ b/mysql-test/main/mdev14101.combinations @@ -0,0 +1,11 @@ +[TLSv1.0] +--loose-tls-version=TLSv1.0 + +[TLSv1.1] +--loose-tls-version=TLSv1.1 + +[TLSv1.2] +--loose-tls-version=TLSv1.2 + +[TLSv1.3] +--loose-tls-version=TLSv1.3 diff --git a/mysql-test/main/mdev14101.result b/mysql-test/main/mdev14101.result new file mode 100644 index 00000000..0d9e1573 --- /dev/null +++ b/mysql-test/main/mdev14101.result @@ -0,0 +1,6 @@ +create user ssl_user1@localhost; +grant select on test.* to ssl_user1@localhost; +flush privileges; +tls_version_ok +1 +drop user ssl_user1@localhost; diff --git a/mysql-test/main/mdev14101.test b/mysql-test/main/mdev14101.test new file mode 100644 index 00000000..cbb02de0 --- /dev/null +++ b/mysql-test/main/mdev14101.test @@ -0,0 +1,27 @@ +# Tests for SSL connections, only run if mysqld is compiled +# with support for SSL. +-- source include/have_ssl_communication.inc +source include/require_openssl_client.inc; + +--enable_warnings +create user ssl_user1@localhost; +grant select on test.* to ssl_user1@localhost; +flush privileges; + +let tls_config=`select @@tls_version`; + +--exec $MYSQL -ussl_user1 --ssl --tls-version=$tls_config -e"SELECT @@tls_version='$tls_config' as tls_version_ok;"; +let $tls_version= ""; +if ($tls_config == "TLSv1.0") +{ + --error 1 + --exec $MYSQL -ussl_user1 --ssl --tls-version=TLSv1.1 -e"SELECT @@tls_version='TLSv1.1' as tls_version_ok;"; +} +if ($tls_config != "TLSv1.0") +{ +--error 1 +--exec $MYSQL -ussl_user1 --ssl --tls-version=TLSv1.0 -e"SELECT @@tls_version='TLSv1.0' as tls_version_ok;"; +} + +drop user ssl_user1@localhost; + diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 9ff47dc1..2c9fe31a 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1483,7 +1483,7 @@ Query_cache query_cache; my_bool opt_use_ssl = 0; char *opt_ssl_ca= NULL, *opt_ssl_capath= NULL, *opt_ssl_cert= NULL, *opt_ssl_cipher= NULL, *opt_ssl_key= NULL, *opt_ssl_crl= NULL, - *opt_ssl_crlpath= NULL; + *opt_ssl_crlpath= NULL, *opt_tls_version= NULL; static scheduler_functions thread_scheduler_struct, extra_thread_scheduler_struct; @@ -4777,7 +4777,8 @@ static void init_ssl() ssl_acceptor_fd= new_VioSSLAcceptorFd(opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher, &error, - opt_ssl_crl, opt_ssl_crlpath); + opt_ssl_crl, opt_ssl_crlpath, + opt_tls_version); DBUG_PRINT("info",("ssl_acceptor_fd: %p", ssl_acceptor_fd)); if (!ssl_acceptor_fd) { diff --git a/sql/mysqld.h b/sql/mysqld.h index 75f35a6d..cd6309b1 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -639,7 +639,7 @@ extern mysql_cond_t COND_slave_background; extern int32 thread_count, service_thread_count; extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher, - *opt_ssl_key, *opt_ssl_crl, *opt_ssl_crlpath; + *opt_ssl_key, *opt_ssl_crl, *opt_ssl_crlpath, *opt_tls_version; extern MYSQL_PLUGIN_IMPORT pthread_key(THD*, THR_THD); @@ -702,6 +702,7 @@ enum options_mysqld OPT_WSREP_SYNC_WAIT, #endif /* WITH_WSREP */ OPT_MYSQL_COMPATIBILITY, + OPT_TLS_VERSION, OPT_MYSQL_TO_BE_IMPLEMENTED, OPT_which_is_always_the_last }; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 92c7d329..f4d5467c 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -3442,6 +3442,12 @@ static Sys_var_charptr Sys_ssl_crlpath( READ_ONLY GLOBAL_VAR(opt_ssl_crlpath), SSL_OPT(OPT_SSL_CRLPATH), IN_FS_CHARSET, DEFAULT(0)); +static Sys_var_charptr Sys_tls_version( + "tls_version", + "TLS protocol version for secure connections.", + READ_ONLY GLOBAL_VAR(opt_tls_version), SSL_OPT(OPT_TLS_VERSION), + IN_FS_CHARSET, DEFAULT(0)); + static Sys_var_mybool Sys_standard_compliant_cte( "standard_compliant_cte", "Allow only CTEs compliant to SQL standard", diff --git a/vio/viosslfactories.c b/vio/viosslfactories.c index fa02eb03..4c533242 100644 --- a/vio/viosslfactories.c +++ b/vio/viosslfactories.c @@ -85,7 +85,8 @@ ssl_error_string[] = "SSL_CTX_set_default_verify_paths failed", "Failed to set ciphers to use", "SSL_CTX_new failed", - "SSL_CTX_set_tmp_dh failed" + "SSL_CTX_set_tmp_dh failed", + "Unknown TLS version" }; const char* @@ -166,28 +167,65 @@ static void check_ssl_init() } } +static long vio_tls_protocol_options(const char *tls_version) +{ + long tls_protocol_flags= +#if !defined(HAVE_YASSL) +#ifdef TLS1_3_VERSION + SSL_OP_NO_TLSv1_3 | +#endif + SSL_OP_NO_TLSv1_2 | SSL_OP_NO_TLSv1_1 | +#endif + SSL_OP_NO_TLSv1, + long disabled_tls_protocols= tls_protocol_flags, + disabled_ssl_protocols= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; + + if (!tls_version) + return disabled_ssl_protocols; + + if (strstr(tls_version, "TLSv1.0")) + disabled_tls_protocols&= ~SSL_OP_NO_TLSv1; +#if !defined(HAVE_YASSL) + if (strstr(tls_version, "TLSv1.1")) + disabled_tls_protocols&= ~SSL_OP_NO_TLSv1_1; + if (strstr(tls_version, "TLSv1.2")) + disabled_tls_protocols&= ~SSL_OP_NO_TLSv1_2; +#ifdef TLS1_3_VERSION + if (strstr(tls_version, "TLSv1.3")) + disabled_tls_protocols&= ~SSL_OP_NO_TLSv1_3; +#endif +#endif + + /* some garbage was specified in tls_version option */ + if (tls_protocol_flags == disabled_tls_protocols) + return -1; + return (disabled_tls_protocols | disabled_ssl_protocols); +} + /************************ VioSSLFd **********************************/ static struct st_VioSSLFd * new_VioSSLFd(const char *key_file, const char *cert_file, const char *ca_file, const char *ca_path, const char *cipher, my_bool is_client_method, enum enum_ssl_init_error *error, - const char *crl_file, const char *crl_path) + const char *crl_file, const char *crl_path, + const char *tls_version __attribute__((unused))) { DH *dh; struct st_VioSSLFd *ssl_fd; - long ssl_ctx_options= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; + long ssl_ctx_options; DBUG_ENTER("new_VioSSLFd"); DBUG_PRINT("enter", ("key_file: '%s' cert_file: '%s' ca_file: '%s' ca_path: '%s' " - "cipher: '%s' crl_file: '%s' crl_path: '%s' ", + "cipher: '%s' crl_file: '%s' crl_path: '%s' tls_version: '%s'", key_file ? key_file : "NULL", cert_file ? cert_file : "NULL", ca_file ? ca_file : "NULL", ca_path ? ca_path : "NULL", cipher ? cipher : "NULL", crl_file ? crl_file : "NULL", - crl_path ? crl_path : "NULL")); + crl_path ? crl_path : "NULL", + tls_version ? tls_version : "NULL")); check_ssl_init(); @@ -203,6 +241,14 @@ new_VioSSLFd(const char *key_file, const char *cert_file, goto err1; } + ssl_ctx_options= vio_tls_protocol_options(tls_version); + if (ssl_ctx_options == -1) + { + *error= SSL_INITERR_PROTOCOL; + DBUG_PRINT("error", ("%s", sslGetErrString(*error))); + goto err1; + } + SSL_CTX_set_options(ssl_fd->ssl_context, ssl_ctx_options); /* @@ -317,7 +363,7 @@ new_VioSSLConnectorFd(const char *key_file, const char *cert_file, if (!(ssl_fd= new_VioSSLFd(key_file, cert_file, ca_file, ca_path, cipher, TRUE, error, - crl_file, crl_path))) + crl_file, crl_path, NULL))) { return 0; } @@ -335,13 +381,14 @@ struct st_VioSSLFd * new_VioSSLAcceptorFd(const char *key_file, const char *cert_file, const char *ca_file, const char *ca_path, const char *cipher, enum enum_ssl_init_error* error, - const char *crl_file, const char *crl_path) + const char *crl_file, const char *crl_path, + const char *tls_version) { struct st_VioSSLFd *ssl_fd; int verify= SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; if (!(ssl_fd= new_VioSSLFd(key_file, cert_file, ca_file, ca_path, cipher, FALSE, error, - crl_file, crl_path))) + crl_file, crl_path, tls_version))) { return 0; }