MDEV-22272 Windows installer - run service unter virtual service account
Change mysql_install_db.exe to run service under virtual account. Set directory permissions so that service has full access to data files. mariabackup --copy-back permission handling (MDEV-17008) needs to be changed as well. Now, whenever a directory is created in course of copy-back, its permissions are copied from the datadir. This handling assumes, that datadir already has the correct permissions for the Windows service.
This commit is contained in:
parent
57e654f59b
commit
8cb3060c5c
@ -58,6 +58,14 @@ Street, Fifth Floor, Boston, MA 02110-1335 USA
|
||||
#include "backup_mysql.h"
|
||||
#include <btr0btr.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#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
|
||||
|
||||
#define ROCKSDB_BACKUP_DIR "#rocksdb"
|
||||
|
||||
/* list of files to sync for --rsync mode */
|
||||
@ -656,6 +664,19 @@ mkdirp(const char *pathname, int Flags, myf MyFlags)
|
||||
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 */
|
||||
if (my_mkdir(pathname, Flags, MyFlags) == 0) {
|
||||
free(parent);
|
||||
@ -667,6 +688,7 @@ mkdirp(const char *pathname, int Flags, myf MyFlags)
|
||||
free(parent);
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
free(parent);
|
||||
return(-1);
|
||||
@ -985,64 +1007,6 @@ run_data_threads(datadir_iter_t *it, os_thread_func_t func, uint n)
|
||||
return(ret);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <accctrl.h>
|
||||
#include <aclapi.h>
|
||||
/*
|
||||
On Windows, fix permission of the file after "copyback"
|
||||
We assume that after copyback, mysqld will run as service as NetworkService
|
||||
user, thus well give full permission on given file to that user.
|
||||
*/
|
||||
|
||||
static int fix_win_file_permissions(const char *file)
|
||||
{
|
||||
struct {
|
||||
TOKEN_USER tokenUser;
|
||||
BYTE buffer[SECURITY_MAX_SID_SIZE];
|
||||
} tokenInfoBuffer;
|
||||
HANDLE hFile = CreateFile(file, READ_CONTROL | WRITE_DAC, 0, NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
ACL* pOldDACL;
|
||||
SECURITY_DESCRIPTOR* pSD = NULL;
|
||||
EXPLICIT_ACCESS ea = { 0 };
|
||||
PSID pSid = NULL;
|
||||
|
||||
GetSecurityInfo(hFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL,
|
||||
&pOldDACL, NULL, (void**)&pSD);
|
||||
DWORD size = SECURITY_MAX_SID_SIZE;
|
||||
pSid = (PSID)tokenInfoBuffer.buffer;
|
||||
if (!CreateWellKnownSid(WinNetworkServiceSid, NULL, pSid,
|
||||
&size))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||||
ea.Trustee.ptstrName = (LPTSTR)pSid;
|
||||
|
||||
ea.grfAccessMode = GRANT_ACCESS;
|
||||
ea.grfAccessPermissions = GENERIC_ALL;
|
||||
ea.grfInheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
|
||||
ea.Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
|
||||
ACL* pNewDACL = 0;
|
||||
DWORD err = SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL);
|
||||
if (!err)
|
||||
{
|
||||
DBUG_ASSERT(pNewDACL);
|
||||
SetSecurityInfo(hFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL,
|
||||
pNewDACL, NULL);
|
||||
LocalFree((HLOCAL)pNewDACL);
|
||||
}
|
||||
if (pSD != NULL)
|
||||
LocalFree((HLOCAL)pSD);
|
||||
CloseHandle(hFile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/************************************************************************
|
||||
Copy file for backup/restore.
|
||||
@ -1091,10 +1055,6 @@ copy_file(ds_ctxt_t *datasink,
|
||||
/* close */
|
||||
msg(thread_n," ...done");
|
||||
datafile_close(&cursor);
|
||||
#ifdef _WIN32
|
||||
if (xtrabackup_copy_back || xtrabackup_move_back)
|
||||
ut_a(!fix_win_file_permissions(dstfile->path));
|
||||
#endif
|
||||
if (ds_close(dstfile)) {
|
||||
goto error_close;
|
||||
}
|
||||
@ -1165,10 +1125,6 @@ move_file(ds_ctxt_t *datasink,
|
||||
errbuf);
|
||||
return(false);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (xtrabackup_copy_back || xtrabackup_move_back)
|
||||
ut_a(!fix_win_file_permissions(dst_file_path_abs));
|
||||
#endif
|
||||
msg(thread_n," ...done");
|
||||
|
||||
return(true);
|
||||
@ -1778,6 +1734,7 @@ apply_log_finish()
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
copy_back()
|
||||
{
|
||||
@ -1798,6 +1755,20 @@ copy_back()
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
/* If we create paths for DATA DIRECTORY, they need
|
||||
the same permissions as the datadir, or service won't
|
||||
be able to access the files. */
|
||||
DWORD res = GetNamedSecurityInfoA(mysql_data_home,
|
||||
SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
|
||||
NULL, NULL, NULL, NULL,
|
||||
&datadir_security_descriptor);
|
||||
if (res != ERROR_SUCCESS) {
|
||||
msg("Unable to read security descriptor of %s",mysql_data_home);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (srv_undo_dir && *srv_undo_dir
|
||||
&& !directory_exists(srv_undo_dir, true)) {
|
||||
return(false);
|
||||
|
@ -29,6 +29,8 @@
|
||||
struct IUnknown;
|
||||
#include <shlwapi.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#define USAGETEXT \
|
||||
"mysql_install_db.exe Ver 1.00 for Windows\n" \
|
||||
"Copyright (C) 2010-2011 Monty Program Ab & Vladislav Vaintroub\n" \
|
||||
@ -51,8 +53,6 @@ static char *opt_password;
|
||||
static int opt_port;
|
||||
static int opt_innodb_page_size;
|
||||
static char *opt_socket;
|
||||
static char *opt_os_user;
|
||||
static char *opt_os_password;
|
||||
static my_bool opt_default_user;
|
||||
static my_bool opt_allow_remote_root_access;
|
||||
static my_bool opt_skip_networking;
|
||||
@ -196,11 +196,6 @@ int main(int argc, char **argv)
|
||||
/* Print some help on errors */
|
||||
verbose_errors= TRUE;
|
||||
|
||||
if (!opt_os_user)
|
||||
{
|
||||
opt_os_user= default_os_user;
|
||||
opt_os_password= NULL;
|
||||
}
|
||||
/* Workaround WiX bug (strip possible quote character at the end of path) */
|
||||
size_t len= strlen(opt_datadir);
|
||||
if (len > 0)
|
||||
@ -382,7 +377,7 @@ static const char end_of_script[]="-- end.";
|
||||
|
||||
/* Register service. Assume my.ini is in datadir */
|
||||
|
||||
static int register_service()
|
||||
static int register_service(const char *user, const char *passwd)
|
||||
{
|
||||
char buf[3*MAX_PATH +32]; /* path to mysqld.exe, to my.ini, service name */
|
||||
SC_HANDLE sc_manager, sc_service;
|
||||
@ -408,7 +403,7 @@ static int register_service()
|
||||
/* Create the service. */
|
||||
sc_service= CreateService(sc_manager, opt_service, opt_service,
|
||||
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START,
|
||||
SERVICE_ERROR_NORMAL, buf, NULL, NULL, NULL, opt_os_user, opt_os_password);
|
||||
SERVICE_ERROR_NORMAL, buf, NULL, NULL, NULL, user, passwd);
|
||||
|
||||
if (!sc_service)
|
||||
{
|
||||
@ -558,6 +553,7 @@ static int create_db_instance()
|
||||
FILE *in;
|
||||
bool cleanup_datadir= true;
|
||||
DWORD last_error;
|
||||
bool service_created= false;
|
||||
|
||||
verbose("Running bootstrap");
|
||||
|
||||
@ -629,16 +625,29 @@ static int create_db_instance()
|
||||
}
|
||||
}
|
||||
|
||||
std::string service_user;
|
||||
/* Register service if requested. */
|
||||
if (opt_service && opt_service[0])
|
||||
{
|
||||
/* Run service under virtual account NT SERVICE\service_name.*/
|
||||
service_user.append("NT SERVICE\\").append(opt_service);
|
||||
ret = register_service(service_user.c_str(), NULL);
|
||||
if (ret)
|
||||
goto end;
|
||||
service_created = true;
|
||||
}
|
||||
/*
|
||||
Set data directory permissions for both current user and
|
||||
Set data directory permissions for both current user and
|
||||
default_os_user (the one who runs services).
|
||||
*/
|
||||
set_directory_permissions(opt_datadir, NULL);
|
||||
set_directory_permissions(opt_datadir, default_os_user);
|
||||
if (!service_user.empty())
|
||||
set_directory_permissions(opt_datadir, service_user.c_str());
|
||||
|
||||
/* Do mysqld --bootstrap. */
|
||||
init_bootstrap_command_line(cmdline, sizeof(cmdline));
|
||||
|
||||
|
||||
if(opt_verbose_bootstrap)
|
||||
printf("Executing %s\n", cmdline);
|
||||
|
||||
@ -723,19 +732,32 @@ static int create_db_instance()
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
/* Register service if requested. */
|
||||
if (opt_service && opt_service[0])
|
||||
{
|
||||
ret= register_service();
|
||||
if (ret)
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
||||
end:
|
||||
if (ret && cleanup_datadir)
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
/* Cleanup after error.*/
|
||||
if (cleanup_datadir)
|
||||
{
|
||||
SetCurrentDirectory(cwd);
|
||||
clean_directory(opt_datadir);
|
||||
}
|
||||
|
||||
if (service_created)
|
||||
{
|
||||
auto sc_manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
||||
if (sc_manager)
|
||||
{
|
||||
auto sc_handle= OpenServiceA(sc_manager,opt_service, DELETE);
|
||||
if (sc_handle)
|
||||
{
|
||||
DeleteService(sc_handle);
|
||||
CloseServiceHandle(sc_handle);
|
||||
}
|
||||
CloseServiceHandle(sc_manager);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user