summaryrefslogtreecommitdiff
path: root/src/md/runtime/mdfileformat.cpp
blob: 844dc3cfae98283484f1c4ae033f6e7c49d05763 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//*****************************************************************************
// MDFileFormat.cpp
// 

//
// This file contains a set of helpers to verify and read the file format.
// This code does not handle the paging of the data, or different types of
// I/O.  See the StgTiggerStorage and StgIO code for this level of support.
//
//*****************************************************************************

#include "stdafx.h"                     // Standard header file.
#include "mdfileformat.h"               // The format helpers.
#include "posterror.h"                  // Error handling code.

//*****************************************************************************
// Verify the signature at the front of the file to see what type it is.
//*****************************************************************************
#define STORAGE_MAGIC_OLD_SIG   0x2B4D4F43  // +MOC (old version of BSJB signature code:STORAGE_MAGIC_SIG)
HRESULT 
MDFormat::VerifySignature(
    PSTORAGESIGNATURE pSig,     // The signature to check.
    ULONG             cbData)
{
    HRESULT hr = S_OK;
    
    // If signature didn't match, you shouldn't be here.
    ULONG dwSignature = pSig->GetSignature();
    if (dwSignature == STORAGE_MAGIC_OLD_SIG)
    {
        Debug_ReportError("Invalid MetaData storage signature - old magic signature +MOC.");
        return PostError(CLDB_E_FILE_OLDVER, 1, 0);
    }
    if (dwSignature != STORAGE_MAGIC_SIG)
    {
        Debug_ReportError("Invalid MetaData storage signature - unrecognized magic signature, should be BSJB.");
        return PostError(CLDB_E_FILE_CORRUPT);
    }
    
    // Check for overflow
    ULONG lVersionString = pSig->GetVersionStringLength();
    ULONG sum = sizeof(STORAGESIGNATURE) + lVersionString;
    if ((sum < sizeof(STORAGESIGNATURE)) || (sum < lVersionString))
    {
        Debug_ReportError("Invalid MetaData storage signature - version string too long, integer overflow.");
        return PostError(CLDB_E_FILE_CORRUPT);
    }
    
    // Check for invalid version string size
    if ((sizeof(STORAGESIGNATURE) + lVersionString) > cbData)
    {
        Debug_ReportError("Invalid MetaData storage signature - version string too long.");
        return PostError(CLDB_E_FILE_CORRUPT);
    }
    
    // Check that the version string is null terminated. This string
    // is ANSI, so no double-null checks need to be made.
    {
        BYTE *pStart = &pSig->pVersion[0];
        BYTE *pEnd = pStart + lVersionString + 1; // Account for terminating NULL
        BYTE *pCur;
        
        for (pCur = pStart; pCur < pEnd; pCur++)
        {
            if (*pCur == 0)
                break;
        }
        
        // If we got to the end without hitting a NULL, we have a bad version string
        if (pCur == pEnd)
        {
            Debug_ReportError("Invalid MetaData storage signature - version string has not null-terminator.");
            return PostError(CLDB_E_FILE_CORRUPT);
        }
    }
    
    // Only a specific version of the 0.x format is supported by this code
    // in order to support the NT 5 beta clients which used this format.
    if (pSig->GetMajorVer() == FILE_VER_MAJOR_v0)
    {
        if (pSig->GetMinorVer() < FILE_VER_MINOR_v0)
        {
            Debug_ReportError("Invalid MetaData storage signature - unrecognized version, should be 1.1.");
            hr = CLDB_E_FILE_OLDVER;
        }
    }
    else
    // There is currently no code to migrate an old format of the 1.x.  This
    // would be added only under special circumstances.
    if ((pSig->GetMajorVer() != FILE_VER_MAJOR) || (pSig->GetMinorVer() != FILE_VER_MINOR))
    {
        Debug_ReportError("Invalid MetaData storage signature - unrecognized version, should be 1.1.");
        hr = CLDB_E_FILE_OLDVER;
    }

    if (FAILED(hr))
        hr = PostError(hr, (int)pSig->GetMajorVer(), (int)pSig->GetMinorVer());
    return hr;
} // MDFormat::VerifySignature

