Bug #75955 replace_user_table() does not check error when reading from mysql.user
Submitted: 18 Feb 2015 13:41 Modified: 21 May 2015 0:43
Reporter: Magnus Blåudd Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Security: Privileges Severity:S3 (Non-critical)
Version:5.7.6 OS:Any
Assigned to: CPU Architecture:Any

[18 Feb 2015 13:41] Magnus Blåudd
Description:
The function replace_user_table() does not properly check expected return code when reading from the mysql.user table. Thus execution will continue as if the user didn't exists even in case it probably did. Normally resulting in that the code tries to update the table.

diff --git a/sql/auth/sql_user_table.cc b/sql/auth/sql_user_table.cc
index 1339080..4031686 100644
--- a/sql/auth/sql_user_table.cc
+++ b/sql/auth/sql_user_table.cc
@@ -449,6 +449,7 @@ int replace_user_table(THD *thd, TABLE *table, LEX_USER *combo,
                                          HA_WHOLE_KEY,
                                          HA_READ_KEY_EXACT))
   {
+    Need to check for expected "record does not exist" error here!
     /*
       The user record wasn't found; if the intention was to revoke privileges
       (indicated by what == 'N') then execution must fail now.

How to repeat:
Provoke error reading from mysql.user so that the table->file->ha_index_read_idx_map() call fails with a different error than "record does not exist".

Normally it is hard to provoke as long as the mysql.user table is using MyISAM but with other engine(s) it might be possible to start an update transaction on the given user and then switch over to another connection and perform the SET PASSWORD command.

With engine being NDB it would (using mysqltest language) look like:

connection server1;
CREATE USER 'billy1'@'127.0.0.1' IDENTIFIED by 'mypass';
BEGIN;
UPDATE mysql.user SET authentication_string = '' WHERE User = 'billy1';

connection server2;
--error <some sensible error message>
SET PASSWORD FOR 'billy1'@'127.0.0.1' = 'newpass';
SHOW WARNINGS;
<should show some warnings describing that an error occured while reading the user form the table>

connection server1;
ROLLBACK;

connection server2;
DROP USER 'billy1'@'127.0.0.1'; 

Suggested fix:
Properly check expected return code after trying to read from mysql.user table.
[21 May 2015 0:43] Paul DuBois
Noted in 5.7.8, 5.8.0 changelogs.

Code for reading and writing the grant tables assumed that these were
MyISAM tables and did not handle errors that can be thrown if the
tables are handled by a different storage engine.