diff --git a/SecurityPkg/FvReportPei/FvReportPei.c b/SecurityPkg/FvReportPei/FvReportPei.c new file mode 100644 index 0000000000..c3cf221fa6 --- /dev/null +++ b/SecurityPkg/FvReportPei/FvReportPei.c @@ -0,0 +1,424 @@ +/** @file + This driver verifies and reports OBB FVs. + +Copyright (c) 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "FvReportPei.h" + +STATIC CONST HASH_ALG_INFO mHashAlgInfo[] = { + {TPM_ALG_SHA256, SHA256_DIGEST_SIZE, Sha256Init, Sha256Update, Sha256Final, Sha256HashAll}, // 000B + {TPM_ALG_SHA384, SHA384_DIGEST_SIZE, Sha384Init, Sha384Update, Sha384Final, Sha384HashAll}, // 000C + {TPM_ALG_SHA512, SHA512_DIGEST_SIZE, Sha512Init, Sha512Update, Sha512Final, Sha512HashAll}, // 000D +}; + +/** + Find hash algorithm information from mHashAlgInfo according to given ID. + + @param[in] HashAlgoId Hash algorithm type id. + + @retval Pointer to HASH_ALG_INFO if given hash algorithm is supported. + @retval NULL if given algorithm is not supported. +**/ +STATIC +CONST +HASH_ALG_INFO * +FindHashAlgInfo ( + IN UINT16 HashAlgId + ) +{ + UINTN Index; + + for (Index = 0; Index < ARRAY_SIZE (mHashAlgInfo); ++Index) { + if (mHashAlgInfo[Index].HashAlgId == HashAlgId) { + return &mHashAlgInfo[Index]; + } + } + + return NULL; +} + +/** + Install a EDKII_PEI_FIRMWARE_VOLUME_INFO_PREHASHED_FV_PPI instance so that + TCG driver may use to extend PCRs. + + @param[in] FvBuffer Buffer containing the whole FV. + @param[in] FvLength Length of the FV. + @param[in] HashAlgoId Hash algorithm type id. + @param[in] HashSize Hash size. + @param[in] HashValue Hash value buffer. +**/ +STATIC +VOID +InstallPreHashFvPpi ( + IN VOID *FvBuffer, + IN UINTN FvLength, + IN UINT16 HashAlgoId, + IN UINT16 HashSize, + IN UINT8 *HashValue + ) +{ + EFI_STATUS Status; + EFI_PEI_PPI_DESCRIPTOR *FvInfoPpiDescriptor; + EDKII_PEI_FIRMWARE_VOLUME_INFO_PREHASHED_FV_PPI *PreHashedFvPpi; + UINTN PpiSize; + HASH_INFO *HashInfo; + + PpiSize = sizeof (EDKII_PEI_FIRMWARE_VOLUME_INFO_PREHASHED_FV_PPI) + + sizeof (sizeof (HASH_INFO)) + + HashSize; + + PreHashedFvPpi = AllocatePool (PpiSize); + ASSERT (PreHashedFvPpi != NULL); + + PreHashedFvPpi->FvBase = (UINT32)(UINTN)FvBuffer; + PreHashedFvPpi->FvLength = (UINT32)FvLength; + PreHashedFvPpi->Count = 1; + + HashInfo = HASH_INFO_PTR (PreHashedFvPpi); + HashInfo->HashAlgoId = HashAlgoId; + HashInfo->HashSize = HashSize; + CopyMem (HASH_VALUE_PTR (HashInfo), HashValue, HashSize); + + FvInfoPpiDescriptor = AllocatePool (sizeof (EFI_PEI_PPI_DESCRIPTOR)); + ASSERT (FvInfoPpiDescriptor != NULL); + + FvInfoPpiDescriptor->Guid = &gEdkiiPeiFirmwareVolumeInfoPrehashedFvPpiGuid; + FvInfoPpiDescriptor->Flags = EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST; + FvInfoPpiDescriptor->Ppi = (VOID *) PreHashedFvPpi; + + Status = PeiServicesInstallPpi (FvInfoPpiDescriptor); + ASSERT_EFI_ERROR (Status); +} + +/** + Calculate and verify hash value for given FV. + + @param[in] HashInfo Hash information of the FV. + @param[in] FvInfo Information of FV used for verification. + @param[in] FvNumber Length of the FV. + @param[in] BootMode Length of the FV. + + @retval EFI_SUCCESS The given FV is integrate. + @retval EFI_VOLUME_CORRUPTED The given FV is corrupted (hash mismatch). + @retval EFI_UNSUPPORTED The hash algorithm is not supported. +**/ +STATIC +EFI_STATUS +VerifyHashedFv ( + IN FV_HASH_INFO *HashInfo, + IN HASHED_FV_INFO *FvInfo, + IN UINTN FvNumber, + IN EFI_BOOT_MODE BootMode + ) +{ + UINTN FvIndex; + CONST HASH_ALG_INFO *AlgInfo; + UINT8 *HashValue; + UINT8 *FvHashValue; + VOID *FvBuffer; + EFI_STATUS Status; + + if (HashInfo == NULL || + HashInfo->HashSize == 0 || + HashInfo->HashAlgoId == TPM_ALG_NULL) { + DEBUG ((DEBUG_INFO, "Bypass FV hash verification\r\n")); + return EFI_SUCCESS; + } + + AlgInfo = FindHashAlgInfo (HashInfo->HashAlgoId); + if (AlgInfo == NULL || AlgInfo->HashSize != HashInfo->HashSize) { + DEBUG ((DEBUG_ERROR, "Unsupported or wrong hash algorithm: %04X (size=%d)\r\n", + HashInfo->HashAlgoId, HashInfo->HashSize)); + return EFI_UNSUPPORTED; + } + + ASSERT (FvInfo != NULL); + ASSERT (FvNumber > 0); + + // + // We need a hash value for each FV as well as one for all FVs. + // + HashValue = AllocateZeroPool (AlgInfo->HashSize * (FvNumber + 1)); + ASSERT (HashValue != NULL); + + // + // Calcuate hash value for each FV first. + // + FvHashValue = HashValue; + for (FvIndex = 0; FvIndex < FvNumber; ++FvIndex) { + // + // FV must be meant for verified boot and/or measured boot. + // + ASSERT ((FvInfo[FvIndex].Flag & HASHED_FV_FLAG_VERIFIED_BOOT) != 0 || + (FvInfo[FvIndex].Flag & HASHED_FV_FLAG_MEASURED_BOOT) != 0); + + // + // Skip any FV not meant for current boot mode. + // + if ((FvInfo[FvIndex].Flag & HASHED_FV_FLAG_SKIP_BOOT_MODE (BootMode)) != 0) { + DEBUG ((DEBUG_INFO, "Skip FV[%016lX] for boot mode[%d]\r\n", + FvInfo[FvIndex].Base, BootMode)); + continue; + } + + DEBUG (( + DEBUG_INFO, + "Pre-hashed[alg=%04X,size=%d,flag=%016lX] FV: 0x%016lX (%08lX) (Flag=%016lX)\r\n", + HashInfo->HashAlgoId, + HashInfo->HashSize, + HashInfo->HashFlag, + FvInfo[FvIndex].Base, + FvInfo[FvIndex].Length, + FvInfo[FvIndex].Flag + )); + + // + // Copy FV to permanent memory to avoid potential TOC/TOU. + // + FvBuffer = AllocatePages (EFI_SIZE_TO_PAGES((UINTN)FvInfo[FvIndex].Length)); + ASSERT (FvBuffer != NULL); + CopyMem (FvBuffer, (CONST VOID *)(UINTN)FvInfo[FvIndex].Base, (UINTN)FvInfo[FvIndex].Length); + + if (!AlgInfo->HashAll (FvBuffer, (UINTN)FvInfo[FvIndex].Length, FvHashValue)) { + Status = EFI_ABORTED; + goto Done; + } + + // + // Report the FV measurement. + // + if ((FvInfo[FvIndex].Flag & HASHED_FV_FLAG_MEASURED_BOOT) != 0) { + InstallPreHashFvPpi ( + FvBuffer, + (UINTN)FvInfo[FvIndex].Length, + HashInfo->HashAlgoId, + HashInfo->HashSize, + FvHashValue + ); + } + + // + // Don't keep the hash value of current FV if we don't need to verify it. + // + if ((FvInfo[FvIndex].Flag & HASHED_FV_FLAG_VERIFIED_BOOT) != 0) { + FvHashValue += AlgInfo->HashSize; + } + + // + // Use memory copy of the FV from now on. + // + FvInfo[FvIndex].Base = (UINT64)(UINTN)FvBuffer; + } + + // + // Check final hash for all FVs. + // + if (FvHashValue == HashValue || + (AlgInfo->HashAll (HashValue, FvHashValue - HashValue, FvHashValue) && + CompareMem (HashInfo->Hash, FvHashValue, AlgInfo->HashSize) == 0)) { + Status = EFI_SUCCESS; + } else { + Status = EFI_VOLUME_CORRUPTED; + } + +Done: + FreePool (HashValue); + return Status; +} + +/** + Report FV to PEI and/or DXE core for dispatch. + + @param[in] FvInfo Information of a FV. + +**/ +STATIC +VOID +ReportHashedFv ( + IN HASHED_FV_INFO *FvInfo + ) +{ + CONST EFI_GUID *FvFormat; + + if ((FvInfo->Flag & HASHED_FV_FLAG_REPORT_FV_HOB) != 0) { + // + // Require DXE core to process this FV. + // + BuildFvHob ( + (EFI_PHYSICAL_ADDRESS)FvInfo->Base, + FvInfo->Length + ); + DEBUG ((DEBUG_INFO, "Reported FV HOB: %016lX (%08lX)\r\n", FvInfo->Base, FvInfo->Length)); + } + + if ((FvInfo->Flag & HASHED_FV_FLAG_REPORT_FV_INFO_PPI) != 0) { + // + // Require PEI core to process this FV. + // + FvFormat = &((EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)FvInfo->Base)->FileSystemGuid; + PeiServicesInstallFvInfoPpi ( + FvFormat, + (VOID *)(UINTN)FvInfo->Base, + (UINT32)FvInfo->Length, + NULL, + NULL + ); + DEBUG ((DEBUG_INFO, "Reported FV PPI: %016lX (%08lX)\r\n", FvInfo->Base, FvInfo->Length)); + } +} + +/** + Verify and report pre-hashed FVs. + + Doing this must be at post-memory to make sure there's enough memory to hold + all FVs to be verified. This is necessary for mitigating TOCTOU issue. + + This function will never return if the verification is failed. + + @param[in] StoredHashFvPpi Pointer to PPI containing hash information. + @param[in] BootMode Current boot mode. + + @retval Pointer to structure containning valid hash information for current boot mode. + @retval NULL if there's no hash associated with current boot mode. +**/ +STATIC +FV_HASH_INFO * +GetHashInfo ( + IN EDKII_PEI_FIRMWARE_VOLUME_INFO_STORED_HASH_FV_PPI *StoredHashFvPpi, + IN EFI_BOOT_MODE BootMode + ) +{ + FV_HASH_INFO *HashInfo; + + if ((StoredHashFvPpi->HashInfo.HashFlag & FV_HASH_FLAG_BOOT_MODE (BootMode)) != 0) { + HashInfo = &StoredHashFvPpi->HashInfo; + } else { + HashInfo = NULL; + } + + return HashInfo; +} + +/** + Verify and report pre-hashed FVs. + + Doing this must be at post-memory to make sure there's enough memory to hold + all FVs to be verified. This is necessary for mitigating TOCTOU issue. + + This function will never return if the verification is failed. + + @param[in] PeiServices General purpose services available to every PEIM. + @param[in] BootMode Current boot mode. + + @retval EFI_SUCCESS The function completed successfully. +**/ +STATIC +EFI_STATUS +CheckStoredHashFv ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_BOOT_MODE BootMode + ) +{ + EFI_STATUS Status; + EDKII_PEI_FIRMWARE_VOLUME_INFO_STORED_HASH_FV_PPI *StoredHashFvPpi; + FV_HASH_INFO *HashInfo; + UINTN FvIndex; + + // + // Check pre-hashed FV list + // + StoredHashFvPpi = NULL; + Status = PeiServicesLocatePpi ( + &gEdkiiPeiFirmwareVolumeInfoStoredHashFvPpiGuid, + 0, + NULL, + (VOID**)&StoredHashFvPpi + ); + if (!EFI_ERROR(Status) && StoredHashFvPpi != NULL && StoredHashFvPpi->FvNumber > 0) { + + HashInfo = GetHashInfo (StoredHashFvPpi, BootMode); + Status = VerifyHashedFv (HashInfo, StoredHashFvPpi->FvInfo, + StoredHashFvPpi->FvNumber, BootMode); + if (!EFI_ERROR (Status)) { + + // + // Report the FVs to PEI core and/or DXE core. + // + for (FvIndex = 0; FvIndex < StoredHashFvPpi->FvNumber; ++FvIndex) { + if ((StoredHashFvPpi->FvInfo[FvIndex].Flag + & HASHED_FV_FLAG_SKIP_BOOT_MODE (BootMode)) == 0) { + ReportHashedFv (&StoredHashFvPpi->FvInfo[FvIndex]); + } + } + + REPORT_STATUS_CODE ( + EFI_PROGRESS_CODE, + PcdGet32 (PcdStatusCodeFvVerificationPass) + ); + + } else { + + DEBUG ((DEBUG_ERROR, "ERROR: Failed to verify OBB FVs (%r)\r\n", Status)); + + REPORT_STATUS_CODE_EX ( + EFI_PROGRESS_CODE, + PcdGet32 (PcdStatusCodeFvVerificationFail), + 0, + NULL, + &gEdkiiPeiFirmwareVolumeInfoStoredHashFvPpiGuid, + StoredHashFvPpi, + sizeof (*StoredHashFvPpi) + ); + + ASSERT_EFI_ERROR (Status); + + } + + } else { + + DEBUG ((DEBUG_ERROR, "ERROR: No/invalid StoredHashFvPpi located\r\n")); + + ASSERT_EFI_ERROR (Status); + ASSERT (StoredHashFvPpi != NULL && StoredHashFvPpi->FvNumber > 0); + + Status = EFI_NOT_FOUND; + } + + return Status; +} + +/** + Main entry for FvReport PEIM. + + @param[in] FileHandle Handle of the file being invoked. + @param[in] PeiServices Pointer to PEI Services table. + + @retval EFI_SUCCESS If all FVs reported by StoredHashFvPpi are verified. + +**/ +EFI_STATUS +EFIAPI +FvReportEntryPoint ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + EFI_BOOT_MODE BootMode; + + Status = PeiServicesGetBootMode (&BootMode); + ASSERT_EFI_ERROR (Status); + + Status = CheckStoredHashFv (PeiServices, BootMode); + if (EFI_ERROR (Status)) { + // + // Never pass control to left part of BIOS if any error. + // + CpuDeadLoop (); + } + + return Status; +} diff --git a/SecurityPkg/FvReportPei/FvReportPei.h b/SecurityPkg/FvReportPei/FvReportPei.h new file mode 100644 index 0000000000..aa35d2d4f1 --- /dev/null +++ b/SecurityPkg/FvReportPei/FvReportPei.h @@ -0,0 +1,122 @@ +/** @file + Definitions for OBB FVs verification. + +Copyright (c) 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __FV_REPORT_PEI_H__ +#define __FV_REPORT_PEI_H__ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define HASH_INFO_PTR(PreHashedFvPpi) \ + (HASH_INFO *)((UINT8 *)(PreHashedFvPpi) + sizeof (EDKII_PEI_FIRMWARE_VOLUME_INFO_PREHASHED_FV_PPI)) + +#define HASH_VALUE_PTR(HashInfo) \ + (VOID *)((UINT8 *)(HashInfo) + sizeof (HASH_INFO)) + +/** + Computes the message digest of a input data buffer. + + This function performs message digest of a given data buffer, and places + the digest value into the specified memory. + + If this interface is not supported, then return FALSE. + + @param[in] Data Pointer to the buffer containing the data to be hashed. + @param[in] DataSize Size of Data buffer in bytes. + @param[out] HashValue Pointer to a buffer that receives digest value. + + @retval TRUE The digest computation succeeded. + @retval FALSE The digest computation failed. + +**/ +typedef +BOOLEAN +(EFIAPI *HASH_ALL_METHOD) ( + IN CONST VOID *Data, + IN UINTN DataSize, + OUT UINT8 *HashValue + ); + +/** + Initializes user-supplied memory as hash context for subsequent use. + + @param[out] HashContext Pointer to hash context being initialized. + + @retval TRUE Hash context initialization succeeded. + @retval FALSE Hash context initialization failed. + @retval FALSE This interface is not supported. + +**/ +typedef +BOOLEAN +(EFIAPI *HASH_INIT_METHOD) ( + OUT VOID *HashContext + ); + +/** + Digests the input data and updates hash context. + + @param[in, out] HashContext Pointer to the hash context. + @param[in] Data Pointer to the buffer containing the data to be hashed. + @param[in] DataSize Size of Data buffer in bytes. + + @retval TRUE Hash data digest succeeded. + @retval FALSE Hash data digest failed. + @retval FALSE This interface is not supported. + +**/ +typedef +BOOLEAN +(EFIAPI *HASH_UPDATE_METHOD) ( + IN OUT VOID *HashContext, + IN CONST VOID *Data, + IN UINTN DataSize + ); + +/** + Completes computation of the hash digest value. + + @param[in, out] HashContext Pointer to the hash context. + @param[out] HashValue Pointer to a buffer that receives the hash digest + value. + + @retval TRUE Hash digest computation succeeded. + @retval FALSE Hash digest computation failed. + @retval FALSE This interface is not supported. + +**/ +typedef +BOOLEAN +(EFIAPI *HASH_FINAL_METHOD) ( + IN OUT VOID *HashContext, + OUT UINT8 *HashValue + ); + +typedef struct { + UINT16 HashAlgId; + UINTN HashSize; + HASH_INIT_METHOD HashInit; + HASH_UPDATE_METHOD HashUpdate; + HASH_FINAL_METHOD HashFinal; + HASH_ALL_METHOD HashAll; +} HASH_ALG_INFO; + +#endif //__FV_REPORT_PEI_H__ + diff --git a/SecurityPkg/FvReportPei/FvReportPei.inf b/SecurityPkg/FvReportPei/FvReportPei.inf new file mode 100644 index 0000000000..4084068897 --- /dev/null +++ b/SecurityPkg/FvReportPei/FvReportPei.inf @@ -0,0 +1,58 @@ +## @file +# FV Report/Verify PEI Driver. +# +# Copyright (c) 2019, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FvReportPei + MODULE_UNI_FILE = FvReportPei.uni + FILE_GUID = 72405B40-38DA-4ABA-9283-CA8321C23E63 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = FvReportEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + FvReportPei.c + FvReportPei.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + CryptoPkg/CryptoPkg.dec + SecurityPkg/SecurityPkg.dec + +[LibraryClasses] + PeimEntryPoint + PeiServicesLib + BaseLib + DebugLib + BaseMemoryLib + PcdLib + HobLib + MemoryAllocationLib + BaseCryptLib + ReportStatusCodeLib + +[Ppis] + gEdkiiPeiFirmwareVolumeInfoPrehashedFvPpiGuid ## PRODUCES + gEdkiiPeiFirmwareVolumeInfoStoredHashFvPpiGuid ## CONSUMES + +[Pcd] + gEfiSecurityPkgTokenSpaceGuid.PcdStatusCodeFvVerificationPass + gEfiSecurityPkgTokenSpaceGuid.PcdStatusCodeFvVerificationFail + +[Depex] + gEdkiiPeiFirmwareVolumeInfoStoredHashFvPpiGuid AND gEfiPeiMemoryDiscoveredPpiGuid + +[UserExtensions.TianoCore."ExtraFiles"] + FvReportPeiPeiExtra.uni diff --git a/SecurityPkg/FvReportPei/FvReportPei.uni b/SecurityPkg/FvReportPei/FvReportPei.uni new file mode 100644 index 0000000000..bad43403c4 --- /dev/null +++ b/SecurityPkg/FvReportPei/FvReportPei.uni @@ -0,0 +1,14 @@ +// /** @file +// FV Verify/Report PEI Driver. +// +// Copyright (c) 2019, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "This module verifies and reports FVs." + +#string STR_MODULE_DESCRIPTION #language en-US "This module verifies FVs' digest passed through gEdkiiPeiFirmwareVolumeInfoStoredHashFvPpiGuid, " + "and installs gEdkiiPeiFirmwareVolumeInfoPrehashedFvPpiGuid, gEfiPeiFirmwareVolumeInfoPpiGuid and/or FV HOB if passed." + diff --git a/SecurityPkg/FvReportPei/FvReportPeiPeiExtra.uni b/SecurityPkg/FvReportPei/FvReportPeiPeiExtra.uni new file mode 100644 index 0000000000..6214bbdaa9 --- /dev/null +++ b/SecurityPkg/FvReportPei/FvReportPeiPeiExtra.uni @@ -0,0 +1,12 @@ +// /** @file +// FV Verify/Report PEI Driver. +// +// Copyright (c) 2019, Intel Corporation. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US "FV Verify/Report PEI Driver" + +