summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <acurtis@pcgem.rdg.cyberkinetica.com>2004-12-23 10:46:24 +0000
committerunknown <acurtis@pcgem.rdg.cyberkinetica.com>2004-12-23 10:46:24 +0000
commit09ce0b330b3af16075958c306db984af5cd8b3b6 (patch)
tree1f9ae909b765f73df023264dce5aa52d83e5cb8e
parentd1db2ebf8434a9b8acf95962251a0f5078f413e7 (diff)
downloadmariadb-git-09ce0b330b3af16075958c306db984af5cd8b3b6.tar.gz
WL#925 - Privileges for stored routines
Implement fine-grained control over access to stored procedures Privileges are cached (same way as existing table/column privs) mysql-test/include/system_db_struct.inc: WL#925 - Privileges for stored routines New system table: procs_priv mysql-test/r/connect.result: WL#925 - Privileges for stored routines New system table: procs_priv mysql-test/r/grant.result: WL#925 - Privileges for stored routines user table has additional privilege attributes SHOW PRIVILEGES amended mysql-test/r/grant2.result: Fix result mysql-test/r/information_schema.result: WL#925 - Privileges for stored routines New system table procs_priv New user privileges mysql-test/r/show_check.result: Fix result mysql-test/r/sp-security.result: WL#925 - Privileges for stored routines Fix existing tests to work with new privileges New tests for new privileges mysql-test/r/sp.result: WL#925 - Privileges for stored routines Fix SHOW PRIVILEGES results mysql-test/r/system_mysql_db.result: WL#925 - Privileges for stored routines New system table: procs_priv user and db tables have new privilege attributes mysql-test/t/grant2.test: Fix test mysql-test/t/show_check.test: Fix test mysql-test/t/sp-security.test: WL#925 - Privileges for stored routines Allow existing tests to run with new privilege checks New tests for privileges mysql-test/t/system_mysql_db_fix.test: WL#925 - Privileges for stored routines New system table: procs_priv scripts/mysql_create_system_tables.sh: WL#925 - Privileges for stored routines db and user has new privilege attributes new system table: procs_priv scripts/mysql_fix_privilege_tables.sql: WL#925 - Privileges for stored routines new system table: procs_priv scripts/mysql_install_db.sh: WL#925 - Privileges for stored routines Amend comment sql/item_func.cc: WL#925 - Privileges for stored routines Privilege check for stored FUNCTION routine sql/lex.h: WL#925 - Privileges for stored routines new token ROUTINE sql/mysql_priv.h: WL#925 - Privileges for stored routines New function: check_procedure_access() sql/mysqld.cc: WL#925 - Privileges for stored routines system option automatic-sp-privileges sql/set_var.cc: WL#925 - Privileges for stored routines system option automatic-sp-privileges sql/share/errmsg.txt: WL#925 - Privileges for stored routines rename errormessage to conform: ER_SP_ACCESS_DENIED_ERROR -> ER_PROCACCESS_DENIED_ERROR New error messages ER_NONEXISTING_PROC_GRANT, ER_PROC_AUTO_GRANT_FAIL, ER_PROC_AUTO_REVOKE_FAIL sql/sp.cc: WL#925 - Privileges for stored routines new function: sp_exists_routine() sql/sp.h: WL#925 - Privileges for stored routines new function: sp_exists_routine() sql/sql_acl.cc: WL#925 - Privileges for stored routines Implementation for SP privileges. Privileges are cached in memory hash. New functions: mysql_procedure_grant() check_grant_procedure() sp_revoke_privileges() sp_grant_privileges() sql/sql_acl.h: WL#925 - Privileges for stored routines New privilege bits: CREATE_PROC_ACL, ALTER_PROC_ACL Alter confusing bit-segments to be shifted New macros: fix_rights_for_procedure() get_rights_for_procedure() New functions: mysql_procedure_grant() check_grant_procedure() sp_grant_privileges() sp_revoke_privileges() sql/sql_lex.h: WL#925 - Privileges for stored routines new all_privileges attribute in LEX sql/sql_parse.cc: WL#925 - Privileges for stored routines Remove function: check_sp_definer_access() Add handling for SP grants/revokes Add privilege checks for stored procedure invocation sql/sql_show.cc: WL#925 - Privileges for stored routines update result for SHOW PRIVILEGES sql/sql_yacc.yy: WL#925 - Privileges for stored routines New token ROUTINE rename some rules handle CREATE ROUTINE / ALTER ROUTINE privileges
-rw-r--r--mysql-test/include/system_db_struct.inc1
-rw-r--r--mysql-test/r/connect.result3
-rw-r--r--mysql-test/r/grant.result11
-rw-r--r--mysql-test/r/grant2.result6
-rw-r--r--mysql-test/r/information_schema.result6
-rw-r--r--mysql-test/r/show_check.result8
-rw-r--r--mysql-test/r/sp-security.result82
-rw-r--r--mysql-test/r/sp.result10
-rw-r--r--mysql-test/r/system_mysql_db.result19
-rw-r--r--mysql-test/t/grant2.test6
-rw-r--r--mysql-test/t/show_check.test8
-rw-r--r--mysql-test/t/sp-security.test97
-rw-r--r--mysql-test/t/system_mysql_db_fix.test2
-rw-r--r--scripts/mysql_create_system_tables.sh49
-rw-r--r--scripts/mysql_fix_privilege_tables.sql39
-rw-r--r--scripts/mysql_install_db.sh2
-rw-r--r--sql/item_func.cc11
-rw-r--r--sql/lex.h1
-rw-r--r--sql/mysql_priv.h3
-rw-r--r--sql/mysqld.cc7
-rw-r--r--sql/set_var.cc5
-rw-r--r--sql/share/errmsg.txt10
-rw-r--r--sql/sp.cc39
-rw-r--r--sql/sp.h3
-rw-r--r--sql/sql_acl.cc888
-rw-r--r--sql/sql_acl.h44
-rw-r--r--sql/sql_lex.h1
-rw-r--r--sql/sql_parse.cc155
-rw-r--r--sql/sql_show.cc5
-rw-r--r--sql/sql_yacc.yy27
30 files changed, 1341 insertions, 207 deletions
diff --git a/mysql-test/include/system_db_struct.inc b/mysql-test/include/system_db_struct.inc
index 5a7aa26c65a..e24c8f3311d 100644
--- a/mysql-test/include/system_db_struct.inc
+++ b/mysql-test/include/system_db_struct.inc
@@ -10,3 +10,4 @@ show create table user;
show create table func;
show create table tables_priv;
show create table columns_priv;
+show create table procs_priv;
diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result
index 702b725764b..fef813371c8 100644
--- a/mysql-test/r/connect.result
+++ b/mysql-test/r/connect.result
@@ -9,6 +9,7 @@ help_relation
help_topic
host
proc
+procs_priv
tables_priv
time_zone
time_zone_leap_second
@@ -31,6 +32,7 @@ help_relation
help_topic
host
proc
+procs_priv
tables_priv
time_zone
time_zone_leap_second
@@ -57,6 +59,7 @@ help_relation
help_topic
host
proc
+procs_priv
tables_priv
time_zone
time_zone_leap_second
diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result
index b69ba9702a6..629a3221330 100644
--- a/mysql-test/r/grant.result
+++ b/mysql-test/r/grant.result
@@ -10,8 +10,8 @@ GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3
GRANT SELECT ON `mysqltest`.* TO 'mysqltest_1'@'localhost'
grant delete on mysqltest.* to mysqltest_1@localhost;
select * from mysql.user where user="mysqltest_1";
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections
-localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N 0 0 0
+Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections
+localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N 0 0 0
show grants for mysqltest_1@localhost;
Grants for mysqltest_1@localhost
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA'
@@ -62,7 +62,7 @@ revoke LOCK TABLES, ALTER on mysqltest.* from mysqltest_1@localhost;
show grants for mysqltest_1@localhost;
Grants for mysqltest_1@localhost
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost'
-GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES, CREATE VIEW, SHOW VIEW ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION
+GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION
revoke all privileges on mysqltest.* from mysqltest_1@localhost;
delete from mysql.user where user='mysqltest_1';
flush privileges;
@@ -347,13 +347,16 @@ drop table t1;
SHOW PRIVILEGES;
Privilege Context Comment
Alter Tables To alter the table
+Alter routine Functions,Procedures To alter or drop stored functions/procedures
Create Databases,Tables,Indexes To create new databases and tables
+Create routine Functions,Procedures To use CREATE FUNCTION/PROCEDURE
Create temporary tables Databases To use CREATE TEMPORARY TABLE
Create view Tables To create new views
Delete Tables To delete existing rows
Drop Databases,Tables To drop databases, tables, and views
+Execute Functions,Procedures To execute stored routines
File File access on server To read and write files on the server
-Grant option Databases,Tables To give to other users those privileges you possess
+Grant option Databases,Tables,Functions,Procedures To give to other users those privileges you possess
Index Tables To create or drop indexes
Insert Tables To insert data into tables
Lock tables Databases To use LOCK TABLES (together with SELECT privilege)
diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result
index edfd1a07680..48cbac10d46 100644
--- a/mysql-test/r/grant2.result
+++ b/mysql-test/r/grant2.result
@@ -48,9 +48,9 @@ GRANT SELECT, INSERT ON `mysqltest`.* TO 'mysqltest_1'@'localhost'
use mysqltest;
insert into t1 values (1, 'I can''t change it!');
update t1 set data='I can change it!' where id = 1;
-ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest'
+ERROR 42000: update command denied to user 'mysqltest_1'@'localhost' for table 't1'
insert into t1 values (1, 'XXX') on duplicate key update data= 'I can change it!';
-ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest'
+ERROR 42000: update command denied to user 'mysqltest_1'@'localhost' for table 't1'
select * from t1;
id data
1 I can't change it!
@@ -202,7 +202,7 @@ drop user '%@a'@'a';
create user mysqltest_2@localhost;
grant usage on *.* to mysqltest_2@localhost with grant option;
select host,user,password from mysql.user where user like 'mysqltest_%' order by host,user,password;
-ERROR 42000: Access denied for user 'mysqltest_2'@'localhost' to database 'mysql'
+ERROR 42000: select command denied to user 'mysqltest_2'@'localhost' for table 'user'
create user mysqltest_A@'%';
rename user mysqltest_A@'%' to mysqltest_B@'%';
drop user mysqltest_B@'%';
diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result
index 0bd3ba14636..6cd58674a3c 100644
--- a/mysql-test/r/information_schema.result
+++ b/mysql-test/r/information_schema.result
@@ -58,6 +58,7 @@ help_relation
help_topic
host
proc
+procs_priv
tables_priv
time_zone
time_zone_leap_second
@@ -346,8 +347,11 @@ GRANTEE TABLE_CATALOG TABLE_SCHEMA PRIVILEGE_TYPE IS_GRANTABLE
'mysqltest_1'@'localhost' NULL test ALTER YES
'mysqltest_1'@'localhost' NULL test CREATE TEMPORARY TABLES YES
'mysqltest_1'@'localhost' NULL test LOCK TABLES YES
+'mysqltest_1'@'localhost' NULL test EXECUTE YES
'mysqltest_1'@'localhost' NULL test CREATE VIEW YES
'mysqltest_1'@'localhost' NULL test SHOW VIEW YES
+'mysqltest_1'@'localhost' NULL test CREATE ROUTINE YES
+'mysqltest_1'@'localhost' NULL test ALTER ROUTINE YES
select * from information_schema.TABLE_PRIVILEGES where grantee like '%mysqltest_1%';
GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE
'mysqltest_1'@'localhost' NULL test t1 SELECT NO
@@ -600,6 +604,8 @@ Process_priv select,insert,update,references
Show_db_priv select,insert,update,references
Lock_tables_priv select,insert,update,references
Show_view_priv select,insert,update,references
+Create_routine_priv select,insert,update,references
+Alter_routine_priv select,insert,update,references
max_questions select,insert,update,references
max_connections select,insert,update,references
use test;
diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result
index ec9bd33d301..c1cabce7e92 100644
--- a/mysql-test/r/show_check.result
+++ b/mysql-test/r/show_check.result
@@ -382,19 +382,19 @@ show create database mysqltest;
Database Create Database
mysqltest CREATE DATABASE `mysqltest` /*!40100 DEFAULT CHARACTER SET latin1 */
drop table t1;
-ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest'
+ERROR 42000: drop command denied to user 'mysqltest_1'@'localhost' for table 't1'
drop database mysqltest;
ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest'
select * from mysqltest.t1;
-ERROR 42000: Access denied for user 'mysqltest_2'@'localhost' to database 'mysqltest'
+ERROR 42000: select command denied to user 'mysqltest_2'@'localhost' for table 't1'
show create database mysqltest;
ERROR 42000: Access denied for user 'mysqltest_2'@'localhost' to database 'mysqltest'
drop table mysqltest.t1;
-ERROR 42000: Access denied for user 'mysqltest_2'@'localhost' to database 'mysqltest'
+ERROR 42000: drop command denied to user 'mysqltest_2'@'localhost' for table 't1'
drop database mysqltest;
ERROR 42000: Access denied for user 'mysqltest_2'@'localhost' to database 'mysqltest'
select * from mysqltest.t1;
-ERROR 42000: Access denied for user 'mysqltest_3'@'localhost' to database 'mysqltest'
+ERROR 42000: select command denied to user 'mysqltest_3'@'localhost' for table 't1'
show create database mysqltest;
ERROR 42000: Access denied for user 'mysqltest_3'@'localhost' to database 'mysqltest'
drop table mysqltest.t1;
diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result
index 25582796812..365368873fa 100644
--- a/mysql-test/r/sp-security.result
+++ b/mysql-test/r/sp-security.result
@@ -23,12 +23,16 @@ root@localhost 1
select db();
db()
db1_secret
+grant execute on db1_secret.stamp to user1@'%';
+grant execute on db1_secret.db to user1@'%';
+grant execute on db1_secret.stamp to ''@'%';
+grant execute on db1_secret.db to ''@'%';
call db1_secret.stamp(2);
select db1_secret.db();
db1_secret.db()
db1_secret
select * from db1_secret.t1;
-ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db1_secret'
+ERROR 42000: select command denied to user 'user1'@'localhost' for table 't1'
create procedure db1_secret.dummy() begin end;
ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db1_secret'
drop procedure db1_secret.dummy;
@@ -38,7 +42,7 @@ select db1_secret.db();
db1_secret.db()
db1_secret
select * from db1_secret.t1;
-ERROR 42000: Access denied for user ''@'localhost' to database 'db1_secret'
+ERROR 42000: select command denied to user ''@'localhost' for table 't1'
create procedure db1_secret.dummy() begin end;
ERROR 42000: Access denied for user ''@'localhost' to database 'db1_secret'
drop procedure db1_secret.dummy;
@@ -82,15 +86,16 @@ insert into t2 values (0);
grant usage on db2.* to user1@localhost;
grant select on db2.* to user1@localhost;
grant usage on db2.* to user2@localhost;
-grant select,insert,update,delete on db2.* to user2@localhost;
+grant select,insert,update,delete,create routine on db2.* to user2@localhost;
+grant create routine on db2.* to user1@localhost;
flush privileges;
use db2;
create procedure p () insert into t2 values (1);
call p();
-ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db2'
+ERROR 42000: insert command denied to user 'user1'@'localhost' for table 't2'
use db2;
call p();
-ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db2'
+ERROR 42000: execute command denied to user 'user2'@'localhost' for routine 'db2.p'
select * from t2;
s1
0
@@ -100,6 +105,8 @@ select * from t2;
s1
0
2
+grant usage on db2.q to user2@localhost with grant option;
+grant execute on db2.q to user1@localhost;
use db2;
call q();
select * from t2;
@@ -110,9 +117,9 @@ s1
alter procedure p modifies sql data;
drop procedure p;
alter procedure q modifies sql data;
-ERROR 42000: Access denied; you are not the procedure/function definer of 'db2.q'
+ERROR 42000: alter procedure command denied to user 'user1'@'localhost' for routine 'db2.q'
drop procedure q;
-ERROR 42000: Access denied; you are not the procedure/function definer of 'db2.q'
+ERROR 42000: alter procedure command denied to user 'user1'@'localhost' for routine 'db2.q'
use db2;
alter procedure q modifies sql data;
drop procedure q;
@@ -126,3 +133,64 @@ drop database db2;
select type,db,name from mysql.proc;
type db name
delete from mysql.user where user='user1' or user='user2';
+delete from mysql.procs_priv where user='user1' or user='user2';
+grant usage on *.* to usera@localhost;
+grant usage on *.* to userb@localhost;
+grant usage on *.* to userc@localhost;
+create database sptest;
+create table t1 ( u varchar(64), i int );
+create procedure sptest.p1(i int) insert into test.t1 values (user(), i);
+grant insert on t1 to usera@localhost;
+grant execute on sptest.p1 to usera@localhost;
+show grants for usera@localhost;
+Grants for usera@localhost
+GRANT USAGE ON *.* TO 'usera'@'localhost'
+GRANT INSERT ON `test`.`t1` TO 'usera'@'localhost'
+GRANT EXECUTE ON `sptest`.`p1` TO 'usera'@'localhost'
+grant execute on sptest.p1 to userc@localhost with grant option;
+show grants for userc@localhost;
+Grants for userc@localhost
+GRANT USAGE ON *.* TO 'userc'@'localhost'
+GRANT EXECUTE ON `sptest`.`p1` TO 'userc'@'localhost' WITH GRANT OPTION
+call sptest.p1(1);
+grant execute on sptest.p1 to userb@localhost;
+ERROR 42000: grant command denied to user 'usera'@'localhost' for routine 'sptest.p1'
+drop procedure sptest.p1;
+ERROR 42000: alter procedure command denied to user 'usera'@'localhost' for routine 'sptest.p1'
+call sptest.p1(2);
+ERROR 42000: execute command denied to user 'userb'@'localhost' for routine 'sptest.p1'
+grant execute on sptest.p1 to userb@localhost;
+ERROR 42000: execute command denied to user 'userb'@'localhost' for routine 'sptest.p1'
+drop procedure sptest.p1;
+ERROR 42000: alter procedure command denied to user 'userb'@'localhost' for routine 'sptest.p1'
+call sptest.p1(3);
+grant execute on sptest.p1 to userb@localhost;
+drop procedure sptest.p1;
+ERROR 42000: alter procedure command denied to user 'userc'@'localhost' for routine 'sptest.p1'
+call sptest.p1(4);
+grant execute on sptest.p1 to userb@localhost;
+ERROR 42000: grant command denied to user 'userb'@'localhost' for routine 'sptest.p1'
+drop procedure sptest.p1;
+ERROR 42000: alter procedure command denied to user 'userb'@'localhost' for routine 'sptest.p1'
+select * from t1;
+u i
+usera@localhost 1
+userc@localhost 3
+userb@localhost 4
+grant all privileges on sptest.p1 to userc@localhost;
+show grants for userc@localhost;
+Grants for userc@localhost
+GRANT USAGE ON *.* TO 'userc'@'localhost'
+GRANT EXECUTE, ALTER ROUTINE ON `sptest`.`p1` TO 'userc'@'localhost' WITH GRANT OPTION
+show grants for userb@localhost;
+Grants for userb@localhost
+GRANT USAGE ON *.* TO 'userb'@'localhost'
+GRANT EXECUTE ON `sptest`.`p1` TO 'userb'@'localhost'
+revoke all privileges on sptest.p1 from userb@localhost;
+show grants for userb@localhost;
+Grants for userb@localhost
+GRANT USAGE ON *.* TO 'userb'@'localhost'
+use test;
+drop database sptest;
+delete from mysql.user where user='usera' or user='userb' or user='userc';
+delete from mysql.procs_priv where user='usera' or user='userb' or user='userc';
diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result
index 0d7f9f7d50c..269c4104a05 100644
--- a/mysql-test/r/sp.result
+++ b/mysql-test/r/sp.result
@@ -1654,13 +1654,16 @@ Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_par
Database Table In_use Name_locked
Privilege Context Comment
Alter Tables To alter the table
+Alter routine Functions,Procedures To alter or drop stored functions/procedures
Create Databases,Tables,Indexes To create new databases and tables
+Create routine Functions,Procedures To use CREATE FUNCTION/PROCEDURE
Create temporary tables Databases To use CREATE TEMPORARY TABLE
Create view Tables To create new views
Delete Tables To delete existing rows
Drop Databases,Tables To drop databases, tables, and views
+Execute Functions,Procedures To execute stored routines
File File access on server To read and write files on the server
-Grant option Databases,Tables To give to other users those privileges you possess
+Grant option Databases,Tables,Functions,Procedures To give to other users those privileges you possess
Index Tables To create or drop indexes
Insert Tables To insert data into tables
Lock tables Databases To use LOCK TABLES (together with SELECT privilege)
@@ -1704,13 +1707,16 @@ Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_par
Database Table In_use Name_locked
Privilege Context Comment
Alter Tables To alter the table
+Alter routine Functions,Procedures To alter or drop stored functions/procedures
Create Databases,Tables,Indexes To create new databases and tables
+Create routine Functions,Procedures To use CREATE FUNCTION/PROCEDURE
Create temporary tables Databases To use CREATE TEMPORARY TABLE
Create view Tables To create new views
Delete Tables To delete existing rows
Drop Databases,Tables To drop databases, tables, and views
+Execute Functions,Procedures To execute stored routines
File File access on server To read and write files on the server
-Grant option Databases,Tables To give to other users those privileges you possess
+Grant option Databases,Tables,Functions,Procedures To give to other users those privileges you possess
Index Tables To create or drop indexes
Insert Tables To insert data into tables
Lock tables Databases To use LOCK TABLES (together with SELECT privilege)
diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result
index 400b508ff50..e9606ec5f88 100644
--- a/mysql-test/r/system_mysql_db.result
+++ b/mysql-test/r/system_mysql_db.result
@@ -9,6 +9,7 @@ help_relation
help_topic
host
proc
+procs_priv
tables_priv
time_zone
time_zone_leap_second
@@ -36,6 +37,9 @@ db CREATE TABLE `db` (
`Lock_tables_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
`Create_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
`Show_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
+ `Create_routine_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
+ `Alter_routine_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
+ `Execute_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
PRIMARY KEY (`Host`,`Db`,`User`),
KEY `User` (`User`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Database privileges'
@@ -89,6 +93,8 @@ user CREATE TABLE `user` (
`Repl_client_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
`Create_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
`Show_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
+ `Create_routine_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
+ `Alter_routine_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
`ssl_type` enum('','ANY','X509','SPECIFIED') collate utf8_bin NOT NULL default '',
`ssl_cipher` blob NOT NULL,
`x509_issuer` blob NOT NULL,
@@ -133,5 +139,18 @@ columns_priv CREATE TABLE `columns_priv` (
`Column_priv` set('Select','Insert','Update','References') collate utf8_bin NOT NULL default '',
PRIMARY KEY (`Host`,`Db`,`User`,`Table_name`,`Column_name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Column privileges'
+show create table procs_priv;
+Table Create Table
+procs_priv CREATE TABLE `procs_priv` (
+ `Host` char(60) collate utf8_bin NOT NULL default '',
+ `Db` char(64) collate utf8_bin NOT NULL default '',
+ `User` char(16) collate utf8_bin NOT NULL default '',
+ `Routine_name` char(64) collate utf8_bin NOT NULL default '',
+ `Grantor` char(77) collate utf8_bin NOT NULL default '',
+ `Timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
+ `Proc_priv` set('Execute','Alter Routine','Grant') collate utf8_bin NOT NULL default '',
+ PRIMARY KEY (`Host`,`Db`,`User`,`Routine_name`),
+ KEY `Grantor` (`Grantor`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Procedure privileges'
show tables;
Tables_in_test
diff --git a/mysql-test/t/grant2.test b/mysql-test/t/grant2.test
index 69c42ce2252..ad3fc1c228e 100644
--- a/mysql-test/t/grant2.test
+++ b/mysql-test/t/grant2.test
@@ -64,10 +64,10 @@ connection mrbad;
show grants for current_user();
use mysqltest;
insert into t1 values (1, 'I can''t change it!');
---error 1044
+--error 1142
update t1 set data='I can change it!' where id = 1;
# This should not be allowed since it too require UPDATE privilege.
---error 1044
+--error 1142
insert into t1 values (1, 'XXX') on duplicate key update data= 'I can change it!';
select * from t1;
@@ -199,7 +199,7 @@ create user mysqltest_2@localhost;
grant usage on *.* to mysqltest_2@localhost with grant option;
connect (user2,localhost,mysqltest_2,,);
connection user2;
---error 1044
+--error 1142
select host,user,password from mysql.user where user like 'mysqltest_%' order by host,user,password;
create user mysqltest_A@'%';
rename user mysqltest_A@'%' to mysqltest_B@'%';
diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test
index 8680da9b31a..566f9f625df 100644
--- a/mysql-test/t/show_check.test
+++ b/mysql-test/t/show_check.test
@@ -287,25 +287,25 @@ connect (con1,localhost,mysqltest_1,,mysqltest);
connection con1;
select * from t1;
show create database mysqltest;
---error 1044
+--error 1142
drop table t1;
--error 1044
drop database mysqltest;
connect (con2,localhost,mysqltest_2,,test);
connection con2;
---error 1044
+--error 1142
select * from mysqltest.t1;
--error 1044
show create database mysqltest;
---error 1044
+--error 1142
drop table mysqltest.t1;
--error 1044
drop database mysqltest;
connect (con3,localhost,mysqltest_3,,test);
connection con3;
---error 1044
+--error 1142
select * from mysqltest.t1;
--error 1044
show create database mysqltest;
diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test
index d1119499cf1..aad5f4eaf9e 100644
--- a/mysql-test/t/sp-security.test
+++ b/mysql-test/t/sp-security.test
@@ -40,6 +40,11 @@ call stamp(1);
select * from t1;
select db();
+grant execute on db1_secret.stamp to user1@'%';
+grant execute on db1_secret.db to user1@'%';
+grant execute on db1_secret.stamp to ''@'%';
+grant execute on db1_secret.db to ''@'%';
+
connect (con2user1,localhost,user1,,);
connect (con3anon,localhost,anon,,);
@@ -54,7 +59,7 @@ call db1_secret.stamp(2);
select db1_secret.db();
# ...but not this
---error 1044
+--error 1142
select * from db1_secret.t1;
# ...and not this
@@ -74,7 +79,7 @@ call db1_secret.stamp(3);
select db1_secret.db();
# ...but not this
---error 1044
+--error 1142
select * from db1_secret.t1;
# ...and not this
@@ -146,7 +151,8 @@ insert into t2 values (0);
grant usage on db2.* to user1@localhost;
grant select on db2.* to user1@localhost;
grant usage on db2.* to user2@localhost;
-grant select,insert,update,delete on db2.* to user2@localhost;
+grant select,insert,update,delete,create routine on db2.* to user2@localhost;
+grant create routine on db2.* to user1@localhost;
flush privileges;
connection con2user1;
@@ -155,7 +161,7 @@ use db2;
create procedure p () insert into t2 values (1);
# Check that this doesn't work.
---error 1044
+--error 1142
call p();
connect (con4user2,localhost,user2,,);
@@ -164,7 +170,7 @@ connection con4user2;
use db2;
# This should not work, since p is executed with definer's (user1's) rights.
---error 1044
+--error 1370
call p();
select * from t2;
@@ -173,6 +179,12 @@ create procedure q () insert into t2 values (2);
call q();
select * from t2;
+connection con1root;
+grant usage on db2.q to user2@localhost with grant option;
+
+connection con4user2;
+grant execute on db2.q to user1@localhost;
+
connection con2user1;
use db2;
@@ -206,6 +218,9 @@ drop procedure q;
# Clean up
#Still connection con1root;
+disconnect con2user1;
+disconnect con3anon;
+disconnect con4user2;
use test;
select type,db,name from mysql.proc;
drop database db1_secret;
@@ -214,3 +229,75 @@ drop database db2;
select type,db,name from mysql.proc;
# Get rid of the users
delete from mysql.user where user='user1' or user='user2';
+# And any routine privileges
+delete from mysql.procs_priv where user='user1' or user='user2';
+
+#
+# Test the new security acls
+#
+grant usage on *.* to usera@localhost;
+grant usage on *.* to userb@localhost;
+grant usage on *.* to userc@localhost;
+create database sptest;
+create table t1 ( u varchar(64), i int );
+create procedure sptest.p1(i int) insert into test.t1 values (user(), i);
+grant insert on t1 to usera@localhost;
+grant execute on sptest.p1 to usera@localhost;
+show grants for usera@localhost;
+grant execute on sptest.p1 to userc@localhost with grant option;
+show grants for userc@localhost;
+
+connect (con2usera,localhost,usera,,);
+connect (con3userb,localhost,userb,,);
+connect (con4userc,localhost,userc,,);
+
+connection con2usera;
+call sptest.p1(1);
+--error 1370
+grant execute on sptest.p1 to userb@localhost;
+--error 1370
+drop procedure sptest.p1;
+
+connection con3userb;
+--error 1370
+call sptest.p1(2);
+--error 1370
+grant execute on sptest.p1 to userb@localhost;
+--error 1370
+drop procedure sptest.p1;
+
+connection con4userc;
+call sptest.p1(3);
+grant execute on sptest.p1 to userb@localhost;
+--error 1370
+drop procedure sptest.p1;
+
+connection con3userb;
+call sptest.p1(4);
+--error 1370
+grant execute on sptest.p1 to userb@localhost;
+--error 1370
+drop procedure sptest.p1;
+
+connection con1root;
+select * from t1;
+
+grant all privileges on sptest.p1 to userc@localhost;
+show grants for userc@localhost;
+show grants for userb@localhost;
+
+connection con4userc;
+revoke all privileges on sptest.p1 from userb@localhost;
+
+connection con1root;
+show grants for userb@localhost;
+
+#cleanup
+disconnect con4userc;
+disconnect con3userb;
+disconnect con2usera;
+use test;
+drop database sptest;
+delete from mysql.user where user='usera' or user='userb' or user='userc';
+delete from mysql.procs_priv where user='usera' or user='userb' or user='userc';
+
diff --git a/mysql-test/t/system_mysql_db_fix.test b/mysql-test/t/system_mysql_db_fix.test
index e34dbefbcba..56f291ae69d 100644
--- a/mysql-test/t/system_mysql_db_fix.test
+++ b/mysql-test/t/system_mysql_db_fix.test
@@ -74,7 +74,7 @@ INSERT INTO user VALUES ('localhost','', '','N','N','N','N','N','N','N','N','
-- disable_query_log
-DROP TABLE db, host, user, func, tables_priv, columns_priv, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type;
+DROP TABLE db, host, user, func, tables_priv, columns_priv, procs_priv, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type;
-- enable_query_log
diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh
index 7a4da55f851..be99a081bcb 100644
--- a/scripts/mysql_create_system_tables.sh
+++ b/scripts/mysql_create_system_tables.sh
@@ -41,7 +41,7 @@ c_hk=""
i_ht=""
c_tzn="" c_tz="" c_tzt="" c_tztt="" c_tzls=""
i_tzn="" i_tz="" i_tzt="" i_tztt="" i_tzls=""
-c_p=""
+c_p="" c_pp=""
# Check for old tables
if test ! -f $mdata/db.frm
@@ -69,14 +69,17 @@ then
c_d="$c_d Lock_tables_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_d="$c_d Create_view_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_d="$c_d Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
+ c_d="$c_d Create_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
+ c_d="$c_d Alter_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
+ c_d="$c_d Execute_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_d="$c_d PRIMARY KEY Host (Host,Db,User),"
c_d="$c_d KEY User (User)"
c_d="$c_d ) engine=MyISAM"
c_d="$c_d CHARACTER SET utf8 COLLATE utf8_bin"
c_d="$c_d comment='Database privileges';"
- i_d="INSERT INTO db VALUES ('%','test','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y');
- INSERT INTO db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y');"
+ i_d="INSERT INTO db VALUES ('%','test','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N');
+ INSERT INTO db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N');"
fi
if test ! -f $mdata/host.frm
@@ -141,6 +144,8 @@ then
c_u="$c_u Repl_client_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_u="$c_u Create_view_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_u="$c_u Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
+ c_u="$c_u Create_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
+ c_u="$c_u Alter_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_u="$c_u ssl_type enum('','ANY','X509', 'SPECIFIED') DEFAULT '' NOT NULL,"
c_u="$c_u ssl_cipher BLOB NOT NULL,"
c_u="$c_u x509_issuer BLOB NOT NULL,"
@@ -155,24 +160,24 @@ then
if test "$1" = "test"
then
- i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
- INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
- REPLACE INTO user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
+ i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
+ INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
+ REPLACE INTO user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
INSERT INTO user (host,user) values ('localhost','');
INSERT INTO user (host,user) values ('$hostname','');"
else
- i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);"
+ i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);"
if test "$windows" = "0"
then
i_u="$i_u
- INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
+ INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
INSERT INTO user (host,user) values ('$hostname','');
INSERT INTO user (host,user) values ('localhost','');"
else
i_u="$i_u
- INSERT INTO user VALUES ('%','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
- INSERT INTO user VALUES ('localhost','','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
- INSERT INTO user VALUES ('%','','','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','','','','',0,0,0);"
+ INSERT INTO user VALUES ('%','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
+ INSERT INTO user VALUES ('localhost','','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
+ INSERT INTO user VALUES ('%','','','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','','','','',0,0,0);"
fi
fi
fi
@@ -236,6 +241,27 @@ then
c_c="$c_c comment='Column privileges';"
fi
+if test ! -f $mdata/procs_priv.frm
+then
+ if test "$1" = "verbose" ; then
+ echo "Preparing procs_priv table" 1>&2;
+ fi
+
+ c_pp="$c_pp CREATE TABLE procs_priv ("
+ c_pp="$c_pp Host char(60) binary DEFAULT '' NOT NULL,"
+ c_pp="$c_pp Db char(64) binary DEFAULT '' NOT NULL,"
+ c_pp="$c_pp User char(16) binary DEFAULT '' NOT NULL,"
+ c_pp="$c_pp Routine_name char(64) binary DEFAULT '' NOT NULL,"
+ c_pp="$c_pp Grantor char(77) DEFAULT '' NOT NULL,"
+ c_pp="$c_pp Timestamp timestamp(14),"
+ c_pp="$c_pp Proc_priv set('Execute','Alter Routine','Grant') DEFAULT '' NOT NULL,"
+ c_pp="$c_pp PRIMARY KEY (Host,Db,User,Routine_name),"
+ c_pp="$c_pp KEY Grantor (Grantor)"
+ c_pp="$c_pp ) engine=MyISAM"
+ c_pp="$c_pp CHARACTER SET utf8 COLLATE utf8_bin"
+ c_pp="$c_pp comment='Procedure privileges';"
+fi
+
if test ! -f $mdata/help_topic.frm
then
if test "$1" = "verbose" ; then
@@ -718,6 +744,7 @@ $c_tzls
$i_tzls
$c_p
+$c_pp
END_OF_DATA
diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql
index cae6a1d07b9..d4f095f5201 100644
--- a/scripts/mysql_fix_privilege_tables.sql
+++ b/scripts/mysql_fix_privilege_tables.sql
@@ -15,6 +15,7 @@ ALTER TABLE host type=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;
ALTER TABLE func type=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;
ALTER TABLE columns_priv type=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;
ALTER TABLE tables_priv type=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;
+ALTER TABLE procs_priv type=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;
ALTER TABLE user change Password Password char(41) binary not null default '';
ALTER TABLE user add File_priv enum('N','Y') NOT NULL;
CREATE TABLE IF NOT EXISTS func (
@@ -171,8 +172,46 @@ ALTER TABLE user ADD Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Cre
UPDATE user SET Create_view_priv=Create_priv, Show_view_priv=Create_priv where user<>"" AND @hadCreateViewPriv = 0;
#
+#
+#
+SET @hadCreateRoutinePriv:=0;
+SELECT @hadCreateRoutinePriv:=1 FROM user WHERE Create_routine_priv LIKE '%';
+
+#
+# Create PROCEDUREs privileges (v5.0)
+#
+ALTER TABLE db ADD Create_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Show_view_priv;
+ALTER TABLE user ADD Create_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Show_view_priv;
+
+#
+# Alter PROCEDUREs privileges (v5.0)
+#
+ALTER TABLE db ADD Alter_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Create_routine_priv;
+ALTER TABLE user ADD Alter_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Create_routine_priv;
+
+ALTER TABLE db ADD Execute_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Alter_routine_priv;
+
+#
+# Assign create/alter routine privileges to people who have create privileges
+#
+UPDATE user SET Create_routine_priv=Create_priv, Alter_routine_priv=Alter_priv where user<>"" AND @hadCreateRoutinePriv = 0;
+UPDATE db SET Create_routine_priv=Create_priv, Alter_routine_priv=Alter_priv, Execute_priv=Select_priv where user<>"" AND @hadCreateRoutinePriv = 0;
+
+#
# Create some possible missing tables
#
+CREATE TABLE IF NOT EXISTS procs_priv (
+Host char(60) binary DEFAULT '' NOT NULL,
+Db char(64) binary DEFAULT '' NOT NULL,
+User char(16) binary DEFAULT '' NOT NULL,
+Routine_name char(64) binary DEFAULT '' NOT NULL,
+Grantor char(77) DEFAULT '' NOT NULL,
+Timestamp timestamp(14),
+Proc_priv set('Execute','Alter Routine','Grant') DEFAULT '' NOT NULL,
+PRIMARY KEY (Host,Db,User,Routine_name),
+KEY Grantor (Grantor)
+) CHARACTER SET utf8 COLLATE utf8_bin comment='Procedure privileges';
+
CREATE TABLE IF NOT EXISTS help_topic (
help_topic_id int unsigned not null,
name varchar(64) not null,
diff --git a/scripts/mysql_install_db.sh b/scripts/mysql_install_db.sh
index b4f59790e73..3be1320f0b0 100644
--- a/scripts/mysql_install_db.sh
+++ b/scripts/mysql_install_db.sh
@@ -3,7 +3,7 @@
# For a more info consult the file COPYRIGHT distributed with this file.
# This scripts creates the privilege tables db, host, user, tables_priv,
-# columns_priv in the mysql database, as well as the func table.
+# columns_priv, procs_priv in the mysql database, as well as the func table.
#
# All unrecognized arguments to this script are passed to mysqld.
diff --git a/sql/item_func.cc b/sql/item_func.cc
index aba53b9b397..35cced090e6 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -22,6 +22,7 @@
#endif
#include "mysql_priv.h"
+#include "sql_acl.h"
#include "slave.h" // for wait_for_master_pos
#include <m_ctype.h>
#include <hash.h>
@@ -3546,7 +3547,17 @@ Item_func_sp::execute(Item **itp)
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (check_procedure_access(thd, EXECUTE_ACL,
+ m_sp->m_db.str, m_sp->m_name.str, 0))
+ DBUG_RETURN(-1);
sp_change_security_context(thd, m_sp, &save_ctx);
+ if (save_ctx.changed &&
+ check_procedure_access(thd, EXECUTE_ACL,
+ m_sp->m_db.str, m_sp->m_name.str, 0))
+ {
+ sp_restore_security_context(thd, m_sp, &save_ctx);
+ DBUG_RETURN(-1);
+ }
#endif
/*
diff --git a/sql/lex.h b/sql/lex.h
index cf0059a1397..5b6c86bf0ed 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -399,6 +399,7 @@ static SYMBOL symbols[] = {
{ "RLIKE", SYM(REGEXP)}, /* Like in mSQL2 */
{ "ROLLBACK", SYM(ROLLBACK_SYM)},
{ "ROLLUP", SYM(ROLLUP_SYM)},
+ { "ROUTINE", SYM(ROUTINE_SYM)},
{ "ROW", SYM(ROW_SYM)},
{ "ROWS", SYM(ROWS_SYM)},
{ "ROW_FORMAT", SYM(ROW_FORMAT_SYM)},
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 2fc82e05f31..fc06893c281 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -420,6 +420,8 @@ void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0,
TABLE *stopper= 0);
bool check_one_table_access(THD *thd, ulong privilege,
TABLE_LIST *tables);
+bool check_procedure_access(THD *thd,ulong want_access,char *db,char *name,
+ bool no_errors);
bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table);
bool check_merge_table_access(THD *thd, char *db,
TABLE_LIST *table_list);
@@ -1024,6 +1026,7 @@ extern my_bool opt_slave_compressed_protocol, use_temp_pool;
extern my_bool opt_readonly, lower_case_file_system;
extern my_bool opt_enable_named_pipe, opt_sync_frm;
extern my_bool opt_secure_auth;
+extern my_bool sp_automatic_privileges;
extern uint opt_crash_binlog_innodb;
extern char *shared_memory_base_name, *mysqld_unix_port;
extern bool opt_enable_shared_memory;
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index c5698469341..8eec97efd81 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -299,6 +299,7 @@ my_bool opt_innodb_safe_binlog= 0;
my_bool opt_large_pages= 0;
uint opt_large_page_size= 0;
volatile bool mqh_used = 0;
+my_bool sp_automatic_privileges= 1;
uint mysqld_port, test_flags, select_errors, dropping_tables, ha_open_options;
uint delay_key_write_options, protocol_version;
@@ -4199,6 +4200,7 @@ enum options_mysqld
OPT_OPTIMIZER_SEARCH_DEPTH,
OPT_OPTIMIZER_PRUNE_LEVEL,
OPT_UPDATABLE_VIEWS_WITH_LIMIT,
+ OPT_SP_AUTOMATIC_PRIVILEGES,
OPT_AUTO_INCREMENT, OPT_AUTO_INCREMENT_OFFSET,
OPT_ENABLE_LARGE_PAGES
};
@@ -4229,6 +4231,10 @@ struct my_option my_long_options[] =
(gptr*) &global_system_variables.auto_increment_offset,
(gptr*) &max_system_variables.auto_increment_offset, 0, GET_ULONG, OPT_ARG,
1, 1, 65535, 0, 1, 0 },
+ {"automatic-sp-privileges", OPT_SP_AUTOMATIC_PRIVILEGES,
+ "Creating and dropping stored procedures alters ACLs. Disable with --skip-automatic-sp-privileges.",
+ (gptr*) &sp_automatic_privileges, (gptr*) &sp_automatic_privileges,
+ 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
{"basedir", 'b',
"Path to installation directory. All paths are usually resolved relative to this.",
(gptr*) &mysql_home_ptr, (gptr*) &mysql_home_ptr, 0, GET_STR, REQUIRED_ARG,
@@ -6128,6 +6134,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE;
myisam_concurrent_insert=0;
myisam_recover_options= HA_RECOVER_NONE;
+ sp_automatic_privileges=0;
my_use_symdir=0;
ha_open_options&= ~(HA_OPEN_ABORT_IF_CRASHED | HA_OPEN_DELAY_KEY_WRITE);
#ifdef HAVE_QUERY_CACHE
diff --git a/sql/set_var.cc b/sql/set_var.cc
index da6341597f1..b42ca91c7dd 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -133,6 +133,9 @@ sys_var_thd_ulong sys_auto_increment_increment("auto_increment_increment",
sys_var_thd_ulong sys_auto_increment_offset("auto_increment_offset",
&SV::auto_increment_offset);
+sys_var_bool_ptr sys_automatic_sp_privileges("automatic_sp_privileges",
+ &sp_automatic_privileges);
+
sys_var_long_ptr sys_binlog_cache_size("binlog_cache_size",
&binlog_cache_size);
sys_var_thd_ulong sys_bulk_insert_buff_size("bulk_insert_buffer_size",
@@ -509,6 +512,7 @@ sys_var *sys_variables[]=
&sys_auto_increment_increment,
&sys_auto_increment_offset,
&sys_autocommit,
+ &sys_automatic_sp_privileges,
&sys_big_tables,
&sys_big_selects,
&sys_binlog_cache_size,
@@ -668,6 +672,7 @@ sys_var *sys_variables[]=
struct show_var_st init_vars[]= {
{"auto_increment_increment", (char*) &sys_auto_increment_increment, SHOW_SYS},
{"auto_increment_offset", (char*) &sys_auto_increment_offset, SHOW_SYS},
+ {sys_automatic_sp_privileges.name,(char*) &sys_automatic_sp_privileges, SHOW_SYS},
{"back_log", (char*) &back_log, SHOW_LONG},
{"basedir", mysql_home, SHOW_CHAR},
#ifdef HAVE_BERKELEY_DB
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index c93e203f0d5..5b48f27d2e3 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -5168,8 +5168,8 @@ ER_VIEW_CHECK_FAILED
eng "CHECK OPTION failed '%-.64s.%-.64s'"
rus "ÐÒÏ×ÅÒËÁ CHECK OPTION ÄÌÑ VIEW '%-.64s.%-.64s' ÐÒÏ×ÁÌÉÌÁÓØ"
ukr "ðÅÒÅצÒËÁ CHECK OPTION ÄÌÑ VIEW '%-.64s.%-.64s' ÎÅ ÐÒÏÊÛÌÁ"
-ER_SP_ACCESS_DENIED_ERROR 42000
- eng "Access denied; you are not the procedure/function definer of '%s'"
+ER_PROCACCESS_DENIED_ERROR 42000
+ eng "%-.16s command denied to user '%-.32s'@'%-.64s' for routine '%-.64s'"
ER_RELAY_LOG_FAIL
eng "Failed purging old relay logs: %s"
ER_PASSWD_LENGTH
@@ -5232,3 +5232,9 @@ ER_CANNOT_USER
eng "Operation %s failed for %.256s"
ger "Das Kommando %s scheiterte für %.256s"
norwegian-ny "Operation %s failed for '%.256s'"
+ER_NONEXISTING_PROC_GRANT 42000
+ eng "There is no such grant defined for user '%-.32s' on host '%-.64s' on routine '%-.64s'"
+ER_PROC_AUTO_GRANT_FAIL
+ eng "Failed to grant EXECUTE and ALTER ROUTINE privileges"
+ER_PROC_AUTO_REVOKE_FAIL
+ eng "Failed to revoke all privileges to dropped routine"
diff --git a/sql/sp.cc b/sql/sp.cc
index 4605d49f3ab..88693de0497 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -739,6 +739,45 @@ sp_find_procedure(THD *thd, sp_name *name)
int
+sp_exists_routine(THD *thd, TABLE_LIST *tables, bool any, bool no_error)
+{
+ TABLE_LIST *table;
+ bool result= 0;
+ DBUG_ENTER("sp_exists_routine");
+ for (table= tables; table; table= table->next_global)
+ {
+ sp_name *name;
+ LEX_STRING lex_db;
+ LEX_STRING lex_name;
+ lex_db.length= strlen(table->db);
+ lex_name.length= strlen(table->real_name);
+ lex_db.str= thd->strmake(table->db, lex_db.length);
+ lex_name.str= thd->strmake(table->real_name, lex_name.length);
+ name= new sp_name(lex_db, lex_name);
+ name->init_qname(thd);
+ if (sp_find_procedure(thd, name) != NULL ||
+ sp_find_function(thd, name) != NULL)
+ {
+ if (any)
+ DBUG_RETURN(1);
+ result= 1;
+ }
+ else if (!any)
+ {
+ if (!no_error)
+ {
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION or PROCEDURE",
+ table->real_name);
+ DBUG_RETURN(-1);
+ }
+ DBUG_RETURN(0);
+ }
+ }
+ DBUG_RETURN(result);
+}
+
+
+int
sp_create_procedure(THD *thd, sp_head *sp)
{
int ret;
diff --git a/sql/sp.h b/sql/sp.h
index acdfe824b97..152c59d0d02 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -37,6 +37,9 @@ sp_head *
sp_find_procedure(THD *thd, sp_name *name);
int
+sp_exists_routine(THD *thd, TABLE_LIST *procs, bool any, bool no_error);
+
+int
sp_create_procedure(THD *thd, sp_head *sp);
int
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index d33faffb2c2..4dcd15741cb 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -33,6 +33,8 @@
#endif
#include <m_ctype.h>
#include <stdarg.h>
+#include "sp_head.h"
+#include "sp.h"
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -59,7 +61,7 @@ static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
static MEM_ROOT mem, memex;
static bool initialized=0;
static bool allow_all_hosts=1;
-static HASH acl_check_hosts, column_priv_hash;
+static HASH acl_check_hosts, column_priv_hash, proc_priv_hash;
static DYNAMIC_ARRAY acl_wild_hosts;
static hash_filo *acl_cache;
static uint grant_version=0;
@@ -307,6 +309,16 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
*/
if (table->fields <= 31 && (user.access & CREATE_ACL))
user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
+
+ /*
+ if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
+ CREATE PROCEDURE & ALTER PROCEDURE privileges
+ */
+ if (table->fields <= 33 && (user.access & CREATE_ACL))
+ user.access|= CREATE_PROC_ACL;
+ if (table->fields <= 33 && (user.access & ALTER_ACL))
+ user.access|= ALTER_PROC_ACL;
+
user.sort= get_sort(2,user.host.hostname,user.user);
user.hostname_length= (user.host.hostname ?
(uint) strlen(user.host.hostname) : 0);
@@ -1859,13 +1871,25 @@ static byte* get_key_column(GRANT_COLUMN *buff,uint *length,
}
-class GRANT_TABLE :public Sql_alloc
+class GRANT_NAME :public Sql_alloc
{
public:
char *host,*db, *user, *tname, *hash_key, *orig_host;
- ulong privs, cols;
+ ulong privs;
ulong sort;
uint key_length;
+ GRANT_NAME(const char *h, const char *d,const char *u,
+ const char *t, ulong p);
+ GRANT_NAME (TABLE *form);
+ virtual ~GRANT_NAME() {};
+ virtual bool ok() { return privs != 0; }
+};
+
+
+class GRANT_TABLE :public GRANT_NAME
+{
+public:
+ ulong cols;
HASH hash_columns;
GRANT_TABLE(const char *h, const char *d,const char *u,
@@ -1877,9 +1901,9 @@ public:
-GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
- const char *t, ulong p, ulong c)
- :privs(p), cols(c)
+GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
+ const char *t, ulong p)
+ :privs(p)
{
/* Host given by user */
orig_host= strdup_root(&memex,h);
@@ -1897,15 +1921,20 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
key_length =(uint) strlen(d)+(uint) strlen(u)+(uint) strlen(t)+3;
hash_key = (char*) alloc_root(&memex,key_length);
strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
+}
+
+
+GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
+ const char *t, ulong p, ulong c)
+ :GRANT_NAME(h,d,u,t,p), cols(c)
+{
(void) hash_init(&hash_columns,system_charset_info,
0,0,0, (hash_get_key) get_key_column,0,0);
}
-GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
+GRANT_NAME::GRANT_NAME(TABLE *form)
{
- byte key[MAX_KEY_LENGTH];
-
orig_host= host= get_field(&memex, form->field[0]);
db= get_field(&memex,form->field[1]);
user= get_field(&memex,form->field[2]);
@@ -1921,8 +1950,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
if (!db || !tname)
{
/* Wrong table row; Ignore it */
- hash_clear(&hash_columns); /* allow for destruction */
- privs= cols= 0; /* purecov: inspected */
+ privs= 0;
return; /* purecov: inspected */
}
if (lower_case_table_names)
@@ -1935,8 +1963,23 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
hash_key = (char*) alloc_root(&memex,key_length);
strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
privs = (ulong) form->field[6]->val_int();
- cols = (ulong) form->field[7]->val_int();
privs = fix_rights_for_table(privs);
+}
+
+
+GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
+ :GRANT_NAME(form)
+{
+ byte key[MAX_KEY_LENGTH];
+
+ if (!db || !tname)
+ {
+ /* Wrong table row; Ignore it */
+ hash_clear(&hash_columns); /* allow for destruction */
+ cols= 0;
+ return;
+ }
+ cols= (ulong) form->field[7]->val_int();
cols = fix_rights_for_column(cols);
(void) hash_init(&hash_columns,system_charset_info,
@@ -1995,7 +2038,7 @@ GRANT_TABLE::~GRANT_TABLE()
}
-static byte* get_grant_table(GRANT_TABLE *buff,uint *length,
+static byte* get_grant_table(GRANT_NAME *buff,uint *length,
my_bool not_used __attribute__((unused)))
{
*length=buff->key_length;
@@ -2011,44 +2054,62 @@ void free_grant_table(GRANT_TABLE *grant_table)
/* Search after a matching grant. Prefer exact grants before not exact ones */
-static GRANT_TABLE *table_hash_search(const char *host,const char* ip,
+static GRANT_NAME *name_hash_search(HASH *name_hash,
+ const char *host,const char* ip,
const char *db,
const char *user, const char *tname,
bool exact)
{
char helping [NAME_LEN*2+USERNAME_LENGTH+3];
uint len;
- GRANT_TABLE *grant_table,*found=0;
+ GRANT_NAME *grant_name,*found=0;
len = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1;
- for (grant_table=(GRANT_TABLE*) hash_search(&column_priv_hash,
+ for (grant_name=(GRANT_NAME*) hash_search(name_hash,
(byte*) helping,
len) ;
- grant_table ;
- grant_table= (GRANT_TABLE*) hash_next(&column_priv_hash,(byte*) helping,
+ grant_name ;
+ grant_name= (GRANT_NAME*) hash_next(name_hash,(byte*) helping,
len))
{
if (exact)
{
if ((host &&
- !my_strcasecmp(system_charset_info, host, grant_table->host)) ||
- (ip && !strcmp(ip,grant_table->host)))
- return grant_table;
+ !my_strcasecmp(system_charset_info, host, grant_name->host)) ||
+ (ip && !strcmp(ip,grant_name->host)))
+ return grant_name;
}
else
{
if (((host && !wild_case_compare(system_charset_info,
- host,grant_table->host)) ||
+ host,grant_name->host)) ||
(ip && !wild_case_compare(system_charset_info,
- ip,grant_table->host))) &&
- (!found || found->sort < grant_table->sort))
- found=grant_table; // Host ok
+ ip,grant_name->host))) &&
+ (!found || found->sort < grant_name->sort))
+ found=grant_name; // Host ok
}
}
return found;
}
+inline GRANT_NAME *
+proc_hash_search(const char *host, const char *ip, const char *db,
+ const char *user, const char *tname, bool exact)
+{
+ return (GRANT_TABLE*) name_hash_search(&proc_priv_hash, host, ip, db,
+ user, tname, exact);
+}
+
+
+inline GRANT_TABLE *
+table_hash_search(const char *host, const char *ip, const char *db,
+ const char *user, const char *tname, bool exact)
+{
+ return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db,
+ user, tname, exact);
+}
+
inline GRANT_COLUMN *
column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
@@ -2358,6 +2419,117 @@ table_error:
}
+static int replace_proc_table(THD *thd, GRANT_NAME *grant_name,
+ TABLE *table, const LEX_USER &combo,
+ const char *db, const char *proc_name,
+ ulong rights, bool revoke_grant)
+{
+ char grantor[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
+ int old_row_exists= 1;
+ int error=0;
+ ulong store_proc_rights;
+ DBUG_ENTER("replace_proc_table");
+
+ if (!initialized)
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
+ DBUG_RETURN(-1);
+ }
+
+ strxmov(grantor, thd->user, "@", thd->host_or_ip, NullS);
+
+ /*
+ The following should always succeed as new users are created before
+ this function is called!
+ */
+ if (!find_acl_user(combo.host.str,combo.user.str))
+ {
+ my_error(ER_PASSWORD_NO_MATCH,MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ restore_record(table,default_values); // Get empty record
+ table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
+ table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
+ table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
+ table->field[3]->store(proc_name,(uint) strlen(proc_name), &my_charset_latin1);
+ store_record(table,record[1]); // store at pos 1
+
+ if (table->file->index_read_idx(table->record[0],0,
+ (byte*) table->field[0]->ptr,0,
+ HA_READ_KEY_EXACT))
+ {
+ /*
+ The following should never happen as we first check the in memory
+ grant tables for the user. There is however always a small change that
+ the user has modified the grant tables directly.
+ */
+ if (revoke_grant)
+ { // no row, no revoke
+ my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
+ combo.user.str, combo.host.str, proc_name);
+ DBUG_RETURN(-1);
+ }
+ old_row_exists= 0;
+ restore_record(table,record[1]); // Get saved record
+ }
+
+ store_proc_rights= get_rights_for_procedure(rights);
+ if (old_row_exists)
+ {
+ ulong j;
+ store_record(table,record[1]);
+ j= (ulong) table->field[6]->val_int();
+
+ if (revoke_grant)
+ {
+ /* column rights are already fixed in mysql_table_grant */
+ store_proc_rights=j & ~store_proc_rights;
+ }
+ else
+ {
+ store_proc_rights|= j;
+ }
+ }
+
+ table->field[4]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
+ table->field[6]->store((longlong) store_proc_rights);
+ rights=fix_rights_for_procedure(store_proc_rights);
+
+ if (old_row_exists)
+ {
+ if (store_proc_rights)
+ {
+ if ((error=table->file->update_row(table->record[1],table->record[0])))
+ goto table_error;
+ }
+ else if ((error= table->file->delete_row(table->record[1])))
+ goto table_error;
+ }
+ else
+ {
+ error=table->file->write_row(table->record[0]);
+ if (error && error != HA_ERR_FOUND_DUPP_KEY)
+ goto table_error;
+ }
+
+ if (rights)
+ {
+ grant_name->privs= rights;
+ }
+ else
+ {
+ hash_delete(&proc_priv_hash,(byte*) grant_name);
+ }
+ DBUG_RETURN(0);
+
+ /* This should never happen */
+table_error:
+ table->file->print_error(error,MYF(0));
+ DBUG_RETURN(-1);
+}
+
+
/*
Store table level and column level grants in the privilege tables
@@ -2601,6 +2773,161 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
}
+/*
+ Store procedure level grants in the privilege tables
+
+ SYNOPSIS
+ mysql_procedure_grant()
+ thd Thread handle
+ table_list List of procedures to give grant
+ user_list List of users to give grant
+ rights Table level grant
+ revoke_grant Set to 1 if this is a REVOKE command
+
+ RETURN
+ 0 ok
+ 1 error
+*/
+
+bool mysql_procedure_grant(THD *thd, TABLE_LIST *table_list,
+ List <LEX_USER> &user_list, ulong rights,
+ bool revoke_grant, bool no_error)
+{
+ List_iterator <LEX_USER> str_list (user_list);
+ LEX_USER *Str;
+ TABLE_LIST tables[2];
+ bool create_new_users=0, result=0;
+ char *db_name, *real_name;
+ DBUG_ENTER("mysql_procedure_grant");
+
+ if (!initialized)
+ {
+ if (!no_error)
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
+ "--skip-grant-tables");
+ DBUG_RETURN(TRUE);
+ }
+ if (rights & ~PROC_ACLS)
+ {
+ if (!no_error)
+ my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
+ MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ if (!revoke_grant)
+ {
+ if (sp_exists_routine(thd, table_list, 0, no_error)<0)
+ DBUG_RETURN(TRUE);
+ }
+
+ /* open the mysql.user and mysql.procs_priv tables */
+
+ bzero((char*) &tables,sizeof(tables));
+ tables[0].alias=tables[0].real_name= (char*) "user";
+ tables[1].alias=tables[1].real_name= (char*) "procs_priv";
+ tables[0].next_local= tables[0].next_global= tables+1;
+ tables[0].lock_type=tables[1].lock_type=TL_WRITE;
+ tables[0].db=tables[1].db=(char*) "mysql";
+
+#ifdef HAVE_REPLICATION
+ /*
+ GRANT and REVOKE are applied the slave in/exclusion rules as they are
+ some kind of updates to the mysql.% tables.
+ */
+ if (thd->slave_thread && table_rules_on)
+ {
+ /*
+ The tables must be marked "updating" so that tables_ok() takes them into
+ account in tests.
+ */
+ tables[0].updating= tables[1].updating= 1;
+ if (!tables_ok(0, tables))
+ DBUG_RETURN(FALSE);
+ }
+#endif
+
+ if (simple_open_n_lock_tables(thd,tables))
+ { // Should never happen
+ close_thread_tables(thd);
+ DBUG_RETURN(TRUE);
+ }
+
+ if (!revoke_grant)
+ create_new_users= test_if_create_new_users(thd);
+ rw_wrlock(&LOCK_grant);
+ MEM_ROOT *old_root= thd->mem_root;
+ thd->mem_root= &memex;
+
+ DBUG_PRINT("info",("now time to iterate and add users"));
+
+ while ((Str= str_list++))
+ {
+ int error;
+ GRANT_NAME *grant_name;
+ if (Str->host.length > HOSTNAME_LENGTH ||
+ Str->user.length > USERNAME_LENGTH)
+ {
+ if (!no_error)
+ my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
+ MYF(0));
+ result= TRUE;
+ continue;
+ }
+ /* Create user if needed */
+ pthread_mutex_lock(&acl_cache->lock);
+ error=replace_user_table(thd, tables[0].table, *Str,
+ 0, revoke_grant, create_new_users);
+ pthread_mutex_unlock(&acl_cache->lock);
+ if (error)
+ {
+ result= TRUE; // Remember error
+ continue; // Add next user
+ }
+
+ db_name= table_list->db;
+ real_name= table_list->real_name;
+
+ grant_name= proc_hash_search(Str->host.str, NullS, db_name,
+ Str->user.str, real_name, 1);
+ if (!grant_name)
+ {
+ if (revoke_grant)
+ {
+ if (!no_error)
+ my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
+ Str->user.str, Str->host.str, real_name);
+ result= TRUE;
+ continue;
+ }
+ grant_name= new GRANT_NAME(Str->host.str, db_name,
+ Str->user.str, real_name,
+ rights);
+ if (!grant_name)
+ {
+ result= TRUE;
+ continue;
+ }
+ my_hash_insert(&proc_priv_hash,(byte*) grant_name);
+ }
+
+ if (replace_proc_table(thd, grant_name, tables[1].table, *Str,
+ db_name, real_name, rights, revoke_grant))
+ {
+ result= TRUE;
+ continue;
+ }
+ }
+ grant_option=TRUE;
+ thd->mem_root= old_root;
+ rw_unlock(&LOCK_grant);
+ if (!result && !no_error)
+ send_ok(thd);
+ /* Tables are automatically closed */
+ DBUG_RETURN(result);
+}
+
+
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
ulong rights, bool revoke_grant)
{
@@ -2713,6 +3040,7 @@ void grant_free(void)
DBUG_ENTER("grant_free");
grant_option = FALSE;
hash_free(&column_priv_hash);
+ hash_free(&proc_priv_hash);
free_root(&memex,MYF(0));
DBUG_VOID_RETURN;
}
@@ -2723,11 +3051,11 @@ void grant_free(void)
my_bool grant_init(THD *org_thd)
{
THD *thd;
- TABLE_LIST tables[2];
+ TABLE_LIST tables[3];
MYSQL_LOCK *lock;
MEM_ROOT *memex_ptr;
my_bool return_val= 1;
- TABLE *t_table, *c_table;
+ TABLE *t_table, *c_table, *p_table;
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
DBUG_ENTER("grant_init");
@@ -2735,6 +3063,9 @@ my_bool grant_init(THD *org_thd)
(void) hash_init(&column_priv_hash,system_charset_info,
0,0,0, (hash_get_key) get_grant_table,
(hash_free_key) free_grant_table,0);
+ (void) hash_init(&proc_priv_hash,system_charset_info,
+ 0,0,0, (hash_get_key) get_grant_table,
+ 0,0);
init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
/* Don't do anything if running with --skip-grant */
@@ -2749,69 +3080,110 @@ my_bool grant_init(THD *org_thd)
bzero((char*) &tables, sizeof(tables));
tables[0].alias=tables[0].real_name= (char*) "tables_priv";
tables[1].alias=tables[1].real_name= (char*) "columns_priv";
+ tables[2].alias=tables[2].real_name= (char*) "procs_priv";
tables[0].next_local= tables[0].next_global= tables+1;
- tables[0].lock_type=tables[1].lock_type=TL_READ;
- tables[0].db=tables[1].db=thd->db;
+ tables[1].next_local= tables[1].next_global= tables+2;
+ tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ;
+ tables[0].db=tables[1].db=tables[2].db=thd->db;
uint counter;
if (open_tables(thd, tables, &counter))
goto end;
- TABLE *ptr[2]; // Lock tables for quick update
+ TABLE *ptr[3]; // Lock tables for quick update
ptr[0]= tables[0].table;
ptr[1]= tables[1].table;
- if (!(lock=mysql_lock_tables(thd,ptr,2)))
+ ptr[2]= tables[2].table;
+ if (!(lock=mysql_lock_tables(thd,ptr,3)))
goto end;
t_table = tables[0].table; c_table = tables[1].table;
+ p_table= tables[2].table;
t_table->file->ha_index_init(0);
- if (t_table->file->index_first(t_table->record[0]))
+ p_table->file->ha_index_init(0);
+ if (!t_table->file->index_first(t_table->record[0]))
{
- return_val= 0;
- goto end_unlock;
- }
- grant_option= TRUE;
+ /* Will be restored by org_thd->store_globals() */
+ memex_ptr= &memex;
+ my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
+ do
+ {
+ GRANT_TABLE *mem_check;
+ if (!(mem_check=new GRANT_TABLE(t_table,c_table)))
+ {
+ /* This could only happen if we are out memory */
+ grant_option= FALSE;
+ goto end_unlock;
+ }
+
+ if (check_no_resolve)
+ {
+ if (hostname_requires_resolving(mem_check->host))
+ {
+ sql_print_warning("'tables_priv' entry '%s %s@%s' "
+ "ignored in --skip-name-resolve mode.",
+ mem_check->tname, mem_check->user,
+ mem_check->host, mem_check->host);
+ continue;
+ }
+ }
- /* Will be restored by org_thd->store_globals() */
- memex_ptr= &memex;
- my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
- do
+ if (! mem_check->ok())
+ delete mem_check;
+ else if (my_hash_insert(&column_priv_hash,(byte*) mem_check))
+ {
+ delete mem_check;
+ grant_option= FALSE;
+ goto end_unlock;
+ }
+ }
+ while (!t_table->file->index_next(t_table->record[0]));
+ }
+ if (!p_table->file->index_first(p_table->record[0]))
{
- GRANT_TABLE *mem_check;
- if (!(mem_check=new GRANT_TABLE(t_table,c_table)))
+ /* Will be restored by org_thd->store_globals() */
+ memex_ptr= &memex;
+ my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
+ do
{
- /* This could only happen if we are out memory */
- grant_option= FALSE; /* purecov: deadcode */
- goto end_unlock;
- }
+ GRANT_NAME *mem_check;
+ if (!(mem_check=new GRANT_NAME(p_table)))
+ {
+ /* This could only happen if we are out memory */
+ grant_option= FALSE;
+ goto end_unlock;
+ }
- if (check_no_resolve)
- {
- if (hostname_requires_resolving(mem_check->host))
+ if (check_no_resolve)
{
- sql_print_warning("'tables_priv' entry '%s %s@%s' "
- "ignored in --skip-name-resolve mode.",
- mem_check->tname, mem_check->user,
- mem_check->host, mem_check->host);
- continue;
+ if (hostname_requires_resolving(mem_check->host))
+ {
+ sql_print_warning("'procs_priv' entry '%s %s@%s' "
+ "ignored in --skip-name-resolve mode.",
+ mem_check->tname, mem_check->user,
+ mem_check->host, mem_check->host);
+ continue;
+ }
}
- }
- if (! mem_check->ok())
- delete mem_check;
- else if (my_hash_insert(&column_priv_hash,(byte*) mem_check))
- {
- delete mem_check;
- grant_option= FALSE;
- goto end_unlock;
+ mem_check->privs= fix_rights_for_procedure(mem_check->privs);
+ if (! mem_check->ok())
+ delete mem_check;
+ else if (my_hash_insert(&proc_priv_hash,(byte*) mem_check))
+ {
+ delete mem_check;
+ grant_option= FALSE;
+ goto end_unlock;
+ }
}
+ while (!p_table->file->index_next(p_table->record[0]));
}
- while (!t_table->file->index_next(t_table->record[0]));
-
+ grant_option= TRUE;
return_val=0; // Return ok
end_unlock:
t_table->file->ha_index_end();
+ p_table->file->ha_index_end();
mysql_unlock_tables(thd, lock);
thd->version--; // Force close to free memory
@@ -2842,7 +3214,7 @@ end:
void grant_reload(THD *thd)
{
- HASH old_column_priv_hash;
+ HASH old_column_priv_hash, old_proc_priv_hash;
bool old_grant_option;
MEM_ROOT old_mem;
DBUG_ENTER("grant_reload");
@@ -2850,6 +3222,7 @@ void grant_reload(THD *thd)
rw_wrlock(&LOCK_grant);
grant_version++;
old_column_priv_hash= column_priv_hash;
+ old_proc_priv_hash= proc_priv_hash;
old_grant_option= grant_option;
old_mem= memex;
@@ -2858,12 +3231,14 @@ void grant_reload(THD *thd)
DBUG_PRINT("error",("Reverting to old privileges"));
grant_free(); /* purecov: deadcode */
column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
+ proc_priv_hash= old_proc_priv_hash;
grant_option= old_grant_option; /* purecov: deadcode */
memex= old_mem; /* purecov: deadcode */
}
else
{
hash_free(&old_column_priv_hash);
+ hash_free(&old_proc_priv_hash);
free_root(&old_mem,MYF(0));
}
rw_unlock(&LOCK_grant);
@@ -3099,7 +3474,7 @@ err2:
/*
Check if a user has the right to access a database
- Access is accepted if he has a grant for any table in the database
+ Access is accepted if he has a grant for any table/routine in the database
Return 1 if access is denied
*/
@@ -3131,6 +3506,72 @@ bool check_grant_db(THD *thd,const char *db)
return error;
}
+
+/****************************************************************************
+ Check procedure level grants
+
+ SYNPOSIS
+ bool check_grant_procedure()
+ thd Thread handler
+ want_access Bits of privileges user needs to have
+ procs List of procedures to check. The user should have 'want_access'
+ no_errors If 0 then we write an error. The error is sent directly to
+ the client
+
+ RETURN
+ 0 ok
+ 1 Error: User did not have the requested privielges
+****************************************************************************/
+
+bool check_grant_procedure(THD *thd, ulong want_access,
+ TABLE_LIST *procs, bool no_errors)
+{
+ TABLE_LIST *table;
+ char *user= thd->priv_user;
+ char *host= thd->priv_host;
+ DBUG_ENTER("check_grant_procedure");
+
+ want_access&= ~thd->master_access;
+ if (!want_access)
+ DBUG_RETURN(0); // ok
+
+ rw_rdlock(&LOCK_grant);
+ for (table= procs; table; table= table->next_global)
+ {
+ GRANT_NAME *grant_proc;
+ if ((grant_proc= proc_hash_search(host,thd->ip,
+ table->db, user, table->real_name, 0)))
+ table->grant.privilege|= grant_proc->privs;
+
+ if (want_access & ~table->grant.privilege)
+ {
+ want_access &= ~table->grant.privilege;
+ goto err;
+ }
+ }
+ rw_unlock(&LOCK_grant);
+ DBUG_RETURN(0);
+err:
+ rw_unlock(&LOCK_grant);
+ if (!no_errors)
+ {
+ char buff[1024];
+ const char *command="";
+ if (table)
+ strxmov(buff, table->db, ".", table->real_name, NullS);
+ if (want_access & EXECUTE_ACL)
+ command= "execute";
+ else if (want_access & ALTER_PROC_ACL)
+ command= "alter procedure";
+ else if (want_access & GRANT_ACL)
+ command= "grant";
+ my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0),
+ command, user, host, table ? buff : "unknown");
+ }
+ DBUG_RETURN(1);
+}
+
+
/*****************************************************************************
Functions to retrieve the grant for a table/column (for SHOW functions)
*****************************************************************************/
@@ -3215,12 +3656,12 @@ static const char *command_array[]=
"SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX",
"ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
"LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT",
- "CREATE VIEW", "SHOW VIEW"
+ "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
};
static uint command_lengths[]=
{
- 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9
+ 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9, 14, 13
};
@@ -3565,6 +4006,74 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
}
}
}
+
+ /* Add procedure access */
+ for (index=0 ; index < proc_priv_hash.records ; index++)
+ {
+ const char *user;
+ GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(&proc_priv_hash,
+ index);
+
+ if (!(user=grant_proc->user))
+ user= "";
+
+ if (!strcmp(lex_user->user.str,user) &&
+ !my_strcasecmp(system_charset_info, lex_user->host.str,
+ grant_proc->orig_host))
+ {
+ ulong proc_access= grant_proc->privs;
+ if (proc_access != 0)
+ {
+ String global(buff, sizeof(buff), system_charset_info);
+ ulong test_access= proc_access & ~GRANT_ACL;
+
+ global.length(0);
+ global.append("GRANT ",6);
+
+ if (!test_access)
+ global.append("USAGE",5);
+ else
+ {
+ /* Add specific procedure access */
+ int found= 0;
+ ulong j;
+
+ for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1)
+ {
+ if (test_access & j)
+ {
+ if (found)
+ global.append(", ",2);
+ found= 1;
+ global.append(command_array[counter],command_lengths[counter]);
+ }
+ }
+ }
+ global.append(" ON ",4);
+ append_identifier(thd, &global, grant_proc->db,
+ strlen(grant_proc->db));
+ global.append('.');
+ append_identifier(thd, &global, grant_proc->tname,
+ strlen(grant_proc->tname));
+ global.append(" TO '",5);
+ global.append(lex_user->user.str, lex_user->user.length,
+ system_charset_info);
+ global.append("'@'",3);
+ global.append(lex_user->host.str,lex_user->host.length,
+ system_charset_info);
+ global.append('\'');
+ if (proc_access & GRANT_ACL)
+ global.append(" WITH GRANT OPTION",18);
+ protocol->prepare_for_resend();
+ protocol->store(global.ptr(),global.length(),global.charset());
+ if (protocol->write())
+ {
+ error= -1;
+ break;
+ }
+ }
+ }
+ }
end:
VOID(pthread_mutex_unlock(&acl_cache->lock));
rw_unlock(&LOCK_grant);
@@ -3632,6 +4141,7 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc)
< 0 Error.
*/
+#define GRANT_TABLES 5
int open_grant_tables(THD *thd, TABLE_LIST *tables)
{
DBUG_ENTER("open_grant_tables");
@@ -3642,17 +4152,21 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
DBUG_RETURN(-1);
}
- bzero((char*) tables, 4*sizeof(*tables));
+ bzero((char*) tables, GRANT_TABLES*sizeof(*tables));
tables->alias= tables->real_name= (char*) "user";
(tables+1)->alias= (tables+1)->real_name= (char*) "db";
(tables+2)->alias= (tables+2)->real_name= (char*) "tables_priv";
(tables+3)->alias= (tables+3)->real_name= (char*) "columns_priv";
+ (tables+4)->alias= (tables+4)->real_name= (char*) "procs_priv";
tables->next_local= tables->next_global= tables+1;
(tables+1)->next_local= (tables+1)->next_global= tables+2;
(tables+2)->next_local= (tables+2)->next_global= tables+3;
+ (tables+3)->next_local= (tables+3)->next_global= tables+4;
tables->lock_type= (tables+1)->lock_type=
- (tables+2)->lock_type= (tables+3)->lock_type= TL_WRITE;
- tables->db= (tables+1)->db= (tables+2)->db= (tables+3)->db=(char*) "mysql";
+ (tables+2)->lock_type= (tables+3)->lock_type=
+ (tables+4)->lock_type= TL_WRITE;
+ tables->db= (tables+1)->db= (tables+2)->db=
+ (tables+3)->db= (tables+4)->db= (char*) "mysql";
#ifdef HAVE_REPLICATION
/*
@@ -3665,10 +4179,12 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
The tables must be marked "updating" so that tables_ok() takes them into
account in tests.
*/
- tables[0].updating=tables[1].updating=tables[2].updating=tables[3].updating=1;
+ tables[0].updating=tables[1].updating=tables[2].updating=
+ tables[3].updating=tables[4].updating=1;
if (!tables_ok(0, tables))
DBUG_RETURN(1);
- tables[0].updating=tables[1].updating=tables[2].updating=tables[3].updating=0;
+ tables[0].updating=tables[1].updating=tables[2].updating=
+ tables[3].updating=tables[4].updating=0;;
}
#endif
@@ -3761,7 +4277,7 @@ static int modify_grant_table(TABLE *table, Field *host_field,
SYNOPSIS
handle_grant_table()
tables The array with the four open tables.
- table_no The number of the table to handle (0..3).
+ table_no The number of the table to handle (0..4).
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,
@@ -3779,6 +4295,7 @@ static int modify_grant_table(TABLE *table, Field *host_field,
1 db
2 tables_priv
3 columns_priv
+ 4 procs_priv
RETURN
> 0 At least one record matched.
@@ -3922,6 +4439,7 @@ 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
RETURN
> 0 At least one element matched.
@@ -3938,7 +4456,7 @@ static int handle_grant_struct(uint struct_no, bool drop,
const char *host;
ACL_USER *acl_user;
ACL_DB *acl_db;
- GRANT_TABLE *grant_table;
+ GRANT_NAME *grant_name;
DBUG_ENTER("handle_grant_struct");
LINT_INIT(acl_user);
LINT_INIT(acl_db);
@@ -3955,8 +4473,15 @@ static int handle_grant_struct(uint struct_no, bool drop,
case 1:
elements= acl_dbs.elements;
break;
- default:
+ case 2:
elements= column_priv_hash.records;
+ break;
+ case 3:
+ elements= proc_priv_hash.records;
+ break;
+ default:
+ DBUG_ASSERT((struct_no < 0) || (struct_no > 3));
+ return -1;
}
#ifdef EXTRA_DEBUG
@@ -3985,10 +4510,17 @@ static int handle_grant_struct(uint struct_no, bool drop,
host= acl_db->host.hostname;
break;
- default:
- grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash, idx);
- user= grant_table->user;
- host= grant_table->host;
+ case 2:
+ grant_name= (GRANT_NAME*) hash_element(&column_priv_hash, idx);
+ user= grant_name->user;
+ host= grant_name->host;
+ break;
+
+ case 3:
+ grant_name= (GRANT_NAME*) hash_element(&proc_priv_hash, idx);
+ user= grant_name->user;
+ host= grant_name->host;
+ break;
}
if (! user)
user= "";
@@ -4015,8 +4547,13 @@ static int handle_grant_struct(uint struct_no, bool drop,
delete_dynamic_element(&acl_dbs, idx);
break;
- default:
- hash_delete(&column_priv_hash, (byte*) grant_table);
+ case 2:
+ hash_delete(&column_priv_hash, (byte*) grant_name);
+ break;
+
+ case 3:
+ hash_delete(&proc_priv_hash, (byte*) grant_name);
+ break;
}
elements--;
idx--;
@@ -4035,9 +4572,11 @@ static int handle_grant_struct(uint struct_no, bool drop,
acl_db->host.hostname= strdup_root(&mem, user_to->host.str);
break;
- default:
- grant_table->user= strdup_root(&mem, user_to->user.str);
- grant_table->host= strdup_root(&mem, user_to->host.str);
+ case 2:
+ case 3:
+ grant_name->user= strdup_root(&mem, user_to->user.str);
+ grant_name->host= strdup_root(&mem, user_to->host.str);
+ break;
}
}
else
@@ -4123,6 +4662,25 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
}
}
+ /* Handle procedures table. */
+ if ((found= handle_grant_table(tables, 4, drop, user_from, user_to)) < 0)
+ {
+ /* Handle of table failed, don't touch in-memory array. */
+ result= -1;
+ }
+ else
+ {
+ /* Handle procs array. */
+ if (((handle_grant_struct(3, 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. */
if ((found= handle_grant_table(tables, 2, drop, user_from, user_to)) < 0)
{
@@ -4191,7 +4749,7 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
ulong sql_mode;
LEX_USER *user_name;
List_iterator <LEX_USER> user_list(list);
- TABLE_LIST tables[4];
+ TABLE_LIST tables[GRANT_TABLES];
DBUG_ENTER("mysql_create_user");
/* CREATE USER may be skipped on replication client. */
@@ -4253,7 +4811,7 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
String wrong_users;
LEX_USER *user_name;
List_iterator <LEX_USER> user_list(list);
- TABLE_LIST tables[4];
+ TABLE_LIST tables[GRANT_TABLES];
DBUG_ENTER("mysql_drop_user");
/* DROP USER may be skipped on replication client. */
@@ -4302,7 +4860,7 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
LEX_USER *user_from;
LEX_USER *user_to;
List_iterator <LEX_USER> user_list(list);
- TABLE_LIST tables[4];
+ TABLE_LIST tables[GRANT_TABLES];
DBUG_ENTER("mysql_rename_user");
/* RENAME USER may be skipped on replication client. */
@@ -4357,7 +4915,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
uint counter, revoked;
int result;
ACL_DB *acl_db;
- TABLE_LIST tables[4];
+ TABLE_LIST tables[GRANT_TABLES];
DBUG_ENTER("mysql_revoke_all");
if ((result= open_grant_tables(thd, tables)))
@@ -4467,6 +5025,35 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
counter++;
}
} while (revoked);
+
+ /* Remove procedure access */
+ do {
+ for (counter= 0, revoked= 0 ; counter < proc_priv_hash.records ; )
+ {
+ const char *user,*host;
+ GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(&proc_priv_hash,
+ counter);
+ if (!(user=grant_proc->user))
+ user= "";
+ if (!(host=grant_proc->host))
+ host= "";
+
+ if (!strcmp(lex_user->user.str,user) &&
+ !my_strcasecmp(system_charset_info, lex_user->host.str, host))
+ {
+ if (!replace_proc_table(thd,grant_proc,tables[4].table,*lex_user,
+ grant_proc->db,
+ grant_proc->tname,
+ ~0, 1))
+ {
+ revoked= 1;
+ continue;
+ }
+ result= -1; // Something went wrong
+ }
+ counter++;
+ }
+ } while (revoked);
}
VOID(pthread_mutex_unlock(&acl_cache->lock));
@@ -4480,6 +5067,129 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
}
+/*
+ Revoke privileges for all users on a stored procedure
+
+ SYNOPSIS
+ sp_revoke_privileges()
+ thd The current thread.
+ db DB of the stored procedure
+ name Name of the stored procedure
+
+ RETURN
+ 0 OK.
+ < 0 Error. Error message not yet sent.
+*/
+
+bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name)
+{
+ uint counter, revoked;
+ int result;
+ ACL_DB *acl_db;
+ TABLE_LIST tables[GRANT_TABLES];
+ DBUG_ENTER("sp_revoke_privileges");
+
+ if ((result= open_grant_tables(thd, tables)))
+ DBUG_RETURN(result != 1);
+
+ rw_wrlock(&LOCK_grant);
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+
+ /* Remove procedure access */
+ do {
+ for (counter= 0, revoked= 0 ; counter < proc_priv_hash.records ; )
+ {
+ const char *db,*name;
+ GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(&proc_priv_hash,
+ counter);
+ if (!my_strcasecmp(system_charset_info, grant_proc->db, sp_db) &&
+ !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
+ {
+ LEX_USER lex_user;
+ lex_user.user.str= grant_proc->user;
+ lex_user.user.length= strlen(grant_proc->user);
+ lex_user.host.str= grant_proc->host;
+ lex_user.host.length= strlen(grant_proc->host);
+ if (!replace_proc_table(thd,grant_proc,tables[4].table,lex_user,
+ grant_proc->db, grant_proc->tname, ~0, 1))
+ {
+ revoked= 1;
+ continue;
+ }
+ result= -1; // Something went wrong
+ }
+ counter++;
+ }
+ } while (revoked);
+
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ rw_unlock(&LOCK_grant);
+ close_thread_tables(thd);
+
+ if (result)
+ my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
+
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Grant EXECUTE,ALTER privilege for a stored procedure
+
+ SYNOPSIS
+ sp_grant_privileges()
+ thd The current thread.
+ db DB of the stored procedure
+ name Name of the stored procedure
+
+ RETURN
+ 0 OK.
+ < 0 Error. Error message not yet sent.
+*/
+
+bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name)
+{
+ LEX_USER *combo;
+ TABLE_LIST tables[1];
+ List<LEX_USER> user_list;
+ bool result;
+ DBUG_ENTER("sp_grant_privileges");
+
+ if (!(combo=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
+ DBUG_RETURN(TRUE);
+
+ combo->user.str= thd->user;
+
+ if (!find_acl_user(combo->host.str=(char*)thd->host_or_ip, combo->user.str) &&
+ !find_acl_user(combo->host.str=(char*)thd->host, combo->user.str) &&
+ !find_acl_user(combo->host.str=(char*)thd->ip, combo->user.str) &&
+ !find_acl_user(combo->host.str=(char*)"%", combo->user.str))
+ DBUG_RETURN(TRUE);
+
+ bzero((char*)tables, sizeof(TABLE_LIST));
+ user_list.empty();
+
+ tables->db= (char*)sp_db;
+ tables->real_name= tables->alias= (char*)sp_name;
+
+ combo->host.length= strlen(combo->host.str);
+ combo->user.length= strlen(combo->user.str);
+ combo->host.str= thd->strmake(combo->host.str,combo->host.length);
+ combo->user.str= thd->strmake(combo->user.str,combo->user.length);
+ combo->password.str= (char*)"";
+ combo->password.length= 0;
+
+ if (user_list.push_back(combo))
+ DBUG_RETURN(TRUE);
+
+ thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
+
+ result= mysql_procedure_grant(thd, tables, user_list,
+ DEFAULT_CREATE_PROC_ACLS, 0, 1);
+ DBUG_RETURN(result);
+}
+
+
/*****************************************************************************
Instantiate used templates
*****************************************************************************/
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index 8f3ee072f43..4ebc3ad7707 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -37,6 +37,8 @@
#define REPL_CLIENT_ACL (1L << 20)
#define CREATE_VIEW_ACL (1L << 21)
#define SHOW_VIEW_ACL (1L << 22)
+#define CREATE_PROC_ACL (1L << 23)
+#define ALTER_PROC_ACL (1L << 24)
/*
don't forget to update
static struct show_privileges_st sys_privileges[]
@@ -47,7 +49,8 @@
#define DB_ACLS \
(UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | \
- LOCK_TABLES_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL)
+ LOCK_TABLES_ACL | EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | \
+ CREATE_PROC_ACL | ALTER_PROC_ACL)
#define TABLE_ACLS \
(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
@@ -57,43 +60,61 @@
#define COL_ACLS \
(SELECT_ACL | INSERT_ACL | UPDATE_ACL | REFERENCES_ACL)
+#define PROC_ACLS \
+(ALTER_PROC_ACL | EXECUTE_ACL | GRANT_ACL)
+
#define GLOBAL_ACLS \
(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL | GRANT_ACL | \
REFERENCES_ACL | INDEX_ACL | ALTER_ACL | SHOW_DB_ACL | SUPER_ACL | \
CREATE_TMP_ACL | LOCK_TABLES_ACL | REPL_SLAVE_ACL | REPL_CLIENT_ACL | \
- EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL)
+ EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | CREATE_PROC_ACL | \
+ ALTER_PROC_ACL )
#define EXTRA_ACL (1L << 29)
#define NO_ACCESS (1L << 30)
+#define DEFAULT_CREATE_PROC_ACLS \
+(ALTER_PROC_ACL | EXECUTE_ACL)
+
/*
Defines to change the above bits to how things are stored in tables
This is needed as the 'host' and 'db' table is missing a few privileges
*/
/* Continius bit-segments that needs to be shifted */
-#define DB_REL1 (RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL)
-#define DB_REL2 (GRANT_ACL | REFERENCES_ACL)
-#define DB_REL3 (INDEX_ACL | ALTER_ACL)
+#define DB_REL1 ((1L << 6) | (1L << 7) | (1L << 8) | (1L << 9))
+#define DB_REL2 ((1L << 10) | (1L << 11))
+#define DB_REL3 ((1L << 12) | (1L << 13) | (1L << 14) | (1L << 15))
+#define DB_REL4 ((1L << 16))
/* Privileges that needs to be reallocated (in continous chunks) */
#define DB_CHUNK1 (GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL)
#define DB_CHUNK2 (CREATE_TMP_ACL | LOCK_TABLES_ACL)
-#define DB_CHUNK3 (CREATE_VIEW_ACL | SHOW_VIEW_ACL)
+#define DB_CHUNK3 (CREATE_VIEW_ACL | SHOW_VIEW_ACL | \
+ CREATE_PROC_ACL | ALTER_PROC_ACL )
+#define DB_CHUNK4 (EXECUTE_ACL)
#define fix_rights_for_db(A) (((A) & 63) | \
(((A) & DB_REL1) << 4) | \
(((A) & DB_REL2) << 6) | \
- (((A) & DB_REL3) << 9))
+ (((A) & DB_REL3) << 9) | \
+ (((A) & DB_REL4) << 2))
#define get_rights_for_db(A) (((A) & 63) | \
(((A) & DB_CHUNK1) >> 4) | \
(((A) & DB_CHUNK2) >> 6) | \
- (((A) & DB_CHUNK3) >> 9))
+ (((A) & DB_CHUNK3) >> 9) | \
+ (((A) & DB_CHUNK4) >> 2))
#define fix_rights_for_table(A) (((A) & 63) | (((A) & ~63) << 4))
#define get_rights_for_table(A) (((A) & 63) | (((A) & ~63) >> 4))
#define fix_rights_for_column(A) (((A) & 7) | (((A) & ~7) << 8))
#define get_rights_for_column(A) (((A) & 7) | ((A) >> 8))
+#define fix_rights_for_procedure(A) ((((A) << 18) & EXECUTE_ACL) | \
+ (((A) << 23) & ALTER_PROC_ACL) | \
+ (((A) << 8) & GRANT_ACL))
+#define get_rights_for_procedure(A) ((((A) & EXECUTE_ACL) >> 18) | \
+ (((A) & ALTER_PROC_ACL) >> 23) | \
+ (((A) & GRANT_ACL) >> 8))
/* Classes */
@@ -163,6 +184,9 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list,
bool mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list,
List <LEX_COLUMN> &column_list, ulong rights,
bool revoke);
+bool mysql_procedure_grant(THD *thd, TABLE_LIST *table,
+ List <LEX_USER> &user_list, ulong rights,
+ bool revoke, bool no_error);
my_bool grant_init(THD *thd);
void grant_free(void);
void grant_reload(THD *thd);
@@ -174,6 +198,8 @@ bool check_grant_column (THD *thd, GRANT_INFO *grant,
bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant,
char* db_name, char *table_name,
Field_iterator *fields);
+bool check_grant_procedure(THD *thd, ulong want_access,
+ TABLE_LIST *procs, bool no_error);
bool check_grant_db(THD *thd,const char *db);
ulong get_table_grant(THD *thd, TABLE_LIST *table);
ulong get_column_grant(THD *thd, GRANT_INFO *grant,
@@ -188,6 +214,8 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list);
bool mysql_revoke_all(THD *thd, List <LEX_USER> &list);
void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
const char *db, const char *table);
+bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name);
+bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name);
#ifdef NO_EMBEDDED_ACCESS_CHECKS
#define check_grant(A,B,C,D,E,F) 0
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 721febab548..19af2d9f243 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -743,6 +743,7 @@ typedef struct st_lex
sp_head *sphead;
sp_name *spname;
bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
+ bool all_privileges;
sp_pcontext *spcont;
HASH spfuns; /* Called functions */
st_sp_chistics sp_chistics;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 0dec6e820be..80639e06f5f 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -69,7 +69,6 @@ static void remove_escape(char *name);
static void refresh_status(void);
static bool append_file_to_dir(THD *thd, const char **filename_ptr,
const char *table_name);
-static bool check_sp_definer_access(THD *thd, sp_head *sp);
const char *any_db="*any*"; // Special symbol for check_access
@@ -3495,15 +3494,30 @@ create_error:
}
if (first_table)
{
- if (grant_option && check_grant(thd,
- (lex->grant | lex->grant_tot_col |
- GRANT_ACL),
- all_tables, 0, UINT_MAX, 0))
- goto error;
- if (!(res = mysql_table_grant(thd, all_tables, lex->users_list,
- lex->columns, lex->grant,
- lex->sql_command == SQLCOM_REVOKE)) &&
- mysql_bin_log.is_open())
+ if (!lex->columns.elements &&
+ sp_exists_routine(thd, all_tables, 1, 1))
+ {
+ uint grants= lex->all_privileges
+ ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL)
+ : lex->grant;
+ if (grant_option &&
+ check_grant_procedure(thd, grants | GRANT_ACL, all_tables, 0))
+ goto error;
+ res= mysql_procedure_grant(thd, all_tables, lex->users_list,
+ grants, lex->sql_command == SQLCOM_REVOKE,0);
+ }
+ else
+ {
+ if (grant_option && check_grant(thd,
+ (lex->grant | lex->grant_tot_col |
+ GRANT_ACL),
+ all_tables, 0, UINT_MAX, 0))
+ goto error;
+ res= mysql_table_grant(thd, all_tables, lex->users_list,
+ lex->columns, lex->grant,
+ lex->sql_command == SQLCOM_REVOKE);
+ }
+ if (!res && mysql_bin_log.is_open())
{
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
@@ -3705,18 +3719,24 @@ create_error:
case SQLCOM_CREATE_SPFUNCTION:
{
uint namelen;
- char *name;
+ char *name, *db;
int result;
DBUG_ASSERT(lex->sphead);
- if (! lex->sphead->m_db.str)
+ if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, 0, 0, 0))
{
- my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
delete lex->sphead;
lex->sphead= 0;
goto error;
}
+
+ if (!lex->sphead->m_db.str || !lex->sphead->m_db.str[0])
+ {
+ lex->sphead->m_db.length= strlen(thd->db);
+ lex->sphead->m_db.str= strmake_root(thd->mem_root, thd->db,
+ lex->sphead->m_db.length);
+ }
name= lex->sphead->name(&namelen);
#ifdef HAVE_DLOPEN
@@ -3742,13 +3762,26 @@ create_error:
goto error;
}
+ name= thd->strdup(name);
+ db= thd->strmake(lex->sphead->m_db.str, lex->sphead->m_db.length);
res= (result= lex->sphead->create(thd));
switch (result) {
case SP_OK:
- send_ok(thd);
lex->unit.cleanup();
delete lex->sphead;
lex->sphead= 0;
+ /* only add privileges if really neccessary */
+ if (sp_automatic_privileges &&
+ check_procedure_access(thd, DEFAULT_CREATE_PROC_ACLS,
+ db, name, 1))
+ {
+ close_thread_tables(thd);
+ if (sp_grant_privileges(thd, db, name))
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_PROC_AUTO_GRANT_FAIL,
+ ER(ER_PROC_AUTO_GRANT_FAIL));
+ }
+ send_ok(thd);
break;
case SP_WRITE_ROW_FAILED:
my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name);
@@ -3815,7 +3848,26 @@ create_error:
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (check_procedure_access(thd, EXECUTE_ACL,
+ sp->m_db.str, sp->m_name.str, 0))
+ {
+#ifndef EMBEDDED_LIBRARY
+ thd->net.no_send_ok= nsok;
+#endif
+ goto error;
+ }
sp_change_security_context(thd, sp, &save_ctx);
+ if (save_ctx.changed &&
+ check_procedure_access(thd, EXECUTE_ACL,
+ sp->m_db.str, sp->m_name.str, 0))
+ {
+#ifndef EMBEDDED_LIBRARY
+ thd->net.no_send_ok= nsok;
+#endif
+ sp_restore_security_context(thd, sp, &save_ctx);
+ goto error;
+ }
+
#endif
select_limit= thd->variables.select_limit;
thd->variables.select_limit= HA_POS_ERROR;
@@ -3861,8 +3913,9 @@ create_error:
result= SP_KEY_NOT_FOUND;
else
{
- if (check_sp_definer_access(thd, sp))
- goto error;
+ if (check_procedure_access(thd, ALTER_PROC_ACL, sp->m_db.str,
+ sp->m_name.str, 0))
+ goto error;
memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics));
if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
result= sp_update_procedure(thd, lex->spname, &lex->sp_chistics);
@@ -3890,6 +3943,7 @@ create_error:
{
sp_head *sp;
int result;
+ char *db, *name;
if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
sp= sp_find_procedure(thd, lex->spname);
@@ -3898,8 +3952,17 @@ create_error:
mysql_reset_errors(thd);
if (sp)
{
- if (check_sp_definer_access(thd, sp))
+ db= thd->strdup(sp->m_db.str);
+ name= thd->strdup(sp->m_name.str);
+ if (check_procedure_access(thd, ALTER_PROC_ACL, db, name, 0))
goto error;
+ if (sp_automatic_privileges &&
+ sp_revoke_privileges(thd, db, name))
+ {
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_PROC_AUTO_REVOKE_FAIL,
+ ER(ER_PROC_AUTO_REVOKE_FAIL));
+ }
if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
result= sp_drop_procedure(thd, lex->spname);
else
@@ -4208,7 +4271,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
/* grant_option is set if there exists a single table or column grant */
if (db_access == want_access ||
(grant_option && !dont_check_global_grants &&
- !(want_access & ~(db_access | TABLE_ACLS))))
+ !(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS))))
DBUG_RETURN(FALSE); /* Ok */
DBUG_PRINT("error",("Access denied"));
@@ -4304,6 +4367,28 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
}
+bool
+check_procedure_access(THD *thd, ulong want_access,char *db, char *name,
+ bool no_errors)
+{
+ TABLE_LIST tables[1];
+
+ bzero((char *)tables, sizeof(TABLE_LIST));
+ tables->db= db;
+ tables->real_name= tables->alias= name;
+
+ if ((thd->master_access & want_access) == want_access && !thd->db)
+ tables->grant.privilege= want_access;
+ else if (check_access(thd,want_access,db,&tables->grant.privilege,
+ 0, no_errors))
+ return TRUE;
+
+ if (grant_option)
+ return check_grant_procedure(thd, want_access, tables, no_errors);
+
+ return FALSE;
+}
+
/*
Check if the given table has any of the asked privileges
@@ -4377,40 +4462,6 @@ static bool check_db_used(THD *thd,TABLE_LIST *tables)
}
-/*
- Check if the given SP is owned by thd->priv_user/host, or priv_user is root.
- QQ This is not quite complete, but it will do as a basic security check
- for now. The question is exactly which rights should 'root' have?
- Should root have access regardless of host for instance?
-
- SYNOPSIS
- check_sp_definer_access()
- thd Thread handler
- sp The SP pointer
-
- RETURN
- 0 ok
- 1 error Error message has been sent
-*/
-
-static bool
-check_sp_definer_access(THD *thd, sp_head *sp)
-{
- LEX_STRING *usr, *hst;
-
- if (strcmp("root", thd->priv_user) == 0)
- return FALSE; /* QQ Any root is ok now */
- usr= &sp->m_definer_user;
- hst= &sp->m_definer_host;
- if (strncmp(thd->priv_user, usr->str, usr->length) == 0 &&
- strncmp(thd->priv_host, hst->str, hst->length) == 0)
- return FALSE; /* Both user and host must match */
-
- my_error(ER_SP_ACCESS_DENIED_ERROR, MYF(0), sp->m_qname.str);
- return TRUE; /* Not definer or root */
-}
-
-
/****************************************************************************
Check stack size; Send error if there isn't enough stack to continue
****************************************************************************/
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 826bd2038f9..f8e7056b705 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -138,13 +138,16 @@ struct show_privileges_st {
static struct show_privileges_st sys_privileges[]=
{
{"Alter", "Tables", "To alter the table"},
+ {"Alter routine", "Functions,Procedures", "To alter or drop stored functions/procedures"},
{"Create", "Databases,Tables,Indexes", "To create new databases and tables"},
+ {"Create routine","Functions,Procedures","To use CREATE FUNCTION/PROCEDURE"},
{"Create temporary tables","Databases","To use CREATE TEMPORARY TABLE"},
{"Create view", "Tables", "To create new views"},
{"Delete", "Tables", "To delete existing rows"},
{"Drop", "Databases,Tables", "To drop databases, tables, and views"},
+ {"Execute", "Functions,Procedures", "To execute stored routines"},
{"File", "File access on server", "To read and write files on the server"},
- {"Grant option", "Databases,Tables", "To give to other users those privileges you possess"},
+ {"Grant option", "Databases,Tables,Functions,Procedures", "To give to other users those privileges you possess"},
{"Index", "Tables", "To create or drop indexes"},
{"Insert", "Tables", "To insert data into tables"},
{"Lock tables","Databases","To use LOCK TABLES (together with SELECT privilege)"},
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 88cd3daf924..75db6e332f9 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -390,6 +390,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token RESTORE_SYM
%token RESTRICT
%token REVOKE
+%token ROUTINE_SYM
%token ROWS_SYM
%token ROW_FORMAT_SYM
%token ROW_SYM
@@ -790,7 +791,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
opt_outer table_list table_name opt_option opt_place
opt_attribute opt_attribute_list attribute column_list column_list_id
opt_column_list grant_privileges opt_table grant_list grant_option
- grant_privilege grant_privilege_list user_list rename_list
+ object_privilege object_privilege_list user_list rename_list
clear_privileges flush_options flush_option
equal optional_braces opt_key_definition key_usage_list2
opt_mi_check_type opt_to mi_check_types normal_join
@@ -1301,6 +1302,7 @@ clear_privileges:
lex->users_list.empty();
lex->columns.empty();
lex->grant= lex->grant_tot_col= 0;
+ lex->all_privileges= 0;
lex->select_lex.db= 0;
lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0;
@@ -7031,6 +7033,7 @@ keyword:
| RETURNS_SYM {}
| ROLLBACK_SYM {}
| ROLLUP_SYM {}
+ | ROUTINE_SYM {}
| ROWS_SYM {}
| ROW_FORMAT_SYM {}
| ROW_SYM {}
@@ -7543,14 +7546,16 @@ revoke_command:
grant:
GRANT clear_privileges grant_privileges ON opt_table TO_SYM grant_list
require_clause grant_options
- {
- Lex->sql_command = SQLCOM_GRANT;
- }
+ { Lex->sql_command= SQLCOM_GRANT; }
;
grant_privileges:
- grant_privilege_list {}
- | ALL opt_privileges { Lex->grant = GLOBAL_ACLS;}
+ object_privilege_list { }
+ | ALL opt_privileges
+ {
+ Lex->all_privileges= 1;
+ Lex->grant= GLOBAL_ACLS;
+ }
;
opt_privileges:
@@ -7558,11 +7563,11 @@ opt_privileges:
| PRIVILEGES
;
-grant_privilege_list:
- grant_privilege
- | grant_privilege_list ',' grant_privilege;
+object_privilege_list:
+ object_privilege
+ | object_privilege_list ',' object_privilege;
-grant_privilege:
+object_privilege:
SELECT_SYM { Lex->which_columns = SELECT_ACL;} opt_column_list {}
| INSERT { Lex->which_columns = INSERT_ACL;} opt_column_list {}
| UPDATE_SYM { Lex->which_columns = UPDATE_ACL; } opt_column_list {}
@@ -7587,6 +7592,8 @@ grant_privilege:
| REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL; }
| CREATE VIEW_SYM { Lex->grant |= CREATE_VIEW_ACL; }
| SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; }
+ | CREATE ROUTINE_SYM { Lex->grant |= CREATE_PROC_ACL; }
+ | ALTER ROUTINE_SYM { Lex->grant |= ALTER_PROC_ACL; }
;