xref: /reactos/base/system/services/config.c (revision 9be382ec)
1 /*
2  * PROJECT:     ReactOS Service Control Manager
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        base/system/services/config.c
5  * PURPOSE:     Service configuration interface
6  * COPYRIGHT:   Copyright 2005 Eric Kohl
7  *
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include "services.h"
13 #include <ntsecapi.h>
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 struct ustring
19 {
20     DWORD Length;
21     DWORD MaximumLength;
22     unsigned char *Buffer;
23 };
24 
25 NTSTATUS
26 WINAPI
27 SystemFunction005(
28     const struct ustring *in,
29     const struct ustring *key,
30     struct ustring *out);
31 
32 NTSTATUS
33 WINAPI
34 SystemFunction028(
35     IN PVOID ContextHandle,
36     OUT LPBYTE SessionKey);
37 
38 /* FUNCTIONS *****************************************************************/
39 
40 
41 DWORD
42 ScmOpenServiceKey(LPWSTR lpServiceName,
43                   REGSAM samDesired,
44                   PHKEY phKey)
45 {
46     HKEY hServicesKey = NULL;
47     DWORD dwError;
48 
49     *phKey = NULL;
50 
51     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
52                             L"System\\CurrentControlSet\\Services",
53                             0,
54                             KEY_READ,
55                             &hServicesKey);
56     if (dwError != ERROR_SUCCESS)
57         return dwError;
58 
59     dwError = RegOpenKeyExW(hServicesKey,
60                             lpServiceName,
61                             0,
62                             samDesired,
63                             phKey);
64 
65     RegCloseKey(hServicesKey);
66 
67     return dwError;
68 }
69 
70 
71 DWORD
72 ScmCreateServiceKey(LPCWSTR lpServiceName,
73                     REGSAM samDesired,
74                     PHKEY phKey)
75 {
76     HKEY hServicesKey = NULL;
77     DWORD dwDisposition;
78     DWORD dwError;
79 
80     *phKey = NULL;
81 
82     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
83                             L"System\\CurrentControlSet\\Services",
84                             0,
85                             KEY_READ | KEY_CREATE_SUB_KEY,
86                             &hServicesKey);
87     if (dwError != ERROR_SUCCESS)
88         return dwError;
89 
90     dwError = RegCreateKeyExW(hServicesKey,
91                               lpServiceName,
92                               0,
93                               NULL,
94                               REG_OPTION_NON_VOLATILE,
95                               samDesired,
96                               NULL,
97                               phKey,
98                               &dwDisposition);
99 #if 0
100     if ((dwError == ERROR_SUCCESS) &&
101         (dwDisposition == REG_OPENED_EXISTING_KEY))
102     {
103         RegCloseKey(*phKey);
104         *phKey = NULL;
105         dwError = ERROR_SERVICE_EXISTS;
106     }
107 #endif
108 
109     RegCloseKey(hServicesKey);
110 
111     return dwError;
112 }
113 
114 
115 
116 DWORD
117 ScmWriteDependencies(HKEY hServiceKey,
118                      LPCWSTR lpDependencies,
119                      DWORD dwDependenciesLength)
120 {
121     DWORD dwError = ERROR_SUCCESS;
122     SIZE_T cchGroupLength = 0;
123     SIZE_T cchServiceLength = 0;
124     SIZE_T cchLength;
125     LPWSTR lpGroupDeps;
126     LPWSTR lpServiceDeps;
127     LPCWSTR lpSrc;
128     LPWSTR lpDst;
129 
130     if (*lpDependencies == 0)
131     {
132         RegDeleteValueW(hServiceKey,
133                        L"DependOnService");
134         RegDeleteValueW(hServiceKey,
135                        L"DependOnGroup");
136     }
137     else
138     {
139         lpGroupDeps = HeapAlloc(GetProcessHeap(),
140                                 HEAP_ZERO_MEMORY,
141                                 (dwDependenciesLength + 2) * sizeof(WCHAR));
142         if (lpGroupDeps == NULL)
143             return ERROR_NOT_ENOUGH_MEMORY;
144 
145         lpSrc = lpDependencies;
146         lpDst = lpGroupDeps;
147         while (*lpSrc != 0)
148         {
149             cchLength = wcslen(lpSrc) + 1;
150             if (*lpSrc == SC_GROUP_IDENTIFIERW)
151             {
152                 lpSrc++;
153                 cchLength--;
154                 cchGroupLength += cchLength;
155                 wcscpy(lpDst, lpSrc);
156                 lpDst = lpDst + cchLength;
157             }
158 
159             lpSrc = lpSrc + cchLength;
160         }
161         *lpDst = 0;
162         lpDst++;
163         cchGroupLength++;
164 
165         lpSrc = lpDependencies;
166         lpServiceDeps = lpDst;
167         while (*lpSrc != 0)
168         {
169             cchLength = wcslen(lpSrc) + 1;
170             if (*lpSrc != SC_GROUP_IDENTIFIERW)
171             {
172                 cchServiceLength += cchLength;
173                 wcscpy(lpDst, lpSrc);
174                 lpDst = lpDst + cchLength;
175             }
176 
177             lpSrc = lpSrc + cchLength;
178         }
179         *lpDst = 0;
180         cchServiceLength++;
181 
182         if (cchGroupLength > 1)
183         {
184             dwError = RegSetValueExW(hServiceKey,
185                                      L"DependOnGroup",
186                                      0,
187                                      REG_MULTI_SZ,
188                                      (LPBYTE)lpGroupDeps,
189                                      (DWORD)(cchGroupLength * sizeof(WCHAR)));
190         }
191         else
192         {
193             RegDeleteValueW(hServiceKey,
194                             L"DependOnGroup");
195         }
196 
197         if (dwError == ERROR_SUCCESS)
198         {
199             if (cchServiceLength > 1)
200             {
201                 dwError = RegSetValueExW(hServiceKey,
202                                          L"DependOnService",
203                                          0,
204                                          REG_MULTI_SZ,
205                                          (LPBYTE)lpServiceDeps,
206                                          (DWORD)(cchServiceLength * sizeof(WCHAR)));
207             }
208             else
209             {
210                 RegDeleteValueW(hServiceKey,
211                                 L"DependOnService");
212             }
213         }
214 
215         HeapFree(GetProcessHeap(), 0, lpGroupDeps);
216     }
217 
218     return dwError;
219 }
220 
221 
222 DWORD
223 ScmMarkServiceForDelete(PSERVICE pService)
224 {
225     HKEY hServiceKey = NULL;
226     DWORD dwValue = 1;
227     DWORD dwError;
228 
229     DPRINT("ScmMarkServiceForDelete() called\n");
230 
231     dwError = ScmOpenServiceKey(pService->lpServiceName,
232                                 KEY_WRITE,
233                                 &hServiceKey);
234     if (dwError != ERROR_SUCCESS)
235         return dwError;
236 
237     dwError = RegSetValueExW(hServiceKey,
238                              L"DeleteFlag",
239                              0,
240                              REG_DWORD,
241                              (LPBYTE)&dwValue,
242                              sizeof(DWORD));
243 
244     RegCloseKey(hServiceKey);
245 
246     return dwError;
247 }
248 
249 
250 BOOL
251 ScmIsDeleteFlagSet(HKEY hServiceKey)
252 {
253     DWORD dwError;
254     DWORD dwType;
255     DWORD dwFlag;
256     DWORD dwSize = sizeof(DWORD);
257 
258     dwError = RegQueryValueExW(hServiceKey,
259                                L"DeleteFlag",
260                                0,
261                                &dwType,
262                                (LPBYTE)&dwFlag,
263                                &dwSize);
264 
265     return (dwError == ERROR_SUCCESS);
266 }
267 
268 
269 DWORD
270 ScmReadString(HKEY hServiceKey,
271               LPCWSTR lpValueName,
272               LPWSTR *lpValue)
273 {
274     DWORD dwError = 0;
275     DWORD dwSize = 0;
276     DWORD dwType = 0;
277     LPWSTR ptr = NULL;
278     LPWSTR expanded = NULL;
279 
280     *lpValue = NULL;
281 
282     dwError = RegQueryValueExW(hServiceKey,
283                                lpValueName,
284                                0,
285                                &dwType,
286                                NULL,
287                                &dwSize);
288     if (dwError != ERROR_SUCCESS)
289         return dwError;
290 
291     ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
292     if (ptr == NULL)
293         return ERROR_NOT_ENOUGH_MEMORY;
294 
295     dwError = RegQueryValueExW(hServiceKey,
296                                lpValueName,
297                                0,
298                                &dwType,
299                                (LPBYTE)ptr,
300                                &dwSize);
301     if (dwError != ERROR_SUCCESS)
302     {
303         HeapFree(GetProcessHeap(), 0, ptr);
304         return dwError;
305     }
306 
307     if (dwType == REG_EXPAND_SZ)
308     {
309         /* Expand the value... */
310         dwSize = ExpandEnvironmentStringsW(ptr, NULL, 0);
311         if (dwSize > 0)
312         {
313             expanded = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize * sizeof(WCHAR));
314             if (expanded)
315             {
316                 if (dwSize == ExpandEnvironmentStringsW(ptr, expanded, dwSize))
317                 {
318                     *lpValue = expanded;
319                     dwError = ERROR_SUCCESS;
320                 }
321                 else
322                 {
323                     dwError = GetLastError();
324                     HeapFree(GetProcessHeap(), 0, expanded);
325                 }
326             }
327             else
328             {
329                 dwError = ERROR_NOT_ENOUGH_MEMORY;
330             }
331         }
332         else
333         {
334             dwError = GetLastError();
335         }
336 
337         HeapFree(GetProcessHeap(), 0, ptr);
338     }
339     else
340     {
341         *lpValue = ptr;
342     }
343 
344     return dwError;
345 }
346 
347 
348 DWORD
349 ScmReadDependencies(HKEY hServiceKey,
350                     LPWSTR *lpDependencies,
351                     DWORD *lpdwDependenciesLength)
352 {
353     LPWSTR lpGroups = NULL;
354     LPWSTR lpServices = NULL;
355     SIZE_T cchGroupsLength = 0;
356     SIZE_T cchServicesLength = 0;
357     LPWSTR lpSrc;
358     LPWSTR lpDest;
359     SIZE_T cchLength;
360     SIZE_T cchTotalLength;
361 
362     *lpDependencies = NULL;
363     *lpdwDependenciesLength = 0;
364 
365     /* Read the dependency values */
366     ScmReadString(hServiceKey,
367                   L"DependOnGroup",
368                   &lpGroups);
369 
370     ScmReadString(hServiceKey,
371                   L"DependOnService",
372                   &lpServices);
373 
374     /* Leave, if there are no dependencies */
375     if (lpGroups == NULL && lpServices == NULL)
376         return ERROR_SUCCESS;
377 
378     /* Determine the total buffer size for the dependencies */
379     if (lpGroups)
380     {
381         DPRINT("Groups:\n");
382         lpSrc = lpGroups;
383         while (*lpSrc != 0)
384         {
385             DPRINT("  %S\n", lpSrc);
386 
387             cchLength = wcslen(lpSrc) + 1;
388             cchGroupsLength += cchLength + 1;
389 
390             lpSrc = lpSrc + cchLength;
391         }
392     }
393 
394     if (lpServices)
395     {
396         DPRINT("Services:\n");
397         lpSrc = lpServices;
398         while (*lpSrc != 0)
399         {
400             DPRINT("  %S\n", lpSrc);
401 
402             cchLength = wcslen(lpSrc) + 1;
403             cchServicesLength += cchLength;
404 
405             lpSrc = lpSrc + cchLength;
406         }
407     }
408 
409     cchTotalLength = cchGroupsLength + cchServicesLength + 1;
410     DPRINT("cchTotalLength: %lu\n", cchTotalLength);
411 
412     /* Allocate the common buffer for the dependencies */
413     *lpDependencies = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cchTotalLength * sizeof(WCHAR));
414     if (*lpDependencies == NULL)
415     {
416         if (lpGroups)
417             HeapFree(GetProcessHeap(), 0, lpGroups);
418 
419         if (lpServices)
420             HeapFree(GetProcessHeap(), 0, lpServices);
421 
422         return ERROR_NOT_ENOUGH_MEMORY;
423     }
424 
425     /* Return the allocated buffer length in characters */
426     *lpdwDependenciesLength = (DWORD)cchTotalLength;
427 
428     /* Copy the service dependencies into the common buffer */
429     lpDest = *lpDependencies;
430     if (lpServices)
431     {
432         memcpy(lpDest,
433                lpServices,
434                cchServicesLength * sizeof(WCHAR));
435 
436         lpDest = lpDest + cchServicesLength;
437     }
438 
439     /* Copy the group dependencies into the common buffer */
440     if (lpGroups)
441     {
442         lpSrc = lpGroups;
443         while (*lpSrc != 0)
444         {
445             cchLength = wcslen(lpSrc) + 1;
446 
447             *lpDest = SC_GROUP_IDENTIFIERW;
448             lpDest++;
449 
450             wcscpy(lpDest, lpSrc);
451 
452             lpDest = lpDest + cchLength;
453             lpSrc = lpSrc + cchLength;
454         }
455     }
456 
457     /* Free the temporary buffers */
458     if (lpGroups)
459         HeapFree(GetProcessHeap(), 0, lpGroups);
460 
461     if (lpServices)
462         HeapFree(GetProcessHeap(), 0, lpServices);
463 
464     return ERROR_SUCCESS;
465 }
466 
467 
468 DWORD
469 ScmSetServicePassword(
470     IN PCWSTR pszServiceName,
471     IN PCWSTR pszPassword)
472 {
473     OBJECT_ATTRIBUTES ObjectAttributes;
474     LSA_HANDLE PolicyHandle = NULL;
475     UNICODE_STRING ServiceName = {0, 0, NULL};
476     UNICODE_STRING Password;
477     NTSTATUS Status;
478     DWORD dwError = ERROR_SUCCESS;
479     SIZE_T ServiceNameLength;
480 
481     RtlZeroMemory(&ObjectAttributes, sizeof(OBJECT_ATTRIBUTES));
482 
483     ServiceNameLength = wcslen(pszServiceName);
484     if (ServiceNameLength > (UNICODE_STRING_MAX_CHARS - 4))
485     {
486         return ERROR_INVALID_PARAMETER;
487     }
488 
489     Status = LsaOpenPolicy(NULL,
490                            &ObjectAttributes,
491                            POLICY_CREATE_SECRET,
492                            &PolicyHandle);
493     if (!NT_SUCCESS(Status))
494         return RtlNtStatusToDosError(Status);
495 
496     ServiceName.Length = ((USHORT)ServiceNameLength + 4) * sizeof(WCHAR);
497     ServiceName.MaximumLength = ServiceName.Length + sizeof(WCHAR);
498     ServiceName.Buffer = HeapAlloc(GetProcessHeap(),
499                                    HEAP_ZERO_MEMORY,
500                                    ServiceName.MaximumLength);
501     if (ServiceName.Buffer == NULL)
502         return ERROR_NOT_ENOUGH_MEMORY;
503 
504     wcscpy(ServiceName.Buffer, L"_SC_");
505     wcscat(ServiceName.Buffer, pszServiceName);
506 
507     RtlInitUnicodeString(&Password, pszPassword);
508 
509     Status = LsaStorePrivateData(PolicyHandle,
510                                  &ServiceName,
511                                  pszPassword ? &Password : NULL);
512     if (!NT_SUCCESS(Status))
513     {
514         dwError = RtlNtStatusToDosError(Status);
515         goto done;
516     }
517 
518 done:
519     if (ServiceName.Buffer != NULL)
520         HeapFree(GetProcessHeap(), 0, ServiceName.Buffer);
521 
522     if (PolicyHandle != NULL)
523         LsaClose(PolicyHandle);
524 
525     return dwError;
526 }
527 
528 
529 DWORD
530 ScmWriteSecurityDescriptor(
531     _In_ HKEY hServiceKey,
532     _In_ PSECURITY_DESCRIPTOR pSecurityDescriptor)
533 {
534     HKEY hSecurityKey = NULL;
535     DWORD dwDisposition;
536     DWORD dwError;
537 
538     DPRINT("ScmWriteSecurityDescriptor(%p %p)\n", hServiceKey, pSecurityDescriptor);
539 
540     dwError = RegCreateKeyExW(hServiceKey,
541                               L"Security",
542                               0,
543                               NULL,
544                               REG_OPTION_NON_VOLATILE,
545                               KEY_SET_VALUE,
546                               NULL,
547                               &hSecurityKey,
548                               &dwDisposition);
549     if (dwError != ERROR_SUCCESS)
550         return dwError;
551 
552     dwError = RegSetValueExW(hSecurityKey,
553                              L"Security",
554                              0,
555                              REG_BINARY,
556                              (LPBYTE)pSecurityDescriptor,
557                              RtlLengthSecurityDescriptor(pSecurityDescriptor));
558 
559     RegCloseKey(hSecurityKey);
560 
561     return dwError;
562 }
563 
564 
565 DWORD
566 ScmReadSecurityDescriptor(
567     _In_ HKEY hServiceKey,
568     _Out_ PSECURITY_DESCRIPTOR *ppSecurityDescriptor)
569 {
570     PSECURITY_DESCRIPTOR pRelativeSD = NULL;
571     HKEY hSecurityKey = NULL;
572     DWORD dwBufferLength = 0;
573     DWORD dwType;
574     DWORD dwError;
575 
576     DPRINT("ScmReadSecurityDescriptor(%p %p)\n", hServiceKey, ppSecurityDescriptor);
577 
578     *ppSecurityDescriptor = NULL;
579 
580     dwError = RegOpenKeyExW(hServiceKey,
581                             L"Security",
582                             0,
583                             KEY_QUERY_VALUE,
584                             &hSecurityKey);
585     if (dwError != ERROR_SUCCESS)
586     {
587         DPRINT("RegOpenKeyExW() failed (Error %lu)\n", dwError);
588 
589         /* Do not fail if the Security key does not exist */
590         if (dwError == ERROR_FILE_NOT_FOUND)
591             dwError = ERROR_SUCCESS;
592         goto done;
593     }
594 
595     dwError = RegQueryValueExW(hSecurityKey,
596                                L"Security",
597                                0,
598                                &dwType,
599                                NULL,
600                                &dwBufferLength);
601     if (dwError != ERROR_SUCCESS)
602     {
603         DPRINT("RegQueryValueExW() failed (Error %lu)\n", dwError);
604 
605         /* Do not fail if the Security value does not exist */
606         if (dwError == ERROR_FILE_NOT_FOUND)
607             dwError = ERROR_SUCCESS;
608         goto done;
609     }
610 
611     DPRINT("dwBufferLength: %lu\n", dwBufferLength);
612     pRelativeSD = RtlAllocateHeap(RtlGetProcessHeap(),
613                                   HEAP_ZERO_MEMORY,
614                                   dwBufferLength);
615     if (pRelativeSD == NULL)
616     {
617         return ERROR_OUTOFMEMORY;
618     }
619 
620     DPRINT("pRelativeSD: %lu\n", pRelativeSD);
621     dwError = RegQueryValueExW(hSecurityKey,
622                                L"Security",
623                                0,
624                                &dwType,
625                                (LPBYTE)pRelativeSD,
626                                &dwBufferLength);
627     if (dwError != ERROR_SUCCESS)
628     {
629         goto done;
630     }
631 
632     *ppSecurityDescriptor = pRelativeSD;
633 
634 done:
635     if (dwError != ERROR_SUCCESS && pRelativeSD != NULL)
636         RtlFreeHeap(RtlGetProcessHeap(), 0, pRelativeSD);
637 
638     if (hSecurityKey != NULL)
639         RegCloseKey(hSecurityKey);
640 
641     return dwError;
642 }
643 
644 
645 DWORD
646 ScmDeleteRegKey(
647     _In_ HKEY hKey,
648     _In_ PCWSTR pszSubKey)
649 {
650     DWORD dwMaxSubkeyLen, dwMaxValueLen;
651     DWORD dwMaxLen, dwSize;
652     PWSTR pszName = NULL;
653     HKEY hSubKey;
654     DWORD dwError;
655 
656     dwError = RegOpenKeyExW(hKey, pszSubKey, 0, KEY_READ, &hSubKey);
657     if (dwError != ERROR_SUCCESS)
658         return dwError;
659 
660     /* Get maximum length of key and value names */
661     dwError = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, NULL,
662                                &dwMaxSubkeyLen, NULL, NULL, &dwMaxValueLen, NULL, NULL, NULL);
663     if (dwError != ERROR_SUCCESS)
664         goto done;
665 
666     dwMaxSubkeyLen++;
667     dwMaxValueLen++;
668     dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen);
669 
670     /* Allocate the name buffer */
671     pszName = HeapAlloc(GetProcessHeap(), 0, dwMaxLen * sizeof(WCHAR));
672     if (pszName == NULL)
673     {
674         dwError = ERROR_NOT_ENOUGH_MEMORY;
675         goto done;
676     }
677 
678     /* Recursively delete all the subkeys */
679     while (TRUE)
680     {
681         dwSize = dwMaxLen;
682         if (RegEnumKeyExW(hSubKey, 0, pszName, &dwSize,
683                           NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
684         {
685             break;
686         }
687 
688         dwError = ScmDeleteRegKey(hSubKey, pszName);
689         if (dwError != ERROR_SUCCESS)
690             goto done;
691     }
692 
693 done:
694     if (pszName != NULL)
695         HeapFree(GetProcessHeap(), 0, pszName);
696 
697     RegCloseKey(hSubKey);
698 
699     /* Finally delete the key */
700     if (dwError == ERROR_SUCCESS)
701         dwError = RegDeleteKeyW(hKey, pszSubKey);
702 
703     return dwError;
704 }
705 
706 
707 DWORD
708 ScmDecryptPassword(
709     _In_ PVOID ContextHandle,
710     _In_ PBYTE pPassword,
711     _In_ DWORD dwPasswordSize,
712     _Out_ PWSTR *pClearTextPassword)
713 {
714     struct ustring inData, keyData, outData;
715     BYTE SessionKey[16];
716     PWSTR pBuffer;
717     NTSTATUS Status;
718 
719     /* Get the session key */
720     Status = SystemFunction028(ContextHandle,
721                                SessionKey);
722     if (!NT_SUCCESS(Status))
723     {
724         DPRINT1("SystemFunction028 failed (Status 0x%08lx)\n", Status);
725         return RtlNtStatusToDosError(Status);
726     }
727 
728     inData.Length = dwPasswordSize;
729     inData.MaximumLength = inData.Length;
730     inData.Buffer = pPassword;
731 
732     keyData.Length = sizeof(SessionKey);
733     keyData.MaximumLength = keyData.Length;
734     keyData.Buffer = SessionKey;
735 
736     outData.Length = 0;
737     outData.MaximumLength = 0;
738     outData.Buffer = NULL;
739 
740     /* Get the required buffer size */
741     Status = SystemFunction005(&inData,
742                                &keyData,
743                                &outData);
744     if (Status != STATUS_BUFFER_TOO_SMALL)
745     {
746         DPRINT1("SystemFunction005 failed (Status 0x%08lx)\n", Status);
747         return RtlNtStatusToDosError(Status);
748     }
749 
750     /* Allocate a buffer for the clear text password */
751     pBuffer = HeapAlloc(GetProcessHeap(), 0, outData.Length);
752     if (pBuffer == NULL)
753         return ERROR_OUTOFMEMORY;
754 
755     outData.MaximumLength = outData.Length;
756     outData.Buffer = (unsigned char *)pBuffer;
757 
758     /* Decrypt the password */
759     Status = SystemFunction005(&inData,
760                                &keyData,
761                                &outData);
762     if (!NT_SUCCESS(Status))
763     {
764         DPRINT1("SystemFunction005 failed (Status 0x%08lx)\n", Status);
765         HeapFree(GetProcessHeap(), 0, pBuffer);
766         return RtlNtStatusToDosError(Status);
767     }
768 
769     *pClearTextPassword = pBuffer;
770 
771     return ERROR_SUCCESS;
772 }
773 
774 /* EOF */
775