#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.
static
BOOL
ScmIsSameServiceAccount(
- IN PWSTR pszAccountName1,
- IN PWSTR pszAccountName2)
+ _In_ PCWSTR pszAccountName1,
+ _In_ PCWSTR pszAccountName2)
{
if (pszAccountName1 == NULL && pszAccountName2 == NULL)
return TRUE;
static
BOOL
ScmIsLocalSystemAccount(
- IN PWSTR pszAccountName)
+ _In_ PCWSTR pszAccountName)
{
if (pszAccountName == NULL ||
wcscmp(pszAccountName, L"LocalSystem") == 0)
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;
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))
dwError = GetLastError();
DPRINT1("LogonUserW() failed (Error %lu)\n", dwError);
}
-#endif
if (ptr != NULL)
*ptr = L'\\';
return dwError;
+#endif
}
pServiceImage->dwImageRunCount = 1;
pServiceImage->hControlPipe = INVALID_HANDLE_VALUE;
+ pServiceImage->hProcess = INVALID_HANDLE_VALUE;
pString = (PWSTR)((INT_PTR)pServiceImage + sizeof(SERVICE_IMAGE));
if (dwError != ERROR_SUCCESS)
{
DPRINT1("ScmLogonService() failed (Error %lu)\n", dwError);
+
+ /* Release the service image */
+ HeapFree(GetProcessHeap(), 0, pServiceImage);
+
goto done;
}
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;
}
/* 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);
}
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
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;
? 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);
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;
}
}
{
PROCESS_INFORMATION ProcessInformation;
STARTUPINFOW StartupInfo;
+ LPVOID lpEnvironment;
BOOL Result;
DWORD dwError = ERROR_SUCCESS;
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;
}
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);
}
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))
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
{