List:Commits« Previous MessageNext Message »
From:Martin Hansson Date:January 1 1970 12:00am
Subject:bzr commit into mysql-5.1 branch (mhansson:2677) Bug#36086
View as plain text  
#At file:///data0/martin/bzr/5.1-5.1.29-rc-privilege-bugs/

 2677 Martin Hansson	2008-09-03
      Bug#36086: SELECT * from views don't check column grants
      This patch also fixes bugs 36963 and 35600.
                            
      - In many places a view was confused with an anonymous derived
        table, i.e. access checking was skipped. Fixed by introducing a
        predicate to tell the difference between named and anonymous
        derived tables.
                            
      - When inserting fields for "SELECT * ", there was no 
        distinction between base tables and views, where one should be
        made. View privileges are checked elsewhere.
modified:
  mysql-test/include/grant_cache.inc
  mysql-test/r/grant2.result
  mysql-test/r/grant_cache_no_prot.result
  mysql-test/r/grant_cache_ps_prot.result
  mysql-test/r/view_grant.result
  mysql-test/t/grant2.test
  mysql-test/t/view_grant.test
  sql/item.cc
  sql/sql_acl.cc
  sql/sql_base.cc
  sql/sql_cache.cc
  sql/sql_derived.cc
  sql/sql_parse.cc
  sql/table.cc
  sql/table.h

per-file messages:
  mysql-test/include/grant_cache.inc
    Bug#36086: Changed test case.
  mysql-test/r/grant2.result
    Bug#36086: Changed test result.
  mysql-test/r/grant_cache_no_prot.result
    Bug#36086: Changed test result.
  mysql-test/r/grant_cache_ps_prot.result
    Bug#36086: Changed test result.
  mysql-test/r/view_grant.result
    Bug#36086: Test result.
  mysql-test/t/grant2.test
    Bug#36086: Changed test case.
  mysql-test/t/view_grant.test
    Bug#36086: Test case.
  sql/item.cc
    Bug#36086: Replaced conditional with new methods.
  sql/sql_acl.cc
    Bug no 35600: 
    In mysql_table_grant:
      Replaced conditional with the new accessor method.
    
    In check_grant:
     - Changed the requirement table->derived != null to 
       checking all anonymous derived tables.
     - Use of the accessor methods for getting object and database 
       names.
        
    Bug#36086: In check_grant_all_columns:
      - Updated comment. This function is now called for views
        as well.
      - The error message should not disclose any column names 
        unless the user has privilege to see all column names.
      - Changed names of Field_iterator_table_ref methods.
  sql/sql_base.cc
    Bug no 36963: In insert_fields()
      - Commented.
      - We should call check_grant_all_columns() for views in  
        this case.        
      - Changed names of Field_iterator_table_ref methods.
      - We should not disclose column names in the error message
        when the user has no approprate privilege.
  sql/sql_cache.cc
    Bug#36086: Replaced test with new predicate method.
  sql/sql_derived.cc
    Bug#36086: commenting only. Updated and doxygenated
    comment for mysql_derived_prepare().
  sql/sql_parse.cc
    Bug no 35600: 
    - In check_single_table_access:
      Due to the bug, check_grant would raise an error for a
      SHOW CREATE TABLE command for a TEMPTABLE view. It should in
      fact not be be invoked in this case. This table privilege
      is checked already.
      There is a test case for this in information_schema_db.test.
        
    - In check_access: replaced table->derived
  sql/table.cc
    Bug#36086: 
    
    - In TABLE_LIST::set_underlying_merge(): 
      Commenting only. Doxygenated, corrected spelling,
      added.
    
    - Renamed table_name() and db_name() methods of 
      Field_iterator_table_ref in order to be consistent
      with new methods in TABLE_LIST.
  sql/table.h
    Bug#36086: 
      - Commented GRANT_INFO.
      - Added a predicate is_anonymous_derived_table() to    
        TABLE_LIST.
      - Added get_table_name() and get_db_name() to   
        TABLE_LIST in order to hide the disparate   
        representation of these properties.
