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