#At file:///home/dlenev/src/bzr/mysql-trunk-bug27480/ based on revid:dmitry.lenev@stripped
3533 Dmitry Lenev 2011-03-26
A follow-up for the patch for Bug#11746602 (27480: Extend
CREATE TEMPORARY TABLES privilege to allow temp table
operations).
After main patch for this bug additional check for privileges
required for SHOW statements might require opening and closing
of temporary tables. Since doing this from within
check_table_access() function looks like a dangerous thing,
the current patch moves this additional check outside of
this function. To support this change it also removes some
duplicated code.
@ sql/sql_parse.cc
- Moved code responsible for the first stage of checking
privileges for SELECT statement (global, db and table-level
privileges) to a separate function - select_precheck().
Adjusted code for select-like statements to use this
function.
- Got rid of duplicate code handling SHOW EVENTS and SHOW
PROCEDURE/FUNCTION STATUS. As a side-effect of this change
now these statements reset last_query_cost status variable
like most of other SHOW statements.
- Moved code which performs additional check for privileges
required to perform SHOW statement from check_table_access()
function to newly created select_precheck() function. Doing
this privilege check, which might require opening and closing
of temporary tables, inside of check_table_access() looks
like a dangerous thing.
- Got rid of redundant code in check_show_access() which
automatically granted SELECT_ACL on I_S table for SHOW
statement. This is already done for all I_S tables in
IS_internal_schema_access::check() member function.
@ sql/sql_parse.h
Introduced select_precheck() function which performs first
stage of privilege checking for SELECT statements.
@ sql/sql_prepare.cc
Code responsible for the first stage of checking privileges
for SELECT statements (global, db and table-level privileges)
has been moved to new function select_precheck().
modified:
sql/sql_parse.cc
sql/sql_parse.h
sql/sql_prepare.cc
=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc 2011-03-26 10:56:27 +0000
+++ b/sql/sql_parse.cc 2011-03-26 16:51:13 +0000
@@ -113,6 +113,7 @@
"FUNCTION" : "PROCEDURE")
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
+static bool check_show_access(THD *thd, TABLE_LIST *table);
static void sql_kill(THD *thd, ulong id, bool only_kill_query);
static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables);
@@ -2109,25 +2110,14 @@ mysql_execute_command(THD *thd)
switch (lex->sql_command) {
- case SQLCOM_SHOW_EVENTS:
-#ifndef HAVE_EVENT_SCHEDULER
- my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
- break;
-#endif
- case SQLCOM_SHOW_STATUS_PROC:
- case SQLCOM_SHOW_STATUS_FUNC:
- if ((res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
- UINT_MAX, FALSE)))
- goto error;
- 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, FALSE,
- UINT_MAX, FALSE)))
+
+ if (!(res= select_precheck(thd, lex, all_tables, first_table)))
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 |
SERVER_QUERY_NO_GOOD_INDEX_USED);
@@ -2142,6 +2132,13 @@ mysql_execute_command(THD *thd)
mysql_mutex_unlock(&LOCK_status);
break;
}
+ case SQLCOM_SHOW_EVENTS:
+#ifndef HAVE_EVENT_SCHEDULER
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
+ break;
+#endif
+ case SQLCOM_SHOW_STATUS_PROC:
+ case SQLCOM_SHOW_STATUS_FUNC:
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_TABLES:
case SQLCOM_SHOW_TRIGGERS:
@@ -2159,21 +2156,7 @@ mysql_execute_command(THD *thd)
{
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,
- privileges_requested,
- all_tables, FALSE, UINT_MAX, FALSE);
- else
- res= check_access(thd, privileges_requested, any_db, NULL, NULL, 0, 0);
-
- if (res)
+ if ((res= select_precheck(thd, lex, all_tables, first_table)))
break;
res= execute_sqlcom_select(thd, all_tables);
@@ -4937,20 +4920,19 @@ check_access(THD *thd, ulong want_access
}
+/**
+ Check if user has enough privileges for execution of SHOW statement,
+ which was converted to query to one of I_S tables.
+
+ @param thd Thread context.
+ @param table Table list element for I_S table to be queried..
+
+ @retval FALSE - Success.
+ @retval TRUE - Failure.
+*/
+
static bool check_show_access(THD *thd, TABLE_LIST *table)
{
- /*
- This is a SHOW command using an INFORMATION_SCHEMA table.
- check_access() has not been called for 'table',
- and SELECT is currently always granted on the I_S, so we automatically
- grant SELECT on table here, to bypass a call to check_access().
- Note that not calling check_access(table) is an optimization,
- which needs to be revisited if the INFORMATION_SCHEMA does
- not always automatically grant SELECT but use the grant tables.
- See Bug#38837 need a way to disable information_schema for security
- */
- table->grant.privilege= SELECT_ACL;
-
switch (get_schema_table_idx(table->schema_table)) {
case SCH_SCHEMATA:
return (specialflag & SPECIAL_SKIP_SHOW_DB) &&
@@ -5088,13 +5070,6 @@ check_table_access(THD *thd, ulong requi
*/
tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
- if (tables->schema_table_reformed)
- {
- if (check_show_access(thd, tables))
- goto deny;
- continue;
- }
-
DBUG_PRINT("info", ("derived: %d view: %d", tables->derived != 0,
tables->view != 0));
@@ -5223,6 +5198,13 @@ bool check_some_access(THD *thd, ulong w
DBUG_RETURN(1);
}
+#else
+
+static bool check_show_access(THD *thd, TABLE_LIST *table)
+{
+ return false;
+}
+
#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
@@ -6672,6 +6654,45 @@ Item * all_any_subquery_creator(Item *le
/**
+ Perform first stage of privilege checking for SELECT statement.
+
+ @param thd Thread context.
+ @param lex LEX for SELECT statement.
+ @param tables List of tables used by statement.
+ @param first_table First table in the main SELECT of the SELECT
+ statement.
+
+ @retval FALSE - Success (column-level privilege checks might be required).
+ @retval TRUE - Failure, privileges are insufficient.
+*/
+
+bool select_precheck(THD *thd, LEX *lex, TABLE_LIST *tables,
+ TABLE_LIST *first_table)
+{
+ bool res;
+ /*
+ 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 (tables)
+ {
+ res= check_table_access(thd,
+ privileges_requested,
+ tables, FALSE, UINT_MAX, FALSE) ||
+ (first_table && first_table->schema_table_reformed &&
+ check_show_access(thd, first_table));
+ }
+ else
+ res= check_access(thd, privileges_requested, any_db, NULL, NULL, 0, 0);
+
+ return res;
+}
+
+
+/**
Multi update query pre-check.
@param thd Thread handler
=== modified file 'sql/sql_parse.h'
--- a/sql/sql_parse.h 2011-02-18 09:56:51 +0000
+++ b/sql/sql_parse.h 2011-03-26 16:51:13 +0000
@@ -35,6 +35,8 @@ enum enum_mysql_completiontype {
extern "C" int test_if_data_home_dir(const char *dir);
+bool select_precheck(THD *thd, LEX *lex, TABLE_LIST *tables,
+ TABLE_LIST *first_table);
bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables);
int mysql_multi_update_prepare(THD *thd);
=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc 2011-03-26 10:56:27 +0000
+++ b/sql/sql_prepare.cc 2011-03-26 16:51:13 +0000
@@ -1457,13 +1457,7 @@ static int mysql_test_select(Prepared_st
lex->select_lex.context.resolve_in_select_list= TRUE;
- ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
- if (tables)
- {
- if (check_table_access(thd, privilege, tables, FALSE, UINT_MAX, FALSE))
- goto error;
- }
- else if (check_access(thd, privilege, any_db, NULL, NULL, 0, 0))
+ if (select_precheck(thd, lex, tables, lex->select_lex.table_list.first))
goto error;
if (!lex->result && !(lex->result= new (stmt->mem_root) select_send))
Attachment: [text/bzr-bundle] bzr/dmitry.lenev@oracle.com-20110326165113-99z45gv5gbex9yc5.bundle