| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | //***************************************************************************** |
| 5 | // MDFileFormat.cpp |
| 6 | // |
| 7 | |
| 8 | // |
| 9 | // This file contains a set of helpers to verify and read the file format. |
| 10 | // This code does not handle the paging of the data, or different types of |
| 11 | // I/O. See the StgTiggerStorage and StgIO code for this level of support. |
| 12 | // |
| 13 | //***************************************************************************** |
| 14 | |
| 15 | #include "stdafx.h" // Standard header file. |
| 16 | #include "mdfileformat.h" // The format helpers. |
| 17 | #include "posterror.h" // Error handling code. |
| 18 | |
| 19 | //***************************************************************************** |
| 20 | // Verify the signature at the front of the file to see what type it is. |
| 21 | //***************************************************************************** |
| 22 | #define STORAGE_MAGIC_OLD_SIG 0x2B4D4F43 // +MOC (old version of BSJB signature code:STORAGE_MAGIC_SIG) |
| 23 | HRESULT |
| 24 | MDFormat::VerifySignature( |
| 25 | PSTORAGESIGNATURE pSig, // The signature to check. |
| 26 | ULONG cbData) |
| 27 | { |
| 28 | HRESULT hr = S_OK; |
| 29 | |
| 30 | // If signature didn't match, you shouldn't be here. |
| 31 | ULONG dwSignature = pSig->GetSignature(); |
| 32 | if (dwSignature == STORAGE_MAGIC_OLD_SIG) |
| 33 | { |
| 34 | Debug_ReportError("Invalid MetaData storage signature - old magic signature +MOC." ); |
| 35 | return PostError(CLDB_E_FILE_OLDVER, 1, 0); |
| 36 | } |
| 37 | if (dwSignature != STORAGE_MAGIC_SIG) |
| 38 | { |
| 39 | Debug_ReportError("Invalid MetaData storage signature - unrecognized magic signature, should be BSJB." ); |
| 40 | return PostError(CLDB_E_FILE_CORRUPT); |
| 41 | } |
| 42 | |
| 43 | // Check for overflow |
| 44 | ULONG lVersionString = pSig->GetVersionStringLength(); |
| 45 | ULONG sum = sizeof(STORAGESIGNATURE) + lVersionString; |
| 46 | if ((sum < sizeof(STORAGESIGNATURE)) || (sum < lVersionString)) |
| 47 | { |
| 48 | Debug_ReportError("Invalid MetaData storage signature - version string too long, integer overflow." ); |
| 49 | return PostError(CLDB_E_FILE_CORRUPT); |
| 50 | } |
| 51 | |
| 52 | // Check for invalid version string size |
| 53 | if ((sizeof(STORAGESIGNATURE) + lVersionString) > cbData) |
| 54 | { |
| 55 | Debug_ReportError("Invalid MetaData storage signature - version string too long." ); |
| 56 | return PostError(CLDB_E_FILE_CORRUPT); |
| 57 | } |
| 58 | |
| 59 | // Check that the version string is null terminated. This string |
| 60 | // is ANSI, so no double-null checks need to be made. |
| 61 | { |
| 62 | BYTE *pStart = &pSig->pVersion[0]; |
| 63 | BYTE *pEnd = pStart + lVersionString + 1; // Account for terminating NULL |
| 64 | BYTE *pCur; |
| 65 | |
| 66 | for (pCur = pStart; pCur < pEnd; pCur++) |
| 67 | { |
| 68 | if (*pCur == 0) |
| 69 | break; |
| 70 | } |
| 71 | |
| 72 | // If we got to the end without hitting a NULL, we have a bad version string |
| 73 | if (pCur == pEnd) |
| 74 | { |
| 75 | Debug_ReportError("Invalid MetaData storage signature - version string has not null-terminator." ); |
| 76 | return PostError(CLDB_E_FILE_CORRUPT); |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | // Only a specific version of the 0.x format is supported by this code |
| 81 | // in order to support the NT 5 beta clients which used this format. |
| 82 | if (pSig->GetMajorVer() == FILE_VER_MAJOR_v0) |
| 83 | { |
| 84 | if (pSig->GetMinorVer() < FILE_VER_MINOR_v0) |
| 85 | { |
| 86 | Debug_ReportError("Invalid MetaData storage signature - unrecognized version, should be 1.1." ); |
| 87 | hr = CLDB_E_FILE_OLDVER; |
| 88 | } |
| 89 | } |
| 90 | else |
| 91 | // There is currently no code to migrate an old format of the 1.x. This |
| 92 | // would be added only under special circumstances. |
| 93 | if ((pSig->GetMajorVer() != FILE_VER_MAJOR) || (pSig->GetMinorVer() != FILE_VER_MINOR)) |
| 94 | { |
| 95 | Debug_ReportError("Invalid MetaData storage signature - unrecognized version, should be 1.1." ); |
| 96 | hr = CLDB_E_FILE_OLDVER; |
| 97 | } |
| 98 | |
| 99 | if (FAILED(hr)) |
| 100 | hr = PostError(hr, (int)pSig->GetMajorVer(), (int)pSig->GetMinorVer()); |
| 101 | return hr; |
| 102 | } // MDFormat::VerifySignature |
| 103 | |
| 104 | //***************************************************************************** |
| 105 | // Skip over the header and find the actual stream data. |
| 106 | // It doesn't perform any checks for buffer overflow - use GetFirstStream_Verify |
| 107 | // instead. |
| 108 | //***************************************************************************** |
| 109 | PSTORAGESTREAM |
| 110 | MDFormat::( |
| 111 | PSTORAGEHEADER , // Return copy of header struct. |
| 112 | const void *pvMd) // Pointer to the full file. |
| 113 | { |
| 114 | const BYTE *pbMd; |
| 115 | |
| 116 | // Header data starts after signature. |
| 117 | pbMd = (const BYTE *) pvMd; |
| 118 | pbMd += sizeof(STORAGESIGNATURE); |
| 119 | pbMd += ((STORAGESIGNATURE*)pvMd)->GetVersionStringLength(); |
| 120 | PSTORAGEHEADER pHdr = (PSTORAGEHEADER) pbMd; |
| 121 | *pHeader = *pHdr; |
| 122 | pbMd += sizeof(STORAGEHEADER); |
| 123 | |
| 124 | // ECMA specifies that the flags field is "reserved, must be 0". |
| 125 | if (pHdr->GetFlags() != 0) |
| 126 | return NULL; |
| 127 | |
| 128 | // The pointer is now at the first stream in the list. |
| 129 | return ((PSTORAGESTREAM) pbMd); |
| 130 | } // MDFormat::GetFirstStream |
| 131 | |
| 132 | //***************************************************************************** |
| 133 | // Skip over the header and find the actual stream data. Secure version of |
| 134 | // GetFirstStream method. |
| 135 | // The header is supposed to be verified by VerifySignature. |
| 136 | // |
| 137 | // Returns pointer to the first stream (behind storage header) and the size of |
| 138 | // the remaining buffer in *pcbMd (could be 0). |
| 139 | // Returns NULL if there is not enough buffer for reading the headers. The *pcbMd |
| 140 | // could be changed if NULL returned. |
| 141 | // |
| 142 | // Caller has to check available buffer size before using the first stream. |
| 143 | //***************************************************************************** |
| 144 | PSTORAGESTREAM |
| 145 | MDFormat::( |
| 146 | PSTORAGEHEADER , // Return copy of header struct. |
| 147 | const void *pvMd, // Pointer to the full file. |
| 148 | ULONG *pcbMd) // [in, out] Size of pvMd buffer (we don't want to read behind it) |
| 149 | { |
| 150 | const BYTE *pbMd; |
| 151 | |
| 152 | // Header data starts after signature. |
| 153 | pbMd = (const BYTE *)pvMd; |
| 154 | // Check read buffer overflow |
| 155 | if (*pcbMd < sizeof(STORAGESIGNATURE)) |
| 156 | { |
| 157 | Debug_ReportError("Invalid MetaData - Storage signature doesn't fit." ); |
| 158 | return NULL; |
| 159 | } |
| 160 | pbMd += sizeof(STORAGESIGNATURE); |
| 161 | *pcbMd -= sizeof(STORAGESIGNATURE); |
| 162 | |
| 163 | ULONG cbVersionString = ((STORAGESIGNATURE *)pvMd)->GetVersionStringLength(); |
| 164 | // Check read buffer overflow |
| 165 | if (*pcbMd < cbVersionString) |
| 166 | { |
| 167 | Debug_ReportError("Invalid MetaData storage signature - Version string doesn't fit." ); |
| 168 | return NULL; |
| 169 | } |
| 170 | pbMd += cbVersionString; |
| 171 | *pcbMd -= cbVersionString; |
| 172 | |
| 173 | // Is there enough space for storage header? |
| 174 | if (*pcbMd < sizeof(STORAGEHEADER)) |
| 175 | { |
| 176 | Debug_ReportError("Invalid MetaData storage header - Storage header doesn't fit." ); |
| 177 | return NULL; |
| 178 | } |
| 179 | PSTORAGEHEADER pHdr = (PSTORAGEHEADER) pbMd; |
| 180 | *pHeader = *pHdr; |
| 181 | pbMd += sizeof(STORAGEHEADER); |
| 182 | *pcbMd -= sizeof(STORAGEHEADER); |
| 183 | |
| 184 | // ECMA specifies that the flags field is "reserved, must be 0". |
| 185 | if (pHdr->GetFlags() != 0) |
| 186 | { |
| 187 | Debug_ReportError("Invalid MetaData storage header - Flags are not 0." ); |
| 188 | return NULL; |
| 189 | } |
| 190 | |
| 191 | // The pointer is now at the first stream in the list. |
| 192 | return (PSTORAGESTREAM)pbMd; |
| 193 | } // MDFormat::GetFirstStream |
| 194 | |