xref: /reactos/base/system/services/database.c (revision 90831e74)
1 /*
2  * PROJECT:     ReactOS Service Control Manager
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        base/system/services/database.c
5  * PURPOSE:     Database control interface
6  * COPYRIGHT:   Copyright 2002-2006 Eric Kohl
7  *              Copyright 2006 Hervé Poussineau <hpoussin@reactos.org>
8  *              Copyright 2007 Ged Murphy <gedmurphy@reactos.org>
9  *                             Gregor Brunmar <gregor.brunmar@home.se>
10  *
11  */
12 
13 /* INCLUDES *****************************************************************/
14 
15 #include "services.h"
16 
17 #include <userenv.h>
18 #include <strsafe.h>
19 
20 #include <reactos/undocuser.h>
21 
22 #define NDEBUG
23 #include <debug.h>
24 
25 
26 /* GLOBALS *******************************************************************/
27 
28 LIST_ENTRY ImageListHead;
29 LIST_ENTRY ServiceListHead;
30 
31 static RTL_RESOURCE DatabaseLock;
32 static DWORD ResumeCount = 1;
33 static DWORD NoInteractiveServices = 0;
34 static DWORD ServiceTag = 0;
35 
36 /* The critical section synchronizes service control requests */
37 static CRITICAL_SECTION ControlServiceCriticalSection;
38 static DWORD PipeTimeout = 30000; /* 30 Seconds */
39 
40 
41 /* FUNCTIONS *****************************************************************/
42 
43 static
44 BOOL
ScmIsSecurityService(_In_ PSERVICE_IMAGE pServiceImage)45 ScmIsSecurityService(
46     _In_ PSERVICE_IMAGE pServiceImage)
47 {
48     return (wcsstr(pServiceImage->pszImagePath, L"\\system32\\lsass.exe") != NULL);
49 }
50 
51 
52 static DWORD
ScmCreateNewControlPipe(_In_ PSERVICE_IMAGE pServiceImage,_In_ BOOL bSecurityServiceProcess)53 ScmCreateNewControlPipe(
54     _In_ PSERVICE_IMAGE pServiceImage,
55     _In_ BOOL bSecurityServiceProcess)
56 {
57     WCHAR szControlPipeName[MAX_PATH + 1];
58     SECURITY_ATTRIBUTES SecurityAttributes;
59     HKEY hServiceCurrentKey = INVALID_HANDLE_VALUE;
60     DWORD dwServiceCurrent = 1;
61     DWORD dwKeyDisposition;
62     DWORD dwKeySize;
63     DWORD dwError;
64 
65     /* Get the service number */
66     if (bSecurityServiceProcess == FALSE)
67     {
68         /* TODO: Create registry entry with correct write access */
69         dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
70                                   L"SYSTEM\\CurrentControlSet\\Control\\ServiceCurrent",
71                                   0,
72                                   NULL,
73                                   REG_OPTION_VOLATILE,
74                                   KEY_WRITE | KEY_READ,
75                                   NULL,
76                                   &hServiceCurrentKey,
77                                   &dwKeyDisposition);
78         if (dwError != ERROR_SUCCESS)
79         {
80             DPRINT1("RegCreateKeyEx() failed with error %lu\n", dwError);
81             return dwError;
82         }
83 
84         if (dwKeyDisposition == REG_OPENED_EXISTING_KEY)
85         {
86             dwKeySize = sizeof(DWORD);
87             dwError = RegQueryValueExW(hServiceCurrentKey,
88                                        L"",
89                                        0,
90                                        NULL,
91                                        (BYTE*)&dwServiceCurrent,
92                                        &dwKeySize);
93             if (dwError != ERROR_SUCCESS)
94             {
95                 RegCloseKey(hServiceCurrentKey);
96                 DPRINT1("RegQueryValueEx() failed with error %lu\n", dwError);
97                 return dwError;
98             }
99 
100             dwServiceCurrent++;
101         }
102 
103         dwError = RegSetValueExW(hServiceCurrentKey,
104                                  L"",
105                                  0,
106                                  REG_DWORD,
107                                  (BYTE*)&dwServiceCurrent,
108                                  sizeof(dwServiceCurrent));
109 
110         RegCloseKey(hServiceCurrentKey);
111 
112         if (dwError != ERROR_SUCCESS)
113         {
114             DPRINT1("RegSetValueExW() failed (Error %lu)\n", dwError);
115             return dwError;
116         }
117     }
118     else
119     {
120         dwServiceCurrent = 0;
121     }
122 
123     /* Create '\\.\pipe\net\NtControlPipeXXX' instance */
124     StringCchPrintfW(szControlPipeName, ARRAYSIZE(szControlPipeName),
125                      L"\\\\.\\pipe\\net\\NtControlPipe%lu", dwServiceCurrent);
126 
127     DPRINT("PipeName: %S\n", szControlPipeName);
128 
129     SecurityAttributes.nLength = sizeof(SecurityAttributes);
130     SecurityAttributes.lpSecurityDescriptor = pPipeSD;
131     SecurityAttributes.bInheritHandle = FALSE;
132 
133     pServiceImage->hControlPipe = CreateNamedPipeW(szControlPipeName,
134                                                    PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
135                                                    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
136                                                    100,
137                                                    8000,
138                                                    4,
139                                                    PipeTimeout,
140                                                    &SecurityAttributes);
141     DPRINT("CreateNamedPipeW(%S) done\n", szControlPipeName);
142     if (pServiceImage->hControlPipe == INVALID_HANDLE_VALUE)
143     {
144         DPRINT1("Failed to create control pipe\n");
145         return GetLastError();
146     }
147 
148     return ERROR_SUCCESS;
149 }
150 
151 
152 static PSERVICE_IMAGE
ScmGetServiceImageByImagePath(LPWSTR lpImagePath)153 ScmGetServiceImageByImagePath(LPWSTR lpImagePath)
154 {
155     PLIST_ENTRY ImageEntry;
156     PSERVICE_IMAGE CurrentImage;
157 
158     DPRINT("ScmGetServiceImageByImagePath(%S) called\n", lpImagePath);
159 
160     ImageEntry = ImageListHead.Flink;
161     while (ImageEntry != &ImageListHead)
162     {
163         CurrentImage = CONTAINING_RECORD(ImageEntry,
164                                          SERVICE_IMAGE,
165                                          ImageListEntry);
166         if (_wcsicmp(CurrentImage->pszImagePath, lpImagePath) == 0)
167         {
168             DPRINT("Found image: '%S'\n", CurrentImage->pszImagePath);
169             return CurrentImage;
170         }
171 
172         ImageEntry = ImageEntry->Flink;
173     }
174 
175     DPRINT("Couldn't find a matching image\n");
176 
177     return NULL;
178 
179 }
180 
181 
182 DWORD
ScmGetServiceNameFromTag(IN PTAG_INFO_NAME_FROM_TAG_IN_PARAMS InParams,OUT PTAG_INFO_NAME_FROM_TAG_OUT_PARAMS * OutParams)183 ScmGetServiceNameFromTag(IN PTAG_INFO_NAME_FROM_TAG_IN_PARAMS InParams,
184                          OUT PTAG_INFO_NAME_FROM_TAG_OUT_PARAMS *OutParams)
185 {
186     PLIST_ENTRY ServiceEntry;
187     PSERVICE CurrentService;
188     PSERVICE_IMAGE CurrentImage;
189     PTAG_INFO_NAME_FROM_TAG_OUT_PARAMS OutBuffer = NULL;
190     DWORD dwError;
191 
192     /* Lock the database */
193     ScmLockDatabaseExclusive();
194 
195     /* Find the matching service */
196     ServiceEntry = ServiceListHead.Flink;
197     while (ServiceEntry != &ServiceListHead)
198     {
199         CurrentService = CONTAINING_RECORD(ServiceEntry,
200                                            SERVICE,
201                                            ServiceListEntry);
202 
203         /* We must match the tag */
204         if (CurrentService->dwServiceTag == InParams->dwTag &&
205             CurrentService->lpImage != NULL)
206         {
207             CurrentImage = CurrentService->lpImage;
208             /* And matching the PID */
209             if (CurrentImage->dwProcessId == InParams->dwPid)
210             {
211                 break;
212             }
213         }
214 
215         ServiceEntry = ServiceEntry->Flink;
216     }
217 
218     /* No match! */
219     if (ServiceEntry == &ServiceListHead)
220     {
221         dwError = ERROR_RETRY;
222         goto Cleanup;
223     }
224 
225     /* Allocate the output buffer */
226     OutBuffer = MIDL_user_allocate(sizeof(TAG_INFO_NAME_FROM_TAG_OUT_PARAMS));
227     if (OutBuffer == NULL)
228     {
229         dwError = ERROR_NOT_ENOUGH_MEMORY;
230         goto Cleanup;
231     }
232 
233     /* And the buffer for the name */
234     OutBuffer->pszName = MIDL_user_allocate(wcslen(CurrentService->lpServiceName) * sizeof(WCHAR) + sizeof(UNICODE_NULL));
235     if (OutBuffer->pszName == NULL)
236     {
237         dwError = ERROR_NOT_ENOUGH_MEMORY;
238         goto Cleanup;
239     }
240 
241     /* Fill in output data */
242     wcscpy(OutBuffer->pszName, CurrentService->lpServiceName);
243     OutBuffer->TagType = TagTypeService;
244 
245     /* And return */
246     *OutParams = OutBuffer;
247     dwError = ERROR_SUCCESS;
248 
249 Cleanup:
250 
251     /* Unlock database */
252     ScmUnlockDatabase();
253 
254     /* If failure, free allocated memory */
255     if (dwError != ERROR_SUCCESS)
256     {
257         if (OutBuffer != NULL)
258         {
259             MIDL_user_free(OutBuffer);
260         }
261     }
262 
263     /* Return error/success */
264     return dwError;
265 }
266 
267 
268 static
269 BOOL
ScmIsSameServiceAccount(_In_ PCWSTR pszAccountName1,_In_ PCWSTR pszAccountName2)270 ScmIsSameServiceAccount(
271     _In_ PCWSTR pszAccountName1,
272     _In_ PCWSTR pszAccountName2)
273 {
274     if (pszAccountName1 == NULL &&
275         pszAccountName2 == NULL)
276         return TRUE;
277 
278     if (pszAccountName1 == NULL &&
279         pszAccountName2 != NULL &&
280         _wcsicmp(pszAccountName2, L"LocalSystem") == 0)
281         return TRUE;
282 
283     if (pszAccountName1 != NULL &&
284         pszAccountName2 == NULL &&
285         _wcsicmp(pszAccountName1, L"LocalSystem") == 0)
286         return TRUE;
287 
288     if (pszAccountName1 != NULL &&
289         pszAccountName2 != NULL &&
290         _wcsicmp(pszAccountName1, pszAccountName2) == 0)
291         return TRUE;
292 
293     return FALSE;
294 }
295 
296 
297 static
298 BOOL
ScmIsLocalSystemAccount(_In_ PCWSTR pszAccountName)299 ScmIsLocalSystemAccount(
300     _In_ PCWSTR pszAccountName)
301 {
302     if (pszAccountName == NULL ||
303         _wcsicmp(pszAccountName, L"LocalSystem") == 0)
304         return TRUE;
305 
306     return FALSE;
307 }
308 
309 
310 static
311 BOOL
ScmEnableBackupRestorePrivileges(_In_ HANDLE hToken,_In_ BOOL bEnable)312 ScmEnableBackupRestorePrivileges(
313     _In_ HANDLE hToken,
314     _In_ BOOL bEnable)
315 {
316     PTOKEN_PRIVILEGES pTokenPrivileges = NULL;
317     DWORD dwSize;
318     BOOL bRet = FALSE;
319 
320     DPRINT("ScmEnableBackupRestorePrivileges(%p %d)\n", hToken, bEnable);
321 
322     dwSize = sizeof(TOKEN_PRIVILEGES) + 2 * sizeof(LUID_AND_ATTRIBUTES);
323     pTokenPrivileges = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
324     if (pTokenPrivileges == NULL)
325     {
326         DPRINT1("Failed to allocate privilege buffer\n");
327         goto done;
328     }
329 
330     pTokenPrivileges->PrivilegeCount = 2;
331     pTokenPrivileges->Privileges[0].Luid.LowPart = SE_BACKUP_PRIVILEGE;
332     pTokenPrivileges->Privileges[0].Luid.HighPart = 0;
333     pTokenPrivileges->Privileges[0].Attributes = (bEnable ? SE_PRIVILEGE_ENABLED : 0);
334     pTokenPrivileges->Privileges[1].Luid.LowPart = SE_RESTORE_PRIVILEGE;
335     pTokenPrivileges->Privileges[1].Luid.HighPart = 0;
336     pTokenPrivileges->Privileges[1].Attributes = (bEnable ? SE_PRIVILEGE_ENABLED : 0);
337 
338     bRet = AdjustTokenPrivileges(hToken, FALSE, pTokenPrivileges, 0, NULL, NULL);
339     if (!bRet)
340     {
341         DPRINT1("AdjustTokenPrivileges() failed with error %lu\n", GetLastError());
342     }
343     else if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
344     {
345         DPRINT1("AdjustTokenPrivileges() succeeded, but with not all privileges assigned\n");
346         bRet = FALSE;
347     }
348 
349 done:
350     if (pTokenPrivileges != NULL)
351         HeapFree(GetProcessHeap(), 0, pTokenPrivileges);
352 
353     return bRet;
354 }
355 
356 
357 static
358 DWORD
ScmLogonService(IN PSERVICE pService,IN PSERVICE_IMAGE pImage)359 ScmLogonService(
360     IN PSERVICE pService,
361     IN PSERVICE_IMAGE pImage)
362 {
363     PROFILEINFOW ProfileInfo;
364     PWSTR pszUserName = NULL;
365     PWSTR pszDomainName = NULL;
366     PWSTR pszPassword = NULL;
367     PWSTR ptr;
368     DWORD dwError = ERROR_SUCCESS;
369 
370     DPRINT("ScmLogonService(%p %p)\n", pService, pImage);
371     DPRINT("Service %S\n", pService->lpServiceName);
372 
373     if (ScmIsLocalSystemAccount(pImage->pszAccountName) || ScmLiveSetup || ScmSetupInProgress)
374         return ERROR_SUCCESS;
375 
376     /* Get the user and domain names */
377     ptr = wcschr(pImage->pszAccountName, L'\\');
378     if (ptr != NULL)
379     {
380         *ptr = L'\0';
381         pszUserName = ptr + 1;
382         pszDomainName = pImage->pszAccountName;
383     }
384     else
385     {
386         // ERROR_INVALID_SERVICE_ACCOUNT
387         pszUserName = pImage->pszAccountName;
388         pszDomainName = NULL;
389     }
390 
391     /* Build the service 'password' */
392     pszPassword = HeapAlloc(GetProcessHeap(),
393                             HEAP_ZERO_MEMORY,
394                             (wcslen(pService->lpServiceName) + 5) * sizeof(WCHAR));
395     if (pszPassword == NULL)
396     {
397         dwError = ERROR_NOT_ENOUGH_MEMORY;
398         goto done;
399     }
400 
401     wcscpy(pszPassword, L"_SC_");
402     wcscat(pszPassword, pService->lpServiceName);
403 
404     DPRINT("Domain: %S  User: %S  Password: %S\n", pszDomainName, pszUserName, pszPassword);
405 
406     /* Do the service logon */
407     if (!LogonUserW(pszUserName,
408                     pszDomainName,
409                     pszPassword,
410                     LOGON32_LOGON_SERVICE,
411                     LOGON32_PROVIDER_DEFAULT,
412                     &pImage->hToken))
413     {
414         dwError = GetLastError();
415         DPRINT1("LogonUserW() failed (Error %lu)\n", dwError);
416 
417         /* Normalize the returned error */
418         dwError = ERROR_SERVICE_LOGON_FAILED;
419         goto done;
420     }
421 
422     /* Load the user profile; the per-user environment variables are thus correctly initialized */
423     ZeroMemory(&ProfileInfo, sizeof(ProfileInfo));
424     ProfileInfo.dwSize = sizeof(ProfileInfo);
425     ProfileInfo.dwFlags = PI_NOUI;
426     ProfileInfo.lpUserName = pszUserName;
427     // ProfileInfo.lpProfilePath = NULL;
428     // ProfileInfo.lpDefaultPath = NULL;
429     // ProfileInfo.lpServerName = NULL;
430     // ProfileInfo.lpPolicyPath = NULL;
431     // ProfileInfo.hProfile = NULL;
432 
433     ScmEnableBackupRestorePrivileges(pImage->hToken, TRUE);
434     if (!LoadUserProfileW(pImage->hToken, &ProfileInfo))
435         dwError = GetLastError();
436     ScmEnableBackupRestorePrivileges(pImage->hToken, FALSE);
437 
438     if (dwError != ERROR_SUCCESS)
439     {
440         DPRINT1("LoadUserProfileW() failed (Error %lu)\n", dwError);
441         goto done;
442     }
443 
444     pImage->hProfile = ProfileInfo.hProfile;
445 
446 done:
447     if (pszPassword != NULL)
448         HeapFree(GetProcessHeap(), 0, pszPassword);
449 
450     if (ptr != NULL)
451         *ptr = L'\\';
452 
453     return dwError;
454 }
455 
456 
457 static DWORD
ScmCreateOrReferenceServiceImage(PSERVICE pService)458 ScmCreateOrReferenceServiceImage(PSERVICE pService)
459 {
460     RTL_QUERY_REGISTRY_TABLE QueryTable[3];
461     UNICODE_STRING ImagePath;
462     UNICODE_STRING ObjectName;
463     PSERVICE_IMAGE pServiceImage = NULL;
464     NTSTATUS Status;
465     DWORD dwError = ERROR_SUCCESS;
466     DWORD dwRecordSize;
467     LPWSTR pString;
468     BOOL bSecurityService;
469 
470     DPRINT("ScmCreateOrReferenceServiceImage(%p)\n", pService);
471 
472     RtlInitUnicodeString(&ImagePath, NULL);
473     RtlInitUnicodeString(&ObjectName, NULL);
474 
475     /* Get service data */
476     RtlZeroMemory(&QueryTable,
477                   sizeof(QueryTable));
478 
479     QueryTable[0].Name = L"ImagePath";
480     QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
481     QueryTable[0].EntryContext = &ImagePath;
482     QueryTable[1].Name = L"ObjectName";
483     QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
484     QueryTable[1].EntryContext = &ObjectName;
485 
486     Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
487                                     pService->lpServiceName,
488                                     QueryTable,
489                                     NULL,
490                                     NULL);
491     if (!NT_SUCCESS(Status))
492     {
493         DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
494         return RtlNtStatusToDosError(Status);
495     }
496 
497     DPRINT("ImagePath: '%wZ'\n", &ImagePath);
498     DPRINT("ObjectName: '%wZ'\n", &ObjectName);
499 
500     pServiceImage = ScmGetServiceImageByImagePath(ImagePath.Buffer);
501     if (pServiceImage == NULL)
502     {
503         dwRecordSize = sizeof(SERVICE_IMAGE) +
504                        ImagePath.Length + sizeof(WCHAR) +
505                        ((ObjectName.Length != 0) ? (ObjectName.Length + sizeof(WCHAR)) : 0);
506 
507         /* Create a new service image */
508         pServiceImage = HeapAlloc(GetProcessHeap(),
509                                   HEAP_ZERO_MEMORY,
510                                   dwRecordSize);
511         if (pServiceImage == NULL)
512         {
513             dwError = ERROR_NOT_ENOUGH_MEMORY;
514             goto done;
515         }
516 
517         pServiceImage->dwImageRunCount = 1;
518         pServiceImage->hControlPipe = INVALID_HANDLE_VALUE;
519         pServiceImage->hProcess = INVALID_HANDLE_VALUE;
520 
521         pString = (PWSTR)((INT_PTR)pServiceImage + sizeof(SERVICE_IMAGE));
522 
523         /* Set the image path */
524         pServiceImage->pszImagePath = pString;
525         wcscpy(pServiceImage->pszImagePath,
526                ImagePath.Buffer);
527 
528         /* Set the account name */
529         if (ObjectName.Length > 0)
530         {
531             pString = pString + wcslen(pString) + 1;
532 
533             pServiceImage->pszAccountName = pString;
534             wcscpy(pServiceImage->pszAccountName,
535                    ObjectName.Buffer);
536         }
537 
538         /* Service logon */
539         dwError = ScmLogonService(pService, pServiceImage);
540         if (dwError != ERROR_SUCCESS)
541         {
542             DPRINT1("ScmLogonService() failed (Error %lu)\n", dwError);
543 
544             /* Release the service image */
545             HeapFree(GetProcessHeap(), 0, pServiceImage);
546 
547             goto done;
548         }
549 
550         bSecurityService = ScmIsSecurityService(pServiceImage);
551 
552         /* Create the control pipe */
553         dwError = ScmCreateNewControlPipe(pServiceImage,
554                                           bSecurityService);
555         if (dwError != ERROR_SUCCESS)
556         {
557             DPRINT1("ScmCreateNewControlPipe() failed (Error %lu)\n", dwError);
558 
559             /* Unload the user profile */
560             if (pServiceImage->hProfile != NULL)
561             {
562                 ScmEnableBackupRestorePrivileges(pServiceImage->hToken, TRUE);
563                 UnloadUserProfile(pServiceImage->hToken, pServiceImage->hProfile);
564                 ScmEnableBackupRestorePrivileges(pServiceImage->hToken, FALSE);
565             }
566 
567             /* Close the logon token */
568             if (pServiceImage->hToken != NULL)
569                 CloseHandle(pServiceImage->hToken);
570 
571             /* Release the service image */
572             HeapFree(GetProcessHeap(), 0, pServiceImage);
573 
574             goto done;
575         }
576 
577         if (bSecurityService)
578         {
579             SetSecurityServicesEvent();
580         }
581 
582         /* FIXME: Add more initialization code here */
583 
584 
585         /* Append service record */
586         InsertTailList(&ImageListHead,
587                        &pServiceImage->ImageListEntry);
588     }
589     else
590     {
591 //        if ((lpService->Status.dwServiceType & SERVICE_WIN32_SHARE_PROCESS) == 0)
592 
593         /* Fail if services in an image use different accounts */
594         if (!ScmIsSameServiceAccount(pServiceImage->pszAccountName, ObjectName.Buffer))
595         {
596             dwError = ERROR_DIFFERENT_SERVICE_ACCOUNT;
597             goto done;
598         }
599 
600         /* Increment the run counter */
601         pServiceImage->dwImageRunCount++;
602     }
603 
604     DPRINT("pServiceImage->pszImagePath: %S\n", pServiceImage->pszImagePath);
605     DPRINT("pServiceImage->pszAccountName: %S\n", pServiceImage->pszAccountName);
606     DPRINT("pServiceImage->dwImageRunCount: %lu\n", pServiceImage->dwImageRunCount);
607 
608     /* Link the service image to the service */
609     pService->lpImage = pServiceImage;
610 
611 done:
612     RtlFreeUnicodeString(&ObjectName);
613     RtlFreeUnicodeString(&ImagePath);
614 
615     DPRINT("ScmCreateOrReferenceServiceImage() done (Error: %lu)\n", dwError);
616 
617     return dwError;
618 }
619 
620 
621 VOID
ScmRemoveServiceImage(PSERVICE_IMAGE pServiceImage)622 ScmRemoveServiceImage(PSERVICE_IMAGE pServiceImage)
623 {
624     DPRINT1("ScmRemoveServiceImage() called\n");
625 
626     /* FIXME: Terminate the process */
627 
628     /* Remove the service image from the list */
629     RemoveEntryList(&pServiceImage->ImageListEntry);
630 
631     /* Close the process handle */
632     if (pServiceImage->hProcess != INVALID_HANDLE_VALUE)
633         CloseHandle(pServiceImage->hProcess);
634 
635     /* Close the control pipe */
636     if (pServiceImage->hControlPipe != INVALID_HANDLE_VALUE)
637         CloseHandle(pServiceImage->hControlPipe);
638 
639     /* Unload the user profile */
640     if (pServiceImage->hProfile != NULL)
641     {
642         ScmEnableBackupRestorePrivileges(pServiceImage->hToken, TRUE);
643         UnloadUserProfile(pServiceImage->hToken, pServiceImage->hProfile);
644         ScmEnableBackupRestorePrivileges(pServiceImage->hToken, FALSE);
645     }
646 
647     /* Close the logon token */
648     if (pServiceImage->hToken != NULL)
649         CloseHandle(pServiceImage->hToken);
650 
651     /* Release the service image */
652     HeapFree(GetProcessHeap(), 0, pServiceImage);
653 }
654 
655 
656 PSERVICE
ScmGetServiceEntryByName(LPCWSTR lpServiceName)657 ScmGetServiceEntryByName(LPCWSTR lpServiceName)
658 {
659     PLIST_ENTRY ServiceEntry;
660     PSERVICE CurrentService;
661 
662     DPRINT("ScmGetServiceEntryByName() called\n");
663 
664     ServiceEntry = ServiceListHead.Flink;
665     while (ServiceEntry != &ServiceListHead)
666     {
667         CurrentService = CONTAINING_RECORD(ServiceEntry,
668                                            SERVICE,
669                                            ServiceListEntry);
670         if (_wcsicmp(CurrentService->lpServiceName, lpServiceName) == 0)
671         {
672             DPRINT("Found service: '%S'\n", CurrentService->lpServiceName);
673             return CurrentService;
674         }
675 
676         ServiceEntry = ServiceEntry->Flink;
677     }
678 
679     DPRINT("Couldn't find a matching service\n");
680 
681     return NULL;
682 }
683 
684 
685 PSERVICE
ScmGetServiceEntryByDisplayName(LPCWSTR lpDisplayName)686 ScmGetServiceEntryByDisplayName(LPCWSTR lpDisplayName)
687 {
688     PLIST_ENTRY ServiceEntry;
689     PSERVICE CurrentService;
690 
691     DPRINT("ScmGetServiceEntryByDisplayName() called\n");
692 
693     ServiceEntry = ServiceListHead.Flink;
694     while (ServiceEntry != &ServiceListHead)
695     {
696         CurrentService = CONTAINING_RECORD(ServiceEntry,
697                                            SERVICE,
698                                            ServiceListEntry);
699         if (_wcsicmp(CurrentService->lpDisplayName, lpDisplayName) == 0)
700         {
701             DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
702             return CurrentService;
703         }
704 
705         ServiceEntry = ServiceEntry->Flink;
706     }
707 
708     DPRINT("Couldn't find a matching service\n");
709 
710     return NULL;
711 }
712 
713 
714 PSERVICE
ScmGetServiceEntryByResumeCount(DWORD dwResumeCount)715 ScmGetServiceEntryByResumeCount(DWORD dwResumeCount)
716 {
717     PLIST_ENTRY ServiceEntry;
718     PSERVICE CurrentService;
719 
720     DPRINT("ScmGetServiceEntryByResumeCount() called\n");
721 
722     ServiceEntry = ServiceListHead.Flink;
723     while (ServiceEntry != &ServiceListHead)
724     {
725         CurrentService = CONTAINING_RECORD(ServiceEntry,
726                                            SERVICE,
727                                            ServiceListEntry);
728         if (CurrentService->dwResumeCount > dwResumeCount)
729         {
730             DPRINT("Found service: '%S'\n", CurrentService->lpDisplayName);
731             return CurrentService;
732         }
733 
734         ServiceEntry = ServiceEntry->Flink;
735     }
736 
737     DPRINT("Couldn't find a matching service\n");
738 
739     return NULL;
740 }
741 
742 
743 DWORD
ScmGenerateServiceTag(PSERVICE lpServiceRecord)744 ScmGenerateServiceTag(PSERVICE lpServiceRecord)
745 {
746     /* Check for an overflow */
747     if (ServiceTag == -1)
748     {
749         return ERROR_INVALID_DATA;
750     }
751 
752     /* This is only valid for Win32 services */
753     if (!(lpServiceRecord->Status.dwServiceType & SERVICE_WIN32))
754     {
755         return ERROR_INVALID_PARAMETER;
756     }
757 
758     /* Increment the tag counter and set it */
759     ServiceTag = ServiceTag % 0xFFFFFFFF + 1;
760     lpServiceRecord->dwServiceTag = ServiceTag;
761 
762     return ERROR_SUCCESS;
763 }
764 
765 
766 DWORD
ScmCreateNewServiceRecord(LPCWSTR lpServiceName,PSERVICE * lpServiceRecord,DWORD dwServiceType,DWORD dwStartType)767 ScmCreateNewServiceRecord(LPCWSTR lpServiceName,
768                           PSERVICE *lpServiceRecord,
769                           DWORD dwServiceType,
770                           DWORD dwStartType)
771 {
772     PSERVICE lpService = NULL;
773 
774     DPRINT("Service: '%S'\n", lpServiceName);
775 
776     /* Allocate service entry */
777     lpService = HeapAlloc(GetProcessHeap(),
778                           HEAP_ZERO_MEMORY,
779                           FIELD_OFFSET(SERVICE, szServiceName[wcslen(lpServiceName) + 1]));
780     if (lpService == NULL)
781         return ERROR_NOT_ENOUGH_MEMORY;
782 
783     *lpServiceRecord = lpService;
784 
785     /* Copy service name */
786     wcscpy(lpService->szServiceName, lpServiceName);
787     lpService->lpServiceName = lpService->szServiceName;
788     lpService->lpDisplayName = lpService->lpServiceName;
789 
790     /* Set the start type */
791     lpService->dwStartType = dwStartType;
792 
793     /* Set the resume count */
794     lpService->dwResumeCount = ResumeCount++;
795 
796     /* Append service record */
797     InsertTailList(&ServiceListHead,
798                    &lpService->ServiceListEntry);
799 
800     /* Initialize the service status */
801     lpService->Status.dwServiceType = dwServiceType;
802     lpService->Status.dwCurrentState = SERVICE_STOPPED;
803     lpService->Status.dwControlsAccepted = 0;
804     lpService->Status.dwWin32ExitCode =
805         (dwStartType == SERVICE_DISABLED) ? ERROR_SERVICE_DISABLED : ERROR_SERVICE_NEVER_STARTED;
806     lpService->Status.dwServiceSpecificExitCode = 0;
807     lpService->Status.dwCheckPoint = 0;
808     lpService->Status.dwWaitHint =
809         (dwServiceType & SERVICE_DRIVER) ? 0 : 2000; /* 2 seconds */
810 
811     return ERROR_SUCCESS;
812 }
813 
814 
815 VOID
ScmDeleteServiceRecord(PSERVICE lpService)816 ScmDeleteServiceRecord(PSERVICE lpService)
817 {
818     DPRINT("Deleting Service %S\n", lpService->lpServiceName);
819 
820     /* Delete the display name */
821     if (lpService->lpDisplayName != NULL &&
822         lpService->lpDisplayName != lpService->lpServiceName)
823         HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName);
824 
825     /* Dereference the service image */
826     if (lpService->lpImage)
827     {
828         lpService->lpImage->dwImageRunCount--;
829 
830         if (lpService->lpImage->dwImageRunCount == 0)
831         {
832             ScmRemoveServiceImage(lpService->lpImage);
833             lpService->lpImage = NULL;
834         }
835     }
836 
837     /* Decrement the group reference counter */
838     ScmSetServiceGroup(lpService, NULL);
839 
840     /* Release the SecurityDescriptor */
841     if (lpService->pSecurityDescriptor != NULL)
842         HeapFree(GetProcessHeap(), 0, lpService->pSecurityDescriptor);
843 
844     /* Remove the Service from the List */
845     RemoveEntryList(&lpService->ServiceListEntry);
846 
847     DPRINT("Deleted Service %S\n", lpService->lpServiceName);
848 
849     /* Delete the service record */
850     HeapFree(GetProcessHeap(), 0, lpService);
851 
852     DPRINT("Done\n");
853 }
854 
855 DWORD
856 Int_EnumDependentServicesW(HKEY hServicesKey,
857                            PSERVICE lpService,
858                            DWORD dwServiceState,
859                            PSERVICE *lpServices,
860                            LPDWORD pcbBytesNeeded,
861                            LPDWORD lpServicesReturned);
862 
ScmDeleteService(PSERVICE lpService)863 DWORD ScmDeleteService(PSERVICE lpService)
864 {
865     DWORD dwError;
866     DWORD pcbBytesNeeded = 0;
867     DWORD dwServicesReturned = 0;
868     HKEY hServicesKey;
869 
870     ASSERT(lpService->RefCount == 0);
871 
872     /* Open the Services Reg key */
873     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
874                             L"System\\CurrentControlSet\\Services",
875                             0,
876                             KEY_SET_VALUE | KEY_READ,
877                             &hServicesKey);
878     if (dwError != ERROR_SUCCESS)
879     {
880         DPRINT1("Failed to open services key\n");
881         return dwError;
882     }
883 
884     /* Call the function with NULL, just to get bytes we need */
885     Int_EnumDependentServicesW(hServicesKey,
886                                lpService,
887                                SERVICE_ACTIVE,
888                                NULL,
889                                &pcbBytesNeeded,
890                                &dwServicesReturned);
891 
892     /* If pcbBytesNeeded returned a value then there are services running that are dependent on this service */
893     if (pcbBytesNeeded)
894     {
895         DPRINT1("Deletion failed due to running dependencies\n");
896         RegCloseKey(hServicesKey);
897         return ERROR_DEPENDENT_SERVICES_RUNNING;
898     }
899 
900     /* There are no references and no running dependencies,
901        it is now safe to delete the service */
902 
903     /* Delete the Service Key */
904     dwError = ScmDeleteRegKey(hServicesKey, lpService->lpServiceName);
905 
906     RegCloseKey(hServicesKey);
907 
908     if (dwError != ERROR_SUCCESS)
909     {
910         DPRINT1("Failed to delete the Service Registry key\n");
911         return dwError;
912     }
913 
914     /* Delete the Service */
915     ScmDeleteServiceRecord(lpService);
916 
917     return ERROR_SUCCESS;
918 }
919 
920 /*
921  * This function allows the caller to be sure that the service won't be freed unexpectedly.
922  * In order to be sure that lpService will be valid until the reference is added
923  * the caller needs to hold the database lock.
924  * A running service will keep a reference for the whole time it is not SERVICE_STOPPED.
925  * A service handle will also keep a reference to a service. Keeping a reference is
926  * really needed so that ScmControlService can be called without keeping the database locked.
927  * This means that eventually the correct order of operations to send a control message to
928  * a service looks like: lock, reference, unlock, send control, lock, dereference, unlock.
929  */
930 DWORD
ScmReferenceService(PSERVICE lpService)931 ScmReferenceService(PSERVICE lpService)
932 {
933     return InterlockedIncrement(&lpService->RefCount);
934 }
935 
936 /* This function must be called with the database lock held exclusively as
937    it can end up deleting the service */
938 DWORD
ScmDereferenceService(PSERVICE lpService)939 ScmDereferenceService(PSERVICE lpService)
940 {
941     DWORD ref;
942 
943     ASSERT(lpService->RefCount > 0);
944 
945     ref = InterlockedDecrement(&lpService->RefCount);
946 
947     if (ref == 0 && lpService->bDeleted &&
948         lpService->Status.dwCurrentState == SERVICE_STOPPED)
949     {
950         ScmDeleteService(lpService);
951     }
952     return ref;
953 }
954 
955 static DWORD
CreateServiceListEntry(LPCWSTR lpServiceName,HKEY hServiceKey)956 CreateServiceListEntry(LPCWSTR lpServiceName,
957                        HKEY hServiceKey)
958 {
959     PSERVICE lpService = NULL;
960     LPWSTR lpDisplayName = NULL;
961     LPWSTR lpGroup = NULL;
962     DWORD dwSize;
963     DWORD dwError;
964     DWORD dwServiceType;
965     DWORD dwStartType;
966     DWORD dwErrorControl;
967     DWORD dwTagId;
968 
969     DPRINT("Service: '%S'\n", lpServiceName);
970     if (*lpServiceName == L'{')
971         return ERROR_SUCCESS;
972 
973     dwSize = sizeof(DWORD);
974     dwError = RegQueryValueExW(hServiceKey,
975                                L"Type",
976                                NULL,
977                                NULL,
978                                (LPBYTE)&dwServiceType,
979                                &dwSize);
980     if (dwError != ERROR_SUCCESS)
981         return ERROR_SUCCESS;
982 
983     if (((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_OWN_PROCESS) &&
984         ((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_SHARE_PROCESS) &&
985         (dwServiceType != SERVICE_KERNEL_DRIVER) &&
986         (dwServiceType != SERVICE_FILE_SYSTEM_DRIVER))
987         return ERROR_SUCCESS;
988 
989     DPRINT("Service type: %lx\n", dwServiceType);
990 
991     dwSize = sizeof(DWORD);
992     dwError = RegQueryValueExW(hServiceKey,
993                                L"Start",
994                                NULL,
995                                NULL,
996                                (LPBYTE)&dwStartType,
997                                &dwSize);
998     if (dwError != ERROR_SUCCESS)
999         return ERROR_SUCCESS;
1000 
1001     DPRINT("Start type: %lx\n", dwStartType);
1002 
1003     dwSize = sizeof(DWORD);
1004     dwError = RegQueryValueExW(hServiceKey,
1005                                L"ErrorControl",
1006                                NULL,
1007                                NULL,
1008                                (LPBYTE)&dwErrorControl,
1009                                &dwSize);
1010     if (dwError != ERROR_SUCCESS)
1011         return ERROR_SUCCESS;
1012 
1013     DPRINT("Error control: %lx\n", dwErrorControl);
1014 
1015     dwError = RegQueryValueExW(hServiceKey,
1016                                L"Tag",
1017                                NULL,
1018                                NULL,
1019                                (LPBYTE)&dwTagId,
1020                                &dwSize);
1021     if (dwError != ERROR_SUCCESS)
1022         dwTagId = 0;
1023 
1024     DPRINT("Tag: %lx\n", dwTagId);
1025 
1026     dwError = ScmReadString(hServiceKey,
1027                             L"Group",
1028                             &lpGroup);
1029     if (dwError != ERROR_SUCCESS)
1030         lpGroup = NULL;
1031 
1032     DPRINT("Group: %S\n", lpGroup);
1033 
1034     dwError = ScmReadString(hServiceKey,
1035                             L"DisplayName",
1036                             &lpDisplayName);
1037     if (dwError != ERROR_SUCCESS)
1038         lpDisplayName = NULL;
1039 
1040     DPRINT("Display name: %S\n", lpDisplayName);
1041 
1042     dwError = ScmCreateNewServiceRecord(lpServiceName,
1043                                         &lpService,
1044                                         dwServiceType,
1045                                         dwStartType);
1046     if (dwError != ERROR_SUCCESS)
1047         goto done;
1048 
1049     lpService->dwErrorControl = dwErrorControl;
1050     lpService->dwTag = dwTagId;
1051 
1052     if (lpGroup != NULL)
1053     {
1054         dwError = ScmSetServiceGroup(lpService, lpGroup);
1055         if (dwError != ERROR_SUCCESS)
1056             goto done;
1057     }
1058 
1059     if (lpDisplayName != NULL)
1060     {
1061         lpService->lpDisplayName = lpDisplayName;
1062         lpDisplayName = NULL;
1063     }
1064 
1065     DPRINT("ServiceName: '%S'\n", lpService->lpServiceName);
1066     if (lpService->lpGroup != NULL)
1067     {
1068         DPRINT("Group: '%S'\n", lpService->lpGroup->lpGroupName);
1069     }
1070     DPRINT("Start %lx  Type %lx  Tag %lx  ErrorControl %lx\n",
1071            lpService->dwStartType,
1072            lpService->Status.dwServiceType,
1073            lpService->dwTag,
1074            lpService->dwErrorControl);
1075 
1076     if (ScmIsDeleteFlagSet(hServiceKey))
1077         lpService->bDeleted = TRUE;
1078     else
1079         ScmGenerateServiceTag(lpService);
1080 
1081     if (lpService->Status.dwServiceType & SERVICE_WIN32)
1082     {
1083         dwError = ScmReadSecurityDescriptor(hServiceKey,
1084                                             &lpService->pSecurityDescriptor);
1085         if (dwError != ERROR_SUCCESS)
1086             goto done;
1087 
1088         /* Assing the default security descriptor if the security descriptor cannot be read */
1089         if (lpService->pSecurityDescriptor == NULL)
1090         {
1091             DPRINT("No security descriptor found! Assign default security descriptor\n");
1092             dwError = ScmCreateDefaultServiceSD(&lpService->pSecurityDescriptor);
1093             if (dwError != ERROR_SUCCESS)
1094                 goto done;
1095 
1096             dwError = ScmWriteSecurityDescriptor(hServiceKey,
1097                                                  lpService->pSecurityDescriptor);
1098             if (dwError != ERROR_SUCCESS)
1099                 goto done;
1100         }
1101     }
1102 
1103 done:
1104     if (lpGroup != NULL)
1105         HeapFree(GetProcessHeap(), 0, lpGroup);
1106 
1107     if (lpDisplayName != NULL)
1108         HeapFree(GetProcessHeap(), 0, lpDisplayName);
1109 
1110     if (lpService != NULL)
1111     {
1112         ASSERT(lpService->lpImage == NULL);
1113     }
1114 
1115     return dwError;
1116 }
1117 
1118 
1119 VOID
ScmDeleteMarkedServices(VOID)1120 ScmDeleteMarkedServices(VOID)
1121 {
1122     PLIST_ENTRY ServiceEntry;
1123     PSERVICE CurrentService;
1124     HKEY hServicesKey;
1125     DWORD dwError;
1126 
1127     ServiceEntry = ServiceListHead.Flink;
1128     while (ServiceEntry != &ServiceListHead)
1129     {
1130         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1131 
1132         ServiceEntry = ServiceEntry->Flink;
1133 
1134         if (CurrentService->bDeleted != FALSE)
1135         {
1136             dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1137                                     L"System\\CurrentControlSet\\Services",
1138                                     0,
1139                                     DELETE,
1140                                     &hServicesKey);
1141             if (dwError == ERROR_SUCCESS)
1142             {
1143                 dwError = ScmDeleteRegKey(hServicesKey, CurrentService->lpServiceName);
1144                 RegCloseKey(hServicesKey);
1145                 if (dwError == ERROR_SUCCESS)
1146                 {
1147                     RemoveEntryList(&CurrentService->ServiceListEntry);
1148                     HeapFree(GetProcessHeap(), 0, CurrentService);
1149                 }
1150             }
1151 
1152             if (dwError != ERROR_SUCCESS)
1153                 DPRINT1("Delete service failed: %S\n", CurrentService->lpServiceName);
1154         }
1155     }
1156 }
1157 
1158 
1159 static
1160 VOID
ScmGetNoInteractiveServicesValue(VOID)1161 ScmGetNoInteractiveServicesValue(VOID)
1162 {
1163     HKEY hKey;
1164     DWORD dwKeySize;
1165     LONG lError;
1166 
1167     lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1168                            L"SYSTEM\\CurrentControlSet\\Control\\Windows",
1169                            0,
1170                            KEY_READ,
1171                            &hKey);
1172     if (lError == ERROR_SUCCESS)
1173     {
1174         dwKeySize = sizeof(NoInteractiveServices);
1175         lError = RegQueryValueExW(hKey,
1176                                   L"NoInteractiveServices",
1177                                   0,
1178                                   NULL,
1179                                   (LPBYTE)&NoInteractiveServices,
1180                                   &dwKeySize);
1181         RegCloseKey(hKey);
1182     }
1183 }
1184 
1185 
1186 DWORD
ScmCreateServiceDatabase(VOID)1187 ScmCreateServiceDatabase(VOID)
1188 {
1189     WCHAR szSubKey[MAX_PATH];
1190     HKEY hServicesKey;
1191     HKEY hServiceKey;
1192     DWORD dwSubKey;
1193     DWORD dwSubKeyLength;
1194     FILETIME ftLastChanged;
1195     DWORD dwError;
1196 
1197     DPRINT("ScmCreateServiceDatabase() called\n");
1198 
1199     /* Retrieve the NoInteractiveServies value */
1200     ScmGetNoInteractiveServicesValue();
1201 
1202     /* Create the service group list */
1203     dwError = ScmCreateGroupList();
1204     if (dwError != ERROR_SUCCESS)
1205         return dwError;
1206 
1207     /* Initialize image and service lists */
1208     InitializeListHead(&ImageListHead);
1209     InitializeListHead(&ServiceListHead);
1210 
1211     /* Initialize the database lock */
1212     RtlInitializeResource(&DatabaseLock);
1213 
1214     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1215                             L"System\\CurrentControlSet\\Services",
1216                             0,
1217                             KEY_READ,
1218                             &hServicesKey);
1219     if (dwError != ERROR_SUCCESS)
1220         return dwError;
1221 
1222     dwSubKey = 0;
1223     for (;;)
1224     {
1225         dwSubKeyLength = MAX_PATH;
1226         dwError = RegEnumKeyExW(hServicesKey,
1227                                 dwSubKey,
1228                                 szSubKey,
1229                                 &dwSubKeyLength,
1230                                 NULL,
1231                                 NULL,
1232                                 NULL,
1233                                 &ftLastChanged);
1234         if (dwError == ERROR_SUCCESS &&
1235             szSubKey[0] != L'{')
1236         {
1237             DPRINT("SubKeyName: '%S'\n", szSubKey);
1238 
1239             dwError = RegOpenKeyExW(hServicesKey,
1240                                     szSubKey,
1241                                     0,
1242                                     KEY_READ,
1243                                     &hServiceKey);
1244             if (dwError == ERROR_SUCCESS)
1245             {
1246                 dwError = CreateServiceListEntry(szSubKey,
1247                                                  hServiceKey);
1248 
1249                 RegCloseKey(hServiceKey);
1250             }
1251         }
1252 
1253         if (dwError != ERROR_SUCCESS)
1254             break;
1255 
1256         dwSubKey++;
1257     }
1258 
1259     RegCloseKey(hServicesKey);
1260 
1261     /* Wait for the LSA server */
1262     ScmWaitForLsa();
1263 
1264     /* Delete services that are marked for delete */
1265     ScmDeleteMarkedServices();
1266 
1267     DPRINT("ScmCreateServiceDatabase() done\n");
1268 
1269     return ERROR_SUCCESS;
1270 }
1271 
1272 
1273 VOID
ScmShutdownServiceDatabase(VOID)1274 ScmShutdownServiceDatabase(VOID)
1275 {
1276     DPRINT("ScmShutdownServiceDatabase() called\n");
1277 
1278     ScmDeleteMarkedServices();
1279     RtlDeleteResource(&DatabaseLock);
1280 
1281     DPRINT("ScmShutdownServiceDatabase() done\n");
1282 }
1283 
1284 
1285 static NTSTATUS
ScmCheckDriver(PSERVICE Service)1286 ScmCheckDriver(PSERVICE Service)
1287 {
1288     OBJECT_ATTRIBUTES ObjectAttributes;
1289     UNICODE_STRING DirName;
1290     HANDLE DirHandle;
1291     NTSTATUS Status;
1292     POBJECT_DIRECTORY_INFORMATION DirInfo;
1293     ULONG BufferLength;
1294     ULONG DataLength;
1295     ULONG Index;
1296 
1297     DPRINT("ScmCheckDriver(%S) called\n", Service->lpServiceName);
1298 
1299     if (Service->Status.dwServiceType == SERVICE_KERNEL_DRIVER)
1300     {
1301         RtlInitUnicodeString(&DirName, L"\\Driver");
1302     }
1303     else // if (Service->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER)
1304     {
1305         ASSERT(Service->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER);
1306         RtlInitUnicodeString(&DirName, L"\\FileSystem");
1307     }
1308 
1309     InitializeObjectAttributes(&ObjectAttributes,
1310                                &DirName,
1311                                0,
1312                                NULL,
1313                                NULL);
1314 
1315     Status = NtOpenDirectoryObject(&DirHandle,
1316                                    DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
1317                                    &ObjectAttributes);
1318     if (!NT_SUCCESS(Status))
1319     {
1320         return Status;
1321     }
1322 
1323     BufferLength = sizeof(OBJECT_DIRECTORY_INFORMATION) +
1324                        2 * MAX_PATH * sizeof(WCHAR);
1325     DirInfo = HeapAlloc(GetProcessHeap(),
1326                         HEAP_ZERO_MEMORY,
1327                         BufferLength);
1328 
1329     Index = 0;
1330     while (TRUE)
1331     {
1332         Status = NtQueryDirectoryObject(DirHandle,
1333                                         DirInfo,
1334                                         BufferLength,
1335                                         TRUE,
1336                                         FALSE,
1337                                         &Index,
1338                                         &DataLength);
1339         if (Status == STATUS_NO_MORE_ENTRIES)
1340         {
1341             /* FIXME: Add current service to 'failed service' list */
1342             DPRINT("Service '%S' failed\n", Service->lpServiceName);
1343             break;
1344         }
1345 
1346         if (!NT_SUCCESS(Status))
1347             break;
1348 
1349         DPRINT("Comparing: '%S'  '%wZ'\n", Service->lpServiceName, &DirInfo->Name);
1350 
1351         if (_wcsicmp(Service->lpServiceName, DirInfo->Name.Buffer) == 0)
1352         {
1353             DPRINT("Found: '%S'  '%wZ'\n",
1354                    Service->lpServiceName, &DirInfo->Name);
1355 
1356             /* Mark service as 'running' */
1357             Service->Status.dwCurrentState = SERVICE_RUNNING;
1358             Service->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
1359             Service->Status.dwWin32ExitCode = ERROR_SUCCESS;
1360             Service->Status.dwServiceSpecificExitCode = 0;
1361             Service->Status.dwCheckPoint = 0;
1362             Service->Status.dwWaitHint = 0;
1363 
1364             /* Mark the service group as 'running' */
1365             if (Service->lpGroup != NULL)
1366             {
1367                 Service->lpGroup->ServicesRunning = TRUE;
1368             }
1369 
1370             break;
1371         }
1372     }
1373 
1374     HeapFree(GetProcessHeap(),
1375              0,
1376              DirInfo);
1377     NtClose(DirHandle);
1378 
1379     return STATUS_SUCCESS;
1380 }
1381 
1382 
1383 VOID
ScmGetBootAndSystemDriverState(VOID)1384 ScmGetBootAndSystemDriverState(VOID)
1385 {
1386     PLIST_ENTRY ServiceEntry;
1387     PSERVICE CurrentService;
1388 
1389     DPRINT("ScmGetBootAndSystemDriverState() called\n");
1390 
1391     ServiceEntry = ServiceListHead.Flink;
1392     while (ServiceEntry != &ServiceListHead)
1393     {
1394         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
1395 
1396         if (CurrentService->dwStartType == SERVICE_BOOT_START ||
1397             CurrentService->dwStartType == SERVICE_SYSTEM_START)
1398         {
1399             /* Check driver */
1400             DPRINT("  Checking service: %S\n", CurrentService->lpServiceName);
1401 
1402             ScmCheckDriver(CurrentService);
1403         }
1404 
1405         ServiceEntry = ServiceEntry->Flink;
1406     }
1407 
1408     DPRINT("ScmGetBootAndSystemDriverState() done\n");
1409 }
1410 
1411 
1412 /*
1413  * ScmControlService must never be called with the database lock being held.
1414  * The service passed must always be referenced instead.
1415  */
1416 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)1417 ScmControlServiceEx(
1418     _In_ HANDLE hControlPipe,
1419     _In_ PCWSTR pServiceName,
1420     _In_ DWORD dwControl,
1421     _In_ SERVICE_STATUS_HANDLE hServiceStatus,
1422     _In_opt_ DWORD dwServiceTag,
1423     _In_opt_ DWORD argc,
1424     _In_reads_opt_(argc) const PCWSTR* argv)
1425 {
1426     DWORD dwError = ERROR_SUCCESS;
1427     BOOL bResult;
1428     PSCM_CONTROL_PACKET ControlPacket;
1429     SCM_REPLY_PACKET ReplyPacket;
1430     DWORD PacketSize;
1431     DWORD i;
1432     PWSTR Ptr;
1433     DWORD dwReadCount = 0;
1434     OVERLAPPED Overlapped = {0};
1435 
1436     DPRINT("ScmControlService(%S, %d) called\n", pServiceName, dwControl);
1437 
1438     /* Calculate the total size of the control packet:
1439      * initial structure, the start command line, and the argument vector */
1440     PacketSize = sizeof(SCM_CONTROL_PACKET);
1441     PacketSize += (DWORD)((wcslen(pServiceName) + 1) * sizeof(WCHAR));
1442 
1443     /*
1444      * Calculate the required packet size for the start argument vector 'argv',
1445      * composed of the pointer offsets list, followed by UNICODE strings.
1446      * The strings are stored successively after the offsets vector, with
1447      * the offsets being relative to the beginning of the vector, as in the
1448      * following layout (with N == argc):
1449      *     [argOff(0)]...[argOff(N-1)][str(0)]...[str(N-1)] .
1450      */
1451     if (argc > 0 && argv != NULL)
1452     {
1453         PacketSize = ALIGN_UP(PacketSize, PWSTR);
1454         PacketSize += (argc * sizeof(PWSTR));
1455 
1456         DPRINT("Argc: %lu\n", argc);
1457         for (i = 0; i < argc; i++)
1458         {
1459             DPRINT("Argv[%lu]: %S\n", i, argv[i]);
1460             PacketSize += (DWORD)((wcslen(argv[i]) + 1) * sizeof(WCHAR));
1461         }
1462     }
1463 
1464     /* Allocate the control packet */
1465     ControlPacket = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, PacketSize);
1466     if (!ControlPacket)
1467         return ERROR_NOT_ENOUGH_MEMORY;
1468 
1469     ControlPacket->dwSize = PacketSize;
1470     ControlPacket->dwControl = dwControl;
1471     ControlPacket->hServiceStatus = hServiceStatus;
1472     ControlPacket->dwServiceTag = dwServiceTag;
1473 
1474     /* Copy the start command line */
1475     ControlPacket->dwServiceNameOffset = sizeof(SCM_CONTROL_PACKET);
1476     Ptr = (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwServiceNameOffset);
1477     wcscpy(Ptr, pServiceName);
1478 
1479     ControlPacket->dwArgumentsCount  = 0;
1480     ControlPacket->dwArgumentsOffset = 0;
1481 
1482     /* Copy the argument vector */
1483     if (argc > 0 && argv != NULL)
1484     {
1485         PWSTR *pOffPtr, pArgPtr;
1486 
1487         Ptr += wcslen(pServiceName) + 1;
1488         pOffPtr = (PWSTR*)ALIGN_UP_POINTER(Ptr, PWSTR);
1489         pArgPtr = (PWSTR)((ULONG_PTR)pOffPtr + argc * sizeof(PWSTR));
1490 
1491         ControlPacket->dwArgumentsCount  = argc;
1492         ControlPacket->dwArgumentsOffset = (DWORD)((ULONG_PTR)pOffPtr - (ULONG_PTR)ControlPacket);
1493 
1494         DPRINT("dwArgumentsCount: %lu\n", ControlPacket->dwArgumentsCount);
1495         DPRINT("dwArgumentsOffset: %lu\n", ControlPacket->dwArgumentsOffset);
1496 
1497         for (i = 0; i < argc; i++)
1498         {
1499             wcscpy(pArgPtr, argv[i]);
1500             pOffPtr[i] = (PWSTR)((ULONG_PTR)pArgPtr - (ULONG_PTR)pOffPtr);
1501             DPRINT("offset[%lu]: %p\n", i, pOffPtr[i]);
1502             pArgPtr += wcslen(argv[i]) + 1;
1503         }
1504     }
1505 
1506     /* Acquire the service control critical section, to synchronize requests */
1507     EnterCriticalSection(&ControlServiceCriticalSection);
1508 
1509     bResult = TransactNamedPipe(hControlPipe,
1510                                 ControlPacket,
1511                                 PacketSize,
1512                                 &ReplyPacket,
1513                                 sizeof(ReplyPacket),
1514                                 &dwReadCount,
1515                                 &Overlapped);
1516     if (!bResult)
1517     {
1518         /* Fail for any error other than pending IO */
1519         dwError = GetLastError();
1520         if (dwError != ERROR_IO_PENDING)
1521         {
1522             DPRINT1("TransactNamedPipe(%S, %d) failed (Error %lu)\n", pServiceName, dwControl, dwError);
1523             goto Done;
1524         }
1525 
1526         DPRINT("TransactNamedPipe(%S, %d) returned ERROR_IO_PENDING\n", pServiceName, dwControl);
1527 
1528         dwError = WaitForSingleObject(hControlPipe, PipeTimeout);
1529         DPRINT("WaitForSingleObject(%S, %d) returned %lu\n", pServiceName, dwControl, dwError);
1530 
1531         if (dwError == WAIT_TIMEOUT)
1532         {
1533             DPRINT1("WaitForSingleObject(%S, %d) timed out\n", pServiceName, dwControl);
1534             bResult = CancelIo(hControlPipe);
1535             if (!bResult)
1536                 DPRINT1("CancelIo(%S, %d) failed (Error %lu)\n", pServiceName, dwControl, GetLastError());
1537 
1538             dwError = ERROR_SERVICE_REQUEST_TIMEOUT;
1539         }
1540         else if (dwError == WAIT_OBJECT_0)
1541         {
1542             bResult = GetOverlappedResult(hControlPipe,
1543                                           &Overlapped,
1544                                           &dwReadCount,
1545                                           TRUE);
1546             if (!bResult)
1547             {
1548                 dwError = GetLastError();
1549                 DPRINT1("GetOverlappedResult(%S, %d) failed (Error %lu)\n", pServiceName, dwControl, dwError);
1550             }
1551         }
1552     }
1553 
1554 Done:
1555     /* Release the service control critical section */
1556     LeaveCriticalSection(&ControlServiceCriticalSection);
1557 
1558     /* Free the control packet */
1559     HeapFree(GetProcessHeap(), 0, ControlPacket);
1560 
1561     if (dwReadCount == sizeof(ReplyPacket))
1562         dwError = ReplyPacket.dwError;
1563 
1564     DPRINT("ScmControlService(%S, %d) done (Error %lu)\n", pServiceName, dwControl, dwError);
1565     return dwError;
1566 }
1567 
1568 DWORD
ScmControlService(_In_ HANDLE hControlPipe,_In_ PCWSTR pServiceName,_In_ DWORD dwControl,_In_ SERVICE_STATUS_HANDLE hServiceStatus)1569 ScmControlService(
1570     _In_ HANDLE hControlPipe,
1571     _In_ PCWSTR pServiceName,
1572     _In_ DWORD dwControl,
1573     _In_ SERVICE_STATUS_HANDLE hServiceStatus)
1574 {
1575     return ScmControlServiceEx(hControlPipe,
1576                                pServiceName,
1577                                dwControl,
1578                                hServiceStatus,
1579                                0, 0, NULL);
1580 }
1581 
1582 
1583 static DWORD
ScmWaitForServiceConnect(PSERVICE Service)1584 ScmWaitForServiceConnect(PSERVICE Service)
1585 {
1586     DWORD dwRead = 0;
1587     DWORD dwProcessId = 0;
1588     DWORD dwError = ERROR_SUCCESS;
1589     BOOL bResult;
1590     OVERLAPPED Overlapped = {0};
1591 #if 0
1592     LPCWSTR lpLogStrings[3];
1593     WCHAR szBuffer1[20];
1594     WCHAR szBuffer2[20];
1595 #endif
1596 
1597     DPRINT("ScmWaitForServiceConnect()\n");
1598 
1599     bResult = ConnectNamedPipe(Service->lpImage->hControlPipe,
1600                                &Overlapped);
1601     if (bResult == FALSE)
1602     {
1603         DPRINT("ConnectNamedPipe() returned FALSE\n");
1604 
1605         dwError = GetLastError();
1606         if (dwError == ERROR_IO_PENDING)
1607         {
1608             DPRINT("dwError: ERROR_IO_PENDING\n");
1609 
1610             dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1611                                           PipeTimeout);
1612             DPRINT("WaitForSingleObject() returned %lu\n", dwError);
1613 
1614             if (dwError == WAIT_TIMEOUT)
1615             {
1616                 DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
1617 
1618                 bResult = CancelIo(Service->lpImage->hControlPipe);
1619                 if (bResult == FALSE)
1620                 {
1621                     DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1622                 }
1623 
1624 #if 0
1625                 _ultow(PipeTimeout, szBuffer1, 10);
1626                 lpLogStrings[0] = Service->lpDisplayName;
1627                 lpLogStrings[1] = szBuffer1;
1628 
1629                 ScmLogEvent(EVENT_CONNECTION_TIMEOUT,
1630                             EVENTLOG_ERROR_TYPE,
1631                             2,
1632                             lpLogStrings);
1633 #endif
1634                 DPRINT1("Log EVENT_CONNECTION_TIMEOUT by %S\n", Service->lpDisplayName);
1635 
1636                 return ERROR_SERVICE_REQUEST_TIMEOUT;
1637             }
1638             else if (dwError == WAIT_OBJECT_0)
1639             {
1640                 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1641                                               &Overlapped,
1642                                               &dwRead,
1643                                               TRUE);
1644                 if (bResult == FALSE)
1645                 {
1646                     dwError = GetLastError();
1647                     DPRINT1("GetOverlappedResult failed (Error %lu)\n", dwError);
1648 
1649                     return dwError;
1650                 }
1651             }
1652         }
1653         else if (dwError != ERROR_PIPE_CONNECTED)
1654         {
1655             DPRINT1("ConnectNamedPipe failed (Error %lu)\n", dwError);
1656             return dwError;
1657         }
1658     }
1659 
1660     DPRINT("Control pipe connected\n");
1661 
1662     Overlapped.hEvent = NULL;
1663 
1664     /* Read the process id from pipe */
1665     bResult = ReadFile(Service->lpImage->hControlPipe,
1666                        (LPVOID)&dwProcessId,
1667                        sizeof(DWORD),
1668                        &dwRead,
1669                        &Overlapped);
1670     if (bResult == FALSE)
1671     {
1672         DPRINT("ReadFile() returned FALSE\n");
1673 
1674         dwError = GetLastError();
1675         if (dwError == ERROR_IO_PENDING)
1676         {
1677             DPRINT("dwError: ERROR_IO_PENDING\n");
1678 
1679             dwError = WaitForSingleObject(Service->lpImage->hControlPipe,
1680                                           PipeTimeout);
1681             if (dwError == WAIT_TIMEOUT)
1682             {
1683                 DPRINT("WaitForSingleObject() returned WAIT_TIMEOUT\n");
1684 
1685                 bResult = CancelIo(Service->lpImage->hControlPipe);
1686                 if (bResult == FALSE)
1687                 {
1688                     DPRINT1("CancelIo() failed (Error: %lu)\n", GetLastError());
1689                 }
1690 
1691 #if 0
1692                 _ultow(PipeTimeout, szBuffer1, 10);
1693                 lpLogStrings[0] = szBuffer1;
1694 
1695                 ScmLogEvent(EVENT_READFILE_TIMEOUT,
1696                             EVENTLOG_ERROR_TYPE,
1697                             1,
1698                             lpLogStrings);
1699 #endif
1700                 DPRINT1("Log EVENT_READFILE_TIMEOUT by %S\n", Service->lpDisplayName);
1701 
1702                 return ERROR_SERVICE_REQUEST_TIMEOUT;
1703             }
1704             else if (dwError == WAIT_OBJECT_0)
1705             {
1706                 DPRINT("WaitForSingleObject() returned WAIT_OBJECT_0\n");
1707 
1708                 DPRINT("Process Id: %lu\n", dwProcessId);
1709 
1710                 bResult = GetOverlappedResult(Service->lpImage->hControlPipe,
1711                                               &Overlapped,
1712                                               &dwRead,
1713                                               TRUE);
1714                 if (bResult == FALSE)
1715                 {
1716                     dwError = GetLastError();
1717                     DPRINT1("GetOverlappedResult() failed (Error %lu)\n", dwError);
1718 
1719                     return dwError;
1720                 }
1721             }
1722             else
1723             {
1724                 DPRINT1("WaitForSingleObject() returned %lu\n", dwError);
1725             }
1726         }
1727         else
1728         {
1729             DPRINT1("ReadFile() failed (Error %lu)\n", dwError);
1730             return dwError;
1731         }
1732     }
1733 
1734     if ((ScmIsSecurityService(Service->lpImage) == FALSE)&&
1735         (dwProcessId != Service->lpImage->dwProcessId))
1736     {
1737 #if 0
1738         _ultow(Service->lpImage->dwProcessId, szBuffer1, 10);
1739         _ultow(dwProcessId, szBuffer2, 10);
1740 
1741         lpLogStrings[0] = Service->lpDisplayName;
1742         lpLogStrings[1] = szBuffer1;
1743         lpLogStrings[2] = szBuffer2;
1744 
1745         ScmLogEvent(EVENT_SERVICE_DIFFERENT_PID_CONNECTED,
1746                     EVENTLOG_WARNING_TYPE,
1747                     3,
1748                     lpLogStrings);
1749 #endif
1750 
1751         DPRINT1("Log EVENT_SERVICE_DIFFERENT_PID_CONNECTED by %S\n", Service->lpDisplayName);
1752     }
1753 
1754     DPRINT("ScmWaitForServiceConnect() done\n");
1755 
1756     return ERROR_SUCCESS;
1757 }
1758 
1759 
1760 static DWORD
ScmStartUserModeService(PSERVICE Service,DWORD argc,const PCWSTR * argv)1761 ScmStartUserModeService(PSERVICE Service,
1762                         DWORD argc,
1763                         const PCWSTR* argv)
1764 {
1765     PROCESS_INFORMATION ProcessInformation;
1766     STARTUPINFOW StartupInfo;
1767     LPVOID lpEnvironment;
1768     BOOL Result;
1769     DWORD dwError = ERROR_SUCCESS;
1770 
1771     DPRINT("ScmStartUserModeService(%p)\n", Service);
1772 
1773     /* If the image is already running, just send a start command */
1774     if (Service->lpImage->dwImageRunCount > 1)
1775         goto Quit;
1776 
1777     /* Otherwise start its process */
1778     ZeroMemory(&StartupInfo, sizeof(StartupInfo));
1779     StartupInfo.cb = sizeof(StartupInfo);
1780     ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
1781 
1782     if (Service->lpImage->hToken)
1783     {
1784         /* User token: Run the service under the user account */
1785 
1786         if (!CreateEnvironmentBlock(&lpEnvironment, Service->lpImage->hToken, FALSE))
1787         {
1788             /* We failed, run the service with the current environment */
1789             DPRINT1("CreateEnvironmentBlock() failed with error %d; service '%S' will run with current environment\n",
1790                     GetLastError(), Service->lpServiceName);
1791             lpEnvironment = NULL;
1792         }
1793 
1794         /* Impersonate the new user */
1795         Result = ImpersonateLoggedOnUser(Service->lpImage->hToken);
1796         if (Result)
1797         {
1798             /* Launch the process in the user's logon session */
1799             Result = CreateProcessAsUserW(Service->lpImage->hToken,
1800                                           NULL,
1801                                           Service->lpImage->pszImagePath,
1802                                           NULL,
1803                                           NULL,
1804                                           FALSE,
1805                                           CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS | CREATE_SUSPENDED,
1806                                           lpEnvironment,
1807                                           NULL,
1808                                           &StartupInfo,
1809                                           &ProcessInformation);
1810             if (!Result)
1811                 dwError = GetLastError();
1812 
1813             /* Revert the impersonation */
1814             RevertToSelf();
1815         }
1816         else
1817         {
1818             dwError = GetLastError();
1819             DPRINT1("ImpersonateLoggedOnUser() failed with error %d\n", dwError);
1820         }
1821     }
1822     else
1823     {
1824         /* No user token: Run the service under the LocalSystem account */
1825 
1826         if (!CreateEnvironmentBlock(&lpEnvironment, NULL, TRUE))
1827         {
1828             /* We failed, run the service with the current environment */
1829             DPRINT1("CreateEnvironmentBlock() failed with error %d; service '%S' will run with current environment\n",
1830                     GetLastError(), Service->lpServiceName);
1831             lpEnvironment = NULL;
1832         }
1833 
1834         /* Use the interactive desktop if the service is interactive */
1835         if ((NoInteractiveServices == 0) &&
1836             (Service->Status.dwServiceType & SERVICE_INTERACTIVE_PROCESS))
1837         {
1838             StartupInfo.dwFlags |= STARTF_INHERITDESKTOP;
1839             StartupInfo.lpDesktop = L"WinSta0\\Default";
1840         }
1841 
1842         if (!ScmIsSecurityService(Service->lpImage))
1843         {
1844             Result = CreateProcessW(NULL,
1845                                     Service->lpImage->pszImagePath,
1846                                     NULL,
1847                                     NULL,
1848                                     FALSE,
1849                                     CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS | CREATE_SUSPENDED,
1850                                     lpEnvironment,
1851                                     NULL,
1852                                     &StartupInfo,
1853                                     &ProcessInformation);
1854             if (!Result)
1855                 dwError = GetLastError();
1856         }
1857         else
1858         {
1859             Result = TRUE;
1860             dwError = ERROR_SUCCESS;
1861         }
1862     }
1863 
1864     if (lpEnvironment)
1865         DestroyEnvironmentBlock(lpEnvironment);
1866 
1867     if (!Result)
1868     {
1869         DPRINT1("Starting '%S' failed with error %d\n",
1870                 Service->lpServiceName, dwError);
1871         return dwError;
1872     }
1873 
1874     DPRINT("Process Id: %lu  Handle %p\n",
1875            ProcessInformation.dwProcessId,
1876            ProcessInformation.hProcess);
1877     DPRINT("Thread Id: %lu  Handle %p\n",
1878            ProcessInformation.dwThreadId,
1879            ProcessInformation.hThread);
1880 
1881     /* Get the process handle and ID */
1882     Service->lpImage->hProcess = ProcessInformation.hProcess;
1883     Service->lpImage->dwProcessId = ProcessInformation.dwProcessId;
1884 
1885     /* Resume the main thread and close its handle */
1886     ResumeThread(ProcessInformation.hThread);
1887     CloseHandle(ProcessInformation.hThread);
1888 
1889     /* Connect control pipe */
1890     dwError = ScmWaitForServiceConnect(Service);
1891     if (dwError != ERROR_SUCCESS)
1892     {
1893         DPRINT1("Connecting control pipe failed! (Error %lu)\n", dwError);
1894         Service->lpImage->dwProcessId = 0;
1895         return dwError;
1896     }
1897 
1898 Quit:
1899     /* Send the start command and return */
1900     return ScmControlServiceEx(Service->lpImage->hControlPipe,
1901                                Service->lpServiceName,
1902                                (Service->Status.dwServiceType & SERVICE_WIN32_OWN_PROCESS)
1903                                   ? SERVICE_CONTROL_START_OWN : SERVICE_CONTROL_START_SHARE,
1904                                (SERVICE_STATUS_HANDLE)Service,
1905                                Service->dwServiceTag,
1906                                argc, argv);
1907 }
1908 
1909 
1910 static DWORD
ScmLoadService(PSERVICE Service,DWORD argc,const PCWSTR * argv)1911 ScmLoadService(PSERVICE Service,
1912                DWORD argc,
1913                const PCWSTR* argv)
1914 {
1915     PSERVICE_GROUP Group = Service->lpGroup;
1916     DWORD dwError = ERROR_SUCCESS;
1917     LPCWSTR lpLogStrings[2];
1918     WCHAR szLogBuffer[80];
1919 
1920     DPRINT("ScmLoadService() called\n");
1921     DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
1922 
1923     if (Service->Status.dwCurrentState != SERVICE_STOPPED)
1924     {
1925         DPRINT("Service %S is already running\n", Service->lpServiceName);
1926         return ERROR_SERVICE_ALREADY_RUNNING;
1927     }
1928 
1929     DPRINT("Service->Type: %lu\n", Service->Status.dwServiceType);
1930 
1931     if (Service->Status.dwServiceType & SERVICE_DRIVER)
1932     {
1933         /* Start the driver */
1934         dwError = ScmStartDriver(Service);
1935     }
1936     else // if (Service->Status.dwServiceType & (SERVICE_WIN32 | SERVICE_INTERACTIVE_PROCESS))
1937     {
1938         /* Start user-mode service */
1939         dwError = ScmCreateOrReferenceServiceImage(Service);
1940         if (dwError == ERROR_SUCCESS)
1941         {
1942             dwError = ScmStartUserModeService(Service, argc, argv);
1943             if (dwError == ERROR_SUCCESS)
1944             {
1945                 Service->Status.dwCurrentState = SERVICE_START_PENDING;
1946                 Service->Status.dwControlsAccepted = 0;
1947                 ScmReferenceService(Service);
1948             }
1949             else
1950             {
1951                 Service->lpImage->dwImageRunCount--;
1952                 if (Service->lpImage->dwImageRunCount == 0)
1953                 {
1954                     ScmRemoveServiceImage(Service->lpImage);
1955                     Service->lpImage = NULL;
1956                 }
1957             }
1958         }
1959     }
1960 
1961     DPRINT("ScmLoadService() done (Error %lu)\n", dwError);
1962 
1963     if (dwError == ERROR_SUCCESS)
1964     {
1965         if (Group != NULL)
1966         {
1967             Group->ServicesRunning = TRUE;
1968         }
1969 
1970         /* Log a successful service start */
1971         LoadStringW(GetModuleHandle(NULL), IDS_SERVICE_START, szLogBuffer, 80);
1972         lpLogStrings[0] = Service->lpDisplayName;
1973         lpLogStrings[1] = szLogBuffer;
1974 
1975         ScmLogEvent(EVENT_SERVICE_CONTROL_SUCCESS,
1976                     EVENTLOG_INFORMATION_TYPE,
1977                     2,
1978                     lpLogStrings);
1979     }
1980     else
1981     {
1982         if (Service->dwErrorControl != SERVICE_ERROR_IGNORE)
1983         {
1984             /* Log a failed service start */
1985             StringCchPrintfW(szLogBuffer, ARRAYSIZE(szLogBuffer),
1986                              L"%lu", dwError);
1987             lpLogStrings[0] = Service->lpServiceName;
1988             lpLogStrings[1] = szLogBuffer;
1989             ScmLogEvent(EVENT_SERVICE_START_FAILED,
1990                         EVENTLOG_ERROR_TYPE,
1991                         2,
1992                         lpLogStrings);
1993         }
1994 
1995 #if 0
1996         switch (Service->dwErrorControl)
1997         {
1998             case SERVICE_ERROR_SEVERE:
1999                 if (IsLastKnownGood == FALSE)
2000                 {
2001                     /* FIXME: Boot last known good configuration */
2002                 }
2003                 break;
2004 
2005             case SERVICE_ERROR_CRITICAL:
2006                 if (IsLastKnownGood == FALSE)
2007                 {
2008                     /* FIXME: Boot last known good configuration */
2009                 }
2010                 else
2011                 {
2012                     /* FIXME: BSOD! */
2013                 }
2014                 break;
2015         }
2016 #endif
2017     }
2018 
2019     return dwError;
2020 }
2021 
2022 
2023 DWORD
ScmStartService(PSERVICE Service,DWORD argc,const PCWSTR * argv)2024 ScmStartService(PSERVICE Service,
2025                 DWORD argc,
2026                 const PCWSTR* argv)
2027 {
2028     DWORD dwError = ERROR_SUCCESS;
2029     SC_RPC_LOCK Lock = NULL;
2030 
2031     DPRINT("ScmStartService() called\n");
2032     DPRINT("Start Service %p (%S)\n", Service, Service->lpServiceName);
2033 
2034     /* Acquire the service control critical section, to synchronize starts */
2035     EnterCriticalSection(&ControlServiceCriticalSection);
2036 
2037     /*
2038      * Acquire the user service start lock while the service is starting, if
2039      * needed (i.e. if we are not starting it during the initialization phase).
2040      * If we don't success, bail out.
2041      */
2042     if (!ScmInitialize)
2043     {
2044         dwError = ScmAcquireServiceStartLock(TRUE, &Lock);
2045         if (dwError != ERROR_SUCCESS)
2046             goto done;
2047     }
2048 
2049     /* Really start the service */
2050     dwError = ScmLoadService(Service, argc, argv);
2051 
2052     /* Release the service start lock, if needed, and the critical section */
2053     if (Lock) ScmReleaseServiceStartLock(&Lock);
2054 
2055 done:
2056     LeaveCriticalSection(&ControlServiceCriticalSection);
2057 
2058     DPRINT("ScmStartService() done (Error %lu)\n", dwError);
2059 
2060     return dwError;
2061 }
2062 
2063 
2064 VOID
ScmAutoStartServices(VOID)2065 ScmAutoStartServices(VOID)
2066 {
2067     DWORD dwError;
2068     PLIST_ENTRY GroupEntry;
2069     PLIST_ENTRY ServiceEntry;
2070     PSERVICE_GROUP CurrentGroup;
2071     PSERVICE CurrentService;
2072     WCHAR szSafeBootServicePath[MAX_PATH];
2073     DWORD SafeBootEnabled;
2074     HKEY hKey;
2075     DWORD dwKeySize;
2076     ULONG i;
2077 
2078     /*
2079      * This function MUST be called ONLY at initialization time.
2080      * Therefore, no need to acquire the user service start lock.
2081      */
2082     ASSERT(ScmInitialize);
2083 
2084     /* Retrieve the SafeBoot parameter */
2085     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2086                             L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Option",
2087                             0,
2088                             KEY_READ,
2089                             &hKey);
2090     if (dwError == ERROR_SUCCESS)
2091     {
2092         dwKeySize = sizeof(SafeBootEnabled);
2093         dwError = RegQueryValueExW(hKey,
2094                                    L"OptionValue",
2095                                    0,
2096                                    NULL,
2097                                    (LPBYTE)&SafeBootEnabled,
2098                                    &dwKeySize);
2099         RegCloseKey(hKey);
2100     }
2101 
2102     /* Default to Normal boot if the value doesn't exist */
2103     if (dwError != ERROR_SUCCESS)
2104         SafeBootEnabled = 0;
2105 
2106     /* Acquire the service control critical section, to synchronize starts */
2107     EnterCriticalSection(&ControlServiceCriticalSection);
2108 
2109     /* Clear 'ServiceVisited' flag (or set if not to start in Safe Mode) */
2110     ServiceEntry = ServiceListHead.Flink;
2111     while (ServiceEntry != &ServiceListHead)
2112     {
2113         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2114 
2115         /* Build the safe boot path */
2116         StringCchCopyW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
2117                        L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot");
2118 
2119         switch (SafeBootEnabled)
2120         {
2121             /* NOTE: Assumes MINIMAL (1) and DSREPAIR (3) load same items */
2122             case 1:
2123             case 3:
2124                 StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
2125                               L"\\Minimal\\");
2126                 break;
2127 
2128             case 2:
2129                 StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
2130                               L"\\Network\\");
2131                 break;
2132         }
2133 
2134         if (SafeBootEnabled != 0)
2135         {
2136             /* If key does not exist then do not assume safe mode */
2137             dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2138                                     szSafeBootServicePath,
2139                                     0,
2140                                     KEY_READ,
2141                                     &hKey);
2142             if (dwError == ERROR_SUCCESS)
2143             {
2144                 RegCloseKey(hKey);
2145 
2146                 /* Finish Safe Boot path off */
2147                 StringCchCatW(szSafeBootServicePath, ARRAYSIZE(szSafeBootServicePath),
2148                               CurrentService->lpServiceName);
2149 
2150                 /* Check that the key is in the Safe Boot path */
2151                 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2152                                         szSafeBootServicePath,
2153                                         0,
2154                                         KEY_READ,
2155                                         &hKey);
2156                 if (dwError != ERROR_SUCCESS)
2157                 {
2158                     /* Mark service as visited so it is not auto-started */
2159                     CurrentService->ServiceVisited = TRUE;
2160                 }
2161                 else
2162                 {
2163                     /* Must be auto-started in safe mode - mark as unvisited */
2164                     RegCloseKey(hKey);
2165                     CurrentService->ServiceVisited = FALSE;
2166                 }
2167             }
2168             else
2169             {
2170                 DPRINT1("WARNING: Could not open the associated Safe Boot key\n");
2171                 CurrentService->ServiceVisited = FALSE;
2172             }
2173         }
2174 
2175         ServiceEntry = ServiceEntry->Flink;
2176     }
2177 
2178     /* Start all services which are members of an existing group */
2179     GroupEntry = GroupListHead.Flink;
2180     while (GroupEntry != &GroupListHead)
2181     {
2182         CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
2183 
2184         DPRINT("Group '%S'\n", CurrentGroup->lpGroupName);
2185 
2186         /* Start all services witch have a valid tag */
2187         for (i = 0; i < CurrentGroup->TagCount; i++)
2188         {
2189             ServiceEntry = ServiceListHead.Flink;
2190             while (ServiceEntry != &ServiceListHead)
2191             {
2192                 CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2193 
2194                 if ((CurrentService->lpGroup == CurrentGroup) &&
2195                     (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2196                     (CurrentService->ServiceVisited == FALSE) &&
2197                     (CurrentService->dwTag == CurrentGroup->TagArray[i]))
2198                 {
2199                     CurrentService->ServiceVisited = TRUE;
2200                     ScmLoadService(CurrentService, 0, NULL);
2201                 }
2202 
2203                 ServiceEntry = ServiceEntry->Flink;
2204              }
2205         }
2206 
2207         /* Start all services which have an invalid tag or which do not have a tag */
2208         ServiceEntry = ServiceListHead.Flink;
2209         while (ServiceEntry != &ServiceListHead)
2210         {
2211             CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2212 
2213             if ((CurrentService->lpGroup == CurrentGroup) &&
2214                 (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2215                 (CurrentService->ServiceVisited == FALSE))
2216             {
2217                 CurrentService->ServiceVisited = TRUE;
2218                 ScmLoadService(CurrentService, 0, NULL);
2219             }
2220 
2221             ServiceEntry = ServiceEntry->Flink;
2222         }
2223 
2224         GroupEntry = GroupEntry->Flink;
2225     }
2226 
2227     /* Start all services which are members of any non-existing group */
2228     ServiceEntry = ServiceListHead.Flink;
2229     while (ServiceEntry != &ServiceListHead)
2230     {
2231         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2232 
2233         if ((CurrentService->lpGroup != NULL) &&
2234             (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2235             (CurrentService->ServiceVisited == FALSE))
2236         {
2237             CurrentService->ServiceVisited = TRUE;
2238             ScmLoadService(CurrentService, 0, NULL);
2239         }
2240 
2241         ServiceEntry = ServiceEntry->Flink;
2242     }
2243 
2244     /* Start all services which are not a member of any group */
2245     ServiceEntry = ServiceListHead.Flink;
2246     while (ServiceEntry != &ServiceListHead)
2247     {
2248         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2249 
2250         if ((CurrentService->lpGroup == NULL) &&
2251             (CurrentService->dwStartType == SERVICE_AUTO_START) &&
2252             (CurrentService->ServiceVisited == FALSE))
2253         {
2254             CurrentService->ServiceVisited = TRUE;
2255             ScmLoadService(CurrentService, 0, NULL);
2256         }
2257 
2258         ServiceEntry = ServiceEntry->Flink;
2259     }
2260 
2261     /* Clear 'ServiceVisited' flag again */
2262     ServiceEntry = ServiceListHead.Flink;
2263     while (ServiceEntry != &ServiceListHead)
2264     {
2265         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2266         CurrentService->ServiceVisited = FALSE;
2267         ServiceEntry = ServiceEntry->Flink;
2268     }
2269 
2270     /* Release the critical section */
2271     LeaveCriticalSection(&ControlServiceCriticalSection);
2272 }
2273 
2274 
2275 VOID
ScmAutoShutdownServices(VOID)2276 ScmAutoShutdownServices(VOID)
2277 {
2278     PLIST_ENTRY ServiceEntry;
2279     PSERVICE CurrentService;
2280 
2281     DPRINT("ScmAutoShutdownServices() called\n");
2282 
2283     /* Lock the service database exclusively */
2284     ScmLockDatabaseExclusive();
2285 
2286     ServiceEntry = ServiceListHead.Flink;
2287     while (ServiceEntry != &ServiceListHead)
2288     {
2289         CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
2290 
2291         if ((CurrentService->Status.dwControlsAccepted & SERVICE_ACCEPT_SHUTDOWN) &&
2292             (CurrentService->Status.dwCurrentState == SERVICE_RUNNING ||
2293              CurrentService->Status.dwCurrentState == SERVICE_START_PENDING))
2294         {
2295             /* Send the shutdown notification */
2296             DPRINT("Shutdown service: %S\n", CurrentService->lpServiceName);
2297             ScmControlService(CurrentService->lpImage->hControlPipe,
2298                               CurrentService->lpServiceName,
2299                               SERVICE_CONTROL_SHUTDOWN,
2300                               (SERVICE_STATUS_HANDLE)CurrentService);
2301         }
2302 
2303         ServiceEntry = ServiceEntry->Flink;
2304     }
2305 
2306     /* Unlock the service database */
2307     ScmUnlockDatabase();
2308 
2309     DPRINT("ScmAutoShutdownServices() done\n");
2310 }
2311 
2312 
2313 BOOL
ScmLockDatabaseExclusive(VOID)2314 ScmLockDatabaseExclusive(VOID)
2315 {
2316     return RtlAcquireResourceExclusive(&DatabaseLock, TRUE);
2317 }
2318 
2319 
2320 BOOL
ScmLockDatabaseShared(VOID)2321 ScmLockDatabaseShared(VOID)
2322 {
2323     return RtlAcquireResourceShared(&DatabaseLock, TRUE);
2324 }
2325 
2326 
2327 VOID
ScmUnlockDatabase(VOID)2328 ScmUnlockDatabase(VOID)
2329 {
2330     RtlReleaseResource(&DatabaseLock);
2331 }
2332 
2333 
2334 VOID
ScmInitNamedPipeCriticalSection(VOID)2335 ScmInitNamedPipeCriticalSection(VOID)
2336 {
2337     HKEY hKey;
2338     DWORD dwKeySize;
2339     DWORD dwError;
2340 
2341     InitializeCriticalSection(&ControlServiceCriticalSection);
2342 
2343     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2344                             L"SYSTEM\\CurrentControlSet\\Control",
2345                             0,
2346                             KEY_READ,
2347                             &hKey);
2348    if (dwError == ERROR_SUCCESS)
2349    {
2350         dwKeySize = sizeof(PipeTimeout);
2351         RegQueryValueExW(hKey,
2352                          L"ServicesPipeTimeout",
2353                          0,
2354                          NULL,
2355                          (LPBYTE)&PipeTimeout,
2356                          &dwKeySize);
2357        RegCloseKey(hKey);
2358    }
2359 }
2360 
2361 
2362 VOID
ScmDeleteNamedPipeCriticalSection(VOID)2363 ScmDeleteNamedPipeCriticalSection(VOID)
2364 {
2365     DeleteCriticalSection(&ControlServiceCriticalSection);
2366 }
2367 
2368 /* EOF */
2369