support of view underlying tables and SP functions security check added (BUG#9505) (WL#2787)

This commit is contained in:
bell@sanja.is.com.ua 2005-10-28 00:18:23 +03:00
parent bee76b78a2
commit 1b164c7b83
24 changed files with 957 additions and 176 deletions

View file

@ -648,21 +648,21 @@ select table_name from information_schema.views
where table_schema='test';
table_name
Warnings:
Warning 1356 View 'test.v2' references invalid table(s) or column(s) or function(s)
Warning 1356 View 'test.v3' references invalid table(s) or column(s) or function(s)
Warning 1356 View 'test.v2' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
Warning 1356 View 'test.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select table_name from information_schema.views
where table_schema='test';
table_name
Warnings:
Warning 1356 View 'test.v2' references invalid table(s) or column(s) or function(s)
Warning 1356 View 'test.v3' references invalid table(s) or column(s) or function(s)
Warning 1356 View 'test.v2' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
Warning 1356 View 'test.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select column_name from information_schema.columns
where table_schema='test';
column_name
f1
Warnings:
Warning 1356 View 'test.v2' references invalid table(s) or column(s) or function(s)
Warning 1356 View 'test.v3' references invalid table(s) or column(s) or function(s)
Warning 1356 View 'test.v2' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
Warning 1356 View 'test.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select index_name from information_schema.statistics where table_schema='test';
index_name
f1_key

View file

@ -1032,7 +1032,7 @@ a f8()
3 1
drop function f1|
select * from v1|
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s)
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
create function f1() returns int
return (select sum(data) from t1) + (select sum(data) from v1)|
drop function f1|

View file

@ -1,4 +1,5 @@
drop table if exists t1;
drop table if exists t1,t2,v1,v2;
drop view if exists t1,t2,v1,v2;
CREATE TABLE `t1` (
a int not null auto_increment,
`pseudo` varchar(35) character set latin2 NOT NULL default '',

View file

@ -574,10 +574,10 @@ create view v1 as select * from t1;
drop table t1;
create table t1 (col1 char(5),newcol2 char(5));
insert into v1 values('a','aa');
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s)
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
drop table t1;
select * from v1;
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s)
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
drop view v1;
create view v1 (a,a) as select 'a','a';
ERROR 42S21: Duplicate column name 'a'
@ -809,11 +809,11 @@ create table t1 (s1 int);
create view v1 as select x1() from t1;
drop function x1;
select * from v1;
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s)
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
show table status;
Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment
t1 MyISAM 10 Fixed 0 0 0 # 1024 0 NULL # # NULL latin1_swedish_ci NULL
v1 NULL NULL NULL NULL NULL NULL # NULL NULL NULL # # NULL NULL NULL NULL View 'test.v1' references invalid table(s) or column(s) or function(s)
v1 NULL NULL NULL NULL NULL NULL # NULL NULL NULL # # NULL NULL NULL NULL View 'test.v1' references invalid table(s) or column(s) or function(s) or define
drop view v1;
drop table t1;
create view v1 as select 99999999999999999999999999999999999999999999999999999 as col1;
@ -1360,7 +1360,7 @@ test.t1 check status OK
drop table t1;
check table v1;
Table Op Msg_type Msg_text
test.v1 check error View 'test.v1' references invalid table(s) or column(s) or function(s)
test.v1 check error View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
drop view v1;
create table t1 (a int);
create table t2 (a int);
@ -1884,11 +1884,11 @@ CREATE VIEW v6 AS SELECT CONVERT_TZ(col1,'GMT','MET') FROM t2;
DROP TABLE t1;
CHECK TABLE v1, v2, v3, v4, v5, v6;
Table Op Msg_type Msg_text
test.v1 check error View 'test.v1' references invalid table(s) or column(s) or function(s)
test.v1 check error View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
test.v2 check status OK
test.v3 check error View 'test.v3' references invalid table(s) or column(s) or function(s)
test.v3 check error View 'test.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
test.v4 check status OK
test.v5 check error View 'test.v5' references invalid table(s) or column(s) or function(s)
test.v5 check error View 'test.v5' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
test.v6 check status OK
drop view v1, v2, v3, v4, v5, v6;
drop table t2;
@ -1908,11 +1908,11 @@ CREATE VIEW v6 AS SELECT f2() FROM t3;
drop function f1;
CHECK TABLE v1, v2, v3, v4, v5, v6;
Table Op Msg_type Msg_text
test.v1 check error View 'test.v1' references invalid table(s) or column(s) or function(s)
test.v1 check error View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
test.v2 check status OK
test.v3 check error View 'test.v3' references invalid table(s) or column(s) or function(s)
test.v3 check error View 'test.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
test.v4 check status OK
test.v5 check error View 'test.v5' references invalid table(s) or column(s) or function(s)
test.v5 check error View 'test.v5' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
test.v6 check status OK
create function f1 () returns int return (select max(col1) from t1);
DROP TABLE t1;
@ -2154,7 +2154,7 @@ Field Type Null Key Default Extra
f4 char(5) YES NULL
ALTER TABLE t1 CHANGE COLUMN f4 f4x CHAR(5);
DESCRIBE v1;
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s)
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
DROP TABLE t1;
DROP VIEW v1;
create table t1 (f1 char);

View file

