From ab6042590186ad14bbd73a64f5d1f3f77223bdb3 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 29 Feb 2008 13:55:00 +0400 Subject: [PATCH 1/2] Bug#32167 another privilege bypass with DATA/INDEX DIRECORY(ver 4.1,5.0) added new function test_if_data_home_dir() which checks that path does not contain mysql data home directory. Using of mysql data home directory in DATA DIRECTORY & INDEX DIRECTORY is disallowed. mysql-test/r/symlink.result: test result mysql-test/t/symlink.test: test case sql/mysql_priv.h: new variable mysql_unpacked_real_data_home sql/mysqld.cc: new variable mysql_unpacked_real_data_home sql/sql_parse.cc: added new function test_if_data_home_dir() which checks that path does not contain mysql data home directory. Using of mysql data home directory in DATA DIRECTORY & INDEX DIRECTORY is disallowed. --- mysql-test/r/symlink.result | 38 ++++++++++++----------- mysql-test/t/symlink.test | 52 ++++++++++++++++++------------- sql/mysql_priv.h | 1 + sql/mysqld.cc | 4 +++ sql/sql_parse.cc | 61 ++++++++++++++++++++++++++++++++++++- 5 files changed, 117 insertions(+), 39 deletions(-) diff --git a/mysql-test/r/symlink.result b/mysql-test/r/symlink.result index 32dc72b8219..84d7d8f4260 100644 --- a/mysql-test/r/symlink.result +++ b/mysql-test/r/symlink.result @@ -91,23 +91,15 @@ t1 CREATE TABLE `t1` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; CREATE TABLE t1(a INT) -DATA DIRECTORY='TEST_DIR/master-data/mysql' -INDEX DIRECTORY='TEST_DIR/master-data/mysql'; -RENAME TABLE t1 TO user; -ERROR HY000: Can't create/write to file 'TEST_DIR/master-data/mysql/user.MYI' (Errcode: 17) -DROP TABLE t1; -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `i` int(11) default NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -drop table t1; -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `i` int(11) default NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -drop table t1; +DATA DIRECTORY='TEST_DIR/tmp' +INDEX DIRECTORY='TEST_DIR/tmp'; +ERROR HY000: Can't create/write to file 'TEST_DIR/tmp/t1.MYI' (Errcode: 17) +CREATE TABLE t2(a INT) +DATA DIRECTORY='TEST_DIR/tmp' +INDEX DIRECTORY='TEST_DIR/tmp'; +RENAME TABLE t2 TO t1; +ERROR HY000: Can't create/write to file 'TEST_DIR/tmp/t1.MYI' (Errcode: 17) +DROP TABLE t2; show create table t1; Table Create Table t1 CREATE TEMPORARY TABLE `t1` ( @@ -144,4 +136,16 @@ t1 CREATE TABLE `t1` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQLTEST_VARDIR/tmp/' drop table t1; deallocate prepare stmt; +CREATE TABLE t1(a INT) +DATA DIRECTORY='TEST_DIR/var/master-data/test'; +ERROR HY000: Incorrect arguments to DATA DIRECORY +CREATE TABLE t1(a INT) +DATA DIRECTORY='/home/gluh/MySQL/Merge/4.1-opt/mysql-test/var/master-data/'; +ERROR HY000: Incorrect arguments to DATA DIRECORY +CREATE TABLE t1(a INT) +INDEX DIRECTORY='/home/gluh/MySQL/Merge/4.1-opt/mysql-test/var/master-data'; +ERROR HY000: Incorrect arguments to INDEX DIRECORY +CREATE TABLE t1(a INT) +INDEX DIRECTORY='/home/gluh/MySQL/Merge/4.1-opt/mysql-test/var/master-data_var'; +ERROR HY000: Can't create/write to file '/home/gluh/MySQL/Merge/4.1-opt/mysql-test/var/master-data_var/t1.MYI' (Errcode: 2) End of 4.1 tests diff --git a/mysql-test/t/symlink.test b/mysql-test/t/symlink.test index 40127a697ac..7dfccaad8dc 100644 --- a/mysql-test/t/symlink.test +++ b/mysql-test/t/symlink.test @@ -121,29 +121,22 @@ drop table t1; # # BUG#32111 - Security Breach via DATA/INDEX DIRECORY and RENAME TABLE # ---replace_result $MYSQLTEST_VARDIR TEST_DIR -eval CREATE TABLE t1(a INT) -DATA DIRECTORY='$MYSQLTEST_VARDIR/master-data/mysql' -INDEX DIRECTORY='$MYSQLTEST_VARDIR/master-data/mysql'; +--write_file $MYSQLTEST_VARDIR/tmp/t1.MYI +EOF --replace_result $MYSQLTEST_VARDIR TEST_DIR --error 1 -RENAME TABLE t1 TO user; -DROP TABLE t1; - -# -# Test specifying DATA DIRECTORY that is the same as what would normally -# have been chosen. (Bug #8707) -# -disable_query_log; -eval create table t1 (i int) data directory = "$MYSQL_TEST_DIR/var/master-data/test/"; -enable_query_log; -show create table t1; -drop table t1; -disable_query_log; -eval create table t1 (i int) index directory = "$MYSQL_TEST_DIR/var/master-data/test/"; -enable_query_log; -show create table t1; -drop table t1; +eval CREATE TABLE t1(a INT) +DATA DIRECTORY='$MYSQLTEST_VARDIR/tmp' +INDEX DIRECTORY='$MYSQLTEST_VARDIR/tmp'; +--replace_result $MYSQLTEST_VARDIR TEST_DIR +eval CREATE TABLE t2(a INT) +DATA DIRECTORY='$MYSQLTEST_VARDIR/tmp' +INDEX DIRECTORY='$MYSQLTEST_VARDIR/tmp'; +--replace_result $MYSQLTEST_VARDIR TEST_DIR +--error 1 +RENAME TABLE t2 TO t1; +DROP TABLE t2; +--remove_file $MYSQLTEST_VARDIR/tmp/t1.MYI # # Bug#8706 - temporary table with data directory option fails @@ -201,4 +194,21 @@ show create table t1; drop table t1; deallocate prepare stmt; +# +# Bug#32167 another privilege bypass with DATA/INDEX DIRECORY +# +--replace_result $MYSQL_TEST_DIR TEST_DIR +--error 1210 +eval CREATE TABLE t1(a INT) +DATA DIRECTORY='$MYSQL_TEST_DIR/var/master-data/test'; +--error 1210 +eval CREATE TABLE t1(a INT) +DATA DIRECTORY='$MYSQL_TEST_DIR/var/master-data/'; +--error 1210 +eval CREATE TABLE t1(a INT) +INDEX DIRECTORY='$MYSQL_TEST_DIR/var/master-data'; +--error 1 +eval CREATE TABLE t1(a INT) +INDEX DIRECTORY='$MYSQL_TEST_DIR/var/master-data_var'; + --echo End of 4.1 tests diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b6170ba35f9..4b0031d1f2a 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -890,6 +890,7 @@ void my_dbopt_free(void); extern time_t start_time; extern char *mysql_data_home,server_version[SERVER_VERSION_LENGTH], mysql_real_data_home[], *opt_mysql_tmpdir, mysql_charsets_dir[], + mysql_unpacked_real_data_home[], def_ft_boolean_syntax[sizeof(ft_boolean_syntax)]; #define mysql_tmpdir (my_tmpdir(&mysql_tmpdir_list)) extern MY_TMPDIR mysql_tmpdir_list; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 62105e0093a..8111df7ad4d 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -390,6 +390,7 @@ const char *opt_date_time_formats[3]; char compiled_default_collation_name[]= MYSQL_DEFAULT_COLLATION_NAME; char *language_ptr, *default_collation_name, *default_character_set_name; char mysql_data_home_buff[2], *mysql_data_home=mysql_real_data_home; +char mysql_unpacked_real_data_home[FN_REFLEN]; struct passwd *user_info; char server_version[SERVER_VERSION_LENGTH]; char *mysqld_unix_port, *opt_mysql_tmpdir; @@ -6896,6 +6897,9 @@ static void fix_paths(void) pos[1]= 0; } convert_dirname(mysql_real_data_home,mysql_real_data_home,NullS); + (void) fn_format(buff, mysql_real_data_home, "", "", + (MY_RETURN_REAL_PATH|MY_RESOLVE_SYMLINKS)); + (void) unpack_dirname(mysql_unpacked_real_data_home, buff); convert_dirname(language,language,NullS); (void) my_load_path(mysql_home,mysql_home,""); // Resolve current dir (void) my_load_path(mysql_real_data_home,mysql_real_data_home,mysql_home); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 24f9ef30569..4bbd425d80b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -65,7 +65,8 @@ static bool append_file_to_dir(THD *thd, const char **filename_ptr, const char *table_name); static TABLE_LIST* get_table_by_alias(TABLE_LIST* tl, const char* db, - const char* alias); + const char* alias); +static bool test_if_data_home_dir(const char *dir); const char *any_db="*any*"; // Special symbol for check_access @@ -2531,6 +2532,20 @@ mysql_execute_command(THD *thd) "INDEX DIRECTORY option ignored"); create_info.data_file_name= create_info.index_file_name= NULL; #else + + if (test_if_data_home_dir(lex->create_info.data_file_name)) + { + my_error(ER_WRONG_ARGUMENTS,MYF(0),"DATA DIRECORY"); + res= -1; + break; + } + if (test_if_data_home_dir(lex->create_info.index_file_name)) + { + my_error(ER_WRONG_ARGUMENTS,MYF(0),"INDEX DIRECORY"); + res= -1; + break; + } + /* Fix names if symlinked tables */ if (append_file_to_dir(thd, &create_info.data_file_name, create_table->real_name) || @@ -5920,3 +5935,47 @@ Item *negate_expression(THD *thd, Item *expr) return negated; return new Item_func_not(expr); } + + +/* + Check if path does not contain mysql data home directory + + SYNOPSIS + test_if_data_home_dir() + dir directory + conv_home_dir converted data home directory + home_dir_len converted data home directory length + + RETURN VALUES + 0 ok + 1 error +*/ + +static bool test_if_data_home_dir(const char *dir) +{ + char path[FN_REFLEN], conv_path[FN_REFLEN]; + uint dir_len, home_dir_len= strlen(mysql_unpacked_real_data_home); + DBUG_ENTER("test_if_data_home_dir"); + + if (!dir) + DBUG_RETURN(0); + + (void) fn_format(path, dir, "", "", + (MY_RETURN_REAL_PATH|MY_RESOLVE_SYMLINKS)); + dir_len= unpack_dirname(conv_path, dir); + + if (home_dir_len <= dir_len) + { + if (lower_case_file_system) + { + if (!my_strnncoll(default_charset_info, (const uchar*) conv_path, + home_dir_len, + (const uchar*) mysql_unpacked_real_data_home, + home_dir_len)) + DBUG_RETURN(1); + } + else if (!memcmp(conv_path, mysql_unpacked_real_data_home, home_dir_len)) + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} From fa9e35b48d1322b80cf3a8f6ffd4b2fde7f46c0c Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 29 Feb 2008 15:04:00 +0400 Subject: [PATCH 2/2] after merge fix --- mysql-test/r/symlink.result | 47 ++++++++++++++++++------------------- mysql-test/t/symlink.test | 38 +++++++----------------------- sql/mysqld.cc | 10 ++------ 3 files changed, 33 insertions(+), 62 deletions(-) diff --git a/mysql-test/r/symlink.result b/mysql-test/r/symlink.result index b39ef971539..f014a794dc7 100644 --- a/mysql-test/r/symlink.result +++ b/mysql-test/r/symlink.result @@ -130,39 +130,38 @@ select * from t1; a 42 drop table t1; +execute stmt; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c` char(10) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQLTEST_VARDIR/tmp/' +drop table t1; +execute stmt; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c` char(10) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQLTEST_VARDIR/tmp/' +drop table t1; +deallocate prepare stmt; CREATE TABLE t1(a INT) DATA DIRECTORY='TEST_DIR/var/master-data/test'; ERROR HY000: Incorrect arguments to DATA DIRECORY CREATE TABLE t1(a INT) -DATA DIRECTORY='/home/gluh/MySQL/Merge/4.1-opt/mysql-test/var/master-data/'; +DATA DIRECTORY='/home/gluh/MySQL/Merge/5.0-opt/mysql-test/var/master-data/'; ERROR HY000: Incorrect arguments to DATA DIRECORY CREATE TABLE t1(a INT) -INDEX DIRECTORY='/home/gluh/MySQL/Merge/4.1-opt/mysql-test/var/master-data'; +INDEX DIRECTORY='/home/gluh/MySQL/Merge/5.0-opt/mysql-test/var/master-data'; ERROR HY000: Incorrect arguments to INDEX DIRECORY CREATE TABLE t1(a INT) -INDEX DIRECTORY='/home/gluh/MySQL/Merge/4.1-opt/mysql-test/var/master-data_var'; -ERROR HY000: Can't create/write to file '/home/gluh/MySQL/Merge/4.1-opt/mysql-test/var/master-data_var/t1.MYI' (Errcode: 2) +INDEX DIRECTORY='/home/gluh/MySQL/Merge/5.0-opt/mysql-test/var/master-data_var'; +ERROR HY000: Can't create/write to file '/home/gluh/MySQL/Merge/5.0-opt/mysql-test/var/master-data_var/t1.MYI' (Errcode: 2) End of 4.1 tests -CREATE DATABASE db1; -CREATE DATABASE db2; -USE db2; -INSERT INTO db2.t1 VALUES (1); -SELECT * FROM db2.t1; -b -1 -RESET QUERY CACHE; -USE db1; SET SESSION keep_files_on_create = TRUE; CREATE TABLE t1 (a INT) ENGINE MYISAM; -ERROR HY000: Can't create/write to file './db1/t1.MYD' (Errcode: 17) -CREATE TABLE t3 (a INT) Engine=MyISAM; -INSERT INTO t3 VALUES (1),(2),(3); -TRUNCATE TABLE t3; -SELECT * from t3; -a -SET SESSION keep_files_on_create = DEFAULT; -DROP TABLE db2.t1, db1.t3; -DROP DATABASE db1; -DROP DATABASE db2; -USE test; +ERROR HY000: Can't create/write to file './test/t1.MYD' (Errcode: 17) +SET SESSION keep_files_on_create = FALSE; +CREATE TABLE t1 (a INT) ENGINE MYISAM; +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/symlink.test b/mysql-test/t/symlink.test index f4a10bc58d8..ffe5cc76737 100644 --- a/mysql-test/t/symlink.test +++ b/mysql-test/t/symlink.test @@ -222,39 +222,17 @@ INDEX DIRECTORY='$MYSQL_TEST_DIR/var/master-data_var'; # # Bug #29325: create table overwrites .MYD file of other table (datadir) # - -CREATE DATABASE db1; -CREATE DATABASE db2; - -USE db2; ---disable_query_log -eval CREATE TABLE t1 (b INT) ENGINE MYISAM -DATA DIRECTORY = '$MYSQLTEST_VARDIR/master-data/db1/'; ---enable_query_log - -INSERT INTO db2.t1 VALUES (1); -SELECT * FROM db2.t1; -RESET QUERY CACHE; - -USE db1; - -#no warning from create table SET SESSION keep_files_on_create = TRUE; +--write_file $MYSQLTEST_VARDIR/master-data/test/t1.MYD +EOF --disable_abort_on_error +--error 1 CREATE TABLE t1 (a INT) ENGINE MYISAM; +--error 0,1 +--remove_file $MYSQLTEST_VARDIR/master-data/test/t1.MYD; --enable_abort_on_error - -CREATE TABLE t3 (a INT) Engine=MyISAM; -INSERT INTO t3 VALUES (1),(2),(3); -TRUNCATE TABLE t3; -SELECT * from t3; - -SET SESSION keep_files_on_create = DEFAULT; - -DROP TABLE db2.t1, db1.t3; -DROP DATABASE db1; -DROP DATABASE db2; -USE test; - +SET SESSION keep_files_on_create = FALSE; +CREATE TABLE t1 (a INT) ENGINE MYISAM; +DROP TABLE t1; --echo End of 5.0 tests diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c03c9c34571..4d688d795f7 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -467,19 +467,13 @@ char log_error_file[FN_REFLEN], glob_hostname[FN_REFLEN]; char mysql_real_data_home[FN_REFLEN], language[FN_REFLEN], reg_ext[FN_EXTLEN], mysql_charsets_dir[FN_REFLEN], *opt_init_file, *opt_tc_log_file, + mysql_unpacked_real_data_home[FN_REFLEN], def_ft_boolean_syntax[sizeof(ft_boolean_syntax)]; - +char *mysql_data_home= mysql_real_data_home; const key_map key_map_empty(0); key_map key_map_full(0); // Will be initialized later const char *opt_date_time_formats[3]; - - -char compiled_default_collation_name[]= MYSQL_DEFAULT_COLLATION_NAME; -char *language_ptr, *default_collation_name, *default_character_set_name; -char mysql_data_home_buff[2], *mysql_data_home=mysql_real_data_home; -char mysql_unpacked_real_data_home[FN_REFLEN]; -struct passwd *user_info; char server_version[SERVER_VERSION_LENGTH]; char *mysqld_unix_port, *opt_mysql_tmpdir; const char **errmesg; /* Error messages */