[SERVICES]: Addendum to r73486: Start all services within LocalSystem account as...
[reactos.git] / reactos / base / system / services / database.c
index 6a54248..ba2ee28 100644 (file)
 
 #include "services.h"
 
-#include <winuser.h>
+#include <userenv.h>
 
 #define NDEBUG
 #include <debug.h>
 
-/*
- * Uncomment the line below to start services
- * using the SERVICE_START_PENDING state.
- */
-#define USE_SERVICE_START_PENDING
-
 /*
  * Uncomment the line below to use asynchronous IO operations
  * on the service control pipes.
@@ -159,8 +153,8 @@ ScmGetServiceImageByImagePath(LPWSTR lpImagePath)
 static
 BOOL
 ScmIsSameServiceAccount(
-    IN PWSTR pszAccountName1,
-    IN PWSTR pszAccountName2)
+    _In_ PCWSTR pszAccountName1,
+    _In_ PCWSTR pszAccountName2)
 {
     if (pszAccountName1 == NULL && pszAccountName2 == NULL)
         return TRUE;
@@ -181,7 +175,7 @@ ScmIsSameServiceAccount(
 static
 BOOL
 ScmIsLocalSystemAccount(
-    IN PWSTR pszAccountName)
+    _In_ PCWSTR pszAccountName)
 {
     if (pszAccountName == NULL ||
         wcscmp(pszAccountName, L"LocalSystem") == 0)
@@ -209,10 +203,14 @@ ScmLogonService(
     if (ScmIsLocalSystemAccount(pImage->pszAccountName))
         return ERROR_SUCCESS;
 
+    // FIXME: Always assume LocalSystem
+    return ERROR_SUCCESS;
+
+#if 0
     ptr = wcschr(pImage->pszAccountName, L'\\');
     if (ptr != NULL)
     {
-        *ptr = 0;
+        *ptr = L'\0';
 
         pUserName = ptr + 1;
         pDomainName = pImage->pszAccountName;
@@ -230,11 +228,11 @@ ScmLogonService(
 
     DPRINT("Domain: %S  User: %S\n", pDomainName, pUserName);
 
-    /* FIXME */
-#if 0
+    /* Logon the user */
+    // FIXME: Use the password!!
     if (!LogonUserW(pUserName,
                     pDomainName,
-                    L"", // lpszPassword,
+                    L"", // FIXME: lpszPassword,
                     LOGON32_LOGON_SERVICE,
                     LOGON32_PROVIDER_DEFAULT,
                     &pImage->hToken))
@@ -242,12 +240,12 @@ ScmLogonService(
         dwError = GetLastError();
         DPRINT1("LogonUserW() failed (Error %lu)\n", dwError);
     }
-#endif
 
     if (ptr != NULL)
         *ptr = L'\\';
 
     return dwError;
+#endif
 }
 
 
@@ -312,6 +310,7 @@ ScmCreateOrReferenceServiceImage(PSERVICE pService)
 
         pServiceImage->dwImageRunCount = 1;
         pServiceImage->hControlPipe = INVALID_HANDLE_VALUE;
+        pServiceImage->hProcess = INVALID_HANDLE_VALUE;
 
         pString = (PWSTR)((INT_PTR)pServiceImage + sizeof(SERVICE_IMAGE));
 
@@ -335,6 +334,10 @@ ScmCreateOrReferenceServiceImage(PSERVICE pService)
         if (dwError != ERROR_SUCCESS)
         {
             DPRINT1("ScmLogonService() failed (Error %lu)\n", dwError);
+
+            /* Release the service image */
+            HeapFree(GetProcessHeap(), 0, pServiceImage);
+
             goto done;
         }
 
@@ -342,7 +345,15 @@ ScmCreateOrReferenceServiceImage(PSERVICE pService)
         dwError = ScmCreateNewControlPipe(pServiceImage);
         if (dwError != ERROR_SUCCESS)
         {
+            DPRINT1("ScmCreateNewControlPipe() failed (Error %lu)\n", dwError);
+
+            /* Close the logon token */
+            if (pServiceImage->hToken != NULL)
+                CloseHandle(pServiceImage->hToken);
+
+            /* Release the service image */
             HeapFree(GetProcessHeap(), 0, pServiceImage);
+
             goto done;
         }
 
