mirror of
https://github.com/MariaDB/server.git
synced 2025-01-28 17:54:16 +01:00
MDEV-27535 Service does not start after MSI install into restricted directory
This happens for example if one installs into home directory of a user C:\Users\<username>\mariadb The reason is that the service user "NT SERVICE\<service_name>" does not have read and execute permissions for service executable mysqld.exe in this directory. Moreover, it would not have read permissions for server.dll loaded by the exe, or to plugin directory, where plugins may reside. The fix is to give service users read and execute permissions to bin, share, and lib\plugin subdirectories. The permission setting is doneby mysql_install_db.exe, but also in MSI. It is important to do that in MSI, as we want permissions to survive the MSI upgrade.
This commit is contained in:
parent
2a0962f39b
commit
12cea09713
3 changed files with 115 additions and 55 deletions
|
@ -479,7 +479,10 @@ IF(WIN32)
|
|||
${CMAKE_CURRENT_BINARY_DIR}/mysql_bootstrap_sql.c
|
||||
COMPONENT Server
|
||||
)
|
||||
SET_TARGET_PROPERTIES(mariadb-install-db PROPERTIES COMPILE_FLAGS -DINSTALL_PLUGINDIR=${INSTALL_PLUGINDIR})
|
||||
|
||||
SET_TARGET_PROPERTIES(mariadb-install-db PROPERTIES COMPILE_DEFINITIONS
|
||||
"INSTALL_PLUGINDIR=${INSTALL_PLUGINDIR};INSTALL_SHAREDIR=${INSTALL_SHAREDIR}"
|
||||
)
|
||||
TARGET_LINK_LIBRARIES(mariadb-install-db mysys shlwapi)
|
||||
|
||||
ADD_LIBRARY(winservice STATIC winservice.c)
|
||||
|
|
|
@ -127,15 +127,6 @@ ATTRIBUTE_NORETURN static void die(const char *fmt, ...)
|
|||
fprintf(stderr, "FATAL ERROR: ");
|
||||
vfprintf(stderr, fmt, args);
|
||||
fputc('\n', stderr);
|
||||
if (verbose_errors)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"https://mariadb.com/kb/en/installation-issues-on-windows contains some help\n"
|
||||
"for solving the most common problems. If this doesn't help you, please\n"
|
||||
"leave a comment in the Knowledge Base or file a bug report at\n"
|
||||
"https://jira.mariadb.org");
|
||||
}
|
||||
fflush(stderr);
|
||||
va_end(args);
|
||||
my_end(0);
|
||||
exit(1);
|
||||
|
@ -252,8 +243,6 @@ int main(int argc, char **argv)
|
|||
|
||||
DBUG_ASSERT(datadir);
|
||||
|
||||
/* Print some help on errors */
|
||||
verbose_errors= TRUE;
|
||||
|
||||
/* Workaround WiX bug (strip possible quote character at the end of path) */
|
||||
size_t len= strlen(datadir);
|
||||
|
@ -286,11 +275,11 @@ int main(int argc, char **argv)
|
|||
Convert slashes in paths into MySQL-compatible form
|
||||
*/
|
||||
|
||||
static void convert_slashes(char *s)
|
||||
static void convert_slashes(char *s, char replacement)
|
||||
{
|
||||
for (; *s ; s++)
|
||||
if (*s == '\\')
|
||||
*s= '/';
|
||||
for (; *s; s++)
|
||||
if (*s == '\\' || *s == '/')
|
||||
*s= replacement;
|
||||
}
|
||||
|
||||
|
||||
|
@ -300,15 +289,16 @@ static void convert_slashes(char *s)
|
|||
E.g basedir for C:\my\bin\mysqld.exe would be C:\my
|
||||
*/
|
||||
|
||||
static void get_basedir(char *basedir, int size, const char *mysqld_path)
|
||||
static void get_basedir(char *basedir, int size, const char *mysqld_path,
|
||||
char slash)
|
||||
{
|
||||
strcpy_s(basedir, size, mysqld_path);
|
||||
convert_slashes(basedir);
|
||||
char *p= strrchr(basedir,'/');
|
||||
convert_slashes(basedir, '\\');
|
||||
char *p= strrchr(basedir, '\\');
|
||||
if (p)
|
||||
{
|
||||
*p = 0;
|
||||
p= strrchr(basedir, '/');
|
||||
p= strrchr(basedir, '\\');
|
||||
if (p)
|
||||
*p= 0;
|
||||
}
|
||||
|
@ -320,7 +310,7 @@ static void get_basedir(char *basedir, int size, const char *mysqld_path)
|
|||
static char *get_plugindir()
|
||||
{
|
||||
static char plugin_dir[2*MAX_PATH];
|
||||
get_basedir(plugin_dir, sizeof(plugin_dir), mysqld_path);
|
||||
get_basedir(plugin_dir, sizeof(plugin_dir), mysqld_path, '/');
|
||||
strcat(plugin_dir, "/" STR(INSTALL_PLUGINDIR));
|
||||
|
||||
if (access(plugin_dir, 0) == 0)
|
||||
|
@ -391,7 +381,7 @@ static int create_myini()
|
|||
}
|
||||
|
||||
/* Write out server settings. */
|
||||
convert_slashes(path_buf);
|
||||
convert_slashes(path_buf,'/');
|
||||
write_myini_str("datadir",path_buf);
|
||||
|
||||
if (opt_skip_networking)
|
||||
|
@ -600,7 +590,8 @@ static void clean_directory(const char *dir)
|
|||
(defined as username or group string or as SID)
|
||||
*/
|
||||
|
||||
static int set_directory_permissions(const char *dir, const char *os_user)
|
||||
static int set_directory_permissions(const char *dir, const char *os_user,
|
||||
DWORD permission)
|
||||
{
|
||||
|
||||
struct{
|
||||
|
@ -676,15 +667,23 @@ static int set_directory_permissions(const char *dir, const char *os_user)
|
|||
ea.Trustee.TrusteeForm= TRUSTEE_IS_SID;
|
||||
ea.Trustee.ptstrName= (LPTSTR)pSid;
|
||||
}
|
||||
ea.Trustee.TrusteeType= TRUSTEE_IS_UNKNOWN;
|
||||
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;
|
||||
SetEntriesInAcl(1,&ea,pOldDACL,&pNewDACL);
|
||||
ea.grfAccessPermissions= permission;
|
||||
ea.grfInheritance= CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
|
||||
ACL *pNewDACL= 0;
|
||||
|
||||
ACCESS_MASK access_mask;
|
||||
if (GetEffectiveRightsFromAcl(pOldDACL, &ea.Trustee, &access_mask) != ERROR_SUCCESS
|
||||
|| (access_mask & permission) != permission)
|
||||
{
|
||||
SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL);
|
||||
}
|
||||
|
||||
if (pNewDACL)
|
||||
{
|
||||
SetSecurityInfo(hDir,SE_FILE_OBJECT,DACL_SECURITY_INFORMATION,NULL, NULL,
|
||||
verbose("Adjust permissions for user %s, directory %s", os_user, dir);
|
||||
SetSecurityInfo(hDir,SE_FILE_OBJECT,DACL_SECURITY_INFORMATION,NULL, NULL,
|
||||
pNewDACL, NULL);
|
||||
}
|
||||
if (pSD != NULL)
|
||||
|
@ -695,7 +694,65 @@ static int set_directory_permissions(const char *dir, const char *os_user)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void set_permissions(const char *datadir, const char *service_user)
|
||||
{
|
||||
/*
|
||||
Set data directory permissions for both current user and
|
||||
the one who who runs services.
|
||||
*/
|
||||
set_directory_permissions(datadir, NULL,
|
||||
FILE_GENERIC_READ | FILE_GENERIC_WRITE);
|
||||
if (!service_user)
|
||||
return;
|
||||
|
||||
/* Datadir permission for the service. */
|
||||
set_directory_permissions(datadir, service_user, FILE_ALL_ACCESS);
|
||||
char basedir[MAX_PATH];
|
||||
char path[MAX_PATH];
|
||||
|
||||
struct
|
||||
{
|
||||
const char *subdir;
|
||||
DWORD perm;
|
||||
} all_subdirs[]= {
|
||||
{STR(INSTALL_PLUGINDIR), FILE_GENERIC_READ | FILE_GENERIC_EXECUTE},
|
||||
{STR(INSTALL_SHAREDIR), FILE_GENERIC_READ},
|
||||
};
|
||||
|
||||
|
||||
if (strncmp(service_user,"NT SERVICE\\",sizeof("NT SERVICE\\")-1) == 0)
|
||||
{
|
||||
/*
|
||||
Read and execute permission for executables can/should be given
|
||||
to any service account, rather than specific one.
|
||||
*/
|
||||
service_user="NT SERVICE\\ALL SERVICES";
|
||||
}
|
||||
|
||||
get_basedir(basedir, sizeof(basedir), mysqld_path, '\\');
|
||||
for (int i= 0; i < array_elements(all_subdirs); i++)
|
||||
{
|
||||
auto subdir=
|
||||
snprintf(path, sizeof(path), "%s\\%s", basedir, all_subdirs[i].subdir);
|
||||
if (access(path, 0) == 0)
|
||||
{
|
||||
set_directory_permissions(path, service_user, all_subdirs[i].perm);
|
||||
}
|
||||
}
|
||||
|
||||
/* Bindir, the directory where mysqld_path is located. */
|
||||
strcpy_s(path, mysqld_path);
|
||||
char *end= strrchr(path, '/');
|
||||
if (!end)
|
||||
end= strrchr(path, '\\');
|
||||
if (end)
|
||||
*end= 0;
|
||||
if (access(path, 0) == 0)
|
||||
{
|
||||
set_directory_permissions(path, service_user,
|
||||
FILE_GENERIC_READ | FILE_GENERIC_EXECUTE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create database instance (including registering as service etc) .*/
|
||||
|
||||
|
@ -776,19 +833,13 @@ static int create_db_instance(const char *datadir)
|
|||
goto end;
|
||||
service_created = true;
|
||||
}
|
||||
|
||||
set_permissions(datadir, service_user.c_str());
|
||||
|
||||
if (opt_large_pages)
|
||||
{
|
||||
handle_user_privileges(service_user.c_str(), L"SeLockMemoryPrivilege", true);
|
||||
}
|
||||
/*
|
||||
Set data directory permissions for both current user and
|
||||
the one who who runs services.
|
||||
*/
|
||||
set_directory_permissions(datadir, NULL);
|
||||
if (!service_user.empty())
|
||||
{
|
||||
set_directory_permissions(datadir, service_user.c_str());
|
||||
}
|
||||
|
||||
/*
|
||||
Get security descriptor for the data directory.
|
||||
|
|
|
@ -409,9 +409,6 @@
|
|||
<RegistryValue Root='HKLM'
|
||||
Key='SOFTWARE\@CPACK_WIX_PACKAGE_NAME@'
|
||||
Name='DATADIR' Value='[DATADIR]' Type='string' KeyPath='yes'/>
|
||||
<CreateFolder>
|
||||
<util:PermissionEx User="NetworkService" GenericAll="yes" />
|
||||
</CreateFolder>
|
||||
</Component>
|
||||
|
||||
<Component Id="C.datadir.permissions" Directory="DATADIR">
|
||||
|
@ -531,6 +528,29 @@
|
|||
AllowAdvertise='no'
|
||||
Level='1'
|
||||
Display='hidden'>
|
||||
|
||||
<Component Id="C_Permissions.bin" Guid="2ce05496-3273-4866-a5b5-1eff2837b4cb" Directory="D.bin">
|
||||
<!-- in case service is installed now on it the future -->
|
||||
<CreateFolder>
|
||||
<util:PermissionEx User="ALL SERVICES" Domain="NT SERVICE" GenericRead="yes" GenericExecute="yes" />
|
||||
</CreateFolder>
|
||||
<Condition>SERVICENAME</Condition>
|
||||
</Component>
|
||||
|
||||
<Component Id="C_Permissions.lib.plugin" Guid="ff2e8f47-83fd-4dee-9e22-f103600cfc80" Directory="D.lib.plugin">
|
||||
<CreateFolder>
|
||||
<util:PermissionEx User="ALL SERVICES" Domain="NT SERVICE" GenericRead="yes" GenericExecute="yes" />
|
||||
</CreateFolder>
|
||||
<Condition>SERVICENAME</Condition>
|
||||
</Component>
|
||||
|
||||
<Component Id="C_Permissions.share" Guid="be8ee2fb-a837-4b31-b59a-68a506d97d81" Directory="D.share">
|
||||
<CreateFolder>
|
||||
<util:PermissionEx User="ALL SERVICES" Domain="NT SERVICE" GenericRead="yes" GenericExecute="yes" />
|
||||
</CreateFolder>
|
||||
<Condition>SERVICENAME</Condition>
|
||||
</Component>
|
||||
|
||||
<ComponentRef Id='C.bin.mysql.exe'/>
|
||||
<ComponentRef Id='C.bin.mysqladmin.exe'/>
|
||||
<ComponentRef Id='C.bin.mysql_upgrade.exe'/>
|
||||
|
@ -551,20 +571,6 @@
|
|||
</Component>
|
||||
<?if $(var.HaveUpgradeWizard) != "0" ?>
|
||||
<ComponentRef Id='C.bin.mysql_upgrade_wizard.exe'/>
|
||||
<!--
|
||||
<Component Id="c.shortcuts.upgrade_wizard" Guid="*" Directory="ShortcutFolder" Transitive="yes">
|
||||
<RegistryValue
|
||||
Root="HKCU" Key="Software\@CPACK_WIX_PACKAGE_NAME@\Uninstall"
|
||||
Name="shortcuts.upgrade_wizard"
|
||||
Value="1" Type="string" KeyPath="yes" />
|
||||
<Shortcut Id="shortcut.upgrade_wizard"
|
||||
Name="Upgrade Wizard (@CPACK_WIX_PACKAGE_NAME@)"
|
||||
Target="[INSTALLDIR]bin\mysql_upgrade_wizard.exe"
|
||||
Directory="ShortcutFolder"
|
||||
Description="Upgrades older instances of MariaDB/MySQL services to version @MAJOR_VERSION@.@MINOR_VERSION@"
|
||||
Advertise="no"/>
|
||||
</Component>
|
||||
-->
|
||||
<?endif?>
|
||||
</Feature>
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue