From d13080133f6de9d89975b4c1f09615d47a10748d Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 11 Jun 2019 12:44:16 +0200 Subject: MDEV-14101 Provide an option to select TLS protocol version Server and command line tools now support option --tls_version to specify the TLS version between client and server. Valid values 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 In case there is a gap between versions, the lowest version will be used: --tls_version=TLSv1.1,TLSv1.3 -> Only TLSv1.1 will be available. If the used TLS library doesn't support the specified TLS version, it will use the default configuration. Limitations: SSLv3 is not supported. The default configuration doesn't support TLSv1.0 anymore. TLSv1.3 protocol currently is only supported by OpenSSL 1.1.0 (client and server) and GnuTLS 3.6.5 (client only). Overview of TLS implementations and protocols Server: +-----------+-----------------------------------------+ | Library | Supported TLS versions | +-----------+-----------------------------------------+ | WolfSSL | TLSv1.1, TLSv1,2 | +-----------+-----------------------------------------+ | OpenSSL | (TLSv1.0), TLSv1.1, TLSv1,2, TLSv1.3 | +-----------+-----------------------------------------+ | LibreSSL | (TLSv1.0), TLSv1.1, TLSv1,2, TLSv1.3 | +-----------+-----------------------------------------+ Client (MariaDB Connector/C) +-----------+-----------------------------------------+ | Library | Supported TLS versions | +-----------+-----------------------------------------+ | GnuTLS | (TLSv1.0), TLSv1.1, TLSv1.2, TLSv1.3 | +-----------+-----------------------------------------+ | Schannel | (TLSv1.0), TLSv1.1, TLSv1.2 | +-----------+-----------------------------------------+ | OpenSSL | (TLSv1.0), TLSv1.1, TLSv1,2, TLSv1.3 | +-----------+-----------------------------------------+ | LibreSSL | (TLSv1.0), TLSv1.1, TLSv1,2, TLSv1.3 | +-----------+-----------------------------------------+ --- client/client_priv.h | 2 +- client/mysql.cc | 1 + client/mysqladmin.cc | 1 + client/mysqlbinlog.cc | 1 + client/mysqldump.c | 1 + client/mysqlimport.c | 1 + client/mysqlshow.c | 1 + client/mysqltest.cc | 1 + extra/mariabackup/xtrabackup.cc | 2 + include/sslopt-longopts.h | 5 ++ include/sslopt-vars.h | 1 + include/violite.h | 11 +++- mysql-test/main/mysqld--help.result | 2 + mysql-test/main/mysqld--help.test | 2 +- mysql-test/main/openssl_6975.test | 24 ++++----- mysql-test/main/tls_version.opt | 1 + mysql-test/main/tls_version.result | 14 +++++ mysql-test/main/tls_version.test | 24 +++++++++ mysql-test/suite/sys_vars/inc/sysvars_server.inc | 2 + .../sys_vars/r/sysvars_server_embedded.result | 12 +++++ .../sys_vars/r/sysvars_server_notembedded.result | 12 +++++ sql/mysqld.cc | 10 ++-- sql/mysqld.h | 2 + sql/sys_vars.cc | 23 +++++++++ vio/viosslfactories.c | 60 +++++++++++++++++++--- 25 files changed, 189 insertions(+), 27 deletions(-) create mode 100644 mysql-test/main/tls_version.opt create mode 100644 mysql-test/main/tls_version.result create mode 100644 mysql-test/main/tls_version.test diff --git a/client/client_priv.h b/client/client_priv.h index 5b6b31ccda9..3ad46cf4b9b 100644 --- a/client/client_priv.h +++ b/client/client_priv.h @@ -46,7 +46,7 @@ enum options_client OPT_MAX_ALLOWED_PACKET, OPT_NET_BUFFER_LENGTH, OPT_SELECT_LIMIT, OPT_MAX_JOIN_SIZE, OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, OPT_SSL_CAPATH, - OPT_SSL_CIPHER, OPT_SHUTDOWN_TIMEOUT, OPT_LOCAL_INFILE, + OPT_SSL_CIPHER, OPT_TLS_VERSION, OPT_SHUTDOWN_TIMEOUT, OPT_LOCAL_INFILE, OPT_DELETE_MASTER_LOGS, OPT_COMPACT, OPT_PROMPT, OPT_IGN_LINES,OPT_TRANSACTION,OPT_MYSQL_PROTOCOL, OPT_FRM, OPT_SKIP_OPTIMIZATION, diff --git a/client/mysql.cc b/client/mysql.cc index f1311991afe..c38ce6bc962 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1363,6 +1363,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, 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 3ad6ae40ab2..10b56f10228 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -359,6 +359,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, 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 11566a8b628..d28de8aeda6 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -2126,6 +2126,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, 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 f1d59b449af..38db869c7ac 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -1709,6 +1709,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_connection, MARIADB_OPT_TLS_VERSION, 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 3e250bdd9ed..619def5f123 100644 --- a/client/mysqlimport.c +++ b/client/mysqlimport.c @@ -448,6 +448,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, 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 54c2a6e39fa..9885b219171 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, 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 be4080384c4..4b663ebd2cd 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -6068,6 +6068,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, 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/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index 24bc739ca57..f0388048cdd 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -199,6 +199,7 @@ static char* log_ignored_opt; extern my_bool opt_use_ssl; +extern char *opt_tls_version; my_bool opt_ssl_verify_server_cert; my_bool opt_extended_validation; my_bool opt_encrypted_backup; @@ -830,6 +831,7 @@ enum options_xtrabackup OPT_XTRA_CHECK_PRIVILEGES }; + struct my_option xb_client_options[] = { {"verbose", 'V', "display verbose output", diff --git a/include/sslopt-longopts.h b/include/sslopt-longopts.h index be64e7f6590..d0278a1645d 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 c65aa693b76..e28f19b919d 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 7ee00912837..4778c5521de 100644 --- a/include/violite.h +++ b/include/violite.h @@ -59,6 +59,11 @@ struct vio_keepalive_opts }; +#define VIO_TLSv1_0 1 +#define VIO_TLSv1_1 2 +#define VIO_TLSv1_2 4 +#define VIO_TLSv1_3 8 + #define VIO_LOCALHOST 1U /* a localhost connection */ #define VIO_BUFFERED_READ 2U /* use buffered read */ #define VIO_READ_BUFFER_SIZE 16384U /* size of read buffer */ @@ -148,7 +153,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); @@ -169,7 +175,8 @@ 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, + ulonglong tls_version); void free_vio_ssl_acceptor_fd(struct st_VioSSLFd *fd); #endif /* HAVE_OPENSSL */ diff --git a/mysql-test/main/mysqld--help.result b/mysql-test/main/mysqld--help.result index 402d64445e6..c5630770eff 100644 --- a/mysql-test/main/mysqld--help.result +++ b/mysql-test/main/mysqld--help.result @@ -1352,6 +1352,8 @@ The following specify which files/extra groups are read (specified before remain --time-format=name The TIME format (ignored) --timed-mutexes Specify whether to time mutexes. Deprecated, has no effect. + --tls-version=name TLS protocol version for secure connections.. Any + combination of: TLSv1.0, TLSv1.1, TLSv1.2, TLSv1.3 --tmp-disk-table-size=# Max size for data for an internal temporary on-disk MyISAM or Aria table. diff --git a/mysql-test/main/mysqld--help.test b/mysql-test/main/mysqld--help.test index c2b6424599a..9e67764d765 100644 --- a/mysql-test/main/mysqld--help.test +++ b/mysql-test/main/mysqld--help.test @@ -23,7 +23,7 @@ perl; log-slow-queries pid-file slow-query-log-file log-basename datadir slave-load-tmpdir tmpdir socket thread-pool-size large-files-support lower-case-file-system system-time-zone - collation-server character-set-server log-tc-size version.*/; + collation-server character-set-server log-tc-size tls-version version.*/; # Plugins which may or may not be there: @plugins=qw/innodb archive blackhole federated partition diff --git a/mysql-test/main/openssl_6975.test b/mysql-test/main/openssl_6975.test index 6a82d013fb6..bfcb0d56681 100644 --- a/mysql-test/main/openssl_6975.test +++ b/mysql-test/main/openssl_6975.test @@ -18,25 +18,25 @@ let $mysql=$MYSQL --ssl-key=$MYSQL_TEST_DIR/std_data/client-key.pem --ssl-cert=$ disable_abort_on_error; echo TLS1.2 ciphers: user is ok with any cipher; -exec $mysql --ssl-cipher=AES128-SHA256; +exec $mysql --tls-version=TLSv1.2 --ssl-cipher=AES128-SHA256; --replace_result DHE-RSA-CHACHA20-POLY1305 DHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-GCM-SHA384 -exec $mysql --ssl-cipher=TLSv1.2; +exec $mysql --tls-version=TLSv1.2 --ssl-cipher=TLSv1.2; echo TLS1.2 ciphers: user requires SSLv3 cipher AES128-SHA; -exec $mysql --user ssl_sslv3 --ssl-cipher=AES128-SHA256; -exec $mysql --user ssl_sslv3 --ssl-cipher=TLSv1.2; +exec $mysql --user ssl_sslv3 --tls-version=TLSv1.2 --ssl-cipher=AES128-SHA256; +exec $mysql --user ssl_sslv3 --tls-version=TLSv1.2 --ssl-cipher=TLSv1.2; echo TLS1.2 ciphers: user requires TLSv1.2 cipher AES128-SHA256; -exec $mysql --user ssl_tls12 --ssl-cipher=AES128-SHA256; -exec $mysql --user ssl_tls12 --ssl-cipher=TLSv1.2; +exec $mysql --user ssl_tls12 --tls-version=TLSv1.2 --ssl-cipher=AES128-SHA256; +exec $mysql --user ssl_tls12 --tls-version=TLSv1.2 --ssl-cipher=TLSv1.2; echo SSLv3 ciphers: user is ok with any cipher; -exec $mysql --ssl-cipher=AES256-SHA; -exec $mysql --ssl-cipher=SSLv3; +exec $mysql --tls-version=TLSv1.0,TLSv1.1,TLSv1.2 --ssl-cipher=AES256-SHA; +exec $mysql --tls-version=TLSv1.0,TLSv1.1,TLSv1.2 --ssl-cipher=SSLv3; echo SSLv3 ciphers: user requires SSLv3 cipher AES128-SHA; -exec $mysql --user ssl_sslv3 --ssl-cipher=AES128-SHA; -exec $mysql --user ssl_sslv3 --ssl-cipher=SSLv3; +exec $mysql --user ssl_sslv3 --tls-version=TLSv1.0,TLSv1.1,TLSv1.2 --ssl-cipher=AES128-SHA; +exec $mysql --user ssl_sslv3 --tls-version=TLSv1.0,TLSv1.1,TLSv1.2 --ssl-cipher=SSLv3; echo SSLv3 ciphers: user requires TLSv1.2 cipher AES128-SHA256; -exec $mysql --user ssl_tls12 --ssl-cipher=AES128-SHA; -exec $mysql --user ssl_tls12 --ssl-cipher=SSLv3; +exec $mysql --user ssl_tls12 --tls-version=TLSv1.0,TLSv1.1,TLSv1.2 --ssl-cipher=AES128-SHA; +exec $mysql --user ssl_tls12 --tls-version=TLSv1.0,TLSv1.1,TLSv1.2 --ssl-cipher=SSLv3; drop user ssl_sslv3@localhost; drop user ssl_tls12@localhost; diff --git a/mysql-test/main/tls_version.opt b/mysql-test/main/tls_version.opt new file mode 100644 index 00000000000..5f30e3749a4 --- /dev/null +++ b/mysql-test/main/tls_version.opt @@ -0,0 +1 @@ +--tls_version=TLSv1.1,TLSv1.2 diff --git a/mysql-test/main/tls_version.result b/mysql-test/main/tls_version.result new file mode 100644 index 00000000000..d1b20a121fe --- /dev/null +++ b/mysql-test/main/tls_version.result @@ -0,0 +1,14 @@ +Variable_name Value +Ssl_version TLSv1.2 +Variable_name Value +Ssl_version TLSv1.2 +Variable_name Value +Ssl_version TLSv1.1 +Variable_name Value +Ssl_version TLSv1.1 +Variable_name Value +Ssl_version TLSv1.2 +Variable_name Value +Ssl_version TLSv1.2 +@@tls_version +TLSv1.1,TLSv1.2 diff --git a/mysql-test/main/tls_version.test b/mysql-test/main/tls_version.test new file mode 100644 index 00000000000..875fed19821 --- /dev/null +++ b/mysql-test/main/tls_version.test @@ -0,0 +1,24 @@ +# Tests for SSL connections, only run if mysqld is compiled +# with support for SSL. + +-- source include/have_ssl_communication.inc +#default is highest available version: TLSv1.2 +--exec $MYSQL --host=localhost --ssl -e "show status like 'ssl_version';" +# TLSv1.2 +--exec $MYSQL --host=localhost --ssl --tls_version=TLSv1.2 -e "show status like 'ssl_version';" +# TLSv1.1 +--exec $MYSQL --host=localhost --ssl --tls_version=TLSv1.1 -e "show status like 'ssl_version';" +# if a gap is between TLS versions, lowest version number should be used (TLS1.1) +--exec $MYSQL --host=localhost --ssl --tls_version=TLSv1.1,TLSv1.3 -e "show status like 'ssl_version';" +# TLSv1.3 is not enabled, so TLSv1.2 should be used +--exec $MYSQL --host=localhost --ssl --tls_version=TLSv1.2,TLSv1.3 -e "show status like 'ssl_version';" +# Highest TLS version number should be used (TLSv1.2) +--exec $MYSQL --host=localhost --ssl --tls_version=TLSv1.1,TLSv1.2 -e "show status like 'ssl_version';" +# Errors: +# TLS v1.0 is disabled on server, so we should get an error +--replace_regex /2026 SSL connection error.*/2026 SSL connection error: xxxx/ +--error 1 +--exec $MYSQL --host=localhost --ssl --tls_version=TLSv1.0 -e "show status like 'ssl_version';" +# finally list available protocols +--exec $MYSQL --host=localhost --ssl -e "select @@tls_version;" + diff --git a/mysql-test/suite/sys_vars/inc/sysvars_server.inc b/mysql-test/suite/sys_vars/inc/sysvars_server.inc index af1b0ce4563..56053449bae 100644 --- a/mysql-test/suite/sys_vars/inc/sysvars_server.inc +++ b/mysql-test/suite/sys_vars/inc/sysvars_server.inc @@ -31,6 +31,7 @@ select * from information_schema.system_variables 'rand_seed1', 'rand_seed2', 'system_time_zone', + 'tls_version', 'version_comment', 'version_source_revision', 'version_compile_machine', 'version_compile_os', @@ -55,6 +56,7 @@ select VARIABLE_NAME, VARIABLE_SCOPE, VARIABLE_TYPE, VARIABLE_COMMENT, 'rand_seed1', 'rand_seed2', 'system_time_zone', + 'tls_version', 'version_comment', 'version_source_revision', 'version_compile_machine', 'version_compile_os', diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result index 509e098498d..0b587ab82c2 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -20,6 +20,7 @@ variable_name not in ( 'rand_seed1', 'rand_seed2', 'system_time_zone', +'tls_version', 'version_comment', 'version_source_revision', 'version_compile_machine', 'version_compile_os', @@ -4661,6 +4662,7 @@ where variable_name in ( 'rand_seed1', 'rand_seed2', 'system_time_zone', +'tls_version', 'version_comment', 'version_source_revision', 'version_compile_machine', 'version_compile_os', @@ -4767,6 +4769,16 @@ NUMERIC_BLOCK_SIZE NULL ENUM_VALUE_LIST NULL READ_ONLY YES COMMAND_LINE_ARGUMENT NULL +VARIABLE_NAME TLS_VERSION +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE SET +VARIABLE_COMMENT TLS protocol version for secure connections. +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST TLSv1.0,TLSv1.1,TLSv1.2,TLSv1.3 +READ_ONLY YES +COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME VERSION VARIABLE_SCOPE GLOBAL VARIABLE_TYPE VARCHAR diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index aa75bb6e2f1..134c457dd8d 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -20,6 +20,7 @@ variable_name not in ( 'rand_seed1', 'rand_seed2', 'system_time_zone', +'tls_version', 'version_comment', 'version_source_revision', 'version_compile_machine', 'version_compile_os', @@ -5725,6 +5726,7 @@ where variable_name in ( 'rand_seed1', 'rand_seed2', 'system_time_zone', +'tls_version', 'version_comment', 'version_source_revision', 'version_compile_machine', 'version_compile_os', @@ -5831,6 +5833,16 @@ NUMERIC_BLOCK_SIZE NULL ENUM_VALUE_LIST NULL READ_ONLY YES COMMAND_LINE_ARGUMENT NULL +VARIABLE_NAME TLS_VERSION +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE SET +VARIABLE_COMMENT TLS protocol version for secure connections. +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST TLSv1.0,TLSv1.1,TLSv1.2,TLSv1.3 +READ_ONLY YES +COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME VERSION VARIABLE_SCOPE GLOBAL VARIABLE_TYPE VARCHAR diff --git a/sql/mysqld.cc b/sql/mysqld.cc index ea49e28384b..289d45cddee 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1444,8 +1444,8 @@ 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; +ulonglong tls_version= 0; static scheduler_functions thread_scheduler_struct, extra_thread_scheduler_struct; scheduler_functions *thread_scheduler= &thread_scheduler_struct, @@ -4722,7 +4722,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, + tls_version); DBUG_PRINT("info",("ssl_acceptor_fd: %p", ssl_acceptor_fd)); if (!ssl_acceptor_fd) { @@ -4761,7 +4762,8 @@ int reinit_ssl() enum enum_ssl_init_error error = SSL_INITERR_NOERROR; st_VioSSLFd *new_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_ca, opt_ssl_capath, opt_ssl_cipher, &error, opt_ssl_crl, + opt_ssl_crlpath, tls_version); if (!new_fd) { diff --git a/sql/mysqld.h b/sql/mysqld.h index a29ca9ef8fe..c5e7872262a 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -635,6 +635,7 @@ extern Atomic_counter 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; +extern ulonglong tls_version; extern MYSQL_PLUGIN_IMPORT pthread_key(THD*, THR_THD); @@ -697,6 +698,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 1160e936a2b..f2a6000843d 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -3565,6 +3565,29 @@ 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 const char *tls_version_names[]= +{ + "TLSv1.0", + "TLSv1.1", + "TLSv1.2", + "TLSv1.3", + 0 +}; + +export bool tls_version_string_representation(THD *thd, sql_mode_t sql_mode, + LEX_CSTRING *ls) +{ + set_to_string(thd, ls, tls_version, tls_version_names); + return ls->str == 0; +} + +static Sys_var_set Sys_tls_version( + "tls_version", + "TLS protocol version for secure connections.", + READ_ONLY GLOBAL_VAR(tls_version), CMD_LINE(REQUIRED_ARG), + tls_version_names, + DEFAULT(VIO_TLSv1_1 | VIO_TLSv1_2 | VIO_TLSv1_3)); + 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 033d71779ab..4a31ebd1798 100644 --- a/vio/viosslfactories.c +++ b/vio/viosslfactories.c @@ -83,7 +83,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* @@ -183,21 +184,57 @@ static int wolfssl_send(WOLFSSL* ssl, char* buf, int sz, void* vio) } #endif /* HAVE_WOLFSSL */ +static long vio_tls_protocol_options(ulonglong tls_version) +{ + long tls_protocol_flags= +#ifdef TLS1_3_VERSION + SSL_OP_NO_TLSv1_3 | +#endif +#if defined(TLS1_2_VERSION) || defined(HAVE_WOLFSSL) + SSL_OP_NO_TLSv1_2 | +#endif + SSL_OP_NO_TLSv1_1 | + 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 (tls_version & VIO_TLSv1_0) + disabled_tls_protocols&= ~SSL_OP_NO_TLSv1; + if (tls_version & VIO_TLSv1_1) + disabled_tls_protocols&= ~SSL_OP_NO_TLSv1_1; +#if defined(TLS1_2_VERSION) || defined(HAVE_WOLFSSL) + if (tls_version & VIO_TLSv1_2) + disabled_tls_protocols&= ~SSL_OP_NO_TLSv1_2; +#endif +#ifdef TLS1_3_VERSION + if (tls_version & VIO_TLSv1_3) + disabled_tls_protocols&= ~SSL_OP_NO_TLSv1_3; +#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, ulonglong tls_version) { 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'", key_file ? key_file : "NULL", cert_file ? cert_file : "NULL", ca_file ? ca_file : "NULL", @@ -220,6 +257,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); /* @@ -342,7 +387,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, 0))) { return 0; } @@ -360,13 +405,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, + ulonglong 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; } -- cgit v1.2.1