mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-17 15:18:06 +01:00
7c0aa811ec
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
894 lines
34 KiB
C
894 lines
34 KiB
C
/*
|
|
* 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
|