mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 10:04:49 +00:00
90831e7451
const PCWSTR* == const (const WCHAR*)* == (const WCHAR*) const *
2368 lines
72 KiB
C
2368 lines
72 KiB
C
/*
|
|
* PROJECT: ReactOS Service Control Manager
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: base/system/services/database.c
|
|
* PURPOSE: Database control interface
|
|
* COPYRIGHT: Copyright 2002-2006 Eric Kohl
|
|
* Copyright 2006 Hervé Poussineau <hpoussin@reactos.org>
|
|
* Copyright 2007 Ged Murphy <gedmurphy@reactos.org>
|
|
* Gregor Brunmar <gregor.brunmar@home.se>
|
|
*
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include "services.h"
|
|
|
|
#include <userenv.h>
|
|
#include <strsafe.h>
|
|
|
|
#include <reactos/undocuser.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
LIST_ENTRY ImageListHead;
|
|
LIST_ENTRY ServiceListHead;
|
|
|
|
static RTL_RESOURCE DatabaseLock;
|
|
static DWORD ResumeCount = 1;
|
|
static DWORD NoInteractiveServices = 0;
|
|
static DWORD ServiceTag = 0;
|
|
|
|
/* The critical section synchronizes service control requests */
|
|
static CRITICAL_SECTION ControlServiceCriticalSection;
|
|
static DWORD PipeTimeout = 30000; /* 30 Seconds */
|
|
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
static
|
|
BOOL
|
|
ScmIsSecurityService(
|
|
_In_ PSERVICE_IMAGE pServiceImage)
|
|
{
|
|
return (wcsstr(pServiceImage->pszImagePath, L"\\system32\\lsass.exe") != NULL);
|
|
}
|
|
|
|
|
|
static DWORD
|
|
ScmCreateNewControlPipe(
|
|
_In_ PSERVICE_IMAGE pServiceImage,
|
|
_In_ BOOL bSecurityServiceProcess)
|
|
{
|
|
WCHAR szControlPipeName[MAX_PATH + 1];
|
|
SECURITY_ATTRIBUTES SecurityAttributes;
|
|
HKEY hServiceCurrentKey = INVALID_HANDLE_VALUE;
|
|
DWORD dwServiceCurrent = 1;
|
|
DWORD dwKeyDisposition;
|
|
DWORD dwKeySize;
|
|
DWORD dwError;
|
|
|
|
/* Get the service number */
|
|
if (bSecurityServiceProcess == FALSE)
|
|
{
|
|
/* TODO: Create registry entry with correct write access */
|
|
dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\CurrentControlSet\\Control\\ServiceCurrent",
|
|
0,
|
|
NULL,
|
|
REG_OPTION_VOLATILE,
|
|
KEY_WRITE | KEY_READ,
|
|
NULL,
|
|
&hServiceCurrentKey,
|
|
&dwKeyDisposition);
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
DPRINT1("RegCreateKeyEx() failed with error %lu\n", dwError);
|
|
return dwError;
|
|
}
|
|
|
|
if (dwKeyDisposition == REG_OPENED_EXISTING_KEY)
|
|
{
|
|
dwKeySize = sizeof(DWORD);
|
|
dwError = RegQueryValueExW(hServiceCurrentKey,
|
|
L"",
|
|
0,
|
|
NULL,
|
|
(BYTE*)&dwServiceCurrent,
|
|
&dwKeySize);
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hServiceCurrentKey);
|
|
DPRINT1("RegQueryValueEx() failed with error %lu\n", dwError);
|
|
return dwError;
|
|
}
|
|
|
|
dwServiceCurrent++;
|
|
}
|
|
|
|
dwError = RegSetValueExW(hServiceCurrentKey,
|
|
L"",
|
|
0,
|
|
REG_DWORD,
|
|
(BYTE*)&dwServiceCurrent,
|
|
sizeof(dwServiceCurrent));
|
|
|
|
RegCloseKey(hServiceCurrentKey);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
DPRINT1("RegSetValueExW() failed (Error %lu)\n", dwError);
|
|
return dwError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwServiceCurrent = 0;
|
|
}
|
|
|
|
/* Create '\\.\pipe\net\NtControlPipeXXX' instance */
|
|
StringCchPrintfW(szControlPipeName, ARRAYSIZE(szControlPipeName),
|
|
L"\\\\.\\pipe\\net\\NtControlPipe%lu", dwServiceCurrent);
|
|
|
|
DPRINT("PipeName: %S\n", szControlPipeName);
|
|
|
|
SecurityAttributes.nLength = sizeof(SecurityAttributes);
|
|
SecurityAttributes.lpSecurityDescriptor = pPipeSD;
|
|
SecurityAttributes.bInheritHandle = FALSE;
|
|
|
|
pServiceImage->hControlPipe = CreateNamedPipeW(szControlPipeName,
|
|
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
|
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
|
100,
|
|
8000,
|
|
4,
|
|
PipeTimeout,
|
|
&SecurityAttributes);
|
|
DPRINT("CreateNamedPipeW(%S) done\n", szControlPipeName);
|
|
if (pServiceImage->hControlPipe == INVALID_HANDLE_VALUE)
|
|
{
|
|
DPRINT1("Failed to create control pipe\n");
|
|
return GetLastError();
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
static PSERVICE_IMAGE
|
|
ScmGetServiceImageByImagePath(LPWSTR lpImagePath)
|
|
{
|
|
PLIST_ENTRY ImageEntry;
|
|
PSERVICE_IMAGE CurrentImage;
|
|
|
|
DPRINT("ScmGetServiceImageByImagePath(%S) called\n", lpImagePath);
|
|
|
|
ImageEntry = ImageListHead.Flink;
|
|
while (ImageEntry != &ImageListHead)
|
|
{
|
|
CurrentImage = CONTAINING_RECORD(ImageEntry,
|
|
SERVICE_IMAGE,
|
|
ImageListEntry);
|
|
if (_wcsicmp(CurrentImage->pszImagePath, lpImagePath) == 0)
|
|
{
|
|
DPRINT("Found image: '%S'\n", CurrentImage->pszImagePath);
|
|
return CurrentImage;
|
|
}
|
|
|
|
ImageEntry = ImageEntry->Flink;
|
|
}
|
|
|
|
DPRINT("Couldn't find a matching image\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScmGetServiceNameFromTag(IN PTAG_INFO_NAME_FROM_TAG_IN_PARAMS InParams,
|
|
OUT PTAG_INFO_NAME_FROM_TAG_OUT_PARAMS *OutParams)
|
|
{
|
|
PLIST_ENTRY ServiceEntry;
|
|
PSERVICE CurrentService;
|
|
PSERVICE_IMAGE CurrentImage;
|
|
PTAG_INFO_NAME_FROM_TAG_OUT_PARAMS OutBuffer = NULL;
|
|
DWORD dwError;
|
|
|
|
/* Lock the database */
|
|
ScmLockDatabaseExclusive();
|
|
|
|
/* Find the matching service */
|
|
ServiceEntry = ServiceListHead.Flink;
|
|
while (ServiceEntry != &ServiceListHead)
|
|
{
|
|
CurrentService = CONTAINING_RECORD(ServiceEntry,
|
|
SERVICE,
|
|
ServiceListEntry);
|
|
|
|
/* We must match the tag */
|
|
if (CurrentService->dwServiceTag == InParams->dwTag &&
|
|
CurrentService->lpImage != NULL)
|
|
{
|
|
CurrentImage = CurrentService->lpImage;
|
|
/* And matching the PID */
|
|
if (CurrentImage->dwProcessId == InParams->dwPid)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
ServiceEntry = ServiceEntry->Flink;
|
|
}
|
|
|
|
/* No match! */
|
|
if (ServiceEntry == &ServiceListHead)
|
|
{
|
|
dwError = ERROR_RETRY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Allocate the output buffer */
|
|
OutBuffer = MIDL_user_allocate(sizeof(TAG_INFO_NAME_FROM_TAG_OUT_PARAMS));
|
|
if (OutBuffer == NULL)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* And the buffer for the name */
|
|
OutBuffer->pszName = MIDL_user_allocate(wcslen(CurrentService->lpServiceName) * sizeof(WCHAR) + sizeof(UNICODE_NULL));
|
|
if (OutBuffer->pszName == NULL)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Fill in output data */
|
|
wcscpy(OutBuffer->pszName, CurrentService->lpServiceName);
|
|
OutBuffer->TagType = TagTypeService;
|
|
|
|
/* And return */
|
|
*OutParams = OutBuffer;
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
/* Unlock database */
|
|
ScmUnlockDatabase();
|
|
|
|
/* If failure, free allocated memory */
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
if (OutBuffer != NULL)
|
|
{
|
|
MIDL_user_free(OutBuffer);
|
|
}
|
|
}
|
|
|
|
/* Return error/success */
|
|
return dwError;
|
|
}
|
|
|
|
|
|
static
|
|
BOOL
|
|
ScmIsSameServiceAccount(
|
|
_In_ PCWSTR pszAccountName1,
|
|
_In_ PCWSTR pszAccountName2)
|
|
{
|
|
if (pszAccountName1 == NULL &&
|
|
pszAccountName2 == NULL)
|
|
return TRUE;
|
|
|
|
if (pszAccountName1 == NULL &&
|
|
pszAccountName2 != NULL &&
|
|
_wcsicmp(pszAccountName2, L"LocalSystem") == 0)
|
|
return TRUE;
|
|
|
|
if (pszAccountName1 != NULL &&
|
|
pszAccountName2 == NULL &&
|
|
_wcsicmp(pszAccountName1, L"LocalSystem") == 0)
|
|
return TRUE;
|
|
|
|
if (pszAccountName1 != NULL &&
|
|
pszAccountName2 != NULL &&
|
|
_wcsicmp(pszAccountName1, pszAccountName2) == 0)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static
|
|
BOOL
|
|
ScmIsLocalSystemAccount(
|
|
_In_ PCWSTR pszAccountName)
|
|
{
|
|
if (pszAccountName == NULL ||
|
|
_wcsicmp(pszAccountName, L"LocalSystem") == 0)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static
|
|
BOOL
|
|
ScmEnableBackupRestorePrivileges(
|
|
_In_ HANDLE hToken,
|
|
_In_ BOOL bEnable)
|
|
{
|
|
PTOKEN_PRIVILEGES pTokenPrivileges = NULL;
|
|
DWORD dwSize;
|
|
BOOL bRet = FALSE;
|
|
|
|
DPRINT("ScmEnableBackupRestorePrivileges(%p %d)\n", hToken, bEnable);
|
|
|
|
dwSize = sizeof(TOKEN_PRIVILEGES) + 2 * sizeof(LUID_AND_ATTRIBUTES);
|
|
pTokenPrivileges = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
|
|
if (pTokenPrivileges == NULL)
|
|
{
|
|
DPRINT1("Failed to allocate privilege buffer\n");
|
|
goto done;
|
|
}
|
|
|
|
pTokenPrivileges->PrivilegeCount = 2;
|
|
pTokenPrivileges->Privileges[0].Luid.LowPart = SE_BACKUP_PRIVILEGE;
|
|
pTokenPrivileges->Privileges[0].Luid.HighPart = 0;
|
|
pTokenPrivileges->Privileges[0].Attributes = (bEnable ? SE_PRIVILEGE_ENABLED : 0);
|
|
pTokenPrivileges->Privileges[1].Luid.LowPart = SE_RESTORE_PRIVILEGE;
|
|
pTokenPrivileges->Privileges[1].Luid.HighPart = 0;
|
|
pTokenPrivileges->Privileges[1].Attributes = (bEnable ? SE_PRIVILEGE_ENABLED : 0);
|
|
|
|
bRet = AdjustTokenPrivileges(hToken, FALSE, pTokenPrivileges, 0, NULL, NULL);
|
|
if (!bRet)
|
|
{
|
|
DPRINT1("AdjustTokenPrivileges() failed with error %lu\n", GetLastError());
|
|
}
|
|
else if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
|
|
{
|
|
DPRINT1("AdjustTokenPrivileges() succeeded, but with not all privileges assigned\n");
|
|
bRet = FALSE;
|
|
}
|
|
|
|
done:
|
|
if (pTokenPrivileges != NULL)
|
|
HeapFree(GetProcessHeap(), 0, pTokenPrivileges);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
static
|
|
DWORD
|
|
ScmLogonService(
|
|
IN PSERVICE pService,
|
|
IN PSERVICE_IMAGE pImage)
|
|
{
|
|
PROFILEINFOW ProfileInfo;
|
|
PWSTR pszUserName = NULL;
|
|
PWSTR pszDomainName = NULL;
|
|
PWSTR pszPassword = NULL;
|
|
PWSTR ptr;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
DPRINT("ScmLogonService(%p %p)\n", pService, pImage);
|
|
DPRINT("Service %S\n", pService->lpServiceName);
|
|
|
|
if (ScmIsLocalSystemAccount(pImage->pszAccountName) || ScmLiveSetup || ScmSetupInProgress)
|
|
return ERROR_SUCCESS;
|
|
|
|
/* Get the user and domain names */
|
|
ptr = wcschr(pImage->pszAccountName, L'\\');
|
|
if (ptr != NULL)
|
|
{
|
|
*ptr = L'\0';
|
|
pszUserName = ptr + 1;
|
|
pszDomainName = pImage->pszAccountName;
|
|
}
|
|
else
|
|
{
|
|
// ERROR_INVALID_SERVICE_ACCOUNT
|
|
pszUserName = pImage->pszAccountName;
|
|
pszDomainName = NULL;
|
|
}
|
|
|
|
/* Build the service 'password' */
|
|
pszPassword = HeapAlloc(GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
(wcslen(pService->lpServiceName) + 5) * sizeof(WCHAR));
|
|
if (pszPassword == NULL)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
wcscpy(pszPassword, L"_SC_");
|
|
wcscat(pszPassword, pService->lpServiceName);
|
|
|
|
DPRINT("Domain: %S User: %S Password: %S\n", pszDomainName, pszUserName, pszPassword);
|
|
|
|
/* Do the service logon */
|
|
if (!LogonUserW(pszUserName,
|
|
pszDomainName,
|
|
pszPassword,
|
|
LOGON32_LOGON_SERVICE,
|
|
LOGON32_PROVIDER_DEFAULT,
|
|
&pImage->hToken))
|
|
{
|
|
dwError = GetLastError();
|
|
DPRINT1("LogonUserW() failed (Error %lu)\n", dwError);
|
|
|
|
/* Normalize the returned error */
|
|
dwError = ERROR_SERVICE_LOGON_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
/* Load the user profile; the per-user environment variables are thus correctly initialized */
|
|
ZeroMemory(&ProfileInfo, sizeof(ProfileInfo));
|
|
ProfileInfo.dwSize = sizeof(ProfileInfo);
|
|
ProfileInfo.dwFlags = PI_NOUI;
|
|
ProfileInfo.lpUserName = pszUserName;
|
|
// ProfileInfo.lpProfilePath = NULL;
|
|
// ProfileInfo.lpDefaultPath = NULL;
|
|
// ProfileInfo.lpServerName = NULL;
|
|
// ProfileInfo.lpPolicyPath = NULL;
|
|
// ProfileInfo.hProfile = NULL;
|
|
|
|
ScmEnableBackupRestorePrivileges(pImage->hToken, TRUE);
|
|
if (!LoadUserProfileW(pImage->hToken, &ProfileInfo))
|
|
dwError = GetLastError();
|
|
ScmEnableBackupRestorePrivileges(pImage->hToken, FALSE);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
DPRINT1("LoadUserProfileW() failed (Error %lu)\n", dwError);
|
|
goto done;
|
|
}
|
|
|
|
pImage->hProfile = ProfileInfo.hProfile;
|
|
|
|
done:
|
|
if (pszPassword != NULL)
|
|
HeapFree(GetProcessHeap(), 0, pszPassword);
|
|
|
|
if (ptr != NULL)
|
|
*ptr = L'\\';
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
static DWORD
|
|
ScmCreateOrReferenceServiceImage(PSERVICE pService)
|
|
{
|
|
RTL_QUERY_REGISTRY_TABLE QueryTable[3];
|
|
UNICODE_STRING ImagePath;
|
|
UNICODE_STRING ObjectName;
|
|
PSERVICE_IMAGE pServiceImage = NULL;
|
|
NTSTATUS Status;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
DWORD dwRecordSize;
|
|
LPWSTR pString;
|
|
BOOL bSecurityService;
|
|
|
|
DPRINT("ScmCreateOrReferenceServiceImage(%p)\n", pService);
|
|
|
|
RtlInitUnicodeString(&ImagePath, NULL);
|
|
RtlInitUnicodeString(&ObjectName, NULL);
|
|
|
|
/* Get service data */
|
|
RtlZeroMemory(&QueryTable,
|
|
sizeof(QueryTable));
|
|
|
|
QueryTable[0].Name = L"ImagePath";
|
|
QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
|
|
QueryTable[0].EntryContext = &ImagePath;
|
|
QueryTable[1].Name = L"ObjectName";
|
|
QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
QueryTable[1].EntryContext = &ObjectName;
|
|
|
|
Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
|
|
pService->lpServiceName,
|
|
QueryTable,
|
|
NULL,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
|
|
return RtlNtStatusToDosError(Status);
|
|
}
|
|
|
|
DPRINT("ImagePath: '%wZ'\n", &ImagePath);
|
|
DPRINT("ObjectName: '%wZ'\n", &ObjectName);
|
|
|
|
pServiceImage = ScmGetServiceImageByImagePath(ImagePath.Buffer);
|
|
if (pServiceImage == NULL)
|
|
{
|
|
dwRecordSize = sizeof(SERVICE_IMAGE) +
|
|
ImagePath.Length + sizeof(WCHAR) +
|
|
((ObjectName.Length != 0) ? (ObjectName.Length + sizeof(WCHAR)) : 0);
|
|
|
|
/* Create a new service image */
|
|
pServiceImage = HeapAlloc(GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
dwRecordSize);
|
|
if (pServiceImage == NULL)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
pServiceImage->dwImageRunCount = 1;
|
|
pServiceImage->hControlPipe = INVALID_HANDLE_VALUE;
|
|
pServiceImage->hProcess = INVALID_HANDLE_VALUE;
|
|
|
|
pString = (PWSTR)((INT_PTR)pServiceImage + sizeof(SERVICE_IMAGE));
|
|
|
|
/* Set the image path */
|
|
pServiceImage->pszImagePath = pString;
|
|
wcscpy(pServiceImage->pszImagePath,
|
|
ImagePath.Buffer);
|
|
|
|
/* Set the account name */
|
|
if (ObjectName.Length > 0)
|
|
{
|
|
pString = pString + wcslen(pString) + 1;
|
|
|
|
pServiceImage->pszAccountName = pString;
|
|
wcscpy(pServiceImage->pszAccountName,
|
|
ObjectName.Buffer);
|
|
}
|
|
|
|
/* Service logon */
|
|
dwError = ScmLogonService(pService, pServiceImage);
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
DPRINT1("ScmLogonService() failed (Error %lu)\n", dwError);
|
|
|
|
/* Release the service image */
|
|
HeapFree(GetProcessHeap(), 0, pServiceImage);
|
|
|
|
goto done;
|
|
}
|
|
|
|
bSecurityService = ScmIsSecurityService(pServiceImage);
|
|
|
|
/* Create the control pipe */
|
|
dwError = ScmCreateNewControlPipe(pServiceImage,
|
|
bSecurityService);
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
DPRINT1("ScmCreateNewControlPipe() failed (Error %lu)\n", dwError);
|
|
|
|
/* Unload the user profile */
|
|
if (pServiceImage->hProfile != NULL)
|
|
{
|
|
ScmEnableBackupRestorePrivileges(pServiceImage->hToken, TRUE);
|
|
UnloadUserProfile(pServiceImage->hToken, pServiceImage->hProfile);
|
|
ScmEnableBackupRestorePrivileges(pServiceImage->hToken, FALSE);
|
|
}
|
|
|
|
/* Close the logon token */
|
|
if (pServiceImage->hToken != NULL)
|
|
CloseHandle(pServiceImage->hToken);
|
|
|
|
/* Release the service image */
|
|
HeapFree(GetProcessHeap(), 0, pServiceImage);
|
|
|
|
goto done;
|
|
}
|
|
|
|
if (bSecurityService)
|
|
{
|
|
SetSecurityServicesEvent();
|
|
}
|
|
|
|
/* FIXME: Add more initialization code here */
|
|
|
|
|
|
/* Append service record */
|
|
InsertTailList(&ImageListHead,
|
|
&pServiceImage->ImageListEntry);
|
|
}
|
|
else
|
|
{
|
|
// if ((lpService->Status.dwServiceType & SERVICE_WIN32_SHARE_PROCESS) == 0)
|
|
|
|
/* Fail if services in an image use different accounts */
|
|
if (!ScmIsSameServiceAccount(pServiceImage->pszAccountName, ObjectName.Buffer))
|
|
{
|
|
dwError = ERROR_DIFFERENT_SERVICE_ACCOUNT;
|
|
goto done;
|
|
}
|
|
|
|
/* Increment the run counter */
|
|
pServiceImage->dwImageRunCount++;
|
|
}
|
|
|
|
DPRINT("pServiceImage->pszImagePath: %S\n", pServiceImage->pszImagePath);
|
|
DPRINT("pServiceImage->pszAccountName: %S\n", pServiceImage->pszAccountName);
|
|
DPRINT("pServiceImage->dwImageRunCount: %lu\n", pServiceImage->dwImageRunCount);
|
|
|
|
/* Link the service image to the service */
|
|
pService->lpImage = pServiceImage;
|
|
|
|
done:
|
|
RtlFreeUnicodeString(&ObjectName);
|
|
RtlFreeUnicodeString(&ImagePath);
|
|
|
|
DPRINT("ScmCreateOrReferenceServiceImage() done (Error: %lu)\n", dwError);
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
VOID
|
|
ScmRemoveServiceImage(PSERVICE_IMAGE pServiceImage)
|
|
{
|
|
DPRINT1("ScmRemoveServiceImage() called\n");
|
|
|
|
/* FIXME: Terminate the process */
|
|
|
|
/* Remove the service image from the list */
|
|
RemoveEntryList(&pServiceImage->ImageListEntry);
|
|
|
|
/* 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);
|
|
|
|
/* Unload the user profile */
|
|
if (pServiceImage->hProfile != NULL)
|
|
{
|
|
ScmEnableBackupRestorePrivileges(pServiceImage->hToken, TRUE);
|
|
UnloadUserProfile(pServiceImage->hToken, pServiceImage->hProfile);
|
|
ScmEnableBackupRestorePrivileges(pServiceImage->hToken, FALSE);
|
|
}
|
|
|
|
/* Close the logon token */
|
|
if (pServiceImage->hToken != NULL)
|
|
CloseHandle(pServiceImage->hToken);
|
|
|
|
/* Release the service image */
|
|
HeapFree(GetProcessHeap(), 0, pServiceImage);
|
|
}
|
|
|
|
|
|
PSERVICE
|
|
ScmGetServiceEntryByName(LPCWSTR lpServiceName)
|
|
{
|
|
PLIST_ENTRY ServiceEntry;
|
|
PSERVICE CurrentService;
|
|
|
|
DPRINT("ScmGetServiceEntryByName() called\n");
|
|
|
|
ServiceEntry = ServiceListHead.Flink;
|
|
while (ServiceEntry != &ServiceListHead)
|
|
{
|
|
CurrentService = CONTAINING_RECORD(ServiceEntry,
|
|
SERVICE,
|
|
ServiceListEntry);
|
|
if (_wcsicmp(CurrentService->lpServiceName, lpServiceName) == 0)
|
|
{
|
|
DPRINT("Found service: '%S'\n", CurrentService->lpServiceName);
|
|
return CurrentService;
|
|
}
|
|
|
|
ServiceEntry = ServiceEntry->Flink;
|
|
}
|
|
|
|
DPRINT("Couldn't find a matching service\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PSERVICE
|
|
ScmGetServiceEntryByDisplayName(LPCWSTR lpDisplayName)
|
|
{
|
|
PLIST_ENTRY ServiceEntry;
|
|
PSERVICE CurrentService;
|
|
|
|
DPRINT("ScmGetServiceEntryByDisplayName() called\n");
|
|
|
|
ServiceEntry = ServiceListHead.Flink;
|
|
while (ServiceEntry != &ServiceListHead)
|
|
{
|
|
CurrentService = CONTAINING_RECORD(ServiceEntry,
|
|
SERVICE,
|
|
ServiceListEntry);
|
|
if (_wcsicmp(CurrentService->lpDisplayName, lpDisplayName) == 0)
|
|
{
|
|
DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
|
|
return CurrentService;
|
|
}
|
|
|
|
ServiceEntry = ServiceEntry->Flink;
|
|
}
|
|
|
|
DPRINT("Couldn't find a matching service\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PSERVICE
|
|
ScmGetServiceEntryByResumeCount(DWORD dwResumeCount)
|
|
{
|
|
PLIST_ENTRY ServiceEntry;
|
|
PSERVICE CurrentService;
|
|
|
|
DPRINT("ScmGetServiceEntryByResumeCount() called\n");
|
|
|
|
ServiceEntry = ServiceListHead.Flink;
|
|
while (ServiceEntry != &ServiceListHead)
|
|
{
|
|
CurrentService = CONTAINING_RECORD(ServiceEntry,
|
|
SERVICE,
|
|
ServiceListEntry);
|
|
if (CurrentService->dwResumeCount > dwResumeCount)
|
|
{
|
|
DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
|
|
return CurrentService;
|
|
}
|
|
|
|
ServiceEntry = ServiceEntry->Flink;
|
|
}
|
|
|
|
DPRINT("Couldn't find a matching service\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScmGenerateServiceTag(PSERVICE lpServiceRecord)
|
|
{
|
|
/* Check for an overflow */
|
|
if (ServiceTag == -1)
|
|
{
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
|
|
/* This is only valid for Win32 services */
|
|
if (!(lpServiceRecord->Status.dwServiceType & SERVICE_WIN32))
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Increment the tag counter and set it */
|
|
ServiceTag = ServiceTag % 0xFFFFFFFF + 1;
|
|
lpServiceRecord->dwServiceTag = ServiceTag;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScmCreateNewServiceRecord(LPCWSTR lpServiceName,
|
|
PSERVICE *lpServiceRecord,
|
|
DWORD dwServiceType,
|
|
DWORD dwStartType)
|
|
{
|
|
PSERVICE lpService = NULL;
|
|
|
|
DPRINT("Service: '%S'\n", lpServiceName);
|
|
|
|
/* Allocate service entry */
|
|
lpService = HeapAlloc(GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
FIELD_OFFSET(SERVICE, szServiceName[wcslen(lpServiceName) + 1]));
|
|
if (lpService == NULL)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
*lpServiceRecord = lpService;
|
|
|
|
/* Copy service name */
|
|
wcscpy(lpService->szServiceName, lpServiceName);
|
|
lpService->lpServiceName = lpService->szServiceName;
|
|
lpService->lpDisplayName = lpService->lpServiceName;
|
|
|
|
/* Set the start type */
|
|
lpService->dwStartType = dwStartType;
|
|
|
|
/* Set the resume count */
|
|
lpService->dwResumeCount = ResumeCount++;
|
|
|
|
/* Append service record */
|
|
InsertTailList(&ServiceListHead,
|
|
&lpService->ServiceListEntry);
|
|
|
|
/* Initialize the service status */
|
|
lpService->Status.dwServiceType = dwServiceType;
|
|
lpService->Status.dwCurrentState = SERVICE_STOPPED;
|
|
lpService->Status.dwControlsAccepted = 0;
|
|
lpService->Status.dwWin32ExitCode =
|
|
(dwStartType == SERVICE_DISABLED) ? ERROR_SERVICE_DISABLED : ERROR_SERVICE_NEVER_STARTED;
|
|
lpService->Status.dwServiceSpecificExitCode = 0;
|
|
lpService->Status.dwCheckPoint = 0;
|
|
lpService->Status.dwWaitHint =
|
|
(dwServiceType & SERVICE_DRIVER) ? 0 : 2000; /* 2 seconds */
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
ScmDeleteServiceRecord(PSERVICE lpService)
|
|
{
|
|
DPRINT("Deleting Service %S\n", lpService->lpServiceName);
|
|
|
|
/* Delete the display name */
|
|
if (lpService->lpDisplayName != NULL &&
|
|
lpService->lpDisplayName != lpService->lpServiceName)
|
|
HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName);
|
|
|
|
/* Dereference the service image */
|
|
if (lpService->lpImage)
|
|
{
|
|
lpService->lpImage->dwImageRunCount--;
|
|
|
|
if (lpService->lpImage->dwImageRunCount == 0)
|
|
{
|
|
ScmRemoveServiceImage(lpService->lpImage);
|
|
lpService->lpImage = NULL;
|
|
}
|
|
}
|
|
|
|
/* Decrement the group reference counter */
|
|
ScmSetServiceGroup(lpService, NULL);
|
|
|
|
/* Release the SecurityDescriptor */
|
|
if (lpService->pSecurityDescriptor != NULL)
|
|
HeapFree(GetProcessHeap(), 0, lpService->pSecurityDescriptor);
|
|
|
|
/* Remove the Service from the List */
|
|
RemoveEntryList(&lpService->ServiceListEntry);
|
|
|
|
DPRINT("Deleted Service %S\n", lpService->lpServiceName);
|
|
|
|
/* Delete the service record */
|
|
HeapFree(GetProcessHeap(), 0, lpService);
|
|
|
|
DPRINT("Done\n");
|
|
}
|
|
|
|
DWORD
|
|
Int_EnumDependentServicesW(HKEY hServicesKey,
|
|
PSERVICE lpService,
|
|
DWORD dwServiceState,
|
|
PSERVICE *lpServices,
|
|
LPDWORD pcbBytesNeeded,
|
|
LPDWORD lpServicesReturned);
|
|
|
|
DWORD ScmDeleteService(PSERVICE lpService)
|
|
{
|
|
DWORD dwError;
|
|
DWORD pcbBytesNeeded = 0;
|
|
DWORD dwServicesReturned = 0;
|
|
HKEY hServicesKey;
|
|
|
|
ASSERT(lpService->RefCount == 0);
|
|
|
|
/* Open the Services Reg key */
|
|
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Services",
|
|
0,
|
|
KEY_SET_VALUE | KEY_READ,
|
|
&hServicesKey);
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
DPRINT1("Failed to open services key\n");
|
|
return dwError;
|
|
}
|
|
|
|
/* Call the function with NULL, just to get bytes we need */
|
|
Int_EnumDependentServicesW(hServicesKey,
|
|
lpService,
|
|
SERVICE_ACTIVE,
|
|
NULL,
|
|
&pcbBytesNeeded,
|
|
&dwServicesReturned);
|
|
|
|
/* If pcbBytesNeeded returned a value then there are services running that are dependent on this service */
|
|
if (pcbBytesNeeded)
|
|
{
|
|
DPRINT1("Deletion failed due to running dependencies\n");
|
|
RegCloseKey(hServicesKey);
|
|
return ERROR_DEPENDENT_SERVICES_RUNNING;
|
|
}
|
|
|
|
/* There are no references and no running dependencies,
|
|
it is now safe to delete the service */
|
|
|
|
/* Delete the Service Key */
|
|
dwError = ScmDeleteRegKey(hServicesKey, lpService->lpServiceName);
|
|
|
|
RegCloseKey(hServicesKey);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
DPRINT1("Failed to delete the Service Registry key\n");
|
|
return dwError;
|
|
}
|
|
|
|
/* Delete the Service */
|
|
ScmDeleteServiceRecord(lpService);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* This function allows the caller to be sure that the service won't be freed unexpectedly.
|
|
* In order to be sure that lpService will be valid until the reference is added
|
|
* the caller needs to hold the database lock.
|
|
* A running service will keep a reference for the whole time it is not SERVICE_STOPPED.
|
|
* A service handle will also keep a reference to a service. Keeping a reference is
|
|
* really needed so that ScmControlService can be called without keeping the database locked.
|
|
* This means that eventually the correct order of operations to send a control message to
|
|
* a service looks like: lock, reference, unlock, send control, lock, dereference, unlock.
|
|
*/
|
|
DWORD
|
|
ScmReferenceService(PSERVICE lpService)
|
|
{
|
|
return InterlockedIncrement(&lpService->RefCount);
|
|
}
|
|
|
|
/* This function must be called with the database lock held exclusively as
|
|
it can end up deleting the service */
|
|
DWORD
|
|
ScmDereferenceService(PSERVICE lpService)
|
|
{
|
|
DWORD ref;
|
|
|
|
ASSERT(lpService->RefCount > 0);
|
|
|
|
ref = InterlockedDecrement(&lpService->RefCount);
|
|
|
|
if (ref == 0 && lpService->bDeleted &&
|
|
lpService->Status.dwCurrentState == SERVICE_STOPPED)
|
|
{
|
|
ScmDeleteService(lpService);
|
|
}
|
|
return ref;
|
|
}
|
|
|
|
static DWORD
|
|
CreateServiceListEntry(LPCWSTR lpServiceName,
|
|
HKEY hServiceKey)
|
|
{
|
|
PSERVICE lpService = NULL;
|
|
LPWSTR lpDisplayName = NULL;
|
|
LPWSTR lpGroup = NULL;
|
|
DWORD dwSize;
|
|
DWORD dwError;
|
|
DWORD dwServiceType;
|
|
DWORD dwStartType;
|
|
DWORD dwErrorControl;
|
|
DWORD dwTagId;
|
|
|
|
DPRINT("Service: '%S'\n", lpServiceName);
|
|
if (*lpServiceName == L'{')
|
|
return ERROR_SUCCESS;
|
|
|
|
dwSize = sizeof(DWORD);
|
|
dwError = RegQueryValueExW(hServiceKey,
|
|
L"Type",
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwServiceType,
|
|
&dwSize);
|
|
if (dwError != ERROR_SUCCESS)
|
|
return ERROR_SUCCESS;
|
|
|
|
if (((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_OWN_PROCESS) &&
|
|
((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_SHARE_PROCESS) &&
|
|
(dwServiceType != SERVICE_KERNEL_DRIVER) &&
|
|
(dwServiceType != SERVICE_FILE_SYSTEM_DRIVER))
|
|
return ERROR_SUCCESS;
|
|
|
|
DPRINT("Service type: %lx\n", dwServiceType);
|
|
|
|
dwSize = sizeof(DWORD);
|
|
dwError = RegQueryValueExW(hServiceKey,
|
|
L"Start",
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwStartType,
|
|
&dwSize);
|
|
if (dwError != ERROR_SUCCESS)
|
|
return ERROR_SUCCESS;
|
|
|
|
DPRINT("Start type: %lx\n", dwStartType);
|
|
|
|
dwSize = sizeof(DWORD);
|
|
dwError = RegQueryValueExW(hServiceKey,
|
|
L"ErrorControl",
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwErrorControl,
|
|
&dwSize);
|
|
if (dwError != ERROR_SUCCESS)
|
|
return ERROR_SUCCESS;
|
|
|
|
DPRINT("Error control: %lx\n", dwErrorControl);
|
|
|
|
dwError = RegQueryValueExW(hServiceKey,
|
|
L"Tag",
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwTagId,
|
|
&dwSize);
|
|
if (dwError != ERROR_SUCCESS)
|
|
dwTagId = 0;
|
|
|
|
DPRINT("Tag: %lx\n", dwTagId);
|
|
|
|
dwError = ScmReadString(hServiceKey,
|
|
L"Group",
|
|
&lpGroup);
|
|
if (dwError != ERROR_SUCCESS)
|
|
lpGroup = NULL;
|
|
|
|
DPRINT("Group: %S\n", lpGroup);
|
|
|
|
dwError = ScmReadString(hServiceKey,
|
|
L"DisplayName",
|
|
&lpDisplayName);
|
|
if (dwError != ERROR_SUCCESS)
|
|
lpDisplayName = NULL;
|
|
|
|
DPRINT("Display name: %S\n", lpDisplayName);
|
|
|
|
dwError = ScmCreateNewServiceRecord(lpServiceName,
|
|
&lpService,
|
|
dwServiceType,
|
|
dwStartType);
|
|
if (dwError != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
lpService->dwErrorControl = dwErrorControl;
|
|
lpService->dwTag = dwTagId;
|
|
|
|
if (lpGroup != NULL)
|
|
{
|
|
dwError = ScmSetServiceGroup(lpService, lpGroup);
|
|
if (dwError != ERROR_SUCCESS)
|
|
goto done;
|
|
}
|
|
|
|
if (lpDisplayName != NULL)
|
|
{
|
|
lpService->lpDisplayName = lpDisplayName;
|
|
lpDisplayName = NULL;
|
|
}
|
|
|
|
DPRINT("ServiceName: '%S'\n", lpService->lpServiceName);
|
|
if (lpService->lpGroup != NULL)
|
|
{
|
|
DPRINT("Group: '%S'\n", lpService->lpGroup->lpGroupName);
|
|
}
|
|
DPRINT("Start %lx Type %lx Tag %lx ErrorControl %lx\n",
|
|
lpService->dwStartType,
|
|
lpService->Status.dwServiceType,
|
|
lpService->dwTag,
|
|
lpService->dwErrorControl);
|
|
|
|
if (ScmIsDeleteFlagSet(hServiceKey))
|
|
lpService->bDeleted = TRUE;
|
|
else
|
|
ScmGenerateServiceTag(lpService);
|
|
|
|
if (lpService->Status.dwServiceType & SERVICE_WIN32)
|
|
{
|
|
dwError = ScmReadSecurityDescriptor(hServiceKey,
|
|
&lpService->pSecurityDescriptor);
|
|
if (dwError != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
/* Assing the default security descriptor if the security descriptor cannot be read */
|
|
if (lpService->pSecurityDescriptor == NULL)
|
|
{
|
|
DPRINT("No security descriptor found! Assign default security descriptor\n");
|
|
dwError = ScmCreateDefaultServiceSD(&lpService->pSecurityDescriptor);
|
|
if (dwError != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
dwError = ScmWriteSecurityDescriptor(hServiceKey,
|
|
lpService->pSecurityDescriptor);
|
|
if (dwError != ERROR_SUCCESS)
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (lpGroup != NULL)
|
|
HeapFree(GetProcessHeap(), 0, lpGroup);
|
|
|
|
if (lpDisplayName != NULL)
|
|
HeapFree(GetProcessHeap(), 0, lpDisplayName);
|
|
|
|
if (lpService != NULL)
|
|
{
|
|
ASSERT(lpService->lpImage == NULL);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
VOID
|
|
ScmDeleteMarkedServices(VOID)
|
|
{
|
|
PLIST_ENTRY ServiceEntry;
|
|
PSERVICE CurrentService;
|
|
HKEY hServicesKey;
|
|
DWORD dwError;
|
|
|
|
ServiceEntry = ServiceListHead.Flink;
|
|
while (ServiceEntry != &ServiceListHead)
|
|
{
|
|
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
|
|
|
|
ServiceEntry = ServiceEntry->Flink;
|
|
|
|
if (CurrentService->bDeleted != FALSE)
|
|
{
|
|
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Services",
|
|
0,
|
|
DELETE,
|
|
&hServicesKey);
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
dwError = ScmDeleteRegKey(hServicesKey, CurrentService->lpServiceName);
|
|
RegCloseKey(hServicesKey);
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
RemoveEntryList(&CurrentService->ServiceListEntry);
|
|
HeapFree(GetProcessHeap(), 0, CurrentService);
|
|
}
|
|
}
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
DPRINT1("Delete service failed: %S\n", CurrentService->lpServiceName);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static
|
|
VOID
|
|
ScmGetNoInteractiveServicesValue(VOID)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwKeySize;
|
|
LONG lError;
|
|
|
|
lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\CurrentControlSet\\Control\\Windows",
|
|
0,
|
|
KEY_READ,
|
|
&hKey);
|
|
if (lError == ERROR_SUCCESS)
|
|
{
|
|
dwKeySize = sizeof(NoInteractiveServices);
|
|
lError = RegQueryValueExW(hKey,
|
|
L"NoInteractiveServices",
|
|
0,
|
|
NULL,
|
|
(LPBYTE)&NoInteractiveServices,
|
|
&dwKeySize);
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScmCreateServiceDatabase(VOID)
|
|
{
|
|
WCHAR szSubKey[MAX_PATH];
|
|
HKEY hServicesKey;
|
|
HKEY hServiceKey;
|
|
DWORD dwSubKey;
|
|
DWORD dwSubKeyLength;
|
|
FILETIME ftLastChanged;
|
|
DWORD dwError;
|
|
|
|
DPRINT("ScmCreateServiceDatabase() called\n");
|
|
|
|
/* Retrieve the NoInteractiveServies value */
|
|
ScmGetNoInteractiveServicesValue();
|
|
|
|
/* Create the service group list */
|
|
dwError = ScmCreateGroupList();
|
|
if (dwError != ERROR_SUCCESS)
|
|
return dwError;
|
|
|
|
/* Initialize image and service lists */
|
|
InitializeListHead(&ImageListHead);
|
|
InitializeListHead(&ServiceListHead);
|
|
|
|
/* Initialize the database lock */
|
|
RtlInitializeResource(&DatabaseLock);
|
|
|
|
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Services",
|
|
0,
|
|
KEY_READ,
|
|
&hServicesKey);
|
|
if (dwError != ERROR_SUCCESS)
|
|
return dwError;
|
|
|
|
dwSubKey = 0;
|
|
for (;;)
|
|
{
|
|
dwSubKeyLength = MAX_PATH;
|
|
dwError = RegEnumKeyExW(hServicesKey,
|
|
dwSubKey,
|
|
szSubKey,
|
|
&dwSubKeyLength,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ftLastChanged);
|
|
if (dwError == ERROR_SUCCESS &&
|
|
szSubKey[0] != L'{')
|
|
{
|
|
DPRINT("SubKeyName: '%S'\n", szSubKey);
|
|
|
|
dwError = RegOpenKeyExW(hServicesKey,
|
|
szSubKey,
|
|
0,
|
|
KEY_READ,
|
|
&hServiceKey);
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
dwError = CreateServiceListEntry(szSubKey,
|
|
hServiceKey);
|
|
|
|
RegCloseKey(hServiceKey);
|
|
}
|
|
}
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
break;
|
|
|
|
dwSubKey++;
|
|
}
|
|
|
|
RegCloseKey(hServicesKey);
|
|
|
|
/* Wait for the LSA server */
|
|
ScmWaitForLsa();
|
|
|
|
/* Delete services that are marked for delete */
|
|
ScmDeleteMarkedServices();
|
|
|
|
DPRINT("ScmCreateServiceDatabase() done\n");
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
ScmShutdownServiceDatabase(VOID)
|
|
{
|
|
DPRINT("ScmShutdownServiceDatabase() called\n");
|
|
|
|
ScmDeleteMarkedServices();
|
|
RtlDeleteResource(&DatabaseLock);
|
|
|
|
DPRINT("ScmShutdownServiceDatabase() done\n");
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
ScmCheckDriver(PSERVICE Service)
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING DirName;
|
|
HANDLE DirHandle;
|
|
NTSTATUS Status;
|
|
POBJECT_DIRECTORY_INFORMATION DirInfo;
|
|
ULONG BufferLength;
|
|
ULONG DataLength;
|
|
ULONG Index;
|
|
|
|
DPRINT("ScmCheckDriver(%S) called\n", Service->lpServiceName);
|
|
|
|
if (Service->Status.dwServiceType == SERVICE_KERNEL_DRIVER)
|
|
{
|
|
RtlInitUnicodeString(&DirName, L"\\Driver");
|
|
}
|
|
else // if (Service->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER)
|
|
{
|
|
ASSERT(Service->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER);
|
|
RtlInitUnicodeString(&DirName, L"\\FileSystem");
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&DirName,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenDirectoryObject(&DirHandle,
|
|
DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
|
|
&ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
BufferLength = sizeof(OBJECT_DIRECTORY_INFORMATION) +
|
|
2 * MAX_PATH * sizeof(WCHAR);
|
|
DirInfo = HeapAlloc(GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
BufferLength);
|
|
|
|
Index = 0;
|
|
while (TRUE)
|
|
{
|
|
Status = NtQueryDirectoryObject(DirHandle,
|
|
DirInfo,
|
|
BufferLength,
|
|
TRUE,
|
|
FALSE,
|
|
&Index,
|
|
&DataLength);
|
|
if (Status == STATUS_NO_MORE_ENTRIES)
|
|
{
|
|
/* FIXME: Add current service to 'failed service' list */
|
|
DPRINT("Service '%S' failed\n", Service->lpServiceName);
|
|
break;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
DPRINT("Comparing: '%S' '%wZ'\n", Service->lpServiceName, &DirInfo->Name);
|
|
|
|
if (_wcsicmp(Service->lpServiceName, DirInfo->Name.Buffer) == 0)
|
|
{
|
|
DPRINT("Found: '%S' '%wZ'\n",
|
|
Service->lpServiceName, &DirInfo->Name);
|
|
|
|
/* Mark service as 'running' */
|
|
Service->Status.dwCurrentState = SERVICE_RUNNING;
|
|
Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
|
Service->Status.dwWin32ExitCode = ERROR_SUCCESS;
|
|
Service->Status.dwServiceSpecificExitCode = 0;
|
|
Service->Status.dwCheckPoint = 0;
|
|
Service->Status.dwWaitHint = 0;
|
|
|
|
/* Mark the service group as 'running' */
|
|
if (Service->lpGroup != NULL)
|
|
{
|
|
Service->lpGroup->ServicesRunning = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(),
|
|
0,
|
|
DirInfo);
|
|
NtClose(DirHandle);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
ScmGetBootAndSystemDriverState(VOID)
|
|
{
|
|
PLIST_ENTRY ServiceEntry;
|
|
PSERVICE CurrentService;
|
|
|
|
DPRINT("ScmGetBootAndSystemDriverState() called\n");
|
|
|
|
ServiceEntry = ServiceListHead.Flink;
|
|
while (ServiceEntry != &ServiceListHead)
|
|
{
|
|
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
|
|
|
|
if (CurrentService->dwStartType == SERVICE_BOOT_START ||
|
|
CurrentService->dwStartType == SERVICE_SYSTEM_START)
|
|
{
|
|
/* Check driver */
|
|
DPRINT(" Checking service: %S\n", CurrentService->lpServiceName);
|
|
|
|
ScmCheckDriver(CurrentService);
|
|
}
|
|
|
|
ServiceEntry = ServiceEntry->Flink;
|
|
}
|
|
|
|
DPRINT("ScmGetBootAndSystemDriverState() done\n");
|
|
}
|
|
|
|
|
|
/*
|
|
* ScmControlService must never be called with the database lock being held.
|
|
* The service passed must always be referenced instead.
|
|
*/
|
|
DWORD
|
|
ScmControlServiceEx(
|
|
_In_ HANDLE hControlPipe,
|
|
_In_ PCWSTR pServiceName,
|
|
_In_ DWORD dwControl,
|
|
_In_ SERVICE_STATUS_HANDLE hServiceStatus,
|
|
_In_opt_ DWORD dwServiceTag,
|
|
_In_opt_ DWORD argc,
|
|
_In_reads_opt_(argc) const PCWSTR* argv)
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
BOOL bResult;
|
|
PSCM_CONTROL_PACKET ControlPacket;
|
|
SCM_REPLY_PACKET ReplyPacket;
|
|
DWORD PacketSize;
|
|
DWORD i;
|
|
PWSTR Ptr;
|
|
DWORD dwReadCount = 0;
|
|
OVERLAPPED Overlapped = {0};
|
|
|
|
DPRINT("ScmControlService(%S, %d) called\n", pServiceName, dwControl);
|
|
|
|
/* Calculate the total size of the control packet:
|
|
* initial structure, the start command line, and the argument vector */
|
|
PacketSize = sizeof(SCM_CONTROL_PACKET);
|
|
PacketSize += (DWORD)((wcslen(pServiceName) + 1) * sizeof(WCHAR));
|
|
|
|
/*
|
|
* Calculate the required packet size for the start argument vector 'argv',
|
|
* composed of the pointer offsets list, followed by UNICODE strings.
|
|
* The strings are stored successively after the offsets vector, 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, 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));
|
|
}
|
|
}
|
|
|
|
/* Allocate the control packet */
|
|
ControlPacket = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, PacketSize);
|
|
if (!ControlPacket)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
ControlPacket->dwSize = PacketSize;
|
|
ControlPacket->dwControl = dwControl;
|
|
ControlPacket->hServiceStatus = hServiceStatus;
|
|
ControlPacket->dwServiceTag = dwServiceTag;
|
|
|
|
/* Copy the start command line */
|
|
ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
|
|
Ptr = (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwServiceNameOffset);
|
|
wcscpy(Ptr, pServiceName);
|
|
|
|
ControlPacket->dwArgumentsCount = 0;
|
|
ControlPacket->dwArgumentsOffset = 0;
|
|
|
|
/* Copy the argument vector */
|
|
if (argc > 0 && argv != NULL)
|
|
{
|
|
PWSTR *pOffPtr, pArgPtr;
|
|
|
|
Ptr += wcslen(pServiceName) + 1;
|
|
pOffPtr = (PWSTR*)ALIGN_UP_POINTER(Ptr, PWSTR);
|
|
pArgPtr = (PWSTR)((ULONG_PTR)pOffPtr + argc * sizeof(PWSTR));
|
|
|
|
ControlPacket->dwArgumentsCount = argc;
|
|
ControlPacket->dwArgumentsOffset = (DWORD)((ULONG_PTR)pOffPtr - (ULONG_PTR)ControlPacket);
|
|
|
|
DPRINT("dwArgumentsCount: %lu\n", ControlPacket->dwArgumentsCount);
|
|
DPRINT("dwArgumentsOffset: %lu\n", ControlPacket->dwArgumentsOffset);
|
|
|
|
for (i = 0; i < argc; i++)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* Acquire the service control critical section, to synchronize requests */
|
|
EnterCriticalSection(&ControlServiceCriticalSection);
|
|
|
|
bResult = TransactNamedPipe(hControlPipe,
|
|
ControlPacket,
|
|
PacketSize,
|
|
&ReplyPacket,
|
|
sizeof(ReplyPacket),
|
|
&dwReadCount,
|
|
&Overlapped);
|
|
if (!bResult)
|
|
{
|
|
/* Fail for any error other than pending IO */
|
|
dwError = GetLastError();
|
|
if (dwError != ERROR_IO_PENDING)
|
|
{
|
|
DPRINT1("TransactNamedPipe(%S, %d) failed (Error %lu)\n", pServiceName, dwControl, dwError);
|
|
goto Done;
|
|
}
|
|
|
|
DPRINT("TransactNamedPipe(%S, %d) returned ERROR_IO_PENDING\n", pServiceName, dwControl);
|
|
|
|
dwError = WaitForSingleObject(hControlPipe, PipeTimeout);
|
|
DPRINT("WaitForSingleObject(%S, %d) returned %lu\n", pServiceName, dwControl, dwError);
|
|
|
|
if (dwError == WAIT_TIMEOUT)
|
|
{
|
|
DPRINT1("WaitForSingleObject(%S, %d) timed out\n", pServiceName, dwControl);
|
|
bResult = CancelIo(hControlPipe);
|
|
if (!bResult)
|
|
DPRINT1("CancelIo(%S, %d) failed (Error %lu)\n", pServiceName, dwControl, GetLastError());
|
|
|
|
dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
|
|
}
|
|
else if (dwError == WAIT_OBJECT_0)
|
|
{
|
|
bResult = GetOverlappedResult(hControlPipe,
|
|
&Overlapped,
|
|
&dwReadCount,
|
|
TRUE);
|
|
if (!bResult)
|
|
{
|
|
dwError = GetLastError();
|
|
DPRINT1("GetOverlappedResult(%S, %d) failed (Error %lu)\n", pServiceName, dwControl, dwError);
|
|
}
|
|
}
|
|
}
|
|
|
|
Done:
|
|
/* Release the service control critical section */
|
|
LeaveCriticalSection(&ControlServiceCriticalSection);
|
|
|
|
/* Free the control packet */
|
|
HeapFree(GetProcessHeap(), 0, ControlPacket);
|
|
|
|
if (dwReadCount == sizeof(ReplyPacket))
|
|
dwError = ReplyPacket.dwError;
|
|
|
|
DPRINT("ScmControlService(%S, %d) done (Error %lu)\n", pServiceName, dwControl, dwError);
|
|
return dwError;
|
|
}
|
|
|
|
DWORD
|
|
ScmControlService(
|
|
_In_ HANDLE hControlPipe,
|
|
_In_ PCWSTR pServiceName,
|
|
_In_ DWORD dwControl,
|
|
_In_ SERVICE_STATUS_HANDLE hServiceStatus)
|
|
{
|
|
return ScmControlServiceEx(hControlPipe,
|
|
pServiceName,
|
|
dwControl,
|
|
hServiceStatus,
|
|
0, 0, NULL);
|
|
}
|
|
|
|
|
|
static DWORD
|
|
ScmWaitForServiceConnect(PSERVICE Service)
|
|
{
|
|
DWORD dwRead = 0;
|
|
DWORD dwProcessId = 0;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
BOOL bResult;
|
|
OVERLAPPED Overlapped = {0};
|
|
#if 0
|
|
LPCWSTR lpLogStrings[3];
|
|
WCHAR szBuffer1[20];
|
|
WCHAR szBuffer2[20];
|
|
#endif
|
|
|
|
DPRINT("ScmWaitForServiceConnect()\n");
|
|
|
|
bResult = ConnectNamedPipe(Service->lpImage->hControlPipe,
|
|
&Overlapped);
|
|
if (bResult == FALSE)
|
|
{
|
|
DPRINT("ConnectNamedPipe() returned FALSE\n");
|
|
|
|
dwError = GetLastError();
|
|
if (dwError == ERROR_IO_PENDING)
|
|
{
|
|
DPRINT("dwError: ERROR_IO_PENDING\n");
|
|
|
|
dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
|
|
PipeTimeout);
|
|
DPRINT("WaitForSingleObject() returned %lu\n", dwError);
|
|
|
|
if (dwError == WAIT_TIMEOUT)
|
|
{
|
|
DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
|
|
|
|
bResult = CancelIo(Service->lpImage->hControlPipe);
|
|
if (bResult == FALSE)
|
|
{
|
|
DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
|
|
}
|
|
|
|
#if 0
|
|
_ultow(PipeTimeout, szBuffer1, 10);
|
|
lpLogStrings[0] = Service->lpDisplayName;
|
|
lpLogStrings[1] = szBuffer1;
|
|
|
|
ScmLogEvent(EVENT_CONNECTION_TIMEOUT,
|
|
EVENTLOG_ERROR_TYPE,
|
|
2,
|
|
lpLogStrings);
|
|
#endif
|
|
DPRINT1("Log EVENT_CONNECTION_TIMEOUT by %S\n", Service->lpDisplayName);
|
|
|
|
return ERROR_SERVICE_REQUEST_TIMEOUT;
|
|
}
|
|
else if (dwError == WAIT_OBJECT_0)
|
|
{
|
|
bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
|
|
&Overlapped,
|
|
&dwRead,
|
|
TRUE);
|
|
if (bResult == FALSE)
|
|
{
|
|
dwError = GetLastError();
|
|
DPRINT1("GetOverlappedResult failed (Error %lu)\n", dwError);
|
|
|
|
return dwError;
|
|
}
|
|
}
|
|
}
|
|
else if (dwError != ERROR_PIPE_CONNECTED)
|
|
{
|
|
DPRINT1("ConnectNamedPipe failed (Error %lu)\n", dwError);
|
|
return dwError;
|
|
}
|
|
}
|
|
|
|
DPRINT("Control pipe connected\n");
|
|
|
|
Overlapped.hEvent = NULL;
|
|
|
|
/* Read the process id from pipe */
|
|
bResult = ReadFile(Service->lpImage->hControlPipe,
|
|
(LPVOID)&dwProcessId,
|
|
sizeof(DWORD),
|
|
&dwRead,
|
|
&Overlapped);
|
|
if (bResult == FALSE)
|
|
{
|
|
DPRINT("ReadFile() returned FALSE\n");
|
|
|
|
dwError = GetLastError();
|
|
if (dwError == ERROR_IO_PENDING)
|
|
{
|
|
DPRINT("dwError: ERROR_IO_PENDING\n");
|
|
|
|
dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
|
|
PipeTimeout);
|
|
if (dwError == WAIT_TIMEOUT)
|
|
{
|
|
DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
|
|
|
|
bResult = CancelIo(Service->lpImage->hControlPipe);
|
|
if (bResult == FALSE)
|
|
{
|
|
DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
|
|
}
|
|
|
|
#if 0
|
|
_ultow(PipeTimeout, szBuffer1, 10);
|
|
lpLogStrings[0] = szBuffer1;
|
|
|
|
ScmLogEvent(EVENT_READFILE_TIMEOUT,
|
|
EVENTLOG_ERROR_TYPE,
|
|
1,
|
|
lpLogStrings);
|
|
#endif
|
|
DPRINT1("Log EVENT_READFILE_TIMEOUT by %S\n", Service->lpDisplayName);
|
|
|
|
return ERROR_SERVICE_REQUEST_TIMEOUT;
|
|
}
|
|
else if (dwError == WAIT_OBJECT_0)
|
|
{
|
|
DPRINT("WaitForSingleObject() returned WAIT_OBJECT_0\n");
|
|
|
|
DPRINT("Process Id: %lu\n", dwProcessId);
|
|
|
|
bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
|
|
&Overlapped,
|
|
&dwRead,
|
|
TRUE);
|
|
if (bResult == FALSE)
|
|
{
|
|
dwError = GetLastError();
|
|
DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
|
|
|
|
return dwError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
|
|
return dwError;
|
|
}
|
|
}
|
|
|
|
if ((ScmIsSecurityService(Service->lpImage) == FALSE)&&
|
|
(dwProcessId != Service->lpImage->dwProcessId))
|
|
{
|
|
#if 0
|
|
_ultow(Service->lpImage->dwProcessId, szBuffer1, 10);
|
|
_ultow(dwProcessId, szBuffer2, 10);
|
|
|
|
lpLogStrings[0] = Service->lpDisplayName;
|
|
lpLogStrings[1] = szBuffer1;
|
|
lpLogStrings[2] = szBuffer2;
|
|
|
|
ScmLogEvent(EVENT_SERVICE_DIFFERENT_PID_CONNECTED,
|
|
EVENTLOG_WARNING_TYPE,
|
|
3,
|
|
lpLogStrings);
|
|
#endif
|
|
|
|
DPRINT1("Log EVENT_SERVICE_DIFFERENT_PID_CONNECTED by %S\n", Service->lpDisplayName);
|
|
}
|
|
|
|
DPRINT("ScmWaitForServiceConnect() done\n");
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
static DWORD
|
|
ScmStartUserModeService(PSERVICE Service,
|
|
DWORD argc,
|
|
const PCWSTR* argv)
|
|
{
|
|
PROCESS_INFORMATION ProcessInformation;
|
|
STARTUPINFOW StartupInfo;
|
|
LPVOID lpEnvironment;
|
|
BOOL Result;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
DPRINT("ScmStartUserModeService(%p)\n", Service);
|
|
|
|
/* If the image is already running, just send a start command */
|
|
if (Service->lpImage->dwImageRunCount > 1)
|
|
goto Quit;
|
|
|
|
/* Otherwise start its process */
|
|
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
|
|
StartupInfo.cb = sizeof(StartupInfo);
|
|
ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
|
|
|
|
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 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 current environment\n",
|
|
GetLastError(), Service->lpServiceName);
|
|
lpEnvironment = NULL;
|
|
}
|
|
|
|
/* Use the interactive desktop if the service is interactive */
|
|
if ((NoInteractiveServices == 0) &&
|
|
(Service->Status.dwServiceType & SERVICE_INTERACTIVE_PROCESS))
|
|
{
|
|
StartupInfo.dwFlags |= STARTF_INHERITDESKTOP;
|
|
StartupInfo.lpDesktop = L"WinSta0\\Default";
|
|
}
|
|
|
|
if (!ScmIsSecurityService(Service->lpImage))
|
|
{
|
|
Result = CreateProcessW(NULL,
|
|
Service->lpImage->pszImagePath,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS | CREATE_SUSPENDED,
|
|
lpEnvironment,
|
|
NULL,
|
|
&StartupInfo,
|
|
&ProcessInformation);
|
|
if (!Result)
|
|
dwError = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
Result = TRUE;
|
|
dwError = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (lpEnvironment)
|
|
DestroyEnvironmentBlock(lpEnvironment);
|
|
|
|
if (!Result)
|
|
{
|
|
DPRINT1("Starting '%S' failed with error %d\n",
|
|
Service->lpServiceName, dwError);
|
|
return dwError;
|
|
}
|
|
|
|
DPRINT("Process Id: %lu Handle %p\n",
|
|
ProcessInformation.dwProcessId,
|
|
ProcessInformation.hProcess);
|
|
DPRINT("Thread Id: %lu Handle %p\n",
|
|
ProcessInformation.dwThreadId,
|
|
ProcessInformation.hThread);
|
|
|
|
/* Get the process handle and ID */
|
|
Service->lpImage->hProcess = ProcessInformation.hProcess;
|
|
Service->lpImage->dwProcessId = ProcessInformation.dwProcessId;
|
|
|
|
/* Resume the main thread and close its handle */
|
|
ResumeThread(ProcessInformation.hThread);
|
|
CloseHandle(ProcessInformation.hThread);
|
|
|
|
/* Connect control pipe */
|
|
dwError = ScmWaitForServiceConnect(Service);
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
|
|
Service->lpImage->dwProcessId = 0;
|
|
return dwError;
|
|
}
|
|
|
|
Quit:
|
|
/* Send the start command and return */
|
|
return ScmControlServiceEx(Service->lpImage->hControlPipe,
|
|
Service->lpServiceName,
|
|
(Service->Status.dwServiceType & SERVICE_WIN32_OWN_PROCESS)
|
|
? SERVICE_CONTROL_START_OWN : SERVICE_CONTROL_START_SHARE,
|
|
(SERVICE_STATUS_HANDLE)Service,
|
|
Service->dwServiceTag,
|
|
argc, argv);
|
|
}
|
|
|
|
|
|
static DWORD
|
|
ScmLoadService(PSERVICE Service,
|
|
DWORD argc,
|
|
const PCWSTR* argv)
|
|
{
|
|
PSERVICE_GROUP Group = Service->lpGroup;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
LPCWSTR lpLogStrings[2];
|
|
WCHAR szLogBuffer[80];
|
|
|
|
DPRINT("ScmLoadService() called\n");
|
|
DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
|
|
|
|
if (Service->Status.dwCurrentState != SERVICE_STOPPED)
|
|
{
|
|
DPRINT("Service %S is already running\n", Service->lpServiceName);
|
|
return ERROR_SERVICE_ALREADY_RUNNING;
|
|
}
|
|
|
|
DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
|
|
|
|
if (Service->Status.dwServiceType & SERVICE_DRIVER)
|
|
{
|
|
/* Start the driver */
|
|
dwError = ScmStartDriver(Service);
|
|
}
|
|
else // if (Service->Status.dwServiceType & (SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS))
|
|
{
|
|
/* Start user-mode service */
|
|
dwError = ScmCreateOrReferenceServiceImage(Service);
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
dwError = ScmStartUserModeService(Service, argc, argv);
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
Service->Status.dwCurrentState = SERVICE_START_PENDING;
|
|
Service->Status.dwControlsAccepted = 0;
|
|
ScmReferenceService(Service);
|
|
}
|
|
else
|
|
{
|
|
Service->lpImage->dwImageRunCount--;
|
|
if (Service->lpImage->dwImageRunCount == 0)
|
|
{
|
|
ScmRemoveServiceImage(Service->lpImage);
|
|
Service->lpImage = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DPRINT("ScmLoadService() done (Error %lu)\n", dwError);
|
|
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
if (Group != NULL)
|
|
{
|
|
Group->ServicesRunning = TRUE;
|
|
}
|
|
|
|
/* Log a successful service start */
|
|
LoadStringW(GetModuleHandle(NULL), IDS_SERVICE_START, szLogBuffer, 80);
|
|
lpLogStrings[0] = Service->lpDisplayName;
|
|
lpLogStrings[1] = szLogBuffer;
|
|
|
|
ScmLogEvent(EVENT_SERVICE_CONTROL_SUCCESS,
|
|
EVENTLOG_INFORMATION_TYPE,
|
|
2,
|
|
lpLogStrings);
|
|
}
|
|
else
|
|
{
|
|
if (Service->dwErrorControl != SERVICE_ERROR_IGNORE)
|
|
{
|
|
/* Log a failed service start */
|
|
StringCchPrintfW(szLogBuffer, ARRAYSIZE(szLogBuffer),
|
|
L"%lu", dwError);
|
|
lpLogStrings[0] = Service->lpServiceName;
|
|
lpLogStrings[1] = szLogBuffer;
|
|
ScmLogEvent(EVENT_SERVICE_START_FAILED,
|
|
EVENTLOG_ERROR_TYPE,
|
|
2,
|
|
lpLogStrings);
|
|
}
|
|
|
|
#if 0
|
|
switch (Service->dwErrorControl)
|
|
{
|
|
case SERVICE_ERROR_SEVERE:
|
|
if (IsLastKnownGood == FALSE)
|
|
{
|
|
/* FIXME: Boot last known good configuration */
|
|
}
|
|
break;
|
|
|
|
case SERVICE_ERROR_CRITICAL:
|
|
if (IsLastKnownGood == FALSE)
|
|
{
|
|
/* FIXME: Boot last known good configuration */
|
|
}
|
|
else
|
|
{
|
|
/* FIXME: BSOD! */
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScmStartService(PSERVICE Service,
|
|
DWORD argc,
|
|
const PCWSTR* argv)
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
SC_RPC_LOCK Lock = NULL;
|
|
|
|
DPRINT("ScmStartService() called\n");
|
|
DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
|
|
|
|
/* Acquire the service control critical section, to synchronize starts */
|
|
EnterCriticalSection(&ControlServiceCriticalSection);
|
|
|
|
/*
|
|
* Acquire the user service start lock while the service is starting, if
|
|
* needed (i.e. if we are not starting it during the initialization phase).
|
|
* If we don't success, bail out.
|
|
*/
|
|
if (!ScmInitialize)
|
|
{
|
|
dwError = ScmAcquireServiceStartLock(TRUE, &Lock);
|
|
if (dwError != ERROR_SUCCESS)
|
|
goto done;
|
|
}
|
|
|
|
/* Really start the service */
|
|
dwError = ScmLoadService(Service, argc, argv);
|
|
|
|
/* Release the service start lock, if needed, and the critical section */
|
|
if (Lock) ScmReleaseServiceStartLock(&Lock);
|
|
|
|
done:
|
|
LeaveCriticalSection(&ControlServiceCriticalSection);
|
|
|
|
DPRINT("ScmStartService() done (Error %lu)\n", dwError);
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
VOID
|
|
ScmAutoStartServices(VOID)
|
|
{
|
|
DWORD dwError;
|
|
PLIST_ENTRY GroupEntry;
|
|
PLIST_ENTRY ServiceEntry;
|
|
PSERVICE_GROUP CurrentGroup;
|
|
PSERVICE CurrentService;
|
|
WCHAR szSafeBootServicePath[MAX_PATH];
|
|
DWORD SafeBootEnabled;
|
|
HKEY hKey;
|
|
DWORD dwKeySize;
|
|
ULONG i;
|
|
|
|
/*
|
|
* This function MUST be called ONLY at initialization time.
|
|
* Therefore, no need to acquire the user service start lock.
|
|
*/
|
|
ASSERT(ScmInitialize);
|
|
|
|
/* Retrieve the SafeBoot parameter */
|
|
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Option",
|
|
0,
|
|
KEY_READ,
|
|
&hKey);
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
dwKeySize = sizeof(SafeBootEnabled);
|
|
dwError = RegQueryValueExW(hKey,
|
|
L"OptionValue",
|
|
0,
|
|
NULL,
|
|
(LPBYTE)&SafeBootEnabled,
|
|
&dwKeySize);
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
/* Default to Normal boot if the value doesn't exist */
|
|
if (dwError != ERROR_SUCCESS)
|
|
SafeBootEnabled = 0;
|
|
|
|
/* Acquire the service control critical section, to synchronize starts */
|
|
EnterCriticalSection(&ControlServiceCriticalSection);
|
|
|
|
/* Clear 'ServiceVisited' flag (or set if not to start in Safe Mode) */
|
|
ServiceEntry = ServiceListHead.Flink;
|
|
while (ServiceEntry != &ServiceListHead)
|
|
{
|
|
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
|
|
|
|
/* Build the safe boot path */
|
|
StringCchCopyW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
|
|
L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot");
|
|
|
|
switch (SafeBootEnabled)
|
|
{
|
|
/* NOTE: Assumes MINIMAL (1) and DSREPAIR (3) load same items */
|
|
case 1:
|
|
case 3:
|
|
StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
|
|
L"\\Minimal\\");
|
|
break;
|
|
|
|
case 2:
|
|
StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
|
|
L"\\Network\\");
|
|
break;
|
|
}
|
|
|
|
if (SafeBootEnabled != 0)
|
|
{
|
|
/* If key does not exist then do not assume safe mode */
|
|
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
szSafeBootServicePath,
|
|
0,
|
|
KEY_READ,
|
|
&hKey);
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hKey);
|
|
|
|
/* Finish Safe Boot path off */
|
|
StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
|
|
CurrentService->lpServiceName);
|
|
|
|
/* Check that the key is in the Safe Boot path */
|
|
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
szSafeBootServicePath,
|
|
0,
|
|
KEY_READ,
|
|
&hKey);
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
/* Mark service as visited so it is not auto-started */
|
|
CurrentService->ServiceVisited = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* Must be auto-started in safe mode - mark as unvisited */
|
|
RegCloseKey(hKey);
|
|
CurrentService->ServiceVisited = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("WARNING: Could not open the associated Safe Boot key\n");
|
|
CurrentService->ServiceVisited = FALSE;
|
|
}
|
|
}
|
|
|
|
ServiceEntry = ServiceEntry->Flink;
|
|
}
|
|
|
|
/* Start all services which are members of an existing group */
|
|
GroupEntry = GroupListHead.Flink;
|
|
while (GroupEntry != &GroupListHead)
|
|
{
|
|
CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
|
|
|
|
DPRINT("Group '%S'\n", CurrentGroup->lpGroupName);
|
|
|
|
/* Start all services witch have a valid tag */
|
|
for (i = 0; i < CurrentGroup->TagCount; i++)
|
|
{
|
|
ServiceEntry = ServiceListHead.Flink;
|
|
while (ServiceEntry != &ServiceListHead)
|
|
{
|
|
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
|
|
|
|
if ((CurrentService->lpGroup == CurrentGroup) &&
|
|
(CurrentService->dwStartType == SERVICE_AUTO_START) &&
|
|
(CurrentService->ServiceVisited == FALSE) &&
|
|
(CurrentService->dwTag == CurrentGroup->TagArray[i]))
|
|
{
|
|
CurrentService->ServiceVisited = TRUE;
|
|
ScmLoadService(CurrentService, 0, NULL);
|
|
}
|
|
|
|
ServiceEntry = ServiceEntry->Flink;
|
|
}
|
|
}
|
|
|
|
/* Start all services which have an invalid tag or which do not have a tag */
|
|
ServiceEntry = ServiceListHead.Flink;
|
|
while (ServiceEntry != &ServiceListHead)
|
|
{
|
|
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
|
|
|
|
if ((CurrentService->lpGroup == CurrentGroup) &&
|
|
(CurrentService->dwStartType == SERVICE_AUTO_START) &&
|
|
(CurrentService->ServiceVisited == FALSE))
|
|
{
|
|
CurrentService->ServiceVisited = TRUE;
|
|
ScmLoadService(CurrentService, 0, NULL);
|
|
}
|
|
|
|
ServiceEntry = ServiceEntry->Flink;
|
|
}
|
|
|
|
GroupEntry = GroupEntry->Flink;
|
|
}
|
|
|
|
/* Start all services which are members of any non-existing group */
|
|
ServiceEntry = ServiceListHead.Flink;
|
|
while (ServiceEntry != &ServiceListHead)
|
|
{
|
|
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
|
|
|
|
if ((CurrentService->lpGroup != NULL) &&
|
|
(CurrentService->dwStartType == SERVICE_AUTO_START) &&
|
|
(CurrentService->ServiceVisited == FALSE))
|
|
{
|
|
CurrentService->ServiceVisited = TRUE;
|
|
ScmLoadService(CurrentService, 0, NULL);
|
|
}
|
|
|
|
ServiceEntry = ServiceEntry->Flink;
|
|
}
|
|
|
|
/* Start all services which are not a member of any group */
|
|
ServiceEntry = ServiceListHead.Flink;
|
|
while (ServiceEntry != &ServiceListHead)
|
|
{
|
|
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
|
|
|
|
if ((CurrentService->lpGroup == NULL) &&
|
|
(CurrentService->dwStartType == SERVICE_AUTO_START) &&
|
|
(CurrentService->ServiceVisited == FALSE))
|
|
{
|
|
CurrentService->ServiceVisited = TRUE;
|
|
ScmLoadService(CurrentService, 0, NULL);
|
|
}
|
|
|
|
ServiceEntry = ServiceEntry->Flink;
|
|
}
|
|
|
|
/* Clear 'ServiceVisited' flag again */
|
|
ServiceEntry = ServiceListHead.Flink;
|
|
while (ServiceEntry != &ServiceListHead)
|
|
{
|
|
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
|
|
CurrentService->ServiceVisited = FALSE;
|
|
ServiceEntry = ServiceEntry->Flink;
|
|
}
|
|
|
|
/* Release the critical section */
|
|
LeaveCriticalSection(&ControlServiceCriticalSection);
|
|
}
|
|
|
|
|
|
VOID
|
|
ScmAutoShutdownServices(VOID)
|
|
{
|
|
PLIST_ENTRY ServiceEntry;
|
|
PSERVICE CurrentService;
|
|
|
|
DPRINT("ScmAutoShutdownServices() called\n");
|
|
|
|
/* Lock the service database exclusively */
|
|
ScmLockDatabaseExclusive();
|
|
|
|
ServiceEntry = ServiceListHead.Flink;
|
|
while (ServiceEntry != &ServiceListHead)
|
|
{
|
|
CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
|
|
|
|
if ((CurrentService->Status.dwControlsAccepted & SERVICE_ACCEPT_SHUTDOWN) &&
|
|
(CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
|
|
CurrentService->Status.dwCurrentState == SERVICE_START_PENDING))
|
|
{
|
|
/* Send the shutdown notification */
|
|
DPRINT("Shutdown service: %S\n", CurrentService->lpServiceName);
|
|
ScmControlService(CurrentService->lpImage->hControlPipe,
|
|
CurrentService->lpServiceName,
|
|
SERVICE_CONTROL_SHUTDOWN,
|
|
(SERVICE_STATUS_HANDLE)CurrentService);
|
|
}
|
|
|
|
ServiceEntry = ServiceEntry->Flink;
|
|
}
|
|
|
|
/* Unlock the service database */
|
|
ScmUnlockDatabase();
|
|
|
|
DPRINT("ScmAutoShutdownServices() done\n");
|
|
}
|
|
|
|
|
|
BOOL
|
|
ScmLockDatabaseExclusive(VOID)
|
|
{
|
|
return RtlAcquireResourceExclusive(&DatabaseLock, TRUE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
ScmLockDatabaseShared(VOID)
|
|
{
|
|
return RtlAcquireResourceShared(&DatabaseLock, TRUE);
|
|
}
|
|
|
|
|
|
VOID
|
|
ScmUnlockDatabase(VOID)
|
|
{
|
|
RtlReleaseResource(&DatabaseLock);
|
|
}
|
|
|
|
|
|
VOID
|
|
ScmInitNamedPipeCriticalSection(VOID)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwKeySize;
|
|
DWORD dwError;
|
|
|
|
InitializeCriticalSection(&ControlServiceCriticalSection);
|
|
|
|
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\CurrentControlSet\\Control",
|
|
0,
|
|
KEY_READ,
|
|
&hKey);
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
dwKeySize = sizeof(PipeTimeout);
|
|
RegQueryValueExW(hKey,
|
|
L"ServicesPipeTimeout",
|
|
0,
|
|
NULL,
|
|
(LPBYTE)&PipeTimeout,
|
|
&dwKeySize);
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
ScmDeleteNamedPipeCriticalSection(VOID)
|
|
{
|
|
DeleteCriticalSection(&ControlServiceCriticalSection);
|
|
}
|
|
|
|
/* EOF */
|