/*++ Copyright (c) 1991 Microsoft Corporation Module Name: cmchek.c Abstract: This module implements consistency checking for the registry. This module can be linked standalone, cmchek2.c cannot. Author: Bryan M. Willman (bryanwi) 27-Jan-92 Environment: Revision History: --*/ #include "cmp.h" #define REG_MAX_PLAUSIBLE_KEY_SIZE \ ((FIELD_OFFSET(CM_KEY_NODE, Name)) + \ (sizeof(WCHAR) * MAX_KEY_NAME_LENGTH) + 16) // // Global data used to support checkout // extern ULONG CmpUsedStorage; extern PCMHIVE CmpMasterHive; // // Private prototypes // ULONG CmpCheckRegistry2( HCELL_INDEX Cell, HCELL_INDEX ParentCell ); ULONG CmpCheckKey( HCELL_INDEX Cell, HCELL_INDEX ParentCell ); ULONG CmpCheckValueList( PHHIVE Hive, PCELL_DATA List, ULONG Count ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE,CmCheckRegistry) #pragma alloc_text(PAGE,CmpCheckRegistry2) #pragma alloc_text(PAGE,CmpCheckKey) #pragma alloc_text(PAGE,CmpCheckValueList) #endif // // debug structures // extern struct { PHHIVE Hive; ULONG Status; } CmCheckRegistryDebug; extern struct { PHHIVE Hive; ULONG Status; } CmpCheckRegistry2Debug; extern struct { PHHIVE Hive; ULONG Status; HCELL_INDEX Cell; PCELL_DATA CellPoint; PVOID RootPoint; ULONG Index; } CmpCheckKeyDebug; extern struct { PHHIVE Hive; ULONG Status; PCELL_DATA List; ULONG Index; HCELL_INDEX Cell; PCELL_DATA CellPoint; } CmpCheckValueListDebug; // // globals private to check code // extern PHHIVE CmpCheckHive; extern BOOLEAN CmpCheckClean; ULONG CmCheckRegistry( PCMHIVE CmHive, BOOLEAN Clean ) /*++ Routine Description: Check consistency of the registry within a given hive. Start from root, and check that: . Each child key points back to its parent. . All allocated cells are refered to exactly once (requires looking inside the hive structure...) [This also detects space leaks.] . All allocated cells are reachable from the root. NOTE: Exactly 1 ref rule may change with security. Arguments: CmHive - supplies a pointer to the CM hive control structure for the hive of interest. Clean - if TRUE, references to volatile cells will be zapped (done at startup only to avoid groveling hives twice.) if FALSE, nothing will be changed. Return Value: 0 if Hive is OK. Error return indicator if not. RANGE: 3000 - 3999 --*/ { PHHIVE Hive; ULONG rc = 0; ULONG Storage; if (CmHive == CmpMasterHive) { return(0); } CmpUsedStorage = 0; CmCheckRegistryDebug.Hive = (PHHIVE)CmHive; CmCheckRegistryDebug.Status = 0; // // check the underlying hive and get storage use // Hive = &CmHive->Hive; rc = HvCheckHive(Hive, &Storage); if (rc != 0) { CmCheckRegistryDebug.Status = rc; return rc; } // // Call recursive helper to check out the values and subkeys // CmpCheckHive = (PHHIVE)CmHive; CmpCheckClean = Clean; rc = CmpCheckRegistry2(Hive->BaseBlock->RootCell, HCELL_NIL); // // Validate all the security descriptors in the hive // if (!CmpValidateHiveSecurityDescriptors(Hive)) { KdPrint(("CmCheckRegistry:")); KdPrint((" CmpValidateHiveSecurityDescriptors failed\n")); rc = 3040; CmCheckRegistryDebug.Status = rc; } #if 0 // // Check allocated storage against used storage // if (CmpUsedStorage != Storage) { KdPrint(("CmCheckRegistry:")); KdPrint((" Storage Used:%08lx Allocated:%08lx\n", UsedStorage, Storage)); rc = 3042; CmCheckRegistryDebug.Status = rc; } #endif // // Print a bit of a summary (make sure this data avail in all error cases) // if (rc > 0) { KdPrint(("CmCheckRegistry Failed (%d): CmHive:%08lx\n", rc, CmHive)); KdPrint((" Hive:%08lx Root:%08lx\n", Hive, Hive->BaseBlock->RootCell)); } return rc; } ULONG CmpCheckRegistry2( HCELL_INDEX Cell, HCELL_INDEX ParentCell ) /*++ Routine Description: Check consistency of the registry, from a particular cell on down. . Check that the cell's value list, child key list, class, security are OK. . Check that each value entry IN the list is OK. . Apply self to each child key list. Globals: CmpCheckHive - hive we're working on CmpCheckClean - flag TRUE -> clean off volatiles, FALSE -> don't Arguments: Cell - HCELL_INDEX of subkey to work on. ParentCell - expected value of parent cell for Cell, unless HCELL_NIL, in which case ignore. Return Value: 0 if Hive is OK. Error return indicator if not. RANGE: 4000 - 4999 --*/ { ULONG Index = 0; HCELL_INDEX StartCell = Cell; HCELL_INDEX SubKey; ULONG rc = 0; PCELL_DATA pcell; CmpCheckRegistry2Debug.Hive = CmpCheckHive; CmpCheckRegistry2Debug.Status = 0; // // A jump to NewKey amounts to a virtual call to check the // next child cell. (a descent into the tree) // // Cell, ParentCell, Index, and globals are defined // NewKey: rc = CmpCheckKey(Cell, ParentCell); if (rc != 0) { KdPrint(("\tChild is list entry #%08lx\n", Index)); CmpCheckRegistry2Debug.Status = rc; return rc; } // // save Index and check out children // pcell = HvGetCell(CmpCheckHive, Cell); pcell->u.KeyNode.WorkVar = Index; for (Index = 0; Indexu.KeyNode.SubKeyCounts[Stable]; Index++) { SubKey = CmpFindSubKeyByNumber(CmpCheckHive, (PCM_KEY_NODE)HvGetCell(CmpCheckHive,Cell), Index); // // "recurse" onto child // ParentCell = Cell; Cell = SubKey; goto NewKey; ResumeKey:; // A jump here is a virtual return // Cell, ParentCell and Index // must be defined } // // since we're here, we've checked out all the children // of the current cell. // if (Cell == StartCell) { // // we are done // return 0; } // // "return" to "parent instance" // pcell = HvGetCell(CmpCheckHive, Cell); Index = pcell->u.KeyNode.WorkVar; Cell = ParentCell; pcell = HvGetCell(CmpCheckHive, Cell); ParentCell = pcell->u.KeyNode.Parent; goto ResumeKey; } ULONG CmpCheckKey( HCELL_INDEX Cell, HCELL_INDEX ParentCell ) /*++ Routine Description: Check consistency of the registry, for a particular cell . Check that the cell's value list, child key list, class, security are OK. . Check that each value entry IN the list is OK. Globals: CmpCheckHive - hive we're working on CmpCheckClean - flag TRUE -> clean off volatiles, FALSE -> don't Arguments: Cell - HCELL_INDEX of subkey to work on. ParentCell - expected value of parent cell for Cell, unless HCELL_NIL, in which case ignore. Return Value: 0 if Hive is OK. Error return indicator if not. RANGE: 4000 - 4999 --*/ { PCELL_DATA pcell; ULONG size; ULONG usedlen; ULONG ClassLength; HCELL_INDEX Class; ULONG ValueCount; HCELL_INDEX ValueList; HCELL_INDEX Security; ULONG rc = 0; ULONG nrc = 0; ULONG i; PCM_KEY_INDEX Root; PCM_KEY_INDEX Leaf; ULONG SubCount; CmpCheckKeyDebug.Hive = CmpCheckHive; CmpCheckKeyDebug.Status = 0; CmpCheckKeyDebug.Cell = Cell; CmpCheckKeyDebug.CellPoint = NULL; CmpCheckKeyDebug.RootPoint = NULL; CmpCheckKeyDebug.Index = (ULONG)-1; // // Check key itself // if (! HvIsCellAllocated(CmpCheckHive, Cell)) { KdPrint(("CmpCheckKey: CmpCheckHive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); KdPrint(("\tNot allocated\n")); rc = 4010; CmpCheckKeyDebug.Status = rc; return rc; } pcell = HvGetCell(CmpCheckHive, Cell); SetUsed(CmpCheckHive, Cell); CmpCheckKeyDebug.CellPoint = pcell; size = HvGetCellSize(CmpCheckHive, pcell); if (size > REG_MAX_PLAUSIBLE_KEY_SIZE) { KdPrint(("CmpCheckKey: CmpCheckHive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); KdPrint(("\tImplausible size\n")); rc = 4020; CmpCheckKeyDebug.Status = rc; return rc; } usedlen = FIELD_OFFSET(CM_KEY_NODE, Name) + pcell->u.KeyNode.NameLength; if (usedlen > size) { KdPrint(("CmpCheckKey: CmpCheckHive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); KdPrint(("\tKey is bigger than containing cell.\n")); rc = 4030; CmpCheckKeyDebug.Status = rc; return rc; } if (pcell->u.KeyNode.Signature != CM_KEY_NODE_SIGNATURE) { KdPrint(("CmpCheckKey: CmpCheckHive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); KdPrint(("\tNo key signature\n")); rc = 4040; CmpCheckKeyDebug.Status = rc; return rc; } if (ParentCell != HCELL_NIL) { if (pcell->u.KeyNode.Parent != ParentCell) { KdPrint(("CmpCheckKey: CmpCheckHive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); KdPrint(("\tWrong parent value.\n")); rc = 4045; CmpCheckKeyDebug.Status = rc; return rc; } } ClassLength = pcell->u.KeyNode.ClassLength; Class = pcell->u.KeyNode.u1.s1.Class; ValueCount = pcell->u.KeyNode.ValueList.Count; ValueList = pcell->u.KeyNode.ValueList.List; Security = pcell->u.KeyNode.u1.s1.Security; // // Check simple non-empty cases // if (ClassLength > 0) { if (HvIsCellAllocated(CmpCheckHive, Class) == FALSE) { KdPrint(("CmpCheckKey: CmpCheckHive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); KdPrint(("\tClass:%08lx - unallocated class\n", Class)); rc = 4080; CmpCheckKeyDebug.Status = rc; return rc; } else { SetUsed(CmpCheckHive, Class); } } if (Security != HCELL_NIL) { if (HvIsCellAllocated(CmpCheckHive, Security) == FALSE) { KdPrint(("CmpCheckKey: CmpCheckHive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); KdPrint(("\tSecurity:%08lx - unallocated security\n", Security)); rc = 4090; CmpCheckKeyDebug.Status = rc; return rc; } else if (HvGetCellType(Security) == Volatile) { SetUsed(CmpCheckHive, Security); } // // Else CmpValidateHiveSecurityDescriptors must do computation // } // // Check value list case // if (ValueCount > 0) { if (HvIsCellAllocated(CmpCheckHive, ValueList) == FALSE) { KdPrint(("CmpCheckKey: CmpCheckHive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); KdPrint(("\tValueList:%08lx - unallocated valuelist\n", ValueList)); rc = 4100; CmpCheckKeyDebug.Status = rc; return rc; } else { SetUsed(CmpCheckHive, ValueList); pcell = HvGetCell(CmpCheckHive, ValueList); nrc = CmpCheckValueList(CmpCheckHive, pcell, ValueCount); if (nrc != 0) { KdPrint(("List was for CmpCheckHive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); rc = nrc; CmpCheckKeyDebug.CellPoint = pcell; CmpCheckKeyDebug.Status = rc; return rc; } } } // // Check subkey list case // pcell = HvGetCell(CmpCheckHive, Cell); CmpCheckKeyDebug.CellPoint = pcell; if ((HvGetCellType(Cell) == Volatile) && (pcell->u.KeyNode.SubKeyCounts[Stable] != 0)) { KdPrint(("CmpCheckKey: CmpCheckHive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); KdPrint(("\tVolatile Cell has Stable children\n")); rc = 4108; CmpCheckKeyDebug.Status = rc; return rc; } else if (pcell->u.KeyNode.SubKeyCounts[Stable] > 0) { if (! HvIsCellAllocated(CmpCheckHive, pcell->u.KeyNode.SubKeyLists[Stable])) { KdPrint(("CmpCheckKey: CmpCheckHive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); KdPrint(("\tStableKeyList:%08lx - unallocated\n", pcell->u.KeyNode.SubKeyLists[Stable])); rc = 4110; CmpCheckKeyDebug.Status = rc; return rc; } else { SetUsed(CmpCheckHive, pcell->u.KeyNode.SubKeyLists[Stable]); // // Prove that the index is OK // Root = (PCM_KEY_INDEX)HvGetCell( CmpCheckHive, pcell->u.KeyNode.SubKeyLists[Stable] ); CmpCheckKeyDebug.RootPoint = Root; if ((Root->Signature == CM_KEY_INDEX_LEAF) || (Root->Signature == CM_KEY_FAST_LEAF)) { if ((ULONG)Root->Count != pcell->u.KeyNode.SubKeyCounts[Stable]) { KdPrint(("CmpCheckKey: CmpCheckHive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); KdPrint(("\tBad Index count @%08lx\n", Root)); rc = 4120; CmpCheckKeyDebug.Status = rc; return rc; } } else if (Root->Signature == CM_KEY_INDEX_ROOT) { SubCount = 0; for (i = 0; i < Root->Count; i++) { CmpCheckKeyDebug.Index = i; if (! HvIsCellAllocated(CmpCheckHive, Root->List[i])) { KdPrint(("CmpCheckKey: Hive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); KdPrint(("\tBad Leaf Cell %08lx Root@%08lx\n", Root->List[i], Root)); rc = 4130; CmpCheckKeyDebug.Status = rc; return rc; } Leaf = (PCM_KEY_INDEX)HvGetCell(CmpCheckHive, Root->List[i]); if ((Leaf->Signature != CM_KEY_INDEX_LEAF) && (Leaf->Signature != CM_KEY_FAST_LEAF)) { KdPrint(("CmpCheckKey: CmpCheckHive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); KdPrint(("\tBad Leaf Index @%08lx Root@%08lx\n", Leaf, Root)); rc = 4140; CmpCheckKeyDebug.Status = rc; return rc; } SetUsed(CmpCheckHive, Root->List[i]); SubCount += Leaf->Count; } if (pcell->u.KeyNode.SubKeyCounts[Stable] != SubCount) { KdPrint(("CmpCheckKey: CmpCheckHive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); KdPrint(("\tBad count in index, SubCount=%08lx\n", SubCount)); rc = 4150; CmpCheckKeyDebug.Status = rc; return rc; } } else { KdPrint(("CmpCheckKey: CmpCheckHive:%08lx Cell:%08lx\n", CmpCheckHive, Cell)); KdPrint(("\tBad Root index signature @%08lx\n", Root)); rc = 4120; CmpCheckKeyDebug.Status = rc; return rc; } } } // // force volatiles to be empty, if this is a load operation // if (CmpCheckClean == TRUE) { pcell->u.KeyNode.SubKeyCounts[Volatile] = 0; pcell->u.KeyNode.SubKeyLists[Volatile] = HCELL_NIL; } return rc; } ULONG CmpCheckValueList( PHHIVE Hive, PCELL_DATA List, ULONG Count ) /*++ Routine Description: Check consistency of a value list. . Each element allocated? . Each element have valid signature? . Data properly allocated? Arguments: Hive - containing Hive. List - pointer to an array of HCELL_INDEX entries. Count - number of entries in list. Return Value: 0 if Hive is OK. Error return indicator if not. RANGE: 5000 - 5999 --*/ { ULONG i; HCELL_INDEX Cell; PCELL_DATA pcell; ULONG size; ULONG usedlen; ULONG DataLength; HCELL_INDEX Data; ULONG rc = 0; CmpCheckValueListDebug.Hive = Hive; CmpCheckValueListDebug.Status = 0; CmpCheckValueListDebug.List = List; CmpCheckValueListDebug.Index = (ULONG)-1; CmpCheckValueListDebug.Cell = 0; // NOT HCELL_NIL CmpCheckValueListDebug.CellPoint = NULL; for (i = 0; i < Count; i++) { // // Check out value entry's refs. // Cell = List->u.KeyList[i]; if (Cell == HCELL_NIL) { KdPrint(("CmpCheckValueList: List:%08lx i:%08lx\n", List, i)); KdPrint(("\tEntry is null\n")); rc = 5010; CmpCheckValueListDebug.Status = rc; CmpCheckValueListDebug.Index = i; CmpCheckValueListDebug.Cell = Cell; return rc; } if (HvIsCellAllocated(Hive, Cell) == FALSE) { KdPrint(("CmpCheckValueList: List:%08lx i:%08lx\n", List, i)); KdPrint(("\tEntry is not allocated\n")); rc = 5020; CmpCheckValueListDebug.Status = rc; CmpCheckValueListDebug.Index = i; CmpCheckValueListDebug.Cell = Cell; return rc; } else { SetUsed(Hive, Cell); } // // Check out the value entry itself // pcell = HvGetCell(Hive, Cell); size = HvGetCellSize(Hive, pcell); if (pcell->u.KeyValue.Signature != CM_KEY_VALUE_SIGNATURE) { KdPrint(("CmpCheckValueList: List:%08lx i:%08lx\n", List, i)); KdPrint(("\tCell:%08lx - invalid value signature\n", Cell)); rc = 5030; CmpCheckValueListDebug.Status = rc; CmpCheckValueListDebug.Index = i; CmpCheckValueListDebug.Cell = Cell; CmpCheckValueListDebug.CellPoint = pcell; return rc; } usedlen = FIELD_OFFSET(CM_KEY_VALUE, Name) + pcell->u.KeyValue.NameLength; if (usedlen > size) { KdPrint(("CmpCheckValueList: List:%08lx i:%08lx\n", List, i)); KdPrint(("\tCell:%08lx - value bigger than containing cell\n", Cell)); rc = 5040; CmpCheckValueListDebug.Status = rc; CmpCheckValueListDebug.Index = i; CmpCheckValueListDebug.Cell = Cell; CmpCheckValueListDebug.CellPoint = pcell; return rc; } // // Check out value entry's data // DataLength = pcell->u.KeyValue.DataLength; if (DataLength < CM_KEY_VALUE_SPECIAL_SIZE) { Data = pcell->u.KeyValue.Data; if ((DataLength == 0) && (Data != HCELL_NIL)) { KdPrint(("CmpCheckValueList: List:%08lx i:%08lx\n", List, i)); KdPrint(("\tCell:%08lx Data:%08lx - data not null\n", Cell, Data)); rc = 5050; CmpCheckValueListDebug.Status = rc; CmpCheckValueListDebug.Index = i; CmpCheckValueListDebug.Cell = Cell; CmpCheckValueListDebug.CellPoint = pcell; return rc; } if (DataLength > 0) { if (HvIsCellAllocated(Hive, Data) == FALSE) { KdPrint(("CmpCheckValueList: List:%08lx i:%08lx\n", List, i)); KdPrint(("\tCell:%08lx Data:%08lx - unallocated\n", Cell, Data)); rc = 5060; CmpCheckValueListDebug.Status = rc; CmpCheckValueListDebug.Index = i; CmpCheckValueListDebug.Cell = Cell; CmpCheckValueListDebug.CellPoint = pcell; return rc; } else { SetUsed(Hive, Data); } } } } return rc; }