MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:kpettersson Date:January 28 2008 2:22pm
Subject:bk commit into 5.1 tree (thek:1.2681) BUG#27145
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of thek. When thek 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, 2008-01-28 15:22:41+01:00, thek@adventure.(none) +16 -0
  Bug#27145 EXTRA_ACL troubles
  
  The flag EXTRA_ACL is used in conjugation with our access checks, yet it is
  not clear what impact this flag has.
  This is a code clean up which replaces use of EXTRA_ACL with an explicit
  function parameter.
  The patch also fixes privilege checks for SHOW CREATE TABLE/VIEW. The new
  privilege requirement is any privilege on the table-level. VIEWs also requires
  the SHOW_VIEW acl.

  mysql-test/r/grant.result@stripped, 2008-01-28 15:22:38+01:00, thek@adventure.(none) +4 -4
    * Error message now shows correct command (SHOW instead of SELECT) 

  mysql-test/r/grant2.result@stripped, 2008-01-28 15:22:38+01:00, thek@adventure.(none) +1 -1
    * Error message now shows correct command (SHOW instead of SELECT) 

  mysql-test/r/grant4.result@stripped, 2008-01-28 15:22:39+01:00, thek@adventure.(none) +118 -0
    * This test file tests privilege requirements for
      SHOW COLUMNS
      CREATE TABLE .. LIKE
      SHOW CREATE TABLE
      SHOW INDEX
      CHECK TABLE
      CHECKSUM TABLE
      SHOW CREATE VIEW

  mysql-test/r/grant4.result@stripped, 2008-01-28 15:22:39+01:00, thek@adventure.(none) +0 -0

  mysql-test/t/grant4.test@stripped, 2008-01-28 15:22:39+01:00, thek@adventure.(none) +134 -0
    * This test file tests privilege requirements for
      SHOW COLUMNS
      CREATE TABLE .. LIKE
      SHOW CREATE TABLE
      SHOW INDEX
      CHECK TABLE
      CHECKSUM TABLE
      SHOW CREATE VIEW

  mysql-test/t/grant4.test@stripped, 2008-01-28 15:22:39+01:00, thek@adventure.(none) +0 -0

  sql/mysql_priv.h@stripped, 2008-01-28 15:22:38+01:00, thek@adventure.(none) +2 -1
    * Replaced EXTRA_ACL with a parameter.

  sql/sp_head.cc@stripped, 2008-01-28 15:22:38+01:00, thek@adventure.(none) +2 -2
    * Replaced EXTRA_ACL with a parameter.

  sql/sql_acl.cc@stripped, 2008-01-28 15:22:38+01:00, thek@adventure.(none) +128 -27
    * Converted function documentation to doxygen and clarified some behaviors.
    * Changed value from uint to bool to better reflect its meaning.
    * Removed pointless variable orig_want_access
    * Added function has_any_table_level_privileges to help with requirements
      checks during SHOW CREATE TABLE.

  sql/sql_acl.h@stripped, 2008-01-28 15:22:38+01:00, thek@adventure.(none) +4 -1
    * Converted function parameter form uint to bool to better reflect its meaning.

  sql/sql_base.cc@stripped, 2008-01-28 15:22:38+01:00, thek@adventure.(none) +1 -1
    * Replaced EXTRA_ACL with a function parameter indicating that any privileges
    on any column combination will do.

  sql/sql_cache.cc@stripped, 2008-01-28 15:22:38+01:00, thek@adventure.(none) +1 -1
    * Replaced EXTRA_ACL with a parameter.

  sql/sql_parse.cc@stripped, 2008-01-28 15:22:39+01:00, thek@adventure.(none) +204 -116
    * Rewrote function documentation in doxygen comments for: check_access,
      check_table_acces, check_grant.
    * Removed EXTRA_ACL flag where it doesn't hold any meaningful purpose anymore
      and replaced it with a function parameter where any privileges on any column
      combination would satisfy the requirement.
    * Fixed privilege check for SHOW COLUMNS and SHOW INDEX
    * Modified check_table_access to gain clarity in what EXTRA_ACL actually does.
    * Modified check_access to gain clarity in what EXTRA_ACL actually does.
    * Fixed privilege check for CREATE TABLE .. LIKE .. ; It now requires SELECT
      privileges on the table.
    * Fixed privilege check for SHOW CREATE TABLE ..; It now requires any privilege
      on any column combination.

  sql/sql_plugin.cc@stripped, 2008-01-28 15:22:39+01:00, thek@adventure.(none) +1 -1
    * Replaced EXTRA_ACL with a parameter.

  sql/sql_prepare.cc@stripped, 2008-01-28 15:22:39+01:00, thek@adventure.(none) +3 -3
    * Replaced EXTRA_ACL with a parameter.

  sql/sql_show.cc@stripped, 2008-01-28 15:22:39+01:00, thek@adventure.(none) +4 -4
    * Removed pointless EXTRA_ACL flag.
    * Added new parameter to check_table_access

  sql/sql_trigger.cc@stripped, 2008-01-28 15:22:39+01:00, thek@adventure.(none) +1 -1
    * Replaced EXTRA_ACL with a parameter.

  sql/sql_view.cc@stripped, 2008-01-28 15:22:39+01:00, thek@adventure.(none) +3 -3
    * Replaced EXTRA_ACL with a parameter.

diff -Nrup a/mysql-test/r/grant.result b/mysql-test/r/grant.result
--- a/mysql-test/r/grant.result	2007-11-28 09:03:18 +01:00
+++ b/mysql-test/r/grant.result	2008-01-28 15:22:38 +01:00
@@ -913,13 +913,13 @@ SHOW CREATE VIEW  mysqltest2.v_ny;
 View	Create View	character_set_client	collation_connection
 v_ny	CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `mysqltest2`.`v_ny` AS select `mysqltest2`.`t_nn`.`c1` AS `c1` from `mysqltest2`.`t_nn`	latin1	latin1_swedish_ci
 SHOW CREATE TABLE mysqltest3.t_nn;
-ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for table 't_nn'
+ERROR 42000: SHOW command denied to user 'mysqltest_1'@'localhost' for table 't_nn'
 SHOW CREATE VIEW  mysqltest3.t_nn;
-ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for table 't_nn'
+ERROR 42000: SHOW command denied to user 'mysqltest_1'@'localhost' for table 't_nn'
 SHOW CREATE VIEW  mysqltest3.v_nn;
-ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for table 'v_nn'
+ERROR 42000: SHOW command denied to user 'mysqltest_1'@'localhost' for table 'v_nn'
 SHOW CREATE TABLE mysqltest3.v_nn;
-ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for table 'v_nn'
+ERROR 42000: SHOW command denied to user 'mysqltest_1'@'localhost' for table 'v_nn'
 SHOW CREATE TABLE mysqltest2.t_nn;
 Table	Create Table
 t_nn	CREATE TABLE `t_nn` (
diff -Nrup a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result
--- a/mysql-test/r/grant2.result	2007-09-27 11:32:57 +02:00
+++ b/mysql-test/r/grant2.result	2008-01-28 15:22:38 +01:00
@@ -390,7 +390,7 @@ grant all on mysqltest_1.* to mysqltest_
 use mysqltest_2;
 create table t1 (i int);
 show create table mysqltest_2.t1;
-ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't1'
+ERROR 42000: SHOW command denied to user 'mysqltest_u1'@'localhost' for table 't1'
 create table t1 like mysqltest_2.t1;
 ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't1'
 grant select on mysqltest_2.t1 to mysqltest_u1@localhost;
diff -Nrup a/mysql-test/r/grant4.result b/mysql-test/r/grant4.result
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/r/grant4.result	2008-01-28 15:22:39 +01:00
@@ -0,0 +1,118 @@
+drop database if exists mysqltest_db1;
+create database mysqltest_db1;
+use mysqltest_db1;
+create table t_column_priv_only (a int, b int);
+create table t_select_priv like t_column_priv_only;
+create table t_no_priv like t_column_priv_only;
+grant all privileges on test.* to mysqltest_u1@localhost;
+grant insert (a) on mysqltest_db1.t_column_priv_only to mysqltest_u1@localhost;
+grant select on mysqltest_db1.t_select_priv to mysqltest_u1@localhost;
+** Connect as restricted user mysqltest_u1.
+
+** Test column level privileges only. No SELECT privileges on the table.
+** INSERT INTO ... VALUES ...
+** Attempting to insert values to a table with only column privileges
+** should work.
+insert into mysqltest_db1.t_column_priv_only (a) VALUES (1);
+
+** SHOW COLUMNS
+** Should succeed because we have privileges (any) on at least one of the columns.
+select column_name as 'Field',column_type as 'Type',is_nullable as 'Null',column_key as 'Key',column_default as 'Default',extra as 'Extra' from information_schema.columns where table_schema='mysqltest_db1' and table_name='t_column_priv_only';
+Field	Type	Null	Key	Default	Extra
+a	int(11)	YES		NULL	
+show columns from mysqltest_db1.t_column_priv_only;
+Field	Type	Null	Key	Default	Extra
+a	int(11)	YES		NULL	
+** SHOW COLUMNS
+** Should fail because there are no privileges on any column combination.
+show columns from mysqltest_db1.t_no_priv;
+ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't_no_priv'
+** However, select from I_S.COLUMNS will succeed but not show anything:
+select column_name as 'Field',column_type as 'Type',is_nullable as 'Null',column_key as 'Key',column_default as 'Default',extra as 'Extra' from information_schema.columns where table_schema='mysqltest_db1' and table_name='t_no_priv';
+Field	Type	Null	Key	Default	Extra
+
+** CREATE TABLE ... LIKE ... require SELECT privleges and will fail.
+create table test.t_no_priv like mysqltest_db1.column_priv_only;
+ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 'column_priv_only'
+
+** Just to be sure... SELECT also fails.
+select * from mysqltest_db1.t_column_priv_only;
+ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't_column_priv_only'
+
+** SHOW CREATE TABLE ... require any privileges on all columns (the entire table).
+** First we try and fail on a table with only one column privilege.
+show create table mysqltest_db1.t_column_priv_only;
+ERROR 42000: SHOW command denied to user 'mysqltest_u1'@'localhost' for table 't_column_priv_only'
+
+** Now we do the same on a table with SELECT privileges.
+
+** SHOW COLUMNS
+** Success because we got some privileges on the table (SELECT_ACL)
+show columns from mysqltest_db1.t_select_priv;
+Field	Type	Null	Key	Default	Extra
+a	int(11)	YES		NULL	
+b	int(11)	YES		NULL	
+
+** CREATE TABLE ... LIKE ... require SELECT privleges and will SUCCEED.
+drop table if exists test.t_duplicated;
+create table test.t_duplicated like mysqltest_db1.t_select_priv;
+drop table test.t_duplicated;
+
+** SHOW CREATE TABLE will succeed because we have a privilege on all columns in the table (table-level privilege).
+show create table mysqltest_db1.t_select_priv;
+Table	Create Table
+t_select_priv	CREATE TABLE `t_select_priv` (
+  `a` int(11) DEFAULT NULL,
+  `b` int(11) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+
+** SHOW CREATE TABLE will fail if there is no grants at all: 
+show create table mysqltest_db1.t_no_priv;
+ERROR 42000: SHOW command denied to user 'mysqltest_u1'@'localhost' for table 't_no_priv'
+
+use mysqltest_db1;
+CREATE TABLE t5 (s1 INT);
+CREATE INDEX i ON t5 (s1);
+CREATE TABLE t6 (s1 INT, s2 INT);
+CREATE VIEW v5 AS SELECT * FROM t5;
+CREATE VIEW v6 AS SELECT * FROM t6;
+CREATE VIEW v2 AS SELECT * FROM t_select_priv;
+CREATE INDEX i ON t6 (s1);
+GRANT UPDATE (s2) ON t6 to mysqltest_u1@localhost;
+GRANT UPDATE (s2) ON v6 to mysqltest_u1@localhost;
+GRANT SHOW VIEW ON v2 to mysqltest_u1@localhost;
+use mysqltest_db1;
+** Connect as restricted user mysqltest_u1.
+** SELECT FROM INFORMATION_SCHEMA.STATISTICS will succeed because any privileges will do (authentication is enough).
+SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE table_name='t5';
+TABLE_CATALOG	TABLE_SCHEMA	TABLE_NAME	NON_UNIQUE	INDEX_SCHEMA	INDEX_NAME	SEQ_IN_INDEX	COLUMN_NAME	COLLATION	CARDINALITY	SUB_PART	PACKED	NULLABLE	INDEX_TYPE	COMMENT
+NULL	mysqltest_db1	t5	1	mysqltest_db1	i	1	s1	A	NULL	NULL	NULL	YES	BTREE	
+** SHOW INDEX FROM t5 will fail because we don't have any privileges on any column combination.
+SHOW INDEX FROM t5;
+ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't5'
+** SHOW INDEX FROM t6 will succeed because there exist a privilege on a column combination on t6.
+SHOW INDEX FROM t6;
+Table	Non_unique	Key_name	Seq_in_index	Column_name	Collation	Cardinality	Sub_part	Packed	Null	Index_type	Comment
+t6	1	i	1	s1	A	NULL	NULL	NULL	YES	BTREE	
+** CHECK TABLE requires any privilege on any column combination and should succeed for t6:
+CHECK TABLE t6;
+Table	Op	Msg_type	Msg_text
+mysqltest_db1.t6	check	status	OK
+** With no privileges access is naturally denied:
+CHECK TABLE t5;
+ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't5'
+** CHECKSUM TABLE requires SELECT privileges on the table. The following should fail:
+CHECKSUM TABLE t6;
+ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't6'
+** And this should work:
+CHECKSUM TABLE t_select_priv;
+Table	Checksum
+mysqltest_db1.t_select_priv	0
+SHOW CREATE VIEW v5;
+ERROR 42000: SHOW command denied to user 'mysqltest_u1'@'localhost' for table 'v5'
+SHOW CREATE VIEW v6;
+ERROR 42000: SHOW command denied to user 'mysqltest_u1'@'localhost' for table 'v6'
+SHOW CREATE VIEW v2;
+View	Create View	character_set_client	collation_connection
+v2	CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select `t_select_priv`.`a` AS `a`,`t_select_priv`.`b` AS `b` from `t_select_priv`	latin1	latin1_swedish_ci
+drop database mysqltest_db1;
diff -Nrup a/mysql-test/t/grant4.test b/mysql-test/t/grant4.test
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/grant4.test	2008-01-28 15:22:39 +01:00
@@ -0,0 +1,134 @@
+# Setup database, tables and user accounts
+--disable_warnings
+drop database if exists mysqltest_db1;
+--enable_warnings
+create database mysqltest_db1;
+use mysqltest_db1;
+create table t_column_priv_only (a int, b int);
+create table t_select_priv like t_column_priv_only;
+create table t_no_priv like t_column_priv_only;
+grant all privileges on test.* to mysqltest_u1@localhost;
+grant insert (a) on mysqltest_db1.t_column_priv_only to mysqltest_u1@localhost;
+grant select on mysqltest_db1.t_select_priv to mysqltest_u1@localhost;
+
+--echo ** Connect as restricted user mysqltest_u1.
+--echo
+connect (con1,localhost,mysqltest_u1,,);
+connection con1;
+
+########################################################################
+--echo ** Test column level privileges only. No SELECT privileges on the table.
+--echo ** INSERT INTO ... VALUES ...
+--echo ** Attempting to insert values to a table with only column privileges
+--echo ** should work.
+insert into mysqltest_db1.t_column_priv_only (a) VALUES (1);
+--echo
+
+#########################################################################
+--echo ** SHOW COLUMNS
+--echo ** Should succeed because we have privileges (any) on at least one of the columns.
+select column_name as 'Field',column_type as 'Type',is_nullable as 'Null',column_key as 'Key',column_default as 'Default',extra as 'Extra' from information_schema.columns where table_schema='mysqltest_db1' and table_name='t_column_priv_only';
+show columns from mysqltest_db1.t_column_priv_only;
+#########################################################################
+--echo ** SHOW COLUMNS
+--echo ** Should fail because there are no privileges on any column combination.
+--error 1142
+show columns from mysqltest_db1.t_no_priv;
+--echo ** However, select from I_S.COLUMNS will succeed but not show anything:
+select column_name as 'Field',column_type as 'Type',is_nullable as 'Null',column_key as 'Key',column_default as 'Default',extra as 'Extra' from information_schema.columns where table_schema='mysqltest_db1' and table_name='t_no_priv';
+--echo 
+#########################################################################
+--echo ** CREATE TABLE ... LIKE ... require SELECT privleges and will fail.
+--error 1142
+create table test.t_no_priv like mysqltest_db1.column_priv_only;
+--echo  
+#########################################################################
+--echo ** Just to be sure... SELECT also fails.
+--error 1142
+select * from mysqltest_db1.t_column_priv_only;
+--echo 
+#########################################################################
+--echo ** SHOW CREATE TABLE ... require any privileges on all columns (the entire table).
+--echo ** First we try and fail on a table with only one column privilege.
+--error 1142
+show create table mysqltest_db1.t_column_priv_only;
+--echo
+#########################################################################
+--echo ** Now we do the same on a table with SELECT privileges.
+--echo
+#########################################################################
+--echo ** SHOW COLUMNS
+--echo ** Success because we got some privileges on the table (SELECT_ACL)
+show columns from mysqltest_db1.t_select_priv;
+--echo
+#########################################################################
+--echo ** CREATE TABLE ... LIKE ... require SELECT privleges and will SUCCEED.
+--disable_warnings
+drop table if exists test.t_duplicated;
+--enable_warnings
+create table test.t_duplicated like mysqltest_db1.t_select_priv;
+drop table test.t_duplicated;
+--echo
+#########################################################################
+--echo ** SHOW CREATE TABLE will succeed because we have a privilege on all columns in the table (table-level privilege).
+show create table mysqltest_db1.t_select_priv;
+--echo
+#########################################################################
+--echo ** SHOW CREATE TABLE will fail if there is no grants at all: 
+--error 1142
+show create table mysqltest_db1.t_no_priv;
+--echo
+
+connection default;
+
+#
+# SHOW INDEX
+#
+use mysqltest_db1;
+CREATE TABLE t5 (s1 INT);
+CREATE INDEX i ON t5 (s1);
+CREATE TABLE t6 (s1 INT, s2 INT);
+CREATE VIEW v5 AS SELECT * FROM t5;
+CREATE VIEW v6 AS SELECT * FROM t6;
+CREATE VIEW v2 AS SELECT * FROM t_select_priv;
+CREATE INDEX i ON t6 (s1);
+GRANT UPDATE (s2) ON t6 to mysqltest_u1@localhost;
+GRANT UPDATE (s2) ON v6 to mysqltest_u1@localhost;
+GRANT SHOW VIEW ON v2 to mysqltest_u1@localhost;
+
+connection con1;
+use mysqltest_db1;
+--echo ** Connect as restricted user mysqltest_u1.
+--echo ** SELECT FROM INFORMATION_SCHEMA.STATISTICS will succeed because any privileges will do (authentication is enough).
+SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE table_name='t5';
+--echo ** SHOW INDEX FROM t5 will fail because we don't have any privileges on any column combination.
+--error 1142
+SHOW INDEX FROM t5;
+--echo ** SHOW INDEX FROM t6 will succeed because there exist a privilege on a column combination on t6.
+SHOW INDEX FROM t6;
+
+# CHECK TABLE
+--echo ** CHECK TABLE requires any privilege on any column combination and should succeed for t6:
+CHECK TABLE t6;
+--echo ** With no privileges access is naturally denied:
+--error 1142
+CHECK TABLE t5;
+
+# CHECKSUM
+--echo ** CHECKSUM TABLE requires SELECT privileges on the table. The following should fail:
+--error 1142
+CHECKSUM TABLE t6;
+--echo ** And this should work:
+CHECKSUM TABLE t_select_priv;
+
+# SHOW CREATE VIEW
+--error 1142
+SHOW CREATE VIEW v5;
+--error 1142
+SHOW CREATE VIEW v6;
+SHOW CREATE VIEW v2;
+
+connection default;
+disconnect con1;
+drop database mysqltest_db1;
+
diff -Nrup a/sql/mysql_priv.h b/sql/mysql_priv.h
--- a/sql/mysql_priv.h	2007-11-29 12:42:23 +01:00
+++ b/sql/mysql_priv.h	2008-01-28 15:22:38 +01:00
@@ -994,7 +994,8 @@ bool reload_acl_and_cache(THD *thd, ulon
 bool check_access(THD *thd, ulong access, const char *db, ulong *save_priv,
 		  bool no_grant, bool no_errors, bool schema_db);
 bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables,
-			bool no_errors);
+                        bool no_errors,
+                        bool any_combination_of_privileges_will_do);
 bool check_global_access(THD *thd, ulong want_access);
 
 /*
diff -Nrup a/sql/sp_head.cc b/sql/sp_head.cc
--- a/sql/sp_head.cc	2007-11-28 17:08:25 +01:00
+++ b/sql/sp_head.cc	2008-01-28 15:22:38 +01:00
@@ -2265,7 +2265,7 @@ bool check_show_routine_access(THD *thd,
   bzero((char*) &tables,sizeof(tables));
   tables.db= (char*) "mysql";
   tables.table_name= tables.alias= (char*) "proc";
-  *full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1) ||
+  *full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1, FALSE) ||
                  (!strcmp(sp->m_definer_user.str,
                           thd->security_ctx->priv_user) &&
                   !strcmp(sp->m_definer_host.str,
@@ -2712,7 +2712,7 @@ int sp_instr::exec_open_and_lock_tables(
     Check whenever we have access to tables for this statement
     and open and lock them before executing instructions core function.
   */
-  if (check_table_access(thd, SELECT_ACL, tables, 0)
+  if (check_table_access(thd, SELECT_ACL, tables, 0, FALSE)
       || open_and_lock_tables(thd, tables))
     result= -1;
   else
diff -Nrup a/sql/sql_acl.cc b/sql/sql_acl.cc
--- a/sql/sql_acl.cc	2007-11-28 15:34:10 +01:00
+++ b/sql/sql_acl.cc	2008-01-28 15:22:38 +01:00
@@ -3814,40 +3814,50 @@ end:
   DBUG_RETURN(return_val);
 }
 
-/****************************************************************************
-  Check table level grants
+/**
+  @brief Check table level grants
 
-  SYNOPSIS
-   bool check_grant()
-   thd		Thread handler
-   want_access  Bits of privileges user needs to have
-   tables	List of tables to check. The user should have 'want_access'
-		to all tables in list.
-   show_table	<> 0 if we are in show table. In this case it's enough to have
-	        any privilege for the table
-   number	Check at most this number of tables.
-   no_errors	If 0 then we write an error. The error is sent directly to
-		the client
-
-   RETURN
-     0  ok
-     1  Error: User did not have the requested privileges
+  @param thd Thread handler
+  @param want_access Bits of privileges user needs to have
+  @param tables List of tables to check. The user should have 'want_access'
+    to all tables in list.
+  @param show_table TRUE if it's enough to have any privilege for the table
+  @param number Check at most this number of tables.
+  @param no_errors TRUE if no error should be sent directly to the client
+
+  If table->grant.want_privilege != 0 then the requested privileges where
+  in the set of COL_ACLS but access was not granted on the table level. As
+  a consequence an extra check of column privileges is required.
+
+  Specifically if this function returns FALSE the user has some kind of 
+  privilege on a combination of columns in each table.
 
-   NOTE
-     This functions assumes that either number of tables to be inspected
+  This function is usually preceeded by check_access which establish the
+  User-, Db- and Host access rights.
+
+  @see check_access
+  @see check_table_access
+
+  @note This functions assumes that either number of tables to be inspected
      by it is limited explicitly (i.e. is is not UINT_MAX) or table list
      used and thd->lex->query_tables_own_last value correspond to each
      other (the latter should be either 0 or point to next_global member
      of one of elements of this table list).
-****************************************************************************/
+
+   @return Access status
+     @retval FALSE Access granted; But column privileges might need to be
+      checked.
+     @retval TRUE The user did not have the requested privileges on any of the
+      tables.
+
+*/
 
 bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
-		 uint show_table, uint number, bool no_errors)
+		 bool show_table, uint number, bool no_errors)
 {
   TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table();
   Security_context *sctx= thd->security_ctx;
   uint i;
-  ulong orig_want_access= want_access;
   DBUG_ENTER("check_grant");
   DBUG_ASSERT(number > 0);
 
@@ -3865,7 +3875,10 @@ bool check_grant(THD *thd, ulong want_ac
        table != first_not_own_table && i < number;
        table= table->next_global, i++)
   {
-    /* Remove SHOW_VIEW_ACL, because it will be checked during making view */
+    /*
+      Save a copy of the privileges without the SHOW_VIEW_ACL attribute.
+      It will be checked during making view.
+    */
     table->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
   }
 
@@ -3878,7 +3891,6 @@ bool check_grant(THD *thd, ulong want_ac
     sctx = test(table->security_ctx) ?
       table->security_ctx : thd->security_ctx;
 
-    want_access= orig_want_access;
     want_access&= ~sctx->master_access;
     if (!want_access)
       continue;                                 // ok
@@ -3908,8 +3920,13 @@ bool check_grant(THD *thd, ulong want_ac
       want_access &= ~table->grant.privilege;
       goto err;					// No grants
     }
+
+    /*
+      For SHOW TABLE, SHOW COLUMNS, SHOW INDEX it is enough to have some
+      privileges on any column combination on the table.
+    */
     if (show_table)
-      continue;					// We have some priv on this
+      continue;
 
     table->grant.grant_table=grant_table;	// Remember for column test
     table->grant.version=grant_version;
@@ -3927,7 +3944,7 @@ bool check_grant(THD *thd, ulong want_ac
     }
   }
   rw_unlock(&LOCK_grant);
-  DBUG_RETURN(0);
+  DBUG_RETURN(FALSE);
 
 err:
   rw_unlock(&LOCK_grant);
@@ -3941,9 +3958,93 @@ err:
              sctx->host_or_ip,
              table ? table->table_name : "unknown");
   }