@ -305,5 +305,172 @@ create table mysqltest.t1 (a int);
grant all privileges on mysqltest.* to mysqltest_1@localhost;
use mysqltest;
create view v1 as select * from t1;
use test;
revoke all privileges on mysqltest.* from mysqltest_1@localhost;
drop database mysqltest;
create database mysqltest;
create table mysqltest.t1 (a int, b int);
grant select on mysqltest.t1 to mysqltest_1@localhost;
grant create view,select on test.* to mysqltest_1@localhost;
create view v1 as select * from mysqltest.t1;
show create view v1;
View Create View
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`mysqltest_1`@`localhost` SQL SECURITY DEFINER VIEW `test`.`v1` AS select `mysqltest`.`t1`.`a` AS `a`,`mysqltest`.`t1`.`b` AS `b` from `mysqltest`.`t1`
revoke select on mysqltest.t1 from mysqltest_1@localhost;
select * from v1;
ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
grant select on mysqltest.t1 to mysqltest_1@localhost;
select * from v1;
a b
REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
drop view v1;
drop database mysqltest;
create database mysqltest;
use mysqltest;
create table t1 (a int);
insert into t1 values (1);
create table t2 (s1 int);
drop function if exists f2;
create function f2 () returns int begin declare v int; select s1 from t2
into v; return v; end//
create algorithm=TEMPTABLE view v1 as select f2() from t1;
create algorithm=MERGE view v2 as select f2() from t1;
create algorithm=TEMPTABLE SQL SECURITY INVOKER view v3 as select f2() from t1;
create algorithm=MERGE SQL SECURITY INVOKER view v4 as select f2() from t1;
create SQL SECURITY INVOKER view v5 as select * from v4;
grant select on v1 to mysqltest_1@localhost;
grant select on v2 to mysqltest_1@localhost;
grant select on v3 to mysqltest_1@localhost;
grant select on v4 to mysqltest_1@localhost;
grant select on v5 to mysqltest_1@localhost;
use mysqltest;
select * from v1;
f2()
NULL
Warnings:
Warning 1329 No data to FETCH
select * from v2;
f2()
NULL
Warnings:
Warning 1329 No data to FETCH
select * from v3;
ERROR HY000: View 'mysqltest.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select * from v4;
ERROR HY000: View 'mysqltest.v4' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select * from v5;
ERROR HY000: View 'mysqltest.v5' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
use test;
drop view v1, v2, v3, v4, v5;
drop function f2;
drop table t1, t2;
use test;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
drop database mysqltest;
create database mysqltest;
use mysqltest;
create table t1 (a int);
insert into t1 values (1);
create table t2 (s1 int);
drop function if exists f2;
create function f2 () returns int begin declare v int; select s1 from t2
into v; return v; end//
grant select on t1 to mysqltest_1@localhost;
grant execute on function f2 to mysqltest_1@localhost;
grant create view on mysqltest.* to mysqltest_1@localhost;
use mysqltest;
create algorithm=TEMPTABLE view v1 as select f2() from t1;
create algorithm=MERGE view v2 as select f2() from t1;
create algorithm=TEMPTABLE SQL SECURITY INVOKER view v3 as select f2() from t1;
create algorithm=MERGE SQL SECURITY INVOKER view v4 as select f2() from t1;
use test;
create view v5 as select * from v1;
revoke execute on function f2 from mysqltest_1@localhost;
select * from v1;
ERROR HY000: View 'mysqltest.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select * from v2;
ERROR HY000: View 'mysqltest.v2' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select * from v3;
f2()
NULL
Warnings:
Warning 1329 No data to FETCH
select * from v4;
f2()
NULL
Warnings:
Warning 1329 No data to FETCH
select * from v5;
ERROR HY000: View 'mysqltest.v5' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
drop view v1, v2, v3, v4, v5;
drop function f2;
drop table t1, t2;
use test;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
drop database mysqltest;
create database mysqltest;
use mysqltest;
create table t1 (a int);
create table v1 (a int);
insert into t1 values (1);
grant select on t1 to mysqltest_1@localhost;
grant select on v1 to mysqltest_1@localhost;
grant create view on mysqltest.* to mysqltest_1@localhost;
drop table v1;
use mysqltest;
create algorithm=TEMPTABLE view v1 as select *, a as b from t1;
create algorithm=MERGE view v2 as select *, a as b from t1;
create algorithm=TEMPTABLE SQL SECURITY INVOKER view v3 as select *, a as b from t1;
create algorithm=MERGE SQL SECURITY INVOKER view v4 as select *, a as b from t1;
create view v5 as select * from v1;
use test;
revoke select on t1 from mysqltest_1@localhost;
select * from v1;
ERROR HY000: View 'mysqltest.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select * from v2;
ERROR HY000: View 'mysqltest.v2' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select * from v3;
a b
1 1
select * from v4;
a b
1 1
select * from v5;
ERROR HY000: View 'mysqltest.v5' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
drop table t1;
use test;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
drop database mysqltest;
create database mysqltest;
use mysqltest;
create table t1 (a int);
insert into t1 values (1);
create algorithm=TEMPTABLE view v1 as select *, a as b from t1;
create algorithm=MERGE view v2 as select *, a as b from t1;
create algorithm=TEMPTABLE SQL SECURITY INVOKER view v3 as select *, a as b from t1;
create algorithm=MERGE SQL SECURITY INVOKER view v4 as select *, a as b from t1;
create SQL SECURITY INVOKER view v5 as select * from v4;
grant select on v1 to mysqltest_1@localhost;
grant select on v2 to mysqltest_1@localhost;
grant select on v3 to mysqltest_1@localhost;
grant select on v4 to mysqltest_1@localhost;
grant select on v5 to mysqltest_1@localhost;
use mysqltest;
select * from v1;
a b
1 1
select * from v2;
a b
1 1
select * from v3;
ERROR HY000: View 'mysqltest.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select * from v4;
ERROR HY000: View 'mysqltest.v4' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select * from v5;
ERROR HY000: View 'mysqltest.v5' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
use test;
drop view v1, v2, v3, v4, v5;
drop table t1;
use test;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
drop database mysqltest;

View file

