1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * PURPOSE: Rtl registry functions 5 * FILE: lib/rtl/registry.c 6 * PROGRAMER: Alex Ionescu (alex.ionescu@reactos.org) 7 * Eric Kohl 8 */ 9 10 /* INCLUDES *****************************************************************/ 11 12 #include <rtl.h> 13 14 #include <ndk/cmfuncs.h> 15 16 #define NDEBUG 17 #include <debug.h> 18 19 #define TAG_RTLREGISTRY 'vrqR' 20 21 extern SIZE_T RtlpAllocDeallocQueryBufferSize; 22 23 /* DATA **********************************************************************/ 24 25 PCWSTR RtlpRegPaths[RTL_REGISTRY_MAXIMUM] = 26 { 27 NULL, 28 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services", 29 L"\\Registry\\Machine\\System\\CurrentControlSet\\Control", 30 L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion", 31 L"\\Registry\\Machine\\Hardware\\DeviceMap", 32 L"\\Registry\\User\\.Default", 33 }; 34 35 /* PRIVATE FUNCTIONS *********************************************************/ 36 37 NTSTATUS 38 NTAPI 39 RtlpQueryRegistryDirect(IN ULONG ValueType, 40 IN PVOID ValueData, 41 IN ULONG ValueLength, 42 IN PVOID Buffer) 43 { 44 USHORT ActualLength; 45 PUNICODE_STRING ReturnString = Buffer; 46 PULONG Length = Buffer; 47 ULONG RealLength; 48 49 /* Check if this is a string */ 50 if ((ValueType == REG_SZ) || 51 (ValueType == REG_EXPAND_SZ) || 52 (ValueType == REG_MULTI_SZ)) 53 { 54 /* Normalize the length */ 55 if (ValueLength > MAXUSHORT) 56 ActualLength = MAXUSHORT; 57 else 58 ActualLength = (USHORT)ValueLength; 59 60 /* Check if the return string has been allocated */ 61 if (!ReturnString->Buffer) 62 { 63 /* Allocate it */ 64 ReturnString->Buffer = RtlpAllocateMemory(ActualLength, TAG_RTLREGISTRY); 65 if (!ReturnString->Buffer) return STATUS_NO_MEMORY; 66 ReturnString->MaximumLength = ActualLength; 67 } 68 else if (ActualLength > ReturnString->MaximumLength) 69 { 70 /* The string the caller allocated is too small */ 71 return STATUS_BUFFER_TOO_SMALL; 72 } 73 74 /* Copy the data */ 75 RtlCopyMemory(ReturnString->Buffer, ValueData, ActualLength); 76 ReturnString->Length = ActualLength - sizeof(UNICODE_NULL); 77 } 78 else if (ValueLength <= sizeof(ULONG)) 79 { 80 /* Check if we can just copy the data */ 81 if ((Buffer != ValueData) && (ValueLength)) 82 { 83 /* Copy it */ 84 RtlCopyMemory(Buffer, ValueData, ValueLength); 85 } 86 } 87 else 88 { 89 /* Check if the length is negative */ 90 if ((LONG)*Length < 0) 91 { 92 /* Get the real length and copy the buffer */ 93 RealLength = -(LONG)*Length; 94 if (RealLength < ValueLength) return STATUS_BUFFER_TOO_SMALL; 95 RtlCopyMemory(Buffer, ValueData, ValueLength); 96 } 97 else 98 { 99 /* Check if there's space for the length and type, plus data */ 100 if (*Length < (2 * sizeof(ULONG) + ValueLength)) 101 { 102 /* Nope, fail */ 103 return STATUS_BUFFER_TOO_SMALL; 104 } 105 106 /* Return the data */ 107 *Length++ = ValueLength; 108 *Length++ = ValueType; 109 RtlCopyMemory(Length, ValueData, ValueLength); 110 } 111 } 112 113 /* All done */ 114 return STATUS_SUCCESS; 115 } 116 117 NTSTATUS 118 NTAPI 119 RtlpCallQueryRegistryRoutine(IN PRTL_QUERY_REGISTRY_TABLE QueryTable, 120 IN PKEY_VALUE_FULL_INFORMATION KeyValueInfo, 121 IN OUT PULONG InfoSize, 122 IN PVOID Context, 123 IN PVOID Environment) 124 { 125 ULONG InfoLength; 126 SIZE_T Length, SpareLength, c; 127 ULONG RequiredLength; 128 PCHAR SpareData, DataEnd; 129 ULONG Type; 130 PWCHAR Name, p, ValueEnd; 131 PVOID Data; 132 NTSTATUS Status; 133 BOOLEAN FoundExpander = FALSE; 134 UNICODE_STRING Source, Destination; 135 136 /* Setup defaults */ 137 InfoLength = *InfoSize; 138 *InfoSize = 0; 139 140 /* Check if there's no data */ 141 if (KeyValueInfo->DataOffset == MAXULONG) 142 { 143 /* Return proper status code */ 144 return (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED) ? 145 STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS; 146 } 147 148 /* Setup spare data pointers */ 149 SpareData = (PCHAR)KeyValueInfo; 150 SpareLength = InfoLength; 151 DataEnd = SpareData + SpareLength; 152 153 /* Check if there's no value or data */ 154 if ((KeyValueInfo->Type == REG_NONE) || 155 (!(KeyValueInfo->DataLength) && 156 (KeyValueInfo->Type == QueryTable->DefaultType))) 157 { 158 /* Check if there's no value */ 159 if (QueryTable->DefaultType == REG_NONE) 160 { 161 /* Return proper status code */ 162 return (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED) ? 163 STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS; 164 } 165 166 /* We can setup a default value... capture the defaults */ 167 Name = (PWCHAR)QueryTable->Name; 168 Type = QueryTable->DefaultType; 169 Data = QueryTable->DefaultData; 170 Length = QueryTable->DefaultLength; 171 if (!Length) 172 { 173 /* No default length given, try to calculate it */ 174 p = Data; 175 if ((Type == REG_SZ) || (Type == REG_EXPAND_SZ)) 176 { 177 /* This is a string, count the characters */ 178 while (*p++); 179 Length = (ULONG_PTR)p - (ULONG_PTR)Data; 180 } 181 else if (Type == REG_MULTI_SZ) 182 { 183 /* This is a multi-string, calculate all characters */ 184 while (*p) while (*p++); 185 Length = (ULONG_PTR)p - (ULONG_PTR)Data + sizeof(UNICODE_NULL); 186 } 187 } 188 } 189 else 190 { 191 /* Check if this isn't a direct return */ 192 if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT)) 193 { 194 /* Check if we have length */ 195 if (KeyValueInfo->DataLength) 196 { 197 /* Increase the spare data */ 198 SpareData += KeyValueInfo->DataOffset + 199 KeyValueInfo->DataLength; 200 } 201 else 202 { 203 /* Otherwise, the spare data only has the name data */ 204 SpareData += FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) + 205 KeyValueInfo->NameLength; 206 } 207 208 /* Align the pointer and get new size of spare data */ 209 SpareData = (PVOID)(((ULONG_PTR)SpareData + 7) & ~7); 210 SpareLength = DataEnd - SpareData; 211 212 /* Check if we have space to copy the data */ 213 RequiredLength = KeyValueInfo->NameLength + sizeof(UNICODE_NULL); 214 if ((SpareData > DataEnd) || (SpareLength < RequiredLength)) 215 { 216 /* Fail and return the missing length */ 217 *InfoSize = (ULONG)(SpareData - (PCHAR)KeyValueInfo) + RequiredLength; 218 return STATUS_BUFFER_TOO_SMALL; 219 } 220 221 /* Copy the data and null-terminate it */ 222 Name = (PWCHAR)SpareData; 223 RtlCopyMemory(Name, KeyValueInfo->Name, KeyValueInfo->NameLength); 224 Name[KeyValueInfo->NameLength / sizeof(WCHAR)] = UNICODE_NULL; 225 226 /* Update the spare data information */ 227 SpareData += RequiredLength; 228 SpareData = (PVOID)(((ULONG_PTR)SpareData + 7) & ~7); 229 SpareLength = DataEnd - SpareData; 230 } 231 else 232 { 233 /* Just return the name */ 234 Name = (PWCHAR)QueryTable->Name; 235 } 236 237 /* Capture key data */ 238 Type = KeyValueInfo->Type; 239 Data = (PVOID)((ULONG_PTR)KeyValueInfo + KeyValueInfo->DataOffset); 240 Length = KeyValueInfo->DataLength; 241 } 242 243 /* Check if we're expanding */ 244 if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_NOEXPAND)) 245 { 246 /* Check if it's a multi-string */ 247 if (Type == REG_MULTI_SZ) 248 { 249 /* Prepare defaults */ 250 Status = STATUS_SUCCESS; 251 /* Skip the last two UNICODE_NULL chars (the terminating null string) */ 252 ValueEnd = (PWSTR)((ULONG_PTR)Data + Length - 2 * sizeof(UNICODE_NULL)); 253 p = Data; 254 255 /* Loop all strings */ 256 while (p < ValueEnd) 257 { 258 /* Go to the next string */ 259 while (*p++); 260 261 /* Get the length and check if this is direct */ 262 Length = (ULONG_PTR)p - (ULONG_PTR)Data; 263 if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) 264 { 265 /* Do the query */ 266 Status = RtlpQueryRegistryDirect(REG_SZ, 267 Data, 268 (ULONG)Length, 269 QueryTable->EntryContext); 270 QueryTable->EntryContext = 271 (PVOID)((ULONG_PTR)QueryTable->EntryContext + 272 sizeof(UNICODE_STRING)); 273 } 274 else 275 { 276 /* Call the custom routine */ 277 Status = QueryTable->QueryRoutine(Name, 278 REG_SZ, 279 Data, 280 (ULONG)Length, 281 Context, 282 QueryTable->EntryContext); 283 } 284 285 /* Normalize status */ 286 if (Status == STATUS_BUFFER_TOO_SMALL) Status = STATUS_SUCCESS; 287 if (!NT_SUCCESS(Status)) break; 288 289 /* Update data pointer */ 290 Data = p; 291 } 292 293 /* Return */ 294 return Status; 295 } 296 297 /* Check if this is an expand string */ 298 if ((Type == REG_EXPAND_SZ) && (Length >= sizeof(WCHAR))) 299 { 300 /* Try to find the expander */ 301 c = Length - sizeof(UNICODE_NULL); 302 p = (PWCHAR)Data; 303 while (c) 304 { 305 /* Check if this is one */ 306 if (*p == L'%') 307 { 308 /* Yup! */ 309 FoundExpander = TRUE; 310 break; 311 } 312 313 /* Continue in the buffer */ 314 p++; 315 c -= sizeof(WCHAR); 316 } 317 318 /* So check if we have one */ 319 if (FoundExpander) 320 { 321 /* Setup the source string */ 322 RtlInitEmptyUnicodeString(&Source, Data, (USHORT)Length); 323 Source.Length = Source.MaximumLength - sizeof(UNICODE_NULL); 324 325 /* Setup the desination string */ 326 RtlInitEmptyUnicodeString(&Destination, (PWCHAR)SpareData, 0); 327 328 /* Check if we're out of space */ 329 if (SpareLength <= 0) 330 { 331 /* Then we don't have any space in our string */ 332 Destination.MaximumLength = 0; 333 } 334 else if (SpareLength <= MAXUSHORT) 335 { 336 /* This is the good case, where we fit into a string */ 337 Destination.MaximumLength = (USHORT)SpareLength; 338 Destination.Buffer[SpareLength / sizeof(WCHAR) - 1] = UNICODE_NULL; 339 } 340 else 341 { 342 /* We can't fit into a string, so truncate */ 343 Destination.MaximumLength = MAXUSHORT; 344 Destination.Buffer[MAXUSHORT / sizeof(WCHAR) - 1] = UNICODE_NULL; 345 } 346 347 /* Expand the strings and set our type as one string */ 348 Status = RtlExpandEnvironmentStrings_U(Environment, 349 &Source, 350 &Destination, 351 &RequiredLength); 352 Type = REG_SZ; 353 354 /* Check for success */ 355 if (NT_SUCCESS(Status)) 356 { 357 /* Set the value name and length to our string */ 358 Data = Destination.Buffer; 359 Length = Destination.Length + sizeof(UNICODE_NULL); 360 } 361 else 362 { 363 /* Check if our buffer is too small */ 364 if (Status == STATUS_BUFFER_TOO_SMALL) 365 { 366 /* Set the required missing length */ 367 *InfoSize = (ULONG)(SpareData - (PCHAR)KeyValueInfo) + 368 RequiredLength; 369 370 /* Notify debugger */ 371 DPRINT1("RTL: Expand variables for %wZ failed - " 372 "Status == %lx Size %x > %x <%x>\n", 373 &Source, 374 Status, 375 *InfoSize, 376 InfoLength, 377 Destination.MaximumLength); 378 } 379 else 380 { 381 /* Notify debugger */ 382 DPRINT1("RTL: Expand variables for %wZ failed - " 383 "Status == %lx\n", 384 &Source, 385 Status); 386 } 387 388 /* Return the status */ 389 return Status; 390 } 391 } 392 } 393 } 394 395 /* Check if this is a direct query */ 396 if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) 397 { 398 /* Return the data */ 399 Status = RtlpQueryRegistryDirect(Type, 400 Data, 401 (ULONG)Length, 402 QueryTable->EntryContext); 403 } 404 else 405 { 406 /* Call the query routine */ 407 Status = QueryTable->QueryRoutine(Name, 408 Type, 409 Data, 410 (ULONG)Length, 411 Context, 412 QueryTable->EntryContext); 413 } 414 415 /* Normalize and return status */ 416 return (Status == STATUS_BUFFER_TOO_SMALL) ? STATUS_SUCCESS : Status; 417 } 418 419 _Success_(return!=NULL || BufferSize==0) 420 _When_(BufferSize!=NULL,__drv_allocatesMem(Mem)) 421 PVOID 422 NTAPI 423 RtlpAllocDeallocQueryBuffer( 424 _In_opt_ PSIZE_T BufferSize, 425 _In_opt_ __drv_freesMem(Mem) PVOID OldBuffer, 426 _In_ SIZE_T OldBufferSize, 427 _Out_opt_ _On_failure_(_Post_satisfies_(*Status < 0)) PNTSTATUS Status) 428 { 429 PVOID Buffer = NULL; 430 431 /* Assume success */ 432 if (Status) *Status = STATUS_SUCCESS; 433 434 /* Free the old buffer */ 435 if (OldBuffer) RtlpFreeMemory(OldBuffer, TAG_RTLREGISTRY); 436 437 /* Check if we need to allocate a new one */ 438 if (BufferSize) 439 { 440 /* Allocate */ 441 Buffer = RtlpAllocateMemory(*BufferSize, TAG_RTLREGISTRY); 442 if (!(Buffer) && (Status)) *Status = STATUS_NO_MEMORY; 443 } 444 445 /* Return the pointer */ 446 return Buffer; 447 } 448 449 NTSTATUS 450 NTAPI 451 RtlpGetRegistryHandle(IN ULONG RelativeTo, 452 IN PCWSTR Path, 453 IN BOOLEAN Create, 454 IN PHANDLE KeyHandle) 455 { 456 UNICODE_STRING KeyPath, KeyName; 457 WCHAR KeyBuffer[MAX_PATH]; 458 OBJECT_ATTRIBUTES ObjectAttributes; 459 NTSTATUS Status; 460 461 /* Check if we just want the handle */ 462 if (RelativeTo & RTL_REGISTRY_HANDLE) 463 { 464 *KeyHandle = (HANDLE)Path; 465 return STATUS_SUCCESS; 466 } 467 468 /* Check for optional flag */ 469 if (RelativeTo & RTL_REGISTRY_OPTIONAL) 470 { 471 /* Mask it out */ 472 RelativeTo &= ~RTL_REGISTRY_OPTIONAL; 473 } 474 475 /* Fail on invalid parameter */ 476 if (RelativeTo >= RTL_REGISTRY_MAXIMUM) return STATUS_INVALID_PARAMETER; 477 478 /* Initialize the key name */ 479 RtlInitEmptyUnicodeString(&KeyName, KeyBuffer, sizeof(KeyBuffer)); 480 481 /* Check if we have to lookup a path to prefix */ 482 if (RelativeTo != RTL_REGISTRY_ABSOLUTE) 483 { 484 /* Check if we need the current user key */ 485 if (RelativeTo == RTL_REGISTRY_USER) 486 { 487 /* Get the user key path */ 488 Status = RtlFormatCurrentUserKeyPath(&KeyPath); 489 490 /* Check if it worked */ 491 if (NT_SUCCESS(Status)) 492 { 493 /* Append the user key path */ 494 Status = RtlAppendUnicodeStringToString(&KeyName, &KeyPath); 495 496 /* Free the user key path */ 497 RtlFreeUnicodeString (&KeyPath); 498 } 499 else 500 { 501 /* It didn't work so fall back to the default user key */ 502 Status = RtlAppendUnicodeToString(&KeyName, RtlpRegPaths[RTL_REGISTRY_USER]); 503 } 504 } 505 else 506 { 507 /* Get one of the prefixes */ 508 Status = RtlAppendUnicodeToString(&KeyName, 509 RtlpRegPaths[RelativeTo]); 510 } 511 512 /* Check for failure, otherwise, append the path separator */ 513 if (!NT_SUCCESS(Status)) return Status; 514 Status = RtlAppendUnicodeToString(&KeyName, L"\\"); 515 if (!NT_SUCCESS(Status)) return Status; 516 } 517 518 /* And now append the path */ 519 if (Path[0] == L'\\' && RelativeTo != RTL_REGISTRY_ABSOLUTE) Path++; // HACK! 520 Status = RtlAppendUnicodeToString(&KeyName, Path); 521 if (!NT_SUCCESS(Status)) return Status; 522 523 /* Initialize the object attributes */ 524 InitializeObjectAttributes(&ObjectAttributes, 525 &KeyName, 526 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 527 NULL, 528 NULL); 529 530 /* Check if we want to create it */ 531 if (Create) 532 { 533 /* Create the key with write privileges */ 534 Status = ZwCreateKey(KeyHandle, 535 GENERIC_WRITE, 536 &ObjectAttributes, 537 0, 538 NULL, 539 0, 540 NULL); 541 } 542 else 543 { 544 /* Otherwise, just open it with read access */ 545 Status = ZwOpenKey(KeyHandle, 546 MAXIMUM_ALLOWED | GENERIC_READ, 547 &ObjectAttributes); 548 } 549 550 /* Return status */ 551 return Status; 552 } 553 554 FORCEINLINE 555 VOID 556 RtlpCloseRegistryHandle( 557 _In_ ULONG RelativeTo, 558 _In_ HANDLE KeyHandle) 559 { 560 /* Did the caller pass a key handle? */ 561 if (!(RelativeTo & RTL_REGISTRY_HANDLE)) 562 { 563 /* We opened the key in RtlpGetRegistryHandle, so close it now */ 564 ZwClose(KeyHandle); 565 } 566 } 567 568 /* PUBLIC FUNCTIONS **********************************************************/ 569 570 /* 571 * @implemented 572 */ 573 NTSTATUS 574 NTAPI 575 RtlCheckRegistryKey(IN ULONG RelativeTo, 576 IN PWSTR Path) 577 { 578 HANDLE KeyHandle; 579 NTSTATUS Status; 580 PAGED_CODE_RTL(); 581 582 /* Call the helper */ 583 Status = RtlpGetRegistryHandle(RelativeTo, 584 Path, 585 FALSE, 586 &KeyHandle); 587 if (!NT_SUCCESS(Status)) return Status; 588 589 /* Close the handle even for RTL_REGISTRY_HANDLE */ 590 ZwClose(KeyHandle); 591 return STATUS_SUCCESS; 592 } 593 594 /* 595 * @implemented 596 */ 597 NTSTATUS 598 NTAPI 599 RtlCreateRegistryKey(IN ULONG RelativeTo, 600 IN PWSTR Path) 601 { 602 HANDLE KeyHandle; 603 NTSTATUS Status; 604 PAGED_CODE_RTL(); 605 606 /* Call the helper */ 607 Status = RtlpGetRegistryHandle(RelativeTo, 608 Path, 609 TRUE, 610 &KeyHandle); 611 if (!NT_SUCCESS(Status)) return Status; 612 613 /* All went well, close the handle and return status */ 614 RtlpCloseRegistryHandle(RelativeTo, KeyHandle); 615 return STATUS_SUCCESS; 616 } 617 618 /* 619 * @implemented 620 */ 621 NTSTATUS 622 NTAPI 623 RtlDeleteRegistryValue(IN ULONG RelativeTo, 624 IN PCWSTR Path, 625 IN PCWSTR ValueName) 626 { 627 HANDLE KeyHandle; 628 NTSTATUS Status; 629 UNICODE_STRING Name; 630 PAGED_CODE_RTL(); 631 632 /* Call the helper */ 633 Status = RtlpGetRegistryHandle(RelativeTo, 634 Path, 635 TRUE, 636 &KeyHandle); 637 if (!NT_SUCCESS(Status)) return Status; 638 639 /* Initialize the key name and delete it */ 640 RtlInitUnicodeString(&Name, ValueName); 641 Status = ZwDeleteValueKey(KeyHandle, &Name); 642 643 /* Close the handle and return status */ 644 RtlpCloseRegistryHandle(RelativeTo, KeyHandle); 645 return Status; 646 } 647 648 /* 649 * @implemented 650 */ 651 NTSTATUS 652 NTAPI 653 RtlWriteRegistryValue(IN ULONG RelativeTo, 654 IN PCWSTR Path, 655 IN PCWSTR ValueName, 656 IN ULONG ValueType, 657 IN PVOID ValueData, 658 IN ULONG ValueLength) 659 { 660 HANDLE KeyHandle; 661 NTSTATUS Status; 662 UNICODE_STRING Name; 663 PAGED_CODE_RTL(); 664 665 /* Call the helper */ 666 Status = RtlpGetRegistryHandle(RelativeTo, 667 Path, 668 TRUE, 669 &KeyHandle); 670 if (!NT_SUCCESS(Status)) return Status; 671 672 /* Initialize the key name and set it */ 673 RtlInitUnicodeString(&Name, ValueName); 674 Status = ZwSetValueKey(KeyHandle, 675 &Name, 676 0, 677 ValueType, 678 ValueData, 679 ValueLength); 680 681 /* Close the handle and return status */ 682 RtlpCloseRegistryHandle(RelativeTo, KeyHandle); 683 return Status; 684 } 685 686 /* 687 * @implemented 688 */ 689 NTSTATUS 690 NTAPI 691 RtlOpenCurrentUser(IN ACCESS_MASK DesiredAccess, 692 OUT PHANDLE KeyHandle) 693 { 694 OBJECT_ATTRIBUTES ObjectAttributes; 695 UNICODE_STRING KeyPath; 696 NTSTATUS Status; 697 PAGED_CODE_RTL(); 698 699 /* Get the user key */ 700 Status = RtlFormatCurrentUserKeyPath(&KeyPath); 701 if (NT_SUCCESS(Status)) 702 { 703 /* Initialize the attributes and open it */ 704 InitializeObjectAttributes(&ObjectAttributes, 705 &KeyPath, 706 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 707 NULL, 708 NULL); 709 Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes); 710 711 /* Free the path and return success if it worked */ 712 RtlFreeUnicodeString(&KeyPath); 713 if (NT_SUCCESS(Status)) return STATUS_SUCCESS; 714 } 715 716 /* It didn't work, so use the default key */ 717 RtlInitUnicodeString(&KeyPath, RtlpRegPaths[RTL_REGISTRY_USER]); 718 InitializeObjectAttributes(&ObjectAttributes, 719 &KeyPath, 720 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 721 NULL, 722 NULL); 723 Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes); 724 725 /* Return status */ 726 return Status; 727 } 728 729 /* 730 * @implemented 731 */ 732 NTSTATUS 733 NTAPI 734 RtlFormatCurrentUserKeyPath(OUT PUNICODE_STRING KeyPath) 735 { 736 HANDLE TokenHandle; 737 UCHAR Buffer[256]; 738 PSID_AND_ATTRIBUTES SidBuffer; 739 ULONG Length; 740 UNICODE_STRING SidString; 741 NTSTATUS Status; 742 PAGED_CODE_RTL(); 743 744 /* Open the thread token */ 745 Status = ZwOpenThreadTokenEx(NtCurrentThread(), 746 TOKEN_QUERY, 747 TRUE, 748 OBJ_KERNEL_HANDLE, 749 &TokenHandle); 750 if (!NT_SUCCESS(Status)) 751 { 752 /* We failed, is it because we don't have a thread token? */ 753 if (Status != STATUS_NO_TOKEN) return Status; 754 755 /* It is, so use the process token */ 756 Status = ZwOpenProcessTokenEx(NtCurrentProcess(), 757 TOKEN_QUERY, 758 OBJ_KERNEL_HANDLE, 759 &TokenHandle); 760 if (!NT_SUCCESS(Status)) return Status; 761 } 762 763 /* Now query the token information */ 764 SidBuffer = (PSID_AND_ATTRIBUTES)Buffer; 765 Status = ZwQueryInformationToken(TokenHandle, 766 TokenUser, 767 (PVOID)SidBuffer, 768 sizeof(Buffer), 769 &Length); 770 771 /* Close the handle and handle failure */ 772 ZwClose(TokenHandle); 773 if (!NT_SUCCESS(Status)) return Status; 774 775 /* Convert the SID */ 776 Status = RtlConvertSidToUnicodeString(&SidString, SidBuffer[0].Sid, TRUE); 777 if (!NT_SUCCESS(Status)) return Status; 778 779 /* Add the length of the prefix */ 780 Length = SidString.Length + sizeof(L"\\REGISTRY\\USER\\"); 781 782 /* Initialize a string */ 783 RtlInitEmptyUnicodeString(KeyPath, 784 RtlpAllocateStringMemory(Length, TAG_USTR), 785 (USHORT)Length); 786 if (!KeyPath->Buffer) 787 { 788 /* Free the string and fail */ 789 RtlFreeUnicodeString(&SidString); 790 return STATUS_NO_MEMORY; 791 } 792 793 /* Append the prefix and SID */ 794 RtlAppendUnicodeToString(KeyPath, L"\\REGISTRY\\USER\\"); 795 RtlAppendUnicodeStringToString(KeyPath, &SidString); 796 797 /* Free the temporary string and return success */ 798 RtlFreeUnicodeString(&SidString); 799 return STATUS_SUCCESS; 800 } 801 802 /* 803 * @implemented 804 */ 805 NTSTATUS 806 NTAPI 807 RtlpNtCreateKey(OUT HANDLE KeyHandle, 808 IN ACCESS_MASK DesiredAccess, 809 IN POBJECT_ATTRIBUTES ObjectAttributes, 810 IN ULONG TitleIndex, 811 IN PUNICODE_STRING Class, 812 OUT PULONG Disposition) 813 { 814 /* Check if we have object attributes */ 815 if (ObjectAttributes) 816 { 817 /* Mask out the unsupported flags */ 818 ObjectAttributes->Attributes &= ~(OBJ_PERMANENT | OBJ_EXCLUSIVE); 819 } 820 821 /* Create the key */ 822 return ZwCreateKey(KeyHandle, 823 DesiredAccess, 824 ObjectAttributes, 825 0, 826 NULL, 827 0, 828 Disposition); 829 } 830 831 /* 832 * @implemented 833 */ 834 NTSTATUS 835 NTAPI 836 RtlpNtEnumerateSubKey(IN HANDLE KeyHandle, 837 OUT PUNICODE_STRING SubKeyName, 838 IN ULONG Index, 839 IN ULONG Unused) 840 { 841 PKEY_BASIC_INFORMATION KeyInfo = NULL; 842 ULONG BufferLength = 0; 843 ULONG ReturnedLength; 844 NTSTATUS Status; 845 846 /* Check if we have a name */ 847 if (SubKeyName->MaximumLength) 848 { 849 /* Allocate a buffer for it */ 850 BufferLength = SubKeyName->MaximumLength + 851 sizeof(KEY_BASIC_INFORMATION); 852 KeyInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); 853 if (!KeyInfo) return STATUS_NO_MEMORY; 854 } 855 856 /* Enumerate the key */ 857 Status = ZwEnumerateKey(KeyHandle, 858 Index, 859 KeyBasicInformation, 860 KeyInfo, 861 BufferLength, 862 &ReturnedLength); 863 if (NT_SUCCESS(Status) && (KeyInfo != NULL)) 864 { 865 /* Check if the name fits */ 866 if (KeyInfo->NameLength <= SubKeyName->MaximumLength) 867 { 868 /* Set the length */ 869 SubKeyName->Length = (USHORT)KeyInfo->NameLength; 870 871 /* Copy it */ 872 RtlMoveMemory(SubKeyName->Buffer, 873 KeyInfo->Name, 874 SubKeyName->Length); 875 } 876 else 877 { 878 /* Otherwise, we ran out of buffer space */ 879 Status = STATUS_BUFFER_OVERFLOW; 880 } 881 } 882 883 /* Free the buffer and return status */ 884 if (KeyInfo) RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo); 885 return Status; 886 } 887 888 /* 889 * @implemented 890 */ 891 NTSTATUS 892 NTAPI 893 RtlpNtMakeTemporaryKey(IN HANDLE KeyHandle) 894 { 895 /* This just deletes the key */ 896 return ZwDeleteKey(KeyHandle); 897 } 898 899 /* 900 * @implemented 901 */ 902 NTSTATUS 903 NTAPI 904 RtlpNtOpenKey(OUT HANDLE KeyHandle, 905 IN ACCESS_MASK DesiredAccess, 906 IN POBJECT_ATTRIBUTES ObjectAttributes, 907 IN ULONG Unused) 908 { 909 /* Check if we have object attributes */ 910 if (ObjectAttributes) 911 { 912 /* Mask out the unsupported flags */ 913 ObjectAttributes->Attributes &= ~(OBJ_PERMANENT | OBJ_EXCLUSIVE); 914 } 915 916 /* Open the key */ 917 return ZwOpenKey(KeyHandle, DesiredAccess, ObjectAttributes); 918 } 919 920 /* 921 * @implemented 922 */ 923 NTSTATUS 924 NTAPI 925 RtlpNtQueryValueKey(IN HANDLE KeyHandle, 926 OUT PULONG Type OPTIONAL, 927 OUT PVOID Data OPTIONAL, 928 IN OUT PULONG DataLength OPTIONAL, 929 IN ULONG Unused) 930 { 931 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo; 932 UNICODE_STRING ValueName; 933 ULONG BufferLength = 0; 934 NTSTATUS Status; 935 936 /* Clear the value name */ 937 RtlInitEmptyUnicodeString(&ValueName, NULL, 0); 938 939 /* Check if we were already given a length */ 940 if (DataLength) BufferLength = *DataLength; 941 942 /* Add the size of the structure */ 943 BufferLength += FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data); 944 945 /* Allocate memory for the value */ 946 ValueInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); 947 if (!ValueInfo) return STATUS_NO_MEMORY; 948 949 /* Query the value */ 950 Status = ZwQueryValueKey(KeyHandle, 951 &ValueName, 952 KeyValuePartialInformation, 953 ValueInfo, 954 BufferLength, 955 &BufferLength); 956 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_OVERFLOW)) 957 { 958 /* Return the length and type */ 959 if (DataLength) *DataLength = ValueInfo->DataLength; 960 if (Type) *Type = ValueInfo->Type; 961 } 962 963 /* Check if the caller wanted data back, and we got it */ 964 if ((NT_SUCCESS(Status)) && (Data)) 965 { 966 /* Copy it */ 967 RtlMoveMemory(Data, ValueInfo->Data, ValueInfo->DataLength); 968 } 969 970 /* Free the memory and return status */ 971 RtlFreeHeap(RtlGetProcessHeap(), 0, ValueInfo); 972 return Status; 973 } 974 975 /* 976 * @implemented 977 */ 978 NTSTATUS 979 NTAPI 980 RtlpNtSetValueKey(IN HANDLE KeyHandle, 981 IN ULONG Type, 982 IN PVOID Data, 983 IN ULONG DataLength) 984 { 985 UNICODE_STRING ValueName; 986 987 /* Set the value */ 988 RtlInitEmptyUnicodeString(&ValueName, NULL, 0); 989 return ZwSetValueKey(KeyHandle, 990 &ValueName, 991 0, 992 Type, 993 Data, 994 DataLength); 995 } 996 997 /* 998 * @implemented 999 */ 1000 NTSTATUS 1001 NTAPI 1002 RtlQueryRegistryValues(IN ULONG RelativeTo, 1003 IN PCWSTR Path, 1004 IN PRTL_QUERY_REGISTRY_TABLE QueryTable, 1005 IN PVOID Context, 1006 IN PVOID Environment OPTIONAL) 1007 { 1008 NTSTATUS Status; 1009 PKEY_VALUE_FULL_INFORMATION KeyValueInfo = NULL; 1010 HANDLE KeyHandle, CurrentKey; 1011 SIZE_T BufferSize, InfoSize; 1012 UNICODE_STRING KeyPath, KeyValueName; 1013 OBJECT_ATTRIBUTES ObjectAttributes; 1014 ULONG i, Value; 1015 ULONG ResultLength; 1016 1017 /* Get the registry handle */ 1018 Status = RtlpGetRegistryHandle(RelativeTo, Path, FALSE, &KeyHandle); 1019 if (!NT_SUCCESS(Status)) return Status; 1020 1021 /* Initialize the path */ 1022 RtlInitUnicodeString(&KeyPath, 1023 (RelativeTo & RTL_REGISTRY_HANDLE) ? NULL : Path); 1024 1025 /* Allocate a query buffer */ 1026 BufferSize = RtlpAllocDeallocQueryBufferSize; 1027 KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize, NULL, 0, &Status); 1028 if (!KeyValueInfo) 1029 { 1030 /* Close the handle if we have one and fail */ 1031 RtlpCloseRegistryHandle(RelativeTo, KeyHandle); 1032 return Status; 1033 } 1034 1035 /* Set defaults */ 1036 KeyValueInfo->DataOffset = 0; 1037 InfoSize = BufferSize - sizeof(UNICODE_NULL); 1038 CurrentKey = KeyHandle; 1039 1040 /* Loop the query table */ 1041 while ((QueryTable->QueryRoutine) || 1042 (QueryTable->Flags & (RTL_QUERY_REGISTRY_SUBKEY | 1043 RTL_QUERY_REGISTRY_DIRECT))) 1044 { 1045 /* Check if the request is invalid */ 1046 if ((QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) && 1047 (!(QueryTable->Name) || 1048 (QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY) || 1049 (QueryTable->QueryRoutine))) 1050 { 1051 /* Fail */ 1052 Status = STATUS_INVALID_PARAMETER; 1053 break; 1054 } 1055 1056 /* Check if we want a specific key */ 1057 if (QueryTable->Flags & (RTL_QUERY_REGISTRY_TOPKEY | 1058 RTL_QUERY_REGISTRY_SUBKEY)) 1059 { 1060 /* Check if we're working with another handle */ 1061 if (CurrentKey != KeyHandle) 1062 { 1063 /* Close our current key and use the top */ 1064 NtClose(CurrentKey); 1065 CurrentKey = KeyHandle; 1066 } 1067 } 1068 1069 /* Check if we're querying the subkey */ 1070 if (QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY) 1071 { 1072 /* Make sure we have a name */ 1073 if (!QueryTable->Name) 1074 { 1075 /* Fail */ 1076 Status = STATUS_INVALID_PARAMETER; 1077 } 1078 else 1079 { 1080 /* Initialize the name */ 1081 RtlInitUnicodeString(&KeyPath, QueryTable->Name); 1082 1083 /* Get the key handle */ 1084 InitializeObjectAttributes(&ObjectAttributes, 1085 &KeyPath, 1086 OBJ_CASE_INSENSITIVE | 1087 OBJ_KERNEL_HANDLE, 1088 KeyHandle, 1089 NULL); 1090 Status = ZwOpenKey(&CurrentKey, 1091 MAXIMUM_ALLOWED, 1092 &ObjectAttributes); 1093 if (NT_SUCCESS(Status)) 1094 { 1095 /* If we have a query routine, go enumerate values */ 1096 if (QueryTable->QueryRoutine) goto ProcessValues; 1097 } 1098 } 1099 } 1100 else if (QueryTable->Name) 1101 { 1102 /* Initialize the path */ 1103 RtlInitUnicodeString(&KeyValueName, QueryTable->Name); 1104 1105 /* Start query loop */ 1106 i = 0; 1107 while (TRUE) 1108 { 1109 /* Make sure we didn't retry too many times */ 1110 if (i++ > 4) 1111 { 1112 /* Fail */ 1113 DPRINT1("RtlQueryRegistryValues: Miscomputed buffer size " 1114 "at line %d\n", __LINE__); 1115 break; 1116 } 1117 1118 /* Query key information */ 1119 Status = ZwQueryValueKey(CurrentKey, 1120 &KeyValueName, 1121 KeyValueFullInformation, 1122 KeyValueInfo, 1123 (ULONG)InfoSize, 1124 &ResultLength); 1125 if (Status == STATUS_BUFFER_OVERFLOW) 1126 { 1127 /* Normalize status code */ 1128 Status = STATUS_BUFFER_TOO_SMALL; 1129 } 1130 1131 /* Check for failure */ 1132 if (!NT_SUCCESS(Status)) 1133 { 1134 /* Check if we didn't find it */ 1135 if (Status == STATUS_OBJECT_NAME_NOT_FOUND) 1136 { 1137 /* Setup a default */ 1138 KeyValueInfo->Type = REG_NONE; 1139 KeyValueInfo->DataLength = 0; 1140 ResultLength = (ULONG)InfoSize; 1141 1142 /* Call the query routine */ 1143 Status = RtlpCallQueryRegistryRoutine(QueryTable, 1144 KeyValueInfo, 1145 &ResultLength, 1146 Context, 1147 Environment); 1148 } 1149 1150 /* Check for buffer being too small */ 1151 if (Status == STATUS_BUFFER_TOO_SMALL) 1152 { 1153 /* Increase allocation size */ 1154 BufferSize = ResultLength + 1155 sizeof(ULONG_PTR) + 1156 sizeof(UNICODE_NULL); 1157 KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize, 1158 KeyValueInfo, 1159 BufferSize, 1160 &Status); 1161 if (!KeyValueInfo) break; 1162 1163 /* Update the data */ 1164 KeyValueInfo->DataOffset = 0; 1165 InfoSize = BufferSize - sizeof(UNICODE_NULL); 1166 continue; 1167 } 1168 } 1169 else 1170 { 1171 /* Check if this is a multi-string */ 1172 if (KeyValueInfo->Type == REG_MULTI_SZ) 1173 { 1174 /* Add a null-char */ 1175 ((PWCHAR)KeyValueInfo)[ResultLength / sizeof(WCHAR)] = UNICODE_NULL; 1176 KeyValueInfo->DataLength += sizeof(UNICODE_NULL); 1177 } 1178 1179 /* Call the query routine */ 1180 ResultLength = (ULONG)InfoSize; 1181 Status = RtlpCallQueryRegistryRoutine(QueryTable, 1182 KeyValueInfo, 1183 &ResultLength, 1184 Context, 1185 Environment); 1186 1187 /* Check for buffer being too small */ 1188 if (Status == STATUS_BUFFER_TOO_SMALL) 1189 { 1190 /* Increase allocation size */ 1191 BufferSize = ResultLength + 1192 sizeof(ULONG_PTR) + 1193 sizeof(UNICODE_NULL); 1194 KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize, 1195 KeyValueInfo, 1196 BufferSize, 1197 &Status); 1198 if (!KeyValueInfo) break; 1199 1200 /* Update the data */ 1201 KeyValueInfo->DataOffset = 0; 1202 InfoSize = BufferSize - sizeof(UNICODE_NULL); 1203 continue; 1204 } 1205 1206 /* Check if we need to delete the key */ 1207 if ((NT_SUCCESS(Status)) && 1208 (QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE)) 1209 { 1210 /* Delete it */ 1211 ZwDeleteValueKey(CurrentKey, &KeyValueName); 1212 } 1213 } 1214 1215 /* We're done, break out */ 1216 break; 1217 } 1218 } 1219 else if (QueryTable->Flags & RTL_QUERY_REGISTRY_NOVALUE) 1220 { 1221 /* Just call the query routine */ 1222 Status = QueryTable->QueryRoutine(NULL, 1223 REG_NONE, 1224 NULL, 1225 0, 1226 Context, 1227 QueryTable->EntryContext); 1228 } 1229 else 1230 { 1231 ProcessValues: 1232 /* Loop every value */ 1233 i = Value = 0; 1234 while (TRUE) 1235 { 1236 /* Enumerate the keys */ 1237 Status = ZwEnumerateValueKey(CurrentKey, 1238 Value, 1239 KeyValueFullInformation, 1240 KeyValueInfo, 1241 (ULONG)InfoSize, 1242 &ResultLength); 1243 if (Status == STATUS_BUFFER_OVERFLOW) 1244 { 1245 /* Normalize the status */ 1246 Status = STATUS_BUFFER_TOO_SMALL; 1247 } 1248 1249 /* Check if we found all the entries */ 1250 if (Status == STATUS_NO_MORE_ENTRIES) 1251 { 1252 /* Check if this was the first entry and caller needs it */ 1253 if (!(Value) && 1254 (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED)) 1255 { 1256 /* Fail */ 1257 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1258 } 1259 else 1260 { 1261 /* Otherwise, it's ok */ 1262 Status = STATUS_SUCCESS; 1263 } 1264 break; 1265 } 1266 1267 /* Check if enumeration worked */ 1268 if (NT_SUCCESS(Status)) 1269 { 1270 /* Call the query routine */ 1271 ResultLength = (ULONG)InfoSize; 1272 Status = RtlpCallQueryRegistryRoutine(QueryTable, 1273 KeyValueInfo, 1274 &ResultLength, 1275 Context, 1276 Environment); 1277 } 1278 1279 /* Check if the query failed */ 1280 if (Status == STATUS_BUFFER_TOO_SMALL) 1281 { 1282 /* Increase allocation size */ 1283 BufferSize = ResultLength + 1284 sizeof(ULONG_PTR) + 1285 sizeof(UNICODE_NULL); 1286 KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize, 1287 KeyValueInfo, 1288 BufferSize, 1289 &Status); 1290 if (!KeyValueInfo) break; 1291 1292 /* Update the data */ 1293 KeyValueInfo->DataOffset = 0; 1294 InfoSize = BufferSize - sizeof(UNICODE_NULL); 1295 1296 /* Try the value again unless it's been too many times */ 1297 if (i++ <= 4) continue; 1298 break; 1299 } 1300 1301 /* Break out if we failed */ 1302 if (!NT_SUCCESS(Status)) break; 1303 1304 /* Reset the number of retries and check if we need to delete */ 1305 i = 0; 1306 if (QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE) 1307 { 1308 /* Build the name */ 1309 RtlInitEmptyUnicodeString(&KeyValueName, 1310 KeyValueInfo->Name, 1311 (USHORT)KeyValueInfo->NameLength); 1312 KeyValueName.Length = KeyValueName.MaximumLength; 1313 1314 /* Delete the key */ 1315 Status = ZwDeleteValueKey(CurrentKey, &KeyValueName); 1316 if (NT_SUCCESS(Status)) Value--; 1317 } 1318 1319 /* Go to the next value */ 1320 Value++; 1321 } 1322 } 1323 1324 /* Check if we failed anywhere along the road */ 1325 if (!NT_SUCCESS(Status)) break; 1326 1327 /* Continue */ 1328 QueryTable++; 1329 } 1330 1331 /* Check if we need to close our handle */ 1332 if (KeyHandle) RtlpCloseRegistryHandle(RelativeTo, KeyHandle); 1333 if ((CurrentKey) && (CurrentKey != KeyHandle)) ZwClose(CurrentKey); 1334 1335 /* Free our buffer and return status */ 1336 RtlpAllocDeallocQueryBuffer(NULL, KeyValueInfo, BufferSize, NULL); 1337 return Status; 1338 } 1339 1340 /* EOF */ 1341