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