Date: Mon, 29 Sep 2008 17:28:17 -0500 (CDT) From: Andrew Daugherity <adaugherity@tamu.edu> To: FreeBSD-gnats-submit@FreeBSD.org Subject: ports/127731: [patch] databases/mysql41-server fix for CVE-2008-2079 Message-ID: <200809292228.m8TMSHAo053521@riemann.isc.tamu.edu> Resent-Message-ID: <200809292250.m8TMo1ma002535@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 127731 >Category: ports >Synopsis: [patch] databases/mysql41-server fix for CVE-2008-2079 >Confidential: no >Severity: serious >Priority: medium >Responsible: freebsd-ports-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Mon Sep 29 22:50:01 UTC 2008 >Closed-Date: >Last-Modified: >Originator: Andrew Daugherity >Release: FreeBSD 6.3-RELEASE-p4 i386 >Organization: Texas A&M University >Environment: System: FreeBSD riemann.isc.tamu.edu 6.3-RELEASE-p4 FreeBSD 6.3-RELEASE-p4 #0: Wed Sep 3 00:08:54 UTC 2008 root@i386-builder.daemonology.net:/usr/obj/usr/src/sys/SMP i386 >Description: Fix for mysql41-server for the vulnerability referred to in http://www.freebsd.org/ports/portaudit/388d9ee4-7f22-11dd-a66a-0019666436c2.html >How-To-Repeat: >Fix: Patch attached. Because MySQL 4.1 is in the "extended support" phase of the lifecycle, there are no tarballs newer than 4.1.22 available, but security patches are still issued, and code is available from the bzr repo. Rather than upgrading to 4.1.24 (the latest tagged version), which would require a build-dep on bzr, or self-hosting a bzr export tarball, I generated a bzr diff for this specific fix. This is the commit log from mysql bzr: ------------------------------------------------------------ revno: 2704.3.2 committer: gluh@mysql.com/eagle.(none) timestamp: Fri 2008-02-29 13:55:00 +0400 message: 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. ------------------------------------------------------------ The attached patch is the output of 'bzr diff -r 2704.3.1..2704.3.2' against a checkout of mysql-4.1 branch, split into multiple files as per FreeBSD ports policy, with the exception of the two files under mysql-test/, which had intervening changes. The diff for those two files came from 'bzr diff -r tag:mysql-4.1.22..tag:mysql-4.1.24 mysql-test/r/symlink.result' and likewise for mysql-test/t/symlink.test. All patches are new files, except for patch-sql::mysqld.cc, which has hunks added to it. Of course, the vulnerabilities database also needs to be updated once this patch is committed. --- mysql41-server.patch begins here --- diff -ruN mysql41-server.orig/Makefile mysql41-server/Makefile --- mysql41-server.orig/Makefile 2008-08-21 01:16:29.000000000 -0500 +++ mysql41-server/Makefile 2008-09-29 15:41:42.000000000 -0500 @@ -7,7 +7,7 @@ PORTNAME?= mysql PORTVERSION= 4.1.22 -PORTREVISION?= 0 +PORTREVISION?= 1 CATEGORIES= databases MASTER_SITES= ${MASTER_SITE_MYSQL} MASTER_SITE_SUBDIR= MySQL-4.1 diff -ruN mysql41-server.orig/files/patch-mysql-test::r::symlink.result mysql41-server/files/patch-mysql-test::r::symlink.result --- mysql41-server.orig/files/patch-mysql-test::r::symlink.result 1969-12-31 18:00:00.000000000 -0600 +++ mysql41-server/files/patch-mysql-test::r::symlink.result 2008-09-29 16:54:09.000000000 -0500 @@ -0,0 +1,78 @@ +--- mysql-test/r/symlink.result 2006-06-27 17:22:43 +0000 ++++ mysql-test/r/symlink.result 2008-02-29 12:56:41 +0000 +@@ -90,15 +90,62 @@ + `b` 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; +-show create table t1; +-Table Create Table +-t1 CREATE TABLE `t1` ( +- `i` int(11) default NULL +-) ENGINE=MyISAM DEFAULT CHARSET=latin1 +-drop table t1; ++CREATE TABLE t1(a INT) ++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` ( ++ `a` int(11) default NULL ++) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQL_TEST_DIR/var/log/' ++show create table t1; ++Table Create Table ++t1 CREATE TEMPORARY TABLE `t1` ( ++ `a` int(11) default NULL ++) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQL_TEST_DIR/var/log/' ++create table t1 (a int) engine=myisam select 42 a; ++select * from t1; ++a ++9 ++select * from t1; ++a ++99 ++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='TEST_DIR/var/master-data/'; ++ERROR HY000: Incorrect arguments to DATA DIRECORY ++CREATE TABLE t1(a INT) ++INDEX DIRECTORY='TEST_DIR/var/master-data'; ++ERROR HY000: Incorrect arguments to INDEX DIRECORY ++CREATE TABLE t1(a INT) ++INDEX DIRECTORY='TEST_DIR/var/master-data_var'; ++ERROR HY000: Can't create/write to file 'TEST_DIR/var/master-data_var/t1.MYI' (Errcode: 2) ++End of 4.1 tests + diff -ruN mysql41-server.orig/files/patch-mysql-test::t::symlink.test mysql41-server/files/patch-mysql-test::t::symlink.test --- mysql41-server.orig/files/patch-mysql-test::t::symlink.test 1969-12-31 18:00:00.000000000 -0600 +++ mysql41-server/files/patch-mysql-test::t::symlink.test 2008-09-29 16:54:19.000000000 -0500 @@ -0,0 +1,118 @@ +--- mysql-test/t/symlink.test 2005-12-08 15:13:53 +0000 ++++ mysql-test/t/symlink.test 2008-02-29 12:56:41 +0000 +@@ -119,18 +119,99 @@ + 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; +- +-# End of 4.1 tests ++# BUG#32111 - Security Breach via DATA/INDEX DIRECORY and RENAME TABLE ++# ++--write_file $MYSQLTEST_VARDIR/tmp/t1.MYI ++EOF ++--replace_result $MYSQLTEST_VARDIR TEST_DIR ++--error 1 ++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 ++# ++connect (session1,localhost,root,,); ++connect (session2,localhost,root,,); ++ ++connection session1; ++disable_query_log; ++eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/log" select 9 a; ++enable_query_log; ++# If running test suite with a non standard tmp dir, the "show create table" ++# will print "DATA_DIRECTORY=". Use replace_result to mask it out ++--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR ++show create table t1; ++ ++connection session2; ++disable_query_log; ++eval create temporary table t1 (a int) engine=myisam data directory="$MYSQL_TEST_DIR/var/log" select 99 a; ++enable_query_log; ++# If running test suite with a non standard tmp dir, the "show create table" ++# will print "DATA_DIRECTORY=". Use replace_result to mask it out ++--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR ++show create table t1; ++ ++connection default; ++create table t1 (a int) engine=myisam select 42 a; ++ ++connection session1; ++select * from t1; ++disconnect session1; ++connection session2; ++select * from t1; ++disconnect session2; ++connection default; ++select * from t1; ++drop table t1; ++ ++# ++# CREATE TABLE with DATA DIRECTORY option ++# ++# Protect ourselves from data left in tmp/ by a previos possibly failed ++# test ++--system rm -f $MYSQLTEST_VARDIR/tmp/t1.* ++--disable_query_log ++eval prepare stmt from "create table t1 (c char(10)) data directory='$MYSQLTEST_VARDIR/tmp'"; ++--enable_query_log ++execute stmt; ++--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR ++show create table t1; ++drop table t1; ++execute stmt; ++--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR ++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'; ++--replace_result $MYSQL_TEST_DIR TEST_DIR ++--error 1210 ++eval CREATE TABLE t1(a INT) ++DATA DIRECTORY='$MYSQL_TEST_DIR/var/master-data/'; ++--replace_result $MYSQL_TEST_DIR TEST_DIR ++--error 1210 ++eval CREATE TABLE t1(a INT) ++INDEX DIRECTORY='$MYSQL_TEST_DIR/var/master-data'; ++--replace_result $MYSQL_TEST_DIR TEST_DIR ++--error 1 ++eval CREATE TABLE t1(a INT) ++INDEX DIRECTORY='$MYSQL_TEST_DIR/var/master-data_var'; ++ ++--echo End of 4.1 tests + diff -ruN mysql41-server.orig/files/patch-sql::mysql_priv.h mysql41-server/files/patch-sql::mysql_priv.h --- mysql41-server.orig/files/patch-sql::mysql_priv.h 1969-12-31 18:00:00.000000000 -0600 +++ mysql41-server/files/patch-sql::mysql_priv.h 2008-09-29 15:50:23.000000000 -0500 @@ -0,0 +1,11 @@ +--- sql/mysql_priv.h 2007-11-09 12:05:01 +0000 ++++ sql/mysql_priv.h 2008-02-29 09:55:00 +0000 +@@ -890,6 +890,7 @@ + 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 -ruN mysql41-server.orig/files/patch-sql::mysqld.cc mysql41-server/files/patch-sql::mysqld.cc --- mysql41-server.orig/files/patch-sql::mysqld.cc 2005-01-16 04:24:46.000000000 -0600 +++ mysql41-server/files/patch-sql::mysqld.cc 2008-09-29 16:21:27.000000000 -0500 @@ -9,3 +9,24 @@ #include <syslog.h> #ifdef NEED_SYS_SYSLOG_H #include <sys/syslog.h> +--- sql/mysqld.cc 2007-11-29 10:52:36 +0000 ++++ sql/mysqld.cc 2008-02-29 09:55:00 +0000 +@@ -390,6 +390,7 @@ + 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 @@ + 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 -ruN mysql41-server.orig/files/patch-sql::sql_parse.cc mysql41-server/files/patch-sql::sql_parse.cc --- mysql41-server.orig/files/patch-sql::sql_parse.cc 1969-12-31 18:00:00.000000000 -0600 +++ mysql41-server/files/patch-sql::sql_parse.cc 2008-09-29 15:49:44.000000000 -0500 @@ -0,0 +1,82 @@ +--- sql/sql_parse.cc 2007-06-12 12:47:36 +0000 ++++ sql/sql_parse.cc 2008-02-29 09:55:00 +0000 +@@ -65,7 +65,8 @@ + 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 @@ + "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 @@ + 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); ++} + --- mysql41-server.patch ends here --- >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200809292228.m8TMSHAo053521>