MDEV-7021 Pass directory security descriptor from mysql_install_db.exe to bootstrap

This ensures that directory permissions are correct in all cases, even if
boostrap is passed non-standard locations for innodb.

Directory permissions are copied from the datadir.
This commit is contained in:
Vladislav Vaintroub 2020-05-11 22:01:40 +02:00
parent d9b81210fd
commit e2bc029211
9 changed files with 98 additions and 42 deletions

View File

@ -60,12 +60,9 @@ Street, Fifth Floor, Boston, MA 02110-1335 USA
#ifdef _WIN32 #ifdef _WIN32
#include <aclapi.h> #include <aclapi.h>
/* During copyback, store datadir permissions,
use them to create paths for tables specified
with DATA DIRECTORY.*/
PSECURITY_DESCRIPTOR datadir_security_descriptor;
#endif #endif
#define ROCKSDB_BACKUP_DIR "#rocksdb" #define ROCKSDB_BACKUP_DIR "#rocksdb"
/* list of files to sync for --rsync mode */ /* list of files to sync for --rsync mode */
@ -664,19 +661,6 @@ mkdirp(const char *pathname, int Flags, myf MyFlags)
return(-1); return(-1);
} }
#ifdef _WIN32
SECURITY_ATTRIBUTES sa{};
sa.lpSecurityDescriptor= datadir_security_descriptor;
sa.nLength= sizeof(sa);
if (CreateDirectory(pathname, datadir_security_descriptor?&sa : NULL)
|| GetLastError() == ERROR_ALREADY_EXISTS
|| GetLastError() == ERROR_ACCESS_DENIED && strlen(pathname) == 2 && pathname[1]==':')
{
free(parent);
return 0;
}
return -1;
#else
/* make this one if parent has been made */ /* make this one if parent has been made */
if (my_mkdir(pathname, Flags, MyFlags) == 0) { if (my_mkdir(pathname, Flags, MyFlags) == 0) {
free(parent); free(parent);
@ -688,7 +672,6 @@ mkdirp(const char *pathname, int Flags, myf MyFlags)
free(parent); free(parent);
return(0); return(0);
} }
#endif
free(parent); free(parent);
return(-1); return(-1);
@ -1757,13 +1740,12 @@ copy_back()
} }
#ifdef _WIN32 #ifdef _WIN32
/* If we create paths for DATA DIRECTORY, they need /* Initialize security descriptor for the new directories
the same permissions as the datadir, or service won't to be the same as for datadir */
be able to access the files. */
DWORD res = GetNamedSecurityInfoA(mysql_data_home, DWORD res = GetNamedSecurityInfoA(mysql_data_home,
SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&datadir_security_descriptor); &my_dir_security_attributes.lpSecurityDescriptor);
if (res != ERROR_SUCCESS) { if (res != ERROR_SUCCESS) {
msg("Unable to read security descriptor of %s",mysql_data_home); msg("Unable to read security descriptor of %s",mysql_data_home);
} }

View File

@ -271,6 +271,10 @@ extern int my_umask_dir,
my_recived_signals, /* Signals we have got */ my_recived_signals, /* Signals we have got */
my_safe_to_handle_signal, /* Set when allowed to SIGTSTP */ my_safe_to_handle_signal, /* Set when allowed to SIGTSTP */
my_dont_interrupt; /* call remember_intr when set */ my_dont_interrupt; /* call remember_intr when set */
#ifdef _WIN32
extern SECURITY_ATTRIBUTES my_dir_security_attributes;
LPSECURITY_ATTRIBUTES my_win_file_secattr();
#endif
extern my_bool my_use_symdir; extern my_bool my_use_symdir;
extern ulong my_default_record_cache_size; extern ulong my_default_record_cache_size;

View File