@@ -401,14 +412,18 @@ ScmDereferenceServiceImage(PSERVICE_IMAGE pServiceImage)
         /* Remove the service image from the list */
         RemoveEntryList(&pServiceImage->ImageListEntry);
 
-        /* Close the logon token */
-        if (pServiceImage->hToken != NULL)
-            CloseHandle(pServiceImage->hToken);
+        /* Close the process handle */
+        if (pServiceImage->hProcess != INVALID_HANDLE_VALUE)
+            CloseHandle(pServiceImage->hProcess);
 
         /* Close the control pipe */
         if (pServiceImage->hControlPipe != INVALID_HANDLE_VALUE)
             CloseHandle(pServiceImage->hControlPipe);
 
+        /* Close the logon token */
+        if (pServiceImage->hToken != NULL)
+            CloseHandle(pServiceImage->hToken);
+
         /* Release the service image */
         HeapFree(GetProcessHeap(), 0, pServiceImage);
     }
@@ -1216,17 +1231,17 @@ ScmSendStartCommand(PSERVICE Service,
                     DWORD argc,
                     LPWSTR* argv)
 {
+    DWORD dwError = ERROR_SUCCESS;
     PSCM_CONTROL_PACKET ControlPacket;
     SCM_REPLY_PACKET ReplyPacket;
     DWORD PacketSize;
-    PWSTR Ptr;
-    DWORD dwWriteCount = 0;
-    DWORD dwReadCount = 0;
-    DWORD dwError = ERROR_SUCCESS;
     DWORD i;
+    PWSTR Ptr;
     PWSTR *pOffPtr;
     PWSTR pArgPtr;
     BOOL bResult;
+    DWORD dwWriteCount = 0;
+    DWORD dwReadCount = 0;
 #ifdef USE_ASYNCHRONOUS_IO
     OVERLAPPED Overlapped = {0};
 #endif
@@ -1234,26 +1249,32 @@ ScmSendStartCommand(PSERVICE Service,
     DPRINT("ScmSendStartCommand() called\n");
 
     /* Calculate the total length of the start command line */
-    PacketSize = sizeof(SCM_CONTROL_PACKET) +
-                 (DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR));
+    PacketSize = sizeof(SCM_CONTROL_PACKET);
+    PacketSize += (DWORD)((wcslen(Service->lpServiceName) + 1) * sizeof(WCHAR));
 
-    /* Calculate the required packet size for the start arguments */
+    /*
+     * Calculate the required packet size for the start argument vector 'argv',
+     * composed of the list of pointer offsets, followed by UNICODE strings.
+     * The strings are stored continuously after the vector of offsets, with
+     * the offsets being relative to the beginning of the vector, as in the
+     * following layout (with N == argc):
+     *     [argOff(0)]...[argOff(N-1)][str(0)]...[str(N-1)] .
+     */
     if (argc > 0 && argv != NULL)
     {
-        PacketSize = ALIGN_UP(PacketSize, LPWSTR);
+        PacketSize = ALIGN_UP(PacketSize, PWSTR);
+        PacketSize += (argc * sizeof(PWSTR));
 
         DPRINT("Argc: %lu\n", argc);
         for (i = 0; i < argc; i++)
         {
             DPRINT("Argv[%lu]: %S\n", i, argv[i]);
-            PacketSize += (DWORD)((wcslen(argv[i]) + 1) * sizeof(WCHAR) + sizeof(PWSTR));
+            PacketSize += (DWORD)((wcslen(argv[i]) + 1) * sizeof(WCHAR));
         }
     }
 
     /* Allocate a control packet */
-    ControlPacket = HeapAlloc(GetProcessHeap(),
-                              HEAP_ZERO_MEMORY,
-                              PacketSize);
+    ControlPacket = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, PacketSize);
     if (ControlPacket == NULL)
         return ERROR_NOT_ENOUGH_MEMORY;
 
