1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Configuration Manager Library - Registry Validation
5 * COPYRIGHT: Copyright 2022 George Bișoc <george.bisoc@reactos.org>
6 */
7
8 #include "cmlib.h"
9 #define NDEBUG
10 #include <debug.h>
11
12 /* STRUCTURES ****************************************************************/
13
14 typedef struct _CMP_REGISTRY_STACK_WORK_STATE
15 {
16 ULONG ChildCellIndex;
17 HCELL_INDEX Parent;
18 HCELL_INDEX Current;
19 HCELL_INDEX Sibling;
20 } CMP_REGISTRY_STACK_WORK_STATE, *PCMP_REGISTRY_STACK_WORK_STATE;
21
22 /* DEFINES ******************************************************************/
23
24 #define GET_HHIVE(CmHive) (&((CmHive)->Hive))
25 #define GET_HHIVE_ROOT_CELL(Hive) ((Hive)->BaseBlock->RootCell)
26 #define GET_HHIVE_BIN(Hive, StorageIndex, BlockIndex) ((PHBIN)Hive->Storage[StorageIndex].BlockList[BlockIndex].BinAddress)
27 #define GET_CELL_BIN(Bin) ((PHCELL)((PUCHAR)Bin + sizeof(HBIN)))
28
29 #define IS_CELL_VOLATILE(Cell) (HvGetCellType(Cell) == Volatile)
30
31 #if !defined(CMLIB_HOST) && !defined(_BLDR_)
32 extern PCMHIVE CmiVolatileHive;
33 #endif
34
35 #define CMP_PRIOR_STACK 1
36 #define CMP_REGISTRY_MAX_LEVELS_TREE_DEPTH 512
37
38 #define CMP_KEY_SIZE_THRESHOLD 0x45C
39 #define CMP_VOLATILE_LIST_UNINTIALIZED 0xBAADF00D
40
41 /* PRIVATE FUNCTIONS **********************************************************/
42
43 /**
44 * @brief
45 * Validates the lexicographical order between a child
46 * and prior sibling of the said child.
47 *
48 * @param[in] Hive
49 * A pointer to a hive descriptor of which lexicographical
50 * order of keys are to be checked.
51 *
52 * @param[in] Child
53 * A child subkey cell used for lexicographical order
54 * validation checks.
55 *
56 * @param[in] Sibling
57 * A subkey cell which is the prior sibling of the child.
58 * This is used in conjuction with the child to perfrom
59 * lexical order checks.
60 *
61 * @return
62 * Returns TRUE if the order is legal, FALSE otherwise.
63 */
64 static
65 BOOLEAN
CmpValidateLexicalOrder(_In_ PHHIVE Hive,_In_ HCELL_INDEX Child,_In_ HCELL_INDEX Sibling)66 CmpValidateLexicalOrder(
67 _In_ PHHIVE Hive,
68 _In_ HCELL_INDEX Child,
69 _In_ HCELL_INDEX Sibling)
70 {
71 LONG Result;
72 UNICODE_STRING ChildString, SiblingString;
73 PCM_KEY_NODE ChildNode, SiblingNode;
74
75 PAGED_CODE();
76
77 /* Obtain the child node */
78 ChildNode = (PCM_KEY_NODE)HvGetCell(Hive, Child);
79 if (!ChildNode)
80 {
81 /* Couldn't get the child node, bail out */
82 DPRINT1("Failed to get the child node\n");
83 return FALSE;
84 }
85
86 /* Obtain the sibling node */
87 SiblingNode = (PCM_KEY_NODE)HvGetCell(Hive, Sibling);
88 if (!SiblingNode)
89 {
90 /* Couldn't get the sibling node, bail out */
91 DPRINT1("Failed to get the sibling node\n");
92 return FALSE;
93 }
94
95 /* CASE 1: Two distinct non-compressed Unicode names */
96 if ((ChildNode->Flags & KEY_COMP_NAME) == 0 &&
97 (SiblingNode->Flags & KEY_COMP_NAME) == 0)
98 {
99 /* Build the sibling string */
100 SiblingString.Buffer = &(SiblingNode->Name[0]);
101 SiblingString.Length = SiblingNode->NameLength;
102 SiblingString.MaximumLength = SiblingNode->NameLength;
103
104 /* Build the child string */
105 ChildString.Buffer = &(ChildNode->Name[0]);
106 ChildString.Length = ChildNode->NameLength;
107 ChildString.MaximumLength = ChildNode->NameLength;
108
109 Result = RtlCompareUnicodeString(&SiblingString, &ChildString, TRUE);
110 if (Result >= 0)
111 {
112 DPRINT1("The sibling node name is greater or equal to that of the child\n");
113 return FALSE;
114 }
115 }
116
117 /* CASE 2: Both compressed Unicode names */
118 if ((ChildNode->Flags & KEY_COMP_NAME) &&
119 (SiblingNode->Flags & KEY_COMP_NAME))
120 {
121 /* FIXME: Checks for two compressed names not implemented yet */
122 DPRINT("Lexicographical order checks for two compressed names is UNIMPLEMENTED, assume the key is healthy...\n");
123 return TRUE;
124 }
125
126 /* CASE 3: The child name is compressed but the sibling is not */
127 if ((ChildNode->Flags & KEY_COMP_NAME) &&
128 (SiblingNode->Flags & KEY_COMP_NAME) == 0)
129 {
130 SiblingString.Buffer = &(SiblingNode->Name[0]);
131 SiblingString.Length = SiblingNode->NameLength;
132 SiblingString.MaximumLength = SiblingNode->NameLength;
133 Result = CmpCompareCompressedName(&SiblingString,
134 ChildNode->Name,
135 ChildNode->NameLength);
136 if (Result >= 0)
137 {
138 DPRINT1("The sibling node name is greater or equal to that of the compressed child\n");
139 return FALSE;
140 }
141 }
142
143 /* CASE 4: The sibling name is compressed but the child is not */
144 if ((SiblingNode->Flags & KEY_COMP_NAME) &&
145 (ChildNode->Flags & KEY_COMP_NAME) == 0)
146 {
147 ChildString.Buffer = &(ChildNode->Name[0]);
148 ChildString.Length = ChildNode->NameLength;
149 ChildString.MaximumLength = ChildNode->NameLength;
150 Result = CmpCompareCompressedName(&ChildString,
151 SiblingNode->Name,
152 SiblingNode->NameLength);
153 if (Result <= 0)
154 {
155 DPRINT1("The compressed sibling node name is lesser or equal to that of the child\n");
156 return FALSE;
157 }
158 }
159
160 /*
161 * One of the cases above has met the conditions
162 * successfully, the lexicographical order is legal.
163 */
164 return TRUE;
165 }
166
167 /**
168 * @brief
169 * Validates the class of a given key cell.
170 *
171 * @param[in] Hive
172 * A pointer to a hive descriptor of which
173 * the registry call is to be validated.
174 *
175 * @param[in] CurrentCell
176 * The current key cell that the class points to.
177 *
178 * @param[in] CellData
179 * A pointer to cell data of the current key cell
180 * that contains the class to be validated.
181 *
182 * @return
183 * Returns CM_CHECK_REGISTRY_GOOD if the class is in good shape.
184 * The same CM status code is returned if the class doesn't
185 * but the class length says otherwise. CM_CHECK_REGISTRY_KEY_CLASS_UNALLOCATED
186 * is returned if the class cell is not allocated.
187 */
188 static
189 CM_CHECK_REGISTRY_STATUS
CmpValidateClass(_In_ PHHIVE Hive,_In_ HCELL_INDEX CurrentCell,_Inout_ PCELL_DATA CellData)190 CmpValidateClass(
191 _In_ PHHIVE Hive,
192 _In_ HCELL_INDEX CurrentCell,
193 _Inout_ PCELL_DATA CellData)
194 {
195 ULONG ClassLength;
196 HCELL_INDEX ClassCell;
197
198 PAGED_CODE();
199
200 ASSERT(CurrentCell != HCELL_NIL);
201 ASSERT(CellData);
202
203 /* Cache the class cell and validate it (if any) */
204 ClassCell = CellData->u.KeyNode.Class;
205 ClassLength = CellData->u.KeyNode.ClassLength;
206 if (ClassLength > 0)
207 {
208 if (ClassCell == HCELL_NIL)
209 {
210 /*
211 * Somebody has freed the class but left the
212 * length as is, reset it.
213 */
214 DPRINT1("The key node class is NIL but the class length is not 0, resetting it\n");
215 HvMarkCellDirty(Hive, CurrentCell, FALSE);
216 CellData->u.KeyNode.ClassLength = 0;
217 return CM_CHECK_REGISTRY_GOOD;
218 }
219
220 if (!HvIsCellAllocated(Hive, ClassCell))
221 {
222 DPRINT1("The key class is not allocated\n");
223 return CM_CHECK_REGISTRY_KEY_CLASS_UNALLOCATED;
224 }
225 }
226
227 return CM_CHECK_REGISTRY_GOOD;
228 }
229
230 /**
231 * @brief
232 * Validates each value in the list by count. A
233 * value that is damaged gets removed from the list.
234 * This routine performs self-healing process in
235 * this case.
236 *
237 * @param[in] Hive
238 * A pointer to a hive descriptor of which a list
239 * of registry values is to be validated.
240 *
241 * @param[in] CurrentCell
242 * The current key cell that the value list points to.
243 *
244 * @param[in] ListCount
245 * The list count that describes the actual number of
246 * values in the list.
247 *
248 * @param[in] ValueListData
249 * A pointer to cell data of the current key cell
250 * that contains the value list to be validated.
251 *
252 * @param[out] ValuesRemoved
253 * When the function finishes doing its operations,
254 * this parameter contains the amount of removed values
255 * from the list. A value of 0 indicates no values have
256 * been removed (which that would imply a self-healing
257 * process of the value list has occurred).
258 *
259 * @param[in] FixHive
260 * If set to TRUE, the target hive will be fixed.
261 *
262 * @return
263 * Returns CM_CHECK_REGISTRY_GOOD if the value list is
264 * sane. CM_CHECK_REGISTRY_VALUE_CELL_NIL is returned
265 * if a certain value cell is HCELL_NIL at specific
266 * count index. CM_CHECK_REGISTRY_VALUE_CELL_UNALLOCATED is
267 * returned if a certain value cell is unallocated at specific
268 * count index. CM_CHECK_REGISTRY_VALUE_CELL_DATA_NOT_FOUND is
269 * returned if cell data could not be mapped from the value cell,
270 * the value list is totally torn apart in this case.
271 * CM_CHECK_REGISTRY_VALUE_CELL_SIZE_NOT_SANE is returned if the
272 * value's size is bogus. CM_CHECK_REGISTRY_CORRUPT_VALUE_DATA
273 * is returned if the data inside the value is HCELL_NIL.
274 * CM_CHECK_REGISTRY_DATA_CELL_NOT_ALLOCATED is returned if the
275 * data cell inside the value is not allocated.
276 * CM_CHECK_REGISTRY_BAD_KEY_VALUE_SIGNATURE is returned if the
277 * value's signature is not valid.
278 */
279 static
280 CM_CHECK_REGISTRY_STATUS
CmpValidateValueListByCount(_In_ PHHIVE Hive,_In_ HCELL_INDEX CurrentCell,_In_ ULONG ListCount,_In_ PCELL_DATA ValueListData,_Out_ PULONG ValuesRemoved,_In_ BOOLEAN FixHive)281 CmpValidateValueListByCount(
282 _In_ PHHIVE Hive,
283 _In_ HCELL_INDEX CurrentCell,
284 _In_ ULONG ListCount,
285 _In_ PCELL_DATA ValueListData,
286 _Out_ PULONG ValuesRemoved,
287 _In_ BOOLEAN FixHive)
288 {
289 ULONG ValueDataSize;
290 ULONG ListCountIndex;
291 ULONG DataSize;
292 HCELL_INDEX DataCell;
293 HCELL_INDEX ValueCell;
294 PCELL_DATA ValueData;
295 ULONG ValueNameLength, TotalValueNameLength;
296
297 PAGED_CODE();
298
299 ASSERT(ValueListData);
300 ASSERT(ListCount != 0);
301
302 /* Assume we haven't removed any value counts for now */
303 *ValuesRemoved = 0;
304
305 /*
306 * Begin looping each value in the list and
307 * validate it accordingly.
308 */
309 ListCountIndex = 0;
310 while (ListCountIndex < ListCount)
311 {
312 ValueCell = ValueListData->u.KeyList[ListCountIndex];
313 if (ValueCell == HCELL_NIL)
314 {
315 if (!CmpRepairValueListCount(Hive,
316 CurrentCell,
317 ListCountIndex,
318 ValueListData,
319 FixHive))
320 {
321 DPRINT1("The value cell is NIL (at index %u, list count %u)\n",
322 ListCountIndex, ListCount);
323 return CM_CHECK_REGISTRY_VALUE_CELL_NIL;
324 }
325
326 /* Decrease the list count and go to the next value */
327 ListCount--;
328 *ValuesRemoved++;
329 DPRINT1("Damaged value removed, continuing with the next value...\n");
330 continue;
331 }
332
333 if (!HvIsCellAllocated(Hive, ValueCell))
334 {
335 if (!CmpRepairValueListCount(Hive,
336 CurrentCell,
337 ListCountIndex,
338 ValueListData,
339 FixHive))
340 {
341 DPRINT1("The value cell is not allocated (at index %u, list count %u)\n",
342 ListCountIndex, ListCount);
343 return CM_CHECK_REGISTRY_VALUE_CELL_UNALLOCATED;
344 }
345
346 /* Decrease the list count and go to the next value */
347 ListCount--;
348 *ValuesRemoved++;
349 DPRINT1("Damaged value removed, continuing with the next value...\n");
350 continue;
351 }
352
353 /* Obtain a cell data from this value */
354 ValueData = (PCELL_DATA)HvGetCell(Hive, ValueCell);
355 if (!ValueData)
356 {
357 DPRINT1("Cell data of the value cell not found (at index %u, value count %u)\n",
358 ListCountIndex, ListCount);
359 return CM_CHECK_REGISTRY_VALUE_CELL_DATA_NOT_FOUND;
360 }
361
362 /* Check that the value size is sane */
363 ValueDataSize = HvGetCellSize(Hive, ValueData);
364 ValueNameLength = ValueData->u.KeyValue.NameLength;
365 TotalValueNameLength = ValueNameLength + FIELD_OFFSET(CM_KEY_VALUE, Name);
366 if (TotalValueNameLength > ValueDataSize)
367 {
368 if (!CmpRepairValueListCount(Hive,
369 CurrentCell,
370 ListCountIndex,
371 ValueListData,
372 FixHive))
373 {
374 DPRINT1("The total size is bigger than the actual cell size (total size %u, cell size %u, at index %u)\n",
375 TotalValueNameLength, ValueDataSize, ListCountIndex);
376 return CM_CHECK_REGISTRY_VALUE_CELL_SIZE_NOT_SANE;
377 }
378
379 /* Decrease the list count and go to the next value */
380 ListCount--;
381 *ValuesRemoved++;
382 DPRINT1("Damaged value removed, continuing with the next value...\n");
383 continue;
384 }
385
386 /*
387 * The value cell has a sane size. The last thing
388 * to validate is the actual data of the value cell.
389 * That is, we want that the data itself and length
390 * are consistent. Technically speaking, value keys
391 * that are small are directly located in the value
392 * cell and it's built-in, in other words, the data
393 * is immediately present in the cell so we don't have
394 * to bother validating them since they're alright on
395 * their own. This can't be said the same about normal
396 * values though.
397 */
398 DataCell = ValueData->u.KeyValue.Data;
399 if (!CmpIsKeyValueSmall(&DataSize, ValueData->u.KeyValue.DataLength))
400 {
401 /* Validate the actual data based on size */
402 if (DataSize == 0)
403 {
404 if (DataCell != HCELL_NIL)
405 {
406 if (!CmpRepairValueListCount(Hive,
407 CurrentCell,
408 ListCountIndex,
409 ValueListData,
410 FixHive))
411 {
412 DPRINT1("The data is not NIL on a 0 length, data is corrupt\n");
413 return CM_CHECK_REGISTRY_CORRUPT_VALUE_DATA;
414 }
415
416 /* Decrease the list count and go to the next value */
417 ListCount--;
418 *ValuesRemoved++;
419 DPRINT1("Damaged value removed, continuing with the next value...\n");
420 continue;
421 }
422 }
423 else
424 {
425 if (!HvIsCellAllocated(Hive, DataCell))
426 {
427 if (!CmpRepairValueListCount(Hive,
428 CurrentCell,
429 ListCountIndex,
430 ValueListData,
431 FixHive))
432 {
433 DPRINT1("The data is not NIL on a 0 length, data is corrupt\n");
434 return CM_CHECK_REGISTRY_DATA_CELL_NOT_ALLOCATED;
435 }
436
437 /* Decrease the list count and go to the next value */
438 ListCount--;
439 *ValuesRemoved++;
440 DPRINT1("Damaged value removed, continuing with the next value...\n");
441 continue;
442 }
443 }
444
445 /* FIXME: Big values not supported yet */
446 ASSERT_VALUE_BIG(Hive, DataSize);
447 }
448
449 /* Is the signature valid? */
450 if (ValueData->u.KeyValue.Signature != CM_KEY_VALUE_SIGNATURE)
451 {
452 if (!CmpRepairValueListCount(Hive,
453 CurrentCell,
454 ListCountIndex,
455 ValueListData,
456 FixHive))
457 {
458 DPRINT1("The key value signature is invalid\n");
459 return CM_CHECK_REGISTRY_BAD_KEY_VALUE_SIGNATURE;
460 }
461
462 /* Decrease the list count and go to the next value */
463 ListCount--;
464 *ValuesRemoved++;
465 DPRINT1("Damaged value removed, continuing with the next value...\n");
466 continue;
467 }
468
469 /* Advance to the next value */
470 ListCountIndex++;
471 }
472
473 return CM_CHECK_REGISTRY_GOOD;
474 }
475
476 /**
477 * @brief
478 * Validates the value list of a key. If the list
479 * is damaged due to corruption, the whole list
480 * is expunged. This function performs self-healing
481 * procedures in this case.
482 *
483 * @param[in] Hive
484 * A pointer to a hive descriptor of which a list of
485 * registry values is to be validated.
486 *
487 * @param[in] CurrentCell
488 * The current key cell that the value list points to.
489 *
490 * @param[in] CellData
491 * The cell data of the current cell of which the value
492 * list comes from.
493 *
494 * @param[in] FixHive
495 * If set to TRUE, the target hive will be fixed.
496 *
497 * @return
498 * Returns CM_CHECK_REGISTRY_GOOD if the value list is
499 * sane. CM_CHECK_REGISTRY_VALUE_LIST_UNALLOCATED is returned
500 * if the value list cell is not allocated. CM_CHECK_REGISTRY_VALUE_LIST_DATA_NOT_FOUND
501 * is returned if cell data could not be mapped from the value
502 * list cell. CM_CHECK_REGISTRY_VALUE_LIST_SIZE_NOT_SANE is returned
503 * if the size of the value list is bogus. A failure CM status code
504 * is returned otherwise.
505 */
506 static
507 CM_CHECK_REGISTRY_STATUS
CmpValidateValueList(_In_ PHHIVE Hive,_In_ HCELL_INDEX CurrentCell,_Inout_ PCELL_DATA CellData,_In_ BOOLEAN FixHive)508 CmpValidateValueList(
509 _In_ PHHIVE Hive,
510 _In_ HCELL_INDEX CurrentCell,
511 _Inout_ PCELL_DATA CellData,
512 _In_ BOOLEAN FixHive)
513 {
514 CM_CHECK_REGISTRY_STATUS CmStatusCode;
515 ULONG TotalValueLength, ValueSize;
516 ULONG ValueListCount;
517 ULONG ValuesRemoved;
518 HCELL_INDEX ValueListCell;
519 PCELL_DATA ValueListData;
520
521 PAGED_CODE();
522
523 ASSERT(CurrentCell != HCELL_NIL);
524 ASSERT(CellData);
525
526 /* Cache the value list and validate it */
527 ValueListCell = CellData->u.KeyNode.ValueList.List;
528 ValueListCount = CellData->u.KeyNode.ValueList.Count;
529 if (ValueListCount > 0)
530 {
531 if (!HvIsCellAllocated(Hive, ValueListCell))
532 {
533 DPRINT1("The value list is not allocated\n");
534 return CM_CHECK_REGISTRY_VALUE_LIST_UNALLOCATED;
535 }
536
537 /* Obtain cell data from the value list cell */
538 ValueListData = (PCELL_DATA)HvGetCell(Hive, ValueListCell);
539 if (!ValueListData)
540 {
541 DPRINT1("Could not get cell data from the value list\n");
542 return CM_CHECK_REGISTRY_VALUE_LIST_DATA_NOT_FOUND;
543 }
544
545 /*
546 * Cache the value size and total length and
547 * assert ourselves this is a sane value list
548 * to begin with.
549 */
550 ValueSize = HvGetCellSize(Hive, ValueListData);
551 TotalValueLength = ValueListCount * sizeof(HCELL_INDEX);
552 if (TotalValueLength > ValueSize)
553 {
554 DPRINT1("The value list is bigger than the cell (value list size %u, cell size %u)\n",
555 TotalValueLength, ValueSize);
556 return CM_CHECK_REGISTRY_VALUE_LIST_SIZE_NOT_SANE;
557 }
558
559 /*
560 * The value list is sane, now we would
561 * need to validate the actual list
562 * by its count.
563 */
564 CmStatusCode = CmpValidateValueListByCount(Hive,
565 CurrentCell,
566 ValueListCount,
567 ValueListData,
568 &ValuesRemoved,
569 FixHive);
570 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode))
571 {
572 DPRINT1("One of the values is corrupt and couldn't be repaired\n");
573 return CmStatusCode;
574 }
575
576 /* Log how much values have been removed */
577 if (ValuesRemoved > 0)
578 {
579 DPRINT1("%u values removed in the list\n", ValuesRemoved);
580 }
581 }
582
583 return CM_CHECK_REGISTRY_GOOD;
584 }
585
586 /**
587 * @brief
588 * Validates the subkeys list of a key. If the list is
589 * damaged from corruption, the function can either
590 * salvage this list or purge the whole of it. The
591 * function performs different validation steps for
592 * different storage types.
593 *
594 * @param[in] Hive
595 * A pointer to a hive descriptor of which a list of
596 * subkeys is to be validated.
597 *
598 * @param[in] CurrentCell
599 * The current key cell that the subkeys list points to.
600 *
601 * @param[in] CellData
602 * The cell data of the current cell of which the subkeys
603 * list comes from.
604 *
605 * @param[in] FixHive
606 * If set to TRUE, the target hive will be fixed.
607 *
608 * @param[out] DoRepair
609 * A pointer to a boolean value set up by the function itself.
610 * The function automatically sets this to FALSE indicating
611 * that repairs can't be done onto the list itself. If the
612 * list can be salvaged, then the function sets this to TRUE.
613 * See Remarks for further information.
614 *
615 * @return
616 * Returns CM_CHECK_REGISTRY_GOOD if the subkeys list is in
617 * perfect shape. CM_CHECK_REGISTRY_STABLE_KEYS_ON_VOLATILE is
618 * returned if the volatile storage has stable data which
619 * that should not happen (this is only for the case of volatile
620 * cells). CM_CHECK_REGISTRY_SUBKEYS_LIST_UNALLOCATED is returned
621 * if the subkeys list cell is not allocated. CM_CHECK_REGISTRY_CORRUPT_SUBKEYS_INDEX
622 * is returned if a key index could not be mapped from the subkeys
623 * list cell. CM_CHECK_REGISTRY_BAD_SUBKEY_COUNT is returned if
624 * the key index is a leaf and the subkeys count doesn't match up
625 * with that of the leaf. CM_CHECK_REGISTRY_KEY_INDEX_CELL_UNALLOCATED is
626 * returned if the key index cell at the specific index in the list of
627 * the index is not allocated. CM_CHECK_REGISTRY_CORRUPT_LEAF_ON_ROOT is
628 * returned if a leaf could not be mapped from an index.
629 * CM_CHECK_REGISTRY_CORRUPT_LEAF_SIGNATURE is returned if the leaf has
630 * an invalid signature. CM_CHECK_REGISTRY_CORRUPT_KEY_INDEX_SIGNATURE
631 * is returned if the key index has an invalid signature, that is, it's
632 * not a leaf nor a root.
633 *
634 * @remarks
635 * Deep subkeys list healing can be done in specific cases where only
636 * a subkey doesn't actually affect the key itself. The function will
637 * mark the subkeys list as repairable by setting DoRepair parameter
638 * to TRUE and the caller is responsible to heal the key by purging
639 * the whole subkeys list. If the damage is so bad that there's
640 * possibility the key itself is even damaged, no healing is done.
641 */
642 static
643 CM_CHECK_REGISTRY_STATUS
CmpValidateSubKeyList(_In_ PHHIVE Hive,_In_ HCELL_INDEX CurrentCell,_Inout_ PCELL_DATA CellData,_In_ BOOLEAN FixHive,_Out_ PBOOLEAN DoRepair)644 CmpValidateSubKeyList(
645 _In_ PHHIVE Hive,
646 _In_ HCELL_INDEX CurrentCell,
647 _Inout_ PCELL_DATA CellData,
648 _In_ BOOLEAN FixHive,
649 _Out_ PBOOLEAN DoRepair)
650 {
651 ULONG SubKeyCounts;
652 HCELL_INDEX KeyIndexCell, SubKeysListCell;
653 PCM_KEY_INDEX RootKeyIndex, LeafKeyIndex;
654 ULONG RootIndex;
655 ULONG TotalLeafCount;
656
657 PAGED_CODE();
658
659 ASSERT(CurrentCell != HCELL_NIL);
660 ASSERT(CellData);
661
662 RootKeyIndex = NULL;
663 LeafKeyIndex = NULL;
664 TotalLeafCount = 0;
665
666 /*
667 * Assume for now that the caller should not
668 * do any kind of repairs on the subkeys list,
669 * unless explicitly given the consent by us.
670 */
671 *DoRepair = FALSE;
672
673 /*
674 * For volatile keys they have data that can
675 * fluctuate and change on the fly so there's
676 * pretty much nothing that we can validate those.
677 * But still, we would want that the volatile key
678 * is not damaged by external factors, like e.g.,
679 * having stable keys on a volatile space.
680 */
681 if (IS_CELL_VOLATILE(CurrentCell))
682 {
683 if (CellData->u.KeyNode.SubKeyCounts[Stable] != 0)
684 {
685 DPRINT1("The volatile key has stable subkeys\n");
686 return CM_CHECK_REGISTRY_STABLE_KEYS_ON_VOLATILE;
687 }
688
689 return CM_CHECK_REGISTRY_GOOD;
690 }
691
692 /*
693 * This is not a volatile key, cache the subkeys list
694 * and validate it.
695 */
696 SubKeysListCell = CellData->u.KeyNode.SubKeyLists[Stable];
697 SubKeyCounts = CellData->u.KeyNode.SubKeyCounts[Stable];
698 if (SubKeyCounts > 0)
699 {
700 if (!HvIsCellAllocated(Hive, SubKeysListCell))
701 {
702 DPRINT1("The subkeys list cell is not allocated\n");
703 *DoRepair = TRUE;
704 return CM_CHECK_REGISTRY_SUBKEYS_LIST_UNALLOCATED;
705 }
706
707 /* Obtain a root index and validate it */
708 RootKeyIndex = (PCM_KEY_INDEX)HvGetCell(Hive, SubKeysListCell);
709 if (!RootKeyIndex)
710 {
711 DPRINT1("Could not get the root key index of the subkeys list cell\n");
712 return CM_CHECK_REGISTRY_CORRUPT_SUBKEYS_INDEX;
713 }
714
715 /*
716 * For simple, fast and hashed leaves we would want
717 * that the corresponding root index count matches with
718 * that of the subkey counts itself. If this is not the
719 * case we can isolate this problem and fix the count.
720 */
721 if (RootKeyIndex->Signature == CM_KEY_INDEX_LEAF ||
722 RootKeyIndex->Signature == CM_KEY_FAST_LEAF ||
723 RootKeyIndex->Signature == CM_KEY_HASH_LEAF)
724 {
725 if (SubKeyCounts != RootKeyIndex->Count)
726 {
727 if (!CmpRepairSubKeyCounts(Hive,
728 CurrentCell,
729 RootKeyIndex->Count,
730 CellData,
731 FixHive))
732 {
733 DPRINT1("The subkeys list has invalid count (subkeys count %u, root key index count %u)\n",
734 SubKeyCounts, RootKeyIndex->Count);
735 return CM_CHECK_REGISTRY_BAD_SUBKEY_COUNT;
736 }
737 }
738
739 return CM_CHECK_REGISTRY_GOOD;
740 }
741
742 /*
743 * The root index is not a leaf, check if the index
744 * is an actual root then.
745 */
746 if (RootKeyIndex->Signature == CM_KEY_INDEX_ROOT)
747 {
748 /*
749 * For the root we have to loop each leaf
750 * from it and increase the total leaf count
751 * in the root after we determined the validity
752 * of a leaf. This way we can see if the subcount
753 * matches with that of the subkeys list count.
754 */
755 for (RootIndex = 0; RootIndex < RootKeyIndex->Count; RootIndex++)
756 {
757 KeyIndexCell = RootKeyIndex->List[RootIndex];
758 if (!HvIsCellAllocated(Hive, KeyIndexCell))
759 {
760 DPRINT1("The key index cell is not allocated at index %u\n", RootIndex);
761 *DoRepair = TRUE;
762 return CM_CHECK_REGISTRY_KEY_INDEX_CELL_UNALLOCATED;
763 }
764
765 /* Obtain a leaf from the root */
766 LeafKeyIndex = (PCM_KEY_INDEX)HvGetCell(Hive, KeyIndexCell);
767 if (!LeafKeyIndex)
768 {
769 DPRINT1("The root key index's signature is invalid!\n");
770 return CM_CHECK_REGISTRY_CORRUPT_LEAF_ON_ROOT;
771 }
772
773 /* Check that the leaf has valid signature */
774 if (LeafKeyIndex->Signature != CM_KEY_INDEX_LEAF &&
775 LeafKeyIndex->Signature != CM_KEY_FAST_LEAF &&
776 LeafKeyIndex->Signature != CM_KEY_HASH_LEAF)
777 {
778 DPRINT1("The leaf's signature is invalid!\n");
779 *DoRepair = TRUE;
780 return CM_CHECK_REGISTRY_CORRUPT_LEAF_SIGNATURE;
781 }
782
783 /* Add up the count of the leaf */
784 TotalLeafCount += LeafKeyIndex->Count;
785 }
786
787 /*
788 * We have built up the total leaf count,
789 * we have to determine this count is exactly
790 * the same as the subkeys list count. Otherwise
791 * just fix it.
792 */
793 if (SubKeyCounts != TotalLeafCount)
794 {
795 if (!CmpRepairSubKeyCounts(Hive,
796 CurrentCell,
797 TotalLeafCount,
798 CellData,
799 FixHive))
800 {
801 DPRINT1("The subkeys list has invalid count (subkeys count %u, total leaf count %u)\n",
802 SubKeyCounts, TotalLeafCount);
803 return CM_CHECK_REGISTRY_BAD_SUBKEY_COUNT;
804 }
805 }
806
807 return CM_CHECK_REGISTRY_GOOD;
808 }
809
810 /*
811 * None of the valid signatures match with that of
812 * the root key index. By definition, the whole subkey
813 * list is total toast.
814 */
815 DPRINT1("The root key index's signature is invalid\n");
816 *DoRepair = TRUE;
817 return CM_CHECK_REGISTRY_CORRUPT_KEY_INDEX_SIGNATURE;
818 }
819
820 /* If we reach here then this key has no subkeys */
821 return CM_CHECK_REGISTRY_GOOD;
822 }
823
824 /**
825 * @brief
826 * Purges the volatile storage of a registry
827 * hive. This operation is done mainly during
828 * the bootup of the system.
829 *
830 * @param[in] Hive
831 * A pointer to a hive descriptor of which volatiles
832 * are to be purged.
833 *
834 * @param[in] CurrentCell
835 * The current key cell that the volatile storage of
836 * the hive points to.
837 *
838 * @param[in] CellData
839 * The cell data of the current cell of which the volatile
840 * subkeys storage comes from.
841 *
842 * @param[in] Flags
843 * A bit mask flag that is used to influence how is the
844 * purging operation to be done. See CmCheckRegistry documentation
845 * below for more information.
846 */
847 static
848 VOID
CmpPurgeVolatiles(_In_ PHHIVE Hive,_In_ HCELL_INDEX CurrentCell,_Inout_ PCELL_DATA CellData,_In_ ULONG Flags)849 CmpPurgeVolatiles(
850 _In_ PHHIVE Hive,
851 _In_ HCELL_INDEX CurrentCell,
852 _Inout_ PCELL_DATA CellData,
853 _In_ ULONG Flags)
854 {
855 PAGED_CODE();
856
857 ASSERT(CellData);
858
859 /* Did the caller ask to purge volatiles? */
860 if (((Flags & CM_CHECK_REGISTRY_PURGE_VOLATILES) ||
861 (Flags & CM_CHECK_REGISTRY_BOOTLOADER_PURGE_VOLATILES)) &&
862 (CellData->u.KeyNode.SubKeyCounts[Volatile] != 0))
863 {
864 /*
865 * OK, the caller wants them cleaned from this
866 * hive. For XP Beta 1 or newer hives, we unintialize
867 * the whole volatile subkeys list. For older hives,
868 * we just do a cleanup.
869 */
870 #if !defined(_BLDR_)
871 HvMarkCellDirty(Hive, CurrentCell, FALSE);
872 #endif
873 if ((Flags & CM_CHECK_REGISTRY_BOOTLOADER_PURGE_VOLATILES) &&
874 (Hive->Version >= HSYS_WHISTLER_BETA1))
875 {
876 CellData->u.KeyNode.SubKeyLists[Volatile] = CMP_VOLATILE_LIST_UNINTIALIZED;
877 }
878 else
879 {
880 CellData->u.KeyNode.SubKeyLists[Volatile] = HCELL_NIL;
881 }
882
883 /* Clear the count */
884 CellData->u.KeyNode.SubKeyCounts[Volatile] = 0;
885 }
886 }
887
888 /**
889 * @brief
890 * Validates the key cell, ensuring that
891 * the key in the registry is valid and not corrupted.
892 *
893 * @param[in] Hive
894 * A pointer to a hive descriptor of the registry where
895 * the key is to be validated.
896 *
897 * @param[in] SecurityDefaulted
898 * If the caller sets this to TRUE, this indicates the
899 * hive has its security property defaulted due to
900 * heal recovery of the said security. If the caller
901 * sets this to FALSE, the hive comes with its own
902 * security details. This parameter is currently unused.
903 *
904 * @param[in] ParentCell
905 * The parent key cell that comes before the current cell.
906 * This parameter can be HCELL_NIL if the first cell is
907 * the root cell which is the parent of its own.
908 *
909 * @param[in] CurrentCell
910 * The current child key cell that is to be validated.
911 *
912 * @param[in] Flags
913 * A bit mask flag that is used to influence how is the
914 * purging operation of volatile keys in the volatile storage
915 * to be done. See CmCheckRegistry documentation below for more
916 * information.
917 *
918 * @param[in] FixHive
919 * If set to TRUE, the target hive will be fixed.
920 *
921 * @return
922 * Returns CM_CHECK_REGISTRY_GOOD if the key that has been validated
923 * is valid and not corrupted. CM_CHECK_REGISTRY_KEY_CELL_NOT_ALLOCATED is
924 * returned if the key cell is not allocated. CM_CHECK_REGISTRY_CELL_DATA_NOT_FOUND
925 * is returned if cell data could not be mapped from the key cell.
926 * CM_CHECK_REGISTRY_CELL_SIZE_NOT_SANE is returned if the key cell
927 * has an abnormal size that is above the trehshold the validation checks
928 * can permit. CM_CHECK_REGISTRY_KEY_NAME_LENGTH_ZERO is returned if the
929 * name length of the key node is 0, meaning that the key has no name.
930 * CM_CHECK_REGISTRY_KEY_TOO_BIG_THAN_CELL is returned if the key is too
931 * big than the cell itself. CM_CHECK_REGISTRY_BAD_KEY_NODE_PARENT is
932 * returned if the parent node of the key is incosistent and it couldn't
933 * be fixed. CM_CHECK_REGISTRY_BAD_KEY_NODE_SIGNATURE is returned if
934 * the signature of the key node is corrupt and it couldn't be fixed.
935 * A failure CM status code is returned otherwise.
936 */
937 static
938 CM_CHECK_REGISTRY_STATUS
CmpValidateKey(_In_ PHHIVE Hive,_In_ BOOLEAN SecurityDefaulted,_In_ HCELL_INDEX ParentCell,_In_ HCELL_INDEX CurrentCell,_In_ ULONG Flags,_In_ BOOLEAN FixHive)939 CmpValidateKey(
940 _In_ PHHIVE Hive,
941 _In_ BOOLEAN SecurityDefaulted,
942 _In_ HCELL_INDEX ParentCell,
943 _In_ HCELL_INDEX CurrentCell,
944 _In_ ULONG Flags,
945 _In_ BOOLEAN FixHive)
946 {
947 CM_CHECK_REGISTRY_STATUS CmStatusCode;
948 PCELL_DATA CellData;
949 ULONG CellSize;
950 BOOLEAN DoSubkeysRepair;
951 ULONG TotalKeyNameLength, NameLength;
952
953 PAGED_CODE();
954
955 /* The current key cell mustn't be NIL here! */
956 ASSERT(CurrentCell != HCELL_NIL);
957
958 /* TODO: To be removed once we support security caching in Cm */
959 UNREFERENCED_PARAMETER(SecurityDefaulted);
960
961 /*
962 * We must ensure that the key cell is
963 * allocated in the first place before
964 * we go further.
965 */
966 if (!HvIsCellAllocated(Hive, CurrentCell))
967 {
968 DPRINT1("The key cell is not allocated\n");
969 return CM_CHECK_REGISTRY_KEY_CELL_NOT_ALLOCATED;
970 }
971
972 /* Obtain cell data from it */
973 CellData = (PCELL_DATA)HvGetCell(Hive, CurrentCell);
974 if (!CellData)
975 {
976 DPRINT1("Could not get cell data from the cell\n");
977 return CM_CHECK_REGISTRY_CELL_DATA_NOT_FOUND;
978 }
979
980 /* Get the size of this cell and validate its size */
981 CellSize = HvGetCellSize(Hive, CellData);
982 if (CellSize > CMP_KEY_SIZE_THRESHOLD)
983 {
984 DPRINT1("The cell size is above the threshold size (size %u)\n", CellSize);
985 return CM_CHECK_REGISTRY_CELL_SIZE_NOT_SANE;
986 }
987
988 /*
989 * The cell size is OK but we must ensure
990 * the key is not bigger than the container
991 * of the cell.
992 */
993 NameLength = CellData->u.KeyNode.NameLength;
994 if (NameLength == 0)
995 {
996 DPRINT1("The key node name length is 0!\n");
997 return CM_CHECK_REGISTRY_KEY_NAME_LENGTH_ZERO;
998 }
999
1000 TotalKeyNameLength = NameLength + FIELD_OFFSET(CM_KEY_NODE, Name);
1001 if (TotalKeyNameLength > CellSize)
1002 {
1003 DPRINT1("The key is too big than the cell (key size %u, cell size %u)\n",
1004 TotalKeyNameLength, CellSize);
1005 return CM_CHECK_REGISTRY_KEY_TOO_BIG_THAN_CELL;
1006 }
1007
1008 /* Is the parent cell consistent? */
1009 if (ParentCell != HCELL_NIL &&
1010 ParentCell != CellData->u.KeyNode.Parent)
1011 {
1012 if (!CmpRepairParentNode(Hive,
1013 CurrentCell,
1014 ParentCell,
1015 CellData,
1016 FixHive))
1017 {
1018 DPRINT1("The parent key node doesn't point to the actual parent\n");
1019 return CM_CHECK_REGISTRY_BAD_KEY_NODE_PARENT;
1020 }
1021 }
1022
1023 /* Is the key node signature valid? */
1024 if (CellData->u.KeyNode.Signature != CM_KEY_NODE_SIGNATURE)
1025 {
1026 if (!CmpRepairKeyNodeSignature(Hive,
1027 CurrentCell,
1028 CellData,
1029 FixHive))
1030 {
1031 DPRINT1("The parent key node signature is not valid\n");
1032 return CM_CHECK_REGISTRY_BAD_KEY_NODE_SIGNATURE;
1033 }
1034 }
1035
1036 /*
1037 * FIXME: Security cell checks have to be implemented here
1038 * once we properly and reliably implement security caching
1039 * in the kernel.
1040 */
1041
1042 /* Validate the class */
1043 CmStatusCode = CmpValidateClass(Hive, CurrentCell, CellData);
1044 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode))
1045 {
1046 if (!CmpRepairClassOfNodeKey(Hive,
1047 CurrentCell,
1048 CellData,
1049 FixHive))
1050 {
1051 DPRINT1("Failed to repair the hive, the cell class is not valid\n");
1052 return CmStatusCode;
1053 }
1054 }
1055
1056 /* Validate the value list */
1057 CmStatusCode = CmpValidateValueList(Hive, CurrentCell, CellData, FixHive);
1058 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode))
1059 {
1060 /*
1061 * It happens that a certain value in the list
1062 * is so bad like we couldn't map a cell data from it
1063 * or the list itself is toast. In such cases what we
1064 * can do here is to do a "value list sacrifice", aka
1065 * purge the whole list.
1066 */
1067 if (!CmpRepairValueList(Hive, CurrentCell, FixHive))
1068 {
1069 DPRINT1("Failed to repair the hive, the value list is corrupt\n");
1070 return CmStatusCode;
1071 }
1072 }
1073
1074 /* Validate the subkeys list */
1075 CmStatusCode = CmpValidateSubKeyList(Hive, CurrentCell, CellData, FixHive, &DoSubkeysRepair);
1076 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode))
1077 {
1078 /*
1079 * The subkeys list is in trouble. Worse when the actual
1080 * subkey list is so severed this key is also kaput on itself.
1081 */
1082 if (!DoSubkeysRepair)
1083 {
1084 DPRINT1("The subkeys list is totally corrupt, can't repair\n");
1085 return CmStatusCode;
1086 }
1087
1088 /*
1089 * OK, there's still some salvation for this key.
1090 * Purge the whole subkeys list in order to fix it.
1091 */
1092 if (!CmpRepairSubKeyList(Hive,
1093 CurrentCell,
1094 CellData,
1095 FixHive))
1096 {
1097 DPRINT1("Failed to repair the hive, the subkeys list is corrupt!\n");
1098 return CmStatusCode;
1099 }
1100 }
1101
1102 /* Purge volatile data if needed */
1103 CmpPurgeVolatiles(Hive, CurrentCell, CellData, Flags);
1104 return CM_CHECK_REGISTRY_GOOD;
1105 }
1106
1107 /**
1108 * @brief
1109 * Performs deep checking of the registry by walking
1110 * down the registry tree using a stack based pool.
1111 * This function is the guts of CmCheckRegistry.
1112 *
1113 * @param[in] Hive
1114 * A pointer to a hive descriptor of the registry where
1115 * the validation is to be performed.
1116 *
1117 * @param[in] Flags
1118 * Bit mask flag used for volatiles purging. Such
1119 * flags influence on how volatile purging is actually
1120 * done. See CmCheckRegistry documentation for more
1121 * information.
1122 *
1123 * @param[in] SecurityDefaulted
1124 * If the caller sets this to FALSE, the registry hive
1125 * uses its own unique security details. Otherwise
1126 * registry hive has the security details defaulted.
1127 *
1128 * @param[in] FixHive
1129 * If set to TRUE, the target hive will be fixed.
1130 *
1131 * @return
1132 * Returns CM_CHECK_REGISTRY_GOOD is returned if the function
1133 * has successfully performed deep registry checking and
1134 * the registry contents are valid. CM_CHECK_REGISTRY_ALLOCATE_MEM_STACK_FAIL
1135 * is returned if the function has failed to allocate the
1136 * stack work state buffer in memory which is necessary for
1137 * deep checking of the registry. CM_CHECK_REGISTRY_ROOT_CELL_NOT_FOUND
1138 * is returned if no root cell has been found of this hive.
1139 * CM_CHECK_REGISTRY_BAD_LEXICOGRAPHICAL_ORDER is returned if the lexical
1140 * order is not valid. CM_CHECK_REGISTRY_NODE_NOT_FOUND is returned if
1141 * the no key node could be mapped from the key. CM_CHECK_REGISTRY_SUBKEY_NOT_FOUND
1142 * is returned if no subkey child cell could be found. CM_CHECK_REGISTRY_TREE_TOO_MANY_LEVELS
1143 * is returned if we have reached the maximum stack limit which means the registry that
1144 * we have checked is too fat.
1145 */
1146 static
1147 CM_CHECK_REGISTRY_STATUS
CmpValidateRegistryInternal(_In_ PHHIVE Hive,_In_ ULONG Flags,_In_ BOOLEAN SecurityDefaulted,_In_ BOOLEAN FixHive)1148 CmpValidateRegistryInternal(
1149 _In_ PHHIVE Hive,
1150 _In_ ULONG Flags,
1151 _In_ BOOLEAN SecurityDefaulted,
1152 _In_ BOOLEAN FixHive)
1153 {
1154 CM_CHECK_REGISTRY_STATUS CmStatusCode;
1155 PCMP_REGISTRY_STACK_WORK_STATE WorkState;
1156 HCELL_INDEX RootCell, ParentCell, CurrentCell;
1157 HCELL_INDEX ChildSubKeyCell;
1158 PCM_KEY_NODE KeyNode;
1159 ULONG WorkStateLength;
1160 LONG StackDepth;
1161 BOOLEAN AllChildrenChecked;
1162
1163 PAGED_CODE();
1164
1165 ASSERT(Hive);
1166
1167 /*
1168 * Allocate some memory blocks for the stack
1169 * state structure. We'll be using it to walk
1170 * down the registry hive tree in a recursive
1171 * way without worrying that we explode the
1172 * kernel stack in the most gruesome and gross
1173 * ways.
1174 */
1175 WorkStateLength = CMP_REGISTRY_MAX_LEVELS_TREE_DEPTH * sizeof(CMP_REGISTRY_STACK_WORK_STATE);
1176 WorkState = CmpAllocate(WorkStateLength,
1177 TRUE,
1178 TAG_REGISTRY_STACK);
1179 if (!WorkState)
1180 {
1181 DPRINT1("Couldn't allocate memory for registry stack work state\n");
1182 return CM_CHECK_REGISTRY_ALLOCATE_MEM_STACK_FAIL;
1183 }
1184
1185 /* Obtain the root cell of the hive */
1186 RootCell = GET_HHIVE_ROOT_CELL(Hive);
1187 if (RootCell == HCELL_NIL)
1188 {
1189 DPRINT1("Couldn't get the root cell of the hive\n");
1190 CmpFree(WorkState, WorkStateLength);
1191 return CM_CHECK_REGISTRY_ROOT_CELL_NOT_FOUND;
1192 }
1193
1194 RestartValidation:
1195 /*
1196 * Prepare the stack state and start from
1197 * the root cell. Ensure that the root cell
1198 * itself is OK before we go forward.
1199 */
1200 StackDepth = 0;
1201 WorkState[StackDepth].ChildCellIndex = 0;
1202 WorkState[StackDepth].Current = RootCell;
1203 WorkState[StackDepth].Parent = HCELL_NIL;
1204 WorkState[StackDepth].Sibling = HCELL_NIL;
1205
1206 /*
1207 * As we start checking the root cell which
1208 * is the top element of a registry hive,
1209 * we'll be going to look for child keys
1210 * in the course of walking down the tree.
1211 */
1212 AllChildrenChecked = FALSE;
1213
1214 while (StackDepth >= 0)
1215 {
1216 /* Cache the current and parent cells */
1217 CurrentCell = WorkState[StackDepth].Current;
1218 ParentCell = WorkState[StackDepth].Parent;
1219
1220 /* Do we have still have children to validate? */
1221 if (!AllChildrenChecked)
1222 {
1223 /* Check that the key is OK */
1224 CmStatusCode = CmpValidateKey(Hive,
1225 SecurityDefaulted,
1226 ParentCell,
1227 CurrentCell,
1228 Flags,
1229 FixHive);
1230 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode))
1231 {
1232 /*
1233 * The key cell is damaged. We have to pray and
1234 * hope that this is not the root cell as any
1235 * damage done to the root is catastrophically
1236 * fatal.
1237 */
1238 if (CurrentCell == RootCell)
1239 {
1240 DPRINT1("THE ROOT CELL IS BROKEN\n");
1241 CmpFree(WorkState, WorkStateLength);
1242 return CmStatusCode;
1243 }
1244
1245 /*
1246 * It is not the root, remove the faulting
1247 * damaged cell from the parent so that we
1248 * can heal the hive.
1249 */
1250 if (!CmpRepairParentKey(Hive, CurrentCell, ParentCell, FixHive))
1251 {
1252 DPRINT1("The key is corrupt (current cell 0x%x, parent cell 0x%x)\n",
1253 CurrentCell, ParentCell);
1254 CmpFree(WorkState, WorkStateLength);
1255 return CmStatusCode;
1256 }
1257
1258 /* Damaged cell removed, restart the loop */
1259 DPRINT1("Hive repaired, restarting the validation loop...\n");
1260 goto RestartValidation;
1261 }
1262
1263 /*
1264 * The key is in perfect shape. If we have advanced
1265 * the stack depth then check the lexicographical
1266 * order of the keys as well.
1267 */
1268 if (StackDepth > 0 &&
1269 CM_CHECK_REGISTRY_SUCCESS(CmStatusCode))
1270 {
1271 if (WorkState[StackDepth - CMP_PRIOR_STACK].Sibling != HCELL_NIL)
1272 {
1273 if (!CmpValidateLexicalOrder(Hive,
1274 CurrentCell,
1275 WorkState[StackDepth - CMP_PRIOR_STACK].Sibling))
1276 {
1277 /*
1278 * The lexicographical order is bad,
1279 * attempt to heal the hive.
1280 */
1281 if (!CmpRepairParentKey(Hive, CurrentCell, ParentCell, FixHive))
1282 {
1283 DPRINT1("The lexicographical order is invalid (sibling 0x%x, current cell 0x%x)\n",
1284 CurrentCell, WorkState[StackDepth - CMP_PRIOR_STACK].Sibling);
1285 CmpFree(WorkState, WorkStateLength);
1286 return CM_CHECK_REGISTRY_BAD_LEXICOGRAPHICAL_ORDER;
1287 }
1288
1289 /* Damaged cell removed, restart the loop */
1290 DPRINT1("Hive repaired, restarting the validation loop...\n");
1291 goto RestartValidation;
1292 }
1293 }
1294
1295 /* Assign the prior sibling for upcoming iteration */
1296 WorkState[StackDepth - CMP_PRIOR_STACK].Sibling = CurrentCell;
1297 }
1298 }
1299
1300 /* Obtain a node for this key */
1301 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CurrentCell);
1302 if (!KeyNode)
1303 {
1304 DPRINT1("Couldn't get the node of key (current cell 0x%x)\n", CurrentCell);
1305 CmpFree(WorkState, WorkStateLength);
1306 return CM_CHECK_REGISTRY_NODE_NOT_FOUND;
1307 }
1308
1309 /*
1310 * If we have processed all the children from this
1311 * node then adjust the stack depth work state by
1312 * going back and restart the loop to lookup for
1313 * the rest of the tree. Acknowledge the code path
1314 * above that we checked all the children so that
1315 * we don't have to validate the same subkey again.
1316 */
1317 if (WorkState[StackDepth].ChildCellIndex < KeyNode->SubKeyCounts[Stable])
1318 {
1319 /*
1320 * We have children to process, obtain the
1321 * child subkey in question so that we can
1322 * cache it later for the next key validation.
1323 */
1324 ChildSubKeyCell = CmpFindSubKeyByNumber(Hive, KeyNode, WorkState[StackDepth].ChildCellIndex);
1325 if (ChildSubKeyCell == HCELL_NIL)
1326 {
1327 DPRINT1("Couldn't get the child subkey cell (at stack index %d)\n", StackDepth);
1328 CmpFree(WorkState, WorkStateLength);
1329 return CM_CHECK_REGISTRY_SUBKEY_NOT_FOUND;
1330 }
1331
1332 /*
1333 * As we got the subkey advance the child index as
1334 * well as the stack depth work state for the next
1335 * key validation. However we must ensure since
1336 * we're advancing the stack depth that we don't
1337 * go over the maximum tree level depth. A registry
1338 * tree can be at maximum 512 levels deep.
1339 *
1340 * For more information see https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits.
1341 */
1342 WorkState[StackDepth].ChildCellIndex++;
1343 StackDepth++;
1344 if (StackDepth >= CMP_REGISTRY_MAX_LEVELS_TREE_DEPTH - 1)
1345 {
1346 /*
1347 * This registry has so many levels it's
1348 * so fat. We don't want to explode our
1349 * kernel stack, so just simply bail out...
1350 */
1351 DPRINT1("The registry tree has so many levels!\n");
1352 CmpFree(WorkState, WorkStateLength);
1353 return CM_CHECK_REGISTRY_TREE_TOO_MANY_LEVELS;
1354 }
1355
1356 /* Prepare the work state for the next key */
1357 WorkState[StackDepth].ChildCellIndex = 0;
1358 WorkState[StackDepth].Current = ChildSubKeyCell;
1359 WorkState[StackDepth].Parent = WorkState[StackDepth - CMP_PRIOR_STACK].Current;
1360 WorkState[StackDepth].Sibling = HCELL_NIL;
1361
1362 /*
1363 * As we prepared the work state, acknowledge the
1364 * code path at the top of the loop that we need
1365 * to process and validate the next child subkey.
1366 */
1367 AllChildrenChecked = FALSE;
1368 continue;
1369 }
1370
1371 /*
1372 * We have validated all the child subkeys
1373 * of the node. Decrease the stack depth
1374 * and tell the above code we looked for all
1375 * children so that we don't need to validate
1376 * the same children again but go for the next
1377 * node.
1378 */
1379 AllChildrenChecked = TRUE;
1380 StackDepth--;
1381 continue;
1382 }
1383
1384 CmpFree(WorkState, WorkStateLength);
1385 return CM_CHECK_REGISTRY_GOOD;
1386 }
1387
1388 /* PUBLIC FUNCTIONS ***********************************************************/
1389
1390 /**
1391 * @brief
1392 * Validates a bin from a hive. It performs checks
1393 * against the cells from this bin, ensuring the
1394 * bin is not corrupt and that the cells are consistent
1395 * with each other.
1396 *
1397 * @param[in] Hive
1398 * A pointer to a hive descriptor of which a hive bin
1399 * is to be validated.
1400 *
1401 * @param[in] Bin
1402 * A pointer to a bin where its cells are to be
1403 * validated.
1404 *
1405 * @return
1406 * CM_CHECK_REGISTRY_GOOD is returned if the bin is
1407 * valid and not corrupt. CM_CHECK_REGISTRY_BIN_SIGNATURE_HEADER_CORRUPT
1408 * is returned if this bin has a corrupt signature. CM_CHECK_REGISTRY_BAD_FREE_CELL
1409 * is returned if the free cell has a bogus size. CM_CHECK_REGISTRY_BAD_ALLOC_CELL
1410 * is returned for the allocated cell has a bogus size.
1411 */
1412 CM_CHECK_REGISTRY_STATUS
1413 NTAPI
HvValidateBin(_In_ PHHIVE Hive,_In_ PHBIN Bin)1414 HvValidateBin(
1415 _In_ PHHIVE Hive,
1416 _In_ PHBIN Bin)
1417 {
1418 PHCELL Cell, Basket;
1419
1420 PAGED_CODE();
1421
1422 ASSERT(Bin);
1423 ASSERT(Hive);
1424
1425 /* Ensure that this bin we got has valid signature header */
1426 if (Bin->Signature != HV_HBIN_SIGNATURE)
1427 {
1428 DPRINT1("The bin's signature header is corrupt\n");
1429 return CM_CHECK_REGISTRY_BIN_SIGNATURE_HEADER_CORRUPT;
1430 }
1431
1432 /*
1433 * Walk over all the cells from this bin and
1434 * validate that they're consistent with the bin.
1435 * Namely we want that each cell from this bin doesn't
1436 * have a bogus size.
1437 */
1438 Basket = (PHCELL)((PUCHAR)Bin + Bin->Size);
1439 for (Cell = GET_CELL_BIN(Bin);
1440 Cell < Basket;
1441 Cell = (PHCELL)((PUCHAR)Cell + abs(Cell->Size)))
1442 {
1443 if (IsFreeCell(Cell))
1444 {
1445 /*
1446 * This cell is free, check that
1447 * the size of this cell is not bogus.
1448 */
1449 if (Cell->Size > Bin->Size ||
1450 Cell->Size == 0)
1451 {
1452 /*
1453 * This cell has too much free space that
1454 * exceeds the boundary of the bin size.
1455 * Otherwise the cell doesn't have actual
1456 * free space (aka Size == 0) which is a
1457 * no go for a bin.
1458 */
1459 DPRINT1("The free cell exceeds the bin size or cell size equal to 0 (cell 0x%p, cell size %d, bin size %u)\n",
1460 Cell, Cell->Size, Bin->Size);
1461 return CM_CHECK_REGISTRY_BAD_FREE_CELL;
1462 }
1463 }
1464 else
1465 {
1466 /*
1467 * This cell is allocated, make sure that
1468 * the size of this cell is not bogus.
1469 */
1470 if (abs(Cell->Size) > Bin->Size)
1471 {
1472 /*
1473 * This cell allocated too much space
1474 * that exceeds the boundary of the
1475 * bin size.
1476 */
1477 DPRINT1("The allocated cell exceeds the bin size (cell 0x%p, cell size %d, bin size %u)\n",
1478 Cell, abs(Cell->Size), Bin->Size);
1479 return CM_CHECK_REGISTRY_BAD_ALLOC_CELL;
1480 }
1481 }
1482 }
1483
1484 return CM_CHECK_REGISTRY_GOOD;
1485 }
1486
1487 /**
1488 * @brief
1489 * Validates a registry hive. This function ensures
1490 * that the storage of this hive has valid bins.
1491 *
1492 * @param[in] Hive
1493 * A pointer to a hive descriptor where validation on
1494 * its hive bins is to be performed.
1495 *
1496 * @return
1497 * CM_CHECK_REGISTRY_GOOD is returned if the hive
1498 * is valid. CM_CHECK_REGISTRY_HIVE_CORRUPT_SIGNATURE is
1499 * returned if the hive has a corrupted signature.
1500 * CM_CHECK_REGISTRY_BIN_SIZE_OR_OFFSET_CORRUPT is returned
1501 * if the captured bin has a bad size. A failure CM status
1502 * code is returned otherwise.
1503 */
1504 CM_CHECK_REGISTRY_STATUS
1505 NTAPI
HvValidateHive(_In_ PHHIVE Hive)1506 HvValidateHive(
1507 _In_ PHHIVE Hive)
1508 {
1509 CM_CHECK_REGISTRY_STATUS CmStatusCode;
1510 ULONG StorageIndex;
1511 ULONG BlockIndex;
1512 ULONG StorageLength;
1513 PHBIN Bin;
1514
1515 PAGED_CODE();
1516
1517 ASSERT(Hive);
1518
1519 /* Is the hive signature valid? */
1520 if (Hive->Signature != HV_HHIVE_SIGNATURE)
1521 {
1522 DPRINT1("Hive's signature corrupted (signature %u)\n", Hive->Signature);
1523 return CM_CHECK_REGISTRY_HIVE_CORRUPT_SIGNATURE;
1524 }
1525
1526 /*
1527 * Now loop each bin in the storage of this
1528 * hive.
1529 */
1530 for (StorageIndex = 0; StorageIndex < Hive->StorageTypeCount; StorageIndex++)
1531 {
1532 /* Get the storage length at this index */
1533 StorageLength = Hive->Storage[StorageIndex].Length;
1534
1535 for (BlockIndex = 0; BlockIndex < StorageLength;)
1536 {
1537 /* Go to the next if this bin does not exist */
1538 if (Hive->Storage[StorageIndex].BlockList[BlockIndex].BinAddress == (ULONG_PTR)NULL)
1539 {
1540 continue;
1541 }
1542
1543 /*
1544 * Capture this bin and ensure that such
1545 * bin is within the offset and the size
1546 * is not bogus.
1547 */
1548 Bin = GET_HHIVE_BIN(Hive, StorageIndex, BlockIndex);
1549 if (Bin->Size > (StorageLength * HBLOCK_SIZE) ||
1550 (Bin->FileOffset / HBLOCK_SIZE) != BlockIndex)
1551 {
1552 DPRINT1("Bin size or offset is corrupt (bin size %u, file offset %u, storage length %u)\n",
1553 Bin->Size, Bin->FileOffset, StorageLength);
1554 return CM_CHECK_REGISTRY_BIN_SIZE_OR_OFFSET_CORRUPT;
1555 }
1556
1557 /* Validate the rest of the bin */
1558 CmStatusCode = HvValidateBin(Hive, Bin);
1559 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode))
1560 {
1561 DPRINT1("This bin is not valid (bin 0x%p)\n", Bin);
1562 return CmStatusCode;
1563 }
1564
1565 /* Go to the next block */
1566 BlockIndex += Bin->Size / HBLOCK_SIZE;
1567 }
1568 }
1569
1570 return CM_CHECK_REGISTRY_GOOD;
1571 }
1572
1573 /**
1574 * @brief
1575 * Checks the registry that is consistent and its
1576 * contents valid and not corrupted. More specifically
1577 * this function performs a deep check of the registry
1578 * for the following properties:
1579 *
1580 * - That the security cache cell of the registry is OK
1581 * - That bins and cells are consistent with each other
1582 * - That the child subkey cell points to the parent
1583 * - That the key itself has sane sizes
1584 * - That the class, values and subkeys lists are valid
1585 * - Much more
1586 *
1587 * @param[in] Hive
1588 * A pointer to a CM hive of the registry to be checked
1589 * in question.
1590 *
1591 * @param[in] Flags
1592 * A bit mask flag used to influence the process of volatile
1593 * keys purging. See Remarks for further information.
1594 *
1595 * @return
1596 * This function returns a CM (Configuration Manager) check
1597 * registry status code. A code of CM_CHECK_REGISTRY_GOOD of
1598 * value 0 indicates the registry hive is valid and not corrupted.
1599 * A non zero unsigned integer value indicates a failure. Consult
1600 * other private routines in this file for other failure status
1601 * codes.
1602 *
1603 * @remarks
1604 * During a load operation CmCheckRegistry can purge the volatile
1605 * data of registry (or not) depending on the submitted flag bit mask
1606 * by the caller. The following supported flags are:
1607 *
1608 * CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES -- Tells the function that
1609 * volatile data purging must not be done.
1610 *
1611 * CM_CHECK_REGISTRY_PURGE_VOLATILES - Tells the function to purge out
1612 * volatile information data from a registry hive, on demand. Purging
1613 * doesn't come into action if no volatile data has been found.
1614 *
1615 * CM_CHECK_REGISTRY_BOOTLOADER_PURGE_VOLATILES - A special flag used
1616 * by FreeLdr and Environ. When this flag is set the function will not
1617 * clean up the volatile storage but it will unintialize the storage
1618 * instead (this is the case if the given registry hive for validation
1619 * is a XP Beta 1 hive or newer). Otherwise it will perform a normal
1620 * cleanup of the volatile storage.
1621 *
1622 * CM_CHECK_REGISTRY_VALIDATE_HIVE - Tells the function to perform a
1623 * thorough analysation of the underlying hive's bins and cells before
1624 * doing validation of the registry tree. HvValidateHive function is called
1625 * in this case.
1626 *
1627 * CM_CHECK_REGISTRY_FIX_HIVE - Tells the function to fix the target registry
1628 * hive if it is damaged. Usually this flag comes from a registry repair tool
1629 * where the user asked to for its damaged hive to be fixed. In this case
1630 * a self-heal procedure against the hive is performed.
1631 */
1632 CM_CHECK_REGISTRY_STATUS
1633 NTAPI
CmCheckRegistry(_In_ PCMHIVE RegistryHive,_In_ ULONG Flags)1634 CmCheckRegistry(
1635 _In_ PCMHIVE RegistryHive,
1636 _In_ ULONG Flags)
1637 {
1638 CM_CHECK_REGISTRY_STATUS CmStatusCode;
1639 PHHIVE Hive;
1640 BOOLEAN ShouldFixHive = FALSE;
1641
1642 PAGED_CODE();
1643
1644 /* Bail out if the caller did not give a hive */
1645 if (!RegistryHive)
1646 {
1647 DPRINT1("No registry hive given for check\n");
1648 return CM_CHECK_REGISTRY_INVALID_PARAMETER;
1649 }
1650
1651 #if !defined(CMLIB_HOST) && !defined(_BLDR_)
1652 /*
1653 * The master hive is the root of the registry,
1654 * it holds all other hives together. So do not
1655 * do any validation checks.
1656 */
1657 if (RegistryHive == CmiVolatileHive)
1658 {
1659 DPRINT("This is master registry hive, don't do anything\n");
1660 return CM_CHECK_REGISTRY_GOOD;
1661 }
1662 #endif
1663
1664 /* Bail out if no valid flag is given */
1665 if (Flags & ~(CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES |
1666 CM_CHECK_REGISTRY_PURGE_VOLATILES |
1667 CM_CHECK_REGISTRY_BOOTLOADER_PURGE_VOLATILES |
1668 CM_CHECK_REGISTRY_VALIDATE_HIVE |
1669 CM_CHECK_REGISTRY_FIX_HIVE))
1670 {
1671 DPRINT1("Invalid flag for registry check given (flag %u)\n", Flags);
1672 return CM_CHECK_REGISTRY_INVALID_PARAMETER;
1673 }
1674
1675 /*
1676 * Obtain the hive and check if the caller wants
1677 * that the hive to be validated.
1678 */
1679 Hive = GET_HHIVE(RegistryHive);
1680 if (Flags & CM_CHECK_REGISTRY_VALIDATE_HIVE)
1681 {
1682 CmStatusCode = HvValidateHive(Hive);
1683 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode))
1684 {
1685 DPRINT1("The hive is not valid (hive 0x%p, check status code %u)\n",
1686 Hive, CmStatusCode);
1687 return CmStatusCode;
1688 }
1689 }
1690
1691 /*
1692 * A registry repair tool such as the ReactOS Check Registry
1693 * Utility wants the damaged hive to be fixed as we check the
1694 * target hive.
1695 */
1696 if (Flags & CM_CHECK_REGISTRY_FIX_HIVE)
1697 {
1698 ShouldFixHive = TRUE;
1699 }
1700
1701 /*
1702 * FIXME: Currently ReactOS does not implement security
1703 * caching algorithms so it's pretty pointless to implement
1704 * security descriptors validation checks at this moment.
1705 * When the time comes to implement these, we would need
1706 * to implement security checks here as well.
1707 */
1708
1709 /* Call the internal API to do the rest of the work bulk */
1710 CmStatusCode = CmpValidateRegistryInternal(Hive, Flags, FALSE, ShouldFixHive);
1711 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode))
1712 {
1713 DPRINT1("The hive is not valid (hive 0x%p, check status code %u)\n",
1714 Hive, CmStatusCode);
1715 return CmStatusCode;
1716 }
1717
1718 return CmStatusCode;
1719 }
1720
1721 /* EOF */
1722