-  DBUG_RETURN(1);
+  DBUG_RETURN(TRUE);
 }
 
+
+/**
+  @brief Check if all tables in the table list has any of the requested
+    table level privileges matching the current user.
+
+  @param thd A pointer to the thread context
+  @param required_access Set of privileges to compare against.
+  @param tables A list of tables to be checked.
+
+  @return
+    @retval TRUE There is a privilege on the table level granted to the 
+      current user.
+    @retval FALSE There are no privileges on the table level granted to the
+      current user.
+*/
+
+bool has_any_table_level_privileges(THD *thd, ulong required_access,
+                                    TABLE_LIST *tables)
+{
+
+    Security_context *sctx;
+    GRANT_TABLE *grant_table;
+    TABLE_LIST *table;
+
+    /* For each table in tables */
+    for (table= tables; table; table= table->next_global)
+    {
+      /*
+        If this table is a VIEW, then it will supply its own security context.
+        This is because VIEWs can have a DEFINER or an INVOKER security role.
+      */
+      sctx= test(table->security_ctx)?table->security_ctx:thd->security_ctx;
+
+      /*
+        Get privileges from table_priv and column_priv tables by searching
+        the cache.
+      */
+      grant_table= table_hash_search(sctx->host, sctx->ip,
+                                    table->db, sctx->priv_user,
+                                    table->table_name,0);
+
+      /* Stop if there are no grants for the current user */
+      if (!grant_table)
+        return FALSE;
+
+      /*
+        Save a pointer to the found grant_table in the table object.
+        This pointer can later be used to verify other access requirements
+        without having to look up the grant table in the hash.
+      */
+      table->grant.grant_table= grant_table;
+      table->grant.version=     grant_version;
+      table->grant.privilege|=  grant_table->privs;
+      /*
+        Save all privileges which might be subject to column privileges
+        but not which aren't yet granted by table level ACLs.
+        This is can later be used for column privilege checks.
+      */
+      table->grant.want_privilege= ((required_access & COL_ACLS)
+                                    & ~table->grant.privilege);
+
+      /*
+        If the requested privileges share any intersection with the current
+        table privileges we have found at least one common privilege on the
+        table level.
+      */
+      if (grant_table->privs & required_access)
+        continue; /* Check next table */
+
+      /*
+        There are no table level privileges which satisfies any of the 
+        requested privileges. There might still be column privileges which
+        does though.
+      */
+      return FALSE;
+  }
+
+  /*
+    All tables in TABLE_LIST satisfy the requirement of having any
+    privilege on the table level.
+  */
+
+  return TRUE;
+}
 
 /*
   Check column rights in given security context
diff -Nrup a/sql/sql_acl.h b/sql/sql_acl.h
--- a/sql/sql_acl.h	2007-09-27 11:25:12 +02:00
+++ b/sql/sql_acl.h	2008-01-28 15:22:38 +01:00
@@ -238,7 +238,7 @@ my_bool grant_init();
 void grant_free(void);
 my_bool grant_reload(THD *thd);
 bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
-		 uint show_command, uint number, bool dont_print_error);
+		 bool show_command, uint number, bool dont_print_error);
 bool check_grant_column (THD *thd, GRANT_INFO *grant,
 			 const char *db_name, const char *table_name,
 			 const char *name, uint length, Security_context *sctx);
@@ -269,6 +269,9 @@ bool sp_grant_privileges(THD *thd, const
 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
                              bool is_proc);
 bool is_acl_user(const char *host, const char *user);
+bool has_any_table_level_privileges(THD *thd, ulong required_access,
+                                    TABLE_LIST *tables);
+
 #ifdef NO_EMBEDDED_ACCESS_CHECKS
 #define check_grant(A,B,C,D,E,F) 0
 #define check_grant_db(A,B) 0
diff -Nrup a/sql/sql_base.cc b/sql/sql_base.cc
--- a/sql/sql_base.cc	2007-11-29 12:42:23 +01:00
+++ b/sql/sql_base.cc	2008-01-28 15:22:38 +01:00
@@ -776,7 +776,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *t
     table_list.table_name= share->table_name.str;
     table_list.grant.privilege=0;
 
-    if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list,1))
+    if (check_table_access(thd, SELECT_ACL, &table_list, 1, TRUE))
       continue;
     /* need to check if we haven't already listed it */
     for (table= open_list  ; table ; table=table->next)
diff -Nrup a/sql/sql_cache.cc b/sql/sql_cache.cc
--- a/sql/sql_cache.cc	2007-10-29 15:46:43 +01:00
+++ b/sql/sql_cache.cc	2008-01-28 15:22:38 +01:00
@@ -1323,7 +1323,7 @@ def_week_frmt: %lu",                    
     table_list.db = table->db();
     table_list.alias= table_list.table_name= table->table();
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
-    if (check_table_access(thd,SELECT_ACL,&table_list,1))
+    if (check_table_access(thd,SELECT_ACL,&table_list,1, FALSE))
     {
       DBUG_PRINT("qcache",
 		 ("probably no SELECT access to %s.%s =>  return to normal processing",
diff -Nrup a/sql/sql_parse.cc b/sql/sql_parse.cc
--- a/sql/sql_parse.cc	2007-12-10 16:43:23 +01:00
+++ b/sql/sql_parse.cc	2008-01-28 15:22:39 +01:00
@@ -44,7 +44,6 @@
    "FUNCTION" : "PROCEDURE")
 
 static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
-static bool check_show_create_table_access(THD *thd, TABLE_LIST *table);
 
 const char *any_db="*any*";	// Special symbol for check_access
 
@@ -504,7 +503,7 @@ static bool check_merge_table_access(THD
         tlist->db= db; /* purecov: inspected */
     }
     error= check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
-                              table_list,0);
+                              table_list,0, FALSE);
   }
   return error;
 }
