Skip to content

Commit b615c3d

Browse files
author
Nisha Gopalakrishnan
committed
BUG#25451091:CREATE TABLE DATA DIRECTORY / INDEX DIRECTORY
SYMLINK CHECK RACE CONDITIONS ANALYSIS: ========= A potential defect exists in the handling of CREATE TABLE .. DATA DIRECTORY/ INDEX DIRECTORY which gives way to the user to gain access to another user table or a system table. FIX: ==== The lstat and fstat output of the target files are now stored which help in determining the identity of the target files thus preventing the unauthorized access to other files.
1 parent 67bec60 commit b615c3d

File tree

4 files changed

+83
-19
lines changed

4 files changed

+83
-19
lines changed

include/my_sys.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2000, 2017, 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
@@ -49,6 +49,7 @@ typedef struct my_aio_result {
4949
#ifdef _WIN32
5050
#include <malloc.h> /*for alloca*/
5151
#endif
52+
#include <sys/stat.h>
5253

5354
#define MY_INIT(name) { my_progname= name; my_init(); }
5455

@@ -491,6 +492,16 @@ typedef struct st_io_cache /* Used when cacheing files */
491492

492493
typedef int (*qsort2_cmp)(const void *, const void *, const void *);
493494

495+
/*
496+
Subset of struct stat fields filled by stat/lstat/fstat that uniquely
497+
identify a file
498+
*/
499+
typedef struct st_file_id
500+
{
501+
dev_t st_dev;
502+
ino_t st_ino;
503+
} ST_FILE_ID;
504+
494505
/* defines for mf_iocache */
495506

496507
/* Test if buffer is inited */
@@ -569,8 +580,9 @@ extern File my_create(const char *FileName,int CreateFlags,
569580
extern int my_close(File Filedes,myf MyFlags);
570581
extern int my_mkdir(const char *dir, int Flags, myf MyFlags);
571582
extern int my_readlink(char *to, const char *filename, myf MyFlags);
572-
extern int my_is_symlink(const char *filename);
583+
extern int my_is_symlink(const char *filename, ST_FILE_ID *file_id);
573584
extern int my_realpath(char *to, const char *filename, myf MyFlags);
585+
extern int my_is_same_file(File file, const ST_FILE_ID *file_id);
574586
extern File my_create_with_symlink(const char *linkname, const char *filename,
575587
int createflags, int access_flags,
576588
myf MyFlags);

mysys/my_symlink.c

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2001, 2017, 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
@@ -99,11 +99,18 @@ int my_symlink(const char *content, const char *linkname, myf MyFlags)
9999
#endif
100100

101101

102-
int my_is_symlink(const char *filename __attribute__((unused)))
102+
int my_is_symlink(const char *filename __attribute__((unused)),
103+
ST_FILE_ID *file_id)
103104
{
104105
#if defined (HAVE_LSTAT) && defined (S_ISLNK)
105106
struct stat stat_buff;
106-
return !lstat(filename, &stat_buff) && S_ISLNK(stat_buff.st_mode);
107+
int result= !lstat(filename, &stat_buff) && S_ISLNK(stat_buff.st_mode);
108+
if (file_id && !result)
109+
{
110+
file_id->st_dev= stat_buff.st_dev;
111+
file_id->st_ino= stat_buff.st_ino;
112+
}
113+
return result;
107114
#elif defined (_WIN32)
108115
DWORD dwAttr = GetFileAttributes(filename);
109116
return (dwAttr != INVALID_FILE_ATTRIBUTES) &&
@@ -164,3 +171,20 @@ int my_realpath(char *to, const char *filename, myf MyFlags)
164171
#endif
165172
return 0;
166173
}
174+
175+
176+
/**
177+
Return non-zero if the file descriptor and a previously lstat-ed file
178+
identified by file_id point to the same file
179+
*/
180+
int my_is_same_file(File file, const ST_FILE_ID *file_id)
181+
{
182+
MY_STAT stat_buf;
183+
if (my_fstat(file, &stat_buf, MYF(0)) == -1)
184+
{
185+
my_errno= errno;
186+
return 0;
187+
}
188+
return (stat_buf.st_dev == file_id->st_dev)
189+
&& (stat_buf.st_ino == file_id->st_ino);
190+
}

storage/myisam/mi_delete_table.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2000, 2017, 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
@@ -29,7 +29,7 @@ int mi_delete_table(const char *name)
2929
#endif
3030

3131
fn_format(from,name,"",MI_NAME_IEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT);
32-
if (my_is_symlink(from) && (*myisam_test_invalid_symlink)(from))
32+
if (my_is_symlink(from, NULL) && (*myisam_test_invalid_symlink)(from))
3333
{
3434
/*
3535
Symlink is pointing to file in data directory.
@@ -44,7 +44,7 @@ int mi_delete_table(const char *name)
4444
DBUG_RETURN(my_errno);
4545
}
4646
fn_format(from,name,"",MI_NAME_DEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT);
47-
if (my_is_symlink(from) && (*myisam_test_invalid_symlink)(from))
47+
if (my_is_symlink(from, NULL) && (*myisam_test_invalid_symlink)(from))
4848
{
4949
/*
5050
Symlink is pointing to file in data directory.

storage/myisam/mi_open.c

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2000, 2017, 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
@@ -78,6 +78,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
7878
ulong rec_per_key_part[HA_MAX_POSSIBLE_KEY*MI_MAX_KEY_SEG];
7979
my_off_t key_root[HA_MAX_POSSIBLE_KEY],key_del[MI_MAX_KEY_BLOCK_SIZE];
8080
ulonglong max_key_file_length, max_data_file_length;
81+
ST_FILE_ID file_id= {0, 0};
8182
DBUG_ENTER("mi_open");
8283

8384
LINT_INIT(m_info);
@@ -89,11 +90,15 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
8990

9091
realpath_err= my_realpath(name_buff,
9192
fn_format(org_name,name,"",MI_NAME_IEXT,4),MYF(0));
92-
if (my_is_symlink(org_name) &&
93-
(realpath_err || (*myisam_test_invalid_symlink)(name_buff)))
93+
if (my_is_symlink(name_buff, &file_id))
9494
{
95-
my_errno= HA_WRONG_CREATE_OPTION;
96-
DBUG_RETURN (NULL);
95+
if (realpath_err ||
96+
(*myisam_test_invalid_symlink)(name_buff) ||
97+
my_is_symlink(name_buff, &file_id))
98+
{
99+
my_errno= HA_WRONG_CREATE_OPTION;
100+
DBUG_RETURN (NULL);
101+
}
97102
}
98103

99104
mysql_mutex_lock(&THR_LOCK_myisam);
@@ -113,17 +118,28 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
113118
my_errno= HA_ERR_CRASHED;
114119
goto err;
115120
});
121+
DEBUG_SYNC_C("before_opening_indexfile");
116122
if ((kfile= mysql_file_open(mi_key_file_kfile,
117123
name_buff,
118-
(open_mode= O_RDWR) | O_SHARE, MYF(0))) < 0)
124+
(open_mode= O_RDWR) | O_SHARE | O_NOFOLLOW,
125+
MYF(0))) < 0)
119126
{
120127
if ((errno != EROFS && errno != EACCES) ||
121128
mode != O_RDONLY ||
122129
(kfile= mysql_file_open(mi_key_file_kfile,
123130
name_buff,
124-
(open_mode= O_RDONLY) | O_SHARE, MYF(0))) < 0)
131+
(open_mode= O_RDONLY) | O_SHARE | O_NOFOLLOW,
132+
MYF(0))) < 0)
125133
goto err;
126134
}
135+
136+
if (!my_is_same_file(kfile, &file_id))
137+
{
138+
mysql_file_close(kfile, MYF(0));
139+
my_errno= HA_WRONG_CREATE_OPTION;
140+
goto err;
141+
}
142+
127143
share->mode=open_mode;
128144
errpos=1;
129145
if (mysql_file_read(kfile, share->state.header.file_version, head_length,
@@ -1206,24 +1222,36 @@ int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share, const char *org_name,
12061222
{
12071223
char *data_name= share->data_file_name;
12081224
char real_data_name[FN_REFLEN];
1225+
ST_FILE_ID file_id= {0, 0};
12091226

12101227
if (org_name)
12111228
{
12121229
fn_format(real_data_name,org_name,"",MI_NAME_DEXT,4);
1213-
if (my_is_symlink(real_data_name))
1230+
if (my_is_symlink(real_data_name, &file_id))
12141231
{
12151232
if (my_realpath(real_data_name, real_data_name, MYF(0)) ||
1216-
(*myisam_test_invalid_symlink)(real_data_name))
1233+
(*myisam_test_invalid_symlink)(real_data_name) ||
1234+
my_is_symlink(real_data_name, &file_id))
12171235
{
12181236
my_errno= HA_WRONG_CREATE_OPTION;
12191237
return 1;
12201238
}
12211239
data_name= real_data_name;
12221240
}
12231241
}
1242+
DEBUG_SYNC_C("before_opening_datafile");
12241243
info->dfile= mysql_file_open(mi_key_file_dfile,
1225-
data_name, share->mode | O_SHARE, MYF(MY_WME));
1226-
return info->dfile >= 0 ? 0 : 1;
1244+
data_name, share->mode | O_SHARE | O_NOFOLLOW,
1245+
MYF(MY_WME));
1246+
if (info->dfile < 0)
1247+
return 1;
1248+
if (org_name && !my_is_same_file(info->dfile, &file_id))
1249+
{
1250+
mysql_file_close(info->dfile, MYF(0));
1251+
my_errno= HA_WRONG_CREATE_OPTION;
1252+
return 1;
1253+
}
1254+
return 0;
12271255
}
12281256

12291257

0 commit comments

Comments
 (0)