@ -26,13 +26,18 @@ int my_mkdir(const char *dir, int Flags, myf MyFlags)
{ {
DBUG_ENTER("my_dir"); DBUG_ENTER("my_dir");
DBUG_PRINT("enter",("dir: %s",dir)); DBUG_PRINT("enter",("dir: %s",dir));
#ifdef _WIN32
#if defined(__WIN__) LPSECURITY_ATTRIBUTES attr =
if (mkdir((char*) dir)) my_dir_security_attributes.lpSecurityDescriptor?
&my_dir_security_attributes : NULL;
BOOL ok = CreateDirectory(dir, attr);
if (!ok)
{
my_osmaperr(GetLastError());
#else #else
if (mkdir((char*) dir, Flags & my_umask_dir)) if (mkdir((char*) dir, Flags & my_umask_dir))
#endif
{ {
#endif
my_errno=errno; my_errno=errno;
DBUG_PRINT("error",("error %d when creating direcory %s",my_errno,dir)); DBUG_PRINT("error",("error %d when creating direcory %s",my_errno,dir));
if (MyFlags & (MY_FFNF | MY_FAE | MY_WME)) if (MyFlags & (MY_FFNF | MY_FAE | MY_WME))

View File

@ -63,7 +63,9 @@ char curr_dir[FN_REFLEN]= {0},
ulong my_stream_opened=0,my_tmp_file_created=0; ulong my_stream_opened=0,my_tmp_file_created=0;
ulong my_file_total_opened= 0; ulong my_file_total_opened= 0;
int my_umask=0664, my_umask_dir=0777; int my_umask=0664, my_umask_dir=0777;
#ifdef _WIN32
SECURITY_ATTRIBUTES my_dir_security_attributes= {sizeof(SECURITY_ATTRIBUTES),NULL,FALSE};
#endif
myf my_global_flags= 0; myf my_global_flags= 0;
#ifndef DBUG_OFF #ifndef DBUG_OFF
my_bool my_assert= 1; my_bool my_assert= 1;

View File

@ -103,6 +103,25 @@ static int my_get_open_flags(File fd)
} }
/*
Default security attributes for files and directories
Usually NULL, but can be set
- by either mysqld --bootstrap when started from
mysql_install_db.exe, and creating windows service
- or by mariabackup --copy-back.
The objective in both cases is to fix file or directory
privileges for those files that are outside of the usual
datadir, so that unprivileged service account has full
access to the files.
*/
LPSECURITY_ATTRIBUTES my_win_file_secattr()
{
return my_dir_security_attributes.lpSecurityDescriptor?
&my_dir_security_attributes : NULL;
}
/* /*
Open a file with sharing. Similar to _sopen() from libc, but allows managing Open a file with sharing. Similar to _sopen() from libc, but allows managing
share delete on win32 share delete on win32
@ -128,7 +147,6 @@ File my_win_sopen(const char *path, int oflag, int shflag, int pmode)
DWORD fileshare; /* OS file sharing mode */ DWORD fileshare; /* OS file sharing mode */
DWORD filecreate; /* OS method of opening/creating */ DWORD filecreate; /* OS method of opening/creating */
DWORD fileattrib; /* OS file attribute flags */ DWORD fileattrib; /* OS file attribute flags */
SECURITY_ATTRIBUTES SecurityAttributes;
DBUG_ENTER("my_win_sopen"); DBUG_ENTER("my_win_sopen");
@ -137,9 +155,6 @@ File my_win_sopen(const char *path, int oflag, int shflag, int pmode)
errno= EACCES; errno= EACCES;
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
SecurityAttributes.nLength= sizeof(SecurityAttributes);
SecurityAttributes.lpSecurityDescriptor= NULL;
SecurityAttributes.bInheritHandle= !(oflag & _O_NOINHERIT);
/* decode the access flags */ /* decode the access flags */
switch (oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)) { switch (oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)) {
@ -247,7 +262,7 @@ File my_win_sopen(const char *path, int oflag, int shflag, int pmode)
fileattrib|= FILE_FLAG_RANDOM_ACCESS; fileattrib|= FILE_FLAG_RANDOM_ACCESS;
/* try to open/create the file */ /* try to open/create the file */
if ((osfh= CreateFile(path, fileaccess, fileshare, &SecurityAttributes, if ((osfh= CreateFile(path, fileaccess, fileshare,my_win_file_secattr(),
filecreate, fileattrib, NULL)) == INVALID_HANDLE_VALUE) filecreate, fileattrib, NULL)) == INVALID_HANDLE_VALUE)
{ {
/* /*

View File

@ -27,6 +27,7 @@
#include <accctrl.h> #include <accctrl.h>
#include <aclapi.h> #include <aclapi.h>
#include <ntsecapi.h> #include <ntsecapi.h>
#include <sddl.h>
struct IUnknown; struct IUnknown;
#include <shlwapi.h> #include <shlwapi.h>
@ -729,6 +730,26 @@ static int create_db_instance()
set_directory_permissions(opt_datadir, service_user.c_str()); set_directory_permissions(opt_datadir, service_user.c_str());
set_directory_permissions("mysql",service_user.c_str()); set_directory_permissions("mysql",service_user.c_str());
} }
/*
Get security descriptor for the new directory.
It will be passed, as SDDL text, to the mysqld bootstrap subprocess,
to allow for correct subdirectory permissions.
*/
PSECURITY_DESCRIPTOR pSD;
if (GetNamedSecurityInfoA(opt_datadir,SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
0,0,0,0,&pSD) == ERROR_SUCCESS)
{
char* string_sd = NULL;
if (ConvertSecurityDescriptorToStringSecurityDescriptor(pSD, SDDL_REVISION_1,
DACL_SECURITY_INFORMATION,&string_sd,0))
{
_putenv_s("MARIADB_NEW_DIRECTORY_SDDL",string_sd);
LocalFree(string_sd);
}
LocalFree(pSD);
}
/* Do mysqld --bootstrap. */ /* Do mysqld --bootstrap. */
init_bootstrap_command_line(cmdline, sizeof(cmdline)); init_bootstrap_command_line(cmdline, sizeof(cmdline));
@ -843,7 +864,7 @@ end:
/*Remove all service user privileges for the user.*/ /*Remove all service user privileges for the user.*/
if(strncmp(service_user.c_str(), "NT SERVICE\\", if(strncmp(service_user.c_str(), "NT SERVICE\\",
sizeof("NT SERVICE\\")-1) sizeof("NT SERVICE\\")-1))
{ {
handle_user_privileges(service_user.c_str(), 0, false); handle_user_privileges(service_user.c_str(), 0, false);
} }

View File

@ -123,6 +123,7 @@
#ifdef _WIN32 #ifdef _WIN32
#include <handle_connections_win.h> #include <handle_connections_win.h>
#include <sddl.h>
#endif #endif
#include <my_service_manager.h> #include <my_service_manager.h>
@ -8166,6 +8167,23 @@ mysqld_get_one_option(const struct my_option *opt, char *argument,
break; break;
case OPT_BOOTSTRAP: case OPT_BOOTSTRAP:
opt_noacl=opt_bootstrap=1; opt_noacl=opt_bootstrap=1;
#ifdef _WIN32
{
/*
Check if security descriptor is passed from
mysql_install_db.exe.
Used by Windows installer to correctly setup
privileges on the new directories.
*/
char* dir_sddl = getenv("MARIADB_NEW_DIRECTORY_SDDL");
if (dir_sddl)
{
ConvertStringSecurityDescriptorToSecurityDescriptor(
dir_sddl, SDDL_REVISION_1, &my_dir_security_attributes.lpSecurityDescriptor, NULL);
DBUG_ASSERT(my_dir_security_attributes.lpSecurityDescriptor);
}
}
#endif
break; break;
case OPT_SERVER_ID: case OPT_SERVER_ID:
::server_id= global_system_variables.server_id; ::server_id= global_system_variables.server_id;

View File

@ -233,6 +233,7 @@ static void buf_dump_generate_path(char *path, size_t path_size)
} }
} }
/*****************************************************************//** /*****************************************************************//**
Perform a buffer pool dump into the file specified by Perform a buffer pool dump into the file specified by
innodb_buffer_pool_filename. If any errors occur then the value of innodb_buffer_pool_filename. If any errors occur then the value of
@ -262,7 +263,10 @@ buf_dump(
buf_dump_status(STATUS_INFO, "Dumping buffer pool(s) to %s", buf_dump_status(STATUS_INFO, "Dumping buffer pool(s) to %s",
full_filename); full_filename);
#if defined(__GLIBC__) || defined(__WIN__) || O_CLOEXEC == 0 #ifdef _WIN32
/* use my_fopen() for correct permissions during bootstrap*/
f = my_fopen(tmp_filename, O_RDWR|O_TRUNC|O_CREAT, 0);
#elif defined(__GLIBC__) || defined(__WIN__) || O_CLOEXEC == 0
f = fopen(tmp_filename, "w" STR_O_CLOEXEC); f = fopen(tmp_filename, "w" STR_O_CLOEXEC);
#else #else
{ {
@ -375,7 +379,7 @@ buf_dump(
ut_free(dump); ut_free(dump);
done: done:
ret = fclose(f); ret = IF_WIN(my_fclose(f,0),fclose(f));
if (ret != 0) { if (ret != 0) {
buf_dump_status(STATUS_ERR, buf_dump_status(STATUS_ERR,
"Cannot close '%s': %s", "Cannot close '%s': %s",

View File

@ -73,11 +73,11 @@ Created 10/21/1995 Heikki Tuuri
#ifdef _WIN32 #ifdef _WIN32
#include <winioctl.h> #include <winioctl.h>
#else
// my_test_if_atomic_write()
#include <my_sys.h>
#endif #endif
// my_test_if_atomic_write() , my_win_secattr()
#include <my_sys.h>
#include <thread> #include <thread>
#include <chrono> #include <chrono>
@ -2005,6 +2005,10 @@ os_file_get_last_error_low(
" because of either a thread exit" " because of either a thread exit"
" or an application request." " or an application request."
" Retry attempt is made."; " Retry attempt is made.";
} else if (err == ERROR_PATH_NOT_FOUND) {
ib::error()
<< "This error means that directory did not exist"
" during file creation.";
} else { } else {
ib::info() << OPERATING_SYSTEM_ERROR_MSG; ib::info() << OPERATING_SYSTEM_ERROR_MSG;
@ -2137,7 +2141,7 @@ os_file_create_simple_func(
file = CreateFile( file = CreateFile(
(LPCTSTR) name, access, (LPCTSTR) name, access,
FILE_SHARE_READ | FILE_SHARE_DELETE, FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL, create_flag, attributes, NULL); my_win_file_secattr(), create_flag, attributes, NULL);
if (file == INVALID_HANDLE_VALUE) { if (file == INVALID_HANDLE_VALUE) {
@ -2543,7 +2547,7 @@ os_file_create_func(
/* Use default security attributes and no template file. */ /* Use default security attributes and no template file. */
file = CreateFile( file = CreateFile(
name, access, share_mode, NULL, name, access, share_mode, my_win_file_secattr(),
create_flag, attributes, NULL); create_flag, attributes, NULL);
/* If FILE_FLAG_NO_BUFFERING was set, check if this can work at all, /* If FILE_FLAG_NO_BUFFERING was set, check if this can work at all,
@ -2597,6 +2601,7 @@ A simple function to open or create a file.
@param[out] success true if succeeded @param[out] success true if succeeded
@return own: handle to the file, not defined if error, error number @return own: handle to the file, not defined if error, error number
can be retrieved with os_file_get_last_error */ can be retrieved with os_file_get_last_error */
pfs_os_file_t pfs_os_file_t
os_file_create_simple_no_error_handling_func( os_file_create_simple_no_error_handling_func(
const char* name, const char* name,
@ -2678,7 +2683,7 @@ os_file_create_simple_no_error_handling_func(
file = CreateFile((LPCTSTR) name, file = CreateFile((LPCTSTR) name,
access, access,
share_mode, share_mode,
NULL, // Security attributes my_win_file_secattr(),
create_flag, create_flag,
attributes, attributes,
NULL); // No template file NULL); // No template file
@ -2968,7 +2973,7 @@ os_file_get_status_win32(
access, access,
FILE_SHARE_READ | FILE_SHARE_WRITE FILE_SHARE_READ | FILE_SHARE_WRITE
| FILE_SHARE_DELETE, // Full sharing | FILE_SHARE_DELETE, // Full sharing
NULL, // Default security my_win_file_secattr(),
OPEN_EXISTING, // Existing file only OPEN_EXISTING, // Existing file only
FILE_ATTRIBUTE_NORMAL, // Normal file FILE_ATTRIBUTE_NORMAL, // Normal file
NULL); // No attr. template NULL); // No attr. template