Commit 774b2e27 authored by Dmitry Lenev's avatar Dmitry Lenev

Merged fix for bug #36544 "DROP USER does not remove stored

function privileges" into 5.5 tree. Did after-merge fixes.
parents 1da630f9 fc725556
......@@ -1326,6 +1326,199 @@ DROP DATABASE mysqltest2;
DROP USER testuser@localhost;
use test;
#
# Test for bug #36544 "DROP USER does not remove stored function
# privileges".
#
create database mysqltest1;
create function mysqltest1.f1() returns int return 0;
create procedure mysqltest1.p1() begin end;
#
# 1) Check that DROP USER properly removes privileges on both
# stored procedures and functions.
#
create user mysqluser1@localhost;
grant execute on function mysqltest1.f1 to mysqluser1@localhost;
grant execute on procedure mysqltest1.p1 to mysqluser1@localhost;
# Quick test that granted privileges are properly reflected
# in privilege tables and in in-memory structures.
show grants for mysqluser1@localhost;
Grants for mysqluser1@localhost
GRANT USAGE ON *.* TO 'mysqluser1'@'localhost'
GRANT EXECUTE ON PROCEDURE `mysqltest1`.`p1` TO 'mysqluser1'@'localhost'
GRANT EXECUTE ON FUNCTION `mysqltest1`.`f1` TO 'mysqluser1'@'localhost'
select db, routine_name, routine_type, proc_priv from mysql.procs_priv where user='mysqluser1' and host='localhost';
db routine_name routine_type proc_priv
mysqltest1 f1 FUNCTION Execute
mysqltest1 p1 PROCEDURE Execute
#
# Create connection 'bug_36544_con1' as 'mysqluser1@localhost'.
call mysqltest1.p1();
select mysqltest1.f1();
mysqltest1.f1()
0
#
# Switch to connection 'default'.
drop user mysqluser1@localhost;
#
# Test that dropping of user is properly reflected in
# both privilege tables and in in-memory structures.
#
# Switch to connection 'bug36544_con1'.
# The connection cold be alive but should not be able to
# access to any of the stored routines.
call mysqltest1.p1();
ERROR 42000: execute command denied to user 'mysqluser1'@'localhost' for routine 'mysqltest1.p1'
select mysqltest1.f1();
ERROR 42000: execute command denied to user 'mysqluser1'@'localhost' for routine 'mysqltest1.f1'
#
# Switch to connection 'default'.
#
# Now create user with the same name and check that he
# has not inherited privileges.
create user mysqluser1@localhost;
show grants for mysqluser1@localhost;
Grants for mysqluser1@localhost
GRANT USAGE ON *.* TO 'mysqluser1'@'localhost'
select db, routine_name, routine_type, proc_priv from mysql.procs_priv where user='mysqluser1' and host='localhost';
db routine_name routine_type proc_priv
#
# Create connection 'bug_36544_con2' as 'mysqluser1@localhost'.
# Newly created user should not be able to access any of the routines.
call mysqltest1.p1();
ERROR 42000: execute command denied to user 'mysqluser1'@'localhost' for routine 'mysqltest1.p1'
select mysqltest1.f1();
ERROR 42000: execute command denied to user 'mysqluser1'@'localhost' for routine 'mysqltest1.f1'
#
# Switch to connection 'default'.
#
# 2) Check that RENAME USER properly updates privileges on both
# stored procedures and functions.
#
grant execute on function mysqltest1.f1 to mysqluser1@localhost;
grant execute on procedure mysqltest1.p1 to mysqluser1@localhost;
#
# Create one more user to make in-memory hashes non-trivial.
# User names 'mysqluser11' and 'mysqluser10' were selected
# to trigger bug discovered during code inspection.
create user mysqluser11@localhost;
grant execute on function mysqltest1.f1 to mysqluser11@localhost;
grant execute on procedure mysqltest1.p1 to mysqluser11@localhost;
# Also create a couple of tables to test for another bug
# discovered during code inspection (again table names were
# chosen especially to trigger the bug).
create table mysqltest1.t11 (i int);
create table mysqltest1.t22 (i int);
grant select on mysqltest1.t22 to mysqluser1@localhost;
grant select on mysqltest1.t11 to mysqluser1@localhost;
# Quick test that granted privileges are properly reflected
# in privilege tables and in in-memory structures.
show grants for mysqluser1@localhost;
Grants for mysqluser1@localhost
GRANT USAGE ON *.* TO 'mysqluser1'@'localhost'
GRANT SELECT ON `mysqltest1`.`t11` TO 'mysqluser1'@'localhost'
GRANT SELECT ON `mysqltest1`.`t22` TO 'mysqluser1'@'localhost'
GRANT EXECUTE ON PROCEDURE `mysqltest1`.`p1` TO 'mysqluser1'@'localhost'
GRANT EXECUTE ON FUNCTION `mysqltest1`.`f1` TO 'mysqluser1'@'localhost'
select db, routine_name, routine_type, proc_priv from mysql.procs_priv where user='mysqluser1' and host='localhost';
db routine_name routine_type proc_priv
mysqltest1 f1 FUNCTION Execute
mysqltest1 p1 PROCEDURE Execute
select db, table_name, table_priv from mysql.tables_priv where user='mysqluser1' and host='localhost';
db table_name table_priv
mysqltest1 t11 Select
mysqltest1 t22 Select
#
# Switch to connection 'bug36544_con2'.
call mysqltest1.p1();
select mysqltest1.f1();
mysqltest1.f1()
0
select * from mysqltest1.t11;
i
select * from mysqltest1.t22;
i
#
# Switch to connection 'default'.
rename user mysqluser1@localhost to mysqluser10@localhost;
#
# Test that there are no privileges left for mysqluser1.
#
# Switch to connection 'bug36544_con2'.
# The connection cold be alive but should not be able to
# access to any of the stored routines or tables.
call mysqltest1.p1();
ERROR 42000: execute command denied to user 'mysqluser1'@'localhost' for routine 'mysqltest1.p1'
select mysqltest1.f1();
ERROR 42000: execute command denied to user 'mysqluser1'@'localhost' for routine 'mysqltest1.f1'
select * from mysqltest1.t11;
ERROR 42000: SELECT command denied to user 'mysqluser1'@'localhost' for table 't11'
select * from mysqltest1.t22;
ERROR 42000: SELECT command denied to user 'mysqluser1'@'localhost' for table 't22'
#
# Switch to connection 'default'.
#
# Now create user with the old name and check that he
# has not inherited privileges.
create user mysqluser1@localhost;
show grants for mysqluser1@localhost;
Grants for mysqluser1@localhost
GRANT USAGE ON *.* TO 'mysqluser1'@'localhost'
select db, routine_name, routine_type, proc_priv from mysql.procs_priv where user='mysqluser1' and host='localhost';
db routine_name routine_type proc_priv
select db, table_name, table_priv from mysql.tables_priv where user='mysqluser1' and host='localhost';
db table_name table_priv
#
# Create connection 'bug_36544_con3' as 'mysqluser1@localhost'.
# Newly created user should not be able to access to any of the
# stored routines or tables.
call mysqltest1.p1();
ERROR 42000: execute command denied to user 'mysqluser1'@'localhost' for routine 'mysqltest1.p1'
select mysqltest1.f1();
ERROR 42000: execute command denied to user 'mysqluser1'@'localhost' for routine 'mysqltest1.f1'
select * from mysqltest1.t11;
ERROR 42000: SELECT command denied to user 'mysqluser1'@'localhost' for table 't11'
select * from mysqltest1.t22;
ERROR 42000: SELECT command denied to user 'mysqluser1'@'localhost' for table 't22'
#
# Switch to connection 'default'.
#
# Now check that privileges became associated with a new user
# name - mysqluser10.
#
show grants for mysqluser10@localhost;
Grants for mysqluser10@localhost
GRANT USAGE ON *.* TO 'mysqluser10'@'localhost'
GRANT SELECT ON `mysqltest1`.`t22` TO 'mysqluser10'@'localhost'
GRANT SELECT ON `mysqltest1`.`t11` TO 'mysqluser10'@'localhost'
GRANT EXECUTE ON PROCEDURE `mysqltest1`.`p1` TO 'mysqluser10'@'localhost'
GRANT EXECUTE ON FUNCTION `mysqltest1`.`f1` TO 'mysqluser10'@'localhost'
select db, routine_name, routine_type, proc_priv from mysql.procs_priv where user='mysqluser10' and host='localhost';
db routine_name routine_type proc_priv
mysqltest1 f1 FUNCTION Execute
mysqltest1 p1 PROCEDURE Execute
select db, table_name, table_priv from mysql.tables_priv where user='mysqluser10' and host='localhost';
db table_name table_priv
mysqltest1 t11 Select
mysqltest1 t22 Select
#
# Create connection 'bug_36544_con4' as 'mysqluser10@localhost'.
call mysqltest1.p1();
select mysqltest1.f1();
mysqltest1.f1()
0
select * from mysqltest1.t11;
i
select * from mysqltest1.t22;
i
#
# Switch to connection 'default'.
#
# Clean-up.
drop user mysqluser1@localhost;
drop user mysqluser10@localhost;
drop user mysqluser11@localhost;
drop database mysqltest1;
End of 5.0 tests
set names utf8;
grant select on test.* to юзер_юзер@localhost;
......@@ -1422,11 +1615,7 @@ fn2()
2
DROP USER 'userbug33464'@'localhost';
DROP FUNCTION fn1;
Warnings:
Warning 1403 There is no such grant defined for user 'userbug33464' on host 'localhost' on routine 'fn1'
DROP FUNCTION fn2;
Warnings:
Warning 1403 There is no such grant defined for user 'userbug33464' on host 'localhost' on routine 'fn2'
DROP PROCEDURE sp3;
DROP USER 'userbug33464'@'localhost';
USE test;
......
......@@ -128,8 +128,6 @@ root@localhost db_storedproc_1
drop user 'user_1'@'localhost';
DROP PROCEDURE sp3;
DROP FUNCTION fn1;
Warnings:
Warning 1403 There is no such grant defined for user 'user_1' on host 'localhost' on routine 'fn1'
Testcase 3.1.6.4:
-----------------
......
......@@ -129,8 +129,6 @@ root@localhost db_storedproc_1
drop user 'user_1'@'localhost';
DROP PROCEDURE sp3;
DROP FUNCTION fn1;
Warnings:
Warning 1403 There is no such grant defined for user 'user_1' on host 'localhost' on routine 'fn1'
Testcase 3.1.6.4:
-----------------
......
......@@ -129,8 +129,6 @@ root@localhost db_storedproc_1
drop user 'user_1'@'localhost';
DROP PROCEDURE sp3;
DROP FUNCTION fn1;
Warnings:
Warning 1403 There is no such grant defined for user 'user_1' on host 'localhost' on routine 'fn1'
Testcase 3.1.6.4:
-----------------
......
......@@ -149,10 +149,6 @@ USE db_storedproc_1;
drop user 'user_1'@'localhost';
DROP PROCEDURE sp3;
# This drop function shouldn't generated a warning as the
# privileges should have been removed when the user was
# dropped. Reported as Bug#36544 DROP USER does not remove
# stored function privileges
DROP FUNCTION fn1;
......
......@@ -1396,6 +1396,183 @@ DROP USER testuser@localhost;
use test;
--echo
--echo #
--echo # Test for bug #36544 "DROP USER does not remove stored function
--echo # privileges".
--echo #
create database mysqltest1;
create function mysqltest1.f1() returns int return 0;
create procedure mysqltest1.p1() begin end;
--echo #
--echo # 1) Check that DROP USER properly removes privileges on both
--echo # stored procedures and functions.
--echo #
create user mysqluser1@localhost;
grant execute on function mysqltest1.f1 to mysqluser1@localhost;
grant execute on procedure mysqltest1.p1 to mysqluser1@localhost;
--echo # Quick test that granted privileges are properly reflected
--echo # in privilege tables and in in-memory structures.
show grants for mysqluser1@localhost;
select db, routine_name, routine_type, proc_priv from mysql.procs_priv where user='mysqluser1' and host='localhost';
--echo #
--echo # Create connection 'bug_36544_con1' as 'mysqluser1@localhost'.
--connect (bug36544_con1,localhost,mysqluser1,,)
call mysqltest1.p1();
select mysqltest1.f1();
--echo #
--echo # Switch to connection 'default'.
--connection default
drop user mysqluser1@localhost;
--echo #
--echo # Test that dropping of user is properly reflected in
--echo # both privilege tables and in in-memory structures.
--echo #
--echo # Switch to connection 'bug36544_con1'.
--connection bug36544_con1
--echo # The connection cold be alive but should not be able to
--echo # access to any of the stored routines.
--error ER_PROCACCESS_DENIED_ERROR
call mysqltest1.p1();
--error ER_PROCACCESS_DENIED_ERROR
select mysqltest1.f1();
--disconnect bug36544_con1
--echo #
--echo # Switch to connection 'default'.
--connection default
--echo #
--echo # Now create user with the same name and check that he
--echo # has not inherited privileges.
create user mysqluser1@localhost;
show grants for mysqluser1@localhost;
select db, routine_name, routine_type, proc_priv from mysql.procs_priv where user='mysqluser1' and host='localhost';
--echo #
--echo # Create connection 'bug_36544_con2' as 'mysqluser1@localhost'.
--connect (bug36544_con2,localhost,mysqluser1,,)
--echo # Newly created user should not be able to access any of the routines.
--error ER_PROCACCESS_DENIED_ERROR
call mysqltest1.p1();
--error ER_PROCACCESS_DENIED_ERROR
select mysqltest1.f1();
--echo #
--echo # Switch to connection 'default'.
--connection default
--echo #
--echo # 2) Check that RENAME USER properly updates privileges on both
--echo # stored procedures and functions.
--echo #
grant execute on function mysqltest1.f1 to mysqluser1@localhost;
grant execute on procedure mysqltest1.p1 to mysqluser1@localhost;
--echo #
--echo # Create one more user to make in-memory hashes non-trivial.
--echo # User names 'mysqluser11' and 'mysqluser10' were selected
--echo # to trigger bug discovered during code inspection.
create user mysqluser11@localhost;
grant execute on function mysqltest1.f1 to mysqluser11@localhost;
grant execute on procedure mysqltest1.p1 to mysqluser11@localhost;
--echo # Also create a couple of tables to test for another bug
--echo # discovered during code inspection (again table names were
--echo # chosen especially to trigger the bug).
create table mysqltest1.t11 (i int);
create table mysqltest1.t22 (i int);
grant select on mysqltest1.t22 to mysqluser1@localhost;
grant select on mysqltest1.t11 to mysqluser1@localhost;
--echo # Quick test that granted privileges are properly reflected
--echo # in privilege tables and in in-memory structures.
show grants for mysqluser1@localhost;
select db, routine_name, routine_type, proc_priv from mysql.procs_priv where user='mysqluser1' and host='localhost';
select db, table_name, table_priv from mysql.tables_priv where user='mysqluser1' and host='localhost';
--echo #
--echo # Switch to connection 'bug36544_con2'.
--connection bug36544_con2
call mysqltest1.p1();
select mysqltest1.f1();
select * from mysqltest1.t11;
select * from mysqltest1.t22;
--echo #
--echo # Switch to connection 'default'.
--connection default
rename user mysqluser1@localhost to mysqluser10@localhost;
--echo #
--echo # Test that there are no privileges left for mysqluser1.
--echo #
--echo # Switch to connection 'bug36544_con2'.
--connection bug36544_con2
--echo # The connection cold be alive but should not be able to
--echo # access to any of the stored routines or tables.
--error ER_PROCACCESS_DENIED_ERROR
call mysqltest1.p1();
--error ER_PROCACCESS_DENIED_ERROR
select mysqltest1.f1();
--error ER_TABLEACCESS_DENIED_ERROR
select * from mysqltest1.t11;
--error ER_TABLEACCESS_DENIED_ERROR
select * from mysqltest1.t22;
--disconnect bug36544_con2
--echo #
--echo # Switch to connection 'default'.
--connection default
--echo #
--echo # Now create user with the old name and check that he
--echo # has not inherited privileges.
create user mysqluser1@localhost;
show grants for mysqluser1@localhost;
select db, routine_name, routine_type, proc_priv from mysql.procs_priv where user='mysqluser1' and host='localhost';
select db, table_name, table_priv from mysql.tables_priv where user='mysqluser1' and host='localhost';
--echo #
--echo # Create connection 'bug_36544_con3' as 'mysqluser1@localhost'.
--connect (bug36544_con3,localhost,mysqluser1,,)
--echo # Newly created user should not be able to access to any of the
--echo # stored routines or tables.
--error ER_PROCACCESS_DENIED_ERROR
call mysqltest1.p1();
--error ER_PROCACCESS_DENIED_ERROR
select mysqltest1.f1();
--error ER_TABLEACCESS_DENIED_ERROR
select * from mysqltest1.t11;
--error ER_TABLEACCESS_DENIED_ERROR
select * from mysqltest1.t22;
--disconnect bug36544_con3
--echo #
--echo # Switch to connection 'default'.
--connection default
--echo #
--echo # Now check that privileges became associated with a new user
--echo # name - mysqluser10.
--echo #
show grants for mysqluser10@localhost;
select db, routine_name, routine_type, proc_priv from mysql.procs_priv where user='mysqluser10' and host='localhost';
select db, table_name, table_priv from mysql.tables_priv where user='mysqluser10' and host='localhost';
--echo #
--echo # Create connection 'bug_36544_con4' as 'mysqluser10@localhost'.
--connect (bug36544_con4,localhost,mysqluser10,,)
call mysqltest1.p1();
select mysqltest1.f1();
select * from mysqltest1.t11;
select * from mysqltest1.t22;
--disconnect bug36544_con4
--echo #
--echo # Switch to connection 'default'.
--connection default
--echo #
--echo # Clean-up.
drop user mysqluser1@localhost;
drop user mysqluser10@localhost;
drop user mysqluser11@localhost;
drop database mysqltest1;
--echo End of 5.0 tests
#
......
/* Copyright (C) 2000-2003 MySQL AB
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -5377,18 +5377,15 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
}
/*
/**
Handle an in-memory privilege structure.
SYNOPSIS
handle_grant_struct()
struct_no The number of the structure to handle (0..3).
drop If user_from is to be dropped.
user_from The the user to be searched/dropped/renamed.
user_to The new name for the user if to be renamed,
NULL otherwise.
@param struct_no The number of the structure to handle (0..4).
@param drop If user_from is to be dropped.
@param user_from The the user to be searched/dropped/renamed.
@param user_to The new name for the user if to be renamed, NULL otherwise.
DESCRIPTION
@note
Scan through all elements in an in-memory grant structure and apply
the requested operation.
Delete from grant structure if drop is true.
......@@ -5398,12 +5395,12 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
0 acl_users
1 acl_dbs
2 column_priv_hash
3 procs_priv_hash
3 proc_priv_hash
4 func_priv_hash
RETURN
> 0 At least one element matched.
0 OK, but no element matched.
-1 Wrong arguments to function
@retval > 0 At least one element matched.
@retval 0 OK, but no element matched.
@retval -1 Wrong arguments to function.
*/
static int handle_grant_struct(uint struct_no, bool drop,
......@@ -5417,6 +5414,7 @@ static int handle_grant_struct(uint struct_no, bool drop,
ACL_USER *acl_user= NULL;
ACL_DB *acl_db= NULL;
GRANT_NAME *grant_name= NULL;
HASH *grant_name_hash= NULL;
DBUG_ENTER("handle_grant_struct");
DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'",
struct_no, user_from->user.str, user_from->host.str));
......@@ -5436,9 +5434,15 @@ static int handle_grant_struct(uint struct_no, bool drop,
break;
case 2:
elements= column_priv_hash.records;
grant_name_hash= &column_priv_hash;
break;
case 3:
elements= proc_priv_hash.records;
grant_name_hash= &proc_priv_hash;
break;
case 4:
elements= func_priv_hash.records;
grant_name_hash= &func_priv_hash;
break;
default:
return -1;
......@@ -5468,16 +5472,13 @@ static int handle_grant_struct(uint struct_no, bool drop,
break;
case 2:
grant_name= (GRANT_NAME*) hash_element(&column_priv_hash, idx);
user= grant_name->user;
host= grant_name->host.hostname;
break;
case 3:
grant_name= (GRANT_NAME*) hash_element(&proc_priv_hash, idx);
case 4:
grant_name= (GRANT_NAME*) hash_element(grant_name_hash, idx);
user= grant_name->user;
host= grant_name->host.hostname;
break;
default:
MY_ASSERT_UNREACHABLE();
}
......@@ -5507,14 +5508,25 @@ static int handle_grant_struct(uint struct_no, bool drop,
break;
case 2:
hash_delete(&column_priv_hash, (uchar*) grant_name);
break;
case 3:
hash_delete(&proc_priv_hash, (uchar*) grant_name);
case 4:
hash_delete(grant_name_hash, (uchar*) grant_name);
break;
}
elements--;
/*
- If we are iterating through an array then we just have moved all
elements after the current element one position closer to its head.
This means that we have to take another look at the element at
current position as it is a new element from the array's tail.
- If we are iterating through a hash the current element was replaced
with one of elements from the tail. So we also have to take a look
at the new element in current position.
Note that in our HASH implementation hash_delete() won't move any
elements with position after current one to position before the
current (i.e. from the tail to the head), so it is safe to continue
iteration without re-starting.
*/
idx--;
}
else if ( user_to )
......@@ -5532,9 +5544,17 @@ static int handle_grant_struct(uint struct_no, bool drop,
case 2:
case 3:
case 4:
{
/*
Update the grant structure with the new user name and
host name
Save old hash key and its length to be able properly update
element position in hash.
*/
char *old_key= grant_name->hash_key;
size_t old_key_length= grant_name->key_length;
/*
Update the grant structure with the new user name and host name.
*/
grant_name->set_user_details(user_to->host.str, grant_name->db,
user_to->user.str, grant_name->tname,
......@@ -5545,11 +5565,22 @@ static int handle_grant_struct(uint struct_no, bool drop,
is renamed, the hash key is changed. Update the hash to
ensure that the position matches the new hash key value
*/
hash_update(&column_priv_hash, (uchar*) grant_name,
(uchar*) grant_name->hash_key, grant_name->key_length);
hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key,
old_key_length);
/*
hash_update() operation could have moved element from the tail
of the hash to the current position. So we need to take a look
at the element in current position once again.
Thanks to the fact that hash_update() for our HASH implementation
won't move any elements from the tail of the hash to the positions
before the current one (a.k.a. head) it is safe to continue
iteration without restarting.
*/
idx--;
break;
}
}
}
else
{
/* If search is requested, we do not need to search further. */
......@@ -5633,7 +5664,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
}
}
/* Handle procedures table. */
/* Handle stored routines table. */
if ((found= handle_grant_table(tables, 4, drop, user_from, user_to)) < 0)
{
/* Handle of table failed, don't touch in-memory array. */
......@@ -5650,6 +5681,15 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
if (! drop && ! user_to)
goto end;
}
/* Handle funcs array. */
if (((handle_grant_struct(4, drop, user_from, user_to) && ! result) ||
found) && ! result)
{
result= 1; /* At least one record/element found. */
/* If search is requested, we do not need to search further. */
if (! drop && ! user_to)
goto end;
}
}
/* Handle tables table. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment