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
RtlpQueryRegistryDirect(IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Buffer)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 = RtlpAllocateStringMemory(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
RtlpCallQueryRegistryRoutine(IN PRTL_QUERY_REGISTRY_TABLE QueryTable,IN PKEY_VALUE_FULL_INFORMATION KeyValueInfo,IN OUT PULONG InfoSize,IN PVOID Context,IN PVOID Environment)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 we have length */
200 if (KeyValueInfo->DataLength)
201 {
202 /* Increase the spare data */
203 SpareData += KeyValueInfo->DataOffset +
204 KeyValueInfo->DataLength;
205 }
206 else
207 {
208 /* Otherwise, the spare data only has the name data */
209 SpareData += FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) +
210 KeyValueInfo->NameLength;
211 }
212
213 /* Align the pointer and get new size of spare data */
214 SpareData = (PVOID)(((ULONG_PTR)SpareData + 7) & ~7);
215 SpareLength = DataEnd - SpareData;
216
217 /* Check if we have space to copy the data */
218 RequiredLength = KeyValueInfo->NameLength + sizeof(UNICODE_NULL);
219 if ((SpareData > DataEnd) || (SpareLength < RequiredLength))
220 {
221 /* Fail and return the missing length */
222 *InfoSize = (ULONG)(SpareData - (PCHAR)KeyValueInfo) + RequiredLength;
223 return STATUS_BUFFER_TOO_SMALL;
224 }
225
226 /* Check if this isn't a direct return */
227 if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT))
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 destination 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
RtlpGetRegistryHandle(IN ULONG RelativeTo,IN PCWSTR Path,IN BOOLEAN Create,IN PHANDLE KeyHandle)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
RtlpCloseRegistryHandle(_In_ ULONG RelativeTo,_In_ HANDLE KeyHandle)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
RtlCheckRegistryKey(IN ULONG RelativeTo,IN PWSTR Path)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
RtlCreateRegistryKey(IN ULONG RelativeTo,IN PWSTR Path)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
RtlDeleteRegistryValue(IN ULONG RelativeTo,IN PCWSTR Path,IN PCWSTR ValueName)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
RtlWriteRegistryValue(IN ULONG RelativeTo,IN PCWSTR Path,IN PCWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength)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
RtlOpenCurrentUser(IN ACCESS_MASK DesiredAccess,OUT PHANDLE KeyHandle)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
RtlFormatCurrentUserKeyPath(OUT PUNICODE_STRING KeyPath)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
RtlpNtCreateKey(OUT HANDLE KeyHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN ULONG TitleIndex,IN PUNICODE_STRING Class,OUT PULONG Disposition)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
RtlpNtEnumerateSubKey(IN HANDLE KeyHandle,OUT PUNICODE_STRING SubKeyName,IN ULONG Index,IN ULONG Unused)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
RtlpNtMakeTemporaryKey(IN HANDLE KeyHandle)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
RtlpNtOpenKey(OUT HANDLE KeyHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN ULONG Unused)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
RtlpNtQueryValueKey(IN HANDLE KeyHandle,OUT PULONG Type OPTIONAL,OUT PVOID Data OPTIONAL,IN OUT PULONG DataLength OPTIONAL,IN ULONG Unused)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
RtlpNtSetValueKey(IN HANDLE KeyHandle,IN ULONG Type,IN PVOID Data,IN ULONG DataLength)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
RtlQueryRegistryValues(IN ULONG RelativeTo,IN PCWSTR Path,IN PRTL_QUERY_REGISTRY_TABLE QueryTable,IN PVOID Context,IN PVOID Environment OPTIONAL)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