diff --git a/mysql-test/main/ps.result b/mysql-test/main/ps.result index 4d757986f9cc5..b10a75e739917 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 2ce78b78e900b..62a7efffbfd66 100644 --- a/mysql-test/main/ps.test +++ b/mysql-test/main/ps.test @@ -4979,6 +4979,43 @@ DEALLOCATE PREPARE stmt; 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 cd188ab693ea3..3ae7c7c7df359 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 26cf992920b56..f0b8fc7309e5c 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(); /*