summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Black <daniel@mariadb.org>2020-07-16 16:31:59 +1000
committerDaniel Black <daniel@mariadb.org>2020-10-31 09:14:37 +1100
commit5b779c220dbe644c9fc4cfcfe15a017ed5e4ff9c (patch)
treec0fbd278d1414a0521580cbf4cace015cd91f412
parent1fddccf676e213f94923f5efaaa76d9793b19a89 (diff)
downloadmariadb-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.result4
-rw-r--r--mysql-test/main/alter_user.test3
-rw-r--r--mysql-test/main/set_password.result20
-rw-r--r--mysql-test/main/set_password.test19
-rw-r--r--sql/sql_acl.cc12
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;
}