Commit a175fc12 authored by unknown's avatar unknown

Fixed BUG#2777: Stored procedure doesn't observe definer's rights.

SQL SECURITY DEFINER must enforce reduced rights too, not just additional rights.


mysql-test/r/sp-security.result:
  Test case for BUG#2777: Make sure that SQL SECURITY DEFINER enforces reduced rights.
mysql-test/t/sp-security.test:
  Test case for BUG#2777: Make sure that SQL SECURITY DEFINER enforces reduced rights.
sql/sql_acl.cc:
  Clear rights before changing them in acl_getroot_no_password so that
  reduced rights work too, and take care of db acls as well.
parent f6b85330
use test; use test;
grant usage on *.* to dummy@localhost; grant usage on *.* to user1@localhost;
flush privileges;
drop database if exists db1_secret; drop database if exists db1_secret;
create database db1_secret; create database db1_secret;
use db1_secret; use db1_secret;
...@@ -15,14 +16,14 @@ u i ...@@ -15,14 +16,14 @@ u i
root@localhost 1 root@localhost 1
call stamp(2); call stamp(2);
select * from db1_secret.t1; select * from db1_secret.t1;
ERROR 42000: Access denied for user: 'dummy'@'localhost' to database 'db1_secret' ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret'
call stamp(3); call stamp(3);
select * from db1_secret.t1; select * from db1_secret.t1;
ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret' ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret'
select * from t1; select * from t1;
u i u i
root@localhost 1 root@localhost 1
dummy@localhost 2 user1@localhost 2
anon@localhost 3 anon@localhost 3
alter procedure stamp sql security invoker; alter procedure stamp sql security invoker;
show procedure status like 'stamp'; show procedure status like 'stamp';
...@@ -32,14 +33,50 @@ call stamp(4); ...@@ -32,14 +33,50 @@ call stamp(4);
select * from t1; select * from t1;
u i u i
root@localhost 1 root@localhost 1
dummy@localhost 2 user1@localhost 2
anon@localhost 3 anon@localhost 3
root@localhost 4 root@localhost 4
call stamp(5); call stamp(5);
ERROR 42000: Access denied for user: 'dummy'@'localhost' to database 'db1_secret' ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db1_secret'
call stamp(6); call stamp(6);
ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret' ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret'
drop database if exists db2;
create database db2;
use db2;
create table t2 (s1 int);
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;
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'
use db2;
call p();
ERROR 42000: Access denied for user: 'user1'@'localhost' to database 'db2'
select * from t2;
s1
0
create procedure q () insert into t2 values (2);
call q();
select * from t2;
s1
0
2
use db2;
call q();
select * from t2;
s1
0
2
2
drop procedure stamp; drop procedure stamp;
drop procedure p;
drop procedure q;
use test; use test;
drop database db1_secret; drop database db1_secret;
delete from mysql.user where user='dummy'; drop database db2;
delete from mysql.user where user='user1' or user='user2';
...@@ -7,8 +7,9 @@ connect (con1root,localhost,root,,); ...@@ -7,8 +7,9 @@ connect (con1root,localhost,root,,);
connection con1root; connection con1root;
use test; use test;
# Create dummy user with no particular access rights # Create user user1 with no particular access rights
grant usage on *.* to dummy@localhost; grant usage on *.* to user1@localhost;
flush privileges;
--disable_warnings --disable_warnings
drop database if exists db1_secret; drop database if exists db1_secret;
...@@ -30,13 +31,13 @@ show procedure status like 'stamp'; ...@@ -30,13 +31,13 @@ show procedure status like 'stamp';
call stamp(1); call stamp(1);
select * from t1; select * from t1;
connect (con2dummy,localhost,dummy,,); connect (con2user1,localhost,user1,,);
connect (con3anon,localhost,anon,,); connect (con3anon,localhost,anon,,);
# #
# Dummy can # User1 can
# #
connection con2dummy; connection con2user1;
# This should work... # This should work...
call stamp(2); call stamp(2);
...@@ -75,9 +76,9 @@ call stamp(4); ...@@ -75,9 +76,9 @@ call stamp(4);
select * from t1; select * from t1;
# #
# Dummy cannot # User1 cannot
# #
connection con2dummy; connection con2user1;
# This should not work # This should not work
--error 1044 --error 1044
...@@ -92,9 +93,65 @@ connection con3anon; ...@@ -92,9 +93,65 @@ connection con3anon;
--error 1044 --error 1044
call stamp(6); call stamp(6);
#
# BUG#2777
#
connection con1root;
--disable_warnings
drop database if exists db2;
--enable_warnings
create database db2;
use db2;
create table t2 (s1 int);
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;
flush privileges;
connection con2user1;
use db2;
create procedure p () insert into t2 values (1);
# Check that this doesn't work.
--error 1044
call p();
connect (con4user2,localhost,user2,,);
connection con4user2;
use db2;
# This should not work, since p is executed with definer's (user1's) rights.
--error 1044
call p();
select * from t2;
create procedure q () insert into t2 values (2);
call q();
select * from t2;
connection con2user1;
use db2;
# This should work
call q();
select * from t2;
# Clean up # Clean up
connection con1root; connection con1root;
drop procedure stamp; drop procedure stamp;
drop procedure p;
drop procedure q;
use test; use test;
drop database db1_secret; drop database db1_secret;
delete from mysql.user where user='dummy'; drop database db2;
delete from mysql.user where user='user1' or user='user2';
...@@ -794,6 +794,7 @@ int acl_getroot_no_password(THD *thd) ...@@ -794,6 +794,7 @@ int acl_getroot_no_password(THD *thd)
{ {
ulong user_access= NO_ACCESS; ulong user_access= NO_ACCESS;
int res= 1; int res= 1;
uint i;
ACL_USER *acl_user= 0; ACL_USER *acl_user= 0;
DBUG_ENTER("acl_getroot_no_password"); DBUG_ENTER("acl_getroot_no_password");
...@@ -810,13 +811,16 @@ int acl_getroot_no_password(THD *thd) ...@@ -810,13 +811,16 @@ int acl_getroot_no_password(THD *thd)
VOID(pthread_mutex_lock(&acl_cache->lock)); VOID(pthread_mutex_lock(&acl_cache->lock));
thd->master_access= 0;
thd->db_access= 0;
/* /*
Find acl entry in user database. Find acl entry in user database.
This is specially tailored to suit the check we do for CALL of This is specially tailored to suit the check we do for CALL of
a stored procedure; thd->user is set to what is actually a a stored procedure; thd->user is set to what is actually a
priv_user, which can be ''. priv_user, which can be ''.
*/ */
for (uint i=0 ; i < acl_users.elements ; i++) for (i=0 ; i < acl_users.elements ; i++)
{ {
acl_user= dynamic_element(&acl_users,i,ACL_USER*); acl_user= dynamic_element(&acl_users,i,ACL_USER*);
if ((!acl_user->user && (!thd->user || !thd->user[0])) || if ((!acl_user->user && (!thd->user || !thd->user[0])) ||
...@@ -832,6 +836,22 @@ int acl_getroot_no_password(THD *thd) ...@@ -832,6 +836,22 @@ int acl_getroot_no_password(THD *thd)
if (acl_user) if (acl_user)
{ {
for (i=0 ; i < acl_dbs.elements ; i++)
{
ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
if (!acl_db->user ||
(thd->user && thd->user[0] && !strcmp(thd->user, acl_db->user)))
{
if (compare_hostname(&acl_db->host, thd->host, thd->ip))
{
if (!acl_db->db || (thd->db && !strcmp(acl_db->db, thd->db)))
{
thd->db_access= acl_db->access;
break;
}
}
}
}
thd->master_access= acl_user->access; thd->master_access= acl_user->access;
thd->priv_user= acl_user->user ? thd->user : (char *) ""; thd->priv_user= acl_user->user ? thd->user : (char *) "";
......
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