//*****************************************************************************
// Skip over the header and find the actual stream data.
// It doesn't perform any checks for buffer overflow - use GetFirstStream_Verify 
// instead.
//*****************************************************************************
PSTORAGESTREAM 
MDFormat::GetFirstStream(
    PSTORAGEHEADER pHeader,     // Return copy of header struct.
    const void    *pvMd)        // Pointer to the full file.
{
    const BYTE *pbMd;
    
    // Header data starts after signature.
    pbMd = (const BYTE *) pvMd;
    pbMd += sizeof(STORAGESIGNATURE);
    pbMd += ((STORAGESIGNATURE*)pvMd)->GetVersionStringLength();
    PSTORAGEHEADER pHdr = (PSTORAGEHEADER) pbMd;
    *pHeader = *pHdr;
    pbMd += sizeof(STORAGEHEADER);
    
    // ECMA specifies that the flags field is "reserved, must be 0".
	if (pHdr->GetFlags() != 0)
		return NULL;
	
    // The pointer is now at the first stream in the list.
    return ((PSTORAGESTREAM) pbMd);
} // MDFormat::GetFirstStream

//*****************************************************************************
// Skip over the header and find the actual stream data.  Secure version of 
// GetFirstStream method.
// The header is supposed to be verified by VerifySignature.
// 
// Returns pointer to the first stream (behind storage header) and the size of 
// the remaining buffer in *pcbMd (could be 0).
// Returns NULL if there is not enough buffer for reading the headers. The *pcbMd 
// could be changed if NULL returned.
// 
// Caller has to check available buffer size before using the first stream.
//*****************************************************************************
PSTORAGESTREAM 
MDFormat::GetFirstStream_Verify(
    PSTORAGEHEADER pHeader,     // Return copy of header struct.
    const void    *pvMd,        // Pointer to the full file.
    ULONG         *pcbMd)       // [in, out] Size of pvMd buffer (we don't want to read behind it)
{
    const BYTE *pbMd;
    
    // Header data starts after signature.
    pbMd = (const BYTE *)pvMd;
    // Check read buffer overflow
    if (*pcbMd < sizeof(STORAGESIGNATURE))
    {
        Debug_ReportError("Invalid MetaData - Storage signature doesn't fit.");
        return NULL;
    }
    pbMd += sizeof(STORAGESIGNATURE);
    *pcbMd -= sizeof(STORAGESIGNATURE);
    
    ULONG cbVersionString = ((STORAGESIGNATURE *)pvMd)->GetVersionStringLength();
    // Check read buffer overflow
    if (*pcbMd < cbVersionString)
    {
        Debug_ReportError("Invalid MetaData storage signature - Version string doesn't fit.");
        return NULL;
    }
    pbMd += cbVersionString;
    *pcbMd -= cbVersionString;
    
    // Is there enough space for storage header?
    if (*pcbMd < sizeof(STORAGEHEADER))
    {
        Debug_ReportError("Invalid MetaData storage header - Storage header doesn't fit.");
        return NULL;
    }
    PSTORAGEHEADER pHdr = (PSTORAGEHEADER) pbMd;
    *pHeader = *pHdr;
    pbMd += sizeof(STORAGEHEADER);
    *pcbMd -= sizeof(STORAGEHEADER);
    
    // ECMA specifies that the flags field is "reserved, must be 0".
    if (pHdr->GetFlags() != 0)
    {
        Debug_ReportError("Invalid MetaData storage header - Flags are not 0.");
        return NULL;
    }
	
    // The pointer is now at the first stream in the list.
    return (PSTORAGESTREAM)pbMd;
} // MDFormat::GetFirstStream