Below is the list of changes that have just been committed into a local
5.0 repository of msvensson. When msvensson does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet@stripped, 2007-02-14 14:44:34+01:00, msvensson@stripped +17 -0
Bug#18628 mysql-test-run: security problem(part1)
- Implement --secure-file-priv=<dir> option that limits
"load_file", "LOAD DATA" and "SELECT .. INTO OUTFILE" to work
with files in specified dir.
- Use above option for mysqld in mysql-test-run.pl
mysql-test/mysql-test-run.pl@stripped, 2007-02-14 14:44:30+01:00, msvensson@stripped +6 -0
Add usage of --secure-file-priv=vardir when starting mysqld
mysql-test/r/loaddata.result@stripped, 2007-02-14 14:44:30+01:00, msvensson@stripped +16 -0
Update test result after adding test to check that secure-file-priv
works for "load data" and "load_file"
mysql-test/r/outfile.result@stripped, 2007-02-14 14:44:30+01:00, msvensson@stripped +6 -1
Update result
mysql-test/r/query_cache.result@stripped, 2007-02-14 14:44:30+01:00, msvensson@stripped +1 -1
Can't load from outside of vardir anymore
mysql-test/r/type_blob.result@stripped, 2007-02-14 14:44:30+01:00, msvensson@stripped +12 -12
Can't load from outside of vardir anymore
mysql-test/t/loaddata.test@stripped, 2007-02-14 14:44:30+01:00, msvensson@stripped +23 -0
Update test result after adding test to check that secure-file-priv
works for "load data" and "load_file"
mysql-test/t/outfile.test@stripped, 2007-02-14 14:44:30+01:00, msvensson@stripped +12 -0
Update test result after adding test to check that secure-file-priv
works for "SELECT .. INTO OUTFILE"
mysql-test/t/query_cache.test@stripped, 2007-02-14 14:44:30+01:00, msvensson@stripped +2 -2
Can't load from outside of vardir anymore
mysql-test/t/type_blob.test@stripped, 2007-02-14 14:44:31+01:00, msvensson@stripped +13 -14
Can't load from outside of vardir anymore
sql/item_strfunc.cc@stripped, 2007-02-14 14:44:31+01:00, msvensson@stripped +5 -0
Check that the path "load_file" uses for the file is within
what's specified with --secure-file-priv
sql/mysql_priv.h@stripped, 2007-02-14 14:44:31+01:00, msvensson@stripped +1 -0
Add secure_file_priv
sql/mysqld.cc@stripped, 2007-02-14 14:44:31+01:00, msvensson@stripped +19 -1
Add "--secure_file_priv"
sql/set_var.cc@stripped, 2007-02-14 14:44:31+01:00, msvensson@stripped +4 -0
Add variable "secure_file_priv" to "show variables"
sql/share/errmsg.txt@stripped, 2007-02-14 14:44:32+01:00, msvensson@stripped +1 -1
Fix swedish error message for ER_OPTION_PREVENTS_STATMENT wich was hardcoded
to --skip-grant-tables
sql/sql_class.cc@stripped, 2007-02-14 14:44:31+01:00, msvensson@stripped +10 -2
Check that the path "load_file" uses for the file is within
what's specified with --secure-file-priv
sql/sql_class.h@stripped, 2007-02-14 14:44:31+01:00, msvensson@stripped +1 -1
Fix spelling error
sql/sql_load.cc@stripped, 2007-02-14 14:44:31+01:00, msvensson@stripped +9 -0
Check that the path "load_file" uses for the file is within
what's specified with --secure-file-priv
# This is a BitKeeper patch. What follows are the unified diffs for the
# set of deltas contained in the patch. The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User: msvensson
# Host: pilot.mysql.com
# Root: /home/msvensson/mysql/bug18628/my50-bug18628
--- 1.292/sql/item_strfunc.cc 2006-12-31 09:39:17 +01:00
+++ 1.293/sql/item_strfunc.cc 2007-02-14 14:44:31 +01:00
@@ -2767,6 +2767,11 @@
(void) fn_format(path, file_name->c_ptr(), mysql_real_data_home, "",
MY_RELATIVE_PATH | MY_UNPACK_FILENAME);
+ /* Read only allowed from within dir specified by secure_file_priv */
+ if (opt_secure_file_priv &&
+ strncmp(opt_secure_file_priv, path, strlen(opt_secure_file_priv)))
+ goto err;
+
if (!my_stat(path, &stat_info, MYF(0)))
goto err;
--- 1.429/sql/mysql_priv.h 2007-01-17 19:43:41 +01:00
+++ 1.430/sql/mysql_priv.h 2007-02-14 14:44:31 +01:00
@@ -1259,6 +1259,7 @@
extern my_bool opt_readonly, lower_case_file_system;
extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
extern my_bool opt_secure_auth;
+extern char* opt_secure_file_priv;
extern my_bool opt_log_slow_admin_statements;
extern my_bool sp_automatic_privileges, opt_noacl;
extern my_bool opt_old_style_user_limits, trust_function_creators;
--- 1.589/sql/mysqld.cc 2007-01-18 18:05:03 +01:00
+++ 1.590/sql/mysqld.cc 2007-02-14 14:44:31 +01:00
@@ -369,6 +369,7 @@
my_bool opt_readonly, use_temp_pool, relay_log_purge;
my_bool opt_sync_frm, opt_allow_suspicious_udfs;
my_bool opt_secure_auth= 0;
+char* opt_secure_file_priv= 0;
my_bool opt_log_slow_admin_statements= 0;
my_bool lower_case_file_system= 0;
my_bool opt_large_pages= 0;
@@ -1145,6 +1146,7 @@
#endif
x_free(opt_bin_logname);
x_free(opt_relay_logname);
+ x_free(opt_secure_file_priv);
bitmap_free(&temp_pool);
free_max_user_conn();
#ifdef HAVE_REPLICATION
@@ -4698,7 +4700,8 @@
OPT_TABLE_LOCK_WAIT_TIMEOUT,
OPT_PORT_OPEN_TIMEOUT,
OPT_MERGE,
- OPT_INNODB_ROLLBACK_ON_TIMEOUT
+ OPT_INNODB_ROLLBACK_ON_TIMEOUT,
+ OPT_SECURE_FILE_PRIV
};
@@ -5337,6 +5340,10 @@
{"secure-auth", OPT_SECURE_AUTH, "Disallow authentication for accounts that have old (pre-4.1) passwords.",
(gptr*) &opt_secure_auth, (gptr*) &opt_secure_auth, 0, GET_BOOL, NO_ARG,
my_bool(0), 0, 0, 0, 0, 0},
+ {"secure-file-priv", OPT_SECURE_FILE_PRIV,
+ "Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to files within specified directory",
+ (gptr*) &opt_secure_file_priv, (gptr*) &opt_secure_file_priv, 0,
+ GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"server-id", OPT_SERVER_ID,
"Uniquely identifies the server instance in the community of replication partners.",
(gptr*) &server_id, (gptr*) &server_id, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 0,
@@ -6366,6 +6373,7 @@
opt_logname= opt_update_logname= opt_binlog_index_name= opt_slow_logname= 0;
opt_tc_log_file= (char *)"tc.log"; // no hostname in tc_log file name !
opt_secure_auth= 0;
+ opt_secure_file_priv= 0;
opt_bootstrap= opt_myisam_log= 0;
mqh_used= 0;
segfaulted= kill_in_progress= 0;
@@ -7404,6 +7412,16 @@
exit(1);
}
#endif /* HAVE_REPLICATION */
+ /*
+ Convert the secure-file-priv option to system format, allowing
+ a quick strcmp to check if read or write is in an allowed dir
+ */
+ if (opt_secure_file_priv)
+ {
+ convert_dirname(buff, opt_secure_file_priv, NullS);
+ my_free(opt_secure_file_priv, MYF(0));
+ opt_secure_file_priv= my_strdup(buff, MYF(MY_FAE));
+ }
}
--- 1.256/sql/sql_class.cc 2007-01-11 19:52:55 +01:00
+++ 1.257/sql/sql_class.cc 2007-02-14 14:44:31 +01:00
@@ -1084,7 +1084,7 @@
IO_CACHE *cache)
{
File file;
- uint option= MY_UNPACK_FILENAME;
+ uint option= MY_UNPACK_FILENAME | MY_RELATIVE_PATH;
#ifdef DONT_ALLOW_FULL_LOAD_DATA_PATHS
option|= MY_REPLACE_DIR; // Force use of db directory
@@ -1097,7 +1097,15 @@
}
else
(void) fn_format(path, exchange->file_name, mysql_real_data_home, "", option);
-
+
+ if (opt_secure_file_priv &&
+ strncmp(opt_secure_file_priv, path, strlen(opt_secure_file_priv)))
+ {
+ /* Write only allowed to dir or subdir specified by secure_file_priv */
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv");
+ return -1;
+ }
+
if (!access(path, F_OK))
{
my_error(ER_FILE_EXISTS_ERROR, MYF(0), exchange->file_name);
--- 1.317/sql/sql_class.h 2007-01-18 18:05:04 +01:00
+++ 1.318/sql/sql_class.h 2007-02-14 14:44:31 +01:00
@@ -1674,7 +1674,7 @@
#define SYSTEM_THREAD_SLAVE_SQL 4
/*
- Used to hold information about file and file structure in exchainge
+ Used to hold information about file and file structure in exchange
via non-DB file (...INTO OUTFILE..., ...LOAD DATA...)
XXX: We never call destructor for objects of this class.
*/
--- 1.104/sql/sql_load.cc 2006-12-30 21:02:07 +01:00
+++ 1.105/sql/sql_load.cc 2007-02-14 14:44:31 +01:00
@@ -302,6 +302,15 @@
if ((stat_info.st_mode & S_IFIFO) == S_IFIFO)
is_fifo = 1;
#endif
+
+ if (opt_secure_file_priv &&
+ strncmp(opt_secure_file_priv, name, strlen(opt_secure_file_priv)))
+ {
+ /* Read only allowed from within dir specified by secure_file_priv */
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv");
+ DBUG_RETURN(TRUE);
+ }
+
}
if ((file=my_open(name,O_RDONLY,MYF(MY_WME))) < 0)
DBUG_RETURN(TRUE);
--- 1.75/sql/share/errmsg.txt 2006-11-13 17:06:41 +01:00
+++ 1.76/sql/share/errmsg.txt 2007-02-14 14:44:32 +01:00
@@ -5045,7 +5045,7 @@
ER_DUPLICATED_VALUE_IN_TYPE
eng "Column '%-.100s' has duplicated value '%-.64s' in %s"
ger "Feld '%-.100s' hat doppelten Wert '%-.64s' in %s"
Binary files /tmp/bk_outfile.result-1.6_1O33vX and /tmp/bk_outfile.result-1.7_aWd8fv differ
--- 1.191/mysql-test/mysql-test-run.pl 2007-01-22 08:59:23 +01:00
+++ 1.192/mysql-test/mysql-test-run.pl 2007-02-14 14:44:30 +01:00
@@ -3581,6 +3581,12 @@
mtr_add_arg($args, "%s--basedir=%s", $prefix, $path_my_basedir);
mtr_add_arg($args, "%s--character-sets-dir=%s", $prefix, $path_charsetsdir);
+ if ( $mysql_version_id >= 50036)
+ {
+ # Prevent the started mysqld to access files outside of vardir
+ mtr_add_arg($args, "%s--secure-file-priv=%s", $prefix, $opt_vardir);
+ }
+
if ( $mysql_version_id >= 50000 )
{
mtr_add_arg($args, "%s--log-bin-trust-function-creators", $prefix);
--- 1.53/mysql-test/r/type_blob.result 2006-10-04 13:09:34 +02:00
+++ 1.54/mysql-test/r/type_blob.result 2007-02-14 14:44:30 +01:00
@@ -506,26 +506,26 @@
Warnings:
Warning 1101 BLOB/TEXT column 'imagem' can't have a default value
insert into t1 (id) values (1);
-select
-charset(load_file('../../std_data/words.dat')),
-collation(load_file('../../std_data/words.dat')),
-coercibility(load_file('../../std_data/words.dat'));
-charset(load_file('../../std_data/words.dat')) collation(load_file('../../std_data/words.dat')) coercibility(load_file('../../std_data/words.dat'))
+select
+charset(load_file('../std_data_ln/words.dat')),
+collation(load_file('../std_data_ln/words.dat')),
+coercibility(load_file('../std_data_ln/words.dat'));
+charset(load_file('../std_data_ln/words.dat')) collation(load_file('../std_data_ln/words.dat')) coercibility(load_file('../std_data_ln/words.dat'))
binary binary 4
-explain extended select
-charset(load_file('../../std_data/words.dat')),
-collation(load_file('../../std_data/words.dat')),
-coercibility(load_file('../../std_data/words.dat'));
+explain extended select
+charset(load_file('MYSQLTEST_VARDIR/std_data_ln/words.dat')),
+collation(load_file('MYSQLTEST_VARDIR/std_data_ln/words.dat')),
+coercibility(load_file('MYSQLTEST_VARDIR/std_data_ln/words.dat'));
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
-Note 1003 select charset(load_file(_latin1'../../std_data/words.dat')) AS `charset(load_file('../../std_data/words.dat'))`,collation(load_file(_latin1'../../std_data/words.dat')) AS `collation(load_file('../../std_data/words.dat'))`,coercibility(load_file(_latin1'../../std_data/words.dat')) AS `coercibility(load_file('../../std_data/words.dat'))`
-update t1 set imagem=load_file('../../std_data/words.dat') where id=1;
+Note 1003 select charset(load_file(_latin1'MYSQLTEST_VARDIR/std_data_ln/words.dat')) AS `charset(load_file('MYSQLTEST_VARDIR/std_data_ln/words.dat'))`,collation(load_file(_latin1'MYSQLTEST_VARDIR/std_data_ln/words.dat')) AS `collation(load_file('MYSQLTEST_VARDIR/std_data_ln/words.dat'))`,coercibility(load_file(_latin1'MYSQLTEST_VARDIR/std_data_ln/words.dat')) AS `coercibility(load_file('MYSQLTEST_VARDIR/std_data_ln/words.dat'))`
+update t1 set imagem=load_file('MYSQLTEST_VARDIR/std_data_ln/words.dat') where id=1;
select if(imagem is null, "ERROR", "OK"),length(imagem) from t1 where id = 1;
if(imagem is null, "ERROR", "OK") length(imagem)
OK 581
drop table t1;
-create table t1 select load_file('../../std_data/words.dat') l;
+create table t1 select load_file('MYSQLTEST_VARDIR/std_data_ln/words.dat') l;
show full fields from t1;
Field Type Collation Null Key Default Extra Privileges Comment
l longblob NULL YES NULL #
--- 1.14/mysql-test/t/outfile.test 2006-05-12 09:15:11 +02:00
+++ 1.15/mysql-test/t/outfile.test 2007-02-14 14:44:30 +01:00
@@ -84,3 +84,15 @@
FROM schemata LIMIT 0, 5;
enable_query_log;
--exec rm $MYSQLTEST_VARDIR/tmp/outfile-test.4
+use test;
+
+#
+# Bug#18628 mysql-test-run: security problem
+#
+# It should not be possible to write to a file outside of vardir
+create table t1(a int);
+--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR
+--error 1290
+eval select * into outfile "$MYSQL_TEST_DIR/outfile-test1" from t1;
+drop table t1;
+
--- 1.33/mysql-test/t/type_blob.test 2006-10-03 16:14:19 +02:00
+++ 1.34/mysql-test/t/type_blob.test 2007-02-14 14:44:31 +01:00
@@ -307,22 +307,21 @@
create table t1 (id integer auto_increment unique,imagem LONGBLOB not null default '');
insert into t1 (id) values (1);
# We have to clean up the path in the results for safe comparison
---replace_result $MYSQL_TEST_DIR ../..
-eval select
- charset(load_file('$MYSQL_TEST_DIR/std_data/words.dat')),
- collation(load_file('$MYSQL_TEST_DIR/std_data/words.dat')),
- coercibility(load_file('$MYSQL_TEST_DIR/std_data/words.dat'));
---replace_result $MYSQL_TEST_DIR ../..
-eval explain extended select
- charset(load_file('$MYSQL_TEST_DIR/std_data/words.dat')),
- collation(load_file('$MYSQL_TEST_DIR/std_data/words.dat')),
- coercibility(load_file('$MYSQL_TEST_DIR/std_data/words.dat'));
---replace_result $MYSQL_TEST_DIR ../..
-eval update t1 set imagem=load_file('$MYSQL_TEST_DIR/std_data/words.dat') where id=1;
+eval select
+ charset(load_file('../std_data_ln/words.dat')),
+ collation(load_file('../std_data_ln/words.dat')),
+ coercibility(load_file('../std_data_ln/words.dat'));
+--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+eval explain extended select
+ charset(load_file('$MYSQLTEST_VARDIR/std_data_ln/words.dat')),
+ collation(load_file('$MYSQLTEST_VARDIR/std_data_ln/words.dat')),
+ coercibility(load_file('$MYSQLTEST_VARDIR/std_data_ln/words.dat'));
+--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+eval update t1 set imagem=load_file('$MYSQLTEST_VARDIR/std_data_ln/words.dat') where id=1;
select if(imagem is null, "ERROR", "OK"),length(imagem) from t1 where id = 1;
drop table t1;
---replace_result $MYSQL_TEST_DIR ../..
-eval create table t1 select load_file('$MYSQL_TEST_DIR/std_data/words.dat') l;
+--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+eval create table t1 select load_file('$MYSQLTEST_VARDIR/std_data_ln/words.dat') l;
# We mask out the Privileges column because it differs for embedded server
--replace_column 8 #
show full fields from t1;
--- 1.77/mysql-test/r/query_cache.result 2006-11-16 20:19:22 +01:00
+++ 1.78/mysql-test/r/query_cache.result 2007-02-14 14:44:30 +01:00
@@ -622,7 +622,7 @@
show status like "Qcache_queries_in_cache";
Variable_name Value
Qcache_queries_in_cache 1
-load data infile 'TEST_DIR/std_data/words.dat' into table t1;
+load data infile 'MYSQLTEST_VARDIR/std_data_ln/words.dat' into table t1;
show status like "Qcache_queries_in_cache";
Variable_name Value
Qcache_queries_in_cache 0
--- 1.56/mysql-test/t/query_cache.test 2006-11-16 20:19:23 +01:00
+++ 1.57/mysql-test/t/query_cache.test 2007-02-14 14:44:30 +01:00
@@ -405,8 +405,8 @@
create table t1 (word char(20) not null);
select * from t1;
show status like "Qcache_queries_in_cache";
---replace_result $MYSQL_TEST_DIR TEST_DIR
-eval load data infile '$MYSQL_TEST_DIR/std_data/words.dat' into table t1;
+--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+eval load data infile '$MYSQLTEST_VARDIR/std_data_ln/words.dat' into table t1;
show status like "Qcache_queries_in_cache";
select count(*) from t1;
drop table t1;
--- 1.176/sql/set_var.cc 2007-01-12 12:22:49 +01:00
+++ 1.177/sql/set_var.cc 2007-02-14 14:44:31 +01:00
@@ -358,6 +358,8 @@
&SV::query_cache_wlock_invalidate);
#endif /* HAVE_QUERY_CACHE */
sys_var_bool_ptr sys_secure_auth("secure_auth", &opt_secure_auth);
+sys_var_const_str_ptr sys_secure_file_priv("secure_file_priv",
+ &opt_secure_file_priv);
sys_var_long_ptr sys_server_id("server_id", &server_id, fix_server_id);
sys_var_bool_ptr sys_slave_compressed_protocol("slave_compressed_protocol",
&opt_slave_compressed_protocol);
@@ -719,6 +721,7 @@
&sys_rpl_recovery_rank,
&sys_safe_updates,
&sys_secure_auth,
+ &sys_secure_file_priv,
&sys_select_limit,
&sys_server_id,
#ifdef HAVE_REPLICATION
@@ -1027,6 +1030,7 @@
#endif
{sys_rpl_recovery_rank.name,(char*) &sys_rpl_recovery_rank, SHOW_SYS},
{"secure_auth", (char*) &sys_secure_auth, SHOW_SYS},
+ {"secure_file_priv", (char*) &sys_secure_file_priv, SHOW_SYS},
#ifdef HAVE_SMEM
{"shared_memory", (char*) &opt_enable_shared_memory, SHOW_MY_BOOL},
{"shared_memory_base_name", (char*) &shared_memory_base_name, SHOW_CHAR_PTR},
--- 1.24/mysql-test/r/loaddata.result 2006-07-20 10:41:05 +02:00
+++ 1.25/mysql-test/r/loaddata.result 2007-02-14 14:44:30 +01:00
@@ -139,4 +139,20 @@
a b c
10 NULL Ten
15 NULL Fifteen
+show variables like "secure_file_pri%";
+Variable_name Value
+secure_file_priv MYSQLTEST_VARDIR/
+select @@secure_file_priv;
+@@secure_file_priv
+MYSQLTEST_VARDIR/
+set @@secure_file_priv= 0;
+ERROR HY000: Variable 'secure_file_priv' is a read only variable
+truncate table t1;
+load data infile 'MYSQL_TEST_DIR/Makefile' into table t1;
+ERROR HY000: The MySQL server is running with the --secure-file-priv option so it cannot execute this statement
+select * from t1;
+a b c
+select load_file("MYSQL_TEST_DIR/Makefile");
+load_file("MYSQL_TEST_DIR/Makefile")
+NULL
drop table t1, t2;
--- 1.18/mysql-test/t/loaddata.test 2006-03-29 17:56:08 +02:00
+++ 1.19/mysql-test/t/loaddata.test 2007-02-14 14:44:30 +01:00
@@ -110,6 +110,29 @@
load data infile '../std_data_ln/rpl_loaddata.dat' into table t1 (@dummy,@n) set a= @n, c= (select str from t2 where num=@n);
select * from t1;
+#
+# Bug#18628 mysql-test-run: security problem
+#
+# It should not be possible to load from a file outside of vardir
+
+--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+show variables like "secure_file_pri%";
+--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+select @@secure_file_priv;
+--error 1238
+set @@secure_file_priv= 0;
+
+# Test "load data"
+truncate table t1;
+--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR
+--error 1290
+eval load data infile '$MYSQL_TEST_DIR/Makefile' into table t1;
+select * from t1;
+
+# Test "load_file" returns NULL
+--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR
+eval select load_file("$MYSQL_TEST_DIR/Makefile");
+
# cleanup
drop table t1, t2;
| Thread |
|---|
| • bk commit into 5.0 tree (msvensson:1.2395) BUG#18628 | msvensson | 14 Feb |