diff --git a/mysql-test/suite/plugins/r/mdev38550.result b/mysql-test/suite/plugins/r/mdev38550.result new file mode 100644 index 0000000000000..1265fb7dc9964 --- /dev/null +++ b/mysql-test/suite/plugins/r/mdev38550.result @@ -0,0 +1,51 @@ +# +# Setup +# +INSTALL PLUGIN IF NOT EXISTS cleartext_plugin_server SONAME ''; +Warnings: +Note 1968 Plugin 'cleartext_plugin_server' already installed +CREATE DATABASE mdev38550_db; +# +# Test 1: COM_CHANGE_USER with short password +# Verify mysql_change_user() works correctly with database parameter +# +CREATE USER changeusertest IDENTIFIED VIA cleartext_plugin_server USING 'changepwd'; +GRANT ALL ON *.* TO changeusertest; +SELECT DATABASE() AS db; +db +mdev38550_db +# +# Test 2: COM_CHANGE_USER with long password (260 bytes) +# This works because auth plugin switching sends password in a SECOND packet +# (via ma_net_write), bypassing the 255-byte limit in send_change_user_packet() +# +# Note: If connection used cleartext directly (no auth switch), it would fail +# due to libmariadb's 255-byte limit in send_change_user_packet() +# +CREATE USER changeuserlongpwd IDENTIFIED VIA cleartext_plugin_server USING 'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'; +GRANT ALL ON *.* TO changeuserlongpwd; +SELECT DATABASE() AS db; +db +mdev38550_db +# +# Test 3: COM_CHANGE_USER with long password - no auth switch +# Connection uses cleartext directly, password goes in first packet +# Requires LENENC support in both client and server for COM_CHANGE_USER +# +connect cleartext_con, 127.0.0.1, changeuserlongpwd, $change_pwd, mdev38550_db, $MASTER_MYPORT, , , mysql_clear_password; +SELECT DATABASE() AS db; +db +mdev38550_db +SELECT CURRENT_USER() AS user; +user +changeuserlongpwd@% +SELECT DATABASE() AS db; +db +mdev38550_db +connection default; +disconnect cleartext_con; +# +# Cleanup +# +DROP USER changeusertest, changeuserlongpwd; +DROP DATABASE mdev38550_db; diff --git a/mysql-test/suite/plugins/t/mdev38431.test b/mysql-test/suite/plugins/t/mdev38431.test index 21b60078f4abf..c494790bb660c 100644 --- a/mysql-test/suite/plugins/t/mdev38431.test +++ b/mysql-test/suite/plugins/t/mdev38431.test @@ -56,4 +56,3 @@ GRANT ALL ON *.* TO verylonguser; --echo # DROP USER shortuser, longuser, verylonguser; DROP DATABASE mdev38431_db; -# Note: Do not uninstall cleartext_plugin_server as it was pre-loaded by MTR diff --git a/mysql-test/suite/plugins/t/mdev38550.test b/mysql-test/suite/plugins/t/mdev38550.test new file mode 100644 index 0000000000000..6ea90b2c3ec33 --- /dev/null +++ b/mysql-test/suite/plugins/t/mdev38550.test @@ -0,0 +1,66 @@ +# +# MDEV-38550: COM_CHANGE_USER with Long Password Corrupts Database Name +# +# When password > 255 bytes with COM_CHANGE_USER, the password length was +# truncated to a single byte, causing the server to read garbage as the +# database name. +# +# Fix: Add LENENC support for COM_CHANGE_USER password field in both +# client (libmariadb) and server (sql_acl.cc). +# + +--source include/not_embedded.inc +--source include/have_plugin_auth.inc + +--echo # +--echo # Setup +--echo # +eval INSTALL PLUGIN IF NOT EXISTS cleartext_plugin_server SONAME '$PLUGIN_AUTH'; +CREATE DATABASE mdev38550_db; + +--echo # +--echo # Test 1: COM_CHANGE_USER with short password +--echo # Verify mysql_change_user() works correctly with database parameter +--echo # +CREATE USER changeusertest IDENTIFIED VIA cleartext_plugin_server USING 'changepwd'; +GRANT ALL ON *.* TO changeusertest; + +# Use change_user command to switch user with password and database +change_user changeusertest,changepwd,mdev38550_db; +SELECT DATABASE() AS db; +change_user root; + +--echo # +--echo # Test 2: COM_CHANGE_USER with long password (260 bytes) +--echo # This works because auth plugin switching sends password in a SECOND packet +--echo # (via ma_net_write), bypassing the 255-byte limit in send_change_user_packet() +--echo # +--echo # Note: If connection used cleartext directly (no auth switch), it would fail +--echo # due to libmariadb's 255-byte limit in send_change_user_packet() +--echo # +--let $change_pwd=`SELECT REPEAT('c', 260)` +eval CREATE USER changeuserlongpwd IDENTIFIED VIA cleartext_plugin_server USING '$change_pwd'; +GRANT ALL ON *.* TO changeuserlongpwd; + +change_user changeuserlongpwd,$change_pwd,mdev38550_db; +SELECT DATABASE() AS db; +change_user root; + +--echo # +--echo # Test 3: COM_CHANGE_USER with long password - no auth switch +--echo # Connection uses cleartext directly, password goes in first packet +--echo # Requires LENENC support in both client and server for COM_CHANGE_USER +--echo # +--connect (cleartext_con, 127.0.0.1, changeuserlongpwd, $change_pwd, mdev38550_db, $MASTER_MYPORT, , , mysql_clear_password) +SELECT DATABASE() AS db; +SELECT CURRENT_USER() AS user; +change_user changeuserlongpwd,$change_pwd,mdev38550_db; +SELECT DATABASE() AS db; +--connection default +--disconnect cleartext_con + +--echo # +--echo # Cleanup +--echo # +DROP USER changeusertest, changeuserlongpwd; +DROP DATABASE mdev38550_db; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 512a0b055f736..ef0a214fbdbd6 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -13613,10 +13613,30 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) Cast *passwd to an unsigned char, so that it doesn't extend the sign for *passwd > 127 and become 2**32-127+ after casting to uint. */ - uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ? - (uchar) (*passwd++) : (uint)strlen(passwd)); - - db+= passwd_len + 1; + size_t passwd_len; + if (!(thd->client_capabilities & CLIENT_SECURE_CONNECTION)) + { + passwd_len= strlen(passwd); + db= passwd + passwd_len + 1; /* +1 to skip null terminator */ + } + else if (!(thd->client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA)) + { + passwd_len= (uchar)(*passwd++); + db= passwd + passwd_len; + } + else + { + ulonglong len= safe_net_field_length_ll((uchar**)&passwd, + end - passwd); + if (len > packet_length) + { + my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR), + MYF(0)); + DBUG_RETURN(1); + } + passwd_len= (size_t)len; + db= passwd + passwd_len; + } /* Database name is always NUL-terminated, so in case of empty database the packet must contain at least the trailing '\0'.