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 %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 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 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 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 %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 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 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 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 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