xref: /reactos/ntoskrnl/config/ntapi.c (revision ebfe9853)
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 
21 /* PRIVATE FUNCTIONS *********************************************************/
22 
23 /*
24  * Adapted from ntoskrnl/include/internal/ob_x.h:ObpReleaseObjectCreateInformation()
25  */
26 VOID
27 ReleaseCapturedObjectAttributes(
28     _In_ POBJECT_ATTRIBUTES CapturedObjectAttributes,
29     _In_ KPROCESSOR_MODE AccessMode)
30 {
31     /* Check if we have a security descriptor */
32     if (CapturedObjectAttributes->SecurityDescriptor)
33     {
34         /* Release it */
35         SeReleaseSecurityDescriptor(CapturedObjectAttributes->SecurityDescriptor,
36                                     AccessMode,
37                                     TRUE);
38         CapturedObjectAttributes->SecurityDescriptor = NULL;
39     }
40 
41     /* Check if we have an object name */
42     if (CapturedObjectAttributes->ObjectName)
43     {
44         /* Release it */
45         ReleaseCapturedUnicodeString(CapturedObjectAttributes->ObjectName, AccessMode);
46     }
47 }
48 
49 /*
50  * Adapted from ntoskrnl/ob/oblife.c:ObpCaptureObjectCreateInformation()
51  */
52 NTSTATUS
53 ProbeAndCaptureObjectAttributes(
54     _Out_ POBJECT_ATTRIBUTES CapturedObjectAttributes,
55     _Out_ PUNICODE_STRING ObjectName,
56     _In_ KPROCESSOR_MODE AccessMode,
57     _In_ POBJECT_ATTRIBUTES ObjectAttributes,
58     _In_ BOOLEAN CaptureSecurity)
59 {
60     NTSTATUS Status = STATUS_SUCCESS;
61     PSECURITY_DESCRIPTOR SecurityDescriptor;
62     // PSECURITY_QUALITY_OF_SERVICE SecurityQos;
63     PUNICODE_STRING LocalObjectName = NULL;
64 
65     /* Zero out the Capture Data */
66     RtlZeroMemory(CapturedObjectAttributes, sizeof(*CapturedObjectAttributes));
67 
68     /* SEH everything here for protection */
69     _SEH2_TRY
70     {
71         /* Check if we got attributes */
72         if (ObjectAttributes)
73         {
74             /* Check if we're in user mode */
75             if (AccessMode != KernelMode)
76             {
77                 /* Probe the attributes */
78                 ProbeForRead(ObjectAttributes,
79                              sizeof(OBJECT_ATTRIBUTES),
80                              sizeof(ULONG));
81             }
82 
83             /* Validate the Size and Attributes */
84             if ((ObjectAttributes->Length != sizeof(OBJECT_ATTRIBUTES)) ||
85                 (ObjectAttributes->Attributes & ~OBJ_VALID_KERNEL_ATTRIBUTES))  // Understood as all the possible valid attributes
86             {
87                 /* Invalid combination, fail */
88                 _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
89             }
90 
91             /* Set some Create Info and do not allow user-mode kernel handles */
92             CapturedObjectAttributes->Length = sizeof(OBJECT_ATTRIBUTES);
93             CapturedObjectAttributes->RootDirectory = ObjectAttributes->RootDirectory;
94             CapturedObjectAttributes->Attributes = ObpValidateAttributes(ObjectAttributes->Attributes, AccessMode);
95             LocalObjectName = ObjectAttributes->ObjectName;
96             SecurityDescriptor = ObjectAttributes->SecurityDescriptor;
97             // SecurityQos = ObjectAttributes->SecurityQualityOfService;
98 
99             /* Check if we have a security descriptor */
100             if (CaptureSecurity && SecurityDescriptor)
101             {
102                 /*
103                  * Capture it.
104                  * Note: This has an implicit memory barrier due
105                  * to the function call, so cleanup is safe here.
106                  */
107                 Status = SeCaptureSecurityDescriptor(SecurityDescriptor,
108                                                      AccessMode,
109                                                      NonPagedPool,
110                                                      TRUE,
111                                                      &CapturedObjectAttributes->
112                                                         SecurityDescriptor);
113                 if (!NT_SUCCESS(Status))
114                 {
115                     /* Capture failed, quit */
116                     CapturedObjectAttributes->SecurityDescriptor = NULL;
117                     _SEH2_YIELD(return Status);
118                 }
119             }
120             else
121             {
122                 CapturedObjectAttributes->SecurityDescriptor = NULL;
123             }
124 
125 #if 0
126 // We don't use the QoS!
127 
128             /* Check if we have QoS */
129             if (SecurityQos)
130             {
131                 /* Check if we came from user mode */
132                 if (AccessMode != KernelMode)
133                 {
134                     /* Validate the QoS */
135                     ProbeForRead(SecurityQos,
136                                  sizeof(SECURITY_QUALITY_OF_SERVICE),
137                                  sizeof(ULONG));
138                 }
139 
140                 /* Save Info */
141                 CapturedObjectAttributes->SecurityQualityOfService = *SecurityQos;
142                 CapturedObjectAttributes->SecurityQos =
143                     &CapturedObjectAttributes->SecurityQualityOfService;
144             }
145 #else
146             CapturedObjectAttributes->SecurityQualityOfService = NULL;
147 #endif
148         }
149         else
150         {
151             /* We don't have a name */
152             LocalObjectName = NULL;
153         }
154     }
155     _SEH2_EXCEPT(ExSystemExceptionFilter())
156     {
157         /* Cleanup and return the exception code */
158         ReleaseCapturedObjectAttributes(CapturedObjectAttributes, AccessMode);
159         _SEH2_YIELD(return _SEH2_GetExceptionCode());
160     }
161     _SEH2_END;
162 
163     /* Now check if the Object Attributes had an Object Name */
164     if (LocalObjectName)
165     {
166         Status = ProbeAndCaptureUnicodeString(ObjectName, AccessMode, LocalObjectName);
167     }
168     else
169     {
170         /* Clear the string */
171         RtlInitEmptyUnicodeString(ObjectName, NULL, 0);
172 
173         /* It cannot have specified a Root Directory */
174         if (CapturedObjectAttributes->RootDirectory)
175         {
176             Status = STATUS_OBJECT_NAME_INVALID;
177         }
178     }
179 
180     /* Set the caputured object attributes name pointer to the one the user gave to us */
181     CapturedObjectAttributes->ObjectName = ObjectName;
182 
183     /* Cleanup if we failed */
184     if (!NT_SUCCESS(Status))
185     {
186         ReleaseCapturedObjectAttributes(CapturedObjectAttributes, AccessMode);
187     }
188 
189     /* Return status to caller */
190     return Status;
191 }
192 
193 static
194 NTSTATUS
195 CmpConvertHandleToKernelHandle(
196     _In_ HANDLE SourceHandle,
197     _In_opt_ POBJECT_TYPE ObjectType,
198     _In_ ACCESS_MASK DesiredAccess,
199     _In_ KPROCESSOR_MODE AccessMode,
200     _Out_ PHANDLE KernelHandle)
201 {
202     NTSTATUS Status;
203     PVOID Object;
204 
205     *KernelHandle = NULL;
206 
207     /* NULL handle is valid */
208     if (SourceHandle == NULL)
209         return STATUS_SUCCESS;
210 
211     /* Get the object pointer */
212     Status = ObReferenceObjectByHandle(SourceHandle,
213                                        DesiredAccess,
214                                        ObjectType,
215                                        AccessMode,
216                                        &Object,
217                                        NULL);
218     if (!NT_SUCCESS(Status))
219         return Status;
220 
221     /* Create a kernel handle from the pointer */
222     Status = ObOpenObjectByPointer(Object,
223                                    OBJ_KERNEL_HANDLE,
224                                    NULL,
225                                    DesiredAccess,
226                                    ObjectType,
227                                    KernelMode,
228                                    KernelHandle);
229 
230     /* Dereference the object */
231     ObDereferenceObject(Object);
232     return Status;
233 }
234 
235 
236 /* FUNCTIONS *****************************************************************/
237 
238 NTSTATUS
239 NTAPI
240 NtCreateKey(OUT PHANDLE KeyHandle,
241             IN ACCESS_MASK DesiredAccess,
242             IN POBJECT_ATTRIBUTES ObjectAttributes,
243             IN ULONG TitleIndex,
244             IN PUNICODE_STRING Class OPTIONAL,
245             IN ULONG CreateOptions,
246             OUT PULONG Disposition OPTIONAL)
247 {
248     NTSTATUS Status;
249     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
250     CM_PARSE_CONTEXT ParseContext = {0};
251     HANDLE Handle;
252     PAGED_CODE();
253 
254     DPRINT("NtCreateKey(Path: %wZ, Root %x, Access: %x, CreateOptions %x)\n",
255             ObjectAttributes->ObjectName, ObjectAttributes->RootDirectory,
256             DesiredAccess, CreateOptions);
257 
258     /* Ignore the WOW64 flag, it's not valid in the kernel */
259     DesiredAccess &= ~KEY_WOW64_RES;
260 
261     /* Check for user-mode caller */
262     if (PreviousMode != KernelMode)
263     {
264         /* Prepare to probe parameters */
265         _SEH2_TRY
266         {
267             /* Check if we have a class */
268             if (Class)
269             {
270                 /* Probe it */
271                 ParseContext.Class = ProbeForReadUnicodeString(Class);
272                 ProbeForRead(ParseContext.Class.Buffer,
273                              ParseContext.Class.Length,
274                              sizeof(WCHAR));
275             }
276 
277             /* Probe the key handle */
278             ProbeForWriteHandle(KeyHandle);
279             *KeyHandle = NULL;
280 
281             /* Probe object attributes */
282             ProbeForRead(ObjectAttributes,
283                          sizeof(OBJECT_ATTRIBUTES),
284                          sizeof(ULONG));
285 
286             if (Disposition)
287                 ProbeForWriteUlong(Disposition);
288         }
289         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
290         {
291             /* Return the exception code */
292             _SEH2_YIELD(return _SEH2_GetExceptionCode());
293         }
294         _SEH2_END;
295     }
296     else
297     {
298         /* Save the class directly */
299         if (Class) ParseContext.Class = *Class;
300     }
301 
302     /* Setup the parse context */
303     ParseContext.CreateOperation = TRUE;
304     ParseContext.CreateOptions = CreateOptions;
305 
306     /* Do the create */
307     Status = ObOpenObjectByName(ObjectAttributes,
308                                 CmpKeyObjectType,
309                                 PreviousMode,
310                                 NULL,
311                                 DesiredAccess,
312                                 &ParseContext,
313                                 &Handle);
314 
315     _SEH2_TRY
316     {
317         /* Return data to user */
318         if (NT_SUCCESS(Status)) *KeyHandle = Handle;
319         if (Disposition) *Disposition = ParseContext.Disposition;
320     }
321     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
322     {
323         /* Get the status */
324         Status = _SEH2_GetExceptionCode();
325     }
326     _SEH2_END;
327 
328     DPRINT("Returning handle %x, Status %x.\n", Handle, Status);
329 
330     /* Return status */
331     return Status;
332 }
333 
334 NTSTATUS
335 NTAPI
336 NtOpenKey(OUT PHANDLE KeyHandle,
337           IN ACCESS_MASK DesiredAccess,
338           IN POBJECT_ATTRIBUTES ObjectAttributes)
339 {
340     CM_PARSE_CONTEXT ParseContext = {0};
341     HANDLE Handle;
342     NTSTATUS Status;
343     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
344     PAGED_CODE();
345     DPRINT("NtOpenKey(Path: %wZ, Root %x, Access: %x)\n",
346             ObjectAttributes->ObjectName, ObjectAttributes->RootDirectory, DesiredAccess);
347 
348     /* Ignore the WOW64 flag, it's not valid in the kernel */
349     DesiredAccess &= ~KEY_WOW64_RES;
350 
351     /* Check for user-mode caller */
352     if (PreviousMode != KernelMode)
353     {
354         /* Prepare to probe parameters */
355         _SEH2_TRY
356         {
357             /* Probe the key handle */
358             ProbeForWriteHandle(KeyHandle);
359             *KeyHandle = NULL;
360 
361             /* Probe object attributes */
362             ProbeForRead(ObjectAttributes,
363                          sizeof(OBJECT_ATTRIBUTES),
364                          sizeof(ULONG));
365         }
366         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
367         {
368             /* Return the exception code */
369             _SEH2_YIELD(return _SEH2_GetExceptionCode());
370         }
371         _SEH2_END;
372     }
373 
374     /* Just let the object manager handle this */
375     Status = ObOpenObjectByName(ObjectAttributes,
376                                 CmpKeyObjectType,
377                                 PreviousMode,
378                                 NULL,
379                                 DesiredAccess,
380                                 &ParseContext,
381                                 &Handle);
382 
383     /* Only do this if we succeeded */
384     if (NT_SUCCESS(Status))
385     {
386         _SEH2_TRY
387         {
388             /* Return the handle to caller */
389             *KeyHandle = Handle;
390         }
391         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
392         {
393             /* Get the status */
394             Status = _SEH2_GetExceptionCode();
395         }
396         _SEH2_END;
397     }
398 
399     DPRINT("Returning handle %x, Status %x.\n", Handle, Status);
400 
401     /* Return status */
402     return Status;
403 }
404 
405 
406 NTSTATUS
407 NTAPI
408 NtDeleteKey(IN HANDLE KeyHandle)
409 {
410     PCM_KEY_BODY KeyObject;
411     NTSTATUS Status;
412     REG_DELETE_KEY_INFORMATION DeleteKeyInfo;
413     REG_POST_OPERATION_INFORMATION PostOperationInfo;
414     PAGED_CODE();
415     DPRINT("NtDeleteKey(KH 0x%p)\n", KeyHandle);
416 
417     /* Verify that the handle is valid and is a registry key */
418     Status = ObReferenceObjectByHandle(KeyHandle,
419                                        DELETE,
420                                        CmpKeyObjectType,
421                                        ExGetPreviousMode(),
422                                        (PVOID*)&KeyObject,
423                                        NULL);
424     if (!NT_SUCCESS(Status)) return Status;
425 
426     /* Setup the callback */
427     PostOperationInfo.Object = (PVOID)KeyObject;
428     DeleteKeyInfo.Object = (PVOID)KeyObject;
429     Status = CmiCallRegisteredCallbacks(RegNtPreDeleteKey, &DeleteKeyInfo);
430     if (NT_SUCCESS(Status))
431     {
432         /* Check if we are read-only */
433         if ((KeyObject->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY) ||
434             (KeyObject->KeyControlBlock->ParentKcb->ExtFlags & CM_KCB_READ_ONLY_KEY))
435         {
436             /* Fail */
437             Status = STATUS_ACCESS_DENIED;
438         }
439         else
440         {
441             /* Call the internal API */
442             Status = CmDeleteKey(KeyObject);
443         }
444 
445         /* Do post callback */
446         PostOperationInfo.Status = Status;
447         CmiCallRegisteredCallbacks(RegNtPostDeleteKey, &PostOperationInfo);
448     }
449 
450     /* Dereference and return status */
451     ObDereferenceObject(KeyObject);
452     return Status;
453 }
454 
455 NTSTATUS
456 NTAPI
457 NtEnumerateKey(IN HANDLE KeyHandle,
458                IN ULONG Index,
459                IN KEY_INFORMATION_CLASS KeyInformationClass,
460                OUT PVOID KeyInformation,
461                IN ULONG Length,
462                OUT PULONG ResultLength)
463 {
464     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
465     NTSTATUS Status;
466     PCM_KEY_BODY KeyObject;
467     REG_ENUMERATE_KEY_INFORMATION EnumerateKeyInfo;
468     REG_POST_OPERATION_INFORMATION PostOperationInfo;
469     PAGED_CODE();
470     DPRINT("NtEnumerateKey() KH 0x%p, Index 0x%x, KIC %d, Length %lu\n",
471            KeyHandle, Index, KeyInformationClass, Length);
472 
473     /* Reject classes we don't know about */
474     if ((KeyInformationClass != KeyBasicInformation) &&
475         (KeyInformationClass != KeyNodeInformation)  &&
476         (KeyInformationClass != KeyFullInformation))
477     {
478         /* Fail */
479         return STATUS_INVALID_PARAMETER;
480     }
481 
482     /* Verify that the handle is valid and is a registry key */
483     Status = ObReferenceObjectByHandle(KeyHandle,
484                                        KEY_ENUMERATE_SUB_KEYS,
485                                        CmpKeyObjectType,
486                                        PreviousMode,
487                                        (PVOID*)&KeyObject,
488                                        NULL);
489     if (!NT_SUCCESS(Status)) return Status;
490 
491     if (PreviousMode != KernelMode)
492     {
493         _SEH2_TRY
494         {
495             ProbeForWriteUlong(ResultLength);
496             ProbeForWrite(KeyInformation,
497                           Length,
498                           sizeof(ULONG));
499         }
500         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
501         {
502             /* Dereference and return status */
503             ObDereferenceObject(KeyObject);
504             _SEH2_YIELD(return _SEH2_GetExceptionCode());
505         }
506         _SEH2_END;
507     }
508 
509     /* Setup the callback */
510     PostOperationInfo.Object = (PVOID)KeyObject;
511     EnumerateKeyInfo.Object = (PVOID)KeyObject;
512     EnumerateKeyInfo.Index = Index;
513     EnumerateKeyInfo.KeyInformationClass = KeyInformationClass;
514     EnumerateKeyInfo.Length = Length;
515     EnumerateKeyInfo.ResultLength = ResultLength;
516 
517     /* Do the callback */
518     Status = CmiCallRegisteredCallbacks(RegNtPreEnumerateKey, &EnumerateKeyInfo);
519     if (NT_SUCCESS(Status))
520     {
521         /* Call the internal API */
522         Status = CmEnumerateKey(KeyObject->KeyControlBlock,
523                                 Index,
524                                 KeyInformationClass,
525                                 KeyInformation,
526                                 Length,
527                                 ResultLength);
528 
529         /* Do the post callback */
530         PostOperationInfo.Status = Status;
531         CmiCallRegisteredCallbacks(RegNtPostEnumerateKey, &PostOperationInfo);
532     }
533 
534     /* Dereference and return status */
535     ObDereferenceObject(KeyObject);
536     DPRINT("Returning status %x.\n", Status);
537     return Status;
538 }
539 
540 NTSTATUS
541 NTAPI
542 NtEnumerateValueKey(IN HANDLE KeyHandle,
543                     IN ULONG Index,
544                     IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
545                     OUT PVOID KeyValueInformation,
546                     IN ULONG Length,
547                     OUT PULONG ResultLength)
548 {
549     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
550     NTSTATUS Status;
551     PCM_KEY_BODY KeyObject;
552     REG_ENUMERATE_VALUE_KEY_INFORMATION EnumerateValueKeyInfo;
553     REG_POST_OPERATION_INFORMATION PostOperationInfo;
554 
555     PAGED_CODE();
556 
557     DPRINT("NtEnumerateValueKey() KH 0x%p, Index 0x%x, KVIC %d, Length %lu\n",
558            KeyHandle, Index, KeyValueInformationClass, Length);
559 
560     /* Reject classes we don't know about */
561     if ((KeyValueInformationClass != KeyValueBasicInformation)       &&
562         (KeyValueInformationClass != KeyValueFullInformation)        &&
563         (KeyValueInformationClass != KeyValuePartialInformation)     &&
564         (KeyValueInformationClass != KeyValueFullInformationAlign64) &&
565         (KeyValueInformationClass != KeyValuePartialInformationAlign64))
566     {
567         /* Fail */
568         return STATUS_INVALID_PARAMETER;
569     }
570 
571     /* Verify that the handle is valid and is a registry key */
572     Status = ObReferenceObjectByHandle(KeyHandle,
573                                        KEY_QUERY_VALUE,
574                                        CmpKeyObjectType,
575                                        PreviousMode,
576                                        (PVOID*)&KeyObject,
577                                        NULL);
578     if (!NT_SUCCESS(Status)) return Status;
579 
580     if (PreviousMode != KernelMode)
581     {
582         _SEH2_TRY
583         {
584             ProbeForWriteUlong(ResultLength);
585             ProbeForWrite(KeyValueInformation,
586                           Length,
587                           sizeof(ULONG));
588         }
589         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
590         {
591             /* Dereference and return status */
592             ObDereferenceObject(KeyObject);
593             _SEH2_YIELD(return _SEH2_GetExceptionCode());
594         }
595         _SEH2_END;
596     }
597 
598     /* Setup the callback */
599     PostOperationInfo.Object = (PVOID)KeyObject;
600     EnumerateValueKeyInfo.Object = (PVOID)KeyObject;
601     EnumerateValueKeyInfo.Index = Index;
602     EnumerateValueKeyInfo.KeyValueInformationClass = KeyValueInformationClass;
603     EnumerateValueKeyInfo.KeyValueInformation = KeyValueInformation;
604     EnumerateValueKeyInfo.Length = Length;
605     EnumerateValueKeyInfo.ResultLength = ResultLength;
606 
607     /* Do the callback */
608     Status = CmiCallRegisteredCallbacks(RegNtPreEnumerateValueKey,
609                                         &EnumerateValueKeyInfo);
610     if (NT_SUCCESS(Status))
611     {
612         /* Call the internal API */
613         Status = CmEnumerateValueKey(KeyObject->KeyControlBlock,
614                                      Index,
615                                      KeyValueInformationClass,
616                                      KeyValueInformation,
617                                      Length,
618                                      ResultLength);
619 
620         /* Do the post callback */
621         PostOperationInfo.Status = Status;
622         CmiCallRegisteredCallbacks(RegNtPostEnumerateValueKey, &PostOperationInfo);
623     }
624 
625     /* Dereference and return status */
626     ObDereferenceObject(KeyObject);
627     return Status;
628 }
629 
630 NTSTATUS
631 NTAPI
632 NtQueryKey(IN HANDLE KeyHandle,
633            IN KEY_INFORMATION_CLASS KeyInformationClass,
634            OUT PVOID KeyInformation,
635            IN ULONG Length,
636            OUT PULONG ResultLength)
637 {
638     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
639     NTSTATUS Status;
640     PCM_KEY_BODY KeyObject;
641     REG_QUERY_KEY_INFORMATION QueryKeyInfo;
642     REG_POST_OPERATION_INFORMATION PostOperationInfo;
643     OBJECT_HANDLE_INFORMATION HandleInfo;
644     PAGED_CODE();
645     DPRINT("NtQueryKey() KH 0x%p, KIC %d, Length %lu\n",
646            KeyHandle, KeyInformationClass, Length);
647 
648     /* Reject invalid classes */
649     if ((KeyInformationClass != KeyBasicInformation) &&
650         (KeyInformationClass != KeyNodeInformation)  &&
651         (KeyInformationClass != KeyFullInformation)  &&
652         (KeyInformationClass != KeyNameInformation) &&
653         (KeyInformationClass != KeyCachedInformation) &&
654         (KeyInformationClass != KeyFlagsInformation))
655     {
656         /* Fail */
657         return STATUS_INVALID_PARAMETER;
658     }
659 
660     /* Check if just the name is required */
661     if (KeyInformationClass == KeyNameInformation)
662     {
663         /* Ignore access level */
664         Status = ObReferenceObjectByHandle(KeyHandle,
665                                            0,
666                                            CmpKeyObjectType,
667                                            PreviousMode,
668                                            (PVOID*)&KeyObject,
669                                            &HandleInfo);
670         if (NT_SUCCESS(Status))
671         {
672             /* At least a single bit of access is required */
673             if (!HandleInfo.GrantedAccess)
674             {
675                 /* No such luck */
676                 ObDereferenceObject(KeyObject);
677                 Status = STATUS_ACCESS_DENIED;
678             }
679         }
680     }
681     else
682     {
683         /* Get a reference */
684         Status = ObReferenceObjectByHandle(KeyHandle,
685                                            KEY_QUERY_VALUE,
686                                            CmpKeyObjectType,
687                                            PreviousMode,
688                                            (PVOID*)&KeyObject,
689                                            NULL);
690     }
691 
692     /* Quit on failure */
693     if (!NT_SUCCESS(Status)) return Status;
694 
695     if (PreviousMode != KernelMode)
696     {
697         _SEH2_TRY
698         {
699             ProbeForWriteUlong(ResultLength);
700             ProbeForWrite(KeyInformation,
701                           Length,
702                           sizeof(ULONG));
703         }
704         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
705         {
706             /* Dereference and return status */
707             ObDereferenceObject(KeyObject);
708             _SEH2_YIELD(return _SEH2_GetExceptionCode());
709         }
710         _SEH2_END;
711     }
712 
713     /* Setup the callback */
714     PostOperationInfo.Object = (PVOID)KeyObject;
715     QueryKeyInfo.Object = (PVOID)KeyObject;
716     QueryKeyInfo.KeyInformationClass = KeyInformationClass;
717     QueryKeyInfo.KeyInformation = KeyInformation;
718     QueryKeyInfo.Length = Length;
719     QueryKeyInfo.ResultLength = ResultLength;
720 
721     /* Do the callback */
722     Status = CmiCallRegisteredCallbacks(RegNtPreQueryKey, &QueryKeyInfo);
723     if (NT_SUCCESS(Status))
724     {
725         /* Call the internal API */
726         Status = CmQueryKey(KeyObject->KeyControlBlock,
727                             KeyInformationClass,
728                             KeyInformation,
729                             Length,
730                             ResultLength);
731 
732         /* Do the post callback */
733         PostOperationInfo.Status = Status;
734         CmiCallRegisteredCallbacks(RegNtPostQueryKey, &PostOperationInfo);
735     }
736 
737     /* Dereference and return status */
738     ObDereferenceObject(KeyObject);
739     return Status;
740 }
741 
742 NTSTATUS
743 NTAPI
744 NtQueryValueKey(IN HANDLE KeyHandle,
745                 IN PUNICODE_STRING ValueName,
746                 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
747                 OUT PVOID KeyValueInformation,
748                 IN ULONG Length,
749                 OUT PULONG ResultLength)
750 {
751     NTSTATUS Status;
752     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
753     PCM_KEY_BODY KeyObject;
754     REG_QUERY_VALUE_KEY_INFORMATION QueryValueKeyInfo;
755     REG_POST_OPERATION_INFORMATION PostOperationInfo;
756     UNICODE_STRING ValueNameCopy;
757 
758     PAGED_CODE();
759 
760     DPRINT("NtQueryValueKey() KH 0x%p, VN '%wZ', KVIC %d, Length %lu\n",
761         KeyHandle, ValueName, KeyValueInformationClass, Length);
762 
763     /* Reject classes we don't know about */
764     if ((KeyValueInformationClass != KeyValueBasicInformation)       &&
765         (KeyValueInformationClass != KeyValueFullInformation)        &&
766         (KeyValueInformationClass != KeyValuePartialInformation)     &&
767         (KeyValueInformationClass != KeyValueFullInformationAlign64) &&
768         (KeyValueInformationClass != KeyValuePartialInformationAlign64))
769     {
770         /* Fail */
771         return STATUS_INVALID_PARAMETER;
772     }
773 
774     /* Verify that the handle is valid and is a registry key */
775     Status = ObReferenceObjectByHandle(KeyHandle,
776                                        KEY_QUERY_VALUE,
777                                        CmpKeyObjectType,
778                                        PreviousMode,
779                                        (PVOID*)&KeyObject,
780                                        NULL);
781     if (!NT_SUCCESS(Status))
782         return Status;
783 
784     if (PreviousMode != KernelMode)
785     {
786         _SEH2_TRY
787         {
788             ProbeForWriteUlong(ResultLength);
789             ProbeForWrite(KeyValueInformation,
790                           Length,
791                           sizeof(ULONG));
792         }
793         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
794         {
795             /* Dereference and return status */
796             ObDereferenceObject(KeyObject);
797             _SEH2_YIELD(return _SEH2_GetExceptionCode());
798         }
799         _SEH2_END;
800     }
801 
802     /* Capture the string */
803     Status = ProbeAndCaptureUnicodeString(&ValueNameCopy, PreviousMode, ValueName);
804     if (!NT_SUCCESS(Status))
805         goto Quit;
806 
807     /* Make sure the name is aligned properly */
808     if ((ValueNameCopy.Length & (sizeof(WCHAR) - 1)))
809     {
810         /* It isn't, so we'll fail */
811         Status = STATUS_INVALID_PARAMETER;
812         goto Quit;
813     }
814 
815     /* Ignore any null characters at the end */
816     while ((ValueNameCopy.Length) &&
817            !(ValueNameCopy.Buffer[ValueNameCopy.Length / sizeof(WCHAR) - 1]))
818     {
819         /* Skip it */
820         ValueNameCopy.Length -= sizeof(WCHAR);
821     }
822 
823     /* Setup the callback */
824     PostOperationInfo.Object = (PVOID)KeyObject;
825     QueryValueKeyInfo.Object = (PVOID)KeyObject;
826     QueryValueKeyInfo.ValueName = &ValueNameCopy;
827     QueryValueKeyInfo.KeyValueInformationClass = KeyValueInformationClass;
828     QueryValueKeyInfo.Length = Length;
829     QueryValueKeyInfo.ResultLength = ResultLength;
830 
831     /* Do the callback */
832     Status = CmiCallRegisteredCallbacks(RegNtPreQueryValueKey, &QueryValueKeyInfo);
833     if (NT_SUCCESS(Status))
834     {
835         /* Call the internal API */
836         Status = CmQueryValueKey(KeyObject->KeyControlBlock,
837                                  ValueNameCopy,
838                                  KeyValueInformationClass,
839                                  KeyValueInformation,
840                                  Length,
841                                  ResultLength);
842 
843         /* Do the post callback */
844         PostOperationInfo.Status = Status;
845         CmiCallRegisteredCallbacks(RegNtPostQueryValueKey, &PostOperationInfo);
846     }
847 
848 Quit:
849     if (ValueNameCopy.Buffer)
850         ReleaseCapturedUnicodeString(&ValueNameCopy, PreviousMode);
851 
852     /* Dereference and return status */
853     ObDereferenceObject(KeyObject);
854     return Status;
855 }
856 
857 NTSTATUS
858 NTAPI
859 NtSetValueKey(IN HANDLE KeyHandle,
860               IN PUNICODE_STRING ValueName,
861               IN ULONG TitleIndex,
862               IN ULONG Type,
863               IN PVOID Data,
864               IN ULONG DataSize)
865 {
866     NTSTATUS Status = STATUS_SUCCESS;
867     KPROCESSOR_MODE PreviousMode;
868     PCM_KEY_BODY KeyObject;
869     REG_SET_VALUE_KEY_INFORMATION SetValueKeyInfo;
870     REG_POST_OPERATION_INFORMATION PostOperationInfo;
871     UNICODE_STRING ValueNameCopy;
872 
873     PAGED_CODE();
874 
875     PreviousMode = ExGetPreviousMode();
876 
877     /* Verify that the handle is valid and is a registry key */
878     Status = ObReferenceObjectByHandle(KeyHandle,
879                                        KEY_SET_VALUE,
880                                        CmpKeyObjectType,
881                                        PreviousMode,
882                                        (PVOID*)&KeyObject,
883                                        NULL);
884     if (!NT_SUCCESS(Status))
885         return Status;
886 
887     if (!DataSize)
888         Data = NULL;
889 
890     /* Probe and copy the data */
891     if ((PreviousMode != KernelMode) && (DataSize != 0))
892     {
893         PVOID DataCopy = ExAllocatePoolWithTag(PagedPool, DataSize, TAG_CM);
894         if (!DataCopy)
895         {
896             /* Dereference and return status */
897             ObDereferenceObject(KeyObject);
898             return STATUS_INSUFFICIENT_RESOURCES;
899         }
900         _SEH2_TRY
901         {
902             ProbeForRead(Data, DataSize, 1);
903             RtlCopyMemory(DataCopy, Data, DataSize);
904         }
905         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
906         {
907             Status = _SEH2_GetExceptionCode();
908         }
909         _SEH2_END;
910 
911         if (!NT_SUCCESS(Status))
912         {
913             /* Dereference and return status */
914             ExFreePoolWithTag(DataCopy, TAG_CM);
915             ObDereferenceObject(KeyObject);
916             return Status;
917         }
918         Data = DataCopy;
919     }
920 
921     /* Capture the string */
922     Status = ProbeAndCaptureUnicodeString(&ValueNameCopy, PreviousMode, ValueName);
923     if (!NT_SUCCESS(Status))
924         goto Quit;
925 
926     DPRINT("NtSetValueKey() KH 0x%p, VN '%wZ', TI %x, T %lu, DS %lu\n",
927         KeyHandle, &ValueNameCopy, TitleIndex, Type, DataSize);
928 
929     /* Make sure the name is aligned, not too long, and the data under 4GB */
930     if ( (ValueNameCopy.Length > 32767) ||
931          ((ValueNameCopy.Length & (sizeof(WCHAR) - 1))) ||
932          (DataSize > 0x80000000))
933     {
934         /* Fail */
935         Status = STATUS_INVALID_PARAMETER;
936         goto Quit;
937     }
938 
939     /* Ignore any null characters at the end */
940     while ((ValueNameCopy.Length) &&
941            !(ValueNameCopy.Buffer[ValueNameCopy.Length / sizeof(WCHAR) - 1]))
942     {
943         /* Skip it */
944         ValueNameCopy.Length -= sizeof(WCHAR);
945     }
946 
947     /* Don't touch read-only keys */
948     if (KeyObject->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY)
949     {
950         /* Fail */
951         Status = STATUS_ACCESS_DENIED;
952         goto Quit;
953     }
954 
955     /* Setup callback */
956     PostOperationInfo.Object = (PVOID)KeyObject;
957     SetValueKeyInfo.Object = (PVOID)KeyObject;
958     SetValueKeyInfo.ValueName = &ValueNameCopy;
959     SetValueKeyInfo.TitleIndex = TitleIndex;
960     SetValueKeyInfo.Type = Type;
961     SetValueKeyInfo.Data = Data;
962     SetValueKeyInfo.DataSize = DataSize;
963 
964     /* Do the callback */
965     Status = CmiCallRegisteredCallbacks(RegNtPreSetValueKey, &SetValueKeyInfo);
966     if (NT_SUCCESS(Status))
967     {
968         /* Call the internal API */
969         Status = CmSetValueKey(KeyObject->KeyControlBlock,
970                                &ValueNameCopy,
971                                Type,
972                                Data,
973                                DataSize);
974 
975         /* Do the post-callback */
976         PostOperationInfo.Status = Status;
977         CmiCallRegisteredCallbacks(RegNtPostSetValueKey, &PostOperationInfo);
978     }
979 
980 Quit:
981     if (ValueNameCopy.Buffer)
982         ReleaseCapturedUnicodeString(&ValueNameCopy, PreviousMode);
983 
984     if ((PreviousMode != KernelMode) && Data)
985         ExFreePoolWithTag(Data, TAG_CM);
986 
987     /* Dereference and return status */
988     ObDereferenceObject(KeyObject);
989     return Status;
990 }
991 
992 NTSTATUS
993 NTAPI
994 NtDeleteValueKey(IN HANDLE KeyHandle,
995                  IN PUNICODE_STRING ValueName)
996 {
997     NTSTATUS Status;
998     PCM_KEY_BODY KeyObject;
999     REG_DELETE_VALUE_KEY_INFORMATION DeleteValueKeyInfo;
1000     REG_POST_OPERATION_INFORMATION PostOperationInfo;
1001     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1002     UNICODE_STRING ValueNameCopy;
1003 
1004     PAGED_CODE();
1005 
1006     /* Verify that the handle is valid and is a registry key */
1007     Status = ObReferenceObjectByHandle(KeyHandle,
1008                                        KEY_SET_VALUE,
1009                                        CmpKeyObjectType,
1010                                        PreviousMode,
1011                                        (PVOID*)&KeyObject,
1012                                        NULL);
1013     if (!NT_SUCCESS(Status))
1014         return Status;
1015 
1016     /* Capture the string */
1017     Status = ProbeAndCaptureUnicodeString(&ValueNameCopy, PreviousMode, ValueName);
1018     if (!NT_SUCCESS(Status))
1019         goto Quit;
1020 
1021     /* Make sure the name is aligned properly */
1022     if ((ValueNameCopy.Length & (sizeof(WCHAR) - 1)))
1023     {
1024         /* It isn't, so we'll fail */
1025         Status = STATUS_INVALID_PARAMETER;
1026         goto Quit;
1027     }
1028 
1029     /* Don't touch read-only keys */
1030     if (KeyObject->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY)
1031     {
1032         /* Fail */
1033         Status = STATUS_ACCESS_DENIED;
1034         goto Quit;
1035     }
1036 
1037     /* Do the callback */
1038     DeleteValueKeyInfo.Object = (PVOID)KeyObject;
1039     DeleteValueKeyInfo.ValueName = ValueName;
1040     Status = CmiCallRegisteredCallbacks(RegNtPreDeleteValueKey,
1041                                         &DeleteValueKeyInfo);
1042     if (NT_SUCCESS(Status))
1043     {
1044         /* Call the internal API */
1045         Status = CmDeleteValueKey(KeyObject->KeyControlBlock, ValueNameCopy);
1046 
1047         /* Do the post callback */
1048         PostOperationInfo.Object = (PVOID)KeyObject;
1049         PostOperationInfo.Status = Status;
1050         CmiCallRegisteredCallbacks(RegNtPostDeleteValueKey,
1051                                    &PostOperationInfo);
1052     }
1053 
1054 Quit:
1055     if (ValueNameCopy.Buffer)
1056         ReleaseCapturedUnicodeString(&ValueNameCopy, PreviousMode);
1057 
1058     /* Dereference and return status */
1059     ObDereferenceObject(KeyObject);
1060     return Status;
1061 }
1062 
1063 NTSTATUS
1064 NTAPI
1065 NtFlushKey(IN HANDLE KeyHandle)
1066 {
1067     NTSTATUS Status;
1068     PCM_KEY_BODY KeyObject;
1069     PAGED_CODE();
1070 
1071     /* Get the key object */
1072     Status = ObReferenceObjectByHandle(KeyHandle,
1073                                        0,
1074                                        CmpKeyObjectType,
1075                                        ExGetPreviousMode(),
1076                                        (PVOID*)&KeyObject,
1077                                        NULL);
1078     if (!NT_SUCCESS(Status)) return Status;
1079 
1080     /* Lock the registry */
1081     CmpLockRegistry();
1082 
1083     /* Lock the KCB */
1084     CmpAcquireKcbLockShared(KeyObject->KeyControlBlock);
1085 
1086     /* Make sure KCB isn't deleted */
1087     if (KeyObject->KeyControlBlock->Delete)
1088     {
1089         /* Fail */
1090         Status = STATUS_KEY_DELETED;
1091     }
1092     else
1093     {
1094         /* Call the internal API */
1095         Status = CmFlushKey(KeyObject->KeyControlBlock, FALSE);
1096     }
1097 
1098     /* Release the locks */
1099     CmpReleaseKcbLock(KeyObject->KeyControlBlock);
1100     CmpUnlockRegistry();
1101 
1102     /* Dereference the object and return status */
1103     ObDereferenceObject(KeyObject);
1104     return Status;
1105 }
1106 
1107 NTSTATUS
1108 NTAPI
1109 NtLoadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
1110           IN POBJECT_ATTRIBUTES FileObjectAttributes)
1111 {
1112     /* Call the newer API */
1113     return NtLoadKeyEx(KeyObjectAttributes, FileObjectAttributes, 0, NULL);
1114 }
1115 
1116 NTSTATUS
1117 NTAPI
1118 NtLoadKey2(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
1119            IN POBJECT_ATTRIBUTES FileObjectAttributes,
1120            IN ULONG Flags)
1121 {
1122     /* Call the newer API */
1123     return NtLoadKeyEx(KeyObjectAttributes, FileObjectAttributes, Flags, NULL);
1124 }
1125 
1126 NTSTATUS
1127 NTAPI
1128 NtLoadKeyEx(IN POBJECT_ATTRIBUTES TargetKey,
1129             IN POBJECT_ATTRIBUTES SourceFile,
1130             IN ULONG Flags,
1131             IN HANDLE TrustClassKey)
1132 {
1133     NTSTATUS Status;
1134     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1135     OBJECT_ATTRIBUTES CapturedTargetKey;
1136     OBJECT_ATTRIBUTES CapturedSourceFile;
1137     UNICODE_STRING TargetKeyName, SourceFileName;
1138     HANDLE KmTargetKeyRootDir = NULL, KmSourceFileRootDir = NULL;
1139     PCM_KEY_BODY KeyBody = NULL;
1140 
1141     PAGED_CODE();
1142 
1143     /* Validate flags */
1144     if (Flags & ~REG_NO_LAZY_FLUSH)
1145         return STATUS_INVALID_PARAMETER;
1146 
1147     /* Validate privilege */
1148     if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode))
1149     {
1150         DPRINT1("Restore Privilege missing!\n");
1151         return STATUS_PRIVILEGE_NOT_HELD;
1152     }
1153 
1154     /* Block APCs */
1155     KeEnterCriticalRegion();
1156 
1157     /* Check for user-mode caller */
1158     if (PreviousMode != KernelMode)
1159     {
1160         /* Prepare to probe parameters */
1161         _SEH2_TRY
1162         {
1163             /* Probe target key */
1164             ProbeForRead(TargetKey,
1165                          sizeof(OBJECT_ATTRIBUTES),
1166                          sizeof(ULONG));
1167 
1168             /* Probe source file */
1169             ProbeForRead(SourceFile,
1170                          sizeof(OBJECT_ATTRIBUTES),
1171                          sizeof(ULONG));
1172         }
1173         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1174         {
1175             /* Return the exception code */
1176             Status = _SEH2_GetExceptionCode();
1177             _SEH2_YIELD(goto Quit);
1178         }
1179         _SEH2_END;
1180     }
1181 
1182     /* Probe and capture the target key attributes, including the security */
1183     Status = ProbeAndCaptureObjectAttributes(&CapturedTargetKey,
1184                                              &TargetKeyName,
1185                                              PreviousMode,
1186                                              TargetKey,
1187                                              TRUE);
1188     if (!NT_SUCCESS(Status))
1189         goto Quit;
1190 
1191     /*
1192      * Probe and capture the source file attributes, but not the security.
1193      * A proper security context is built by CmLoadKey().
1194      */
1195     Status = ProbeAndCaptureObjectAttributes(&CapturedSourceFile,
1196                                              &SourceFileName,
1197                                              PreviousMode,
1198                                              SourceFile,
1199                                              FALSE);
1200     if (!NT_SUCCESS(Status))
1201     {
1202         ReleaseCapturedObjectAttributes(&CapturedTargetKey, PreviousMode);
1203         goto Quit;
1204     }
1205 
1206     /* Make sure the target key root directory handle is a kernel handle */
1207     Status = CmpConvertHandleToKernelHandle(CapturedTargetKey.RootDirectory,
1208                                             CmpKeyObjectType,
1209                                             KEY_READ,
1210                                             PreviousMode,
1211                                             &KmTargetKeyRootDir);
1212     if (!NT_SUCCESS(Status))
1213         goto Cleanup;
1214     CapturedTargetKey.RootDirectory = KmTargetKeyRootDir;
1215     CapturedTargetKey.Attributes |= OBJ_KERNEL_HANDLE;
1216 
1217     /* Make sure the source file root directory handle is a kernel handle */
1218     Status = CmpConvertHandleToKernelHandle(CapturedSourceFile.RootDirectory,
1219                                             IoFileObjectType,
1220                                             FILE_TRAVERSE,
1221                                             PreviousMode,
1222                                             &KmSourceFileRootDir);
1223     if (!NT_SUCCESS(Status))
1224         goto Cleanup;
1225     CapturedSourceFile.RootDirectory = KmSourceFileRootDir;
1226     CapturedSourceFile.Attributes |= OBJ_KERNEL_HANDLE;
1227 
1228     /* Check if we have a trust class */
1229     if (TrustClassKey)
1230     {
1231         /* Reference it */
1232         Status = ObReferenceObjectByHandle(TrustClassKey,
1233                                            0,
1234                                            CmpKeyObjectType,
1235                                            PreviousMode,
1236                                            (PVOID*)&KeyBody,
1237                                            NULL);
1238     }
1239 
1240     /* Call the internal API */
1241     Status = CmLoadKey(&CapturedTargetKey,
1242                        &CapturedSourceFile,
1243                        Flags,
1244                        KeyBody);
1245 
1246     /* Dereference the trust key, if any */
1247     if (KeyBody) ObDereferenceObject(KeyBody);
1248 
1249 Cleanup:
1250     /* Close the local kernel handles */
1251     if (KmSourceFileRootDir)
1252         ObCloseHandle(KmSourceFileRootDir, KernelMode);
1253     if (KmTargetKeyRootDir)
1254         ObCloseHandle(KmTargetKeyRootDir, KernelMode);
1255 
1256     /* Release the captured object attributes */
1257     ReleaseCapturedObjectAttributes(&CapturedSourceFile, PreviousMode);
1258     ReleaseCapturedObjectAttributes(&CapturedTargetKey, PreviousMode);
1259 
1260 Quit:
1261     /* Bring back APCs */
1262     KeLeaveCriticalRegion();
1263 
1264     /* Return status */
1265     return Status;
1266 }
1267 
1268 NTSTATUS
1269 NTAPI
1270 NtNotifyChangeKey(IN HANDLE KeyHandle,
1271                   IN HANDLE Event,
1272                   IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
1273                   IN PVOID ApcContext OPTIONAL,
1274                   OUT PIO_STATUS_BLOCK IoStatusBlock,
1275                   IN ULONG CompletionFilter,
1276                   IN BOOLEAN WatchTree,
1277                   OUT PVOID Buffer,
1278                   IN ULONG Length,
1279                   IN BOOLEAN Asynchronous)
1280 {
1281     /* Call the newer API */
1282     return NtNotifyChangeMultipleKeys(KeyHandle,
1283                                       0,
1284                                       NULL,
1285                                       Event,
1286                                       ApcRoutine,
1287                                       ApcContext,
1288                                       IoStatusBlock,
1289                                       CompletionFilter,
1290                                       WatchTree,
1291                                       Buffer,
1292                                       Length,
1293                                       Asynchronous);
1294 }
1295 
1296 NTSTATUS
1297 NTAPI
1298 NtInitializeRegistry(IN USHORT Flag)
1299 {
1300     BOOLEAN SetupBoot;
1301     NTSTATUS Status = STATUS_SUCCESS;
1302     PAGED_CODE();
1303 
1304     /* Always do this as kernel mode */
1305     if (KeGetPreviousMode() == UserMode)
1306         return ZwInitializeRegistry(Flag);
1307 
1308     /* Enough of the system has booted by now */
1309     Ki386PerfEnd();
1310 
1311     /* Validate flag */
1312     if (Flag > CM_BOOT_FLAG_MAX) return STATUS_INVALID_PARAMETER;
1313 
1314     /* Check if boot was accepted */
1315     if ((Flag >= CM_BOOT_FLAG_ACCEPTED) && (Flag <= CM_BOOT_FLAG_MAX))
1316     {
1317         /* Only allow once */
1318         if (!CmBootAcceptFirstTime) return STATUS_ACCESS_DENIED;
1319         CmBootAcceptFirstTime = FALSE;
1320 
1321         /* Get the control set accepted */
1322         Flag -= CM_BOOT_FLAG_ACCEPTED;
1323         if (Flag)
1324         {
1325             /* Save the last known good boot */
1326             Status = CmpSaveBootControlSet(Flag);
1327 
1328             /* Notify HAL */
1329             HalEndOfBoot();
1330 
1331             /* Enable lazy flush */
1332             CmpHoldLazyFlush = FALSE;
1333             CmpLazyFlush();
1334             return Status;
1335         }
1336 
1337         /* Otherwise, invalid boot */
1338         return STATUS_INVALID_PARAMETER;
1339     }
1340 
1341     /* Check if this was a setup boot */
1342     SetupBoot = (Flag == CM_BOOT_FLAG_SETUP ? TRUE : FALSE);
1343 
1344     /* Make sure we're only called once */
1345     if (!CmFirstTime) return STATUS_ACCESS_DENIED;
1346     CmFirstTime = FALSE;
1347 
1348     /* Lock the registry exclusively */
1349     CmpLockRegistryExclusive();
1350 
1351     /* Initialize the hives and lazy flusher */
1352     CmpCmdInit(SetupBoot);
1353 
1354     /* Save version data */
1355     CmpSetVersionData();
1356 
1357     /* Release the registry lock */
1358     CmpUnlockRegistry();
1359     return STATUS_SUCCESS;
1360 }
1361 
1362 NTSTATUS
1363 NTAPI
1364 NtCompactKeys(IN ULONG Count,
1365               IN PHANDLE KeyArray)
1366 {
1367     UNIMPLEMENTED;
1368     return STATUS_NOT_IMPLEMENTED;
1369 }
1370 
1371 NTSTATUS
1372 NTAPI
1373 NtCompressKey(IN HANDLE Key)
1374 {
1375     UNIMPLEMENTED;
1376     return STATUS_NOT_IMPLEMENTED;
1377 }
1378 
1379 // FIXME: different for different windows versions!
1380 #define PRODUCT_ACTIVATION_VERSION 7749
1381 
1382 NTSTATUS
1383 NTAPI
1384 NtLockProductActivationKeys(IN PULONG pPrivateVer,
1385                             IN PULONG pSafeMode)
1386 {
1387     KPROCESSOR_MODE PreviousMode;
1388 
1389     PreviousMode = ExGetPreviousMode();
1390     _SEH2_TRY
1391     {
1392         /* Check if the caller asked for the version */
1393         if (pPrivateVer != NULL)
1394         {
1395             /* For user mode, probe it */
1396             if (PreviousMode != KernelMode)
1397             {
1398                 ProbeForWriteUlong(pPrivateVer);
1399             }
1400 
1401             /* Return the expected version */
1402             *pPrivateVer = PRODUCT_ACTIVATION_VERSION;
1403         }
1404 
1405         /* Check if the caller asked for safe mode mode state */
1406         if (pSafeMode != NULL)
1407         {
1408             /* For user mode, probe it */
1409             if (PreviousMode != KernelMode)
1410             {
1411                 ProbeForWriteUlong(pSafeMode);
1412             }
1413 
1414             /* Return the safe boot mode state */
1415             *pSafeMode = InitSafeBootMode;
1416         }
1417     }
1418     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1419     {
1420         _SEH2_YIELD(return _SEH2_GetExceptionCode());
1421     }
1422     _SEH2_END;
1423 
1424     return STATUS_SUCCESS;
1425 }
1426 
1427 NTSTATUS
1428 NTAPI
1429 NtLockRegistryKey(IN HANDLE KeyHandle)
1430 {
1431     UNIMPLEMENTED;
1432     return STATUS_NOT_IMPLEMENTED;
1433 }
1434 
1435 NTSTATUS
1436 NTAPI
1437 NtNotifyChangeMultipleKeys(IN HANDLE MasterKeyHandle,
1438                            IN ULONG Count,
1439                            IN POBJECT_ATTRIBUTES SlaveObjects,
1440                            IN HANDLE Event,
1441                            IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
1442                            IN PVOID ApcContext OPTIONAL,
1443                            OUT PIO_STATUS_BLOCK IoStatusBlock,
1444                            IN ULONG CompletionFilter,
1445                            IN BOOLEAN WatchTree,
1446                            OUT PVOID Buffer,
1447                            IN ULONG Length,
1448                            IN BOOLEAN Asynchronous)
1449 {
1450     UNIMPLEMENTED_ONCE;
1451     return STATUS_NOT_IMPLEMENTED;
1452 }
1453 
1454 NTSTATUS
1455 NTAPI
1456 NtQueryMultipleValueKey(IN HANDLE KeyHandle,
1457                         IN OUT PKEY_VALUE_ENTRY ValueList,
1458                         IN ULONG NumberOfValues,
1459                         OUT PVOID Buffer,
1460                         IN OUT PULONG Length,
1461                         OUT PULONG ReturnLength)
1462 {
1463     UNIMPLEMENTED;
1464     return STATUS_NOT_IMPLEMENTED;
1465 }
1466 
1467 NTSTATUS
1468 NTAPI
1469 NtQueryOpenSubKeys(IN POBJECT_ATTRIBUTES TargetKey,
1470                    OUT PULONG HandleCount)
1471 {
1472     KPROCESSOR_MODE PreviousMode;
1473     PCM_KEY_BODY KeyBody = NULL;
1474     HANDLE KeyHandle;
1475     NTSTATUS Status;
1476     ULONG SubKeys;
1477 
1478     DPRINT("NtQueryOpenSubKeys()\n");
1479 
1480     PAGED_CODE();
1481 
1482     /* Get the processor mode */
1483     PreviousMode = KeGetPreviousMode();
1484 
1485     /* Check for user-mode caller */
1486     if (PreviousMode != KernelMode)
1487     {
1488         /* Prepare to probe parameters */
1489         _SEH2_TRY
1490         {
1491             /* Probe target key */
1492             ProbeForRead(TargetKey,
1493                          sizeof(OBJECT_ATTRIBUTES),
1494                          sizeof(ULONG));
1495 
1496             /* Probe handle count */
1497             ProbeForWriteUlong(HandleCount);
1498         }
1499         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1500         {
1501             /* Return the exception code */
1502             _SEH2_YIELD(return _SEH2_GetExceptionCode());
1503         }
1504         _SEH2_END;
1505     }
1506 
1507     /* Open a handle to the key */
1508     Status = ObOpenObjectByName(TargetKey,
1509                                 CmpKeyObjectType,
1510                                 PreviousMode,
1511                                 NULL,
1512                                 KEY_READ,
1513                                 NULL,
1514                                 &KeyHandle);
1515     if (NT_SUCCESS(Status))
1516     {
1517         /* Reference the key object */
1518         Status = ObReferenceObjectByHandle(KeyHandle,
1519                                            KEY_READ,
1520                                            CmpKeyObjectType,
1521                                            PreviousMode,
1522                                            (PVOID*)&KeyBody,
1523                                            NULL);
1524 
1525         /* Close the handle */
1526         NtClose(KeyHandle);
1527     }
1528 
1529     /* Fail, if the key object could not be referenced */
1530     if (!NT_SUCCESS(Status))
1531         return Status;
1532 
1533     /* Lock the registry exclusively */
1534     CmpLockRegistryExclusive();
1535 
1536     /* Fail, if we did not open a hive root key */
1537     if (KeyBody->KeyControlBlock->KeyCell !=
1538         KeyBody->KeyControlBlock->KeyHive->BaseBlock->RootCell)
1539     {
1540         DPRINT("Error: Key is not a hive root key!\n");
1541         CmpUnlockRegistry();
1542         ObDereferenceObject(KeyBody);
1543         return STATUS_INVALID_PARAMETER;
1544     }
1545 
1546     /* Call the internal API */
1547     SubKeys = CmpEnumerateOpenSubKeys(KeyBody->KeyControlBlock,
1548                                       FALSE, FALSE);
1549 
1550     /* Unlock the registry */
1551     CmpUnlockRegistry();
1552 
1553     /* Dereference the key object */
1554     ObDereferenceObject(KeyBody);
1555 
1556     /* Write back the result */
1557     _SEH2_TRY
1558     {
1559         *HandleCount = SubKeys;
1560     }
1561     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1562     {
1563         Status = _SEH2_GetExceptionCode();
1564     }
1565     _SEH2_END;
1566 
1567     DPRINT("Done.\n");
1568 
1569     return Status;
1570 }
1571 
1572 NTSTATUS
1573 NTAPI
1574 NtQueryOpenSubKeysEx(IN POBJECT_ATTRIBUTES TargetKey,
1575                      IN ULONG BufferLength,
1576                      IN PVOID Buffer,
1577                      IN PULONG RequiredSize)
1578 {
1579     UNIMPLEMENTED;
1580     return STATUS_NOT_IMPLEMENTED;
1581 }
1582 
1583 NTSTATUS
1584 NTAPI
1585 NtRenameKey(IN HANDLE KeyHandle,
1586             IN PUNICODE_STRING ReplacementName)
1587 {
1588     UNIMPLEMENTED;
1589     return STATUS_NOT_IMPLEMENTED;
1590 }
1591 
1592 NTSTATUS
1593 NTAPI
1594 NtReplaceKey(IN POBJECT_ATTRIBUTES ObjectAttributes,
1595              IN HANDLE Key,
1596              IN POBJECT_ATTRIBUTES ReplacedObjectAttributes)
1597 {
1598     UNIMPLEMENTED;
1599     return STATUS_NOT_IMPLEMENTED;
1600 }
1601 
1602 NTSTATUS
1603 NTAPI
1604 NtRestoreKey(IN HANDLE KeyHandle,
1605              IN HANDLE FileHandle,
1606              IN ULONG RestoreFlags)
1607 {
1608     UNIMPLEMENTED;
1609     return STATUS_NOT_IMPLEMENTED;
1610 }
1611 
1612 NTSTATUS
1613 NTAPI
1614 NtSaveKey(IN HANDLE KeyHandle,
1615           IN HANDLE FileHandle)
1616 {
1617     /* Call the extended API */
1618     return NtSaveKeyEx(KeyHandle, FileHandle, REG_STANDARD_FORMAT);
1619 }
1620 
1621 NTSTATUS
1622 NTAPI
1623 NtSaveKeyEx(IN HANDLE KeyHandle,
1624             IN HANDLE FileHandle,
1625             IN ULONG Flags)
1626 {
1627     NTSTATUS Status;
1628     HANDLE KmFileHandle = NULL;
1629     PCM_KEY_BODY KeyObject;
1630     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1631 
1632     PAGED_CODE();
1633 
1634     DPRINT("NtSaveKeyEx(0x%p, 0x%p, %lu)\n", KeyHandle, FileHandle, Flags);
1635 
1636     /* Verify the flags */
1637     if ((Flags != REG_STANDARD_FORMAT)
1638         && (Flags != REG_LATEST_FORMAT)
1639         && (Flags != REG_NO_COMPRESSION))
1640     {
1641         /* Only one of these values can be specified */
1642         return STATUS_INVALID_PARAMETER;
1643     }
1644 
1645     /* Validate privilege */
1646     if (!SeSinglePrivilegeCheck(SeBackupPrivilege, PreviousMode))
1647     {
1648         return STATUS_PRIVILEGE_NOT_HELD;
1649     }
1650 
1651     /* Make sure the target file handle is a kernel handle */
1652     Status = CmpConvertHandleToKernelHandle(FileHandle,
1653                                             IoFileObjectType,
1654                                             FILE_WRITE_DATA,
1655                                             PreviousMode,
1656                                             &KmFileHandle);
1657     if (!NT_SUCCESS(Status))
1658         goto Quit;
1659 
1660     /* Verify that the handle is valid and is a registry key */
1661     Status = ObReferenceObjectByHandle(KeyHandle,
1662                                        KEY_READ,
1663                                        CmpKeyObjectType,
1664                                        PreviousMode,
1665                                        (PVOID*)&KeyObject,
1666                                        NULL);
1667     if (!NT_SUCCESS(Status))
1668         goto Quit;
1669 
1670     /* Call the internal API */
1671     Status = CmSaveKey(KeyObject->KeyControlBlock, KmFileHandle, Flags);
1672 
1673     /* Dereference the registry key */
1674     ObDereferenceObject(KeyObject);
1675 
1676 Quit:
1677     /* Close the local kernel handle */
1678     if (KmFileHandle)
1679         ObCloseHandle(KmFileHandle, KernelMode);
1680 
1681     return Status;
1682 }
1683 
1684 NTSTATUS
1685 NTAPI
1686 NtSaveMergedKeys(IN HANDLE HighPrecedenceKeyHandle,
1687                  IN HANDLE LowPrecedenceKeyHandle,
1688                  IN HANDLE FileHandle)
1689 {
1690     NTSTATUS Status;
1691     KPROCESSOR_MODE PreviousMode;
1692     HANDLE KmFileHandle = NULL;
1693     PCM_KEY_BODY HighPrecedenceKeyObject = NULL;
1694     PCM_KEY_BODY LowPrecedenceKeyObject = NULL;
1695 
1696     PAGED_CODE();
1697 
1698     DPRINT("NtSaveMergedKeys(0x%p, 0x%p, 0x%p)\n",
1699            HighPrecedenceKeyHandle, LowPrecedenceKeyHandle, FileHandle);
1700 
1701     PreviousMode = ExGetPreviousMode();
1702 
1703     /* Validate privilege */
1704     if (!SeSinglePrivilegeCheck(SeBackupPrivilege, PreviousMode))
1705     {
1706         return STATUS_PRIVILEGE_NOT_HELD;
1707     }
1708 
1709     /* Make sure the target file handle is a kernel handle */
1710     Status = CmpConvertHandleToKernelHandle(FileHandle,
1711                                             IoFileObjectType,
1712                                             FILE_WRITE_DATA,
1713                                             PreviousMode,
1714                                             &KmFileHandle);
1715     if (!NT_SUCCESS(Status))
1716         goto Quit;
1717 
1718     /* Verify that the handles are valid and are registry keys */
1719     Status = ObReferenceObjectByHandle(HighPrecedenceKeyHandle,
1720                                        KEY_READ,
1721                                        CmpKeyObjectType,
1722                                        PreviousMode,
1723                                        (PVOID*)&HighPrecedenceKeyObject,
1724                                        NULL);
1725     if (!NT_SUCCESS(Status))
1726         goto Quit;
1727 
1728     Status = ObReferenceObjectByHandle(LowPrecedenceKeyHandle,
1729                                        KEY_READ,
1730                                        CmpKeyObjectType,
1731                                        PreviousMode,
1732                                        (PVOID*)&LowPrecedenceKeyObject,
1733                                        NULL);
1734     if (!NT_SUCCESS(Status))
1735         goto Quit;
1736 
1737     /* Call the internal API */
1738     Status = CmSaveMergedKeys(HighPrecedenceKeyObject->KeyControlBlock,
1739                               LowPrecedenceKeyObject->KeyControlBlock,
1740                               KmFileHandle);
1741 
1742 Quit:
1743     /* Dereference the opened key objects */
1744     if (LowPrecedenceKeyObject)
1745         ObDereferenceObject(LowPrecedenceKeyObject);
1746     if (HighPrecedenceKeyObject)
1747         ObDereferenceObject(HighPrecedenceKeyObject);
1748 
1749     /* Close the local kernel handle */
1750     if (KmFileHandle)
1751         ObCloseHandle(KmFileHandle, KernelMode);
1752 
1753     return Status;
1754 }
1755 
1756 NTSTATUS
1757 NTAPI
1758 NtSetInformationKey(IN HANDLE KeyHandle,
1759                     IN KEY_SET_INFORMATION_CLASS KeyInformationClass,
1760                     IN PVOID KeyInformation,
1761                     IN ULONG KeyInformationLength)
1762 {
1763     UNIMPLEMENTED;
1764     return STATUS_NOT_IMPLEMENTED;
1765 }
1766 
1767 NTSTATUS
1768 NTAPI
1769 NtUnloadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes)
1770 {
1771     return NtUnloadKey2(KeyObjectAttributes, 0);
1772 }
1773 
1774 NTSTATUS
1775 NTAPI
1776 NtUnloadKey2(IN POBJECT_ATTRIBUTES TargetKey,
1777              IN ULONG Flags)
1778 {
1779     NTSTATUS Status;
1780     OBJECT_ATTRIBUTES CapturedTargetKey;
1781     UNICODE_STRING ObjectName;
1782     HANDLE KmTargetKeyRootDir = NULL;
1783     CM_PARSE_CONTEXT ParseContext = {0};
1784     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1785     PCM_KEY_BODY KeyBody = NULL;
1786     ULONG ParentConv = 0, ChildConv = 0;
1787     HANDLE Handle;
1788 
1789     PAGED_CODE();
1790 
1791     /* Validate privilege */
1792     if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode))
1793     {
1794         DPRINT1("Restore Privilege missing!\n");
1795         return STATUS_PRIVILEGE_NOT_HELD;
1796     }
1797 
1798     /* Check for user-mode caller */
1799     if (PreviousMode != KernelMode)
1800     {
1801         /* Prepare to probe parameters */
1802         _SEH2_TRY
1803         {
1804             /* Probe object attributes */
1805             ProbeForRead(TargetKey,
1806                          sizeof(OBJECT_ATTRIBUTES),
1807                          sizeof(ULONG));
1808 
1809             CapturedTargetKey = *TargetKey;
1810 
1811             /* Probe the string */
1812             ObjectName = ProbeForReadUnicodeString(CapturedTargetKey.ObjectName);
1813             ProbeForRead(ObjectName.Buffer,
1814                          ObjectName.Length,
1815                          sizeof(WCHAR));
1816 
1817             CapturedTargetKey.ObjectName = &ObjectName;
1818         }
1819         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1820         {
1821             /* Return the exception code */
1822             _SEH2_YIELD(return _SEH2_GetExceptionCode());
1823         }
1824         _SEH2_END;
1825     }
1826     else
1827     {
1828         /* Save the target attributes directly */
1829         CapturedTargetKey = *TargetKey;
1830     }
1831 
1832     /* Make sure the target key root directory handle is a kernel handle */
1833     Status = CmpConvertHandleToKernelHandle(CapturedTargetKey.RootDirectory,
1834                                             CmpKeyObjectType,
1835                                             KEY_WRITE,
1836                                             PreviousMode,
1837                                             &KmTargetKeyRootDir);
1838     if (!NT_SUCCESS(Status))
1839         return Status;
1840     CapturedTargetKey.RootDirectory = KmTargetKeyRootDir;
1841     CapturedTargetKey.Attributes |= OBJ_KERNEL_HANDLE;
1842 
1843     /* Setup the parse context */
1844     ParseContext.CreateOperation = TRUE;
1845     ParseContext.CreateOptions = REG_OPTION_BACKUP_RESTORE;
1846 
1847     /* Do the create */
1848     /* Open a local handle to the key */
1849     Status = ObOpenObjectByName(&CapturedTargetKey,
1850                                 CmpKeyObjectType,
1851                                 KernelMode,
1852                                 NULL,
1853                                 KEY_WRITE,
1854                                 &ParseContext,
1855                                 &Handle);
1856     if (NT_SUCCESS(Status))
1857     {
1858         /* Reference the key object */
1859         Status = ObReferenceObjectByHandle(Handle,
1860                                            KEY_WRITE,
1861                                            CmpKeyObjectType,
1862                                            KernelMode,
1863                                            (PVOID*)&KeyBody,
1864                                            NULL);
1865 
1866         /* Close the handle */
1867         ObCloseHandle(Handle, KernelMode);
1868     }
1869 
1870     /* Close the local kernel handle */
1871     if (KmTargetKeyRootDir)
1872         ObCloseHandle(KmTargetKeyRootDir, KernelMode);
1873 
1874     /* Return if a failure was encountered */
1875     if (!NT_SUCCESS(Status))
1876         return Status;
1877 
1878     /* Acquire the lock depending on flags */
1879     if (Flags == REG_FORCE_UNLOAD)
1880     {
1881         /* Lock registry exclusively */
1882         CmpLockRegistryExclusive();
1883     }
1884     else
1885     {
1886         /* Lock registry */
1887         CmpLockRegistry();
1888 
1889         /* Acquire the hive loading lock */
1890         ExAcquirePushLockExclusive(&CmpLoadHiveLock);
1891 
1892         /* Lock parent and child */
1893         if (KeyBody->KeyControlBlock->ParentKcb)
1894             ParentConv = KeyBody->KeyControlBlock->ParentKcb->ConvKey;
1895         else
1896             ParentConv = KeyBody->KeyControlBlock->ConvKey;
1897 
1898         ChildConv = KeyBody->KeyControlBlock->ConvKey;
1899 
1900         CmpAcquireTwoKcbLocksExclusiveByKey(ChildConv, ParentConv);
1901     }
1902 
1903     /* Check if it's being deleted already */
1904     if (KeyBody->KeyControlBlock->Delete)
1905     {
1906         /* Return appropriate status */
1907         Status = STATUS_KEY_DELETED;
1908         goto Quit;
1909     }
1910 
1911     /* Check if it's a read-only key */
1912     if (KeyBody->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY)
1913     {
1914         /* Return appropriate status */
1915         Status = STATUS_ACCESS_DENIED;
1916         goto Quit;
1917     }
1918 
1919     /* Call the internal API. Note that CmUnloadKey() unlocks the registry only on success. */
1920     Status = CmUnloadKey(KeyBody->KeyControlBlock, Flags);
1921 
1922     /* Check if we failed, but really need to succeed */
1923     if ((Status == STATUS_CANNOT_DELETE) && (Flags == REG_FORCE_UNLOAD))
1924     {
1925         /* TODO: We should perform another attempt here */
1926         _SEH2_TRY
1927         {
1928             DPRINT1("NtUnloadKey2(%wZ): We want to force-unload the hive but couldn't unload it: Retrying is UNIMPLEMENTED!\n", TargetKey->ObjectName);
1929         }
1930         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1931         {
1932         }
1933         _SEH2_END;
1934     }
1935 
1936     /* If CmUnloadKey() failed we need to unlock registry ourselves */
1937     if (!NT_SUCCESS(Status))
1938     {
1939         if (Flags != REG_FORCE_UNLOAD)
1940         {
1941             /* Release the KCB locks */
1942             CmpReleaseTwoKcbLockByKey(ChildConv, ParentConv);
1943 
1944             /* Release the hive loading lock */
1945             ExReleasePushLockExclusive(&CmpLoadHiveLock);
1946         }
1947 
1948         /* Unlock the registry */
1949         CmpUnlockRegistry();
1950     }
1951 
1952 Quit:
1953     /* Dereference the key */
1954     ObDereferenceObject(KeyBody);
1955 
1956     /* Return status */
1957     return Status;
1958 }
1959 
1960 NTSTATUS
1961 NTAPI
1962 NtUnloadKeyEx(IN POBJECT_ATTRIBUTES TargetKey,
1963               IN HANDLE Event)
1964 {
1965     UNIMPLEMENTED;
1966     return STATUS_NOT_IMPLEMENTED;
1967 }
1968 
1969 /* EOF */
1970