@@ -1262,22 +1283,23 @@ ScmSendStartCommand(PSERVICE Service,
                                ? SERVICE_CONTROL_START_OWN
                                : SERVICE_CONTROL_START_SHARE;
     ControlPacket->hServiceStatus = (SERVICE_STATUS_HANDLE)Service;
-    ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
 
-    Ptr = (PWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset);
+    /* Copy the start command line */
+    ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
+    Ptr = (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwServiceNameOffset);
     wcscpy(Ptr, Service->lpServiceName);
 
-    ControlPacket->dwArgumentsCount = 0;
+    ControlPacket->dwArgumentsCount  = 0;
     ControlPacket->dwArgumentsOffset = 0;
 
-    /* Copy argument list */
+    /* Copy the argument vector */
     if (argc > 0 && argv != NULL)
     {
         Ptr += wcslen(Service->lpServiceName) + 1;
         pOffPtr = (PWSTR*)ALIGN_UP_POINTER(Ptr, PWSTR);
         pArgPtr = (PWSTR)((ULONG_PTR)pOffPtr + argc * sizeof(PWSTR));
 
-        ControlPacket->dwArgumentsCount = argc;
+        ControlPacket->dwArgumentsCount  = argc;
         ControlPacket->dwArgumentsOffset = (DWORD)((ULONG_PTR)pOffPtr - (ULONG_PTR)ControlPacket);
 
         DPRINT("dwArgumentsCount: %lu\n", ControlPacket->dwArgumentsCount);
@@ -1285,12 +1307,10 @@ ScmSendStartCommand(PSERVICE Service,
 
         for (i = 0; i < argc; i++)
         {
-             wcscpy(pArgPtr, argv[i]);
-             *pOffPtr = (PWSTR)((ULONG_PTR)pArgPtr - (ULONG_PTR)pOffPtr);
-             DPRINT("offset: %p\n", *pOffPtr);
-
-             pArgPtr += wcslen(argv[i]) + 1;
-             pOffPtr++;
+            wcscpy(pArgPtr, argv[i]);
+            pOffPtr[i] = (PWSTR)((ULONG_PTR)pArgPtr - (ULONG_PTR)pOffPtr);
+            DPRINT("offset[%lu]: %p\n", i, pOffPtr[i]);
+            pArgPtr += wcslen(argv[i]) + 1;
         }
     }
 
@@ -1665,6 +1685,7 @@ ScmStartUserModeService(PSERVICE Service,
 {
     PROCESS_INFORMATION ProcessInformation;
     STARTUPINFOW StartupInfo;
+    LPVOID lpEnvironment;
     BOOL Result;
     DWORD dwError = ERROR_SUCCESS;
 
@@ -1682,21 +1703,86 @@ ScmStartUserModeService(PSERVICE Service,
     StartupInfo.cb = sizeof(StartupInfo);
     ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
 
-    Result = CreateProcessAsUserW(Service->lpImage->hToken,
-                                  NULL,
-                                  Service->lpImage->pszImagePath,
-                                  NULL,
-                                  NULL,
-                                  FALSE,
-                                  DETACHED_PROCESS | CREATE_SUSPENDED,
-                                  NULL,
-                                  NULL,
-                                  &StartupInfo,
-                                  &ProcessInformation);
+    /* Use the interactive desktop if the service is interactive */
+    // TODO: We should also check the value "NoInteractiveServices ":
+    // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683502(v=vs.85).aspx
+    // for more details.
+    if (Service->Status.dwServiceType & SERVICE_INTERACTIVE_PROCESS)
+        StartupInfo.lpDesktop = L"WinSta0\\Default";
+
+    if (Service->lpImage->hToken)
+    {
+        /* User token: Run the service under the user account */
+
+        if (!CreateEnvironmentBlock(&lpEnvironment, Service->lpImage->hToken, FALSE))
+        {
+            /* We failed, run the service with the current environment */
+            DPRINT1("CreateEnvironmentBlock() failed with error %d; service '%S' will run with the current environment.\n",
+                    GetLastError(), Service->lpServiceName);
+            lpEnvironment = NULL;
+        }
+
+        /* Impersonate the new user */
+        Result = ImpersonateLoggedOnUser(Service->lpImage->hToken);
+        if (Result)
+        {
+            /* Launch the process in the user's logon session */
+            Result = CreateProcessAsUserW(Service->lpImage->hToken,
+                                          NULL,
+                                          Service->lpImage->pszImagePath,
+                                          NULL,
+                                          NULL,
+                                          FALSE,
+                                          CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS | CREATE_SUSPENDED,
+                                          lpEnvironment,
+                                          NULL,
+                                          &StartupInfo,
+                                          &ProcessInformation);
+            if (!Result)
+                dwError = GetLastError();
+
+            /* Revert the impersonation */
+            RevertToSelf();
+        }
+        else
+        {
+            dwError = GetLastError();
+            DPRINT1("ImpersonateLoggedOnUser() failed with error %d\n", dwError);
+        }
+    }
+    else
+    {
+        /* No user token: Run the service under the LocalSystem account */
+
+        if (!CreateEnvironmentBlock(&lpEnvironment, NULL, TRUE))
+        {
+            /* We failed, run the service with the current environment */
+            DPRINT1("CreateEnvironmentBlock() failed with error %d; service '%S' will run with the current environment.\n",
+                    GetLastError(), Service->lpServiceName);
+            lpEnvironment = NULL;
+        }
+
+        Result = CreateProcessW(NULL,
+                                Service->lpImage->pszImagePath,
+                                NULL,
+                                NULL,
+                                FALSE,
+                                CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS | CREATE_SUSPENDED,
+                                lpEnvironment,
+                                NULL,
+                                &StartupInfo,
+                                &ProcessInformation);
+        if (!Result)
+            dwError = GetLastError();
+    }
+
+    if (lpEnvironment)
+        DestroyEnvironmentBlock(lpEnvironment);
+
     if (!Result)
     {
-        dwError = GetLastError();
-        DPRINT1("Starting '%S' failed!\n", Service->lpServiceName);
+        DPRINT1("Starting '%S' failed with error %d\n",
+                Service->lpServiceName, dwError);
         return dwError;
     }
 
@@ -1707,30 +1793,25 @@ ScmStartUserModeService(PSERVICE Service,
            ProcessInformation.dwThreadId,
            ProcessInformation.hThread);
 
-    /* Get process handle and id */
+    /* Get the process handle and ID */
+    Service->lpImage->hProcess = ProcessInformation.hProcess;
     Service->lpImage->dwProcessId = ProcessInformation.dwProcessId;
 
-    /* Resume Thread */
+    /* Resume the main thread and close its handle */
     ResumeThread(ProcessInformation.hThread);
+    CloseHandle(ProcessInformation.hThread);
 
     /* Connect control pipe */
     dwError = ScmWaitForServiceConnect(Service);
-    if (dwError == ERROR_SUCCESS)
-    {
-        /* Send start command */
-        dwError = ScmSendStartCommand(Service, argc, argv);
-    }
-    else
+    if (dwError != ERROR_SUCCESS)
     {
         DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
         Service->lpImage->dwProcessId = 0;
+        return dwError;
     }
 
-    /* Close thread and process handle */
-    CloseHandle(ProcessInformation.hThread);
-    CloseHandle(ProcessInformation.hProcess);
-
-    return dwError;
+    /* Send the start command */
+    return ScmSendStartCommand(Service, argc, argv);
 }
 
 
@@ -1761,8 +1842,8 @@ ScmLoadService(PSERVICE Service,
         dwError = ScmLoadDriver(Service);
         if (dwError == ERROR_SUCCESS)
         {
-            Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
             Service->Status.dwCurrentState = SERVICE_RUNNING;
+            Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
         }
     }
     else // if (Service->Status.dwServiceType & (SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS))
@@ -1774,11 +1855,8 @@ ScmLoadService(PSERVICE Service,
             dwError = ScmStartUserModeService(Service, argc, argv);
             if (dwError == ERROR_SUCCESS)
             {
-#ifdef USE_SERVICE_START_PENDING
                 Service->Status.dwCurrentState = SERVICE_START_PENDING;
-#else
-                Service->Status.dwCurrentState = SERVICE_RUNNING;
-#endif
+                Service->Status.dwControlsAccepted = 0;
             }
             else
             {