@@ -1997,7 +1996,7 @@ mysql_execute_command(THD *thd)
       res= check_table_access(thd,
                               lex->exchange ? SELECT_ACL | FILE_ACL :
                               SELECT_ACL,
-                              all_tables, 0);
+                              all_tables, 0, FALSE);
     }
     else
       res= check_access(thd,
@@ -2022,7 +2021,7 @@ mysql_execute_command(THD *thd)
     break;
   }
   case SQLCOM_DO:
-    if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+    if (check_table_access(thd, SELECT_ACL, all_tables, 0, FALSE) ||
         open_and_lock_tables(thd, all_tables))
       goto error;
 
@@ -2119,7 +2118,7 @@ mysql_execute_command(THD *thd)
   case SQLCOM_BACKUP_TABLE:
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+    if (check_table_access(thd, SELECT_ACL, all_tables, 0, FALSE) ||
 	check_global_access(thd, FILE_ACL))
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
@@ -2131,7 +2130,7 @@ mysql_execute_command(THD *thd)
   case SQLCOM_RESTORE_TABLE:
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (check_table_access(thd, INSERT_ACL, all_tables, 0) ||
+    if (check_table_access(thd, INSERT_ACL, all_tables, 0, FALSE) ||
 	check_global_access(thd, FILE_ACL))
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
@@ -2668,11 +2667,50 @@ end_with_restore_list:
     goto error;
 #else
     {
-      /* Ignore temporary tables if this is "SHOW CREATE VIEW" */
+      /*
+        Access check:
+        SHOW CREATE TABLE require any privileges on the table level (ie
+        effecting  columns in the table).
+        In addition SHOW CREATE VIEW require the SHOW_VIEW acl as well.
+        First grant access by global scope: User-priv | (Db-priv & Host-priv)
+      */
+      ulong save_priv;
+      if (check_access(thd, TABLE_ACLS, first_table->db,
+                       &save_priv, FALSE, FALSE, FALSE ))
+        goto error;
+      /*
+        save_priv contains any privileges actually granted by check_access.
+        If there are no global privileges (save_priv == 0) and no table level
+        privileges, access is denied.
+      */
+      if( !save_priv &&
+          !has_any_table_level_privileges(thd, TABLE_ACLS,
+                                          first_table))
+      {
+        my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
+                 "SHOW", thd->security_ctx->priv_user,
+                 thd->security_ctx->host_or_ip, first_table->alias);
+        goto error;
+      }
+
+      /* If this is a VIEW; check for SHOW_VIEW acl */
       if (lex->only_view)
+      {
+        if (first_table->grant.privilege & SHOW_VIEW_ACL != SHOW_VIEW_ACL)
+        {
+          my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
+                  "SHOW", thd->security_ctx->priv_user,
+                  thd->security_ctx->host_or_ip, first_table->alias);
+          goto error;
+        }
+
+        /* Ignore temporary tables if this is "SHOW CREATE VIEW" */
         first_table->skip_temporary= 1;
-      if (check_show_create_table_access(thd, first_table))
-	goto error;
+      }
+
+      /*
+        Access is granted. Execute command.
+      */
       res= mysqld_show_create(thd, first_table);
       break;
     }
@@ -2680,7 +2718,7 @@ end_with_restore_list:
   case SQLCOM_CHECKSUM:
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, 0))
+    if (check_table_access(thd, SELECT_ACL, all_tables, 0, FALSE))
       goto error; /* purecov: inspected */
     res = mysql_checksum_table(thd, first_table, &lex->check_opt);
     break;
@@ -2688,7 +2726,7 @@ end_with_restore_list:
   case SQLCOM_REPAIR:
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
+    if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0, FALSE))
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
     res= mysql_repair_table(thd, first_table, &lex->check_opt);
@@ -2707,7 +2745,7 @@ end_with_restore_list:
   case SQLCOM_CHECK:
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, 0))
+    if (check_table_access(thd, SELECT_ACL, all_tables, 0, TRUE))
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
     res = mysql_check_table(thd, first_table, &lex->check_opt);
@@ -2718,7 +2756,7 @@ end_with_restore_list:
   case SQLCOM_ANALYZE:
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
+    if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0, FALSE))
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
     res= mysql_analyze_table(thd, first_table, &lex->check_opt);
