List:Commits« Previous MessageNext Message »
From:Dmitry Lenev Date:March 28 2011 11:49am
Subject:bzr commit into mysql-trunk branch (Dmitry.Lenev:3533) Bug#11746602
View as plain text  
#At file:///home/dlenev/src/bzr/mysql-trunk-bug27480/ based on revid:dmitry.lenev@stripped

 3533 Dmitry Lenev	2011-03-28
      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-28 11:49:45 +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,12 +5070,16 @@ 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;
-    }
+    /*
+      We should not encounter table list elements for reformed SHOW
+      statements unless this is first table list element in the main
+      select.
+      Such table list elements require additional privilege check
+      (see check_show_access()). This check is carried out by caller,
+      but only for the first table list element from the main select.
+    */
+    DBUG_ASSERT(!tables->schema_table_reformed ||
+                tables == thd->lex->select_lex.table_list.first);
 
     DBUG_PRINT("info", ("derived: %d  view: %d", tables->derived != 0,
                         tables->view != 0));
@@ -5223,6 +5209,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 +6665,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-28 11:49:45 +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-28 11:49:45 +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-20110328114945-mn6r9cxwywygq3f8.bundle
Thread
bzr commit into mysql-trunk branch (Dmitry.Lenev:3533) Bug#11746602Dmitry Lenev28 Mar