]> git.proxmox.com Git - mirror_edk2.git/blobdiff - SecurityPkg/Tcg/TdTcg2Dxe/MeasureBootPeCoff.c
SecurityPkg: Move TdTcg2Dxe from OvmfPkg to SecurityPkg
[mirror_edk2.git] / SecurityPkg / Tcg / TdTcg2Dxe / MeasureBootPeCoff.c
diff --git a/SecurityPkg/Tcg/TdTcg2Dxe/MeasureBootPeCoff.c b/SecurityPkg/Tcg/TdTcg2Dxe/MeasureBootPeCoff.c
new file mode 100644 (file)
index 0000000..4d54215
--- /dev/null
@@ -0,0 +1,407 @@
+/** @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