=== modified file 'mysql-test/include/grant_cache.inc'
--- a/mysql-test/include/grant_cache.inc	2007-05-24 20:13:49 +0000
+++ b/mysql-test/include/grant_cache.inc	2008-09-03 13:57:13 +0000
@@ -136,7 +136,7 @@ connect (user3,localhost,mysqltest_3,,my
 connection user3;
 select "user3";
 --replace_result 127.0.0.1 localhost
---error ER_COLUMNACCESS_DENIED_ERROR
+--error ER_TABLEACCESS_DENIED_ERROR
 select * from t1;
 select a from t1;
 --replace_result 127.0.0.1 localhost

=== modified file 'mysql-test/r/grant2.result'
--- a/mysql-test/r/grant2.result	2007-09-27 09:32:59 +0000
+++ b/mysql-test/r/grant2.result	2008-09-03 13:57:13 +0000
@@ -434,7 +434,7 @@ USE db1;
 SELECT c FROM t2;
 ERROR 42000: SELECT command denied to user 'mysqltest1'@'localhost' for column 'c' in table 't2'
 SELECT * FROM t2;
-ERROR 42000: SELECT command denied to user 'mysqltest1'@'localhost' for column 'c' in table 't2'
+ERROR 42000: SELECT command denied to user 'mysqltest1'@'localhost' for table 't2'
 SELECT * FROM t1 JOIN t2 USING (b);
 ERROR 42000: SELECT command denied to user 'mysqltest1'@'localhost' for column 'c' in table 't2'
 DROP TABLE db1.t1, db1.t2;

=== modified file 'mysql-test/r/grant_cache_no_prot.result'
--- a/mysql-test/r/grant_cache_no_prot.result	2007-05-24 20:13:49 +0000
+++ b/mysql-test/r/grant_cache_no_prot.result	2008-09-03 13:57:13 +0000
@@ -155,7 +155,7 @@ select "user3";
 user3
 user3
 select * from t1;
-ERROR 42000: SELECT command denied to user 'mysqltest_3'@'localhost' for column 'b' in table 't1'
+ERROR 42000: SELECT command denied to user 'mysqltest_3'@'localhost' for table 't1'
 select a from t1;
 a
 1

=== modified file 'mysql-test/r/grant_cache_ps_prot.result'
--- a/mysql-test/r/grant_cache_ps_prot.result	2007-05-24 20:13:49 +0000
+++ b/mysql-test/r/grant_cache_ps_prot.result	2008-09-03 13:57:13 +0000
@@ -155,7 +155,7 @@ select "user3";
 user3
 user3
 select * from t1;
-ERROR 42000: SELECT command denied to user 'mysqltest_3'@'localhost' for column 'b' in table 't1'
+ERROR 42000: SELECT command denied to user 'mysqltest_3'@'localhost' for table 't1'
 select a from t1;
 a
 1

=== modified file 'mysql-test/r/view_grant.result'
--- a/mysql-test/r/view_grant.result	2008-03-22 08:01:31 +0000
+++ b/mysql-test/r/view_grant.result	2008-09-03 13:57:13 +0000
@@ -957,3 +957,35 @@ Warning	1356	View 'test.v1' references i
 DROP VIEW v1;
 DROP TABLE t1;
 End of 5.1 tests.
+CREATE USER mysqluser1@localhost;
+CREATE DATABASE mysqltest1;
+USE mysqltest1;
+CREATE TABLE t1 ( a INT, b INT );
+CREATE TABLE t2 ( a INT, b INT );
+CREATE VIEW v1 AS SELECT a, b FROM t1;
+GRANT SELECT( a ) ON v1 TO mysqluser1@localhost;
+GRANT UPDATE( b ) ON t2 TO mysqluser1@localhost;
+SELECT * FROM mysqltest1.v1;
+ERROR 42000: SELECT command denied to user 'mysqluser1'@'localhost' for table 'v1'
+CREATE VIEW v1 AS SELECT * FROM mysqltest1.t2;
+ERROR 42000: ANY command denied to user 'mysqluser1'@'localhost' for table 't2'
+DROP TABLE t1, t2;
+DROP VIEW v1;
+DROP DATABASE mysqltest1;
+DROP USER mysqluser1@localhost;
+CREATE USER mysqluser1@localhost;
+CREATE DATABASE mysqltest1;
+USE mysqltest1;
+CREATE VIEW v1 AS SELECT * FROM information_schema.tables LIMIT 1;
+CREATE ALGORITHM = TEMPTABLE VIEW v2 AS SELECT 1 AS A;
+GRANT SELECT ON mysqltest1.* to mysqluser1@localhost;
+PREPARE stmt_v1     FROM "SELECT * FROM mysqltest1.v1";
+PREPARE stmt_v2 FROM "SELECT * FROM mysqltest1.v2";
+REVOKE SELECT ON mysqltest1.* FROM mysqluser1@localhost;
+EXECUTE stmt_v1;
+ERROR 42000: SELECT command denied to user 'mysqluser1'@'localhost' for table 'v1'
+EXECUTE stmt_v2;
+ERROR 42000: SELECT command denied to user 'mysqluser1'@'localhost' for table 'v2'
+DROP VIEW v1, v2;
+DROP DATABASE mysqltest1;
+DROP USER mysqluser1@localhost;

=== modified file 'mysql-test/t/grant2.test'
--- a/mysql-test/t/grant2.test	2007-09-27 09:32:59 +0000
+++ b/mysql-test/t/grant2.test	2008-09-03 13:57:13 +0000
@@ -605,7 +605,7 @@ connection conn1;
 USE db1;
 --error ER_COLUMNACCESS_DENIED_ERROR
 SELECT c FROM t2;
---error ER_COLUMNACCESS_DENIED_ERROR
+--error ER_TABLEACCESS_DENIED_ERROR
 SELECT * FROM t2;
 --error ER_COLUMNACCESS_DENIED_ERROR
 SELECT * FROM t1 JOIN t2 USING (b);

=== modified file 'mysql-test/t/view_grant.test'
--- a/mysql-test/t/view_grant.test	2008-02-21 09:24:40 +0000
+++ b/mysql-test/t/view_grant.test	2008-09-03 13:57:13 +0000
@@ -1219,3 +1219,71 @@ DROP VIEW v1;
 DROP TABLE t1;
 
 --echo End of 5.1 tests.
+
+#
+# Bug#36086: SELECT * from views don't check column grants
+#
+CREATE USER mysqluser1@localhost;
+CREATE DATABASE mysqltest1;
+
+USE mysqltest1;
+
+CREATE TABLE t1 ( a INT, b INT );
+CREATE TABLE t2 ( a INT, b INT );
+
+CREATE VIEW v1 AS SELECT a, b FROM t1;
+
+GRANT SELECT( a ) ON v1 TO mysqluser1@localhost;
+GRANT UPDATE( b ) ON t2 TO mysqluser1@localhost;
+
+--connect (connection1, localhost, mysqluser1, , test)
+
+--error ER_TABLEACCESS_DENIED_ERROR
+SELECT * FROM mysqltest1.v1;
+
+--error ER_TABLEACCESS_DENIED_ERROR
+CREATE VIEW v1 AS SELECT * FROM mysqltest1.t2;
+
+--disconnect connection1
+
+--connection default
+
+DROP TABLE t1, t2;
+DROP VIEW v1;
+DROP DATABASE mysqltest1;
+DROP USER mysqluser1@localhost;
+
+#
+# Bug#35600: Security breach via view, I_S table and prepared 
+# statement/stored procedure
+#
+CREATE USER mysqluser1@localhost;
+CREATE DATABASE mysqltest1;
+
+USE mysqltest1;
+
+CREATE VIEW v1 AS SELECT * FROM information_schema.tables LIMIT 1;
+CREATE ALGORITHM = TEMPTABLE VIEW v2 AS SELECT 1 AS A;
+
+--connection default
+GRANT SELECT ON mysqltest1.* to mysqluser1@localhost;
+
+--connect (connection1, localhost, mysqluser1, , test)
+PREPARE stmt_v1     FROM "SELECT * FROM mysqltest1.v1";
+PREPARE stmt_v2 FROM "SELECT * FROM mysqltest1.v2";
+
+--connection default
+REVOKE SELECT ON mysqltest1.* FROM mysqluser1@localhost;
+
+--connection connection1
+
+--error ER_TABLEACCESS_DENIED_ERROR
+EXECUTE stmt_v1;
+--error ER_TABLEACCESS_DENIED_ERROR
+EXECUTE stmt_v2;
+
+--disconnect connection1
+--connection default
+DROP VIEW v1, v2;
+DROP DATABASE mysqltest1;
+DROP USER mysqluser1@localhost;

=== modified file 'sql/item.cc'
--- a/sql/item.cc	2008-08-20 11:42:01 +0000
+++ b/sql/item.cc	2008-09-03 13:57:13 +0000
@@ -4120,16 +4120,8 @@ bool Item_field::fix_fields(THD *thd, It
   if (any_privileges)
   {
     char *db, *tab;
-    if (cached_table->view)
-    {
-      db= cached_table->view_db.str;
-      tab= cached_table->view_name.str;
-    }
-    else
-    {
-      db= cached_table->db;
-      tab= cached_table->table_name;
-    }
+    db= cached_table->get_db_name();
+    tab= cached_table->get_table_name();
     if (!(have_privileges= (get_column_grant(thd, &field->table->grant,
                                              db, tab, field_name) &
                             VIEW_ANY_ACL)))

=== modified file 'sql/sql_acl.cc'
--- a/sql/sql_acl.cc	2008-08-06 14:20:41 +0000
+++ b/sql/sql_acl.cc	2008-09-03 13:57:13 +0000
@@ -3092,12 +3092,8 @@ int mysql_table_grant(THD *thd, TABLE_LI
       continue;					// Add next user
     }
 
-    db_name= (table_list->view_db.length ?
-	      table_list->view_db.str :
-	      table_list->db);
-    table_name= (table_list->view_name.length ?
-		table_list->view_name.str :
-		table_list->table_name);
+    db_name= table_list->get_db_name();
+    table_name= table_list->get_table_name();
 
     /* Find/create cached table grant */
     grant_table= table_hash_search(Str->host.str, NullS, db_name,
@@ -3907,8 +3903,8 @@ bool check_grant(THD *thd, ulong want_ac
     if (!want_access)
       continue;                                 // ok
 
-    if (!(~table->grant.privilege & want_access) || 
-        table->derived || table->schema_table)
+    if (!(~table->grant.privilege & want_access) ||
+        table->is_anonymous_derived_table() || table->schema_table)
     {
       /*
         It is subquery in the FROM clause. VIEW set table->derived after
@@ -3926,8 +3922,8 @@ bool check_grant(THD *thd, ulong want_ac
       continue;
     }
     if (!(grant_table= table_hash_search(sctx->host, sctx->ip,
-                                         table->db, sctx->priv_user,
-                                         table->table_name,0)))
+                                         table->get_db_name(), sctx->priv_user,
+                                         table->get_table_name(), FALSE)))
     {
       want_access &= ~table->grant.privilege;
       goto err;					// No grants
@@ -3963,7 +3959,7 @@ err:
              command,
              sctx->priv_user,
              sctx->host_or_ip,
-             table ? table->table_name : "unknown");
+             table ? table->get_table_name() : "unknown");
   }
   DBUG_RETURN(1);
 }
@@ -4118,7 +4114,7 @@ bool check_column_grant_in_table_ref(THD
     @retval 1 Falure
   @details This function walks over the columns of a table reference 
    The columns may originate from different tables, depending on the kind of
-   table reference, e.g. join.
+   table reference, e.g. join, view.
    For each table it will retrieve the grant information and will use it
    to check the required access privileges for the fields requested from it.
 */    
@@ -4133,6 +4129,11 @@ bool check_grant_all_columns(THD *thd, u
   GRANT_INFO *grant;
   /* Initialized only to make gcc happy */
   GRANT_TABLE *grant_table= NULL;
+  /* 
+     Flag that is true if privilege checking had to be performed on column
+     level.
+  */
+  bool using_column_privileges= FALSE;
 
   rw_rdlock(&LOCK_grant);
 
@@ -4140,10 +4141,10 @@ bool check_grant_all_columns(THD *thd, u
   {
     const char *field_name= fields->name();
 
-    if (table_name != fields->table_name())
+    if (table_name != fields->get_table_name())
     {
-      table_name= fields->table_name();
-      db_name= fields->db_name();
+      table_name= fields->get_table_name();
+      db_name= fields->get_db_name();
       grant= fields->grant();
       /* get a fresh one for each table */
       want_access= want_access_arg & ~grant->privilege;
@@ -4169,6 +4170,8 @@ bool check_grant_all_columns(THD *thd, u
       GRANT_COLUMN *grant_column= 
         column_hash_search(grant_table, field_name,
                            (uint) strlen(field_name));
+      if (grant_column)
+        using_column_privileges= TRUE;
       if (!grant_column || (~grant_column->rights & want_access))
         goto err;
     }
@@ -4181,12 +4184,21 @@ err:
 
   char command[128];
   get_privilege_desc(command, sizeof(command), want_access);
-  my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
-           command,
-           sctx->priv_user,
-           sctx->host_or_ip,
-           fields->name(),
-           table_name);
+  /*
+    Do not give an error message listing a column name unless the user has
+    privilege to see all columns.
+  */
+  if (using_column_privileges)
+    my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
+             command, sctx->priv_user,
+             sctx->host_or_ip, table_name); 
+  else
+    my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
+             command,
+             sctx->priv_user,
+             sctx->host_or_ip,
+             fields->name(),
+             table_name);
   return 1;
 }
 

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2008-08-21 11:47:23 +0000
+++ b/sql/sql_base.cc	2008-09-03 13:57:13 +0000
@@ -7620,9 +7620,34 @@ insert_fields(THD *thd, Name_resolution_
       continue;
 
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
-    /* Ensure that we have access rights to all fields to be inserted. */
-    if (!((table && (table->grant.privilege & SELECT_ACL) ||
-           tables->view && (tables->grant.privilege & SELECT_ACL))) &&
+    /* 
+       Ensure that we have access rights to all fields to be inserted. Under
+       some circumstances, this check may be skipped.
+
+       - If any_privileges is true, skip the check.
+
+       - If the SELECT privilege has been found as fulfilled already for both
+         the TABLE and TABLE_LIST objects (and both of these exist, of
+         course), the check is skipped.
+
+       - If the SELECT privilege has been found fulfilled for the TABLE object
+         and the TABLE_LIST represents a derived table other than a view (see
+         below), the check is skipped.
+
+       - If the TABLE_LIST object represents a view, we may skip checking if
+         the SELECT privilege has been found fulfilled for it, regardless of
+         the TABLE object.
+
+       - If there is no TABLE object, the test is skipped if either 
+         * the TABLE_LIST does not represent a view, or
+         * the SELECT privilege has been found fulfilled.         
+
+       A TABLE_LIST that is not a view may be a subquery, an
+       information_schema table, or a nested table reference. See the comment
+       for TABLE_LIST.
+    */
+    if (!(table && !tables->view && (table->grant.privilege & SELECT_ACL) ||
+          tables->view && (tables->grant.privilege & SELECT_ACL)) &&
         !any_privileges)
     {
       field_iterator.set(tables);
@@ -7676,19 +7701,19 @@ insert_fields(THD *thd, Name_resolution_
                     tables->is_natural_join);
         DBUG_ASSERT(item->type() == Item::FIELD_ITEM);
         Item_field *fld= (Item_field*) item;
-        const char *field_table_name= field_iterator.table_name();
+        const char *field_table_name= field_iterator.get_table_name();
 
         if (!tables->schema_table && 
             !(fld->have_privileges=
               (get_column_grant(thd, field_iterator.grant(),
-                                field_iterator.db_name(),
+                                field_iterator.get_db_name(),
                                 field_table_name, fld->field_name) &
                VIEW_ANY_ACL)))
         {
-          my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), "ANY",
+          my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), "ANY",
                    thd->security_ctx->priv_user,
                    thd->security_ctx->host_or_ip,
-                   fld->field_name, field_table_name);
+                   field_table_name);
           DBUG_RETURN(TRUE);
         }
       }

=== modified file 'sql/sql_cache.cc'
--- a/sql/sql_cache.cc	2008-07-25 15:12:47 +0000
+++ b/sql/sql_cache.cc	2008-09-03 13:57:13 +0000
@@ -2633,7 +2633,7 @@ Query_cache::register_tables_from_list(T
        tables_used;
        tables_used= tables_used->next_global, n++, block_table++)
   {
-    if (tables_used->derived && !tables_used->view)
+    if (tables_used->is_anonymous_derived_table())
     {
       DBUG_PRINT("qcache", ("derived table skipped"));
       n--;

=== modified file 'sql/sql_derived.cc'
--- a/sql/sql_derived.cc	2007-12-12 15:21:01 +0000
+++ b/sql/sql_derived.cc	2008-09-03 13:57:13 +0000
@@ -73,29 +73,59 @@ out:
 }
 
 
-/*
-  Create temporary table structure (but do not fill it)
-
-  SYNOPSIS
-    mysql_derived_prepare()
-    thd			Thread handle
-    lex                 LEX for this thread
-    orig_table_list     TABLE_LIST for the upper SELECT
-
-  IMPLEMENTATION
-    Derived table is resolved with temporary table.
-
-    After table creation, the above TABLE_LIST is updated with a new table.
-
-    This function is called before any command containing derived table
-    is executed.
-
-    Derived tables is stored in thd->derived_tables and freed in
-    close_thread_tables()
+/**
+  @brief Create temporary table structure (but do not fill it).
 
-  RETURN
-    FALSE  OK
-    TRUE   Error
+  @param thd Thread handle
+  @param lex LEX for this thread
+  @param orig_table_list TABLE_LIST for the upper SELECT
+
+  @details 
+
+  This function is called before any command containing derived tables is
+  executed. Currently the function is used for derived tables, i.e.
+
+  - Anonymous derived tables, or 
+  - Named derived tables (aka views) with the @c TEMPTABLE algorithm.
+   
+  The table reference, contained in @c orig_table_list, is updated with the
+  fields of a new temporary table.
+
+  Derived tables are stored in @c thd->derived_tables and closed by
+  close_thread_tables().
+
+  This function is part of the procedure that starts in
+  open_and_lock_tables(), a procedure that - among other things - introduces
+  new table and table reference objects (to represent derived tables) that
+  don't exist in the privilege database. This means that normal privilege
+  checking cannot handle them. Hence this function does some extra tricks in
+  order to bypass normal privilege checking, by exploiting the fact that the
+  current state of privilege verification is attached as GRANT_INFO structures
+  on the relevant TABLE and TABLE_REF objects.
+
+  For table references, the current state of accrued access is stored inside
+  TABLE_LIST::grant. Hence this function must update the state of fulfilled
+  privileges for the new TABLE_LIST, an operation which is normally performed
+  exclusively by the table and database access checking functions,
+  check_access() and check_grant(), respectively. This modification is done
+  for both views and anonymous derived tables: The @c SELECT privilege is set
+  as fulfilled by the user. However, if a view is referenced and the table
+  reference is queried against directly (see TABLE_LIST::referencing_view),
+  the state of privilege checking (GRANT_INFO struct) is copied as-is to the
+  temporary table.
+
+  This function implements a signature called "derived table processor", and
+  is passed as a function pointer to mysql_handle_derived().
+
+  @note This function sets @c SELECT_ACL for @c TEMPTABLE views as well as
+  anonymous derived tables, but this is ok since later access checking will
+  distinguish between them.
+
+  @see mysql_handle_derived(), mysql_derived_filling(), GRANT_INFO
+
+  @return
+    false  OK
+    true   Error
 */
 
 bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2008-08-27 18:52:44 +0000
+++ b/sql/sql_parse.cc	2008-09-03 13:57:13 +0000
@@ -4872,6 +4872,8 @@ bool check_single_table_access(THD *thd,
   /* Show only 1 table for check_grant */
   if (!(all_tables->belong_to_view &&
         (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))
     goto deny;
 
@@ -5184,7 +5186,7 @@ check_table_access(THD *thd, ulong want_
       continue;
     }
 
-    if (tables->derived ||
+    if (tables->is_anonymous_derived_table() ||
         (tables->table && (int)tables->table->s->tmp_table))
       continue;
     thd->security_ctx= sctx;

=== modified file 'sql/table.cc'
--- a/sql/table.cc	2008-07-24 20:38:44 +0000
+++ b/sql/table.cc	2008-09-03 13:57:13 +0000
@@ -2993,16 +2993,27 @@ void  TABLE_LIST::calc_md5(char *buffer)
 }
 
 
-/*
-  set underlying TABLE for table place holder of VIEW
+/**
+   @brief Set underlying table for table place holder of view.
 
-  DESCRIPTION
-    Replace all views that only uses one table with the table itself.
-    This allows us to treat the view as a simple table and even update
-    it (it is a kind of optimisation)
+   @details
 
-  SYNOPSIS
-    TABLE_LIST::set_underlying_merge()
+   Replace all views that only use one table with the table itself.  This
+   allows us to treat the view as a simple table and even update it (it is a
+   kind of optimization).
+
+   @note 
+
+   This optimization is potentially dangerous as it makes views
+   masquerade as base tables: Views don't have the pointer TABLE_LIST::table
+   set to non-@c NULL.
+
+   We may have the case where a view accesses tables not normally accessible
+   in the current Security_context (only in the definer's
+   Security_context). According to the table's GRANT_INFO (TABLE::grant),
+   access is fulfilled, but this is implicitly meant in the definer's security
+   context. Hence we must never look at only a TABLE's GRANT_INFO without
+   looking at the one of the referring TABLE_LIST.
 */
 
 void TABLE_LIST::set_underlying_merge()
@@ -4082,7 +4093,7 @@ void Field_iterator_table_ref::next()
 }
 
 
-const char *Field_iterator_table_ref::table_name()
+const char *Field_iterator_table_ref::get_table_name()
 {
   if (table_ref->view)
     return table_ref->view_name.str;
@@ -4095,7 +4106,7 @@ const char *Field_iterator_table_ref::ta
 }
 
 
-const char *Field_iterator_table_ref::db_name()
+const char *Field_iterator_table_ref::get_db_name()
 {
   if (table_ref->view)
     return table_ref->view_db.str;

=== modified file 'sql/table.h'
--- a/sql/table.h	2008-07-24 20:38:44 +0000
+++ b/sql/table.h	2008-09-03 13:57:13 +0000
@@ -66,13 +66,63 @@ typedef struct st_order {
   table_map used, depend_map;
 } ORDER;
 
+/**
+   @brief The current state of the privilege checking process for the current
+   user, SQL statement and SQL object.
+
+   @details The privilege checking process is divided into phases depending on
+   the level of the privilege to be checked and the type of object to be
+   accessed. Due to the mentioned scattering of privilege checking
+   functionality, it is necessary to keep track of the state of the
+   process. This information is stored in privilege, want_privilege, and
+   orig_want_privilege.
+
+   A GRANT_INFO also serves as a cache of the privilege hash tables. Relevant
+   members are grant_table and version.
+ */
 typedef struct st_grant_info
 {
+  /**
+     @brief A copy of the privilege information regarding the current host,
+     database, object and user.
+
+     @details The version of this copy is found in GRANT_INFO::version.
+   */
   GRANT_TABLE *grant_table;
+  /**
+     @brief Used for cache invalidation when caching privilege information.
+
+     @details The privilege information is stored on disk, with dedicated
+     caches residing in memory: table-level and column-level privileges,
+     respectively, have their own dedicated caches.
+
+     The GRANT_INFO works as a level 1 cache with this member updated to the
+     current value of the global variable @c grant_version (@c static variable
+     in sql_acl.cc). It is updated Whenever the GRANT_INFO is refreshed from
+     the level 2 cache. The level 2 cache is the @c column_priv_hash structure
+     (@c static variable in sql_acl.cc)
+
+     @see grant_version
+   */
   uint version;
+  /**
+     @brief The set of privileges that the current user has fulfilled for a
+     certain host, database, and object.
+     
+     @details This field is continually updated throughout the access checking
+     process. In each step the "wanted privilege" is checked against the
+     fulfilled privileges. When/if the intersection of these sets is empty,
+     access is granted.
+
+     The set is implemented as a bitmap, with the bits defined in sql_acl.h.
+   */
   ulong privilege;
+  /**
+     @brief the set of privileges that the current user needs to fulfil in
+     order to carry out the requested operation.
+   */
   ulong want_privilege;
-  /*
+  /**
     Stores the requested access acl of top level tables list. Is used to
     check access rights to the underlying tables of a view.
   */
@@ -1104,6 +1154,27 @@ struct TABLE_LIST
     can see this lists can't be merged)
   */
   TABLE_LIST	*correspondent_table;
+  /**
+     @brief Normally, this field is non-null for anonymous derived tables only.
+
+     @details This field is set to non-null for 
+     
+     - Anonymous derived tables, In this case it points to the SELECT_LEX_UNIT
+     representing the derived table. E.g. for a query
+     
+     @verbatim SELECT * FROM (SELECT a FROM t1) b @endverbatim
+     
+     For the @c TABLE_LIST representing the derived table @c b, @c derived
+     points to the SELECT_LEX_UNIT representing the result of the query within
+     parenteses.
+     
+     - Views. This is set for views with @verbatim ALGORITHM = TEMPTABLE
+     @endverbatim by mysql_make_view().
+     
+     @note Inside views, a subquery in the @c FROM clause is not allowed.
+     @note Do not use this field to separate views/base tables/anonymous
+     derived tables. Use TABLE_LIST::is_anonymous_derived_table().
+  */
   st_select_lex_unit *derived;		/* SELECT_LEX_UNIT of derived table */
   ST_SCHEMA_TABLE *schema_table;        /* Information_schema table */
   st_select_lex	*schema_select_lex;
@@ -1169,7 +1240,15 @@ struct TABLE_LIST
   ulonglong	file_version;		/* version of file's field set */
   ulonglong     updatable_view;         /* VIEW can be updated */
   ulonglong	revision;		/* revision control number */
-  ulonglong	algorithm;		/* 0 any, 1 tmp tables , 2 merging */
+  /** 
+      @brief The declared algorithm, if this is a view.
+      @details One of
+      - VIEW_ALGORITHM_UNDEFINED
+      - VIEW_ALGORITHM_TMPTABLE
+      - VIEW_ALGORITHM_MERGE
+      @to do Replace with an enum 
+  */
+  ulonglong	algorithm;
   ulonglong     view_suid;              /* view is suid (TRUE dy default) */
   ulonglong     with_check;             /* WITH CHECK OPTION */
   /*
@@ -1177,7 +1256,15 @@ struct TABLE_LIST
     algorithm)
   */
   uint8         effective_with_check;
-  uint8         effective_algorithm;    /* which algorithm was really used */
+  /** 
+      @brief The view algorithm that is actually used, if this is a view.
+      @details One of
+      - VIEW_ALGORITHM_UNDEFINED
+      - VIEW_ALGORITHM_TMPTABLE
+      - VIEW_ALGORITHM_MERGE
+      @to do Replace with an enum 
+  */
+  uint8         effective_algorithm;
   GRANT_INFO	grant;
   /* data need by some engines in query cache*/
   ulonglong     engine_data;
@@ -1362,6 +1449,26 @@ struct TABLE_LIST
     m_table_ref_version= s->get_table_ref_version();
   }
 
+  /**
+     @brief True if this TABLE_LIST represents an anonymous derived table,
+     i.e.  the result of a subquery.
+  */
+  bool is_anonymous_derived_table() const { return derived && !view; }
+
+  /**
+     @brief Returns the name of the database that the referenced table belongs
+     to.
+  */
+  char *get_db_name() { return view != NULL ? view_db.str : db; }
+
+  /**
+     @brief Returns the name of the table that this TABLE_LIST represents.
+
+     @details The unqualified table name or view name for a table or view,
+     respectively.
+   */
+  char *get_table_name() { return view != NULL ? view_name.str : table_name; }
+
 private:
   bool prep_check_option(THD *thd, uint8 check_opt_type);
   bool prep_where(THD *thd, Item **conds, bool no_where_clause);
@@ -1491,8 +1598,8 @@ public:
   bool end_of_fields()
   { return (table_ref == last_leaf && field_it->end_of_fields()); }
   const char *name() { return field_it->name(); }
-  const char *table_name();
-  const char *db_name();
+  const char *get_table_name();
+  const char *get_db_name();
   GRANT_INFO *grant();
   Item *create_item(THD *thd) { return field_it->create_item(thd); }
   Field *field() { return field_it->field(); }

Thread
bzr commit into mysql-5.1 branch (mhansson:2677) Bug#36086Martin Hansson3 Sep