summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Shulga <dmitry.shulga@mariadb.com>2021-04-13 09:38:32 +0700
committerDmitry Shulga <dmitry.shulga@mariadb.com>2021-04-13 09:38:32 +0700
commit61f84bba603aa85957b48d151f9ddf5ba4e71ab1 (patch)
tree2be7a6d393c6dff0b96c91d0433180ffd57674ce
parente14b682636bc78a20cb7bedf2af696a3f1eb79bc (diff)
downloadmariadb-git-bb-10.4-MDEV-25197-3.tar.gz
MDEV-25197: The statement set password=password('') executed in PS mode fails in case it is run by a user with expired passwordbb-10.4-MDEV-25197-3
A user connected to a server with an expired password can't change password with the statement "SET password=..." if this statement is run in PS mode. In mentioned use case a user gets the error ER_MUST_CHANGE_PASSWORD on attempt to run the statement PREPARE stmt FOR "SET password=..."; The reason of failure to reset password by a locked user using the statement PREPARE stmt FOR "SET password=..." is that PS-related statements are not listed among the commands allowed for execution by a user with expired password. However, simple adding of PS-related statements (PREPARE FOR/EXECUTE/DEALLOCATE PREPARE ) to the list of statements allowed for execution by a locked user is not enough to solve problems, since it opens the opportunity for a locked user to execute any statement in the PS mode. To exclude this opportunity, additional checking that the statement being prepared for execution in PS-mode is the SET statement has to be added. This extra checking has been added by this patch into the method Prepared_statement::prepared() that executed on preparing any statement for execution in PS-mode.
-rw-r--r--mysql-test/main/ps.result32
-rw-r--r--mysql-test/main/ps.test37
-rw-r--r--sql/sql_parse.cc9
-rw-r--r--sql/sql_prepare.cc9
4 files changed, 85 insertions, 2 deletions
diff --git a/mysql-test/main/ps.result b/mysql-test/main/ps.result
index 4d757986f9c..b10a75e7399 100644
--- a/mysql-test/main/ps.result
+++ b/mysql-test/main/ps.result
@@ -5536,5 +5536,37 @@ DEALLOCATE PREPARE stmt;
DROP VIEW v1;
DROP TABLE t1;
#
+# MDEV-25197: The statement set password=password('') executed in PS mode
+# fails in case it is run by a user with expired password
+#
+CREATE USER user1@localhost PASSWORD EXPIRE;
+SET @disconnect_on_expired_password_save=@@global.disconnect_on_expired_password;
+SET GLOBAL disconnect_on_expired_password=OFF;
+connect con1,localhost,user1;
+connection con1;
+# Check that no regular statement like SELECT can be prepared
+# by a user with an expired password
+PREPARE stmt FROM "SELECT 1";
+ERROR HY000: You must SET PASSWORD before executing this statement
+# Check that the DEALLOCATE PREPARE statement can be run by a user
+# with an expired password
+PREPARE stmt FROM "SET password=password('')";
+DEALLOCATE PREPARE stmt;
+# Check that the SET PASSWORD statement can be executed in PS mode by
+# a user with an expired password
+PREPARE stmt FROM "SET password=password('')";
+EXECUTE stmt;
+PREPARE stmt FROM "SELECT 1";
+# Check that user's password is not expired anymore
+EXECUTE stmt;
+1
+1
+DEALLOCATE PREPARE stmt;
+# Clean up
+disconnect con1;
+connection default;
+SET GLOBAL disconnect_on_expired_password=@disconnect_on_expired_password_save;
+DROP USER user1@localhost;
+#
# End of 10.4 tests
#
diff --git a/mysql-test/main/ps.test b/mysql-test/main/ps.test
index 2ce78b78e90..62a7efffbfd 100644
--- a/mysql-test/main/ps.test
+++ b/mysql-test/main/ps.test
@@ -4980,5 +4980,42 @@ DROP VIEW v1;
DROP TABLE t1;
--echo #
+--echo # MDEV-25197: The statement set password=password('') executed in PS mode
+--echo # fails in case it is run by a user with expired password
+--echo #
+CREATE USER user1@localhost PASSWORD EXPIRE;
+
+SET @disconnect_on_expired_password_save=@@global.disconnect_on_expired_password;
+SET GLOBAL disconnect_on_expired_password=OFF;
+
+connect(con1,localhost,user1);
+connection con1;
+--echo # Check that no regular statement like SELECT can be prepared
+--echo # by a user with an expired password
+--error ER_MUST_CHANGE_PASSWORD
+PREPARE stmt FROM "SELECT 1";
+
+--echo # Check that the DEALLOCATE PREPARE statement can be run by a user
+--echo # with an expired password
+PREPARE stmt FROM "SET password=password('')";
+DEALLOCATE PREPARE stmt;
+
+--echo # Check that the SET PASSWORD statement can be executed in PS mode by
+--echo # a user with an expired password
+PREPARE stmt FROM "SET password=password('')";
+EXECUTE stmt;
+PREPARE stmt FROM "SELECT 1";
+--echo # Check that user's password is not expired anymore
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+--echo # Clean up
+disconnect con1;
+connection default;
+
+SET GLOBAL disconnect_on_expired_password=@disconnect_on_expired_password_save;
+DROP USER user1@localhost;
+
+--echo #
--echo # End of 10.4 tests
--echo #
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index cd188ab693e..3ae7c7c7df3 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1641,7 +1641,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (unlikely(thd->security_ctx->password_expired &&
command != COM_QUERY &&
command != COM_PING &&
- command != COM_QUIT))
+ command != COM_QUIT &&
+ command != COM_STMT_PREPARE &&
+ command != COM_STMT_EXECUTE))
{
my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
goto dispatch_end;
@@ -3490,7 +3492,10 @@ mysql_execute_command(THD *thd)
first_table->for_insert_data);
if (thd->security_ctx->password_expired &&
- lex->sql_command != SQLCOM_SET_OPTION)
+ lex->sql_command != SQLCOM_SET_OPTION &&
+ lex->sql_command != SQLCOM_PREPARE &&
+ lex->sql_command != SQLCOM_EXECUTE &&
+ lex->sql_command != SQLCOM_DEALLOCATE_PREPARE)
{
my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
DBUG_RETURN(1);
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 26cf992920b..f0b8fc7309e 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -4217,6 +4217,15 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
thd->is_error() ||
init_param_array(this));
+ if (thd->security_ctx->password_expired &&
+ lex->sql_command != SQLCOM_SET_OPTION)
+ {
+ thd->restore_backup_statement(this, &stmt_backup);
+ thd->restore_active_arena(this, &stmt_backup);
+ thd->stmt_arena= old_stmt_arena;
+ my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
+ DBUG_RETURN(true);
+ }
lex->set_trg_event_type_for_tables();
/*