#At file:///Users/cbell/source/bzr/mysql-6.0-bug-44787-elevation/ based on revid:charles.bell@stripped
2888 Chuck Bell 2009-11-02
BUG#44787 : Backup: Check privileges before executing BACKUP/RESTORE
The backup system must be changed to meet the Server PT decision
to elevate backup if BACKUP and restore if RESTORE + SUPER.
This patch implements the privilege elevation change for backup
as well as privilege elevation for restore iff the user has both
RESTORE and SUPER on all databases in the image. The restore
will fall back to object-level privilege checking if this condition
is not met.
Note: This is patch 2 of 3. Patch 1 implements privilege checking,
patch 3 implements the options to skip precheck and turn
elevation off.
@ mysql-test/suite/backup/r/backup_restore_security.result
Corrected result file.
@ mysql-test/suite/backup/r/backup_security.result
Corrected result file.
@ mysql-test/suite/backup/t/backup_restore_security.test
Added new test cases.
Corrected error return codes.
@ mysql-test/suite/backup/t/backup_security.test
Corrected error return codes.
@ sql/backup/backup_info.cc
Added privilege elevation for backup using security context
method (similar to replication).
@ sql/backup/kernel.cc
Added privilege elevation code for elevating iff the user
has RESTORE + SUPER.
Added object-level privilege checking for normal access.
@ sql/backup/restore_info.h
Added attributes for privilege checking.
Moved privilege checking to new method.
@ sql/si_objects.cc
Removed privilege elevation from si_objects code.
@ sql/sql_class.cc
Added methods to save the access levels for turning off
elevation and preserving user context.
@ sql/sql_class.h
Added methods to preserve privilege access.
modified:
mysql-test/suite/backup/r/backup_restore_security.result
mysql-test/suite/backup/r/backup_security.result
mysql-test/suite/backup/t/backup_restore_security.test
mysql-test/suite/backup/t/backup_security.test
sql/backup/backup_info.cc
sql/backup/kernel.cc
sql/backup/restore_info.h
sql/si_objects.cc
sql/sql_class.cc
sql/sql_class.h
=== modified file 'mysql-test/suite/backup/r/backup_restore_security.result'
--- a/mysql-test/suite/backup/r/backup_restore_security.result 2009-10-30 15:59:07 +0000
+++ b/mysql-test/suite/backup/r/backup_restore_security.result 2009-11-02 21:57:32 +0000
@@ -40,6 +40,13 @@ DELETE FROM backup_test.t1 WHERE a = "no
#
GRANT ALL ON backup_test.* TO 'joe'@'user';
#
+# Create a second database for testing multiple database privilege
+# checking.
+#
+CREATE DATABASE backup_test_alt;
+CREATE TABLE backup_test_alt.t1 (a int);
+INSERT INTO backup_test_alt.t1 VALUES (10), (11), (12);
+#
# Revoke grants for bup_some_priv
#
REVOKE ALL ON *.* FROM 'bup_some_priv'@'localhost';
@@ -56,6 +63,12 @@ GRANT USAGE ON *.* TO 'bup_some_priv'@'l
BACKUP DATABASE backup_test to 'backup_test_orig.bak';
backup_id
#
+BACKUP DATABASE backup_test, backup_test_alt to 'backup_test_orig_alt1.bak';
+backup_id
+#
+BACKUP DATABASE backup_test_alt, backup_test to 'backup_test_orig_alt2.bak';
+backup_id
+#
#
# Show list of all objects in the database.
#
@@ -88,6 +101,7 @@ backup_id
# the database.
#
GRANT RESTORE ON backup_test.* TO 'bup_some_priv'@'localhost';
+GRANT CREATE, DROP ON backup_test.* TO 'bup_some_priv'@'localhost';
FLUSH PRIVILEGES;
#
# Show grants for user.
@@ -95,7 +109,7 @@ FLUSH PRIVILEGES;
SHOW GRANTS FOR 'bup_some_priv'@'localhost';
Grants for bup_some_priv@localhost
GRANT USAGE ON *.* TO 'bup_some_priv'@'localhost'
-GRANT RESTORE ON `backup_test`.* TO 'bup_some_priv'@'localhost'
+GRANT CREATE, DROP, RESTORE ON `backup_test`.* TO 'bup_some_priv'@'localhost'
#
# Connect as user with only some privileges.
#
@@ -104,10 +118,10 @@ GRANT RESTORE ON `backup_test`.* TO 'bup
# error ER_RESTORE_ACCESS_OBJS_INCOMPLETE
#
RESTORE FROM 'backup_test_orig.bak' OVERWRITE;
-ERROR HY000: Insufficient privileges. You do not have privileges to restore the object 'backup_test' from this backup image.
+ERROR HY000: Insufficient privileges. You do not have privileges to restore the object 'p1' from this backup image.
SHOW ERRORS;
Level Code Message
-Error # Insufficient privileges. You do not have privileges to restore the object 'backup_test' from this backup image.
+Error # Insufficient privileges. You do not have privileges to restore the object 'p1' from this backup image.
#
# Test Case 2 : Ensure a user with only SELECT on db1.* cannot restore
# the database.
@@ -124,7 +138,7 @@ FLUSH PRIVILEGES;
SHOW GRANTS FOR 'bup_some_priv'@'localhost';
Grants for bup_some_priv@localhost
GRANT USAGE ON *.* TO 'bup_some_priv'@'localhost'
-GRANT SELECT, RESTORE ON `backup_test`.* TO 'bup_some_priv'@'localhost'
+GRANT SELECT, CREATE, DROP, RESTORE ON `backup_test`.* TO 'bup_some_priv'@'localhost'
#
# Connect as user with only some privileges.
#
@@ -133,10 +147,10 @@ GRANT SELECT, RESTORE ON `backup_test`.*
# error ER_RESTORE_ACCESS_OBJS_INCOMPLETE
#
RESTORE FROM 'backup_test_orig.bak' OVERWRITE;
-ERROR HY000: Insufficient privileges. You do not have privileges to restore the object 'backup_test' from this backup image.
+ERROR HY000: Insufficient privileges. You do not have privileges to restore the object 'p1' from this backup image.
SHOW ERRORS;
Level Code Message
-Error # Insufficient privileges. You do not have privileges to restore the object 'backup_test' from this backup image.
+Error # Insufficient privileges. You do not have privileges to restore the object 'p1' from this backup image.
#
# Test Case 5 : Show that SUPER is needed to complete restore
# when there are objects with definer clauses.
@@ -224,14 +238,13 @@ REVOKE ALL ON *.* FROM 'bup_some_priv'@'
GRANT INSERT, UPDATE, DELETE, BACKUP, RESTORE, SELECT, CREATE, DROP
ON backup_test_trig.* TO 'bup_some_priv'@'localhost' WITH GRANT OPTION;
GRANT SELECT ON mysql.* TO 'bup_some_priv'@'localhost';
-GRANT SUPER ON *.* TO 'bup_some_priv'@'localhost';
FLUSH PRIVILEGES;
#
# Show grants for user.
#
SHOW GRANTS FOR 'bup_some_priv'@'localhost';
Grants for bup_some_priv@localhost
-GRANT SUPER ON *.* TO 'bup_some_priv'@'localhost'
+GRANT USAGE ON *.* TO 'bup_some_priv'@'localhost'
GRANT SELECT ON `mysql`.* TO 'bup_some_priv'@'localhost'
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, BACKUP, RESTORE ON `backup_test_trig`.* TO 'bup_some_priv'@'localhost' WITH GRANT OPTION
#
@@ -241,11 +254,11 @@ GRANT SELECT, INSERT, UPDATE, DELETE, CR
# conn_some_priv: Attempting restore. Should fail with
# error ER_RESTORE_ACCESS_OBJS_INCOMPLETE
#
-RESTORE FROM 'backup_test_orig.bak' OVERWRITE;
-ERROR HY000: Insufficient privileges. You do not have privileges to restore the object 'backup_test' from this backup image.
+RESTORE FROM 'backup_test_trig.bak' OVERWRITE;
+ERROR HY000: Insufficient privileges. You do not have privileges to restore the object 'trg' from this backup image.
SHOW ERRORS;
Level Code Message
-Error # Insufficient privileges. You do not have privileges to restore the object 'backup_test' from this backup image.
+Error # Insufficient privileges. You do not have privileges to restore the object 'trg' from this backup image.
#
# Connect as root and add privilege for trigger.
#
@@ -255,16 +268,109 @@ FLUSH PRIVILEGES;
# Connect as user with only some privileges.
#
#
+# conn_some_priv: Attempting restore. Should fail with
+# ER_RESTORE_ACCESS_DEFINER
+#
+RESTORE FROM 'backup_test_trig.bak' OVERWRITE;
+ERROR HY000: Insufficient privileges. You must have the SUPER privilege to restore the object 'backup_test_trig'.'trg'.
+SHOW ERRORS;
+Level Code Message
+Error 1799 Insufficient privileges. You must have the SUPER privilege to restore the object 'backup_test_trig'.'trg'.
+#
+# Connect as root and add privilege for trigger.
+# In this case, we must add SUPER to overcome limitation with
+# the definer clause.
+#
+GRANT SUPER ON *.* TO 'bup_some_priv'@'localhost';
+FLUSH PRIVILEGES;
+#
+# Connect as user with only some privileges.
+#
+#
# conn_some_priv: Attempting restore. Should succeed.
#
RESTORE FROM 'backup_test_trig.bak' OVERWRITE;
backup_id
#
#
-# Connect as root and cleanup.
+# Change privileges for elevated restore test case.
#
DROP DATABASE backup_test_trig;
#
+# Test Case 8 : Show that user can have only TRIGGER + SUPER and
+# perform an elevated restore.
+#
+DROP USER 'bup_some_priv'@'localhost';
+FLUSH PRIVILEGES;
+CREATE USER 'bup_some_priv'@'localhost';
+REVOKE ALL ON *.* FROM 'bup_some_priv'@'localhost';
+GRANT RESTORE ON backup_test.* TO 'bup_some_priv'@'localhost';
+GRANT SUPER ON *.* TO 'bup_some_priv'@'localhost';
+FLUSH PRIVILEGES;
+#
+# Connect as user with only some privileges.
+#
+#
+# conn_some_priv: Attempting restore. Should succeed.
+#
+RESTORE FROM 'backup_test_orig.bak' OVERWRITE;
+backup_id
+#
+#
+# Change privileges for elevated restore test case.
+#
+#
+# Test Case 9 : Show that users who have RESTORE + SUPER for db1 but not
+# for db2 cannot restore the image.
+#
+DROP USER 'bup_some_priv'@'localhost';
+FLUSH PRIVILEGES;
+CREATE USER 'bup_some_priv'@'localhost';
+REVOKE ALL ON *.* FROM 'bup_some_priv'@'localhost';
+GRANT RESTORE ON backup_test.* TO 'bup_some_priv'@'localhost';
+GRANT SUPER ON *.* TO 'bup_some_priv'@'localhost';
+FLUSH PRIVILEGES;
+#
+# Connect as user with only some privileges.
+#
+#
+# conn_some_priv: Attempting restore. Should fail with
+# error ER_RESTORE_ACCESS_DENIED_ERROR
+#
+RESTORE FROM 'backup_test_orig_alt1.bak' OVERWRITE;
+ERROR HY000: Insufficient privileges. You must have the RESTORE privilege to restore database 'backup_test_alt'.
+SHOW ERRORS;
+Level Code Message
+Error 1794 Insufficient privileges. You must have the RESTORE privilege to restore database 'backup_test_alt'.
+Error 1798 Insufficient privileges. You do not have privileges to restore the object 't1' from this backup image.
+#
+# conn_some_priv: Attempting restore. Should fail with
+# error ER_RESTORE_ACCESS_DENIED_ERROR
+#
+RESTORE FROM 'backup_test_orig_alt2.bak' OVERWRITE;
+ERROR HY000: Insufficient privileges. You must have the RESTORE privilege to restore database 'backup_test_alt'.
+SHOW ERRORS;
+Level Code Message
+Error 1794 Insufficient privileges. You must have the RESTORE privilege to restore database 'backup_test_alt'.
+Error 1798 Insufficient privileges. You do not have privileges to restore the object 'backup_test' from this backup image.
+#
+# Change privileges for elevated restore test case.
+#
+GRANT RESTORE ON backup_test_alt.* TO 'bup_some_priv'@'localhost';
+FLUSH PRIVILEGES;
+#
+# Connect as user with only some privileges.
+#
+#
+# conn_some_priv: Attempting restore. Should succeed.
+#
+RESTORE FROM 'backup_test_orig.bak' OVERWRITE;
+backup_id
+#
+#
+# Connect as root and cleanup.
+#
+#
# Compare to original backup image file.
#
RESTORE FROM 'backup_test_orig.bak' OVERWRITE;
@@ -294,4 +400,5 @@ trg
DROP USER 'bup_some_priv'@'localhost';
DROP USER 'joe'@'user';
DROP DATABASE backup_test;
+DROP DATABASE backup_test_alt;
FLUSH PRIVILEGES;
=== modified file 'mysql-test/suite/backup/r/backup_security.result'
--- a/mysql-test/suite/backup/r/backup_security.result 2009-10-23 21:26:11 +0000
+++ b/mysql-test/suite/backup/r/backup_security.result 2009-11-02 21:57:32 +0000
@@ -122,6 +122,7 @@ ERROR HY000: Insufficient privileges. Yo
SHOW ERRORS;
Level Code Message
Error # Insufficient privileges. You must have the RESTORE privilege to restore database 'backup_test'.
+Error # Insufficient privileges. You do not have privileges to restore the object 't1' from this backup image.
#
# Show user has not gained rights.
#
@@ -401,13 +402,13 @@ Got one of the listed errors
#
#
# conn_user1: Attempting backup. Should fail with
-# error ER_BACKUP_ACCESS_DENIED_ERROR
+# error ER_BAD_DB_ERROR
#
BACKUP DATABASE backup_test_alt to 'bup_no_priv.bak';
-ERROR HY000: Insufficient privileges. You must have the BACKUP privilege to backup database 'backup_test_alt'.
+ERROR 42000: Unknown database 'backup_test_alt'
SHOW ERRORS;
Level Code Message
-Error # Insufficient privileges. You must have the BACKUP privilege to backup database 'backup_test_alt'.
+Error # Unknown database 'backup_test_alt'
#
# conn_user1: Attempting backup. Should fail with
# error ER_BACKUP_ACCESS_DENIED_ERROR
=== modified file 'mysql-test/suite/backup/t/backup_restore_security.test'
--- a/mysql-test/suite/backup/t/backup_restore_security.test 2009-10-30 15:59:07 +0000
+++ b/mysql-test/suite/backup/t/backup_restore_security.test 2009-11-02 21:57:32 +0000
@@ -12,6 +12,10 @@
# 6. Ensure a user can only restore the database if and only if she has
# all of the required permissions to create and populate all objects.
# 7. Show that having TRIGGER on a specific table permits restore.
+# 8. Show that user can have only TRIGGER + SUPER and perform an
+# elevated restore.
+# 9. Show that users who have RESTORE + SUPER for db1 but not for db2
+# cannot restore the image.
#
--source include/not_embedded.inc
@@ -40,6 +44,15 @@ CREATE USER 'joe'@'user';
--source suite/backup/include/basic_data.inc
--echo #
+--echo # Create a second database for testing multiple database privilege
+--echo # checking.
+--echo #
+
+CREATE DATABASE backup_test_alt;
+CREATE TABLE backup_test_alt.t1 (a int);
+INSERT INTO backup_test_alt.t1 VALUES (10), (11), (12);
+
+--echo #
--echo # Revoke grants for bup_some_priv
--echo #
REVOKE ALL ON *.* FROM 'bup_some_priv'@'localhost';
@@ -58,6 +71,12 @@ SHOW GRANTS FOR 'bup_some_priv'@'localho
--replace_column 1 #
BACKUP DATABASE backup_test to 'backup_test_orig.bak';
+--replace_column 1 #
+BACKUP DATABASE backup_test, backup_test_alt to 'backup_test_orig_alt1.bak';
+
+--replace_column 1 #
+BACKUP DATABASE backup_test_alt, backup_test to 'backup_test_orig_alt2.bak';
+
--echo #
--echo # Show list of all objects in the database.
--echo #
@@ -83,6 +102,7 @@ BACKUP DATABASE backup_test to 'backup_t
--echo #
GRANT RESTORE ON backup_test.* TO 'bup_some_priv'@'localhost';
+GRANT CREATE, DROP ON backup_test.* TO 'bup_some_priv'@'localhost';
FLUSH PRIVILEGES;
@@ -254,7 +274,6 @@ REVOKE ALL ON *.* FROM 'bup_some_priv'@'
GRANT INSERT, UPDATE, DELETE, BACKUP, RESTORE, SELECT, CREATE, DROP
ON backup_test_trig.* TO 'bup_some_priv'@'localhost' WITH GRANT OPTION;
GRANT SELECT ON mysql.* TO 'bup_some_priv'@'localhost';
-GRANT SUPER ON *.* TO 'bup_some_priv'@'localhost';
FLUSH PRIVILEGES;
@@ -275,7 +294,7 @@ connect (conn_some_priv,localhost,bup_so
--echo #
--replace_column 1 #
--error ER_RESTORE_ACCESS_OBJS_INCOMPLETE
-RESTORE FROM 'backup_test_orig.bak' OVERWRITE;
+RESTORE FROM 'backup_test_trig.bak' OVERWRITE;
--replace_column 2 #
SHOW ERRORS;
@@ -296,6 +315,33 @@ disconnect conn_root;
connect (conn_some_priv,localhost,bup_some_priv,,);
--echo #
+--echo # conn_some_priv: Attempting restore. Should fail with
+--echo # ER_RESTORE_ACCESS_DEFINER
+--echo #
+--replace_column 1 #
+--error ER_RESTORE_ACCESS_DEFINER
+RESTORE FROM 'backup_test_trig.bak' OVERWRITE;
+SHOW ERRORS;
+
+disconnect conn_some_priv;
+--echo #
+--echo # Connect as root and add privilege for trigger.
+--echo # In this case, we must add SUPER to overcome limitation with
+--echo # the definer clause.
+--echo #
+connect (conn_root,localhost,root,,);
+
+GRANT SUPER ON *.* TO 'bup_some_priv'@'localhost';
+
+FLUSH PRIVILEGES;
+
+disconnect conn_root;
+--echo #
+--echo # Connect as user with only some privileges.
+--echo #
+connect (conn_some_priv,localhost,bup_some_priv,,);
+
+--echo #
--echo # conn_some_priv: Attempting restore. Should succeed.
--echo #
--replace_column 1 #
@@ -303,13 +349,118 @@ RESTORE FROM 'backup_test_trig.bak' OVER
disconnect conn_some_priv;
--echo #
---echo # Connect as root and cleanup.
+--echo # Change privileges for elevated restore test case.
--echo #
connect (conn_root,localhost,root,,);
DROP DATABASE backup_test_trig;
--echo #
+--echo # Test Case 8 : Show that user can have only TRIGGER + SUPER and
+--echo # perform an elevated restore.
+--echo #
+
+DROP USER 'bup_some_priv'@'localhost';
+
+FLUSH PRIVILEGES;
+
+CREATE USER 'bup_some_priv'@'localhost';
+
+REVOKE ALL ON *.* FROM 'bup_some_priv'@'localhost';
+GRANT RESTORE ON backup_test.* TO 'bup_some_priv'@'localhost';
+GRANT SUPER ON *.* TO 'bup_some_priv'@'localhost';
+
+FLUSH PRIVILEGES;
+
+disconnect conn_root;
+--echo #
+--echo # Connect as user with only some privileges.
+--echo #
+connect (conn_some_priv,localhost,bup_some_priv,,);
+
+--echo #
+--echo # conn_some_priv: Attempting restore. Should succeed.
+--echo #
+--replace_column 1 #
+RESTORE FROM 'backup_test_orig.bak' OVERWRITE;
+
+disconnect conn_some_priv;
+--echo #
+--echo # Change privileges for elevated restore test case.
+--echo #
+connect (conn_root,localhost,root,,);
+
+--echo #
+--echo # Test Case 9 : Show that users who have RESTORE + SUPER for db1 but not
+--echo # for db2 cannot restore the image.
+--echo #
+
+DROP USER 'bup_some_priv'@'localhost';
+
+FLUSH PRIVILEGES;
+
+CREATE USER 'bup_some_priv'@'localhost';
+
+REVOKE ALL ON *.* FROM 'bup_some_priv'@'localhost';
+GRANT RESTORE ON backup_test.* TO 'bup_some_priv'@'localhost';
+GRANT SUPER ON *.* TO 'bup_some_priv'@'localhost';
+
+FLUSH PRIVILEGES;
+
+disconnect conn_root;
+--echo #
+--echo # Connect as user with only some privileges.
+--echo #
+connect (conn_some_priv,localhost,bup_some_priv,,);
+
+--echo #
+--echo # conn_some_priv: Attempting restore. Should fail with
+--echo # error ER_RESTORE_ACCESS_DENIED_ERROR
+--echo #
+--replace_column 1 #
+--error ER_RESTORE_ACCESS_DENIED_ERROR
+RESTORE FROM 'backup_test_orig_alt1.bak' OVERWRITE;
+SHOW ERRORS;
+
+--echo #
+--echo # conn_some_priv: Attempting restore. Should fail with
+--echo # error ER_RESTORE_ACCESS_DENIED_ERROR
+--echo #
+--replace_column 1 #
+--error ER_RESTORE_ACCESS_DENIED_ERROR
+RESTORE FROM 'backup_test_orig_alt2.bak' OVERWRITE;
+SHOW ERRORS;
+
+disconnect conn_some_priv;
+--echo #
+--echo # Change privileges for elevated restore test case.
+--echo #
+connect (conn_root,localhost,root,,);
+
+GRANT RESTORE ON backup_test_alt.* TO 'bup_some_priv'@'localhost';
+
+FLUSH PRIVILEGES;
+
+disconnect conn_root;
+--echo #
+--echo # Connect as user with only some privileges.
+--echo #
+connect (conn_some_priv,localhost,bup_some_priv,,);
+
+--echo #
+--echo # conn_some_priv: Attempting restore. Should succeed.
+--echo #
+--replace_column 1 #
+RESTORE FROM 'backup_test_orig.bak' OVERWRITE;
+
+
+disconnect conn_some_priv;
+--echo #
+--echo # Connect as root and cleanup.
+--echo #
+connect (conn_root,localhost,root,,);
+
+--echo #
--echo # Compare to original backup image file.
--echo #
@@ -332,9 +483,12 @@ DROP USER 'bup_some_priv'@'localhost';
DROP USER 'joe'@'user';
DROP DATABASE backup_test;
+DROP DATABASE backup_test_alt;
FLUSH PRIVILEGES;
let $MYSQLD_BACKUPDIR= `select @@backupdir`;
remove_file $MYSQLD_BACKUPDIR/backup_test_orig.bak;
+remove_file $MYSQLD_BACKUPDIR/backup_test_orig_alt1.bak;
+remove_file $MYSQLD_BACKUPDIR/backup_test_orig_alt2.bak;
remove_file $MYSQLD_BACKUPDIR/backup_test_trig.bak;
remove_file $MYSQLD_BACKUPDIR/backup_test_no_proc.bak;
=== modified file 'mysql-test/suite/backup/t/backup_security.test'
--- a/mysql-test/suite/backup/t/backup_security.test 2009-10-23 21:26:11 +0000
+++ b/mysql-test/suite/backup/t/backup_security.test 2009-11-02 21:57:32 +0000
@@ -188,7 +188,7 @@ SHOW TABLES FROM backup_test;
--echo # catalog errors.
--echo #
--replace_column 1 #
---error ER_BACKUP_CANT_RESTORE_DB, ER_BACKUP_CANT_RESTORE_TABLE, ER_BACKUP_CANT_RESTORE_VIEW, ER_BACKUP_CANT_RESTORE_SROUT, ER_BACKUP_CANT_RESTORE_EVENT, ER_BACKUP_CANT_RESTORE_TRIGGER
+--error ER_BACKUP_CANT_RESTORE_DB, ER_BACKUP_CANT_RESTORE_TABLE, ER_BACKUP_CANT_RESTORE_VIEW, ER_BACKUP_CANT_RESTORE_SROUT, ER_BACKUP_CANT_RESTORE_EVENT, ER_BACKUP_CANT_RESTORE_TRIGGER, ER_RESTORE_ACCESS_OBJS_INCOMPLETE
eval RESTORE FROM 'bup_user1.bak' OVERWRITE;
--echo #
@@ -198,10 +198,10 @@ eval RESTORE FROM 'bup_user1.bak' OVERWR
--echo #
--echo # conn_user1: Attempting backup. Should fail with
---echo # error ER_BACKUP_ACCESS_DENIED_ERROR
+--echo # error ER_BAD_DB_ERROR
--echo #
--replace_column 1 #
---error ER_BACKUP_ACCESS_DENIED_ERROR
+--error ER_BAD_DB_ERROR
BACKUP DATABASE backup_test_alt to 'bup_no_priv.bak';
--replace_column 2 #
SHOW ERRORS;
=== modified file 'sql/backup/backup_info.cc'
--- a/sql/backup/backup_info.cc 2009-10-23 15:41:56 +0000
+++ b/sql/backup/backup_info.cc 2009-11-02 21:57:32 +0000
@@ -665,21 +665,31 @@ backup::Image_info::Db* Backup_info::add
/*
Check privileges for this database. User must have BACKUP
privilege in order to execute a backup.
+
+ We must turn privilege checking back on first.
*/
+ m_thd->security_ctx->reset_grant_info();
+
DEBUG_SYNC(m_thd, "before_backup_privileges");
if (check_access(m_thd, BACKUP_ACL, name->ptr(), 0, 1, 1, 0))
{
m_log.report_error(ER_BACKUP_ACCESS_DENIED_ERROR, name->ptr());
return NULL;
- }
+ }
+ /*
+ The base check for BACKUP_ACL for this database is satisfied,
+ ok to elevate by turning off privilege checking.
+ */
+ m_thd->security_ctx->skip_grants_user();
+
// Check to see if the user can see all of the objects in the database.
if (obs::check_user_access(m_thd, name))
{
m_log.report_error(ER_BACKUP_ACCESS_OBJS_INCOMPLETE, name->ptr());
return NULL;
}
-
+
Db *db= Image_info::add_db(*name, pos);
if (!db)
@@ -766,7 +776,7 @@ int Backup_info::add_dbs(THD *thd, List<
delete obj;
goto error;
}
-
+
if (add_db_items(*db)) // Reports errors.
goto error;
}
=== modified file 'sql/backup/kernel.cc'
--- a/sql/backup/kernel.cc 2009-10-30 15:59:07 +0000
+++ b/sql/backup/kernel.cc 2009-11-02 21:57:32 +0000
@@ -241,7 +241,7 @@ execute_backup_command(THD *thd,
/* Backup databases specified by user. */
res= info->add_dbs(thd, lex->db_list);
}
-
+
info->close(); // Close catalogue after filling it with objects to backup.
DBUG_EXECUTE_IF("kill_backup", thd->killed= THD::KILL_QUERY;);
@@ -639,6 +639,11 @@ int Backup_restore_ctx::prepare_path(::S
int Backup_restore_ctx::prepare(::String *backupdir, LEX_STRING location)
{
+ /*
+ Save privilege information for restoring later.
+ */
+ m_thd->security_ctx->save_grant_info();
+
if (m_error)
return m_error;
@@ -1180,6 +1185,11 @@ int Backup_restore_ctx::close()
{
int res= 0;
+ /*
+ Restore privilege information.
+ */
+ m_thd->security_ctx->reset_grant_info();
+
if (m_state == CLOSED)
return 0;
@@ -1961,27 +1971,27 @@ int bcat_close(st_bstream_image_header *
/**
- Check access to a database-level object.
+ Check access to a database-level object.
- This method gets the database name for an object and checks
- the privilege requested for the object.
-
- @param[in] thd Current thread.
- @param[in] item The item to check.
- @param[in] info The restore info object.
- @param[in] priv The privileged being checked.
+ This method gets the database name for an object and checks
+ the privilege requested for the object.
- @returns bool
- @retval TRUE the user has the privilege
- @retval FALSE the user does not have the privilege
-*/
+ @param[in] THD Current thread.
+ @param[in] item The item to check.
+ @param[in] info The restore info object.
+ @param[in] priv The privileged being checked.
+
+ @returns bool
+ @retval TRUE the user has the privilege
+ @retval FALSE the user does not have the privilege
+ */
bool check_catalog_item_access(THD *thd,
struct st_bstream_item_info *item,
Restore_info *info,
ulong priv)
{
using namespace backup;
-
+
st_bstream_dbitem_info *it= (st_bstream_dbitem_info*)item;
if (!it)
return BSTREAM_ERROR;
@@ -1989,183 +1999,246 @@ bool check_catalog_item_access(THD *thd,
Image_info::Db *db= (Image_info::Db*) info->get_db(it->db->base.pos);
if (!db)
return BSTREAM_ERROR;
-
+
return check_access(thd, priv, db->name().ptr(), 0, 1, 1, 0);
}
+
/**
- Perform privilege checking for restore.
+ Perform privilege checking for restore.
- This method checks the known minimal privileges needed to restore each
- object in the backup image. For tables, triggers, and views, an object-
- level privilege check is made. For all other objects encountered, a
- database-level privilege check is made.
+ This method checks the known minimal privileges needed to restore each
+ object in the backup image. For tables, triggers, and views, an object-
+ level privilege check is made. For all other objects encountered, a
+ database-level privilege check is made.
- This method is called once for every object in the catalog. It ensures
- there are no unusual restrictions that would prohibit the execution of
- restore.
+ This method is called once for every object in the catalog. It ensures
+ there are no unusual restrictions that would prohibit the execution of
+ restore.
- @param[in] item The item to check.
- @param[in] info The restore info object.
- @param[in] name_str The name of the object.
+ @param[in] item The item to check.
+ @param[in] info The restore info object.
+ @param[in] name_str The name of the object.
- @returns
- @retval BSTREAM_OK User has privileges for the object.
- @retval BSTREAM_ERROR User does not have privileges for object.
-*/
+ @returns
+ @retval BSTREAM_OK User has privileges for the object.
+ @retval BSTREAM_ERROR User does not have privileges for object.
+ */
int check_restore_privileges(struct st_bstream_item_info *item,
Restore_info *info,
const char *name_str)
{
using namespace backup;
int result= 0;
- THD *thd= ::current_thd;
Image_info::Db *db= NULL;
-
- Logger &log= info->m_log;
+
+ Logger &m_log= info->m_log;
/*
- Check privileges for each object based on type of object.
- */
- switch (item->type) {
- case BSTREAM_IT_TABLESPACE:
- {
- result= check_global_access(thd, CREATE_TABLESPACE_ACL);
- break;
- }
- case BSTREAM_IT_DB:
+ Check privileges for this database.
+
+ If user has RESTORE + SUPER, do not do object-level privilege checking.
+
+ If the user does not have RESTORE, fail with an error.
+
+ @note Databases are listed after tablesspaces and charsets in the
+ catalog. Thus, the list of databases are read before the database
+ objects.
+ */
+ if (item->type == BSTREAM_IT_DB)
+ {
+ // We must turn privilege checking back on first.
+ info->m_thd->security_ctx->reset_grant_info();
+
+ /*
+ If this is the first check, set m_skip_precheck.
+ */
+ if (info->m_first_priv_check)
{
- result= check_access(thd, CREATE_ACL + DROP_ACL,
- name_str, 0, 1, 1, 0);
- break;
+ info->m_skip_precheck= TRUE;
+ info->m_first_priv_check= FALSE;
}
- case BSTREAM_IT_TABLE:
- case BSTREAM_IT_VIEW:
+ if (!check_access(info->m_thd, RESTORE_ACL, name_str, 0, 1, 1, 0) &&
+ (info->m_thd->security_ctx->master_access & SUPER_ACL) &&
+ info->m_skip_precheck)
{
- st_bstream_table_info *it= (st_bstream_table_info*)item;
- if (!it)
- return BSTREAM_ERROR;
-
- db= info->get_db(it->base.db->base.pos); // reports errors
- if (!db)
- return BSTREAM_ERROR;
-
- TABLE_LIST *ptr= (TABLE_LIST*)alloc_root(thd->mem_root,
- sizeof(TABLE_LIST));
- if (!ptr)
- return BSTREAM_ERROR;
-
- ptr->init_one_table(db->name().ptr(), db->name().length(),
- name_str, strlen(name_str),
- name_str, TL_READ);
-
- result= check_table_access(thd, CREATE_ACL, ptr, TRUE, TRUE, 1);
-
- break;
+ /*
+ The base check for RESTORE_ACL + SUPER_ACL for this database is
+ satisfied. It is ok to elevate by turning off privilege checking.
+ */
+ info->m_thd->security_ctx->skip_grants_user();
+ info->m_skip_precheck= TRUE;
}
- case BSTREAM_IT_SPROC:
- case BSTREAM_IT_SFUNC:
+ else
{
- // If binary log is turned on we also have to have SUPER.
- if (obs::is_binlog_engaged())
- result= check_catalog_item_access(thd, item, info,
- (CREATE_PROC_ACL | SUPER_ACL));
- else
- result= check_catalog_item_access(thd, item, info, CREATE_PROC_ACL);
-
+ info->m_skip_precheck= FALSE;
}
- case BSTREAM_IT_EVENT:
+
+ if (check_access(info->m_thd, RESTORE_ACL, name_str, 0, 1, 1, 0))
{
- result= check_catalog_item_access(thd, item, info, EVENT_ACL);
- break;
+ m_log.report_error(ER_RESTORE_ACCESS_DENIED_ERROR, name_str);
+ return NULL;
}
- case BSTREAM_IT_TRIGGER:
- {
- /*
- Get the table reference for this trigger and check access.
- */
- st_bstream_dbitem_info *it= (st_bstream_dbitem_info*)item;
- if (!it)
- return BSTREAM_ERROR;
-
- db= info->get_db(it->db->base.pos); // reports errors
- if (!db)
- return BSTREAM_ERROR;
-
- TABLE_LIST *ptr= (TABLE_LIST*)alloc_root(thd->mem_root,
- sizeof(TABLE_LIST));
- if (!ptr)
- return BSTREAM_ERROR;
-
- Image_info::Table *tbl= info->get_table(it->snap_num, it->pos);
-
- /*
- If the table is not found, revert to checking at the database
- level.
- */
- if (!tbl)
+ }
+
+ /*
+ If the user does not have RESTORE + SUPER, as indicated by m_skip_precheck
+ == FALSE, we must perform object-level privilege checking for all objects
+ being restored. The user must have, at a minimum, the ability to create
+ each object plus any specific privileges for the object type (e.g.
+ functions, triggers, events, etc.).
+
+ An error is returned if the user does not have the correct privileges
+ to create the object.
+ */
+ if (!info->m_skip_precheck)
+ {
+
+ switch (item->type) {
+ case BSTREAM_IT_TABLESPACE:
{
- result= check_catalog_item_access(thd, item, info, TRIGGER_ACL);
+ result= check_global_access(info->m_thd, CREATE_TABLESPACE_ACL);
+ break;
}
- else
+ case BSTREAM_IT_DB:
{
+ result= check_access(info->m_thd, CREATE_ACL + DROP_ACL,
+ name_str, 0, 1, 1, 0);
+ break;
+ }
+ case BSTREAM_IT_TABLE:
+ case BSTREAM_IT_VIEW:
+ {
+ st_bstream_table_info *it= (st_bstream_table_info*)item;
+ if (!it)
+ return BSTREAM_ERROR;
+
+ db= info->get_db(it->base.db->base.pos); // reports errors
+ if (!db)
+ return BSTREAM_ERROR;
+
+ TABLE_LIST *ptr= (TABLE_LIST*)alloc_root(info->m_thd->mem_root,
+ sizeof(TABLE_LIST));
+ if (!ptr)
+ return BSTREAM_ERROR;
+
ptr->init_one_table(db->name().ptr(), db->name().length(),
- tbl->name().ptr(), tbl->name().length(),
- tbl->name().ptr(), TL_READ);
- result= check_table_access(thd, TRIGGER_ACL, ptr, TRUE, TRUE, 1);
+ name_str, strlen(name_str),
+ name_str, TL_READ);
+
+ result= check_table_access(info->m_thd, CREATE_ACL, ptr, TRUE, TRUE, 1);
+
+ break;
}
- break;
- }
- case BSTREAM_IT_PRIVILEGE:
+ case BSTREAM_IT_SPROC:
+ case BSTREAM_IT_SFUNC:
+ {
+ // If binary log is turned on we also have to have SUPER.
+ if (obs::is_binlog_engaged())
+ result= check_catalog_item_access(info->m_thd, item, info,
+ (CREATE_PROC_ACL | SUPER_ACL));
+ else
+ result= check_catalog_item_access(info->m_thd, item, info,
+ CREATE_PROC_ACL);
+
+ }
+ case BSTREAM_IT_EVENT:
+ {
+ result= check_catalog_item_access(info->m_thd, item, info, EVENT_ACL);
+ break;
+ }
+ case BSTREAM_IT_TRIGGER:
+ {
+ /*
+ Get the table reference for this trigger and check access.
+ */
+ st_bstream_dbitem_info *it= (st_bstream_dbitem_info*)item;
+ if (!it)
+ return BSTREAM_ERROR;
+
+ db= info->get_db(it->db->base.pos); // reports errors
+ if (!db)
+ return BSTREAM_ERROR;
+
+ TABLE_LIST *ptr= (TABLE_LIST*)alloc_root(info->m_thd->mem_root,
+ sizeof(TABLE_LIST));
+ if (!ptr)
+ return BSTREAM_ERROR;
+
+ Image_info::Table *tbl= info->get_table(it->snap_num, it->pos);
+
+ /*
+ If the table is not found, revert to checking at the database
+ level.
+ */
+ if (!tbl)
+ {
+ result= check_catalog_item_access(info->m_thd, item, info,
+ TRIGGER_ACL);
+ }
+ else
+ {
+ ptr->init_one_table(db->name().ptr(), db->name().length(),
+ tbl->name().ptr(), tbl->name().length(),
+ tbl->name().ptr(), TL_READ);
+ result= check_table_access(info->m_thd, TRIGGER_ACL, ptr, TRUE,
+ TRUE, 1);
+ }
+ break;
+ }
+ case BSTREAM_IT_PRIVILEGE:
+ {
+ result= check_catalog_item_access(info->m_thd, item, info, GRANT_ACL);
+ break;
+ }
+ default:
+ break;
+ } // switch (item->type)
+
+ // Return error if privilege check fails.
+ if (result)
{
- result= check_catalog_item_access(thd, item, info, GRANT_ACL);
- break;
+ m_log.report_error(ER_RESTORE_ACCESS_OBJS_INCOMPLETE, name_str);
+ return BSTREAM_ERROR;
}
- default:
- break;
- } // switch (item->type)
- // Return error if privilege check fails.
- if (result)
- {
- log.report_error(ER_RESTORE_ACCESS_OBJS_INCOMPLETE, name_str);
- return BSTREAM_ERROR;
- }
-
- /*
- Check for SUPER_ACL if any objects exist that have a definer clause.
- */
- switch (item->type) {
- case BSTREAM_IT_VIEW:
- case BSTREAM_IT_SPROC:
- case BSTREAM_IT_SFUNC:
- case BSTREAM_IT_EVENT:
- case BSTREAM_IT_TRIGGER:
+ /*
+ Check for SUPER_ACL if any objects exist that have a definer clause.
+ */
+ switch (item->type) {
+ case BSTREAM_IT_VIEW:
+ case BSTREAM_IT_SPROC:
+ case BSTREAM_IT_SFUNC:
+ case BSTREAM_IT_EVENT:
+ case BSTREAM_IT_TRIGGER:
+ {
+ st_bstream_table_info *it= (st_bstream_table_info*)item;
+ if (!it)
+ return BSTREAM_ERROR;
+
+ db= info->get_db(it->base.db->base.pos); // reports errors
+ if (!db)
+ return BSTREAM_ERROR;
+
+ result= check_access(info->m_thd, SUPER_ACL, db->name().ptr(),
+ 0, 1, 1, 0);
+ break;
+ }
+ default:
+ break;
+ } // switch (item->type)
+
+ // Return error if privilege check fails.
+ if (result)
{
- st_bstream_table_info *it= (st_bstream_table_info*)item;
- if (!it)
- return BSTREAM_ERROR;
-
- db= info->get_db(it->base.db->base.pos); // reports errors
- if (!db)
- return BSTREAM_ERROR;
-
- result= check_access(thd, SUPER_ACL, db->name().ptr(), 0, 1, 1, 0);
- break;
+ m_log.report_error(ER_RESTORE_ACCESS_DEFINER, db->name().ptr(), name_str);
+ return BSTREAM_ERROR;
}
- default:
- break;
- } // switch (item->type)
-
- // Return error if privilege check fails.
- if (result)
- {
- log.report_error(ER_RESTORE_ACCESS_DEFINER, db->name().ptr(), name_str);
- return BSTREAM_ERROR;
}
return BSTREAM_OK;
}
+
/**
Add item to restore catalogue.
@@ -2192,11 +2265,8 @@ int bcat_add_item(st_bstream_image_heade
item->pos));
/*
- Do object-level privilege checking for all objects being restored.
- The user must have, at a minimum, the ability to create each object
- plus any specific privileges for the object type (e.g. functions,
- triggers).
-
+ Check privileges for all objects.
+
An error is returned if the user does not have the correct privileges
to create the object.
*/
@@ -2204,7 +2274,7 @@ int bcat_add_item(st_bstream_image_heade
int result= check_restore_privileges(item, info, name_str.c_ptr_safe());
if (result != BSTREAM_OK)
return result;
-
+
switch (item->type) {
case BSTREAM_IT_TABLESPACE:
=== modified file 'sql/backup/restore_info.h'
--- a/sql/backup/restore_info.h 2009-10-21 13:32:24 +0000
+++ b/sql/backup/restore_info.h 2009-11-02 21:57:32 +0000
@@ -45,6 +45,10 @@ public:
Image_info::Db* add_db(const ::String&, uint);
Image_info::Table* add_table(Image_info::Db&, const ::String&,
backup::Snapshot_info&, ulong);
+
+ THD *m_thd;
+ bool m_skip_precheck; // skip prechecking if privilege check ok
+ bool m_first_priv_check; // determines if this is the first db to be checked
private:
/*
@@ -57,7 +61,6 @@ private:
Restore_info(const Restore_info&);
Restore_info& operator=(const Restore_info&);
- THD *m_thd;
/// A flag to indicate that data has been modified during restore operation.
bool m_data_changed;
@@ -76,7 +79,8 @@ private:
inline
Restore_info::Restore_info(backup::Logger &log, THD *thd)
- :m_log(log), m_thd(thd), m_data_changed(FALSE)
+ :m_log(log), m_thd(thd), m_data_changed(FALSE),
+ m_skip_precheck(FALSE), m_first_priv_check(TRUE)
{}
@@ -111,23 +115,12 @@ Restore_info::add_ts(const ::String &nam
}
-/// Wrapper around Image_info method which reports errors and checks privileges.
+/// Wrapper around Image_info method which reports errors.
inline
backup::Image_info::Db*
Restore_info::add_db(const ::String &name, uint pos)
{
- /*
- Check privileges for this database. User must have RESTORE
- privilege in order to execute a restore.
- */
- DEBUG_SYNC(m_thd, "before_restore_privileges");
- if (check_access(m_thd, RESTORE_ACL, name.ptr(), 0, 1, 1, 0))
- {
- m_log.report_error(ER_RESTORE_ACCESS_DENIED_ERROR, name.ptr());
- return NULL;
- }
-
Db *db= Image_info::add_db(name, pos);
if (!db)
=== modified file 'sql/si_objects.cc'
--- a/sql/si_objects.cc 2009-10-29 21:09:25 +0000
+++ b/sql/si_objects.cc 2009-11-02 21:57:32 +0000
@@ -202,10 +202,7 @@ bool
run_service_interface_sql(THD *thd, Ed_connection *ed_connection,
const LEX_STRING *query, bool get_warnings)
{
- Si_session_context session_context;
- ulong saved_master_access; // Saved master access ACLs
- ulong saved_db_access; // Saved db access ACLs
-
+ Si_session_context session_context;
DBUG_ENTER("run_service_interface_sql");
DBUG_PRINT("run_service_interface_sql",
@@ -215,25 +212,6 @@ run_service_interface_sql(THD *thd, Ed_c
session_context.save_si_ctx(thd);
session_context.reset_si_ctx(thd);
- /*
- We only elevate for SELECT or SHOW CREATE queries.
- */
- my_bool elevate= strncmp(query->str, "SELECT", 6) == 0 ||
- strncmp(query->str, "(SELECT", 7) == 0 ||
- strncmp(query->str, "SHOW CREATE", 11) == 0 ? TRUE : FALSE;
-
- /*
- Temporarily give user SELECT privilege so operations on
- mysql and information_schema can succeed.
- */
- if (elevate)
- {
- saved_master_access= thd->security_ctx->master_access;
- saved_db_access= thd->security_ctx->db_access;
- thd->security_ctx->master_access |= (SELECT_ACL | SHOW_VIEW_ACL |
- TRIGGER_ACL | PROC_ACLS | EVENT_ACL);
- }
-
bool rc= ed_connection->execute_direct(*query);
if (get_warnings)
@@ -242,15 +220,6 @@ run_service_interface_sql(THD *thd, Ed_c
thd->warning_info->append_warnings(thd, ed_connection->get_warn_list());
}
- /*
- Remove elevated privilege.
- */
- if (elevate)
- {
- thd->security_ctx->master_access= saved_master_access;
- thd->security_ctx->db_access= saved_db_access;
- }
-
session_context.restore_si_ctx(thd);
DBUG_RETURN(rc);
=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc 2009-10-23 06:24:37 +0000
+++ b/sql/sql_class.cc 2009-11-02 21:57:32 +0000
@@ -2991,6 +2991,24 @@ void Security_context::skip_grants()
*priv_host= '\0';
}
+// Turns off grants but keeps user context.
+void Security_context::skip_grants_user()
+{
+ master_access= ~NO_ACCESS;
+}
+
+// Saves current privilege settings.
+void Security_context::save_grant_info()
+{
+ /* save the values for restoring later with reset_grant_info() */
+ m_saved_master_access= master_access;
+}
+
+// Turns grants back on.
+void Security_context::reset_grant_info()
+{
+ master_access= m_saved_master_access;
+}
bool Security_context::set_user(char *user_arg)
{
=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h 2009-10-23 06:24:37 +0000
+++ b/sql/sql_class.h 2009-11-02 21:57:32 +0000
@@ -852,6 +852,9 @@ public:
void init();
void destroy();
void skip_grants();
+ void skip_grants_user();
+ void save_grant_info();
+ void reset_grant_info();
inline char *priv_host_name()
{
return (*priv_host ? priv_host : (char *)"%");
@@ -871,6 +874,8 @@ public:
restore_security_context(THD *thd, Security_context *backup);
#endif
bool user_matches(Security_context *);
+private:
+ ulong m_saved_master_access;
};
Attachment: [text/bzr-bundle] bzr/charles.bell@sun.com-20091102215732-dg8apzer5pd12h5k.bundle