summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/drop.result39
-rw-r--r--mysql-test/t/drop.test62
-rw-r--r--sql/sql_acl.cc105
3 files changed, 184 insertions, 22 deletions
diff --git a/mysql-test/r/drop.result b/mysql-test/r/drop.result
index 71d6fcc7cd0..b4f6395819f 100644
--- a/mysql-test/r/drop.result
+++ b/mysql-test/r/drop.result
@@ -91,4 +91,43 @@ create table mysql_test.`#sql-347f_7` (f1 int);
create table mysql_test.`#sql-347f_8` (f1 int);
drop table mysql_test.`#sql-347f_8`;
drop database mysql_test;
+CREATE DATABASE dbbug33464;
+CREATE USER 'userbug33464'@'localhost';
+GRANT CREATE ROUTINE ON dbbug33464.* TO 'userbug33464'@'localhost';
+
+userbug33464@localhost dbbug33464
+DROP PROCEDURE IF EXISTS sp3;
+DROP FUNCTION IF EXISTS fn1;
+CREATE PROCEDURE sp3(v1 char(20))
+BEGIN
+SELECT * from dbbug33464.t6 where t6.f2= 'xyz';
+END//
+CREATE FUNCTION fn1() returns char(50) SQL SECURITY INVOKER
+BEGIN
+return 1;
+END//
+CREATE FUNCTION fn2() returns char(50) SQL SECURITY DEFINER
+BEGIN
+return 2;
+END//
+USE dbbug33464;
+
+root@localhost dbbug33464
+SELECT fn1();
+fn1()
+1
+SELECT fn2();
+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;
+DROP DATABASE dbbug33464;
End of 5.1 tests
diff --git a/mysql-test/t/drop.test b/mysql-test/t/drop.test
index a79044436eb..3dd180f2d78 100644
--- a/mysql-test/t/drop.test
+++ b/mysql-test/t/drop.test
@@ -134,4 +134,66 @@ drop table mysql_test.`#sql-347f_8`;
copy_file $MYSQLTEST_VARDIR/master-data/mysql_test/t1.frm $MYSQLTEST_VARDIR/master-data/mysql_test/#sql-347f_6.frm;
drop database mysql_test;
+
+#
+# Bug#33464: DROP FUNCTION caused a crash.
+#
+CREATE DATABASE dbbug33464;
+CREATE USER 'userbug33464'@'localhost';
+
+GRANT CREATE ROUTINE ON dbbug33464.* TO 'userbug33464'@'localhost';
+
+--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
+connect (connbug33464, localhost, userbug33464, , dbbug33464);
+--source suite/funcs_1/include/show_connection.inc
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS sp3;
+DROP FUNCTION IF EXISTS fn1;
+--enable_warnings
+
+delimiter //;
+CREATE PROCEDURE sp3(v1 char(20))
+BEGIN
+ SELECT * from dbbug33464.t6 where t6.f2= 'xyz';
+END//
+delimiter ;//
+
+delimiter //;
+CREATE FUNCTION fn1() returns char(50) SQL SECURITY INVOKER
+BEGIN
+ return 1;
+END//
+delimiter ;//
+
+delimiter //;
+CREATE FUNCTION fn2() returns char(50) SQL SECURITY DEFINER
+BEGIN
+ return 2;
+END//
+delimiter ;//
+
+disconnect connbug33464;
+
+# cleanup
+connection default;
+USE dbbug33464;
+--source suite/funcs_1/include/show_connection.inc
+
+SELECT fn1();
+SELECT fn2();
+
+--error 0, ER_CANNOT_USER
+DROP USER 'userbug33464'@'localhost';
+
+DROP FUNCTION fn1;
+DROP FUNCTION fn2;
+DROP PROCEDURE sp3;
+
+--error 0, ER_CANNOT_USER
+DROP USER 'userbug33464'@'localhost';
+
+use test;
+DROP DATABASE dbbug33464;
+
--echo End of 5.1 tests
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 3805d9fa8b6..8457b48fdf5 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -2779,6 +2779,10 @@ table_error:
}
+/**
+ @retval 0 success
+ @retval -1 error
+*/
static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
TABLE *table, const LEX_USER &combo,
const char *db, const char *routine_name,
@@ -2800,14 +2804,11 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
thd->security_ctx->host_or_ip, NullS);
/*
- The following should always succeed as new users are created before
- this function is called!
+ New users are created before this function is called.
+
+ There may be some cases where a routine's definer is removed but the
+ routine remains.
*/
- if (!find_acl_user(combo.host.str, combo.user.str, FALSE))
- {
- my_error(ER_PASSWORD_NO_MATCH,MYF(0));
- DBUG_RETURN(-1);
- }
table->use_all_columns();
restore_record(table, s->default_values); // Get empty record
@@ -3328,7 +3329,8 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
}
if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
- db_name, table_name, is_proc, rights, revoke_grant))
+ db_name, table_name, is_proc, rights,
+ revoke_grant) != 0)
{
result= TRUE;
continue;
@@ -5956,11 +5958,11 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
if (!strcmp(lex_user->user.str,user) &&
!strcmp(lex_user->host.str, host))
{
- if (!replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
+ if (replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
grant_proc->db,
grant_proc->tname,
is_proc,
- ~(ulong)0, 1))
+ ~(ulong)0, 1) == 0)
{
revoked= 1;
continue;
@@ -5986,17 +5988,73 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
}
-/*
- Revoke privileges for all users on a stored procedure
- SYNOPSIS
- sp_revoke_privileges()
+
+/**
+ If the defining user for a routine does not exist, then the ACL lookup
+ code should raise two errors which we should intercept. We convert the more
+ descriptive error into a warning, and consume the other.
+
+ If any other errors are raised, then we set a flag that should indicate
+ that there was some failure we should complain at a higher level.
+*/
+class Silence_routine_definer_errors : public Internal_error_handler
+{
+public:
+ Silence_routine_definer_errors()
+ : is_grave(FALSE)
+ {}
+
+ virtual ~Silence_routine_definer_errors()
+ {}
+
+ virtual bool handle_error(uint sql_errno, const char *message,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd);
+
+ bool has_errors() { return is_grave; }
+
+private:
+ bool is_grave;
+};
+
+bool
+Silence_routine_definer_errors::handle_error(uint sql_errno,
+ const char *message,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd)
+{
+ if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
+ {
+ switch (sql_errno)
+ {
+ case ER_NONEXISTING_PROC_GRANT:
+ /* Convert the error into a warning. */
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, sql_errno, message);
+ return TRUE;
+ default:
+ is_grave= TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Revoke privileges for all users on a stored procedure. Use an error handler
+ that converts errors about missing grants into warnings.
+
+ @param
thd The current thread.
+ @param
db DB of the stored procedure
+ @param
name Name of the stored procedure
- RETURN
+ @retval
0 OK.
+ @retval
< 0 Error. Error message not yet sent.
*/
@@ -6007,11 +6065,15 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
int result;
TABLE_LIST tables[GRANT_TABLES];
HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
+ Silence_routine_definer_errors error_handler;
DBUG_ENTER("sp_revoke_privileges");
if ((result= open_grant_tables(thd, tables)))
DBUG_RETURN(result != 1);
+ /* Be sure to pop this before exiting this scope! */
+ thd->push_internal_handler(&error_handler);
+
rw_wrlock(&LOCK_grant);
VOID(pthread_mutex_lock(&acl_cache->lock));
@@ -6038,14 +6100,14 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
grant_proc->host.hostname : (char*)"";
lex_user.host.length= grant_proc->host.hostname ?
strlen(grant_proc->host.hostname) : 0;
- if (!replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
- grant_proc->db, grant_proc->tname,
- is_proc, ~(ulong)0, 1))
+
+ if (replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
+ grant_proc->db, grant_proc->tname,
+ is_proc, ~(ulong)0, 1) == 0)
{
revoked= 1;
continue;
}
- result= -1; // Something went wrong
}
counter++;
}
@@ -6055,10 +6117,9 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
rw_unlock(&LOCK_grant);
close_thread_tables(thd);
- if (result)
- my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
+ thd->pop_internal_handler();
- DBUG_RETURN(result);
+ DBUG_RETURN(error_handler.has_errors());
}