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