From: Dmitry Lenev Date: January 12 2011 2:22pm Subject: bzr commit into mysql-trunk branch (Dmitry.Lenev:3489) Bug#58499 List-Archive: http://lists.mysql.com/commits/128520 X-Bug: 58499 Message-Id: <20110112142253.7203F7402F1@bandersnatch> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit #At file:///home/dlenev/src/bzr/mysql-trunk-mrg/ based on revid:ole.john.aske@stripped 3489 Dmitry Lenev 2011-01-12 [merge] Merged fix for bug #58499 "DEFINER-security view selecting from INVOKER-security view access check wrong" into mysql-trunk tree. modified: mysql-test/r/view_grant.result mysql-test/t/view_grant.test sql/sql_view.cc === modified file 'mysql-test/r/view_grant.result' --- a/mysql-test/r/view_grant.result 2010-03-20 09:35:40 +0000 +++ b/mysql-test/r/view_grant.result 2011-01-12 14:22:13 +0000 @@ -1248,3 +1248,129 @@ Note 1449 The user specified as a define LOCK TABLES v1 READ; ERROR HY000: The user specified as a definer ('unknown'@'unknown') does not exist DROP VIEW v1; +# +# Bug #58499 "DEFINER-security view selecting from INVOKER-security view +# access check wrong". +# +# Check that we correctly handle privileges for various combinations +# of INVOKER and DEFINER-security views using each other. +DROP DATABASE IF EXISTS mysqltest1; +CREATE DATABASE mysqltest1; +USE mysqltest1; +CREATE TABLE t1 (i INT); +CREATE TABLE t2 (j INT); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +# +# 1) DEFINER-security view uses INVOKER-security view (covers +# scenario originally described in the bug report). +CREATE SQL SECURITY INVOKER VIEW v1_uses_t1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1_uses_t2 AS SELECT * FROM t2; +CREATE USER 'mysqluser1'@'%'; +GRANT CREATE VIEW ON mysqltest1.* TO 'mysqluser1'@'%'; +GRANT SELECT ON t1 TO 'mysqluser1'@'%'; +# To be able create 'v2_uses_t2' we also need select on t2. +GRANT SELECT ON t2 TO 'mysqluser1'@'%'; +GRANT SELECT ON v1_uses_t1 TO 'mysqluser1'@'%'; +GRANT SELECT ON v1_uses_t2 TO 'mysqluser1'@'%'; +# +# Connection 'mysqluser1'. +CREATE SQL SECURITY DEFINER VIEW v2_uses_t1 AS SELECT * FROM v1_uses_t1; +CREATE SQL SECURITY DEFINER VIEW v2_uses_t2 AS SELECT * FROM v1_uses_t2; +# +# Connection 'default'. +CREATE USER 'mysqluser2'@'%'; +GRANT SELECT ON v2_uses_t1 TO 'mysqluser2'@'%'; +GRANT SELECT ON v2_uses_t2 TO 'mysqluser2'@'%'; +GRANT SELECT ON t2 TO 'mysqluser2'@'%'; +GRANT CREATE VIEW ON mysqltest1.* TO 'mysqluser2'@'%'; +# Make 'mysqluser1' unable to access t2. +REVOKE SELECT ON t2 FROM 'mysqluser1'@'%'; +# +# Connection 'mysqluser2'. +# The below statement should succeed thanks to suid nature of v2_uses_t1. +SELECT * FROM v2_uses_t1; +i +1 +# The below statement should fail due to suid nature of v2_uses_t2. +SELECT * FROM v2_uses_t2; +ERROR HY000: View 'mysqltest1.v2_uses_t2' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +# +# 2) INVOKER-security view uses INVOKER-security view. +# +# Connection 'default'. +DROP VIEW v2_uses_t1, v2_uses_t2; +CREATE SQL SECURITY INVOKER VIEW v2_uses_t1 AS SELECT * FROM v1_uses_t1; +CREATE SQL SECURITY INVOKER VIEW v2_uses_t2 AS SELECT * FROM v1_uses_t2; +GRANT SELECT ON v2_uses_t1 TO 'mysqluser1'@'%'; +GRANT SELECT ON v2_uses_t2 TO 'mysqluser1'@'%'; +GRANT SELECT ON v1_uses_t1 TO 'mysqluser2'@'%'; +GRANT SELECT ON v1_uses_t2 TO 'mysqluser2'@'%'; +# +# Connection 'mysqluser1'. +# For both versions of 'v2' 'mysqluser1' privileges should be used. +SELECT * FROM v2_uses_t1; +i +1 +SELECT * FROM v2_uses_t2; +ERROR HY000: View 'mysqltest1.v2_uses_t2' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +# +# Connection 'mysqluser2'. +# And now for both versions of 'v2' 'mysqluser2' privileges should +# be used. +SELECT * FROM v2_uses_t1; +ERROR HY000: View 'mysqltest1.v2_uses_t1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +SELECT * FROM v2_uses_t2; +j +2 +# +# 3) INVOKER-security view uses DEFINER-security view. +# +# Connection 'default'. +DROP VIEW v1_uses_t1, v1_uses_t2; +# To be able create 'v1_uses_t2' we also need select on t2. +GRANT SELECT ON t2 TO 'mysqluser1'@'%'; +# +# Connection 'mysqluser1'. +CREATE SQL SECURITY DEFINER VIEW v1_uses_t1 AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_uses_t2 AS SELECT * FROM t2; +# +# Connection 'default'. +# Make 'mysqluser1' unable to access t2. +REVOKE SELECT ON t2 FROM 'mysqluser1'@'%'; +# +# Connection 'mysqluser2'. +# Due to suid nature of v1_uses_t1 and v1_uses_t2 the first +# select should succeed and the second select should fail. +SELECT * FROM v2_uses_t1; +i +1 +SELECT * FROM v2_uses_t2; +ERROR HY000: View 'mysqltest1.v2_uses_t2' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +# +# 4) DEFINER-security view uses DEFINER-security view. +# +# Connection 'default'. +DROP VIEW v2_uses_t1, v2_uses_t2; +# To be able create 'v2_uses_t2' we also need select on t2. +GRANT SELECT ON t2 TO 'mysqluser1'@'%'; +# +# Connection 'mysqluser2'. +CREATE SQL SECURITY DEFINER VIEW v2_uses_t1 AS SELECT * FROM v1_uses_t1; +CREATE SQL SECURITY DEFINER VIEW v2_uses_t2 AS SELECT * FROM v1_uses_t2; +# +# Connection 'default'. +# Make 'mysqluser1' unable to access t2. +REVOKE SELECT ON t2 FROM 'mysqluser1'@'%'; +# +# Connection 'mysqluser2'. +# Again privileges of creator of innermost views should apply. +SELECT * FROM v2_uses_t1; +i +1 +SELECT * FROM v2_uses_t2; +ERROR HY000: View 'mysqltest1.v2_uses_t2' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +USE test; +DROP DATABASE mysqltest1; +DROP USER 'mysqluser1'@'%'; +DROP USER 'mysqluser2'@'%'; === modified file 'mysql-test/t/view_grant.test' --- a/mysql-test/t/view_grant.test 2010-02-24 13:52:27 +0000 +++ b/mysql-test/t/view_grant.test 2011-01-12 13:28:33 +0000 @@ -1503,8 +1503,6 @@ SHOW CREATE VIEW v1; DROP TABLE t1; DROP VIEW v1; -# Wait till we reached the initial number of concurrent sessions ---source include/wait_until_count_sessions.inc --echo # --echo # Bug #46019: ERROR 1356 When selecting from within another @@ -1546,3 +1544,145 @@ CREATE DEFINER=`unknown`@`unknown` SQL S --error ER_NO_SUCH_USER LOCK TABLES v1 READ; DROP VIEW v1; + + +--echo # +--echo # Bug #58499 "DEFINER-security view selecting from INVOKER-security view +--echo # access check wrong". +--echo # +--echo # Check that we correctly handle privileges for various combinations +--echo # of INVOKER and DEFINER-security views using each other. +--disable_warnings +DROP DATABASE IF EXISTS mysqltest1; +--enable_warnings +CREATE DATABASE mysqltest1; +USE mysqltest1; +CREATE TABLE t1 (i INT); +CREATE TABLE t2 (j INT); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +--echo # +--echo # 1) DEFINER-security view uses INVOKER-security view (covers +--echo # scenario originally described in the bug report). +CREATE SQL SECURITY INVOKER VIEW v1_uses_t1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1_uses_t2 AS SELECT * FROM t2; +CREATE USER 'mysqluser1'@'%'; +GRANT CREATE VIEW ON mysqltest1.* TO 'mysqluser1'@'%'; +GRANT SELECT ON t1 TO 'mysqluser1'@'%'; +--echo # To be able create 'v2_uses_t2' we also need select on t2. +GRANT SELECT ON t2 TO 'mysqluser1'@'%'; +GRANT SELECT ON v1_uses_t1 TO 'mysqluser1'@'%'; +GRANT SELECT ON v1_uses_t2 TO 'mysqluser1'@'%'; +--echo # +--echo # Connection 'mysqluser1'. +--connect (mysqluser1, localhost, mysqluser1,,mysqltest1) +CREATE SQL SECURITY DEFINER VIEW v2_uses_t1 AS SELECT * FROM v1_uses_t1; +CREATE SQL SECURITY DEFINER VIEW v2_uses_t2 AS SELECT * FROM v1_uses_t2; +--echo # +--echo # Connection 'default'. +--connection default +CREATE USER 'mysqluser2'@'%'; +GRANT SELECT ON v2_uses_t1 TO 'mysqluser2'@'%'; +GRANT SELECT ON v2_uses_t2 TO 'mysqluser2'@'%'; +GRANT SELECT ON t2 TO 'mysqluser2'@'%'; +GRANT CREATE VIEW ON mysqltest1.* TO 'mysqluser2'@'%'; +--echo # Make 'mysqluser1' unable to access t2. +REVOKE SELECT ON t2 FROM 'mysqluser1'@'%'; +--echo # +--echo # Connection 'mysqluser2'. +--connect (mysqluser2, localhost, mysqluser2,,mysqltest1) +--echo # The below statement should succeed thanks to suid nature of v2_uses_t1. +SELECT * FROM v2_uses_t1; +--echo # The below statement should fail due to suid nature of v2_uses_t2. +--error ER_VIEW_INVALID +SELECT * FROM v2_uses_t2; +--echo # +--echo # 2) INVOKER-security view uses INVOKER-security view. +--echo # +--echo # Connection 'default'. +--connection default +DROP VIEW v2_uses_t1, v2_uses_t2; +CREATE SQL SECURITY INVOKER VIEW v2_uses_t1 AS SELECT * FROM v1_uses_t1; +CREATE SQL SECURITY INVOKER VIEW v2_uses_t2 AS SELECT * FROM v1_uses_t2; +GRANT SELECT ON v2_uses_t1 TO 'mysqluser1'@'%'; +GRANT SELECT ON v2_uses_t2 TO 'mysqluser1'@'%'; +GRANT SELECT ON v1_uses_t1 TO 'mysqluser2'@'%'; +GRANT SELECT ON v1_uses_t2 TO 'mysqluser2'@'%'; +--echo # +--echo # Connection 'mysqluser1'. +--connection mysqluser1 +--echo # For both versions of 'v2' 'mysqluser1' privileges should be used. +SELECT * FROM v2_uses_t1; +--error ER_VIEW_INVALID +SELECT * FROM v2_uses_t2; +--echo # +--echo # Connection 'mysqluser2'. +--connection mysqluser2 +--echo # And now for both versions of 'v2' 'mysqluser2' privileges should +--echo # be used. +--error ER_VIEW_INVALID +SELECT * FROM v2_uses_t1; +SELECT * FROM v2_uses_t2; +--echo # +--echo # 3) INVOKER-security view uses DEFINER-security view. +--echo # +--echo # Connection 'default'. +--connection default +DROP VIEW v1_uses_t1, v1_uses_t2; +--echo # To be able create 'v1_uses_t2' we also need select on t2. +GRANT SELECT ON t2 TO 'mysqluser1'@'%'; +--echo # +--echo # Connection 'mysqluser1'. +--connection mysqluser1 +CREATE SQL SECURITY DEFINER VIEW v1_uses_t1 AS SELECT * FROM t1; +CREATE SQL SECURITY DEFINER VIEW v1_uses_t2 AS SELECT * FROM t2; +--echo # +--echo # Connection 'default'. +--connection default +--echo # Make 'mysqluser1' unable to access t2. +REVOKE SELECT ON t2 FROM 'mysqluser1'@'%'; +--echo # +--echo # Connection 'mysqluser2'. +--connection mysqluser2 +--echo # Due to suid nature of v1_uses_t1 and v1_uses_t2 the first +--echo # select should succeed and the second select should fail. +SELECT * FROM v2_uses_t1; +--error ER_VIEW_INVALID +SELECT * FROM v2_uses_t2; +--echo # +--echo # 4) DEFINER-security view uses DEFINER-security view. +--echo # +--echo # Connection 'default'. +--connection default +DROP VIEW v2_uses_t1, v2_uses_t2; +--echo # To be able create 'v2_uses_t2' we also need select on t2. +GRANT SELECT ON t2 TO 'mysqluser1'@'%'; +--echo # +--echo # Connection 'mysqluser2'. +--connection mysqluser2 +CREATE SQL SECURITY DEFINER VIEW v2_uses_t1 AS SELECT * FROM v1_uses_t1; +CREATE SQL SECURITY DEFINER VIEW v2_uses_t2 AS SELECT * FROM v1_uses_t2; +--echo # +--echo # Connection 'default'. +--connection default +--echo # Make 'mysqluser1' unable to access t2. +REVOKE SELECT ON t2 FROM 'mysqluser1'@'%'; +--echo # +--echo # Connection 'mysqluser2'. +--connection mysqluser2 +--echo # Again privileges of creator of innermost views should apply. +SELECT * FROM v2_uses_t1; +--error ER_VIEW_INVALID +SELECT * FROM v2_uses_t2; + +--disconnect mysqluser1 +--disconnect mysqluser2 +--connection default +USE test; +DROP DATABASE mysqltest1; +DROP USER 'mysqluser1'@'%'; +DROP USER 'mysqluser2'@'%'; + + +# Wait till we reached the initial number of concurrent sessions +--source include/wait_until_count_sessions.inc === modified file 'sql/sql_view.cc' --- a/sql/sql_view.cc 2010-12-14 11:15:13 +0000 +++ b/sql/sql_view.cc 2011-01-12 14:22:13 +0000 @@ -1269,6 +1269,7 @@ bool mysql_make_view(THD *thd, File_pars TABLE_LIST *view_tables= lex->query_tables; TABLE_LIST *view_tables_tail= 0; TABLE_LIST *tbl; + Security_context *security_ctx; /* Check rights to run commands (EXPLAIN SELECT & SHOW CREATE) which show @@ -1415,26 +1416,39 @@ bool mysql_make_view(THD *thd, File_pars if (table->view_suid) { /* - Prepare a security context to check underlying objects of the view + For suid views prepare a security context for checking underlying + objects of the view. */ 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 */ - if (view_tables) - { - DBUG_ASSERT(view_tables_tail); - for (tbl= view_tables; tbl != view_tables_tail->next_global; - 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; + security_ctx= table->view_sctx; + } + else + { + /* + For non-suid views inherit security context from view's table list. + This allows properly handle situation when non-suid view is used + from within suid view. + */ + security_ctx= table->security_ctx; } + /* Assign the context to the tables referenced in the view */ + if (view_tables) + { + DBUG_ASSERT(view_tables_tail); + for (tbl= view_tables; tbl != view_tables_tail->next_global; + tbl= tbl->next_global) + tbl->security_ctx= security_ctx; + } + + /* 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= security_ctx; + /* Setup an error processor to hide error messages issued by stored routines referenced in the view No bundle (reason: revision is a merge).