Description:
If MySQL is being started as a service, and that service is configured to run as a non-Administrator account, the service will fail to start.
The cause of this problem is in line 501 of sql/nt_servc.cc. The function NTService::IsService checks to see whether the specified service exists on the machine by checking if it has full access to the service (SERVICE_ALL_ACCESS). By default, only Administrators have this right. When run as a non-Administrator, the OpenService call will fail with ERROR_ACCESS_DENIED. Because of the structure of the code, this error has the same effect as the expected ERROR_SERVICE_DOES_NOT_EXIST (IsService returns false, leading mysqld to think it's being run as a console app, not a service).
Workaround: Change the DACL for the service to grant the non-Administrator account SERVICE_ALL_ACCESS. For example, get scacl.exe from LS-Tools (http://www.losoft.de/lstools.html) and run the following command:
scacl.exe MySql /E /G mysqluser:F
Security consequences of this bug: Because the mysqld service cannot run as a non-Admininstrator without doing the workaround, an attacker might be able to gain complete control over the Windows system if he manages to compromise the mysqld server.
How to repeat:
In Windows XP:
* Open the Services management console (Start..Run, services.msc).
* Double click the MySql service (or any other MySQL service set up using "mysqld --install [optional-service-name]")
* Click on the "Log On" tab
* Under "Log on as" choose "This account"
* Choose a non-Administrator account with sufficient file system rights and the "log on as a service" right
* Enter the password for that account
* Hit OK, start the service, and watch it fail
* While waiting for the service to fail, you can verify that mysqld is actually running. Connect to it from a client before the service times out.
Suggested fix:
The following patch should fix the bug. It changes the desired access from SERVICE_ALL_ACCESS (which only Administrators have) to SERVICE_QUERY_STATUS (which is a read-only access that just about everyone has). Because mysqld doesn't actually do anything with the service (the status isn't queried, no changes are made, etc.) it doesn't matter which access right is requested in OpenService() -- the only thing that matters is whether OpenService() returns ERROR_SERVICE_DOES_NOT_EXIST or not.
===== sql/nt_servc.cc 1.13 vs edited =====
--- 1.13/sql/nt_servc.cc Tue Jul 01 07:40:13 2003
+++ edited/sql/nt_servc.cc Thu Oct 30 18:02:05 2003
@@ -498,7 +498,7 @@
if (scm = OpenSCManager(0, 0,SC_MANAGER_ENUMERATE_SERVICE))
{
- if ((service = OpenService(scm,ServiceName, SERVICE_ALL_ACCESS )))
+ if ((service = OpenService(scm,ServiceName, SERVICE_QUERY_STATUS )))
{
ret_value=TRUE;
CloseServiceHandle(service);