@@ -2738,7 +2776,7 @@ end_with_restore_list:
   case SQLCOM_OPTIMIZE:
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
+    if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0, FALSE))
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
     res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
@@ -3071,7 +3109,7 @@ end_with_restore_list:
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
     if (!lex->drop_temporary)
     {
-      if (check_table_access(thd, DROP_ACL, all_tables, 0))
+      if (check_table_access(thd, DROP_ACL, all_tables, 0, FALSE))
 	goto error;				/* purecov: inspected */
       if (end_active_trans(thd))
         goto error;
@@ -3175,7 +3213,7 @@ end_with_restore_list:
     if (lex->autocommit && end_active_trans(thd))
       goto error;
 
-    if ((check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+    if ((check_table_access(thd, SELECT_ACL, all_tables, 0, FALSE) ||
 	 open_and_lock_tables(thd, all_tables)))
       goto error;
     if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
@@ -3217,7 +3255,8 @@ end_with_restore_list:
     /* we must end the trasaction first, regardless of anything */
     if (end_active_trans(thd))
       goto error;
-    if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0))
+    if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0,
+                           FALSE))
       goto error;
     thd->in_lock_tables=1;
     thd->options|= OPTION_TABLE_LOCK;
@@ -3711,7 +3750,7 @@ end_with_restore_list:
 #endif
   case SQLCOM_HA_OPEN:
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (check_table_access(thd, SELECT_ACL, all_tables, 0))
+    if (check_table_access(thd, SELECT_ACL, all_tables, 0, FALSE))
       goto error;
     res= mysql_ha_open(thd, first_table, 0);
     break;
@@ -3959,7 +3998,7 @@ create_sp_error:
         This will cache all SP and SF and open and lock all tables
         required for execution.
       */
-      if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+      if (check_table_access(thd, SELECT_ACL, all_tables, 0, FALSE) ||
 	  open_and_lock_tables(thd, all_tables))
        goto error;
 
@@ -4313,7 +4352,7 @@ create_sp_error:
     }
   case SQLCOM_DROP_VIEW:
     {
-      if (check_table_access(thd, DROP_ACL, all_tables, 0) ||
+      if (check_table_access(thd, DROP_ACL, all_tables, 0, FALSE) ||
           end_active_trans(thd))
         goto error;
       /* Conditionally writes to binlog. */
@@ -4788,31 +4827,40 @@ bool check_one_table_access(THD *thd, ul
       subselects_tables= subselects_tables->next_global;
     }
     if (subselects_tables &&
-        (check_table_access(thd, SELECT_ACL, subselects_tables, 0)))
+        (check_table_access(thd, SELECT_ACL, subselects_tables, 0, FALSE)))
       return 1;
   }
   return 0;
 }
 
 
-/****************************************************************************
-  Get the user (global) and database privileges for all used tables
-
-  NOTES
-    The idea of EXTRA_ACL is that one will be granted access to the table if
-    one has the asked privilege on any column combination of the table; For
-    example to be able to check a table one needs to have SELECT privilege on
-    any column of the table.
-
-  RETURN
-    0  ok
-    1  If we can't get the privileges and we don't use table/column grants.
+/**
+  @brief Compare requested privileges with the privileges acquired from the
+    User- and Db-tables.
 
-    save_priv	In this we store global and db level grants for the table
-		Note that we don't store db level grants if the global grants
-                is enough to satisfy the request and the global grants contains
-                a SELECT grant.
-****************************************************************************/
+  @param thd          Thread handler
+  @param want_access  The requested access privileges.
+  @param db           A pointer to the Db name.
+  @param[out] save_priv A pointer to the granted privileges will be stored.
+  @param dont_check_global_grants True if no global grants are checked.
+  @param no_error     True if no errors should be sent to the client.
+  @param schema_db    True if the db specified belongs to the meta data tables.
+
+  'save_priv' is used to save the User-table (global) and Db-table grants for
+  the supplied db name. Note that we don't store db level grants if the global
+  grants is enough to satisfy the request AND the global grants contains a
+  SELECT grant.
+
+  A meta data table (from INFORMATION_SCHEMA) can always be accessed with
+  a SELECT_ACL.
+
+  @see check_grant
+
+  @return Status of denial of access by exclusive ACLs.
+    @retval FALSE Access can't exclusively be denied by Db- and User-table
+      access unless Column- and Table-grants are checked too.
+    @retval TRUE Access denied.
+*/
 
 bool
 check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
@@ -4851,10 +4899,13 @@ check_access(THD *thd, ulong want_access
     DBUG_RETURN(TRUE);				/* purecov: tested */
   }
 
+  /*
+     Meta data inspection of meta data.
+  */
   if (schema_db)
   {
     if (!(sctx->master_access & FILE_ACL) && (want_access & FILE_ACL) ||
-        (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
+        (want_access & ~(SELECT_ACL | FILE_ACL)))
     {
       if (!no_errors)
       {
@@ -4862,10 +4913,16 @@ check_access(THD *thd, ulong want_access
         my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
                  sctx->priv_user, sctx->priv_host, db_name);
       }
+
+      /*
+        Access denied;
+        [out] *save_privileges= 0
+      */
       DBUG_RETURN(TRUE);
     }
     else
     {
+      /* Access granted */
       *save_priv= SELECT_ACL;
       DBUG_RETURN(FALSE);
     }
@@ -4876,20 +4933,27 @@ check_access(THD *thd, ulong want_access
 #else
   if ((sctx->master_access & want_access) == want_access)
   {
+    /* get access for current db */
+    db_access= sctx->db_access;
     /*
-      If we don't have a global SELECT privilege, we have to get the database
-      specific access rights to be able to handle queries of type
+      1. If we don't have a global SELECT privilege, we have to get the
+      database specific access rights to be able to handle queries of type
       UPDATE t1 SET a=1 WHERE b > 0
+      2. Change db access if it isn't current db which is being addressed
     */
-    db_access= sctx->db_access;
     if (!(sctx->master_access & SELECT_ACL) &&
 	(db && (!thd->db || db_is_pattern || strcmp(db,thd->db))))
       db_access=acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
                         db_is_pattern);
+
+    /*
+      The effective privileges are the union of the global privileges
+      and the the intersection of db- and host-privileges.
+    */
     *save_priv=sctx->master_access | db_access;
     DBUG_RETURN(FALSE);
   }
-  if (((want_access & ~sctx->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
+  if (((want_access & ~sctx->master_access) & ~DB_ACLS) ||
       ! db && dont_check_global_grants)
   {						// We can never grant this
     DBUG_PRINT("error",("No possible access"));
@@ -4904,33 +4968,66 @@ check_access(THD *thd, ulong want_access
   }
 
   if (db == any_db)
-    DBUG_RETURN(FALSE);				// Allow select on anything
+  {
+    /*
+      Access granted; Allow select on *any* db.
+      [out] *save_privileges= 0
+    */
+    DBUG_RETURN(FALSE);
+  }
 
   if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
     db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
                        db_is_pattern);
   else
     db_access= sctx->db_access;
-  DBUG_PRINT("info",("db_access: %lu", db_access));
-  /* Remove SHOW attribute and access rights we already have */
-  want_access &= ~(sctx->master_access | EXTRA_ACL);
   DBUG_PRINT("info",("db_access: %lu  want_access: %lu",
                      db_access, want_access));
-  db_access= ((*save_priv=(db_access | sctx->master_access)) & want_access);
 
-  if (db_access == want_access ||
+  /*
+    Save the union of User-table and the intersection between Db-table and
+    Host-table privileges.
+  */
+  db_access= (db_access | sctx->master_access);
+  *save_priv= db_access;
+
+  /*
+    We need to investigate column- and table access if all requested privileges
+    belongs to the bit set (TABLE_ACLS | PROC_ACLS).
+  */
+  bool need_table_or_column_check=
+    (want_access & (TABLE_ACLS | PROC_ACLS)) == want_access;
+
+  /*
+    Grant access if the requested access is in the intersection of
+    host- and db-privileges (as retrieved from the acl cache),
+    also grant access if all the requested privileges are in the union of
+    TABLES_ACLS and PROC_ACLS; see check_grant.
+  */
+  if ( (db_access & want_access) == want_access ||
       (!dont_check_global_grants &&
-       !(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS))))
-    DBUG_RETURN(FALSE);				/* Ok */
+       need_table_or_column_check))
+  {
+    /*
+       Ok; but need to check table- and column privileges.
+       [out] *save_privileges is (User-priv | (Db-priv & Host-priv))
+    */
+    DBUG_RETURN(FALSE);
+  }
 
+  /*
+    Access is denied;
+    [out] *save_privileges is (User-priv | (Db-priv & Host-priv))
+  */
   DBUG_PRINT("error",("Access denied"));
   if (!no_errors)
     my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
              sctx->priv_user, sctx->priv_host,
              (db ? db : (thd->db ?
                          thd->db :
-                         "unknown")));          /* purecov: tested */
-  DBUG_RETURN(TRUE);				/* purecov: tested */
+                         "unknown")));
+  DBUG_RETURN(TRUE);
+
 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
 }
 
