From 4fa4bbe5b985dd9b1a36ff6fa4743ad6d86ff617 Mon Sep 17 00:00:00 2001 From: Rophy Tsai Date: Thu, 15 Jan 2026 13:25:32 +0000 Subject: [PATCH 1/2] MDEV-38550: add LENENC support for COM_CHANGE_USER Add support for CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA in COM_CHANGE_USER packet parsing, allowing passwords >= 251 bytes. Changes: - Server: parse_com_change_user_packet() now handles LENENC-encoded password length when CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA is set - Server: fix db pointer calculation for old protocol without CLIENT_SECURE_CONNECTION (need +1 to skip null terminator) - Add regression test for COM_CHANGE_USER with long passwords Note: This fix requires a corresponding client-side fix in libmariadb's send_change_user_packet() to send LENENC-encoded passwords. --- mysql-test/suite/plugins/r/mdev38431.result | 41 ++++++++++++++++++- mysql-test/suite/plugins/t/mdev38431.test | 44 ++++++++++++++++++++- sql/sql_acl.cc | 28 +++++++++++-- 3 files changed, 106 insertions(+), 7 deletions(-) diff --git a/mysql-test/suite/plugins/r/mdev38431.result b/mysql-test/suite/plugins/r/mdev38431.result index d3bff9a3ecc09..8c179a95a5323 100644 --- a/mysql-test/suite/plugins/r/mdev38431.result +++ b/mysql-test/suite/plugins/r/mdev38431.result @@ -29,7 +29,46 @@ GRANT ALL ON *.* TO verylonguser; db mdev38431_db # +# Test 4: 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 +mdev38431_db +# +# Test 5: 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 +mdev38431_db +# +# Test 6: 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, mdev38431_db, $MASTER_MYPORT, , , mysql_clear_password; +SELECT DATABASE() AS db; +db +mdev38431_db +SELECT CURRENT_USER() AS user; +user +changeuserlongpwd@% +SELECT DATABASE() AS db; +db +mdev38431_db +connection default; +disconnect cleartext_con; +# # Cleanup # -DROP USER shortuser, longuser, verylonguser; +DROP USER shortuser, longuser, verylonguser, changeusertest, changeuserlongpwd; DROP DATABASE mdev38431_db; diff --git a/mysql-test/suite/plugins/t/mdev38431.test b/mysql-test/suite/plugins/t/mdev38431.test index 21b60078f4abf..24de4189d3308 100644 --- a/mysql-test/suite/plugins/t/mdev38431.test +++ b/mysql-test/suite/plugins/t/mdev38431.test @@ -51,9 +51,49 @@ GRANT ALL ON *.* TO verylonguser; --exec $MYSQL -h 127.0.0.1 -P $MASTER_MYPORT --default-auth=mysql_clear_password -u verylonguser -p"$very_long_pwd" --database=mdev38431_db -e "SELECT DATABASE() AS db" +--echo # +--echo # Test 4: 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,mdev38431_db; +SELECT DATABASE() AS db; +change_user root; + +--echo # +--echo # Test 5: 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,mdev38431_db; +SELECT DATABASE() AS db; +change_user root; + +--echo # +--echo # Test 6: 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, mdev38431_db, $MASTER_MYPORT, , , mysql_clear_password) +SELECT DATABASE() AS db; +SELECT CURRENT_USER() AS user; +change_user changeuserlongpwd,$change_pwd,mdev38431_db; +SELECT DATABASE() AS db; +--connection default +--disconnect cleartext_con + --echo # --echo # Cleanup --echo # -DROP USER shortuser, longuser, verylonguser; +DROP USER shortuser, longuser, verylonguser, changeusertest, changeuserlongpwd; DROP DATABASE mdev38431_db; -# Note: Do not uninstall cleartext_plugin_server as it was pre-loaded by MTR 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'. From cfc9e1ecf03796b28dd17e838bf0c7cfd3a54282 Mon Sep 17 00:00:00 2001 From: Rophy Tsai Date: Sat, 17 Jan 2026 04:22:52 +0000 Subject: [PATCH 2/2] test: separate MDEV-38550 tests from MDEV-38431 --- mysql-test/suite/plugins/r/mdev38431.result | 41 +------------ mysql-test/suite/plugins/r/mdev38550.result | 51 ++++++++++++++++ mysql-test/suite/plugins/t/mdev38431.test | 43 +------------- mysql-test/suite/plugins/t/mdev38550.test | 66 +++++++++++++++++++++ 4 files changed, 119 insertions(+), 82 deletions(-) create mode 100644 mysql-test/suite/plugins/r/mdev38550.result create mode 100644 mysql-test/suite/plugins/t/mdev38550.test diff --git a/mysql-test/suite/plugins/r/mdev38431.result b/mysql-test/suite/plugins/r/mdev38431.result index 8c179a95a5323..d3bff9a3ecc09 100644 --- a/mysql-test/suite/plugins/r/mdev38431.result +++ b/mysql-test/suite/plugins/r/mdev38431.result @@ -29,46 +29,7 @@ GRANT ALL ON *.* TO verylonguser; db mdev38431_db # -# Test 4: 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 -mdev38431_db -# -# Test 5: 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 -mdev38431_db -# -# Test 6: 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, mdev38431_db, $MASTER_MYPORT, , , mysql_clear_password; -SELECT DATABASE() AS db; -db -mdev38431_db -SELECT CURRENT_USER() AS user; -user -changeuserlongpwd@% -SELECT DATABASE() AS db; -db -mdev38431_db -connection default; -disconnect cleartext_con; -# # Cleanup # -DROP USER shortuser, longuser, verylonguser, changeusertest, changeuserlongpwd; +DROP USER shortuser, longuser, verylonguser; DROP DATABASE mdev38431_db; 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 24de4189d3308..c494790bb660c 100644 --- a/mysql-test/suite/plugins/t/mdev38431.test +++ b/mysql-test/suite/plugins/t/mdev38431.test @@ -51,49 +51,8 @@ GRANT ALL ON *.* TO verylonguser; --exec $MYSQL -h 127.0.0.1 -P $MASTER_MYPORT --default-auth=mysql_clear_password -u verylonguser -p"$very_long_pwd" --database=mdev38431_db -e "SELECT DATABASE() AS db" ---echo # ---echo # Test 4: 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,mdev38431_db; -SELECT DATABASE() AS db; -change_user root; - ---echo # ---echo # Test 5: 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,mdev38431_db; -SELECT DATABASE() AS db; -change_user root; - ---echo # ---echo # Test 6: 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, mdev38431_db, $MASTER_MYPORT, , , mysql_clear_password) -SELECT DATABASE() AS db; -SELECT CURRENT_USER() AS user; -change_user changeuserlongpwd,$change_pwd,mdev38431_db; -SELECT DATABASE() AS db; ---connection default ---disconnect cleartext_con - --echo # --echo # Cleanup --echo # -DROP USER shortuser, longuser, verylonguser, changeusertest, changeuserlongpwd; +DROP USER shortuser, longuser, verylonguser; DROP DATABASE mdev38431_db; 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;