Commit 51ff281e authored by Dmitry Lenev's avatar Dmitry Lenev

Fix for bug#57061 "User without privilege on routine can

discover its existence".

The problem was that user without any privileges on 
routine was able to find out whether it existed or not.
DROP FUNCTION and DROP PROCEDURE statements were 
checking if routine being dropped existed and reported 
ER_SP_DOES_NOT_EXIST error/warning before checking 
if user had enough privileges to drop it.

This patch solves this problem by changing code not to 
check if routine exists before checking if user has enough 
privileges to drop it. Moreover we no longer perform this 
check using a separate call instead we rely on 
sp_drop_routine() returning SP_KEY_NOT_FOUND if routine 
doesn't exist.

This change also simplifies one of upcoming patches
refactoring global read lock implementation.
parent 53c5decb
...@@ -1448,8 +1448,6 @@ CREATE USER 'userbug33464'@'localhost'; ...@@ -1448,8 +1448,6 @@ CREATE USER 'userbug33464'@'localhost';
GRANT CREATE ROUTINE ON dbbug33464.* TO 'userbug33464'@'localhost'; GRANT CREATE ROUTINE ON dbbug33464.* TO 'userbug33464'@'localhost';
userbug33464@localhost dbbug33464 userbug33464@localhost dbbug33464
DROP PROCEDURE IF EXISTS sp3;
DROP FUNCTION IF EXISTS fn1;
CREATE PROCEDURE sp3(v1 char(20)) CREATE PROCEDURE sp3(v1 char(20))
BEGIN BEGIN
SELECT * from dbbug33464.t6 where t6.f2= 'xyz'; SELECT * from dbbug33464.t6 where t6.f2= 'xyz';
......
...@@ -44,7 +44,7 @@ ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1' ...@@ -44,7 +44,7 @@ ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1'
create procedure db1_secret.dummy() begin end; create procedure db1_secret.dummy() begin end;
ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db1_secret' ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db1_secret'
drop procedure db1_secret.dummy; drop procedure db1_secret.dummy;
ERROR 42000: PROCEDURE db1_secret.dummy does not exist ERROR 42000: alter routine command denied to user 'user1'@'localhost' for routine 'db1_secret.dummy'
drop procedure db1_secret.stamp; drop procedure db1_secret.stamp;
ERROR 42000: alter routine command denied to user 'user1'@'localhost' for routine 'db1_secret.stamp' ERROR 42000: alter routine command denied to user 'user1'@'localhost' for routine 'db1_secret.stamp'
drop function db1_secret.db; drop function db1_secret.db;
...@@ -58,7 +58,7 @@ ERROR 42000: SELECT command denied to user ''@'localhost' for table 't1' ...@@ -58,7 +58,7 @@ ERROR 42000: SELECT command denied to user ''@'localhost' for table 't1'
create procedure db1_secret.dummy() begin end; create procedure db1_secret.dummy() begin end;
ERROR 42000: Access denied for user ''@'%' to database 'db1_secret' ERROR 42000: Access denied for user ''@'%' to database 'db1_secret'
drop procedure db1_secret.dummy; drop procedure db1_secret.dummy;
ERROR 42000: PROCEDURE db1_secret.dummy does not exist ERROR 42000: alter routine command denied to user ''@'%' for routine 'db1_secret.dummy'
drop procedure db1_secret.stamp; drop procedure db1_secret.stamp;
ERROR 42000: alter routine command denied to user ''@'%' for routine 'db1_secret.stamp' ERROR 42000: alter routine command denied to user ''@'%' for routine 'db1_secret.stamp'
drop function db1_secret.db; drop function db1_secret.db;
...@@ -567,3 +567,28 @@ DROP USER 'tester'; ...@@ -567,3 +567,28 @@ DROP USER 'tester';
DROP USER 'Tester'; DROP USER 'Tester';
DROP DATABASE B48872; DROP DATABASE B48872;
End of 5.0 tests. End of 5.0 tests.
#
# Test for bug#57061 "User without privilege on routine can discover
# its existence."
#
drop database if exists mysqltest_db;
create database mysqltest_db;
# Create user with no privileges on mysqltest_db database.
create user bug57061_user@localhost;
create function mysqltest_db.f1() returns int return 0;
create procedure mysqltest_db.p1() begin end;
# Connect as user 'bug57061_user@localhost'
# Attempt to drop routine on which user doesn't have privileges
# should result in the same 'access denied' type of error whether
# routine exists or not.
drop function if exists mysqltest_db.f_does_not_exist;
ERROR 42000: alter routine command denied to user 'bug57061_user'@'localhost' for routine 'mysqltest_db.f_does_not_exist'
drop procedure if exists mysqltest_db.p_does_not_exist;
ERROR 42000: alter routine command denied to user 'bug57061_user'@'localhost' for routine 'mysqltest_db.p_does_not_exist'
drop function if exists mysqltest_db.f1;
ERROR 42000: alter routine command denied to user 'bug57061_user'@'localhost' for routine 'mysqltest_db.f1'
drop procedure if exists mysqltest_db.p1;
ERROR 42000: alter routine command denied to user 'bug57061_user'@'localhost' for routine 'mysqltest_db.p1'
# Connection 'default'.
drop user bug57061_user@localhost;
drop database mysqltest_db;
...@@ -110,10 +110,10 @@ Ensure that root always has the GRANT CREATE ROUTINE privilege. ...@@ -110,10 +110,10 @@ Ensure that root always has the GRANT CREATE ROUTINE privilege.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
grant create routine on db_storedproc_1.* to 'user_1'@'localhost'; grant create routine on db_storedproc_1.* to 'user_1'@'localhost';
flush privileges; flush privileges;
DROP PROCEDURE IF EXISTS db_storedproc_1.sp3;
DROP FUNCTION IF EXISTS db_storedproc_1.fn1;
user_1@localhost db_storedproc_1 user_1@localhost db_storedproc_1
DROP PROCEDURE IF EXISTS sp3;
DROP FUNCTION IF EXISTS fn1;
CREATE PROCEDURE sp3(v1 char(20)) CREATE PROCEDURE sp3(v1 char(20))
BEGIN BEGIN
SELECT * from db_storedproc_1.t6 where t6.f2= 'xyz'; SELECT * from db_storedproc_1.t6 where t6.f2= 'xyz';
......
...@@ -111,10 +111,10 @@ Ensure that root always has the GRANT CREATE ROUTINE privilege. ...@@ -111,10 +111,10 @@ Ensure that root always has the GRANT CREATE ROUTINE privilege.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
grant create routine on db_storedproc_1.* to 'user_1'@'localhost'; grant create routine on db_storedproc_1.* to 'user_1'@'localhost';
flush privileges; flush privileges;
DROP PROCEDURE IF EXISTS db_storedproc_1.sp3;
DROP FUNCTION IF EXISTS db_storedproc_1.fn1;
user_1@localhost db_storedproc_1 user_1@localhost db_storedproc_1
DROP PROCEDURE IF EXISTS sp3;
DROP FUNCTION IF EXISTS fn1;
CREATE PROCEDURE sp3(v1 char(20)) CREATE PROCEDURE sp3(v1 char(20))
BEGIN BEGIN
SELECT * from db_storedproc_1.t6 where t6.f2= 'xyz'; SELECT * from db_storedproc_1.t6 where t6.f2= 'xyz';
......
...@@ -111,10 +111,10 @@ Ensure that root always has the GRANT CREATE ROUTINE privilege. ...@@ -111,10 +111,10 @@ Ensure that root always has the GRANT CREATE ROUTINE privilege.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
grant create routine on db_storedproc_1.* to 'user_1'@'localhost'; grant create routine on db_storedproc_1.* to 'user_1'@'localhost';
flush privileges; flush privileges;
DROP PROCEDURE IF EXISTS db_storedproc_1.sp3;
DROP FUNCTION IF EXISTS db_storedproc_1.fn1;
user_1@localhost db_storedproc_1 user_1@localhost db_storedproc_1
DROP PROCEDURE IF EXISTS sp3;
DROP FUNCTION IF EXISTS fn1;
CREATE PROCEDURE sp3(v1 char(20)) CREATE PROCEDURE sp3(v1 char(20))
BEGIN BEGIN
SELECT * from db_storedproc_1.t6 where t6.f2= 'xyz'; SELECT * from db_storedproc_1.t6 where t6.f2= 'xyz';
......
...@@ -117,15 +117,15 @@ create user 'user_1'@'localhost'; ...@@ -117,15 +117,15 @@ create user 'user_1'@'localhost';
grant create routine on db_storedproc_1.* to 'user_1'@'localhost'; grant create routine on db_storedproc_1.* to 'user_1'@'localhost';
flush privileges; flush privileges;
--disable_warnings
DROP PROCEDURE IF EXISTS db_storedproc_1.sp3;
DROP FUNCTION IF EXISTS db_storedproc_1.fn1;
--enable_warnings
# disconnect default; # disconnect default;
connect (user2, localhost, user_1, , db_storedproc_1); connect (user2, localhost, user_1, , db_storedproc_1);
--source suite/funcs_1/include/show_connection.inc --source suite/funcs_1/include/show_connection.inc
--disable_warnings
DROP PROCEDURE IF EXISTS sp3;
DROP FUNCTION IF EXISTS fn1;
--enable_warnings
delimiter //; delimiter //;
CREATE PROCEDURE sp3(v1 char(20)) CREATE PROCEDURE sp3(v1 char(20))
BEGIN BEGIN
......
...@@ -1419,11 +1419,6 @@ GRANT CREATE ROUTINE ON dbbug33464.* TO 'userbug33464'@'localhost'; ...@@ -1419,11 +1419,6 @@ GRANT CREATE ROUTINE ON dbbug33464.* TO 'userbug33464'@'localhost';
connect (connbug33464, localhost, userbug33464, , dbbug33464); connect (connbug33464, localhost, userbug33464, , dbbug33464);
--source suite/funcs_1/include/show_connection.inc --source suite/funcs_1/include/show_connection.inc
--disable_warnings
DROP PROCEDURE IF EXISTS sp3;
DROP FUNCTION IF EXISTS fn1;
--enable_warnings
delimiter //; delimiter //;
CREATE PROCEDURE sp3(v1 char(20)) CREATE PROCEDURE sp3(v1 char(20))
BEGIN BEGIN
......
...@@ -82,7 +82,7 @@ select * from db1_secret.t1; ...@@ -82,7 +82,7 @@ select * from db1_secret.t1;
# ...and not this # ...and not this
--error ER_DBACCESS_DENIED_ERROR --error ER_DBACCESS_DENIED_ERROR
create procedure db1_secret.dummy() begin end; create procedure db1_secret.dummy() begin end;
--error ER_SP_DOES_NOT_EXIST --error ER_PROCACCESS_DENIED_ERROR
drop procedure db1_secret.dummy; drop procedure db1_secret.dummy;
--error ER_PROCACCESS_DENIED_ERROR --error ER_PROCACCESS_DENIED_ERROR
drop procedure db1_secret.stamp; drop procedure db1_secret.stamp;
...@@ -106,7 +106,7 @@ select * from db1_secret.t1; ...@@ -106,7 +106,7 @@ select * from db1_secret.t1;
# ...and not this # ...and not this
--error ER_DBACCESS_DENIED_ERROR --error ER_DBACCESS_DENIED_ERROR
create procedure db1_secret.dummy() begin end; create procedure db1_secret.dummy() begin end;
--error ER_SP_DOES_NOT_EXIST --error ER_PROCACCESS_DENIED_ERROR
drop procedure db1_secret.dummy; drop procedure db1_secret.dummy;
--error ER_PROCACCESS_DENIED_ERROR --error ER_PROCACCESS_DENIED_ERROR
drop procedure db1_secret.stamp; drop procedure db1_secret.stamp;
...@@ -926,6 +926,39 @@ DROP DATABASE B48872; ...@@ -926,6 +926,39 @@ DROP DATABASE B48872;
--echo End of 5.0 tests. --echo End of 5.0 tests.
--echo #
--echo # Test for bug#57061 "User without privilege on routine can discover
--echo # its existence."
--echo #
--disable_warnings
drop database if exists mysqltest_db;
--enable_warnings
create database mysqltest_db;
--echo # Create user with no privileges on mysqltest_db database.
create user bug57061_user@localhost;
create function mysqltest_db.f1() returns int return 0;
create procedure mysqltest_db.p1() begin end;
--echo # Connect as user 'bug57061_user@localhost'
connect (conn1, localhost, bug57061_user,,);
--echo # Attempt to drop routine on which user doesn't have privileges
--echo # should result in the same 'access denied' type of error whether
--echo # routine exists or not.
--error ER_PROCACCESS_DENIED_ERROR
drop function if exists mysqltest_db.f_does_not_exist;
--error ER_PROCACCESS_DENIED_ERROR
drop procedure if exists mysqltest_db.p_does_not_exist;
--error ER_PROCACCESS_DENIED_ERROR
drop function if exists mysqltest_db.f1;
--error ER_PROCACCESS_DENIED_ERROR
drop procedure if exists mysqltest_db.p1;
--echo # Connection 'default'.
connection default;
disconnect conn1;
drop user bug57061_user@localhost;
drop database mysqltest_db;
# Wait till all disconnects are completed # Wait till all disconnects are completed
--source include/wait_until_count_sessions.inc --source include/wait_until_count_sessions.inc
...@@ -1636,38 +1636,6 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any) ...@@ -1636,38 +1636,6 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any)
} }
/**
Check if a routine exists in the mysql.proc table, without actually
parsing the definition. (Used for dropping).
@param thd thread context
@param name name of procedure
@retval
0 Success
@retval
non-0 Error; SP_OPEN_TABLE_FAILED or SP_KEY_NOT_FOUND
*/
int
sp_routine_exists_in_table(THD *thd, int type, sp_name *name)
{
TABLE *table;
int ret;
Open_tables_backup open_tables_state_backup;
if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup)))
ret= SP_OPEN_TABLE_FAILED;
else
{
if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK)
ret= SP_KEY_NOT_FOUND;
close_system_tables(thd, &open_tables_state_backup);
}
return ret;
}
extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
my_bool first) my_bool first)
{ {
......
...@@ -100,9 +100,6 @@ sp_cache_routine(THD *thd, int type, sp_name *name, ...@@ -100,9 +100,6 @@ sp_cache_routine(THD *thd, int type, sp_name *name,
bool bool
sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any); sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any);
int
sp_routine_exists_in_table(THD *thd, int type, sp_name *name);
bool bool
sp_show_create_routine(THD *thd, int type, sp_name *name); sp_show_create_routine(THD *thd, int type, sp_name *name);
......
...@@ -4023,17 +4023,6 @@ create_sp_error: ...@@ -4023,17 +4023,6 @@ create_sp_error:
int sp_result; int sp_result;
int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ? int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
/*
@todo: here we break the metadata locking protocol by
looking up the information about the routine without
a metadata lock. Rewrite this piece to make sp_drop_routine
return whether the routine existed or not.
*/
sp_result= sp_routine_exists_in_table(thd, type, lex->spname);
thd->warning_info->opt_clear_warning_info(thd->query_id);
if (sp_result == SP_OK)
{
char *db= lex->spname->m_db.str; char *db= lex->spname->m_db.str;
char *name= lex->spname->m_name.str; char *name= lex->spname->m_name.str;
...@@ -4054,7 +4043,8 @@ create_sp_error: ...@@ -4054,7 +4043,8 @@ create_sp_error:
*/ */
close_mysql_tables(thd); close_mysql_tables(thd);
if (sp_automatic_privileges && !opt_noacl && if (sp_result != SP_KEY_NOT_FOUND &&
sp_automatic_privileges && !opt_noacl &&
sp_revoke_privileges(thd, db, name, sp_revoke_privileges(thd, db, name,
lex->sql_command == SQLCOM_DROP_PROCEDURE)) lex->sql_command == SQLCOM_DROP_PROCEDURE))
{ {
...@@ -4065,7 +4055,7 @@ create_sp_error: ...@@ -4065,7 +4055,7 @@ create_sp_error:
goto error; goto error;
} }
#endif #endif
}
res= sp_result; res= sp_result;
switch (sp_result) { switch (sp_result) {
case SP_OK: case SP_OK:
......
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