From 0c3723e1d50e61303b63f6264c2c193397ee4475 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 25 Oct 2020 18:17:34 +0100 Subject: Bug#31304432 "INSUFFICIENT PRIVILEGE CHECK BY LOCK TABLES" `LOCK TABLES view_name` should require * invoker to have SELECT and LOCK TABLES privileges on the view * either invoker or definer (only if sql security definer) to have SELECT and LOCK TABLES privileges on the used tables/views. --- mysql-test/r/lock_view.result | 216 ++++++++++++++++++++++++++++++++++++++++++ mysql-test/t/lock_view.test | 76 +++++++++++++++ sql/sql_parse.cc | 30 ++++++ 3 files changed, 322 insertions(+) create mode 100644 mysql-test/r/lock_view.result create mode 100644 mysql-test/t/lock_view.test diff --git a/mysql-test/r/lock_view.result b/mysql-test/r/lock_view.result new file mode 100644 index 00000000000..aa76dd73609 --- /dev/null +++ b/mysql-test/r/lock_view.result @@ -0,0 +1,216 @@ +create database mysqltest1; +create database mysqltest2; +create database mysqltest3; +create user invoker@localhost; +create user definer@localhost; +grant select,show view on mysqltest1.* to invoker@localhost; +grant select,show view on mysqltest1.* to definer@localhost; +grant select,show view on mysqltest2.* to invoker@localhost; +grant select,show view on mysqltest2.* to definer@localhost; +grant select,show view on mysqltest3.* to invoker@localhost; +grant select on performance_schema.* to definer@localhost; +create table mysqltest1.t1 (a int); +create definer=definer@localhost view mysqltest2.v2 as select * from mysqltest1.t1; +create definer=definer@localhost view mysqltest3.v3 as select * from mysqltest2.v2; +create definer=definer@localhost view mysqltest3.v3is as select schema_name from information_schema.schemata order by schema_name; +create definer=definer@localhost view mysqltest3.v3ps as select user from performance_schema.users where current_connections>0 order by user; +create definer=definer@localhost view mysqltest3.v3nt as select 1; +create definer=definer@localhost sql security invoker view mysqltest3.v3i as select * from mysqltest1.t1; + +CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqltest1` /*!40100 DEFAULT CHARACTER SET latin1 */; + +USE `mysqltest1`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqltest2` /*!40100 DEFAULT CHARACTER SET latin1 */; + +USE `mysqltest2`; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE TABLE `v2` ( + `a` tinyint NOT NULL +) ENGINE=MyISAM */; +SET character_set_client = @saved_cs_client; + +CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqltest3` /*!40100 DEFAULT CHARACTER SET latin1 */; + +USE `mysqltest3`; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE TABLE `v3` ( + `a` tinyint NOT NULL +) ENGINE=MyISAM */; +SET character_set_client = @saved_cs_client; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE TABLE `v3i` ( + `a` tinyint NOT NULL +) ENGINE=MyISAM */; +SET character_set_client = @saved_cs_client; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE TABLE `v3is` ( + `schema_name` tinyint NOT NULL +) ENGINE=MyISAM */; +SET character_set_client = @saved_cs_client; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE TABLE `v3nt` ( + `1` tinyint NOT NULL +) ENGINE=MyISAM */; +SET character_set_client = @saved_cs_client; +SET @saved_cs_client = @@character_set_client; +SET character_set_client = utf8; +/*!50001 CREATE TABLE `v3ps` ( + `user` tinyint NOT NULL +) ENGINE=MyISAM */; +SET character_set_client = @saved_cs_client; + +USE `mysqltest1`; + +USE `mysqltest2`; +/*!50001 DROP TABLE IF EXISTS `v2`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = latin1 */; +/*!50001 SET character_set_results = latin1 */; +/*!50001 SET collation_connection = latin1_swedish_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`definer`@`localhost` SQL SECURITY DEFINER */ +/*!50001 VIEW `v2` AS select `mysqltest1`.`t1`.`a` AS `a` from `mysqltest1`.`t1` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +USE `mysqltest3`; +/*!50001 DROP TABLE IF EXISTS `v3`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = latin1 */; +/*!50001 SET character_set_results = latin1 */; +/*!50001 SET collation_connection = latin1_swedish_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`definer`@`localhost` SQL SECURITY DEFINER */ +/*!50001 VIEW `v3` AS select `v2`.`a` AS `a` from `mysqltest2`.`v2` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; +/*!50001 DROP TABLE IF EXISTS `v3i`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = latin1 */; +/*!50001 SET character_set_results = latin1 */; +/*!50001 SET collation_connection = latin1_swedish_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`definer`@`localhost` SQL SECURITY INVOKER */ +/*!50001 VIEW `v3i` AS select `mysqltest1`.`t1`.`a` AS `a` from `mysqltest1`.`t1` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; +/*!50001 DROP TABLE IF EXISTS `v3is`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = latin1 */; +/*!50001 SET character_set_results = latin1 */; +/*!50001 SET collation_connection = latin1_swedish_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`definer`@`localhost` SQL SECURITY DEFINER */ +/*!50001 VIEW `v3is` AS select `information_schema`.`schemata`.`SCHEMA_NAME` AS `schema_name` from `information_schema`.`schemata` order by `information_schema`.`schemata`.`SCHEMA_NAME` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; +/*!50001 DROP TABLE IF EXISTS `v3nt`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = latin1 */; +/*!50001 SET character_set_results = latin1 */; +/*!50001 SET collation_connection = latin1_swedish_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`definer`@`localhost` SQL SECURITY DEFINER */ +/*!50001 VIEW `v3nt` AS select 1 AS `1` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; +/*!50001 DROP TABLE IF EXISTS `v3ps`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = latin1 */; +/*!50001 SET character_set_results = latin1 */; +/*!50001 SET collation_connection = latin1_swedish_ci */; +/*!50001 CREATE ALGORITHM=UNDEFINED */ +/*!50013 DEFINER=`definer`@`localhost` SQL SECURITY DEFINER */ +/*!50001 VIEW `v3ps` AS select `performance_schema`.`users`.`USER` AS `user` from `performance_schema`.`users` where (`performance_schema`.`users`.`CURRENT_CONNECTIONS` > 0) order by `performance_schema`.`users`.`USER` */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; +lock table mysqltest3.v3 write; +ERROR 42000: Access denied for user 'invoker'@'localhost' to database 'mysqltest3' +grant lock tables on mysqltest3.* to invoker@localhost; +show create view mysqltest3.v3; +View Create View character_set_client collation_connection +v3 CREATE ALGORITHM=UNDEFINED DEFINER=`definer`@`localhost` SQL SECURITY DEFINER VIEW `mysqltest3`.`v3` AS select `v2`.`a` AS `a` from `mysqltest2`.`v2` latin1 latin1_swedish_ci +show create view mysqltest3.v3is; +View Create View character_set_client collation_connection +v3is CREATE ALGORITHM=UNDEFINED DEFINER=`definer`@`localhost` SQL SECURITY DEFINER VIEW `mysqltest3`.`v3is` AS select `information_schema`.`schemata`.`SCHEMA_NAME` AS `schema_name` from `information_schema`.`schemata` order by `information_schema`.`schemata`.`SCHEMA_NAME` latin1 latin1_swedish_ci +show create view mysqltest3.v3ps; +View Create View character_set_client collation_connection +v3ps CREATE ALGORITHM=UNDEFINED DEFINER=`definer`@`localhost` SQL SECURITY DEFINER VIEW `mysqltest3`.`v3ps` AS select `performance_schema`.`users`.`USER` AS `user` from `performance_schema`.`users` where (`performance_schema`.`users`.`CURRENT_CONNECTIONS` > 0) order by `performance_schema`.`users`.`USER` latin1 latin1_swedish_ci +show create view mysqltest3.v3nt; +View Create View character_set_client collation_connection +v3nt CREATE ALGORITHM=UNDEFINED DEFINER=`definer`@`localhost` SQL SECURITY DEFINER VIEW `mysqltest3`.`v3nt` AS select 1 AS `1` latin1 latin1_swedish_ci +show create view mysqltest3.v3i; +View Create View character_set_client collation_connection +v3i CREATE ALGORITHM=UNDEFINED DEFINER=`definer`@`localhost` SQL SECURITY INVOKER VIEW `mysqltest3`.`v3i` AS select `mysqltest1`.`t1`.`a` AS `a` from `mysqltest1`.`t1` latin1 latin1_swedish_ci +lock table mysqltest3.v3 write; +ERROR HY000: View 'mysqltest3.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +lock table mysqltest3.v3i write; +ERROR HY000: View 'mysqltest3.v3i' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +lock table mysqltest3.v3is write; +select * from mysqltest3.v3is; +schema_name +information_schema +mysqltest1 +mysqltest2 +mysqltest3 +test +lock table mysqltest3.v3ps write; +select * from mysqltest3.v3ps; +user +NULL +invoker +root +lock table mysqltest3.v3nt write; +select * from mysqltest3.v3nt; +1 +1 +grant lock tables on mysqltest2.* to invoker@localhost; +lock table mysqltest3.v3 write; +ERROR HY000: View 'mysqltest3.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +lock table mysqltest3.v3i write; +ERROR HY000: View 'mysqltest3.v3i' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +grant lock tables on mysqltest1.* to definer@localhost; +lock table mysqltest3.v3 write; +select * from mysqltest3.v3; +a +lock table mysqltest3.v3i write; +ERROR HY000: View 'mysqltest3.v3i' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +grant lock tables on mysqltest1.* to invoker@localhost; +lock table mysqltest3.v3i write; +select * from mysqltest3.v3i; +a +drop user invoker@localhost; +drop user definer@localhost; +drop database mysqltest1; +drop database mysqltest2; +drop database mysqltest3; diff --git a/mysql-test/t/lock_view.test b/mysql-test/t/lock_view.test new file mode 100644 index 00000000000..dd8809ab89d --- /dev/null +++ b/mysql-test/t/lock_view.test @@ -0,0 +1,76 @@ +source include/not_embedded.inc; +# +# LOCK TABLES and privileges on views +# +create database mysqltest1; +create database mysqltest2; +create database mysqltest3; +create user invoker@localhost; +create user definer@localhost; +grant select,show view on mysqltest1.* to invoker@localhost; +grant select,show view on mysqltest1.* to definer@localhost; +grant select,show view on mysqltest2.* to invoker@localhost; +grant select,show view on mysqltest2.* to definer@localhost; +grant select,show view on mysqltest3.* to invoker@localhost; +grant select on performance_schema.* to definer@localhost; +create table mysqltest1.t1 (a int); +create definer=definer@localhost view mysqltest2.v2 as select * from mysqltest1.t1; +create definer=definer@localhost view mysqltest3.v3 as select * from mysqltest2.v2; +create definer=definer@localhost view mysqltest3.v3is as select schema_name from information_schema.schemata order by schema_name; +create definer=definer@localhost view mysqltest3.v3ps as select user from performance_schema.users where current_connections>0 order by user; +create definer=definer@localhost view mysqltest3.v3nt as select 1; +create definer=definer@localhost sql security invoker view mysqltest3.v3i as select * from mysqltest1.t1; + +exec $MYSQL_DUMP --compact -B mysqltest1 mysqltest2 mysqltest3; + +connect inv,localhost,invoker; +error ER_DBACCESS_DENIED_ERROR; +lock table mysqltest3.v3 write; +disconnect inv; +connection default; + +grant lock tables on mysqltest3.* to invoker@localhost; +connect inv,localhost,invoker; +show create view mysqltest3.v3; +show create view mysqltest3.v3is; +show create view mysqltest3.v3ps; +show create view mysqltest3.v3nt; +show create view mysqltest3.v3i; +error ER_VIEW_INVALID; +lock table mysqltest3.v3 write; +error ER_VIEW_INVALID; +lock table mysqltest3.v3i write; +lock table mysqltest3.v3is write; select * from mysqltest3.v3is; +lock table mysqltest3.v3ps write; select * from mysqltest3.v3ps; +lock table mysqltest3.v3nt write; select * from mysqltest3.v3nt; +disconnect inv; +connection default; + +grant lock tables on mysqltest2.* to invoker@localhost; +connect inv,localhost,invoker; +error ER_VIEW_INVALID; +lock table mysqltest3.v3 write; +error ER_VIEW_INVALID; +lock table mysqltest3.v3i write; +disconnect inv; +connection default; + +grant lock tables on mysqltest1.* to definer@localhost; +connect inv,localhost,invoker; +lock table mysqltest3.v3 write; select * from mysqltest3.v3; +error ER_VIEW_INVALID; +lock table mysqltest3.v3i write; +disconnect inv; +connection default; + +grant lock tables on mysqltest1.* to invoker@localhost; +connect inv,localhost,invoker; +lock table mysqltest3.v3i write; select * from mysqltest3.v3i; +disconnect inv; +connection default; + +drop user invoker@localhost; +drop user definer@localhost; +drop database mysqltest1; +drop database mysqltest2; +drop database mysqltest3; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index fb7552e5929..bf1dad804e3 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2392,10 +2392,40 @@ static bool lock_tables_open_and_lock_tables(THD *thd, TABLE_LIST *tables) We don't set TABLE_LIST::lock_type in this case as this might result in extra warnings from THD::decide_logging_format() even though binary logging is totally irrelevant for LOCK TABLES. + + Check privileges of view tables here, after views were opened. + Either definer or invoker has to have PRIV_LOCK_TABLES to be able to + lock view and its tables. For mysqldump (that locks views before dumping + their structures) compatibility we allow locking views that select + from I_S or P_S tables, but downrade the lock to TL_READ */ for (table= tables; table; table= table->next_global) + { if (!table->placeholder() && table->table->s->tmp_table) table->table->reginfo.lock_type= TL_WRITE; + else if (table->belong_to_view && + check_single_table_access(thd, PRIV_LOCK_TABLES, table, 1)) + { + if (table->grant.m_internal.m_schema_access) + table->lock_type= TL_READ; + else + { + bool error= true; + if (Security_context *sctx= table->security_ctx) + { + table->security_ctx= 0; + error= check_single_table_access(thd, PRIV_LOCK_TABLES, table, 1); + table->security_ctx= sctx; + } + if (error) + { + my_error(ER_VIEW_INVALID, MYF(0), table->belong_to_view->view_db.str, + table->belong_to_view->view_name.str); + goto err; + } + } + } + } if (lock_tables(thd, tables, counter, 0) || thd->locked_tables_list.init_locked_tables(thd)) -- cgit v1.2.1