diff options
Diffstat (limited to 'mysql-cve-2016-6662-c-2135853b.patch')
-rw-r--r-- | mysql-cve-2016-6662-c-2135853b.patch | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/mysql-cve-2016-6662-c-2135853b.patch b/mysql-cve-2016-6662-c-2135853b.patch new file mode 100644 index 0000000..3564e13 --- /dev/null +++ b/mysql-cve-2016-6662-c-2135853b.patch @@ -0,0 +1,186 @@ +From 2135853b41f2d124f1869371154b0f9ab9a7b2da Mon Sep 17 00:00:00 2001 +From: Honza Horak <hhorak@redhat.com> +Date: Fri, 9 Dec 2016 17:48:50 +0100 +Subject: [PATCH 3/4] Bug#24388753: PRIVILEGE ESCALATION USING MYSQLD_SAFE + + MySQL 5.1 upstream backport of: + https://github.com/mysql/mysql-server/commit/48bd8b16fe382be302c6f0b45931be5aa6f29a0e + + The problem was that it was possible to write log files ending + in .ini/.cnf that later could be parsed as an options file. + This made it possible for users to specify startup options + without the permissions to do so. + + This patch fixes the problem by disallowing general query log + and slow query log to be written to files ending in .ini and .cnf. +--- + sql/log.cc | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- + sql/log.h | 10 ++++++++ + sql/mysqld.cc | 16 ++++++++++++ + sql/set_var.cc | 5 ++++ + 4 files changed, 110 insertions(+), 2 deletions(-) + +diff --git a/sql/log.cc b/sql/log.cc +index 60692b7..d53b487 100644 +--- a/sql/log.cc ++++ b/sql/log.cc +@@ -1933,6 +1933,73 @@ bool MYSQL_LOG::init_and_set_log_file_name(const char *log_name, + } + + ++bool is_valid_log_name(const char *name, size_t len) ++{ ++ if (len > 3) ++ { ++ const char *tail= name + len - 4; ++ if (my_strcasecmp(system_charset_info, tail, ".ini") == 0 || ++ my_strcasecmp(system_charset_info, tail, ".cnf") == 0) ++ { ++ return false; ++ } ++ } ++ return true; ++} ++ ++ ++/** ++ Get the real log file name, and possibly reopen file. ++ ++ Use realpath() to get the path with symbolic links ++ expanded. Then, close the file, and reopen the real path using the ++ O_NOFOLLOW flag. This will reject following symbolic links. ++ ++ @param file File descriptor. ++ @param open_flags Flags to use for opening the file. ++ @param opened_file_name Name of the open fd. ++ ++ @retval file descriptor to open file with 'real_file_name', or '-1' ++ in case of errors. ++*/ ++ ++#ifndef _WIN32 ++static File mysql_file_real_name_reopen(File file, ++ int open_flags, ++ const char *opened_file_name) ++{ ++ DBUG_ASSERT(file); ++ DBUG_ASSERT(opened_file_name); ++ ++ /* Buffer for realpath must have capacity for PATH_MAX. */ ++ char real_file_name[PATH_MAX]; ++ ++ /* Get realpath, validate, open realpath with O_NOFOLLOW. */ ++ if (realpath(opened_file_name, real_file_name) == NULL) ++ { ++ (void) my_close(file, MYF(0)); ++ return -1; ++ } ++ ++ if (my_close(file, MYF(0))) ++ return -1; ++ ++ if (strlen(real_file_name) > FN_REFLEN) ++ return -1; ++ ++ if (!is_valid_log_name(real_file_name, strlen(real_file_name))) ++ { ++ sql_print_error("Invalid log file name after expanding symlinks: '%s'", ++ real_file_name); ++ return -1; ++ } ++ ++ return my_open(real_file_name, ++ open_flags | O_NOFOLLOW, ++ MYF(MY_WME | ME_WAITTANG)); ++} ++#endif // _WIN32 ++ + /* + Open a (new) log file. + +@@ -1983,8 +2050,18 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, + db[0]= 0; + + if ((file= my_open(log_file_name, open_flags, +- MYF(MY_WME | ME_WAITTANG))) < 0 || +- init_io_cache(&log_file, file, IO_SIZE, io_cache_type, ++ MYF(MY_WME | ME_WAITTANG))) < 0) ++ goto err; ++ ++#ifndef _WIN32 ++ /* Reopen and validate path. */ ++ if ((log_type_arg == LOG_UNKNOWN || log_type_arg == LOG_NORMAL) && ++ (file= mysql_file_real_name_reopen(file, ++ open_flags, ++ log_file_name)) < 0) ++ goto err; ++#endif // _WIN32 ++ if (init_io_cache(&log_file, file, IO_SIZE, io_cache_type, + my_tell(file, MYF(MY_WME)), 0, + MYF(MY_WME | MY_NABP | + ((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0)))) +diff --git a/sql/log.h b/sql/log.h +index 02721f1..5354447 100644 +--- a/sql/log.h ++++ b/sql/log.h +@@ -605,4 +605,14 @@ extern TYPELIB binlog_format_typelib; + + int query_error_code(THD *thd, bool not_killed); + ++/** ++ Check given log name against certain blacklisted names/extensions. ++ ++ @param name Log name to check ++ @param len Length of log name ++ ++ @returns true if name is valid, false otherwise. ++*/ ++bool is_valid_log_name(const char *name, size_t len); ++ + #endif /* LOG_H */ +diff --git a/sql/mysqld.cc b/sql/mysqld.cc +index 41cef57..aa1dab4 100644 +--- a/sql/mysqld.cc ++++ b/sql/mysqld.cc +@@ -3458,6 +3458,22 @@ static int init_common_variables(const char *conf_file_name, int argc, + "--log_slow_queries option, log tables are used. " + "To enable logging to files use the --log-output=file option."); + ++ if (opt_logname && ++ !is_valid_log_name(opt_logname, strlen(opt_logname))) ++ { ++ sql_print_error("Invalid value for --general_log_file: %s", ++ opt_logname); ++ return 1; ++ } ++ ++ if (opt_slow_logname && ++ !is_valid_log_name(opt_slow_logname, strlen(opt_slow_logname))) ++ { ++ sql_print_error("Invalid value for --slow_query_log_file: %s", ++ opt_slow_logname); ++ return 1; ++ } ++ + s= opt_logname ? opt_logname : make_default_log_name(buff, ".log"); + sys_var_general_log_path.value= my_strdup(s, MYF(0)); + sys_var_general_log_path.value_length= strlen(s); +diff --git a/sql/set_var.cc b/sql/set_var.cc +index f4035ed..73727c8 100644 +--- a/sql/set_var.cc ++++ b/sql/set_var.cc +@@ -2493,6 +2493,11 @@ static int sys_check_log_path(THD *thd, set_var *var) + log_file_str= res->c_ptr(); + bzero(&f_stat, sizeof(MY_STAT)); + ++ if (!is_valid_log_name(log_file_str, strlen(log_file_str))) ++ { ++ goto err; ++ } ++ + path_length= unpack_filename(path, log_file_str); + + if (!path_length) +-- +2.7.4 + |