diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/SecurityPkg/FvReportPei/FvReportPei.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/SecurityPkg/FvReportPei/FvReportPei.c | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/SecurityPkg/FvReportPei/FvReportPei.c b/src/VBox/Devices/EFI/Firmware/SecurityPkg/FvReportPei/FvReportPei.c new file mode 100644 index 00000000..3029fa74 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/SecurityPkg/FvReportPei/FvReportPei.c @@ -0,0 +1,424 @@ +/** @file + This driver verifies and reports OBB FVs. + +Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> +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] HashAlgId 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 (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); + + // + // Calculate 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 containing 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; +} |