/*++ Copyright (c) 1998-1999 Microsoft Corporation Module Name: verifyBlob.c Abstract: This file contains blob verification code Author: Neal Christiansen (nealch) 12/18/2000 Revision History: --*/ #include "precomp.h" // // If DEBUG is enabled we want to display all of the errors. // if NOT DEBUG then return on first error // #if DBG #define HANDLE_FAILURE(_good) ((_good) = FALSE) #else #define HANDLE_FAILURE(_good) return FALSE #endif // // Private prototypes. // BOOL SrVerifyBlobHeader( BlobHeader *BlobHead, PCHAR Name, DWORD BlobType ); BOOL SrVerifyHashHeader( ListHeader *HashHead, PCHAR Name, DWORD Offset ); BOOL SrVerifyHash( ListHeader *HashHead, PCHAR Name, DWORD Offset ); BOOL SrVerifyTreeHeader( TreeHeader *TreeHead, PCHAR Name ); BOOL SrVerifyTree( TreeHeader *TreeHead, PCHAR Name, DWORD Offset ); #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, SrVerifyBlobHeader ) #pragma alloc_text( PAGE, SrVerifyHashHeader ) #pragma alloc_text( PAGE, SrVerifyHash ) #pragma alloc_text( PAGE, SrVerifyTreeHeader ) #pragma alloc_text( PAGE, SrVerifyTree ) #pragma alloc_text( PAGE, SrVerifyBlob ) #endif // ALLOC_PRAGMA BOOL SrVerifyBlobHeader( BlobHeader *BlobHead, PCHAR Name, DWORD BlobType ) /*++ Routine Description: Verify that the given BLOB Header is valid Arguments: Return Value: TRUE if OK else FALSE --*/ { BOOL good = TRUE; UNREFERENCED_PARAMETER( Name ); if (BlobHead->m_dwVersion != BLOB_VERSION_NUM) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyBlobHeader: Invalid VERSION in %s blob header, was %08x, should be %08x\n", Name, BlobHead->m_dwVersion, BLOB_VERSION_NUM) ); HANDLE_FAILURE(good); } if (BlobHead->m_dwBlbType != BlobType) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyBlobHeader: Invalid TYPE in %s blob header, was %08x, should be %08x\n", Name, BlobHead->m_dwBlbType, BlobType) ); HANDLE_FAILURE(good); } if (BlobHead->m_dwMagicNum != BLOB_MAGIC_NUM) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyBlobHeader: Invalid MAGIC NUMBER in %s blob header, was %08x, should be %08x\n", Name, BlobHead->m_dwMagicNum, BLOB_MAGIC_NUM) ); HANDLE_FAILURE(good); } if ((BlobHead->m_dwEntries <= 0) || (BlobHead->m_dwEntries >= 10000)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyBlobHeader: Invalid ENTRIES in %s blob header, was %08x, should be > 0 and < 10,000\n", Name, BlobHead->m_dwEntries) ); HANDLE_FAILURE(good); } return good; } BOOL SrVerifyHashHeader( ListHeader *HashHead, PCHAR Name, DWORD Offset ) /*++ Routine Description: Verify that the given TREE Header is valid Arguments: Return Value: TRUE if OK else FALSE --*/ { BOOL good = TRUE; DWORD calculatedSize; DWORD numNodes; UNREFERENCED_PARAMETER( Offset ); // // Verify BLOB header // if (!SrVerifyBlobHeader(&HashHead->m_BlobHeader,Name,BLOB_TYPE_HASHLIST)) { return FALSE; } // // paulmcd: jan/2001 // m_iHashBuckets will not be exact with m_dwEntries as it is the next // highest prime number. but it will always be larger than or equalto. // if (HashHead->m_iHashBuckets < HashHead->m_BlobHeader.m_dwEntries) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyHashHeader: Invalid HASH BUCKET COUNT in %s header, is %08x, should be %08x or %08x\n", Name, HashHead->m_iHashBuckets, HashHead->m_BlobHeader.m_dwEntries, HashHead->m_BlobHeader.m_dwEntries+1) ); HANDLE_FAILURE(good); } if ((HashHead->m_dwDataOff != HashHead->m_BlobHeader.m_dwMaxSize)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyHashHeader: Invalid DATA OFFSET in %s header, is %08x, should be %08x\n", Name, HashHead->m_dwDataOff, HashHead->m_BlobHeader.m_dwMaxSize) ); HANDLE_FAILURE(good); } if ((HashHead->m_iFreeNode != 0)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyHashHeader: Invalid FREE NODE in %s header, is %08x, should be 0\n", Name, HashHead->m_iFreeNode) ); HANDLE_FAILURE(good); } // // Make sure the calucalted size is accurate // numNodes = HashHead->m_BlobHeader.m_dwEntries + 1; calculatedSize = sizeof(ListHeader) + (HashHead->m_iHashBuckets * sizeof(DWORD)) + (numNodes * sizeof(ListEntry)); if (calculatedSize >= HashHead->m_BlobHeader.m_dwMaxSize) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyHashHeader: Invalid CALCULATED SIZE in %s header, is %08x, should be < %08x\n", Name, calculatedSize, HashHead->m_BlobHeader.m_dwMaxSize) ); HANDLE_FAILURE(good); } return good; } BOOL SrVerifyHash( ListHeader *HashHead, PCHAR Name, DWORD Offset ) /*++ Routine Description: Verify that the given TREE entries are valid Arguments: Return Value: TRUE if OK else FALSE --*/ { BOOL good = TRUE; UINT i; DWORD dataStart; DWORD numNodes; DWORD numBuckets; DWORD *hTable; ListEntry *hNode; // // Validate the HEADER // if (!SrVerifyHashHeader(HashHead,Name,Offset)) { return FALSE; } // // paulmcd: we have hash bucket and actual nodes. there is one extra // actual node than reported due to offset zero being null. the bucket // count is the next largest prime from numNodes // numBuckets = HashHead->m_iHashBuckets; numNodes = HashHead->m_BlobHeader.m_dwEntries + 1; // // Validate Hash table entries // hTable = (DWORD *)(HashHead + 1); for (i=0;i < HashHead->m_iHashBuckets;i++,hTable++) { if (*hTable >= numNodes) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyHash: Invalid HASH TABLE ENTRY[%d] in %s, is %08x, should be < %08x\n", i, Name, *hTable, numNodes) ); HANDLE_FAILURE(good); } } // // Validate the start of the Hash LIST entries // { ULONG_PTR actualOffset; ULONG_PTR calculatedOffset; actualOffset = (ULONG_PTR)hTable - (ULONG_PTR)HashHead; calculatedOffset = sizeof(ListHeader) + (HashHead->m_iHashBuckets * sizeof(DWORD)); if (calculatedOffset != actualOffset) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyHash: Invalid HASH LIST START in %s, offset is %08x, should be %08x\n", Name, actualOffset, calculatedOffset) ); HANDLE_FAILURE(good); } } // // Validate the Hash DATA entries // hNode = (ListEntry *)hTable; dataStart = sizeof(ListHeader) + (HashHead->m_iHashBuckets * sizeof(DWORD)) + (numNodes * sizeof(ListEntry)); for (i=0;i < numNodes;i++,hNode++) { if ((hNode->m_iNext < 0) || (hNode->m_iNext >= (INT)HashHead->m_iHashBuckets)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyHash: Invalid HASH NODE[%d] NEXT INDEX in %s, is %08x, should be < %08x\n", i, Name, hNode->m_iNext, HashHead->m_iHashBuckets) ); HANDLE_FAILURE(good); } if ((hNode->m_dwData != 0) && ((hNode->m_dwData < dataStart) || (hNode->m_dwData >= HashHead->m_BlobHeader.m_dwMaxSize))) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyHash: Invalid HASH NODE[%d] DATA INDEX in %s, is %08x, should be >= %08x and < %08x\n", i, Name, hNode->m_dwData, dataStart, HashHead->m_BlobHeader.m_dwMaxSize) ); HANDLE_FAILURE(good); } else if ((hNode->m_dwData != 0) && ((WCHAR)hNode->m_dwDataLen != *(PWCHAR)((DWORD_PTR)HashHead + hNode->m_dwData))) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyHash: Invalid HASH NODE[%d] DATA LENGTH in %s, is %08x, should be %08x\n", i, Name, hNode->m_dwDataLen, *(PWCHAR)((DWORD_PTR)HashHead + hNode->m_dwData)) ); HANDLE_FAILURE(good); } if ((hNode->m_dwType != NODE_TYPE_UNKNOWN) && (hNode->m_dwType != NODE_TYPE_INCLUDE) && (hNode->m_dwType != NODE_TYPE_EXCLUDE)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyHash: Invalid HASH NODE[%d] TYPE in %s, is %08x, should be %08x, %08x or %08x\n", i, Name, hNode->m_dwType, NODE_TYPE_UNKNOWN, NODE_TYPE_INCLUDE, NODE_TYPE_EXCLUDE) ); HANDLE_FAILURE(good); } } return good; } BOOL SrVerifyTreeHeader( TreeHeader *TreeHead, PCHAR Name ) /*++ Routine Description: Verify that the given TREE Header is valid Arguments: Return Value: TRUE if OK else FALSE --*/ { BOOL good = TRUE; DWORD calculatedSize; // // Verify BLOB header // if (!SrVerifyBlobHeader(&TreeHead->m_BlobHeader,Name,BLOB_TYPE_PATHTREE)) { return FALSE; } if ((TreeHead->m_dwMaxNodes != TreeHead->m_BlobHeader.m_dwEntries)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyTreeHeader: Invalid MAX NODES in %s header, is %08x, should be %08x\n", Name, TreeHead->m_dwMaxNodes, TreeHead->m_BlobHeader.m_dwEntries) ); HANDLE_FAILURE(good); } if ((TreeHead->m_dwDataSize >= TreeHead->m_BlobHeader.m_dwMaxSize)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyTreeHeader: Invalid DATA SIZE in %s header, is %08x, should be < %08x\n", Name, TreeHead->m_dwDataSize, TreeHead->m_BlobHeader.m_dwMaxSize) ); HANDLE_FAILURE(good); } if ((TreeHead->m_dwDataOff != TreeHead->m_BlobHeader.m_dwMaxSize)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyTreeHeader: Invalid DATA OFFSET in %s header, is %08x, should be %08x\n", Name, TreeHead->m_dwDataOff, TreeHead->m_BlobHeader.m_dwMaxSize) ); HANDLE_FAILURE(good); } if ((TreeHead->m_iFreeNode != 0)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyTreeHeader: Invalid FREE NODE in %s header, is %08x, should be 0\n", Name, TreeHead->m_iFreeNode) ); HANDLE_FAILURE(good); } if ((TreeHead->m_dwDefault != NODE_TYPE_EXCLUDE)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyTreeHeader: Invalid DEFAULT in %s header, is %08x, should be %08x\n", Name, TreeHead->m_dwDefault, NODE_TYPE_EXCLUDE) ); HANDLE_FAILURE(good); } // // Make sure the calucalted size is accurate // calculatedSize = sizeof(TreeHeader) + (TreeHead->m_dwMaxNodes * sizeof(TreeNode)) + TreeHead->m_dwDataSize; if (calculatedSize != TreeHead->m_BlobHeader.m_dwMaxSize) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyTreeHeader: Invalid CALCULATED SIZE in %s header, is %08x, should be %08x\n", Name, calculatedSize, TreeHead->m_BlobHeader.m_dwMaxSize) ); HANDLE_FAILURE(good); } return good; } BOOL SrVerifyTree( TreeHeader *TreeHead, PCHAR Name, DWORD Offset ) /*++ Routine Description: Verify that the given TREE entries are valid Arguments: Return Value: TRUE if OK else FALSE --*/ { BOOL good = TRUE; UINT i; DWORD dataStart; TreeNode *tn; ListHeader *localHashHead; char localName[128]; // // Validate the HEADER // if (!SrVerifyTreeHeader(TreeHead,Name)) { return FALSE; } // // Validate the DATA entries // tn = (TreeNode *)(TreeHead + 1); dataStart = sizeof(TreeHeader) + (TreeHead->m_dwMaxNodes * sizeof(TreeNode)); for (i=0;i < TreeHead->m_dwMaxNodes;i++,tn++) { if ((tn->m_iFather < 0) || (tn->m_iFather >= (INT)TreeHead->m_dwMaxNodes)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyTree: Invalid TREE NODE[%d] FATHER index in %s, is %08x, should be < %08x\n", i, Name, tn->m_iFather, TreeHead->m_dwMaxNodes) ); HANDLE_FAILURE(good); } if ((tn->m_iSon < 0) || (tn->m_iSon >= (INT)TreeHead->m_dwMaxNodes)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyTree: Invalid TREE NODE[%d] SON index in %s, is %08x, should be < %08x\n", i, Name, tn->m_iSon, TreeHead->m_dwMaxNodes) ); HANDLE_FAILURE(good); } if ((tn->m_iSibling < 0) || (tn->m_iSibling >= (INT)TreeHead->m_dwMaxNodes)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyTree: Invalid TREE NODE[%d] SIBLING index in %s, is %08x, should be < %08x\n", i, Name, tn->m_iSibling, TreeHead->m_dwMaxNodes) ); HANDLE_FAILURE(good); } if ((tn->m_dwData < dataStart) || (tn->m_dwData >= TreeHead->m_BlobHeader.m_dwMaxSize)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyTree: Invalid TREE NODE[%d] DATA index in %s, is %08x, should be >= %08x and < %08x\n", i, Name, tn->m_dwData, dataStart, TreeHead->m_BlobHeader.m_dwMaxSize) ); HANDLE_FAILURE(good); } if (tn->m_dwFileList != 0) { if ((tn->m_dwFileList < dataStart) || (tn->m_dwFileList >= TreeHead->m_BlobHeader.m_dwMaxSize)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyTree: Invalid TREE NODE[%d] FILELIST index in %s, is %08x, should be >= %08x and < %08x\n", i, Name, tn->m_dwData, dataStart, TreeHead->m_BlobHeader.m_dwMaxSize) ); HANDLE_FAILURE(good); } else { localHashHead = (ListHeader *)((DWORD_PTR)TreeHead + tn->m_dwFileList); sprintf(localName,"TreeNode[%d]",i); if (!SrVerifyHash(localHashHead,localName,(Offset+tn->m_dwFileList))) { HANDLE_FAILURE(good); } } } if ((tn->m_dwType != NODE_TYPE_UNKNOWN) && (tn->m_dwType != NODE_TYPE_INCLUDE) && (tn->m_dwType != NODE_TYPE_EXCLUDE)) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyTree: Invalid TREE NODE[%d] TYPE in %s, is %08x, should be %08x, %08x or %08x\n", i, Name, tn->m_dwType, NODE_TYPE_UNKNOWN, NODE_TYPE_INCLUDE, NODE_TYPE_EXCLUDE) ); HANDLE_FAILURE(good); } } return good; } BOOL SrVerifyBlob( PBYTE Blob ) /*++ Routine Description: Verify that the given BLOB is valid Arguments: Return Value: TRUE if OK else FALSE --*/ { BlobHeader *blobHead; TreeHeader *treeHead; ListHeader *hashHead; DWORD calculatedSize = 0; // // Verify header to entire blob // blobHead = (BlobHeader *)Blob; if (!SrVerifyBlobHeader(blobHead,"PRIMARY",BLOB_TYPE_CONTAINER)) { return FALSE; } calculatedSize += sizeof(BlobHeader); // // Verify TreeHeader and Data // treeHead = (TreeHeader *)(Blob + calculatedSize); if (!SrVerifyTree(treeHead,"PRIMARY TREE",calculatedSize)) { return FALSE; } calculatedSize += treeHead->m_BlobHeader.m_dwMaxSize; // // Verify HashHeader and DATA // hashHead = (ListHeader *)(Blob + calculatedSize); if (!SrVerifyHash(hashHead,"PRIMARY HASH",calculatedSize)) { return FALSE; } calculatedSize += hashHead->m_BlobHeader.m_dwMaxSize; // // Validate total SIZE // if (calculatedSize != blobHead->m_dwMaxSize) { SrTrace( BLOB_VERIFICATION, ("sr!SrVerifyBlob: Invalid PRIMARY BLOB size, is %08x, calculated to be %08x\n", blobHead->m_dwMaxSize, calculatedSize) ); return FALSE; } return TRUE; }