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