X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=SecurityPkg%2FLibrary%2FDxeImageVerificationLib%2FDxeImageVerificationLib.c;h=a713d0d4ae1949e23622a3da29b8d6294c3083ff;hp=7bc3cc0ec037fb2665f4542cc9b75c38449f4802;hb=3f63bc365d685375ffcfa09a74ec79288fa572ef;hpb=beda2356f5128efa4461046f882b6516ece6afc7
diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
index 7bc3cc0ec0..a713d0d4ae 100644
--- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
+++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
@@ -1,27 +1,45 @@
/** @file
Implement image verification services for secure boot service in UEFI2.3.1.
-Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
-This program and the accompanying materials
-are licensed and made available under the terms and conditions of the BSD License
-which accompanies this distribution. The full text of the license may be found at
+ 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 - 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "DxeImageVerificationLib.h"
+//
+// 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;
+UINT32 mPeCoffHeaderOffset;
+EFI_GUID mCertType;
+
+//
+// Information on current PE/COFF image
+//
UINTN mImageSize;
-UINT32 mPeCoffHeaderOffset;
+UINT8 *mImageBase = NULL;
UINT8 mImageDigest[MAX_DIGEST_SIZE];
UINTN mImageDigestSize;
-EFI_IMAGE_DATA_DIRECTORY *mSecDataDir = NULL;
-UINT8 *mImageBase = NULL;
-EFI_GUID mCertType;
//
// Notify string for authorization UI.
@@ -38,7 +56,6 @@ CONST UINT8 mRsaE[] = { 0x01, 0x00, 0x01 };
// OID ASN.1 Value for Hash Algorithms
//
UINT8 mHashOidValue[] = {
- 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, // OBJ_md5
0x2B, 0x0E, 0x03, 0x02, 0x1A, // OBJ_sha1
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, // OBJ_sha224
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, // OBJ_sha256
@@ -47,21 +64,88 @@ UINT8 mHashOidValue[] = {
};
HASH_TABLE mHash[] = {
- { L"SHA1", 20, &mHashOidValue[8], 5, Sha1GetContextSize, Sha1Init, Sha1Update, Sha1Final },
- { L"SHA224", 28, &mHashOidValue[13], 9, NULL, NULL, NULL, NULL },
- { L"SHA256", 32, &mHashOidValue[22], 9, Sha256GetContextSize,Sha256Init, Sha256Update, Sha256Final},
- { L"SHA384", 48, &mHashOidValue[31], 9, NULL, NULL, NULL, NULL },
- { L"SHA512", 64, &mHashOidValue[40], 9, NULL, NULL, NULL, NULL }
+ { L"SHA1", 20, &mHashOidValue[0], 5, Sha1GetContextSize, Sha1Init, Sha1Update, Sha1Final },
+ { L"SHA224", 28, &mHashOidValue[5], 9, NULL, NULL, NULL, NULL },
+ { L"SHA256", 32, &mHashOidValue[14], 9, Sha256GetContextSize, Sha256Init, Sha256Update, Sha256Final},
+ { L"SHA384", 48, &mHashOidValue[23], 9, Sha384GetContextSize, Sha384Init, Sha384Update, Sha384Final},
+ { L"SHA512", 64, &mHashOidValue[32], 9, Sha512GetContextSize, Sha512Init, Sha512Update, Sha512Final}
};
+/**
+ SecureBoot Hook for processing image verification.
+
+ @param[in] VariableName Name of Variable to be found.
+ @param[in] VendorGuid Variable vendor GUID.
+ @param[in] DataSize Size of Data found. If size is less than the
+ data, this value contains the required size.
+ @param[in] Data Data pointer.
+
+**/
+VOID
+EFIAPI
+SecureBootHook (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINTN DataSize,
+ IN VOID *Data
+ );
+
+/**
+ 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
+ )
+{
+ UINTN EndPosition;
+
+ if (FileHandle == NULL || ReadSize == NULL || Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (MAX_ADDRESS - FileOffset < *ReadSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EndPosition = FileOffset + *ReadSize;
+ if (EndPosition > mImageSize) {
+ *ReadSize = (UINT32)(mImageSize - FileOffset);
+ }
+
+ if (FileOffset >= mImageSize) {
+ *ReadSize = 0;
+ }
+
+ 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.
+ being dispatched.
- @return UINT32 Image Type
+ @return UINT32 Image Type
**/
UINT32
@@ -70,15 +154,19 @@ GetImageType (
)
{
EFI_STATUS Status;
- EFI_HANDLE DeviceHandle;
+ EFI_HANDLE DeviceHandle;
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ if (File == NULL) {
+ return IMAGE_UNKNOWN;
+ }
+
//
// First check to see if File is from a Firmware Volume
//
DeviceHandle = NULL;
- TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File;
+ TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;
Status = gBS->LocateDevicePath (
&gEfiFirmwareVolume2ProtocolGuid,
&TempDevicePath,
@@ -102,7 +190,7 @@ GetImageType (
// Next check to see if File is from a Block I/O device
//
DeviceHandle = NULL;
- TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File;
+ TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;
Status = gBS->LocateDevicePath (
&gEfiBlockIoProtocolGuid,
&TempDevicePath,
@@ -136,11 +224,11 @@ GetImageType (
}
//
- // File is not in a Firmware Volume or on a Block I/O device, so check to see if
+ // 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;
+ TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;
Status = gBS->LocateDevicePath (
&gEfiSimpleFileSystemProtocolGuid,
&TempDevicePath,
@@ -155,12 +243,12 @@ GetImageType (
//
// 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.
+ // 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;
+ TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;
while (!IsDevicePathEndType (TempDevicePath)) {
switch (DevicePathType (TempDevicePath)) {
-
+
case MEDIA_DEVICE_PATH:
if (DevicePathSubType (TempDevicePath) == MEDIA_RELATIVE_OFFSET_RANGE_DP) {
return IMAGE_FROM_OPTION_ROM;
@@ -170,7 +258,7 @@ GetImageType (
case MESSAGING_DEVICE_PATH:
if (DevicePathSubType(TempDevicePath) == MSG_MAC_ADDR_DP) {
return IMAGE_FROM_REMOVABLE_MEDIA;
- }
+ }
break;
default:
@@ -178,20 +266,24 @@ GetImageType (
}
TempDevicePath = NextDevicePathNode (TempDevicePath);
}
- return IMAGE_UNKNOWN;
+ 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
+BOOLEAN
HashPeImage (
IN UINT32 HashAlg
)
@@ -207,34 +299,49 @@ HashPeImage (
EFI_IMAGE_SECTION_HEADER *SectionHeader;
UINTN Index;
UINTN Pos;
- UINTN SumOfSectionBytes;
- EFI_IMAGE_SECTION_HEADER *SectionCache;
-
+ UINT32 CertSize;
+ UINT32 NumberOfRvaAndSizes;
+
HashCtx = NULL;
SectionHeader = NULL;
Status = FALSE;
- if ((HashAlg != HASHALG_SHA1) && (HashAlg != HASHALG_SHA256)) {
+ if ((HashAlg >= HASHALG_MAX)) {
return FALSE;
}
-
+
//
// Initialize context of hash.
//
ZeroMem (mImageDigest, MAX_DIGEST_SIZE);
- if (HashAlg == HASHALG_SHA1) {
- mImageDigestSize = SHA1_DIGEST_SIZE;
- mCertType = gEfiCertSha1Guid;
- } else if (HashAlg == HASHALG_SHA256) {
- mImageDigestSize = SHA256_DIGEST_SIZE;
- mCertType = gEfiCertSha256Guid;
- } else {
+ switch (HashAlg) {
+ case HASHALG_SHA1:
+ mImageDigestSize = SHA1_DIGEST_SIZE;
+ mCertType = gEfiCertSha1Guid;
+ break;
+
+ case HASHALG_SHA256:
+ mImageDigestSize = SHA256_DIGEST_SIZE;
+ mCertType = gEfiCertSha256Guid;
+ break;
+
+ case HASHALG_SHA384:
+ mImageDigestSize = SHA384_DIGEST_SIZE;
+ mCertType = gEfiCertSha384Guid;
+ break;
+
+ case HASHALG_SHA512:
+ mImageDigestSize = SHA512_DIGEST_SIZE;
+ mCertType = gEfiCertSha512Guid;
+ break;
+
+ default:
return FALSE;
}
CtxSize = mHash[HashAlg].GetContextSize();
-
+
HashCtx = AllocatePool (CtxSize);
if (HashCtx == NULL) {
return FALSE;
@@ -244,15 +351,30 @@ HashPeImage (
// 2. Initialize a SHA hash context.
Status = mHash[HashAlg].HashInit(HashCtx);
-
+
if (!Status) {
goto Done;
}
+
//
// Measuring PE/COFF Image Header;
// But CheckSum field and SECURITY data directory (certificate) are excluded
//
- Magic = mNtHeader.Pe32->OptionalHeader.Magic;
+ if (mNtHeader.Pe32->FileHeader.Machine == IMAGE_FILE_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.
@@ -263,11 +385,13 @@ HashPeImage (
// 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.
@@ -280,51 +404,86 @@ HashPeImage (
if (!Status) {
goto Done;
}
+
//
// 5. Skip over the image checksum (it occupies a single ULONG).
- // 6. Get the address of the beginning of the Cert Directory.
- // 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.
+ if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
//
- HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);
- HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase);
- } else {
+ // 6. Since there is no Cert Directory in optional header, hash everything
+ // from the end of the checksum to the end of image header.
//
- // 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 (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);
+ }
- Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize);
- if (!Status) {
- 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) {
+ if (HashSize != 0) {
+ Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize);
+ if (!Status) {
+ goto Done;
+ }
+ }
+ } else {
//
- // Use PE32 offset
+ // 7. Hash everything from the end of the checksum to the start of the Cert Directory.
//
- HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];
- HashSize = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) ((UINT8 *) (&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - mImageBase);
- } else {
+ 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) {
+ Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize);
+ if (!Status) {
+ goto Done;
+ }
+ }
+
//
- // Use PE32+ offset.
+ // 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.
//
- HashBase = (UINT8 *) &mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];
- HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) ((UINT8 *) (&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - mImageBase);
- }
+ 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);
+ }
- Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize);
- if (!Status) {
- goto Done;
+ if (HashSize != 0) {
+ Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize);
+ if (!Status) {
+ goto Done;
+ }
+ }
}
+
//
// 10. Set the SUM_OF_BYTES_HASHED to the size of the header.
//
@@ -349,20 +508,6 @@ HashPeImage (
mNtHeader.Pe32->FileHeader.SizeOfOptionalHeader
);
- SectionCache = Section;
- for (Index = 0, SumOfSectionBytes = 0; Index < mNtHeader.Pe32->FileHeader.NumberOfSections; Index++, SectionCache++) {
- SumOfSectionBytes += SectionCache->SizeOfRawData;
- }
-
- //
- // Sanity check for file corruption. Sections raw data size should be smaller
- // than Image Size.
- //
- if (SumOfSectionBytes >= mImageSize) {
- Status = FALSE;
- goto Done;
- }
-
//
// 11. Build a temporary table of pointers to all the IMAGE_SECTION_HEADER
// structures in the image. The 'NumberOfSections' field of the image
@@ -421,29 +566,36 @@ HashPeImage (
//
if (mImageSize > SumOfBytesHashed) {
HashBase = mImageBase + SumOfBytesHashed;
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
- //
- // Use PE32 offset.
- //
- HashSize = (UINTN)(
- mImageSize -
- mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size -
- SumOfBytesHashed);
+
+ if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
+ CertSize = 0;
} else {
- //
- // Use PE32+ offset.
- //
- HashSize = (UINTN)(
- mImageSize -
- mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size -
- SumOfBytesHashed);
+ 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;
+ }
}
- Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize);
- if (!Status) {
+ if (mImageSize > CertSize + SumOfBytesHashed) {
+ HashSize = (UINTN) (mImageSize - CertSize - SumOfBytesHashed);
+
+ Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize);
+ if (!Status) {
+ goto Done;
+ }
+ } else if (mImageSize < CertSize + SumOfBytesHashed) {
+ Status = FALSE;
goto Done;
}
}
+
Status = mHash[HashAlg].HashFinal(HashCtx, mImageDigest);
Done:
@@ -457,28 +609,33 @@ Done:
}
/**
- 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
+ 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
+EFI_STATUS
HashPeImageByType (
- VOID
+ IN UINT8 *AuthData,
+ IN UINTN AuthDataSize
)
{
UINT8 Index;
- WIN_CERTIFICATE_EFI_PKCS *PkcsCertData;
-
- PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) (mImageBase + mSecDataDir->VirtualAddress);
- for (Index = 0; Index < HASHALG_MAX; Index++) {
+ for (Index = 0; Index < HASHALG_MAX; Index++) {
//
// Check the Hash algorithm in PE/COFF Authenticode.
- // According to PKCS#7 Definition:
+ // According to PKCS#7 Definition:
// SignedData ::= SEQUENCE {
// version Version,
// digestAlgorithms DigestAlgorithmIdentifiers,
@@ -486,8 +643,20 @@ HashPeImageByType (
// .... }
// The DigestAlgorithmIdentifiers can be used to determine the hash algorithm in PE/COFF hashing
// This field has the fixed offset (+32) in final Authenticode ASN.1 data.
- //
- if (CompareMem (PkcsCertData->CertData + 32, mHash[Index].OidValue, mHash[Index].OidLength) == 0) {
+ // Fixed offset (+32) is calculated based on two bytes of length encoding.
+ //
+ if ((*(AuthData + 1) & TWO_BYTE_ENCODE) != TWO_BYTE_ENCODE) {
+ //
+ // Only support two bytes of Long Form of Length Encoding.
+ //
+ continue;
+ }
+
+ if (AuthDataSize < 32 + mHash[Index].OidLength) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (CompareMem (AuthData + 32, mHash[Index].OidValue, mHash[Index].OidLength) == 0) {
break;
}
}
@@ -514,7 +683,7 @@ HashPeImageByType (
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.
@@ -550,12 +719,12 @@ GetImageExeInfoTableSize (
@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 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
@@ -577,17 +746,18 @@ AddImageExeInfo (
if (DevicePath == NULL) {
return ;
}
-
+
if (Name != NULL) {
NameStringLen = StrSize (Name);
+ } else {
+ NameStringLen = sizeof (CHAR16);
}
- ImageExeInfoTable = NULL;
- EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID**)&ImageExeInfoTable);
+ EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID **) &ImageExeInfoTable);
if (ImageExeInfoTable != NULL) {
//
// The table has been found!
- // We must enlarge the table to accmodate the new exe info entry.
+ // We must enlarge the table to accomodate the new exe info entry.
//
ImageExeInfoTableSize = GetImageExeInfoTableSize (ImageExeInfoTable);
} else {
@@ -599,7 +769,7 @@ AddImageExeInfo (
}
DevicePathSize = GetDevicePathSize (DevicePath);
- NewImageExeInfoEntrySize = sizeof (EFI_IMAGE_EXECUTION_INFO) + NameStringLen + DevicePathSize + SignatureSize;
+ NewImageExeInfoEntrySize = sizeof (EFI_IMAGE_EXECUTION_INFO) - sizeof (EFI_SIGNATURE_LIST) + NameStringLen + DevicePathSize + SignatureSize;
NewImageExeInfoTable = (EFI_IMAGE_EXECUTION_INFO_TABLE *) AllocateRuntimePool (ImageExeInfoTableSize + NewImageExeInfoEntrySize);
if (NewImageExeInfoTable == NULL) {
return ;
@@ -613,22 +783,24 @@ AddImageExeInfo (
NewImageExeInfoTable->NumberOfImages++;
ImageExeInfoEntry = (EFI_IMAGE_EXECUTION_INFO *) ((UINT8 *) NewImageExeInfoTable + ImageExeInfoTableSize);
//
- // Update new item's infomation.
+ // Update new item's information.
//
- WriteUnaligned32 ((UINT32 *) &ImageExeInfoEntry->Action, Action);
- WriteUnaligned32 ((UINT32 *) &ImageExeInfoEntry->InfoSize, (UINT32) NewImageExeInfoEntrySize);
+ WriteUnaligned32 ((UINT32 *) ImageExeInfoEntry, Action);
+ WriteUnaligned32 ((UINT32 *) ((UINT8 *) ImageExeInfoEntry + sizeof (EFI_IMAGE_EXECUTION_ACTION)), (UINT32) NewImageExeInfoEntrySize);
if (Name != NULL) {
- CopyMem ((UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32), Name, NameStringLen);
+ CopyMem ((UINT8 *) ImageExeInfoEntry + sizeof (EFI_IMAGE_EXECUTION_ACTION) + sizeof (UINT32), Name, NameStringLen);
+ } else {
+ ZeroMem ((UINT8 *) ImageExeInfoEntry + sizeof (EFI_IMAGE_EXECUTION_ACTION) + sizeof (UINT32), sizeof (CHAR16));
}
CopyMem (
- (UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32) + NameStringLen,
+ (UINT8 *) ImageExeInfoEntry + sizeof (EFI_IMAGE_EXECUTION_ACTION) + sizeof (UINT32) + NameStringLen,
DevicePath,
DevicePathSize
);
if (Signature != NULL) {
CopyMem (
- (UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32) + NameStringLen + DevicePathSize,
+ (UINT8 *) ImageExeInfoEntry + sizeof (EFI_IMAGE_EXECUTION_ACTION) + sizeof (UINT32) + NameStringLen + DevicePathSize,
Signature,
SignatureSize
);
@@ -637,7 +809,7 @@ AddImageExeInfo (
// Update/replace the image execution table.
//
gBS->InstallConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID *) NewImageExeInfoTable);
-
+
//
// Free Old table data!
//
@@ -647,57 +819,132 @@ AddImageExeInfo (
}
/**
- Discover if the UEFI image is authorized by user's policy setting.
+ Check whether the hash of an given X.509 certificate is in forbidden database (DBX).
- @param[in] Policy Specify platform's policy setting.
+ @param[in] Certificate Pointer to X.509 Certificate that is searched for.
+ @param[in] CertSize Size of X.509 Certificate.
+ @param[in] SignatureList Pointer to the Signature List in forbidden database.
+ @param[in] SignatureListSize Size of Signature List.
+ @param[out] RevocationTime Return the time that the certificate was revoked.
- @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.
+ @return TRUE The certificate hash is found in the forbidden database.
+ @return FALSE The certificate hash is not found in the forbidden database.
**/
-EFI_STATUS
-ImageAuthorization (
- IN UINT32 Policy
+BOOLEAN
+IsCertHashFoundInDatabase (
+ IN UINT8 *Certificate,
+ IN UINTN CertSize,
+ IN EFI_SIGNATURE_LIST *SignatureList,
+ IN UINTN SignatureListSize,
+ OUT EFI_TIME *RevocationTime
)
{
- EFI_STATUS Status;
- EFI_INPUT_KEY Key;
+ BOOLEAN IsFound;
+ BOOLEAN Status;
+ EFI_SIGNATURE_LIST *DbxList;
+ UINTN DbxSize;
+ EFI_SIGNATURE_DATA *CertHash;
+ UINTN CertHashCount;
+ UINTN Index;
+ UINT32 HashAlg;
+ VOID *HashCtx;
+ UINT8 CertDigest[MAX_DIGEST_SIZE];
+ UINT8 *DbxCertHash;
+ UINTN SiglistHeaderSize;
+ UINT8 *TBSCert;
+ UINTN TBSCertSize;
+
+ IsFound = FALSE;
+ DbxList = SignatureList;
+ DbxSize = SignatureListSize;
+ HashCtx = NULL;
+ HashAlg = HASHALG_MAX;
+
+ if ((RevocationTime == NULL) || (DbxList == NULL)) {
+ return FALSE;
+ }
- Status = EFI_ACCESS_DENIED;
+ //
+ // Retrieve the TBSCertificate from the X.509 Certificate.
+ //
+ if (!X509GetTBSCert (Certificate, CertSize, &TBSCert, &TBSCertSize)) {
+ return FALSE;
+ }
- switch (Policy) {
-
- case QUERY_USER_ON_SECURITY_VIOLATION:
- do {
- CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, mNotifyString1, mNotifyString2, NULL);
- if (Key.UnicodeChar == L'Y' || Key.UnicodeChar == L'y') {
- Status = EFI_SUCCESS;
- break;
- } else if (Key.UnicodeChar == L'N' || Key.UnicodeChar == L'n') {
- Status = EFI_ACCESS_DENIED;
- break;
- } else if (Key.UnicodeChar == L'D' || Key.UnicodeChar == L'd') {
- Status = EFI_SECURITY_VIOLATION;
- break;
- }
- } while (TRUE);
- break;
+ while ((DbxSize > 0) && (SignatureListSize >= DbxList->SignatureListSize)) {
+ //
+ // Determine Hash Algorithm of Certificate in the forbidden database.
+ //
+ if (CompareGuid (&DbxList->SignatureType, &gEfiCertX509Sha256Guid)) {
+ HashAlg = HASHALG_SHA256;
+ } else if (CompareGuid (&DbxList->SignatureType, &gEfiCertX509Sha384Guid)) {
+ HashAlg = HASHALG_SHA384;
+ } else if (CompareGuid (&DbxList->SignatureType, &gEfiCertX509Sha512Guid)) {
+ HashAlg = HASHALG_SHA512;
+ } else {
+ DbxSize -= DbxList->SignatureListSize;
+ DbxList = (EFI_SIGNATURE_LIST *) ((UINT8 *) DbxList + DbxList->SignatureListSize);
+ continue;
+ }
- case ALLOW_EXECUTE_ON_SECURITY_VIOLATION:
- Status = EFI_SUCCESS;
- break;
+ //
+ // Calculate the hash value of current TBSCertificate for comparision.
+ //
+ if (mHash[HashAlg].GetContextSize == NULL) {
+ goto Done;
+ }
+ ZeroMem (CertDigest, MAX_DIGEST_SIZE);
+ HashCtx = AllocatePool (mHash[HashAlg].GetContextSize ());
+ if (HashCtx == NULL) {
+ goto Done;
+ }
+ Status = mHash[HashAlg].HashInit (HashCtx);
+ if (!Status) {
+ goto Done;
+ }
+ Status = mHash[HashAlg].HashUpdate (HashCtx, TBSCert, TBSCertSize);
+ if (!Status) {
+ goto Done;
+ }
+ Status = mHash[HashAlg].HashFinal (HashCtx, CertDigest);
+ if (!Status) {
+ goto Done;
+ }
- case DEFER_EXECUTE_ON_SECURITY_VIOLATION:
- Status = EFI_SECURITY_VIOLATION;
- break;
+ SiglistHeaderSize = sizeof (EFI_SIGNATURE_LIST) + DbxList->SignatureHeaderSize;
+ CertHash = (EFI_SIGNATURE_DATA *) ((UINT8 *) DbxList + SiglistHeaderSize);
+ CertHashCount = (DbxList->SignatureListSize - SiglistHeaderSize) / DbxList->SignatureSize;
+ for (Index = 0; Index < CertHashCount; Index++) {
+ //
+ // Iterate each Signature Data Node within this CertList for verify.
+ //
+ DbxCertHash = CertHash->SignatureData;
+ if (CompareMem (DbxCertHash, CertDigest, mHash[HashAlg].DigestLength) == 0) {
+ //
+ // Hash of Certificate is found in forbidden database.
+ //
+ IsFound = TRUE;
+
+ //
+ // Return the revocation time.
+ //
+ CopyMem (RevocationTime, (EFI_TIME *)(DbxCertHash + mHash[HashAlg].DigestLength), sizeof (EFI_TIME));
+ goto Done;
+ }
+ CertHash = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertHash + DbxList->SignatureSize);
+ }
- case DENY_EXECUTE_ON_SECURITY_VIOLATION:
- Status = EFI_ACCESS_DENIED;
- break;
+ DbxSize -= DbxList->SignatureListSize;
+ DbxList = (EFI_SIGNATURE_LIST *) ((UINT8 *) DbxList + DbxList->SignatureListSize);
}
- return Status;
+Done:
+ if (HashCtx != NULL) {
+ FreePool (HashCtx);
+ }
+
+ return IsFound;
}
/**
@@ -715,7 +962,7 @@ ImageAuthorization (
BOOLEAN
IsSignatureFoundInDatabase (
IN CHAR16 *VariableName,
- IN UINT8 *Signature,
+ IN UINT8 *Signature,
IN EFI_GUID *CertType,
IN UINTN SignatureSize
)
@@ -728,6 +975,7 @@ IsSignatureFoundInDatabase (
UINTN Index;
UINTN CertCount;
BOOLEAN IsFound;
+
//
// Read signature database variable.
//
@@ -753,7 +1001,7 @@ IsSignatureFoundInDatabase (
//
CertList = (EFI_SIGNATURE_LIST *) Data;
while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) {
- CertCount = (CertList->SignatureListSize - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+ 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) && (CompareGuid(&CertList->SignatureType, CertType))) {
for (Index = 0; Index < CertCount; Index++) {
@@ -762,6 +1010,7 @@ IsSignatureFoundInDatabase (
// Find the signature in database.
//
IsFound = TRUE;
+ SecureBootHook (VariableName, &gEfiImageSecurityDatabaseGuid, CertList->SignatureSize, Cert);
break;
}
@@ -786,327 +1035,508 @@ Done:
}
/**
- Verify certificate in WIN_CERT_TYPE_PKCS_SIGNED_DATA format .
+ Check whether the timestamp is valid by comparing the signing time and the revocation time.
+
+ @param SigningTime A pointer to the signing time.
+ @param RevocationTime A pointer to the revocation time.
+
+ @retval TRUE The SigningTime is not later than the RevocationTime.
+ @retval FALSE The SigningTime is later than the RevocationTime.
+
+**/
+BOOLEAN
+IsValidSignatureByTimestamp (
+ IN EFI_TIME *SigningTime,
+ IN EFI_TIME *RevocationTime
+ )
+{
+ if (SigningTime->Year != RevocationTime->Year) {
+ return (BOOLEAN) (SigningTime->Year < RevocationTime->Year);
+ } else if (SigningTime->Month != RevocationTime->Month) {
+ return (BOOLEAN) (SigningTime->Month < RevocationTime->Month);
+ } else if (SigningTime->Day != RevocationTime->Day) {
+ return (BOOLEAN) (SigningTime->Day < RevocationTime->Day);
+ } else if (SigningTime->Hour != RevocationTime->Hour) {
+ return (BOOLEAN) (SigningTime->Hour < RevocationTime->Hour);
+ } else if (SigningTime->Minute != RevocationTime->Minute) {
+ return (BOOLEAN) (SigningTime->Minute < RevocationTime->Minute);
+ }
+
+ return (BOOLEAN) (SigningTime->Second <= RevocationTime->Second);
+}
+
+/**
+ Check if the given time value is zero.
+
+ @param[in] Time Pointer of a time value.
+
+ @retval TRUE The Time is Zero.
+ @retval FALSE The Time is not Zero.
+
+**/
+BOOLEAN
+IsTimeZero (
+ IN EFI_TIME *Time
+ )
+{
+ if ((Time->Year == 0) && (Time->Month == 0) && (Time->Day == 0) &&
+ (Time->Hour == 0) && (Time->Minute == 0) && (Time->Second == 0)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Check whether the timestamp signature is valid and the signing time is also earlier than
+ the revocation time.
+
+ @param[in] AuthData Pointer to the Authenticode signature retrieved from signed image.
+ @param[in] AuthDataSize Size of the Authenticode signature in bytes.
+ @param[in] RevocationTime The time that the certificate was revoked.
- @retval EFI_SUCCESS Image pass verification.
- @retval EFI_SECURITY_VIOLATION Image fail verification.
- @retval EFI_OUT_OF_RESOURCE Fail to allocate memory.
+ @retval TRUE Timestamp signature is valid and signing time is no later than the
+ revocation time.
+ @retval FALSE Timestamp signature is not valid or the signing time is later than the
+ revocation time.
**/
-EFI_STATUS
-VerifyCertPkcsSignedData (
- VOID
+BOOLEAN
+PassTimestampCheck (
+ IN UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ IN EFI_TIME *RevocationTime
)
{
EFI_STATUS Status;
BOOLEAN VerifyStatus;
- WIN_CERTIFICATE_EFI_PKCS *PkcsCertData;
EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *Cert;
- UINTN DataSize;
- UINT8 *KekData;
- UINT8 *DbData;
+ UINT8 *DbtData;
+ UINTN DbtDataSize;
UINT8 *RootCert;
UINTN RootCertSize;
UINTN Index;
UINTN CertCount;
+ EFI_TIME SigningTime;
- KekData = NULL;
- DbData = NULL;
- CertList = NULL;
- Cert = NULL;
- RootCert = NULL;
- RootCertSize = 0;
- VerifyStatus = FALSE;
- PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) (mImageBase + mSecDataDir->VirtualAddress);
+ //
+ // Variable Initialization
+ //
+ VerifyStatus = FALSE;
+ DbtData = NULL;
+ CertList = NULL;
+ Cert = NULL;
+ RootCert = NULL;
+ RootCertSize = 0;
//
- // 1: Find certificate from KEK database and try to verify authenticode struct.
+ // If RevocationTime is zero, the certificate shall be considered to always be revoked.
//
- DataSize = 0;
- Status = gRT->GetVariable (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid, NULL, &DataSize, NULL);
- if (Status == EFI_BUFFER_TOO_SMALL) {
- KekData = (UINT8 *)AllocateZeroPool (DataSize);
- if (KekData == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
+ if (IsTimeZero (RevocationTime)) {
+ return FALSE;
+ }
- Status = gRT->GetVariable (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid, NULL, &DataSize, (VOID *)KekData);
- if (EFI_ERROR (Status)) {
- goto Done;
- }
-
- //
- // Find Cert Enrolled in KEK database to verify the signature in pkcs7 signed data.
- //
- CertList = (EFI_SIGNATURE_LIST *) KekData;
- while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) {
- if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
- Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
- CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
- for (Index = 0; Index < CertCount; Index++) {
- //
- // Iterate each Signature Data Node within this CertList for a verify
- //
- RootCert = Cert->SignatureData;
- RootCertSize = CertList->SignatureSize;
-
+ //
+ // RevocationTime is non-zero, the certificate should be considered to be revoked from that time and onwards.
+ // Using the dbt to get the trusted TSA certificates.
+ //
+ DbtDataSize = 0;
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE2, &gEfiImageSecurityDatabaseGuid, NULL, &DbtDataSize, NULL);
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ goto Done;
+ }
+ DbtData = (UINT8 *) AllocateZeroPool (DbtDataSize);
+ if (DbtData == NULL) {
+ goto Done;
+ }
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE2, &gEfiImageSecurityDatabaseGuid, NULL, &DbtDataSize, (VOID *) DbtData);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ CertList = (EFI_SIGNATURE_LIST *) DbtData;
+ while ((DbtDataSize > 0) && (DbtDataSize >= CertList->SignatureListSize)) {
+ if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+ for (Index = 0; Index < CertCount; Index++) {
+ //
+ // Iterate each Signature Data Node within this CertList for verify.
+ //
+ RootCert = Cert->SignatureData;
+ RootCertSize = CertList->SignatureSize - sizeof (EFI_GUID);
+ //
+ // Get the signing time if the timestamp signature is valid.
+ //
+ if (ImageTimestampVerify (AuthData, AuthDataSize, RootCert, RootCertSize, &SigningTime)) {
//
- // Call AuthenticodeVerify library to Verify Authenticode struct.
+ // The signer signature is valid only when the signing time is earlier than revocation time.
//
- VerifyStatus = AuthenticodeVerify (
- PkcsCertData->CertData,
- mSecDataDir->Size - sizeof(PkcsCertData->Hdr),
- RootCert,
- RootCertSize,
- mImageDigest,
- mImageDigestSize
- );
-
- if (VerifyStatus) {
+ if (IsValidSignatureByTimestamp (&SigningTime, RevocationTime)) {
+ VerifyStatus = TRUE;
goto Done;
}
- Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
- }
+ }
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
+ }
+ }
+ DbtDataSize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
+ }
+
+Done:
+ if (DbtData != NULL) {
+ FreePool (DbtData);
+ }
+
+ return VerifyStatus;
+}
+
+/**
+ Check whether the image signature is forbidden by the forbidden database (dbx).
+ The image is forbidden to load if any certificates for signing are revoked before signing time.
+
+ @param[in] AuthData Pointer to the Authenticode signature retrieved from the signed image.
+ @param[in] AuthDataSize Size of the Authenticode signature in bytes.
+
+ @retval TRUE Image is forbidden by dbx.
+ @retval FALSE Image is not forbidden by dbx.
+
+**/
+BOOLEAN
+IsForbiddenByDbx (
+ IN UINT8 *AuthData,
+ IN UINTN AuthDataSize
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN IsForbidden;
+ UINT8 *Data;
+ UINTN DataSize;
+ EFI_SIGNATURE_LIST *CertList;
+ UINTN CertListSize;
+ EFI_SIGNATURE_DATA *CertData;
+ UINT8 *RootCert;
+ UINTN RootCertSize;
+ UINTN CertCount;
+ UINTN Index;
+ UINT8 *CertBuffer;
+ UINTN BufferLength;
+ UINT8 *TrustedCert;
+ UINTN TrustedCertLength;
+ UINT8 CertNumber;
+ UINT8 *CertPtr;
+ UINT8 *Cert;
+ UINTN CertSize;
+ EFI_TIME RevocationTime;
+
+ //
+ // Variable Initialization
+ //
+ IsForbidden = FALSE;
+ Data = NULL;
+ CertList = NULL;
+ CertData = NULL;
+ RootCert = NULL;
+ RootCertSize = 0;
+ Cert = NULL;
+ CertBuffer = NULL;
+ BufferLength = 0;
+ TrustedCert = NULL;
+ TrustedCertLength = 0;
+
+ //
+ // The image will not be forbidden if dbx can't be got.
+ //
+ DataSize = 0;
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, NULL);
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return IsForbidden;
+ }
+ Data = (UINT8 *) AllocateZeroPool (DataSize);
+ if (Data == NULL) {
+ return IsForbidden;
+ }
+
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, (VOID *) Data);
+ if (EFI_ERROR (Status)) {
+ return IsForbidden;
+ }
+
+ //
+ // Verify image signature with RAW X509 certificates in DBX database.
+ // If passed, the image will be forbidden.
+ //
+ CertList = (EFI_SIGNATURE_LIST *) Data;
+ CertListSize = DataSize;
+ while ((CertListSize > 0) && (CertListSize >= CertList->SignatureListSize)) {
+ if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
+ CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+
+ for (Index = 0; Index < CertCount; Index++) {
+ //
+ // Iterate each Signature Data Node within this CertList for verify.
+ //
+ RootCert = CertData->SignatureData;
+ RootCertSize = CertList->SignatureSize - sizeof (EFI_GUID);
+
+ //
+ // Call AuthenticodeVerify library to Verify Authenticode struct.
+ //
+ IsForbidden = AuthenticodeVerify (
+ AuthData,
+ AuthDataSize,
+ RootCert,
+ RootCertSize,
+ mImageDigest,
+ mImageDigestSize
+ );
+ if (IsForbidden) {
+ SecureBootHook (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, CertList->SignatureSize, Cert);
+ goto Done;
+ }
+
+ CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertData + CertList->SignatureSize);
}
- DataSize -= CertList->SignatureListSize;
- CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
}
+
+ CertListSize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
}
-
+ //
+ // Check X.509 Certificate Hash & Possible Timestamp.
+ //
+
+ //
+ // Retrieve the certificate stack from AuthData
+ // The output CertStack format will be:
+ // UINT8 CertNumber;
+ // UINT32 Cert1Length;
+ // UINT8 Cert1[];
+ // UINT32 Cert2Length;
+ // UINT8 Cert2[];
+ // ...
+ // UINT32 CertnLength;
+ // UINT8 Certn[];
+ //
+ Pkcs7GetSigners (AuthData, AuthDataSize, &CertBuffer, &BufferLength, &TrustedCert, &TrustedCertLength);
+ if ((BufferLength == 0) || (CertBuffer == NULL)) {
+ IsForbidden = TRUE;
+ goto Done;
+ }
//
- // 2: Find certificate from DB database and try to verify authenticode struct.
+ // Check if any hash of certificates embedded in AuthData is in the forbidden database.
//
+ CertNumber = (UINT8) (*CertBuffer);
+ CertPtr = CertBuffer + 1;
+ for (Index = 0; Index < CertNumber; Index++) {
+ CertSize = (UINTN) ReadUnaligned32 ((UINT32 *)CertPtr);
+ Cert = (UINT8 *)CertPtr + sizeof (UINT32);
+
+ if (IsCertHashFoundInDatabase (Cert, CertSize, (EFI_SIGNATURE_LIST *)Data, DataSize, &RevocationTime)) {
+ //
+ // Check the timestamp signature and signing time to determine if the image can be trusted.
+ //
+ IsForbidden = TRUE;
+ if (PassTimestampCheck (AuthData, AuthDataSize, &RevocationTime)) {
+ IsForbidden = FALSE;
+ }
+ goto Done;
+ }
+
+ CertPtr = CertPtr + sizeof (UINT32) + CertSize;
+ }
+
+Done:
+ if (Data != NULL) {
+ FreePool (Data);
+ }
+
+ Pkcs7FreeSigners (CertBuffer);
+ Pkcs7FreeSigners (TrustedCert);
+
+ return IsForbidden;
+}
+
+/**
+ Check whether the image signature can be verified by the trusted certificates in DB database.
+
+ @param[in] AuthData Pointer to the Authenticode signature retrieved from signed image.
+ @param[in] AuthDataSize Size of the Authenticode signature in bytes.
+
+ @retval TRUE Image passed verification using certificate in db.
+ @retval FALSE Image didn't pass verification using certificate in db.
+
+**/
+BOOLEAN
+IsAllowedByDb (
+ IN UINT8 *AuthData,
+ IN UINTN AuthDataSize
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN VerifyStatus;
+ EFI_SIGNATURE_LIST *CertList;
+ EFI_SIGNATURE_DATA *Cert;
+ UINTN DataSize;
+ UINT8 *Data;
+ UINT8 *RootCert;
+ UINTN RootCertSize;
+ UINTN Index;
+ UINTN CertCount;
+ UINTN DbxDataSize;
+ UINT8 *DbxData;
+ EFI_TIME RevocationTime;
+
+ Data = NULL;
+ CertList = NULL;
+ Cert = NULL;
+ RootCert = NULL;
+ DbxData = NULL;
+ RootCertSize = 0;
+ VerifyStatus = FALSE;
+
DataSize = 0;
Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, NULL);
if (Status == EFI_BUFFER_TOO_SMALL) {
- DbData = (UINT8 *)AllocateZeroPool (DataSize);
- if (DbData == NULL) {
- goto Done;
+ Data = (UINT8 *) AllocateZeroPool (DataSize);
+ if (Data == NULL) {
+ return VerifyStatus;
}
- Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, (VOID *)DbData);
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, (VOID *) Data);
if (EFI_ERROR (Status)) {
goto Done;
}
//
- // Find Cert Enrolled in DB database to verify the signature in pkcs7 signed data.
- //
- CertList = (EFI_SIGNATURE_LIST *) DbData;
+ // Find X509 certificate in Signature List to verify the signature in pkcs7 signed data.
+ //
+ CertList = (EFI_SIGNATURE_LIST *) Data;
while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) {
if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
- Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
- CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+
for (Index = 0; Index < CertCount; Index++) {
//
- // Iterate each Signature Data Node within this CertList for a verify
- //
- RootCert = Cert->SignatureData;
- RootCertSize = CertList->SignatureSize;
-
+ // Iterate each Signature Data Node within this CertList for verify.
//
- // Call AuthenticodeVerify library to Verify Authenticode struct.
+ RootCert = Cert->SignatureData;
+ RootCertSize = CertList->SignatureSize - sizeof (EFI_GUID);
+
+ //
+ // Call AuthenticodeVerify library to Verify Authenticode struct.
//
VerifyStatus = AuthenticodeVerify (
- PkcsCertData->CertData,
- mSecDataDir->Size - sizeof(PkcsCertData->Hdr),
+ AuthData,
+ AuthDataSize,
RootCert,
RootCertSize,
mImageDigest,
mImageDigestSize
);
-
if (VerifyStatus) {
+ //
+ // Here We still need to check if this RootCert's Hash is revoked
+ //
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DbxDataSize, NULL);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ goto Done;
+ }
+ DbxData = (UINT8 *) AllocateZeroPool (DataSize);
+ if (DbxData == NULL) {
+ goto Done;
+ }
+
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DbxDataSize, (VOID *) DbxData);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ if (IsCertHashFoundInDatabase (RootCert, RootCertSize, (EFI_SIGNATURE_LIST *)DbxData, DbxDataSize, &RevocationTime)) {
+ //
+ // Check the timestamp signature and signing time to determine if the image can be trusted.
+ //
+ VerifyStatus = PassTimestampCheck (AuthData, AuthDataSize, &RevocationTime);
+ }
+
goto Done;
}
+
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
- }
+ }
}
+
DataSize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
}
}
Done:
- if (KekData != NULL) {
- FreePool (KekData);
- }
-
- if (DbData != NULL) {
- FreePool (DbData);
- }
-
if (VerifyStatus) {
- return EFI_SUCCESS;
- } else {
- return EFI_SECURITY_VIOLATION;
+ SecureBootHook (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, CertList->SignatureSize, Cert);
}
-}
-/**
- Verify certificate in WIN_CERTIFICATE_UEFI_GUID format.
-
- @retval EFI_SUCCESS Image pass verification.
- @retval EFI_SECURITY_VIOLATION Image fail verification.
- @retval other error value
-
-**/
-EFI_STATUS
-VerifyCertUefiGuid (
- VOID
- )
-{
- BOOLEAN Status;
- WIN_CERTIFICATE_UEFI_GUID *EfiCert;
- EFI_SIGNATURE_LIST *KekList;
- EFI_SIGNATURE_DATA *KekItem;
- EFI_CERT_BLOCK_RSA_2048_SHA256 *CertBlock;
- VOID *Rsa;
- UINTN KekCount;
- UINTN Index;
- UINTN KekDataSize;
- BOOLEAN IsFound;
- EFI_STATUS Result;
-
- EfiCert = NULL;
- KekList = NULL;
- KekItem = NULL;
- CertBlock = NULL;
- Rsa = NULL;
- Status = FALSE;
- IsFound = FALSE;
- KekDataSize = 0;
-
- EfiCert = (WIN_CERTIFICATE_UEFI_GUID *) (mImageBase + mSecDataDir->VirtualAddress);
- CertBlock = (EFI_CERT_BLOCK_RSA_2048_SHA256 *) EfiCert->CertData;
- if (!CompareGuid (&EfiCert->CertType, &gEfiCertTypeRsa2048Sha256Guid)) {
- //
- // Invalid Certificate Data Type.
- //
- return EFI_SECURITY_VIOLATION;
+ if (Data != NULL) {
+ FreePool (Data);
}
-
- //
- // Get KEK database variable data size
- //
- Result = gRT->GetVariable (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid, NULL, &KekDataSize, NULL);
- if (Result != EFI_BUFFER_TOO_SMALL) {
- return EFI_SECURITY_VIOLATION;
+ if (DbxData != NULL) {
+ FreePool (DbxData);
}
- //
- // Get KEK database variable.
- //
- KekList = GetEfiGlobalVariable (EFI_KEY_EXCHANGE_KEY_NAME);
- if (KekList == NULL) {
- return EFI_SECURITY_VIOLATION;
- }
-
- //
- // Enumerate all Kek items in this list to verify the variable certificate data.
- // If anyone is authenticated successfully, it means the variable is correct!
- //
- while ((KekDataSize > 0) && (KekDataSize >= KekList->SignatureListSize)) {
- if (CompareGuid (&KekList->SignatureType, &gEfiCertRsa2048Guid)) {
- KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekList + sizeof (EFI_SIGNATURE_LIST) + KekList->SignatureHeaderSize);
- KekCount = (KekList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - KekList->SignatureHeaderSize) / KekList->SignatureSize;
- for (Index = 0; Index < KekCount; Index++) {
- if (CompareMem (KekItem->SignatureData, CertBlock->PublicKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) {
- IsFound = TRUE;
- break;
- }
- KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekItem + KekList->SignatureSize);
- }
- }
- KekDataSize -= KekList->SignatureListSize;
- KekList = (EFI_SIGNATURE_LIST *) ((UINT8 *) KekList + KekList->SignatureListSize);
- }
-
- if (!IsFound) {
- //
- // Signed key is not a trust one.
- //
- goto Done;
- }
-
- //
- // Now, we found the corresponding security policy.
- // Verify the data payload.
- //
- Rsa = RsaNew ();
- if (Rsa == NULL) {
- Status = FALSE;
- goto Done;
- }
-
- //
- // Set RSA Key Components.
- // NOTE: Only N and E are needed to be set as RSA public key for signature verification.
- //
- Status = RsaSetKey (Rsa, RsaKeyN, CertBlock->PublicKey, EFI_CERT_TYPE_RSA2048_SIZE);
- if (!Status) {
- goto Done;
- }
- Status = RsaSetKey (Rsa, RsaKeyE, mRsaE, sizeof (mRsaE));
- if (!Status) {
- goto Done;
- }
- //
- // Verify the signature.
- //
- Status = RsaPkcs1Verify (
- Rsa,
- mImageDigest,
- mImageDigestSize,
- CertBlock->Signature,
- EFI_CERT_TYPE_RSA2048_SHA256_SIZE
- );
-
-Done:
- if (KekList != NULL) {
- FreePool (KekList);
- }
- if (Rsa != NULL ) {
- RsaFree (Rsa);
- }
- if (Status) {
- return EFI_SUCCESS;
- } else {
- return EFI_SECURITY_VIOLATION;
- }
+ return VerifyStatus;
}
/**
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
+ 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.
- @param[in] AuthenticationStatus
+ 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.
+
+ @param[in] AuthenticationStatus
This is the authentication status returned from the security
measurement services for the input file.
@param[in] File This is a pointer to the device path of the file that is
being dispatched. This will optionally be used for logging.
@param[in] FileBuffer File buffer matches the input file device path.
@param[in] FileSize Size of File buffer matches the input file device path.
-
- @retval EFI_SUCCESS The file specified by File did authenticate, and the
- platform policy dictates that the DXE Core may use File.
- @retval EFI_INVALID_PARAMETER Input argument is incorrect.
+ @param[in] BootPolicy A boot policy that was used to call LoadImage() UEFI service.
+
+ @retval EFI_SUCCESS The file specified by DevicePath and non-NULL
+ FileBuffer did authenticate, and the platform policy dictates
+ that the DXE Foundation may use the file.
+ @retval EFI_SUCCESS The device path specified by NULL device path DevicePath
+ and non-NULL FileBuffer did authenticate, and the platform
+ policy dictates that the DXE Foundation may execute the image in
+ FileBuffer.
@retval EFI_OUT_RESOURCE Fail to allocate memory.
@retval 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.
- @retval 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.
+ in the untrusted state. The image has been added to the file
+ execution table.
+ @retval EFI_ACCESS_DENIED The file specified by File and FileBuffer did not
+ authenticate, and the platform policy dictates that the DXE
+ Foundation many not use File.
**/
EFI_STATUS
@@ -1115,36 +1545,45 @@ DxeImageVerificationHandler (
IN UINT32 AuthenticationStatus,
IN CONST EFI_DEVICE_PATH_PROTOCOL *File,
IN VOID *FileBuffer,
- IN UINTN FileSize
+ IN UINTN FileSize,
+ IN BOOLEAN BootPolicy
)
{
- EFI_STATUS Status;
- UINT16 Magic;
- EFI_IMAGE_DOS_HEADER *DosHdr;
- EFI_STATUS VerifyStatus;
- UINT8 *SetupMode;
- EFI_SIGNATURE_LIST *SignatureList;
- UINTN SignatureListSize;
- EFI_SIGNATURE_DATA *Signature;
- EFI_IMAGE_EXECUTION_ACTION Action;
- WIN_CERTIFICATE *WinCertificate;
- UINT32 Policy;
- UINT8 *SecureBootEnable;
-
- if (File == NULL) {
- return EFI_INVALID_PARAMETER;
- }
+ 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;
+ UINT8 *SecureBoot;
+ 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;
+ CHAR16 *NameStr;
SignatureList = NULL;
SignatureListSize = 0;
WinCertificate = NULL;
+ SecDataDir = NULL;
+ PkcsCertData = NULL;
Action = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
Status = EFI_ACCESS_DENIED;
+ VerifyStatus = EFI_ACCESS_DENIED;
+
//
// Check the image type and get policy setting.
//
switch (GetImageType (File)) {
-
+
case IMAGE_FROM_FV:
Policy = ALWAYS_EXECUTE;
break;
@@ -1162,7 +1601,7 @@ DxeImageVerificationHandler (
break;
default:
- Policy = DENY_EXECUTE_ON_SECURITY_VIOLATION;
+ Policy = DENY_EXECUTE_ON_SECURITY_VIOLATION;
break;
}
//
@@ -1174,52 +1613,63 @@ DxeImageVerificationHandler (
return EFI_ACCESS_DENIED;
}
- SecureBootEnable = GetVariable (EFI_SECURE_BOOT_ENABLE_NAME, &gEfiSecureBootEnableDisableGuid);
//
- // Skip verification if SecureBootEnable variable doesn't exist.
+ // The policy QUERY_USER_ON_SECURITY_VIOLATION and ALLOW_EXECUTE_ON_SECURITY_VIOLATION
+ // violates the UEFI spec and has been removed.
//
- if (SecureBootEnable == NULL) {
- return EFI_SUCCESS;
+ ASSERT (Policy != QUERY_USER_ON_SECURITY_VIOLATION && Policy != ALLOW_EXECUTE_ON_SECURITY_VIOLATION);
+ if (Policy == QUERY_USER_ON_SECURITY_VIOLATION || Policy == ALLOW_EXECUTE_ON_SECURITY_VIOLATION) {
+ CpuDeadLoop ();
}
+ GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME, (VOID**)&SecureBoot, NULL);
//
- // Skip verification if SecureBootEnable is disabled.
- //
- if (*SecureBootEnable == SECURE_BOOT_DISABLE) {
- FreePool (SecureBootEnable);
- return EFI_SUCCESS;
- }
-
- SetupMode = GetEfiGlobalVariable (EFI_SETUP_MODE_NAME);
-
- //
- // SetupMode doesn't exist means no AuthVar driver is dispatched,
- // skip verification.
+ // Skip verification if SecureBoot variable doesn't exist.
//
- if (SetupMode == NULL) {
+ if (SecureBoot == NULL) {
return EFI_SUCCESS;
}
//
- // If platform is in SETUP MODE, skip verification.
+ // Skip verification if SecureBoot is disabled.
//
- if (*SetupMode == SETUP_MODE) {
- FreePool (SetupMode);
+ if (*SecureBoot == SECURE_BOOT_MODE_DISABLE) {
+ FreePool (SecureBoot);
return EFI_SUCCESS;
}
+ FreePool (SecureBoot);
+
//
// Read the Dos header.
//
if (FileBuffer == NULL) {
- FreePool (SetupMode);
return EFI_INVALID_PARAMETER;
}
+
mImageBase = (UINT8 *) FileBuffer;
mImageSize = FileSize;
- DosHdr = (EFI_IMAGE_DOS_HEADER *) (mImageBase);
+
+ ZeroMem (&ImageContext, sizeof (ImageContext));
+ ImageContext.Handle = (VOID *) FileBuffer;
+ ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE) DxeImageVerificationLibImageRead;
+
+ //
+ // Get information about the image being loaded
+ //
+ Status = PeCoffLoaderGetImageInfo (&ImageContext);
+ if (EFI_ERROR (Status)) {
+ //
+ // The information can't be got from the invalid PeImage
+ //
+ goto Done;
+ }
+
+ Status = EFI_ACCESS_DENIED;
+
+ DosHdr = (EFI_IMAGE_DOS_HEADER *) mImageBase;
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
//
- // DOS image header is present,
+ // DOS image header is present,
// so read the PE header after the DOS image header.
//
mPeCoffHeaderOffset = DosHdr->e_lfanew;
@@ -1234,126 +1684,187 @@ DxeImageVerificationHandler (
//
// It is not a valid Pe/Coff file.
//
- return EFI_ACCESS_DENIED;
+ goto Done;
}
- Magic = mNtHeader.Pe32->OptionalHeader.Magic;
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
- //
- // Use PE32 offset.
- //
- mSecDataDir = (EFI_IMAGE_DATA_DIRECTORY *)&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
- } else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ if (mNtHeader.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 && mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
- // Use PE32+ offset.
+ // 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
//
- mSecDataDir = (EFI_IMAGE_DATA_DIRECTORY *)&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
+ Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
} else {
//
- // Invalid header magic number.
+ // Get the magic value from the PE/COFF Optional Header
//
- Status = EFI_INVALID_PARAMETER;
- goto Done;
+ Magic = mNtHeader.Pe32->OptionalHeader.Magic;
}
- if (mSecDataDir->VirtualAddress >= mImageSize) {
+ if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
- // Sanity check to see if this file is corrupted.
+ // Use PE32 offset.
//
- Status = EFI_INVALID_PARAMETER;
- goto Done;
- }
-
- if (mSecDataDir->Size == 0) {
+ 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 {
//
- // This image is not signed.
+ // Use PE32+ offset.
//
- Action = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
- Status = EFI_ACCESS_DENIED;
- goto Done;
+ 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];
+ }
}
+
//
- // Verify signature of executables.
+ // Start Image Validation.
//
- WinCertificate = (WIN_CERTIFICATE *) (mImageBase + mSecDataDir->VirtualAddress);
-
- switch (WinCertificate->wCertificateType) {
-
- case WIN_CERT_TYPE_EFI_GUID:
+ if (SecDataDir == NULL || SecDataDir->Size == 0) {
+ //
+ // This 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".
//
- // Verify UEFI GUID type.
- //
if (!HashPeImage (HASHALG_SHA256)) {
goto Done;
}
- VerifyStatus = VerifyCertUefiGuid ();
- break;
-
- case WIN_CERT_TYPE_PKCS_SIGNED_DATA:
- //
- // Verify Pkcs signed data type.
- //
- Status = HashPeImageByType();
- if (EFI_ERROR(Status)) {
+ if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) {
+ //
+ // Image Hash is in forbidden database (DBX).
+ //
goto Done;
}
- VerifyStatus = VerifyCertPkcsSignedData ();
-
- //
- // For image verification against enrolled certificate(root or intermediate),
- // no need to check image's hash in the allowed database.
- //
- if (!EFI_ERROR (VerifyStatus)) {
+ if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, mImageDigest, &mCertType, mImageDigestSize)) {
+ //
+ // Image Hash is in allowed database (DB).
+ //
return EFI_SUCCESS;
}
- default:
- return EFI_ACCESS_DENIED;
- }
- //
- // Get image hash value as executable's signature.
- //
- SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + mImageDigestSize;
- SignatureList = (EFI_SIGNATURE_LIST *) AllocateZeroPool (SignatureListSize);
- if (SignatureList == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
+ //
+ // Image Hash is not found in both forbidden and allowed database.
+ //
goto Done;
}
- 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);
+
//
- // Signature database check after verification.
+ // 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.
//
- if (EFI_ERROR (VerifyStatus)) {
+ for (OffSet = SecDataDir->VirtualAddress;
+ OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
+ OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {
+ WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
+ if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof (WIN_CERTIFICATE) ||
+ (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength) {
+ break;
+ }
+
//
- // Verification failure.
+ // Verify the image's Authenticode signature, only DER-encoded PKCS#7 signed data is supported.
//
- Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
- Status = EFI_ACCESS_DENIED;
- } else if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, Signature->SignatureData, &mCertType, mImageDigestSize)) {
+ 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 (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
+ continue;
+ }
+ AuthData = WinCertUefiGuid->CertData;
+ AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
+ } else {
+ if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
+ break;
+ }
+ continue;
+ }
+
+ Status = HashPeImageByType (AuthData, AuthDataSize);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
//
- // Executable signature verification passes, but is found in forbidden signature database.
+ // Check the digital signature against the revoked certificate in forbidden database (dbx).
//
- Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
- Status = EFI_ACCESS_DENIED;
- } else if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, Signature->SignatureData, &mCertType, mImageDigestSize)) {
+ if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
+ Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
+ VerifyStatus = EFI_ACCESS_DENIED;
+ break;
+ }
+
//
- // Executable signature is found in authorized signature database.
+ // Check the digital signature against the valid certificate in allowed database (db).
//
- Status = EFI_SUCCESS;
- } else {
+ if (EFI_ERROR (VerifyStatus)) {
+ if (IsAllowedByDb (AuthData, AuthDataSize)) {
+ VerifyStatus = EFI_SUCCESS;
+ }
+ }
+
+ //
+ // Check the image's hash value.
+ //
+ if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) {
+ Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
+ VerifyStatus = EFI_ACCESS_DENIED;
+ break;
+ } else if (EFI_ERROR (VerifyStatus)) {
+ if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, mImageDigest, &mCertType, mImageDigestSize)) {
+ VerifyStatus = EFI_SUCCESS;
+ }
+ }
+ }
+
+ if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
//
- // Executable signature verification passes, but cannot be found in authorized signature database.
- // Get platform policy to determine the action.
+ // The Size in Certificate Table or the attribute certicate table is corrupted.
//
- Action = EFI_IMAGE_EXECUTION_AUTH_SIG_PASSED;
- Status = ImageAuthorization (Policy);
+ VerifyStatus = EFI_ACCESS_DENIED;
+ }
+
+ if (!EFI_ERROR (VerifyStatus)) {
+ return EFI_SUCCESS;
+ } else {
+ Status = EFI_ACCESS_DENIED;
+ if (Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED || Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND) {
+ //
+ // Get image hash value as executable's signature.
+ //
+ SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + mImageDigestSize;
+ SignatureList = (EFI_SIGNATURE_LIST *) AllocateZeroPool (SignatureListSize);
+ if (SignatureList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+ 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);
+ }
}
Done:
@@ -1361,64 +1872,56 @@ Done:
//
// Policy decides to defer or reject the image; add its information in image executable information table.
//
- AddImageExeInfo (Action, NULL, File, SignatureList, SignatureListSize);
+ NameStr = ConvertDevicePathToText (File, FALSE, TRUE);
+ AddImageExeInfo (Action, NameStr, File, SignatureList, SignatureListSize);
+ if (NameStr != NULL) {
+ DEBUG((EFI_D_INFO, "The image doesn't pass verification: %s\n", NameStr));
+ FreePool(NameStr);
+ }
+ Status = EFI_SECURITY_VIOLATION;
}
if (SignatureList != NULL) {
FreePool (SignatureList);
}
- FreePool (SetupMode);
-
return Status;
}
/**
- When VariableWriteArchProtocol install, create "SecureBoot" variable.
-
- @param[in] Event Event whose notification function is being invoked.
- @param[in] Context Pointer to the notification function's context.
-
+ On Ready To Boot Services Event notification handler.
+
+ Add the image execution information table if it is not in system configuration table.
+
+ @param[in] Event Event whose notification function is being invoked
+ @param[in] Context Pointer to the notification function's context
+
**/
VOID
EFIAPI
-VariableWriteCallBack (
- IN EFI_EVENT Event,
- IN VOID *Context
+OnReadyToBoot (
+ IN EFI_EVENT Event,
+ IN VOID *Context
)
{
- UINT8 SecureBootMode;
- UINT8 *SecureBootModePtr;
- EFI_STATUS Status;
- VOID *ProtocolPointer;
+ EFI_IMAGE_EXECUTION_INFO_TABLE *ImageExeInfoTable;
+ UINTN ImageExeInfoTableSize;
- Status = gBS->LocateProtocol (&gEfiVariableWriteArchProtocolGuid, NULL, &ProtocolPointer);
- if (EFI_ERROR (Status)) {
+ EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID **) &ImageExeInfoTable);
+ if (ImageExeInfoTable != NULL) {
return;
}
-
- //
- // Check whether "SecureBoot" variable exists.
- // If this library is built-in, it means firmware has capability to perform
- // driver signing verification.
- //
- SecureBootModePtr = GetEfiGlobalVariable (EFI_SECURE_BOOT_MODE_NAME);
- if (SecureBootModePtr == NULL) {
- SecureBootMode = SECURE_BOOT_MODE_DISABLE;
- //
- // Authenticated variable driver will update "SecureBoot" depending on SetupMode variable.
- //
- gRT->SetVariable (
- EFI_SECURE_BOOT_MODE_NAME,
- &gEfiGlobalVariableGuid,
- EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
- sizeof (UINT8),
- &SecureBootMode
- );
- } else {
- FreePool (SecureBootModePtr);
+
+ ImageExeInfoTableSize = sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE);
+ ImageExeInfoTable = (EFI_IMAGE_EXECUTION_INFO_TABLE *) AllocateRuntimePool (ImageExeInfoTableSize);
+ if (ImageExeInfoTable == NULL) {
+ return ;
}
-}
+
+ ImageExeInfoTable->NumberOfImages = 0;
+ gBS->InstallConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID *) ImageExeInfoTable);
+
+}
/**
Register security measurement handler.
@@ -1435,21 +1938,20 @@ DxeImageVerificationLibConstructor (
IN EFI_SYSTEM_TABLE *SystemTable
)
{
- VOID *Registration;
+ EFI_EVENT Event;
//
- // Register callback function upon VariableWriteArchProtocol.
- //
- EfiCreateProtocolNotifyEvent (
- &gEfiVariableWriteArchProtocolGuid,
+ // Register the event to publish the image execution table.
+ //
+ EfiCreateEventReadyToBootEx (
TPL_CALLBACK,
- VariableWriteCallBack,
+ OnReadyToBoot,
NULL,
- &Registration
+ &Event
);
- return RegisterSecurityHandler (
+ return RegisterSecurity2Handler (
DxeImageVerificationHandler,
EFI_AUTH_OPERATION_VERIFY_IMAGE | EFI_AUTH_OPERATION_IMAGE_REQUIRED
- );
+ );
}