/** @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 - 2012, 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
-UINTN mImageSize;\r
UINT32 mPeCoffHeaderOffset;\r
+EFI_GUID mCertType;\r
+\r
+//\r
+// Information on current PE/COFF image\r
+//\r
+UINTN mImageSize;\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
+ @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
+ @retval EFI_SUCCESS The specified portion of the PE/COFF image was read and the size\r
**/\r
EFI_STATUS\r
EFIAPI\r
UINTN EndPosition;\r
\r
if (FileHandle == NULL || ReadSize == NULL || Buffer == NULL) {\r
- return EFI_INVALID_PARAMETER; \r
+ return EFI_INVALID_PARAMETER;\r
}\r
\r
if (MAX_ADDRESS - FileOffset < *ReadSize) {\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
}\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
@retval TRUE Successfully hash image.\r
)\r
{\r
BOOLEAN Status;\r
- UINT16 Magic;\r
EFI_IMAGE_SECTION_HEADER *Section;\r
VOID *HashCtx;\r
UINTN CtxSize;\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
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
HashCtx = AllocatePool (CtxSize);\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
+ HashSize = (UINTN) (&mNtHeader.Pe32->OptionalHeader.CheckSum) - (UINTN) HashBase;\r
NumberOfRvaAndSizes = mNtHeader.Pe32->OptionalHeader.NumberOfRvaAndSizes;\r
- } else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {\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
// 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
- 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
HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);\r
- HashSize = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - mImageBase);\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 - mImageBase);\r
+ HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - ((UINTN) HashBase - (UINTN) mImageBase);\r
}\r
\r
if (HashSize != 0) {\r
//\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
+ 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) ((UINT8 *) (&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase);\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) ((UINT8 *) (&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase);\r
+ HashSize = (UINTN) (&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN) HashBase;\r
}\r
\r
if (HashSize != 0) {\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 (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 - mImageBase);\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 - mImageBase);\r
+ HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - ((UINTN) HashBase - (UINTN) mImageBase);\r
}\r
\r
if (HashSize != 0) {\r
if (!Status) {\r
goto Done;\r
}\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
if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {\r
CertSize = 0;\r
} else {\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
}\r
\r
/**\r
- Recognize the Hash algorithm in PE/COFF Authenticode and caculate hash of\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
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
- if (PkcsCertData->Hdr.dwLength < sizeof (WIN_CERTIFICATE_EFI_PKCS) + 32) {\r
- return EFI_UNSUPPORTED;\r
- }\r
\r
for (Index = 0; Index < HASHALG_MAX; Index++) {\r
//\r
// This field has the fixed offset (+32) in final Authenticode ASN.1 data.\r
// Fixed offset (+32) is calculated based on two bytes of length encoding.\r
//\r
- if ((*(PkcsCertData->CertData + 1) & TWO_BYTE_ENCODE) != TWO_BYTE_ENCODE) {\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 (PkcsCertData->Hdr.dwLength < sizeof (WIN_CERTIFICATE_EFI_PKCS) + 32 + mHash[Index].OidLength) {\r
+ if (AuthDataSize < 32 + mHash[Index].OidLength) {\r
return EFI_UNSUPPORTED;\r
}\r
\r
- if (CompareMem (PkcsCertData->CertData + 32, mHash[Index].OidValue, mHash[Index].OidLength) == 0) {\r
+ if (CompareMem (AuthData + 32, mHash[Index].OidValue, mHash[Index].OidLength) == 0) {\r
break;\r
}\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
+ @param[in] SignatureSize Size of signature. Must be zero if Signature is NULL.\r
\r
**/\r
VOID\r
UINTN NewImageExeInfoEntrySize;\r
UINTN NameStringLen;\r
UINTN DevicePathSize;\r
+ CHAR16 *NameStr;\r
\r
ImageExeInfoTable = NULL;\r
NewImageExeInfoTable = NULL;\r
ImageExeInfoEntry = NULL;\r
NameStringLen = 0;\r
+ NameStr = NULL;\r
\r
if (DevicePath == NULL) {\r
return ;\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
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
if (NewImageExeInfoTable == NULL) {\r
return ;\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
\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
\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
+ @return TRUE The certificate hash is found in the forbidden database.\r
+ @return FALSE The certificate hash is not found in the forbidden database.\r
\r
**/\r
-EFI_STATUS\r
-ImageAuthorization (\r
- IN UINT32 Policy\r
+BOOLEAN\r
+IsCertHashFoundInDatabase (\r
+ IN UINT8 *Certificate,\r
+ IN UINTN CertSize,\r
+ IN EFI_SIGNATURE_LIST *SignatureList,\r
+ IN UINTN SignatureListSize,\r
+ OUT EFI_TIME *RevocationTime\r
)\r
{\r
- EFI_STATUS Status;\r
- EFI_INPUT_KEY Key;\r
+ BOOLEAN IsFound;\r
+ BOOLEAN 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
+ IsFound = FALSE;\r
+ DbxList = SignatureList;\r
+ DbxSize = SignatureListSize;\r
+ HashCtx = NULL;\r
+ HashAlg = HASHALG_MAX;\r
+\r
+ if ((RevocationTime == NULL) || (DbxList == NULL)) {\r
+ return FALSE;\r
+ }\r
\r
- Status = EFI_ACCESS_DENIED;\r
+ //\r
+ // Retrieve the TBSCertificate from the X.509 Certificate.\r
+ //\r
+ if (!X509GetTBSCert (Certificate, CertSize, &TBSCert, &TBSCertSize)) {\r
+ return FALSE;\r
+ }\r
\r
- switch (Policy) {\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
- 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
- }\r
- } while (TRUE);\r
- break;\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
+ Status = mHash[HashAlg].HashInit (HashCtx);\r
+ if (!Status) {\r
+ goto Done;\r
+ }\r
+ Status = mHash[HashAlg].HashUpdate (HashCtx, TBSCert, TBSCertSize);\r
+ if (!Status) {\r
+ goto Done;\r
+ }\r
+ Status = mHash[HashAlg].HashFinal (HashCtx, CertDigest);\r
+ if (!Status) {\r
+ goto Done;\r
+ }\r
\r
- case ALLOW_EXECUTE_ON_SECURITY_VIOLATION:\r
- Status = EFI_SUCCESS;\r
- break;\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
+ IsFound = TRUE;\r
\r
- case DEFER_EXECUTE_ON_SECURITY_VIOLATION:\r
- Status = EFI_SECURITY_VIOLATION;\r
- break;\r
+ //\r
+ // Return the revocation time.\r
+ //\r
+ CopyMem (RevocationTime, (EFI_TIME *)(DbxCertHash + mHash[HashAlg].DigestLength), sizeof (EFI_TIME));\r
+ goto Done;\r
+ }\r
+ CertHash = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertHash + DbxList->SignatureSize);\r
+ }\r
\r
- case DENY_EXECUTE_ON_SECURITY_VIOLATION:\r
- Status = EFI_ACCESS_DENIED;\r
- break;\r
+ DbxSize -= DbxList->SignatureListSize;\r
+ DbxList = (EFI_SIGNATURE_LIST *) ((UINT8 *) DbxList + DbxList->SignatureListSize);\r
}\r
\r
- return Status;\r
+Done:\r
+ if (HashCtx != NULL) {\r
+ FreePool (HashCtx);\r
+ }\r
+\r
+ return IsFound;\r
}\r
\r
/**\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
\r
@return TRUE Found the signature in the variable database.\r
UINTN Index;\r
UINTN CertCount;\r
BOOLEAN IsFound;\r
+\r
//\r
// Read signature database variable.\r
//\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
// Find the signature in database.\r
//\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
}\r
\r
/**\r
- Verify PKCS#7 SignedData using certificate found in Variable which formatted\r
- as EFI_SIGNATURE_LIST. The Variable may be PK, KEK, DB or DBX.\r
+ Check whether the timestamp is valid by comparing the signing time and the revocation time.\r
\r
- @param VariableName Name of Variable to search for Certificate.\r
- @param VendorGuid Variable vendor GUID.\r
+ @param SigningTime A pointer to the signing time.\r
+ @param RevocationTime A pointer to the revocation time.\r
\r
- @retval TRUE Image pass verification.\r
- @retval FALSE Image fail verification.\r
+ @retval TRUE The SigningTime is not later than the RevocationTime.\r
+ @retval FALSE The SigningTime is later than the RevocationTime.\r
\r
**/\r
BOOLEAN\r
-IsPkcsSignedDataVerifiedBySignatureList (\r
- IN CHAR16 *VariableName,\r
- IN EFI_GUID *VendorGuid\r
+IsValidSignatureByTimestamp (\r
+ IN EFI_TIME *SigningTime,\r
+ IN EFI_TIME *RevocationTime\r
+ )\r
+{\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
+/**\r
+ Check if the given time value is zero.\r
+\r
+ @param[in] Time Pointer of a time value.\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
+ 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
- WIN_CERTIFICATE_EFI_PKCS *PkcsCertData;\r
EFI_SIGNATURE_LIST *CertList;\r
EFI_SIGNATURE_DATA *Cert;\r
- UINTN DataSize;\r
- UINT8 *Data;\r
+ UINT8 *DbtData;\r
+ UINTN DbtDataSize;\r
UINT8 *RootCert;\r
UINTN RootCertSize;\r
UINTN Index;\r
UINTN CertCount;\r
+ EFI_TIME SigningTime;\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
- DataSize = 0;\r
- Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, NULL);\r
- if (Status == EFI_BUFFER_TOO_SMALL) {\r
- Data = (UINT8 *) AllocateZeroPool (DataSize);\r
- if (Data == NULL) {\r
- return VerifyStatus;\r
- }\r
+ //\r
+ // Variable Initialization\r
+ //\r
+ VerifyStatus = FALSE;\r
+ DbtData = NULL;\r
+ CertList = NULL;\r
+ Cert = NULL;\r
+ RootCert = NULL;\r
+ RootCertSize = 0;\r
\r
- Status = gRT->GetVariable (VariableName, VendorGuid, 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 X509 certificate in Signature List 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 verify.\r
- //\r
- RootCert = Cert->SignatureData;\r
- RootCertSize = CertList->SignatureSize;\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
- PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr),\r
- RootCert,\r
- RootCertSize,\r
- mImageDigest,\r
- mImageDigestSize\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
+ 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
return VerifyStatus;\r
}\r
\r
/**\r
- Verify certificate in WIN_CERT_TYPE_PKCS_SIGNED_DATA 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
- @retval EFI_SUCCESS Image pass verification.\r
- @retval EFI_SECURITY_VIOLATION Image fail verification.\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 TRUE Image is forbidden by dbx.\r
+ @retval FALSE Image is not forbidden by dbx.\r
\r
**/\r
-EFI_STATUS\r
-VerifyCertPkcsSignedData (\r
- VOID\r
+BOOLEAN\r
+IsForbiddenByDbx (\r
+ IN UINT8 *AuthData,\r
+ IN UINTN AuthDataSize\r
)\r
{\r
+ EFI_STATUS Status;\r
+ BOOLEAN IsForbidden;\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 = FALSE;\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
- // 1: Find certificate from DBX forbidden database for revoked certificate.\r
- //\r
- if (IsPkcsSignedDataVerifiedBySignatureList (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid)) {\r
- //\r
- // DBX is forbidden database, if Authenticode verification pass with\r
- // one of the certificate in DBX, this image should be rejected.\r
- //\r
- return EFI_SECURITY_VIOLATION;\r
+ DataSize = 0;\r
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, NULL);\r
+ if (Status != EFI_BUFFER_TOO_SMALL) {\r
+ return IsForbidden;\r
+ }\r
+ Data = (UINT8 *) AllocateZeroPool (DataSize);\r
+ if (Data == NULL) {\r
+ return IsForbidden;\r
}\r
\r
- //\r
- // 2: Find certificate from KEK database and try to verify authenticode struct.\r
- //\r
- if (IsPkcsSignedDataVerifiedBySignatureList (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid)) {\r
- return EFI_SUCCESS;\r
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, (VOID *) Data);\r
+ if (EFI_ERROR (Status)) {\r
+ return IsForbidden;\r
}\r
\r
//\r
- // 3: Find certificate from DB database and try to verify authenticode struct.\r
+ // Verify image signature with RAW X509 certificates in DBX database.\r
+ // If passed, the image will be forbidden.\r
//\r
- if (IsPkcsSignedDataVerifiedBySignatureList (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid)) {\r
- return EFI_SUCCESS;\r
- } else {\r
- return EFI_SECURITY_VIOLATION;\r
- }\r
-}\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
-/**\r
- Verify certificate in WIN_CERTIFICATE_UEFI_GUID format.\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
- @retval EFI_SUCCESS Image pass verification.\r
- @retval EFI_SECURITY_VIOLATION Image fail verification.\r
- @retval other error value\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
-**/\r
-EFI_STATUS\r
-VerifyCertUefiGuid (\r
- VOID\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
+ CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertData + CertList->SignatureSize);\r
+ }\r
+ }\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
+ CertListSize -= CertList->SignatureListSize;\r
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);\r
}\r
\r
//\r
- // Get KEK database variable data size\r
+ // Check X.509 Certificate Hash & Possible Timestamp.\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
- }\r
\r
//\r
- // Get KEK database variable.\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
- GetEfiGlobalVariable2 (EFI_KEY_EXCHANGE_KEY_NAME, (VOID**)&KekList, NULL);\r
- if (KekList == NULL) {\r
- return EFI_SECURITY_VIOLATION;\r
+ Pkcs7GetSigners (AuthData, AuthDataSize, &CertBuffer, &BufferLength, &TrustedCert, &TrustedCertLength);\r
+ if ((BufferLength == 0) || (CertBuffer == NULL)) {\r
+ IsForbidden = TRUE;\r
+ goto Done;\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
+ if (IsCertHashFoundInDatabase (Cert, CertSize, (EFI_SIGNATURE_LIST *)Data, DataSize, &RevocationTime)) {\r
+ //\r
+ // Check the timestamp signature and signing time to determine if the image can be trusted.\r
+ //\r
+ IsForbidden = TRUE;\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
}\r
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature failed the timestamp check.\n"));\r
+ goto Done;\r
}\r
- KekDataSize -= KekList->SignatureListSize;\r
- KekList = (EFI_SIGNATURE_LIST *) ((UINT8 *) KekList + KekList->SignatureListSize);\r
+\r
+ }\r
+\r
+Done:\r
+ if (Data != NULL) {\r
+ FreePool (Data);\r
}\r
\r
- if (!IsFound) {\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
+ 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
+ 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
+ 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
- // Signed key is not a trust one.\r
+ // Find X509 certificate in Signature List to verify the signature in pkcs7 signed data.\r
//\r
- goto Done;\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
- //\r
- // Now, we found the corresponding security policy.\r
- // Verify the data payload.\r
- //\r
- Rsa = RsaNew ();\r
- if (Rsa == NULL) {\r
- Status = FALSE;\r
- goto Done;\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
- // Set RSA Key Components.\r
- // NOTE: Only N and E are needed to be set as RSA public key for signature verification.\r
- //\r
- Status = RsaSetKey (Rsa, RsaKeyN, CertBlock->PublicKey, EFI_CERT_TYPE_RSA2048_SIZE);\r
- if (!Status) {\r
- goto Done;\r
- }\r
- Status = RsaSetKey (Rsa, RsaKeyE, mRsaE, sizeof (mRsaE));\r
- if (!Status) {\r
- goto Done;\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
+ // Here We still need to check if this RootCert's Hash is revoked\r
+ //\r
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DbxDataSize, NULL);\r
+ if (Status == EFI_BUFFER_TOO_SMALL) {\r
+ goto Done;\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
+ if (IsCertHashFoundInDatabase (RootCert, RootCertSize, (EFI_SIGNATURE_LIST *)DbxData, DbxDataSize, &RevocationTime)) {\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
+ 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
- //\r
- // Verify the signature.\r
- //\r
- Status = RsaPkcs1Verify (\r
- Rsa,\r
- mImageDigest,\r
- mImageDigestSize,\r
- CertBlock->Signature,\r
- EFI_CERT_TYPE_RSA2048_SHA256_SIZE\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
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 process is:\r
- Is the Image signed?\r
- If yes,\r
- Does the image verify against a certificate (root or intermediate) in the allowed db?\r
- Run it\r
- Image verification fail\r
- Is the Image's Hash not in forbidden database and the Image's Hash in allowed db?\r
- Run it\r
- If no,\r
- Is the Image's Hash in the forbidden database (DBX)?\r
- if yes,\r
- Error out\r
- Is the Image's Hash in the allowed database (DB)?\r
- If yes,\r
- Run it\r
- If no,\r
- Error out\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
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 Input argument is incorrect.\r
- @retval EFI_OUT_RESOURCE Fail to allocate memory.\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 many not use File.\r
\r
**/\r
EFI_STATUS\r
IN UINT32 AuthenticationStatus,\r
IN CONST EFI_DEVICE_PATH_PROTOCOL *File,\r
IN VOID *FileBuffer,\r
- IN UINTN FileSize\r
+ IN UINTN FileSize,\r
+ IN BOOLEAN BootPolicy\r
)\r
{\r
- EFI_STATUS Status;\r
- UINT16 Magic;\r
EFI_IMAGE_DOS_HEADER *DosHdr;\r
- EFI_STATUS VerifyStatus;\r
- UINT8 *SetupMode;\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 *SecureBootEnable;\r
+ UINT8 *SecureBoot;\r
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;\r
UINT32 NumberOfRvaAndSizes;\r
- UINT32 CertSize;\r
-\r
- if (File == NULL) {\r
- return EFI_INVALID_PARAMETER;\r
- }\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
\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
+\r
+\r
//\r
// Check the image type and get policy setting.\r
//\r
//\r
if (Policy == ALWAYS_EXECUTE) {\r
return EFI_SUCCESS;\r
- } else if (Policy == NEVER_EXECUTE) {\r
- return EFI_ACCESS_DENIED;\r
}\r
-\r
- GetVariable2 (EFI_SECURE_BOOT_ENABLE_NAME, &gEfiSecureBootEnableDisableGuid, (VOID**)&SecureBootEnable, NULL);\r
- //\r
- // Skip verification if SecureBootEnable variable doesn't exist.\r
- //\r
- if (SecureBootEnable == NULL) {\r
- return EFI_SUCCESS;\r
+ if (Policy == NEVER_EXECUTE) {\r
+ return EFI_ACCESS_DENIED;\r
}\r
\r
//\r
- // Skip verification if SecureBootEnable is disabled.\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
- if (*SecureBootEnable == SECURE_BOOT_DISABLE) {\r
- FreePool (SecureBootEnable);\r
- return EFI_SUCCESS;\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
- FreePool (SecureBootEnable);\r
-\r
- GetEfiGlobalVariable2 (EFI_SETUP_MODE_NAME, (VOID**)&SetupMode, NULL);\r
-\r
+ GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME, (VOID**)&SecureBoot, NULL);\r
//\r
- // SetupMode doesn't exist means no AuthVar driver is dispatched,\r
- // skip verification.\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
-\r
- FreePool (SetupMode);\r
+ FreePool (SecureBoot);\r
\r
//\r
// Read the Dos header.\r
//\r
if (FileBuffer == NULL) {\r
- return EFI_INVALID_PARAMETER;\r
+ return EFI_ACCESS_DENIED;\r
}\r
\r
mImageBase = (UINT8 *) FileBuffer;\r
//\r
// Get information about the image being loaded\r
//\r
- Status = PeCoffLoaderGetImageInfo (&ImageContext);\r
- if (EFI_ERROR (Status)) {\r
+ PeCoffStatus = PeCoffLoaderGetImageInfo (&ImageContext);\r
+ if (RETURN_ERROR (PeCoffStatus)) {\r
//\r
// The information can't be got from the invalid PeImage\r
//\r
- goto Done;\r
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: PeImage invalid. Cannot retrieve image information.\n"));\r
+ goto Failed;\r
}\r
\r
- Status = EFI_ACCESS_DENIED;\r
-\r
DosHdr = (EFI_IMAGE_DOS_HEADER *) mImageBase;\r
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {\r
//\r
//\r
// It is not a valid Pe/Coff file.\r
//\r
- goto Done;\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
NumberOfRvaAndSizes = mNtHeader.Pe32->OptionalHeader.NumberOfRvaAndSizes;\r
if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {\r
- mSecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];\r
- } \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
NumberOfRvaAndSizes = mNtHeader.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;\r
if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {\r
- mSecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[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 == NULL) || ((mSecDataDir != NULL) && (mSecDataDir->Size == 0))) {\r
+ //\r
+ // Start Image Validation.\r
+ //\r
+ if (SecDataDir == NULL || SecDataDir->Size == 0) {\r
//\r
- // This image is not signed.\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
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
if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) {\r
//\r
// Image Hash is in forbidden database (DBX).\r
//\r
- goto Done;\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
if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, mImageDigest, &mCertType, mImageDigestSize)) {\r
//\r
// Image Hash is not found in both forbidden and allowed database.\r
//\r
- goto Done;\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
//\r
- // Verify signature of executables.\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
- WinCertificate = (WIN_CERTIFICATE *) (mImageBase + mSecDataDir->VirtualAddress);\r
-\r
- CertSize = sizeof (WIN_CERTIFICATE);\r
-\r
- if ((mSecDataDir->Size <= CertSize) || (mSecDataDir->Size < WinCertificate->dwLength)) {\r
- goto Done;\r
- }\r
-\r
- switch (WinCertificate->wCertificateType) {\r
-\r
- case WIN_CERT_TYPE_EFI_GUID:\r
- CertSize = sizeof (WIN_CERTIFICATE_UEFI_GUID) + sizeof (EFI_CERT_BLOCK_RSA_2048_SHA256) - sizeof (UINT8);\r
- if (WinCertificate->dwLength < CertSize) {\r
- goto Done;\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
- // Verify UEFI GUID type.\r
+ // Verify the image's Authenticode signature, only DER-encoded PKCS#7 signed data is supported.\r
//\r
- if (!HashPeImage (HASHALG_SHA256)) {\r
- goto Done;\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
- VerifyStatus = VerifyCertUefiGuid ();\r
- break;\r
+ HashStatus = HashPeImageByType (AuthData, AuthDataSize);\r
+ if (EFI_ERROR (HashStatus)) {\r
+ continue;\r
+ }\r
\r
- case WIN_CERT_TYPE_PKCS_SIGNED_DATA:\r
//\r
- // Verify Pkcs signed data type.\r
+ // Check the digital signature against the revoked certificate in forbidden database (dbx).\r
//\r
- Status = HashPeImageByType();\r
- if (EFI_ERROR (Status)) {\r
- goto Done;\r
+ if (IsForbiddenByDbx (AuthData, AuthDataSize)) {\r
+ Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;\r
+ IsVerified = FALSE;\r
+ break;\r
}\r
\r
- VerifyStatus = VerifyCertPkcsSignedData ();\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
+ // Check the digital signature against the valid certificate in allowed database (db).\r
//\r
- if (!EFI_ERROR (VerifyStatus)) {\r
- if (!IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) {\r
- return EFI_SUCCESS;\r
+ if (!IsVerified) {\r
+ if (IsAllowedByDb (AuthData, AuthDataSize)) {\r
+ IsVerified = TRUE;\r
}\r
}\r
- break;\r
\r
- default:\r
- goto Done;\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
- if (SignatureList == NULL) {\r
- Status = EFI_OUT_OF_RESOURCES;\r
- goto Done;\r
- }\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 image's hash value.\r
//\r
- if (!IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize) &&\r
- IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, mImageDigest, &mCertType, mImageDigestSize)) {\r
- //\r
- // Verification fail, Image Hash is not in forbidden database (DBX),\r
- // and Image Hash is in allowed database (DB).\r
- //\r
- Status = EFI_SUCCESS;\r
- } else {\r
- Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;\r
- Status = EFI_ACCESS_DENIED;\r
+ if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) {\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
- } else if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, Signature->SignatureData, &mCertType, mImageDigestSize)) {\r
- //\r
- // Executable signature verification passes, but is found in forbidden signature database.\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
- //\r
- // Executable signature is found in authorized signature database.\r
- //\r
- Status = EFI_SUCCESS;\r
- } else {\r
+ if (!IsVerified) {\r
+ if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, mImageDigest, &mCertType, mImageDigestSize)) {\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 executable information table.\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
- return Status;\r
+ return EFI_SECURITY_VIOLATION;\r
}\r
\r
/**\r
- When VariableWriteArchProtocol install, create "SecureBoot" variable.\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
+ @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
- GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME, (VOID**)&SecureBootModePtr, NULL);\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
+ 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
+ ImageExeInfoTable->NumberOfImages = 0;\r
+ gBS->InstallConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID *) ImageExeInfoTable);\r
+\r
}\r
\r
/**\r
IN EFI_SYSTEM_TABLE *SystemTable\r
)\r
{\r
- VOID *Registration;\r
+ EFI_EVENT Event;\r
\r
//\r
- // Register callback function upon VariableWriteArchProtocol.\r
+ // Register the event to publish the image execution table.\r
//\r
- EfiCreateProtocolNotifyEvent (\r
- &gEfiVariableWriteArchProtocolGuid,\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