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 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 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 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 %lu, list count %lu)\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 %lu, list count %lu)\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 %lu, value count %lu)\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 %lu, cell size %lu, at index %lu)\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 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 %lu, cell size %lu)\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("Values removed in the list -- %lu\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 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 %lu, root key index count %lu)\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 %lu\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 %lu, total leaf count %lu)\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 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 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 %lu)\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 %lu, cell size %lu)\n", TotalKeyNameLength, CellSize); 1004 return CM_CHECK_REGISTRY_KEY_TOO_BIG_THAN_CELL; 1005 } 1006 1007 /* Is the parent cell consistent? */ 1008 if (ParentCell != HCELL_NIL && 1009 ParentCell != CellData->u.KeyNode.Parent) 1010 { 1011 if (!CmpRepairParentNode(Hive, 1012 CurrentCell, 1013 ParentCell, 1014 CellData, 1015 FixHive)) 1016 { 1017 DPRINT1("The parent key node doesn't point to the actual parent\n"); 1018 return CM_CHECK_REGISTRY_BAD_KEY_NODE_PARENT; 1019 } 1020 } 1021 1022 /* Is the key node signature valid? */ 1023 if (CellData->u.KeyNode.Signature != CM_KEY_NODE_SIGNATURE) 1024 { 1025 if (!CmpRepairKeyNodeSignature(Hive, 1026 CurrentCell, 1027 CellData, 1028 FixHive)) 1029 { 1030 DPRINT1("The parent key node signature is not valid\n"); 1031 return CM_CHECK_REGISTRY_BAD_KEY_NODE_SIGNATURE; 1032 } 1033 } 1034 1035 /* 1036 * FIXME: Security cell checks have to be implemented here 1037 * once we properly and reliably implement security caching 1038 * in the kernel. 1039 */ 1040 1041 /* Validate the class */ 1042 CmStatusCode = CmpValidateClass(Hive, CurrentCell, CellData); 1043 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode)) 1044 { 1045 if (!CmpRepairClassOfNodeKey(Hive, 1046 CurrentCell, 1047 CellData, 1048 FixHive)) 1049 { 1050 DPRINT1("Failed to repair the hive, the cell class is not valid\n"); 1051 return CmStatusCode; 1052 } 1053 } 1054 1055 /* Validate the value list */ 1056 CmStatusCode = CmpValidateValueList(Hive, CurrentCell, CellData, FixHive); 1057 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode)) 1058 { 1059 /* 1060 * It happens that a certain value in the list 1061 * is so bad like we couldn't map a cell data from it 1062 * or the list itself is toast. In such cases what we 1063 * can do here is to do a "value list sacrifice", aka 1064 * purge the whole list. 1065 */ 1066 if (!CmpRepairValueList(Hive, CurrentCell, FixHive)) 1067 { 1068 DPRINT1("Failed to repair the hive, the value list is corrupt\n"); 1069 return CmStatusCode; 1070 } 1071 } 1072 1073 /* Validate the subkeys list */ 1074 CmStatusCode = CmpValidateSubKeyList(Hive, CurrentCell, CellData, FixHive, &DoSubkeysRepair); 1075 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode)) 1076 { 1077 /* 1078 * The subkeys list is in trouble. Worse when the actual 1079 * subkey list is so severed this key is also kaput on itself. 1080 */ 1081 if (!DoSubkeysRepair) 1082 { 1083 DPRINT1("The subkeys list is totally corrupt, can't repair\n"); 1084 return CmStatusCode; 1085 } 1086 1087 /* 1088 * OK, there's still some salvation for this key. 1089 * Purge the whole subkeys list in order to fix it. 1090 */ 1091 if (!CmpRepairSubKeyList(Hive, 1092 CurrentCell, 1093 CellData, 1094 FixHive)) 1095 { 1096 DPRINT1("Failed to repair the hive, the subkeys list is corrupt!\n"); 1097 return CmStatusCode; 1098 } 1099 } 1100 1101 /* Purge volatile data if needed */ 1102 CmpPurgeVolatiles(Hive, CurrentCell, CellData, Flags); 1103 return CM_CHECK_REGISTRY_GOOD; 1104 } 1105 1106 /** 1107 * @brief 1108 * Performs deep checking of the registry by walking 1109 * down the registry tree using a stack based pool. 1110 * This function is the guts of CmCheckRegistry. 1111 * 1112 * @param[in] Hive 1113 * A pointer to a hive descriptor of the registry where 1114 * the validation is to be performed. 1115 * 1116 * @param[in] Flags 1117 * Bit mask flag used for volatiles purging. Such 1118 * flags influence on how volatile purging is actually 1119 * done. See CmCheckRegistry documentation for more 1120 * information. 1121 * 1122 * @param[in] SecurityDefaulted 1123 * If the caller sets this to FALSE, the registry hive 1124 * uses its own unique security details. Otherwise 1125 * registry hive has the security details defaulted. 1126 * 1127 * @param[in] FixHive 1128 * If set to TRUE, the target hive will be fixed. 1129 * 1130 * @return 1131 * Returns CM_CHECK_REGISTRY_GOOD is returned if the function 1132 * has successfully performed deep registry checking and 1133 * the registry contents are valid. CM_CHECK_REGISTRY_ALLOCATE_MEM_STACK_FAIL 1134 * is returned if the function has failed to allocate the 1135 * stack work state buffer in memory which is necessary for 1136 * deep checking of the registry. CM_CHECK_REGISTRY_ROOT_CELL_NOT_FOUND 1137 * is returned if no root cell has been found of this hive. 1138 * CM_CHECK_REGISTRY_BAD_LEXICOGRAPHICAL_ORDER is returned if the lexical 1139 * order is not valid. CM_CHECK_REGISTRY_NODE_NOT_FOUND is returned if 1140 * the no key node could be mapped from the key. CM_CHECK_REGISTRY_SUBKEY_NOT_FOUND 1141 * is returned if no subkey child cell could be found. CM_CHECK_REGISTRY_TREE_TOO_MANY_LEVELS 1142 * is returned if we have reached the maximum stack limit which means the registry that 1143 * we have checked is too fat. 1144 */ 1145 static 1146 CM_CHECK_REGISTRY_STATUS 1147 CmpValidateRegistryInternal( 1148 _In_ PHHIVE Hive, 1149 _In_ ULONG Flags, 1150 _In_ BOOLEAN SecurityDefaulted, 1151 _In_ BOOLEAN FixHive) 1152 { 1153 CM_CHECK_REGISTRY_STATUS CmStatusCode; 1154 PCMP_REGISTRY_STACK_WORK_STATE WorkState; 1155 HCELL_INDEX RootCell, ParentCell, CurrentCell; 1156 HCELL_INDEX ChildSubKeyCell; 1157 PCM_KEY_NODE KeyNode; 1158 ULONG WorkStateLength; 1159 LONG StackDepth; 1160 BOOLEAN AllChildrenChecked; 1161 1162 PAGED_CODE(); 1163 1164 ASSERT(Hive); 1165 1166 /* 1167 * Allocate some memory blocks for the stack 1168 * state structure. We'll be using it to walk 1169 * down the registry hive tree in a recursive 1170 * way without worrying that we explode the 1171 * kernel stack in the most gruesome and gross 1172 * ways. 1173 */ 1174 WorkStateLength = CMP_REGISTRY_MAX_LEVELS_TREE_DEPTH * sizeof(CMP_REGISTRY_STACK_WORK_STATE); 1175 WorkState = CmpAllocate(WorkStateLength, 1176 TRUE, 1177 TAG_REGISTRY_STACK); 1178 if (!WorkState) 1179 { 1180 DPRINT1("Couldn't allocate memory for registry stack work state\n"); 1181 return CM_CHECK_REGISTRY_ALLOCATE_MEM_STACK_FAIL; 1182 } 1183 1184 /* Obtain the root cell of the hive */ 1185 RootCell = GET_HHIVE_ROOT_CELL(Hive); 1186 if (RootCell == HCELL_NIL) 1187 { 1188 DPRINT1("Couldn't get the root cell of the hive\n"); 1189 CmpFree(WorkState, WorkStateLength); 1190 return CM_CHECK_REGISTRY_ROOT_CELL_NOT_FOUND; 1191 } 1192 1193 RestartValidation: 1194 /* 1195 * Prepare the stack state and start from 1196 * the root cell. Ensure that the root cell 1197 * itself is OK before we go forward. 1198 */ 1199 StackDepth = 0; 1200 WorkState[StackDepth].ChildCellIndex = 0; 1201 WorkState[StackDepth].Current = RootCell; 1202 WorkState[StackDepth].Parent = HCELL_NIL; 1203 WorkState[StackDepth].Sibling = HCELL_NIL; 1204 1205 /* 1206 * As we start checking the root cell which 1207 * is the top element of a registry hive, 1208 * we'll be going to look for child keys 1209 * in the course of walking down the tree. 1210 */ 1211 AllChildrenChecked = FALSE; 1212 1213 while (StackDepth >= 0) 1214 { 1215 /* Cache the current and parent cells */ 1216 CurrentCell = WorkState[StackDepth].Current; 1217 ParentCell = WorkState[StackDepth].Parent; 1218 1219 /* Do we have still have children to validate? */ 1220 if (!AllChildrenChecked) 1221 { 1222 /* Check that the key is OK */ 1223 CmStatusCode = CmpValidateKey(Hive, 1224 SecurityDefaulted, 1225 ParentCell, 1226 CurrentCell, 1227 Flags, 1228 FixHive); 1229 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode)) 1230 { 1231 /* 1232 * The key cell is damaged. We have to pray and 1233 * hope that this is not the root cell as any 1234 * damage done to the root is catastrophically 1235 * fatal. 1236 */ 1237 if (CurrentCell == RootCell) 1238 { 1239 DPRINT1("THE ROOT CELL IS BROKEN\n"); 1240 CmpFree(WorkState, WorkStateLength); 1241 return CmStatusCode; 1242 } 1243 1244 /* 1245 * It is not the root, remove the faulting 1246 * damaged cell from the parent so that we 1247 * can heal the hive. 1248 */ 1249 if (!CmpRepairParentKey(Hive, CurrentCell, ParentCell, FixHive)) 1250 { 1251 DPRINT1("The key is corrupt (current cell %lu, parent cell %lu)\n", 1252 CurrentCell, ParentCell); 1253 CmpFree(WorkState, WorkStateLength); 1254 return CmStatusCode; 1255 } 1256 1257 /* Damaged cell removed, restart the loop */ 1258 DPRINT1("Hive repaired, restarting the validation loop...\n"); 1259 goto RestartValidation; 1260 } 1261 1262 /* 1263 * The key is in perfect shape. If we have advanced 1264 * the stack depth then check the lexicographical 1265 * order of the keys as well. 1266 */ 1267 if (StackDepth > 0 && 1268 CM_CHECK_REGISTRY_SUCCESS(CmStatusCode)) 1269 { 1270 if (WorkState[StackDepth - CMP_PRIOR_STACK].Sibling != HCELL_NIL) 1271 { 1272 if (!CmpValidateLexicalOrder(Hive, 1273 CurrentCell, 1274 WorkState[StackDepth - CMP_PRIOR_STACK].Sibling)) 1275 { 1276 /* 1277 * The lexicographical order is bad, 1278 * attempt to heal the hive. 1279 */ 1280 if (!CmpRepairParentKey(Hive, CurrentCell, ParentCell, FixHive)) 1281 { 1282 DPRINT1("The lexicographical order is invalid (sibling %lu, current cell %lu)\n", 1283 CurrentCell, WorkState[StackDepth - CMP_PRIOR_STACK].Sibling); 1284 CmpFree(WorkState, WorkStateLength); 1285 return CM_CHECK_REGISTRY_BAD_LEXICOGRAPHICAL_ORDER; 1286 } 1287 1288 /* Damaged cell removed, restart the loop */ 1289 DPRINT1("Hive repaired, restarting the validation loop...\n"); 1290 goto RestartValidation; 1291 } 1292 } 1293 1294 /* Assign the prior sibling for upcoming iteration */ 1295 WorkState[StackDepth - CMP_PRIOR_STACK].Sibling = CurrentCell; 1296 } 1297 } 1298 1299 /* Obtain a node for this key */ 1300 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CurrentCell); 1301 if (!KeyNode) 1302 { 1303 DPRINT1("Couldn't get the node of key (current cell %lu)\n", CurrentCell); 1304 CmpFree(WorkState, WorkStateLength); 1305 return CM_CHECK_REGISTRY_NODE_NOT_FOUND; 1306 } 1307 1308 /* 1309 * If we have processed all the children from this 1310 * node then adjust the stack depth work state by 1311 * going back and restart the loop to lookup for 1312 * the rest of the tree. Acknowledge the code path 1313 * above that we checked all the children so that 1314 * we don't have to validate the same subkey again. 1315 */ 1316 if (WorkState[StackDepth].ChildCellIndex < KeyNode->SubKeyCounts[Stable]) 1317 { 1318 /* 1319 * We have children to process, obtain the 1320 * child subkey in question so that we can 1321 * cache it later for the next key validation. 1322 */ 1323 ChildSubKeyCell = CmpFindSubKeyByNumber(Hive, KeyNode, WorkState[StackDepth].ChildCellIndex); 1324 if (ChildSubKeyCell == HCELL_NIL) 1325 { 1326 DPRINT1("Couldn't get the child subkey cell (at stack index %lu)\n", StackDepth); 1327 CmpFree(WorkState, WorkStateLength); 1328 return CM_CHECK_REGISTRY_SUBKEY_NOT_FOUND; 1329 } 1330 1331 /* 1332 * As we got the subkey advance the child index as 1333 * well as the stack depth work state for the next 1334 * key validation. However we must ensure since 1335 * we're advancing the stack depth that we don't 1336 * go over the maximum tree level depth. A registry 1337 * tree can be at maximum 512 levels deep. 1338 * 1339 * For more information see https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits. 1340 */ 1341 WorkState[StackDepth].ChildCellIndex++; 1342 StackDepth++; 1343 if (StackDepth >= CMP_REGISTRY_MAX_LEVELS_TREE_DEPTH - 1) 1344 { 1345 /* 1346 * This registry has so many levels it's 1347 * so fat. We don't want to explode our 1348 * kernel stack, so just simply bail out... 1349 */ 1350 DPRINT1("The registry tree has so many levels!\n"); 1351 CmpFree(WorkState, WorkStateLength); 1352 return CM_CHECK_REGISTRY_TREE_TOO_MANY_LEVELS; 1353 } 1354 1355 /* Prepare the work state for the next key */ 1356 WorkState[StackDepth].ChildCellIndex = 0; 1357 WorkState[StackDepth].Current = ChildSubKeyCell; 1358 WorkState[StackDepth].Parent = WorkState[StackDepth - CMP_PRIOR_STACK].Current; 1359 WorkState[StackDepth].Sibling = HCELL_NIL; 1360 1361 /* 1362 * As we prepared the work state, acknowledge the 1363 * code path at the top of the loop that we need 1364 * to process and validate the next child subkey. 1365 */ 1366 AllChildrenChecked = FALSE; 1367 continue; 1368 } 1369 1370 /* 1371 * We have validated all the child subkeys 1372 * of the node. Decrease the stack depth 1373 * and tell the above code we looked for all 1374 * children so that we don't need to validate 1375 * the same children again but go for the next 1376 * node. 1377 */ 1378 AllChildrenChecked = TRUE; 1379 StackDepth--; 1380 continue; 1381 } 1382 1383 CmpFree(WorkState, WorkStateLength); 1384 return CM_CHECK_REGISTRY_GOOD; 1385 } 1386 1387 /* PUBLIC FUNCTIONS ***********************************************************/ 1388 1389 /** 1390 * @brief 1391 * Validates a bin from a hive. It performs checks 1392 * against the cells from this bin, ensuring the 1393 * bin is not corrupt and that the cells are consistent 1394 * with each other. 1395 * 1396 * @param[in] Hive 1397 * A pointer to a hive descriptor of which a hive bin 1398 * is to be validated. 1399 * 1400 * @param[in] Bin 1401 * A pointer to a bin where its cells are to be 1402 * validated. 1403 * 1404 * @return 1405 * CM_CHECK_REGISTRY_GOOD is returned if the bin is 1406 * valid and not corrupt. CM_CHECK_REGISTRY_BIN_SIGNATURE_HEADER_CORRUPT 1407 * is returned if this bin has a corrupt signature. CM_CHECK_REGISTRY_BAD_FREE_CELL 1408 * is returned if the free cell has a bogus size. CM_CHECK_REGISTRY_BAD_ALLOC_CELL 1409 * is returned for the allocated cell has a bogus size. 1410 */ 1411 CM_CHECK_REGISTRY_STATUS 1412 NTAPI 1413 HvValidateBin( 1414 _In_ PHHIVE Hive, 1415 _In_ PHBIN Bin) 1416 { 1417 PHCELL Cell, Basket; 1418 1419 PAGED_CODE(); 1420 1421 ASSERT(Bin); 1422 ASSERT(Hive); 1423 1424 /* Ensure that this bin we got has valid signature header */ 1425 if (Bin->Signature != HV_HBIN_SIGNATURE) 1426 { 1427 DPRINT1("The bin's signature header is corrupt\n"); 1428 return CM_CHECK_REGISTRY_BIN_SIGNATURE_HEADER_CORRUPT; 1429 } 1430 1431 /* 1432 * Walk over all the cells from this bin and 1433 * validate that they're consistent with the bin. 1434 * Namely we want that each cell from this bin doesn't 1435 * have a bogus size. 1436 */ 1437 Basket = (PHCELL)((PUCHAR)Bin + Bin->Size); 1438 for (Cell = GET_CELL_BIN(Bin); 1439 Cell < Basket; 1440 Cell = (PHCELL)((PUCHAR)Cell + abs(Cell->Size))) 1441 { 1442 if (IsFreeCell(Cell)) 1443 { 1444 /* 1445 * This cell is free, check that 1446 * the size of this cell is not bogus. 1447 */ 1448 if (Cell->Size > Bin->Size || 1449 Cell->Size == 0) 1450 { 1451 /* 1452 * This cell has too much free space that 1453 * exceeds the boundary of the bin size. 1454 * Otherwise the cell doesn't have actual 1455 * free space (aka Size == 0) which is a 1456 * no go for a bin. 1457 */ 1458 DPRINT1("The free cell exceeds the bin size or cell size equal to 0 (cell 0x%p, cell size %d, bin size %lu)\n", 1459 Cell, Cell->Size, Bin->Size); 1460 return CM_CHECK_REGISTRY_BAD_FREE_CELL; 1461 } 1462 } 1463 else 1464 { 1465 /* 1466 * This cell is allocated, make sure that 1467 * the size of this cell is not bogus. 1468 */ 1469 if (abs(Cell->Size) > Bin->Size) 1470 { 1471 /* 1472 * This cell allocated too much space 1473 * that exceeds the boundary of the 1474 * bin size. 1475 */ 1476 DPRINT1("The allocated cell exceeds the bin size (cell 0x%p, cell size %d, bin size %lu)\n", 1477 Cell, abs(Cell->Size), Bin->Size); 1478 return CM_CHECK_REGISTRY_BAD_ALLOC_CELL; 1479 } 1480 } 1481 } 1482 1483 return CM_CHECK_REGISTRY_GOOD; 1484 } 1485 1486 /** 1487 * @brief 1488 * Validates a registry hive. This function ensures 1489 * that the storage of this hive has valid bins. 1490 * 1491 * @param[in] Hive 1492 * A pointer to a hive descriptor where validation on 1493 * its hive bins is to be performed. 1494 * 1495 * @return 1496 * CM_CHECK_REGISTRY_GOOD is returned if the hive 1497 * is valid. CM_CHECK_REGISTRY_HIVE_CORRUPT_SIGNATURE is 1498 * returned if the hive has a corrupted signature. 1499 * CM_CHECK_REGISTRY_BIN_SIZE_OR_OFFSET_CORRUPT is returned 1500 * if the captured bin has a bad size. A failure CM status 1501 * code is returned otherwise. 1502 */ 1503 CM_CHECK_REGISTRY_STATUS 1504 NTAPI 1505 HvValidateHive( 1506 _In_ PHHIVE Hive) 1507 { 1508 CM_CHECK_REGISTRY_STATUS CmStatusCode; 1509 ULONG StorageIndex; 1510 ULONG BlockIndex; 1511 ULONG StorageLength; 1512 PHBIN Bin; 1513 1514 PAGED_CODE(); 1515 1516 ASSERT(Hive); 1517 1518 /* Is the hive signature valid? */ 1519 if (Hive->Signature != HV_HHIVE_SIGNATURE) 1520 { 1521 DPRINT1("Hive's signature corrupted (signature %lu)\n", Hive->Signature); 1522 return CM_CHECK_REGISTRY_HIVE_CORRUPT_SIGNATURE; 1523 } 1524 1525 /* 1526 * Now loop each bin in the storage of this 1527 * hive. 1528 */ 1529 for (StorageIndex = 0; StorageIndex < Hive->StorageTypeCount; StorageIndex++) 1530 { 1531 /* Get the storage length at this index */ 1532 StorageLength = Hive->Storage[StorageIndex].Length; 1533 1534 for (BlockIndex = 0; BlockIndex < StorageLength;) 1535 { 1536 /* Go to the next if this bin does not exist */ 1537 if (Hive->Storage[StorageIndex].BlockList[BlockIndex].BinAddress == (ULONG_PTR)NULL) 1538 { 1539 continue; 1540 } 1541 1542 /* 1543 * Capture this bin and ensure that such 1544 * bin is within the offset and the size 1545 * is not bogus. 1546 */ 1547 Bin = GET_HHIVE_BIN(Hive, StorageIndex, BlockIndex); 1548 if (Bin->Size > (StorageLength * HBLOCK_SIZE) || 1549 (Bin->FileOffset / HBLOCK_SIZE) != BlockIndex) 1550 { 1551 DPRINT1("Bin size or offset is corrupt (bin size %lu, file offset %lu, storage length %lu)\n", 1552 Bin->Size, Bin->FileOffset, StorageLength); 1553 return CM_CHECK_REGISTRY_BIN_SIZE_OR_OFFSET_CORRUPT; 1554 } 1555 1556 /* Validate the rest of the bin */ 1557 CmStatusCode = HvValidateBin(Hive, Bin); 1558 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode)) 1559 { 1560 DPRINT1("This bin is not valid (bin 0x%p)\n", Bin); 1561 return CmStatusCode; 1562 } 1563 1564 /* Go to the next block */ 1565 BlockIndex += Bin->Size / HBLOCK_SIZE; 1566 } 1567 } 1568 1569 return CM_CHECK_REGISTRY_GOOD; 1570 } 1571 1572 /** 1573 * @brief 1574 * Checks the registry that is consistent and its 1575 * contents valid and not corrupted. More specifically 1576 * this function performs a deep check of the registry 1577 * for the following properties: 1578 * 1579 * - That the security cache cell of the registry is OK 1580 * - That bins and cells are consistent with each other 1581 * - That the child subkey cell points to the parent 1582 * - That the key itself has sane sizes 1583 * - That the class, values and subkeys lists are valid 1584 * - Much more 1585 * 1586 * @param[in] Hive 1587 * A pointer to a CM hive of the registry to be checked 1588 * in question. 1589 * 1590 * @param[in] Flags 1591 * A bit mask flag used to influence the process of volatile 1592 * keys purging. See Remarks for further information. 1593 * 1594 * @return 1595 * This function returns a CM (Configuration Manager) check 1596 * registry status code. A code of CM_CHECK_REGISTRY_GOOD of 1597 * value 0 indicates the registry hive is valid and not corrupted. 1598 * A non zero unsigned integer value indicates a failure. Consult 1599 * other private routines in this file for other failure status 1600 * codes. 1601 * 1602 * @remarks 1603 * During a load operation CmCheckRegistry can purge the volatile 1604 * data of registry (or not) depending on the submitted flag bit mask 1605 * by the caller. The following supported flags are: 1606 * 1607 * CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES -- Tells the function that 1608 * volatile data purging must not be done. 1609 * 1610 * CM_CHECK_REGISTRY_PURGE_VOLATILES - Tells the function to purge out 1611 * volatile information data from a registry hive, on demand. Purging 1612 * doesn't come into action if no volatile data has been found. 1613 * 1614 * CM_CHECK_REGISTRY_BOOTLOADER_PURGE_VOLATILES - A special flag used 1615 * by FreeLdr and Environ. When this flag is set the function will not 1616 * clean up the volatile storage but it will unintialize the storage 1617 * instead (this is the case if the given registry hive for validation 1618 * is a XP Beta 1 hive or newer). Otherwise it will perform a normal 1619 * cleanup of the volatile storage. 1620 * 1621 * CM_CHECK_REGISTRY_VALIDATE_HIVE - Tells the function to perform a 1622 * thorough analysation of the underlying hive's bins and cells before 1623 * doing validation of the registry tree. HvValidateHive function is called 1624 * in this case. 1625 * 1626 * CM_CHECK_REGISTRY_FIX_HIVE - Tells the function to fix the target registry 1627 * hive if it is damaged. Usually this flag comes from a registry repair tool 1628 * where the user asked to for its damaged hive to be fixed. In this case 1629 * a self-heal procedure against the hive is performed. 1630 */ 1631 CM_CHECK_REGISTRY_STATUS 1632 NTAPI 1633 CmCheckRegistry( 1634 _In_ PCMHIVE RegistryHive, 1635 _In_ ULONG Flags) 1636 { 1637 CM_CHECK_REGISTRY_STATUS CmStatusCode; 1638 PHHIVE Hive; 1639 BOOLEAN ShouldFixHive = FALSE; 1640 1641 PAGED_CODE(); 1642 1643 /* Bail out if the caller did not give a hive */ 1644 if (!RegistryHive) 1645 { 1646 DPRINT1("No registry hive given for check\n"); 1647 return CM_CHECK_REGISTRY_INVALID_PARAMETER; 1648 } 1649 1650 #if !defined(CMLIB_HOST) && !defined(_BLDR_) 1651 /* 1652 * The master hive is the root of the registry, 1653 * it holds all other hives together. So do not 1654 * do any validation checks. 1655 */ 1656 if (RegistryHive == CmiVolatileHive) 1657 { 1658 DPRINT("This is master registry hive, don't do anything\n"); 1659 return CM_CHECK_REGISTRY_GOOD; 1660 } 1661 #endif 1662 1663 /* Bail out if no valid flag is given */ 1664 if (Flags & ~(CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES | 1665 CM_CHECK_REGISTRY_PURGE_VOLATILES | 1666 CM_CHECK_REGISTRY_BOOTLOADER_PURGE_VOLATILES | 1667 CM_CHECK_REGISTRY_VALIDATE_HIVE | 1668 CM_CHECK_REGISTRY_FIX_HIVE)) 1669 { 1670 DPRINT1("Invalid flag for registry check given (flag %lu)\n", Flags); 1671 return CM_CHECK_REGISTRY_INVALID_PARAMETER; 1672 } 1673 1674 /* 1675 * Obtain the hive and check if the caller wants 1676 * that the hive to be validated. 1677 */ 1678 Hive = GET_HHIVE(RegistryHive); 1679 if (Flags & CM_CHECK_REGISTRY_VALIDATE_HIVE) 1680 { 1681 CmStatusCode = HvValidateHive(Hive); 1682 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode)) 1683 { 1684 DPRINT1("The hive is not valid (hive 0x%p, check status code %lu)\n", Hive, CmStatusCode); 1685 return CmStatusCode; 1686 } 1687 } 1688 1689 /* 1690 * A registry repair tool such as the ReactOS Check Registry 1691 * Utility wants the damaged hive to be fixed as we check the 1692 * target hive. 1693 */ 1694 if (Flags & CM_CHECK_REGISTRY_FIX_HIVE) 1695 { 1696 ShouldFixHive = TRUE; 1697 } 1698 1699 /* 1700 * FIXME: Currently ReactOS does not implement security 1701 * caching algorithms so it's pretty pointless to implement 1702 * security descriptors validation checks at this moment. 1703 * When the time comes to implement these, we would need 1704 * to implement security checks here as well. 1705 */ 1706 1707 /* Call the internal API to do the rest of the work bulk */ 1708 CmStatusCode = CmpValidateRegistryInternal(Hive, Flags, FALSE, ShouldFixHive); 1709 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode)) 1710 { 1711 DPRINT1("The hive is not valid (hive 0x%p, check status code %lu)\n", Hive, CmStatusCode); 1712 return CmStatusCode; 1713 } 1714 1715 return CmStatusCode; 1716 } 1717 1718 /* EOF */ 1719