@ -1,5 +1,6 @@
--disable_warnings
drop table if exists t1;
drop table if exists t1,t2,v1,v2;
drop view if exists t1,t2,v1,v2;
--enable_warnings
CREATE TABLE `t1` (

View file

@ -401,8 +401,221 @@ grant all privileges on mysqltest.* to mysqltest_1@localhost;
connection user1;
use mysqltest;
create view v1 as select * from t1;
use test;
connection root;
revoke all privileges on mysqltest.* from mysqltest_1@localhost;
drop database mysqltest;
#
# view definer grants revoking
#
connection root;
--disable_warnings
create database mysqltest;
--enable_warnings
create table mysqltest.t1 (a int, b int);
grant select on mysqltest.t1 to mysqltest_1@localhost;
grant create view,select on test.* to mysqltest_1@localhost;
connection user1;
create view v1 as select * from mysqltest.t1;
connection root;
# check view definer information
show create view v1;
revoke select on mysqltest.t1 from mysqltest_1@localhost;
-- error ER_VIEW_INVALID
select * from v1;
grant select on mysqltest.t1 to mysqltest_1@localhost;
select * from v1;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
drop view v1;
drop database mysqltest;
#
# rights on execution of view underlying functiond (BUG#9505)
#
connection root;
--disable_warnings
create database mysqltest;
--enable_warnings
use mysqltest;
create table t1 (a int);
insert into t1 values (1);
create table t2 (s1 int);
--disable_warnings
drop function if exists f2;
--enable_warnings
delimiter //;
create function f2 () returns int begin declare v int; select s1 from t2
into v; return v; end//
delimiter ;//
create algorithm=TEMPTABLE view v1 as select f2() from t1;
create algorithm=MERGE view v2 as select f2() from t1;
create algorithm=TEMPTABLE SQL SECURITY INVOKER view v3 as select f2() from t1;
create algorithm=MERGE SQL SECURITY INVOKER view v4 as select f2() from t1;
create SQL SECURITY INVOKER view v5 as select * from v4;
grant select on v1 to mysqltest_1@localhost;
grant select on v2 to mysqltest_1@localhost;
grant select on v3 to mysqltest_1@localhost;
grant select on v4 to mysqltest_1@localhost;
grant select on v5 to mysqltest_1@localhost;
connection user1;
use mysqltest;
select * from v1;
select * from v2;
-- error ER_VIEW_INVALID
select * from v3;
-- error ER_VIEW_INVALID
select * from v4;
-- error ER_VIEW_INVALID
select * from v5;
use test;
connection root;
drop view v1, v2, v3, v4, v5;
drop function f2;
drop table t1, t2;
use test;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
drop database mysqltest;
#
# revertion of previous test, definer of view lost his/her rights to execute
# function
#
connection root;
--disable_warnings
create database mysqltest;
--enable_warnings
use mysqltest;
create table t1 (a int);
insert into t1 values (1);
create table t2 (s1 int);
--disable_warnings
drop function if exists f2;
--enable_warnings
delimiter //;
create function f2 () returns int begin declare v int; select s1 from t2
into v; return v; end//
delimiter ;//
grant select on t1 to mysqltest_1@localhost;
grant execute on function f2 to mysqltest_1@localhost;
grant create view on mysqltest.* to mysqltest_1@localhost;
connection user1;
use mysqltest;
create algorithm=TEMPTABLE view v1 as select f2() from t1;
create algorithm=MERGE view v2 as select f2() from t1;
create algorithm=TEMPTABLE SQL SECURITY INVOKER view v3 as select f2() from t1;
create algorithm=MERGE SQL SECURITY INVOKER view v4 as select f2() from t1;
use test;
connection root;
create view v5 as select * from v1;
revoke execute on function f2 from mysqltest_1@localhost;
-- error ER_VIEW_INVALID
select * from v1;
-- error ER_VIEW_INVALID
select * from v2;
select * from v3;
select * from v4;
-- error ER_VIEW_INVALID
select * from v5;
drop view v1, v2, v3, v4, v5;
drop function f2;
drop table t1, t2;
use test;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
drop database mysqltest;
#
# definer/invoker rights for columns
#
connection root;
--disable_warnings
create database mysqltest;
--enable_warnings
use mysqltest;
create table t1 (a int);
create table v1 (a int);
insert into t1 values (1);
grant select on t1 to mysqltest_1@localhost;
grant select on v1 to mysqltest_1@localhost;
grant create view on mysqltest.* to mysqltest_1@localhost;
drop table v1;
connection user1;
use mysqltest;
create algorithm=TEMPTABLE view v1 as select *, a as b from t1;
create algorithm=MERGE view v2 as select *, a as b from t1;
create algorithm=TEMPTABLE SQL SECURITY INVOKER view v3 as select *, a as b from t1;
create algorithm=MERGE SQL SECURITY INVOKER view v4 as select *, a as b from t1;
create view v5 as select * from v1;
use test;
connection root;
revoke select on t1 from mysqltest_1@localhost;
-- error ER_VIEW_INVALID
select * from v1;
-- error ER_VIEW_INVALID
select * from v2;
select * from v3;
select * from v4;
-- error ER_VIEW_INVALID
select * from v5;
#drop view v1, v2, v3, v4, v5;
drop table t1;
use test;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
drop database mysqltest;
connection root;
--disable_warnings
create database mysqltest;
--enable_warnings
use mysqltest;
create table t1 (a int);
insert into t1 values (1);
create algorithm=TEMPTABLE view v1 as select *, a as b from t1;
create algorithm=MERGE view v2 as select *, a as b from t1;
create algorithm=TEMPTABLE SQL SECURITY INVOKER view v3 as select *, a as b from t1;
create algorithm=MERGE SQL SECURITY INVOKER view v4 as select *, a as b from t1;
create SQL SECURITY INVOKER view v5 as select * from v4;
grant select on v1 to mysqltest_1@localhost;
grant select on v2 to mysqltest_1@localhost;
grant select on v3 to mysqltest_1@localhost;
grant select on v4 to mysqltest_1@localhost;
grant select on v5 to mysqltest_1@localhost;
connection user1;
use mysqltest;
select * from v1;
select * from v2;
-- error ER_VIEW_INVALID
select * from v3;
-- error ER_VIEW_INVALID
select * from v4;
-- error ER_VIEW_INVALID
select * from v5;
use test;
connection root;
drop view v1, v2, v3, v4, v5;
drop table t1;
use test;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
drop database mysqltest;

View file

@ -3208,8 +3208,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
context->last_name_resolution_table,
reference,
IGNORE_EXCEPT_NON_UNIQUE,
!any_privileges &&
context->check_privileges,
!any_privileges,
TRUE)) ==
not_found_field)
{
@ -3271,9 +3270,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
last_name_resolution_table,
reference,
IGNORE_EXCEPT_NON_UNIQUE,
outer_context->
check_privileges,
TRUE)) !=
TRUE, TRUE)) !=
not_found_field)
{
if (from_field)
@ -3348,7 +3345,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
context->last_name_resolution_table,
reference, REPORT_ALL_ERRORS,
!any_privileges &&
context->check_privileges, TRUE);
TRUE, TRUE);
}
goto error;
}
@ -3423,7 +3420,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
We can leave expression substituted from view for next PS/SP rexecution
(i.e. do not register this substitution for reverting on cleupup()
(register_item_tree_changing())), because this subtree will be
fix_field'ed during setup_tables()->setup_ancestor() (i.e. before
fix_field'ed during setup_tables()->setup_underlying() (i.e. before
all other expressions of query, and references on tables which do
not present in query will not make problems.
@ -4518,8 +4515,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
last_name_resolution_table,
reference,
IGNORE_EXCEPT_NON_UNIQUE,
outer_context->check_privileges,
TRUE);
TRUE, TRUE);
if (! from_field)
goto error;
if (from_field == view_ref_found)
@ -5131,7 +5127,7 @@ void Item_trigger_field::setup_field(THD *thd, TABLE *table)
set field_idx properly.
*/
(void)find_field_in_table(thd, table, field_name, (uint) strlen(field_name),
0, 0, &field_idx);
0, 0, &field_idx, 0);
thd->set_query_id= save_set_query_id;
triggers= table->triggers;
}

View file

@ -294,15 +294,15 @@ struct Name_resolution_context: Sql_alloc
bool resolve_in_select_list;
/*
When FALSE we do not check columns right of resolving items, used to
prevent rights check on underlying tables of view
Security context of this name resolution context. It's used for views
and is non-zero only if the view is defined with SQL SECURITY DEFINER.
*/
bool check_privileges;
Security_context *security_ctx;
Name_resolution_context()
:outer_context(0), table_list(0), select_lex(0),
error_processor_data(0),
check_privileges(TRUE)
security_ctx(0)
{}
void init()

View file

@ -4707,6 +4707,7 @@ Item_func_sp::execute(Field **flp)
if (execute(&it))
{
null_value= 1;
context->process_error(current_thd);
return 1;
}
if (!(f= *flp))
@ -4729,9 +4730,17 @@ Item_func_sp::execute(Item **itp)
THD *thd= current_thd;
int res= -1;
Sub_statement_state statement_state;
Security_context *save_ctx;
Security_context *save_security_ctx= 0, *save_ctx_func;
if (find_and_check_access(thd, EXECUTE_ACL, &save_ctx))
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (context->security_ctx)
{
/* Set view definer security context */
save_security_ctx= thd->security_ctx;
thd->security_ctx= context->security_ctx;
}
#endif
if (find_and_check_access(thd, EXECUTE_ACL, &save_ctx_func))
goto error;
/*
@ -4749,9 +4758,14 @@ Item_func_sp::execute(Item **itp)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_FAILED_ROUTINE_BREAK_BINLOG,
ER(ER_FAILED_ROUTINE_BREAK_BINLOG));
sp_restore_security_context(thd, save_ctx);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
sp_restore_security_context(thd, save_ctx_func);
error:
if (save_security_ctx)
thd->security_ctx= save_security_ctx;
#else
error:
#endif
DBUG_RETURN(res);
}
@ -4932,8 +4946,20 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
bool res;
DBUG_ASSERT(fixed == 0);
res= Item_func::fix_fields(thd, ref);
if (!res)
if (!res && thd->lex->view_prepare_mode)
{
/*
Here we check privileges of the stored routine only during view
creation, in order to validate the view. A runtime check is perfomed
in Item_func_sp::execute(), and this method is not called during
context analysis. We do not need to restore the security context
changed in find_and_check_access because all view structures created
in CREATE VIEW are not used for execution. Notice, that during view
creation we do not infer into stored routine bodies and do not check
privileges of its statements, which would probably be a good idea
especially if the view has SQL SECURITY DEFINER and the used stored
procedure has SQL
*/
Security_context *save_ctx;
if (!(res= find_and_check_access(thd, EXECUTE_ACL, &save_ctx)))
sp_restore_security_context(thd, save_ctx);

