/** @file\r
- Implement image verification services for secure boot service in UEFI2.3.1.\r
+ Implement image verification services for secure boot service\r
\r
-Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>\r
-This program and the accompanying materials \r
-are licensed and made available under the terms and conditions of the BSD License \r
-which accompanies this distribution. The full text of the license may be found at \r
-http://opensource.org/licenses/bsd-license.php\r
+ Caution: This file requires additional review when modified.\r
+ This library will have external input - PE/COFF image.\r
+ This external input must be validated carefully to avoid security issue like\r
+ buffer overflow, integer overflow.\r
\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+ DxeImageVerificationLibImageRead() function will make sure the PE/COFF image content\r
+ read is within the image buffer.\r
+\r
+ DxeImageVerificationHandler(), HashPeImageByType(), HashPeImage() function will accept\r
+ untrusted PE/COFF image and validate its data structure within this image buffer before use.\r
+\r
+Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>\r
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
#include "DxeImageVerificationLib.h"\r
\r
+//\r
+// Caution: This is used by a function which may receive untrusted input.\r
+// These global variables hold PE/COFF image data, and they should be validated before use.\r
+//\r
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION mNtHeader;\r
+UINT32 mPeCoffHeaderOffset;\r
+EFI_GUID mCertType;\r
+\r
+//\r
+// Information on current PE/COFF image\r
+//\r
UINTN mImageSize;\r
-UINT32 mPeCoffHeaderOffset; \r
+UINT8 *mImageBase = NULL;\r
UINT8 mImageDigest[MAX_DIGEST_SIZE];\r
UINTN mImageDigestSize;\r
-EFI_IMAGE_DATA_DIRECTORY *mSecDataDir = NULL;\r
-UINT8 *mImageBase = NULL;\r
-EFI_GUID mCertType;\r
\r
//\r
// Notify string for authorization UI.\r
// OID ASN.1 Value for Hash Algorithms\r
//\r
UINT8 mHashOidValue[] = {\r
- 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, // OBJ_md5\r
0x2B, 0x0E, 0x03, 0x02, 0x1A, // OBJ_sha1\r
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, // OBJ_sha224\r
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, // OBJ_sha256\r
};\r
\r
HASH_TABLE mHash[] = {\r
- { L"SHA1", 20, &mHashOidValue[8], 5, Sha1GetContextSize, Sha1Init, Sha1Update, Sha1Final },\r
- { L"SHA224", 28, &mHashOidValue[13], 9, NULL, NULL, NULL, NULL },\r
- { L"SHA256", 32, &mHashOidValue[22], 9, Sha256GetContextSize,Sha256Init, Sha256Update, Sha256Final},\r
- { L"SHA384", 48, &mHashOidValue[31], 9, NULL, NULL, NULL, NULL },\r
- { L"SHA512", 64, &mHashOidValue[40], 9, NULL, NULL, NULL, NULL }\r
+ { L"SHA1", 20, &mHashOidValue[0], 5, Sha1GetContextSize, Sha1Init, Sha1Update, Sha1Final },\r
+ { L"SHA224", 28, &mHashOidValue[5], 9, NULL, NULL, NULL, NULL },\r
+ { L"SHA256", 32, &mHashOidValue[14], 9, Sha256GetContextSize, Sha256Init, Sha256Update, Sha256Final},\r
+ { L"SHA384", 48, &mHashOidValue[23], 9, Sha384GetContextSize, Sha384Init, Sha384Update, Sha384Final},\r
+ { L"SHA512", 64, &mHashOidValue[32], 9, Sha512GetContextSize, Sha512Init, Sha512Update, Sha512Final}\r
};\r
\r
+EFI_STRING mHashTypeStr;\r
+\r
+/**\r
+ SecureBoot Hook for processing image verification.\r
+\r
+ @param[in] VariableName Name of Variable to be found.\r
+ @param[in] VendorGuid Variable vendor GUID.\r
+ @param[in] DataSize Size of Data found. If size is less than the\r
+ data, this value contains the required size.\r
+ @param[in] Data Data pointer.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+SecureBootHook (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN UINTN DataSize,\r
+ IN VOID *Data\r
+ );\r
+\r
+/**\r
+ Reads contents of a PE/COFF image in memory buffer.\r
+\r
+ Caution: This function may receive untrusted input.\r
+ PE/COFF image is external input, so this function will make sure the PE/COFF image content\r
+ read is within the image buffer.\r
+\r
+ @param FileHandle Pointer to the file handle to read the PE/COFF image.\r
+ @param FileOffset Offset into the PE/COFF image to begin the read operation.\r
+ @param ReadSize On input, the size in bytes of the requested read operation.\r
+ On output, the number of bytes actually read.\r
+ @param Buffer Output buffer that contains the data read from the PE/COFF image.\r
+\r
+ @retval EFI_SUCCESS The specified portion of the PE/COFF image was read and the size\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DxeImageVerificationLibImageRead (\r
+ IN VOID *FileHandle,\r
+ IN UINTN FileOffset,\r
+ IN OUT UINTN *ReadSize,\r
+ OUT VOID *Buffer\r
+ )\r
+{\r
+ UINTN EndPosition;\r
+\r
+ if (FileHandle == NULL || ReadSize == NULL || Buffer == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (MAX_ADDRESS - FileOffset < *ReadSize) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ EndPosition = FileOffset + *ReadSize;\r
+ if (EndPosition > mImageSize) {\r
+ *ReadSize = (UINT32)(mImageSize - FileOffset);\r
+ }\r
+\r
+ if (FileOffset >= mImageSize) {\r
+ *ReadSize = 0;\r
+ }\r
+\r
+ CopyMem (Buffer, (UINT8 *)((UINTN) FileHandle + FileOffset), *ReadSize);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
\r
/**\r
Get the image type.\r
\r
@param[in] File This is a pointer to the device path of the file that is\r
- being dispatched. \r
+ being dispatched.\r
\r
- @return UINT32 Image Type \r
+ @return UINT32 Image Type\r
\r
**/\r
UINT32\r
)\r
{\r
EFI_STATUS Status;\r
- EFI_HANDLE DeviceHandle; \r
+ EFI_HANDLE DeviceHandle;\r
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
\r
+ if (File == NULL) {\r
+ return IMAGE_UNKNOWN;\r
+ }\r
+\r
//\r
// First check to see if File is from a Firmware Volume\r
//\r
DeviceHandle = NULL;\r
- TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File;\r
+ TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;\r
Status = gBS->LocateDevicePath (\r
&gEfiFirmwareVolume2ProtocolGuid,\r
&TempDevicePath,\r
// Next check to see if File is from a Block I/O device\r
//\r
DeviceHandle = NULL;\r
- TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File;\r
+ TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;\r
Status = gBS->LocateDevicePath (\r
&gEfiBlockIoProtocolGuid,\r
&TempDevicePath,\r
}\r
\r
//\r
- // File is not in a Firmware Volume or on a Block I/O device, so check to see if \r
+ // File is not in a Firmware Volume or on a Block I/O device, so check to see if\r
// the device path supports the Simple File System Protocol.\r
//\r
DeviceHandle = NULL;\r
- TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File;\r
+ TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;\r
Status = gBS->LocateDevicePath (\r
&gEfiSimpleFileSystemProtocolGuid,\r
&TempDevicePath,\r
\r
//\r
// File is not from an FV, Block I/O or Simple File System, so the only options\r
- // left are a PCI Option ROM and a Load File Protocol such as a PXE Boot from a NIC. \r
+ // left are a PCI Option ROM and a Load File Protocol such as a PXE Boot from a NIC.\r
//\r
- TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File;\r
+ TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;\r
while (!IsDevicePathEndType (TempDevicePath)) {\r
switch (DevicePathType (TempDevicePath)) {\r
- \r
+\r
case MEDIA_DEVICE_PATH:\r
if (DevicePathSubType (TempDevicePath) == MEDIA_RELATIVE_OFFSET_RANGE_DP) {\r
return IMAGE_FROM_OPTION_ROM;\r
case MESSAGING_DEVICE_PATH:\r
if (DevicePathSubType(TempDevicePath) == MSG_MAC_ADDR_DP) {\r
return IMAGE_FROM_REMOVABLE_MEDIA;\r
- } \r
+ }\r
break;\r
\r
default:\r
}\r
TempDevicePath = NextDevicePathNode (TempDevicePath);\r
}\r
- return IMAGE_UNKNOWN; \r
+ return IMAGE_UNKNOWN;\r
}\r
\r
/**\r
- Caculate hash of Pe/Coff image based on the authenticode image hashing in\r
+ Calculate hash of Pe/Coff image based on the authenticode image hashing in\r
PE/COFF Specification 8.0 Appendix A\r
\r
+ Caution: This function may receive untrusted input.\r
+ PE/COFF image is external input, so this function will validate its data structure\r
+ within this image buffer before use.\r
+\r
+ Notes: PE/COFF image has been checked by BasePeCoffLib PeCoffLoaderGetImageInfo() in\r
+ its caller function DxeImageVerificationHandler().\r
+\r
@param[in] HashAlg Hash algorithm type.\r
- \r
+\r
@retval TRUE Successfully hash image.\r
@retval FALSE Fail in hash image.\r
\r
**/\r
-BOOLEAN \r
+BOOLEAN\r
HashPeImage (\r
IN UINT32 HashAlg\r
)\r
{\r
BOOLEAN Status;\r
- UINT16 Magic;\r
EFI_IMAGE_SECTION_HEADER *Section;\r
VOID *HashCtx;\r
UINTN CtxSize;\r
EFI_IMAGE_SECTION_HEADER *SectionHeader;\r
UINTN Index;\r
UINTN Pos;\r
+ UINT32 CertSize;\r
+ UINT32 NumberOfRvaAndSizes;\r
\r
HashCtx = NULL;\r
SectionHeader = NULL;\r
Status = FALSE;\r
\r
- if ((HashAlg != HASHALG_SHA1) && (HashAlg != HASHALG_SHA256)) {\r
+ if ((HashAlg >= HASHALG_MAX)) {\r
return FALSE;\r
}\r
- \r
+\r
//\r
// Initialize context of hash.\r
//\r
ZeroMem (mImageDigest, MAX_DIGEST_SIZE);\r
\r
- if (HashAlg == HASHALG_SHA1) {\r
- mImageDigestSize = SHA1_DIGEST_SIZE;\r
- mCertType = gEfiCertSha1Guid;\r
- } else if (HashAlg == HASHALG_SHA256) {\r
- mImageDigestSize = SHA256_DIGEST_SIZE;\r
- mCertType = gEfiCertSha256Guid;\r
- } else {\r
+ switch (HashAlg) {\r
+ case HASHALG_SHA1:\r
+ mImageDigestSize = SHA1_DIGEST_SIZE;\r
+ mCertType = gEfiCertSha1Guid;\r
+ break;\r
+\r
+ case HASHALG_SHA256:\r
+ mImageDigestSize = SHA256_DIGEST_SIZE;\r
+ mCertType = gEfiCertSha256Guid;\r
+ break;\r
+\r
+ case HASHALG_SHA384:\r
+ mImageDigestSize = SHA384_DIGEST_SIZE;\r
+ mCertType = gEfiCertSha384Guid;\r
+ break;\r
+\r
+ case HASHALG_SHA512:\r
+ mImageDigestSize = SHA512_DIGEST_SIZE;\r
+ mCertType = gEfiCertSha512Guid;\r
+ break;\r
+\r
+ default:\r
return FALSE;\r
}\r
\r
+ mHashTypeStr = mHash[HashAlg].Name;\r
CtxSize = mHash[HashAlg].GetContextSize();\r
- \r
+\r
HashCtx = AllocatePool (CtxSize);\r
- ASSERT (HashCtx != NULL);\r
+ if (HashCtx == NULL) {\r
+ return FALSE;\r
+ }\r
\r
// 1. Load the image header into memory.\r
\r
// 2. Initialize a SHA hash context.\r
Status = mHash[HashAlg].HashInit(HashCtx);\r
- \r
+\r
if (!Status) {\r
goto Done;\r
}\r
+\r
//\r
// Measuring PE/COFF Image Header;\r
// But CheckSum field and SECURITY data directory (certificate) are excluded\r
//\r
- Magic = mNtHeader.Pe32->OptionalHeader.Magic;\r
+\r
//\r
// 3. Calculate the distance from the base of the image header to the image checksum address.\r
// 4. Hash the image header from its base to beginning of the image checksum.\r
//\r
HashBase = mImageBase;\r
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
//\r
// Use PE32 offset.\r
//\r
- HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32->OptionalHeader.CheckSum) - HashBase);\r
- } else {\r
+ HashSize = (UINTN) (&mNtHeader.Pe32->OptionalHeader.CheckSum) - (UINTN) HashBase;\r
+ NumberOfRvaAndSizes = mNtHeader.Pe32->OptionalHeader.NumberOfRvaAndSizes;\r
+ } else if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {\r
//\r
// Use PE32+ offset.\r
//\r
- HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32Plus->OptionalHeader.CheckSum) - HashBase);\r
+ HashSize = (UINTN) (&mNtHeader.Pe32Plus->OptionalHeader.CheckSum) - (UINTN) HashBase;\r
+ NumberOfRvaAndSizes = mNtHeader.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;\r
+ } else {\r
+ //\r
+ // Invalid header magic number.\r
+ //\r
+ Status = FALSE;\r
+ goto Done;\r
}\r
\r
Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize);\r
if (!Status) {\r
goto Done;\r
}\r
+\r
//\r
// 5. Skip over the image checksum (it occupies a single ULONG).\r
- // 6. Get the address of the beginning of the Cert Directory.\r
- // 7. Hash everything from the end of the checksum to the start of the Cert Directory.\r
//\r
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
- //\r
- // Use PE32 offset.\r
+ if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {\r
//\r
- HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);\r
- HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase);\r
- } else {\r
+ // 6. Since there is no Cert Directory in optional header, hash everything\r
+ // from the end of the checksum to the end of image header.\r
//\r
- // Use PE32+ offset.\r
- // \r
- HashBase = (UINT8 *) &mNtHeader.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32);\r
- HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase);\r
- }\r
+ if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ //\r
+ // Use PE32 offset.\r
+ //\r
+ HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);\r
+ HashSize = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders - ((UINTN) HashBase - (UINTN) mImageBase);\r
+ } else {\r
+ //\r
+ // Use PE32+ offset.\r
+ //\r
+ HashBase = (UINT8 *) &mNtHeader.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32);\r
+ HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - ((UINTN) HashBase - (UINTN) mImageBase);\r
+ }\r
\r
- Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize);\r
- if (!Status) {\r
- goto Done;\r
- }\r
- //\r
- // 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.)\r
- // 9. Hash everything from the end of the Cert Directory to the end of image header.\r
- //\r
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ if (HashSize != 0) {\r
+ Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize);\r
+ if (!Status) {\r
+ goto Done;\r
+ }\r
+ }\r
+ } else {\r
//\r
- // Use PE32 offset\r
+ // 7. Hash everything from the end of the checksum to the start of the Cert Directory.\r
//\r
- HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];\r
- HashSize = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) ((UINT8 *) (&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - mImageBase);\r
- } else {\r
+ if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ //\r
+ // Use PE32 offset.\r
+ //\r
+ HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);\r
+ HashSize = (UINTN) (&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN) HashBase;\r
+ } else {\r
+ //\r
+ // Use PE32+ offset.\r
+ //\r
+ HashBase = (UINT8 *) &mNtHeader.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32);\r
+ HashSize = (UINTN) (&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN) HashBase;\r
+ }\r
+\r
+ if (HashSize != 0) {\r
+ Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize);\r
+ if (!Status) {\r
+ goto Done;\r
+ }\r
+ }\r
+\r
//\r
- // Use PE32+ offset.\r
+ // 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.)\r
+ // 9. Hash everything from the end of the Cert Directory to the end of image header.\r
//\r
- HashBase = (UINT8 *) &mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];\r
- HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) ((UINT8 *) (&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - mImageBase);\r
- }\r
+ if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ //\r
+ // Use PE32 offset\r
+ //\r
+ HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];\r
+ HashSize = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders - ((UINTN) HashBase - (UINTN) mImageBase);\r
+ } else {\r
+ //\r
+ // Use PE32+ offset.\r
+ //\r
+ HashBase = (UINT8 *) &mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];\r
+ HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - ((UINTN) HashBase - (UINTN) mImageBase);\r
+ }\r
\r
- Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize);\r
- if (!Status) {\r
- goto Done;\r
+ if (HashSize != 0) {\r
+ Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize);\r
+ if (!Status) {\r
+ goto Done;\r
+ }\r
+ }\r
}\r
+\r
//\r
// 10. Set the SUM_OF_BYTES_HASHED to the size of the header.\r
//\r
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
//\r
// Use PE32 offset.\r
//\r
SumOfBytesHashed = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders;\r
}\r
\r
+\r
+ Section = (EFI_IMAGE_SECTION_HEADER *) (\r
+ mImageBase +\r
+ mPeCoffHeaderOffset +\r
+ sizeof (UINT32) +\r
+ sizeof (EFI_IMAGE_FILE_HEADER) +\r
+ mNtHeader.Pe32->FileHeader.SizeOfOptionalHeader\r
+ );\r
+\r
//\r
// 11. Build a temporary table of pointers to all the IMAGE_SECTION_HEADER\r
// structures in the image. The 'NumberOfSections' field of the image\r
// IMAGE_SECTION_HEADERs in the table whose 'SizeOfRawData' field is zero.\r
//\r
SectionHeader = (EFI_IMAGE_SECTION_HEADER *) AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * mNtHeader.Pe32->FileHeader.NumberOfSections);\r
- ASSERT (SectionHeader != NULL);\r
+ if (SectionHeader == NULL) {\r
+ Status = FALSE;\r
+ goto Done;\r
+ }\r
//\r
// 12. Using the 'PointerToRawData' in the referenced section headers as\r
// a key, arrange the elements in the table in ascending order. In other\r
// words, sort the section headers according to the disk-file offset of\r
// the section.\r
//\r
- Section = (EFI_IMAGE_SECTION_HEADER *) (\r
- mImageBase +\r
- mPeCoffHeaderOffset +\r
- sizeof (UINT32) +\r
- sizeof (EFI_IMAGE_FILE_HEADER) +\r
- mNtHeader.Pe32->FileHeader.SizeOfOptionalHeader\r
- );\r
for (Index = 0; Index < mNtHeader.Pe32->FileHeader.NumberOfSections; Index++) {\r
Pos = Index;\r
while ((Pos > 0) && (Section->PointerToRawData < SectionHeader[Pos - 1].PointerToRawData)) {\r
//\r
if (mImageSize > SumOfBytesHashed) {\r
HashBase = mImageBase + SumOfBytesHashed;\r
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
- //\r
- // Use PE32 offset.\r
- //\r
- HashSize = (UINTN)(\r
- mImageSize -\r
- mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size -\r
- SumOfBytesHashed);\r
+\r
+ if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {\r
+ CertSize = 0;\r
} else {\r
- //\r
- // Use PE32+ offset.\r
- //\r
- HashSize = (UINTN)(\r
- mImageSize -\r
- mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size -\r
- SumOfBytesHashed); \r
+ if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ //\r
+ // Use PE32 offset.\r
+ //\r
+ CertSize = mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size;\r
+ } else {\r
+ //\r
+ // Use PE32+ offset.\r
+ //\r
+ CertSize = mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size;\r
+ }\r
}\r
\r
- Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize);\r
- if (!Status) {\r
+ if (mImageSize > CertSize + SumOfBytesHashed) {\r
+ HashSize = (UINTN) (mImageSize - CertSize - SumOfBytesHashed);\r
+\r
+ Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize);\r
+ if (!Status) {\r
+ goto Done;\r
+ }\r
+ } else if (mImageSize < CertSize + SumOfBytesHashed) {\r
+ Status = FALSE;\r
goto Done;\r
}\r
}\r
+\r
Status = mHash[HashAlg].HashFinal(HashCtx, mImageDigest);\r
\r
Done:\r
}\r
\r
/**\r
- Recognize the Hash algorithm in PE/COFF Authenticode and caculate hash of \r
- Pe/Coff image based on the authenticode image hashing in PE/COFF Specification \r
+ Recognize the Hash algorithm in PE/COFF Authenticode and calculate hash of\r
+ Pe/Coff image based on the authenticode image hashing in PE/COFF Specification\r
8.0 Appendix A\r
\r
+ Caution: This function may receive untrusted input.\r
+ PE/COFF image is external input, so this function will validate its data structure\r
+ within this image buffer before use.\r
+\r
+ @param[in] AuthData Pointer to the Authenticode Signature retrieved from signed image.\r
+ @param[in] AuthDataSize Size of the Authenticode Signature in bytes.\r
+\r
@retval EFI_UNSUPPORTED Hash algorithm is not supported.\r
@retval EFI_SUCCESS Hash successfully.\r
\r
**/\r
-EFI_STATUS \r
+EFI_STATUS\r
HashPeImageByType (\r
- VOID\r
+ IN UINT8 *AuthData,\r
+ IN UINTN AuthDataSize\r
)\r
{\r
UINT8 Index;\r
- WIN_CERTIFICATE_EFI_PKCS *PkcsCertData;\r
-\r
- PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) (mImageBase + mSecDataDir->VirtualAddress);\r
\r
- for (Index = 0; Index < HASHALG_MAX; Index++) { \r
+ for (Index = 0; Index < HASHALG_MAX; Index++) {\r
//\r
// Check the Hash algorithm in PE/COFF Authenticode.\r
- // According to PKCS#7 Definition: \r
+ // According to PKCS#7 Definition:\r
// SignedData ::= SEQUENCE {\r
// version Version,\r
// digestAlgorithms DigestAlgorithmIdentifiers,\r
// .... }\r
// The DigestAlgorithmIdentifiers can be used to determine the hash algorithm in PE/COFF hashing\r
// This field has the fixed offset (+32) in final Authenticode ASN.1 data.\r
- // \r
- if (CompareMem (PkcsCertData->CertData + 32, mHash[Index].OidValue, mHash[Index].OidLength) == 0) {\r
+ // Fixed offset (+32) is calculated based on two bytes of length encoding.\r
+ //\r
+ if ((*(AuthData + 1) & TWO_BYTE_ENCODE) != TWO_BYTE_ENCODE) {\r
+ //\r
+ // Only support two bytes of Long Form of Length Encoding.\r
+ //\r
+ continue;\r
+ }\r
+\r
+ if (AuthDataSize < 32 + mHash[Index].OidLength) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ if (CompareMem (AuthData + 32, mHash[Index].OidValue, mHash[Index].OidLength) == 0) {\r
break;\r
}\r
}\r
ImageExeInfoTable. If ImageExeInfoTable is NULL, then 0 is returned.\r
\r
@param ImageExeInfoTable A pointer to a image execution info table structure.\r
- \r
+\r
@retval 0 If ImageExeInfoTable is NULL.\r
@retval Others The size of a image execution info table in bytes.\r
\r
@param[in] Name Input a null-terminated, user-friendly name.\r
@param[in] DevicePath Input device path pointer.\r
@param[in] Signature Input signature info in EFI_SIGNATURE_LIST data structure.\r
- @param[in] SignatureSize Size of signature.\r
- \r
+ @param[in] SignatureSize Size of signature. Must be zero if Signature is NULL.\r
+\r
**/\r
VOID\r
AddImageExeInfo (\r
- IN EFI_IMAGE_EXECUTION_ACTION Action, \r
- IN CHAR16 *Name OPTIONAL, \r
+ IN EFI_IMAGE_EXECUTION_ACTION Action,\r
+ IN CHAR16 *Name OPTIONAL,\r
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
IN EFI_SIGNATURE_LIST *Signature OPTIONAL,\r
IN UINTN SignatureSize\r
)\r
{\r
- EFI_STATUS Status;\r
EFI_IMAGE_EXECUTION_INFO_TABLE *ImageExeInfoTable;\r
EFI_IMAGE_EXECUTION_INFO_TABLE *NewImageExeInfoTable;\r
EFI_IMAGE_EXECUTION_INFO *ImageExeInfoEntry;\r
UINTN NewImageExeInfoEntrySize;\r
UINTN NameStringLen;\r
UINTN DevicePathSize;\r
+ CHAR16 *NameStr;\r
\r
- ASSERT (DevicePath != NULL);\r
ImageExeInfoTable = NULL;\r
NewImageExeInfoTable = NULL;\r
ImageExeInfoEntry = NULL;\r
NameStringLen = 0;\r
+ NameStr = NULL;\r
+\r
+ if (DevicePath == NULL) {\r
+ return ;\r
+ }\r
\r
if (Name != NULL) {\r
NameStringLen = StrSize (Name);\r
+ } else {\r
+ NameStringLen = sizeof (CHAR16);\r
}\r
\r
- ImageExeInfoTable = NULL;\r
- EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID**)&ImageExeInfoTable);\r
+ EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID **) &ImageExeInfoTable);\r
if (ImageExeInfoTable != NULL) {\r
//\r
// The table has been found!\r
- // We must enlarge the table to accmodate the new exe info entry.\r
+ // We must enlarge the table to accommodate the new exe info entry.\r
//\r
ImageExeInfoTableSize = GetImageExeInfoTableSize (ImageExeInfoTable);\r
} else {\r
}\r
\r
DevicePathSize = GetDevicePathSize (DevicePath);\r
- NewImageExeInfoEntrySize = sizeof (EFI_IMAGE_EXECUTION_INFO) + NameStringLen + DevicePathSize + SignatureSize;\r
+\r
+ //\r
+ // Signature size can be odd. Pad after signature to ensure next EXECUTION_INFO entry align\r
+ //\r
+ ASSERT (Signature != NULL || SignatureSize == 0);\r
+ NewImageExeInfoEntrySize = sizeof (EFI_IMAGE_EXECUTION_INFO) + NameStringLen + DevicePathSize + SignatureSize;\r
+\r
NewImageExeInfoTable = (EFI_IMAGE_EXECUTION_INFO_TABLE *) AllocateRuntimePool (ImageExeInfoTableSize + NewImageExeInfoEntrySize);\r
- ASSERT (NewImageExeInfoTable != NULL);\r
+ if (NewImageExeInfoTable == NULL) {\r
+ return ;\r
+ }\r
\r
if (ImageExeInfoTable != NULL) {\r
CopyMem (NewImageExeInfoTable, ImageExeInfoTable, ImageExeInfoTableSize);\r
NewImageExeInfoTable->NumberOfImages++;\r
ImageExeInfoEntry = (EFI_IMAGE_EXECUTION_INFO *) ((UINT8 *) NewImageExeInfoTable + ImageExeInfoTableSize);\r
//\r
- // Update new item's infomation.\r
+ // Update new item's information.\r
//\r
- WriteUnaligned32 ((UINT32 *) &ImageExeInfoEntry->Action, Action);\r
- WriteUnaligned32 ((UINT32 *) &ImageExeInfoEntry->InfoSize, (UINT32) NewImageExeInfoEntrySize);\r
+ WriteUnaligned32 ((UINT32 *) ImageExeInfoEntry, Action);\r
+ WriteUnaligned32 ((UINT32 *) ((UINT8 *) ImageExeInfoEntry + sizeof (EFI_IMAGE_EXECUTION_ACTION)), (UINT32) NewImageExeInfoEntrySize);\r
\r
+ NameStr = (CHAR16 *)(ImageExeInfoEntry + 1);\r
if (Name != NULL) {\r
- CopyMem ((UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32), Name, NameStringLen);\r
+ CopyMem ((UINT8 *) NameStr, Name, NameStringLen);\r
+ } else {\r
+ ZeroMem ((UINT8 *) NameStr, sizeof (CHAR16));\r
}\r
+\r
CopyMem (\r
- (UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32) + NameStringLen,\r
+ (UINT8 *) NameStr + NameStringLen,\r
DevicePath,\r
DevicePathSize\r
);\r
if (Signature != NULL) {\r
CopyMem (\r
- (UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32) + NameStringLen + DevicePathSize,\r
+ (UINT8 *) NameStr + NameStringLen + DevicePathSize,\r
Signature,\r
SignatureSize\r
);\r
//\r
// Update/replace the image execution table.\r
//\r
- Status = gBS->InstallConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID *) NewImageExeInfoTable);\r
- ASSERT_EFI_ERROR (Status);\r
+ gBS->InstallConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID *) NewImageExeInfoTable);\r
+\r
//\r
// Free Old table data!\r
//\r
}\r
\r
/**\r
- Discover if the UEFI image is authorized by user's policy setting.\r
+ Check whether the hash of an given X.509 certificate is in forbidden database (DBX).\r
\r
- @param[in] Policy Specify platform's policy setting. \r
+ @param[in] Certificate Pointer to X.509 Certificate that is searched for.\r
+ @param[in] CertSize Size of X.509 Certificate.\r
+ @param[in] SignatureList Pointer to the Signature List in forbidden database.\r
+ @param[in] SignatureListSize Size of Signature List.\r
+ @param[out] RevocationTime Return the time that the certificate was revoked.\r
+ @param[out] IsFound Search result. Only valid if EFI_SUCCESS returned.\r
\r
- @retval EFI_ACCESS_DENIED Image is not allowed to run.\r
- @retval EFI_SECURITY_VIOLATION Image is deferred.\r
- @retval EFI_SUCCESS Image is authorized to run.\r
+ @retval EFI_SUCCESS Finished the search without any error.\r
+ @retval Others Error occurred in the search of database.\r
\r
**/\r
EFI_STATUS\r
-ImageAuthorization (\r
- IN UINT32 Policy\r
+IsCertHashFoundInDbx (\r
+ IN UINT8 *Certificate,\r
+ IN UINTN CertSize,\r
+ IN EFI_SIGNATURE_LIST *SignatureList,\r
+ IN UINTN SignatureListSize,\r
+ OUT EFI_TIME *RevocationTime,\r
+ OUT BOOLEAN *IsFound\r
)\r
{\r
- EFI_STATUS Status;\r
- EFI_INPUT_KEY Key;\r
-\r
- Status = EFI_ACCESS_DENIED;\r
-\r
- switch (Policy) {\r
- \r
- case QUERY_USER_ON_SECURITY_VIOLATION:\r
- do {\r
- CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, mNotifyString1, mNotifyString2, NULL);\r
- if (Key.UnicodeChar == L'Y' || Key.UnicodeChar == L'y') {\r
- Status = EFI_SUCCESS;\r
- break;\r
- } else if (Key.UnicodeChar == L'N' || Key.UnicodeChar == L'n') {\r
- Status = EFI_ACCESS_DENIED;\r
- break;\r
- } else if (Key.UnicodeChar == L'D' || Key.UnicodeChar == L'd') {\r
- Status = EFI_SECURITY_VIOLATION;\r
- break;\r
+ EFI_STATUS Status;\r
+ EFI_SIGNATURE_LIST *DbxList;\r
+ UINTN DbxSize;\r
+ EFI_SIGNATURE_DATA *CertHash;\r
+ UINTN CertHashCount;\r
+ UINTN Index;\r
+ UINT32 HashAlg;\r
+ VOID *HashCtx;\r
+ UINT8 CertDigest[MAX_DIGEST_SIZE];\r
+ UINT8 *DbxCertHash;\r
+ UINTN SiglistHeaderSize;\r
+ UINT8 *TBSCert;\r
+ UINTN TBSCertSize;\r
+\r
+ Status = EFI_ABORTED;\r
+ *IsFound = FALSE;\r
+ DbxList = SignatureList;\r
+ DbxSize = SignatureListSize;\r
+ HashCtx = NULL;\r
+ HashAlg = HASHALG_MAX;\r
+\r
+ if ((RevocationTime == NULL) || (DbxList == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Retrieve the TBSCertificate from the X.509 Certificate.\r
+ //\r
+ if (!X509GetTBSCert (Certificate, CertSize, &TBSCert, &TBSCertSize)) {\r
+ return Status;\r
+ }\r
+\r
+ while ((DbxSize > 0) && (SignatureListSize >= DbxList->SignatureListSize)) {\r
+ //\r
+ // Determine Hash Algorithm of Certificate in the forbidden database.\r
+ //\r
+ if (CompareGuid (&DbxList->SignatureType, &gEfiCertX509Sha256Guid)) {\r
+ HashAlg = HASHALG_SHA256;\r
+ } else if (CompareGuid (&DbxList->SignatureType, &gEfiCertX509Sha384Guid)) {\r
+ HashAlg = HASHALG_SHA384;\r
+ } else if (CompareGuid (&DbxList->SignatureType, &gEfiCertX509Sha512Guid)) {\r
+ HashAlg = HASHALG_SHA512;\r
+ } else {\r
+ DbxSize -= DbxList->SignatureListSize;\r
+ DbxList = (EFI_SIGNATURE_LIST *) ((UINT8 *) DbxList + DbxList->SignatureListSize);\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Calculate the hash value of current TBSCertificate for comparision.\r
+ //\r
+ if (mHash[HashAlg].GetContextSize == NULL) {\r
+ goto Done;\r
+ }\r
+ ZeroMem (CertDigest, MAX_DIGEST_SIZE);\r
+ HashCtx = AllocatePool (mHash[HashAlg].GetContextSize ());\r
+ if (HashCtx == NULL) {\r
+ goto Done;\r
+ }\r
+ if (!mHash[HashAlg].HashInit (HashCtx)) {\r
+ goto Done;\r
+ }\r
+ if (!mHash[HashAlg].HashUpdate (HashCtx, TBSCert, TBSCertSize)) {\r
+ goto Done;\r
+ }\r
+ if (!mHash[HashAlg].HashFinal (HashCtx, CertDigest)) {\r
+ goto Done;\r
+ }\r
+\r
+ FreePool (HashCtx);\r
+ HashCtx = NULL;\r
+\r
+ SiglistHeaderSize = sizeof (EFI_SIGNATURE_LIST) + DbxList->SignatureHeaderSize;\r
+ CertHash = (EFI_SIGNATURE_DATA *) ((UINT8 *) DbxList + SiglistHeaderSize);\r
+ CertHashCount = (DbxList->SignatureListSize - SiglistHeaderSize) / DbxList->SignatureSize;\r
+ for (Index = 0; Index < CertHashCount; Index++) {\r
+ //\r
+ // Iterate each Signature Data Node within this CertList for verify.\r
+ //\r
+ DbxCertHash = CertHash->SignatureData;\r
+ if (CompareMem (DbxCertHash, CertDigest, mHash[HashAlg].DigestLength) == 0) {\r
+ //\r
+ // Hash of Certificate is found in forbidden database.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ *IsFound = TRUE;\r
+\r
+ //\r
+ // Return the revocation time.\r
+ //\r
+ CopyMem (RevocationTime, (EFI_TIME *)(DbxCertHash + mHash[HashAlg].DigestLength), sizeof (EFI_TIME));\r
+ goto Done;\r
}\r
- } while (TRUE);\r
- break;\r
+ CertHash = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertHash + DbxList->SignatureSize);\r
+ }\r
\r
- case ALLOW_EXECUTE_ON_SECURITY_VIOLATION:\r
- Status = EFI_SUCCESS;\r
- break;\r
+ DbxSize -= DbxList->SignatureListSize;\r
+ DbxList = (EFI_SIGNATURE_LIST *) ((UINT8 *) DbxList + DbxList->SignatureListSize);\r
+ }\r
\r
- case DEFER_EXECUTE_ON_SECURITY_VIOLATION:\r
- Status = EFI_SECURITY_VIOLATION;\r
- break;\r
+ Status = EFI_SUCCESS;\r
\r
- case DENY_EXECUTE_ON_SECURITY_VIOLATION:\r
- Status = EFI_ACCESS_DENIED;\r
- break;\r
+Done:\r
+ if (HashCtx != NULL) {\r
+ FreePool (HashCtx);\r
}\r
\r
return Status;\r
\r
@param[in] VariableName Name of database variable that is searched in.\r
@param[in] Signature Pointer to signature that is searched for.\r
- @param[in] CertType Pointer to hash algrithom.\r
+ @param[in] CertType Pointer to hash algorithm.\r
@param[in] SignatureSize Size of Signature.\r
+ @param[out] IsFound Search result. Only valid if EFI_SUCCESS returned\r
\r
- @return TRUE Found the signature in the variable database.\r
- @return FALSE Not found the signature in the variable database.\r
+ @retval EFI_SUCCESS Finished the search without any error.\r
+ @retval Others Error occurred in the search of database.\r
\r
**/\r
-BOOLEAN\r
+EFI_STATUS\r
IsSignatureFoundInDatabase (\r
- IN CHAR16 *VariableName,\r
- IN UINT8 *Signature, \r
- IN EFI_GUID *CertType,\r
- IN UINTN SignatureSize\r
+ IN CHAR16 *VariableName,\r
+ IN UINT8 *Signature,\r
+ IN EFI_GUID *CertType,\r
+ IN UINTN SignatureSize,\r
+ OUT BOOLEAN *IsFound\r
)\r
{\r
EFI_STATUS Status;\r
UINT8 *Data;\r
UINTN Index;\r
UINTN CertCount;\r
- BOOLEAN IsFound;\r
+\r
//\r
// Read signature database variable.\r
//\r
- IsFound = FALSE;\r
+ *IsFound = FALSE;\r
Data = NULL;\r
DataSize = 0;\r
Status = gRT->GetVariable (VariableName, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, NULL);\r
if (Status != EFI_BUFFER_TOO_SMALL) {\r
- return FALSE;\r
+ if (Status == EFI_NOT_FOUND) {\r
+ //\r
+ // No database, no need to search.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ }\r
+\r
+ return Status;\r
}\r
\r
Data = (UINT8 *) AllocateZeroPool (DataSize);\r
- ASSERT (Data != NULL);\r
+ if (Data == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
\r
Status = gRT->GetVariable (VariableName, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, Data);\r
if (EFI_ERROR (Status)) {\r
goto Done;\r
}\r
//\r
- // Enumerate all signature data in SigDB to check if executable's signature exists.\r
+ // Enumerate all signature data in SigDB to check if signature exists for executable.\r
//\r
CertList = (EFI_SIGNATURE_LIST *) Data;\r
while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) {\r
- CertCount = (CertList->SignatureListSize - CertList->SignatureHeaderSize) / CertList->SignatureSize;\r
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;\r
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);\r
if ((CertList->SignatureSize == sizeof(EFI_SIGNATURE_DATA) - 1 + SignatureSize) && (CompareGuid(&CertList->SignatureType, CertType))) {\r
for (Index = 0; Index < CertCount; Index++) {\r
//\r
// Find the signature in database.\r
//\r
- IsFound = TRUE;\r
+ *IsFound = TRUE;\r
+ //\r
+ // Entries in UEFI_IMAGE_SECURITY_DATABASE that are used to validate image should be measured\r
+ //\r
+ if (StrCmp(VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) {\r
+ SecureBootHook (VariableName, &gEfiImageSecurityDatabaseGuid, CertList->SignatureSize, Cert);\r
+ }\r
break;\r
}\r
\r
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);\r
}\r
\r
- if (IsFound) {\r
+ if (*IsFound) {\r
break;\r
}\r
}\r
FreePool (Data);\r
}\r
\r
- return IsFound;\r
+ return Status;\r
}\r
\r
/**\r
- Verify certificate in WIN_CERT_TYPE_PKCS_SIGNED_DATA format .\r
+ Check whether the timestamp is valid by comparing the signing time and the revocation time.\r
\r
- @retval EFI_SUCCESS Image pass verification.\r
- @retval EFI_SECURITY_VIOLATION Image fail verification.\r
- @retval other error value\r
+ @param SigningTime A pointer to the signing time.\r
+ @param RevocationTime A pointer to the revocation time.\r
+\r
+ @retval TRUE The SigningTime is not later than the RevocationTime.\r
+ @retval FALSE The SigningTime is later than the RevocationTime.\r
\r
**/\r
-EFI_STATUS \r
-VerifyCertPkcsSignedData (\r
- VOID\r
+BOOLEAN\r
+IsValidSignatureByTimestamp (\r
+ IN EFI_TIME *SigningTime,\r
+ IN EFI_TIME *RevocationTime\r
)\r
{\r
- EFI_STATUS Status;\r
- BOOLEAN VerifyStatus;\r
- WIN_CERTIFICATE_EFI_PKCS *PkcsCertData;\r
- EFI_SIGNATURE_LIST *CertList;\r
- EFI_SIGNATURE_DATA *Cert;\r
- UINTN DataSize;\r
- UINT8 *Data;\r
- UINT8 *RootCert;\r
- UINTN RootCertSize;\r
- UINTN Index;\r
- UINTN CertCount;\r
+ if (SigningTime->Year != RevocationTime->Year) {\r
+ return (BOOLEAN) (SigningTime->Year < RevocationTime->Year);\r
+ } else if (SigningTime->Month != RevocationTime->Month) {\r
+ return (BOOLEAN) (SigningTime->Month < RevocationTime->Month);\r
+ } else if (SigningTime->Day != RevocationTime->Day) {\r
+ return (BOOLEAN) (SigningTime->Day < RevocationTime->Day);\r
+ } else if (SigningTime->Hour != RevocationTime->Hour) {\r
+ return (BOOLEAN) (SigningTime->Hour < RevocationTime->Hour);\r
+ } else if (SigningTime->Minute != RevocationTime->Minute) {\r
+ return (BOOLEAN) (SigningTime->Minute < RevocationTime->Minute);\r
+ }\r
+\r
+ return (BOOLEAN) (SigningTime->Second <= RevocationTime->Second);\r
+}\r
\r
- Data = NULL;\r
- CertList = NULL;\r
- Cert = NULL;\r
- RootCert = NULL;\r
- RootCertSize = 0;\r
- VerifyStatus = FALSE;\r
- PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) (mImageBase + mSecDataDir->VirtualAddress);\r
+/**\r
+ Check if the given time value is zero.\r
\r
- //\r
- // 1: Find certificate from KEK database and try to verify authenticode struct.\r
- //\r
- DataSize = 0;\r
- Status = gRT->GetVariable (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid, NULL, &DataSize, NULL);\r
- if (Status == EFI_BUFFER_TOO_SMALL) {\r
- Data = (UINT8 *)AllocateZeroPool (DataSize);\r
- ASSERT (Data != NULL);\r
+ @param[in] Time Pointer of a time value.\r
\r
- Status = gRT->GetVariable (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid, NULL, &DataSize, (VOID *)Data);\r
- if (EFI_ERROR (Status)) {\r
- goto Done;\r
- }\r
- \r
- //\r
- // Find Cert Enrolled in KEK database to verify the signature in pkcs7 signed data.\r
- // \r
- CertList = (EFI_SIGNATURE_LIST *) Data;\r
- while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) {\r
- if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {\r
- Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);\r
- CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;\r
- for (Index = 0; Index < CertCount; Index++) {\r
- //\r
- // Iterate each Signature Data Node within this CertList for a verify\r
- // \r
- RootCert = Cert->SignatureData;\r
- RootCertSize = CertList->SignatureSize;\r
- \r
- //\r
- // Call AuthenticodeVerify library to Verify Authenticode struct. \r
- //\r
- VerifyStatus = AuthenticodeVerify (\r
- PkcsCertData->CertData,\r
- mSecDataDir->Size - sizeof(PkcsCertData->Hdr),\r
- RootCert,\r
- RootCertSize,\r
- mImageDigest,\r
- mImageDigestSize\r
- );\r
- \r
- if (VerifyStatus) {\r
- goto Done;\r
- }\r
- Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);\r
- } \r
- }\r
- DataSize -= CertList->SignatureListSize;\r
- CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);\r
- }\r
+ @retval TRUE The Time is Zero.\r
+ @retval FALSE The Time is not Zero.\r
+\r
+**/\r
+BOOLEAN\r
+IsTimeZero (\r
+ IN EFI_TIME *Time\r
+ )\r
+{\r
+ if ((Time->Year == 0) && (Time->Month == 0) && (Time->Day == 0) &&\r
+ (Time->Hour == 0) && (Time->Minute == 0) && (Time->Second == 0)) {\r
+ return TRUE;\r
}\r
\r
- \r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Check whether the timestamp signature is valid and the signing time is also earlier than\r
+ the revocation time.\r
+\r
+ @param[in] AuthData Pointer to the Authenticode signature retrieved from signed image.\r
+ @param[in] AuthDataSize Size of the Authenticode signature in bytes.\r
+ @param[in] RevocationTime The time that the certificate was revoked.\r
+\r
+ @retval TRUE Timestamp signature is valid and signing time is no later than the\r
+ revocation time.\r
+ @retval FALSE Timestamp signature is not valid or the signing time is later than the\r
+ revocation time.\r
+\r
+**/\r
+BOOLEAN\r
+PassTimestampCheck (\r
+ IN UINT8 *AuthData,\r
+ IN UINTN AuthDataSize,\r
+ IN EFI_TIME *RevocationTime\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ BOOLEAN VerifyStatus;\r
+ EFI_SIGNATURE_LIST *CertList;\r
+ EFI_SIGNATURE_DATA *Cert;\r
+ UINT8 *DbtData;\r
+ UINTN DbtDataSize;\r
+ UINT8 *RootCert;\r
+ UINTN RootCertSize;\r
+ UINTN Index;\r
+ UINTN CertCount;\r
+ EFI_TIME SigningTime;\r
\r
//\r
- // 2: Find certificate from DB database and try to verify authenticode struct.\r
+ // Variable Initialization\r
//\r
- DataSize = 0;\r
- Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, NULL);\r
- if (Status == EFI_BUFFER_TOO_SMALL) {\r
- Data = (UINT8 *)AllocateZeroPool (DataSize);\r
- ASSERT (Data != NULL);\r
+ VerifyStatus = FALSE;\r
+ DbtData = NULL;\r
+ CertList = NULL;\r
+ Cert = NULL;\r
+ RootCert = NULL;\r
+ RootCertSize = 0;\r
\r
- Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, (VOID *)Data);\r
- if (EFI_ERROR (Status)) {\r
- goto Done;\r
- }\r
+ //\r
+ // If RevocationTime is zero, the certificate shall be considered to always be revoked.\r
+ //\r
+ if (IsTimeZero (RevocationTime)) {\r
+ return FALSE;\r
+ }\r
\r
- //\r
- // Find Cert Enrolled in DB database to verify the signature in pkcs7 signed data.\r
- // \r
- CertList = (EFI_SIGNATURE_LIST *) Data;\r
- while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) {\r
- if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {\r
- Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);\r
- CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;\r
- for (Index = 0; Index < CertCount; Index++) {\r
- //\r
- // Iterate each Signature Data Node within this CertList for a verify\r
- // \r
- RootCert = Cert->SignatureData;\r
- RootCertSize = CertList->SignatureSize;\r
- \r
+ //\r
+ // RevocationTime is non-zero, the certificate should be considered to be revoked from that time and onwards.\r
+ // Using the dbt to get the trusted TSA certificates.\r
+ //\r
+ DbtDataSize = 0;\r
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE2, &gEfiImageSecurityDatabaseGuid, NULL, &DbtDataSize, NULL);\r
+ if (Status != EFI_BUFFER_TOO_SMALL) {\r
+ goto Done;\r
+ }\r
+ DbtData = (UINT8 *) AllocateZeroPool (DbtDataSize);\r
+ if (DbtData == NULL) {\r
+ goto Done;\r
+ }\r
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE2, &gEfiImageSecurityDatabaseGuid, NULL, &DbtDataSize, (VOID *) DbtData);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ CertList = (EFI_SIGNATURE_LIST *) DbtData;\r
+ while ((DbtDataSize > 0) && (DbtDataSize >= CertList->SignatureListSize)) {\r
+ if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {\r
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);\r
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;\r
+ for (Index = 0; Index < CertCount; Index++) {\r
+ //\r
+ // Iterate each Signature Data Node within this CertList for verify.\r
+ //\r
+ RootCert = Cert->SignatureData;\r
+ RootCertSize = CertList->SignatureSize - sizeof (EFI_GUID);\r
+ //\r
+ // Get the signing time if the timestamp signature is valid.\r
+ //\r
+ if (ImageTimestampVerify (AuthData, AuthDataSize, RootCert, RootCertSize, &SigningTime)) {\r
//\r
- // Call AuthenticodeVerify library to Verify Authenticode struct. \r
+ // The signer signature is valid only when the signing time is earlier than revocation time.\r
//\r
- VerifyStatus = AuthenticodeVerify (\r
- PkcsCertData->CertData,\r
- mSecDataDir->Size - sizeof(PkcsCertData->Hdr),\r
- RootCert,\r
- RootCertSize,\r
- mImageDigest,\r
- mImageDigestSize\r
- );\r
- \r
- if (VerifyStatus) {\r
+ if (IsValidSignatureByTimestamp (&SigningTime, RevocationTime)) {\r
+ VerifyStatus = TRUE;\r
goto Done;\r
}\r
- Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);\r
- } \r
+ }\r
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);\r
}\r
- DataSize -= CertList->SignatureListSize;\r
- CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);\r
}\r
+ DbtDataSize -= CertList->SignatureListSize;\r
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);\r
}\r
\r
Done:\r
- if (Data != NULL) {\r
- FreePool (Data);\r
+ if (DbtData != NULL) {\r
+ FreePool (DbtData);\r
}\r
\r
- if (VerifyStatus) {\r
- return EFI_SUCCESS;\r
- } else {\r
- return EFI_SECURITY_VIOLATION;\r
- }\r
+ return VerifyStatus;\r
}\r
\r
/**\r
- Verify certificate in WIN_CERTIFICATE_UEFI_GUID format. \r
+ Check whether the image signature is forbidden by the forbidden database (dbx).\r
+ The image is forbidden to load if any certificates for signing are revoked before signing time.\r
+\r
+ @param[in] AuthData Pointer to the Authenticode signature retrieved from the signed image.\r
+ @param[in] AuthDataSize Size of the Authenticode signature in bytes.\r
\r
- @retval EFI_SUCCESS Image pass verification.\r
- @retval EFI_SECURITY_VIOLATION Image fail verification.\r
- @retval other error value\r
+ @retval TRUE Image is forbidden by dbx.\r
+ @retval FALSE Image is not forbidden by dbx.\r
\r
**/\r
-EFI_STATUS \r
-VerifyCertUefiGuid (\r
- VOID\r
+BOOLEAN\r
+IsForbiddenByDbx (\r
+ IN UINT8 *AuthData,\r
+ IN UINTN AuthDataSize\r
)\r
{\r
- BOOLEAN Status;\r
- WIN_CERTIFICATE_UEFI_GUID *EfiCert;\r
- EFI_SIGNATURE_LIST *KekList;\r
- EFI_SIGNATURE_DATA *KekItem;\r
- EFI_CERT_BLOCK_RSA_2048_SHA256 *CertBlock;\r
- VOID *Rsa;\r
- UINTN KekCount;\r
- UINTN Index;\r
- UINTN KekDataSize;\r
- BOOLEAN IsFound;\r
- EFI_STATUS Result;\r
-\r
- EfiCert = NULL;\r
- KekList = NULL;\r
- KekItem = NULL;\r
- CertBlock = NULL;\r
- Rsa = NULL;\r
- Status = FALSE;\r
- IsFound = FALSE;\r
- KekDataSize = 0;\r
-\r
- EfiCert = (WIN_CERTIFICATE_UEFI_GUID *) (mImageBase + mSecDataDir->VirtualAddress);\r
- CertBlock = (EFI_CERT_BLOCK_RSA_2048_SHA256 *) EfiCert->CertData;\r
- if (!CompareGuid (&EfiCert->CertType, &gEfiCertTypeRsa2048Sha256Guid)) {\r
- //\r
- // Invalid Certificate Data Type.\r
- //\r
- return EFI_SECURITY_VIOLATION;\r
+ EFI_STATUS Status;\r
+ BOOLEAN IsForbidden;\r
+ BOOLEAN IsFound;\r
+ UINT8 *Data;\r
+ UINTN DataSize;\r
+ EFI_SIGNATURE_LIST *CertList;\r
+ UINTN CertListSize;\r
+ EFI_SIGNATURE_DATA *CertData;\r
+ UINT8 *RootCert;\r
+ UINTN RootCertSize;\r
+ UINTN CertCount;\r
+ UINTN Index;\r
+ UINT8 *CertBuffer;\r
+ UINTN BufferLength;\r
+ UINT8 *TrustedCert;\r
+ UINTN TrustedCertLength;\r
+ UINT8 CertNumber;\r
+ UINT8 *CertPtr;\r
+ UINT8 *Cert;\r
+ UINTN CertSize;\r
+ EFI_TIME RevocationTime;\r
+ //\r
+ // Variable Initialization\r
+ //\r
+ IsForbidden = TRUE;\r
+ Data = NULL;\r
+ CertList = NULL;\r
+ CertData = NULL;\r
+ RootCert = NULL;\r
+ RootCertSize = 0;\r
+ Cert = NULL;\r
+ CertBuffer = NULL;\r
+ BufferLength = 0;\r
+ TrustedCert = NULL;\r
+ TrustedCertLength = 0;\r
+\r
+ //\r
+ // The image will not be forbidden if dbx can't be got.\r
+ //\r
+ DataSize = 0;\r
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, NULL);\r
+ ASSERT (EFI_ERROR (Status));\r
+ if (Status != EFI_BUFFER_TOO_SMALL) {\r
+ if (Status == EFI_NOT_FOUND) {\r
+ //\r
+ // Evidently not in dbx if the database doesn't exist.\r
+ //\r
+ IsForbidden = FALSE;\r
+ }\r
+ return IsForbidden;\r
+ }\r
+ Data = (UINT8 *) AllocateZeroPool (DataSize);\r
+ if (Data == NULL) {\r
+ return IsForbidden;\r
+ }\r
+\r
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, (VOID *) Data);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
}\r
\r
//\r
- // Get KEK database variable data size\r
+ // Verify image signature with RAW X509 certificates in DBX database.\r
+ // If passed, the image will be forbidden.\r
//\r
- Result = gRT->GetVariable (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid, NULL, &KekDataSize, NULL);\r
- if (Result != EFI_BUFFER_TOO_SMALL) {\r
- return EFI_SECURITY_VIOLATION;\r
+ CertList = (EFI_SIGNATURE_LIST *) Data;\r
+ CertListSize = DataSize;\r
+ while ((CertListSize > 0) && (CertListSize >= CertList->SignatureListSize)) {\r
+ if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {\r
+ CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);\r
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;\r
+\r
+ for (Index = 0; Index < CertCount; Index++) {\r
+ //\r
+ // Iterate each Signature Data Node within this CertList for verify.\r
+ //\r
+ RootCert = CertData->SignatureData;\r
+ RootCertSize = CertList->SignatureSize - sizeof (EFI_GUID);\r
+\r
+ //\r
+ // Call AuthenticodeVerify library to Verify Authenticode struct.\r
+ //\r
+ IsForbidden = AuthenticodeVerify (\r
+ AuthData,\r
+ AuthDataSize,\r
+ RootCert,\r
+ RootCertSize,\r
+ mImageDigest,\r
+ mImageDigestSize\r
+ );\r
+ if (IsForbidden) {\r
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature is forbidden by DBX.\n"));\r
+ goto Done;\r
+ }\r
+\r
+ CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertData + CertList->SignatureSize);\r
+ }\r
+ }\r
+\r
+ CertListSize -= CertList->SignatureListSize;\r
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);\r
}\r
\r
//\r
- // Get KEK database variable.\r
+ // Check X.509 Certificate Hash & Possible Timestamp.\r
//\r
- KekList = GetEfiGlobalVariable (EFI_KEY_EXCHANGE_KEY_NAME);\r
- if (KekList == NULL) {\r
- return EFI_SECURITY_VIOLATION;\r
+\r
+ //\r
+ // Retrieve the certificate stack from AuthData\r
+ // The output CertStack format will be:\r
+ // UINT8 CertNumber;\r
+ // UINT32 Cert1Length;\r
+ // UINT8 Cert1[];\r
+ // UINT32 Cert2Length;\r
+ // UINT8 Cert2[];\r
+ // ...\r
+ // UINT32 CertnLength;\r
+ // UINT8 Certn[];\r
+ //\r
+ Pkcs7GetSigners (AuthData, AuthDataSize, &CertBuffer, &BufferLength, &TrustedCert, &TrustedCertLength);\r
+ if ((BufferLength == 0) || (CertBuffer == NULL) || (*CertBuffer) == 0) {\r
+ IsForbidden = TRUE;\r
+ goto Done;\r
}\r
- \r
+\r
//\r
- // Enumerate all Kek items in this list to verify the variable certificate data.\r
- // If anyone is authenticated successfully, it means the variable is correct!\r
+ // Check if any hash of certificates embedded in AuthData is in the forbidden database.\r
//\r
- while ((KekDataSize > 0) && (KekDataSize >= KekList->SignatureListSize)) {\r
- if (CompareGuid (&KekList->SignatureType, &gEfiCertRsa2048Guid)) {\r
- KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekList + sizeof (EFI_SIGNATURE_LIST) + KekList->SignatureHeaderSize);\r
- KekCount = (KekList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - KekList->SignatureHeaderSize) / KekList->SignatureSize;\r
- for (Index = 0; Index < KekCount; Index++) {\r
- if (CompareMem (KekItem->SignatureData, CertBlock->PublicKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) {\r
- IsFound = TRUE;\r
- break;\r
- }\r
- KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekItem + KekList->SignatureSize);\r
+ CertNumber = (UINT8) (*CertBuffer);\r
+ CertPtr = CertBuffer + 1;\r
+ for (Index = 0; Index < CertNumber; Index++) {\r
+ CertSize = (UINTN) ReadUnaligned32 ((UINT32 *)CertPtr);\r
+ Cert = (UINT8 *)CertPtr + sizeof (UINT32);\r
+ //\r
+ // Advance CertPtr to the next cert in image signer's cert list\r
+ //\r
+ CertPtr = CertPtr + sizeof (UINT32) + CertSize;\r
+\r
+ Status = IsCertHashFoundInDbx (Cert, CertSize, (EFI_SIGNATURE_LIST *)Data, DataSize, &RevocationTime, &IsFound);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Error in searching dbx. Consider it as 'found'. RevocationTime might\r
+ // not be valid in such situation.\r
+ //\r
+ IsForbidden = TRUE;\r
+ } else if (IsFound) {\r
+ //\r
+ // Found Cert in dbx successfully. Check the timestamp signature and\r
+ // signing time to determine if the image can be trusted.\r
+ //\r
+ if (PassTimestampCheck (AuthData, AuthDataSize, &RevocationTime)) {\r
+ IsForbidden = FALSE;\r
+ //\r
+ // Pass DBT check. Continue to check other certs in image signer's cert list against DBX, DBT\r
+ //\r
+ continue;\r
+ } else {\r
+ IsForbidden = TRUE;\r
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature failed the timestamp check.\n"));\r
+ goto Done;\r
}\r
}\r
- KekDataSize -= KekList->SignatureListSize;\r
- KekList = (EFI_SIGNATURE_LIST *) ((UINT8 *) KekList + KekList->SignatureListSize);\r
+\r
}\r
- \r
- if (!IsFound) {\r
- //\r
- // Signed key is not a trust one.\r
- //\r
- goto Done;\r
+\r
+ IsForbidden = FALSE;\r
+\r
+Done:\r
+ if (Data != NULL) {\r
+ FreePool (Data);\r
}\r
\r
+ Pkcs7FreeSigners (CertBuffer);\r
+ Pkcs7FreeSigners (TrustedCert);\r
+\r
+ return IsForbidden;\r
+}\r
+\r
+\r
+/**\r
+ Check whether the image signature can be verified by the trusted certificates in DB database.\r
+\r
+ @param[in] AuthData Pointer to the Authenticode signature retrieved from signed image.\r
+ @param[in] AuthDataSize Size of the Authenticode signature in bytes.\r
+\r
+ @retval TRUE Image passed verification using certificate in db.\r
+ @retval FALSE Image didn't pass verification using certificate in db.\r
+\r
+**/\r
+BOOLEAN\r
+IsAllowedByDb (\r
+ IN UINT8 *AuthData,\r
+ IN UINTN AuthDataSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ BOOLEAN VerifyStatus;\r
+ BOOLEAN IsFound;\r
+ EFI_SIGNATURE_LIST *CertList;\r
+ EFI_SIGNATURE_DATA *CertData;\r
+ UINTN DataSize;\r
+ UINT8 *Data;\r
+ UINT8 *RootCert;\r
+ UINTN RootCertSize;\r
+ UINTN Index;\r
+ UINTN CertCount;\r
+ UINTN DbxDataSize;\r
+ UINT8 *DbxData;\r
+ EFI_TIME RevocationTime;\r
+\r
+ Data = NULL;\r
+ CertList = NULL;\r
+ CertData = NULL;\r
+ RootCert = NULL;\r
+ DbxData = NULL;\r
+ RootCertSize = 0;\r
+ VerifyStatus = FALSE;\r
+\r
//\r
- // Now, we found the corresponding security policy.\r
- // Verify the data payload.\r
- //\r
- Rsa = RsaNew ();\r
- ASSERT (Rsa != NULL);\r
- // \r
- // Set RSA Key Components.\r
- // NOTE: Only N and E are needed to be set as RSA public key for signature verification.\r
+ // Fetch 'db' content. If 'db' doesn't exist or encounters problem to get the\r
+ // data, return not-allowed-by-db (FALSE).\r
//\r
- Status = RsaSetKey (Rsa, RsaKeyN, CertBlock->PublicKey, EFI_CERT_TYPE_RSA2048_SIZE);\r
- if (!Status) {\r
- goto Done;\r
+ DataSize = 0;\r
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, NULL);\r
+ ASSERT (EFI_ERROR (Status));\r
+ if (Status != EFI_BUFFER_TOO_SMALL) {\r
+ return VerifyStatus;\r
}\r
- Status = RsaSetKey (Rsa, RsaKeyE, mRsaE, sizeof (mRsaE));\r
- if (!Status) {\r
+\r
+ Data = (UINT8 *) AllocateZeroPool (DataSize);\r
+ if (Data == NULL) {\r
+ return VerifyStatus;\r
+ }\r
+\r
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, (VOID *) Data);\r
+ if (EFI_ERROR (Status)) {\r
goto Done;\r
}\r
+\r
+ //\r
+ // Fetch 'dbx' content. If 'dbx' doesn't exist, continue to check 'db'.\r
+ // If any other errors occured, no need to check 'db' but just return\r
+ // not-allowed-by-db (FALSE) to avoid bypass.\r
+ //\r
+ DbxDataSize = 0;\r
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DbxDataSize, NULL);\r
+ ASSERT (EFI_ERROR (Status));\r
+ if (Status != EFI_BUFFER_TOO_SMALL) {\r
+ if (Status != EFI_NOT_FOUND) {\r
+ goto Done;\r
+ }\r
+ //\r
+ // 'dbx' does not exist. Continue to check 'db'.\r
+ //\r
+ } else {\r
+ //\r
+ // 'dbx' exists. Get its content.\r
+ //\r
+ DbxData = (UINT8 *) AllocateZeroPool (DbxDataSize);\r
+ if (DbxData == NULL) {\r
+ goto Done;\r
+ }\r
+\r
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DbxDataSize, (VOID *) DbxData);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ }\r
+\r
//\r
- // Verify the signature.\r
+ // Find X509 certificate in Signature List to verify the signature in pkcs7 signed data.\r
//\r
- Status = RsaPkcs1Verify (\r
- Rsa, \r
- mImageDigest, \r
- mImageDigestSize, \r
- CertBlock->Signature, \r
- EFI_CERT_TYPE_RSA2048_SHA256_SIZE\r
- );\r
- \r
+ CertList = (EFI_SIGNATURE_LIST *) Data;\r
+ while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) {\r
+ if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {\r
+ CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);\r
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;\r
+\r
+ for (Index = 0; Index < CertCount; Index++) {\r
+ //\r
+ // Iterate each Signature Data Node within this CertList for verify.\r
+ //\r
+ RootCert = CertData->SignatureData;\r
+ RootCertSize = CertList->SignatureSize - sizeof (EFI_GUID);\r
+\r
+ //\r
+ // Call AuthenticodeVerify library to Verify Authenticode struct.\r
+ //\r
+ VerifyStatus = AuthenticodeVerify (\r
+ AuthData,\r
+ AuthDataSize,\r
+ RootCert,\r
+ RootCertSize,\r
+ mImageDigest,\r
+ mImageDigestSize\r
+ );\r
+ if (VerifyStatus) {\r
+ //\r
+ // The image is signed and its signature is found in 'db'.\r
+ //\r
+ if (DbxData != NULL) {\r
+ //\r
+ // Here We still need to check if this RootCert's Hash is revoked\r
+ //\r
+ Status = IsCertHashFoundInDbx (RootCert, RootCertSize, (EFI_SIGNATURE_LIST *)DbxData, DbxDataSize, &RevocationTime, &IsFound);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Error in searching dbx. Consider it as 'found'. RevocationTime might\r
+ // not be valid in such situation.\r
+ //\r
+ VerifyStatus = FALSE;\r
+ } else if (IsFound) {\r
+ //\r
+ // Check the timestamp signature and signing time to determine if the RootCert can be trusted.\r
+ //\r
+ VerifyStatus = PassTimestampCheck (AuthData, AuthDataSize, &RevocationTime);\r
+ if (!VerifyStatus) {\r
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed and signature is accepted by DB, but its root cert failed the timestamp check.\n"));\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // There's no 'dbx' to check revocation time against (must-be pass),\r
+ // or, there's revocation time found in 'dbx' and checked againt 'dbt'\r
+ // (maybe pass or fail, depending on timestamp compare result). Either\r
+ // way the verification job has been completed at this point.\r
+ //\r
+ goto Done;\r
+ }\r
+\r
+ CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertData + CertList->SignatureSize);\r
+ }\r
+ }\r
+\r
+ DataSize -= CertList->SignatureListSize;\r
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);\r
+ }\r
+\r
Done:\r
- if (KekList != NULL) {\r
- FreePool (KekList);\r
+\r
+ if (VerifyStatus) {\r
+ SecureBootHook (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, CertList->SignatureSize, CertData);\r
}\r
- if (Rsa != NULL ) {\r
- RsaFree (Rsa);\r
+\r
+ if (Data != NULL) {\r
+ FreePool (Data);\r
}\r
- if (Status) {\r
- return EFI_SUCCESS;\r
- } else {\r
- return EFI_SECURITY_VIOLATION;\r
+ if (DbxData != NULL) {\r
+ FreePool (DbxData);\r
}\r
+\r
+ return VerifyStatus;\r
}\r
\r
/**\r
Provide verification service for signed images, which include both signature validation\r
- and platform policy control. For signature types, both UEFI WIN_CERTIFICATE_UEFI_GUID and \r
+ and platform policy control. For signature types, both UEFI WIN_CERTIFICATE_UEFI_GUID and\r
MSFT Authenticode type signatures are supported.\r
- \r
- In this implementation, only verify external executables when in USER MODE.\r
- Executables from FV is bypass, so pass in AuthenticationStatus is ignored. \r
\r
- @param[in] AuthenticationStatus \r
+ In this implementation, only verify external executables when in USER MODE.\r
+ Executables from FV is bypass, so pass in AuthenticationStatus is ignored.\r
+\r
+ The image verification policy is:\r
+ If the image is signed,\r
+ At least one valid signature or at least one hash value of the image must match a record\r
+ in the security database "db", and no valid signature nor any hash value of the image may\r
+ be reflected in the security database "dbx".\r
+ Otherwise, the image is not signed,\r
+ The SHA256 hash value of the image must match a record in the security database "db", and\r
+ not be reflected in the security data base "dbx".\r
+\r
+ Caution: This function may receive untrusted input.\r
+ PE/COFF image is external input, so this function will validate its data structure\r
+ within this image buffer before use.\r
+\r
+ @param[in] AuthenticationStatus\r
This is the authentication status returned from the security\r
measurement services for the input file.\r
@param[in] File This is a pointer to the device path of the file that is\r
being dispatched. This will optionally be used for logging.\r
@param[in] FileBuffer File buffer matches the input file device path.\r
@param[in] FileSize Size of File buffer matches the input file device path.\r
-\r
- @retval EFI_SUCCESS The file specified by File did authenticate, and the\r
- platform policy dictates that the DXE Core may use File.\r
- @retval EFI_INVALID_PARAMETER File is NULL.\r
+ @param[in] BootPolicy A boot policy that was used to call LoadImage() UEFI service.\r
+\r
+ @retval EFI_SUCCESS The file specified by DevicePath and non-NULL\r
+ FileBuffer did authenticate, and the platform policy dictates\r
+ that the DXE Foundation may use the file.\r
+ @retval EFI_SUCCESS The device path specified by NULL device path DevicePath\r
+ and non-NULL FileBuffer did authenticate, and the platform\r
+ policy dictates that the DXE Foundation may execute the image in\r
+ FileBuffer.\r
@retval EFI_SECURITY_VIOLATION The file specified by File did not authenticate, and\r
the platform policy dictates that File should be placed\r
- in the untrusted state. A file may be promoted from\r
- the untrusted to the trusted state at a future time\r
- with a call to the Trust() DXE Service.\r
- @retval EFI_ACCESS_DENIED The file specified by File did not authenticate, and\r
- the platform policy dictates that File should not be\r
- used for any purpose.\r
+ in the untrusted state. The image has been added to the file\r
+ execution table.\r
+ @retval EFI_ACCESS_DENIED The file specified by File and FileBuffer did not\r
+ authenticate, and the platform policy dictates that the DXE\r
+ Foundation may not use File. The image has\r
+ been added to the file execution table.\r
\r
**/\r
EFI_STATUS\r
EFIAPI\r
DxeImageVerificationHandler (\r
IN UINT32 AuthenticationStatus,\r
- IN CONST EFI_DEVICE_PATH_PROTOCOL *File,\r
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *File, OPTIONAL\r
IN VOID *FileBuffer,\r
- IN UINTN FileSize\r
+ IN UINTN FileSize,\r
+ IN BOOLEAN BootPolicy\r
)\r
-\r
{\r
- EFI_STATUS Status;\r
- UINT16 Magic;\r
- EFI_IMAGE_DOS_HEADER *DosHdr;\r
- EFI_STATUS VerifyStatus;\r
- UINT8 *SetupMode;\r
- EFI_SIGNATURE_LIST *SignatureList;\r
- UINTN SignatureListSize;\r
- EFI_SIGNATURE_DATA *Signature;\r
- EFI_IMAGE_EXECUTION_ACTION Action;\r
- WIN_CERTIFICATE *WinCertificate;\r
- UINT32 Policy;\r
-\r
- if (File == NULL) {\r
- return EFI_INVALID_PARAMETER;\r
- }\r
+ EFI_IMAGE_DOS_HEADER *DosHdr;\r
+ BOOLEAN IsVerified;\r
+ EFI_SIGNATURE_LIST *SignatureList;\r
+ UINTN SignatureListSize;\r
+ EFI_SIGNATURE_DATA *Signature;\r
+ EFI_IMAGE_EXECUTION_ACTION Action;\r
+ WIN_CERTIFICATE *WinCertificate;\r
+ UINT32 Policy;\r
+ UINT8 *SecureBoot;\r
+ PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;\r
+ UINT32 NumberOfRvaAndSizes;\r
+ WIN_CERTIFICATE_EFI_PKCS *PkcsCertData;\r
+ WIN_CERTIFICATE_UEFI_GUID *WinCertUefiGuid;\r
+ UINT8 *AuthData;\r
+ UINTN AuthDataSize;\r
+ EFI_IMAGE_DATA_DIRECTORY *SecDataDir;\r
+ UINT32 OffSet;\r
+ CHAR16 *NameStr;\r
+ RETURN_STATUS PeCoffStatus;\r
+ EFI_STATUS HashStatus;\r
+ EFI_STATUS DbStatus;\r
+ BOOLEAN IsFound;\r
\r
SignatureList = NULL;\r
SignatureListSize = 0;\r
WinCertificate = NULL;\r
+ SecDataDir = NULL;\r
+ PkcsCertData = NULL;\r
Action = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;\r
- Status = EFI_ACCESS_DENIED;\r
+ IsVerified = FALSE;\r
+ IsFound = FALSE;\r
+\r
//\r
// Check the image type and get policy setting.\r
//\r
switch (GetImageType (File)) {\r
- \r
+\r
case IMAGE_FROM_FV:\r
Policy = ALWAYS_EXECUTE;\r
break;\r
break;\r
\r
default:\r
- Policy = DENY_EXECUTE_ON_SECURITY_VIOLATION; \r
+ Policy = DENY_EXECUTE_ON_SECURITY_VIOLATION;\r
break;\r
}\r
//\r
//\r
if (Policy == ALWAYS_EXECUTE) {\r
return EFI_SUCCESS;\r
- } else if (Policy == NEVER_EXECUTE) {\r
+ }\r
+ if (Policy == NEVER_EXECUTE) {\r
return EFI_ACCESS_DENIED;\r
}\r
- SetupMode = GetEfiGlobalVariable (EFI_SETUP_MODE_NAME);\r
\r
//\r
- // SetupMode doesn't exist means no AuthVar driver is dispatched,\r
- // skip verification.\r
+ // The policy QUERY_USER_ON_SECURITY_VIOLATION and ALLOW_EXECUTE_ON_SECURITY_VIOLATION\r
+ // violates the UEFI spec and has been removed.\r
+ //\r
+ ASSERT (Policy != QUERY_USER_ON_SECURITY_VIOLATION && Policy != ALLOW_EXECUTE_ON_SECURITY_VIOLATION);\r
+ if (Policy == QUERY_USER_ON_SECURITY_VIOLATION || Policy == ALLOW_EXECUTE_ON_SECURITY_VIOLATION) {\r
+ CpuDeadLoop ();\r
+ }\r
+\r
+ GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME, (VOID**)&SecureBoot, NULL);\r
+ //\r
+ // Skip verification if SecureBoot variable doesn't exist.\r
//\r
- if (SetupMode == NULL) {\r
+ if (SecureBoot == NULL) {\r
return EFI_SUCCESS;\r
}\r
\r
//\r
- // If platform is in SETUP MODE, skip verification.\r
+ // Skip verification if SecureBoot is disabled but not AuditMode\r
//\r
- if (*SetupMode == SETUP_MODE) {\r
- FreePool (SetupMode);\r
+ if (*SecureBoot == SECURE_BOOT_MODE_DISABLE) {\r
+ FreePool (SecureBoot);\r
return EFI_SUCCESS;\r
}\r
+ FreePool (SecureBoot);\r
+\r
//\r
// Read the Dos header.\r
//\r
- ASSERT (FileBuffer != NULL);\r
+ if (FileBuffer == NULL) {\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
mImageBase = (UINT8 *) FileBuffer;\r
mImageSize = FileSize;\r
- DosHdr = (EFI_IMAGE_DOS_HEADER *) (mImageBase);\r
+\r
+ ZeroMem (&ImageContext, sizeof (ImageContext));\r
+ ImageContext.Handle = (VOID *) FileBuffer;\r
+ ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE) DxeImageVerificationLibImageRead;\r
+\r
+ //\r
+ // Get information about the image being loaded\r
+ //\r
+ PeCoffStatus = PeCoffLoaderGetImageInfo (&ImageContext);\r
+ if (RETURN_ERROR (PeCoffStatus)) {\r
+ //\r
+ // The information can't be got from the invalid PeImage\r
+ //\r
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: PeImage invalid. Cannot retrieve image information.\n"));\r
+ goto Failed;\r
+ }\r
+\r
+ DosHdr = (EFI_IMAGE_DOS_HEADER *) mImageBase;\r
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {\r
//\r
- // DOS image header is present, \r
+ // DOS image header is present,\r
// so read the PE header after the DOS image header.\r
//\r
mPeCoffHeaderOffset = DosHdr->e_lfanew;\r
//\r
// It is not a valid Pe/Coff file.\r
//\r
- return EFI_ACCESS_DENIED;\r
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Not a valid PE/COFF image.\n"));\r
+ goto Failed;\r
}\r
\r
- Magic = mNtHeader.Pe32->OptionalHeader.Magic;\r
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
//\r
// Use PE32 offset.\r
//\r
- mSecDataDir = (EFI_IMAGE_DATA_DIRECTORY *)&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];\r
+ NumberOfRvaAndSizes = mNtHeader.Pe32->OptionalHeader.NumberOfRvaAndSizes;\r
+ if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {\r
+ SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];\r
+ }\r
} else {\r
//\r
// Use PE32+ offset.\r
//\r
- mSecDataDir = (EFI_IMAGE_DATA_DIRECTORY *)&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];\r
+ NumberOfRvaAndSizes = mNtHeader.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;\r
+ if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {\r
+ SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];\r
+ }\r
}\r
\r
- if (mSecDataDir->Size == 0) {\r
- //\r
- // This image is not signed.\r
- //\r
- Action = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;\r
- Status = EFI_ACCESS_DENIED; \r
- goto Done; \r
- }\r
//\r
- // Verify signature of executables.\r
+ // Start Image Validation.\r
//\r
- WinCertificate = (WIN_CERTIFICATE *) (mImageBase + mSecDataDir->VirtualAddress);\r
-\r
- switch (WinCertificate->wCertificateType) {\r
- \r
- case WIN_CERT_TYPE_EFI_GUID:\r
+ if (SecDataDir == NULL || SecDataDir->Size == 0) {\r
+ //\r
+ // This image is not signed. The SHA256 hash value of the image must match a record in the security database "db",\r
+ // and not be reflected in the security data base "dbx".\r
//\r
- // Verify UEFI GUID type.\r
- // \r
if (!HashPeImage (HASHALG_SHA256)) {\r
- goto Done;\r
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Failed to hash this image using %s.\n", mHashTypeStr));\r
+ goto Failed;\r
}\r
\r
- VerifyStatus = VerifyCertUefiGuid ();\r
- break;\r
+ DbStatus = IsSignatureFoundInDatabase (\r
+ EFI_IMAGE_SECURITY_DATABASE1,\r
+ mImageDigest,\r
+ &mCertType,\r
+ mImageDigestSize,\r
+ &IsFound\r
+ );\r
+ if (EFI_ERROR (DbStatus) || IsFound) {\r
+ //\r
+ // Image Hash is in forbidden database (DBX).\r
+ //\r
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is not signed and %s hash of image is forbidden by DBX.\n", mHashTypeStr));\r
+ goto Failed;\r
+ }\r
+\r
+ DbStatus = IsSignatureFoundInDatabase (\r
+ EFI_IMAGE_SECURITY_DATABASE,\r
+ mImageDigest,\r
+ &mCertType,\r
+ mImageDigestSize,\r
+ &IsFound\r
+ );\r
+ if (!EFI_ERROR (DbStatus) && IsFound) {\r
+ //\r
+ // Image Hash is in allowed database (DB).\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
\r
- case WIN_CERT_TYPE_PKCS_SIGNED_DATA:\r
//\r
- // Verify Pkcs signed data type.\r
+ // Image Hash is not found in both forbidden and allowed database.\r
//\r
- Status = HashPeImageByType();\r
- if (EFI_ERROR(Status)) {\r
- goto Done;\r
- }\r
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is not signed and %s hash of image is not found in DB/DBX.\n", mHashTypeStr));\r
+ goto Failed;\r
+ }\r
\r
- VerifyStatus = VerifyCertPkcsSignedData ();\r
+ //\r
+ // Verify the signature of the image, multiple signatures are allowed as per PE/COFF Section 4.7\r
+ // "Attribute Certificate Table".\r
+ // The first certificate starts at offset (SecDataDir->VirtualAddress) from the start of the file.\r
+ //\r
+ for (OffSet = SecDataDir->VirtualAddress;\r
+ OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);\r
+ OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {\r
+ WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);\r
+ if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof (WIN_CERTIFICATE) ||\r
+ (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength) {\r
+ break;\r
+ }\r
\r
//\r
- // For image verification against enrolled certificate(root or intermediate),\r
- // no need to check image's hash in the allowed database.\r
+ // Verify the image's Authenticode signature, only DER-encoded PKCS#7 signed data is supported.\r
//\r
- if (!EFI_ERROR (VerifyStatus)) {\r
- return EFI_SUCCESS;\r
+ if (WinCertificate->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) {\r
+ //\r
+ // The certificate is formatted as WIN_CERTIFICATE_EFI_PKCS which is described in the\r
+ // Authenticode specification.\r
+ //\r
+ PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) WinCertificate;\r
+ if (PkcsCertData->Hdr.dwLength <= sizeof (PkcsCertData->Hdr)) {\r
+ break;\r
+ }\r
+ AuthData = PkcsCertData->CertData;\r
+ AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr);\r
+ } else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID) {\r
+ //\r
+ // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which is described in UEFI Spec.\r
+ //\r
+ WinCertUefiGuid = (WIN_CERTIFICATE_UEFI_GUID *) WinCertificate;\r
+ if (WinCertUefiGuid->Hdr.dwLength <= OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {\r
+ break;\r
+ }\r
+ if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {\r
+ continue;\r
+ }\r
+ AuthData = WinCertUefiGuid->CertData;\r
+ AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);\r
+ } else {\r
+ if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {\r
+ break;\r
+ }\r
+ continue;\r
+ }\r
+\r
+ HashStatus = HashPeImageByType (AuthData, AuthDataSize);\r
+ if (EFI_ERROR (HashStatus)) {\r
+ continue;\r
}\r
\r
- default:\r
- return EFI_ACCESS_DENIED;\r
- }\r
- //\r
- // Get image hash value as executable's signature.\r
- //\r
- SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + mImageDigestSize;\r
- SignatureList = (EFI_SIGNATURE_LIST *) AllocateZeroPool (SignatureListSize);\r
- ASSERT (SignatureList != NULL);\r
- SignatureList->SignatureHeaderSize = 0;\r
- SignatureList->SignatureListSize = (UINT32) SignatureListSize;\r
- SignatureList->SignatureSize = (UINT32) mImageDigestSize;\r
- CopyMem (&SignatureList->SignatureType, &mCertType, sizeof (EFI_GUID));\r
- Signature = (EFI_SIGNATURE_DATA *) ((UINT8 *) SignatureList + sizeof (EFI_SIGNATURE_LIST));\r
- CopyMem (Signature->SignatureData, mImageDigest, mImageDigestSize);\r
- //\r
- // Signature database check after verification.\r
- //\r
- if (EFI_ERROR (VerifyStatus)) {\r
//\r
- // Verification failure.\r
+ // Check the digital signature against the revoked certificate in forbidden database (dbx).\r
//\r
- Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;\r
- Status = EFI_ACCESS_DENIED;\r
- } else if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, Signature->SignatureData, &mCertType, mImageDigestSize)) {\r
+ if (IsForbiddenByDbx (AuthData, AuthDataSize)) {\r
+ Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;\r
+ IsVerified = FALSE;\r
+ break;\r
+ }\r
+\r
//\r
- // Executable signature verification passes, but is found in forbidden signature database.\r
+ // Check the digital signature against the valid certificate in allowed database (db).\r
//\r
- Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;\r
- Status = EFI_ACCESS_DENIED;\r
- } else if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, Signature->SignatureData, &mCertType, mImageDigestSize)) {\r
+ if (!IsVerified) {\r
+ if (IsAllowedByDb (AuthData, AuthDataSize)) {\r
+ IsVerified = TRUE;\r
+ }\r
+ }\r
+\r
//\r
- // Executable signature is found in authorized signature database.\r
+ // Check the image's hash value.\r
//\r
- Status = EFI_SUCCESS;\r
- } else {\r
+ DbStatus = IsSignatureFoundInDatabase (\r
+ EFI_IMAGE_SECURITY_DATABASE1,\r
+ mImageDigest,\r
+ &mCertType,\r
+ mImageDigestSize,\r
+ &IsFound\r
+ );\r
+ if (EFI_ERROR (DbStatus) || IsFound) {\r
+ Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;\r
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but %s hash of image is found in DBX.\n", mHashTypeStr));\r
+ IsVerified = FALSE;\r
+ break;\r
+ }\r
+\r
+ if (!IsVerified) {\r
+ DbStatus = IsSignatureFoundInDatabase (\r
+ EFI_IMAGE_SECURITY_DATABASE,\r
+ mImageDigest,\r
+ &mCertType,\r
+ mImageDigestSize,\r
+ &IsFound\r
+ );\r
+ if (!EFI_ERROR (DbStatus) && IsFound) {\r
+ IsVerified = TRUE;\r
+ } else {\r
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n", mHashTypeStr));\r
+ }\r
+ }\r
+ }\r
+\r
+ if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {\r
//\r
- // Executable signature verification passes, but cannot be found in authorized signature database.\r
- // Get platform policy to determine the action.\r
+ // The Size in Certificate Table or the attribute certificate table is corrupted.\r
//\r
- Action = EFI_IMAGE_EXECUTION_AUTH_SIG_PASSED;\r
- Status = ImageAuthorization (Policy);\r
+ IsVerified = FALSE;\r
}\r
\r
-Done:\r
- if (Status != EFI_SUCCESS) {\r
+ if (IsVerified) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ if (Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED || Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND) {\r
//\r
- // Policy decides to defer or reject the image; add its information in image executable information table.\r
+ // Get image hash value as signature of executable.\r
//\r
- AddImageExeInfo (Action, NULL, File, SignatureList, SignatureListSize);\r
+ SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + mImageDigestSize;\r
+ SignatureList = (EFI_SIGNATURE_LIST *) AllocateZeroPool (SignatureListSize);\r
+ if (SignatureList == NULL) {\r
+ SignatureListSize = 0;\r
+ goto Failed;\r
+ }\r
+ SignatureList->SignatureHeaderSize = 0;\r
+ SignatureList->SignatureListSize = (UINT32) SignatureListSize;\r
+ SignatureList->SignatureSize = (UINT32) (sizeof (EFI_SIGNATURE_DATA) - 1 + mImageDigestSize);\r
+ CopyMem (&SignatureList->SignatureType, &mCertType, sizeof (EFI_GUID));\r
+ Signature = (EFI_SIGNATURE_DATA *) ((UINT8 *) SignatureList + sizeof (EFI_SIGNATURE_LIST));\r
+ CopyMem (Signature->SignatureData, mImageDigest, mImageDigestSize);\r
+ }\r
+\r
+Failed:\r
+ //\r
+ // Policy decides to defer or reject the image; add its information in image\r
+ // executable information table in either case.\r
+ //\r
+ NameStr = ConvertDevicePathToText (File, FALSE, TRUE);\r
+ AddImageExeInfo (Action, NameStr, File, SignatureList, SignatureListSize);\r
+ if (NameStr != NULL) {\r
+ DEBUG ((DEBUG_INFO, "The image doesn't pass verification: %s\n", NameStr));\r
+ FreePool(NameStr);\r
}\r
\r
if (SignatureList != NULL) {\r
FreePool (SignatureList);\r
}\r
\r
- FreePool (SetupMode);\r
-\r
- return Status;\r
+ if (Policy == DEFER_EXECUTE_ON_SECURITY_VIOLATION) {\r
+ return EFI_SECURITY_VIOLATION;\r
+ }\r
+ return EFI_ACCESS_DENIED;\r
}\r
\r
/**\r
- When VariableWriteArchProtocol install, create "SecureBoot" variable.\r
- \r
- @param[in] Event Event whose notification function is being invoked.\r
- @param[in] Context Pointer to the notification function's context.\r
- \r
+ On Ready To Boot Services Event notification handler.\r
+\r
+ Add the image execution information table if it is not in system configuration table.\r
+\r
+ @param[in] Event Event whose notification function is being invoked\r
+ @param[in] Context Pointer to the notification function's context\r
+\r
**/\r
VOID\r
EFIAPI\r
-VariableWriteCallBack (\r
- IN EFI_EVENT Event,\r
- IN VOID *Context\r
+OnReadyToBoot (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
)\r
{\r
- UINT8 SecureBootMode;\r
- UINT8 *SecureBootModePtr;\r
- EFI_STATUS Status;\r
- VOID *ProtocolPointer;\r
+ EFI_IMAGE_EXECUTION_INFO_TABLE *ImageExeInfoTable;\r
+ UINTN ImageExeInfoTableSize;\r
\r
- Status = gBS->LocateProtocol (&gEfiVariableWriteArchProtocolGuid, NULL, &ProtocolPointer);\r
- if (EFI_ERROR (Status)) {\r
+ EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID **) &ImageExeInfoTable);\r
+ if (ImageExeInfoTable != NULL) {\r
return;\r
}\r
- \r
- //\r
- // Check whether "SecureBoot" variable exists.\r
- // If this library is built-in, it means firmware has capability to perform\r
- // driver signing verification.\r
- //\r
- SecureBootModePtr = GetEfiGlobalVariable (EFI_SECURE_BOOT_MODE_NAME);\r
- if (SecureBootModePtr == NULL) {\r
- SecureBootMode = SECURE_BOOT_MODE_DISABLE;\r
- //\r
- // Authenticated variable driver will update "SecureBoot" depending on SetupMode variable.\r
- //\r
- gRT->SetVariable (\r
- EFI_SECURE_BOOT_MODE_NAME,\r
- &gEfiGlobalVariableGuid,\r
- EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
- sizeof (UINT8),\r
- &SecureBootMode\r
- );\r
- } else {\r
- FreePool (SecureBootModePtr);\r
+\r
+ ImageExeInfoTableSize = sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE);\r
+ ImageExeInfoTable = (EFI_IMAGE_EXECUTION_INFO_TABLE *) AllocateRuntimePool (ImageExeInfoTableSize);\r
+ if (ImageExeInfoTable == NULL) {\r
+ return ;\r
}\r
-} \r
+\r
+ ImageExeInfoTable->NumberOfImages = 0;\r
+ gBS->InstallConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID *) ImageExeInfoTable);\r
+\r
+}\r
\r
/**\r
Register security measurement handler.\r
IN EFI_SYSTEM_TABLE *SystemTable\r
)\r
{\r
- VOID *Registration;\r
+ EFI_EVENT Event;\r
\r
//\r
- // Register callback function upon VariableWriteArchProtocol.\r
- // \r
- EfiCreateProtocolNotifyEvent (\r
- &gEfiVariableWriteArchProtocolGuid,\r
+ // Register the event to publish the image execution table.\r
+ //\r
+ EfiCreateEventReadyToBootEx (\r
TPL_CALLBACK,\r
- VariableWriteCallBack,\r
+ OnReadyToBoot,\r
NULL,\r
- &Registration\r
+ &Event\r
);\r
\r
- return RegisterSecurityHandler (\r
+ return RegisterSecurity2Handler (\r
DxeImageVerificationHandler,\r
EFI_AUTH_OPERATION_VERIFY_IMAGE | EFI_AUTH_OPERATION_IMAGE_REQUIRED\r
- ); \r
+ );\r
}\r