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