xref: /reactos/ntoskrnl/config/ntapi.c (revision d6d1efe7)
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                 ProbeForRead(pPrivateVer, sizeof(ULONG), sizeof(ULONG));
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                 ProbeForRead(pSafeMode, sizeof(ULONG), sizeof(ULONG));
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 
1477     DPRINT("NtQueryOpenSubKeys()\n");
1478 
1479     PAGED_CODE();
1480 
1481     /* Get the processor mode */
1482     PreviousMode = KeGetPreviousMode();
1483 
1484     /* Check for user-mode caller */
1485     if (PreviousMode != KernelMode)
1486     {
1487         /* Prepare to probe parameters */
1488         _SEH2_TRY
1489         {
1490             /* Probe target key */
1491             ProbeForRead(TargetKey,
1492                          sizeof(OBJECT_ATTRIBUTES),
1493                          sizeof(ULONG));
1494 
1495             /* Probe handle count */
1496             ProbeForWriteUlong(HandleCount);
1497         }
1498         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1499         {
1500             /* Return the exception code */
1501             _SEH2_YIELD(return _SEH2_GetExceptionCode());
1502         }
1503         _SEH2_END;
1504     }
1505 
1506     /* Open a handle to the key */
1507     Status = ObOpenObjectByName(TargetKey,
1508                                 CmpKeyObjectType,
1509                                 PreviousMode,
1510                                 NULL,
1511                                 KEY_READ,
1512                                 NULL,
1513                                 &KeyHandle);
1514     if (NT_SUCCESS(Status))
1515     {
1516         /* Reference the key object */
1517         Status = ObReferenceObjectByHandle(KeyHandle,
1518                                            KEY_READ,
1519                                            CmpKeyObjectType,
1520                                            PreviousMode,
1521                                            (PVOID*)&KeyBody,
1522                                            NULL);
1523 
1524         /* Close the handle */
1525         NtClose(KeyHandle);
1526     }
1527 
1528     /* Fail, if the key object could not be referenced */
1529     if (!NT_SUCCESS(Status))
1530         return Status;
1531 
1532     /* Lock the registry exclusively */
1533     CmpLockRegistryExclusive();
1534 
1535     /* Fail, if we did not open a hive root key */
1536     if (KeyBody->KeyControlBlock->KeyCell !=
1537         KeyBody->KeyControlBlock->KeyHive->BaseBlock->RootCell)
1538     {
1539         DPRINT("Error: Key is not a hive root key!\n");
1540         CmpUnlockRegistry();
1541         ObDereferenceObject(KeyBody);
1542         return STATUS_INVALID_PARAMETER;
1543     }
1544 
1545     /* Call the internal API */
1546     *HandleCount = CmpEnumerateOpenSubKeys(KeyBody->KeyControlBlock,
1547                                            FALSE, FALSE);
1548 
1549     /* Unlock the registry */
1550     CmpUnlockRegistry();
1551 
1552     /* Dereference the key object */
1553     ObDereferenceObject(KeyBody);
1554 
1555     DPRINT("Done.\n");
1556 
1557     return Status;
1558 }
1559 
1560 NTSTATUS
1561 NTAPI
1562 NtQueryOpenSubKeysEx(IN POBJECT_ATTRIBUTES TargetKey,
1563                      IN ULONG BufferLength,
1564                      IN PVOID Buffer,
1565                      IN PULONG RequiredSize)
1566 {
1567     UNIMPLEMENTED;
1568     return STATUS_NOT_IMPLEMENTED;
1569 }
1570 
1571 NTSTATUS
1572 NTAPI
1573 NtRenameKey(IN HANDLE KeyHandle,
1574             IN PUNICODE_STRING ReplacementName)
1575 {
1576     UNIMPLEMENTED;
1577     return STATUS_NOT_IMPLEMENTED;
1578 }
1579 
1580 NTSTATUS
1581 NTAPI
1582 NtReplaceKey(IN POBJECT_ATTRIBUTES ObjectAttributes,
1583              IN HANDLE Key,
1584              IN POBJECT_ATTRIBUTES ReplacedObjectAttributes)
1585 {
1586     UNIMPLEMENTED;
1587     return STATUS_NOT_IMPLEMENTED;
1588 }
1589 
1590 NTSTATUS
1591 NTAPI
1592 NtRestoreKey(IN HANDLE KeyHandle,
1593              IN HANDLE FileHandle,
1594              IN ULONG RestoreFlags)
1595 {
1596     UNIMPLEMENTED;
1597     return STATUS_NOT_IMPLEMENTED;
1598 }
1599 
1600 NTSTATUS
1601 NTAPI
1602 NtSaveKey(IN HANDLE KeyHandle,
1603           IN HANDLE FileHandle)
1604 {
1605     /* Call the extended API */
1606     return NtSaveKeyEx(KeyHandle, FileHandle, REG_STANDARD_FORMAT);
1607 }
1608 
1609 NTSTATUS
1610 NTAPI
1611 NtSaveKeyEx(IN HANDLE KeyHandle,
1612             IN HANDLE FileHandle,
1613             IN ULONG Flags)
1614 {
1615     NTSTATUS Status;
1616     HANDLE KmFileHandle = NULL;
1617     PCM_KEY_BODY KeyObject;
1618     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1619 
1620     PAGED_CODE();
1621 
1622     DPRINT("NtSaveKeyEx(0x%p, 0x%p, %lu)\n", KeyHandle, FileHandle, Flags);
1623 
1624     /* Verify the flags */
1625     if ((Flags != REG_STANDARD_FORMAT)
1626         && (Flags != REG_LATEST_FORMAT)
1627         && (Flags != REG_NO_COMPRESSION))
1628     {
1629         /* Only one of these values can be specified */
1630         return STATUS_INVALID_PARAMETER;
1631     }
1632 
1633     /* Validate privilege */
1634     if (!SeSinglePrivilegeCheck(SeBackupPrivilege, PreviousMode))
1635     {
1636         return STATUS_PRIVILEGE_NOT_HELD;
1637     }
1638 
1639     /* Make sure the target file handle is a kernel handle */
1640     Status = CmpConvertHandleToKernelHandle(FileHandle,
1641                                             IoFileObjectType,
1642                                             FILE_WRITE_DATA,
1643                                             PreviousMode,
1644                                             &KmFileHandle);
1645     if (!NT_SUCCESS(Status))
1646         goto Quit;
1647 
1648     /* Verify that the handle is valid and is a registry key */
1649     Status = ObReferenceObjectByHandle(KeyHandle,
1650                                        KEY_READ,
1651                                        CmpKeyObjectType,
1652                                        PreviousMode,
1653                                        (PVOID*)&KeyObject,
1654                                        NULL);
1655     if (!NT_SUCCESS(Status))
1656         goto Quit;
1657 
1658     /* Call the internal API */
1659     Status = CmSaveKey(KeyObject->KeyControlBlock, KmFileHandle, Flags);
1660 
1661     /* Dereference the registry key */
1662     ObDereferenceObject(KeyObject);
1663 
1664 Quit:
1665     /* Close the local kernel handle */
1666     if (KmFileHandle)
1667         ObCloseHandle(KmFileHandle, KernelMode);
1668 
1669     return Status;
1670 }
1671 
1672 NTSTATUS
1673 NTAPI
1674 NtSaveMergedKeys(IN HANDLE HighPrecedenceKeyHandle,
1675                  IN HANDLE LowPrecedenceKeyHandle,
1676                  IN HANDLE FileHandle)
1677 {
1678     NTSTATUS Status;
1679     KPROCESSOR_MODE PreviousMode;
1680     HANDLE KmFileHandle = NULL;
1681     PCM_KEY_BODY HighPrecedenceKeyObject = NULL;
1682     PCM_KEY_BODY LowPrecedenceKeyObject = NULL;
1683 
1684     PAGED_CODE();
1685 
1686     DPRINT("NtSaveMergedKeys(0x%p, 0x%p, 0x%p)\n",
1687            HighPrecedenceKeyHandle, LowPrecedenceKeyHandle, FileHandle);
1688 
1689     PreviousMode = ExGetPreviousMode();
1690 
1691     /* Validate privilege */
1692     if (!SeSinglePrivilegeCheck(SeBackupPrivilege, PreviousMode))
1693     {
1694         return STATUS_PRIVILEGE_NOT_HELD;
1695     }
1696 
1697     /* Make sure the target file handle is a kernel handle */
1698     Status = CmpConvertHandleToKernelHandle(FileHandle,
1699                                             IoFileObjectType,
1700                                             FILE_WRITE_DATA,
1701                                             PreviousMode,
1702                                             &KmFileHandle);
1703     if (!NT_SUCCESS(Status))
1704         goto Quit;
1705 
1706     /* Verify that the handles are valid and are registry keys */
1707     Status = ObReferenceObjectByHandle(HighPrecedenceKeyHandle,
1708                                        KEY_READ,
1709                                        CmpKeyObjectType,
1710                                        PreviousMode,
1711                                        (PVOID*)&HighPrecedenceKeyObject,
1712                                        NULL);
1713     if (!NT_SUCCESS(Status))
1714         goto Quit;
1715 
1716     Status = ObReferenceObjectByHandle(LowPrecedenceKeyHandle,
1717                                        KEY_READ,
1718                                        CmpKeyObjectType,
1719                                        PreviousMode,
1720                                        (PVOID*)&LowPrecedenceKeyObject,
1721                                        NULL);
1722     if (!NT_SUCCESS(Status))
1723         goto Quit;
1724 
1725     /* Call the internal API */
1726     Status = CmSaveMergedKeys(HighPrecedenceKeyObject->KeyControlBlock,
1727                               LowPrecedenceKeyObject->KeyControlBlock,
1728                               KmFileHandle);
1729 
1730 Quit:
1731     /* Dereference the opened key objects */
1732     if (LowPrecedenceKeyObject)
1733         ObDereferenceObject(LowPrecedenceKeyObject);
1734     if (HighPrecedenceKeyObject)
1735         ObDereferenceObject(HighPrecedenceKeyObject);
1736 
1737     /* Close the local kernel handle */
1738     if (KmFileHandle)
1739         ObCloseHandle(KmFileHandle, KernelMode);
1740 
1741     return Status;
1742 }
1743 
1744 NTSTATUS
1745 NTAPI
1746 NtSetInformationKey(IN HANDLE KeyHandle,
1747                     IN KEY_SET_INFORMATION_CLASS KeyInformationClass,
1748                     IN PVOID KeyInformation,
1749                     IN ULONG KeyInformationLength)
1750 {
1751     UNIMPLEMENTED;
1752     return STATUS_NOT_IMPLEMENTED;
1753 }
1754 
1755 NTSTATUS
1756 NTAPI
1757 NtUnloadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes)
1758 {
1759     return NtUnloadKey2(KeyObjectAttributes, 0);
1760 }
1761 
1762 NTSTATUS
1763 NTAPI
1764 NtUnloadKey2(IN POBJECT_ATTRIBUTES TargetKey,
1765              IN ULONG Flags)
1766 {
1767     NTSTATUS Status;
1768     OBJECT_ATTRIBUTES CapturedTargetKey;
1769     UNICODE_STRING ObjectName;
1770     HANDLE KmTargetKeyRootDir = NULL;
1771     CM_PARSE_CONTEXT ParseContext = {0};
1772     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1773     PCM_KEY_BODY KeyBody = NULL;
1774     ULONG ParentConv = 0, ChildConv = 0;
1775     HANDLE Handle;
1776 
1777     PAGED_CODE();
1778 
1779     /* Validate privilege */
1780     if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode))
1781     {
1782         DPRINT1("Restore Privilege missing!\n");
1783         return STATUS_PRIVILEGE_NOT_HELD;
1784     }
1785 
1786     /* Check for user-mode caller */
1787     if (PreviousMode != KernelMode)
1788     {
1789         /* Prepare to probe parameters */
1790         _SEH2_TRY
1791         {
1792             /* Probe object attributes */
1793             ProbeForRead(TargetKey,
1794                          sizeof(OBJECT_ATTRIBUTES),
1795                          sizeof(ULONG));
1796 
1797             CapturedTargetKey = *TargetKey;
1798 
1799             /* Probe the string */
1800             ObjectName = ProbeForReadUnicodeString(CapturedTargetKey.ObjectName);
1801             ProbeForRead(ObjectName.Buffer,
1802                          ObjectName.Length,
1803                          sizeof(WCHAR));
1804 
1805             CapturedTargetKey.ObjectName = &ObjectName;
1806         }
1807         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1808         {
1809             /* Return the exception code */
1810             _SEH2_YIELD(return _SEH2_GetExceptionCode());
1811         }
1812         _SEH2_END;
1813     }
1814     else
1815     {
1816         /* Save the target attributes directly */
1817         CapturedTargetKey = *TargetKey;
1818     }
1819 
1820     /* Make sure the target key root directory handle is a kernel handle */
1821     Status = CmpConvertHandleToKernelHandle(CapturedTargetKey.RootDirectory,
1822                                             CmpKeyObjectType,
1823                                             KEY_WRITE,
1824                                             PreviousMode,
1825                                             &KmTargetKeyRootDir);
1826     if (!NT_SUCCESS(Status))
1827         return Status;
1828     CapturedTargetKey.RootDirectory = KmTargetKeyRootDir;
1829     CapturedTargetKey.Attributes |= OBJ_KERNEL_HANDLE;
1830 
1831     /* Setup the parse context */
1832     ParseContext.CreateOperation = TRUE;
1833     ParseContext.CreateOptions = REG_OPTION_BACKUP_RESTORE;
1834 
1835     /* Do the create */
1836     /* Open a local handle to the key */
1837     Status = ObOpenObjectByName(&CapturedTargetKey,
1838                                 CmpKeyObjectType,
1839                                 KernelMode,
1840                                 NULL,
1841                                 KEY_WRITE,
1842                                 &ParseContext,
1843                                 &Handle);
1844     if (NT_SUCCESS(Status))
1845     {
1846         /* Reference the key object */
1847         Status = ObReferenceObjectByHandle(Handle,
1848                                            KEY_WRITE,
1849                                            CmpKeyObjectType,
1850                                            KernelMode,
1851                                            (PVOID*)&KeyBody,
1852                                            NULL);
1853 
1854         /* Close the handle */
1855         ObCloseHandle(Handle, KernelMode);
1856     }
1857 
1858     /* Close the local kernel handle */
1859     if (KmTargetKeyRootDir)
1860         ObCloseHandle(KmTargetKeyRootDir, KernelMode);
1861 
1862     /* Return if a failure was encountered */
1863     if (!NT_SUCCESS(Status))
1864         return Status;
1865 
1866     /* Acquire the lock depending on flags */
1867     if (Flags == REG_FORCE_UNLOAD)
1868     {
1869         /* Lock registry exclusively */
1870         CmpLockRegistryExclusive();
1871     }
1872     else
1873     {
1874         /* Lock registry */
1875         CmpLockRegistry();
1876 
1877         /* Acquire the hive loading lock */
1878         ExAcquirePushLockExclusive(&CmpLoadHiveLock);
1879 
1880         /* Lock parent and child */
1881         if (KeyBody->KeyControlBlock->ParentKcb)
1882             ParentConv = KeyBody->KeyControlBlock->ParentKcb->ConvKey;
1883         else
1884             ParentConv = KeyBody->KeyControlBlock->ConvKey;
1885 
1886         ChildConv = KeyBody->KeyControlBlock->ConvKey;
1887 
1888         CmpAcquireTwoKcbLocksExclusiveByKey(ChildConv, ParentConv);
1889     }
1890 
1891     /* Check if it's being deleted already */
1892     if (KeyBody->KeyControlBlock->Delete)
1893     {
1894         /* Return appropriate status */
1895         Status = STATUS_KEY_DELETED;
1896         goto Quit;
1897     }
1898 
1899     /* Check if it's a read-only key */
1900     if (KeyBody->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY)
1901     {
1902         /* Return appropriate status */
1903         Status = STATUS_ACCESS_DENIED;
1904         goto Quit;
1905     }
1906 
1907     /* Call the internal API. Note that CmUnloadKey() unlocks the registry only on success. */
1908     Status = CmUnloadKey(KeyBody->KeyControlBlock, Flags);
1909 
1910     /* Check if we failed, but really need to succeed */
1911     if ((Status == STATUS_CANNOT_DELETE) && (Flags == REG_FORCE_UNLOAD))
1912     {
1913         /* TODO: We should perform another attempt here */
1914         _SEH2_TRY
1915         {
1916             DPRINT1("NtUnloadKey2(%wZ): We want to force-unload the hive but couldn't unload it: Retrying is UNIMPLEMENTED!\n", TargetKey->ObjectName);
1917         }
1918         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1919         {
1920         }
1921         _SEH2_END;
1922     }
1923 
1924     /* If CmUnloadKey() failed we need to unlock registry ourselves */
1925     if (!NT_SUCCESS(Status))
1926     {
1927         if (Flags != REG_FORCE_UNLOAD)
1928         {
1929             /* Release the KCB locks */
1930             CmpReleaseTwoKcbLockByKey(ChildConv, ParentConv);
1931 
1932             /* Release the hive loading lock */
1933             ExReleasePushLockExclusive(&CmpLoadHiveLock);
1934         }
1935 
1936         /* Unlock the registry */
1937         CmpUnlockRegistry();
1938     }
1939 
1940 Quit:
1941     /* Dereference the key */
1942     ObDereferenceObject(KeyBody);
1943 
1944     /* Return status */
1945     return Status;
1946 }
1947 
1948 NTSTATUS
1949 NTAPI
1950 NtUnloadKeyEx(IN POBJECT_ATTRIBUTES TargetKey,
1951               IN HANDLE Event)
1952 {
1953     UNIMPLEMENTED;
1954     return STATUS_NOT_IMPLEMENTED;
1955 }
1956 
1957 /* EOF */
1958