View file

@ -679,11 +679,11 @@ int mysql_explain_select(THD *thd, SELECT_LEX *sl, char const *type,
select_result *result);
bool mysql_union(THD *thd, LEX *lex, select_result *result,
SELECT_LEX_UNIT *unit, ulong setup_tables_done_option);
int mysql_handle_derived(LEX *lex, int (*processor)(THD *thd,
LEX *lex,
TABLE_LIST *table));
int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *t);
int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *t);
bool mysql_handle_derived(LEX *lex, bool (*processor)(THD *thd,
LEX *lex,
TABLE_LIST *table));
bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *t);
bool mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *t);
Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
Item ***copy_func, Field **from_field,
bool group, bool modify_item,
@ -793,7 +793,9 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
Field *
find_field_in_table(THD *thd, TABLE *table, const char *name,
uint length, bool check_grants, bool allow_rowid,
uint *cached_field_index_ptr);
uint *cached_field_index_ptr,
Security_context *sctx);
#ifdef HAVE_OPENSSL
#include <openssl/des.h>
struct st_des_keyblock

View file

@ -5210,8 +5210,7 @@ ER_WARN_VIEW_WITHOUT_KEY
rus "Обновляемый view не содержит ключа использованных(ой) в нем таблиц(ы)"
ukr "View, що оновлюеться, не м╕стить повного ключа таблиц╕(ь), що викор╕стана в ньюому"
ER_VIEW_INVALID
eng "View '%-.64s.%-.64s' references invalid table(s) or column(s) or function(s)"
rus "View '%-.64s.%-.64s' ÓÓÙÌÁÅÔÓÑ ÎÁ ÎÅÓÕÝÅÓÔ×ÕÀÝÉÅ ÔÁÂÌÉÃÙ ÉÌÉ ÓÔÏÌÂÃÙ ÉÌÉ ÆÕÎËÃÉÉ"
eng "View '%-.64s.%-.64s' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them"
ER_SP_NO_DROP_SP
eng "Can't drop or alter a %s from within another stored routine"
ER_SP_GOTO_IN_HNDLR

View file

@ -931,6 +931,9 @@ bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
ACL_USER *acl_user= 0;
DBUG_ENTER("acl_getroot_no_password");
DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
(host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
(user ? user : "(NULL)"), (db ? db : "(NULL)")));
sctx->user= user;
sctx->host= host;
sctx->ip= ip;
@ -3509,17 +3512,32 @@ end:
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
uint show_table, uint number, bool no_errors)
{
TABLE_LIST *table;
TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table();
Security_context *sctx= thd->security_ctx;
uint i;
DBUG_ENTER("check_grant");
DBUG_ASSERT(number > 0);
/*
Iterate tables until first prelocking placeholder (if this query do not
have placeholders first_not_own_table is 0)
*/
for (i= 0, table= tables;
table && table != first_not_own_table && i < number;
table= table->next_global, i++)
{
/* Remove SHOW_VIEW_ACL, because it will be checked during making view */
table->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
}
want_access&= ~sctx->master_access;
if (!want_access)
DBUG_RETURN(0); // ok
rw_rdlock(&LOCK_grant);
for (table= tables; table && number--; table= table->next_global)
for (table= tables;
table && number-- && table != first_not_own_table;
table= table->next_global)
{
GRANT_TABLE *grant_table;
if (!(~table->grant.privilege & want_access) ||
@ -3529,8 +3547,16 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
It is subquery in the FROM clause. VIEW set table->derived after
table opening, but this function always called before table opening.
*/
table->grant.want_privilege= 0;
continue; // Already checked
if (!table->referencing_view)
{
/*
If it's a temporary table created for a subquery in the FROM
clause, or an INFORMATION_SCHEMA table, drop the request for
a privilege.
*/
table->grant.want_privilege= 0;
}
continue;
}
if (!(grant_table= table_hash_search(sctx->host, sctx->ip,
table->db, sctx->priv_user,
@ -5839,24 +5865,37 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
const char *db, const char *table)
{
Security_context *sctx= thd->security_ctx;
DBUG_ENTER("fill_effective_table_privileges");
DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
sctx->priv_host, (sctx->ip ? sctx->ip : "(NULL)"),
(sctx->priv_user ? sctx->priv_user : "(NULL)"),
db, table));
/* --skip-grants */
if (!initialized)
{
DBUG_PRINT("info", ("skip grants"));
grant->privilege= ~NO_ACCESS; // everything is allowed
return;
DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
DBUG_VOID_RETURN;
}
/* global privileges */
grant->privilege= sctx->master_access;
if (!sctx->priv_user)
return; // it is slave
{
DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
DBUG_VOID_RETURN; // it is slave
}
/* db privileges */
grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
if (!grant_option)
return;
{
DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
DBUG_VOID_RETURN;
}
/* table privileges */
if (grant->version != grant_version)
@ -5873,6 +5912,8 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
{
grant->privilege|= grant->grant_table->privs;
}
DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
DBUG_VOID_RETURN;
}
#else /* NO_EMBEDDED_ACCESS_CHECKS */

View file

@ -1971,11 +1971,11 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
derived/information schema tables and views possible. Thus "counter"
may be still zero for prelocked statement...
NOTE: The above notes may be out of date. Please wait for psergey to
NOTE: The above notes may be out of date. Please wait for psergey to
document new prelocked behavior.
*/
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
thd->lex->sroutines_list.elements)
{
bool first_no_prelocking, need_prelocking;
@ -2025,7 +2025,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
/* VIEW placeholder */
(*counter)--;
/*
/*
tables->next_global list consists of two parts:
1) Query tables and underlying tables of views.
2) Tables used by all stored routines that this statement invokes on
@ -2677,6 +2677,49 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
Check column rights in given security context
SYNOPSIS
check_grant_column_in_sctx()
thd thread handler
grant grant information structure
db db name
table table name
name column name
length column name length
check_grants need to check grants
sctx 0 or security context
RETURN
FALSE OK
TRUE access denied
*/
static bool check_grant_column_in_sctx(THD *thd, GRANT_INFO *grant,
const char *db, const char *table,
const char *name, uint length,
bool check_grants,
Security_context *sctx)
{
if (!check_grants)
return FALSE;
Security_context *save_security_ctx= 0;
bool res;
if (sctx)
{
save_security_ctx= thd->security_ctx;
thd->security_ctx= sctx;
}
res= check_grant_column(thd, grant, db, table, name, length);
if (save_security_ctx)
thd->security_ctx= save_security_ctx;
return res;
}
#endif
/*
Find a field by name in a view that uses merge algorithm.
@ -2727,11 +2770,11 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list,
*/
DBUG_RETURN(((Item_field*) (field_it.item()))->field);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (check_grants &&
check_grant_column(thd, &table_list->grant,
table_list->view_db.str,
table_list->view_name.str,
name, length))
if (check_grant_column_in_sctx(thd, &table_list->grant,
table_list->view_db.str,
table_list->view_name.str, name, length,
check_grants,
table_list->security_ctx))
DBUG_RETURN(WRONG_GRANT);
#endif
// in PS use own arena or data will be freed after prepare
@ -2900,7 +2943,8 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
Field *
find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
bool check_grants, bool allow_rowid,
uint *cached_field_index_ptr)
uint *cached_field_index_ptr,
Security_context *sctx)
{
Field **field_ptr, *field;
uint cached_field_index= *cached_field_index_ptr;
@ -2909,7 +2953,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
/* We assume here that table->field < NO_CACHED_FIELD_INDEX = UINT_MAX */
if (cached_field_index < table->s->fields &&
!my_strcasecmp(system_charset_info,
!my_strcasecmp(system_charset_info,
table->field[cached_field_index]->field_name, name))
field_ptr= table->field + cached_field_index;
else if (table->s->name_hash.records)
@ -2940,9 +2984,10 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
update_field_dependencies(thd, field, table);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (check_grants && check_grant_column(thd, &table->grant,
table->s->db,
table->s->table_name, name, length))
if (check_grant_column_in_sctx(thd, &table->grant,
table->s->db, table->s->table_name,
name, length,
check_grants, sctx))
field= WRONG_GRANT;
#endif
DBUG_RETURN(field);
@ -3054,7 +3099,8 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
DBUG_ASSERT(table_list->table);
if ((fld= find_field_in_table(thd, table_list->table, name, length,
check_grants_table, allow_rowid,
cached_field_index_ptr)))
cached_field_index_ptr,
table_list->security_ctx)))
*actual_table= table_list;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* check for views with temporary table algorithm */
@ -3188,7 +3234,8 @@ find_field_in_tables(THD *thd, Item_ident *item,
test(table_ref->table->
grant.want_privilege) &&
check_privileges,
1, &(item->cached_field_index));
1, &(item->cached_field_index),
table_ref->security_ctx);
else
found= find_field_in_table_ref(thd, table_ref, name, item->name,
NULL, NULL, length, ref,
@ -4324,8 +4371,12 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables)
{
for (TABLE_LIST *table= tables; table; table= table->next_local)
{
if (table->view && table->effective_algorithm == VIEW_ALGORITHM_MERGE)
list= make_leaves_list(list, table->ancestor);
if (table->merge_underlying_list)
{
DBUG_ASSERT(table->view &&
table->effective_algorithm == VIEW_ALGORITHM_MERGE);
list= make_leaves_list(list, table->merge_underlying_list);
}
else
{
*list= table;
@ -4425,16 +4476,17 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
table_list;
table_list= table_list->next_local)
{
if (table_list->ancestor)
if (table_list->merge_underlying_list)
{
DBUG_ASSERT(table_list->view);
DBUG_ASSERT(table_list->view &&
table_list->effective_algorithm == VIEW_ALGORITHM_MERGE);
Query_arena *arena= thd->stmt_arena, backup;
bool res;
if (arena->is_conventional())
arena= 0; // For easier test
else
thd->set_n_backup_active_arena(arena, &backup);
res= table_list->setup_ancestor(thd);
res= table_list->setup_underlying(thd);
if (arena)
thd->restore_active_arena(arena, &backup);
if (res)

View file

@ -2207,15 +2207,10 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
tables_used->view_db.length + 1,
HA_CACHE_TBL_NONTRANSACT, 0, 0))
DBUG_RETURN(0);
{
TABLE_COUNTER_TYPE inc= register_tables_from_list(tables_used->ancestor,
n + 1,
block_table + 1);
if (!inc)
DBUG_RETURN(0);
n+= inc;
block_table+= inc;
}
/*
We do not need to register view tables here because they are already
present in the global list.
*/
}
else
{
@ -2831,13 +2826,6 @@ static TABLE_COUNTER_TYPE process_and_count_tables(TABLE_LIST *tables_used,
tables_used->view_name.str,
tables_used->view_db.str));
*tables_type|= HA_CACHE_TBL_NONTRANSACT;
{
TABLE_COUNTER_TYPE subcount;
if (!(subcount= process_and_count_tables(tables_used->ancestor,
tables_type)))
DBUG_RETURN(0);
table_count+= subcount;
}
}
else
{

View file

@ -414,8 +414,9 @@ bool mysql_multi_delete_prepare(THD *thd)
if (!(target_tbl->table= target_tbl->correspondent_table->table))
{
DBUG_ASSERT(target_tbl->correspondent_table->view &&
target_tbl->correspondent_table->ancestor &&
target_tbl->correspondent_table->ancestor->next_local);
target_tbl->correspondent_table->merge_underlying_list &&
target_tbl->correspondent_table->merge_underlying_list->
next_local);
my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
target_tbl->correspondent_table->view_db.str,
target_tbl->correspondent_table->view_name.str);

