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