@@ -5011,14 +5108,20 @@ static bool check_show_access(THD *thd, 
 
     DBUG_ASSERT(dst_table);
 
-    if (check_access(thd, SELECT_ACL | EXTRA_ACL,
-                     dst_table->db,
-                     &dst_table->grant.privilege,
-                     FALSE, FALSE,
+    if (check_access(thd, SELECT_ACL, dst_table->db,
+                     &dst_table->grant.privilege, FALSE, FALSE,
                      test(dst_table->schema_table)))
-      return FALSE;
+          return TRUE; /* Access denied */
+
+    /*
+      Check_grant will grant access if there is any column privileges on
+      all of the tables thanks to the fourth parameter (bool show_table).
+    */
+    if (check_grant(thd, SELECT_ACL, dst_table, TRUE, UINT_MAX, FALSE))
+      return TRUE; /* Access denied */
 
-    return (check_grant(thd, SELECT_ACL, dst_table, 2, UINT_MAX, FALSE));
+    /* Access granted */
+    return FALSE;
   }
   default:
     break;
@@ -5028,32 +5131,43 @@ static bool check_show_access(THD *thd, 
 }
 
 
-/*
-  Check the privilege for all used tables.
-
-  SYNOPSYS
-    check_table_access()
-      thd          Thread context
-      want_access  Privileges requested
-      tables       List of tables to be checked
-      no_errors    FALSE/TRUE - report/don't report error to
-                   the client (using my_error() call).
-
-  NOTES
-    Table privileges are cached in the table list for GRANT checking.
-    This functions assumes that table list used and
-    thd->lex->query_tables_own_last value correspond to each other
-    (the latter should be either 0 or point to next_global member
-    of one of elements of this table list).
+/**
+  @brief Check if the requested privileges exists in either User-, Host- or
+    Db-tables.
 
-  RETURN VALUE
-    FALSE - OK
-    TRUE  - Access denied
+  @param thd          Thread context
+  @param want_access  Privileges requested
+  @param tables       List of tables to be compared against
+  @param no_errors    Don't report error to the client (using my_error() call).
+  @param any_combination_of_privileges_will_do TRUE if any privileges on any
+    column combination is enough.
+
+  The suppled table list contains cached privileges. This functions calls the
+  help functions check_access and check_grant to verify the first three steps
+  in the privileges check queue:
+  1. Global privileges
+  2. OR (db privileges AND host privileges)
+  3. OR table privileges
+  4. OR column privileges (not checked by this function!)
+  5. OR routine privileges (not checked by this function!)
+
+  @see check_access
+  @see check_grant
+
+  @note This functions assumes that table list used and
+  thd->lex->query_tables_own_last value correspond to each other
+  (the latter should be either 0 or point to next_global member
+  of one of elements of this table list).
+
+  @return
+    @retval FALSE OK
+    @retval TRUE  Access denied; But column or routine privileges might need to
+      be checked also.
 */
 
 bool
 check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
-		   bool no_errors)
+		   bool no_errors, bool any_combination_of_privileges_will_do)
 {
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
   TABLE_LIST *org_tables= tables;
@@ -5073,7 +5187,7 @@ check_table_access(THD *thd, ulong want_
       sctx= backup_ctx;
 
     if (tables->schema_table && 
-        (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
+        (want_access & ~(SELECT_ACL | FILE_ACL)))
     {
       if (!no_errors)
         my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
@@ -5091,7 +5205,6 @@ check_table_access(THD *thd, ulong want_
     {
       if (check_show_access(thd, tables))
         goto deny;
-
       continue;
     }
 
@@ -5100,22 +5213,16 @@ check_table_access(THD *thd, ulong want_
       continue;
     thd->security_ctx= sctx;
     if ((sctx->master_access & want_access) ==
-        (want_access & ~EXTRA_ACL) &&
-	thd->db)
+        want_access && thd->db)
       tables->grant.privilege= want_access;
-    else if (tables->db && thd->db && strcmp(tables->db, thd->db) == 0)
-    {
-      if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
-			 0, no_errors, test(tables->schema_table)))
-        goto deny;                            // Access denied
-    }
     else if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
-			  0, no_errors, test(tables->schema_table)))
+                          0, no_errors, test(tables->schema_table)))
       goto deny;
   }
   thd->security_ctx= backup_ctx;
-  return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
-		       test(want_access & EXTRA_ACL), UINT_MAX, no_errors);
+  return check_grant(thd,want_access,org_tables,
+                     any_combination_of_privileges_will_do,
+                     UINT_MAX, no_errors);
 deny:
   thd->security_ctx= backup_ctx;
   return TRUE;
