]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/IntelTdx/TdTcg2Dxe/MeasureBootPeCoff.c
OvmfPkg/IntelTdx: Add TdTcg2Dxe
[mirror_edk2.git] / OvmfPkg / IntelTdx / TdTcg2Dxe / MeasureBootPeCoff.c
CommitLineData
57a6ee34
MX
1/** @file\r
2 This module implements measuring PeCoff image for Tcg2 Protocol.\r
3\r
4 Caution: This file requires additional review when modified.\r
5 This driver will have external input - PE/COFF image.\r
6 This external input must be validated carefully to avoid security issue like\r
7 buffer overflow, integer overflow.\r
8\r
9Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>\r
10SPDX-License-Identifier: BSD-2-Clause-Patent\r
11\r
12**/\r
13\r
14#include <PiDxe.h>\r
15\r
16#include <Library/BaseLib.h>\r
17#include <Library/DebugLib.h>\r
18#include <Library/BaseMemoryLib.h>\r
19#include <Library/MemoryAllocationLib.h>\r
20#include <Library/DevicePathLib.h>\r
21#include <Library/UefiBootServicesTableLib.h>\r
22#include <Library/PeCoffLib.h>\r
23#include <Library/HashLib.h>\r
24\r
25UINTN mTcg2DxeImageSize = 0;\r
26\r
27/**\r
28 Reads contents of a PE/COFF image in memory buffer.\r
29\r
30 Caution: This function may receive untrusted input.\r
31 PE/COFF image is external input, so this function will make sure the PE/COFF image content\r
32 read is within the image buffer.\r
33\r
34 @param FileHandle Pointer to the file handle to read the PE/COFF image.\r
35 @param FileOffset Offset into the PE/COFF image to begin the read operation.\r
36 @param ReadSize On input, the size in bytes of the requested read operation.\r
37 On output, the number of bytes actually read.\r
38 @param Buffer Output buffer that contains the data read from the PE/COFF image.\r
39\r
40 @retval EFI_SUCCESS The specified portion of the PE/COFF image was read and the size\r
41**/\r
42EFI_STATUS\r
43EFIAPI\r
44Tcg2DxeImageRead (\r
45 IN VOID *FileHandle,\r
46 IN UINTN FileOffset,\r
47 IN OUT UINTN *ReadSize,\r
48 OUT VOID *Buffer\r
49 )\r
50{\r
51 UINTN EndPosition;\r
52\r
53 if ((FileHandle == NULL) || (ReadSize == NULL) || (Buffer == NULL)) {\r
54 return EFI_INVALID_PARAMETER;\r
55 }\r
56\r
57 if (MAX_ADDRESS - FileOffset < *ReadSize) {\r
58 return EFI_INVALID_PARAMETER;\r
59 }\r
60\r
61 EndPosition = FileOffset + *ReadSize;\r
62 if (EndPosition > mTcg2DxeImageSize) {\r
63 *ReadSize = (UINT32)(mTcg2DxeImageSize - FileOffset);\r
64 }\r
65\r
66 if (FileOffset >= mTcg2DxeImageSize) {\r
67 *ReadSize = 0;\r
68 }\r
69\r
70 CopyMem (Buffer, (UINT8 *)((UINTN)FileHandle + FileOffset), *ReadSize);\r
71\r
72 return EFI_SUCCESS;\r
73}\r
74\r
75/**\r
76 Measure PE image into TPM log based on the authenticode image hashing in\r
77 PE/COFF Specification 8.0 Appendix A.\r
78\r
79 Caution: This function may receive untrusted input.\r
80 PE/COFF image is external input, so this function will validate its data structure\r
81 within this image buffer before use.\r
82\r
83 Notes: PE/COFF image is checked by BasePeCoffLib PeCoffLoaderGetImageInfo().\r
84\r
85 @param[in] RtmrIndex Rtmr index\r
86 @param[in] ImageAddress Start address of image buffer.\r
87 @param[in] ImageSize Image size\r
88 @param[out] DigestList Digest list of this image.\r
89\r
90 @retval EFI_SUCCESS Successfully measure image.\r
91 @retval EFI_OUT_OF_RESOURCES No enough resource to measure image.\r
92 @retval other error value\r
93**/\r
94EFI_STATUS\r
95MeasurePeImageAndExtend (\r
96 IN UINT32 RtmrIndex,\r
97 IN EFI_PHYSICAL_ADDRESS ImageAddress,\r
98 IN UINTN ImageSize,\r
99 OUT TPML_DIGEST_VALUES *DigestList\r
100 )\r
101{\r
102 EFI_STATUS Status;\r
103 EFI_IMAGE_DOS_HEADER *DosHdr;\r
104 UINT32 PeCoffHeaderOffset;\r
105 EFI_IMAGE_SECTION_HEADER *Section;\r
106 UINT8 *HashBase;\r
107 UINTN HashSize;\r
108 UINTN SumOfBytesHashed;\r
109 EFI_IMAGE_SECTION_HEADER *SectionHeader;\r
110 UINTN Index;\r
111 UINTN Pos;\r
112 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;\r
113 UINT32 NumberOfRvaAndSizes;\r
114 UINT32 CertSize;\r
115 HASH_HANDLE HashHandle;\r
116 PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;\r
117\r
118 HashHandle = 0xFFFFFFFF; // Know bad value\r
119\r
120 Status = EFI_UNSUPPORTED;\r
121 SectionHeader = NULL;\r
122\r
123 //\r
124 // Check PE/COFF image\r
125 //\r
126 ZeroMem (&ImageContext, sizeof (ImageContext));\r
127 ImageContext.Handle = (VOID *)(UINTN)ImageAddress;\r
128 mTcg2DxeImageSize = ImageSize;\r
129 ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE)Tcg2DxeImageRead;\r
130\r
131 //\r
132 // Get information about the image being loaded\r
133 //\r
134 Status = PeCoffLoaderGetImageInfo (&ImageContext);\r
135 if (EFI_ERROR (Status)) {\r
136 //\r
137 // The information can't be got from the invalid PeImage\r
138 //\r
139 DEBUG ((DEBUG_INFO, "Tcg2Dxe: PeImage invalid. Cannot retrieve image information.\n"));\r
140 goto Finish;\r
141 }\r
142\r
143 DosHdr = (EFI_IMAGE_DOS_HEADER *)(UINTN)ImageAddress;\r
144 PeCoffHeaderOffset = 0;\r
145 if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {\r
146 PeCoffHeaderOffset = DosHdr->e_lfanew;\r
147 }\r
148\r
149 Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *)(UINTN)ImageAddress + PeCoffHeaderOffset);\r
150 if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {\r
151 Status = EFI_UNSUPPORTED;\r
152 goto Finish;\r
153 }\r
154\r
155 //\r
156 // PE/COFF Image Measurement\r
157 //\r
158 // NOTE: The following codes/steps are based upon the authenticode image hashing in\r
159 // PE/COFF Specification 8.0 Appendix A.\r
160 //\r
161 //\r
162\r
163 // 1. Load the image header into memory.\r
164\r
165 // 2. Initialize a SHA hash context.\r
166\r
167 Status = HashStart (&HashHandle);\r
168 if (EFI_ERROR (Status)) {\r
169 goto Finish;\r
170 }\r
171\r
172 //\r
173 // Measuring PE/COFF Image Header;\r
174 // But CheckSum field and SECURITY data directory (certificate) are excluded\r
175 //\r
176\r
177 //\r
178 // 3. Calculate the distance from the base of the image header to the image checksum address.\r
179 // 4. Hash the image header from its base to beginning of the image checksum.\r
180 //\r
181 HashBase = (UINT8 *)(UINTN)ImageAddress;\r
182 if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
183 //\r
184 // Use PE32 offset\r
185 //\r
186 NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes;\r
187 HashSize = (UINTN)(&Hdr.Pe32->OptionalHeader.CheckSum) - (UINTN)HashBase;\r
188 } else {\r
189 //\r
190 // Use PE32+ offset\r
191 //\r
192 NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;\r
193 HashSize = (UINTN)(&Hdr.Pe32Plus->OptionalHeader.CheckSum) - (UINTN)HashBase;\r
194 }\r
195\r
196 Status = HashUpdate (HashHandle, HashBase, HashSize);\r
197 if (EFI_ERROR (Status)) {\r
198 goto Finish;\r
199 }\r
200\r
201 //\r
202 // 5. Skip over the image checksum (it occupies a single ULONG).\r
203 //\r
204 if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {\r
205 //\r
206 // 6. Since there is no Cert Directory in optional header, hash everything\r
207 // from the end of the checksum to the end of image header.\r
208 //\r
209 if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
210 //\r
211 // Use PE32 offset.\r
212 //\r
213 HashBase = (UINT8 *)&Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);\r
214 HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN)(HashBase - ImageAddress);\r
215 } else {\r
216 //\r
217 // Use PE32+ offset.\r
218 //\r
219 HashBase = (UINT8 *)&Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32);\r
220 HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN)(HashBase - ImageAddress);\r
221 }\r
222\r
223 if (HashSize != 0) {\r
224 Status = HashUpdate (HashHandle, HashBase, HashSize);\r
225 if (EFI_ERROR (Status)) {\r
226 goto Finish;\r
227 }\r
228 }\r
229 } else {\r
230 //\r
231 // 7. Hash everything from the end of the checksum to the start of the Cert Directory.\r
232 //\r
233 if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
234 //\r
235 // Use PE32 offset\r
236 //\r
237 HashBase = (UINT8 *)&Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);\r
238 HashSize = (UINTN)(&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN)HashBase;\r
239 } else {\r
240 //\r
241 // Use PE32+ offset\r
242 //\r
243 HashBase = (UINT8 *)&Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32);\r
244 HashSize = (UINTN)(&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN)HashBase;\r
245 }\r
246\r
247 if (HashSize != 0) {\r
248 Status = HashUpdate (HashHandle, HashBase, HashSize);\r
249 if (EFI_ERROR (Status)) {\r
250 goto Finish;\r
251 }\r
252 }\r
253\r
254 //\r
255 // 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.)\r
256 // 9. Hash everything from the end of the Cert Directory to the end of image header.\r
257 //\r
258 if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
259 //\r
260 // Use PE32 offset\r
261 //\r
262 HashBase = (UINT8 *)&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];\r
263 HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN)(HashBase - ImageAddress);\r
264 } else {\r
265 //\r
266 // Use PE32+ offset\r
267 //\r
268 HashBase = (UINT8 *)&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];\r
269 HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN)(HashBase - ImageAddress);\r
270 }\r
271\r
272 if (HashSize != 0) {\r
273 Status = HashUpdate (HashHandle, HashBase, HashSize);\r
274 if (EFI_ERROR (Status)) {\r
275 goto Finish;\r
276 }\r
277 }\r
278 }\r
279\r
280 //\r
281 // 10. Set the SUM_OF_BYTES_HASHED to the size of the header\r
282 //\r
283 if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
284 //\r
285 // Use PE32 offset\r
286 //\r
287 SumOfBytesHashed = Hdr.Pe32->OptionalHeader.SizeOfHeaders;\r
288 } else {\r
289 //\r
290 // Use PE32+ offset\r
291 //\r
292 SumOfBytesHashed = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders;\r
293 }\r
294\r
295 //\r
296 // 11. Build a temporary table of pointers to all the IMAGE_SECTION_HEADER\r
297 // structures in the image. The 'NumberOfSections' field of the image\r
298 // header indicates how big the table should be. Do not include any\r
299 // IMAGE_SECTION_HEADERs in the table whose 'SizeOfRawData' field is zero.\r
300 //\r
301 SectionHeader = (EFI_IMAGE_SECTION_HEADER *)AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * Hdr.Pe32->FileHeader.NumberOfSections);\r
302 if (SectionHeader == NULL) {\r
303 Status = EFI_OUT_OF_RESOURCES;\r
304 goto Finish;\r
305 }\r
306\r
307 //\r
308 // 12. Using the 'PointerToRawData' in the referenced section headers as\r
309 // a key, arrange the elements in the table in ascending order. In other\r
310 // words, sort the section headers according to the disk-file offset of\r
311 // the section.\r
312 //\r
313 Section = (EFI_IMAGE_SECTION_HEADER *)(\r
314 (UINT8 *)(UINTN)ImageAddress +\r
315 PeCoffHeaderOffset +\r
316 sizeof (UINT32) +\r
317 sizeof (EFI_IMAGE_FILE_HEADER) +\r
318 Hdr.Pe32->FileHeader.SizeOfOptionalHeader\r
319 );\r
320 for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {\r
321 Pos = Index;\r
322 while ((Pos > 0) && (Section->PointerToRawData < SectionHeader[Pos - 1].PointerToRawData)) {\r
323 CopyMem (&SectionHeader[Pos], &SectionHeader[Pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER));\r
324 Pos--;\r
325 }\r
326\r
327 CopyMem (&SectionHeader[Pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER));\r
328 Section += 1;\r
329 }\r
330\r
331 //\r
332 // 13. Walk through the sorted table, bring the corresponding section\r
333 // into memory, and hash the entire section (using the 'SizeOfRawData'\r
334 // field in the section header to determine the amount of data to hash).\r
335 // 14. Add the section's 'SizeOfRawData' to SUM_OF_BYTES_HASHED .\r
336 // 15. Repeat steps 13 and 14 for all the sections in the sorted table.\r
337 //\r
338 for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {\r
339 Section = (EFI_IMAGE_SECTION_HEADER *)&SectionHeader[Index];\r
340 if (Section->SizeOfRawData == 0) {\r
341 continue;\r
342 }\r
343\r
344 HashBase = (UINT8 *)(UINTN)ImageAddress + Section->PointerToRawData;\r
345 HashSize = (UINTN)Section->SizeOfRawData;\r
346\r
347 Status = HashUpdate (HashHandle, HashBase, HashSize);\r
348 if (EFI_ERROR (Status)) {\r
349 goto Finish;\r
350 }\r
351\r
352 SumOfBytesHashed += HashSize;\r
353 }\r
354\r
355 //\r
356 // 16. If the file size is greater than SUM_OF_BYTES_HASHED, there is extra\r
357 // data in the file that needs to be added to the hash. This data begins\r
358 // at file offset SUM_OF_BYTES_HASHED and its length is:\r
359 // FileSize - (CertDirectory->Size)\r
360 //\r
361 if (ImageSize > SumOfBytesHashed) {\r
362 HashBase = (UINT8 *)(UINTN)ImageAddress + SumOfBytesHashed;\r
363\r
364 if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {\r
365 CertSize = 0;\r
366 } else {\r
367 if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
368 //\r
369 // Use PE32 offset.\r
370 //\r
371 CertSize = Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size;\r
372 } else {\r
373 //\r
374 // Use PE32+ offset.\r
375 //\r
376 CertSize = Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size;\r
377 }\r
378 }\r
379\r
380 if (ImageSize > CertSize + SumOfBytesHashed) {\r
381 HashSize = (UINTN)(ImageSize - CertSize - SumOfBytesHashed);\r
382\r
383 Status = HashUpdate (HashHandle, HashBase, HashSize);\r
384 if (EFI_ERROR (Status)) {\r
385 goto Finish;\r
386 }\r
387 } else if (ImageSize < CertSize + SumOfBytesHashed) {\r
388 Status = EFI_UNSUPPORTED;\r
389 goto Finish;\r
390 }\r
391 }\r
392\r
393 //\r
394 // 17. Finalize the SHA hash.\r
395 //\r
396 Status = HashCompleteAndExtend (HashHandle, RtmrIndex, NULL, 0, DigestList);\r
397 if (EFI_ERROR (Status)) {\r
398 goto Finish;\r
399 }\r
400\r
401Finish:\r
402 if (SectionHeader != NULL) {\r
403 FreePool (SectionHeader);\r
404 }\r
405\r
406 return Status;\r
407}\r