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