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)
  
parent 8fb20802
......@@ -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;
......@@ -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
......
......@@ -10,7 +10,7 @@ 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
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
......@@ -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)
......
......@@ -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@'%';
......
......@@ -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;
......
......@@ -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;
......
......@@ -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';
......@@ -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)
......
......@@ -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
......@@ -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@'%';
......
......@@ -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;
......
......@@ -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';
......@@ -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
......
......@@ -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
......@@ -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 (
......@@ -170,9 +171,47 @@ 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,
......
......@@ -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.
......
......@@ -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
/*
......
......@@ -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)},
......
......@@ -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;
......
......@@ -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
......
......@@ -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
......
......@@ -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 fr %.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"
......@@ -738,6 +738,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)
{
......
......@@ -36,6 +36,9 @@ sp_drop_db_routines(THD *thd, char *db);
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);
......
......@@ -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,29 +3080,29 @@ 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);
......@@ -2781,7 +3112,7 @@ my_bool grant_init(THD *org_thd)
if (!(mem_check=new GRANT_TABLE(t_table,c_table)))
{
/* This could only happen if we are out memory */
grant_option= FALSE; /* purecov: deadcode */
grant_option= FALSE;
goto end_unlock;
}
......@@ -2807,11 +3138,52 @@ my_bool grant_init(THD *org_thd)
}
}
while (!t_table->file->index_next(t_table->record[0]));
}
if (!p_table->file->index_first(p_table->record[0]))
{
/* Will be restored by org_thd->store_globals() */
memex_ptr= &memex;
my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
do
{
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))
{
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;
}
}
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]));
}
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
*****************************************************************************/
......
......@@ -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
......
......@@ -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;
......
......@@ -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
......@@ -3494,16 +3493,31 @@ create_error:
}
}
if (first_table)
{
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;
if (!(res = mysql_table_grant(thd, all_tables, lex->users_list,
res= mysql_table_grant(thd, all_tables, lex->users_list,
lex->columns, lex->grant,
lex->sql_command == SQLCOM_REVOKE)) &&
mysql_bin_log.is_open())
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,19 +3719,25 @@ 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
if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
......@@ -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,7 +3913,8 @@ create_error:
result= SP_KEY_NOT_FOUND;
else
{
if (check_sp_definer_access(thd, sp))
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)
......@@ -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
****************************************************************************/
......
......@@ -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)"},
......
......@@ -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; }
;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment