X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=SecurityPkg%2FLibrary%2FDxeImageVerificationLib%2FDxeImageVerificationLib.c;h=470a0d20efcafd7f9c7bc7ad8efeb686e0056c34;hb=5cd8be6079ea7e5638903b2f3da0f4c10ec7f1da;hp=f83e530c5c52f6d0b82774fae8eaa28c038ae3ad;hpb=3277a4e5ed1d54a9ec97b17a60cfbe91b685b1e7;p=mirror_edk2.git
diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
index f83e530c5c..470a0d20ef 100644
--- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
+++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
@@ -1,5 +1,5 @@
/** @file
- Implement image verification services for secure boot service in UEFI2.3.1.
+ Implement image verification services for secure boot service
Caution: This file requires additional review when modified.
This library will have external input - PE/COFF image.
@@ -12,14 +12,9 @@
DxeImageVerificationHandler(), HashPeImageByType(), HashPeImage() function will accept
untrusted PE/COFF image and validate its data structure within this image buffer before use.
-Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
-This program and the accompanying materials
-are licensed and made available under the terms and conditions of the BSD License
-which accompanies this distribution. The full text of the license may be found at
-http://opensource.org/licenses/bsd-license.php
-
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP
+SPDX-License-Identifier: BSD-2-Clause-Patent
**/
@@ -31,7 +26,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
//
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION mNtHeader;
UINT32 mPeCoffHeaderOffset;
-EFI_IMAGE_DATA_DIRECTORY *mSecDataDir = NULL;
EFI_GUID mCertType;
//
@@ -57,7 +51,6 @@ CONST UINT8 mRsaE[] = { 0x01, 0x00, 0x01 };
// OID ASN.1 Value for Hash Algorithms
//
UINT8 mHashOidValue[] = {
- 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, // OBJ_md5
0x2B, 0x0E, 0x03, 0x02, 0x1A, // OBJ_sha1
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, // OBJ_sha224
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, // OBJ_sha256
@@ -66,13 +59,34 @@ UINT8 mHashOidValue[] = {
};
HASH_TABLE mHash[] = {
- { L"SHA1", 20, &mHashOidValue[8], 5, Sha1GetContextSize, Sha1Init, Sha1Update, Sha1Final },
- { L"SHA224", 28, &mHashOidValue[13], 9, NULL, NULL, NULL, NULL },
- { L"SHA256", 32, &mHashOidValue[22], 9, Sha256GetContextSize,Sha256Init, Sha256Update, Sha256Final},
- { L"SHA384", 48, &mHashOidValue[31], 9, NULL, NULL, NULL, NULL },
- { L"SHA512", 64, &mHashOidValue[40], 9, NULL, NULL, NULL, NULL }
+ { L"SHA1", 20, &mHashOidValue[0], 5, Sha1GetContextSize, Sha1Init, Sha1Update, Sha1Final },
+ { L"SHA224", 28, &mHashOidValue[5], 9, NULL, NULL, NULL, NULL },
+ { L"SHA256", 32, &mHashOidValue[14], 9, Sha256GetContextSize, Sha256Init, Sha256Update, Sha256Final},
+ { L"SHA384", 48, &mHashOidValue[23], 9, Sha384GetContextSize, Sha384Init, Sha384Update, Sha384Final},
+ { L"SHA512", 64, &mHashOidValue[32], 9, Sha512GetContextSize, Sha512Init, Sha512Update, Sha512Final}
};
+EFI_STRING mHashTypeStr;
+
+/**
+ SecureBoot Hook for processing image verification.
+
+ @param[in] VariableName Name of Variable to be found.
+ @param[in] VendorGuid Variable vendor GUID.
+ @param[in] DataSize Size of Data found. If size is less than the
+ data, this value contains the required size.
+ @param[in] Data Data pointer.
+
+**/
+VOID
+EFIAPI
+SecureBootHook (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINTN DataSize,
+ IN VOID *Data
+ );
+
/**
Reads contents of a PE/COFF image in memory buffer.
@@ -82,11 +96,11 @@ HASH_TABLE mHash[] = {
@param FileHandle Pointer to the file handle to read the PE/COFF image.
@param FileOffset Offset into the PE/COFF image to begin the read operation.
- @param ReadSize On input, the size in bytes of the requested read operation.
+ @param ReadSize On input, the size in bytes of the requested read operation.
On output, the number of bytes actually read.
@param Buffer Output buffer that contains the data read from the PE/COFF image.
-
- @retval EFI_SUCCESS The specified portion of the PE/COFF image was read and the size
+
+ @retval EFI_SUCCESS The specified portion of the PE/COFF image was read and the size
**/
EFI_STATUS
EFIAPI
@@ -100,7 +114,7 @@ DxeImageVerificationLibImageRead (
UINTN EndPosition;
if (FileHandle == NULL || ReadSize == NULL || Buffer == NULL) {
- return EFI_INVALID_PARAMETER;
+ return EFI_INVALID_PARAMETER;
}
if (MAX_ADDRESS - FileOffset < *ReadSize) {
@@ -141,6 +155,10 @@ GetImageType (
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ if (File == NULL) {
+ return IMAGE_UNKNOWN;
+ }
+
//
// First check to see if File is from a Firmware Volume
//
@@ -249,13 +267,16 @@ GetImageType (
}
/**
- Caculate hash of Pe/Coff image based on the authenticode image hashing in
+ Calculate hash of Pe/Coff image based on the authenticode image hashing in
PE/COFF Specification 8.0 Appendix A
Caution: This function may receive untrusted input.
PE/COFF image is external input, so this function will validate its data structure
within this image buffer before use.
+ Notes: PE/COFF image has been checked by BasePeCoffLib PeCoffLoaderGetImageInfo() in
+ its caller function DxeImageVerificationHandler().
+
@param[in] HashAlg Hash algorithm type.
@retval TRUE Successfully hash image.
@@ -268,7 +289,6 @@ HashPeImage (
)
{
BOOLEAN Status;
- UINT16 Magic;
EFI_IMAGE_SECTION_HEADER *Section;
VOID *HashCtx;
UINTN CtxSize;
@@ -285,7 +305,7 @@ HashPeImage (
SectionHeader = NULL;
Status = FALSE;
- if ((HashAlg != HASHALG_SHA1) && (HashAlg != HASHALG_SHA256)) {
+ if ((HashAlg >= HASHALG_MAX)) {
return FALSE;
}
@@ -294,16 +314,32 @@ HashPeImage (
//
ZeroMem (mImageDigest, MAX_DIGEST_SIZE);
- if (HashAlg == HASHALG_SHA1) {
- mImageDigestSize = SHA1_DIGEST_SIZE;
- mCertType = gEfiCertSha1Guid;
- } else if (HashAlg == HASHALG_SHA256) {
- mImageDigestSize = SHA256_DIGEST_SIZE;
- mCertType = gEfiCertSha256Guid;
- } else {
+ switch (HashAlg) {
+ case HASHALG_SHA1:
+ mImageDigestSize = SHA1_DIGEST_SIZE;
+ mCertType = gEfiCertSha1Guid;
+ break;
+
+ case HASHALG_SHA256:
+ mImageDigestSize = SHA256_DIGEST_SIZE;
+ mCertType = gEfiCertSha256Guid;
+ break;
+
+ case HASHALG_SHA384:
+ mImageDigestSize = SHA384_DIGEST_SIZE;
+ mCertType = gEfiCertSha384Guid;
+ break;
+
+ case HASHALG_SHA512:
+ mImageDigestSize = SHA512_DIGEST_SIZE;
+ mCertType = gEfiCertSha512Guid;
+ break;
+
+ default:
return FALSE;
}
+ mHashTypeStr = mHash[HashAlg].Name;
CtxSize = mHash[HashAlg].GetContextSize();
HashCtx = AllocatePool (CtxSize);
@@ -324,37 +360,23 @@ HashPeImage (
// Measuring PE/COFF Image Header;
// But CheckSum field and SECURITY data directory (certificate) are excluded
//
- if (mNtHeader.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 && mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
- //
- // NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value
- // in the PE/COFF Header. If the MachineType is Itanium(IA64) and the
- // Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
- // then override the magic value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
- //
- Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
- } else {
- //
- // Get the magic value from the PE/COFF Optional Header
- //
- Magic = mNtHeader.Pe32->OptionalHeader.Magic;
- }
-
+
//
// 3. Calculate the distance from the base of the image header to the image checksum address.
// 4. Hash the image header from its base to beginning of the image checksum.
//
HashBase = mImageBase;
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset.
//
- HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32->OptionalHeader.CheckSum) - HashBase);
+ HashSize = (UINTN) (&mNtHeader.Pe32->OptionalHeader.CheckSum) - (UINTN) HashBase;
NumberOfRvaAndSizes = mNtHeader.Pe32->OptionalHeader.NumberOfRvaAndSizes;
- } else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ } else if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
//
// Use PE32+ offset.
//
- HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32Plus->OptionalHeader.CheckSum) - HashBase);
+ HashSize = (UINTN) (&mNtHeader.Pe32Plus->OptionalHeader.CheckSum) - (UINTN) HashBase;
NumberOfRvaAndSizes = mNtHeader.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
} else {
//
@@ -377,18 +399,18 @@ HashPeImage (
// 6. Since there is no Cert Directory in optional header, hash everything
// from the end of the checksum to the end of image header.
//
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset.
//
HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);
- HashSize = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - mImageBase);
+ HashSize = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders - ((UINTN) HashBase - (UINTN) mImageBase);
} else {
//
// Use PE32+ offset.
//
HashBase = (UINT8 *) &mNtHeader.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32);
- HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - mImageBase);
+ HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - ((UINTN) HashBase - (UINTN) mImageBase);
}
if (HashSize != 0) {
@@ -401,18 +423,18 @@ HashPeImage (
//
// 7. Hash everything from the end of the checksum to the start of the Cert Directory.
//
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset.
//
HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);
- HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase);
+ HashSize = (UINTN) (&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN) HashBase;
} else {
//
// Use PE32+ offset.
//
HashBase = (UINT8 *) &mNtHeader.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32);
- HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase);
+ HashSize = (UINTN) (&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN) HashBase;
}
if (HashSize != 0) {
@@ -426,18 +448,18 @@ HashPeImage (
// 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.)
// 9. Hash everything from the end of the Cert Directory to the end of image header.
//
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset
//
HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];
- HashSize = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - mImageBase);
+ HashSize = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders - ((UINTN) HashBase - (UINTN) mImageBase);
} else {
//
// Use PE32+ offset.
//
HashBase = (UINT8 *) &mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];
- HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - mImageBase);
+ HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - ((UINTN) HashBase - (UINTN) mImageBase);
}
if (HashSize != 0) {
@@ -445,13 +467,13 @@ HashPeImage (
if (!Status) {
goto Done;
}
- }
+ }
}
//
// 10. Set the SUM_OF_BYTES_HASHED to the size of the header.
//
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset.
//
@@ -534,7 +556,7 @@ HashPeImage (
if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
CertSize = 0;
} else {
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset.
//
@@ -573,7 +595,7 @@ Done:
}
/**
- Recognize the Hash algorithm in PE/COFF Authenticode and caculate hash of
+ Recognize the Hash algorithm in PE/COFF Authenticode and calculate hash of
Pe/Coff image based on the authenticode image hashing in PE/COFF Specification
8.0 Appendix A
@@ -581,23 +603,20 @@ Done:
PE/COFF image is external input, so this function will validate its data structure
within this image buffer before use.
+ @param[in] AuthData Pointer to the Authenticode Signature retrieved from signed image.
+ @param[in] AuthDataSize Size of the Authenticode Signature in bytes.
+
@retval EFI_UNSUPPORTED Hash algorithm is not supported.
@retval EFI_SUCCESS Hash successfully.
**/
EFI_STATUS
HashPeImageByType (
- VOID
+ IN UINT8 *AuthData,
+ IN UINTN AuthDataSize
)
{
UINT8 Index;
- WIN_CERTIFICATE_EFI_PKCS *PkcsCertData;
-
- PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) (mImageBase + mSecDataDir->VirtualAddress);
-
- if (PkcsCertData->Hdr.dwLength < sizeof (WIN_CERTIFICATE_EFI_PKCS) + 32) {
- return EFI_UNSUPPORTED;
- }
for (Index = 0; Index < HASHALG_MAX; Index++) {
//
@@ -612,18 +631,18 @@ HashPeImageByType (
// This field has the fixed offset (+32) in final Authenticode ASN.1 data.
// Fixed offset (+32) is calculated based on two bytes of length encoding.
//
- if ((*(PkcsCertData->CertData + 1) & TWO_BYTE_ENCODE) != TWO_BYTE_ENCODE) {
+ if ((*(AuthData + 1) & TWO_BYTE_ENCODE) != TWO_BYTE_ENCODE) {
//
// Only support two bytes of Long Form of Length Encoding.
//
continue;
}
- if (PkcsCertData->Hdr.dwLength < sizeof (WIN_CERTIFICATE_EFI_PKCS) + 32 + mHash[Index].OidLength) {
+ if (AuthDataSize < 32 + mHash[Index].OidLength) {
return EFI_UNSUPPORTED;
}
- if (CompareMem (PkcsCertData->CertData + 32, mHash[Index].OidValue, mHash[Index].OidLength) == 0) {
+ if (CompareMem (AuthData + 32, mHash[Index].OidValue, mHash[Index].OidLength) == 0) {
break;
}
}
@@ -685,7 +704,7 @@ GetImageExeInfoTableSize (
@param[in] Name Input a null-terminated, user-friendly name.
@param[in] DevicePath Input device path pointer.
@param[in] Signature Input signature info in EFI_SIGNATURE_LIST data structure.
- @param[in] SignatureSize Size of signature.
+ @param[in] SignatureSize Size of signature. Must be zero if Signature is NULL.
**/
VOID
@@ -704,11 +723,13 @@ AddImageExeInfo (
UINTN NewImageExeInfoEntrySize;
UINTN NameStringLen;
UINTN DevicePathSize;
+ CHAR16 *NameStr;
ImageExeInfoTable = NULL;
NewImageExeInfoTable = NULL;
ImageExeInfoEntry = NULL;
NameStringLen = 0;
+ NameStr = NULL;
if (DevicePath == NULL) {
return ;
@@ -716,14 +737,15 @@ AddImageExeInfo (
if (Name != NULL) {
NameStringLen = StrSize (Name);
+ } else {
+ NameStringLen = sizeof (CHAR16);
}
- ImageExeInfoTable = NULL;
EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID **) &ImageExeInfoTable);
if (ImageExeInfoTable != NULL) {
//
// The table has been found!
- // We must enlarge the table to accmodate the new exe info entry.
+ // We must enlarge the table to accommodate the new exe info entry.
//
ImageExeInfoTableSize = GetImageExeInfoTableSize (ImageExeInfoTable);
} else {
@@ -735,7 +757,13 @@ AddImageExeInfo (
}
DevicePathSize = GetDevicePathSize (DevicePath);
- NewImageExeInfoEntrySize = sizeof (EFI_IMAGE_EXECUTION_INFO) + NameStringLen + DevicePathSize + SignatureSize;
+
+ //
+ // Signature size can be odd. Pad after signature to ensure next EXECUTION_INFO entry align
+ //
+ ASSERT (Signature != NULL || SignatureSize == 0);
+ NewImageExeInfoEntrySize = sizeof (EFI_IMAGE_EXECUTION_INFO) + NameStringLen + DevicePathSize + SignatureSize;
+
NewImageExeInfoTable = (EFI_IMAGE_EXECUTION_INFO_TABLE *) AllocateRuntimePool (ImageExeInfoTableSize + NewImageExeInfoEntrySize);
if (NewImageExeInfoTable == NULL) {
return ;
@@ -749,22 +777,26 @@ AddImageExeInfo (
NewImageExeInfoTable->NumberOfImages++;
ImageExeInfoEntry = (EFI_IMAGE_EXECUTION_INFO *) ((UINT8 *) NewImageExeInfoTable + ImageExeInfoTableSize);
//
- // Update new item's infomation.
+ // Update new item's information.
//
- WriteUnaligned32 ((UINT32 *) &ImageExeInfoEntry->Action, Action);
- WriteUnaligned32 ((UINT32 *) &ImageExeInfoEntry->InfoSize, (UINT32) NewImageExeInfoEntrySize);
+ WriteUnaligned32 ((UINT32 *) ImageExeInfoEntry, Action);
+ WriteUnaligned32 ((UINT32 *) ((UINT8 *) ImageExeInfoEntry + sizeof (EFI_IMAGE_EXECUTION_ACTION)), (UINT32) NewImageExeInfoEntrySize);
+ NameStr = (CHAR16 *)(ImageExeInfoEntry + 1);
if (Name != NULL) {
- CopyMem ((UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32), Name, NameStringLen);
+ CopyMem ((UINT8 *) NameStr, Name, NameStringLen);
+ } else {
+ ZeroMem ((UINT8 *) NameStr, sizeof (CHAR16));
}
+
CopyMem (
- (UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32) + NameStringLen,
+ (UINT8 *) NameStr + NameStringLen,
DevicePath,
DevicePathSize
);
if (Signature != NULL) {
CopyMem (
- (UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32) + NameStringLen + DevicePathSize,
+ (UINT8 *) NameStr + NameStringLen + DevicePathSize,
Signature,
SignatureSize
);
@@ -783,54 +815,134 @@ AddImageExeInfo (
}
/**
- Discover if the UEFI image is authorized by user's policy setting.
+ Check whether the hash of an given X.509 certificate is in forbidden database (DBX).
- @param[in] Policy Specify platform's policy setting.
+ @param[in] Certificate Pointer to X.509 Certificate that is searched for.
+ @param[in] CertSize Size of X.509 Certificate.
+ @param[in] SignatureList Pointer to the Signature List in forbidden database.
+ @param[in] SignatureListSize Size of Signature List.
+ @param[out] RevocationTime Return the time that the certificate was revoked.
+ @param[out] IsFound Search result. Only valid if EFI_SUCCESS returned.
- @retval EFI_ACCESS_DENIED Image is not allowed to run.
- @retval EFI_SECURITY_VIOLATION Image is deferred.
- @retval EFI_SUCCESS Image is authorized to run.
+ @retval EFI_SUCCESS Finished the search without any error.
+ @retval Others Error occurred in the search of database.
**/
EFI_STATUS
-ImageAuthorization (
- IN UINT32 Policy
+IsCertHashFoundInDatabase (
+ IN UINT8 *Certificate,
+ IN UINTN CertSize,
+ IN EFI_SIGNATURE_LIST *SignatureList,
+ IN UINTN SignatureListSize,
+ OUT EFI_TIME *RevocationTime,
+ OUT BOOLEAN *IsFound
)
{
- EFI_STATUS Status;
- EFI_INPUT_KEY Key;
+ EFI_STATUS Status;
+ EFI_SIGNATURE_LIST *DbxList;
+ UINTN DbxSize;
+ EFI_SIGNATURE_DATA *CertHash;
+ UINTN CertHashCount;
+ UINTN Index;
+ UINT32 HashAlg;
+ VOID *HashCtx;
+ UINT8 CertDigest[MAX_DIGEST_SIZE];
+ UINT8 *DbxCertHash;
+ UINTN SiglistHeaderSize;
+ UINT8 *TBSCert;
+ UINTN TBSCertSize;
+
+ Status = EFI_ABORTED;
+ *IsFound = FALSE;
+ DbxList = SignatureList;
+ DbxSize = SignatureListSize;
+ HashCtx = NULL;
+ HashAlg = HASHALG_MAX;
+
+ if ((RevocationTime == NULL) || (DbxList == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Retrieve the TBSCertificate from the X.509 Certificate.
+ //
+ if (!X509GetTBSCert (Certificate, CertSize, &TBSCert, &TBSCertSize)) {
+ return Status;
+ }
+
+ while ((DbxSize > 0) && (SignatureListSize >= DbxList->SignatureListSize)) {
+ //
+ // Determine Hash Algorithm of Certificate in the forbidden database.
+ //
+ if (CompareGuid (&DbxList->SignatureType, &gEfiCertX509Sha256Guid)) {
+ HashAlg = HASHALG_SHA256;
+ } else if (CompareGuid (&DbxList->SignatureType, &gEfiCertX509Sha384Guid)) {
+ HashAlg = HASHALG_SHA384;
+ } else if (CompareGuid (&DbxList->SignatureType, &gEfiCertX509Sha512Guid)) {
+ HashAlg = HASHALG_SHA512;
+ } else {
+ DbxSize -= DbxList->SignatureListSize;
+ DbxList = (EFI_SIGNATURE_LIST *) ((UINT8 *) DbxList + DbxList->SignatureListSize);
+ continue;
+ }
- Status = EFI_ACCESS_DENIED;
+ //
+ // Calculate the hash value of current TBSCertificate for comparision.
+ //
+ if (mHash[HashAlg].GetContextSize == NULL) {
+ goto Done;
+ }
+ ZeroMem (CertDigest, MAX_DIGEST_SIZE);
+ HashCtx = AllocatePool (mHash[HashAlg].GetContextSize ());
+ if (HashCtx == NULL) {
+ goto Done;
+ }
+ if (!mHash[HashAlg].HashInit (HashCtx)) {
+ goto Done;
+ }
+ if (!mHash[HashAlg].HashUpdate (HashCtx, TBSCert, TBSCertSize)) {
+ goto Done;
+ }
+ if (!mHash[HashAlg].HashFinal (HashCtx, CertDigest)) {
+ goto Done;
+ }
- switch (Policy) {
+ FreePool (HashCtx);
+ HashCtx = NULL;
- case QUERY_USER_ON_SECURITY_VIOLATION:
- do {
- CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, mNotifyString1, mNotifyString2, NULL);
- if (Key.UnicodeChar == L'Y' || Key.UnicodeChar == L'y') {
- Status = EFI_SUCCESS;
- break;
- } else if (Key.UnicodeChar == L'N' || Key.UnicodeChar == L'n') {
- Status = EFI_ACCESS_DENIED;
- break;
- } else if (Key.UnicodeChar == L'D' || Key.UnicodeChar == L'd') {
- Status = EFI_SECURITY_VIOLATION;
- break;
+ SiglistHeaderSize = sizeof (EFI_SIGNATURE_LIST) + DbxList->SignatureHeaderSize;
+ CertHash = (EFI_SIGNATURE_DATA *) ((UINT8 *) DbxList + SiglistHeaderSize);
+ CertHashCount = (DbxList->SignatureListSize - SiglistHeaderSize) / DbxList->SignatureSize;
+ for (Index = 0; Index < CertHashCount; Index++) {
+ //
+ // Iterate each Signature Data Node within this CertList for verify.
+ //
+ DbxCertHash = CertHash->SignatureData;
+ if (CompareMem (DbxCertHash, CertDigest, mHash[HashAlg].DigestLength) == 0) {
+ //
+ // Hash of Certificate is found in forbidden database.
+ //
+ Status = EFI_SUCCESS;
+ *IsFound = TRUE;
+
+ //
+ // Return the revocation time.
+ //
+ CopyMem (RevocationTime, (EFI_TIME *)(DbxCertHash + mHash[HashAlg].DigestLength), sizeof (EFI_TIME));
+ goto Done;
}
- } while (TRUE);
- break;
+ CertHash = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertHash + DbxList->SignatureSize);
+ }
- case ALLOW_EXECUTE_ON_SECURITY_VIOLATION:
- Status = EFI_SUCCESS;
- break;
+ DbxSize -= DbxList->SignatureListSize;
+ DbxList = (EFI_SIGNATURE_LIST *) ((UINT8 *) DbxList + DbxList->SignatureListSize);
+ }
- case DEFER_EXECUTE_ON_SECURITY_VIOLATION:
- Status = EFI_SECURITY_VIOLATION;
- break;
+ Status = EFI_SUCCESS;
- case DENY_EXECUTE_ON_SECURITY_VIOLATION:
- Status = EFI_ACCESS_DENIED;
- break;
+Done:
+ if (HashCtx != NULL) {
+ FreePool (HashCtx);
}
return Status;
@@ -841,7 +953,7 @@ ImageAuthorization (
@param[in] VariableName Name of database variable that is searched in.
@param[in] Signature Pointer to signature that is searched for.
- @param[in] CertType Pointer to hash algrithom.
+ @param[in] CertType Pointer to hash algorithm.
@param[in] SignatureSize Size of Signature.
@return TRUE Found the signature in the variable database.
@@ -864,6 +976,7 @@ IsSignatureFoundInDatabase (
UINTN Index;
UINTN CertCount;
BOOLEAN IsFound;
+
//
// Read signature database variable.
//
@@ -885,11 +998,11 @@ IsSignatureFoundInDatabase (
goto Done;
}
//
- // Enumerate all signature data in SigDB to check if executable's signature exists.
+ // Enumerate all signature data in SigDB to check if signature exists for executable.
//
CertList = (EFI_SIGNATURE_LIST *) Data;
while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) {
- CertCount = (CertList->SignatureListSize - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
if ((CertList->SignatureSize == sizeof(EFI_SIGNATURE_DATA) - 1 + SignatureSize) && (CompareGuid(&CertList->SignatureType, CertType))) {
for (Index = 0; Index < CertCount; Index++) {
@@ -898,6 +1011,12 @@ IsSignatureFoundInDatabase (
// Find the signature in database.
//
IsFound = TRUE;
+ //
+ // Entries in UEFI_IMAGE_SECURITY_DATABASE that are used to validate image should be measured
+ //
+ if (StrCmp(VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) {
+ SecureBootHook (VariableName, &gEfiImageSecurityDatabaseGuid, CertList->SignatureSize, Cert);
+ }
break;
}
@@ -922,271 +1041,534 @@ Done:
}
/**
- Verify PKCS#7 SignedData using certificate found in Variable which formatted
- as EFI_SIGNATURE_LIST. The Variable may be PK, KEK, DB or DBX.
+ Check whether the timestamp is valid by comparing the signing time and the revocation time.
- @param VariableName Name of Variable to search for Certificate.
- @param VendorGuid Variable vendor GUID.
+ @param SigningTime A pointer to the signing time.
+ @param RevocationTime A pointer to the revocation time.
- @retval TRUE Image pass verification.
- @retval FALSE Image fail verification.
+ @retval TRUE The SigningTime is not later than the RevocationTime.
+ @retval FALSE The SigningTime is later than the RevocationTime.
**/
BOOLEAN
-IsPkcsSignedDataVerifiedBySignatureList (
- IN CHAR16 *VariableName,
- IN EFI_GUID *VendorGuid
+IsValidSignatureByTimestamp (
+ IN EFI_TIME *SigningTime,
+ IN EFI_TIME *RevocationTime
+ )
+{
+ if (SigningTime->Year != RevocationTime->Year) {
+ return (BOOLEAN) (SigningTime->Year < RevocationTime->Year);
+ } else if (SigningTime->Month != RevocationTime->Month) {
+ return (BOOLEAN) (SigningTime->Month < RevocationTime->Month);
+ } else if (SigningTime->Day != RevocationTime->Day) {
+ return (BOOLEAN) (SigningTime->Day < RevocationTime->Day);
+ } else if (SigningTime->Hour != RevocationTime->Hour) {
+ return (BOOLEAN) (SigningTime->Hour < RevocationTime->Hour);
+ } else if (SigningTime->Minute != RevocationTime->Minute) {
+ return (BOOLEAN) (SigningTime->Minute < RevocationTime->Minute);
+ }
+
+ return (BOOLEAN) (SigningTime->Second <= RevocationTime->Second);
+}
+
+/**
+ Check if the given time value is zero.
+
+ @param[in] Time Pointer of a time value.
+
+ @retval TRUE The Time is Zero.
+ @retval FALSE The Time is not Zero.
+
+**/
+BOOLEAN
+IsTimeZero (
+ IN EFI_TIME *Time
+ )
+{
+ if ((Time->Year == 0) && (Time->Month == 0) && (Time->Day == 0) &&
+ (Time->Hour == 0) && (Time->Minute == 0) && (Time->Second == 0)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Check whether the timestamp signature is valid and the signing time is also earlier than
+ the revocation time.
+
+ @param[in] AuthData Pointer to the Authenticode signature retrieved from signed image.
+ @param[in] AuthDataSize Size of the Authenticode signature in bytes.
+ @param[in] RevocationTime The time that the certificate was revoked.
+
+ @retval TRUE Timestamp signature is valid and signing time is no later than the
+ revocation time.
+ @retval FALSE Timestamp signature is not valid or the signing time is later than the
+ revocation time.
+
+**/
+BOOLEAN
+PassTimestampCheck (
+ IN UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ IN EFI_TIME *RevocationTime
)
{
EFI_STATUS Status;
BOOLEAN VerifyStatus;
- WIN_CERTIFICATE_EFI_PKCS *PkcsCertData;
EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *Cert;
- UINTN DataSize;
- UINT8 *Data;
+ UINT8 *DbtData;
+ UINTN DbtDataSize;
UINT8 *RootCert;
UINTN RootCertSize;
UINTN Index;
UINTN CertCount;
+ EFI_TIME SigningTime;
- Data = NULL;
- CertList = NULL;
- Cert = NULL;
- RootCert = NULL;
- RootCertSize = 0;
- VerifyStatus = FALSE;
- PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) (mImageBase + mSecDataDir->VirtualAddress);
-
- DataSize = 0;
- Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, NULL);
- if (Status == EFI_BUFFER_TOO_SMALL) {
- Data = (UINT8 *) AllocateZeroPool (DataSize);
- if (Data == NULL) {
- return VerifyStatus;
- }
+ //
+ // Variable Initialization
+ //
+ VerifyStatus = FALSE;
+ DbtData = NULL;
+ CertList = NULL;
+ Cert = NULL;
+ RootCert = NULL;
+ RootCertSize = 0;
- Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, (VOID *) Data);
- if (EFI_ERROR (Status)) {
- goto Done;
- }
+ //
+ // If RevocationTime is zero, the certificate shall be considered to always be revoked.
+ //
+ if (IsTimeZero (RevocationTime)) {
+ return FALSE;
+ }
- //
- // Find X509 certificate in Signature List to verify the signature in pkcs7 signed data.
- //
- CertList = (EFI_SIGNATURE_LIST *) Data;
- while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) {
- if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
- Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
- CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
- for (Index = 0; Index < CertCount; Index++) {
- //
- // Iterate each Signature Data Node within this CertList for verify.
- //
- RootCert = Cert->SignatureData;
- RootCertSize = CertList->SignatureSize - sizeof (EFI_GUID);
+ //
+ // RevocationTime is non-zero, the certificate should be considered to be revoked from that time and onwards.
+ // Using the dbt to get the trusted TSA certificates.
+ //
+ DbtDataSize = 0;
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE2, &gEfiImageSecurityDatabaseGuid, NULL, &DbtDataSize, NULL);
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ goto Done;
+ }
+ DbtData = (UINT8 *) AllocateZeroPool (DbtDataSize);
+ if (DbtData == NULL) {
+ goto Done;
+ }
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE2, &gEfiImageSecurityDatabaseGuid, NULL, &DbtDataSize, (VOID *) DbtData);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+ CertList = (EFI_SIGNATURE_LIST *) DbtData;
+ while ((DbtDataSize > 0) && (DbtDataSize >= CertList->SignatureListSize)) {
+ if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+ for (Index = 0; Index < CertCount; Index++) {
+ //
+ // Iterate each Signature Data Node within this CertList for verify.
+ //
+ RootCert = Cert->SignatureData;
+ RootCertSize = CertList->SignatureSize - sizeof (EFI_GUID);
+ //
+ // Get the signing time if the timestamp signature is valid.
+ //
+ if (ImageTimestampVerify (AuthData, AuthDataSize, RootCert, RootCertSize, &SigningTime)) {
//
- // Call AuthenticodeVerify library to Verify Authenticode struct.
+ // The signer signature is valid only when the signing time is earlier than revocation time.
//
- VerifyStatus = AuthenticodeVerify (
- PkcsCertData->CertData,
- PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr),
- RootCert,
- RootCertSize,
- mImageDigest,
- mImageDigestSize
- );
- if (VerifyStatus) {
+ if (IsValidSignatureByTimestamp (&SigningTime, RevocationTime)) {
+ VerifyStatus = TRUE;
goto Done;
}
- Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
}
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
}
- DataSize -= CertList->SignatureListSize;
- CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
}
+ DbtDataSize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
}
Done:
- if (Data != NULL) {
- FreePool (Data);
+ if (DbtData != NULL) {
+ FreePool (DbtData);
}
return VerifyStatus;
}
/**
- Verify certificate in WIN_CERT_TYPE_PKCS_SIGNED_DATA format.
+ Check whether the image signature is forbidden by the forbidden database (dbx).
+ The image is forbidden to load if any certificates for signing are revoked before signing time.
- @retval EFI_SUCCESS Image pass verification.
- @retval EFI_SECURITY_VIOLATION Image fail verification.
+ @param[in] AuthData Pointer to the Authenticode signature retrieved from the signed image.
+ @param[in] AuthDataSize Size of the Authenticode signature in bytes.
+
+ @retval TRUE Image is forbidden by dbx.
+ @retval FALSE Image is not forbidden by dbx.
**/
-EFI_STATUS
-VerifyCertPkcsSignedData (
- VOID
+BOOLEAN
+IsForbiddenByDbx (
+ IN UINT8 *AuthData,
+ IN UINTN AuthDataSize
)
{
+ EFI_STATUS Status;
+ BOOLEAN IsForbidden;
+ BOOLEAN IsFound;
+ UINT8 *Data;
+ UINTN DataSize;
+ EFI_SIGNATURE_LIST *CertList;
+ UINTN CertListSize;
+ EFI_SIGNATURE_DATA *CertData;
+ UINT8 *RootCert;
+ UINTN RootCertSize;
+ UINTN CertCount;
+ UINTN Index;
+ UINT8 *CertBuffer;
+ UINTN BufferLength;
+ UINT8 *TrustedCert;
+ UINTN TrustedCertLength;
+ UINT8 CertNumber;
+ UINT8 *CertPtr;
+ UINT8 *Cert;
+ UINTN CertSize;
+ EFI_TIME RevocationTime;
+ //
+ // Variable Initialization
+ //
+ IsForbidden = TRUE;
+ Data = NULL;
+ CertList = NULL;
+ CertData = NULL;
+ RootCert = NULL;
+ RootCertSize = 0;
+ Cert = NULL;
+ CertBuffer = NULL;
+ BufferLength = 0;
+ TrustedCert = NULL;
+ TrustedCertLength = 0;
+
+ //
+ // The image will not be forbidden if dbx can't be got.
//
- // 1: Find certificate from DBX forbidden database for revoked certificate.
- //
- if (IsPkcsSignedDataVerifiedBySignatureList (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid)) {
- //
- // DBX is forbidden database, if Authenticode verification pass with
- // one of the certificate in DBX, this image should be rejected.
- //
- return EFI_SECURITY_VIOLATION;
+ DataSize = 0;
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, NULL);
+ ASSERT (EFI_ERROR (Status));
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ if (Status == EFI_NOT_FOUND) {
+ //
+ // Evidently not in dbx if the database doesn't exist.
+ //
+ IsForbidden = FALSE;
+ }
+ return IsForbidden;
+ }
+ Data = (UINT8 *) AllocateZeroPool (DataSize);
+ if (Data == NULL) {
+ return IsForbidden;
}
- //
- // 2: Find certificate from KEK database and try to verify authenticode struct.
- //
- if (IsPkcsSignedDataVerifiedBySignatureList (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid)) {
- return EFI_SUCCESS;
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, (VOID *) Data);
+ if (EFI_ERROR (Status)) {
+ return IsForbidden;
}
//
- // 3: Find certificate from DB database and try to verify authenticode struct.
+ // Verify image signature with RAW X509 certificates in DBX database.
+ // If passed, the image will be forbidden.
//
- if (IsPkcsSignedDataVerifiedBySignatureList (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid)) {
- return EFI_SUCCESS;
- } else {
- return EFI_SECURITY_VIOLATION;
- }
-}
+ CertList = (EFI_SIGNATURE_LIST *) Data;
+ CertListSize = DataSize;
+ while ((CertListSize > 0) && (CertListSize >= CertList->SignatureListSize)) {
+ if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
+ CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
-/**
- Verify certificate in WIN_CERTIFICATE_UEFI_GUID format.
+ for (Index = 0; Index < CertCount; Index++) {
+ //
+ // Iterate each Signature Data Node within this CertList for verify.
+ //
+ RootCert = CertData->SignatureData;
+ RootCertSize = CertList->SignatureSize - sizeof (EFI_GUID);
- @retval EFI_SUCCESS Image pass verification.
- @retval EFI_SECURITY_VIOLATION Image fail verification.
- @retval other error value
+ //
+ // Call AuthenticodeVerify library to Verify Authenticode struct.
+ //
+ IsForbidden = AuthenticodeVerify (
+ AuthData,
+ AuthDataSize,
+ RootCert,
+ RootCertSize,
+ mImageDigest,
+ mImageDigestSize
+ );
+ if (IsForbidden) {
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature is forbidden by DBX.\n"));
+ goto Done;
+ }
-**/
-EFI_STATUS
-VerifyCertUefiGuid (
- VOID
- )
-{
- BOOLEAN Status;
- WIN_CERTIFICATE_UEFI_GUID *EfiCert;
- EFI_SIGNATURE_LIST *KekList;
- EFI_SIGNATURE_DATA *KekItem;
- EFI_CERT_BLOCK_RSA_2048_SHA256 *CertBlock;
- VOID *Rsa;
- UINTN KekCount;
- UINTN Index;
- UINTN KekDataSize;
- BOOLEAN IsFound;
- EFI_STATUS Result;
-
- EfiCert = NULL;
- KekList = NULL;
- KekItem = NULL;
- CertBlock = NULL;
- Rsa = NULL;
- Status = FALSE;
- IsFound = FALSE;
- KekDataSize = 0;
+ CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertData + CertList->SignatureSize);
+ }
+ }
- EfiCert = (WIN_CERTIFICATE_UEFI_GUID *) (mImageBase + mSecDataDir->VirtualAddress);
- CertBlock = (EFI_CERT_BLOCK_RSA_2048_SHA256 *) EfiCert->CertData;
- if (!CompareGuid (&EfiCert->CertType, &gEfiCertTypeRsa2048Sha256Guid)) {
- //
- // Invalid Certificate Data Type.
- //
- return EFI_SECURITY_VIOLATION;
+ CertListSize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
}
//
- // Get KEK database variable data size
+ // Check X.509 Certificate Hash & Possible Timestamp.
//
- Result = gRT->GetVariable (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid, NULL, &KekDataSize, NULL);
- if (Result != EFI_BUFFER_TOO_SMALL) {
- return EFI_SECURITY_VIOLATION;
- }
//
- // Get KEK database variable.
+ // Retrieve the certificate stack from AuthData
+ // The output CertStack format will be:
+ // UINT8 CertNumber;
+ // UINT32 Cert1Length;
+ // UINT8 Cert1[];
+ // UINT32 Cert2Length;
+ // UINT8 Cert2[];
+ // ...
+ // UINT32 CertnLength;
+ // UINT8 Certn[];
//
- GetEfiGlobalVariable2 (EFI_KEY_EXCHANGE_KEY_NAME, (VOID**)&KekList, NULL);
- if (KekList == NULL) {
- return EFI_SECURITY_VIOLATION;
+ Pkcs7GetSigners (AuthData, AuthDataSize, &CertBuffer, &BufferLength, &TrustedCert, &TrustedCertLength);
+ if ((BufferLength == 0) || (CertBuffer == NULL) || (*CertBuffer) == 0) {
+ IsForbidden = TRUE;
+ goto Done;
}
//
- // Enumerate all Kek items in this list to verify the variable certificate data.
- // If anyone is authenticated successfully, it means the variable is correct!
+ // Check if any hash of certificates embedded in AuthData is in the forbidden database.
//
- while ((KekDataSize > 0) && (KekDataSize >= KekList->SignatureListSize)) {
- if (CompareGuid (&KekList->SignatureType, &gEfiCertRsa2048Guid)) {
- KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekList + sizeof (EFI_SIGNATURE_LIST) + KekList->SignatureHeaderSize);
- KekCount = (KekList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - KekList->SignatureHeaderSize) / KekList->SignatureSize;
- for (Index = 0; Index < KekCount; Index++) {
- if (CompareMem (KekItem->SignatureData, CertBlock->PublicKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) {
- IsFound = TRUE;
- break;
- }
- KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekItem + KekList->SignatureSize);
+ CertNumber = (UINT8) (*CertBuffer);
+ CertPtr = CertBuffer + 1;
+ for (Index = 0; Index < CertNumber; Index++) {
+ CertSize = (UINTN) ReadUnaligned32 ((UINT32 *)CertPtr);
+ Cert = (UINT8 *)CertPtr + sizeof (UINT32);
+ //
+ // Advance CertPtr to the next cert in image signer's cert list
+ //
+ CertPtr = CertPtr + sizeof (UINT32) + CertSize;
+
+ Status = IsCertHashFoundInDatabase (Cert, CertSize, (EFI_SIGNATURE_LIST *)Data, DataSize, &RevocationTime, &IsFound);
+ if (EFI_ERROR (Status)) {
+ //
+ // Error in searching dbx. Consider it as 'found'. RevocationTime might
+ // not be valid in such situation.
+ //
+ IsForbidden = TRUE;
+ } else if (IsFound) {
+ //
+ // Found Cert in dbx successfully. Check the timestamp signature and
+ // signing time to determine if the image can be trusted.
+ //
+ if (PassTimestampCheck (AuthData, AuthDataSize, &RevocationTime)) {
+ IsForbidden = FALSE;
+ //
+ // Pass DBT check. Continue to check other certs in image signer's cert list against DBX, DBT
+ //
+ continue;
+ } else {
+ IsForbidden = TRUE;
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature failed the timestamp check.\n"));
+ goto Done;
}
}
- KekDataSize -= KekList->SignatureListSize;
- KekList = (EFI_SIGNATURE_LIST *) ((UINT8 *) KekList + KekList->SignatureListSize);
+
}
- if (!IsFound) {
- //
- // Signed key is not a trust one.
- //
- goto Done;
+ IsForbidden = FALSE;
+
+Done:
+ if (Data != NULL) {
+ FreePool (Data);
}
+ Pkcs7FreeSigners (CertBuffer);
+ Pkcs7FreeSigners (TrustedCert);
+
+ return IsForbidden;
+}
+
+
+/**
+ Check whether the image signature can be verified by the trusted certificates in DB database.
+
+ @param[in] AuthData Pointer to the Authenticode signature retrieved from signed image.
+ @param[in] AuthDataSize Size of the Authenticode signature in bytes.
+
+ @retval TRUE Image passed verification using certificate in db.
+ @retval FALSE Image didn't pass verification using certificate in db.
+
+**/
+BOOLEAN
+IsAllowedByDb (
+ IN UINT8 *AuthData,
+ IN UINTN AuthDataSize
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN VerifyStatus;
+ BOOLEAN IsFound;
+ EFI_SIGNATURE_LIST *CertList;
+ EFI_SIGNATURE_DATA *CertData;
+ UINTN DataSize;
+ UINT8 *Data;
+ UINT8 *RootCert;
+ UINTN RootCertSize;
+ UINTN Index;
+ UINTN CertCount;
+ UINTN DbxDataSize;
+ UINT8 *DbxData;
+ EFI_TIME RevocationTime;
+
+ Data = NULL;
+ CertList = NULL;
+ CertData = NULL;
+ RootCert = NULL;
+ DbxData = NULL;
+ RootCertSize = 0;
+ VerifyStatus = FALSE;
+
//
- // Now, we found the corresponding security policy.
- // Verify the data payload.
+ // Fetch 'db' content. If 'db' doesn't exist or encounters problem to get the
+ // data, return not-allowed-by-db (FALSE).
//
- Rsa = RsaNew ();
- if (Rsa == NULL) {
- Status = FALSE;
+ DataSize = 0;
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, NULL);
+ ASSERT (EFI_ERROR (Status));
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return VerifyStatus;
+ }
+
+ Data = (UINT8 *) AllocateZeroPool (DataSize);
+ if (Data == NULL) {
+ return VerifyStatus;
+ }
+
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, (VOID *) Data);
+ if (EFI_ERROR (Status)) {
goto Done;
}
//
- // Set RSA Key Components.
- // NOTE: Only N and E are needed to be set as RSA public key for signature verification.
+ // Fetch 'dbx' content. If 'dbx' doesn't exist, continue to check 'db'.
+ // If any other errors occured, no need to check 'db' but just return
+ // not-allowed-by-db (FALSE) to avoid bypass.
//
- Status = RsaSetKey (Rsa, RsaKeyN, CertBlock->PublicKey, EFI_CERT_TYPE_RSA2048_SIZE);
- if (!Status) {
- goto Done;
- }
- Status = RsaSetKey (Rsa, RsaKeyE, mRsaE, sizeof (mRsaE));
- if (!Status) {
- goto Done;
+ DbxDataSize = 0;
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DbxDataSize, NULL);
+ ASSERT (EFI_ERROR (Status));
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ if (Status != EFI_NOT_FOUND) {
+ goto Done;
+ }
+ //
+ // 'dbx' does not exist. Continue to check 'db'.
+ //
+ } else {
+ //
+ // 'dbx' exists. Get its content.
+ //
+ DbxData = (UINT8 *) AllocateZeroPool (DbxDataSize);
+ if (DbxData == NULL) {
+ goto Done;
+ }
+
+ Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DbxDataSize, (VOID *) DbxData);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
}
+
//
- // Verify the signature.
+ // Find X509 certificate in Signature List to verify the signature in pkcs7 signed data.
//
- Status = RsaPkcs1Verify (
- Rsa,
- mImageDigest,
- mImageDigestSize,
- CertBlock->Signature,
- EFI_CERT_TYPE_RSA2048_SHA256_SIZE
- );
+ CertList = (EFI_SIGNATURE_LIST *) Data;
+ while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) {
+ if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
+ CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+
+ for (Index = 0; Index < CertCount; Index++) {
+ //
+ // Iterate each Signature Data Node within this CertList for verify.
+ //
+ RootCert = CertData->SignatureData;
+ RootCertSize = CertList->SignatureSize - sizeof (EFI_GUID);
+
+ //
+ // Call AuthenticodeVerify library to Verify Authenticode struct.
+ //
+ VerifyStatus = AuthenticodeVerify (
+ AuthData,
+ AuthDataSize,
+ RootCert,
+ RootCertSize,
+ mImageDigest,
+ mImageDigestSize
+ );
+ if (VerifyStatus) {
+ //
+ // The image is signed and its signature is found in 'db'.
+ //
+ if (DbxData != NULL) {
+ //
+ // Here We still need to check if this RootCert's Hash is revoked
+ //
+ Status = IsCertHashFoundInDatabase (RootCert, RootCertSize, (EFI_SIGNATURE_LIST *)DbxData, DbxDataSize, &RevocationTime, &IsFound);
+ if (EFI_ERROR (Status)) {
+ //
+ // Error in searching dbx. Consider it as 'found'. RevocationTime might
+ // not be valid in such situation.
+ //
+ VerifyStatus = FALSE;
+ } else if (IsFound) {
+ //
+ // Check the timestamp signature and signing time to determine if the RootCert can be trusted.
+ //
+ VerifyStatus = PassTimestampCheck (AuthData, AuthDataSize, &RevocationTime);
+ if (!VerifyStatus) {
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed and signature is accepted by DB, but its root cert failed the timestamp check.\n"));
+ }
+ }
+ }
+
+ //
+ // There's no 'dbx' to check revocation time against (must-be pass),
+ // or, there's revocation time found in 'dbx' and checked againt 'dbt'
+ // (maybe pass or fail, depending on timestamp compare result). Either
+ // way the verification job has been completed at this point.
+ //
+ goto Done;
+ }
+
+ CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertData + CertList->SignatureSize);
+ }
+ }
+
+ DataSize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
+ }
Done:
- if (KekList != NULL) {
- FreePool (KekList);
+
+ if (VerifyStatus) {
+ SecureBootHook (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, CertList->SignatureSize, CertData);
}
- if (Rsa != NULL ) {
- RsaFree (Rsa);
+
+ if (Data != NULL) {
+ FreePool (Data);
}
- if (Status) {
- return EFI_SUCCESS;
- } else {
- return EFI_SECURITY_VIOLATION;
+ if (DbxData != NULL) {
+ FreePool (DbxData);
}
+
+ return VerifyStatus;
}
/**
@@ -1197,23 +1579,14 @@ Done:
In this implementation, only verify external executables when in USER MODE.
Executables from FV is bypass, so pass in AuthenticationStatus is ignored.
- The image verification process is:
- Is the Image signed?
- If yes,
- Does the image verify against a certificate (root or intermediate) in the allowed db?
- Run it
- Image verification fail
- Is the Image's Hash not in forbidden database and the Image's Hash in allowed db?
- Run it
- If no,
- Is the Image's Hash in the forbidden database (DBX)?
- if yes,
- Error out
- Is the Image's Hash in the allowed database (DB)?
- If yes,
- Run it
- If no,
- Error out
+ The image verification policy is:
+ If the image is signed,
+ At least one valid signature or at least one hash value of the image must match a record
+ in the security database "db", and no valid signature nor any hash value of the image may
+ be reflected in the security database "dbx".
+ Otherwise, the image is not signed,
+ The SHA256 hash value of the image must match a record in the security database "db", and
+ not be reflected in the security data base "dbx".
Caution: This function may receive untrusted input.
PE/COFF image is external input, so this function will validate its data structure
@@ -1226,19 +1599,23 @@ Done:
being dispatched. This will optionally be used for logging.
@param[in] FileBuffer File buffer matches the input file device path.
@param[in] FileSize Size of File buffer matches the input file device path.
-
- @retval EFI_SUCCESS The file specified by File did authenticate, and the
- platform policy dictates that the DXE Core may use File.
- @retval EFI_INVALID_PARAMETER Input argument is incorrect.
- @retval EFI_OUT_RESOURCE Fail to allocate memory.
+ @param[in] BootPolicy A boot policy that was used to call LoadImage() UEFI service.
+
+ @retval EFI_SUCCESS The file specified by DevicePath and non-NULL
+ FileBuffer did authenticate, and the platform policy dictates
+ that the DXE Foundation may use the file.
+ @retval EFI_SUCCESS The device path specified by NULL device path DevicePath
+ and non-NULL FileBuffer did authenticate, and the platform
+ policy dictates that the DXE Foundation may execute the image in
+ FileBuffer.
@retval EFI_SECURITY_VIOLATION The file specified by File did not authenticate, and
the platform policy dictates that File should be placed
- in the untrusted state. A file may be promoted from
- the untrusted to the trusted state at a future time
- with a call to the Trust() DXE Service.
- @retval EFI_ACCESS_DENIED The file specified by File did not authenticate, and
- the platform policy dictates that File should not be
- used for any purpose.
+ in the untrusted state. The image has been added to the file
+ execution table.
+ @retval EFI_ACCESS_DENIED The file specified by File and FileBuffer did not
+ authenticate, and the platform policy dictates that the DXE
+ Foundation may not use File. The image has
+ been added to the file execution table.
**/
EFI_STATUS
@@ -1247,13 +1624,12 @@ DxeImageVerificationHandler (
IN UINT32 AuthenticationStatus,
IN CONST EFI_DEVICE_PATH_PROTOCOL *File,
IN VOID *FileBuffer,
- IN UINTN FileSize
+ IN UINTN FileSize,
+ IN BOOLEAN BootPolicy
)
{
- EFI_STATUS Status;
- UINT16 Magic;
EFI_IMAGE_DOS_HEADER *DosHdr;
- EFI_STATUS VerifyStatus;
+ BOOLEAN IsVerified;
EFI_SIGNATURE_LIST *SignatureList;
UINTN SignatureListSize;
EFI_SIGNATURE_DATA *Signature;
@@ -1263,17 +1639,25 @@ DxeImageVerificationHandler (
UINT8 *SecureBoot;
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
UINT32 NumberOfRvaAndSizes;
- UINT32 CertSize;
-
- if (File == NULL) {
- return EFI_INVALID_PARAMETER;
- }
+ WIN_CERTIFICATE_EFI_PKCS *PkcsCertData;
+ WIN_CERTIFICATE_UEFI_GUID *WinCertUefiGuid;
+ UINT8 *AuthData;
+ UINTN AuthDataSize;
+ EFI_IMAGE_DATA_DIRECTORY *SecDataDir;
+ UINT32 OffSet;
+ CHAR16 *NameStr;
+ RETURN_STATUS PeCoffStatus;
+ EFI_STATUS HashStatus;
SignatureList = NULL;
SignatureListSize = 0;
WinCertificate = NULL;
+ SecDataDir = NULL;
+ PkcsCertData = NULL;
Action = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
- Status = EFI_ACCESS_DENIED;
+ IsVerified = FALSE;
+
+
//
// Check the image type and get policy setting.
//
@@ -1304,10 +1688,20 @@ DxeImageVerificationHandler (
//
if (Policy == ALWAYS_EXECUTE) {
return EFI_SUCCESS;
- } else if (Policy == NEVER_EXECUTE) {
+ }
+ if (Policy == NEVER_EXECUTE) {
return EFI_ACCESS_DENIED;
}
+ //
+ // The policy QUERY_USER_ON_SECURITY_VIOLATION and ALLOW_EXECUTE_ON_SECURITY_VIOLATION
+ // violates the UEFI spec and has been removed.
+ //
+ ASSERT (Policy != QUERY_USER_ON_SECURITY_VIOLATION && Policy != ALLOW_EXECUTE_ON_SECURITY_VIOLATION);
+ if (Policy == QUERY_USER_ON_SECURITY_VIOLATION || Policy == ALLOW_EXECUTE_ON_SECURITY_VIOLATION) {
+ CpuDeadLoop ();
+ }
+
GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME, (VOID**)&SecureBoot, NULL);
//
// Skip verification if SecureBoot variable doesn't exist.
@@ -1317,7 +1711,7 @@ DxeImageVerificationHandler (
}
//
- // Skip verification if SecureBoot is disabled.
+ // Skip verification if SecureBoot is disabled but not AuditMode
//
if (*SecureBoot == SECURE_BOOT_MODE_DISABLE) {
FreePool (SecureBoot);
@@ -1329,7 +1723,7 @@ DxeImageVerificationHandler (
// Read the Dos header.
//
if (FileBuffer == NULL) {
- return EFI_INVALID_PARAMETER;
+ return EFI_ACCESS_DENIED;
}
mImageBase = (UINT8 *) FileBuffer;
@@ -1342,16 +1736,15 @@ DxeImageVerificationHandler (
//
// Get information about the image being loaded
//
- Status = PeCoffLoaderGetImageInfo (&ImageContext);
- if (EFI_ERROR (Status)) {
+ PeCoffStatus = PeCoffLoaderGetImageInfo (&ImageContext);
+ if (RETURN_ERROR (PeCoffStatus)) {
//
// The information can't be got from the invalid PeImage
//
- goto Done;
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: PeImage invalid. Cannot retrieve image information.\n"));
+ goto Failed;
}
- Status = EFI_ACCESS_DENIED;
-
DosHdr = (EFI_IMAGE_DOS_HEADER *) mImageBase;
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
//
@@ -1370,55 +1763,47 @@ DxeImageVerificationHandler (
//
// It is not a valid Pe/Coff file.
//
- goto Done;
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Not a valid PE/COFF image.\n"));
+ goto Failed;
}
- if (mNtHeader.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 && mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
- //
- // NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value
- // in the PE/COFF Header. If the MachineType is Itanium(IA64) and the
- // Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
- // then override the magic value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
- //
- Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
- } else {
- //
- // Get the magic value from the PE/COFF Optional Header
- //
- Magic = mNtHeader.Pe32->OptionalHeader.Magic;
- }
-
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//
// Use PE32 offset.
//
NumberOfRvaAndSizes = mNtHeader.Pe32->OptionalHeader.NumberOfRvaAndSizes;
if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
- mSecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
- }
+ SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
+ }
} else {
//
// Use PE32+ offset.
//
NumberOfRvaAndSizes = mNtHeader.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
- mSecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
+ SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
}
}
- if ((mSecDataDir == NULL) || ((mSecDataDir != NULL) && (mSecDataDir->Size == 0))) {
+ //
+ // Start Image Validation.
+ //
+ if (SecDataDir == NULL || SecDataDir->Size == 0) {
//
- // This image is not signed.
+ // This image is not signed. The SHA256 hash value of the image must match a record in the security database "db",
+ // and not be reflected in the security data base "dbx".
//
if (!HashPeImage (HASHALG_SHA256)) {
- goto Done;
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Failed to hash this image using %s.\n", mHashTypeStr));
+ goto Failed;
}
if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) {
//
// Image Hash is in forbidden database (DBX).
//
- goto Done;
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is not signed and %s hash of image is forbidden by DBX.\n", mHashTypeStr));
+ goto Failed;
}
if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, mImageDigest, &mCertType, mImageDigestSize)) {
@@ -1431,176 +1816,182 @@ DxeImageVerificationHandler (
//
// Image Hash is not found in both forbidden and allowed database.
//
- goto Done;
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is not signed and %s hash of image is not found in DB/DBX.\n", mHashTypeStr));
+ goto Failed;
}
//
- // Verify signature of executables.
+ // Verify the signature of the image, multiple signatures are allowed as per PE/COFF Section 4.7
+ // "Attribute Certificate Table".
+ // The first certificate starts at offset (SecDataDir->VirtualAddress) from the start of the file.
//
- WinCertificate = (WIN_CERTIFICATE *) (mImageBase + mSecDataDir->VirtualAddress);
-
- CertSize = sizeof (WIN_CERTIFICATE);
-
- if ((mSecDataDir->Size <= CertSize) || (mSecDataDir->Size < WinCertificate->dwLength)) {
- goto Done;
- }
-
- switch (WinCertificate->wCertificateType) {
-
- case WIN_CERT_TYPE_EFI_GUID:
- CertSize = sizeof (WIN_CERTIFICATE_UEFI_GUID) + sizeof (EFI_CERT_BLOCK_RSA_2048_SHA256) - sizeof (UINT8);
- if (WinCertificate->dwLength < CertSize) {
- goto Done;
+ for (OffSet = SecDataDir->VirtualAddress;
+ OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
+ OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {
+ WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);
+ if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof (WIN_CERTIFICATE) ||
+ (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength) {
+ break;
}
//
- // Verify UEFI GUID type.
+ // Verify the image's Authenticode signature, only DER-encoded PKCS#7 signed data is supported.
//
- if (!HashPeImage (HASHALG_SHA256)) {
- goto Done;
+ if (WinCertificate->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
+ //
+ // The certificate is formatted as WIN_CERTIFICATE_EFI_PKCS which is described in the
+ // Authenticode specification.
+ //
+ PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) WinCertificate;
+ if (PkcsCertData->Hdr.dwLength <= sizeof (PkcsCertData->Hdr)) {
+ break;
+ }
+ AuthData = PkcsCertData->CertData;
+ AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr);
+ } else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID) {
+ //
+ // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which is described in UEFI Spec.
+ //
+ WinCertUefiGuid = (WIN_CERTIFICATE_UEFI_GUID *) WinCertificate;
+ if (WinCertUefiGuid->Hdr.dwLength <= OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {
+ break;
+ }
+ if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {
+ continue;
+ }
+ AuthData = WinCertUefiGuid->CertData;
+ AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
+ } else {
+ if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {
+ break;
+ }
+ continue;
}
- VerifyStatus = VerifyCertUefiGuid ();
- break;
+ HashStatus = HashPeImageByType (AuthData, AuthDataSize);
+ if (EFI_ERROR (HashStatus)) {
+ continue;
+ }
- case WIN_CERT_TYPE_PKCS_SIGNED_DATA:
//
- // Verify Pkcs signed data type.
+ // Check the digital signature against the revoked certificate in forbidden database (dbx).
//
- Status = HashPeImageByType();
- if (EFI_ERROR (Status)) {
- goto Done;
+ if (IsForbiddenByDbx (AuthData, AuthDataSize)) {
+ Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
+ IsVerified = FALSE;
+ break;
}
- VerifyStatus = VerifyCertPkcsSignedData ();
-
//
- // For image verification against enrolled certificate(root or intermediate),
- // no need to check image's hash in the allowed database.
+ // Check the digital signature against the valid certificate in allowed database (db).
//
- if (!EFI_ERROR (VerifyStatus)) {
- if (!IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) {
- return EFI_SUCCESS;
+ if (!IsVerified) {
+ if (IsAllowedByDb (AuthData, AuthDataSize)) {
+ IsVerified = TRUE;
}
}
- break;
- default:
- goto Done;
- }
- //
- // Get image hash value as executable's signature.
- //
- SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + mImageDigestSize;
- SignatureList = (EFI_SIGNATURE_LIST *) AllocateZeroPool (SignatureListSize);
- if (SignatureList == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto Done;
- }
- SignatureList->SignatureHeaderSize = 0;
- SignatureList->SignatureListSize = (UINT32) SignatureListSize;
- SignatureList->SignatureSize = (UINT32) mImageDigestSize;
- CopyMem (&SignatureList->SignatureType, &mCertType, sizeof (EFI_GUID));
- Signature = (EFI_SIGNATURE_DATA *) ((UINT8 *) SignatureList + sizeof (EFI_SIGNATURE_LIST));
- CopyMem (Signature->SignatureData, mImageDigest, mImageDigestSize);
- //
- // Signature database check after verification.
- //
- if (EFI_ERROR (VerifyStatus)) {
//
- // Verification failure.
+ // Check the image's hash value.
//
- if (!IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize) &&
- IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, mImageDigest, &mCertType, mImageDigestSize)) {
- //
- // Verification fail, Image Hash is not in forbidden database (DBX),
- // and Image Hash is in allowed database (DB).
- //
- Status = EFI_SUCCESS;
- } else {
- Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
- Status = EFI_ACCESS_DENIED;
+ if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) {
+ Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but %s hash of image is found in DBX.\n", mHashTypeStr));
+ IsVerified = FALSE;
+ break;
}
- } else if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, Signature->SignatureData, &mCertType, mImageDigestSize)) {
- //
- // Executable signature verification passes, but is found in forbidden signature database.
- //
- Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
- Status = EFI_ACCESS_DENIED;
- } else if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, Signature->SignatureData, &mCertType, mImageDigestSize)) {
- //
- // Executable signature is found in authorized signature database.
- //
- Status = EFI_SUCCESS;
- } else {
+ if (!IsVerified) {
+ if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, mImageDigest, &mCertType, mImageDigestSize)) {
+ IsVerified = TRUE;
+ } else {
+ 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));
+ }
+ }
+ }
+
+ if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {
//
- // Executable signature verification passes, but cannot be found in authorized signature database.
- // Get platform policy to determine the action.
+ // The Size in Certificate Table or the attribute certificate table is corrupted.
//
- Action = EFI_IMAGE_EXECUTION_AUTH_SIG_PASSED;
- Status = ImageAuthorization (Policy);
+ IsVerified = FALSE;
}
-Done:
- if (Status != EFI_SUCCESS) {
+ if (IsVerified) {
+ return EFI_SUCCESS;
+ }
+ if (Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED || Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND) {
//
- // Policy decides to defer or reject the image; add its information in image executable information table.
+ // Get image hash value as signature of executable.
//
- AddImageExeInfo (Action, NULL, File, SignatureList, SignatureListSize);
+ SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + mImageDigestSize;
+ SignatureList = (EFI_SIGNATURE_LIST *) AllocateZeroPool (SignatureListSize);
+ if (SignatureList == NULL) {
+ SignatureListSize = 0;
+ goto Failed;
+ }
+ SignatureList->SignatureHeaderSize = 0;
+ SignatureList->SignatureListSize = (UINT32) SignatureListSize;
+ SignatureList->SignatureSize = (UINT32) (sizeof (EFI_SIGNATURE_DATA) - 1 + mImageDigestSize);
+ CopyMem (&SignatureList->SignatureType, &mCertType, sizeof (EFI_GUID));
+ Signature = (EFI_SIGNATURE_DATA *) ((UINT8 *) SignatureList + sizeof (EFI_SIGNATURE_LIST));
+ CopyMem (Signature->SignatureData, mImageDigest, mImageDigestSize);
+ }
+
+Failed:
+ //
+ // Policy decides to defer or reject the image; add its information in image
+ // executable information table in either case.
+ //
+ NameStr = ConvertDevicePathToText (File, FALSE, TRUE);
+ AddImageExeInfo (Action, NameStr, File, SignatureList, SignatureListSize);
+ if (NameStr != NULL) {
+ DEBUG ((DEBUG_INFO, "The image doesn't pass verification: %s\n", NameStr));
+ FreePool(NameStr);
}
if (SignatureList != NULL) {
FreePool (SignatureList);
}
- return Status;
+ if (Policy == DEFER_EXECUTE_ON_SECURITY_VIOLATION) {
+ return EFI_SECURITY_VIOLATION;
+ }
+ return EFI_ACCESS_DENIED;
}
/**
- When VariableWriteArchProtocol install, create "SecureBoot" variable.
+ On Ready To Boot Services Event notification handler.
+
+ Add the image execution information table if it is not in system configuration table.
- @param[in] Event Event whose notification function is being invoked.
- @param[in] Context Pointer to the notification function's context.
+ @param[in] Event Event whose notification function is being invoked
+ @param[in] Context Pointer to the notification function's context
**/
VOID
EFIAPI
-VariableWriteCallBack (
- IN EFI_EVENT Event,
- IN VOID *Context
+OnReadyToBoot (
+ IN EFI_EVENT Event,
+ IN VOID *Context
)
{
- UINT8 SecureBootMode;
- UINT8 *SecureBootModePtr;
- EFI_STATUS Status;
- VOID *ProtocolPointer;
+ EFI_IMAGE_EXECUTION_INFO_TABLE *ImageExeInfoTable;
+ UINTN ImageExeInfoTableSize;
- Status = gBS->LocateProtocol (&gEfiVariableWriteArchProtocolGuid, NULL, &ProtocolPointer);
- if (EFI_ERROR (Status)) {
+ EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID **) &ImageExeInfoTable);
+ if (ImageExeInfoTable != NULL) {
return;
}
- //
- // Check whether "SecureBoot" variable exists.
- // If this library is built-in, it means firmware has capability to perform
- // driver signing verification.
- //
- GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME, (VOID**)&SecureBootModePtr, NULL);
- if (SecureBootModePtr == NULL) {
- SecureBootMode = SECURE_BOOT_MODE_DISABLE;
- //
- // Authenticated variable driver will update "SecureBoot" depending on SetupMode variable.
- //
- gRT->SetVariable (
- EFI_SECURE_BOOT_MODE_NAME,
- &gEfiGlobalVariableGuid,
- EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
- sizeof (UINT8),
- &SecureBootMode
- );
- } else {
- FreePool (SecureBootModePtr);
+ ImageExeInfoTableSize = sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE);
+ ImageExeInfoTable = (EFI_IMAGE_EXECUTION_INFO_TABLE *) AllocateRuntimePool (ImageExeInfoTableSize);
+ if (ImageExeInfoTable == NULL) {
+ return ;
}
+
+ ImageExeInfoTable->NumberOfImages = 0;
+ gBS->InstallConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID *) ImageExeInfoTable);
+
}
/**
@@ -1618,20 +2009,19 @@ DxeImageVerificationLibConstructor (
IN EFI_SYSTEM_TABLE *SystemTable
)
{
- VOID *Registration;
+ EFI_EVENT Event;
//
- // Register callback function upon VariableWriteArchProtocol.
+ // Register the event to publish the image execution table.
//
- EfiCreateProtocolNotifyEvent (
- &gEfiVariableWriteArchProtocolGuid,
+ EfiCreateEventReadyToBootEx (
TPL_CALLBACK,
- VariableWriteCallBack,
+ OnReadyToBoot,
NULL,
- &Registration
+ &Event
);
- return RegisterSecurityHandler (
+ return RegisterSecurity2Handler (
DxeImageVerificationHandler,
EFI_AUTH_OPERATION_VERIFY_IMAGE | EFI_AUTH_OPERATION_IMAGE_REQUIRED
);