mirror of
https://github.com/MariaDB/server.git
synced 2025-01-15 19:42:28 +01:00
MDEV-31855 validate ssl certificates using client password
if the client enabled --ssl-verify-server-cert, then the server certificate is verified as follows: * if --ssl-ca or --ssl-capath were specified, the cert must have a proper signature by the specified CA (or CA in the path) and the cert's hostname must match the server's hostname. If the cert isn't signed or a hostname is wrong - the connection is aborted. * if MARIADB_OPT_TLS_PEER_FP was used and the fingerprint matches, the connection is allowed, if it doesn't match - aborted. * If the connection uses unix socket or named pipes - it's allowed. (consistent with server's --require-secure-transport behavior) otherwise the cert is still in doubt, we don't know if we can trust it or there's an active MitM in progress. * If the user has provided no password or the server requested an authentication plugin that sends the password in cleartext - the connection is aborted. * Perform the authentication. If the server accepts the password, it'll send SHA2(scramble || password hash || cert fingerprint) with the OK packet. * Verify the SHA2 digest, if it matches - the connection is allowed, otherwise it's aborted.
This commit is contained in:
parent
585c096aa5
commit
1ef1bab99e
13 changed files with 196 additions and 18 deletions
1
debian/libmariadb-dev.install
vendored
1
debian/libmariadb-dev.install
vendored
|
@ -20,7 +20,6 @@ usr/include/mariadb/mysql.h
|
|||
usr/include/mariadb/mysql/
|
||||
usr/include/mariadb/mysql/client_plugin.h
|
||||
usr/include/mariadb/mysql/plugin_auth.h
|
||||
usr/include/mariadb/mysql/plugin_auth_common.h
|
||||
usr/include/mariadb/mysql_com.h
|
||||
usr/include/mariadb/mysql_version.h
|
||||
usr/include/mariadb/mysqld_error.h
|
||||
|
|
|
@ -53,8 +53,7 @@
|
|||
|
||||
#ifdef MYSQL_CLIENT
|
||||
{"ssl-verify-server-cert", OPT_SSL_VERIFY_SERVER_CERT,
|
||||
"Verify server's \"Common Name\" in its cert against hostname used "
|
||||
"when connecting. This option is disabled by default.",
|
||||
"Verify server's certificate to prevent man-in-the-middle attacks",
|
||||
&opt_ssl_verify_server_cert, &opt_ssl_verify_server_cert,
|
||||
0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
|
||||
#endif
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 75ab6fb1746824648ce09805acbe535f9501df37
|
||||
Subproject commit fcef411ecb4a2c013e7aac655a96669474110225
|
|
@ -27,7 +27,7 @@ openssl rsa -in server-key.pem -out server-key.pem
|
|||
# sign the server certificate with CA certificate
|
||||
openssl ca -keyfile cakey.pem -days 7300 -batch -cert cacert.pem -policy policy_anything -out server-cert.pem -in demoCA/server-req.pem
|
||||
|
||||
# server certificate with different validity period (MDEV-7598)
|
||||
# server certificate with different validity period (MDEV-16266)
|
||||
openssl req -newkey rsa:4096 -keyout server-new-key.pem -out demoCA/server-new-req.pem -days 7301 -nodes -subj '/CN=server-new/C=FI/ST=Helsinki/L=Helsinki/O=MariaDB'
|
||||
openssl rsa -in server-new-key.pem -out server-new-key.pem
|
||||
openssl ca -keyfile cakey.pem -days 7301 -batch -cert cacert.pem -policy policy_anything -out server-new-cert.pem -in demoCA/server-new-req.pem
|
||||
|
|
|
@ -6666,7 +6666,7 @@ drop table t1;
|
|||
mariadb-dump: --xml can't be used with --tab.
|
||||
select @@max_connections into @save_max_connections;
|
||||
set global max_connections=10;
|
||||
mariadb-dump: Got error: 1040: "Too many connections" when trying to connect
|
||||
mariadb-dump: Got error: 2002: "Received error packet before completion of TLS handshake. The authenticity of the following error cannot be verified: 1040 - Too many connections" when trying to connect
|
||||
set global max_connections=300;
|
||||
mariadb-dump: Too many connections
|
||||
set global max_connections=@save_max_connections;
|
||||
|
|
|
@ -14,15 +14,15 @@ create procedure have_ssl()
|
|||
|
||||
--disable_abort_on_error
|
||||
--echo mysql --ssl-ca=cacert.pem -e "call test.have_ssl()"
|
||||
--exec $MYSQL --ssl-ca=$MYSQL_TEST_DIR/std_data/cacert.pem -e "call test.have_ssl()" 2>&1
|
||||
--exec $MYSQL --protocol tcp --ssl-ca=$MYSQL_TEST_DIR/std_data/cacert.pem -e "call test.have_ssl()" 2>&1
|
||||
--echo mysql --ssl -e "call test.have_ssl()"
|
||||
--exec $MYSQL --ssl -e "call test.have_ssl()" 2>&1
|
||||
--exec $MYSQL --protocol tcp --ssl -e "call test.have_ssl()" 2>&1
|
||||
--echo mysql --ssl-ca=cacert.pem --ssl-verify-server-cert -e "call test.have_ssl()"
|
||||
--exec $MYSQL --ssl-ca=$MYSQL_TEST_DIR/std_data/cacert.pem --ssl-verify-server-cert -e "call test.have_ssl()" 2>&1
|
||||
--exec $MYSQL --protocol tcp --ssl-ca=$MYSQL_TEST_DIR/std_data/cacert.pem --ssl-verify-server-cert -e "call test.have_ssl()" 2>&1
|
||||
|
||||
--echo mysql --ssl --ssl-verify-server-cert -e "call test.have_ssl()"
|
||||
--replace_regex /TLS\/SSL error.*certificate[^\n]*/TLS\/SSL error: Failed to verify the server certificate/
|
||||
--exec $MYSQL --ssl --ssl-verify-server-cert -e "call test.have_ssl()" 2>&1
|
||||
--exec $MYSQL --protocol tcp --ssl --ssl-verify-server-cert -e "call test.have_ssl()" 2>&1
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-27105 --ssl option set as default for mariadb CLI
|
||||
|
|
14
mysql-test/main/ssl_autoverify,win.rdiff
Normal file
14
mysql-test/main/ssl_autoverify,win.rdiff
Normal file
|
@ -0,0 +1,14 @@
|
|||
--- main/ssl_autoverify.reject
|
||||
+++ main/ssl_autoverify.result
|
||||
@@ -18,9 +18,9 @@
|
||||
yes
|
||||
# mysql -uroot --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
ERROR 2026 (HY000): TLS/SSL error: Failed to verify the server certificate
|
||||
-# mysql --protocol socket -uroot --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
+# mysql --protocol pipe -uroot --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
test.have_ssl()
|
||||
-yes
|
||||
+no
|
||||
# mysql -unative -pfoo --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
test.have_ssl()
|
||||
yes
|
1
mysql-test/main/ssl_autoverify.opt
Normal file
1
mysql-test/main/ssl_autoverify.opt
Normal file
|
@ -0,0 +1 @@
|
|||
--loose-enable-named-pipe
|
47
mysql-test/main/ssl_autoverify.result
Normal file
47
mysql-test/main/ssl_autoverify.result
Normal file
|
@ -0,0 +1,47 @@
|
|||
install soname 'auth_ed25519';
|
||||
install plugin three_attempts soname 'dialog_examples';
|
||||
create user native@'%' identified via mysql_native_password using password('foo');
|
||||
create user ed@'%' identified via ed25519 using password('bar');
|
||||
create user nohash@'%' identified via three_attempts using 'onetwothree';
|
||||
create user multi@'%' identified via mysql_native_password using password('pw1')
|
||||
or ed25519 using password('pw2');
|
||||
grant all privileges on test.* to native@'%';
|
||||
grant all privileges on test.* to ed@'%';
|
||||
grant all privileges on test.* to nohash@'%';
|
||||
grant all privileges on test.* to multi@'%';
|
||||
create function have_ssl() returns char(3)
|
||||
return (select if(variable_value > '','yes','no') as 'have_ssl'
|
||||
from information_schema.session_status
|
||||
where variable_name='ssl_cipher');
|
||||
# mysql -uroot --disable-ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
test.have_ssl()
|
||||
yes
|
||||
# mysql -uroot --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
ERROR 2026 (HY000): TLS/SSL error: Failed to verify the server certificate
|
||||
# mysql --protocol socket -uroot --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
test.have_ssl()
|
||||
yes
|
||||
# mysql -unative -pfoo --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
test.have_ssl()
|
||||
yes
|
||||
# mysql -ued -pbar --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
test.have_ssl()
|
||||
yes
|
||||
# mysql -unohash -ponetwothree --disable-ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
test.have_ssl()
|
||||
yes
|
||||
# mysql -unohash -ponetwothree --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
ERROR 2026 (HY000): TLS/SSL error: Failed to verify the server certificate
|
||||
# mysql -umulti -ppw1 --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
test.have_ssl()
|
||||
yes
|
||||
# mysql -umulti -ppw2 --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
test.have_ssl()
|
||||
yes
|
||||
drop function have_ssl;
|
||||
drop user native@'%';
|
||||
drop user ed@'%';
|
||||
drop user nohash@'%';
|
||||
drop user multi@'%';
|
||||
uninstall plugin ed25519;
|
||||
uninstall plugin three_attempts;
|
84
mysql-test/main/ssl_autoverify.test
Normal file
84
mysql-test/main/ssl_autoverify.test
Normal file
|
@ -0,0 +1,84 @@
|
|||
source include/platform.inc;
|
||||
source include/not_embedded.inc;
|
||||
if (!$AUTH_ED25519_SO) {
|
||||
skip No auth_ed25519 plugin;
|
||||
}
|
||||
if (!$DIALOG_EXAMPLES_SO) {
|
||||
skip No dialog_examples plugin;
|
||||
}
|
||||
|
||||
install soname 'auth_ed25519';
|
||||
install plugin three_attempts soname 'dialog_examples';
|
||||
|
||||
create user native@'%' identified via mysql_native_password using password('foo');
|
||||
create user ed@'%' identified via ed25519 using password('bar');
|
||||
create user nohash@'%' identified via three_attempts using 'onetwothree';
|
||||
create user multi@'%' identified via mysql_native_password using password('pw1')
|
||||
or ed25519 using password('pw2');
|
||||
grant all privileges on test.* to native@'%';
|
||||
grant all privileges on test.* to ed@'%';
|
||||
grant all privileges on test.* to nohash@'%';
|
||||
grant all privileges on test.* to multi@'%';
|
||||
|
||||
create function have_ssl() returns char(3)
|
||||
return (select if(variable_value > '','yes','no') as 'have_ssl'
|
||||
from information_schema.session_status
|
||||
where variable_name='ssl_cipher');
|
||||
|
||||
#
|
||||
# root user, no password, so cannot validate cert.
|
||||
#
|
||||
--echo # mysql -uroot --disable-ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
--exec $MYSQL --protocol tcp -uroot --disable-ssl-verify-server-cert -e "select test.have_ssl()" 2>&1
|
||||
--echo # mysql -uroot --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
--replace_regex /TLS\/SSL error.*certificate[^\n]*/TLS\/SSL error: Failed to verify the server certificate/
|
||||
--error 1
|
||||
--exec $MYSQL --protocol tcp -uroot --ssl-verify-server-cert -e "select test.have_ssl()" 2>&1
|
||||
#
|
||||
# unless using a secure transport, like unix_socket or named pipes
|
||||
#
|
||||
# note that SSL works over unix_socket, and it doesn't work over named pipes
|
||||
# but the connection is allowed either way, as the transport is secure
|
||||
#
|
||||
let proto=socket;
|
||||
if ($MTR_COMBINATION_WIN) {
|
||||
let proto=pipe;
|
||||
}
|
||||
--echo # mysql --protocol $proto -uroot --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
--exec $MYSQL --protocol $proto -uroot --ssl-verify-server-cert -e "select test.have_ssl()" 2>&1
|
||||
#
|
||||
# mysql_native_password with password works fine
|
||||
#
|
||||
--echo # mysql -unative -pfoo --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
--exec $MYSQL --protocol tcp -unative -pfoo --ssl-verify-server-cert -e "select test.have_ssl()" 2>&1
|
||||
#
|
||||
# ed25519 with password works fine
|
||||
#
|
||||
--echo # mysql -ued -pbar --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
--exec $MYSQL --protocol tcp -ued -pbar --ssl-verify-server-cert -e "select test.have_ssl()" 2>&1
|
||||
#
|
||||
# three_attempts uses auth string as is, doesn't hash.
|
||||
# so it's not safe over untrusted connection and thus cannot validate cert
|
||||
#
|
||||
--echo # mysql -unohash -ponetwothree --disable-ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
--exec $MYSQL --protocol tcp -unohash -ponetwothree --disable-ssl-verify-server-cert -e "select test.have_ssl()" 2>&1
|
||||
--echo # mysql -unohash -ponetwothree --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
--replace_regex /TLS\/SSL error.*certificate[^\n]*/TLS\/SSL error: Failed to verify the server certificate/
|
||||
--error 1
|
||||
--exec $MYSQL --protocol tcp -unohash -ponetwothree --ssl-verify-server-cert -e "select test.have_ssl()" 2>&1
|
||||
#
|
||||
# multi-auth case, both client and server must use
|
||||
# the same plugin for cert validation
|
||||
#
|
||||
--echo # mysql -umulti -ppw1 --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
--exec $MYSQL --protocol tcp -umulti -ppw1 --ssl-verify-server-cert -e "select test.have_ssl()" 2>&1
|
||||
--echo # mysql -umulti -ppw2 --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
--exec $MYSQL --protocol tcp -umulti -ppw2 --ssl-verify-server-cert -e "select test.have_ssl()" 2>&1
|
||||
|
||||
drop function have_ssl;
|
||||
drop user native@'%';
|
||||
drop user ed@'%';
|
||||
drop user nohash@'%';
|
||||
drop user multi@'%';
|
||||
uninstall plugin ed25519;
|
||||
uninstall plugin three_attempts;
|
|
@ -4518,6 +4518,7 @@ struct SSL_ACCEPTOR_STATS
|
|||
long verify_depth;
|
||||
long zero;
|
||||
const char *session_cache_mode;
|
||||
uchar fprint[256/8];
|
||||
|
||||
SSL_ACCEPTOR_STATS():
|
||||
accept(),accept_good(),cache_size(),verify_mode(),verify_depth(),zero(),
|
||||
|
@ -4527,7 +4528,8 @@ struct SSL_ACCEPTOR_STATS
|
|||
|
||||
void init()
|
||||
{
|
||||
DBUG_ASSERT(ssl_acceptor_fd !=0 && ssl_acceptor_fd->ssl_context != 0);
|
||||
DBUG_ASSERT(ssl_acceptor_fd !=0);
|
||||
DBUG_ASSERT(ssl_acceptor_fd->ssl_context != 0);
|
||||
SSL_CTX *ctx= ssl_acceptor_fd->ssl_context;
|
||||
accept= 0;
|
||||
accept_good= 0;
|
||||
|
@ -4551,6 +4553,9 @@ struct SSL_ACCEPTOR_STATS
|
|||
default:
|
||||
session_cache_mode= "Unknown"; break;
|
||||
}
|
||||
X509 *cert= SSL_CTX_get0_certificate(ctx);
|
||||
uint fplen= sizeof(fprint);
|
||||
X509_digest(cert, EVP_sha256(), fprint, &fplen);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -4562,6 +4567,11 @@ void ssl_acceptor_stats_update(int sslaccept_ret)
|
|||
statistic_increment(ssl_acceptor_stats.accept_good,&LOCK_status);
|
||||
}
|
||||
|
||||
LEX_CUSTRING ssl_acceptor_fingerprint()
|
||||
{
|
||||
return { ssl_acceptor_stats.fprint, sizeof(ssl_acceptor_stats.fprint) };
|
||||
}
|
||||
|
||||
static void init_ssl()
|
||||
{
|
||||
#if !defined(EMBEDDED_LIBRARY)
|
||||
|
|
|
@ -728,6 +728,7 @@ extern pthread_t signal_thread;
|
|||
|
||||
#ifdef HAVE_OPENSSL
|
||||
extern struct st_VioSSLFd * ssl_acceptor_fd;
|
||||
extern LEX_CUSTRING ssl_acceptor_fingerprint();
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
/*
|
||||
|
|
|
@ -201,15 +201,11 @@ public:
|
|||
};
|
||||
|
||||
|
||||
class ACL_USER :public ACL_USER_BASE,
|
||||
public ACL_USER_PARAM
|
||||
class ACL_USER :public ACL_USER_BASE, public ACL_USER_PARAM
|
||||
{
|
||||
public:
|
||||
|
||||
ACL_USER() = default;
|
||||
ACL_USER(THD *thd, const LEX_USER &combo,
|
||||
const Account_options &options,
|
||||
const privilege_t privileges);
|
||||
ACL_USER(THD *, const LEX_USER &, const Account_options &, const privilege_t);
|
||||
|
||||
ACL_USER *copy(MEM_ROOT *root)
|
||||
{
|
||||
|
@ -14390,6 +14386,29 @@ static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void make_ssl_info(THD *thd, LEX_CSTRING salt, char *info)
|
||||
{
|
||||
#ifdef HAVE_OPENSSL
|
||||
uchar digest[256/8];
|
||||
if (!salt.length)
|
||||
return;
|
||||
|
||||
/*
|
||||
mark that it's after-auth mysql->info version 1.
|
||||
meaning, it contains sha2(salt, scramble, sha2_cert_fingerprint)
|
||||
encoded in 64 lowercase letters 'a'..'p', one letter per 4 bits (0..15)
|
||||
*/
|
||||
*info++= 1; // Version 1
|
||||
|
||||
DBUG_ASSERT(thd->scramble[SCRAMBLE_LENGTH] == 0);
|
||||
|
||||
LEX_CUSTRING fp= ssl_acceptor_fingerprint();
|
||||
my_sha256_multi(digest, salt.str, salt.length, thd->scramble,
|
||||
(size_t)SCRAMBLE_LENGTH, fp.str, fp.length, NULL);
|
||||
octet2hex(info, digest, sizeof(digest));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static int do_auth_once(THD *thd, const LEX_CSTRING *auth_plugin_name,
|
||||
MPVIO_EXT *mpvio)
|
||||
|
@ -14507,6 +14526,7 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
|
|||
{
|
||||
int res= CR_OK;
|
||||
MPVIO_EXT mpvio;
|
||||
char ssl_info[256/4 + 2]= {0}; // '\1', SHA256 (1 char per 4 bits), '\0'
|
||||
enum enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
|
||||
: COM_CONNECT;
|
||||
DBUG_ENTER("acl_authenticate");
|
||||
|
@ -14844,7 +14864,10 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
|
|||
sctx->external_user= my_strdup(key_memory_MPVIO_EXT_auth_info,
|
||||
mpvio.auth_info.external_user, MYF(0));
|
||||
|
||||
my_ok(thd);
|
||||
if (initialized && !com_change_user_pkt_len)
|
||||
make_ssl_info(thd, acl_user->auth[mpvio.curr_auth-1].salt, ssl_info);
|
||||
|
||||
my_ok(thd, 0, 0, ssl_info[0] == '\1' ? ssl_info : NULL);
|
||||
|
||||
PSI_CALL_set_thread_account
|
||||
(thd->main_security_ctx.user, static_cast<uint>(strlen(thd->main_security_ctx.user)),
|
||||
|
|
Loading…
Reference in a new issue