xref: /reactos/base/system/services/config.c (revision 5e2fe089)
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 
480     RtlZeroMemory(&ObjectAttributes, sizeof(OBJECT_ATTRIBUTES));
481 
482     Status = LsaOpenPolicy(NULL,
483                            &ObjectAttributes,
484                            POLICY_CREATE_SECRET,
485                            &PolicyHandle);
486     if (!NT_SUCCESS(Status))
487         return RtlNtStatusToDosError(Status);
488 
489     ServiceName.Length = (wcslen(pszServiceName) + 4) * sizeof(WCHAR);
490     ServiceName.MaximumLength = ServiceName.Length + sizeof(WCHAR);
491     ServiceName.Buffer = HeapAlloc(GetProcessHeap(),
492                                    HEAP_ZERO_MEMORY,
493                                    ServiceName.MaximumLength);
494     if (ServiceName.Buffer == NULL)
495         return ERROR_NOT_ENOUGH_MEMORY;
496 
497     wcscpy(ServiceName.Buffer, L"_SC_");
498     wcscat(ServiceName.Buffer, pszServiceName);
499 
500     RtlInitUnicodeString(&Password, pszPassword);
501 
502     Status = LsaStorePrivateData(PolicyHandle,
503                                  &ServiceName,
504                                  pszPassword ? &Password : NULL);
505     if (!NT_SUCCESS(Status))
506     {
507         dwError = RtlNtStatusToDosError(Status);
508         goto done;
509     }
510 
511 done:
512     if (ServiceName.Buffer != NULL)
513         HeapFree(GetProcessHeap(), 0, ServiceName.Buffer);
514 
515     if (PolicyHandle != NULL)
516         LsaClose(PolicyHandle);
517 
518     return dwError;
519 }
520 
521 
522 DWORD
523 ScmWriteSecurityDescriptor(
524     _In_ HKEY hServiceKey,
525     _In_ PSECURITY_DESCRIPTOR pSecurityDescriptor)
526 {
527     HKEY hSecurityKey = NULL;
528     DWORD dwDisposition;
529     DWORD dwError;
530 
531     DPRINT("ScmWriteSecurityDescriptor(%p %p)\n", hServiceKey, pSecurityDescriptor);
532 
533     dwError = RegCreateKeyExW(hServiceKey,
534                               L"Security",
535                               0,
536                               NULL,
537                               REG_OPTION_NON_VOLATILE,
538                               KEY_SET_VALUE,
539                               NULL,
540                               &hSecurityKey,
541                               &dwDisposition);
542     if (dwError != ERROR_SUCCESS)
543         return dwError;
544 
545     dwError = RegSetValueExW(hSecurityKey,
546                              L"Security",
547                              0,
548                              REG_BINARY,
549                              (LPBYTE)pSecurityDescriptor,
550                              RtlLengthSecurityDescriptor(pSecurityDescriptor));
551 
552     RegCloseKey(hSecurityKey);
553 
554     return dwError;
555 }
556 
557 
558 DWORD
559 ScmReadSecurityDescriptor(
560     _In_ HKEY hServiceKey,
561     _Out_ PSECURITY_DESCRIPTOR *ppSecurityDescriptor)
562 {
563     PSECURITY_DESCRIPTOR pRelativeSD = NULL;
564     HKEY hSecurityKey = NULL;
565     DWORD dwBufferLength = 0;
566     DWORD dwType;
567     DWORD dwError;
568 
569     DPRINT("ScmReadSecurityDescriptor(%p %p)\n", hServiceKey, ppSecurityDescriptor);
570 
571     *ppSecurityDescriptor = NULL;
572 
573     dwError = RegOpenKeyExW(hServiceKey,
574                             L"Security",
575                             0,
576                             KEY_QUERY_VALUE,
577                             &hSecurityKey);
578     if (dwError != ERROR_SUCCESS)
579     {
580         DPRINT("RegOpenKeyExW() failed (Error %lu)\n", dwError);
581 
582         /* Do not fail if the Security key does not exist */
583         if (dwError == ERROR_FILE_NOT_FOUND)
584             dwError = ERROR_SUCCESS;
585         goto done;
586     }
587 
588     dwError = RegQueryValueExW(hSecurityKey,
589                                L"Security",
590                                0,
591                                &dwType,
592                                NULL,
593                                &dwBufferLength);
594     if (dwError != ERROR_SUCCESS)
595     {
596         DPRINT("RegQueryValueExW() failed (Error %lu)\n", dwError);
597 
598         /* Do not fail if the Security value does not exist */
599         if (dwError == ERROR_FILE_NOT_FOUND)
600             dwError = ERROR_SUCCESS;
601         goto done;
602     }
603 
604     DPRINT("dwBufferLength: %lu\n", dwBufferLength);
605     pRelativeSD = RtlAllocateHeap(RtlGetProcessHeap(),
606                                   HEAP_ZERO_MEMORY,
607                                   dwBufferLength);
608     if (pRelativeSD == NULL)
609     {
610         return ERROR_OUTOFMEMORY;
611     }
612 
613     DPRINT("pRelativeSD: %lu\n", pRelativeSD);
614     dwError = RegQueryValueExW(hSecurityKey,
615                                L"Security",
616                                0,
617                                &dwType,
618                                (LPBYTE)pRelativeSD,
619                                &dwBufferLength);
620     if (dwError != ERROR_SUCCESS)
621     {
622         goto done;
623     }
624 
625     *ppSecurityDescriptor = pRelativeSD;
626 
627 done:
628     if (dwError != ERROR_SUCCESS && pRelativeSD != NULL)
629         RtlFreeHeap(RtlGetProcessHeap(), 0, pRelativeSD);
630 
631     if (hSecurityKey != NULL)
632         RegCloseKey(hSecurityKey);
633 
634     return dwError;
635 }
636 
637 
638 DWORD
639 ScmDeleteRegKey(
640     _In_ HKEY hKey,
641     _In_ PCWSTR pszSubKey)
642 {
643     DWORD dwMaxSubkeyLen, dwMaxValueLen;
644     DWORD dwMaxLen, dwSize;
645     PWSTR pszName = NULL;
646     HKEY hSubKey;
647     DWORD dwError;
648 
649     dwError = RegOpenKeyExW(hKey, pszSubKey, 0, KEY_READ, &hSubKey);
650     if (dwError != ERROR_SUCCESS)
651         return dwError;
652 
653     /* Get maximum length of key and value names */
654     dwError = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, NULL,
655                                &dwMaxSubkeyLen, NULL, NULL, &dwMaxValueLen, NULL, NULL, NULL);
656     if (dwError != ERROR_SUCCESS)
657         goto done;
658 
659     dwMaxSubkeyLen++;
660     dwMaxValueLen++;
661     dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen);
662 
663     /* Allocate the name buffer */
664     pszName = HeapAlloc(GetProcessHeap(), 0, dwMaxLen * sizeof(WCHAR));
665     if (pszName == NULL)
666     {
667         dwError = ERROR_NOT_ENOUGH_MEMORY;
668         goto done;
669     }
670 
671     /* Recursively delete all the subkeys */
672     while (TRUE)
673     {
674         dwSize = dwMaxLen;
675         if (RegEnumKeyExW(hSubKey, 0, pszName, &dwSize,
676                           NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
677         {
678             break;
679         }
680 
681         dwError = ScmDeleteRegKey(hSubKey, pszName);
682         if (dwError != ERROR_SUCCESS)
683             goto done;
684     }
685 
686 done:
687     if (pszName != NULL)
688         HeapFree(GetProcessHeap(), 0, pszName);
689 
690     RegCloseKey(hSubKey);
691 
692     /* Finally delete the key */
693     if (dwError == ERROR_SUCCESS)
694         dwError = RegDeleteKeyW(hKey, pszSubKey);
695 
696     return dwError;
697 }
698 
699 
700 DWORD
701 ScmDecryptPassword(
702     _In_ PBYTE pPassword,
703     _In_ DWORD dwPasswordSize,
704     _Out_ PWSTR *pClearTextPassword)
705 {
706     struct ustring inData, keyData, outData;
707     BYTE SessionKey[16];
708     PWSTR pBuffer;
709     NTSTATUS Status;
710 
711     /* Get the session key */
712     Status = SystemFunction028(NULL,
713                                SessionKey);
714     if (!NT_SUCCESS(Status))
715     {
716         DPRINT1("SystemFunction028 failed (Status 0x%08lx)\n", Status);
717         return RtlNtStatusToDosError(Status);
718     }
719 
720     inData.Length = dwPasswordSize;
721     inData.MaximumLength = inData.Length;
722     inData.Buffer = pPassword;
723 
724     keyData.Length = sizeof(SessionKey);
725     keyData.MaximumLength = keyData.Length;
726     keyData.Buffer = SessionKey;
727 
728     outData.Length = 0;
729     outData.MaximumLength = 0;
730     outData.Buffer = NULL;
731 
732     /* Get the required buffer size */
733     Status = SystemFunction005(&inData,
734                                &keyData,
735                                &outData);
736     if (Status != STATUS_BUFFER_TOO_SMALL)
737     {
738         DPRINT1("SystemFunction005 failed (Status 0x%08lx)\n", Status);
739         return RtlNtStatusToDosError(Status);
740     }
741 
742     /* Allocate a buffer for the clear text password */
743     pBuffer = HeapAlloc(GetProcessHeap(), 0, outData.Length);
744     if (pBuffer == NULL)
745         return ERROR_OUTOFMEMORY;
746 
747     outData.MaximumLength = outData.Length;
748     outData.Buffer = (unsigned char *)pBuffer;
749 
750     /* Decrypt the password */
751     Status = SystemFunction005(&inData,
752                                &keyData,
753                                &outData);
754     if (!NT_SUCCESS(Status))
755     {
756         DPRINT1("SystemFunction005 failed (Status 0x%08lx)\n", Status);
757         HeapFree(GetProcessHeap(), 0, pBuffer);
758         return RtlNtStatusToDosError(Status);
759     }
760 
761     *pClearTextPassword = pBuffer;
762 
763     return ERROR_SUCCESS;
764 }
765 
766 /* EOF */
767