diff options
author | Konstantin Osipov <kostja@sun.com> | 2009-10-22 12:46:07 +0400 |
---|---|---|
committer | Konstantin Osipov <kostja@sun.com> | 2009-10-22 12:46:07 +0400 |
commit | bd83ad899336c0e4a2212d6f05285c5b94e9f912 (patch) | |
tree | a95283cd66786801d4ce585e3240e9280e2fd10c | |
parent | 8ec23470f14323bb85220bae42d154681a4be7f3 (diff) | |
parent | 481066db52cfaa406ea73812bc237513e0daa076 (diff) | |
download | mariadb-git-bd83ad899336c0e4a2212d6f05285c5b94e9f912.tar.gz |
Merge with next-mr-runtime.
-rw-r--r-- | .bzrignore | 1 | ||||
-rw-r--r-- | client/mysqltest.cc | 19 | ||||
-rw-r--r-- | include/my_base.h | 12 | ||||
-rw-r--r-- | mysql-test/r/grant.result | 4 | ||||
-rw-r--r-- | mysql-test/r/grant2.result | 2 | ||||
-rw-r--r-- | mysql-test/r/grant4.result | 123 | ||||
-rw-r--r-- | mysql-test/r/information_schema_db.result | 14 | ||||
-rw-r--r-- | mysql-test/r/outfile.result | bin | 2149 -> 2137 bytes | |||
-rw-r--r-- | mysql-test/r/sp.result | 15 | ||||
-rw-r--r-- | mysql-test/r/view_grant.result | 8 | ||||
-rw-r--r-- | mysql-test/t/grant4.test | 146 | ||||
-rw-r--r-- | mysql-test/t/information_schema_db.test | 12 | ||||
-rw-r--r-- | mysql-test/t/outfile.test | 2 | ||||
-rw-r--r-- | mysql-test/t/sp.test | 28 | ||||
-rw-r--r-- | mysql-test/t/view_grant.test | 8 | ||||
-rw-r--r-- | sql/item_sum.cc | 8 | ||||
-rw-r--r-- | sql/mysql_priv.h | 14 | ||||
-rw-r--r-- | sql/sp_head.cc | 5 | ||||
-rw-r--r-- | sql/sql_acl.cc | 165 | ||||
-rw-r--r-- | sql/sql_acl.h | 7 | ||||
-rw-r--r-- | sql/sql_base.cc | 2 | ||||
-rw-r--r-- | sql/sql_cache.cc | 2 | ||||
-rw-r--r-- | sql/sql_parse.cc | 433 | ||||
-rw-r--r-- | sql/sql_plugin.cc | 2 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 13 | ||||
-rw-r--r-- | sql/sql_show.cc | 14 | ||||
-rw-r--r-- | sql/sql_trigger.cc | 2 | ||||
-rw-r--r-- | sql/sql_update.cc | 2 | ||||
-rw-r--r-- | sql/sql_view.cc | 13 |
29 files changed, 833 insertions, 243 deletions
diff --git a/.bzrignore b/.bzrignore index 0dd59e5cee8..49cef565bfc 100644 --- a/.bzrignore +++ b/.bzrignore @@ -3065,3 +3065,4 @@ sql/share/swedish sql/share/ukrainian libmysqld/examples/mysqltest.cc libmysqld/sql_signal.cc +libmysqld/rpl_handler.cc diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 4377f944e78..ed2613eaef0 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -112,6 +112,8 @@ static uint my_end_arg= 0; /* Number of lines of the result to include in failure report */ static uint opt_tail_lines= 0; +static uint opt_connect_timeout= 0; + static char delimiter[MAX_DELIMITER_LENGTH]= ";"; static uint delimiter_length= 1; @@ -4964,6 +4966,11 @@ void do_connect(struct st_command *command) #endif if (!mysql_init(&con_slot->mysql)) die("Failed on mysql_init()"); + + if (opt_connect_timeout) + mysql_options(&con_slot->mysql, MYSQL_OPT_CONNECT_TIMEOUT, + (void *) &opt_connect_timeout); + if (opt_compress || con_compress) mysql_options(&con_slot->mysql, MYSQL_OPT_COMPRESS, NullS); mysql_options(&con_slot->mysql, MYSQL_OPT_LOCAL_INFILE, 0); @@ -5719,6 +5726,11 @@ static struct my_option my_long_options[] = {"view-protocol", OPT_VIEW_PROTOCOL, "Use views for select", (uchar**) &view_protocol, (uchar**) &view_protocol, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"connect_timeout", OPT_CONNECT_TIMEOUT, + "Number of seconds before connection timeout.", + (uchar**) &opt_connect_timeout, + (uchar**) &opt_connect_timeout, 0, GET_UINT, REQUIRED_ARG, + 120, 0, 3600 * 12, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; @@ -6941,6 +6953,10 @@ int util_query(MYSQL* org_mysql, const char* query){ if (!(mysql= mysql_init(mysql))) die("Failed in mysql_init()"); + if (opt_connect_timeout) + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, + (void *) &opt_connect_timeout); + /* enable local infile, in non-binary builds often disabled by default */ mysql_options(mysql, MYSQL_OPT_LOCAL_INFILE, 0); safe_connect(mysql, "util", org_mysql->host, org_mysql->user, @@ -7600,6 +7616,9 @@ int main(int argc, char **argv) st_connection *con= connections; if (!( mysql_init(&con->mysql))) die("Failed in mysql_init()"); + if (opt_connect_timeout) + mysql_options(&con->mysql, MYSQL_OPT_CONNECT_TIMEOUT, + (void *) &opt_connect_timeout); if (opt_compress) mysql_options(&con->mysql,MYSQL_OPT_COMPRESS,NullS); mysql_options(&con->mysql, MYSQL_OPT_LOCAL_INFILE, 0); diff --git a/include/my_base.h b/include/my_base.h index a01b2ec9b82..70bd9b5e073 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -255,7 +255,17 @@ enum ha_base_keytype { HA_BINARY_PACK_KEY | HA_FULLTEXT | HA_UNIQUE_CHECK | \ HA_SPATIAL | HA_NULL_ARE_EQUAL | HA_GENERATED_KEY) -#define HA_KEY_HAS_PART_KEY_SEG 65536 /* Key contains partial segments */ +/* + Key contains partial segments. + + This flag is internal to the MySQL server by design. It is not supposed + neither to be saved in FRM-files, nor to be passed to storage engines. + It is intended to pass information into internal static sort_keys(KEY *, + KEY *) function. + + This flag can be calculated -- it's based on key lengths comparison. +*/ +#define HA_KEY_HAS_PART_KEY_SEG 65536 /* Automatic bits in key-flag */ diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index 1c51ce689be..0fe729407be 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -915,13 +915,13 @@ SHOW CREATE VIEW mysqltest2.v_ny; View Create View character_set_client collation_connection v_ny CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `mysqltest2`.`v_ny` AS select `mysqltest2`.`t_nn`.`c1` AS `c1` from `mysqltest2`.`t_nn` latin1 latin1_swedish_ci SHOW CREATE TABLE mysqltest3.t_nn; -ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for table 't_nn' +ERROR 42000: SHOW command denied to user 'mysqltest_1'@'localhost' for table 't_nn' SHOW CREATE VIEW mysqltest3.t_nn; ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for table 't_nn' SHOW CREATE VIEW mysqltest3.v_nn; ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for table 'v_nn' SHOW CREATE TABLE mysqltest3.v_nn; -ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for table 'v_nn' +ERROR 42000: SHOW command denied to user 'mysqltest_1'@'localhost' for table 'v_nn' SHOW CREATE TABLE mysqltest2.t_nn; Table Create Table t_nn CREATE TABLE `t_nn` ( diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result index 7c2023127f0..1f69ab6c2cf 100644 --- a/mysql-test/r/grant2.result +++ b/mysql-test/r/grant2.result @@ -391,7 +391,7 @@ grant all on mysqltest_1.* to mysqltest_u1@localhost; use mysqltest_2; create table t1 (i int); show create table mysqltest_2.t1; -ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't1' +ERROR 42000: SHOW command denied to user 'mysqltest_u1'@'localhost' for table 't1' create table t1 like mysqltest_2.t1; ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't1' grant select on mysqltest_2.t1 to mysqltest_u1@localhost; diff --git a/mysql-test/r/grant4.result b/mysql-test/r/grant4.result new file mode 100644 index 00000000000..adfe53b659d --- /dev/null +++ b/mysql-test/r/grant4.result @@ -0,0 +1,123 @@ +drop database if exists mysqltest_db1; +create database mysqltest_db1; +use mysqltest_db1; +create table t_column_priv_only (a int, b int); +create table t_select_priv like t_column_priv_only; +create table t_no_priv like t_column_priv_only; +grant all privileges on test.* to mysqltest_u1@localhost; +grant insert (a) on mysqltest_db1.t_column_priv_only to mysqltest_u1@localhost; +grant select on mysqltest_db1.t_select_priv to mysqltest_u1@localhost; +** Connect as restricted user mysqltest_u1. + +** Test column level privileges only. No SELECT privileges on the table. +** INSERT INTO ... VALUES ... +** Attempting to insert values to a table with only column privileges +** should work. +insert into mysqltest_db1.t_column_priv_only (a) VALUES (1); + +** SHOW COLUMNS +** Should succeed because we have privileges (any) on at least one of the columns. +select column_name as 'Field',column_type as 'Type',is_nullable as 'Null',column_key as 'Key',column_default as 'Default',extra as 'Extra' from information_schema.columns where table_schema='mysqltest_db1' and table_name='t_column_priv_only'; +Field Type Null Key Default Extra +a int(11) YES NULL +show columns from mysqltest_db1.t_column_priv_only; +Field Type Null Key Default Extra +a int(11) YES NULL +** SHOW COLUMNS +** Should fail because there are no privileges on any column combination. +show columns from mysqltest_db1.t_no_priv; +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't_no_priv' +** However, select from I_S.COLUMNS will succeed but not show anything: +select column_name as 'Field',column_type as 'Type',is_nullable as 'Null',column_key as 'Key',column_default as 'Default',extra as 'Extra' from information_schema.columns where table_schema='mysqltest_db1' and table_name='t_no_priv'; +Field Type Null Key Default Extra + +** CREATE TABLE ... LIKE ... require SELECT privleges and will fail. +create table test.t_no_priv like mysqltest_db1.column_priv_only; +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 'column_priv_only' + +** Just to be sure... SELECT also fails. +select * from mysqltest_db1.t_column_priv_only; +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't_column_priv_only' + +** SHOW CREATE TABLE ... require any privileges on all columns (the entire table). +** First we try and fail on a table with only one column privilege. +show create table mysqltest_db1.t_column_priv_only; +ERROR 42000: SHOW command denied to user 'mysqltest_u1'@'localhost' for table 't_column_priv_only' + +** Now we do the same on a table with SELECT privileges. + +** SHOW COLUMNS +** Success because we got some privileges on the table (SELECT_ACL) +show columns from mysqltest_db1.t_select_priv; +Field Type Null Key Default Extra +a int(11) YES NULL +b int(11) YES NULL + +** CREATE TABLE ... LIKE ... require SELECT privleges and will SUCCEED. +drop table if exists test.t_duplicated; +create table test.t_duplicated like mysqltest_db1.t_select_priv; +drop table test.t_duplicated; + +** SHOW CREATE TABLE will succeed because we have a privilege on all columns in the table (table-level privilege). +show create table mysqltest_db1.t_select_priv; +Table Create Table +t_select_priv CREATE TABLE `t_select_priv` ( + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 + +** SHOW CREATE TABLE will fail if there is no grants at all: +show create table mysqltest_db1.t_no_priv; +ERROR 42000: SHOW command denied to user 'mysqltest_u1'@'localhost' for table 't_no_priv' + +use mysqltest_db1; +CREATE TABLE t5 (s1 INT); +CREATE INDEX i ON t5 (s1); +CREATE TABLE t6 (s1 INT, s2 INT); +CREATE VIEW v5 AS SELECT * FROM t5; +CREATE VIEW v6 AS SELECT * FROM t6; +CREATE VIEW v2 AS SELECT * FROM t_select_priv; +CREATE VIEW v3 AS SELECT * FROM t_select_priv; +CREATE INDEX i ON t6 (s1); +GRANT UPDATE (s2) ON t6 to mysqltest_u1@localhost; +GRANT UPDATE (s2) ON v6 to mysqltest_u1@localhost; +GRANT SHOW VIEW ON v2 to mysqltest_u1@localhost; +GRANT SHOW VIEW, SELECT ON v3 to mysqltest_u1@localhost; +use mysqltest_db1; +** Connect as restricted user mysqltest_u1. +** SELECT FROM INFORMATION_SCHEMA.STATISTICS will succeed because any privileges will do (authentication is enough). +SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE table_name='t5'; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME NON_UNIQUE INDEX_SCHEMA INDEX_NAME SEQ_IN_INDEX COLUMN_NAME COLLATION CARDINALITY SUB_PART PACKED NULLABLE INDEX_TYPE COMMENT +NULL mysqltest_db1 t5 1 mysqltest_db1 i 1 s1 A NULL NULL NULL YES BTREE +** SHOW INDEX FROM t5 will fail because we don't have any privileges on any column combination. +SHOW INDEX FROM t5; +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't5' +** SHOW INDEX FROM t6 will succeed because there exist a privilege on a column combination on t6. +SHOW INDEX FROM t6; +Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment +t6 1 i 1 s1 A NULL NULL NULL YES BTREE +** CHECK TABLE requires any privilege on any column combination and should succeed for t6: +CHECK TABLE t6; +Table Op Msg_type Msg_text +mysqltest_db1.t6 check status OK +** With no privileges access is naturally denied: +CHECK TABLE t5; +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't5' +** CHECKSUM TABLE requires SELECT privileges on the table. The following should fail: +CHECKSUM TABLE t6; +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't6' +** And this should work: +CHECKSUM TABLE t_select_priv; +Table Checksum +mysqltest_db1.t_select_priv 0 +SHOW CREATE VIEW v5; +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 'v5' +SHOW CREATE VIEW v6; +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 'v6' +SHOW CREATE VIEW v2; +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 'v2' +SHOW CREATE VIEW v3; +View Create View character_set_client collation_connection +v3 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v3` AS select `t_select_priv`.`a` AS `a`,`t_select_priv`.`b` AS `b` from `t_select_priv` latin1 latin1_swedish_ci +drop database mysqltest_db1; +drop user mysqltest_u1@localhost; diff --git a/mysql-test/r/information_schema_db.result b/mysql-test/r/information_schema_db.result index 756c010843a..4830543008d 100644 --- a/mysql-test/r/information_schema_db.result +++ b/mysql-test/r/information_schema_db.result @@ -3,7 +3,7 @@ drop view if exists v1,v2; drop function if exists f1; drop function if exists f2; use INFORMATION_SCHEMA; -show tables where Tables_in_information_schema not like "Innodb%"; +show tables where Tables_in_information_schema NOT LIKE 'Innodb%'; Tables_in_information_schema CHARACTER_SETS COLLATIONS @@ -119,12 +119,12 @@ create table t1 (f1 char(4)); create view v1 as select f1 from t1; grant insert on v1 to testdb_2@localhost; create view v5 as select f1 from t1; -grant show view on v5 to testdb_2@localhost; +grant select, show view on v5 to testdb_2@localhost; create definer=`no_such_user`@`no_such_host` view v6 as select f1 from t1; ERROR 42000: Access denied; you need the SUPER privilege for this operation use testdb_1; create view v6 as select f1 from t1; -grant show view on v6 to testdb_2@localhost; +grant select, show view on v6 to testdb_2@localhost; create table t2 (f1 char(4)); create definer=`no_such_user`@`no_such_host` view v7 as select * from t2; Warnings: @@ -152,11 +152,13 @@ create view v2 as select f1 from testdb_1.v1; create view v4 as select f1,f2 from testdb_1.v3; show fields from testdb_1.v5; Field Type Null Key Default Extra +f1 char(4) YES NULL show create view testdb_1.v5; View Create View character_set_client collation_connection v5 CREATE ALGORITHM=UNDEFINED DEFINER=`testdb_1`@`localhost` SQL SECURITY DEFINER VIEW `testdb_1`.`v5` AS select `testdb_1`.`t1`.`f1` AS `f1` from `testdb_1`.`t1` latin1 latin1_swedish_ci show fields from testdb_1.v6; Field Type Null Key Default Extra +f1 char(4) YES NULL show create view testdb_1.v6; View Create View character_set_client collation_connection v6 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `testdb_1`.`v6` AS select `testdb_1`.`t1`.`f1` AS `f1` from `testdb_1`.`t1` latin1 latin1_swedish_ci @@ -171,9 +173,9 @@ v7 CREATE ALGORITHM=UNDEFINED DEFINER=`no_such_user`@`no_such_host` SQL SECURITY Warnings: Warning 1356 View 'testdb_1.v7' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them revoke insert(f1) on v3 from testdb_2@localhost; -revoke show view on v5 from testdb_2@localhost; +revoke select,show view on v5 from testdb_2@localhost; use testdb_1; -revoke show view on v6 from testdb_2@localhost; +revoke select,show view on v6 from testdb_2@localhost; show fields from testdb_1.v5; ERROR 42000: SELECT command denied to user 'testdb_2'@'localhost' for table 'v5' show create view testdb_1.v5; @@ -202,7 +204,7 @@ show create view v2; View Create View character_set_client collation_connection v2 CREATE ALGORITHM=UNDEFINED DEFINER=`testdb_2`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select `v1`.`f1` AS `f1` from `testdb_1`.`v1` latin1 latin1_swedish_ci show create view testdb_1.v1; -ERROR 42000: SHOW VIEW command denied to user 'testdb_2'@'localhost' for table 'v1' +ERROR 42000: SELECT command denied to user 'testdb_2'@'localhost' for table 'v1' select table_name from information_schema.columns a where a.table_name = 'v2'; table_name diff --git a/mysql-test/r/outfile.result b/mysql-test/r/outfile.result Binary files differindex e5f5a53c1f3..84ef17857f4 100644 --- a/mysql-test/r/outfile.result +++ b/mysql-test/r/outfile.result diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index b8e2b4cfe58..2105c5ce725 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -6933,6 +6933,21 @@ DROP TABLE t1, t2; # ------------------------------------------------------------------ # -- End of 5.1 tests # ------------------------------------------------------------------ +DROP FUNCTION IF EXISTS f1; +DROP TABLE IF EXISTS t_non_existing; +DROP TABLE IF EXISTS t1; +CREATE FUNCTION f1() RETURNS INT +BEGIN +DECLARE v INT; +SELECT a INTO v FROM t_non_existing; +RETURN 1; +END| +CREATE TABLE t1 (a INT) ENGINE = myisam; +INSERT INTO t1 VALUES (1); +SELECT * FROM t1 WHERE a = f1(); +ERROR 42S02: Table 'test.t_non_existing' doesn't exist +DROP FUNCTION f1; +DROP TABLE t1; # # Bug#34197: CREATE PROCEDURE fails when COMMENT truncated in non # strict SQL mode diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index 7e280fa2fe5..0db95a9a6f7 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -799,12 +799,12 @@ CREATE USER u29908_1@localhost; CREATE DEFINER = u29908_1@localhost VIEW v1 AS SELECT f1 FROM t1; CREATE DEFINER = u29908_1@localhost SQL SECURITY INVOKER VIEW v2 AS SELECT f1 FROM t1; -GRANT DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v1 TO u29908_1@localhost; -GRANT DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_1@localhost; +GRANT SELECT, DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v1 TO u29908_1@localhost; +GRANT SELECT, DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_1@localhost; GRANT SELECT ON mysqltest_29908.t1 TO u29908_1@localhost; CREATE USER u29908_2@localhost; -GRANT DROP, CREATE VIEW ON mysqltest_29908.v1 TO u29908_2@localhost; -GRANT DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_2@localhost; +GRANT SELECT, DROP, CREATE VIEW ON mysqltest_29908.v1 TO u29908_2@localhost; +GRANT SELECT, DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_2@localhost; GRANT SELECT ON mysqltest_29908.t1 TO u29908_2@localhost; ALTER VIEW v1 AS SELECT f2 FROM t1; ERROR 42000: Access denied; you need the SUPER privilege for this operation diff --git a/mysql-test/t/grant4.test b/mysql-test/t/grant4.test new file mode 100644 index 00000000000..f3e551cd623 --- /dev/null +++ b/mysql-test/t/grant4.test @@ -0,0 +1,146 @@ +--source include/not_embedded.inc + +# Setup database, tables and user accounts +--disable_warnings +drop database if exists mysqltest_db1; +--enable_warnings +create database mysqltest_db1; +use mysqltest_db1; +create table t_column_priv_only (a int, b int); +create table t_select_priv like t_column_priv_only; +create table t_no_priv like t_column_priv_only; +grant all privileges on test.* to mysqltest_u1@localhost; +grant insert (a) on mysqltest_db1.t_column_priv_only to mysqltest_u1@localhost; +grant select on mysqltest_db1.t_select_priv to mysqltest_u1@localhost; + +--echo ** Connect as restricted user mysqltest_u1. +--echo +connect (con1,localhost,mysqltest_u1,,); +connection con1; + +######################################################################## +--echo ** Test column level privileges only. No SELECT privileges on the table. +--echo ** INSERT INTO ... VALUES ... +--echo ** Attempting to insert values to a table with only column privileges +--echo ** should work. +insert into mysqltest_db1.t_column_priv_only (a) VALUES (1); +--echo + +######################################################################### +--echo ** SHOW COLUMNS +--echo ** Should succeed because we have privileges (any) on at least one of the columns. +select column_name as 'Field',column_type as 'Type',is_nullable as 'Null',column_key as 'Key',column_default as 'Default',extra as 'Extra' from information_schema.columns where table_schema='mysqltest_db1' and table_name='t_column_priv_only'; +show columns from mysqltest_db1.t_column_priv_only; +######################################################################### +--echo ** SHOW COLUMNS +--echo ** Should fail because there are no privileges on any column combination. +--error 1142 +show columns from mysqltest_db1.t_no_priv; +--echo ** However, select from I_S.COLUMNS will succeed but not show anything: +select column_name as 'Field',column_type as 'Type',is_nullable as 'Null',column_key as 'Key',column_default as 'Default',extra as 'Extra' from information_schema.columns where table_schema='mysqltest_db1' and table_name='t_no_priv'; +--echo +######################################################################### +--echo ** CREATE TABLE ... LIKE ... require SELECT privleges and will fail. +--error 1142 +create table test.t_no_priv like mysqltest_db1.column_priv_only; +--echo +######################################################################### +--echo ** Just to be sure... SELECT also fails. +--error 1142 +select * from mysqltest_db1.t_column_priv_only; +--echo +######################################################################### +--echo ** SHOW CREATE TABLE ... require any privileges on all columns (the entire table). +--echo ** First we try and fail on a table with only one column privilege. +--error 1142 +show create table mysqltest_db1.t_column_priv_only; +--echo +######################################################################### +--echo ** Now we do the same on a table with SELECT privileges. +--echo +######################################################################### +--echo ** SHOW COLUMNS +--echo ** Success because we got some privileges on the table (SELECT_ACL) +show columns from mysqltest_db1.t_select_priv; +--echo +######################################################################### +--echo ** CREATE TABLE ... LIKE ... require SELECT privleges and will SUCCEED. +--disable_warnings +drop table if exists test.t_duplicated; +--enable_warnings +create table test.t_duplicated like mysqltest_db1.t_select_priv; +drop table test.t_duplicated; +--echo +######################################################################### +--echo ** SHOW CREATE TABLE will succeed because we have a privilege on all columns in the table (table-level privilege). +show create table mysqltest_db1.t_select_priv; +--echo +######################################################################### +--echo ** SHOW CREATE TABLE will fail if there is no grants at all: +--error 1142 +show create table mysqltest_db1.t_no_priv; +--echo + +connection default; + +# +# SHOW INDEX +# +use mysqltest_db1; +CREATE TABLE t5 (s1 INT); +CREATE INDEX i ON t5 (s1); +CREATE TABLE t6 (s1 INT, s2 INT); +CREATE VIEW v5 AS SELECT * FROM t5; +CREATE VIEW v6 AS SELECT * FROM t6; +CREATE VIEW v2 AS SELECT * FROM t_select_priv; +CREATE VIEW v3 AS SELECT * FROM t_select_priv; +CREATE INDEX i ON t6 (s1); +GRANT UPDATE (s2) ON t6 to mysqltest_u1@localhost; +GRANT UPDATE (s2) ON v6 to mysqltest_u1@localhost; +GRANT SHOW VIEW ON v2 to mysqltest_u1@localhost; +GRANT SHOW VIEW, SELECT ON v3 to mysqltest_u1@localhost; + +connection con1; +use mysqltest_db1; +--echo ** Connect as restricted user mysqltest_u1. +--echo ** SELECT FROM INFORMATION_SCHEMA.STATISTICS will succeed because any privileges will do (authentication is enough). +# +# this result is wrong. reported as bug#34104 +# +SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE table_name='t5'; +# +# Bug27145 EXTRA_ACL trouble +# +--echo ** SHOW INDEX FROM t5 will fail because we don't have any privileges on any column combination. +--error 1142 +SHOW INDEX FROM t5; +--echo ** SHOW INDEX FROM t6 will succeed because there exist a privilege on a column combination on t6. +SHOW INDEX FROM t6; + +# CHECK TABLE +--echo ** CHECK TABLE requires any privilege on any column combination and should succeed for t6: +CHECK TABLE t6; +--echo ** With no privileges access is naturally denied: +--error 1142 +CHECK TABLE t5; + +# CHECKSUM +--echo ** CHECKSUM TABLE requires SELECT privileges on the table. The following should fail: +--error 1142 +CHECKSUM TABLE t6; +--echo ** And this should work: +CHECKSUM TABLE t_select_priv; + +# SHOW CREATE VIEW +--error 1142 +SHOW CREATE VIEW v5; +--error 1142 +SHOW CREATE VIEW v6; +--error 1142 +SHOW CREATE VIEW v2; +SHOW CREATE VIEW v3; + +connection default; +disconnect con1; +drop database mysqltest_db1; +drop user mysqltest_u1@localhost; diff --git a/mysql-test/t/information_schema_db.test b/mysql-test/t/information_schema_db.test index 52ec0d9272a..9bc558254ee 100644 --- a/mysql-test/t/information_schema_db.test +++ b/mysql-test/t/information_schema_db.test @@ -1,5 +1,5 @@ # this test mostly test privilege control (what doesn't work -# in the embedded server by default). So disabled in embedded-server mode +# in the embedded server by default). So skip the test in embedded-server mode. -- source include/not_embedded.inc -- source include/testdb_only.inc @@ -13,7 +13,7 @@ drop function if exists f2; use INFORMATION_SCHEMA; --replace_result Tables_in_INFORMATION_SCHEMA Tables_in_information_schema -show tables where Tables_in_information_schema not like "Innodb%"; +show tables where Tables_in_INFORMATION_SCHEMA NOT LIKE 'Innodb%'; --replace_result 'Tables_in_INFORMATION_SCHEMA (T%)' 'Tables_in_information_schema (T%)' show tables from INFORMATION_SCHEMA like 'T%'; create database `inf%`; @@ -123,7 +123,7 @@ create view v1 as select f1 from t1; grant insert on v1 to testdb_2@localhost; create view v5 as select f1 from t1; -grant show view on v5 to testdb_2@localhost; +grant select, show view on v5 to testdb_2@localhost; --error ER_SPECIFIC_ACCESS_DENIED_ERROR create definer=`no_such_user`@`no_such_host` view v6 as select f1 from t1; @@ -131,7 +131,7 @@ create definer=`no_such_user`@`no_such_host` view v6 as select f1 from t1; connection default; use testdb_1; create view v6 as select f1 from t1; -grant show view on v6 to testdb_2@localhost; +grant select, show view on v6 to testdb_2@localhost; create table t2 (f1 char(4)); create definer=`no_such_user`@`no_such_host` view v7 as select * from t2; @@ -163,10 +163,10 @@ show fields from testdb_1.v7; show create view testdb_1.v7; revoke insert(f1) on v3 from testdb_2@localhost; -revoke show view on v5 from testdb_2@localhost; +revoke select,show view on v5 from testdb_2@localhost; connection default; use testdb_1; -revoke show view on v6 from testdb_2@localhost; +revoke select,show view on v6 from testdb_2@localhost; connection testdb_2; --error ER_TABLEACCESS_DENIED_ERROR diff --git a/mysql-test/t/outfile.test b/mysql-test/t/outfile.test index 45b13f141f7..9f2fc22da99 100644 --- a/mysql-test/t/outfile.test +++ b/mysql-test/t/outfile.test @@ -109,7 +109,7 @@ create user user_1@localhost; grant all on mysqltest.* to user_1@localhost; connect (con28181_1,localhost,user_1,,mysqltest); ---error ER_DBACCESS_DENIED_ERROR +--error ER_ACCESS_DENIED_ERROR eval select schema_name into outfile "../../tmp/outfile-test.4" fields terminated by ',' optionally enclosed by '"' diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index a560076e89f..d82abab6f17 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -8268,6 +8268,34 @@ DROP TABLE t1, t2; --echo # -- End of 5.1 tests --echo # ------------------------------------------------------------------ +# +# Bug#39255: Stored procedures: crash if function references nonexistent table +# + +--disable_warnings +DROP FUNCTION IF EXISTS f1; +DROP TABLE IF EXISTS t_non_existing; +DROP TABLE IF EXISTS t1; +--enable_warnings + +delimiter |; +CREATE FUNCTION f1() RETURNS INT +BEGIN + DECLARE v INT; + SELECT a INTO v FROM t_non_existing; + RETURN 1; +END| +delimiter ;| + +CREATE TABLE t1 (a INT) ENGINE = myisam; +INSERT INTO t1 VALUES (1); + +--error ER_NO_SUCH_TABLE +SELECT * FROM t1 WHERE a = f1(); + +DROP FUNCTION f1; +DROP TABLE t1; + --echo # --echo # Bug#34197: CREATE PROCEDURE fails when COMMENT truncated in non --echo # strict SQL mode diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index 824c67d867e..579cdb6d5c4 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -1072,12 +1072,12 @@ CREATE USER u29908_1@localhost; CREATE DEFINER = u29908_1@localhost VIEW v1 AS SELECT f1 FROM t1; CREATE DEFINER = u29908_1@localhost SQL SECURITY INVOKER VIEW v2 AS SELECT f1 FROM t1; -GRANT DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v1 TO u29908_1@localhost; -GRANT DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_1@localhost; +GRANT SELECT, DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v1 TO u29908_1@localhost; +GRANT SELECT, DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_1@localhost; GRANT SELECT ON mysqltest_29908.t1 TO u29908_1@localhost; CREATE USER u29908_2@localhost; -GRANT DROP, CREATE VIEW ON mysqltest_29908.v1 TO u29908_2@localhost; -GRANT DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_2@localhost; +GRANT SELECT, DROP, CREATE VIEW ON mysqltest_29908.v1 TO u29908_2@localhost; +GRANT SELECT, DROP, CREATE VIEW, SHOW VIEW ON mysqltest_29908.v2 TO u29908_2@localhost; GRANT SELECT ON mysqltest_29908.t1 TO u29908_2@localhost; connect (u2,localhost,u29908_2,,mysqltest_29908); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index c8576722c69..5dafee7ff27 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -847,7 +847,7 @@ bool Aggregator_distinct::setup(THD *thd) DBUG_ENTER("Item_sum_distinct::setup"); /* It's legal to call setup() more than once when in a subquery */ if (tree) - return FALSE; + DBUG_RETURN(FALSE); /* Virtual table and the tree are created anew on each re-execution of @@ -855,7 +855,7 @@ bool Aggregator_distinct::setup(THD *thd) mem_root. */ if (field_list.push_back(&field_def)) - return TRUE; + DBUG_RETURN(TRUE); item_sum->null_value= item_sum->maybe_null= 1; item_sum->quick_group= 0; @@ -871,7 +871,7 @@ bool Aggregator_distinct::setup(THD *thd) } if (always_null) - return FALSE; + DBUG_RETURN(FALSE); enum enum_field_types field_type; @@ -884,7 +884,7 @@ bool Aggregator_distinct::setup(THD *thd) arg->unsigned_flag); if (! (table= create_virtual_tmp_table(thd, field_list))) - return TRUE; + DBUG_RETURN(TRUE); /* XXX: check that the case of CHAR(0) works OK */ tree_key_length= table->s->reclength - table->s->null_bytes; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index ec3779d1ddf..0650cdd7603 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1122,9 +1122,11 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, bool *write_to_binlog); #ifndef NO_EMBEDDED_ACCESS_CHECKS bool check_access(THD *thd, ulong access, const char *db, ulong *save_priv, - bool no_grant, bool no_errors, bool schema_db); -bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables, - uint number, bool no_errors); + bool no_grant, bool no_errors, bool schema_db); +bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, + bool any_combination_of_privileges_will_do, + uint number, + bool no_errors); #else inline bool check_access(THD *thd, ulong access, const char *db, ulong *save_priv, bool no_grant, bool no_errors, @@ -1134,8 +1136,10 @@ inline bool check_access(THD *thd, ulong access, const char *db, *save_priv= GLOBAL_ACLS; return false; } -inline bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables, - uint number, bool no_errors) +inline bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, + bool no_errors, + bool any_combination_of_privileges_will_do, + uint number) { return false; } #endif /*NO_EMBEDDED_ACCESS_CHECKS*/ diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 965949223a5..1421c1809bc 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2378,7 +2378,8 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access) bzero((char*) &tables,sizeof(tables)); tables.db= (char*) "mysql"; tables.table_name= tables.alias= (char*) "proc"; - *full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1, TRUE) || + *full_access= (!check_table_access(thd, SELECT_ACL, &tables, FALSE, + 1, TRUE) || (!strcmp(sp->m_definer_user.str, thd->security_ctx->priv_user) && !strcmp(sp->m_definer_host.str, @@ -2824,7 +2825,7 @@ int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables) Check whenever we have access to tables for this statement and open and lock them before executing instructions core function. */ - if (check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE) + if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE) || open_and_lock_tables(thd, tables)) result= -1; else diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d3fb8e3fe5c..c996f68f16a 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1880,7 +1880,7 @@ static bool test_if_create_new_users(THD *thd) sctx->priv_user, tl.db, 0); if (!(db_access & INSERT_ACL)) { - if (check_grant(thd, INSERT_ACL, &tl, 0, UINT_MAX, 1)) + if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE)) create_new_users=0; } } @@ -3838,40 +3838,52 @@ end: DBUG_RETURN(return_val); } -/**************************************************************************** - Check table level grants - SYNOPSIS - bool check_grant() - thd Thread handler - want_access Bits of privileges user needs to have - tables List of tables to check. The user should have 'want_access' - to all tables in list. - show_table <> 0 if we are in show table. In this case it's enough to have - any privilege for the table - number Check at most this number of tables. - no_errors If 0 then we write an error. The error is sent directly to - the client +/** + @brief Check table level grants - RETURN - 0 ok - 1 Error: User did not have the requested privileges + @param thd Thread handler + @param want_access Bits of privileges user needs to have. + @param tables List of tables to check. The user should have + 'want_access' to all tables in list. + @param any_combination_will_do TRUE if it's enough to have any privilege for + any combination of the table columns. + @param number Check at most this number of tables. + @param no_errors TRUE if no error should be sent directly to the client. - NOTE - This functions assumes that either number of tables to be inspected + If table->grant.want_privilege != 0 then the requested privileges where + in the set of COL_ACLS but access was not granted on the table level. As + a consequence an extra check of column privileges is required. + + Specifically if this function returns FALSE the user has some kind of + privilege on a combination of columns in each table. + + This function is usually preceeded by check_access which establish the + User-, Db- and Host access rights. + + @see check_access + @see check_table_access + + @note This functions assumes that either number of tables to be inspected by it is limited explicitly (i.e. is is not UINT_MAX) or table list used and thd->lex->query_tables_own_last value correspond to each other (the latter should be either 0 or point to next_global member of one of elements of this table list). -****************************************************************************/ + + @return Access status + @retval FALSE Access granted; But column privileges might need to be + checked. + @retval TRUE The user did not have the requested privileges on any of the + tables. + +*/ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, - uint show_table, uint number, bool no_errors) + bool any_combination_will_do, uint number, bool no_errors) { TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table(); Security_context *sctx= thd->security_ctx; uint i; - ulong orig_want_access= want_access; DBUG_ENTER("check_grant"); DBUG_ASSERT(number > 0); @@ -3889,7 +3901,10 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, i < number && table != first_not_own_table; table= table->next_global, i++) { - /* Remove SHOW_VIEW_ACL, because it will be checked during making view */ + /* + Save a copy of the privileges without the SHOW_VIEW_ACL attribute. + It will be checked during making view. + */ table->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL); } @@ -3902,7 +3917,6 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, sctx = test(table->security_ctx) ? table->security_ctx : thd->security_ctx; - want_access= orig_want_access; want_access&= ~sctx->master_access; if (!want_access) continue; // ok @@ -3932,8 +3946,13 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, want_access &= ~table->grant.privilege; goto err; // No grants } - if (show_table) - continue; // We have some priv on this + + /* + For SHOW COLUMNS, SHOW INDEX it is enough to have some + privileges on any column combination on the table. + */ + if (any_combination_will_do) + continue; table->grant.grant_table=grant_table; // Remember for column test table->grant.version=grant_version; @@ -3951,7 +3970,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, } } rw_unlock(&LOCK_grant); - DBUG_RETURN(0); + DBUG_RETURN(FALSE); err: rw_unlock(&LOCK_grant); @@ -3965,7 +3984,97 @@ err: sctx->host_or_ip, table ? table->get_table_name() : "unknown"); } - DBUG_RETURN(1); + DBUG_RETURN(TRUE); +} + + +/** + Check if all tables in the table list has any of the requested table level + privileges matching the current user. + + @param thd A pointer to the thread context. + @param required_access Set of privileges to compare against. + @param tables[in,out] A list of tables to be checked. + + @note If the table grant hash contains any grant table, this table will be + attached to the corresponding TABLE_LIST object in 'tables'. + + @return + @retval TRUE There is a privilege on the table level granted to the + current user. + @retval FALSE There are no privileges on the table level granted to the + current user. +*/ + +bool has_any_table_level_privileges(THD *thd, ulong required_access, + TABLE_LIST *tables) +{ + + Security_context *sctx; + GRANT_TABLE *grant_table; + TABLE_LIST *table; + + /* For each table in tables */ + for (table= tables; table; table= table->next_global) + { + /* + If this table is a VIEW, then it will supply its own security context. + This is because VIEWs can have a DEFINER or an INVOKER security role. + */ + sctx= table->security_ctx ? table->security_ctx : thd->security_ctx; + + /* + Get privileges from table_priv and column_priv tables by searching + the cache. + */ + rw_rdlock(&LOCK_grant); + grant_table= table_hash_search(sctx->host, sctx->ip, + table->db, sctx->priv_user, + table->table_name,0); + rw_unlock(&LOCK_grant); + + /* Stop if there are no grants for the current user */ + if (!grant_table) + return FALSE; + + /* + Save a pointer to the found grant_table in the table object. + This pointer can later be used to verify other access requirements + without having to look up the grant table in the hash. + */ + table->grant.grant_table= grant_table; + table->grant.version= grant_version; + table->grant.privilege|= grant_table->privs; + /* + Save all privileges which might be subject to column privileges + but not which aren't yet granted by table level ACLs. + This is can later be used for column privilege checks. + */ + table->grant.want_privilege= ((required_access & COL_ACLS) + & ~table->grant.privilege); + + /* + If the requested privileges share any intersection with the current + table privileges we have found at least one common privilege on the + table level. + */ + if (grant_table->privs & required_access) + continue; /* Check next table */ + + /* + There are no table level privileges which satisfies any of the + requested privileges. There might still be column privileges which + does though. + */ + return FALSE; + } + + /* + All tables in TABLE_LIST satisfy the requirement of having any + privilege on the table level. + */ + + return TRUE; } diff --git a/sql/sql_acl.h b/sql/sql_acl.h index a08510e66ae..ad401fa7064 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -52,7 +52,6 @@ 4. acl_init() or whatever - to define behaviour for old privilege tables 5. sql_yacc.yy - for GRANT/REVOKE to work */ -#define EXTRA_ACL (1L << 29) #define NO_ACCESS (1L << 30) #define DB_ACLS \ (UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ @@ -240,7 +239,7 @@ my_bool grant_init(); void grant_free(void); my_bool grant_reload(THD *thd); bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, - uint show_command, uint number, bool dont_print_error); + bool any_combination_will_do, uint number, bool no_errors); bool check_grant_column (THD *thd, GRANT_INFO *grant, const char *db_name, const char *table_name, const char *name, uint length, Security_context *sctx); @@ -271,7 +270,11 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, bool check_routine_level_acl(THD *thd, const char *db, const char *name, bool is_proc); bool is_acl_user(const char *host, const char *user); +bool has_any_table_level_privileges(THD *thd, ulong required_access, + TABLE_LIST *tables); + #ifdef NO_EMBEDDED_ACCESS_CHECKS #define check_grant(A,B,C,D,E,F) 0 #define check_grant_db(A,B) 0 +#define has_any_table_level_privileges(A,B,C) 0 #endif diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b5d713ebe62..4eb2d55040f 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -733,7 +733,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) table_list.table_name= share->table_name.str; table_list.grant.privilege=0; - if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list, 1, TRUE)) + if (check_table_access(thd,SELECT_ACL,&table_list, TRUE, 1, TRUE)) continue; /* need to check if we haven't already listed it */ for (table= open_list ; table ; table=table->next) diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 0d7e45705f4..198a6a6d8d8 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1566,7 +1566,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", table_list.db = table->db(); table_list.alias= table_list.table_name= table->table(); #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (check_table_access(thd,SELECT_ACL,&table_list, 1, TRUE)) + if (check_table_access(thd,SELECT_ACL,&table_list, FALSE, 1,TRUE)) { DBUG_PRINT("qcache", ("probably no SELECT access to %s.%s => return to normal processing", diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a9a6367d877..e7f32693ad5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -46,7 +46,6 @@ "FUNCTION" : "PROCEDURE") static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables); -static bool check_show_create_table_access(THD *thd, TABLE_LIST *table); const char *any_db="*any*"; // Special symbol for check_access @@ -590,7 +589,7 @@ static bool check_merge_table_access(THD *thd, char *db, tlist->db= db; /* purecov: inspected */ } error= check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL, - table_list, UINT_MAX, FALSE); + table_list, FALSE, UINT_MAX, FALSE); } return error; } @@ -1335,7 +1334,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege, 0, 0, test(table_list.schema_table))) break; - if (check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0)) + if (check_grant(thd, SELECT_ACL, &table_list, TRUE, UINT_MAX, FALSE)) break; /* init structures for VIEW processing */ table_list.select_lex= &(thd->lex->select_lex); @@ -2194,14 +2193,16 @@ mysql_execute_command(THD *thd) #endif case SQLCOM_SHOW_STATUS_PROC: case SQLCOM_SHOW_STATUS_FUNC: - if (!(res= check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE))) + if (!(res= check_table_access(thd, SELECT_ACL, all_tables, FALSE, + UINT_MAX, FALSE))) res= execute_sqlcom_select(thd, all_tables); break; case SQLCOM_SHOW_STATUS: { system_status_var old_status_var= thd->status_var; thd->initial_status_var= &old_status_var; - if (!(res= check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE))) + if (!(res= check_table_access(thd, SELECT_ACL, all_tables, FALSE, + UINT_MAX, FALSE))) res= execute_sqlcom_select(thd, all_tables); /* Don't log SHOW STATUS commands to slow query log */ thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | @@ -2231,18 +2232,22 @@ mysql_execute_command(THD *thd) case SQLCOM_SHOW_STORAGE_ENGINES: case SQLCOM_SHOW_PROFILE: case SQLCOM_SELECT: + { thd->status_var.last_query_cost= 0.0; + + /* + lex->exchange != NULL implies SELECT .. INTO OUTFILE and this + requires FILE_ACL access. + */ + ulong privileges_requested= lex->exchange ? SELECT_ACL | FILE_ACL : + SELECT_ACL; + if (all_tables) - { res= check_table_access(thd, - lex->exchange ? SELECT_ACL | FILE_ACL : - SELECT_ACL, - all_tables, UINT_MAX, FALSE); - } + privileges_requested, + all_tables, FALSE, UINT_MAX, FALSE); else - res= check_access(thd, - lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, - any_db, 0, 0, 0, 0); + res= check_access(thd, privileges_requested, any_db, 0, 0, 0, UINT_MAX); if (res) break; @@ -2253,7 +2258,8 @@ mysql_execute_command(THD *thd) res= execute_sqlcom_select(thd, all_tables); break; - case SQLCOM_PREPARE: + } +case SQLCOM_PREPARE: { mysql_sql_stmt_prepare(thd); break; @@ -2269,8 +2275,8 @@ mysql_execute_command(THD *thd) break; } case SQLCOM_DO: - if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) || - open_and_lock_tables(thd, all_tables)) + if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || open_and_lock_tables(thd, all_tables)) goto error; res= mysql_do(thd, *lex->insert_list); @@ -2379,8 +2385,8 @@ mysql_execute_command(THD *thd) case SQLCOM_BACKUP_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) || - check_global_access(thd, FILE_ACL)) + if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || check_global_access(thd, FILE_ACL)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res = mysql_backup_table(thd, first_table); @@ -2391,8 +2397,8 @@ mysql_execute_command(THD *thd) case SQLCOM_RESTORE_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_table_access(thd, INSERT_ACL, all_tables, UINT_MAX, FALSE) || - check_global_access(thd, FILE_ACL)) + if (check_table_access(thd, INSERT_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || check_global_access(thd, FILE_ACL)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res = mysql_restore_table(thd, first_table); @@ -2491,7 +2497,7 @@ mysql_execute_command(THD *thd) test(first_table->schema_table))) goto error; /* purecov: inspected */ /* Check that the first table has CREATE privilege */ - if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0)) + if (check_grant(thd, CREATE_ACL, all_tables, FALSE, 1, FALSE)) goto error; pthread_mutex_lock(&LOCK_active_mi); @@ -2861,7 +2867,7 @@ end_with_restore_list: (TABLE_LIST *) create_info.merge_list.first)) goto error; /* purecov: inspected */ - if (check_grant(thd, priv_needed, all_tables, 0, UINT_MAX, 0)) + if (check_grant(thd, priv_needed, all_tables, FALSE, UINT_MAX, FALSE)) goto error; if (lex->name.str && !test_all_bits(priv,INSERT_ACL | CREATE_ACL)) { // Rename of table @@ -2870,8 +2876,8 @@ end_with_restore_list: tmp_table.table_name= lex->name.str; tmp_table.db=select_lex->db; tmp_table.grant.privilege=priv; - if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0, - UINT_MAX, 0)) + if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, FALSE, + UINT_MAX, FALSE)) goto error; } @@ -2925,10 +2931,11 @@ end_with_restore_list: */ old_list= table[0]; new_list= table->next_local[0]; - if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, 0, 1, 0) || + if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, FALSE, 1, FALSE) || (!test_all_bits(table->next_local->grant.privilege, INSERT_ACL | CREATE_ACL) && - check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0))) + check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, FALSE, 1, + FALSE))) goto error; } @@ -2959,11 +2966,54 @@ end_with_restore_list: goto error; #else { - /* Ignore temporary tables if this is "SHOW CREATE VIEW" */ + /* + Access check: + SHOW CREATE TABLE require any privileges on the table level (ie + effecting all columns in the table). + SHOW CREATE VIEW require the SHOW_VIEW and SELECT ACLs on the table + level. + NOTE: SHOW_VIEW ACL is checked when the view is created. + */ + if (lex->only_view) + { + if (check_table_access(thd, SELECT_ACL, first_table, FALSE, 1, FALSE)) + { + my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), + "SHOW", thd->security_ctx->priv_user, + thd->security_ctx->host_or_ip, first_table->alias); + goto error; + } + + /* Ignore temporary tables if this is "SHOW CREATE VIEW" */ first_table->skip_temporary= 1; - if (check_show_create_table_access(thd, first_table)) - goto error; + } + else + { + ulong save_priv; + if (check_access(thd, SELECT_ACL, first_table->db, + &save_priv, FALSE, FALSE, + test(first_table->schema_table))) + goto error; + /* + save_priv contains any privileges actually granted by check_access. + If there are no global privileges (save_priv == 0) and no table level + privileges, access is denied. + */ + if (!save_priv && + !has_any_table_level_privileges(thd, TABLE_ACLS, + first_table)) + { + my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), + "SHOW", thd->security_ctx->priv_user, + thd->security_ctx->host_or_ip, first_table->alias); + goto error; + } + } + + /* + Access is granted. Execute command. + */ res= mysqld_show_create(thd, first_table); break; } @@ -2971,8 +3021,8 @@ end_with_restore_list: case SQLCOM_CHECKSUM: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, - UINT_MAX, FALSE)) + if (check_table_access(thd, SELECT_ACL, all_tables, + FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ res = mysql_checksum_table(thd, first_table, &lex->check_opt); break; @@ -2981,7 +3031,7 @@ end_with_restore_list: { DBUG_ASSERT(first_table == all_tables && first_table != 0); if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, - UINT_MAX, FALSE)) + FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res= mysql_repair_table(thd, first_table, &lex->check_opt); @@ -3000,8 +3050,8 @@ end_with_restore_list: case SQLCOM_CHECK: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, - UINT_MAX, FALSE)) + if (check_table_access(thd, SELECT_ACL, all_tables, + TRUE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res = mysql_check_table(thd, first_table, &lex->check_opt); @@ -3013,7 +3063,7 @@ end_with_restore_list: { DBUG_ASSERT(first_table == all_tables && first_table != 0); if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, - UINT_MAX, FALSE)) + FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res= mysql_analyze_table(thd, first_table, &lex->check_opt); @@ -3034,7 +3084,7 @@ end_with_restore_list: { DBUG_ASSERT(first_table == all_tables && first_table != 0); if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, - UINT_MAX, FALSE)) + FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ? @@ -3407,7 +3457,7 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); if (!lex->drop_temporary) { - if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE)) + if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ if (end_active_trans(thd)) goto error; @@ -3515,8 +3565,8 @@ end_with_restore_list: if (lex->autocommit && end_active_trans(thd)) goto error; - if ((check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) || - open_and_lock_tables(thd, all_tables))) + if ((check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || open_and_lock_tables(thd, all_tables))) goto error; if (lex->one_shot_set && not_all_support_one_shot(lex_var_list)) { @@ -3570,7 +3620,7 @@ end_with_restore_list: if (end_active_trans(thd)) goto error; if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, - UINT_MAX, FALSE)) + FALSE, UINT_MAX, FALSE)) goto error; if (lex->protect_against_global_read_lock && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) @@ -3966,7 +4016,7 @@ end_with_restore_list: else { if (check_grant(thd,(lex->grant | lex->grant_tot_col | GRANT_ACL), - all_tables, 0, UINT_MAX, 0)) + all_tables, FALSE, UINT_MAX, FALSE)) goto error; /* Conditionally writes to binlog */ res= mysql_table_grant(thd, all_tables, lex->users_list, @@ -4075,7 +4125,7 @@ end_with_restore_list: #endif case SQLCOM_HA_OPEN: DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE)) + if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; res= mysql_ha_open(thd, first_table, 0); break; @@ -4361,7 +4411,8 @@ create_sp_error: This will cache all SP and SF and open and lock all tables required for execution. */ - if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) || + if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, + UINT_MAX, FALSE) || open_and_lock_tables(thd, all_tables)) goto error; @@ -4689,8 +4740,8 @@ create_sp_error: } case SQLCOM_DROP_VIEW: { - if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE) || - end_active_trans(thd)) + if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || end_active_trans(thd)) goto error; /* Conditionally writes to binlog. */ res= mysql_drop_view(thd, first_table, thd->lex->drop_mode); @@ -5139,7 +5190,7 @@ bool check_single_table_access(THD *thd, ulong privilege, (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) && !(all_tables->view && all_tables->effective_algorithm == VIEW_ALGORITHM_TMPTABLE) && - check_grant(thd, privilege, all_tables, 0, 1, no_errors)) + check_grant(thd, privilege, all_tables, FALSE, 1, no_errors)) goto deny; thd->security_ctx= backup_ctx; @@ -5184,7 +5235,8 @@ bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) subselects_tables= subselects_tables->next_global; } if (subselects_tables && - (check_table_access(thd, SELECT_ACL, subselects_tables, UINT_MAX, FALSE))) + (check_table_access(thd, SELECT_ACL, subselects_tables, FALSE, + UINT_MAX, FALSE))) return 1; } return 0; @@ -5192,46 +5244,54 @@ bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) /** - Get the user (global) and database privileges for all used tables. - - @param save_priv In this we store global and db level grants for the - table. Note that we don't store db level grants if the - global grants is enough to satisfy the request and the - global grants contains a SELECT grant. - - @note - The idea of EXTRA_ACL is that one will be granted access to the table if - one has the asked privilege on any column combination of the table; For - example to be able to check a table one needs to have SELECT privilege on - any column of the table. - - @retval - 0 ok - @retval - 1 If we can't get the privileges and we don't use table/column - grants. + @brief Compare requested privileges with the privileges acquired from the + User- and Db-tables. + @param thd Thread handler + @param want_access The requested access privileges. + @param db A pointer to the Db name. + @param[out] save_priv A pointer to the granted privileges will be stored. + @param dont_check_global_grants True if no global grants are checked. + @param no_error True if no errors should be sent to the client. + @param schema_db True if the db specified belongs to the meta data tables. + + 'save_priv' is used to save the User-table (global) and Db-table grants for + the supplied db name. Note that we don't store db level grants if the global + grants is enough to satisfy the request AND the global grants contains a + SELECT grant. + + A meta data table (from INFORMATION_SCHEMA) can always be accessed with + a SELECT_ACL. + + @see check_grant + + @return Status of denial of access by exclusive ACLs. + @retval FALSE Access can't exclusively be denied by Db- and User-table + access unless Column- and Table-grants are checked too. + @retval TRUE Access denied. */ + bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, bool dont_check_global_grants, bool no_errors, bool schema_db) { Security_context *sctx= thd->security_ctx; ulong db_access; + /* GRANT command: In case of database level grant the database name may be a pattern, in case of table|column level grant the database name can not be a pattern. We use 'dont_check_global_grants' as a flag to determine - if it's database level grant command + if it's database level grant command (see SQLCOM_GRANT case, mysql_execute_command() function) and set db_is_pattern according to 'dont_check_global_grants' value. */ - bool db_is_pattern= (test(want_access & GRANT_ACL) && - dont_check_global_grants); + bool db_is_pattern= ((want_access & GRANT_ACL) && dont_check_global_grants); ulong dummy; DBUG_ENTER("check_access"); DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu", db ? db : "", want_access, sctx->master_access)); + if (save_priv) *save_priv=0; else @@ -5249,8 +5309,12 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if (schema_db) { - if ((!(sctx->master_access & FILE_ACL) && (want_access & FILE_ACL)) || - (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL))) + /* + We don't allow any simple privileges but SELECT_ACL or CREATE_VIEW_ACL + on the information_schema database. + */ + want_access &= ~SELECT_ACL; + if (want_access & DB_ACLS) { if (!no_errors) { @@ -5258,10 +5322,15 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, db_name); } + /* + Access denied; + [out] *save_privileges= 0 + */ DBUG_RETURN(TRUE); } else { + /* Access granted */ *save_priv= SELECT_ACL; DBUG_RETURN(FALSE); } @@ -5269,20 +5338,27 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if ((sctx->master_access & want_access) == want_access) { + /* get access for current db */ + db_access= sctx->db_access; /* - If we don't have a global SELECT privilege, we have to get the database - specific access rights to be able to handle queries of type + 1. If we don't have a global SELECT privilege, we have to get the + database specific access rights to be able to handle queries of type UPDATE t1 SET a=1 WHERE b > 0 + 2. Change db access if it isn't current db which is being addressed */ - db_access= sctx->db_access; if (!(sctx->master_access & SELECT_ACL) && (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))) db_access=acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_is_pattern); + + /* + The effective privileges are the union of the global privileges + and the the intersection of db- and host-privileges. + */ *save_priv=sctx->master_access | db_access; DBUG_RETURN(FALSE); } - if (((want_access & ~sctx->master_access) & ~(DB_ACLS | EXTRA_ACL)) || + if (((want_access & ~sctx->master_access) & ~DB_ACLS) || (! db && dont_check_global_grants)) { // We can never grant this DBUG_PRINT("error",("No possible access")); @@ -5297,33 +5373,66 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, } if (db == any_db) - DBUG_RETURN(FALSE); // Allow select on anything + { + /* + Access granted; Allow select on *any* db. + [out] *save_privileges= 0 + */ + DBUG_RETURN(FALSE); + } if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db))) db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_is_pattern); else db_access= sctx->db_access; - DBUG_PRINT("info",("db_access: %lu", db_access)); - /* Remove SHOW attribute and access rights we already have */ - want_access &= ~(sctx->master_access | EXTRA_ACL); DBUG_PRINT("info",("db_access: %lu want_access: %lu", db_access, want_access)); - db_access= ((*save_priv=(db_access | sctx->master_access)) & want_access); - if (db_access == want_access || + /* + Save the union of User-table and the intersection between Db-table and + Host-table privileges. + */ + db_access= (db_access | sctx->master_access); + *save_priv= db_access; + + /* + We need to investigate column- and table access if all requested privileges + belongs to the bit set of . + */ + bool need_table_or_column_check= + (want_access & (TABLE_ACLS | PROC_ACLS | db_access)) == want_access; + + /* + Grant access if the requested access is in the intersection of + host- and db-privileges (as retrieved from the acl cache), + also grant access if all the requested privileges are in the union of + TABLES_ACLS and PROC_ACLS; see check_grant. + */ + if ( (db_access & want_access) == want_access || (!dont_check_global_grants && - !(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS)))) - DBUG_RETURN(FALSE); /* Ok */ + need_table_or_column_check)) + { + /* + Ok; but need to check table- and column privileges. + [out] *save_privileges is (User-priv | (Db-priv & Host-priv)) + */ + DBUG_RETURN(FALSE); + } + /* + Access is denied; + [out] *save_privileges is (User-priv | (Db-priv & Host-priv)) + */ DBUG_PRINT("error",("Access denied")); if (!no_errors) my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, (db ? db : (thd->db ? thd->db : - "unknown"))); /* purecov: tested */ - DBUG_RETURN(TRUE); /* purecov: tested */ + "unknown"))); + DBUG_RETURN(TRUE); + } @@ -5369,14 +5478,20 @@ static bool check_show_access(THD *thd, TABLE_LIST *table) DBUG_ASSERT(dst_table); - if (check_access(thd, SELECT_ACL | EXTRA_ACL, - dst_table->db, - &dst_table->grant.privilege, - FALSE, FALSE, + if (check_access(thd, SELECT_ACL, dst_table->db, + &dst_table->grant.privilege, FALSE, FALSE, test(dst_table->schema_table))) - return FALSE; + return TRUE; /* Access denied */ + + /* + Check_grant will grant access if there is any column privileges on + all of the tables thanks to the fourth parameter (bool show_table). + */ + if (check_grant(thd, SELECT_ACL, dst_table, TRUE, UINT_MAX, FALSE)) + return TRUE; /* Access denied */ - return (check_grant(thd, SELECT_ACL, dst_table, 2, UINT_MAX, FALSE)); + /* Access granted */ + return FALSE; } default: break; @@ -5386,30 +5501,46 @@ static bool check_show_access(THD *thd, TABLE_LIST *table) } -/** - Check the privilege for all used tables. - @param thd Thread context - @param want_access Privileges requested - @param tables List of tables to be checked - @param number Check at most this number of tables. - @param no_errors FALSE/TRUE - report/don't report error to - the client (using my_error() call). +/** + @brief Check if the requested privileges exists in either User-, Host- or + Db-tables. + @param thd Thread context + @param want_access Privileges requested + @param tables List of tables to be compared against + @param no_errors Don't report error to the client (using my_error() call). + @param any_combination_of_privileges_will_do TRUE if any privileges on any + column combination is enough. + @param number Only the first 'number' tables in the linked list are + relevant. + + The suppled table list contains cached privileges. This functions calls the + help functions check_access and check_grant to verify the first three steps + in the privileges check queue: + 1. Global privileges + 2. OR (db privileges AND host privileges) + 3. OR table privileges + 4. OR column privileges (not checked by this function!) + 5. OR routine privileges (not checked by this function!) + + @see check_access + @see check_grant + + @note This functions assumes that table list used and + thd->lex->query_tables_own_last value correspond to each other + (the latter should be either 0 or point to next_global member + of one of elements of this table list). - @note - Table privileges are cached in the table list for GRANT checking. - This functions assumes that table list used and - thd->lex->query_tables_own_last value correspond to each other - (the latter should be either 0 or point to next_global member - of one of elements of this table list). - - @retval FALSE OK - @retval TRUE Access denied + @return + @retval FALSE OK + @retval TRUE Access denied; But column or routine privileges might need to + be checked also. */ bool -check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, - uint number, bool no_errors) +check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, + bool any_combination_of_privileges_will_do, + uint number, bool no_errors) { TABLE_LIST *org_tables= tables; TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); @@ -5420,22 +5551,31 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, the given table list refers to the list for prelocking (contains tables of other queries). For simple queries first_not_own_table is 0. */ - for (; i < number && tables != first_not_own_table; + for (; i < number && tables != first_not_own_table && tables; tables= tables->next_global, i++) { + ulong want_access= requirements; if (tables->security_ctx) sctx= tables->security_ctx; else sctx= backup_ctx; - if (tables->schema_table && - (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL))) + /* + Always allow SELECT on schema tables. This is done by removing the + required SELECT_ACL privilege in the want_access parameter. + Disallow any other DDL or DML operation on any schema table. + */ + if (tables->schema_table) { - if (!no_errors) - my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), - sctx->priv_user, sctx->priv_host, - INFORMATION_SCHEMA_NAME.str); - return TRUE; + want_access &= ~SELECT_ACL; + if (want_access & DB_ACLS) + { + if (!no_errors) + my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), + sctx->priv_user, sctx->priv_host, + INFORMATION_SCHEMA_NAME.str); + goto deny; + } } /* Register access for view underlying table. @@ -5447,33 +5587,34 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, { if (check_show_access(thd, tables)) goto deny; - continue; } + DBUG_PRINT("info", ("derived: %d view: %d", tables->derived != 0, + tables->view != 0)); if (tables->is_anonymous_derived_table() || - (tables->table && (int)tables->table->s->tmp_table)) + (tables->table && tables->table->s && + (int)tables->table->s->tmp_table)) continue; thd->security_ctx= sctx; - if ((sctx->master_access & want_access) == - (want_access & ~EXTRA_ACL) && - thd->db) + if ((sctx->master_access & want_access) == want_access && + thd->db) tables->grant.privilege= want_access; else if (tables->db && thd->db && strcmp(tables->db, thd->db) == 0) { if (check_access(thd, want_access, tables->get_db_name(), - &tables->grant.privilege, 0, no_errors, + &tables->grant.privilege, 0, no_errors, test(tables->schema_table))) goto deny; // Access denied } else if (check_access(thd, want_access, tables->get_db_name(), - &tables->grant.privilege, 0, no_errors, - test(tables->schema_table))) + &tables->grant.privilege, 0, no_errors, 0)) goto deny; } thd->security_ctx= backup_ctx; - return check_grant(thd,want_access & ~EXTRA_ACL,org_tables, - test(want_access & EXTRA_ACL), number, no_errors); + return check_grant(thd,requirements,org_tables, + any_combination_of_privileges_will_do, + number, no_errors); deny: thd->security_ctx= backup_ctx; return TRUE; @@ -5561,7 +5702,7 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) if (!check_access(thd, access, table->db, &table->grant.privilege, 0, 1, test(table->schema_table)) && - !check_grant(thd, access, table, 0, 1, 1)) + !check_grant(thd, access, table, FALSE, 1, TRUE)) DBUG_RETURN(0); } } @@ -7254,11 +7395,11 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) else if ((check_access(thd, UPDATE_ACL, table->db, &table->grant.privilege, 0, 1, test(table->schema_table)) || - check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) && + check_grant(thd, UPDATE_ACL, table, FALSE, 1, TRUE)) && (check_access(thd, SELECT_ACL, table->db, &table->grant.privilege, 0, 0, test(table->schema_table)) || - check_grant(thd, SELECT_ACL, table, 0, 1, 0))) + check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE))) DBUG_RETURN(TRUE); table->table_in_first_from_clause= 1; @@ -7276,7 +7417,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) if (check_access(thd, SELECT_ACL, table->db, &table->grant.privilege, 0, 0, test(table->schema_table)) || - check_grant(thd, SELECT_ACL, table, 0, 1, 0)) + check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); } } @@ -7316,7 +7457,7 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables) /* sql_yacc guarantees that tables and aux_tables are not zero */ DBUG_ASSERT(aux_tables != 0); - if (check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) + if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) DBUG_RETURN(TRUE); /* @@ -7325,7 +7466,7 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables) call check_table_access() safely. */ thd->lex->query_tables_own_last= 0; - if (check_table_access(thd, DELETE_ACL, aux_tables, UINT_MAX, FALSE)) + if (check_table_access(thd, DELETE_ACL, aux_tables, FALSE, UINT_MAX, FALSE)) { thd->lex->query_tables_own_last= save_query_tables_own_last; DBUG_RETURN(TRUE); @@ -7479,25 +7620,6 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables) /** - @brief Check privileges for SHOW CREATE TABLE statement. - - @param thd Thread context - @param table Target table - - @retval TRUE Failure - @retval FALSE Success -*/ - -static bool check_show_create_table_access(THD *thd, TABLE_LIST *table) -{ - return check_access(thd, SELECT_ACL | EXTRA_ACL, table->db, - &table->grant.privilege, 0, 0, - test(table->schema_table)) || - check_grant(thd, SELECT_ACL, table, 2, UINT_MAX, 0); -} - - -/** CREATE TABLE query pre-check. @param thd Thread handler @@ -7536,7 +7658,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, lex->create_info.merge_list.first)) goto err; if (want_priv != CREATE_TMP_ACL && - check_grant(thd, want_priv, create_table, 0, 1, 0)) + check_grant(thd, want_priv, create_table, FALSE, 1, FALSE)) goto err; if (select_lex->item_list.elements) @@ -7564,12 +7686,13 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, } } #endif - if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) + if (tables && check_table_access(thd, SELECT_ACL, tables, FALSE, + UINT_MAX, FALSE)) goto err; } else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) { - if (check_show_create_table_access(thd, tables)) + if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) goto err; } error= FALSE; diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index f7f1eee59e6..06db45b895d 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1661,7 +1661,7 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl bzero(&tables, sizeof(tables)); tables.db= (char *)"mysql"; tables.table_name= tables.alias= (char *)"plugin"; - if (check_table_access(thd, INSERT_ACL, &tables, 1, FALSE)) + if (check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); /* need to open before acquiring LOCK_plugin or it will deadlock */ diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 5feaa02cf7e..e54c704589f 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1410,7 +1410,7 @@ static int mysql_test_select(Prepared_statement *stmt, ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL; if (tables) { - if (check_table_access(thd, privilege, tables, UINT_MAX, FALSE)) + if (check_table_access(thd, privilege, tables, FALSE, UINT_MAX, FALSE)) goto error; } else if (check_access(thd, privilege, any_db,0,0,0,0)) @@ -1479,7 +1479,8 @@ static bool mysql_test_do_fields(Prepared_statement *stmt, THD *thd= stmt->thd; DBUG_ENTER("mysql_test_do_fields"); - if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) + if (tables && check_table_access(thd, SELECT_ACL, tables, FALSE, + UINT_MAX, FALSE)) DBUG_RETURN(TRUE); if (open_normal_and_derived_tables(thd, tables, 0)) @@ -1510,8 +1511,9 @@ static bool mysql_test_set_fields(Prepared_statement *stmt, THD *thd= stmt->thd; set_var_base *var; - if ((tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) - || open_normal_and_derived_tables(thd, tables, 0)) + if ((tables && check_table_access(thd, SELECT_ACL, tables, FALSE, + UINT_MAX, FALSE)) || + open_normal_and_derived_tables(thd, tables, 0)) goto error; while ((var= it++)) @@ -1546,7 +1548,8 @@ static bool mysql_test_call_fields(Prepared_statement *stmt, THD *thd= stmt->thd; Item *item; - if ((tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) || + if ((tables && check_table_access(thd, SELECT_ACL, tables, FALSE, + UINT_MAX, FALSE)) || open_normal_and_derived_tables(thd, tables, 0)) goto err; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 79079007099..7e594368667 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -561,7 +561,7 @@ find_files(THD *thd, List<LEX_STRING> *files, const char *db, table_list.table_name= uname; table_list.table_name_length= file_name_len; table_list.grant.privilege=col_access; - if (check_grant(thd, TABLE_ACLS, &table_list, 1, 1, 1)) + if (check_grant(thd, TABLE_ACLS, &table_list, TRUE, 1, TRUE)) continue; } #endif @@ -3765,8 +3765,9 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, #ifndef NO_EMBEDDED_ACCESS_CHECKS uint col_access; - check_access(thd,SELECT_ACL | EXTRA_ACL, db_name->str, - &tables->grant.privilege, 0, 0, test(tables->schema_table)); + check_access(thd,SELECT_ACL, db_name->str, + &tables->grant.privilege, FALSE, FALSE, + test(tables->schema_table)); col_access= get_column_grant(thd, &tables->grant, db_name->str, table_name->str, field->field_name) & COL_ACLS; @@ -4194,7 +4195,8 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) proc_tables.table_name= proc_tables.alias= (char*) "proc"; proc_tables.table_name_length= 4; proc_tables.lock_type= TL_READ; - full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1, TRUE); + full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, FALSE, + 1, TRUE); if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup))) { DBUG_RETURN(1); @@ -4603,7 +4605,7 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables, Table_triggers_list *triggers= tables->table->triggers; int event, timing; - if (check_table_access(thd, TRIGGER_ACL, tables, 1, TRUE)) + if (check_table_access(thd, TRIGGER_ACL, tables, FALSE, 1, TRUE)) goto ret; for (event= 0; event < (int)TRG_EVENT_MAX; event++) @@ -7064,7 +7066,7 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name) if (!lst) return TRUE; - if (check_table_access(thd, TRIGGER_ACL, lst, 1, TRUE)) + if (check_table_access(thd, TRIGGER_ACL, lst, FALSE, 1, TRUE)) { my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "TRIGGER"); return TRUE; diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index c055268ecca..7b689167783 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -422,7 +422,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last; thd->lex->query_tables_own_last= 0; - err_status= check_table_access(thd, TRIGGER_ACL, tables, 1, FALSE); + err_status= check_table_access(thd, TRIGGER_ACL, tables, FALSE, 1, FALSE); thd->lex->query_tables_own_last= save_query_tables_own_last; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index b0d81d6b480..cf6bf5d0633 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1076,7 +1076,7 @@ reopen_tables: if (check_access(thd, want_privilege, tl->db, &tl->grant.privilege, 0, 0, test(tl->schema_table)) || - check_grant(thd, want_privilege, tl, 0, 1, 0)) + check_grant(thd, want_privilege, tl, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); } } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 43d0b9fade0..87092fb14ca 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -269,11 +269,11 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, */ if ((check_access(thd, CREATE_VIEW_ACL, view->db, &view->grant.privilege, 0, 0, is_schema_db(view->db)) || - check_grant(thd, CREATE_VIEW_ACL, view, 0, 1, 0)) || + check_grant(thd, CREATE_VIEW_ACL, view, FALSE, 1, FALSE)) || (mode != VIEW_CREATE_NEW && (check_access(thd, DROP_ACL, view->db, &view->grant.privilege, 0, 0, is_schema_db(view->db)) || - check_grant(thd, DROP_ACL, view, 0, 1, 0)))) + check_grant(thd, DROP_ACL, view, FALSE, 1, FALSE)))) goto err; for (sl= select_lex; sl; sl= sl->next_select()) @@ -323,7 +323,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, { if (check_access(thd, SELECT_ACL, tbl->db, &tbl->grant.privilege, 0, 0, test(tbl->schema_table)) || - check_grant(thd, SELECT_ACL, tbl, 0, 1, 0)) + check_grant(thd, SELECT_ACL, tbl, FALSE, 1, FALSE)) goto err; } } @@ -1233,8 +1233,9 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, if (!table->prelocking_placeholder && (old_lex->sql_command == SQLCOM_SELECT && old_lex->describe)) { - if (check_table_access(thd, SELECT_ACL, view_tables, UINT_MAX, TRUE) && - check_table_access(thd, SHOW_VIEW_ACL, table, UINT_MAX, TRUE)) + if (check_table_access(thd, SELECT_ACL, view_tables, FALSE, + UINT_MAX, TRUE) && + check_table_access(thd, SHOW_VIEW_ACL, table, FALSE, UINT_MAX, TRUE)) { my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0)); goto err; @@ -1244,7 +1245,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, (old_lex->sql_command == SQLCOM_SHOW_CREATE) && !table->belong_to_view) { - if (check_table_access(thd, SHOW_VIEW_ACL, table, UINT_MAX, FALSE)) + if (check_table_access(thd, SHOW_VIEW_ACL, table, FALSE, UINT_MAX, FALSE)) goto err; } |