https://bugzilla.tianocore.org/show_bug.cgi?id=2265 Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Chao Zhang <chao.b.zhang@intel.com> Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Jiewen Yao <Jiewen.yao@intel.com> Reviewed-by: Jian J Wang <jian.j.wang@intel.com>
		
			
				
	
	
		
			425 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			425 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @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 (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;
 | 
						|
}
 |