@@ -6878,7 +6985,7 @@ bool multi_delete_precheck(THD *thd, TAB
 
   /* sql_yacc guarantees that tables and aux_tables are not zero */
   DBUG_ASSERT(aux_tables != 0);
-  if (check_table_access(thd, SELECT_ACL, tables, 0))
+  if (check_table_access(thd, SELECT_ACL, tables, 0, FALSE))
     DBUG_RETURN(TRUE);
 
   /*
@@ -6887,7 +6994,7 @@ bool multi_delete_precheck(THD *thd, TAB
     call check_table_access() safely.
   */
   thd->lex->query_tables_own_last= 0;
-  if (check_table_access(thd, DELETE_ACL, aux_tables, 0))
+  if (check_table_access(thd, DELETE_ACL, aux_tables, 0, FALSE))
   {
     thd->lex->query_tables_own_last= save_query_tables_own_last;
     DBUG_RETURN(TRUE);
@@ -7044,25 +7151,6 @@ bool insert_precheck(THD *thd, TABLE_LIS
 }
 
 
-/**
-    @brief  Check privileges for SHOW CREATE TABLE statement.
-
-    @param  thd    Thread context
-    @param  table  Target table
-
-    @retval TRUE  Failure
-    @retval FALSE Success
-*/
-
-static bool check_show_create_table_access(THD *thd, TABLE_LIST *table)
-{
-  return check_access(thd, SELECT_ACL | EXTRA_ACL, table->db,
-                      &table->grant.privilege, 0, 0,
-                      test(table->schema_table)) ||
-         check_grant(thd, SELECT_ACL, table, 2, UINT_MAX, 0);
-}
-
-
 /*
   CREATE TABLE query pre-check
 
@@ -7124,12 +7212,12 @@ bool create_table_precheck(THD *thd, TAB
       }
     }
 #endif
-    if (tables && check_table_access(thd, SELECT_ACL, tables,0))
+    if (tables && check_table_access(thd, SELECT_ACL, tables,0, FALSE))
       goto err;
   }
   else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
   {
-    if (check_show_create_table_access(thd, tables))
+    if (check_table_access(thd, SELECT_ACL, tables, 0, FALSE))
       goto err;
   }
   error= FALSE;
diff -Nrup a/sql/sql_plugin.cc b/sql/sql_plugin.cc
--- a/sql/sql_plugin.cc	2007-11-14 14:28:20 +01:00
+++ b/sql/sql_plugin.cc	2008-01-28 15:22:39 +01:00
@@ -1618,7 +1618,7 @@ bool mysql_install_plugin(THD *thd, cons
   bzero(&tables, sizeof(tables));
   tables.db= (char *)"mysql";
   tables.table_name= tables.alias= (char *)"plugin";
-  if (check_table_access(thd, INSERT_ACL, &tables, 0))
+  if (check_table_access(thd, INSERT_ACL, &tables, 0, FALSE))
     DBUG_RETURN(TRUE);
 
   /* need to open before acquiring LOCK_plugin or it will deadlock */
diff -Nrup a/sql/sql_prepare.cc b/sql/sql_prepare.cc
--- a/sql/sql_prepare.cc	2007-11-21 20:57:24 +01:00
+++ b/sql/sql_prepare.cc	2008-01-28 15:22:39 +01:00
@@ -1264,7 +1264,7 @@ static int mysql_test_select(Prepared_st
   ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
   if (tables)
   {
-    if (check_table_access(thd, privilege, tables,0))
+    if (check_table_access(thd, privilege, tables,0, FALSE))
       goto error;
   }
   else if (check_access(thd, privilege, any_db,0,0,0,0))
@@ -1334,7 +1334,7 @@ static bool mysql_test_do_fields(Prepare
   THD *thd= stmt->thd;
 
   DBUG_ENTER("mysql_test_do_fields");
-  if (tables && check_table_access(thd, SELECT_ACL, tables, 0))
+  if (tables && check_table_access(thd, SELECT_ACL, tables, 0, FALSE))
     DBUG_RETURN(TRUE);
 
   if (open_normal_and_derived_tables(thd, tables, 0))
@@ -1366,7 +1366,7 @@ static bool mysql_test_set_fields(Prepar
   THD *thd= stmt->thd;
   set_var_base *var;
 
-  if (tables && check_table_access(thd, SELECT_ACL, tables, 0) ||
+  if (tables && check_table_access(thd, SELECT_ACL, tables, 0, FALSE) ||
       open_normal_and_derived_tables(thd, tables, 0))
     goto error;
 
diff -Nrup a/sql/sql_show.cc b/sql/sql_show.cc
--- a/sql/sql_show.cc	2007-11-21 21:09:25 +01:00
+++ b/sql/sql_show.cc	2008-01-28 15:22:39 +01:00
@@ -3658,7 +3658,7 @@ static int get_schema_column_record(THD 
 
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
     uint col_access;
-    check_access(thd,SELECT_ACL | EXTRA_ACL, db_name->str,
+    check_access(thd,SELECT_ACL, db_name->str,
                  &tables->grant.privilege, 0, 0, test(tables->schema_table));
     col_access= get_column_grant(thd, &tables->grant, 
                                  db_name->str, table_name->str,
@@ -4051,7 +4051,7 @@ int fill_schema_proc(THD *thd, TABLE_LIS
   proc_tables.table_name= proc_tables.alias= (char*) "proc";
   proc_tables.table_name_length= 4;
   proc_tables.lock_type= TL_READ;
-  full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1);
+  full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1, FALSE);
   if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup)))
   {
     DBUG_RETURN(1);
@@ -4440,7 +4440,7 @@ static int get_schema_triggers_record(TH
     int event, timing;
 
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
-    if (check_table_access(thd, TRIGGER_ACL, tables, 1))
+    if (check_table_access(thd, TRIGGER_ACL, tables, 1, FALSE))
       goto ret;
 #endif
 
@@ -5817,7 +5817,7 @@ bool get_schema_tables_result(JOIN *join
 
   thd->no_warnings_for_error= 1;
   for (JOIN_TAB *tab= join->join_tab; tab < tmp_join_tab; tab++)
-  {  
+  {
     if (!tab->table || !tab->table->pos_in_table_list)
       break;
 
diff -Nrup a/sql/sql_trigger.cc b/sql/sql_trigger.cc
--- a/sql/sql_trigger.cc	2007-11-29 12:42:24 +01:00
+++ b/sql/sql_trigger.cc	2008-01-28 15:22:39 +01:00
@@ -418,7 +418,7 @@ bool mysql_create_or_drop_trigger(THD *t
     TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
     thd->lex->query_tables_own_last= 0;
 
-    err_status= check_table_access(thd, TRIGGER_ACL, tables, 0);
+    err_status= check_table_access(thd, TRIGGER_ACL, tables, 0, FALSE);
 
     thd->lex->query_tables_own_last= save_query_tables_own_last;
 
diff -Nrup a/sql/sql_view.cc b/sql/sql_view.cc
--- a/sql/sql_view.cc	2007-10-30 18:08:13 +01:00
+++ b/sql/sql_view.cc	2008-01-28 15:22:39 +01:00
@@ -1123,8 +1123,8 @@ bool mysql_make_view(THD *thd, File_pars
     if (!table->prelocking_placeholder &&
         (old_lex->sql_command == SQLCOM_SELECT && old_lex->describe))
     {
-      if (check_table_access(thd, SELECT_ACL, view_tables, 1) &&
-          check_table_access(thd, SHOW_VIEW_ACL, table, 1))
+      if (check_table_access(thd, SELECT_ACL, view_tables, 1, FALSE) &&
+          check_table_access(thd, SHOW_VIEW_ACL, table, 1, FALSE))
       {
         my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
         goto err;
@@ -1134,7 +1134,7 @@ bool mysql_make_view(THD *thd, File_pars
              (old_lex->sql_command == SQLCOM_SHOW_CREATE) &&
              !table->belong_to_view)
     {
-      if (check_table_access(thd, SHOW_VIEW_ACL, table, 0))
+      if (check_table_access(thd, SHOW_VIEW_ACL, table, 0, FALSE))
         goto err;
     }
 
Thread
bk commit into 5.1 tree (thek:1.2681) BUG#27145kpettersson28 Jan
  • Re: bk commit into 5.1 tree (thek:1.2681) BUG#27145Sergei Golubchik30 Jan