//**********************************************************************
//**********************************************************************
//** **
//** (C)Copyright 1985-2013, American Megatrends, Inc. **
//** **
//** All Rights Reserved. **
//** **
//** 5555 Oakbrook Parkway, Suite 200, Norcross, GA 30093 **
//** **
//** Phone: (770)-246-8600 **
//** **
//**********************************************************************
//**********************************************************************
//**********************************************************************
// $Header: /Alaska/SOURCE/Modules/SecureBoot_WIN8/ImageVerification_efi/DxeImageVerificationLib.c 79 6/22/15 6:26p Alexp $
//
// $Revision: 79 $
//
// $Date: 6/22/15 6:26p $
//**********************************************************************
// Revision History
// ----------------
// $Log: /Alaska/SOURCE/Modules/SecureBoot_WIN8/ImageVerification_efi/DxeImageVerificationLib.c $
//
// 79 6/22/15 6:26p Alexp
// EIP219415
// [Category] Bug Fix
// [Severity] Minor
// [Symptom] UEFI PXE can launch unsigned images when
// SecureBoot is enabled.
// [RootCause] Image from PXE server gets cloned device path
// from its parent network controller with PciIo protocol
// and embedded PCI ROM attributes
// [Solution] Add checking for unique path for embedded
// Pci OpROMs to prevent non-OpROM images
//
// 77 5/14/15 9:49a Alexp
// DxeImageVerificationHandler()
// Modify certificate offset calculation to match EDKII version
//
// 76 3/09/15 4:24p Alexp
// EIP192504: Prevent Built In EFI Shell boot option from being created in
// BDS when Secure Boot is enabled
// [Resolution]: Leave old logic to allow launching embedded shell
// based on the token:LOAD_UNSIGNED_EMBEDDED_SHELL
// enabled by default only in Debug mode
// EIP197749: Need to authenticate images loaded from external FV
// [Resolution] Add logic inside GetImageType() to detect images loaded
// from external FV
//
// 75 5/29/14 8:52a Alexp
// minor debug trace message change;
//
// 74 8/15/13 11:29a Alexp
// EIP#118850: Implement support for Certificate Revocation Storage
// reduction and Timestamp Support.
// Link AmyCryptoLib to make use of os_mktime()
//
// 73 7/26/13 3:40p Alexp
// 1. EIP118850: Integrate ECR1009 chnages for TimeStamp dbt processing
// 2. Image policy settings are checked against the build time defaults.
// Prevents un-authorized change to NVRAM policy settings
// to lower security policy.
//
// 72 6/26/13 10:48a Alexp
// EIP[125931]: Security review. Fix after follow up review.
// UINT32 integer wrapping still occurs due to incorrect placement
// of delimiting parentheses
//
// 70 6/25/13 7:21p Alexp
// EIP:127292 Item#2. Add dependency on ENABLE_IMAGE_EXEC_POLICY_OVERRIDE
// EIP:127292 Item#3 Only For deprecated Security protocol:
// DxeImageVerificationHandler Returns Error if FileRead() is
// unsuccessful
//
// 67 6/21/13 10:41a Alexp
// EIP[125931]: Security review: HashPeImage does not validate certificate
// table offset and size
//
// 66 5/21/13 4:05p Alexp
// EIP:124444 Secure boot improvement for LoadImage call with NULL
// DevicePath
// EIP:122339 GetImageType() detect if PciOpROM image is loaded from
// internal FV by
// checking the corresponding PCI I/O instance for the embedded
// attribute.
//
// 63 3/25/13 3:41p Alexp
// 1. EIP:118243 add support for multi-signed PE Images
// 2. Removed append image certificates to db from User Query dialog.
//
// 56 3/11/13 5:07p Alexp
// IsSignatureFoundInDatabase(): fix method to calculate CertCount.
// AddImageExeInfo(): code cleanup for cases when *Name is NULL
// DxeImageVerificationHandler(): Optimized code to determine if PE image
// is signed
//
// 55 3/09/13 4:31p Alexp
// EIP114998: wrong cert size passed in PCR[7] calculation
//
// 54 12/19/12 10:28a Alexp
// EIP[104046]: Hardened code per #2,3,4,5,6,7 of security review findings
//
//
// 53 12/17/12 3:10p Alexp
// code fixes according to "cppcheck" style & performance suggestions
//
// 52 12/06/12 7:31p Alexp
// Update ImageAuthorization()
//
// 51 11/26/12 10:46a Alexp
// replaced hardwired Var name L"SecureBootSetup" to generic Var define
//
// 50 11/19/12 4:42p Alexp
// Fix for Win8 SecureBoot logo requirement: restore Secure Boot state
// across flash updates.
// Move all secure boot Setup settings to a separate varsore variable.
// Preserve var across re-flash
//
// 49 11/09/12 4:32p Alexp
// IsPkcsSignedDataVerifiedBySignatureList()
// Fix CertData size - excude EFI_GUID size
//
// 48 10/17/12 3:03p Alexp
// EIP[104046]: Findings from Security review on Aptio4 Image verification
// Includes the fix for item #4:
// HashPeImage returns TRUE even in error case
//
// 47 10/16/12 3:53p Alexp
// EIP[104046]: Findings from Security review on Aptio4 Image verification
// Includes the fix for item #7 from the list attached to the EIP
//
// 46 9/25/12 11:22a Alexp
// removed ImageVerify simulation when system in SetupMode.
//
// 45 9/24/12 12:26p Alexp
// fix code branch for WIN_CERT_TYPE_EFI_GUID certificates
//
// 44 9/19/12 4:07p Alexp
// fix code branch to set action EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED
//
// 43 9/14/12 3:42p Alexp
// Hardwire LoadFromFv to AlwaysEnable irrespective of SetupData
//
// 42 9/13/12 11:38a Alexp
// Fix Bug in HashPeiImage.
// Last step 16 in hashing of optional data structures after the
// Certificate entry was missing
//
// 41 9/07/12 9:37a Alexp
// 1. Set all global variables Static
// 2. IsPkcsSignedDataVerifiedBySignatureList(). Compiler optimization for
// "IsVerified = VerifyWinCertificateForPkcsSignedData(
// &(PkcsCertData->Hdr));"
// may not link the main code.
// 3. Run Verify code when platform is in SetupMode and SecureBoot set to
// Custom
//
// 39 9/05/12 5:42p Alexp
// GetImageType() :
// Change Device Path search criteria to match EDKII example:
// check FV path first and then Block I/O
//
// 38 8/29/12 11:16a Alexp
// Add definition for EFI_MAX_ADDRESS if it's not defined elsewhere in
// included header files
//
// 37 8/28/12 4:21p Alexp
// Restore dependencies on SecureBootMode->Custom Setup option
//
// 35 8/15/12 4:16p Alexp
// IsPkcsSignedDataVerifiedBySignatureList()
// Fix bug prev version in processing multiple Certs from PE Hdr.
//
// 34 7/26/12 3:54p Alexp
// Query User -> Append Certificate only available in Setup Mode.
//
// 33 7/25/12 6:45p Alexp
// 1. Update ImageVerification logic to the latest EDK2 patch (13469)
// Add support for multiple Signatures within PE/COFF table
// Additional Header validation checks
// 2. Add code flow when SecureBoot set to Custom mode.
// Image Verification is performed but failing images are allowed to
// run
// Use this mode to append image certificates to DB in User Query mode.
//
// 32 6/11/12 3:15p Alexp
// use modified WIN_CERTIFICATE_UEFI_GUID_1 struc in
// AppendEfiImageSecurityDatabaseVariableEx()
// moded struc is shorter by 4 bytes
//
// 30 5/18/12 9:21a Alexp
// Fine tune User Query messages. Only display the messages if Admin user
// has signed in
//
// 29 5/01/12 4:43p Alexp
// PCR[7] - mTrustSigDbOffs contains offset to SigData struct within SigDb
//
// 28 4/27/12 3:57p Alexp
// Fix Certificate type in AppendEfiImageSecurityDatabaseVariableEx()
//
// 27 4/23/12 5:19p Alexp
// EIP:80874 Made changes to support PCR[7] measurments for Secure Boot
// state in TCG
// Install "Efi Boot Image Certificate" info strusture on Efi System
// Table.
//
// 26 4/20/12 5:14p Alexp
// Add new function to install the handle on Efi System Table with the
// location
// within DB variable of the Trusted Certificate that was used to verify
// signature of Efi OS BootLoader image.
//
// 25 4/10/12 6:52p Alexp
// Update VerifyCertPkcsSignedData() to support extended search in
// Forbidded db for any leaf certificate, not only Root CA
//
// 23 4/03/12 7:35p Alexp
// Move ReadImage() under legacy SecureFileAuthentication()
//
// 22 4/02/12 4:24p Alexp
// Installs EFI_SECURITY2_ARCH_PROTOCOL.
// Input arguments include File Path and pointer to a File buffer in
// memory.
//
// 20 3/20/12 10:51a Alexp
// 1. fix in AppendEfiImageSecurityDatabaseVariableEx()
// 2. removed unused global vars
//
// 18 3/16/12 10:29a Alexp
// [EIP85500] Build problem with (INT)4.6.5.1_SECUREBOOT_WIN8_11
// move "extern EFI_GUID BdsConnectDriversProtocolGuid " outside of #ifdef
// scope
//
// 17 3/13/12 2:41p Alexp
// Add check for Admin signed credentials while allowing User Querry
// policy
//
// 16 3/12/12 3:01p Alexp
// 1. Deffer image verification till BdsConnectDriversProtocol event.
// Speeds up the boot time.
// 2. Enforce default (hardwired by SDL token) image veriication policy
// for image device path if SecureBootMode is set to Standard.
//
// 15 3/09/12 3:39p Alexp
// 1. Fixed Image authentication by Hash certificate in "db"
// 2. Add User interructive controls to append image certificate to Sig
// "db" (test implementation, will change in later releases)
//
// 14 2/27/12 6:47p Alexp
// Reduce time spent in the DxeImageVerificationHandler function during
// non-Secure Boot mode. Original code caused additional 0.5 sec to boot
// time.
// Note: internal SecureBoot mode flag once registered during
// initializaton - will not change until restart.
//
// 13 1/04/12 3:27p Alexp
// Fixed possible security risk:
// GetImageType(). Block I/O Device Path check made higher priority then
// of FV device path.
//
// 12 12/29/11 6:49p Alexp
// GetImageType() - Fix for Image Device Path resolution
// Added SubType = MEDIA_FV_FILEPATH_DP to search options for
// MEDIA_DEVICE_PATH.
// It appears SmmDriverDispatcher sets this device path for files loaded
// by SmmDispatcher.
//
// 11 12/23/11 8:59a Alexp
// Bug fix:
// EIP:78978:Secure boot will hang at CP 0x72
// ConOut & ConIn are not available until later in BDS phase, earlier
// calls to display User Prompt may hang the system
//
// 10 12/07/11 7:23p Alexp
// Bug fix:
// EIP:77088 SecureBoot: Image can be loaded if click "No" when query
// user after image verification failed
// ImageAuthorization() -> Return EFI_ACCESS_DENIED of User Override
// option
//
// 9 8/22/11 11:14a Alexp
// optimizations to GetImageType()
//
// 6 7/18/11 4:12p Alexp
// EIP:65085. LoadImage Error status is different When Secure Boot is ON
// EIP:65194: No way to control the Secure Boot module to not interact
// with the USER without changing the code
//
// 4 6/23/11 9:27a Alexp
// moved mDigitalSig Protocol check up before API may be called
//
// 3 6/22/11 5:45p Alexp
// draft change: add Sha256 as valid cert for "db" databases
//
// 2 6/13/11 7:21p Alexp
// fix Hash calculation for Sha256 Certs in Forbidden database dbx
//
// 10 5/17/11 5:36p Alexp
// update conOut messages
//
// 9 5/17/11 12:50p Alexp
// replace AmiPostManager with pST->ConOut
// use pBS instead of gBS
//
// 6 5/13/11 4:02p Alexp
// 1. add dependency on Core rev. 4.6.5+ to buld for older Aptio cores
// 2. Ignore Querry User policy for internal FV files
//
//**********************************************************************
/*++
This file contains an 'Intel Peripheral Driver' and is
licensed for Intel CPUs and chipsets under the terms of your
license agreement with Intel or your vendor. This file may
be modified by the user, subject to additional terms of the
license agreement
--*/
/** @file
Implement image verification services for secure boot
service in UEFI2.2.
Caution: This file requires additional review when modified.
This library will have external input - PE/COFF image.
This external input must be validated carefully to avoid security issue like
buffer overflow, integer overflow.
DxeImageVerificationLibImageRead() function will make sure the PE/COFF image content
read is within the image buffer.
DxeImageVerificationHandler(), HashPeImageByType(), HashPeImage() function will accept
untrusted PE/COFF image and validate its data structure within this image buffer before use.
Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
This software and associated documentation (if any) is furnished
under a license and may only be used or copied in accordance
with the terms of the license. Except as permitted by such
license, no part of this software or documentation may be
reproduced, stored in a retrieval system, or transmitted in any
form or by any means without the express written consent of
Intel Corporation.
**/
#pragma warning (disable : 4090)
#include
#include
#include "DxeImageVerificationLib.h"
#include "EfiImage.h"
#include
#include
#include
#include
#include
#include
#include //EIP 122339
#include
extern EFI_GUID BdsConnectDriversProtocolGuid;
// 4.6.5.4
#if defined(CORE_COMBINED_VERSION) && CORE_COMBINED_VERSION >=0x4028e
#include
#endif
// 4.6.5.1
#if defined(CORE_COMBINED_VERSION) && CORE_COMBINED_VERSION >=0x4028b
#include
#else
#include
//********tmp declaration from EdkIICommon.h
#define MEDIA_RELATIVE_OFFSET_RANGE_DP 0x08
extern VOID ZeroMem (
OUT VOID *Buffer,
IN UINTN Size
);
VOID* CopyMem (
OUT VOID *DestinationBuffer,
IN VOID *SourceBuffer,
IN UINTN Length
);
#endif
EFI_STATUS
EFIAPI
PeCoffLoaderGetImageInfo (
IN EFI_PEI_PE_COFF_LOADER_PROTOCOL *This,
IN OUT EFI_PEI_PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
);
#include
#include
#define AMI_MEDIA_DEVICE_PATH_GUID \
{ 0x5023b95c, 0xdb26, 0x429b, 0xa6, 0x48, 0xbd, 0x47, 0x66, 0x4c, 0x80, 0x12 }
#define STANDARD_SECURE_BOOT 0
#define CUSTOM_SECURE_BOOT 1
static EFI_GUID AmiMediaDevicePathGuid = AMI_MEDIA_DEVICE_PATH_GUID;
static EFI_GUID gSecureSetupGuid = SECURITY_FORM_SET_GUID;
static EFI_GUID gEfiGlobalVariableGuid = EFI_GLOBAL_VARIABLE;
static AMI_DIGITAL_SIGNATURE_PROTOCOL *mDigitalSigProtocol = NULL;
//
// Caution: This is used by a function which may receive untrusted input.
// These global variables hold PE/COFF image data, and they should be validated before use.
//
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION mNtHeader;
static UINT8 *mImageBase = NULL;
static UINTN mImageSize;
static EFI_GUID *mCertType;
static UINT32 mPeCoffHeaderOffset;
static UINT8 mImageDigest[MAX_DIGEST_SIZE];
static UINTN mImageDigestSize;
static SECURE_BOOT_SETUP_VAR mSecureBootSetup;
static UINT8 mSecureBootEnable = 0;
static UINTN mFvHandlesCount = 0;
static EFI_HANDLE *mFvHandles = NULL;
static BOOLEAN gImageLoadingAfterBDS = FALSE;
//
// Notify string for authorization UI.
//
CHAR16 mNotifyString1[MAX_NOTIFY_STRING_LEN] = L"\r\nImage verification failed!\r\n";
CHAR16 mNotifyString2[MAX_NOTIFY_STRING_LEN] = L"\r\nLaunch this image anyway? (Y/N)";
CHAR16 mNotifyString3[MAX_NOTIFY_STRING_LEN] = L"Image Certificate not found in Authorized database(db)!";
CHAR16 mNotifyString4[MAX_NOTIFY_STRING_LEN] = L"Image Certificate is found in Forbidden database(dbx)!";
CHAR16 mNotifyString5[MAX_NOTIFY_STRING_LEN] = L"Image is unsigned or Certificate is invalid!";
//6683D10C-CF6E-4914-B5B4-AB8ED7370ED7
EFI_GUID gBootImageCertTblGuid = AMI_VALID_BOOT_IMAGE_CERT_TBL_GUID;
static UINT32 mTrustSigDbOffs = 0;
static UINT32 mTrustSigDbSize = 0;
//---------------------------------------
// NEW TIME STAMP definitions ECR#1009
//---------------------------------------
extern EFI_GUID gEfiCertX509Sha256Guid;
extern EFI_GUID gEfiCertX509Sha384Guid;
extern EFI_GUID gEfiCertX509Sha512Guid;
static INT32 mTimeOfSigningLong;
typedef enum {
CertUndefined,
CertSha256,
CertX509,
CertX509Sha256,
CertX509Sha384,
CertX509Sha512,
} nCertType;
//---------------------------------------
// NEW TIME STAMP definitions ECR#1009
//---------------------------------------
//************TEMP UTILITY FUNCTIONS. Use EDKII common wrapper lib instead****************
#if defined(CORE_COMBINED_VERSION) && CORE_COMBINED_VERSION >=0x4028b
#else
UINTN StrSize (
IN CHAR16 *String)
{
UINTN Size;
for (Size = 2; *String != L'\0'; String++, Size += 2);
return Size;
}
#endif
VOID* GetEfiGlobalVariableEx (
IN CHAR16 *Name,
IN OUT UINTN *VarSize
)
{
EFI_STATUS Status;
VOID *Var=NULL;
Status = GetEfiVariable(Name, &gEfiGlobalVariableGuid, NULL, VarSize, &Var);
return (EFI_ERROR(Status)) ? NULL : Var;
}
VOID* GetEfiImageSecurityDatabaseVariableEx (
IN CHAR16 *Name,
IN OUT UINTN *VarSize
)
{
EFI_STATUS Status;
VOID *Var = NULL;
Status = GetEfiVariable(Name, &gEfiImageSecurityDatabaseGuid, NULL, VarSize, &Var);
return (EFI_ERROR(Status)) ? NULL : Var;
}
/**
Reads contents of a PE/COFF image in memory buffer.
Caution: This function may receive untrusted input.
PE/COFF image is external input, so this function will make sure the PE/COFF image content
read is within the image buffer.
@param FileHandle Pointer to the file handle to read the PE/COFF image.
@param FileOffset Offset into the PE/COFF image to begin the read operation.
@param ReadSize On input, the size in bytes of the requested read operation.
On output, the number of bytes actually read.
@param Buffer Output buffer that contains the data read from the PE/COFF image.
@retval EFI_SUCCESS The specified portion of the PE/COFF image was read and the size
**/
EFI_STATUS
EFIAPI
DxeImageVerificationLibImageRead (
IN VOID *FileHandle,
IN UINTN FileOffset,
IN OUT UINTN *ReadSize,
OUT VOID *Buffer
)
{
if (FileHandle == NULL || ReadSize == NULL || Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
if ((EFI_MAX_ADDRESS - FileOffset) < *ReadSize) {
return EFI_INVALID_PARAMETER;
}
if (((UINT64)FileOffset + *ReadSize) > mImageSize) {
// *ReadSize = (UINT32)(mImageSize - FileOffset);
return EFI_INVALID_PARAMETER;
}
if (FileOffset >= mImageSize) {
// *ReadSize = 0;
return EFI_INVALID_PARAMETER;
}
CopyMem (Buffer, (UINT8 *)((UINTN) FileHandle + FileOffset), *ReadSize);
return EFI_SUCCESS;
}
/**
Get the image type.
@param[in] File This is a pointer to the device path of the file that is
being dispatched.
@return UINT32 Image Type
**/
UINT32
GetImageType (
IN CONST EFI_DEVICE_PATH_PROTOCOL *File
)
{
EFI_STATUS Status;
EFI_HANDLE DeviceHandle;
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
UINT8 nDevicePathSubType;
//EIP 122339 Start
EFI_PCI_IO_PROTOCOL *PciIoInterface = NULL;
UINT64 AttributesResult;
//EIP 122339 Stop
UINTN Index;
BOOLEAN IsFv = FALSE;
// Unknown device path: image security policy is applied to the image with the least trusted origin.
if (File == NULL) {
return IMAGE_UNKNOWN;
}
//
// Check if File is just a Firmware Volume.
//
TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File;
while (!IsDevicePathEndType(TempDevicePath)){
nDevicePathSubType = DevicePathSubType (TempDevicePath);
TRACE((TRACE_ALWAYS,"Device PathType %x, SubType %x\n", DevicePathType(TempDevicePath), nDevicePathSubType ));
//
// EFI Specification extension on Media Device Path. MEDIA_FW_VOL_FILEPATH_DEVICE_PATH is adopted by UEFI later and added in UEFI2.10.
// In EdkCompatibility Package, we only support MEDIA_FW_VOL_FILEPATH_DEVICE_PATH that complies with EFI 1.10 and UEFI 2.10.
//
if( (DevicePathType(TempDevicePath) == MEDIA_DEVICE_PATH && nDevicePathSubType == MEDIA_FV_FILEPATH_DP) ||
(DevicePathType(TempDevicePath) == HARDWARE_DEVICE_PATH && nDevicePathSubType == HW_MEMMAP_DP)
){
IsFv = TRUE;
} else {
IsFv = FALSE;
break;
}
TempDevicePath = NextDevicePathNode(TempDevicePath);
}
//
// Check to see if a File or a FV is from internal Firmware Volume.
//
if(IsFv) {
DeviceHandle = NULL;
TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File;
Status = pBS->LocateDevicePath (
#if (PI_SPECIFICATION_VERSION < 0x00010000)
&gEfiFirmwareVolumeProtocolGuid,
#else
&gEfiFirmwareVolume2ProtocolGuid,
#endif
&TempDevicePath,
&DeviceHandle
);
if (!EFI_ERROR (Status)) {
if(!gImageLoadingAfterBDS)
return IMAGE_FROM_FV;
Status = pBS->OpenProtocol (
DeviceHandle,
#if (PI_SPECIFICATION_VERSION < 0x00010000)
&gEfiFirmwareVolumeProtocolGuid,
#else
&gEfiFirmwareVolume2ProtocolGuid,
#endif
NULL,
NULL,
NULL,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
if (!EFI_ERROR (Status)) {
for(Index=0;Index < mFvHandlesCount;Index++){
TRACE((TRACE_ALWAYS,"FV%02d. DeviceHandle %X=%X\n", Index, DeviceHandle, mFvHandles[Index] ));
if(DeviceHandle == mFvHandles[Index] )
return IMAGE_FROM_FV;
}
}
}
return IMAGE_UNKNOWN;
}
//
// Next check to see if File is from a Block I/O device
// Must be a Block I/O device since we reached here after Int FV path check
//
DeviceHandle = NULL;
TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File;
Status = pBS->LocateDevicePath (
&gEfiBlockIoProtocolGuid,
&TempDevicePath,
&DeviceHandle
);
if (!EFI_ERROR (Status)) {
BlockIo = NULL;
Status = pBS->OpenProtocol (
DeviceHandle,
&gEfiBlockIoProtocolGuid,
(VOID **) &BlockIo,
NULL,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status) && BlockIo != NULL) {
if (BlockIo->Media != NULL) {
if (BlockIo->Media->RemovableMedia) {
//
// Block I/O is present and specifies the media is removable
//
return IMAGE_FROM_REMOVABLE_MEDIA;
} else {
//
// Block I/O is present and specifies the media is not removable
//
return IMAGE_FROM_FIXED_MEDIA;
}
}
}
}
//
// File is not in a Firmware Volume or on a Block I/O device, so check to see if
// the device path supports the Simple File System Protocol.
//
DeviceHandle = NULL;
TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File;
Status = pBS->LocateDevicePath (
&gEfiSimpleFileSystemProtocolGuid,
&TempDevicePath,
&DeviceHandle
);
if (!EFI_ERROR (Status)) {
//
// Simple File System is present without Block I/O, so assume media is fixed.
//
return IMAGE_FROM_FIXED_MEDIA;
}
//
// File is not from an FV, Block I/O or Simple File System, so the only options
// left are a PCI Option ROM and a Load File Protocol such as a PXE Boot from a NIC.
//
TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File;
while (!IsDevicePathEndType (TempDevicePath)) {
nDevicePathSubType = DevicePathSubType (TempDevicePath);
switch (DevicePathType (TempDevicePath)) {
//EIP 219415 Start
//Embedded OpROM
case HARDWARE_DEVICE_PATH:
if(nDevicePathSubType == HW_MEMMAP_DP) {
//EIP 122339 Start: Return IMAGE_FROM_FV if the EFI_PCI_IO_PROTOCOL installed
//on the same handle as the Device Path has the attribute EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM
DeviceHandle = NULL;
//
// Check if an instance of the EFI_PCI_IO_PROTOCOL is installed on the same handle
// as the Device Path. If an instance is found WorkAroundDevHandle contains the
// handle for the Device Path and EFI_PCI_IO_PROTOCOL instance.
//
Status = pBS->LocateDevicePath(
&gEfiPciIoProtocolGuid,
&File,
&DeviceHandle
);
if(EFI_ERROR(Status)) break;
Status = pBS->HandleProtocol(
DeviceHandle,
&gEfiPciIoProtocolGuid,
&PciIoInterface
);
if(EFI_ERROR(Status)) break;
//
// Using the EFI_PCI_IO_PROTOCOL get the value of the PCI controller's
// Embedded Rom attribute (EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM)
//
Status = PciIoInterface->Attributes(
PciIoInterface,
EfiPciIoAttributeOperationGet,
0, //this parameter is ignored during Get operation
&AttributesResult
);
if(EFI_ERROR(Status)) break;
//
// Check if the PCI controller's EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM is set
//
if(AttributesResult & EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM)
return IMAGE_FROM_FV;
//EIP 122339 Stop
}
break;
//EIP 219415 end
case MEDIA_DEVICE_PATH:
if (nDevicePathSubType == MEDIA_RELATIVE_OFFSET_RANGE_DP) {
return IMAGE_FROM_OPTION_ROM;
}
#if LOAD_UNSIGNED_EMBEDDED_SHELL == 1
// Case for embedded FV application such as Shell(check GUID. BootOptions.h)
if (nDevicePathSubType == MEDIA_VENDOR_DP &&
!guidcmp(&((VENDOR_DEVICE_PATH*)TempDevicePath)->Guid, &AmiMediaDevicePathGuid)) {
return IMAGE_FROM_FV;
}
#endif
break;
case MESSAGING_DEVICE_PATH:
if (nDevicePathSubType == MSG_MAC_ADDR_DP) {
return IMAGE_FROM_REMOVABLE_MEDIA;
}
break;
}
TempDevicePath = NextDevicePathNode (TempDevicePath);
}
return IMAGE_UNKNOWN;
}
/**
Caculate hash of Pe/Coff image based on the authenticode image hashing in
PE/COFF Specification 8.0 Appendix A
Caution: This function may receive untrusted input.
PE/COFF image is external input, so this function will validate its data structure
within this image buffer before use.
@param[in] HashAlg Hash algorithm type.
@retval TRUE Successfully hash image.
@retval FALSE Fail in hash image.
**/
BOOLEAN
HashPeImage (
IN UINT32 HashAlg
)
{
EFI_STATUS EfiStatus;
BOOLEAN Status;
UINT16 Magic;
EFI_IMAGE_SECTION_HEADER *Section;
UINT8 *HashBase;
UINTN HashSize;
UINTN SumOfBytesHashed;
EFI_IMAGE_SECTION_HEADER *SectionHeader;
UINTN Index;
UINTN Pos;
const UINT8 *addr[MAX_ELEM_NUM]; // tbd. test if 20 elements is enough
UINTN num_elem, len[MAX_ELEM_NUM];
EFI_GUID *EfiHashAlgorithmGuid;
UINT32 CertSize;
UINT32 NumberOfRvaAndSizes;
SectionHeader = NULL;
Status = FALSE;
EfiStatus = EFI_SECURITY_VIOLATION;
num_elem = 0;
//
// Initialize context of hash.
//
ZeroMem (mImageDigest, MAX_DIGEST_SIZE);
if (HashAlg == HASHALG_SHA1) {
mImageDigestSize = SHA1_DIGEST_SIZE;
EfiHashAlgorithmGuid = &gEfiHashAlgorithmSha1Guid;
mCertType = &gEfiCertSha1Guid;
} else if (HashAlg == HASHALG_SHA256) {
mImageDigestSize = SHA256_DIGEST_SIZE;
EfiHashAlgorithmGuid = &gEfiHashAlgorithmSha256Guid;
mCertType = &gEfiCertSha256Guid;
} else {
return FALSE;
}
// 1. Load the image header into memory.
//
// Measuring PE/COFF Image Header;
// But CheckSum field and SECURITY data directory (certificate) are excluded
//
if (mNtHeader.Pe32->FileHeader.Machine == EFI_IMAGE_MACHINE_IA64 && mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value
// in the PE/COFF Header. If the MachineType is Itanium(IA64) and the
// Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
// then override the magic value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
//
Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
} else {
//
// Get the magic value from the PE/COFF Optional Header
//
Magic = mNtHeader.Pe32->OptionalHeader.Magic;
}
//
// 3. Calculate the distance from the base of the image header to the image checksum address.
// 4. Hash the image header from its base to beginning of the image checksum.
//
HashBase = mImageBase;
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset.
//
HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32->OptionalHeader.CheckSum) - HashBase);
NumberOfRvaAndSizes = mNtHeader.Pe32->OptionalHeader.NumberOfRvaAndSizes;
} else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
//
// Use PE32+ offset.
//
HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32Plus->OptionalHeader.CheckSum) - HashBase);
NumberOfRvaAndSizes = mNtHeader.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
} else {
//
// Invalid header magic number.
//
TRACE((TRACE_ALWAYS,"Invalid header magic number.\n"));
goto Done;
}
if (HashSize != 0)
{
addr[num_elem] = HashBase;
len[num_elem++] = HashSize;
} else {
//TRACE((TRACE_ALWAYS,"Hash1.%x\n", HashSize));
goto Done;}
//
// 5. Skip over the image checksum (it occupies a single ULONG).
//
if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
//
// 6. Since there is no Cert Directory in optional header, hash everything
// from the end of the checksum to the end of image header.
//
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset.
//
HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);
HashSize = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - mImageBase);
} else {
//
// Use PE32+ offset.
//
HashBase = (UINT8 *) &mNtHeader.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32);
HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - mImageBase);
}
if (HashSize != 0)
{
addr[num_elem] = HashBase;
len[num_elem++] = HashSize;
} else {
//TRACE((TRACE_ALWAYS,"Hash2.%x\n", HashSize));
goto Done;}
} else {
//
// 7. Hash everything from the end of the checksum to the start of the Cert Directory.
//
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset.
//
HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);
HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase);
} else {
//
// Use PE32+ offset.
//
HashBase = (UINT8 *) &mNtHeader.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32);
HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase);
}
if (HashSize != 0)
{
addr[num_elem] = HashBase;
len[num_elem++] = HashSize;
} else {
//TRACE((TRACE_ALWAYS,"Hash3.%x\n", HashSize));
goto Done;}
//
// 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.)
// 9. Hash everything from the end of the Cert Directory to the end of image header.
//
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset
//
HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];
HashSize = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - mImageBase);
} else {
//
// Use PE32+ offset.
//
HashBase = (UINT8 *) &mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];
HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - mImageBase);
}
if (HashSize != 0)
{
addr[num_elem] = HashBase;
len[num_elem++] = HashSize;
} else {
//TRACE((TRACE_ALWAYS,"Hash4.%x\n", HashSize));
goto Done;}
}
//
// 10. Set the SUM_OF_BYTES_HASHED to the size of the header.
//
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset.
//
SumOfBytesHashed = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders;
} else {
//
// Use PE32+ offset
//
SumOfBytesHashed = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders;
}
Section = (EFI_IMAGE_SECTION_HEADER *) (
mImageBase +
mPeCoffHeaderOffset +
sizeof (UINT32) +
sizeof (EFI_IMAGE_FILE_HEADER) +
mNtHeader.Pe32->FileHeader.SizeOfOptionalHeader
);
//
// 11. Build a temporary table of pointers to all the IMAGE_SECTION_HEADER
// structures in the image. The 'NumberOfSections' field of the image
// header indicates how big the table should be. Do not include any
// IMAGE_SECTION_HEADERs in the table whose 'SizeOfRawData' field is zero.
//
// TRACE((TRACE_ALWAYS,"Num Sections = %x\n", mNtHeader.Pe32->FileHeader.NumberOfSections));
// Security review [305408]: HashPeImage does not validate NumberOfSections field
Pos = sizeof (EFI_IMAGE_SECTION_HEADER) * mNtHeader.Pe32->FileHeader.NumberOfSections;
if(Pos > mImageSize)
goto Done;
EfiStatus = pBS->AllocatePool(EfiBootServicesData, Pos, &SectionHeader);
if (SectionHeader == NULL || EFI_ERROR(EfiStatus)) {
//TRACE((TRACE_ALWAYS,"Hash4.\n"));
goto Done;
}
MemSet(SectionHeader, Pos, 0);
//
// 12. Using the 'PointerToRawData' in the referenced section headers as
// a key, arrange the elements in the table in ascending order. In other
// words, sort the section headers according to the disk-file offset of
// the section.
//
for (Index = 0; Index < mNtHeader.Pe32->FileHeader.NumberOfSections; Index++) {
Pos = Index;
// Security review EIP[104046]:6. HashPeImage does not validate NumberOfSections field
if(((UINT64)Section + sizeof (EFI_IMAGE_SECTION_HEADER))> ((UINT64)mImageBase+mImageSize) )
goto Done;
while ((Pos > 0) && (Section->PointerToRawData < SectionHeader[Pos - 1].PointerToRawData)) {
CopyMem (&SectionHeader[Pos], &SectionHeader[Pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER));
Pos--;
}
CopyMem (&SectionHeader[Pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER));
Section += 1;
}
//
// 13. Walk through the sorted table, bring the corresponding section
// into memory, and hash the entire section (using the 'SizeOfRawData'
// field in the section header to determine the amount of data to hash).
// 14. Add the section's 'SizeOfRawData' to SUM_OF_BYTES_HASHED .
// 15. Repeat steps 13 and 14 for all the sections in the sorted table.
//
for (Index = 0; Index < mNtHeader.Pe32->FileHeader.NumberOfSections; Index++) {
Section = &SectionHeader[Index];
if (Section->SizeOfRawData == 0) {
continue;
}
//TRACE((TRACE_ALWAYS,"Section->PointerToRawData = %x\nSection->SizeOfRawData = %x\nSumOfBytesHashed = %x\n",Section->PointerToRawData, Section->SizeOfRawData, SumOfBytesHashed+Section->SizeOfRawData));
// Security review EIP[104046]: 5.HashPeImage does not validate PointerToRawData and SizeOfRawData fields
if( ((UINT64)Section->PointerToRawData + Section->SizeOfRawData) > mImageSize )
goto Done;
HashBase = mImageBase + Section->PointerToRawData;
HashSize = (UINTN) Section->SizeOfRawData;
addr[num_elem] = HashBase;
len[num_elem++] = HashSize;
if(num_elem >= MAX_ELEM_NUM)
// goto Done;
{
TRACE((TRACE_ALWAYS,"MAX_ELEM_NUM.1 = %d\n", num_elem));
goto Done;}
// Security review EIP[125931]: HashPeImage does not validate certificate table offset and size
if (((UINT64)SumOfBytesHashed+HashSize) > mImageSize)
goto Done;
SumOfBytesHashed += HashSize;
}
//
// 16. If the file size is greater than SUM_OF_BYTES_HASHED, there is extra
// data in the file that needs to be added to the hash. This data begins
// at file offset SUM_OF_BYTES_HASHED and its length is:
// FileSize - (CertDirectory->Size)
//
//dbg
//TRACE((TRACE_ALWAYS,"mImageSize > SumOfBytesHashed.\n%x > %x\n", mImageSize, SumOfBytesHashed));
//dbg
if (mImageSize > SumOfBytesHashed) {
HashBase = mImageBase + SumOfBytesHashed;
if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
CertSize = 0;
} else {
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset.
//
CertSize = mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
} else {
//
// Use PE32+ offset.
//
CertSize = mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
}
}
if (mImageSize > ((UINT64)CertSize + SumOfBytesHashed)) {
HashSize = (UINTN) (mImageSize - CertSize - SumOfBytesHashed);
//TRACE((TRACE_ALWAYS,"\nData beyond nSumOfBytesHashed\nBase = %x, Size = %x\n",SumOfBytesHashed, HashSize));
addr[num_elem] = HashBase;
len[num_elem++] = HashSize;
if(num_elem >= MAX_ELEM_NUM)
// goto Done;
{
TRACE((TRACE_ALWAYS,"MAX_ELEM_NUM.2=%d\n", num_elem));
goto Done;}
} else if (mImageSize < ((UINT64)CertSize + SumOfBytesHashed)) {
TRACE((TRACE_ALWAYS,"mImageSize < CertSize + SumOfBytesHashed.\n%d < %d\n", mImageSize, CertSize + SumOfBytesHashed));
goto Done;
}
else
HashSize = 0;
// ??? HashBase += CertSize;
//TRACE((TRACE_ALWAYS,"\nData beyond CertDir\nBase = %x, Size = %x\n",SumOfBytesHashed+HashSize+CertSize, mImageSize-(SumOfBytesHashed+HashSize+CertSize)));
}
EfiStatus = mDigitalSigProtocol->Hash(
mDigitalSigProtocol,
EfiHashAlgorithmGuid, num_elem, addr, len, (UINT8*)&mImageDigest);
TRACE((TRACE_ALWAYS,"Found HashElements %d\nImageHash: [%2X],[%2X],..\n", num_elem, mImageDigest[0], mImageDigest[1]));
if(!EFI_ERROR(EfiStatus))
Status = TRUE;
Done:
if (SectionHeader != NULL) {
pBS->FreePool (SectionHeader);
}
// Security review EIP[104046]: 4.HashPeImage returns TRUE even in error case
// Status = FALSE unless updated to TRUE in the final Hash step before "Done" label
return Status;
}
/**
Recognize the Hash algorithm in PE/COFF Authenticode and caculate hash of
Pe/Coff image based on the authenticode image hashing in PE/COFF Specification
8.0 Appendix A
Caution: This function may receive untrusted input.
PE/COFF image is external input, so this function will validate its data structure
within this image buffer before use.
@param[in] AuthData Pointer to the Authenticode Signature retrieved from signed image.
@param[in] AuthDataSize Size of the Authenticode Signature in bytes.
@retval EFI_UNSUPPORTED Hash algorithm is not supported.
@retval EFI_SUCCESS Hash successfully.
**/
EFI_STATUS
HashPeImageByType (
IN UINT8 *AuthData,
IN UINTN AuthDataSize
)
{
EFI_STATUS Status;
UINT8 DigestAlgo;
UINT8 *pDigestAlgo;
UINTN Size;
// get Digest Algorithm
Size = 0;
pDigestAlgo = (UINT8*)&DigestAlgo;
Status = mDigitalSigProtocol->Pkcs7Verify (
mDigitalSigProtocol,
AuthData,
AuthDataSize,
NULL,
0,
&pDigestAlgo, // returns DER Ptr to Sign Cert
&Size,
Pkcs7GetDigestAlgorithm,
LOCK
);
if (EFI_ERROR(Status))
return Status;
switch(DigestAlgo) {
case SHA1:
if (!HashPeImage (HASHALG_SHA1)) {
Status = EFI_SECURITY_VIOLATION;
}
break;
case SHA256:
if (!HashPeImage (HASHALG_SHA256)) {
Status = EFI_SECURITY_VIOLATION;
}
break;
default:
Status = EFI_UNSUPPORTED;
break;
}
return Status;
}
/**
Returns the size of a given image execution info table in bytes.
This function returns the size, in bytes, of the image execution info table specified by
ImageExeInfoTable. If ImageExeInfoTable is NULL, then 0 is returned.
@param ImageExeInfoTable A pointer to a image execution info table structure.
@retval 0 If ImageExeInfoTable is NULL.
@retval Others The size of a image execution info table in bytes.
**/
UINTN
GetImageExeInfoTableSize (
EFI_IMAGE_EXECUTION_INFO_TABLE *ImageExeInfoTable
)
{
UINTN Index;
EFI_IMAGE_EXECUTION_INFO *ImageExeInfoItem;
UINTN TotalSize;
if (ImageExeInfoTable == NULL) {
return 0;
}
ImageExeInfoItem = (EFI_IMAGE_EXECUTION_INFO *) ((UINT8 *) ImageExeInfoTable + sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE));
TotalSize = sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE);
for (Index = 0; Index < ImageExeInfoTable->NumberOfImages; Index++) {
TotalSize += ImageExeInfoItem->InfoSize;
ImageExeInfoItem = (EFI_IMAGE_EXECUTION_INFO *) ((UINT8 *) ImageExeInfoItem + ImageExeInfoItem->InfoSize);
}
return TotalSize;
}
/**
Create an Image Execution Information Table entry and add it to system configuration table.
@param[in] Action Describes the action taken by the firmware regarding this image.
@param[in] Name Input a null-terminated, user-friendly name.
@param[in] DevicePath Input device path pointer.
@param[in] Signature Input signature info in EFI_SIGNATURE_LIST data structure.
@param[in] SignatureSize Size of signature.
**/
VOID
AddImageExeInfo (
IN EFI_IMAGE_EXECUTION_ACTION Action,
IN CHAR16 *Name OPTIONAL,
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
IN EFI_SIGNATURE_LIST *Signature OPTIONAL,
IN UINTN SignatureSize
)
{
EFI_STATUS Status;
EFI_IMAGE_EXECUTION_INFO_TABLE *ImageExeInfoTable;
EFI_IMAGE_EXECUTION_INFO_TABLE *NewImageExeInfoTable;
EFI_IMAGE_EXECUTION_INFO *ImageExeInfoEntry;
UINTN ImageExeInfoTableSize;
UINTN NewImageExeInfoEntrySize;
UINTN NameStringLen;
UINTN DevicePathSize;
NewImageExeInfoTable = NULL;
ImageExeInfoEntry = NULL;
NameStringLen = sizeof (CHAR16);
if (DevicePath == NULL) {
return ;
}
if (Name != NULL) {
NameStringLen = StrSize (Name);
}
ImageExeInfoTable = GetEfiConfigurationTable(pST, &gEfiImageSecurityDatabaseGuid);
if (ImageExeInfoTable != NULL) {
//
// The table has been found!
// We must enlarge the table to accmodate the new exe info entry.
//
ImageExeInfoTableSize = GetImageExeInfoTableSize (ImageExeInfoTable);
} else {
//
// Not Found!
// We should create a new table to append to the configuration table.
//
ImageExeInfoTableSize = sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE);
}
DevicePathSize = DPLength(DevicePath);
NewImageExeInfoEntrySize = sizeof (EFI_IMAGE_EXECUTION_INFO) + NameStringLen + DevicePathSize + SignatureSize;
Status = pBS->AllocatePool(EfiRuntimeServicesData, ImageExeInfoTableSize + NewImageExeInfoEntrySize, &NewImageExeInfoTable);
if(EFI_ERROR(Status)) return;
if (ImageExeInfoTable != NULL) {
CopyMem (NewImageExeInfoTable, ImageExeInfoTable, ImageExeInfoTableSize);
} else {
NewImageExeInfoTable->NumberOfImages = 0;
}
NewImageExeInfoTable->NumberOfImages++;
ImageExeInfoEntry = (EFI_IMAGE_EXECUTION_INFO *) ((UINT8 *) NewImageExeInfoTable + ImageExeInfoTableSize);
//
// Update new item's infomation.
//
ImageExeInfoEntry->Action = Action;
ImageExeInfoEntry->InfoSize = (UINT32) NewImageExeInfoEntrySize;
if (Name != NULL) {
CopyMem ((UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32), Name, NameStringLen);
} else {
ZeroMem ((UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32), NameStringLen);
}
CopyMem (
(UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32) + NameStringLen,
DevicePath,
DevicePathSize
);
if (Signature != NULL) {
CopyMem (
(UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32) + NameStringLen + DevicePathSize,
Signature,
SignatureSize
);
}
//
// Update/replace the image execution table.
//
Status = pBS->InstallConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID *) NewImageExeInfoTable);
ASSERT_EFI_ERROR (Status);
//
// Free Old table data!
//
if (ImageExeInfoTable != NULL) {
pBS->FreePool (ImageExeInfoTable);
}
}
/**
Create a Boot Image Certificate table entry and add it to system configuration table.
@param[in] Name Input a null-terminated, user-friendly name.
@param[in] DevicePath Input device path pointer.
@param[in] Signature Input signature info in EFI_SIGNATURE_LIST data structure.
@param[in] SignatureSize Size of signature.
**/
VOID
AddBootImageCertInfo (
IN CHAR16 *Name OPTIONAL,
IN UINT32 SignatureOffs,
IN UINT32 SignatureSize
)
{
EFI_STATUS Status;
AMI_VALID_CERT_IN_SIG_DB *BootImageCertInfoTable;
UINTN BootImageCertInfoTableSize;
if(SignatureOffs == 0 || SignatureSize == 0)
return;
BootImageCertInfoTable = GetEfiConfigurationTable(pST, &gBootImageCertTblGuid);
if (BootImageCertInfoTable == NULL) {
//
// Not Found!
// We should create a new table.
//
BootImageCertInfoTableSize = sizeof (AMI_VALID_CERT_IN_SIG_DB);
Status = pBS->AllocatePool(EfiRuntimeServicesData, BootImageCertInfoTableSize, &BootImageCertInfoTable);
if(EFI_ERROR(Status)) return;
//
// Update/replace the image execution table.
//
Status = pBS->InstallConfigurationTable (&gBootImageCertTblGuid, (VOID *) BootImageCertInfoTable);
ASSERT_EFI_ERROR (Status);
TRACE((TRACE_ALWAYS,"Install BootImageCert Table ...%r\n", Status));
if (EFI_ERROR (Status)) return;
}
//
// Update Table's infomation.
//
BootImageCertInfoTable->SigOffset = SignatureOffs;
BootImageCertInfoTable->SigLength = SignatureSize;
TRACE((TRACE_ALWAYS,"BootImage Cert in db\nOffset=%X\nLength=%X\n", BootImageCertInfoTable->SigOffset, BootImageCertInfoTable->SigLength));
}
/**
Discover if the UEFI image is authorized by user's policy setting.
@param[in] Policy Specify platform's policy setting.
@param[in] Action Image Validation status.
@retval EFI_ACCESS_DENIED Image is not allowed to run.
@retval EFI_SECURITY_VIOLATION Image is deferred.
@retval EFI_SUCCESS Image is authorized to run.
**/
EFI_STATUS
ImageAuthorization (
IN UINT32 Policy,
IN UINT32 Action
)
{
EFI_STATUS Status;
EFI_INPUT_KEY Key = {0,0};
UINTN Index;
BOOLEAN bQuery;
Status = EFI_ACCESS_DENIED;
switch (Policy) {
case QUERY_USER_ON_SECURITY_VIOLATION:
// Conditions to allow overwrite of Execution Policy:
// 1. Output Console available
if((pST->ConIn != NULL && pST->ConOut != NULL))
{
pST->ConOut->OutputString(pST->ConOut, mNotifyString1);
bQuery = TRUE;
switch(Action) {
case EFI_IMAGE_EXECUTION_AUTH_SIG_NOT_FOUND: // not found in db
pST->ConOut->OutputString(pST->ConOut, mNotifyString3);
break;
case EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND: // found in dbx
pST->ConOut->OutputString(pST->ConOut, mNotifyString4);
break;
case EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED:
case EFI_IMAGE_EXECUTION_AUTH_UNTESTED:
pST->ConOut->OutputString(pST->ConOut, mNotifyString5);
break;
default:
bQuery = FALSE;
break;
}
if(!bQuery) break;
//Wait for key
pST->ConOut->OutputString(pST->ConOut, mNotifyString2);
Repeat:
pBS->WaitForEvent( 1, &pST->ConIn->WaitForKey, &Index );
Status = pST->ConIn->ReadKeyStroke( pST->ConIn, &Key );
if (!EFI_ERROR (Status)) {
if ((Key.UnicodeChar == L'y') || (Key.UnicodeChar == L'Y')) { //yes
Status = EFI_SUCCESS;
} else if ((Key.UnicodeChar == L'n') || (Key.UnicodeChar == L'N')) { //no
Status = EFI_ACCESS_DENIED;
} else {
goto Repeat;
}
}
pST->ConOut->OutputString(pST->ConOut, L"\r\n");
}
break;
case DEFER_EXECUTE_ON_SECURITY_VIOLATION:
Status = EFI_SECURITY_VIOLATION;
break;
// case DENY_EXECUTE_ON_SECURITY_VIOLATION:
default:
Status = EFI_ACCESS_DENIED;
break;
}
TRACE((TRACE_ALWAYS,"Image Authorization policy...%r\n", Status));
return Status;
}
/**
Advanced check in Signature Database
Check whether signature is located in target database
@param[in] Signature Pointer to signature that is searched for.
@param[in] SignatureSize Size of Signature.
@param[in] SignatureType Type of the Certificate, Guid
@return TRUE - found in IsSigForbidden
@return FALSE
**/
BOOLEAN
IsSignatureFoundInDatabase (
IN CHAR16 *Name,
EFI_GUID *SignatureType,
IN UINT8 *Signature,
IN UINTN SignatureSize
)
{
EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *Cert;
UINT8 *Data;
UINTN DataSize;
UINTN Index;
UINTN CertCount;
BOOLEAN IsSigFound;
//
// Read signature database variable.
//
IsSigFound = FALSE;
//
// Get Signature Database variable.
//
Data = GetEfiImageSecurityDatabaseVariableEx (Name, &DataSize);
if (Data != NULL) {
// not an error if no "dbx" defined
//
// Enumerate all signature data in SigDB to check if executable's signature exists.
//
CertList = (EFI_SIGNATURE_LIST *) Data;
while (!IsSigFound && (DataSize > 0) && (DataSize >= CertList->SignatureListSize)) {
if (!guidcmp(&CertList->SignatureType, SignatureType)) { // Cert types do match
CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
if (CertList->SignatureSize == (sizeof(EFI_SIGNATURE_DATA) - 1 + SignatureSize)) {
for (Index = 0; Index < CertCount; Index++) {
if (MemCmp(Cert->SignatureData, Signature, SignatureSize) == 0) {
//
// Find the signature in database.
//
IsSigFound = TRUE;
break;
}
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
}
}
} // next CertList
DataSize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
}
pBS->FreePool (Data);
}
return IsSigFound;
}
/**
Verify PKCS#7 SignedData using certificate found in Variable which formatted
as EFI_SIGNATURE_LIST. The Variable may be PK, KEK, DB or DBX.
@param VariableName Name of Variable to search for Certificate.
@retval TRUE Image pass verification.
@retval FALSE Image fail verification.
**/
BOOLEAN
IsPkcsSignedDataVerifiedBySignatureList (
IN UINT8 *AuthData,
IN UINTN AuthDataSize,
IN CHAR16 *VariableName,
IN UINT8 Operation
)
{
EFI_STATUS Status;
EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *Cert;
UINT32 CertCount;
UINT32 Index;
UINT8 *Data;
UINTN DataSize;
UINT8 *RootCert;
UINTN RootCertSize;
UINT8 *SigCert;
UINTN SigCertSize;
BOOLEAN IsVerified;
EFI_TIME *TimeStamp;
INT32 TimeOfRevocationLong;
UINT8 ValidCertType;
BOOLEAN bVerifyTimeStampStatus;
BOOLEAN bProcessVerifyTimeStampStatus;
CertList = NULL;
Cert = NULL;
RootCert = NULL;
RootCertSize = 0;
IsVerified = FALSE;
ValidCertType = CertUndefined;
bVerifyTimeStampStatus= FALSE;
bProcessVerifyTimeStampStatus = FALSE;
mTimeOfSigningLong = 0;
TimeOfRevocationLong = 0;
// Verify the certificate's header
//
// Find certificate in store and verify authenticode struct.
//
Data = GetEfiImageSecurityDatabaseVariableEx (VariableName, &DataSize);
if (!Data)
return IsVerified;
TRACE((TRACE_ALWAYS,"\nLook for a match in '%S'===>\n\n", VariableName));
CertList = (EFI_SIGNATURE_LIST *)Data;
//
// TO DO Optimization.
// 1. Find Root CA Cert Ptr or Signer Cert if Root is not found
// 2. loop thru Trust Cert list and compare each one against Root CA cert(verify SignCert signature with Trust Cert)
// 3. If found, break the 'while' loop and Pkcs7Verify with Trust Cert
//
//
// Find Cert Enrolled in database to verify the signature in pkcs7 signed data.
//
// here should be a loop thru Cert list till CertCount
while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) {
if(!guidcmp(&CertList->SignatureType, &gEfiCertX509Guid))
ValidCertType = CertX509;
else
if(!guidcmp(&CertList->SignatureType, mCertType))
ValidCertType = CertSha256;
else
if(!guidcmp(&CertList->SignatureType, &gEfiCertX509Sha256Guid))
ValidCertType = CertX509Sha256;
else
if(!guidcmp(&CertList->SignatureType, &gEfiCertX509Sha384Guid))
ValidCertType = CertX509Sha384;
else
if(!guidcmp(&CertList->SignatureType, &gEfiCertX509Sha512Guid))
ValidCertType = CertX509Sha512;
if (ValidCertType)
{
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
RootCertSize = CertList->SignatureSize-sizeof(EFI_GUID); // sig data structure starts with SigOwner Guid
TRACE((TRACE_ALWAYS,"Signature Type %g\nRootCertSize = %X\n",CertList->SignatureType, RootCertSize));
for (Index = 0; Index < CertCount; Index++) {
//
// Iterate each Signature Data Node within this CertList for verify.
//
RootCert = Cert->SignatureData;
switch(ValidCertType) {
// is Sha256/Sha1
case CertSha256:
if(RootCertSize == mImageDigestSize &&
MemCmp(RootCert, mImageDigest, mImageDigestSize) == 0) {
TRACE((TRACE_ALWAYS,"Hash Cert match found...\n"));
//
// Found the signature in database.
//
IsVerified = TRUE;
}
break;
// x509, x509_ShaXXX
/*
Revocation is true:
- return success && Time = 0
- return success && Time != 0 && VerifyTimeStampStatus
- success
if success but mVerifyTimeStampStatus = ACCESS_DENIED = return FAIL
timestamp non-0 Year > 0
*/
case CertX509Sha256:
case CertX509Sha384:
case CertX509Sha512:
switch(ValidCertType) {
case CertX509Sha256:
TimeStamp = &((EFI_CERT_X509_SHA256*)RootCert)->TimeOfRevocation;
break;
case CertX509Sha384:
TimeStamp = &((EFI_CERT_X509_SHA384*)RootCert)->TimeOfRevocation;
break;
case CertX509Sha512:
TimeStamp = &((EFI_CERT_X509_SHA512*)RootCert)->TimeOfRevocation;
break;
}
// Check the timestamp cert validity the valid certificate in allowed database (dbt).
if(!bProcessVerifyTimeStampStatus) {
bVerifyTimeStampStatus = IsPkcsSignedDataVerifiedBySignatureList (AuthData, AuthDataSize, EFI_IMAGE_SECURITY_DATABASE2, Pkcs7TimeStampCertValidateGet);
bProcessVerifyTimeStampStatus = TRUE;
}
if(os_mktime(TimeStamp->Year, TimeStamp->Month, TimeStamp->Day, TimeStamp->Hour, TimeStamp->Minute, TimeStamp->Second,&TimeOfRevocationLong))
TimeOfRevocationLong = 0;
TRACE((TRACE_ALWAYS,"\nTimeStamp cert validate %s\nTimeOfSigning %X\nTimeOfRevocation %X\n", (!bVerifyTimeStampStatus?"FAIL":"PASS"), mTimeOfSigningLong, TimeOfRevocationLong));
if(TimeOfRevocationLong) {
if(!bVerifyTimeStampStatus) {
IsVerified = TRUE; // found in dbx -> image is revoked!
break;
}
// keep looking for cert with sign time passed revocation
if(mTimeOfSigningLong < TimeOfRevocationLong)
break;
}
case CertX509:
TRACE((TRACE_ALWAYS,"proceed with Pkcs7Verify...\n"));
//
//
// Verify Authenticode struct for image's current certificate.
//
// using protocol
SigCert = mImageDigest;
SigCertSize = mImageDigestSize;
Status = mDigitalSigProtocol->Pkcs7Verify (
mDigitalSigProtocol,
AuthData,
AuthDataSize,
RootCert,
RootCertSize,
// mDigest, DigestLen
&SigCert,
&SigCertSize,
Operation, // Operation; Pkcs7CertValidate OR Pkcs7CertGetMatchInCertChain
KEEP // Flags
);
TRACE((TRACE_ALWAYS,"====> %r\n", Status));
if (!EFI_ERROR(Status)) {
IsVerified = TRUE; // found matching certificate in the image
switch(Operation) {
case Pkcs7TimeStampCertValidateGet:
mTimeOfSigningLong = (INT32)SigCertSize;
break;
case Pkcs7CertValidate:
// only x509 certs frm db are measured in PCR[7]
mTrustSigDbOffs = (UINT32)(UINT8*)(((UINT8*)Cert-Data));
mTrustSigDbSize = CertList->SignatureSize;
break;
}
}
break;
default :
goto Exit;
} // switch
if (IsVerified)
{
goto Exit;
}
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
} // end for
} // end If
DataSize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
} // end while
Exit:
pBS->FreePool (Data);
TRACE((TRACE_ALWAYS,"\n<===%s cert match in '%S'\n",(IsVerified?"Got":"No"), VariableName));
return IsVerified;
}
//
//----------------------------------------------------------------------------
// Procedure: DxeImageVerificationHandler
//
// Description: Handle for Security Architectural Protocol
// Provide verification service for signed images, which include both signature validation
// and platform policy control. For signature types, both UEFI WIN_CERTIFICATE_UEFI_GUID and
// MSFT Authenticode type signatures are supported.
//
// In this implementation, only verify external executables when in USER MODE.
// Executables from FV is bypass, so pass in AuthenticationStatus is ignored.
//
// The image verification policy is:
// If the image is signed,
// At least one valid signature or at least one hash value of the image must match a record
// in the security database "db", and no valid signature nor any hash value of the image may
// be reflected in the security database "dbx".
// Otherwise, the image is not signed,
// The SHA256 hash value of the image must match a record in the security database "db", and
// not be reflected in the security data base "dbx".
//
// Caution: This function may receive untrusted input.
// PE/COFF image is external input, so this function will validate its data structure
// within this image buffer before use.
//
// Input:
// File This is a pointer to the device path of the file that is
// being dispatched. This will optionally be used for logging.
// FileBuffer File buffer matches the input file device path.
// FileSize Size of File buffer matches the input file device path.
//
// Output: EFI_STATUS
//
// EFI_SUCCESS The file specified by File did authenticate, and the
// platform policy dictates that the DXE Core may use File.
// EFI_INVALID_PARAMETER File is NULL.
// EFI_SECURITY_VIOLATION The file specified by File did not authenticate, and
// the platform policy dictates that File should be placed
// in the untrusted state. A file may be promoted from
// the untrusted to the trusted state at a future time
// with a call to the Trust() DXE Service.
// EFI_ACCESS_DENIED The file specified by File did not authenticate, and
// the platform policy dictates that File should not be
// used for any purpose.
//
//----------------------------------------------------------------------------
//
EFI_STATUS
DxeImageVerificationHandler (
IN CONST EFI_DEVICE_PATH_PROTOCOL *File,
IN VOID *FileBuffer,
IN UINTN FileSize,
IN BOOLEAN BootPolicy
)
{
EFI_STATUS Status;
UINT16 Magic;
EFI_IMAGE_DOS_HEADER *DosHdr;
EFI_STATUS VerifyStatus;
EFI_SIGNATURE_LIST *SignatureList;
UINTN SignatureListSize;
EFI_SIGNATURE_DATA *Signature;
EFI_IMAGE_EXECUTION_ACTION Action;
WIN_CERTIFICATE *WinCertificate;
UINT32 Policy;
BOOLEAN bFileRead;
EFI_PEI_PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
UINT32 NumberOfRvaAndSizes;
WIN_CERTIFICATE_EFI_PKCS *PkcsCertData;
WIN_CERTIFICATE_UEFI_GUID *WinCertUefiGuid;
UINT8 *AuthData;
UINTN AuthDataSize;
EFI_IMAGE_DATA_DIRECTORY *SecDataDir;
UINT32 OffSet;
SignatureList = NULL;
SignatureListSize = 0;
WinCertificate = NULL;
SecDataDir = NULL;
PkcsCertData = NULL;
Action = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
VerifyStatus = EFI_ACCESS_DENIED;
Status = EFI_ACCESS_DENIED;
bFileRead = FALSE;
mImageDigestSize = 0;
mCertType = NULL;
//
// Check the image type and get policy setting.
//
switch (GetImageType (File)) {
case IMAGE_FROM_FV:
Policy = ALWAYS_EXECUTE;
break;
case IMAGE_FROM_OPTION_ROM:
Policy = mSecureBootSetup.Load_from_OROM;
break;
case IMAGE_FROM_REMOVABLE_MEDIA:
Policy = mSecureBootSetup.Load_from_REMOVABLE_MEDIA;
break;
case IMAGE_FROM_FIXED_MEDIA:
Policy = mSecureBootSetup.Load_from_FIXED_MEDIA;
break;
default:
TRACE((TRACE_ALWAYS,"Unknown Image Path\n"));
Policy = DENY_EXECUTE_ON_SECURITY_VIOLATION;
break;
}
//
// If policy is always/never execute, return directly.
//
if (Policy == ALWAYS_EXECUTE) {
return EFI_SUCCESS;
} else
if (Policy == NEVER_EXECUTE) {
return EFI_ACCESS_DENIED;
}
if (FileBuffer == NULL) {
//
// Read the PE/COFF file.
//
TRACE((TRACE_ALWAYS,"FileAuthentication\nFileType = %x, SubType = %x\n", File->Type, File->SubType));
Status = ReadImage(FALSE, File, &FileBuffer, &FileSize, NULL, NULL, NULL);
TRACE((TRACE_ALWAYS,"File read(1)...%r\n", Status));
if(EFI_ERROR(Status)) {
Status = ReadImage(TRUE, File, &FileBuffer, &FileSize, NULL, NULL, NULL);
TRACE((TRACE_ALWAYS,"File read(2)...%r\n", Status));
if(EFI_ERROR(Status))
return EFI_ACCESS_DENIED;
}
bFileRead = TRUE;
}
//
// Read the Dos header.
//
ASSERT (FileBuffer != NULL);
if (FileBuffer == NULL) {
return EFI_INVALID_PARAMETER;
}
mImageBase = (UINT8 *) FileBuffer;
mImageSize = FileSize;
ZeroMem (&ImageContext, sizeof (ImageContext));
ImageContext.Handle = (VOID *) FileBuffer;
ImageContext.ImageRead = (EFI_PEI_PE_COFF_LOADER_READ_FILE) DxeImageVerificationLibImageRead;
//
// Get information about the image being loaded
//
if (EFI_ERROR (PeCoffLoaderGetImageInfo (NULL, &ImageContext)) ||
( mImageSize< sizeof(EFI_IMAGE_DOS_HEADER)) ) {
//
// The information can't be gotten from the invalid PeImage
//
TRACE((TRACE_ALWAYS,"PeCoffLoaderGetImageInfo Error\n"));
goto Done;
}
DosHdr = (EFI_IMAGE_DOS_HEADER *) mImageBase;
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
//
// DOS image header is present,
// so read the PE header after the DOS image header.
//
mPeCoffHeaderOffset = DosHdr->e_lfanew;
} else {
mPeCoffHeaderOffset = 0;
}
// Security review EIP[104046]: 3.HashPeImage does not validate minimum image size
// Validate mPeCoffHeaderOffset is within the Image size range
if(((UINT64)mPeCoffHeaderOffset+sizeof(EFI_IMAGE_NT_HEADERS32)) > mImageSize) {
TRACE((TRACE_ALWAYS,"EFI_IMAGE_NT_SIGNATURE Error\n"));
goto Done;
}
//
// Check PE/COFF image.
//
mNtHeader.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) (mImageBase + mPeCoffHeaderOffset);
if (mNtHeader.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
//
// It is not a valid Pe/Coff file.
//
TRACE((TRACE_ALWAYS,"EFI_IMAGE_NT_SIGNATURE Error\n"));
goto Done;
}
if (mNtHeader.Pe32->FileHeader.Machine == EFI_IMAGE_MACHINE_IA64 && mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value
// in the PE/COFF Header. If the MachineType is Itanium(IA64) and the
// Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
// then override the magic value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
//
Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
} else {
//
// Get the magic value from the PE/COFF Optional Header
//
Magic = mNtHeader.Pe32->OptionalHeader.Magic;
}
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset.
//
NumberOfRvaAndSizes = mNtHeader.Pe32->OptionalHeader.NumberOfRvaAndSizes;
if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
}
} else {
//
// Use PE32+ offset.
//
NumberOfRvaAndSizes = mNtHeader.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
}
}
TRACE((TRACE_ALWAYS,"mImageSize %x\n", mImageSize));
//
// Start Image Validation.
//
if (SecDataDir && SecDataDir->Size) {
//
// Image is possibly signed
//
TRACE((TRACE_ALWAYS,"SecDataDir->VirtualAddress %x\nSecDataDir->Size = %x\nIMAGE is Signed\n",
SecDataDir->VirtualAddress, SecDataDir->Size));
// Security review EIP[104046]: 2.Multiple integer underflows calculating size of certificate data in PE file
if(((UINT64)SecDataDir->VirtualAddress+SecDataDir->Size) > mImageSize)
goto Done;
//
// Verify the signature of the image, multiple signatures are allowed as per PE/COFF Section 4.7
// "Attribute Certificate Table".
// The first certificate starts at offset (SecDataDir->VirtualAddress) from the start of the file.
//
Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
for (OffSet = SecDataDir->VirtualAddress;
OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
// OffSet += WinCertificate->dwLength, OffSet += ALIGN_SIZE (OffSet)) {
OffSet +=(WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {
WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
TRACE((TRACE_ALWAYS,"\nOffSet = %x, WinCertificate->dwLength %x\n\n", OffSet, WinCertificate->dwLength));
if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof (WIN_CERTIFICATE) ||
(SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength) {
break;
}
//
// Verify the image's Authenticode signature, only DER-encoded PKCS#7 signed data is supported.
//
//
// Verify signature of executables.
//
if (WinCertificate->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
//
// The certificate is formatted as WIN_CERTIFICATE_EFI_PKCS which is described in the
// Authenticode specification.
//
PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) WinCertificate;
if (PkcsCertData->Hdr.dwLength <= sizeof (PkcsCertData->Hdr)) {
break;
}
AuthData = PkcsCertData->CertData;
AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr);
} else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID) {
//
// The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which is described in UEFI Spec.
//
WinCertUefiGuid = (WIN_CERTIFICATE_UEFI_GUID *) WinCertificate;
if (WinCertUefiGuid->Hdr.dwLength <= OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
break;
}
if (guidcmp (&WinCertUefiGuid->CertType, &gEfiCertTypePkcs7Guid)) {
continue;
}
AuthData = WinCertUefiGuid->CertData;
AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
} else {
if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
break;
}
continue;
}
//
// Verify Pkcs signed data type.
//
// At least 1 Cert from Pe Image should be in DB
// None of Certs should be found in DBX
//
// get Digest Algorithm, set mCertType
// lock mutex, preserve Pkcs7 context
Status = HashPeImageByType (AuthData, AuthDataSize);
if (EFI_ERROR (Status)) {
continue;
}
//
// Check the digital signature against the revoked certificate in forbidden database (dbx).
//
if (IsPkcsSignedDataVerifiedBySignatureList (AuthData, AuthDataSize, EFI_IMAGE_SECURITY_DATABASE1, Pkcs7CertGetMatchInCertChain)) {
Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
VerifyStatus = EFI_ACCESS_DENIED;
break;
}
//
// Check the digital signature against the valid certificate in allowed database (db).
//
if (EFI_ERROR (VerifyStatus)) {
if(IsPkcsSignedDataVerifiedBySignatureList (AuthData, AuthDataSize, EFI_IMAGE_SECURITY_DATABASE, Pkcs7CertValidate)) {
VerifyStatus = EFI_SUCCESS;
}
}
// clear context
Status = mDigitalSigProtocol->Pkcs7Verify (
mDigitalSigProtocol,
AuthData, AuthDataSize,
NULL, 0,
NULL, NULL,
Pkcs7Arg0, // Dummy op
RESET // Flags
);
} // end multi-sig for
// if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size + ALIGN_SIZE(SecDataDir->Size))) {
if(Action != EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND) {
if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
TRACE((TRACE_ALWAYS,"Offset outside of CertDir boundary %X != %x\n", OffSet, SecDataDir->VirtualAddress + SecDataDir->Size));
//
// The Size in Certificate Table or the attribute certificate table is corrupted.
//
VerifyStatus = EFI_ACCESS_DENIED;
} else
Action = EFI_IMAGE_EXECUTION_AUTH_SIG_NOT_FOUND;
}
} // end Signed cert branch
// Image not Signed or
// Image is Signed, its Cert verified in db but not found in DBX
if(mCertType != &gEfiCertSha256Guid &&
Action != EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND) {
//
// This image is not signed or signed but found only in db or failed previous checks.
// Expected:
// The SHA256 hash value of the image must match a record in the security database "db",
// and not be reflected in the security data base "dbx".
//
TRACE((TRACE_ALWAYS,"IMAGE not Signed\n\tor signed with non-Sha256, Try Sha256 Hash Cert...\n"));
if(!HashPeImage (HASHALG_SHA256)) // init Hash type and calculate PE hash if not done already
goto Done;
if(IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, &gEfiCertSha256Guid, mImageDigest, mImageDigestSize)) {
//
// Image Hash in forbidden database (DBX)
//
VerifyStatus = EFI_ACCESS_DENIED;
Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
} else
// no - try in allowed db if not there already
if(EFI_ERROR (VerifyStatus) &&
IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, &gEfiCertSha256Guid, mImageDigest, mImageDigestSize)) {
//
// Image Hash is in allowed database (DB).
//
VerifyStatus = EFI_SUCCESS;
}
}
//
// Signature database check after verification.
//
if(!EFI_ERROR (VerifyStatus) ) {
//
// Executable signature is found in authorized signature database.
//
Status = EFI_SUCCESS;
} else {
Status = EFI_ACCESS_DENIED;
if(Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND) {
//
// Executable signature is found in forbidden signature database.
//
TRACE((TRACE_ALWAYS,"Certificate IS FOUND in Forbidden database!\n"));
} else
//
// Verification failure.
//
if(Action == EFI_IMAGE_EXECUTION_AUTH_SIG_NOT_FOUND) {
//
// Executable signature cannot be found in authorized signature database.
// Use platform policy to determine the action.
//
TRACE((TRACE_ALWAYS,"Certificate NOT FOUND in authorized database!\n"));
}
else
TRACE((TRACE_ALWAYS,"Image Certificate is Invalid!\n"));
TRACE((TRACE_ALWAYS,"%r Record added to Image Execution Table\n", Status));
if(!mCertType || !mImageDigestSize ) {
goto Done;
}
SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + mImageDigestSize;
// Security review EIP[104046]:7. Unchecked AllocatePool may result in NULL pointer dereference
if (!EFI_ERROR(pBS->AllocatePool(EfiBootServicesData, SignatureListSize, &SignatureList)))
{
MemSet(SignatureList, SignatureListSize, 0);
SignatureList->SignatureHeaderSize = 0;
SignatureList->SignatureListSize = (UINT32) SignatureListSize;
SignatureList->SignatureSize = (UINT32) mImageDigestSize;
CopyMem (&SignatureList->SignatureType, mCertType, sizeof (EFI_GUID));
Signature = (EFI_SIGNATURE_DATA *) ((UINT8 *) SignatureList + sizeof (EFI_SIGNATURE_LIST));
CopyMem (Signature->SignatureData, mImageDigest, mImageDigestSize);
}
if (SignatureList != NULL) {
pBS->FreePool (SignatureList);
}
}
Done:
if (Status != EFI_SUCCESS) {
//
// Policy decides to defer or reject the image; add its information in image executable information table.
//
AddImageExeInfo (Action, NULL, File, SignatureList, SignatureListSize);
// Treat unsecured images according to Image Authorization policy
Status = ImageAuthorization (Policy, Action);
}
if(bFileRead)
pBS->FreePool (FileBuffer);
TRACE((TRACE_ALWAYS,"Image Verification ...%r\n", Status));
return Status;
}
//===================== Security Architectural Protocol ======================
#ifndef EFI_SECURITY2_ARCH_PROTOCOL_GUID
#define EFI_SECURITY2_ARCH_PROTOCOL_GUID \
{ 0x94ab2f58, 0x1438, 0x4ef1, 0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0xe, 0x68 }
typedef struct _EFI_SECURITY2_ARCH_PROTOCOL EFI_SECURITY2_ARCH_PROTOCOL;
typedef EFI_STATUS (EFIAPI *EFI_SECURITY2_FILE_AUTHENTICATION) (
IN CONST EFI_SECURITY2_ARCH_PROTOCOL *This,
IN CONST EFI_DEVICE_PATH_PROTOCOL *File,
IN VOID *FileBuffer,
IN UINTN FileSize,
IN BOOLEAN BootPolicy
);
struct _EFI_SECURITY2_ARCH_PROTOCOL {
EFI_SECURITY2_FILE_AUTHENTICATION FileAuthentication;
};
static EFI_GUID gEfiSecurity2ArchProtocolGuid = EFI_SECURITY2_ARCH_PROTOCOL_GUID;
#endif
EFI_STATUS SecureFileAuthentication2(
IN CONST EFI_SECURITY2_ARCH_PROTOCOL *This,
IN CONST EFI_DEVICE_PATH_PROTOCOL *File,
IN VOID *FileBuffer,
IN UINTN FileSize,
IN BOOLEAN BootPolicy
)
{
EFI_STATUS Status;
TRACE((TRACE_ALWAYS,"...............Call Security2 Arch proto.. File=%x, FileBuffer=%x, FileSize=%d\n",File,FileBuffer,FileSize ));
//
// skip verification if platform is NOT in SECURE BOOT MODE
//
if(mSecureBootEnable == 0)
return EFI_SUCCESS;
//
// Protocol should have been installed by AmiDigitalSig Driver once it's dispatched
//
if( mDigitalSigProtocol == NULL )
return EFI_ACCESS_DENIED;
Status = DxeImageVerificationHandler(File, FileBuffer, FileSize, BootPolicy);
// Install Tbl only on the Boot Loader launch
if (!EFI_ERROR (Status)/* && BootPolicy*/) {
AddBootImageCertInfo(NULL, mTrustSigDbOffs, mTrustSigDbSize);
mTrustSigDbOffs = 0;
mTrustSigDbSize = 0;
}
TRACE((TRACE_ALWAYS,"...............return status %r\n",Status));
return Status;
}
EFI_STATUS SecureFileAuthentication(
IN EFI_SECURITY_ARCH_PROTOCOL *This,
IN UINT32 AuthenticationStatus,
IN EFI_DEVICE_PATH_PROTOCOL *File
)
{
BOOLEAN BootPolicy;
VOID *FileBuffer;
UINTN FileSize;
BootPolicy = TRUE;
FileBuffer = NULL;
FileSize = 0;
TRACE((TRACE_ALWAYS,".........Call Security Arch proto.. File=%x\n",File));
if (File == NULL)
return EFI_INVALID_PARAMETER;
//Security Protocol w/a: BootPolicy == 1 if File->Type == 4 Media Dev Path
// if(File->Type == 4) // Media Dev Path
// BootPolicy=TRUE;
return SecureFileAuthentication2(NULL, File, FileBuffer, FileSize, BootPolicy);
}
EFI_SECURITY2_ARCH_PROTOCOL mSecurity2 = {SecureFileAuthentication2};
EFI_SECURITY_ARCH_PROTOCOL mSecurity = {SecureFileAuthentication};
//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------
// Procedure: InitDigitalProtocolCallback
//
// Description: This function initialize mDigitalSigProtocol ptr
//
//
// Input: IN EFI_EVENT Event - Event that was triggered
// IN VOID *Context - data pointer to information that is defined
// when the event is registered
//
// Output: EFI_STATUS
//
//----------------------------------------------------------------------------
//
static EFI_STATUS InitDigitalProtocolCallback(IN EFI_EVENT Event, IN VOID *Context)
{
EFI_STATUS Status;
UINTN DataSize;
UINT8 *pSecureBootEnable=NULL;
//
// Look up for image authorization policy in "SetupData" variable
//
#if (defined(ENABLE_IMAGE_EXEC_POLICY_OVERRIDE) && ENABLE_IMAGE_EXEC_POLICY_OVERRIDE == 1)
DataSize = sizeof(mSecureBootSetup);
Status = pRS->GetVariable (AMI_SECURE_BOOT_SETUP_VAR,&gSecureSetupGuid,NULL,&DataSize,&mSecureBootSetup);
if(EFI_ERROR(Status) ||
// Standard boot mode policy->apply defaults
mSecureBootSetup.SecureBootMode == STANDARD_SECURE_BOOT ||
// Policy check against fixed settings
mSecureBootSetup.Load_from_OROM < LOAD_FROM_OROM ||
mSecureBootSetup.Load_from_REMOVABLE_MEDIA < LOAD_FROM_REMOVABLE_MEDIA ||
mSecureBootSetup.Load_from_FIXED_MEDIA < LOAD_FROM_FIXED_MEDIA
)
#endif
{
mSecureBootSetup.Load_from_OROM = LOAD_FROM_OROM;
mSecureBootSetup.Load_from_REMOVABLE_MEDIA = LOAD_FROM_REMOVABLE_MEDIA;
mSecureBootSetup.Load_from_FIXED_MEDIA = LOAD_FROM_FIXED_MEDIA;
}
//
// SecureBoot variable to be installed along with NVRAM driver
//
pSecureBootEnable = GetEfiGlobalVariableEx (EFI_SECURE_BOOT_NAME, &DataSize);
if(pSecureBootEnable && DataSize==1)
mSecureBootEnable = *pSecureBootEnable;
//
// will skip verification if platform is NOT in SECURE BOOT MODE
//
if(mSecureBootEnable == 0)
return EFI_SUCCESS;
Status = pBS->LocateHandleBuffer (
ByProtocol,
#if (PI_SPECIFICATION_VERSION < 0x00010000)
&gEfiFirmwareVolumeProtocolGuid,
#else
&gEfiFirmwareVolume2ProtocolGuid,
#endif
NULL,
&mFvHandlesCount,
&mFvHandles
);
if(!EFI_ERROR(Status)){
gImageLoadingAfterBDS = TRUE;
}
return pBS->LocateProtocol( &gAmiDigitalSignatureProtocolGuid, NULL, &mDigitalSigProtocol );
}
extern BOOLEAN const InstallDummySecurityProtocol;
//
//----------------------------------------------------------------------------
// Procedure: InstallSecurityArchProtocol
//
// Description: This function installs the EFI_SECURITY_ARCH_PROTOCOL.
// It is called at DxeCoreInitialize.
//
// Input:
// EFI_HANDLE ImageHandle Image handle.
// EFI_SYSTEM_TABLE *SystemTable Pointer to the EFI system table.
//
// Output:
// EFI_SUCCESS : Security Architecture protocols are successfully installed.
//
//----------------------------------------------------------------------------
//
VOID InstallSecurityArchProtocol(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_HANDLE Handle = NULL;
EFI_EVENT Event;
VOID *p;
if (InstallDummySecurityProtocol)
return;
// Enable Security verification at beginning of BDS connect controller phase,
// We assume all drivers before the event were launched from internal FV
// !!! This bds event is only available since core 4.6.3.5.
RegisterProtocolCallback(
&BdsConnectDriversProtocolGuid,
InitDigitalProtocolCallback,
NULL,
&Event,
&p
);
// Security2 Arch protocol must be installed before Security Arch protocol
pBS->InstallMultipleProtocolInterfaces(
&Handle,
&gEfiSecurity2ArchProtocolGuid, &mSecurity2,
&gEfiSecurityArchProtocolGuid, &mSecurity,
NULL
);
}
#pragma warning (default : 4090)
//**********************************************************************
//**********************************************************************
//** **
//** (C)Copyright 1985-2011, American Megatrends, Inc. **
//** **
//** All Rights Reserved. **
//** **
//** 5555 Oakbrook Parkway, Suite 200, Norcross, GA 30093 **
//** **
//** Phone: (770)-246-8600 **
//** **
//**********************************************************************
//**********************************************************************
//**********************************************************************