Skip to content

Commit 48bd8b1

Browse files
ssorumgardnawazn
authored andcommitted
Bug#24388753: PRIVILEGE ESCALATION USING MYSQLD_SAFE
[This is the 5.5/5.6 version of the bugfix]. 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.
1 parent 4e54738 commit 48bd8b1

File tree

4 files changed

+131
-11
lines changed

4 files changed

+131
-11
lines changed

sql/log.cc

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2293,6 +2293,77 @@ bool MYSQL_LOG::init_and_set_log_file_name(const char *log_name,
22932293
}
22942294

22952295

2296+
bool is_valid_log_name(const char *name, size_t len)
2297+
{
2298+
if (len > 3)
2299+
{
2300+
const char *tail= name + len - 4;
2301+
if (my_strcasecmp(system_charset_info, tail, ".ini") == 0 ||
2302+
my_strcasecmp(system_charset_info, tail, ".cnf") == 0)
2303+
{
2304+
return false;
2305+
}
2306+
}
2307+
return true;
2308+
}
2309+
2310+
2311+
/**
2312+
Get the real log file name, and possibly reopen file.
2313+
2314+
Use realpath() to get the path with symbolic links
2315+
expanded. Then, close the file, and reopen the real path using the
2316+
O_NOFOLLOW flag. This will reject following symbolic links.
2317+
2318+
@param file File descriptor.
2319+
@param log_file_key Key for P_S instrumentation.
2320+
@param open_flags Flags to use for opening the file.
2321+
@param opened_file_name Name of the open fd.
2322+
2323+
@retval file descriptor to open file with 'real_file_name', or '-1'
2324+
in case of errors.
2325+
*/
2326+
2327+
#ifndef _WIN32
2328+
static File mysql_file_real_name_reopen(File file,
2329+
#ifdef HAVE_PSI_INTERFACE
2330+
PSI_file_key log_file_key,
2331+
#endif
2332+
int open_flags,
2333+
const char *opened_file_name)
2334+
{
2335+
DBUG_ASSERT(file);
2336+
DBUG_ASSERT(opened_file_name);
2337+
2338+
/* Buffer for realpath must have capacity for PATH_MAX. */
2339+
char real_file_name[PATH_MAX];
2340+
2341+
/* Get realpath, validate, open realpath with O_NOFOLLOW. */
2342+
if (realpath(opened_file_name, real_file_name) == NULL)
2343+
{
2344+
(void) mysql_file_close(file, MYF(0));
2345+
return -1;
2346+
}
2347+
2348+
if (mysql_file_close(file, MYF(0)))
2349+
return -1;
2350+
2351+
if (strlen(real_file_name) > FN_REFLEN)
2352+
return -1;
2353+
2354+
if (!is_valid_log_name(real_file_name, strlen(real_file_name)))
2355+
{
2356+
sql_print_error("Invalid log file name after expanding symlinks: '%s'",
2357+
real_file_name);
2358+
return -1;
2359+
}
2360+
2361+
return mysql_file_open(log_file_key, real_file_name,
2362+
open_flags | O_NOFOLLOW,
2363+
MYF(MY_WME | ME_WAITTANG));
2364+
}
2365+
#endif // _WIN32
2366+
22962367
/*
22972368
Open a (new) log file.
22982369
@@ -2358,8 +2429,22 @@ bool MYSQL_LOG::open(
23582429

23592430
if ((file= mysql_file_open(log_file_key,
23602431
log_file_name, open_flags,
2361-
MYF(MY_WME | ME_WAITTANG))) < 0 ||
2362-
init_io_cache(&log_file, file, IO_SIZE, io_cache_type,
2432+
MYF(MY_WME | ME_WAITTANG))) < 0)
2433+
goto err;
2434+
2435+
#ifndef _WIN32
2436+
/* Reopen and validate path. */
2437+
if ((log_type_arg == LOG_UNKNOWN || log_type_arg == LOG_NORMAL) &&
2438+
(file= mysql_file_real_name_reopen(file,
2439+
#ifdef HAVE_PSI_INTERFACE
2440+
log_file_key,
2441+
#endif
2442+
open_flags,
2443+
log_file_name)) < 0)
2444+
goto err;
2445+
#endif // _WIN32
2446+
2447+
if (init_io_cache(&log_file, file, IO_SIZE, io_cache_type,
23632448
mysql_file_tell(file, MYF(MY_WME)), 0,
23642449
MYF(MY_WME | MY_NABP |
23652450
((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0))))

sql/log.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,16 @@ File open_binlog(IO_CACHE *log, const char *log_file_name,
717717

718718
char *make_log_name(char *buff, const char *name, const char* log_ext);
719719

720+
/**
721+
Check given log name against certain blacklisted names/extensions.
722+
723+
@param name Log name to check
724+
@param len Length of log name
725+
726+
@returns true if name is valid, false otherwise.
727+
*/
728+
bool is_valid_log_name(const char *name, size_t len);
729+
720730
extern MYSQL_PLUGIN_IMPORT MYSQL_BIN_LOG mysql_bin_log;
721731
extern LOGGER logger;
722732

sql/mysqld.cc

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights
1+
/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights
22
reserved.
33
44
This program is free software; you can redistribute it and/or modify
@@ -3512,6 +3512,22 @@ static int init_common_variables()
35123512
"--log-slow-queries option, log tables are used. "
35133513
"To enable logging to files use the --log-output=file option.");
35143514

3515+
if (opt_logname &&
3516+
!is_valid_log_name(opt_logname, strlen(opt_logname)))
3517+
{
3518+
sql_print_error("Invalid value for --general_log_file: %s",
3519+
opt_logname);
3520+
return 1;
3521+
}
3522+
3523+
if (opt_slow_logname &&
3524+
!is_valid_log_name(opt_slow_logname, strlen(opt_slow_logname)))
3525+
{
3526+
sql_print_error("Invalid value for --slow_query_log_file: %s",
3527+
opt_slow_logname);
3528+
return 1;
3529+
}
3530+
35153531
#define FIX_LOG_VAR(VAR, ALT) \
35163532
if (!VAR || !*VAR) \
35173533
{ \

sql/sys_vars.cc

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
22

33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License as published by
@@ -2810,6 +2810,14 @@ static bool check_log_path(sys_var *self, THD *thd, set_var *var)
28102810
if (!var->save_result.string_value.str)
28112811
return true;
28122812

2813+
if (!is_valid_log_name(var->save_result.string_value.str,
2814+
var->save_result.string_value.length))
2815+
{
2816+
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0),
2817+
self->name.str, var->save_result.string_value.str);
2818+
return true;
2819+
}
2820+
28132821
if (var->save_result.string_value.length > FN_REFLEN)
28142822
{ // path is too long
28152823
my_error(ER_PATH_LENGTH, MYF(0), self->name.str);
@@ -2856,7 +2864,7 @@ static bool check_log_path(sys_var *self, THD *thd, set_var *var)
28562864
return false;
28572865
}
28582866
static bool fix_log(char** logname, const char* default_logname,
2859-
const char*ext, bool enabled, void (*reopen)(char*))
2867+
const char*ext, bool enabled, bool (*reopen)(char*))
28602868
{
28612869
if (!*logname) // SET ... = DEFAULT
28622870
{
@@ -2868,16 +2876,17 @@ static bool fix_log(char** logname, const char* default_logname,
28682876
}
28692877
logger.lock_exclusive();
28702878
mysql_mutex_unlock(&LOCK_global_system_variables);
2879+
bool error= false;
28712880
if (enabled)
2872-
reopen(*logname);
2881+
error= reopen(*logname);
28732882
logger.unlock();
28742883
mysql_mutex_lock(&LOCK_global_system_variables);
2875-
return false;
2884+
return error;
28762885
}
2877-
static void reopen_general_log(char* name)
2886+
static bool reopen_general_log(char* name)
28782887
{
28792888
logger.get_log_file_handler()->close(0);
2880-
logger.get_log_file_handler()->open_query_log(name);
2889+
return logger.get_log_file_handler()->open_query_log(name);
28812890
}
28822891
static bool fix_general_log_file(sys_var *self, THD *thd, enum_var_type type)
28832892
{
@@ -2890,10 +2899,10 @@ static Sys_var_charptr Sys_general_log_path(
28902899
IN_FS_CHARSET, DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG,
28912900
ON_CHECK(check_log_path), ON_UPDATE(fix_general_log_file));
28922901

2893-
static void reopen_slow_log(char* name)
2902+
static bool reopen_slow_log(char* name)
28942903
{
28952904
logger.get_slow_log_file_handler()->close(0);
2896-
logger.get_slow_log_file_handler()->open_slow_log(name);
2905+
return logger.get_slow_log_file_handler()->open_slow_log(name);
28972906
}
28982907
static bool fix_slow_log_file(sys_var *self, THD *thd, enum_var_type type)
28992908
{

0 commit comments

Comments
 (0)