mirror of
https://github.com/MariaDB/server.git
synced 2026-05-15 19:37:16 +02:00
MDEV-11902 mi_open race condition
TOCTOU bug. The path is checked to be valid, symlinks are resolved. Then the resolved path is opened. Between the check and the open, there's a window when one can replace some path component with a symlink, bypassing validity checks. Fix: after we resolved all symlinks in the path, don't allow open() to resolve symlinks, there should be none. Compared to the old MyISAM/Aria code: * fastpath. Opening of not-symlinked files is just one open(), no fn_format() and lstat() anymore. * opening of symlinked tables doesn't do fn_format() and lstat() either. it also doesn't to realpath() (which was lstat-ing every path component), instead if opens every path component with O_PATH. * share->data_file_name stores realpath(path) not readlink(path). So, SHOW CREATE TABLE needs to do lstat/readlink() now (see ::info()), and certain error messages (cannot open file "XXX") show the real file path with all symlinks resolved.
This commit is contained in:
parent
d78d0d459d
commit
b27fd90ad3
13 changed files with 298 additions and 68 deletions
|
|
@ -15,9 +15,14 @@
|
|||
|
||||
#include "mysys_priv.h"
|
||||
#include "mysys_err.h"
|
||||
#include <my_dir.h>
|
||||
#include <m_string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if !defined(O_PATH) && defined(O_EXEC) /* FreeBSD */
|
||||
#define O_PATH O_EXEC
|
||||
#endif
|
||||
|
||||
static int open_nosymlinks(const char *pathname, int flags, int mode);
|
||||
|
||||
/*
|
||||
Open a file
|
||||
|
|
@ -46,7 +51,10 @@ File my_open(const char *FileName, int Flags, myf MyFlags)
|
|||
#if defined(_WIN32)
|
||||
fd= my_win_open(FileName, Flags);
|
||||
#else
|
||||
fd = open(FileName, Flags, my_umask);
|
||||
if (MyFlags & MY_NOSYMLINKS)
|
||||
fd = open_nosymlinks(FileName, Flags, my_umask);
|
||||
else
|
||||
fd = open(FileName, Flags, my_umask);
|
||||
#endif
|
||||
|
||||
fd= my_register_filename(fd, FileName, FILE_BY_OPEN,
|
||||
|
|
@ -174,3 +182,81 @@ void my_print_open_files(void)
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
like open(), but with symlinks are not accepted anywhere in the path
|
||||
|
||||
This is used for opening symlinked tables for DATA/INDEX DIRECTORY.
|
||||
The paths there have been realpath()-ed. So, we can assume here that
|
||||
|
||||
* `pathname` is an absolute path
|
||||
* no '.', '..', and '//' in the path
|
||||
* file exists
|
||||
*/
|
||||
static int open_nosymlinks(const char *pathname, int flags, int mode)
|
||||
{
|
||||
#ifndef O_PATH
|
||||
#ifdef HAVE_REALPATH
|
||||
char buf[PATH_MAX+1];
|
||||
if (realpath(pathname, buf) == NULL)
|
||||
return -1;
|
||||
if (strcmp(pathname, buf))
|
||||
{
|
||||
errno= ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
return open(pathname, flags, mode | O_NOFOLLOW);
|
||||
#else
|
||||
|
||||
char buf[PATH_MAX+1];
|
||||
char *s= buf, *e= buf+1, *end= strnmov(buf, pathname, sizeof(buf));
|
||||
int fd, dfd= -1;
|
||||
|
||||
if (*end)
|
||||
{
|
||||
errno= ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*s != '/') /* not an absolute path */
|
||||
{
|
||||
errno= ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (*e == '/') /* '//' in the path */
|
||||
{
|
||||
errno= ENOENT;
|
||||
goto err;
|
||||
}
|
||||
while (*e && *e != '/')
|
||||
e++;
|
||||
*e= 0;
|
||||
if (!memcmp(s, ".", 2) || !memcmp(s, "..", 3))
|
||||
{
|
||||
errno= ENOENT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
fd = openat(dfd, s, O_NOFOLLOW | (e < end ? O_PATH : flags), mode);
|
||||
if (fd < 0)
|
||||
goto err;
|
||||
|
||||
if (dfd >= 0)
|
||||
close(dfd);
|
||||
|
||||
dfd= fd;
|
||||
s= ++e;
|
||||
|
||||
if (e >= end)
|
||||
return fd;
|
||||
}
|
||||
err:
|
||||
if (dfd >= 0)
|
||||
close(dfd);
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue