Skip to content

Commit

Permalink
MDEV-22272 Windows installer - run service unter virtual service account
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vaintroub committed May 18, 2020
1 parent 57e654f commit 8cb3060
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 85 deletions.
103 changes: 37 additions & 66 deletions extra/mariabackup/backup_copy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
Expand All @@ -667,6 +688,7 @@ mkdirp(const char *pathname, int Flags, myf MyFlags)
free(parent);
return(0);
}
#endif

free(parent);
return(-1);
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1778,6 +1734,7 @@ apply_log_finish()
return(true);
}


bool
copy_back()
{
Expand All @@ -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);
Expand Down
60 changes: 41 additions & 19 deletions sql/mysql_install_db.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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" \
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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)
{
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
}

0 comments on commit 8cb3060

Please sign in to comment.