List:Commits« Previous MessageNext Message »
From:Alexander Nozdrin Date:April 3 2007 11:11am
Subject:bk commit into 5.0 tree (anozdrin:1.2427) BUG#27337
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of alik. When alik does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-04-03 15:11:34+04:00, anozdrin@stripped +6 -0
  Fix for BUG#27337: Privileges are not properly restored.
  
  The problem was that THD::db_access variable was not restored after
  database switch in stored-routine-execution code.
  
  The fix is to restore THD::db_access in this case.
  
  Unfortunately, this fix requires additional changes,
  because in prepare_schema_table(), called on the parsing stage, we checked
  privileges. That was wrong according to our design, but this flaw haven't
  struck so far, because it was masked. All privilege checkings must be
  done on the execution stage in order to be compatible with prepared statements
  and stored routines. So, this patch also contains patch for
  prepare_schema_table(), which moves the checkings to the execution phase.

  mysql-test/r/grant.result@stripped, 2007-04-03 15:11:32+04:00, anozdrin@stripped +75 -0
    Updated result file.

  mysql-test/t/grant.test@stripped, 2007-04-03 15:11:32+04:00, anozdrin@stripped +144 -0
    Added test case for BUG#27337.

  sql/mysql_priv.h@stripped, 2007-04-03 15:11:32+04:00, anozdrin@stripped +2 -0
    Added function declaration.

  sql/sql_db.cc@stripped, 2007-04-03 15:11:32+04:00, anozdrin@stripped +20 -23
    Fix for BUG#27337 -- set THD::db_access even if we're called
    from stored-routine-execution code.

  sql/sql_parse.cc@stripped, 2007-04-03 15:11:32+04:00, anozdrin@stripped +120 -47
    Split prepare_schema_table() into two functions:
      - prepare_schema_table(), which is called from the parser (parsing stage);
      - check_show_access(), which is called on the execution stage.

  sql/sql_show.cc@stripped, 2007-04-03 15:11:32+04:00, anozdrin@stripped +1 -1
    Ignore schema_select_lex member if its table is NULL.

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	anozdrin
# Host:	ibm.opbmk
# Root:	/home/alik/Documents/MySQL/devel/5.0-marvel-27337

--- 1.442/sql/mysql_priv.h	2007-03-27 21:54:59 +04:00
+++ 1.443/sql/mysql_priv.h	2007-04-03 15:11:32 +04:00
@@ -955,6 +955,8 @@
 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))
 

--- 1.135/sql/sql_db.cc	2007-03-27 21:54:59 +04:00
+++ 1.136/sql/sql_db.cc	2007-04-03 15:11:32 +04:00
@@ -1308,30 +1308,27 @@
   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
 

--- 1.616/sql/sql_parse.cc	2007-03-28 21:46:37 +04:00
+++ 1.617/sql/sql_parse.cc	2007-04-03 15:11:32 +04:00
@@ -2237,7 +2237,8 @@
                          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 @@
                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 @@
                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 @@
                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<String> *) 0, (List<String> *) 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<String> *) 0, (List<String> *) 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 @@
     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 @@
 }
 
 
+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 @@
        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))

--- 1.345/sql/sql_show.cc	2007-03-27 21:54:59 +04:00
+++ 1.346/sql/sql_show.cc	2007-04-03 15:11:32 +04:00
@@ -2148,7 +2148,7 @@
   */
   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;

--- 1.62/mysql-test/r/grant.result	2007-03-23 14:12:09 +03:00
+++ 1.63/mysql-test/r/grant.result	2007-04-03 15:11:32 +04:00
@@ -1019,4 +1019,79 @@
 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

--- 1.52/mysql-test/t/grant.test	2007-03-23 14:12:09 +03:00
+++ 1.53/mysql-test/t/grant.test	2007-04-03 15:11:32 +04:00
@@ -958,4 +958,148 @@
 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
Thread
bk commit into 5.0 tree (anozdrin:1.2427) BUG#27337Alexander Nozdrin3 Apr