[NTOS:CM] Reuse the saved previous-mode from the ExGetPreviousMode() calls.
[reactos.git] / ntoskrnl / config / ntapi.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/config/ntapi.c
5 * PURPOSE: Configuration Manager - Internal Registry APIs
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Eric Kohl
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include "ntoskrnl.h"
13 #define NDEBUG
14 #include "debug.h"
15
16 BOOLEAN CmBootAcceptFirstTime = TRUE;
17 BOOLEAN CmFirstTime = TRUE;
18 extern ULONG InitSafeBootMode;
19
20 /* FUNCTIONS *****************************************************************/
21
22 NTSTATUS
23 NTAPI
24 NtCreateKey(OUT PHANDLE KeyHandle,
25 IN ACCESS_MASK DesiredAccess,
26 IN POBJECT_ATTRIBUTES ObjectAttributes,
27 IN ULONG TitleIndex,
28 IN PUNICODE_STRING Class OPTIONAL,
29 IN ULONG CreateOptions,
30 OUT PULONG Disposition OPTIONAL)
31 {
32 NTSTATUS Status;
33 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
34 CM_PARSE_CONTEXT ParseContext = {0};
35 HANDLE Handle;
36 PAGED_CODE();
37
38 DPRINT("NtCreateKey(Path: %wZ, Root %x, Access: %x, CreateOptions %x)\n",
39 ObjectAttributes->ObjectName, ObjectAttributes->RootDirectory,
40 DesiredAccess, CreateOptions);
41
42 /* Ignore the WOW64 flag, it's not valid in the kernel */
43 DesiredAccess &= ~KEY_WOW64_RES;
44
45 /* Check for user-mode caller */
46 if (PreviousMode != KernelMode)
47 {
48 /* Prepare to probe parameters */
49 _SEH2_TRY
50 {
51 /* Check if we have a class */
52 if (Class)
53 {
54 /* Probe it */
55 ParseContext.Class = ProbeForReadUnicodeString(Class);
56 ProbeForRead(ParseContext.Class.Buffer,
57 ParseContext.Class.Length,
58 sizeof(WCHAR));
59 }
60
61 /* Probe the key handle */
62 ProbeForWriteHandle(KeyHandle);
63 *KeyHandle = NULL;
64
65 /* Probe object attributes */
66 ProbeForRead(ObjectAttributes,
67 sizeof(OBJECT_ATTRIBUTES),
68 sizeof(ULONG));
69
70 if (Disposition)
71 ProbeForWriteUlong(Disposition);
72 }
73 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
74 {
75 /* Return the exception code */
76 _SEH2_YIELD(return _SEH2_GetExceptionCode());
77 }
78 _SEH2_END;
79 }
80 else
81 {
82 /* Save the class directly */
83 if (Class) ParseContext.Class = *Class;
84 }
85
86 /* Setup the parse context */
87 ParseContext.CreateOperation = TRUE;
88 ParseContext.CreateOptions = CreateOptions;
89
90 /* Do the create */
91 Status = ObOpenObjectByName(ObjectAttributes,
92 CmpKeyObjectType,
93 PreviousMode,
94 NULL,
95 DesiredAccess,
96 &ParseContext,
97 &Handle);
98
99 _SEH2_TRY
100 {
101 /* Return data to user */
102 if (NT_SUCCESS(Status)) *KeyHandle = Handle;
103 if (Disposition) *Disposition = ParseContext.Disposition;
104 }
105 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
106 {
107 /* Get the status */
108 Status = _SEH2_GetExceptionCode();
109 }
110 _SEH2_END;
111
112 DPRINT("Returning handle %x, Status %x.\n", Handle, Status);
113
114 /* Return status */
115 return Status;
116 }
117
118 NTSTATUS
119 NTAPI
120 NtOpenKey(OUT PHANDLE KeyHandle,
121 IN ACCESS_MASK DesiredAccess,
122 IN POBJECT_ATTRIBUTES ObjectAttributes)
123 {
124 CM_PARSE_CONTEXT ParseContext = {0};
125 HANDLE Handle;
126 NTSTATUS Status;
127 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
128 PAGED_CODE();
129 DPRINT("NtOpenKey(Path: %wZ, Root %x, Access: %x)\n",
130 ObjectAttributes->ObjectName, ObjectAttributes->RootDirectory, DesiredAccess);
131
132 /* Ignore the WOW64 flag, it's not valid in the kernel */
133 DesiredAccess &= ~KEY_WOW64_RES;
134
135 /* Check for user-mode caller */
136 if (PreviousMode != KernelMode)
137 {
138 /* Prepare to probe parameters */
139 _SEH2_TRY
140 {
141 /* Probe the key handle */
142 ProbeForWriteHandle(KeyHandle);
143 *KeyHandle = NULL;
144
145 /* Probe object attributes */
146 ProbeForRead(ObjectAttributes,
147 sizeof(OBJECT_ATTRIBUTES),
148 sizeof(ULONG));
149 }
150 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
151 {
152 /* Return the exception code */
153 _SEH2_YIELD(return _SEH2_GetExceptionCode());
154 }
155 _SEH2_END;
156 }
157
158 /* Just let the object manager handle this */
159 Status = ObOpenObjectByName(ObjectAttributes,
160 CmpKeyObjectType,
161 PreviousMode,
162 NULL,
163 DesiredAccess,
164 &ParseContext,
165 &Handle);
166
167 /* Only do this if we succeeded */
168 if (NT_SUCCESS(Status))
169 {
170 _SEH2_TRY
171 {
172 /* Return the handle to caller */
173 *KeyHandle = Handle;
174 }
175 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
176 {
177 /* Get the status */
178 Status = _SEH2_GetExceptionCode();
179 }
180 _SEH2_END;
181 }
182
183 DPRINT("Returning handle %x, Status %x.\n", Handle, Status);
184
185 /* Return status */
186 return Status;
187 }
188
189
190 NTSTATUS
191 NTAPI
192 NtDeleteKey(IN HANDLE KeyHandle)
193 {
194 PCM_KEY_BODY KeyObject;
195 NTSTATUS Status;
196 REG_DELETE_KEY_INFORMATION DeleteKeyInfo;
197 REG_POST_OPERATION_INFORMATION PostOperationInfo;
198 PAGED_CODE();
199 DPRINT("NtDeleteKey(KH 0x%p)\n", KeyHandle);
200
201 /* Verify that the handle is valid and is a registry key */
202 Status = ObReferenceObjectByHandle(KeyHandle,
203 DELETE,
204 CmpKeyObjectType,
205 ExGetPreviousMode(),
206 (PVOID*)&KeyObject,
207 NULL);
208 if (!NT_SUCCESS(Status)) return Status;
209
210 /* Setup the callback */
211 PostOperationInfo.Object = (PVOID)KeyObject;
212 DeleteKeyInfo.Object = (PVOID)KeyObject;
213 Status = CmiCallRegisteredCallbacks(RegNtPreDeleteKey, &DeleteKeyInfo);
214 if (NT_SUCCESS(Status))
215 {
216 /* Check if we are read-only */
217 if ((KeyObject->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY) ||
218 (KeyObject->KeyControlBlock->ParentKcb->ExtFlags & CM_KCB_READ_ONLY_KEY))
219 {
220 /* Fail */
221 Status = STATUS_ACCESS_DENIED;
222 }
223 else
224 {
225 /* Call the internal API */
226 Status = CmDeleteKey(KeyObject);
227 }
228
229 /* Do post callback */
230 PostOperationInfo.Status = Status;
231 CmiCallRegisteredCallbacks(RegNtPostDeleteKey, &PostOperationInfo);
232 }
233
234 /* Dereference the object */
235 ObDereferenceObject(KeyObject);
236 return Status;
237 }
238
239 NTSTATUS
240 NTAPI
241 NtEnumerateKey(IN HANDLE KeyHandle,
242 IN ULONG Index,
243 IN KEY_INFORMATION_CLASS KeyInformationClass,
244 OUT PVOID KeyInformation,
245 IN ULONG Length,
246 OUT PULONG ResultLength)
247 {
248 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
249 NTSTATUS Status;
250 PCM_KEY_BODY KeyObject;
251 REG_ENUMERATE_KEY_INFORMATION EnumerateKeyInfo;
252 REG_POST_OPERATION_INFORMATION PostOperationInfo;
253 PAGED_CODE();
254 DPRINT("NtEnumerateKey() KH 0x%p, Index 0x%x, KIC %d, Length %lu\n",
255 KeyHandle, Index, KeyInformationClass, Length);
256
257 /* Reject classes we don't know about */
258 if ((KeyInformationClass != KeyBasicInformation) &&
259 (KeyInformationClass != KeyNodeInformation) &&
260 (KeyInformationClass != KeyFullInformation))
261 {
262 /* Fail */
263 return STATUS_INVALID_PARAMETER;
264 }
265
266 /* Verify that the handle is valid and is a registry key */
267 Status = ObReferenceObjectByHandle(KeyHandle,
268 KEY_ENUMERATE_SUB_KEYS,
269 CmpKeyObjectType,
270 PreviousMode,
271 (PVOID*)&KeyObject,
272 NULL);
273 if (!NT_SUCCESS(Status)) return Status;
274
275 if (PreviousMode != KernelMode)
276 {
277 _SEH2_TRY
278 {
279 ProbeForWriteUlong(ResultLength);
280 ProbeForWrite(KeyInformation,
281 Length,
282 sizeof(ULONG));
283 }
284 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
285 {
286 /* Dereference and return status */
287 ObDereferenceObject(KeyObject);
288 _SEH2_YIELD(return _SEH2_GetExceptionCode());
289 }
290 _SEH2_END;
291 }
292
293 /* Setup the callback */
294 PostOperationInfo.Object = (PVOID)KeyObject;
295 EnumerateKeyInfo.Object = (PVOID)KeyObject;
296 EnumerateKeyInfo.Index = Index;
297 EnumerateKeyInfo.KeyInformationClass = KeyInformationClass;
298 EnumerateKeyInfo.Length = Length;
299 EnumerateKeyInfo.ResultLength = ResultLength;
300
301 /* Do the callback */
302 Status = CmiCallRegisteredCallbacks(RegNtPreEnumerateKey, &EnumerateKeyInfo);
303 if (NT_SUCCESS(Status))
304 {
305 /* Call the internal API */
306 Status = CmEnumerateKey(KeyObject->KeyControlBlock,
307 Index,
308 KeyInformationClass,
309 KeyInformation,
310 Length,
311 ResultLength);
312
313 /* Do the post callback */
314 PostOperationInfo.Status = Status;
315 CmiCallRegisteredCallbacks(RegNtPostEnumerateKey, &PostOperationInfo);
316 }
317
318 /* Dereference and return status */
319 ObDereferenceObject(KeyObject);
320 DPRINT("Returning status %x.\n", Status);
321 return Status;
322 }
323
324 NTSTATUS
325 NTAPI
326 NtEnumerateValueKey(IN HANDLE KeyHandle,
327 IN ULONG Index,
328 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
329 OUT PVOID KeyValueInformation,
330 IN ULONG Length,
331 OUT PULONG ResultLength)
332 {
333 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
334 NTSTATUS Status;
335 PCM_KEY_BODY KeyObject;
336 REG_ENUMERATE_VALUE_KEY_INFORMATION EnumerateValueKeyInfo;
337 REG_POST_OPERATION_INFORMATION PostOperationInfo;
338 PAGED_CODE();
339 DPRINT("NtEnumerateValueKey() KH 0x%p, Index 0x%x, KVIC %d, Length %lu\n",
340 KeyHandle, Index, KeyValueInformationClass, Length);
341
342 /* Reject classes we don't know about */
343 if ((KeyValueInformationClass != KeyValueBasicInformation) &&
344 (KeyValueInformationClass != KeyValueFullInformation) &&
345 (KeyValueInformationClass != KeyValuePartialInformation))
346 {
347 /* Fail */
348 return STATUS_INVALID_PARAMETER;
349 }
350
351 /* Verify that the handle is valid and is a registry key */
352 Status = ObReferenceObjectByHandle(KeyHandle,
353 KEY_QUERY_VALUE,
354 CmpKeyObjectType,
355 PreviousMode,
356 (PVOID*)&KeyObject,
357 NULL);
358 if (!NT_SUCCESS(Status)) return Status;
359
360 if (PreviousMode != KernelMode)
361 {
362 _SEH2_TRY
363 {
364 ProbeForWriteUlong(ResultLength);
365 ProbeForWrite(KeyValueInformation,
366 Length,
367 sizeof(ULONG));
368 }
369 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
370 {
371 /* Dereference and return status */
372 ObDereferenceObject(KeyObject);
373 _SEH2_YIELD(return _SEH2_GetExceptionCode());
374 }
375 _SEH2_END;
376 }
377
378 /* Setup the callback */
379 PostOperationInfo.Object = (PVOID)KeyObject;
380 EnumerateValueKeyInfo.Object = (PVOID)KeyObject;
381 EnumerateValueKeyInfo.Index = Index;
382 EnumerateValueKeyInfo.KeyValueInformationClass = KeyValueInformationClass;
383 EnumerateValueKeyInfo.KeyValueInformation = KeyValueInformation;
384 EnumerateValueKeyInfo.Length = Length;
385 EnumerateValueKeyInfo.ResultLength = ResultLength;
386
387 /* Do the callback */
388 Status = CmiCallRegisteredCallbacks(RegNtPreEnumerateValueKey,
389 &EnumerateValueKeyInfo);
390 if (NT_SUCCESS(Status))
391 {
392 /* Call the internal API */
393 Status = CmEnumerateValueKey(KeyObject->KeyControlBlock,
394 Index,
395 KeyValueInformationClass,
396 KeyValueInformation,
397 Length,
398 ResultLength);
399
400 /* Do the post callback */
401 PostOperationInfo.Status = Status;
402 CmiCallRegisteredCallbacks(RegNtPostEnumerateValueKey, &PostOperationInfo);
403 }
404
405 ObDereferenceObject(KeyObject);
406 return Status;
407 }
408
409 NTSTATUS
410 NTAPI
411 NtQueryKey(IN HANDLE KeyHandle,
412 IN KEY_INFORMATION_CLASS KeyInformationClass,
413 OUT PVOID KeyInformation,
414 IN ULONG Length,
415 OUT PULONG ResultLength)
416 {
417 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
418 NTSTATUS Status;
419 PCM_KEY_BODY KeyObject;
420 REG_QUERY_KEY_INFORMATION QueryKeyInfo;
421 REG_POST_OPERATION_INFORMATION PostOperationInfo;
422 OBJECT_HANDLE_INFORMATION HandleInfo;
423 PAGED_CODE();
424 DPRINT("NtQueryKey() KH 0x%p, KIC %d, Length %lu\n",
425 KeyHandle, KeyInformationClass, Length);
426
427 /* Reject invalid classes */
428 if ((KeyInformationClass != KeyBasicInformation) &&
429 (KeyInformationClass != KeyNodeInformation) &&
430 (KeyInformationClass != KeyFullInformation) &&
431 (KeyInformationClass != KeyNameInformation) &&
432 (KeyInformationClass != KeyCachedInformation) &&
433 (KeyInformationClass != KeyFlagsInformation))
434 {
435 /* Fail */
436 return STATUS_INVALID_PARAMETER;
437 }
438
439 /* Check if just the name is required */
440 if (KeyInformationClass == KeyNameInformation)
441 {
442 /* Ignore access level */
443 Status = ObReferenceObjectByHandle(KeyHandle,
444 0,
445 CmpKeyObjectType,
446 PreviousMode,
447 (PVOID*)&KeyObject,
448 &HandleInfo);
449 if (NT_SUCCESS(Status))
450 {
451 /* At least a single bit of access is required */
452 if (!HandleInfo.GrantedAccess)
453 {
454 /* No such luck */
455 ObDereferenceObject(KeyObject);
456 Status = STATUS_ACCESS_DENIED;
457 }
458 }
459 }
460 else
461 {
462 /* Get a reference */
463 Status = ObReferenceObjectByHandle(KeyHandle,
464 KEY_QUERY_VALUE,
465 CmpKeyObjectType,
466 PreviousMode,
467 (PVOID*)&KeyObject,
468 NULL);
469 }
470
471 /* Quit on failure */
472 if (!NT_SUCCESS(Status)) return Status;
473
474 if (PreviousMode != KernelMode)
475 {
476 _SEH2_TRY
477 {
478 ProbeForWriteUlong(ResultLength);
479 ProbeForWrite(KeyInformation,
480 Length,
481 sizeof(ULONG));
482 }
483 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
484 {
485 /* Dereference and return status */
486 ObDereferenceObject(KeyObject);
487 _SEH2_YIELD(return _SEH2_GetExceptionCode());
488 }
489 _SEH2_END;
490 }
491
492 /* Setup the callback */
493 PostOperationInfo.Object = (PVOID)KeyObject;
494 QueryKeyInfo.Object = (PVOID)KeyObject;
495 QueryKeyInfo.KeyInformationClass = KeyInformationClass;
496 QueryKeyInfo.KeyInformation = KeyInformation;
497 QueryKeyInfo.Length = Length;
498 QueryKeyInfo.ResultLength = ResultLength;
499
500 /* Do the callback */
501 Status = CmiCallRegisteredCallbacks(RegNtPreQueryKey, &QueryKeyInfo);
502 if (NT_SUCCESS(Status))
503 {
504 /* Call the internal API */
505 Status = CmQueryKey(KeyObject->KeyControlBlock,
506 KeyInformationClass,
507 KeyInformation,
508 Length,
509 ResultLength);
510
511 /* Do the post callback */
512 PostOperationInfo.Status = Status;
513 CmiCallRegisteredCallbacks(RegNtPostQueryKey, &PostOperationInfo);
514 }
515
516 /* Dereference and return status */
517 ObDereferenceObject(KeyObject);
518 return Status;
519 }
520
521 NTSTATUS
522 NTAPI
523 NtQueryValueKey(IN HANDLE KeyHandle,
524 IN PUNICODE_STRING ValueName,
525 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
526 OUT PVOID KeyValueInformation,
527 IN ULONG Length,
528 OUT PULONG ResultLength)
529 {
530 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
531 NTSTATUS Status;
532 PCM_KEY_BODY KeyObject;
533 REG_QUERY_VALUE_KEY_INFORMATION QueryValueKeyInfo;
534 REG_POST_OPERATION_INFORMATION PostOperationInfo;
535 UNICODE_STRING ValueNameCopy = *ValueName;
536 PAGED_CODE();
537 DPRINT("NtQueryValueKey() KH 0x%p, VN '%wZ', KVIC %d, Length %lu\n",
538 KeyHandle, ValueName, KeyValueInformationClass, Length);
539
540 /* Verify that the handle is valid and is a registry key */
541 Status = ObReferenceObjectByHandle(KeyHandle,
542 KEY_QUERY_VALUE,
543 CmpKeyObjectType,
544 PreviousMode,
545 (PVOID*)&KeyObject,
546 NULL);
547 if (!NT_SUCCESS(Status)) return Status;
548
549 if (PreviousMode != KernelMode)
550 {
551 _SEH2_TRY
552 {
553 ProbeForWriteUlong(ResultLength);
554 ProbeForWrite(KeyValueInformation,
555 Length,
556 sizeof(ULONG));
557 }
558 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
559 {
560 /* Dereference and return status */
561 ObDereferenceObject(KeyObject);
562 _SEH2_YIELD(return _SEH2_GetExceptionCode());
563 }
564 _SEH2_END;
565 }
566
567 /* Make sure the name is aligned properly */
568 if ((ValueNameCopy.Length & (sizeof(WCHAR) - 1)))
569 {
570 /* It isn't, so we'll fail */
571 ObDereferenceObject(KeyObject);
572 return STATUS_INVALID_PARAMETER;
573 }
574 else
575 {
576 /* Ignore any null characters at the end */
577 while ((ValueNameCopy.Length) &&
578 !(ValueNameCopy.Buffer[ValueNameCopy.Length / sizeof(WCHAR) - 1]))
579 {
580 /* Skip it */
581 ValueNameCopy.Length -= sizeof(WCHAR);
582 }
583 }
584
585 /* Setup the callback */
586 PostOperationInfo.Object = (PVOID)KeyObject;
587 QueryValueKeyInfo.Object = (PVOID)KeyObject;
588 QueryValueKeyInfo.ValueName = &ValueNameCopy;
589 QueryValueKeyInfo.KeyValueInformationClass = KeyValueInformationClass;
590 QueryValueKeyInfo.Length = Length;
591 QueryValueKeyInfo.ResultLength = ResultLength;
592
593 /* Do the callback */
594 Status = CmiCallRegisteredCallbacks(RegNtPreQueryValueKey, &QueryValueKeyInfo);
595 if (NT_SUCCESS(Status))
596 {
597 /* Call the internal API */
598 Status = CmQueryValueKey(KeyObject->KeyControlBlock,
599 ValueNameCopy,
600 KeyValueInformationClass,
601 KeyValueInformation,
602 Length,
603 ResultLength);
604
605 /* Do the post callback */
606 PostOperationInfo.Status = Status;
607 CmiCallRegisteredCallbacks(RegNtPostQueryValueKey, &PostOperationInfo);
608 }
609
610 /* Dereference and return status */
611 ObDereferenceObject(KeyObject);
612 return Status;
613 }
614
615 NTSTATUS
616 NTAPI
617 NtSetValueKey(IN HANDLE KeyHandle,
618 IN PUNICODE_STRING ValueName,
619 IN ULONG TitleIndex,
620 IN ULONG Type,
621 IN PVOID Data,
622 IN ULONG DataSize)
623 {
624 NTSTATUS Status = STATUS_SUCCESS;
625 PCM_KEY_BODY KeyObject = NULL;
626 REG_SET_VALUE_KEY_INFORMATION SetValueKeyInfo;
627 REG_POST_OPERATION_INFORMATION PostOperationInfo;
628 UNICODE_STRING ValueNameCopy;
629 KPROCESSOR_MODE PreviousMode;
630
631 PAGED_CODE();
632
633 PreviousMode = ExGetPreviousMode();
634
635 if (!DataSize)
636 Data = NULL;
637
638 /* Probe and copy the data */
639 if ((PreviousMode != KernelMode) && (DataSize != 0))
640 {
641 PVOID DataCopy = ExAllocatePoolWithTag(PagedPool, DataSize, TAG_CM);
642 if (!DataCopy)
643 return STATUS_INSUFFICIENT_RESOURCES;
644 _SEH2_TRY
645 {
646 ProbeForRead(Data, DataSize, 1);
647 RtlCopyMemory(DataCopy, Data, DataSize);
648 }
649 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
650 {
651 Status = _SEH2_GetExceptionCode();
652 }
653 _SEH2_END;
654
655 if (!NT_SUCCESS(Status))
656 {
657 ExFreePoolWithTag(DataCopy, TAG_CM);
658 return Status;
659 }
660 Data = DataCopy;
661 }
662
663 /* Capture the string */
664 Status = ProbeAndCaptureUnicodeString(&ValueNameCopy, PreviousMode, ValueName);
665 if (!NT_SUCCESS(Status))
666 goto end;
667
668 DPRINT("NtSetValueKey() KH 0x%p, VN '%wZ', TI %x, T %lu, DS %lu\n",
669 KeyHandle, &ValueNameCopy, TitleIndex, Type, DataSize);
670
671 /* Verify that the handle is valid and is a registry key */
672 Status = ObReferenceObjectByHandle(KeyHandle,
673 KEY_SET_VALUE,
674 CmpKeyObjectType,
675 PreviousMode,
676 (PVOID*)&KeyObject,
677 NULL);
678 if (!NT_SUCCESS(Status))
679 goto end;
680
681 /* Make sure the name is aligned, not too long, and the data under 4GB */
682 if ( (ValueNameCopy.Length > 32767) ||
683 ((ValueNameCopy.Length & (sizeof(WCHAR) - 1))) ||
684 (DataSize > 0x80000000))
685 {
686 /* Fail */
687 Status = STATUS_INVALID_PARAMETER;
688 goto end;
689 }
690
691 /* Ignore any null characters at the end */
692 while ((ValueNameCopy.Length) &&
693 !(ValueNameCopy.Buffer[ValueNameCopy.Length / sizeof(WCHAR) - 1]))
694 {
695 /* Skip it */
696 ValueNameCopy.Length -= sizeof(WCHAR);
697 }
698
699 /* Don't touch read-only keys */
700 if (KeyObject->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY)
701 {
702 /* Fail */
703 Status = STATUS_ACCESS_DENIED;
704 goto end;
705 }
706
707 /* Setup callback */
708 PostOperationInfo.Object = (PVOID)KeyObject;
709 SetValueKeyInfo.Object = (PVOID)KeyObject;
710 SetValueKeyInfo.ValueName = &ValueNameCopy;
711 SetValueKeyInfo.TitleIndex = TitleIndex;
712 SetValueKeyInfo.Type = Type;
713 SetValueKeyInfo.Data = Data;
714 SetValueKeyInfo.DataSize = DataSize;
715
716 /* Do the callback */
717 Status = CmiCallRegisteredCallbacks(RegNtPreSetValueKey, &SetValueKeyInfo);
718 if (NT_SUCCESS(Status))
719 {
720 /* Call the internal API */
721 Status = CmSetValueKey(KeyObject->KeyControlBlock,
722 &ValueNameCopy,
723 Type,
724 Data,
725 DataSize);
726 }
727
728 /* Do the post-callback */
729 PostOperationInfo.Status = Status;
730 CmiCallRegisteredCallbacks(RegNtPostSetValueKey, &PostOperationInfo);
731
732 end:
733 /* Dereference and return status */
734 if (KeyObject)
735 ObDereferenceObject(KeyObject);
736 ReleaseCapturedUnicodeString(&ValueNameCopy, PreviousMode);
737 if ((PreviousMode != KernelMode) && Data)
738 ExFreePoolWithTag(Data, TAG_CM);
739 return Status;
740 }
741
742 NTSTATUS
743 NTAPI
744 NtDeleteValueKey(IN HANDLE KeyHandle,
745 IN PUNICODE_STRING ValueName)
746 {
747 PCM_KEY_BODY KeyObject;
748 NTSTATUS Status;
749 REG_DELETE_VALUE_KEY_INFORMATION DeleteValueKeyInfo;
750 REG_POST_OPERATION_INFORMATION PostOperationInfo;
751 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
752 UNICODE_STRING ValueNameCopy = *ValueName;
753 PAGED_CODE();
754
755 /* Verify that the handle is valid and is a registry key */
756 Status = ObReferenceObjectByHandle(KeyHandle,
757 KEY_SET_VALUE,
758 CmpKeyObjectType,
759 PreviousMode,
760 (PVOID*)&KeyObject,
761 NULL);
762 if (!NT_SUCCESS(Status)) return Status;
763
764 /* Don't touch read-only keys */
765 if (KeyObject->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY)
766 {
767 /* Fail */
768 ObDereferenceObject(KeyObject);
769 return STATUS_ACCESS_DENIED;
770 }
771
772 /* Make sure the name is aligned properly */
773 if ((ValueNameCopy.Length & (sizeof(WCHAR) - 1)))
774 {
775 /* It isn't, so we'll fail */
776 ObDereferenceObject(KeyObject);
777 return STATUS_INVALID_PARAMETER;
778 }
779
780 /* Do the callback */
781 DeleteValueKeyInfo.Object = (PVOID)KeyObject;
782 DeleteValueKeyInfo.ValueName = ValueName;
783 Status = CmiCallRegisteredCallbacks(RegNtPreDeleteValueKey,
784 &DeleteValueKeyInfo);
785 if (NT_SUCCESS(Status))
786 {
787 /* Call the internal API */
788 Status = CmDeleteValueKey(KeyObject->KeyControlBlock, ValueNameCopy);
789
790 /* Do the post callback */
791 PostOperationInfo.Object = (PVOID)KeyObject;
792 PostOperationInfo.Status = Status;
793 CmiCallRegisteredCallbacks(RegNtPostDeleteValueKey,
794 &PostOperationInfo);
795 }
796
797 /* Dereference the key body */
798 ObDereferenceObject(KeyObject);
799 return Status;
800 }
801
802 NTSTATUS
803 NTAPI
804 NtFlushKey(IN HANDLE KeyHandle)
805 {
806 NTSTATUS Status;
807 PCM_KEY_BODY KeyObject;
808 PAGED_CODE();
809
810 /* Get the key object */
811 Status = ObReferenceObjectByHandle(KeyHandle,
812 0,
813 CmpKeyObjectType,
814 ExGetPreviousMode(),
815 (PVOID*)&KeyObject,
816 NULL);
817 if (!NT_SUCCESS(Status)) return Status;
818
819 /* Lock the registry */
820 CmpLockRegistry();
821
822 /* Lock the KCB */
823 CmpAcquireKcbLockShared(KeyObject->KeyControlBlock);
824
825 /* Make sure KCB isn't deleted */
826 if (KeyObject->KeyControlBlock->Delete)
827 {
828 /* Fail */
829 Status = STATUS_KEY_DELETED;
830 }
831 else
832 {
833 /* Call the internal API */
834 Status = CmFlushKey(KeyObject->KeyControlBlock, FALSE);
835 }
836
837 /* Release the locks */
838 CmpReleaseKcbLock(KeyObject->KeyControlBlock);
839 CmpUnlockRegistry();
840
841 /* Dereference the object and return status */
842 ObDereferenceObject(KeyObject);
843 return Status;
844 }
845
846 NTSTATUS
847 NTAPI
848 NtLoadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
849 IN POBJECT_ATTRIBUTES FileObjectAttributes)
850 {
851 /* Call the newer API */
852 return NtLoadKeyEx(KeyObjectAttributes, FileObjectAttributes, 0, NULL);
853 }
854
855 NTSTATUS
856 NTAPI
857 NtLoadKey2(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
858 IN POBJECT_ATTRIBUTES FileObjectAttributes,
859 IN ULONG Flags)
860 {
861 /* Call the newer API */
862 return NtLoadKeyEx(KeyObjectAttributes, FileObjectAttributes, Flags, NULL);
863 }
864
865 NTSTATUS
866 NTAPI
867 NtLoadKeyEx(IN POBJECT_ATTRIBUTES TargetKey,
868 IN POBJECT_ATTRIBUTES SourceFile,
869 IN ULONG Flags,
870 IN HANDLE TrustClassKey)
871 {
872 NTSTATUS Status;
873 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
874 PCM_KEY_BODY KeyBody = NULL;
875 PAGED_CODE();
876
877 /* Validate flags */
878 if (Flags & ~REG_NO_LAZY_FLUSH) return STATUS_INVALID_PARAMETER;
879
880 /* Validate privilege */
881 if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode))
882 {
883 DPRINT1("Restore Privilege missing!\n");
884 return STATUS_PRIVILEGE_NOT_HELD;
885 }
886
887 /* Block APCs */
888 KeEnterCriticalRegion();
889
890 /* Check if we have a trust class */
891 if (TrustClassKey)
892 {
893 /* Reference it */
894 Status = ObReferenceObjectByHandle(TrustClassKey,
895 0,
896 CmpKeyObjectType,
897 PreviousMode,
898 (PVOID*)&KeyBody,
899 NULL);
900 }
901
902 /* Call the internal API */
903 Status = CmLoadKey(TargetKey, SourceFile, Flags, KeyBody);
904
905 /* Dereference the trust key, if any */
906 if (KeyBody) ObDereferenceObject(KeyBody);
907
908 /* Bring back APCs */
909 KeLeaveCriticalRegion();
910
911 /* Return status */
912 return Status;
913 }
914
915 NTSTATUS
916 NTAPI
917 NtNotifyChangeKey(IN HANDLE KeyHandle,
918 IN HANDLE Event,
919 IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
920 IN PVOID ApcContext OPTIONAL,
921 OUT PIO_STATUS_BLOCK IoStatusBlock,
922 IN ULONG CompletionFilter,
923 IN BOOLEAN WatchTree,
924 OUT PVOID Buffer,
925 IN ULONG Length,
926 IN BOOLEAN Asynchronous)
927 {
928 /* Call the newer API */
929 return NtNotifyChangeMultipleKeys(KeyHandle,
930 0,
931 NULL,
932 Event,
933 ApcRoutine,
934 ApcContext,
935 IoStatusBlock,
936 CompletionFilter,
937 WatchTree,
938 Buffer,
939 Length,
940 Asynchronous);
941 }
942
943 NTSTATUS
944 NTAPI
945 NtInitializeRegistry(IN USHORT Flag)
946 {
947 BOOLEAN SetupBoot;
948 NTSTATUS Status = STATUS_SUCCESS;
949 PAGED_CODE();
950
951 /* Always do this as kernel mode */
952 if (KeGetPreviousMode() == UserMode) return ZwInitializeRegistry(Flag);
953
954 /* Enough of the system has booted by now */
955 Ki386PerfEnd();
956
957 /* Validate flag */
958 if (Flag > CM_BOOT_FLAG_MAX) return STATUS_INVALID_PARAMETER;
959
960 /* Check if boot was accepted */
961 if ((Flag >= CM_BOOT_FLAG_ACCEPTED) && (Flag <= CM_BOOT_FLAG_MAX))
962 {
963 /* Only allow once */
964 if (!CmBootAcceptFirstTime) return STATUS_ACCESS_DENIED;
965 CmBootAcceptFirstTime = FALSE;
966
967 /* Get the control set accepted */
968 Flag -= CM_BOOT_FLAG_ACCEPTED;
969 if (Flag)
970 {
971 /* FIXME: Save the last known good boot */
972 //Status = CmpSaveBootControlSet(Flag);
973
974 /* Notify HAL */
975 HalEndOfBoot();
976
977 /* Enable lazy flush */
978 CmpHoldLazyFlush = FALSE;
979 CmpLazyFlush();
980 return Status;
981 }
982
983 /* Otherwise, invalid boot */
984 return STATUS_INVALID_PARAMETER;
985 }
986
987 /* Check if this was a setup boot */
988 SetupBoot = (Flag == CM_BOOT_FLAG_SETUP ? TRUE : FALSE);
989
990 /* Make sure we're only called once */
991 if (!CmFirstTime) return STATUS_ACCESS_DENIED;
992 CmFirstTime = FALSE;
993
994 /* Acquire registry lock */
995 //CmpLockRegistryExclusive();
996
997 /* Initialize the hives and lazy flusher */
998 CmpCmdInit(SetupBoot);
999
1000 /* Save version data */
1001 CmpSetVersionData();
1002
1003 /* Release the registry lock */
1004 //CmpUnlockRegistry();
1005 return STATUS_SUCCESS;
1006 }
1007
1008 NTSTATUS
1009 NTAPI
1010 NtCompactKeys(IN ULONG Count,
1011 IN PHANDLE KeyArray)
1012 {
1013 UNIMPLEMENTED;
1014 return STATUS_NOT_IMPLEMENTED;
1015 }
1016
1017 NTSTATUS
1018 NTAPI
1019 NtCompressKey(IN HANDLE Key)
1020 {
1021 UNIMPLEMENTED;
1022 return STATUS_NOT_IMPLEMENTED;
1023 }
1024
1025 // FIXME: different for different windows versions!
1026 #define PRODUCT_ACTIVATION_VERSION 7749
1027
1028 NTSTATUS
1029 NTAPI
1030 NtLockProductActivationKeys(IN PULONG pPrivateVer,
1031 IN PULONG pSafeMode)
1032 {
1033 KPROCESSOR_MODE PreviousMode;
1034
1035 PreviousMode = ExGetPreviousMode();
1036 _SEH2_TRY
1037 {
1038 /* Check if the caller asked for the version */
1039 if (pPrivateVer != NULL)
1040 {
1041 /* For user mode, probe it */
1042 if (PreviousMode != KernelMode)
1043 {
1044 ProbeForRead(pPrivateVer, sizeof(ULONG), sizeof(ULONG));
1045 }
1046
1047 /* Return the expected version */
1048 *pPrivateVer = PRODUCT_ACTIVATION_VERSION;
1049 }
1050
1051 /* Check if the caller asked for safe mode mode state */
1052 if (pSafeMode != NULL)
1053 {
1054 /* For user mode, probe it */
1055 if (PreviousMode != KernelMode)
1056 {
1057 ProbeForRead(pSafeMode, sizeof(ULONG), sizeof(ULONG));
1058 }
1059
1060 /* Return the safe boot mode state */
1061 *pSafeMode = InitSafeBootMode;
1062 }
1063 }
1064 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1065 {
1066 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1067 }
1068 _SEH2_END;
1069
1070 return STATUS_SUCCESS;
1071 }
1072
1073 NTSTATUS
1074 NTAPI
1075 NtLockRegistryKey(IN HANDLE KeyHandle)
1076 {
1077 UNIMPLEMENTED;
1078 return STATUS_NOT_IMPLEMENTED;
1079 }
1080
1081 NTSTATUS
1082 NTAPI
1083 NtNotifyChangeMultipleKeys(IN HANDLE MasterKeyHandle,
1084 IN ULONG Count,
1085 IN POBJECT_ATTRIBUTES SlaveObjects,
1086 IN HANDLE Event,
1087 IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
1088 IN PVOID ApcContext OPTIONAL,
1089 OUT PIO_STATUS_BLOCK IoStatusBlock,
1090 IN ULONG CompletionFilter,
1091 IN BOOLEAN WatchTree,
1092 OUT PVOID Buffer,
1093 IN ULONG Length,
1094 IN BOOLEAN Asynchronous)
1095 {
1096 UNIMPLEMENTED_ONCE;
1097 return STATUS_NOT_IMPLEMENTED;
1098 }
1099
1100 NTSTATUS
1101 NTAPI
1102 NtQueryMultipleValueKey(IN HANDLE KeyHandle,
1103 IN OUT PKEY_VALUE_ENTRY ValueList,
1104 IN ULONG NumberOfValues,
1105 OUT PVOID Buffer,
1106 IN OUT PULONG Length,
1107 OUT PULONG ReturnLength)
1108 {
1109 UNIMPLEMENTED;
1110 return STATUS_NOT_IMPLEMENTED;
1111 }
1112
1113 NTSTATUS
1114 NTAPI
1115 NtQueryOpenSubKeys(IN POBJECT_ATTRIBUTES TargetKey,
1116 OUT PULONG HandleCount)
1117 {
1118 KPROCESSOR_MODE PreviousMode;
1119 PCM_KEY_BODY KeyBody = NULL;
1120 HANDLE KeyHandle;
1121 NTSTATUS Status;
1122
1123 DPRINT("NtQueryOpenSubKeys()\n");
1124
1125 PAGED_CODE();
1126
1127 /* Get the processor mode */
1128 PreviousMode = KeGetPreviousMode();
1129
1130 /* Check for user-mode caller */
1131 if (PreviousMode != KernelMode)
1132 {
1133 /* Prepare to probe parameters */
1134 _SEH2_TRY
1135 {
1136 /* Probe target key */
1137 ProbeForRead(TargetKey,
1138 sizeof(OBJECT_ATTRIBUTES),
1139 sizeof(ULONG));
1140
1141 /* Probe handle count */
1142 ProbeForWriteUlong(HandleCount);
1143 }
1144 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1145 {
1146 /* Return the exception code */
1147 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1148 }
1149 _SEH2_END;
1150 }
1151
1152 /* Open a handle to the key */
1153 Status = ObOpenObjectByName(TargetKey,
1154 CmpKeyObjectType,
1155 PreviousMode,
1156 NULL,
1157 KEY_READ,
1158 NULL,
1159 &KeyHandle);
1160 if (NT_SUCCESS(Status))
1161 {
1162 /* Reference the key object */
1163 Status = ObReferenceObjectByHandle(KeyHandle,
1164 KEY_READ,
1165 CmpKeyObjectType,
1166 PreviousMode,
1167 (PVOID*)&KeyBody,
1168 NULL);
1169
1170 /* Close the handle */
1171 NtClose(KeyHandle);
1172 }
1173
1174 /* Fail, if the key object could not be referenced */
1175 if (!NT_SUCCESS(Status))
1176 return Status;
1177
1178 /* Lock the registry exclusively */
1179 CmpLockRegistryExclusive();
1180
1181 /* Fail, if we did not open a hive root key */
1182 if (KeyBody->KeyControlBlock->KeyCell !=
1183 KeyBody->KeyControlBlock->KeyHive->BaseBlock->RootCell)
1184 {
1185 DPRINT("Error: Key is not a hive root key!\n");
1186 CmpUnlockRegistry();
1187 ObDereferenceObject(KeyBody);
1188 return STATUS_INVALID_PARAMETER;
1189 }
1190
1191 /* Call the internal API */
1192 *HandleCount = CmCountOpenSubKeys(KeyBody->KeyControlBlock,
1193 FALSE);
1194
1195 /* Unlock the registry */
1196 CmpUnlockRegistry();
1197
1198 /* Dereference the key object */
1199 ObDereferenceObject(KeyBody);
1200
1201 DPRINT("Done.\n");
1202
1203 return Status;
1204 }
1205
1206 NTSTATUS
1207 NTAPI
1208 NtQueryOpenSubKeysEx(IN POBJECT_ATTRIBUTES TargetKey,
1209 IN ULONG BufferLength,
1210 IN PVOID Buffer,
1211 IN PULONG RequiredSize)
1212 {
1213 UNIMPLEMENTED;
1214 return STATUS_NOT_IMPLEMENTED;
1215 }
1216
1217 NTSTATUS
1218 NTAPI
1219 NtRenameKey(IN HANDLE KeyHandle,
1220 IN PUNICODE_STRING ReplacementName)
1221 {
1222 UNIMPLEMENTED;
1223 return STATUS_NOT_IMPLEMENTED;
1224 }
1225
1226 NTSTATUS
1227 NTAPI
1228 NtReplaceKey(IN POBJECT_ATTRIBUTES ObjectAttributes,
1229 IN HANDLE Key,
1230 IN POBJECT_ATTRIBUTES ReplacedObjectAttributes)
1231 {
1232 UNIMPLEMENTED;
1233 return STATUS_NOT_IMPLEMENTED;
1234 }
1235
1236 NTSTATUS
1237 NTAPI
1238 NtRestoreKey(IN HANDLE KeyHandle,
1239 IN HANDLE FileHandle,
1240 IN ULONG RestoreFlags)
1241 {
1242 UNIMPLEMENTED;
1243 return STATUS_NOT_IMPLEMENTED;
1244 }
1245
1246 NTSTATUS
1247 NTAPI
1248 NtSaveKey(IN HANDLE KeyHandle,
1249 IN HANDLE FileHandle)
1250 {
1251 /* Call the extended API */
1252 return NtSaveKeyEx(KeyHandle, FileHandle, REG_STANDARD_FORMAT);
1253 }
1254
1255 NTSTATUS
1256 NTAPI
1257 NtSaveKeyEx(IN HANDLE KeyHandle,
1258 IN HANDLE FileHandle,
1259 IN ULONG Flags)
1260 {
1261 NTSTATUS Status;
1262 PCM_KEY_BODY KeyObject;
1263 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1264
1265 PAGED_CODE();
1266
1267 DPRINT("NtSaveKeyEx(0x%p, 0x%p, %lu)\n", KeyHandle, FileHandle, Flags);
1268
1269 /* Verify the flags */
1270 if ((Flags != REG_STANDARD_FORMAT)
1271 && (Flags != REG_LATEST_FORMAT)
1272 && (Flags != REG_NO_COMPRESSION))
1273 {
1274 /* Only one of these values can be specified */
1275 return STATUS_INVALID_PARAMETER;
1276 }
1277
1278 /* Validate privilege */
1279 if (!SeSinglePrivilegeCheck(SeBackupPrivilege, PreviousMode))
1280 {
1281 return STATUS_PRIVILEGE_NOT_HELD;
1282 }
1283
1284 /* Verify that the handle is valid and is a registry key */
1285 Status = ObReferenceObjectByHandle(KeyHandle,
1286 KEY_READ,
1287 CmpKeyObjectType,
1288 PreviousMode,
1289 (PVOID*)&KeyObject,
1290 NULL);
1291 if (!NT_SUCCESS(Status)) return Status;
1292
1293 /* Call the internal API */
1294 Status = CmSaveKey(KeyObject->KeyControlBlock, FileHandle, Flags);
1295
1296 ObDereferenceObject(KeyObject);
1297 return Status;
1298 }
1299
1300 NTSTATUS
1301 NTAPI
1302 NtSaveMergedKeys(IN HANDLE HighPrecedenceKeyHandle,
1303 IN HANDLE LowPrecedenceKeyHandle,
1304 IN HANDLE FileHandle)
1305 {
1306 KPROCESSOR_MODE PreviousMode;
1307 PCM_KEY_BODY HighPrecedenceKeyObject = NULL;
1308 PCM_KEY_BODY LowPrecedenceKeyObject = NULL;
1309 NTSTATUS Status;
1310
1311 PAGED_CODE();
1312
1313 DPRINT("NtSaveMergedKeys(0x%p, 0x%p, 0x%p)\n",
1314 HighPrecedenceKeyHandle, LowPrecedenceKeyHandle, FileHandle);
1315
1316 PreviousMode = ExGetPreviousMode();
1317
1318 /* Validate privilege */
1319 if (!SeSinglePrivilegeCheck(SeBackupPrivilege, PreviousMode))
1320 {
1321 return STATUS_PRIVILEGE_NOT_HELD;
1322 }
1323
1324 /* Verify that the handles are valid and are registry keys */
1325 Status = ObReferenceObjectByHandle(HighPrecedenceKeyHandle,
1326 KEY_READ,
1327 CmpKeyObjectType,
1328 PreviousMode,
1329 (PVOID*)&HighPrecedenceKeyObject,
1330 NULL);
1331 if (!NT_SUCCESS(Status))
1332 goto done;
1333
1334 Status = ObReferenceObjectByHandle(LowPrecedenceKeyHandle,
1335 KEY_READ,
1336 CmpKeyObjectType,
1337 PreviousMode,
1338 (PVOID*)&LowPrecedenceKeyObject,
1339 NULL);
1340 if (!NT_SUCCESS(Status))
1341 goto done;
1342
1343 /* Call the internal API */
1344 Status = CmSaveMergedKeys(HighPrecedenceKeyObject->KeyControlBlock,
1345 LowPrecedenceKeyObject->KeyControlBlock,
1346 FileHandle);
1347
1348 done:
1349 if (LowPrecedenceKeyObject)
1350 ObDereferenceObject(LowPrecedenceKeyObject);
1351
1352 if (HighPrecedenceKeyObject)
1353 ObDereferenceObject(HighPrecedenceKeyObject);
1354
1355 return Status;
1356 }
1357
1358 NTSTATUS
1359 NTAPI
1360 NtSetInformationKey(IN HANDLE KeyHandle,
1361 IN KEY_SET_INFORMATION_CLASS KeyInformationClass,
1362 IN PVOID KeyInformation,
1363 IN ULONG KeyInformationLength)
1364 {
1365 UNIMPLEMENTED;
1366 return STATUS_NOT_IMPLEMENTED;
1367 }
1368
1369 NTSTATUS
1370 NTAPI
1371 NtUnloadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes)
1372 {
1373 return NtUnloadKey2(KeyObjectAttributes, 0);
1374 }
1375
1376 NTSTATUS
1377 NTAPI
1378 NtUnloadKey2(IN POBJECT_ATTRIBUTES TargetKey,
1379 IN ULONG Flags)
1380 {
1381 NTSTATUS Status;
1382 OBJECT_ATTRIBUTES ObjectAttributes;
1383 UNICODE_STRING ObjectName;
1384 CM_PARSE_CONTEXT ParseContext = {0};
1385 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1386 PCM_KEY_BODY KeyBody = NULL;
1387 ULONG ParentConv = 0, ChildConv = 0;
1388 HANDLE Handle;
1389
1390 PAGED_CODE();
1391
1392 /* Validate privilege */
1393 if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode))
1394 {
1395 DPRINT1("Restore Privilege missing!\n");
1396 return STATUS_PRIVILEGE_NOT_HELD;
1397 }
1398
1399 /* Check for user-mode caller */
1400 if (PreviousMode != KernelMode)
1401 {
1402 /* Prepare to probe parameters */
1403 _SEH2_TRY
1404 {
1405 /* Probe object attributes */
1406 ProbeForRead(TargetKey,
1407 sizeof(OBJECT_ATTRIBUTES),
1408 sizeof(ULONG));
1409
1410 ObjectAttributes = *TargetKey;
1411
1412 /* Probe the string */
1413 ProbeForReadUnicodeString(&TargetKey->ObjectName);
1414
1415 ObjectName = *TargetKey->ObjectName;
1416
1417 ProbeForRead(ObjectName.Buffer,
1418 ObjectName.Length,
1419 sizeof(WCHAR));
1420
1421 ObjectAttributes.ObjectName = &ObjectName;
1422 }
1423 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1424 {
1425 /* Return the exception code */
1426 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1427 }
1428 _SEH2_END;
1429 }
1430 else
1431 {
1432 /* Save the target attributes directly */
1433 ObjectAttributes = *TargetKey;
1434 }
1435
1436 /* Setup the parse context */
1437 ParseContext.CreateOperation = TRUE;
1438 ParseContext.CreateOptions = REG_OPTION_BACKUP_RESTORE;
1439
1440 /* Do the create */
1441 Status = ObOpenObjectByName(&ObjectAttributes,
1442 CmpKeyObjectType,
1443 KernelMode,
1444 NULL,
1445 KEY_WRITE,
1446 &ParseContext,
1447 &Handle);
1448
1449 /* Return if failure encountered */
1450 if (!NT_SUCCESS(Status)) return Status;
1451
1452 /* Reference it */
1453 Status = ObReferenceObjectByHandle(Handle,
1454 KEY_WRITE,
1455 CmpKeyObjectType,
1456 KernelMode,
1457 (PVOID *)&KeyBody,
1458 NULL);
1459
1460 /* Close the handle */
1461 ZwClose(Handle);
1462
1463 /* Return if failure encountered */
1464 if (!NT_SUCCESS(Status)) return Status;
1465
1466 /* Acquire the lock depending on flags */
1467 if (Flags == REG_FORCE_UNLOAD)
1468 {
1469 /* Lock registry exclusively */
1470 CmpLockRegistryExclusive();
1471 }
1472 else
1473 {
1474 /* Lock registry */
1475 CmpLockRegistry();
1476
1477 /* Acquire the hive loading lock */
1478 ExAcquirePushLockExclusive(&CmpLoadHiveLock);
1479
1480 /* Lock parent and child */
1481 if (KeyBody->KeyControlBlock->ParentKcb)
1482 ParentConv = KeyBody->KeyControlBlock->ParentKcb->ConvKey;
1483 else
1484 ParentConv = KeyBody->KeyControlBlock->ConvKey;
1485
1486 ChildConv = KeyBody->KeyControlBlock->ConvKey;
1487
1488 CmpAcquireTwoKcbLocksExclusiveByKey(ChildConv, ParentConv);
1489 }
1490
1491 /* Check if it's being deleted already */
1492 if (KeyBody->KeyControlBlock->Delete)
1493 {
1494 /* Return appropriate status */
1495 Status = STATUS_KEY_DELETED;
1496 goto Quickie;
1497 }
1498
1499 /* Check if it's a read-only key */
1500 if (KeyBody->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY)
1501 {
1502 /* Return appropriate status */
1503 Status = STATUS_ACCESS_DENIED;
1504 goto Quickie;
1505 }
1506
1507 /* Call the internal API */
1508 Status = CmUnloadKey(KeyBody->KeyControlBlock,
1509 Flags);
1510
1511 /* Check if we failed, but really need to succeed */
1512 if ((Status == STATUS_CANNOT_DELETE) && (Flags == REG_FORCE_UNLOAD))
1513 {
1514 /* TODO: We should perform another attempt here */
1515 ASSERT(FALSE);
1516 }
1517
1518 /* If CmUnloadKey failed we need to unlock registry ourselves */
1519 if (!NT_SUCCESS(Status))
1520 {
1521 if (Flags != REG_FORCE_UNLOAD)
1522 {
1523 /* Release the KCB locks */
1524 CmpReleaseTwoKcbLockByKey(ChildConv, ParentConv);
1525
1526 /* Release the hive loading lock */
1527 ExReleasePushLockExclusive(&CmpLoadHiveLock);
1528 }
1529
1530 /* Unlock the registry */
1531 CmpUnlockRegistry();
1532 }
1533
1534 Quickie:
1535 /* Dereference the key */
1536 ObDereferenceObject(KeyBody);
1537
1538 /* Return status */
1539 return Status;
1540 }
1541
1542 NTSTATUS
1543 NTAPI
1544 NtUnloadKeyEx(IN POBJECT_ATTRIBUTES TargetKey,
1545 IN HANDLE Event)
1546 {
1547 UNIMPLEMENTED;
1548 return STATUS_NOT_IMPLEMENTED;
1549 }
1550
1551 /* EOF */