xref: /reactos/boot/environ/lib/misc/bootreg.c (revision 0c2cdcae)
1 /*
2  * COPYRIGHT:       See COPYING.ARM in the top level directory
3  * PROJECT:         ReactOS UEFI Boot Library
4  * FILE:            boot/environ/lib/misc/bootreg.c
5  * PURPOSE:         Boot Library Boot Registry Wrapper for CMLIB
6  * PROGRAMMER:      Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "bl.h"
12 #include <bcd.h>
13 
14 /* DEFINITIONS ***************************************************************/
15 
16 #define BI_FLUSH_HIVE       0x01
17 #define BI_HIVE_WRITEABLE   0x02
18 
19 /* DATA STRUCTURES ***********************************************************/
20 
21 typedef struct _BI_KEY_HIVE
22 {
23     PHBASE_BLOCK BaseBlock;
24     ULONG HiveSize;
25     PBL_FILE_PATH_DESCRIPTOR FilePath;
26     CMHIVE Hive;
27     LONG ReferenceCount;
28     ULONG Flags;
29     PCM_KEY_NODE RootNode;
30 } BI_KEY_HIVE, *PBI_KEY_HIVE;
31 
32 typedef struct _BI_KEY_OBJECT
33 {
34     PBI_KEY_HIVE KeyHive;
35     PCM_KEY_NODE KeyNode;
36     HCELL_INDEX KeyCell;
37     PWCHAR KeyName;
38 } BI_KEY_OBJECT, *PBI_KEY_OBJECT;
39 
40 /* GLOBALS *******************************************************************/
41 
42 BOOLEAN BiHiveHashLibraryInitialized;
43 ULONGLONG HvSymcryptSeed;
44 
45 /* FUNCTIONS *****************************************************************/
46 
47 BOOLEAN
HvIsInPlaceBaseBlockValid(_In_ PHBASE_BLOCK BaseBlock)48 HvIsInPlaceBaseBlockValid (
49     _In_ PHBASE_BLOCK BaseBlock
50     )
51 {
52     ULONG HiveLength, HeaderSum;
53     BOOLEAN Valid;
54 
55     /* Assume failure */
56     Valid = FALSE;
57 
58     /* Check for incorrect signature, type, version, or format */
59     if ((BaseBlock->Signature == 'fger') &&
60         (BaseBlock->Type == 0) &&
61         (BaseBlock->Major <= 1) &&
62         (BaseBlock->Minor <= 5) &&
63         (BaseBlock->Minor >= 3) &&
64         (BaseBlock->Format == 1))
65     {
66         /* Check for invalid hive size */
67         HiveLength = BaseBlock->Length;
68         if (HiveLength)
69         {
70             /* Check for misaligned or too large hive size */
71             if (!(HiveLength & 0xFFF) && HiveLength <= 0x7FFFE000)
72             {
73                 /* Check for invalid header checksum */
74                 HeaderSum = HvpHiveHeaderChecksum(BaseBlock);
75                 if (HeaderSum == BaseBlock->CheckSum)
76                 {
77                     /* All good */
78                     Valid = TRUE;
79                 }
80             }
81         }
82     }
83 
84     /* Return validity */
85     return Valid;
86 }
87 
88 PVOID
89 NTAPI
CmpAllocate(_In_ SIZE_T Size,_In_ BOOLEAN Paged,_In_ ULONG Tag)90 CmpAllocate (
91     _In_ SIZE_T Size,
92     _In_ BOOLEAN Paged,
93     _In_ ULONG Tag
94     )
95 {
96     UNREFERENCED_PARAMETER(Paged);
97     UNREFERENCED_PARAMETER(Tag);
98 
99     /* Call the heap allocator */
100     return BlMmAllocateHeap(Size);
101 }
102 
103 VOID
104 NTAPI
CmpFree(_In_ PVOID Ptr,_In_ ULONG Quota)105 CmpFree (
106     _In_ PVOID Ptr,
107     _In_ ULONG Quota
108     )
109 {
110     UNREFERENCED_PARAMETER(Quota);
111 
112     /* Call the heap allocator */
113     BlMmFreeHeap(Ptr);
114 }
115 
116 VOID
BiDereferenceHive(_In_ HANDLE KeyHandle)117 BiDereferenceHive (
118     _In_ HANDLE KeyHandle
119     )
120 {
121     PBI_KEY_OBJECT KeyObject;
122 
123     /* Get the key object */
124     KeyObject = (PBI_KEY_OBJECT)KeyHandle;
125 
126     /* Drop a reference on the parent hive */
127     --KeyObject->KeyHive->ReferenceCount;
128 }
129 
130 VOID
BiFlushHive(_In_ HANDLE KeyHandle)131 BiFlushHive (
132     _In_ HANDLE KeyHandle
133     )
134 {
135     /* Not yet implemented */
136     EfiPrintf(L"NO reg flush\r\n");
137     return;
138 }
139 
140 VOID
BiCloseKey(_In_ HANDLE KeyHandle)141 BiCloseKey (
142     _In_ HANDLE KeyHandle
143     )
144 {
145     PBI_KEY_HIVE KeyHive;
146     PBI_KEY_OBJECT KeyObject;
147 
148     /* Get the key object and hive */
149     KeyObject = (PBI_KEY_OBJECT)KeyHandle;
150     KeyHive = KeyObject->KeyHive;
151 
152     /* Check if we have a hive, or name, or key node */
153     if ((KeyHive) || (KeyObject->KeyNode) || (KeyObject->KeyName))
154     {
155         /* Drop a reference, see if it's the last one */
156         BiDereferenceHive(KeyHandle);
157         if (!KeyHive->ReferenceCount)
158         {
159             /* Check if we should flush it */
160             if (KeyHive->Flags & BI_FLUSH_HIVE)
161             {
162                 BiFlushHive(KeyHandle);
163             }
164 
165             /* Unmap the hive */
166             MmPapFreePages(KeyHive->BaseBlock, BL_MM_INCLUDE_MAPPED_ALLOCATED);
167 
168             /* Free the hive and hive path */
169             BlMmFreeHeap(KeyHive->FilePath);
170             BlMmFreeHeap(KeyHive);
171         }
172 
173         /* Check if a key name is present */
174         if (KeyObject->KeyName)
175         {
176             /* Free it */
177             BlMmFreeHeap(KeyObject->KeyName);
178         }
179     }
180 
181     /* Free the object */
182     BlMmFreeHeap(KeyObject);
183 }
184 
185 NTSTATUS
BiOpenKey(_In_ HANDLE ParentHandle,_In_ PWCHAR KeyName,_Out_ PHANDLE Handle)186 BiOpenKey(
187     _In_ HANDLE ParentHandle,
188     _In_ PWCHAR KeyName,
189     _Out_ PHANDLE Handle
190     )
191 {
192     PBI_KEY_OBJECT ParentKey, NewKey;
193     PBI_KEY_HIVE ParentHive;
194     NTSTATUS Status;
195     SIZE_T NameLength, SubNameLength, NameBytes;
196     PWCHAR NameStart, NameBuffer;
197     UNICODE_STRING KeyString;
198     HCELL_INDEX KeyCell;
199     PHHIVE Hive;
200     PCM_KEY_NODE ParentNode;
201 
202     /* Convert from a handle to our key object */
203     ParentKey = (PBI_KEY_OBJECT)ParentHandle;
204 
205     /* Extract the hive and node information */
206     ParentHive = ParentKey->KeyHive;
207     ParentNode = ParentKey->KeyNode;
208     Hive = &ParentKey->KeyHive->Hive.Hive;
209 
210     /* Initialize variables */
211     KeyCell = HCELL_NIL;
212     Status = STATUS_SUCCESS;
213     NameBuffer = NULL;
214 
215     /* Loop as long as there's still portions of the key name in play */
216     NameLength = wcslen(KeyName);
217     while (NameLength)
218     {
219         /* Find the first path separator */
220         NameStart = wcschr(KeyName, OBJ_NAME_PATH_SEPARATOR);
221         if (NameStart)
222         {
223             /* Look only at the key before the separator */
224             SubNameLength = NameStart - KeyName;
225             ++NameStart;
226         }
227         else
228         {
229             /* No path separator, this is the final leaf key */
230             SubNameLength = NameLength;
231         }
232 
233         /* Free the name buffer from the previous pass if needed */
234         if (NameBuffer)
235         {
236             BlMmFreeHeap(NameBuffer);
237         }
238 
239         /* Allocate a buffer to hold the name of this specific subkey only */
240         NameBytes = SubNameLength * sizeof(WCHAR);
241         NameBuffer = BlMmAllocateHeap(NameBytes + sizeof(UNICODE_NULL));
242         if (!NameBuffer)
243         {
244             Status = STATUS_NO_MEMORY;
245             goto Quickie;
246         }
247 
248         /* Copy and null-terminate the name of the subkey */
249         RtlCopyMemory(NameBuffer, KeyName, NameBytes);
250         NameBuffer[SubNameLength] = UNICODE_NULL;
251 
252         /* Convert it into a UNICODE_STRING and try to find it */
253         RtlInitUnicodeString(&KeyString, NameBuffer);
254         KeyCell = CmpFindSubKeyByName(Hive, ParentNode, &KeyString);
255         if (KeyCell == HCELL_NIL)
256         {
257             Status = STATUS_OBJECT_NAME_NOT_FOUND;
258             goto Quickie;
259         }
260 
261         /* We found it -- get the key node out of it */
262         ParentNode = (PCM_KEY_NODE)HvGetCell(Hive, KeyCell);
263         if (!ParentNode)
264         {
265             Status = STATUS_REGISTRY_CORRUPT;
266             goto Quickie;
267         }
268 
269         /* Update the key name to the next remaining path element */
270         KeyName = NameStart;
271         if (NameStart)
272         {
273             /* Update the length to the remainder of the path */
274             NameLength += -1 - SubNameLength;
275         }
276         else
277         {
278             /* There's nothing left, this was the leaf key */
279             NameLength = 0;
280         }
281     }
282 
283     /* Allocate a key object */
284     NewKey = BlMmAllocateHeap(sizeof(*NewKey));
285     if (!NewKey)
286     {
287         /* Bail out if we had no memory for it */
288         Status = STATUS_NO_MEMORY;
289         goto Quickie;
290     }
291 
292     /* Fill out the key object data */
293     NewKey->KeyNode = ParentNode;
294     NewKey->KeyHive = ParentHive;
295     NewKey->KeyName = NameBuffer;
296     NewKey->KeyCell = KeyCell;
297 
298     /* Add a reference to the hive */
299     ++ParentHive->ReferenceCount;
300 
301     /* Return the object back to the caller */
302     *Handle = NewKey;
303 
304 Quickie:
305     /* If we had a name buffer, free it */
306     if (NameBuffer)
307     {
308         BlMmFreeHeap(NameBuffer);
309     }
310 
311     /* Return status of the open operation */
312     return Status;
313 }
314 
315 NTSTATUS
BiInitializeAndValidateHive(_In_ PBI_KEY_HIVE Hive)316 BiInitializeAndValidateHive (
317     _In_ PBI_KEY_HIVE Hive
318     )
319 {
320     ULONG HiveSize;
321     CM_CHECK_REGISTRY_STATUS CmStatusCode;
322     NTSTATUS Status;
323 
324     /* Make sure the hive is at least the size of a base block */
325     if (Hive->HiveSize < sizeof(HBASE_BLOCK))
326     {
327         return STATUS_REGISTRY_CORRUPT;
328     }
329 
330     /* Make sure that the base block accurately describes the size of the hive */
331     HiveSize = Hive->BaseBlock->Length + sizeof(HBASE_BLOCK);
332     if ((HiveSize < sizeof(HBASE_BLOCK)) || (HiveSize > Hive->HiveSize))
333     {
334         return STATUS_REGISTRY_CORRUPT;
335     }
336 
337     /* Initialize a flat memory hive */
338     RtlZeroMemory(&Hive->Hive, sizeof(Hive->Hive));
339     Status = HvInitialize(&Hive->Hive.Hive,
340                           HINIT_FLAT,
341                           0,
342                           0,
343                           Hive->BaseBlock,
344                           CmpAllocate,
345                           CmpFree,
346                           NULL,
347                           NULL,
348                           NULL,
349                           NULL,
350                           0,
351                           NULL);
352     if (NT_SUCCESS(Status))
353     {
354         /* Cleanup volatile/old data */
355         CmStatusCode = CmCheckRegistry(Hive->Hive, CM_CHECK_REGISTRY_BOOTLOADER_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE);
356         if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode))
357         {
358             return STATUS_REGISTRY_CORRUPT;
359         }
360 
361         Status = STATUS_SUCCESS;
362     }
363 
364     /* Return the final status */
365     return Status;
366 }
367 
368 NTSTATUS
BiLoadHive(_In_ PBL_FILE_PATH_DESCRIPTOR FilePath,_Out_ PHANDLE HiveHandle)369 BiLoadHive (
370     _In_ PBL_FILE_PATH_DESCRIPTOR FilePath,
371     _Out_ PHANDLE HiveHandle
372     )
373 {
374     ULONG DeviceId;
375     PHBASE_BLOCK BaseBlock, NewBaseBlock;
376     PBI_KEY_OBJECT KeyObject;
377     PBI_KEY_HIVE BcdHive;
378     PBL_DEVICE_DESCRIPTOR BcdDevice;
379     ULONG PathLength, DeviceLength, HiveSize, HiveLength, NewHiveSize;
380     PWCHAR HiveName, LogName;
381     BOOLEAN HaveWriteAccess;
382     NTSTATUS Status;
383     PVOID LogData;
384     PHHIVE Hive;
385     UNICODE_STRING KeyString;
386     PCM_KEY_NODE RootNode;
387     HCELL_INDEX CellIndex;
388 
389     /* Initialize variables */
390     DeviceId = -1;
391     BaseBlock = NULL;
392     BcdHive = NULL;
393     KeyObject = NULL;
394     LogData = NULL;
395     LogName = NULL;
396 
397     /* Initialize the crypto seed */
398     if (!BiHiveHashLibraryInitialized)
399     {
400         HvSymcryptSeed = 0x82EF4D887A4E55C5;
401         BiHiveHashLibraryInitialized = TRUE;
402     }
403 
404     /* Extract and validate the input path */
405     BcdDevice = (PBL_DEVICE_DESCRIPTOR)&FilePath->Path;
406     PathLength = FilePath->Length;
407     DeviceLength = BcdDevice->Size;
408     HiveName = (PWCHAR)((ULONG_PTR)BcdDevice + BcdDevice->Size);
409     if (PathLength <= DeviceLength)
410     {
411         /* Doesn't make sense, bail out */
412         Status = STATUS_INVALID_PARAMETER;
413         goto Quickie;
414     }
415 
416     /* Attempt to open the underlying device for RW access */
417     HaveWriteAccess = TRUE;
418     Status = BlpDeviceOpen(BcdDevice,
419                            BL_DEVICE_READ_ACCESS | BL_DEVICE_WRITE_ACCESS,
420                            0,
421                            &DeviceId);
422     if (!NT_SUCCESS(Status))
423     {
424         /* Try for RO access instead */
425         HaveWriteAccess = FALSE;
426         Status = BlpDeviceOpen(BcdDevice, BL_DEVICE_READ_ACCESS, 0, &DeviceId);
427         if (!NT_SUCCESS(Status))
428         {
429             /* No access at all -- bail out */
430             goto Quickie;
431         }
432     }
433 
434     /* Now try to load the hive on disk */
435     Status = BlImgLoadImageWithProgress2(DeviceId,
436                                          BlLoaderRegistry,
437                                          HiveName,
438                                          (PVOID*)&BaseBlock,
439                                          &HiveSize,
440                                          0,
441                                          FALSE,
442                                          NULL,
443                                          NULL);
444     if (!NT_SUCCESS(Status))
445     {
446         EfiPrintf(L"Hive read failure: % lx\r\n", Status);
447         goto Quickie;
448     }
449 
450     /* Allocate a hive structure */
451     BcdHive = BlMmAllocateHeap(sizeof(*BcdHive));
452     if (!BcdHive)
453     {
454         Status = STATUS_NO_MEMORY;
455         goto Quickie;
456     }
457 
458     /* Initialize it */
459     RtlZeroMemory(BcdHive, sizeof(*BcdHive));
460     BcdHive->BaseBlock = BaseBlock;
461     BcdHive->HiveSize = HiveSize;
462     if (HaveWriteAccess)
463     {
464         BcdHive->Flags |= BI_HIVE_WRITEABLE;
465     }
466 
467     /* Make sure the hive was at least one bin long */
468     if (HiveSize < sizeof(*BaseBlock))
469     {
470         Status = STATUS_REGISTRY_CORRUPT;
471         goto Quickie;
472     }
473 
474     /* Make sure the hive contents are at least one bin long */
475     HiveLength = BaseBlock->Length;
476     if (BaseBlock->Length < sizeof(*BaseBlock))
477     {
478         Status = STATUS_REGISTRY_CORRUPT;
479         goto Quickie;
480     }
481 
482     /* Validate the initial bin (the base block) */
483     if (!HvIsInPlaceBaseBlockValid(BaseBlock))
484     {
485         EfiPrintf(L"Recovery not implemented\r\n");
486         Status = STATUS_REGISTRY_CORRUPT;
487         goto Quickie;
488     }
489 
490     /* Check if there's log recovery that needs to happen */
491     if (BaseBlock->Sequence1 != BaseBlock->Sequence2)
492     {
493         EfiPrintf(L"Log fix not implemented: %lx %lx\r\n");
494         Status = STATUS_REGISTRY_CORRUPT;
495         goto Quickie;
496     }
497 
498     /*
499      * Check if the whole hive doesn't fit in the buffer.
500      * Note: HiveLength does not include the size of the baseblock itself
501      */
502     if (HiveSize < (HiveLength + sizeof(*BaseBlock)))
503     {
504         EfiPrintf(L"Need bigger hive buffer path\r\n");
505 
506         /* Allocate a slightly bigger buffer */
507         NewHiveSize = HiveLength + sizeof(*BaseBlock);
508         Status = MmPapAllocatePagesInRange((PVOID*)&NewBaseBlock,
509                                            BlLoaderRegistry,
510                                            NewHiveSize >> PAGE_SHIFT,
511                                            0,
512                                            0,
513                                            NULL,
514                                            0);
515         if (!NT_SUCCESS(Status))
516         {
517             goto Quickie;
518         }
519 
520         /* Copy the current data in there */
521         RtlCopyMemory(NewBaseBlock, BaseBlock, HiveSize);
522 
523         /* Free the old data */
524         MmPapFreePages(BaseBlock, BL_MM_INCLUDE_MAPPED_ALLOCATED);
525 
526         /* Update our pointers */
527         BaseBlock = NewBaseBlock;
528         HiveSize = NewHiveSize;
529         BcdHive->BaseBlock = BaseBlock;
530         BcdHive->HiveSize = HiveSize;
531     }
532 
533     /* Check if any log stuff needs to happen */
534     if (LogData)
535     {
536         EfiPrintf(L"Log fix not implemented: %lx %lx\r\n");
537         Status = STATUS_REGISTRY_CORRUPT;
538         goto Quickie;
539     }
540 
541     /* Call Hv to setup the hive library */
542     Status = BiInitializeAndValidateHive(BcdHive);
543     if (!NT_SUCCESS(Status))
544     {
545         goto Quickie;
546     }
547 
548     /* Now get the root node */
549     Hive = &BcdHive->Hive.Hive;
550     RootNode = (PCM_KEY_NODE)HvGetCell(Hive, Hive->BaseBlock->RootCell);
551     if (!RootNode)
552     {
553         Status = STATUS_OBJECT_NAME_NOT_FOUND;
554         goto Quickie;
555     }
556 
557     /* Find the Objects subkey under it to see if it's a real BCD hive */
558     RtlInitUnicodeString(&KeyString, L"Objects");
559     CellIndex = CmpFindSubKeyByName(Hive, RootNode, &KeyString);
560     if (CellIndex == HCELL_NIL)
561     {
562         EfiPrintf(L"No OBJECTS subkey found!\r\n");
563         Status = STATUS_OBJECT_NAME_NOT_FOUND;
564         goto Quickie;
565     }
566 
567     /* This is a valid BCD hive, store its root node here */
568     BcdHive->RootNode = RootNode;
569 
570     /* Allocate a copy of the file path */
571     BcdHive->FilePath = BlMmAllocateHeap(FilePath->Length);
572     if (!BcdHive->FilePath)
573     {
574         Status = STATUS_NO_MEMORY;
575         goto Quickie;
576     }
577 
578     /* Make a copy of it */
579     RtlCopyMemory(BcdHive->FilePath, FilePath, FilePath->Length);
580 
581     /* Create a key object to describe the rot */
582     KeyObject = BlMmAllocateHeap(sizeof(*KeyObject));
583     if (!KeyObject)
584     {
585         Status = STATUS_NO_MEMORY;
586         goto Quickie;
587     }
588 
589     /* Fill out the details */
590     KeyObject->KeyNode = RootNode;
591     KeyObject->KeyHive = BcdHive;
592     KeyObject->KeyName = NULL;
593     KeyObject->KeyCell = Hive->BaseBlock->RootCell;
594 
595     /* One reference for the key object, plus one lifetime reference */
596     BcdHive->ReferenceCount = 2;
597 
598     /* This is the hive handle */
599     *HiveHandle  = KeyObject;
600 
601     /* We're all good */
602     Status = STATUS_SUCCESS;
603 
604 Quickie:
605     /* If we had a log name, free it */
606     if (LogName)
607     {
608         BlMmFreeHeap(LogName);
609     }
610 
611     /* If we had logging data, free it */
612     if (LogData)
613     {
614         MmPapFreePages(LogData, BL_MM_INCLUDE_MAPPED_ALLOCATED);
615     }
616 
617     /* Check if this is the failure path */
618     if (!NT_SUCCESS(Status))
619     {
620         /* If we mapped the hive, free it */
621         if (BaseBlock)
622         {
623             MmPapFreePages(BaseBlock, BL_MM_INCLUDE_MAPPED_ALLOCATED);
624         }
625 
626         /* If we opened the device, close it */
627         if (DeviceId != -1)
628         {
629             BlDeviceClose(DeviceId);
630         }
631 
632         /* Did we create a hive object? */
633         if (BcdHive)
634         {
635             /* Free the file path if we made a copy of it */
636             if (BcdHive->FilePath)
637             {
638                 BlMmFreeHeap(BcdHive->FilePath);
639             }
640 
641             /* Free the hive itself */
642             BlMmFreeHeap(BcdHive);
643         }
644 
645         /* Finally, free the root key object if we created one */
646         if (KeyObject)
647         {
648             BlMmFreeHeap(KeyObject);
649         }
650     }
651 
652     /* Return the final status */
653     return Status;
654 }
655 
656 NTSTATUS
BiGetRegistryValue(_In_ HANDLE KeyHandle,_In_ PWCHAR ValueName,_In_ ULONG Type,_Out_ PVOID * Buffer,_Out_ PULONG ValueLength)657 BiGetRegistryValue (
658     _In_ HANDLE KeyHandle,
659     _In_ PWCHAR ValueName,
660     _In_ ULONG Type,
661     _Out_ PVOID* Buffer,
662     _Out_ PULONG ValueLength
663     )
664 {
665     PCM_KEY_NODE KeyNode;
666     PHHIVE KeyHive;
667     UNICODE_STRING ValueString;
668     PBI_KEY_OBJECT KeyObject;
669     PCM_KEY_VALUE KeyValue;
670     PVOID ValueCopy;
671     ULONG Size;
672     HCELL_INDEX CellIndex;
673     PCELL_DATA ValueData;
674 
675     /* Get the key object, node,and hive */
676     KeyObject = (PBI_KEY_OBJECT)KeyHandle;
677     KeyNode = KeyObject->KeyNode;
678     KeyHive = &KeyObject->KeyHive->Hive.Hive;
679 
680     /* Find the value cell index in the list of values */
681     RtlInitUnicodeString(&ValueString, ValueName);
682     CmpFindNameInList(KeyHive,
683                       &KeyNode->ValueList,
684                       &ValueString,
685                       NULL,
686                       &CellIndex);
687     if (CellIndex == HCELL_NIL)
688     {
689         return STATUS_OBJECT_NAME_NOT_FOUND;
690     }
691 
692     /* Get the cell data for it */
693     KeyValue = (PCM_KEY_VALUE)HvGetCell(KeyHive, CellIndex);
694     if (!KeyValue)
695     {
696         return STATUS_REGISTRY_CORRUPT;
697     }
698 
699     /* Make sure the type matches */
700     if (KeyValue->Type != Type)
701     {
702         return STATUS_OBJECT_TYPE_MISMATCH;
703     }
704 
705     /* Now get the data cell */
706     ValueData = CmpValueToData(KeyHive, KeyValue, &Size);
707 
708     /* Make a copy of it */
709     ValueCopy = BlMmAllocateHeap(Size);
710     if (!ValueCopy)
711     {
712         return STATUS_NO_MEMORY;
713     }
714 
715     /* Copy it in the buffer, and return it and its size */
716     RtlCopyMemory(ValueCopy, ValueData, Size);
717     *Buffer = ValueCopy;
718     *ValueLength = Size;
719     return STATUS_SUCCESS;
720 }
721 
722 NTSTATUS
BiEnumerateSubKeys(_In_ HANDLE KeyHandle,_Out_ PWCHAR ** SubKeyList,_Out_ PULONG SubKeyCount)723 BiEnumerateSubKeys (
724     _In_ HANDLE KeyHandle,
725     _Out_ PWCHAR** SubKeyList,
726     _Out_ PULONG SubKeyCount
727     )
728 {
729     PCM_KEY_NODE KeyNode, Node;
730     PBI_KEY_OBJECT KeyObject;
731     ULONG KeyCount;
732     ULONG NameLength, NewTotalNameLength, FinalLength, TotalNameLength;
733     PHHIVE Hive;
734     PWCHAR KeyName, NameEnd;
735     HCELL_INDEX CellIndex;
736     PWCHAR* SubKeys;
737     NTSTATUS Status;
738     ULONG i;
739 
740     /* Get the key object, node, and hive */
741     KeyObject = (PBI_KEY_OBJECT)KeyHandle;
742     KeyNode = KeyObject->KeyNode;
743     Hive = &KeyObject->KeyHive->Hive.Hive;
744 
745     /* Assume it's empty */
746     *SubKeyList = 0;
747     *SubKeyCount = 0;
748 
749     /* Initialize locals */
750     KeyCount = 0;
751     SubKeys = 0;
752     TotalNameLength = 0;
753 
754     /* Find the first subkey cell index */
755     CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, KeyCount);
756     while (CellIndex != HCELL_NIL)
757     {
758         /* Move to the next one */
759         KeyCount++;
760 
761         /* Get the cell data for it */
762         Node = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex);
763         if (!Node)
764         {
765             return STATUS_REGISTRY_CORRUPT;
766         }
767 
768         /* Check if the value is compressed */
769         if (Node->Flags & KEY_COMP_NAME)
770         {
771             /* Get the compressed name size */
772             NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength);
773         }
774         else
775         {
776             /* Get the real size */
777             NameLength = Node->NameLength;
778         }
779 
780         /* Add up the new length, protecting against overflow */
781         NewTotalNameLength = TotalNameLength + NameLength + sizeof(UNICODE_NULL);
782         if (NewTotalNameLength < TotalNameLength)
783         {
784             Status = STATUS_NAME_TOO_LONG;
785             goto Quickie;
786         }
787 
788         /* We're good, use the new length */
789         TotalNameLength = NewTotalNameLength;
790 
791         /* Find the next subkey cell index */
792         CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, KeyCount);
793     }
794 
795     /* Were there no keys? We're done, if so */
796     if (!KeyCount)
797     {
798         return STATUS_SUCCESS;
799     }
800 
801     /* Safely compute the size of the array needed */
802     Status = RtlULongLongToULong(sizeof(PWCHAR) * KeyCount, &FinalLength);
803     if (!NT_SUCCESS(Status))
804     {
805         goto Quickie;
806     }
807 
808     /* Safely add that to the name length */
809     Status = RtlULongAdd(TotalNameLength, FinalLength, &FinalLength);
810     if (!NT_SUCCESS(Status))
811     {
812         goto Quickie;
813     }
814 
815     /* Allocate an array big enough for the names and pointers */
816     SubKeys = BlMmAllocateHeap(FinalLength);
817     if (!SubKeys)
818     {
819         Status = STATUS_NO_MEMORY;
820         goto Quickie;
821     }
822 
823     /* Go over each key again */
824     NameEnd = (PWCHAR)&SubKeys[KeyCount];
825     for (i = 0; i < KeyCount; i++)
826     {
827         /* Get the cell index for this subkey */
828         CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, i);
829         if (CellIndex == HCELL_NIL)
830         {
831             break;
832         }
833 
834         /* Get the cell data for it */
835         Node = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex);
836         if (!Node)
837         {
838             Status = STATUS_REGISTRY_CORRUPT;
839             goto Quickie;
840         }
841 
842         /* Check if the value is compressed */
843         KeyName = Node->Name;
844         if (Node->Flags & KEY_COMP_NAME)
845         {
846             /* Get the compressed name size */
847             NameLength = CmpCompressedNameSize(KeyName, Node->NameLength);
848             CmpCopyCompressedName(NameEnd, NameLength, KeyName, Node->NameLength);
849         }
850         else
851         {
852             /* Get the real size */
853             NameLength = Node->NameLength;
854             RtlCopyMemory(NameEnd, KeyName, NameLength);
855         }
856 
857         /* Move the name buffer to the next spot, and NULL-terminate */
858         SubKeys[i] = NameEnd;
859         NameEnd += (NameLength / sizeof(WCHAR));
860         *NameEnd = UNICODE_NULL;
861 
862         /* Keep going */
863         NameEnd++;
864     }
865 
866     /* Check if the subkeys were empty */
867     if (i == 0)
868     {
869         /* They disappeared in the middle of enumeration */
870         Status = STATUS_OBJECT_NAME_NOT_FOUND;
871         goto Quickie;
872     }
873 
874     /* Return the count and the array of names */
875     *SubKeyList = SubKeys;
876     *SubKeyCount = i;
877     SubKeys = NULL;
878     Status = STATUS_SUCCESS;
879 
880 Quickie:
881     /* On the failure path, free the subkeys if any exist */
882     if (SubKeys)
883     {
884         BlMmFreeHeap(SubKeys);
885     }
886 
887     /* All done, return the result */
888     return Status;
889 }
890 
891 NTSTATUS
BiDeleteKey(_In_ HANDLE KeyHandle)892 BiDeleteKey (
893     _In_ HANDLE KeyHandle
894     )
895 {
896     NTSTATUS Status;
897     PBI_KEY_OBJECT KeyObject;
898     PHHIVE Hive;
899     ULONG SubKeyCount, i;
900     PWCHAR* SubKeyList;
901     HANDLE SubKeyHandle;
902 
903     /* Get the key object and hive */
904     KeyObject = (PBI_KEY_OBJECT)KeyHandle;
905     Hive = &KeyObject->KeyHive->Hive.Hive;
906 
907     /* Make sure the hive is writeable */
908     if (!(KeyObject->KeyHive->Flags & BI_HIVE_WRITEABLE))
909     {
910         return STATUS_MEDIA_WRITE_PROTECTED;
911     }
912 
913     /* Enumerate all of the subkeys */
914     Status = BiEnumerateSubKeys(KeyHandle, &SubKeyList, &SubKeyCount);
915     if ((NT_SUCCESS(Status)) && (SubKeyCount > 0))
916     {
917         /* Loop through each one */
918         for (i = 0; i < SubKeyCount; i++)
919         {
920             /* Open a handle to it */
921             Status = BiOpenKey(KeyHandle, SubKeyList[i], &SubKeyHandle);
922             if (NT_SUCCESS(Status))
923             {
924                 /* Recursively call us to delete it */
925                 Status = BiDeleteKey(SubKeyHandle);
926                 if (Status != STATUS_SUCCESS)
927                 {
928                     /* Close the key on failure */
929                     BiCloseKey(SubKeyHandle);
930                 }
931             }
932         }
933     }
934 
935     /* Check if we had a list of subkeys */
936     if (SubKeyList)
937     {
938         /* Free it */
939         BlMmFreeHeap(SubKeyList);
940     }
941 
942     /* Delete this key cell */
943     Status = CmpFreeKeyByCell(Hive, KeyObject->KeyCell, TRUE);
944     if (NT_SUCCESS(Status))
945     {
946         /* Mark the hive as requiring a flush */
947         KeyObject->KeyHive->Flags |= BI_FLUSH_HIVE;
948         BiCloseKey(KeyHandle);
949     }
950 
951     /* All done */
952     return Status;
953 }
954