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
ScmOpenServiceKey(LPWSTR lpServiceName,REGSAM samDesired,PHKEY phKey)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
ScmCreateServiceKey(LPCWSTR lpServiceName,REGSAM samDesired,PHKEY phKey)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
ScmWriteDependencies(HKEY hServiceKey,LPCWSTR lpDependencies,DWORD dwDependenciesLength)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
ScmMarkServiceForDelete(PSERVICE pService)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
ScmIsDeleteFlagSet(HKEY hServiceKey)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
ScmReadString(HKEY hServiceKey,LPCWSTR lpValueName,LPWSTR * lpValue)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
ScmReadDependencies(HKEY hServiceKey,LPWSTR * lpDependencies,DWORD * lpdwDependenciesLength)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
ScmSetServicePassword(IN PCWSTR pszServiceName,IN PCWSTR pszPassword)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
ScmWriteSecurityDescriptor(_In_ HKEY hServiceKey,_In_ PSECURITY_DESCRIPTOR pSecurityDescriptor)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
ScmReadSecurityDescriptor(_In_ HKEY hServiceKey,_Out_ PSECURITY_DESCRIPTOR * ppSecurityDescriptor)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
ScmDeleteRegKey(_In_ HKEY hKey,_In_ PCWSTR pszSubKey)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
ScmDecryptPassword(_In_ PVOID ContextHandle,_In_ PBYTE pPassword,_In_ DWORD dwPasswordSize,_Out_ PWSTR * pClearTextPassword)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