diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index 73d5bf94264..ae550649951 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -1019,4 +1019,79 @@ DROP DATABASE mysqltest2; DROP DATABASE mysqltest3; DROP DATABASE mysqltest4; DROP USER mysqltest_1@localhost; +DROP DATABASE IF EXISTS mysqltest1; +DROP DATABASE IF EXISTS mysqltest2; +CREATE DATABASE mysqltest1; +CREATE DATABASE mysqltest2; +GRANT ALL PRIVILEGES ON mysqltest1.* TO mysqltest_1@localhost; +GRANT SELECT ON mysqltest2.* TO mysqltest_1@localhost; +CREATE PROCEDURE mysqltest1.p1() SQL SECURITY INVOKER +SELECT 1; + +---> connection: bug27337_con1 +CREATE TABLE t1(c INT); +ERROR 42000: CREATE command denied to user 'mysqltest_1'@'localhost' for table 't1' +CALL mysqltest1.p1(); +1 +1 +CREATE TABLE t1(c INT); +ERROR 42000: CREATE command denied to user 'mysqltest_1'@'localhost' for table 't1' + +---> connection: bug27337_con2 +CREATE TABLE t1(c INT); +ERROR 42000: CREATE command denied to user 'mysqltest_1'@'localhost' for table 't1' +SHOW TABLES; +Tables_in_mysqltest2 + +---> connection: default +DROP DATABASE mysqltest1; +DROP DATABASE mysqltest2; +DROP USER mysqltest_1@localhost; +DROP DATABASE IF EXISTS mysqltest1; +DROP DATABASE IF EXISTS mysqltest2; +CREATE DATABASE mysqltest1; +CREATE DATABASE mysqltest2; +CREATE TABLE mysqltest1.t1(c INT); +CREATE TABLE mysqltest2.t2(c INT); +GRANT SELECT ON mysqltest1.t1 TO mysqltest_1@localhost; +GRANT SELECT ON mysqltest2.t2 TO mysqltest_2@localhost; + +---> connection: bug27337_con1 +SHOW TABLES FROM mysqltest1; +Tables_in_mysqltest1 +t1 +PREPARE stmt1 FROM 'SHOW TABLES FROM mysqltest1'; +EXECUTE stmt1; +Tables_in_mysqltest1 +t1 + +---> connection: bug27337_con2 +SHOW COLUMNS FROM mysqltest2.t2; +Field Type Null Key Default Extra +c int(11) YES NULL +PREPARE stmt2 FROM 'SHOW COLUMNS FROM mysqltest2.t2'; +EXECUTE stmt2; +Field Type Null Key Default Extra +c int(11) YES NULL + +---> connection: default +REVOKE SELECT ON mysqltest1.t1 FROM mysqltest_1@localhost; +REVOKE SELECT ON mysqltest2.t2 FROM mysqltest_2@localhost; + +---> connection: bug27337_con1 +SHOW TABLES FROM mysqltest1; +ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest1' +EXECUTE stmt1; +ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest1' + +---> connection: bug27337_con2 +SHOW COLUMNS FROM mysqltest2.t2; +ERROR 42000: SELECT command denied to user 'mysqltest_2'@'localhost' for table 't2' +EXECUTE stmt2; +ERROR 42000: SELECT command denied to user 'mysqltest_2'@'localhost' for table 't2' + +---> connection: default +DROP DATABASE mysqltest1; +DROP DATABASE mysqltest2; +DROP USER mysqltest_1@localhost; End of 5.0 tests diff --git a/mysql-test/t/grant.test b/mysql-test/t/grant.test index 92ed69d3f4b..623bd4363cb 100644 --- a/mysql-test/t/grant.test +++ b/mysql-test/t/grant.test @@ -958,4 +958,148 @@ DROP DATABASE mysqltest4; DROP USER mysqltest_1@localhost; +# +# BUG#27337: Privileges are not restored properly. +# +# Actually, the patch for this bugs fixes two problems. So, here are two test +# cases. + +# Test case 1: privileges are not restored properly after calling a stored +# routine defined with SQL SECURITY INVOKER clause. + +# Prepare. + +--disable_warnings +DROP DATABASE IF EXISTS mysqltest1; +DROP DATABASE IF EXISTS mysqltest2; +--enable_warnings + +CREATE DATABASE mysqltest1; +CREATE DATABASE mysqltest2; + +GRANT ALL PRIVILEGES ON mysqltest1.* TO mysqltest_1@localhost; +GRANT SELECT ON mysqltest2.* TO mysqltest_1@localhost; + +CREATE PROCEDURE mysqltest1.p1() SQL SECURITY INVOKER + SELECT 1; + +# Test. + +--connect (bug27337_con1,localhost,mysqltest_1,,mysqltest2) +--echo +--echo ---> connection: bug27337_con1 + +--error ER_TABLEACCESS_DENIED_ERROR +CREATE TABLE t1(c INT); + +CALL mysqltest1.p1(); + +--error ER_TABLEACCESS_DENIED_ERROR +CREATE TABLE t1(c INT); + +--disconnect bug27337_con1 + +--connect (bug27337_con2,localhost,mysqltest_1,,mysqltest2) +--echo +--echo ---> connection: bug27337_con2 + +--error ER_TABLEACCESS_DENIED_ERROR +CREATE TABLE t1(c INT); + +SHOW TABLES; + +# Cleanup. + +--connection default +--echo +--echo ---> connection: default + +--disconnect bug27337_con2 + +DROP DATABASE mysqltest1; +DROP DATABASE mysqltest2; + +DROP USER mysqltest_1@localhost; + +# Test case 2: priveleges are not checked properly for prepared statements. + +# Prepare. + +--disable_warnings +DROP DATABASE IF EXISTS mysqltest1; +DROP DATABASE IF EXISTS mysqltest2; +--enable_warnings + +CREATE DATABASE mysqltest1; +CREATE DATABASE mysqltest2; + +CREATE TABLE mysqltest1.t1(c INT); +CREATE TABLE mysqltest2.t2(c INT); + +GRANT SELECT ON mysqltest1.t1 TO mysqltest_1@localhost; +GRANT SELECT ON mysqltest2.t2 TO mysqltest_2@localhost; + +# Test. + +--connect (bug27337_con1,localhost,mysqltest_1,,mysqltest1) +--echo +--echo ---> connection: bug27337_con1 + +SHOW TABLES FROM mysqltest1; + +PREPARE stmt1 FROM 'SHOW TABLES FROM mysqltest1'; + +EXECUTE stmt1; + +--connect (bug27337_con2,localhost,mysqltest_2,,mysqltest2) +--echo +--echo ---> connection: bug27337_con2 + +SHOW COLUMNS FROM mysqltest2.t2; + +PREPARE stmt2 FROM 'SHOW COLUMNS FROM mysqltest2.t2'; + +EXECUTE stmt2; + +--connection default +--echo +--echo ---> connection: default + +REVOKE SELECT ON mysqltest1.t1 FROM mysqltest_1@localhost; +REVOKE SELECT ON mysqltest2.t2 FROM mysqltest_2@localhost; + +--connection bug27337_con1 +--echo +--echo ---> connection: bug27337_con1 + +--error ER_DBACCESS_DENIED_ERROR +SHOW TABLES FROM mysqltest1; + +--error ER_DBACCESS_DENIED_ERROR +EXECUTE stmt1; + +--connection bug27337_con2 +--echo +--echo ---> connection: bug27337_con2 + +--error ER_TABLEACCESS_DENIED_ERROR +SHOW COLUMNS FROM mysqltest2.t2; + +--error ER_TABLEACCESS_DENIED_ERROR +EXECUTE stmt2; + +# Cleanup. + +--connection default +--echo +--echo ---> connection: default + +--disconnect bug27337_con2 + +DROP DATABASE mysqltest1; +DROP DATABASE mysqltest2; + +DROP USER mysqltest_1@localhost; + + --echo End of 5.0 tests diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 5e37cd8987f..629ffb93b97 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -955,6 +955,8 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond); int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond); bool get_schema_tables_result(JOIN *join, enum enum_schema_table_state executed_place); +enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table); + #define is_schema_db(X) \ !my_strcasecmp(system_charset_info, INFORMATION_SCHEMA_NAME.str, (X)) diff --git a/sql/sql_db.cc b/sql/sql_db.cc index d7aecb78363..963457cc896 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1308,30 +1308,27 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) DBUG_PRINT("info",("Use database: %s", new_db_file_name.str)); #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (!force_switch) /* FIXME: this is BUG#27337. */ - { - db_access= - test_all_bits(sctx->master_access, DB_ACLS) ? - DB_ACLS : - acl_get(sctx->host, - sctx->ip, - sctx->priv_user, - new_db_file_name.str, - FALSE) | sctx->master_access; + db_access= + test_all_bits(sctx->master_access, DB_ACLS) ? + DB_ACLS : + acl_get(sctx->host, + sctx->ip, + sctx->priv_user, + new_db_file_name.str, + FALSE) | sctx->master_access; - if (!force_switch && - !(db_access & DB_ACLS) && - (!grant_option || check_grant_db(thd, new_db_file_name.str))) - { - my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), - sctx->priv_user, - sctx->priv_host, - new_db_file_name.str); - mysql_log.write(thd, COM_INIT_DB, ER(ER_DBACCESS_DENIED_ERROR), - sctx->priv_user, sctx->priv_host, new_db_file_name.str); - my_free(new_db_file_name.str, MYF(0)); - DBUG_RETURN(TRUE); - } + if (!force_switch && + !(db_access & DB_ACLS) && + (!grant_option || check_grant_db(thd, new_db_file_name.str))) + { + my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), + sctx->priv_user, + sctx->priv_host, + new_db_file_name.str); + mysql_log.write(thd, COM_INIT_DB, ER(ER_DBACCESS_DENIED_ERROR), + sctx->priv_user, sctx->priv_host, new_db_file_name.str); + my_free(new_db_file_name.str, MYF(0)); + DBUG_RETURN(TRUE); } #endif diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index cbf9e374dac..ac75e6ea2fa 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2237,7 +2237,8 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, enum enum_schema_tables schema_table_idx) { DBUG_ENTER("prepare_schema_table"); - SELECT_LEX *sel= 0; + SELECT_LEX *schema_select_lex= NULL; + switch (schema_table_idx) { case SCH_SCHEMATA: #if defined(DONT_ALLOW_SHOW_COMMANDS) @@ -2245,11 +2246,9 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */ DBUG_RETURN(1); #else - if ((specialflag & SPECIAL_SKIP_SHOW_DB) && - check_global_access(thd, SHOW_DB_ACL)) - DBUG_RETURN(1); break; #endif + case SCH_TABLE_NAMES: case SCH_TABLES: case SCH_VIEWS: @@ -2259,32 +2258,25 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */ DBUG_RETURN(1); #else + if (lex->select_lex.db == NULL && + thd->copy_db_to(&lex->select_lex.db, NULL)) { - char *db; - if (lex->select_lex.db == NULL && - thd->copy_db_to(&lex->select_lex.db, 0)) - { - DBUG_RETURN(1); - } - db= lex->select_lex.db; - remove_escape(db); // Fix escaped '_' - if (check_db_name(db)) - { - my_error(ER_WRONG_DB_NAME, MYF(0), db); - DBUG_RETURN(1); - } - if (check_access(thd, SELECT_ACL, db, &thd->col_access, 0, 0, - is_schema_db(db))) - DBUG_RETURN(1); /* purecov: inspected */ - if (!thd->col_access && check_grant_db(thd,db)) - { - my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), - thd->security_ctx->priv_user, thd->security_ctx->priv_host, - db); - DBUG_RETURN(1); - } - break; + DBUG_RETURN(1); } + + schema_select_lex= new SELECT_LEX(); + schema_select_lex->db= lex->select_lex.db; + schema_select_lex->table_list.first= NULL; + remove_escape(schema_select_lex->db); // Fix escaped '_' + + if (check_db_name(schema_select_lex->db)) + { + my_error(ER_WRONG_DB_NAME, MYF(0), schema_select_lex->db); + DBUG_RETURN(1); + } + + + break; #endif case SCH_COLUMNS: case SCH_STATISTICS: @@ -2293,28 +2285,23 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */ DBUG_RETURN(1); #else - if (table_ident) { + DBUG_ASSERT(table_ident); + TABLE_LIST **query_tables_last= lex->query_tables_last; - sel= new SELECT_LEX(); + schema_select_lex= new SELECT_LEX(); /* 'parent_lex' is used in init_query() so it must be before it. */ - sel->parent_lex= lex; - sel->init_query(); - if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, - (List *) 0, (List *) 0)) + schema_select_lex->parent_lex= lex; + schema_select_lex->init_query(); + if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ, + (List *) 0, (List *) 0)) DBUG_RETURN(1); lex->query_tables_last= query_tables_last; - TABLE_LIST *table_list= (TABLE_LIST*) sel->table_list.first; - char *db= table_list->db; - remove_escape(db); // Fix escaped '_' - remove_escape(table_list->table_name); - if (check_access(thd,SELECT_ACL | EXTRA_ACL,db, - &table_list->grant.privilege, 0, 0, - test(table_list->schema_table))) - DBUG_RETURN(1); /* purecov: inspected */ - if (grant_option && check_grant(thd, SELECT_ACL, table_list, 2, - UINT_MAX, 0)) - DBUG_RETURN(1); + + TABLE_LIST *dst_table= (TABLE_LIST*) schema_select_lex->table_list.first; + remove_escape(dst_table->db); // Fix escaped '_' + remove_escape(dst_table->table_name); + break; } #endif @@ -2341,7 +2328,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, DBUG_RETURN(1); } TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first; - table_list->schema_select_lex= sel; + table_list->schema_select_lex= schema_select_lex; table_list->schema_table_reformed= 1; statistic_increment(thd->status_var.com_stat[lex->orig_sql_command], &LOCK_status); @@ -5390,6 +5377,83 @@ bool check_global_access(THD *thd, ulong want_access) } +static bool check_show_access(THD *thd, TABLE_LIST *table) +{ + switch (get_schema_table_idx(table->schema_table)) + { + case SCH_SCHEMATA: + return (specialflag & SPECIAL_SKIP_SHOW_DB) && + check_global_access(thd, SHOW_DB_ACL); + + case SCH_TABLE_NAMES: + case SCH_TABLES: + case SCH_VIEWS: + case SCH_TRIGGERS: + { + const char *dst_db_name= table->schema_select_lex->db; + + DBUG_ASSERT(dst_db_name); + + if (check_access(thd, SELECT_ACL, dst_db_name, + &thd->col_access, FALSE, FALSE, + is_schema_db(dst_db_name))) + { + return TRUE; + } + + if (!thd->col_access && check_grant_db(thd, dst_db_name)) + { + my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), + thd->security_ctx->priv_user, + thd->security_ctx->priv_host, + dst_db_name); + return TRUE; + } + + return FALSE; + } + + case SCH_COLUMNS: + case SCH_STATISTICS: + { + TABLE_LIST *dst_table= + (TABLE_LIST *) table->schema_select_lex->table_list.first; + + DBUG_ASSERT(dst_table); + + if (check_access(thd, SELECT_ACL | EXTRA_ACL, + dst_table->db, + &dst_table->grant.privilege, + FALSE, FALSE, + test(dst_table->schema_table))) + { + return FALSE; + } + + return grant_option && + check_grant(thd, SELECT_ACL, dst_table, 2, UINT_MAX, FALSE); + } + + case SCH_OPEN_TABLES: + case SCH_VARIABLES: + case SCH_STATUS: + case SCH_PROCEDURES: + case SCH_CHARSETS: + case SCH_COLLATIONS: + case SCH_COLLATION_CHARACTER_SET_APPLICABILITY: + case SCH_USER_PRIVILEGES: + case SCH_SCHEMA_PRIVILEGES: + case SCH_TABLE_PRIVILEGES: + case SCH_COLUMN_PRIVILEGES: + case SCH_TABLE_CONSTRAINTS: + case SCH_KEY_COLUMN_USAGE: + break; + } + + return FALSE; +} + + /* Check the privilege for all used tables. @@ -5450,7 +5514,16 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, Remove SHOW_VIEW_ACL, because it will be checked during making view */ tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL); - if (tables->derived || tables->schema_table || + + if (tables->schema_table_reformed) + { + if (check_show_access(thd, tables)) + goto deny; + + continue; + } + + if (tables->derived || (tables->table && (int)tables->table->s->tmp_table) || my_tz_check_n_skip_implicit_tables(&tables, thd->lex->time_zone_tables_used)) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index cf0cb9ba8d2..fa78db50532 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2148,7 +2148,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) */ thd->reset_n_backup_open_tables_state(&open_tables_state_backup); - if (lsel) + if (lsel && lsel->table_list.first) { TABLE_LIST *show_table_list= (TABLE_LIST*) lsel->table_list.first; bool res;