View file

@ -35,14 +35,14 @@
processor procedure of derived table processing
RETURN
0 ok
1 Error and error message given
FALSE OK
TRUE Error
*/
int
mysql_handle_derived(LEX *lex, int (*processor)(THD*, LEX*, TABLE_LIST*))
bool
mysql_handle_derived(LEX *lex, bool (*processor)(THD*, LEX*, TABLE_LIST*))
{
int res= 0;
bool res= FALSE;
if (lex->derived_tables)
{
lex->thd->derived_tables_processing= TRUE;
@ -95,16 +95,16 @@ out:
close_thread_tables()
RETURN
0 ok
1 Error and an error message was given
FALSE OK
TRUE Error
*/
int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
{
SELECT_LEX_UNIT *unit= orig_table_list->derived;
int res= 0;
ulonglong create_options;
DBUG_ENTER("mysql_derived_prepare");
bool res= FALSE;
if (unit)
{
SELECT_LEX *first_select= unit->first_select();
@ -118,7 +118,7 @@ int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
sl->context.outer_context= 0;
if (!(derived_result= new select_union))
DBUG_RETURN(1); // out of memory
DBUG_RETURN(TRUE); // out of memory
// st_select_lex_unit::prepare correctly work for single select
if ((res= unit->prepare(thd, derived_result, 0)))
@ -184,7 +184,10 @@ exit:
table->derived_select_number= first_select->select_number;
table->s->tmp_table= TMP_TABLE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table->grant.privilege= SELECT_ACL;
if (orig_table_list->referencing_view)
table->grant= orig_table_list->grant;
else
table->grant.privilege= SELECT_ACL;
#endif
orig_table_list->db= (char *)"";
orig_table_list->db_length= 0;
@ -195,8 +198,8 @@ exit:
thd->derived_tables= table;
}
}
else if (orig_table_list->ancestor)
orig_table_list->set_ancestor();
else if (orig_table_list->merge_underlying_list)
orig_table_list->set_underlying_merge();
DBUG_RETURN(res);
}
@ -220,15 +223,15 @@ exit:
Due to evaluation of LIMIT clause it can not be used at prepared stage.
RETURN
0 ok
1 Error and an error message was given
FALSE OK
TRUE Error
*/
int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
bool mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
{
TABLE *table= orig_table_list->table;
SELECT_LEX_UNIT *unit= orig_table_list->derived;
int res= 0;
bool res= FALSE;
/*check that table creation pass without problem and it is derived table */
if (table && unit)
@ -271,7 +274,7 @@ int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
there were no derived tables
*/
if (derived_result->flush())
res= 1;
res= TRUE;
if (!lex->describe)
unit->cleanup();

View file

@ -800,6 +800,11 @@ typedef struct st_lex
*/
uint table_count;
uint8 describe;
/*
A flag that indicates what kinds of derived tables are present in the
query (0 if no derived tables, otherwise a combination of flags
DERIVED_SUBQUERY and DERIVED_VIEW.
*/
uint8 derived_tables;
uint8 create_view_algorithm;
uint8 create_view_check;

View file

@ -5026,8 +5026,13 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
{
uint found=0;
ulong found_access=0;
TABLE_LIST *org_tables=tables;
for (; tables; tables= tables->next_global)
TABLE_LIST *org_tables= tables;
TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
/*
Iterate tables until first prelocking placeholder (if this query do not
have placeholders first_not_own_table is 0)
*/
for (; tables && tables != first_not_own_table; tables= tables->next_global)
{
if (tables->schema_table &&
(want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
@ -5038,6 +5043,11 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
information_schema_name.str);
return TRUE;
}
/*
Register access for view underlying table.
Remove SHOW_VIEW_ACL, because it will be checked during making view
*/
tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
if (tables->derived || tables->schema_table || tables->belong_to_view ||
(tables->table && (int)tables->table->s->tmp_table) ||
my_tz_check_n_skip_implicit_tables(&tables,

View file

@ -1158,6 +1158,7 @@ static int mysql_test_update(Prepared_statement *stmt,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table_list->grant.want_privilege= want_privilege;
table_list->table->grant.want_privilege= want_privilege;
table_list->register_want_access(want_privilege);
#endif
thd->lex->select_lex.no_wrap_view_item= TRUE;
res= setup_fields(thd, 0, select->item_list, 1, 0, 0);
@ -1169,6 +1170,7 @@ static int mysql_test_update(Prepared_statement *stmt,
table_list->grant.want_privilege=
table_list->table->grant.want_privilege=
(SELECT_ACL & ~table_list->table->grant.privilege);
table_list->register_want_access(SELECT_ACL);
#endif
if (setup_fields(thd, 0, stmt->lex->value_list, 0, 0, 0))
goto error;

View file

@ -195,6 +195,7 @@ int mysql_update(THD *thd,
/* Check the fields we are going to modify */
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table_list->grant.want_privilege= table->grant.want_privilege= want_privilege;
table_list->register_want_access(want_privilege);
#endif
if (setup_fields_with_no_wrap(thd, 0, fields, 1, 0, 0))
DBUG_RETURN(1); /* purecov: inspected */
@ -584,6 +585,7 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table_list->grant.want_privilege= table->grant.want_privilege=
(SELECT_ACL & ~table->grant.privilege);
table_list->register_want_access(SELECT_ACL);
#endif
bzero((char*) &tables,sizeof(tables)); // For ORDER BY

View file

@ -721,6 +721,7 @@ loop_out:
}
/*
read VIEW .frm and create structures
@ -737,11 +738,26 @@ loop_out:
my_bool
mysql_make_view(File_parser *parser, TABLE_LIST *table)
{
THD *thd= current_thd;
DBUG_ENTER("mysql_make_view");
DBUG_PRINT("info", ("table=%p (%s)", table, table->table_name));
if (table->view)
{
/*
It's an execution of a PS/SP and the view has already been unfolded
into a list of used tables. Now we only need to update the information
about granted privileges in the view tables with the actual data
stored in MySQL privilege system. We don't need to restore the
required privileges (by calling register_want_access) because they has
not changed since PREPARE or the previous execution: the only case
when this information is changed is execution of UPDATE on a view, but
the original want_access is restored in its end.
*/
if (!table->prelocking_placeholder && table->prepare_security(thd))
{
DBUG_RETURN(1);
}
DBUG_PRINT("info",
("VIEW %s.%s is already processed on previous PS/SP execution",
table->view_db.str, table->view_name.str));
@ -749,7 +765,6 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
}
SELECT_LEX *end;
THD *thd= current_thd;
LEX *old_lex= thd->lex, *lex;
SELECT_LEX *view_select;
int res= 0;
@ -768,7 +783,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
if (!table->timestamp.str)
table->timestamp.str= table->timestamp_buffer;
/* prepare default values for old format */
table->view_suid= 1;
table->view_suid= TRUE;
table->definer.user.str= table->definer.host.str= 0;
table->definer.user.length= table->definer.host.length= 0;
@ -879,6 +894,10 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
goto err;
}
if (!(table->view_tables=
(List<TABLE_LIST>*) new(thd->mem_root) List<TABLE_LIST>))
goto err;
/*
mark to avoid temporary table using and put view reference and find
last view table
@ -889,6 +908,22 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
{
tbl->skip_temporary= 1;
tbl->belong_to_view= top_view;
tbl->referencing_view= table;
/*
First we fill want_privilege with SELECT_ACL (this is needed for the
tables which belongs to view subqueries and temporary table views,
then for the merged view underlying tables we will set wanted
privileges of top_view
*/
tbl->grant.want_privilege= SELECT_ACL;
/*
After unfolding the view we lose the list of tables referenced in it
(we will have only a list of underlying tables in case of MERGE
algorithm, which does not include the tables referenced from
subqueries used in view definition).
Let's build a list of all tables referenced in the view.
*/
table->view_tables->push_back(tbl);
}
/*
@ -913,16 +948,6 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
table->next_global= view_tables;
}
/*
Let us set proper lock type for tables of the view's main select
since we may want to perform update or insert on view. This won't
work for view containing union. But this is ok since we don't
allow insert and update on such views anyway.
*/
if (!lex->select_lex.next_select())
for (tbl= lex->select_lex.get_table_list(); tbl; tbl= tbl->next_local)
tbl->lock_type= table->lock_type;
/*
If we are opening this view as part of implicit LOCK TABLES, then
this view serves as simple placeholder and we should not continue
@ -931,7 +956,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
if (table->prelocking_placeholder)
goto ok2;
old_lex->derived_tables|= DERIVED_VIEW;
old_lex->derived_tables|= (DERIVED_VIEW | lex->derived_tables);
/* move SQL_NO_CACHE & Co to whole query */
old_lex->safe_to_cache_query= (old_lex->safe_to_cache_query &&
@ -940,6 +965,37 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
if (view_select->options & OPTION_TO_QUERY_CACHE)
old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE;
if (table->view_suid)
{
/*
Prepare a security context to check underlying objects of the view
*/
Security_context *save_security_ctx= thd->security_ctx;
if (!(table->view_sctx= (Security_context *)
thd->stmt_arena->alloc(sizeof(Security_context))))
goto err;
/* Assign the context to the tables referenced in the view */
for (tbl= view_tables; tbl; tbl= tbl->next_global)
tbl->security_ctx= table->view_sctx;
/* assign security context to SELECT name resolution contexts of view */
for(SELECT_LEX *sl= lex->all_selects_list;
sl;
sl= sl->next_select_in_list())
sl->context.security_ctx= table->view_sctx;
}
/*
Setup an error processor to hide error messages issued by stored
routines referenced in the view
*/
for (SELECT_LEX *sl= lex->all_selects_list;
sl;
sl= sl->next_select_in_list())
{
sl->context.error_processor= &view_error_processor;
sl->context.error_processor_data= (void *)table;
}
/*
check MERGE algorithm ability
- algorithm is not explicit TEMPORARY TABLE
@ -961,24 +1017,28 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
table->updatable= (table->updatable_view != 0);
table->effective_with_check=
old_lex->get_effective_with_check(table);
table->merge_underlying_list= view_tables;
/*
Let us set proper lock type for tables of the view's main select
since we may want to perform update or insert on view. This won't
work for view containing union. But this is ok since we don't
allow insert and update on such views anyway.
Also we fill correct wanted privileges.
*/
for (tbl= table->merge_underlying_list; tbl; tbl= tbl->next_local)
{
tbl->lock_type= table->lock_type;
tbl->grant.want_privilege= top_view->grant.orig_want_privilege;
}
/* prepare view context */
lex->select_lex.context.resolve_in_table_list_only(table->ancestor=
view_tables);
lex->select_lex.context.resolve_in_table_list_only(view_tables);
lex->select_lex.context.outer_context= 0;
lex->select_lex.context.select_lex= table->select_lex;
lex->select_lex.select_n_having_items+=
table->select_lex->select_n_having_items;
/* do not check privileges & hide errors for view underlyings */
for (SELECT_LEX *sl= lex->all_selects_list;
sl;
sl= sl->next_select_in_list())
{
sl->context.check_privileges= FALSE;
sl->context.error_processor= &view_error_processor;
sl->context.error_processor_data= (void *)table;
}
/*
Tables of the main select of the view should be marked as belonging
to the same select as original view (again we can use LEX::select_lex
@ -1011,7 +1071,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
}
}
/* Store WHERE clause for post-processing in setup_ancestor */
/* Store WHERE clause for post-processing in setup_underlying */
table->where= view_select->where;
/*
Add subqueries units to SELECT into which we merging current view.
@ -1077,6 +1137,11 @@ ok2:
if (!old_lex->time_zone_tables_used && thd->lex->time_zone_tables_used)
old_lex->time_zone_tables_used= thd->lex->time_zone_tables_used;
thd->lex= old_lex;
if (!table->prelocking_placeholder && table->prepare_security(thd))
{
DBUG_RETURN(1);
}
DBUG_RETURN(0);
err:

View file

@ -1798,41 +1798,43 @@ void st_table_list::calc_md5(char *buffer)
/*
set ancestor TABLE for table place holder of VIEW
set underlying TABLE for table place holder of VIEW
DESCRIPTION
Replace all views that only uses one table with the table itself.
This allows us to treat the view as a simple table and even update
it
it (it is a kind of optimisation)
SYNOPSIS
st_table_list::set_ancestor()
st_table_list::set_underlying_merge()
*/
void st_table_list::set_ancestor()
void st_table_list::set_underlying_merge()
{
TABLE_LIST *tbl;
if ((tbl= ancestor))
if ((tbl= merge_underlying_list))
{
/* This is a view. Process all tables of view */
DBUG_ASSERT(view);
DBUG_ASSERT(view && effective_algorithm == VIEW_ALGORITHM_MERGE);
do
{
if (tbl->ancestor) // This is a view
if (tbl->merge_underlying_list) // This is a view
{
DBUG_ASSERT(tbl->view &&
tbl->effective_algorithm == VIEW_ALGORITHM_MERGE);
/*
This is the only case where set_ancestor is called on an object
that may not be a view (in which case ancestor is 0)
*/
tbl->ancestor->set_ancestor();
tbl->merge_underlying_list->set_underlying_merge();
}
} while ((tbl= tbl->next_local));
if (!multitable_view)
{
table= ancestor->table;
schema_table= ancestor->schema_table;
table= merge_underlying_list->table;
schema_table= merge_underlying_list->schema_table;
}
}
}
@ -1842,12 +1844,9 @@ void st_table_list::set_ancestor()
setup fields of placeholder of merged VIEW
SYNOPSIS
st_table_list::setup_ancestor()
st_table_list::setup_underlying()
thd - thread handler
NOTES
ancestor is list of tables and views used by view (underlying tables/views)
DESCRIPTION
It is:
- preparing translation table for view columns
@ -1858,10 +1857,11 @@ void st_table_list::set_ancestor()
TRUE - error
*/
bool st_table_list::setup_ancestor(THD *thd)
bool st_table_list::setup_underlying(THD *thd)
{
DBUG_ENTER("st_table_list::setup_ancestor");
if (!field_translation)
DBUG_ENTER("st_table_list::setup_underlying");
if (!field_translation && merge_underlying_list)
{
Field_translator *transl;
SELECT_LEX *select= &view->select_lex;
@ -1875,10 +1875,10 @@ bool st_table_list::setup_ancestor(THD *thd)
DBUG_RETURN(TRUE);
}
for (tbl= ancestor; tbl; tbl= tbl->next_local)
for (tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
if (tbl->ancestor &&
tbl->setup_ancestor(thd))
if (tbl->merge_underlying_list &&
tbl->setup_underlying(thd))
{
DBUG_RETURN(TRUE);
}
@ -1941,7 +1941,7 @@ bool st_table_list::prep_where(THD *thd, Item **conds,
{
DBUG_ENTER("st_table_list::prep_where");
for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
if (tbl->view && tbl->prep_where(thd, conds, no_where_clause))
{
@ -2023,7 +2023,7 @@ bool st_table_list::prep_check_option(THD *thd, uint8 check_opt_type)
{
DBUG_ENTER("st_table_list::prep_check_option");
for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
/* see comment of check_opt_type parameter */
if (tbl->view &&
@ -2046,7 +2046,7 @@ bool st_table_list::prep_check_option(THD *thd, uint8 check_opt_type)
}
if (check_opt_type == VIEW_CHECK_CASCADED)
{
for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
if (tbl->check_option)
item= and_conds(item, tbl->check_option);
@ -2085,16 +2085,21 @@ void st_table_list::hide_view_error(THD *thd)
{
/* Hide "Unknown column" or "Unknown function" error */
if (thd->net.last_errno == ER_BAD_FIELD_ERROR ||
thd->net.last_errno == ER_SP_DOES_NOT_EXIST)
thd->net.last_errno == ER_SP_DOES_NOT_EXIST ||
thd->net.last_errno == ER_PROCACCESS_DENIED_ERROR ||
thd->net.last_errno == ER_COLUMNACCESS_DENIED_ERROR)
{
TABLE_LIST *top= top_table();
thd->clear_error();
my_error(ER_VIEW_INVALID, MYF(0), view_db.str, view_name.str);
my_error(ER_VIEW_INVALID, MYF(0), top->view_db.str, top->view_name.str);
}
else if (thd->net.last_errno == ER_NO_DEFAULT_FOR_FIELD)
{
TABLE_LIST *top= top_table();
thd->clear_error();
// TODO: make correct error message
my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0), view_db.str, view_name.str);
my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0),
top->view_db.str, top->view_name.str);
}
}
@ -2115,10 +2120,10 @@ void st_table_list::hide_view_error(THD *thd)
st_table_list *st_table_list::find_underlying_table(TABLE *table_to_find)
{
/* is this real table and table which we are looking for? */
if (table == table_to_find && ancestor == 0)
if (table == table_to_find && merge_underlying_list == 0)
return this;
for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
TABLE_LIST *result;
if ((result= tbl->find_underlying_table(table_to_find)))
@ -2201,7 +2206,7 @@ int st_table_list::view_check_option(THD *thd, bool ignore_failure)
bool st_table_list::check_single_table(st_table_list **table, table_map map,
st_table_list *view)
{
for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
{
if (tbl->table)
{
@ -2243,8 +2248,8 @@ bool st_table_list::set_insert_values(MEM_ROOT *mem_root)
}
else
{
DBUG_ASSERT(view && ancestor);
for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
DBUG_ASSERT(view && merge_underlying_list);
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
if (tbl->set_insert_values(mem_root))
return TRUE;
}
@ -2390,6 +2395,159 @@ TABLE_LIST *st_table_list::last_leaf_for_name_resolution()
}
/*
Register access mode which we need for underlying tables
SYNOPSIS
register_want_access()
want_access Acess which we require
*/
void st_table_list::register_want_access(ulong want_access)
{
/* Remove SHOW_VIEW_ACL, because it will be checked during making view */
want_access&= ~SHOW_VIEW_ACL;
if (belong_to_view)
{
grant.want_privilege= want_access;
if (table)
table->grant.want_privilege= want_access;
}
for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local)
tbl->register_want_access(want_access);
}
/*
Load security context infoemation for this view
SYNOPSIS
st_table_list::prepare_view_securety_context()
thd [in] thread handler
RETURN
FALSE OK
TRUE Error
*/
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bool st_table_list::prepare_view_securety_context(THD *thd)
{
DBUG_ENTER("st_table_list::prepare_view_securety_context");
DBUG_PRINT("enter", ("table: %s", alias));
DBUG_ASSERT(!prelocking_placeholder && view);
if (view_suid)
{
DBUG_PRINT("info", ("This table is suid view => load contest"));
DBUG_ASSERT(view && view_sctx);
if (acl_getroot_no_password(view_sctx,
definer.user.str,
definer.host.str,
definer.host.str,
thd->db))
{
my_error(ER_NO_SUCH_USER, MYF(0), definer.user.str, definer.host.str);
DBUG_RETURN(TRUE);
}
}
DBUG_RETURN(FALSE);
}
#endif
/*
Find security context of current view
SYNOPSIS
st_table_list::find_view_security_context()
thd [in] thread handler
*/
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *st_table_list::find_view_security_context(THD *thd)
{
Security_context *sctx;
TABLE_LIST *upper_view= this;
DBUG_ENTER("st_table_list::find_view_security_context");
DBUG_ASSERT(view);
while (upper_view && !upper_view->view_suid)
{
DBUG_ASSERT(!upper_view->prelocking_placeholder);
upper_view= upper_view->referencing_view;
}
if (upper_view)
{
DBUG_PRINT("info", ("Securety context of view %s will be used",
upper_view->alias));
sctx= upper_view->view_sctx;
DBUG_ASSERT(sctx);
}
else
{
DBUG_PRINT("info", ("Current global context will be used"));
sctx= thd->security_ctx;
}
DBUG_RETURN(sctx);
}
#endif
/*
Prepare security context and load underlying tables priveleges for view
SYNOPSIS
st_table_list::prepare_security()
thd [in] thread handler
RETURN
FALSE OK
TRUE Error
*/
bool st_table_list::prepare_security(THD *thd)
{
List_iterator_fast<TABLE_LIST> tb(*view_tables);
TABLE_LIST *tbl;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *save_security_ctx= thd->security_ctx;
DBUG_ENTER("st_table_list::prepare_security");
DBUG_ASSERT(!prelocking_placeholder);
if (prepare_view_securety_context(thd))
{
DBUG_RETURN(TRUE);
}
thd->security_ctx= find_view_security_context(thd);
while ((tbl= tb++))
{
DBUG_ASSERT(tbl->referencing_view);
char *db, *table_name;
if (tbl->view)
{
db= tbl->view_db.str;
table_name= tbl->view_name.str;
}
else
{
db= tbl->db;
table_name= tbl->table_name;
}
fill_effective_table_privileges(thd, &tbl->grant, db, table_name);
if (tbl->table)
tbl->table->grant= grant;
}
thd->security_ctx= save_security_ctx;
DBUG_RETURN(FALSE);
#else
while ((tbl= tb++))
tbl->grant.privilege= ~NO_ACCESS;
#endif
}
Natural_join_column::Natural_join_column(Field_translator *field_param,
TABLE_LIST *tab)
{
@ -2498,6 +2656,9 @@ Natural_join_column::check_grants(THD *thd, const char *name, uint length)
GRANT_INFO *grant;
const char *db_name;
const char *table_name;
Security_context *save_security_ctx= 0;
Security_context *new_sctx= table_ref->security_ctx;
bool res;
if (view_field)
{
@ -2514,7 +2675,15 @@ Natural_join_column::check_grants(THD *thd, const char *name, uint length)
table_name= table_ref->table->s->table_name;
}
return check_grant_column(thd, grant, db_name, table_name, name, length);
if (new_sctx)
{
save_security_ctx= thd->security_ctx;
thd->security_ctx= new_sctx;
}
res= check_grant_column(thd, grant, db_name, table_name, name, length);
if (save_security_ctx)
thd->security_ctx= save_security_ctx;
return res;
}
#endif

View file

@ -22,6 +22,7 @@ class GRANT_TABLE;
class st_select_lex_unit;
class st_select_lex;
class COND_EQUAL;
class Security_context;
/* Order clause list element */
@ -47,6 +48,11 @@ typedef struct st_grant_info
uint version;
ulong privilege;
ulong want_privilege;
/*
Stores the requested access acl of top level tables list. Is used to
check access rights to the underlying tables of a view.
*/
ulong orig_want_privilege;
} GRANT_INFO;
enum tmp_table_type {NO_TMP_TABLE=0, TMP_TABLE=1, TRANSACTIONAL_TMP_TABLE=2,
@ -359,7 +365,6 @@ typedef struct st_schema_table
#define VIEW_CHECK_SKIP 2
struct st_lex;
struct st_table_list;
class select_union;
class TMP_TABLE_PARAM;
@ -525,10 +530,35 @@ typedef struct st_table_list
Field_translator *field_translation; /* array of VIEW fields */
/* pointer to element after last one in translation table above */
Field_translator *field_translation_end;
/* list of ancestor(s) of this table (underlying table(s)/view(s) */
st_table_list *ancestor;
/*
List (based on next_local) of underlying tables of this view. I.e. it
does not include the tables of subqueries used in the view. Is set only
for merged views.
*/
st_table_list *merge_underlying_list;
/*
- 0 for base tables
- in case of the view it is the list of all (not only underlying
tables but also used in subquery ones) tables of the view.
*/
List<st_table_list> *view_tables;
/* most upper view this table belongs to */
st_table_list *belong_to_view;
/*
The view directly referencing this table
(non-zero only for merged underlying tables of a view).
*/
st_table_list *referencing_view;
/*
security context (non-zero only for tables which belong
to view with SQL SEURITY DEFINER)
*/
Security_context *security_ctx;
/*
this view security context (non-zero only for views with
SQL SEURITY DEFINER)
*/
Security_context *view_sctx;
/*
List of all base tables local to a subquery including all view
tables. Unlike 'next_local', this in this list views are *not*
@ -595,9 +625,9 @@ typedef struct st_table_list
bool prelocking_placeholder;
void calc_md5(char *buffer);
void set_ancestor();
void set_underlying_merge();
int view_check_option(THD *thd, bool ignore_failure);
bool setup_ancestor(THD *thd);
bool setup_underlying(THD *thd);
void cleanup_items();
bool placeholder() {return derived || view; }
void print(THD *thd, String *str);
@ -625,6 +655,14 @@ typedef struct st_table_list
return prep_where(thd, conds, no_where_clause);
return FALSE;
}
void register_want_access(ulong want_access);
bool prepare_security(THD *thd);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *find_view_security_context(THD *thd);
bool prepare_view_securety_context(THD *thd);
#endif
private:
bool prep_check_option(THD *thd, uint8 check_opt_type);
bool prep_where(THD *thd, Item **conds, bool no_where_clause);