CloverBootloader/rEFIt_UEFI/entry_scan/securehash.c

894 lines
34 KiB
C
Raw Normal View History

/*
* refit/scan/securehash.c
*
* Copyright (c) 2006-2010 Christoph Pfisterer
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Christoph Pfisterer nor the names of the
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
CONST INTN SecureHashDef = 0;
#ifdef ENABLE_SECURE_BOOT
#include "entry_scan.h"
#include <openssl/sha.h>
#include <Guid/ImageAuthentication.h>
#ifndef DEBUG_ALL
#define DEBUG_SECURE_HASH 1
#else
#define DEBUG_SECURE_HASH DEBUG_ALL
#endif
#if DEBUG_SECURE_HASH == 0
#define DBG(...)
#else
#define DBG(...) DebugLog(DEBUG_SECURE_HASH, __VA_ARGS__)
#endif
#define SECDIR_ALIGNMENT_SIZE 8
#define CERT_SIZE (sizeof(UINT32) + sizeof(UINT16) + sizeof(UINT16))
#define PKCS1_1_5_SIZE (CERT_SIZE + sizeof(EFI_GUID))
#define EFIGUID_SIZE (CERT_SIZE + sizeof(EFI_GUID))
// Check database for signature
STATIC EFI_STATUS CheckSignatureIsInDatabase(IN VOID *Database,
IN UINTN DatabaseSize,
IN EFI_GUID *SignatureType,
IN VOID *Signature,
IN UINTN SignatureSize)
{
UINT8 *DatabasePtr;
UINT8 *DatabaseEnd;
// Check parameters
if ((SignatureType == NULL) || (Signature == NULL) || (SignatureSize == 0)) {
return EFI_INVALID_PARAMETER;
}
if ((Database == NULL) || (DatabaseSize <= sizeof(EFI_SIGNATURE_LIST))) {
return EFI_NOT_FOUND;
}
// Get the database start and end
DatabasePtr = (UINT8 *)Database;
DatabaseEnd = DatabasePtr + DatabaseSize;
// Traverse the database
while (DatabasePtr < DatabaseEnd) {
EFI_SIGNATURE_LIST *SignatureList = (EFI_SIGNATURE_LIST *)DatabasePtr;
UINT8 *Ptr, *End;
// Check the list is valid
if ((SignatureList->SignatureListSize <= sizeof(EFI_SIGNATURE_LIST)) || (SignatureList->SignatureSize <= sizeof(EFI_GUID))) {
return EFI_INVALID_PARAMETER;
}
// Check if this signature list can contain the signature
if (((SignatureSize + sizeof(EFI_GUID)) == SignatureList->SignatureSize) &&
(CompareMem(SignatureType, &(SignatureList->SignatureType), sizeof(EFI_GUID)))) {
// Get signature list data start
UINTN Offset = SignatureList->SignatureHeaderSize + sizeof(EFI_SIGNATURE_LIST);
Ptr = ((UINT8 *)SignatureList) + Offset;
End = Ptr + (SignatureList->SignatureListSize - Offset);
Ptr += sizeof(EFI_GUID);
// Check for the signature
while (Ptr < End) {
if (CompareMem(Ptr, Signature, SignatureSize) == 0) {
// The signature was found
return EFI_SUCCESS;
}
Ptr += SignatureList->SignatureSize;
}
}
// Get the next signature list
DatabasePtr += SignatureList->SignatureListSize;
}
return EFI_NOT_FOUND;
}
// Append a signature to a signature list
STATIC EFI_STATUS AppendSignatureToList(IN OUT EFI_SIGNATURE_LIST **SignatureList,
IN EFI_GUID *SignatureType,
IN VOID *Signature,
IN UINTN SignatureSize)
{
EFI_SIGNATURE_LIST *OldSignatureList;
EFI_SIGNATURE_LIST *NewSignatureList;
UINT8 *Ptr, *End;
UINTN Offset;
UINT32 DataSize = (UINT32)(SignatureSize + sizeof(EFI_GUID));
// Check parameters
if ((SignatureList == NULL) || (SignatureType == NULL) ||
(Signature == NULL) || (SignatureSize == 0)) {
return EFI_INVALID_PARAMETER;
}
// Check if there is an old signature list
OldSignatureList = *SignatureList;
if (OldSignatureList == NULL) {
// There is no list so create a new signature list
NewSignatureList = (EFI_SIGNATURE_LIST *)AllocateZeroPool(sizeof(EFI_SIGNATURE_LIST) + DataSize);
if (NewSignatureList == NULL) {
return EFI_OUT_OF_RESOURCES;
}
// Copy the signature to the list
CopyMem(&(NewSignatureList->SignatureType), SignatureType, sizeof(EFI_GUID));
NewSignatureList->SignatureListSize = (UINT32)(DataSize + sizeof(EFI_SIGNATURE_LIST));
NewSignatureList->SignatureSize = (UINT32)DataSize;
*SignatureList = NewSignatureList;
return EFI_SUCCESS;
}
// Check the signature type and size matches this list
if ((OldSignatureList->SignatureListSize <= sizeof(EFI_SIGNATURE_LIST)) ||
(OldSignatureList->SignatureSize <= sizeof(EFI_GUID)) ||
(DataSize != OldSignatureList->SignatureSize) ||
(CompareMem(SignatureType, &(OldSignatureList->SignatureType), sizeof(EFI_GUID)) != 0)) {
return EFI_INVALID_PARAMETER;
}
// Get the start of signatures data but offset by sizeof(EFI_GUID) so we can skip owner
Offset = sizeof(EFI_SIGNATURE_LIST) + OldSignatureList->SignatureHeaderSize;
Ptr = ((UINT8 *)OldSignatureList) + Offset;
End = Ptr + (OldSignatureList->SignatureListSize - Offset);
Ptr += sizeof(EFI_GUID);
// Check the signature doesn't already exist in list
while (Ptr < End) {
if (CompareMem(Ptr + sizeof(EFI_GUID), Signature, SignatureSize) == 0) {
// Just pretend like we added it if it exists already
return EFI_SUCCESS;
}
Ptr += OldSignatureList->SignatureSize;
}
// Create a new list for signatures
NewSignatureList = (EFI_SIGNATURE_LIST *)AllocateZeroPool(OldSignatureList->SignatureListSize + DataSize);
if (NewSignatureList == NULL) {
return EFI_OUT_OF_RESOURCES;
}
// Copy old list to new
CopyMem(NewSignatureList, OldSignatureList, OldSignatureList->SignatureListSize);
// Increase list size
NewSignatureList->SignatureListSize += DataSize;
// Copy new signature
CopyMem(((UINT8 *)NewSignatureList) + OldSignatureList->SignatureListSize + sizeof(EFI_GUID), Signature, SignatureSize);
// Update the list and free old
*SignatureList = NewSignatureList;
FreePool(OldSignatureList);
return EFI_SUCCESS;
}
// Append a signature list to a signature database
STATIC EFI_STATUS AppendSignatureListToDatabase(IN OUT VOID **Database,
IN OUT UINTN *DatabaseSize,
IN EFI_SIGNATURE_LIST *SignatureList)
{
EFI_SIGNATURE_LIST *List = NULL;
UINT8 *Ptr, *End;
UINT8 *OldDatabase;
UINT8 *NewDatabase;
UINTN OldDatabaseSize;
UINTN NewDatabaseSize;
UINTN Size;
// Check parameters
if ((Database == NULL) || (DatabaseSize == NULL) || (SignatureList == NULL) ||
(SignatureList->SignatureListSize <= sizeof(EFI_SIGNATURE_LIST)) ||
(SignatureList->SignatureSize <= sizeof(EFI_GUID))) {
return EFI_INVALID_PARAMETER;
}
// Get old database
OldDatabase = (UINT8 *)*Database;
OldDatabaseSize = *DatabaseSize;
if ((OldDatabase == NULL) || (OldDatabaseSize <= sizeof(EFI_SIGNATURE_LIST))) {
// No database so just set it to the signature list
NewDatabaseSize = SignatureList->SignatureListSize;
NewDatabase = (UINT8 *)AllocatePool(NewDatabaseSize);
if (NewDatabase == NULL) {
return EFI_OUT_OF_RESOURCES;
}
// Free the old database if needed
if (OldDatabase != NULL) {
FreePool(OldDatabase);
}
// Copy the signature list to the database
CopyMem(*Database = NewDatabase, SignatureList, *DatabaseSize = NewDatabaseSize);
return EFI_SUCCESS;
}
// Rebuild the signature list with only signatures that aren't found in database
Ptr = ((UINT8 *)SignatureList) + SignatureList->SignatureHeaderSize + sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID);
End = ((UINT8 *)SignatureList) + SignatureList->SignatureListSize;
Size = (SignatureList->SignatureSize - sizeof(EFI_GUID));
while (Ptr < End) {
// Check signature is already in database
EFI_STATUS Status = CheckSignatureIsInDatabase(OldDatabase, OldDatabaseSize, &(SignatureList->SignatureType), Ptr, Size);
if (Status == EFI_NOT_FOUND) {
// Add to new signature list if not found in database
Status = AppendSignatureToList(&List, &(SignatureList->SignatureType), Ptr, Size);
}
if (EFI_ERROR(Status)) {
if (List != NULL) {
FreePool(List);
}
return Status;
}
Ptr += SignatureList->SignatureSize;
}
// Check any signatures remain to be added
if (List == NULL) {
return EFI_SUCCESS;
}
// Check the list is valid
if ((List->SignatureListSize <= sizeof(EFI_SIGNATURE_LIST)) ||
(List->SignatureSize <= sizeof(EFI_GUID))) {
FreePool(List);
// Unsure if this should return an error or success
return EFI_SUCCESS;
}
// Create a new database
NewDatabaseSize = OldDatabaseSize + List->SignatureListSize;
NewDatabase = (UINT8 *)AllocatePool(NewDatabaseSize);
if (NewDatabase == NULL) {
FreePool(List);
return EFI_OUT_OF_RESOURCES;
}
// Copy original database and free it
CopyMem(*Database = NewDatabase, OldDatabase, OldDatabaseSize);
FreePool(OldDatabase);
// Append signature list to end of database
CopyMem(NewDatabase + OldDatabaseSize, List, List->SignatureListSize);
*DatabaseSize = NewDatabaseSize;
FreePool(List);
return EFI_SUCCESS;
}
// Append a signature to a signature database
EFI_STATUS AppendSignatureToDatabase(IN OUT VOID **Database,
IN OUT UINTN *DatabaseSize,
IN EFI_GUID *SignatureType,
IN VOID *Signature,
IN UINTN SignatureSize)
{
// Create a new signature list
EFI_SIGNATURE_LIST *List = NULL;
EFI_STATUS Status = AppendSignatureToList(&List, SignatureType, Signature, SignatureSize);
if (EFI_ERROR(Status)) {
if (List != NULL) {
FreePool(List);
}
return Status;
}
// Add the signature list to database
Status = AppendSignatureListToDatabase(Database, DatabaseSize, List);
FreePool(List);
return Status;
}
// Append a signature database to another signature database
EFI_STATUS AppendSignatureDatabaseToDatabase(IN OUT VOID **Database,
IN OUT UINTN *DatabaseSize,
IN VOID *SignatureDatabase,
IN UINTN SignatureDatabaseSize)
{
UINT8 *Ptr, *End;
// Check parameters
if ((Database == NULL) || (DatabaseSize == NULL) ||
(SignatureDatabase == NULL) || (SignatureDatabaseSize <= sizeof(EFI_SIGNATURE_LIST))) {
return EFI_INVALID_PARAMETER;
}
// Get the signature database start
Ptr = (UINT8 *)SignatureDatabase;
End = Ptr + SignatureDatabaseSize;
while (Ptr < End) {
// Get each signature list in signature database
EFI_SIGNATURE_LIST *List = (EFI_SIGNATURE_LIST *)Ptr;
EFI_STATUS Status;
if ((List->SignatureListSize <= sizeof(EFI_SIGNATURE_LIST)) || (List->SignatureSize <= sizeof(EFI_GUID))) {
return EFI_INVALID_PARAMETER;
}
// Add the signature list to the database
Status = AppendSignatureListToDatabase(Database, DatabaseSize, List);
if (EFI_ERROR(Status)) {
return Status;
}
Ptr += List->SignatureListSize;
}
return EFI_SUCCESS;
}
// Add image signature database to authorized database
EFI_STATUS AppendImageDatabaseToAuthorizedDatabase(IN VOID *Database,
IN UINTN DatabaseSize)
{
EFI_STATUS Status;
VOID *AuthDatabase;
UINTN AuthDatabaseSize;
// Check parameters
if ((Database == NULL) || (DatabaseSize <= sizeof(EFI_SIGNATURE_LIST))) {
return EFI_INVALID_PARAMETER;
}
// Get the authorized database
AuthDatabase = GetAuthorizedDatabase(&AuthDatabaseSize);
// Add the signature database to the authorized database
Status = AppendSignatureDatabaseToDatabase(&AuthDatabase, &AuthDatabaseSize, Database, DatabaseSize);
if (EFI_ERROR(Status)) {
if (AuthDatabase != NULL) {
FreePool(AuthDatabase);
}
return Status;
}
// Check there is any to set
if (AuthDatabase == NULL) {
return ClearAuthorizedDatabase();
}
if (AuthDatabaseSize <= sizeof(EFI_SIGNATURE_LIST)) {
FreePool(AuthDatabase);
return ClearAuthorizedDatabase();
}
// Set the authorized database
Status = SetAuthorizedDatabase(AuthDatabase, AuthDatabaseSize);
FreePool(AuthDatabase);
return Status;
}
STATIC EFI_STATUS RemoveSignatureFromDatabase(IN OUT VOID **Database,
IN OUT UINTN *DatabaseSize,
IN EFI_GUID *SignatureType,
IN VOID *Signature,
IN UINTN SignatureSize)
{
UINT8 *Ptr, *End;
VOID *OldDatabase;
VOID *NewDatabase = NULL;
UINTN OldDatabaseSize;
UINTN NewDatabaseSize = 0;
// Check parameters
if ((Database == NULL) || (DatabaseSize == NULL) || (SignatureType == NULL) ||
(Signature == NULL) || (SignatureSize <= sizeof(EFI_SIGNATURE_LIST))) {
return EFI_INVALID_PARAMETER;
}
OldDatabase = (UINT8 *)*Database;
OldDatabaseSize = *DatabaseSize;
if ((OldDatabase == NULL) || (OldDatabaseSize <= sizeof(EFI_SIGNATURE_LIST))) {
// Nothing to do if the database is empty
return EFI_SUCCESS;
}
// Get the signature database start
Ptr = (UINT8 *)OldDatabase;
End = Ptr + OldDatabaseSize;
while (Ptr < End) {
// Get each signature list in signature database
EFI_SIGNATURE_LIST *List = (EFI_SIGNATURE_LIST *)Ptr;
EFI_STATUS Status = EFI_SUCCESS;
if ((List->SignatureListSize <= sizeof(EFI_SIGNATURE_LIST)) || (List->SignatureSize <= sizeof(EFI_GUID))) {
if (NewDatabase != NULL) {
FreePool(NewDatabase);
}
return EFI_INVALID_PARAMETER;
}
// Check this signature could be found in this list
if (((List->SignatureSize - sizeof(EFI_GUID)) == SignatureSize) &&
(CompareMem(SignatureType, &(List->SignatureType), sizeof(EFI_GUID)) == 0)) {
// Remove the signature list from the database
UINT8 *ListPtr = Ptr + List->SignatureHeaderSize + sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID);
UINT8 *ListEnd = Ptr + List->SignatureListSize;
EFI_SIGNATURE_LIST *NewList = NULL;
while (ListPtr < ListEnd) {
// Compare the signatures
if (CompareMem(Ptr, Signature, SignatureSize) != 0) {
// Not a match so append it to new list
Status = AppendSignatureToList(&NewList, SignatureType, Ptr, SignatureSize);
if (EFI_ERROR(Status)) {
if (NewList != NULL) {
FreePool(NewList);
}
if (NewDatabase != NULL) {
FreePool(NewDatabase);
}
return Status;
}
}
ListPtr += List->SignatureSize;
}
// Append new list if any
if (NewList != NULL) {
Status = AppendSignatureListToDatabase(&NewDatabase, &NewDatabaseSize, NewList);
FreePool(NewList);
}
} else {
// Append this whole list as it can't hold the signature
Status = AppendSignatureListToDatabase(&NewDatabase, &NewDatabaseSize, List);
}
if (EFI_ERROR(Status)) {
if (NewDatabase != NULL) {
FreePool(NewDatabase);
}
return Status;
}
Ptr += List->SignatureListSize;
}
// Set new database
*Database = NewDatabase;
*DatabaseSize = NewDatabaseSize;
FreePool(OldDatabase);
return EFI_SUCCESS;
}
STATIC EFI_STATUS RemoveSignatureListFromDatabase(IN OUT VOID **Database,
IN OUT UINTN *DatabaseSize,
IN EFI_SIGNATURE_LIST *SignatureList)
{
UINT8 *Ptr, *End;
UINT8 *OldDatabase;
UINTN OldDatabaseSize;
UINTN Size;
// Check parameters
if ((Database == NULL) || (DatabaseSize == NULL) || (SignatureList == NULL) ||
(SignatureList->SignatureListSize <= sizeof(EFI_SIGNATURE_LIST)) ||
(SignatureList->SignatureSize <= sizeof(EFI_GUID))) {
return EFI_INVALID_PARAMETER;
}
// Get old database
OldDatabase = (UINT8 *)*Database;
OldDatabaseSize = *DatabaseSize;
if ((OldDatabase == NULL) || (OldDatabaseSize == 0)) {
// Nothing to remove
return EFI_SUCCESS;
}
// Remove the signatures found in the list from the database
Ptr = ((UINT8 *)SignatureList) + SignatureList->SignatureHeaderSize + sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID);
End = ((UINT8 *)SignatureList) + SignatureList->SignatureListSize;
Size = (SignatureList->SignatureSize - sizeof(EFI_GUID));
while (Ptr < End) {
// Remove signature from database
EFI_STATUS Status = RemoveSignatureFromDatabase(Database, DatabaseSize, &(SignatureList->SignatureType), Ptr, Size);
if (EFI_ERROR(Status)) {
return Status;
}
Ptr += SignatureList->SignatureSize;
}
return EFI_SUCCESS;
}
STATIC EFI_STATUS RemoveSignatureDatabaseFromDatabase(IN OUT VOID **Database,
IN OUT UINTN *DatabaseSize,
IN VOID *SignatureDatabase,
IN UINTN SignatureDatabaseSize)
{
UINT8 *Ptr, *End;
// Check parameters
if ((Database == NULL) || (DatabaseSize == NULL) ||
(SignatureDatabase == NULL) || (SignatureDatabaseSize <= sizeof(EFI_SIGNATURE_LIST))) {
return EFI_INVALID_PARAMETER;
}
// Get the signature database start
Ptr = (UINT8 *)SignatureDatabase;
End = Ptr + SignatureDatabaseSize;
while (Ptr < End) {
// Get each signature list in signature database
EFI_SIGNATURE_LIST *List = (EFI_SIGNATURE_LIST *)Ptr;
EFI_STATUS Status;
if ((List->SignatureListSize <= sizeof(EFI_SIGNATURE_LIST)) || (List->SignatureSize <= sizeof(EFI_GUID))) {
return EFI_INVALID_PARAMETER;
}
// Remove the signature list from the database
Status = RemoveSignatureListFromDatabase(Database, DatabaseSize, List);
if (EFI_ERROR(Status)) {
return Status;
}
Ptr += List->SignatureListSize;
}
return EFI_SUCCESS;
}
// Remove image signature database from authorized database
EFI_STATUS RemoveImageDatabaseFromAuthorizedDatabase(IN VOID *Database,
IN UINTN DatabaseSize)
{
EFI_STATUS Status;
VOID *AuthDatabase;
UINTN AuthDatabaseSize;
// Check parameters
if ((Database == NULL) || (DatabaseSize <= sizeof(EFI_SIGNATURE_LIST))) {
return EFI_INVALID_PARAMETER;
}
// Get the authorized database
AuthDatabase = GetAuthorizedDatabase(&AuthDatabaseSize);
// Remove the signature database from the authorized database
Status = RemoveSignatureDatabaseFromDatabase(&AuthDatabase, &AuthDatabaseSize, Database, DatabaseSize);
if (EFI_ERROR(Status)) {
FreePool(AuthDatabase);
return Status;
}
// Check there is any to set
if (AuthDatabase == NULL) {
return ClearAuthorizedDatabase();
}
if (AuthDatabaseSize <= sizeof(EFI_SIGNATURE_LIST)) {
FreePool(AuthDatabase);
return ClearAuthorizedDatabase();
}
// Set the authorized database
Status = SetAuthorizedDatabase(AuthDatabase, AuthDatabaseSize);
FreePool(AuthDatabase);
return Status;
}
VOID *GetAuthorizedDatabase(UINTN *DatabaseSize)
{
return GetSignatureDatabase(AUTHORIZED_DATABASE_NAME, &AUTHORIZED_DATABASE_GUID, DatabaseSize);
}
EFI_STATUS SetAuthorizedDatabase(IN VOID *Database,
IN UINTN DatabaseSize)
{
return SetSignatureDatabase(AUTHORIZED_DATABASE_NAME, &AUTHORIZED_DATABASE_GUID, Database, DatabaseSize);
}
// Clear authorized signature database
EFI_STATUS ClearAuthorizedDatabase(VOID)
{
return SetAuthorizedDatabase(NULL, 0);
}
// Create a secure boot image signature
STATIC VOID *CreateImageSignatureDatabase(IN VOID *FileBuffer,
IN UINT64 FileSize,
IN UINTN *DatabaseSize)
{
UINTN Index, Size = 0;
UINTN BytesHashed, HashSize;
UINT8 *Database = NULL;
UINT8 *ImageBase = (UINT8 *)FileBuffer;
UINT8 *HashBase = ImageBase;
UINT8 *HashPtr;
SHA256_CTX HashCtx;
EFI_SIGNATURE_LIST *SignatureListPtr;
EFI_IMAGE_SECTION_HEADER *Sections = NULL;
EFI_IMAGE_SECTION_HEADER *SectionPtr;
EFI_IMAGE_SECTION_HEADER *SectionEnd;
EFI_IMAGE_DOS_HEADER *DosHeader;
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION PeHeader;
UINT32 PeHeaderOffset;
UINT32 CertSize = 0;
UINT16 Magic;
// Check parameters
if (DatabaseSize == NULL) {
return NULL;
}
*DatabaseSize = 0;
if ((FileBuffer == NULL) || (FileSize == 0)) {
return NULL;
}
// Check for DOS PE header
DosHeader = (EFI_IMAGE_DOS_HEADER *)FileBuffer;
if (DosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
PeHeaderOffset = DosHeader->e_lfanew;
} else {
PeHeaderOffset = 0;
}
// Check for PE header
PeHeader.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)(((UINT8 *)FileBuffer) + PeHeaderOffset);
if (PeHeader.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
// Invalid PE image
return NULL;
}
// Fix magic number if needed
if ((PeHeader.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64) &&
(PeHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC)) {
Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
} else {
Magic = PeHeader.Pe32->OptionalHeader.Magic;
}
// Check magic number to get size
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
// PE32
HashPtr = (UINT8 *)(&(PeHeader.Pe32->OptionalHeader.CheckSum));
} else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
// PE32+
HashPtr = (UINT8 *)(&(PeHeader.Pe32Plus->OptionalHeader.CheckSum));
} else {
// Invalid image
DBG("Invalid image: 0x%X (0x%X)\n", FileBuffer, FileSize);
return NULL;
}
HashSize = (UINTN)(HashPtr - HashBase);
// Initialize the hash context
if (SHA256_Init(&HashCtx) == 0) {
goto Failed;
}
// Begin hashing the pe image
if (SHA256_Update(&HashCtx, HashBase, HashSize) == 0) {
goto Failed;
}
// Skip the checksum
HashBase = HashPtr + sizeof(UINT32);
// Skip over the security directory if present
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
// PE32
if (PeHeader.Pe32->OptionalHeader.NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
// Hash before the security directory
HashPtr = (UINT8 *)(&(PeHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]));
HashSize = (HashPtr - HashBase);
if (HashSize != 0) {
if (SHA256_Update(&HashCtx, HashBase, HashSize) == 0) {
goto Failed;
}
}
// Set to point at the remaining data if any
HashBase = (UINT8 *)(&(PeHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]));
CertSize = PeHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
}
BytesHashed = PeHeader.Pe32->OptionalHeader.SizeOfHeaders;
} else {
// PE32+
if (PeHeader.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
// Hash before the security directory
HashPtr = (UINT8 *)(&(PeHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]));
HashSize = (HashPtr - HashBase);
if (HashSize != 0) {
if (SHA256_Update(&HashCtx, HashBase, HashSize) == 0) {
goto Failed;
}
}
// Set to point at the remaining data if any
HashBase = (UINT8 *)(&(PeHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]));
CertSize = PeHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
}
BytesHashed = PeHeader.Pe32->OptionalHeader.SizeOfHeaders;
}
// Hash the rest of the data directories if any
HashSize = BytesHashed - (UINTN)(HashBase - ImageBase);
if (HashSize != 0) {
if (SHA256_Update(&HashCtx, HashBase, HashSize) == 0) {
goto Failed;
}
}
// Get the image section headers
SectionPtr = (EFI_IMAGE_SECTION_HEADER *)(ImageBase + PeHeaderOffset + sizeof(EFI_IMAGE_FILE_HEADER) +
sizeof(UINT32) + PeHeader.Pe32->FileHeader.SizeOfOptionalHeader);
// Allocate a new array for the image section headers
Sections = AllocateZeroPool(sizeof(EFI_IMAGE_SECTION_HEADER) * PeHeader.Pe32->FileHeader.NumberOfSections);
if (Sections == NULL) {
goto Failed;
}
// Sort the image section headers
Index = 0;
while (Index < PeHeader.Pe32->FileHeader.NumberOfSections) {
UINTN Pos = Index++;
while ((Pos > 0) && (SectionPtr->PointerToRawData < Sections[Pos - 1].PointerToRawData)) {
CopyMem(&Sections[Pos], &Sections[Pos - 1], sizeof(EFI_IMAGE_SECTION_HEADER));
--Pos;
}
CopyMem(&Sections[Pos], SectionPtr++, sizeof(EFI_IMAGE_SECTION_HEADER));
}
// Hash each image section
SectionEnd = Sections + PeHeader.Pe32->FileHeader.NumberOfSections;
for (SectionPtr = Sections; SectionPtr < SectionEnd; ++SectionPtr) {
// Nothing to do if no size
if (SectionPtr->SizeOfRawData == 0) {
continue;
}
// Calculate hash base and size
HashBase = ImageBase + SectionPtr->PointerToRawData;
HashSize = (UINTN)SectionPtr->SizeOfRawData;
// Hash the image section
if (SHA256_Update(&HashCtx, HashBase, HashSize) == 0) {
goto Failed;
}
BytesHashed += HashSize;
}
// Hash any data remaining after the sections
if (BytesHashed < FileSize) {
HashBase = ImageBase + BytesHashed;
if (FileSize < (BytesHashed + CertSize)) {
goto Failed;
}
HashSize = (UINTN)(FileSize - (BytesHashed + CertSize));
if (HashSize != 0) {
if (SHA256_Update(&HashCtx, HashBase, HashSize) == 0) {
goto Failed;
}
}
}
// Create the signature list
Size = (sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID) + 256);
Database = AllocateZeroPool(Size);
if (Database == NULL) {
goto Failed;
}
// Copy the hash to the signature list
SignatureListPtr = (EFI_SIGNATURE_LIST *)Database;
CopyMem(&(SignatureListPtr->SignatureType), &gEfiCertSha256Guid, sizeof(EFI_GUID));
SignatureListPtr->SignatureListSize = (UINT32)Size;
SignatureListPtr->SignatureSize = (UINT32)(Size - sizeof(EFI_SIGNATURE_LIST));
// Finalize the hash by placing it in the signature list
HashPtr = ((UINT8 *)(Database)) + sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID);
if (SHA256_Final(HashPtr, &HashCtx) == 0) {
goto Failed;
}
// Cleanup and return success
FreePool(Sections);
*DatabaseSize = Size;
return Database;
Failed:
// Hashing failed
if (Database != NULL) {
FreePool(Database);
}
if (Sections != NULL) {
FreePool(Sections);
}
return NULL;
}
// Get a secure boot image signature
VOID *GetImageSignatureDatabase(IN VOID *FileBuffer,
IN UINT64 FileSize,
IN UINTN *DatabaseSize,
IN BOOLEAN HashIfNoDatabase)
{
UINTN Size = 0;
VOID *Database = NULL;
UINT8 *Ptr, *End;
WIN_CERTIFICATE_UEFI_GUID *GuidCert;
EFI_IMAGE_DOS_HEADER *DosHeader;
EFI_IMAGE_DATA_DIRECTORY *SecDataDir = NULL;
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION PeHeader;
UINT32 PeHeaderOffset;
UINT16 Magic;
// Check parameters
if (DatabaseSize == NULL) {
return NULL;
}
*DatabaseSize = 0;
if ((FileBuffer == NULL) || (FileSize == 0)) {
return NULL;
}
// Check for DOS PE header
DosHeader = (EFI_IMAGE_DOS_HEADER *)FileBuffer;
if (DosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
PeHeaderOffset = DosHeader->e_lfanew;
} else {
PeHeaderOffset = 0;
}
// Check for PE header
PeHeader.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)(((UINT8 *)FileBuffer) + PeHeaderOffset);
if (PeHeader.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
// Invalid PE image
DBG("Invalid PE image for signature retrieval (no NT signature)\n");
return NULL;
}
// Fix magic number if needed
if ((PeHeader.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64) &&
(PeHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC)) {
Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
} else {
Magic = PeHeader.Pe32->OptionalHeader.Magic;
}
// Get the security data directory of the image
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
// PE32
if (PeHeader.Pe32->OptionalHeader.NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *)&(PeHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]);
}
} else if (PeHeader.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
// PE32+
SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *)&(PeHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]);
}
DBG("Get image database: 0x%X (0x%X) 0x%X 0x%X 0x%X (0x%X)\n", FileBuffer, FileSize, SecDataDir, SecDataDir->VirtualAddress, ((UINT8 *)FileBuffer) + SecDataDir->VirtualAddress, SecDataDir->Size);
// Check the security data directory is found and valid
if ((SecDataDir->VirtualAddress >= FileSize) || ((SecDataDir->VirtualAddress + SecDataDir->Size) > FileSize)) {
DBG("Security directory exceeds the file limits\n");
SecDataDir = NULL;
}
if ((SecDataDir == NULL) || (SecDataDir->Size == 0)) {
if (HashIfNoDatabase) {
// Try to hash the image instead
return CreateImageSignatureDatabase(FileBuffer, FileSize, DatabaseSize);
}
// No certificate
DBG("Security directory not found in image!\n");
return NULL;
}
// There may be multiple certificates so grab each and update signature list
Ptr = (((UINT8 *)FileBuffer) + SecDataDir->VirtualAddress);
End = Ptr + SecDataDir->Size;
while ((Ptr + CERT_SIZE) < End) {
WIN_CERTIFICATE *Cert = (WIN_CERTIFICATE *)Ptr;
UINTN Length = Cert->dwLength;
UINTN Alignment = (Length % SECDIR_ALIGNMENT_SIZE);
UINTN SigSize = 0;
VOID *Signature = NULL;
EFI_GUID *SigGuid = NULL;
// Get the alignment length
if (Alignment != 0) {
Alignment = SECDIR_ALIGNMENT_SIZE - Alignment;
}
DBG("Embedded certificate: 0x%X (0x%X) [0x%X]\n", Cert, Length, Cert->wCertificateType);
// Get the certificate's type
if (Cert->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
// PKCS#7
if (Length < CERT_SIZE) {
break;
}
Signature = Ptr + CERT_SIZE;
SigSize = Length - CERT_SIZE;
SigGuid = &gEfiCertPkcs7Guid;
} else if (Cert->wCertificateType == WIN_CERT_TYPE_EFI_GUID) {
// EFI GUID
if (Length < EFIGUID_SIZE) {
break;
}
Signature = Ptr + EFIGUID_SIZE;
SigSize = Length - EFIGUID_SIZE;
// Get the appropriate signature GUID
GuidCert = (WIN_CERTIFICATE_UEFI_GUID *)Cert;
if (CompareMem(&(GuidCert->CertType), &gEfiCertX509Guid, sizeof(EFI_GUID)) == 0) {
SigGuid = &gEfiCertX509Guid;
} else if (CompareMem(&(GuidCert->CertType), &gEfiCertTypeRsa2048Sha256Guid, sizeof(EFI_GUID)) == 0) {
SigGuid = &gEfiCertRsa2048Sha256Guid;
} else if (CompareMem(&(GuidCert->CertType), &gEfiCertPkcs7Guid, sizeof(EFI_GUID)) == 0) {
SigGuid = &gEfiCertPkcs7Guid;
}
} else if (Cert->wCertificateType == WIN_CERT_TYPE_EFI_PKCS115) {
// PKCS#1v1.5
if (Length < PKCS1_1_5_SIZE) {
break;
}
Signature = Ptr + PKCS1_1_5_SIZE;
SigSize = (Length - PKCS1_1_5_SIZE);
GuidCert = (WIN_CERTIFICATE_UEFI_GUID *)Cert;
SigGuid = &(GuidCert->CertType);
}
// Append the signature if valid
if ((SigGuid != NULL) && (Signature != NULL) && (SigSize > 0)) {
DBG("Found signature certificate: 0x%X (0x%X) %g\n", Signature, SigSize, SigGuid);
if (EFI_ERROR(AppendSignatureToDatabase(&Database, &Size, SigGuid, Signature, SigSize))) {
break;
}
} else {
DBG("Skipping non-signature certificate: 0x%X (0x%X) [0x%X]\n", Cert, Length, Cert->wCertificateType);
}
// Advance to next certificate
Ptr += (Length + Alignment);
}
// Check if there is some sort of corruption
if (Ptr != End) {
DBG("Failed to retrieve image database: 0x%X - 0x%X @ 0x%X\n", (((UINT8 *)FileBuffer) + SecDataDir->VirtualAddress), End, Ptr);
// Don't return anything if not at end
if (Database != NULL) {
FreePool(Database);
Database = NULL;
}
}
if (Database != NULL) {
*DatabaseSize = Size;
} else if (HashIfNoDatabase) {
// Try to hash the image instead
return CreateImageSignatureDatabase(FileBuffer, FileSize, DatabaseSize);
}
return Database;
}
#endif // ENABLE_SECURE_BOOT