--- /dev/null
+/** @file\r
+ This module implements measuring PeCoff image for Tcg2 Protocol.\r
+\r
+ Caution: This file requires additional review when modified.\r
+ This driver will have external input - PE/COFF image.\r
+ This external input must be validated carefully to avoid security issue like\r
+ buffer overflow, integer overflow.\r
+\r
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include <PiDxe.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/PeCoffLib.h>\r
+#include <Library/HashLib.h>\r
+\r
+UINTN mTcg2DxeImageSize = 0;\r
+\r
+/**\r
+ Reads contents of a PE/COFF image in memory buffer.\r
+\r
+ Caution: This function may receive untrusted input.\r
+ PE/COFF image is external input, so this function will make sure the PE/COFF image content\r
+ read is within the image buffer.\r
+\r
+ @param FileHandle Pointer to the file handle to read the PE/COFF image.\r
+ @param FileOffset Offset into the PE/COFF image to begin the read operation.\r
+ @param ReadSize On input, the size in bytes of the requested read operation.\r
+ On output, the number of bytes actually read.\r
+ @param Buffer Output buffer that contains the data read from the PE/COFF image.\r
+\r
+ @retval EFI_SUCCESS The specified portion of the PE/COFF image was read and the size\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Tcg2DxeImageRead (\r
+ IN VOID *FileHandle,\r
+ IN UINTN FileOffset,\r
+ IN OUT UINTN *ReadSize,\r
+ OUT VOID *Buffer\r
+ )\r
+{\r
+ UINTN EndPosition;\r
+\r
+ if ((FileHandle == NULL) || (ReadSize == NULL) || (Buffer == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (MAX_ADDRESS - FileOffset < *ReadSize) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ EndPosition = FileOffset + *ReadSize;\r
+ if (EndPosition > mTcg2DxeImageSize) {\r
+ *ReadSize = (UINT32)(mTcg2DxeImageSize - FileOffset);\r
+ }\r
+\r
+ if (FileOffset >= mTcg2DxeImageSize) {\r
+ *ReadSize = 0;\r
+ }\r
+\r
+ CopyMem (Buffer, (UINT8 *)((UINTN)FileHandle + FileOffset), *ReadSize);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Measure PE image into TPM log based on the authenticode image hashing in\r
+ PE/COFF Specification 8.0 Appendix A.\r
+\r
+ Caution: This function may receive untrusted input.\r
+ PE/COFF image is external input, so this function will validate its data structure\r
+ within this image buffer before use.\r
+\r
+ Notes: PE/COFF image is checked by BasePeCoffLib PeCoffLoaderGetImageInfo().\r
+\r
+ @param[in] RtmrIndex Rtmr index\r
+ @param[in] ImageAddress Start address of image buffer.\r
+ @param[in] ImageSize Image size\r
+ @param[out] DigestList Digest list of this image.\r
+\r
+ @retval EFI_SUCCESS Successfully measure image.\r
+ @retval EFI_OUT_OF_RESOURCES No enough resource to measure image.\r
+ @retval other error value\r
+**/\r
+EFI_STATUS\r
+MeasurePeImageAndExtend (\r
+ IN UINT32 RtmrIndex,\r
+ IN EFI_PHYSICAL_ADDRESS ImageAddress,\r
+ IN UINTN ImageSize,\r
+ OUT TPML_DIGEST_VALUES *DigestList\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_IMAGE_DOS_HEADER *DosHdr;\r
+ UINT32 PeCoffHeaderOffset;\r
+ EFI_IMAGE_SECTION_HEADER *Section;\r
+ UINT8 *HashBase;\r
+ UINTN HashSize;\r
+ UINTN SumOfBytesHashed;\r
+ EFI_IMAGE_SECTION_HEADER *SectionHeader;\r
+ UINTN Index;\r
+ UINTN Pos;\r
+ EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;\r
+ UINT32 NumberOfRvaAndSizes;\r
+ UINT32 CertSize;\r
+ HASH_HANDLE HashHandle;\r
+ PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;\r
+\r
+ HashHandle = 0xFFFFFFFF; // Know bad value\r
+\r
+ Status = EFI_UNSUPPORTED;\r
+ SectionHeader = NULL;\r
+\r
+ //\r
+ // Check PE/COFF image\r
+ //\r
+ ZeroMem (&ImageContext, sizeof (ImageContext));\r
+ ImageContext.Handle = (VOID *)(UINTN)ImageAddress;\r
+ mTcg2DxeImageSize = ImageSize;\r
+ ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE)Tcg2DxeImageRead;\r
+\r
+ //\r
+ // Get information about the image being loaded\r
+ //\r
+ Status = PeCoffLoaderGetImageInfo (&ImageContext);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // The information can't be got from the invalid PeImage\r
+ //\r
+ DEBUG ((DEBUG_INFO, "Tcg2Dxe: PeImage invalid. Cannot retrieve image information.\n"));\r
+ goto Finish;\r
+ }\r
+\r
+ DosHdr = (EFI_IMAGE_DOS_HEADER *)(UINTN)ImageAddress;\r
+ PeCoffHeaderOffset = 0;\r
+ if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {\r
+ PeCoffHeaderOffset = DosHdr->e_lfanew;\r
+ }\r
+\r
+ Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *)(UINTN)ImageAddress + PeCoffHeaderOffset);\r
+ if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {\r
+ Status = EFI_UNSUPPORTED;\r
+ goto Finish;\r
+ }\r
+\r
+ //\r
+ // PE/COFF Image Measurement\r
+ //\r
+ // NOTE: The following codes/steps are based upon the authenticode image hashing in\r
+ // PE/COFF Specification 8.0 Appendix A.\r
+ //\r
+ //\r
+\r
+ // 1. Load the image header into memory.\r
+\r
+ // 2. Initialize a SHA hash context.\r
+\r
+ Status = HashStart (&HashHandle);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Finish;\r
+ }\r
+\r
+ //\r
+ // Measuring PE/COFF Image Header;\r
+ // But CheckSum field and SECURITY data directory (certificate) are excluded\r
+ //\r
+\r
+ //\r
+ // 3. Calculate the distance from the base of the image header to the image checksum address.\r
+ // 4. Hash the image header from its base to beginning of the image checksum.\r
+ //\r
+ HashBase = (UINT8 *)(UINTN)ImageAddress;\r
+ if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ //\r
+ // Use PE32 offset\r
+ //\r
+ NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes;\r
+ HashSize = (UINTN)(&Hdr.Pe32->OptionalHeader.CheckSum) - (UINTN)HashBase;\r
+ } else {\r
+ //\r
+ // Use PE32+ offset\r
+ //\r
+ NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;\r
+ HashSize = (UINTN)(&Hdr.Pe32Plus->OptionalHeader.CheckSum) - (UINTN)HashBase;\r
+ }\r
+\r
+ Status = HashUpdate (HashHandle, HashBase, HashSize);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Finish;\r
+ }\r
+\r
+ //\r
+ // 5. Skip over the image checksum (it occupies a single ULONG).\r
+ //\r
+ if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {\r
+ //\r
+ // 6. Since there is no Cert Directory in optional header, hash everything\r
+ // from the end of the checksum to the end of image header.\r
+ //\r
+ if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ //\r
+ // Use PE32 offset.\r
+ //\r
+ HashBase = (UINT8 *)&Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);\r
+ HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN)(HashBase - ImageAddress);\r
+ } else {\r
+ //\r
+ // Use PE32+ offset.\r
+ //\r
+ HashBase = (UINT8 *)&Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32);\r
+ HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN)(HashBase - ImageAddress);\r
+ }\r
+\r
+ if (HashSize != 0) {\r
+ Status = HashUpdate (HashHandle, HashBase, HashSize);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Finish;\r
+ }\r
+ }\r
+ } else {\r
+ //\r
+ // 7. Hash everything from the end of the checksum to the start of the Cert Directory.\r
+ //\r
+ if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ //\r
+ // Use PE32 offset\r
+ //\r
+ HashBase = (UINT8 *)&Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);\r
+ HashSize = (UINTN)(&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN)HashBase;\r
+ } else {\r
+ //\r
+ // Use PE32+ offset\r
+ //\r
+ HashBase = (UINT8 *)&Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32);\r
+ HashSize = (UINTN)(&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN)HashBase;\r
+ }\r
+\r
+ if (HashSize != 0) {\r
+ Status = HashUpdate (HashHandle, HashBase, HashSize);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Finish;\r
+ }\r
+ }\r
+\r
+ //\r
+ // 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.)\r
+ // 9. Hash everything from the end of the Cert Directory to the end of image header.\r
+ //\r
+ if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ //\r
+ // Use PE32 offset\r
+ //\r
+ HashBase = (UINT8 *)&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];\r
+ HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN)(HashBase - ImageAddress);\r
+ } else {\r
+ //\r
+ // Use PE32+ offset\r
+ //\r
+ HashBase = (UINT8 *)&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];\r
+ HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN)(HashBase - ImageAddress);\r
+ }\r
+\r
+ if (HashSize != 0) {\r
+ Status = HashUpdate (HashHandle, HashBase, HashSize);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Finish;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // 10. Set the SUM_OF_BYTES_HASHED to the size of the header\r
+ //\r
+ if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ //\r
+ // Use PE32 offset\r
+ //\r
+ SumOfBytesHashed = Hdr.Pe32->OptionalHeader.SizeOfHeaders;\r
+ } else {\r
+ //\r
+ // Use PE32+ offset\r
+ //\r
+ SumOfBytesHashed = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders;\r
+ }\r
+\r
+ //\r
+ // 11. Build a temporary table of pointers to all the IMAGE_SECTION_HEADER\r
+ // structures in the image. The 'NumberOfSections' field of the image\r
+ // header indicates how big the table should be. Do not include any\r
+ // IMAGE_SECTION_HEADERs in the table whose 'SizeOfRawData' field is zero.\r
+ //\r
+ SectionHeader = (EFI_IMAGE_SECTION_HEADER *)AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * Hdr.Pe32->FileHeader.NumberOfSections);\r
+ if (SectionHeader == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Finish;\r
+ }\r
+\r
+ //\r
+ // 12. Using the 'PointerToRawData' in the referenced section headers as\r
+ // a key, arrange the elements in the table in ascending order. In other\r
+ // words, sort the section headers according to the disk-file offset of\r
+ // the section.\r
+ //\r
+ Section = (EFI_IMAGE_SECTION_HEADER *)(\r
+ (UINT8 *)(UINTN)ImageAddress +\r
+ PeCoffHeaderOffset +\r
+ sizeof (UINT32) +\r
+ sizeof (EFI_IMAGE_FILE_HEADER) +\r
+ Hdr.Pe32->FileHeader.SizeOfOptionalHeader\r
+ );\r
+ for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {\r
+ Pos = Index;\r
+ while ((Pos > 0) && (Section->PointerToRawData < SectionHeader[Pos - 1].PointerToRawData)) {\r
+ CopyMem (&SectionHeader[Pos], &SectionHeader[Pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER));\r
+ Pos--;\r
+ }\r
+\r
+ CopyMem (&SectionHeader[Pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER));\r
+ Section += 1;\r
+ }\r
+\r
+ //\r
+ // 13. Walk through the sorted table, bring the corresponding section\r
+ // into memory, and hash the entire section (using the 'SizeOfRawData'\r
+ // field in the section header to determine the amount of data to hash).\r
+ // 14. Add the section's 'SizeOfRawData' to SUM_OF_BYTES_HASHED .\r
+ // 15. Repeat steps 13 and 14 for all the sections in the sorted table.\r
+ //\r
+ for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {\r
+ Section = (EFI_IMAGE_SECTION_HEADER *)&SectionHeader[Index];\r
+ if (Section->SizeOfRawData == 0) {\r
+ continue;\r
+ }\r
+\r
+ HashBase = (UINT8 *)(UINTN)ImageAddress + Section->PointerToRawData;\r
+ HashSize = (UINTN)Section->SizeOfRawData;\r
+\r
+ Status = HashUpdate (HashHandle, HashBase, HashSize);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Finish;\r
+ }\r
+\r
+ SumOfBytesHashed += HashSize;\r
+ }\r
+\r
+ //\r
+ // 16. If the file size is greater than SUM_OF_BYTES_HASHED, there is extra\r
+ // data in the file that needs to be added to the hash. This data begins\r
+ // at file offset SUM_OF_BYTES_HASHED and its length is:\r
+ // FileSize - (CertDirectory->Size)\r
+ //\r
+ if (ImageSize > SumOfBytesHashed) {\r
+ HashBase = (UINT8 *)(UINTN)ImageAddress + SumOfBytesHashed;\r
+\r
+ if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {\r
+ CertSize = 0;\r
+ } else {\r
+ if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ //\r
+ // Use PE32 offset.\r
+ //\r
+ CertSize = Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size;\r
+ } else {\r
+ //\r
+ // Use PE32+ offset.\r
+ //\r
+ CertSize = Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size;\r
+ }\r
+ }\r
+\r
+ if (ImageSize > CertSize + SumOfBytesHashed) {\r
+ HashSize = (UINTN)(ImageSize - CertSize - SumOfBytesHashed);\r
+\r
+ Status = HashUpdate (HashHandle, HashBase, HashSize);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Finish;\r
+ }\r
+ } else if (ImageSize < CertSize + SumOfBytesHashed) {\r
+ Status = EFI_UNSUPPORTED;\r
+ goto Finish;\r
+ }\r
+ }\r
+\r
+ //\r
+ // 17. Finalize the SHA hash.\r
+ //\r
+ Status = HashCompleteAndExtend (HashHandle, RtmrIndex, NULL, 0, DigestList);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Finish;\r
+ }\r
+\r
+Finish:\r
+ if (SectionHeader != NULL) {\r
+ FreePool (SectionHeader);\r
+ }\r
+\r
+ return Status;\r
+}\r