diff options
author | Daniel Black <daniel@mariadb.org> | 2020-07-16 16:31:59 +1000 |
---|---|---|
committer | Daniel Black <daniel@mariadb.org> | 2020-10-31 09:14:37 +1100 |
commit | 5b779c220dbe644c9fc4cfcfe15a017ed5e4ff9c (patch) | |
tree | c0fbd278d1414a0521580cbf4cace015cd91f412 | |
parent | 1fddccf676e213f94923f5efaaa76d9793b19a89 (diff) | |
download | mariadb-git-5b779c220dbe644c9fc4cfcfe15a017ed5e4ff9c.tar.gz |
MDEV-22974: mysql_native_password make "invalid" valid
Per b9f3f06857ac, mysql_system_tables_data.sql creates
a mysql_native_password with a salted hash of "invalid" so that `set password`
will detect a native password can be applied:.
SHOW CREATE USER; diligently uses this value in its output
generating the SQL:
MariaDB [(none)]> show create user;
+---------------------------------------------------------------------------------------------------+
| CREATE USER for dan@localhost |
+---------------------------------------------------------------------------------------------------+
| CREATE USER `dan`@`localhost` IDENTIFIED VIA mysql_native_password USING 'invalid' OR unix_socket |
+---------------------------------------------------------------------------------------------------+
Attempting to execute this before this patch results in:
MariaDB [(none)]> CREATE USER `dan2`@`localhost` IDENTIFIED VIA mysql_native_password USING 'invalid' OR unix_socket;
ERROR 1372 (HY000): Password hash should be a 41-digit hexadecimal number
As such, deep the implementation of mysql_native_password we make "invalid" valid (pun intended)
such that the above create user will succeed. We do this by storing
"*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE" (credit: Oracle MySQL), that is of an INCORRECT
length for a scramble.
In native_password_authenticate we check the length of this cached value
and immediately fail if it is anything other than the scramble length.
native_password_get_salt is only called in the context of set_user_salt, so all setting of native
passwords to hashed content of 'invalid', quite literally create an invalid password.
So other forms of "invalid" are valid SQL in creating invalid passwords:
MariaDB [(none)]> set password = 'invalid';
Query OK, 0 rows affected (0.001 sec)
MariaDB [(none)]> alter user dan@localhost IDENTIFIED BY PASSWORD 'invalid';
Query OK, 0 rows affected (0.000 sec)
closes #1628
Reviewer: serg@mariadb.com
-rw-r--r-- | mysql-test/main/alter_user.result | 4 | ||||
-rw-r--r-- | mysql-test/main/alter_user.test | 3 | ||||
-rw-r--r-- | mysql-test/main/set_password.result | 20 | ||||
-rw-r--r-- | mysql-test/main/set_password.test | 19 | ||||
-rw-r--r-- | sql/sql_acl.cc | 12 |
5 files changed, 57 insertions, 1 deletions
diff --git a/mysql-test/main/alter_user.result b/mysql-test/main/alter_user.result index 6f88f0311bd..0dcec5835b7 100644 --- a/mysql-test/main/alter_user.result +++ b/mysql-test/main/alter_user.result @@ -61,6 +61,10 @@ alter user foo identified by password '*88C89BE093D4ECF72D039F62EBB7477EA1FD4D63 select * from mysql.user where user = 'foo'; Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv Delete_history_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string password_expired is_role default_role max_statement_time % foo *88C89BE093D4ECF72D039F62EBB7477EA1FD4D63 N N N N N N N N N N N N N N N Y N N N N N N N N N Y N N N N 0 0 0 0 mysql_native_password *88C89BE093D4ECF72D039F62EBB7477EA1FD4D63 N N 0.000000 +alter user foo identified by password 'invalid'; +select * from mysql.user where user = 'foo'; +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv Delete_history_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string password_expired is_role default_role max_statement_time +% foo invalid N N N N N N N N N N N N N N N Y N N N N N N N N N Y N N N N 0 0 0 0 mysql_native_password invalid N N 0.000000 alter user foo identified with 'somecoolplugin'; ERROR HY000: Operation ALTER USER failed for 'foo'@'%' show warnings; diff --git a/mysql-test/main/alter_user.test b/mysql-test/main/alter_user.test index 1e9a9b5625b..5a05a671277 100644 --- a/mysql-test/main/alter_user.test +++ b/mysql-test/main/alter_user.test @@ -55,6 +55,9 @@ select * from mysql.user where user = 'foo'; alter user foo identified by password '*88C89BE093D4ECF72D039F62EBB7477EA1FD4D63'; select * from mysql.user where user = 'foo'; +alter user foo identified by password 'invalid'; +select * from mysql.user where user = 'foo'; + --error ER_CANNOT_USER alter user foo identified with 'somecoolplugin'; show warnings; diff --git a/mysql-test/main/set_password.result b/mysql-test/main/set_password.result index 8a8af3b3350..6a4e0da937e 100644 --- a/mysql-test/main/set_password.result +++ b/mysql-test/main/set_password.result @@ -1,14 +1,21 @@ set global secure_auth=0; create user natauth@localhost identified via 'mysql_native_password' using '*94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29'; +create user invalidauth@localhost identified via 'mysql_native_password' using 'invalid'; create user newpass@localhost identified by password '*94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29'; +create user invalidpass@localhost identified by password 'invalid'; create user newpassnat@localhost identified via 'mysql_native_password'; set password for newpassnat@localhost = '*94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29'; +create user invalidpassnat@localhost identified by password 'invalid'; +set password for invalidpassnat@localhost = 'invalid'; create user oldauth@localhost identified with 'mysql_old_password' using '378b243e220ca493'; create user oldpass@localhost identified by password '378b243e220ca493'; create user oldpassold@localhost identified with 'mysql_old_password'; set password for oldpassold@localhost = '378b243e220ca493'; select user, host, password, plugin, authentication_string from mysql.user where user != 'root'; User Host Password plugin authentication_string +invalidauth localhost invalid mysql_native_password invalid +invalidpass localhost invalid mysql_native_password invalid +invalidpassnat localhost invalid mysql_native_password invalid mariadb.sys localhost mysql_native_password natauth localhost *94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29 mysql_native_password *94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29 newpass localhost *94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29 mysql_native_password *94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29 @@ -87,6 +94,9 @@ set password for oldpass@localhost = PASSWORD('test2'); set password for oldpassold@localhost = PASSWORD('test2'); select user, host, password, plugin, authentication_string from mysql.user where user != 'root'; User Host Password plugin authentication_string +invalidauth localhost invalid mysql_native_password invalid +invalidpass localhost invalid mysql_native_password invalid +invalidpassnat localhost invalid mysql_native_password invalid mariadb.sys localhost mysql_native_password natauth localhost *7CEB3FDE5F7A9C4CE5FBE610D7D8EDA62EBE5F4E mysql_native_password *7CEB3FDE5F7A9C4CE5FBE610D7D8EDA62EBE5F4E newpass localhost *7CEB3FDE5F7A9C4CE5FBE610D7D8EDA62EBE5F4E mysql_native_password *7CEB3FDE5F7A9C4CE5FBE610D7D8EDA62EBE5F4E @@ -141,6 +151,15 @@ select current_user(); current_user() newpassnat@localhost disconnect con; +connect(localhost,invalidauth,invalid,test,MASTER_PORT,MASTER_SOCKET); +connect con,localhost,invalidauth,invalid,; +ERROR 28000: Access denied for user 'invalidauth'@'localhost' (using password: YES) +connect(localhost,invalidpass,invalid,test,MASTER_PORT,MASTER_SOCKET); +connect con,localhost,invalidpass,invalid,; +ERROR 28000: Access denied for user 'invalidpass'@'localhost' (using password: YES) +connect(localhost,invalidpassnat,invalid,test,MASTER_PORT,MASTER_SOCKET); +connect con,localhost,invalidpassnat,invalid,; +ERROR 28000: Access denied for user 'invalidpassnat'@'localhost' (using password: YES) connect con,localhost,oldauth,test2,; select current_user(); current_user() @@ -158,6 +177,7 @@ oldpassold@localhost disconnect con; connection default; drop user natauth@localhost, newpass@localhost, newpassnat@localhost; +drop user invalidauth@localhost, invalidpass@localhost, invalidpassnat@localhost; drop user oldauth@localhost, oldpass@localhost, oldpassold@localhost; set global secure_auth=default; # switching from mysql.global_priv to mysql.user diff --git a/mysql-test/main/set_password.test b/mysql-test/main/set_password.test index c67dc22dc81..dd5e261346b 100644 --- a/mysql-test/main/set_password.test +++ b/mysql-test/main/set_password.test @@ -12,11 +12,18 @@ set global secure_auth=0; # The hash (old and new) is for 'test' create user natauth@localhost identified via 'mysql_native_password' using '*94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29'; +create user invalidauth@localhost identified via 'mysql_native_password' using 'invalid'; + create user newpass@localhost identified by password '*94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29'; +create user invalidpass@localhost identified by password 'invalid'; + create user newpassnat@localhost identified via 'mysql_native_password'; set password for newpassnat@localhost = '*94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29'; +create user invalidpassnat@localhost identified by password 'invalid'; +set password for invalidpassnat@localhost = 'invalid'; + create user oldauth@localhost identified with 'mysql_old_password' using '378b243e220ca493'; create user oldpass@localhost identified by password '378b243e220ca493'; @@ -114,6 +121,17 @@ select current_user(); --connect(con,localhost,newpassnat,test2,) select current_user(); --disconnect con + +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error ER_ACCESS_DENIED_ERROR +--connect(con,localhost,invalidauth,invalid,) +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error ER_ACCESS_DENIED_ERROR +--connect(con,localhost,invalidpass,invalid,) +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error ER_ACCESS_DENIED_ERROR +--connect(con,localhost,invalidpassnat,invalid,) + --connect(con,localhost,oldauth,test2,) select current_user(); --disconnect con @@ -126,6 +144,7 @@ select current_user(); --connection default drop user natauth@localhost, newpass@localhost, newpassnat@localhost; +drop user invalidauth@localhost, invalidpass@localhost, invalidpassnat@localhost; drop user oldauth@localhost, oldpass@localhost, oldpassold@localhost; set global secure_auth=default; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 42bf1782bdb..86cea04f91e 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -14166,7 +14166,7 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio, info->password_used= PASSWORD_USED_YES; if (pkt_len == SCRAMBLE_LENGTH) { - if (!info->auth_string_length) + if (info->auth_string_length != SCRAMBLE_LENGTH) DBUG_RETURN(CR_AUTH_USER_CREDENTIALS); if (check_scramble(pkt, thd->scramble, (uchar*)info->auth_string)) @@ -14193,9 +14193,13 @@ static int native_password_make_scramble(const char *password, return 0; } +/* As this contains is a string of not a valid SCRAMBLE_LENGTH */ +static const char invalid_password[] = "*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE"; + static int native_password_get_salt(const char *hash, size_t hash_length, unsigned char *out, size_t *out_length) { + DBUG_ASSERT(sizeof(invalid_password) > SCRAMBLE_LENGTH); DBUG_ASSERT(*out_length >= SCRAMBLE_LENGTH); if (hash_length == 0) { @@ -14205,6 +14209,12 @@ static int native_password_get_salt(const char *hash, size_t hash_length, if (hash_length != SCRAMBLED_PASSWORD_CHAR_LENGTH) { + if (hash_length == 7 && strcmp(hash, "invalid") == 0) + { + memcpy(out, invalid_password, SCRAMBLED_PASSWORD_CHAR_LENGTH); + *out_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; + return 0; + } my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH); return 1; } |