Skip site navigation (1)Skip section navigation (2)
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>