2199 lines
66 KiB
C
2199 lines
66 KiB
C
/** @file
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2012 - 2021, Insyde Software Corp. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
|
|
#include "EnrollHash.h"
|
|
|
|
|
|
#define TWO_BYTE_ENCODE 0x82
|
|
#define SHORT_FORM_MASK 0x80
|
|
|
|
ENROLL_MENU_OPTION mSecureBootFsOptionMenu = {
|
|
EM_MENU_ENTRY_SIGNATURE,
|
|
NULL,
|
|
0
|
|
};
|
|
ENROLL_MENU_OPTION mSecureBootDirectoryMenu = {
|
|
EM_MENU_ENTRY_SIGNATURE,
|
|
NULL,
|
|
0
|
|
};
|
|
STATIC EFI_TEXT_STRING mOriginalOutputString;
|
|
|
|
|
|
STATIC 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
|
|
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, // OBJ_sha384
|
|
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, // OBJ_sha512
|
|
};
|
|
|
|
STATIC HASH_TABLE mHash[] = {
|
|
{ &gEfiHashAlgorithmMD5Guid, L"MD5", 16, &mHashOidValue[0], 8},
|
|
{ &gEfiHashAlgorithmSha1Guid, L"SHA1", 20, &mHashOidValue[8], 5},
|
|
{ &gEfiHashAlgorithmSha224Guid, L"SHA224", 28, &mHashOidValue[13], 9},
|
|
{ &gEfiHashAlgorithmSha256Guid, L"SHA256", 32, &mHashOidValue[22], 9},
|
|
{ &gEfiHashAlgorithmSha384Guid, L"SHA384", 48, &mHashOidValue[31], 9},
|
|
{ &gEfiHashAlgorithmSha512Guid, L"SHA512", 64, &mHashOidValue[40], 9}
|
|
};
|
|
|
|
typedef struct {
|
|
UINTN FileFormat;
|
|
EFI_STRING_ID StringId;
|
|
EFI_GUID *SignatureTypeGuid;
|
|
} EM_FILE_FORMAT_INFO;
|
|
|
|
STATIC EM_FILE_FORMAT_INFO mEmFileFormatList[] = {
|
|
{EM_FILE_FORMAT_CER , STRING_TOKEN (STR_SIGNATURE_TYPE_PCKS7) , &gEfiCertX509Guid },
|
|
{EM_FILE_FORMAT_SHA256, STRING_TOKEN (STR_SIGNATURE_TYPE_SHA256), &gEfiCertSha256Guid},
|
|
{EM_FILE_FORMAT_SHA512, STRING_TOKEN (STR_SIGNATURE_TYPE_SHA512), &gEfiCertSha512Guid}
|
|
};
|
|
|
|
STATIC
|
|
ENROLL_MENU_ENTRY *
|
|
EmCreateFileMenuEntry (
|
|
VOID
|
|
);
|
|
;
|
|
|
|
|
|
STATIC
|
|
VOID
|
|
EmDestroyMenuEntry (
|
|
ENROLL_MENU_ENTRY *MenuEntry
|
|
)
|
|
;
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EnrollHashImage (
|
|
EM_FILE_CONTEXT *FileContext
|
|
);
|
|
|
|
EFI_DEVICE_PATH_PROTOCOL EndDevicePath[] = {
|
|
{
|
|
END_DEVICE_PATH_TYPE,
|
|
END_ENTIRE_DEVICE_PATH_SUBTYPE,
|
|
{
|
|
END_DEVICE_PATH_LENGTH,
|
|
0
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
Destroy the menu entry passed in
|
|
|
|
@param MenuEntry The menu entry need to be destroyed
|
|
|
|
**/
|
|
STATIC
|
|
VOID
|
|
EmDestroyMenuEntry (
|
|
ENROLL_MENU_ENTRY *MenuEntry
|
|
)
|
|
{
|
|
EM_FILE_CONTEXT *FileContext;
|
|
|
|
FileContext = (EM_FILE_CONTEXT *) MenuEntry->VariableContext;
|
|
|
|
SecureBootSafeFreePool ((VOID **) &FileContext->DevicePath);
|
|
//
|
|
// All of the FHandles in the same folder are the same (because the FHandle
|
|
// is for this folder). If we close any of these FHandles, it will cause the
|
|
// FHandle for the folder is closed. Therefore, we only need close file handle
|
|
// if the location is root.
|
|
//
|
|
if (FileContext->IsRoot && FileContext->FHandle != NULL) {
|
|
FileContext->FHandle->Close (FileContext->FHandle);
|
|
}
|
|
|
|
SecureBootSafeFreePool ((VOID **) &FileContext->FileName);
|
|
SecureBootSafeFreePool ((VOID **) &FileContext->Info);
|
|
SecureBootSafeFreePool ((VOID **) &FileContext);
|
|
SecureBootSafeFreePool ((VOID **) &MenuEntry->DisplayString);
|
|
SecureBootSafeFreePool ((VOID **) &MenuEntry->HelpString);
|
|
SecureBootSafeFreePool ((VOID **) &MenuEntry);
|
|
}
|
|
|
|
|
|
/**
|
|
Check this ROM is a EFI format image or not
|
|
|
|
@param Buffer Pointer to the image buffer
|
|
@param BufferSize The image size
|
|
@param IsSigned TRUE: It is a signed EFI image
|
|
FALSE: It isn't a signed EFI image.
|
|
|
|
@retval TRUE This image is EFI format image.
|
|
@retval FALSE This image isn't EFI format image.
|
|
|
|
**/
|
|
BOOLEAN
|
|
IsEfiImage (
|
|
IN UINT8 *Buffer,
|
|
IN UINTN BufferSize,
|
|
OUT BOOLEAN *IsSigned
|
|
)
|
|
{
|
|
EFI_IMAGE_DOS_HEADER *DosHdr;
|
|
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION NtHeader;
|
|
EFI_IMAGE_DATA_DIRECTORY *SecDataDir;
|
|
UINTN PeCoffHeaderOffset;
|
|
UINT16 Magic;
|
|
BOOLEAN IsSignedEfi;
|
|
BOOLEAN IsEfi;
|
|
|
|
IsSignedEfi = FALSE;
|
|
IsEfi = FALSE;
|
|
SecDataDir = NULL;
|
|
|
|
DosHdr = (EFI_IMAGE_DOS_HEADER *) Buffer;
|
|
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
|
|
//
|
|
// DOS image header is present,
|
|
// so read the PE header after the DOS image header.
|
|
//
|
|
PeCoffHeaderOffset = DosHdr->e_lfanew;
|
|
} else {
|
|
PeCoffHeaderOffset = 0;
|
|
}
|
|
//
|
|
// Check PE/COFF image.
|
|
//
|
|
NtHeader.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) (Buffer + PeCoffHeaderOffset);
|
|
if (NtHeader.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
|
|
Magic = NtHeader.Pe32->OptionalHeader.Magic;
|
|
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
//
|
|
// Use PE32 offset.
|
|
//
|
|
SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &NtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
|
|
IsEfi = TRUE;
|
|
}
|
|
|
|
if (IsEfi && SecDataDir->Size != 0) {
|
|
//
|
|
// This image is signed.
|
|
//
|
|
IsSignedEfi = TRUE;
|
|
}
|
|
}
|
|
NtHeader.Pe32Plus = (EFI_IMAGE_NT_HEADERS64 *) (Buffer + PeCoffHeaderOffset);
|
|
if (NtHeader.Pe32Plus->Signature == EFI_IMAGE_NT_SIGNATURE) {
|
|
Magic = NtHeader.Pe32Plus->OptionalHeader.Magic;
|
|
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
|
//
|
|
// Use PE32 offset.
|
|
//
|
|
SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &NtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
|
|
IsEfi = TRUE;
|
|
}
|
|
|
|
if (IsEfi && SecDataDir->Size != 0) {
|
|
//
|
|
// This image is signed.
|
|
//
|
|
IsSignedEfi = TRUE;
|
|
}
|
|
}
|
|
*IsSigned = IsSignedEfi;
|
|
|
|
return IsEfi;
|
|
}
|
|
|
|
|
|
/**
|
|
Check this ROM is a EFI format image or not
|
|
|
|
@param HashAlg Hash algorithm type
|
|
@param ImageDigest Pointer to EFI_HASH_OUTPUT instance.
|
|
|
|
@retval EFI_SUCCESS add hash image to link list successful.
|
|
@retval EFI_INVALID_PARAMETER Any input parameter is invalid.
|
|
@retval EFI_UNSUPPORTED The input hash type is unsupported.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AddHashDataToList (
|
|
IN UINT32 HashAlg,
|
|
IN EFI_HASH_OUTPUT *ImageDigest
|
|
)
|
|
{
|
|
HASH_LINK_LIST *HashLink;
|
|
|
|
if (ImageDigest == NULL || ImageDigest->Sha256Hash == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (HashAlg != HASHALG_SHA1 && HashAlg != HASHALG_SHA256) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
HashLink = AllocateZeroPool (sizeof (HASH_LINK_LIST));
|
|
ASSERT (HashLink != NULL);
|
|
if (HashLink == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
HashLink->Signature = HASH_LINK_LIST_SIGNATURE;
|
|
HashLink->ShaType = HashAlg;
|
|
HashLink->HashSize = (HashAlg == HASHALG_SHA1) ? sizeof (EFI_SHA1_HASH) : sizeof (EFI_SHA256_HASH);
|
|
CopyMem (HashLink->Hash, ImageDigest->Sha256Hash, HashLink->HashSize);
|
|
InsertTailList (&mSecureBootHashLinkList, &HashLink->Link);
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
Caculate hash of Pe/Coff image based on the authenticode image hashing in
|
|
PE/COFF Specification 8.0 Appendix A
|
|
|
|
@param[in] HashAlg Hash algorithm type.
|
|
|
|
@retval TRUE Successfully hash image.
|
|
@retval FALSE Fail in hash image.
|
|
|
|
**/
|
|
BOOLEAN
|
|
HashPeImage (
|
|
IN UINT8 *Buffer,
|
|
IN UINTN BufferSize,
|
|
IN UINT32 HashAlg
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT16 Magic;
|
|
EFI_IMAGE_SECTION_HEADER *Section;
|
|
EFI_IMAGE_SECTION_HEADER *NextSection;
|
|
UINT8 *HashBase;
|
|
UINTN HashSize;
|
|
UINTN SumOfBytesHashed;
|
|
EFI_IMAGE_SECTION_HEADER *SectionHeader;
|
|
UINTN Index;
|
|
UINTN Pos;
|
|
EFI_HASH_PROTOCOL *Hash;
|
|
EFI_GUID CertType;
|
|
UINTN ImageDigestSize;
|
|
EFI_HASH_OUTPUT *ImageDigest;
|
|
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION NtHeader;
|
|
EFI_IMAGE_DOS_HEADER *DosHdr;
|
|
UINTN PeCoffHeaderOffset;
|
|
UINT32 CertSize;
|
|
UINT32 NumberOfRvaAndSizes;
|
|
|
|
SectionHeader = NULL;
|
|
ImageDigest = NULL;
|
|
|
|
//
|
|
// Initialize context of hash.
|
|
//
|
|
if (HashAlg == HASHALG_SHA1) {
|
|
CertType = gEfiCertSha1Guid;
|
|
} else if (HashAlg == HASHALG_SHA256) {
|
|
CertType = gEfiCertSha256Guid;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
Status = gBS->LocateProtocol (
|
|
&gEfiHashProtocolGuid,
|
|
NULL,
|
|
(VOID **) &Hash
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
Status = Hash->GetHashSize (
|
|
Hash,
|
|
mHash[HashAlg].Index,
|
|
&ImageDigestSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return FALSE;
|
|
}
|
|
ImageDigest = AllocateZeroPool (sizeof (EFI_HASH_OUTPUT));
|
|
if (ImageDigest == NULL) {
|
|
return FALSE;
|
|
}
|
|
ImageDigest->Sha256Hash = AllocateZeroPool (sizeof (EFI_SHA256_HASH));
|
|
if (ImageDigest->Sha256Hash == NULL) {
|
|
SecureBootSafeFreePool ((VOID **) &ImageDigest);
|
|
return FALSE;
|
|
}
|
|
|
|
DosHdr = (EFI_IMAGE_DOS_HEADER *) Buffer;
|
|
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
|
|
//
|
|
// DOS image header is present,
|
|
// so read the PE header after the DOS image header.
|
|
//
|
|
PeCoffHeaderOffset = DosHdr->e_lfanew;
|
|
} else {
|
|
PeCoffHeaderOffset = 0;
|
|
}
|
|
//
|
|
// Check PE/COFF image.
|
|
//
|
|
NtHeader.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) (Buffer + PeCoffHeaderOffset);
|
|
//
|
|
// Measuring PE/COFF Image Header;
|
|
// But CheckSum field and SECURITY data directory (certificate) are excluded
|
|
//
|
|
if (NtHeader.Pe32->FileHeader.Machine == EFI_IMAGE_MACHINE_IA64 && NtHeader.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 = NtHeader.Pe32->OptionalHeader.Magic;
|
|
}
|
|
//
|
|
// 3. Calculate the distance from the base of the image header to the image checksum address.
|
|
// 4. Hash the image header from its base to beginning of the image checksum.
|
|
//
|
|
HashBase = Buffer;
|
|
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
//
|
|
// Use PE32 offset.
|
|
//
|
|
HashSize = (UINTN) ((UINT8 *) (&NtHeader.Pe32->OptionalHeader.CheckSum) - HashBase);
|
|
NumberOfRvaAndSizes = NtHeader.Pe32->OptionalHeader.NumberOfRvaAndSizes;
|
|
} else {
|
|
//
|
|
// Use PE32+ offset.
|
|
//
|
|
HashSize = (UINTN) ((UINT8 *) (&NtHeader.Pe32Plus->OptionalHeader.CheckSum) - HashBase);
|
|
NumberOfRvaAndSizes = NtHeader.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
|
|
}
|
|
|
|
Status = Hash->Hash (
|
|
Hash,
|
|
mHash[HashAlg].Index,
|
|
FALSE,
|
|
HashBase,
|
|
HashSize,
|
|
(EFI_HASH_OUTPUT *) ImageDigest
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// 5. Skip over the image checksum (it occupies a single ULONG).
|
|
//
|
|
if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
|
|
//
|
|
// 6. Since there is no Cert Directory in optional header, hash everything
|
|
// from the end of the checksum to the end of image header.
|
|
//
|
|
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
//
|
|
// Use PE32 offset.
|
|
//
|
|
HashBase = (UINT8 *) &NtHeader.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);
|
|
HashSize = NtHeader.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - Buffer);
|
|
} else {
|
|
//
|
|
// Use PE32+ offset.
|
|
//
|
|
HashBase = (UINT8 *) &NtHeader.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32);
|
|
HashSize = NtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - Buffer);
|
|
}
|
|
|
|
if (HashSize != 0) {
|
|
Status = Hash->Hash (
|
|
Hash,
|
|
mHash[HashAlg].Index,
|
|
TRUE,
|
|
HashBase,
|
|
HashSize,
|
|
(EFI_HASH_OUTPUT *) ImageDigest
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// 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.
|
|
//
|
|
HashBase = (UINT8 *) &NtHeader.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);
|
|
HashSize = (UINTN) ((UINT8 *) (&NtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase);
|
|
} else {
|
|
//
|
|
// Use PE32+ offset.
|
|
//
|
|
HashBase = (UINT8 *) &NtHeader.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32);
|
|
HashSize = (UINTN) ((UINT8 *) (&NtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase);
|
|
}
|
|
if (HashSize != 0) {
|
|
Status = Hash->Hash (
|
|
Hash,
|
|
mHash[HashAlg].Index,
|
|
TRUE,
|
|
HashBase,
|
|
HashSize,
|
|
(EFI_HASH_OUTPUT *) ImageDigest
|
|
);
|
|
|
|
|
|
if (EFI_ERROR (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) {
|
|
//
|
|
// Use PE32 offset
|
|
//
|
|
HashBase = (UINT8 *) &NtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];
|
|
HashSize = NtHeader.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) ((UINT8 *) (&NtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - Buffer);
|
|
} else {
|
|
//
|
|
// Use PE32+ offset.
|
|
//
|
|
HashBase = (UINT8 *) &NtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];
|
|
HashSize = NtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) ((UINT8 *) (&NtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - Buffer);
|
|
}
|
|
if (HashSize != 0) {
|
|
Status = Hash->Hash (
|
|
Hash,
|
|
mHash[HashAlg].Index,
|
|
TRUE,
|
|
HashBase,
|
|
HashSize,
|
|
(EFI_HASH_OUTPUT *) ImageDigest
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// 10. Set the SUM_OF_BYTES_HASHED to the size of the header.
|
|
//
|
|
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
//
|
|
// Use PE32 offset.
|
|
//
|
|
SumOfBytesHashed = NtHeader.Pe32->OptionalHeader.SizeOfHeaders;
|
|
} else {
|
|
//
|
|
// Use PE32+ offset
|
|
//
|
|
SumOfBytesHashed = NtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders;
|
|
}
|
|
|
|
//
|
|
// 11. Build a temporary table of pointers to all the IMAGE_SECTION_HEADER
|
|
// structures in the image. The 'NumberOfSections' field of the image
|
|
// header indicates how big the table should be. Do not include any
|
|
// IMAGE_SECTION_HEADERs in the table whose 'SizeOfRawData' field is zero.
|
|
//
|
|
SectionHeader = (EFI_IMAGE_SECTION_HEADER *) AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * NtHeader.Pe32->FileHeader.NumberOfSections);
|
|
ASSERT (SectionHeader != NULL);
|
|
if (SectionHeader == NULL) {
|
|
SecureBootSafeFreePool ((VOID **) &ImageDigest);
|
|
SecureBootSafeFreePool ((VOID **) &ImageDigest->Sha256Hash);
|
|
return FALSE;
|
|
}
|
|
//
|
|
// 12. Using the 'PointerToRawData' in the referenced section headers as
|
|
// a key, arrange the elements in the table in ascending order. In other
|
|
// words, sort the section headers according to the disk-file offset of
|
|
// the section.
|
|
//
|
|
Section = (EFI_IMAGE_SECTION_HEADER *) (
|
|
Buffer +
|
|
PeCoffHeaderOffset +
|
|
sizeof (UINT32) +
|
|
sizeof (EFI_IMAGE_FILE_HEADER) +
|
|
NtHeader.Pe32->FileHeader.SizeOfOptionalHeader
|
|
);
|
|
for (Index = 0; Index < NtHeader.Pe32->FileHeader.NumberOfSections; Index++) {
|
|
Pos = Index;
|
|
while ((Pos > 0) && (Section->PointerToRawData < SectionHeader[Pos - 1].PointerToRawData)) {
|
|
CopyMem (&SectionHeader[Pos], &SectionHeader[Pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER));
|
|
Pos--;
|
|
}
|
|
CopyMem (&SectionHeader[Pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER));
|
|
Section += 1;
|
|
}
|
|
|
|
//
|
|
// 13. Walk through the sorted table, bring the corresponding section
|
|
// into memory, and hash the entire section (If two section data isn't
|
|
// continuous, the 'SizeOfRawData' field in the section header is incorrect.
|
|
// Therefore, We use the whole data between two sections.).
|
|
// 14. Add the section's hash size to SUM_OF_BYTES_HASHED .
|
|
// 15. Repeat steps 13 and 14 for all the sections in the sorted table.
|
|
//
|
|
for (Index = 0; Index < NtHeader.Pe32->FileHeader.NumberOfSections; Index++) {
|
|
Section = &SectionHeader[Index];
|
|
if (Section->SizeOfRawData == 0) {
|
|
continue;
|
|
}
|
|
HashBase = Buffer + Section->PointerToRawData;
|
|
if (Index != (UINTN) (NtHeader.Pe32->FileHeader.NumberOfSections - 1)) {
|
|
NextSection = &SectionHeader[Index + 1];
|
|
HashSize = (UINTN) NextSection->PointerToRawData - (UINTN) Section->PointerToRawData;
|
|
} else {
|
|
HashSize = (UINTN) Section->SizeOfRawData;
|
|
}
|
|
Status = Hash->Hash (
|
|
Hash,
|
|
mHash[HashAlg].Index,
|
|
TRUE,
|
|
HashBase,
|
|
HashSize,
|
|
(EFI_HASH_OUTPUT *) ImageDigest
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
SumOfBytesHashed += HashSize;
|
|
}
|
|
|
|
//
|
|
// 16. If the file size is greater than SUM_OF_BYTES_HASHED, there is extra
|
|
// data in the file that needs to be added to the hash. This data begins
|
|
// at file offset SUM_OF_BYTES_HASHED and its length is:
|
|
// FileSize - (CertDirectory->Size)
|
|
//
|
|
if (BufferSize > SumOfBytesHashed) {
|
|
HashBase = Buffer + SumOfBytesHashed;
|
|
if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
|
|
CertSize = 0;
|
|
} else {
|
|
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
//
|
|
// Use PE32 offset.
|
|
//
|
|
CertSize = NtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
|
|
} else {
|
|
//
|
|
// Use PE32+ offset.
|
|
//
|
|
CertSize = NtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
|
|
}
|
|
if (BufferSize > CertSize + SumOfBytesHashed) {
|
|
HashSize = (UINTN) (BufferSize - CertSize - SumOfBytesHashed);
|
|
Status = Hash->Hash (
|
|
Hash,
|
|
mHash[HashAlg].Index,
|
|
TRUE,
|
|
HashBase,
|
|
HashSize,
|
|
(EFI_HASH_OUTPUT *) ImageDigest
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
} else if (BufferSize < CertSize + SumOfBytesHashed) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto Done;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FeaturePcdGet (PcdUpdateSecureBootVariablesSupported)) {
|
|
Status = SecureBootVarAddSignature (
|
|
mSecureBootPrivate.SecureBootMgrState,
|
|
&CertType,
|
|
&gZeroGuid,
|
|
(UINT8 *) ImageDigest->Sha256Hash,
|
|
mHash[HashAlg].DigestLength,
|
|
TRUE
|
|
);
|
|
} else {
|
|
Status = AddHashDataToList (HashAlg, ImageDigest);
|
|
}
|
|
|
|
Done:
|
|
|
|
SecureBootSafeFreePool ((VOID **) &SectionHeader);
|
|
SecureBootSafeFreePool ((VOID **) &ImageDigest->Sha256Hash);
|
|
SecureBootSafeFreePool ((VOID **) &ImageDigest);
|
|
return EFI_ERROR (Status) ? FALSE : TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
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
|
|
|
|
@param Buffer Pointer to the image buffer.
|
|
@param BufferSize The image size.
|
|
@param AuthData Pointer to the Authenticode Signature retrieved from signed image.
|
|
@param AuthDataSize Size of the Authenticode Signature in bytes.
|
|
|
|
@retval EFI_SUCCESS Hash successfully.
|
|
@retval EFI_UNSUPPORTED Hash algorithm is not supported.
|
|
@retval EFI_UNSUPPORTED AuthData is NULL or AuthDataSize is 0.
|
|
**/
|
|
EFI_STATUS
|
|
HashPeImageByType (
|
|
IN UINT8 *Buffer,
|
|
IN UINTN BufferSize,
|
|
IN UINT8 *AuthData,
|
|
IN UINTN AuthDataSize
|
|
|
|
)
|
|
{
|
|
UINT8 Index;
|
|
|
|
if (AuthData == NULL || AuthDataSize == 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
for (Index = 0; Index < HASHALG_MAX; Index++) {
|
|
//
|
|
// Check the Hash algorithm in PE/COFF Authenticode.
|
|
// According to PKCS#7 Definition:
|
|
// SignedData ::= SEQUENCE {
|
|
// version Version,
|
|
// digestAlgorithms DigestAlgorithmIdentifiers,
|
|
// contentInfo ContentInfo,
|
|
// .... }
|
|
// 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 (AuthDataSize < 32 + mHash[Index].OidLength) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
if ((*(AuthData + 1) & TWO_BYTE_ENCODE) != TWO_BYTE_ENCODE && (*(AuthData + 1) & SHORT_FORM_MASK) != 0x00) {
|
|
//
|
|
// Only support two bytes of Long Form of Length Encoding and short form Encoding.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
if (CompareMem (AuthData + 32, mHash[Index].OidValue, mHash[Index].OidLength) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Index == HASHALG_MAX) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// HASH PE Image based on Hash algorithm in PE/COFF Authenticode.
|
|
//
|
|
if (!HashPeImage(Buffer, BufferSize, Index)) {
|
|
//
|
|
// If specific alogrithm doesn't support, try to use SHA256
|
|
//
|
|
if (!HashPeImage(Buffer, BufferSize, HASHALG_SHA256)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Check this ROM is a EFI format image or not
|
|
|
|
@param Buffer Pointer to the image buffer
|
|
@param BufferSize The image size
|
|
|
|
@retval EFI_SUCCESS This image is EFI format image.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EnrollSignedEfiHahsImage (
|
|
IN UINT8 *Buffer,
|
|
IN UINTN BufferSize
|
|
)
|
|
{
|
|
EFI_IMAGE_DOS_HEADER *DosHdr;
|
|
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION NtHeader;
|
|
EFI_IMAGE_DATA_DIRECTORY *SecDataDir;
|
|
UINTN PeCoffHeaderOffset;
|
|
UINT16 Magic;
|
|
BOOLEAN IsSignedEfi;
|
|
BOOLEAN IsEfi;
|
|
WIN_CERTIFICATE *WinCertificate;
|
|
WIN_CERTIFICATE_UEFI_GUID *WinCertUefiGuid;
|
|
WIN_CERTIFICATE_EFI_PKCS *PkcsCertData;
|
|
BOOLEAN SupportedSig;
|
|
UINT8 *AuthData;
|
|
UINTN AuthDataSize;
|
|
|
|
IsSignedEfi = FALSE;
|
|
IsEfi = FALSE;
|
|
SecDataDir = NULL;
|
|
|
|
DosHdr = (EFI_IMAGE_DOS_HEADER *) Buffer;
|
|
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
|
|
//
|
|
// DOS image header is present,
|
|
// so read the PE header after the DOS image header.
|
|
//
|
|
PeCoffHeaderOffset = DosHdr->e_lfanew;
|
|
} else {
|
|
PeCoffHeaderOffset = 0;
|
|
}
|
|
//
|
|
// Check PE/COFF image.
|
|
//
|
|
NtHeader.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) (Buffer + PeCoffHeaderOffset);
|
|
if (NtHeader.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
|
|
Magic = NtHeader.Pe32->OptionalHeader.Magic;
|
|
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
//
|
|
// Use PE32 offset.
|
|
//
|
|
SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &NtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
|
|
IsEfi = TRUE;
|
|
}
|
|
|
|
if (IsEfi && SecDataDir->Size != 0) {
|
|
//
|
|
// This image is signed.
|
|
//
|
|
IsSignedEfi = TRUE;
|
|
}
|
|
}
|
|
NtHeader.Pe32Plus = (EFI_IMAGE_NT_HEADERS64 *) (Buffer + PeCoffHeaderOffset);
|
|
if (NtHeader.Pe32Plus->Signature == EFI_IMAGE_NT_SIGNATURE) {
|
|
Magic = NtHeader.Pe32Plus->OptionalHeader.Magic;
|
|
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
|
//
|
|
// Use PE32 offset.
|
|
//
|
|
SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &NtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
|
|
IsEfi = TRUE;
|
|
}
|
|
|
|
if (IsEfi && SecDataDir->Size != 0) {
|
|
//
|
|
// This image is signed.
|
|
//
|
|
IsSignedEfi = TRUE;
|
|
}
|
|
}
|
|
if (!IsSignedEfi) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
AuthData = NULL;
|
|
AuthDataSize = 0;
|
|
SupportedSig = FALSE;
|
|
WinCertificate = (WIN_CERTIFICATE *) (Buffer + SecDataDir->VirtualAddress);
|
|
//
|
|
// Check is whether supported signature
|
|
//
|
|
if (WinCertificate->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
|
|
PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) WinCertificate;
|
|
if (PkcsCertData->Hdr.dwLength >= sizeof(PkcsCertData->Hdr)) {
|
|
AuthData = PkcsCertData->CertData;
|
|
AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr);
|
|
SupportedSig = TRUE;
|
|
}
|
|
} else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID) {
|
|
WinCertUefiGuid = (WIN_CERTIFICATE_UEFI_GUID *) WinCertificate;
|
|
if (CompareGuid(&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid) &&
|
|
WinCertUefiGuid->Hdr.dwLength >= OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)) {
|
|
AuthData = WinCertUefiGuid->CertData;
|
|
AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData);
|
|
SupportedSig = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!SupportedSig) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
return HashPeImageByType (Buffer, BufferSize, AuthData, AuthDataSize);
|
|
}
|
|
|
|
/**
|
|
Dummy function uses to hook original OutputString ().
|
|
|
|
@param[in] This The protocol instance pointer.
|
|
@param[in] String The NULL-terminated string to be displayed on the output
|
|
device(s). All output devices must also support the Unicode
|
|
drawing character codes defined in this file.
|
|
|
|
@retval EFI_SUCCESS Always return EFI_SUCCESS.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
DummyOutputString (
|
|
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
|
|
IN CHAR16 *String
|
|
)
|
|
{
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Fuction to hook original Outputstring function to disable all of
|
|
output strings.
|
|
|
|
@retval EFI_SUCCESS Always return EFI_SUCCESS.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
HookOutputString (
|
|
VOID
|
|
)
|
|
{
|
|
mOriginalOutputString = gST->ConOut->OutputString;
|
|
gST->ConOut->OutputString = DummyOutputString;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Fuction to restore original Outputstring function to ConOut in
|
|
system table.
|
|
|
|
@retval EFI_SUCCESS Always return EFI_SUCCESS.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
RestoreOutputString (
|
|
VOID
|
|
)
|
|
{
|
|
gST->ConOut->OutputString = mOriginalOutputString;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Get the signature type GUID based on the input enroll file format.
|
|
|
|
@param[in] EnrollFileFormat Enroll file format.
|
|
|
|
@return The pointer of signature type GUID or NULL if not found.
|
|
**/
|
|
STATIC
|
|
EFI_GUID *
|
|
GetSignatureTypeGuid (
|
|
IN UINTN EnrollFileFormat
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
for (Index = 0; Index < sizeof (mEmFileFormatList) / sizeof (EM_FILE_FORMAT_INFO); Index++) {
|
|
if (mEmFileFormatList[Index].FileFormat == EnrollFileFormat) {
|
|
return mEmFileFormatList[Index].SignatureTypeGuid;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Read file based on the input file context.
|
|
|
|
@param[in] FileContext Pointer to the file context
|
|
@param[out] FileSize Returns the size of the file that was read
|
|
@param[out] File Dynamically allocated memory that contains a copy of the file
|
|
|
|
@retval EFI_SUCCESS Read file successfully.
|
|
@retval EFI_UNSUPPORTED File context is invalid or load file function return failed.
|
|
@retval EFI_OUT_OF_RESOURCES Fail to allocate memory.
|
|
@retval EFI_NOT_FOUND Fail to get file info.
|
|
@retval Other Fail to get load file protocol instance or open/read file.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EmReadFile (
|
|
IN EM_FILE_CONTEXT *FileContext,
|
|
OUT UINTN *FileSize,
|
|
OUT UINT8 **File
|
|
)
|
|
{
|
|
EFI_FILE_HANDLE Dir;
|
|
EFI_FILE_HANDLE NewDir;
|
|
EFI_FILE_INFO *DirInfo;
|
|
EFI_STATUS Status;
|
|
UINTN BufferSize;
|
|
UINT8 *Buffer;
|
|
EFI_LOAD_FILE_PROTOCOL *LoadFile;
|
|
|
|
if (FileContext->IsLoadFile && !FileContext->IsBootLegacy) {
|
|
if (FileContext->Handle == NULL) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
Status = gBS->HandleProtocol (FileContext->Handle, &gEfiLoadFileProtocolGuid, (VOID**)&LoadFile);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
HookOutputString ();
|
|
|
|
while (!IsDevicePathEnd (FileContext->DevicePath)) {
|
|
FileContext->DevicePath = NextDevicePathNode (FileContext->DevicePath);
|
|
}
|
|
|
|
//
|
|
// Call LoadFile with the correct buffer size
|
|
//
|
|
BufferSize = 0;
|
|
Buffer = NULL;
|
|
Status = LoadFile->LoadFile (
|
|
LoadFile,
|
|
FileContext->DevicePath,
|
|
TRUE,
|
|
&BufferSize,
|
|
Buffer
|
|
);
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
Buffer = AllocatePool (BufferSize);
|
|
if (Buffer == NULL) {
|
|
RestoreOutputString ();
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
Status = LoadFile->LoadFile (
|
|
LoadFile,
|
|
FileContext->DevicePath,
|
|
TRUE,
|
|
&BufferSize,
|
|
Buffer
|
|
);
|
|
}
|
|
RestoreOutputString ();
|
|
if (EFI_ERROR (Status)) {
|
|
SecureBootSafeFreePool ((VOID **) &Buffer);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
} else {
|
|
Dir = FileContext->FHandle;
|
|
Status = Dir->Open (
|
|
Dir,
|
|
&NewDir,
|
|
FileContext->FileName,
|
|
EFI_FILE_READ_ONLY,
|
|
0
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
DirInfo = UtilityLibFileInfo (NewDir);
|
|
if (!DirInfo) {
|
|
NewDir->Close (NewDir);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
BufferSize = (UINTN) DirInfo->FileSize;
|
|
Buffer = AllocateZeroPool (BufferSize);
|
|
ASSERT (Buffer != NULL);
|
|
if (Buffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
SecureBootSafeFreePool ((VOID **) &DirInfo);
|
|
|
|
Status = NewDir->Read (NewDir, &BufferSize, Buffer);
|
|
NewDir->Close (NewDir);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (Buffer);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
*File = Buffer;
|
|
*FileSize = BufferSize;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Hash image based on the input file context.
|
|
|
|
@param[in] FileContext Pointer to the file context
|
|
|
|
@retval EFI_SUCCESS Hash image successfully.
|
|
@retval EFI_UNSUPPORTED Fail to read file or it is not an EFI image.
|
|
@retval Other Fail to hash image.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EnrollHashImage (
|
|
IN EM_FILE_CONTEXT *FileContext
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN BufferSize;
|
|
UINT8 *Buffer;
|
|
BOOLEAN IsSignedEfi;
|
|
CHAR16 *StringPtr;
|
|
EFI_INPUT_KEY Key;
|
|
|
|
Status = EmReadFile (FileContext, &BufferSize, &Buffer);
|
|
if (EFI_ERROR (Status)) {
|
|
SecureBootShowOkConfirmDlg (STRING_TOKEN (STR_FILE_NOT_FOUND_MESSAGE));
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
if (Buffer == NULL || !IsEfiImage (Buffer, BufferSize, &IsSignedEfi)) {
|
|
SecureBootShowOkConfirmDlg (STRING_TOKEN (STR_FORMATE_INCORRECT_MESSAGE));
|
|
SecureBootSafeFreePool ((VOID **) &Buffer);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
StringPtr = HiiGetString (
|
|
mSecureBootPrivate.HiiHandle,
|
|
STRING_TOKEN (STR_ADD_HASH_MESSAGE),
|
|
NULL
|
|
);
|
|
mSecureBootPrivate.H2ODialog->ConfirmDialog (
|
|
DlgYesNo,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
&Key,
|
|
StringPtr
|
|
);
|
|
FreePool (StringPtr);
|
|
if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
|
|
if (IsSignedEfi) {
|
|
Status = EnrollSignedEfiHahsImage (Buffer, BufferSize);
|
|
} else {
|
|
Status = HashPeImage (Buffer, BufferSize, HASHALG_SHA256) ? EFI_SUCCESS : EFI_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
FreePool (Buffer);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Enroll file based on the input file context.
|
|
|
|
@param[in] CallbackData Pointer to the secure boot manager private data
|
|
@param[in] MenuEntry Pointer to the enroll menu data
|
|
@param[in] FileContext Pointer to the file context
|
|
|
|
@retval EFI_SUCCESS Enroll file successfully.
|
|
@retval EFI_UNSUPPORTED Fail to read file.
|
|
@retval EFI_OUT_OF_RESOURCES Fail to allocate memory.
|
|
@retval EFI_ABORTED User aborts the enroll process.
|
|
@retval Other Fail to enroll file.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EnrollFile (
|
|
IN SECURE_BOOT_MANAGER_CALLBACK_DATA *CallbackData,
|
|
IN ENROLL_MENU_ENTRY *MenuEntry,
|
|
IN EM_FILE_CONTEXT *FileContext
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN BufferSize;
|
|
UINT8 *Buffer;
|
|
CHAR16 *MessageStr;
|
|
UINTN MessageStrSize;
|
|
EFI_INPUT_KEY Key;
|
|
CHAR16 GuidStr[37];
|
|
CHAR16 FormatStr[] = L"Enroll %s (Owner_GUID:%s)";
|
|
|
|
Status = EmReadFile (FileContext, &BufferSize, &Buffer);
|
|
if (EFI_ERROR (Status) || Buffer == NULL) {
|
|
SecureBootShowOkConfirmDlg (STRING_TOKEN (STR_FILE_NOT_FOUND_MESSAGE));
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
MessageStrSize = sizeof (FormatStr) + sizeof (GuidStr) + StrSize (MenuEntry->DisplayString);
|
|
MessageStr = AllocateZeroPool (MessageStrSize);
|
|
if (MessageStr == NULL) {
|
|
FreePool (Buffer);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Key.UnicodeChar = CHAR_NULL;
|
|
SecureBootGuidToStr (&CallbackData->EmEnrollSigOwnerGuid, GuidStr, sizeof(GuidStr));
|
|
UnicodeSPrint (
|
|
MessageStr,
|
|
MessageStrSize,
|
|
FormatStr,
|
|
MenuEntry->DisplayString,
|
|
GuidStr
|
|
);
|
|
CallbackData->H2ODialog->ConfirmDialog (
|
|
DlgYesNo,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
&Key,
|
|
MessageStr
|
|
);
|
|
if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
|
|
Status = SecureBootVarAddSignature (
|
|
CallbackData->SecureBootMgrState,
|
|
GetSignatureTypeGuid (CallbackData->EmFileFormat),
|
|
&CallbackData->EmEnrollSigOwnerGuid,
|
|
Buffer,
|
|
BufferSize,
|
|
TRUE
|
|
);
|
|
} else {
|
|
Status = EFI_ABORTED;
|
|
}
|
|
|
|
FreePool (MessageStr);
|
|
FreePool (Buffer);
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Append file name to existing file name.
|
|
|
|
@param Str1 existing file name
|
|
@param Str2 file name to be appended
|
|
|
|
@return Allocate a new string to hold the appended result.
|
|
Caller is responsible to free the returned string.
|
|
|
|
**/
|
|
CHAR16 *
|
|
EmAppendFileName (
|
|
IN CHAR16 *Str1,
|
|
IN CHAR16 *Str2
|
|
)
|
|
{
|
|
UINTN Size1;
|
|
UINTN Size2;
|
|
CHAR16 *Str;
|
|
CHAR16 *TmpStr;
|
|
CHAR16 *Ptr;
|
|
CHAR16 *LastSlash;
|
|
|
|
Size1 = StrSize (Str1);
|
|
Size2 = StrSize (Str2);
|
|
//
|
|
// Integer Overflow check
|
|
//
|
|
if (Size1 > Size1 + Size2 + sizeof (CHAR16) || Size2 > Size1 + Size2 + sizeof (CHAR16)) {
|
|
ASSERT (FALSE);
|
|
return NULL;
|
|
}
|
|
Str = AllocateZeroPool (Size1 + Size2 + sizeof (CHAR16));
|
|
ASSERT (Str != NULL);
|
|
if (Str == NULL) {
|
|
return NULL;
|
|
}
|
|
TmpStr = AllocateZeroPool (Size1 + Size2 + sizeof (CHAR16));
|
|
ASSERT (TmpStr != NULL);
|
|
if (TmpStr == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
StrCatS (Str, (Size1 + Size2 + sizeof (CHAR16)) / sizeof(CHAR16), Str1);
|
|
if (!((*Str == '\\') && (*(Str + 1) == 0))) {
|
|
StrCatS (Str, (Size1 + Size2 + sizeof (CHAR16)) / sizeof(CHAR16), L"\\");
|
|
}
|
|
|
|
StrCatS (Str, (Size1 + Size2 + sizeof (CHAR16)) / sizeof(CHAR16), Str2);
|
|
|
|
Ptr = Str;
|
|
LastSlash = Str;
|
|
while (*Ptr != 0) {
|
|
if (*Ptr == '\\' && *(Ptr + 1) == '.' && *(Ptr + 2) == '.' && *(Ptr + 3) == L'\\') {
|
|
//
|
|
// Convert "\Name\..\" to "\"
|
|
// DO NOT convert the .. if it is at the end of the string. This will
|
|
// break the .. behavior in changing directories.
|
|
//
|
|
|
|
//
|
|
// Use TmpStr as a backup, as StrCpy in BaseLib does not handle copy of two strings
|
|
// that overlap.
|
|
//
|
|
StrCpyS (TmpStr, (Size1 + Size2 + sizeof (CHAR16)) / sizeof(CHAR16), Ptr + 3);
|
|
StrCpyS (LastSlash, (Size1 + Size2 + sizeof (CHAR16) - ((UINT8 *) LastSlash - (UINT8 *) Str)) / sizeof(CHAR16), TmpStr);
|
|
Ptr = LastSlash;
|
|
} else if (*Ptr == '\\' && *(Ptr + 1) == '.' && *(Ptr + 2) == '\\') {
|
|
//
|
|
// Convert a "\.\" to a "\"
|
|
//
|
|
|
|
//
|
|
// Use TmpStr as a backup, as StrCpy in BaseLib does not handle copy of two strings
|
|
// that overlap.
|
|
//
|
|
StrCpyS (TmpStr, (Size1 + Size2 + sizeof (CHAR16))/sizeof(CHAR16), Ptr + 2);
|
|
StrCpyS (Ptr, (Size1 + Size2 + sizeof (CHAR16)) / sizeof(CHAR16), TmpStr);
|
|
Ptr = LastSlash;
|
|
} else if (*Ptr == '\\') {
|
|
LastSlash = Ptr;
|
|
}
|
|
|
|
Ptr++;
|
|
}
|
|
|
|
FreePool (TmpStr);
|
|
|
|
return Str;
|
|
}
|
|
|
|
/**
|
|
Check if input file name matches with the file extension string or not.
|
|
|
|
@param[in] FileName Pointer to the file name
|
|
@param[in] FileExtensionStr Pointer to the file extension string
|
|
|
|
@retval TRUE Input file name matches with the file extension string.
|
|
@retval FALSE Input file name does not match with the file extension string.
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
EmIsFileExtensionMatched (
|
|
IN CHAR16 *FileName,
|
|
IN CHAR16 *FileExtensionStr
|
|
)
|
|
{
|
|
STATIC CHAR16 *UpperCaseStr = NULL;
|
|
STATIC CHAR16 *LowerCaseStr = NULL;
|
|
STATIC UINTN BufferLen = 0;
|
|
UINTN FileExtensionStrLen;
|
|
UINTN Index;
|
|
CHAR16 Char;
|
|
|
|
if (FileName == NULL || FileExtensionStr == NULL || *FileName == CHAR_NULL || *FileExtensionStr == CHAR_NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Allocate memory to store file extension string in upper case and lower case.
|
|
//
|
|
FileExtensionStrLen = StrLen (FileExtensionStr);
|
|
if (BufferLen < FileExtensionStrLen) {
|
|
SecureBootSafeFreePool ((VOID **) &UpperCaseStr);
|
|
SecureBootSafeFreePool ((VOID **) &LowerCaseStr);
|
|
UpperCaseStr = AllocatePool (FileExtensionStrLen * sizeof (CHAR16));
|
|
LowerCaseStr = AllocatePool (FileExtensionStrLen * sizeof (CHAR16));
|
|
BufferLen = FileExtensionStrLen;
|
|
}
|
|
if (UpperCaseStr == NULL || LowerCaseStr == NULL) {
|
|
BufferLen = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
for (Index = 0; Index < FileExtensionStrLen; Index++) {
|
|
Char = FileExtensionStr[Index];
|
|
|
|
UpperCaseStr[Index] = (Char >= L'a' && Char <= L'z') ? ((Char - L'a') + L'A') : Char;
|
|
LowerCaseStr[Index] = (Char >= L'A' && Char <= L'Z') ? ((Char - L'A') + L'a') : Char;
|
|
}
|
|
|
|
//
|
|
// Check if file name contains the file extension name.
|
|
//
|
|
while (*FileName) {
|
|
for (Index = 0; Index < FileExtensionStrLen; Index++) {
|
|
if (FileName[Index] == CHAR_NULL) {
|
|
return FALSE;
|
|
}
|
|
if (FileName[Index] != LowerCaseStr[Index] &&
|
|
FileName[Index] != UpperCaseStr[Index]) {
|
|
break;
|
|
}
|
|
}
|
|
if (Index == FileExtensionStrLen) {
|
|
return TRUE;
|
|
}
|
|
|
|
FileName += 1;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Based on target file format, check if the input file is qualified or not.
|
|
|
|
@param[in] FileFormat Target file format
|
|
@param[in] FileInfo Pointer to the file info
|
|
|
|
@retval TRUE The input file is qualified.
|
|
@retval FALSE The input file is not qualified or input parameter is invalid.
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
EmIsFileQualified (
|
|
IN UINTN FileFormat,
|
|
IN EFI_FILE_INFO *FileInfo
|
|
)
|
|
{
|
|
if (FileInfo == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if ((FileFormat == EM_FILE_FORMAT_EFI && EmIsFileExtensionMatched (FileInfo->FileName, L".efi")) ||
|
|
(FileFormat == EM_FILE_FORMAT_CER && EmIsFileExtensionMatched (FileInfo->FileName, L".cer")) ||
|
|
(FileFormat == EM_FILE_FORMAT_SHA256 && FileInfo->FileSize == sizeof (EFI_SHA256_HASH)) ||
|
|
(FileFormat == EM_FILE_FORMAT_SHA512 && FileInfo->FileSize == sizeof (EFI_SHA512_HASH))) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/**
|
|
Create Menu Entry for future use, make all types together
|
|
in order to reduce code size
|
|
|
|
@param MenuType Use this parameter to identify current
|
|
Menu type
|
|
|
|
@return A valid pointer pointing to the allocated memory pool for current menu entry
|
|
or NULL if it cannot allocate memory for current menu entry
|
|
|
|
**/
|
|
STATIC
|
|
ENROLL_MENU_ENTRY *
|
|
EmCreateFileMenuEntry (
|
|
VOID
|
|
)
|
|
{
|
|
ENROLL_MENU_ENTRY *MenuEntry;
|
|
UINTN ContextSize;
|
|
|
|
|
|
ContextSize = sizeof (BM_FILE_CONTEXT);
|
|
|
|
MenuEntry = AllocateZeroPool (sizeof (ENROLL_MENU_ENTRY));
|
|
if (NULL == MenuEntry) {
|
|
return MenuEntry;
|
|
}
|
|
|
|
MenuEntry->VariableContext = AllocateZeroPool (ContextSize);
|
|
if (NULL == MenuEntry->VariableContext) {
|
|
SecureBootSafeFreePool ((VOID**) &MenuEntry);
|
|
MenuEntry = NULL;
|
|
return MenuEntry;
|
|
}
|
|
|
|
MenuEntry->Signature = EM_MENU_ENTRY_SIGNATURE;
|
|
MenuEntry->ContextSelection = EM_FILE_CONTEXT_SELECT;
|
|
return MenuEntry;
|
|
}
|
|
|
|
|
|
/**
|
|
Find files under current directory
|
|
All files and sub-directories in current directory
|
|
will be stored in DirectoryMenu for future use.
|
|
|
|
@param FileOption Pointer for Dir to explore
|
|
|
|
@retval TRUE Get files from current dir successfully
|
|
@retval FALSE Can't get files from current dir
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EmFindFiles (
|
|
IN SECURE_BOOT_MANAGER_CALLBACK_DATA *CallbackData,
|
|
IN ENROLL_MENU_ENTRY *MenuEntry
|
|
)
|
|
{
|
|
EFI_FILE_HANDLE NewDir;
|
|
EFI_FILE_HANDLE Dir;
|
|
EFI_FILE_INFO *DirInfo;
|
|
UINTN BufferSize;
|
|
UINTN DirBufferSize;
|
|
ENROLL_MENU_ENTRY *NewMenuEntry;
|
|
EM_FILE_CONTEXT *FileContext;
|
|
EM_FILE_CONTEXT *NewFileContext;
|
|
UINTN Pass;
|
|
EFI_STATUS Status;
|
|
UINTN OptionNumber;
|
|
|
|
FileContext = (EM_FILE_CONTEXT *) MenuEntry->VariableContext;
|
|
Dir = FileContext->FHandle;
|
|
OptionNumber = 0;
|
|
//
|
|
// Open current directory to get files from it
|
|
//
|
|
Status = Dir->Open (
|
|
Dir,
|
|
&NewDir,
|
|
FileContext->FileName,
|
|
EFI_FILE_READ_ONLY,
|
|
0
|
|
);
|
|
if (!FileContext->IsRoot) {
|
|
Dir->Close (Dir);
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
DirInfo = UtilityLibFileInfo (NewDir);
|
|
if (!DirInfo) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
if (!(DirInfo->Attribute & EFI_FILE_DIRECTORY)) {
|
|
SecureBootSafeFreePool ((VOID **) &DirInfo);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
SecureBootSafeFreePool ((VOID **) &DirInfo);
|
|
|
|
FileContext->DevicePath = FileDevicePath (
|
|
FileContext->Handle,
|
|
FileContext->FileName
|
|
);
|
|
|
|
DirBufferSize = sizeof (EFI_FILE_INFO) + 1024;
|
|
DirInfo = AllocateZeroPool (DirBufferSize);
|
|
if (!DirInfo) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
//
|
|
// Get all files in current directory
|
|
// Pass 1 to get Directories
|
|
// Pass 2 to get files that are EFI images
|
|
//
|
|
for (Pass = 1; Pass <= 2; Pass++) {
|
|
NewDir->SetPosition (NewDir, 0);
|
|
for (;;) {
|
|
BufferSize = DirBufferSize;
|
|
Status = NewDir->Read (NewDir, &BufferSize, DirInfo);
|
|
if (EFI_ERROR (Status) || BufferSize == 0) {
|
|
break;
|
|
}
|
|
|
|
if ((DirInfo->Attribute & EFI_FILE_DIRECTORY && Pass == 2) ||
|
|
(!(DirInfo->Attribute & EFI_FILE_DIRECTORY) && Pass == 1)
|
|
) {
|
|
//
|
|
// Pass 1 is for Directories
|
|
// Pass 2 is for file names
|
|
//
|
|
continue;
|
|
}
|
|
|
|
if (!(DirInfo->Attribute & EFI_FILE_DIRECTORY || EmIsFileQualified (CallbackData->EmFileFormat, DirInfo))) {
|
|
//
|
|
// Slip file unless it is a directory entry or file is qualified in current format.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
NewMenuEntry = EmCreateFileMenuEntry ();
|
|
if (NULL == NewMenuEntry) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
NewFileContext = (EM_FILE_CONTEXT *) NewMenuEntry->VariableContext;
|
|
NewFileContext->Handle = FileContext->Handle;
|
|
NewFileContext->FileName = EmAppendFileName (
|
|
FileContext->FileName,
|
|
DirInfo->FileName
|
|
);
|
|
if (NewFileContext->FileName == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
NewFileContext->FHandle = NewDir;
|
|
NewFileContext->DevicePath = FileDevicePath (
|
|
NewFileContext->Handle,
|
|
NewFileContext->FileName
|
|
);
|
|
NewMenuEntry->HelpString = NULL;
|
|
|
|
MenuEntry->DisplayStringToken = SecureBootGetStrTokenFromDepository (
|
|
CallbackData,
|
|
mSecureBootFileOptionStrDepository
|
|
);
|
|
|
|
NewFileContext->IsDir = (BOOLEAN) ((DirInfo->Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY);
|
|
|
|
if (NewFileContext->IsDir) {
|
|
BufferSize = StrLen (DirInfo->FileName) * 2 + 6;
|
|
NewMenuEntry->DisplayString = AllocateZeroPool (BufferSize);
|
|
|
|
UnicodeSPrint (
|
|
NewMenuEntry->DisplayString,
|
|
BufferSize,
|
|
L"<%s>",
|
|
DirInfo->FileName
|
|
);
|
|
|
|
} else {
|
|
NewMenuEntry->DisplayString = AllocateCopyPool (StrSize (DirInfo->FileName), DirInfo->FileName);
|
|
}
|
|
|
|
NewFileContext->IsRoot = FALSE;
|
|
NewFileContext->IsLoadFile = FALSE;
|
|
NewFileContext->IsRemovableMedia = FALSE;
|
|
|
|
NewMenuEntry->OptionNumber = OptionNumber;
|
|
OptionNumber++;
|
|
InsertTailList (&mSecureBootDirectoryMenu.Head, &NewMenuEntry->Link);
|
|
}
|
|
}
|
|
|
|
mSecureBootDirectoryMenu.MenuNumber = OptionNumber;
|
|
SecureBootSafeFreePool ((VOID**) &DirInfo);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Use this routine to get one particular menu entry in specified menu
|
|
|
|
@param MenuOption The menu that we will search
|
|
@param MenuNumber The menunubmer that we want
|
|
|
|
@return The desired menu entry
|
|
|
|
**/
|
|
ENROLL_MENU_ENTRY *
|
|
EmGetMenuEntry (
|
|
ENROLL_MENU_OPTION *MenuOption,
|
|
UINTN MenuNumber
|
|
)
|
|
{
|
|
ENROLL_MENU_ENTRY *NewMenuEntry;
|
|
UINTN Index;
|
|
LIST_ENTRY *List;
|
|
|
|
if (MenuNumber >= MenuOption->MenuNumber) {
|
|
return NULL;
|
|
}
|
|
|
|
List = MenuOption->Head.ForwardLink;
|
|
for (Index = 0; Index < MenuNumber; Index++) {
|
|
List = List->ForwardLink;
|
|
}
|
|
|
|
NewMenuEntry = CR (List, ENROLL_MENU_ENTRY, Link, EM_MENU_ENTRY_SIGNATURE);
|
|
|
|
return NewMenuEntry;
|
|
}
|
|
|
|
/**
|
|
Get the form ID of enroll menu based on secure boot manager state.
|
|
|
|
@param[in] SecureBootMgrState Secure boot manager state
|
|
|
|
@return The form ID of enroll menu or zero if not found.
|
|
**/
|
|
STATIC
|
|
EFI_FORM_ID
|
|
GetEnrollMenuFormId (
|
|
IN SECURE_BOOT_MANAGER_STATE SecureBootMgrState
|
|
)
|
|
{
|
|
switch (SecureBootMgrState) {
|
|
|
|
case PkForm:
|
|
return FORM_ID_PK_ENROLL_SIGNATURE;
|
|
|
|
case KekForm:
|
|
return FORM_ID_KEK_ENROLL_SIGNATURE;
|
|
|
|
case DbForm:
|
|
return FORM_ID_DB_ENROLL_SIGNATURE;
|
|
|
|
case DbxForm:
|
|
return FORM_ID_DBX_ENROLL_SIGNATURE;
|
|
|
|
case DbtForm:
|
|
return FORM_ID_DBT_ENROLL_SIGNATURE;
|
|
|
|
case DbrForm:
|
|
return FORM_ID_DBR_ENROLL_SIGNATURE;
|
|
|
|
case HashImageForm:
|
|
return FORM_ENROLL_HASH_ID;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Update the Enroll Menu page.
|
|
|
|
@param CallbackData Pointer to SECURE_BOOT_MANAGER_CALLBACK_DATA instance
|
|
@param MenuOption Pointer to menu options to display.
|
|
|
|
**/
|
|
VOID
|
|
UpdateEnrollMenuPage (
|
|
IN SECURE_BOOT_MANAGER_CALLBACK_DATA *CallbackData,
|
|
IN ENROLL_MENU_OPTION *MenuOption
|
|
)
|
|
{
|
|
UINTN Index;
|
|
ENROLL_MENU_ENTRY *NewMenuEntry;
|
|
EM_FILE_CONTEXT *NewFileContext;
|
|
EFI_FORM_ID FormId;
|
|
VOID *StartOpCodeHandle;
|
|
VOID *EndOpCodeHandle;
|
|
EFI_IFR_GUID_LABEL *StartLabel;
|
|
EFI_IFR_GUID_LABEL *EndLabel;
|
|
|
|
NewMenuEntry = NULL;
|
|
NewFileContext = NULL;
|
|
FormId = GetEnrollMenuFormId (CallbackData->SecureBootMgrState);
|
|
ASSERT (FormId != 0);
|
|
if (FormId == 0) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Allocate space for creation of UpdateData Buffer
|
|
//
|
|
StartOpCodeHandle = HiiAllocateOpCodeHandle ();
|
|
ASSERT (StartOpCodeHandle != NULL);
|
|
|
|
EndOpCodeHandle = HiiAllocateOpCodeHandle ();
|
|
ASSERT (EndOpCodeHandle != NULL);
|
|
//
|
|
// Create Hii Extend Label OpCode as the start opcode
|
|
//
|
|
StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
|
|
StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
|
|
StartLabel->Number = FORM_ENROLL_FILE_ID;
|
|
|
|
//
|
|
// Create Hii Extend Label OpCode as the end opcode
|
|
//
|
|
EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
|
|
EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
|
|
EndLabel->Number = FORM_ENROLL_FILE_END_ID;
|
|
|
|
for (Index = 0; Index < MenuOption->MenuNumber; Index++) {
|
|
NewMenuEntry = EmGetMenuEntry (MenuOption, Index);
|
|
NewFileContext = (EM_FILE_CONTEXT *) NewMenuEntry->VariableContext;
|
|
|
|
if (NewFileContext->IsBootLegacy) {
|
|
continue;
|
|
}
|
|
|
|
if (CallbackData->SecureBootMgrState == HashImageForm || NewFileContext->IsDir || NewFileContext->IsLoadFile) {
|
|
HiiCreateActionOpCode (
|
|
StartOpCodeHandle,
|
|
(UINT16) (ENROLL_FILE_OPTION_OFFSET + Index),
|
|
NewMenuEntry->DisplayStringToken,
|
|
STRING_TOKEN (STR_SB_NULL_STRING),
|
|
EFI_IFR_FLAG_CALLBACK,
|
|
0
|
|
);
|
|
} else {
|
|
HiiCreateStringOpCode (
|
|
StartOpCodeHandle,
|
|
(UINT16) (ENROLL_FILE_OPTION_OFFSET + Index),
|
|
SECURE_BOOT_FORM_ID,
|
|
OFFSET_OF(SECURE_BOOT_NV_DATA, EnrollSigGuidStr),
|
|
NewMenuEntry->DisplayStringToken,
|
|
STRING_TOKEN (STR_ENROLL_SIGNATURE_FILE_HELP),
|
|
EFI_IFR_FLAG_CALLBACK,
|
|
0,
|
|
GUID_STRING_LENGTH,
|
|
GUID_STRING_LENGTH,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
HiiUpdateForm (
|
|
CallbackData->HiiHandle,
|
|
&mSecureBootGuid,
|
|
FormId,
|
|
StartOpCodeHandle,
|
|
EndOpCodeHandle
|
|
);
|
|
HiiFreeOpCodeHandle (StartOpCodeHandle);
|
|
HiiFreeOpCodeHandle (EndOpCodeHandle);
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
Create string tokens for a menu from its help strings and display strings
|
|
|
|
@param HiiHandle Hii Handle of the package to be updated.
|
|
@param MenuOption The Menu whose string tokens need to be created
|
|
|
|
@retval EFI_SUCCESS string tokens created successfully
|
|
@retval Other contain some errors
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EmCreateMenuStringToken (
|
|
IN EFI_HII_HANDLE HiiHandle,
|
|
IN ENROLL_MENU_OPTION *MenuOption
|
|
)
|
|
{
|
|
ENROLL_MENU_ENTRY *NewMenuEntry;
|
|
UINTN Index;
|
|
|
|
for (Index = 0; Index < MenuOption->MenuNumber; Index++) {
|
|
NewMenuEntry = EmGetMenuEntry (MenuOption, Index);
|
|
if (NewMenuEntry == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
NewMenuEntry->DisplayStringToken = HiiSetString (
|
|
HiiHandle,
|
|
NewMenuEntry->DisplayStringToken,
|
|
NewMenuEntry->DisplayString,
|
|
NULL
|
|
);
|
|
if (NULL == NewMenuEntry->HelpString) {
|
|
NewMenuEntry->HelpStringToken = NewMenuEntry->DisplayStringToken;
|
|
} else {
|
|
NewMenuEntry->HelpStringToken = HiiSetString (
|
|
HiiHandle,
|
|
NewMenuEntry->HelpStringToken,
|
|
NewMenuEntry->HelpString,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Free resources allocated in Allocate Rountine
|
|
|
|
@param FreeMenu Menu to be freed
|
|
|
|
**/
|
|
VOID
|
|
EmFreeMenu (
|
|
ENROLL_MENU_OPTION *FreeMenu
|
|
)
|
|
{
|
|
ENROLL_MENU_ENTRY *MenuEntry;
|
|
|
|
while (!IsListEmpty (&FreeMenu->Head)) {
|
|
MenuEntry = CR (
|
|
FreeMenu->Head.ForwardLink,
|
|
ENROLL_MENU_ENTRY,
|
|
Link,
|
|
EM_MENU_ENTRY_SIGNATURE
|
|
);
|
|
RemoveEntryList (&MenuEntry->Link);
|
|
EmDestroyMenuEntry (MenuEntry);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Find file systems for current Extensible Firmware
|
|
Including Handles that support Simple File System
|
|
protocol, Load File protocol.
|
|
|
|
Building up the FileSystem Menu for user selection
|
|
All file system will be stored in FsOptionMenu
|
|
for future use.
|
|
|
|
@param CallbackData BMM context data
|
|
|
|
@retval EFI_SUCCESS Success find the file system
|
|
@retval EFI_OUT_OF_RESOURCES Can not create menu entry
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EmFindFileSystem (
|
|
VOID
|
|
)
|
|
{
|
|
UINTN NoBlkIoHandles;
|
|
UINTN NoSimpleFsHandles;
|
|
EFI_HANDLE *BlkIoHandle;
|
|
EFI_HANDLE *SimpleFsHandle;
|
|
UINT16 *VolumeLabel;
|
|
EFI_BLOCK_IO_PROTOCOL *BlkIo;
|
|
UINTN Index;
|
|
EFI_STATUS Status;
|
|
ENROLL_MENU_ENTRY *MenuEntry;
|
|
EM_FILE_CONTEXT *FileContext;
|
|
UINT16 *TempStr;
|
|
UINTN OptionNumber;
|
|
VOID *Buffer;
|
|
BOOLEAN RemovableMedia;
|
|
|
|
if ((mSecureBootFsOptionMenu.MenuNumber != 0) && !IsListEmpty (&mSecureBootFsOptionMenu.Head)) {
|
|
EmFreeMenu (&mSecureBootFsOptionMenu);
|
|
mSecureBootFsOptionMenu.MenuNumber = 0;
|
|
}
|
|
|
|
NoSimpleFsHandles = 0;
|
|
OptionNumber = 0;
|
|
InitializeListHead (&mSecureBootFsOptionMenu.Head);
|
|
//
|
|
// Locate Handles that support BlockIo protocol
|
|
//
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiBlockIoProtocolGuid,
|
|
NULL,
|
|
&NoBlkIoHandles,
|
|
&BlkIoHandle
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
|
|
for (Index = 0; Index < NoBlkIoHandles; Index++) {
|
|
Status = gBS->HandleProtocol (
|
|
BlkIoHandle[Index],
|
|
&gEfiBlockIoProtocolGuid,
|
|
(VOID **) &BlkIo
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Issue a dummy read to trigger reinstall of BlockIo protocol for removable media
|
|
//
|
|
if (BlkIo->Media->RemovableMedia) {
|
|
Buffer = AllocateZeroPool (BlkIo->Media->BlockSize);
|
|
if (NULL == Buffer) {
|
|
SecureBootSafeFreePool ((VOID**) &BlkIoHandle);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
BlkIo->ReadBlocks (
|
|
BlkIo,
|
|
BlkIo->Media->MediaId,
|
|
0,
|
|
BlkIo->Media->BlockSize,
|
|
Buffer
|
|
);
|
|
SecureBootSafeFreePool ((VOID**) &Buffer);
|
|
}
|
|
}
|
|
SecureBootSafeFreePool ((VOID**) &BlkIoHandle);
|
|
}
|
|
|
|
//
|
|
// Locate Handles that support Simple File System protocol
|
|
//
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
NULL,
|
|
&NoSimpleFsHandles,
|
|
&SimpleFsHandle
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Find all the instances of the File System prototocol
|
|
//
|
|
for (Index = 0; Index < NoSimpleFsHandles; Index++) {
|
|
Status = gBS->HandleProtocol (
|
|
SimpleFsHandle[Index],
|
|
&gEfiBlockIoProtocolGuid,
|
|
(VOID **) &BlkIo
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// If no block IO exists assume it's NOT a removable media
|
|
//
|
|
RemovableMedia = FALSE;
|
|
} else {
|
|
//
|
|
// If block IO exists check to see if it's remobable media
|
|
//
|
|
RemovableMedia = BlkIo->Media->RemovableMedia;
|
|
}
|
|
|
|
//
|
|
// Allocate pool for this load option
|
|
//
|
|
MenuEntry = EmCreateFileMenuEntry ();
|
|
if (NULL == MenuEntry) {
|
|
SecureBootSafeFreePool ((VOID**) &SimpleFsHandle);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
FileContext = (EM_FILE_CONTEXT *) MenuEntry->VariableContext;
|
|
|
|
FileContext->Handle = SimpleFsHandle[Index];
|
|
MenuEntry->OptionNumber = Index;
|
|
FileContext->FHandle = UtilityLibOpenRoot (FileContext->Handle);
|
|
if (!FileContext->FHandle) {
|
|
EmDestroyMenuEntry (MenuEntry);
|
|
continue;
|
|
}
|
|
|
|
MenuEntry->HelpString = DevicePathToStr (DevicePathFromHandle (FileContext->Handle));
|
|
FileContext->Info = UtilityLibFileSystemVolumeLabelInfo (FileContext->FHandle);
|
|
FileContext->FileName = UtilityLibStrDuplicate (L"\\");
|
|
if (FileContext->FileName == NULL) {
|
|
EmDestroyMenuEntry (MenuEntry);
|
|
SecureBootSafeFreePool ((VOID**) &SimpleFsHandle);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
FileContext->DevicePath = FileDevicePath (
|
|
FileContext->Handle,
|
|
FileContext->FileName
|
|
);
|
|
FileContext->IsDir = TRUE;
|
|
FileContext->IsRoot = TRUE;
|
|
FileContext->IsRemovableMedia = FALSE;
|
|
FileContext->IsLoadFile = FALSE;
|
|
|
|
//
|
|
// Get current file system's Volume Label
|
|
//
|
|
if (FileContext->Info == NULL) {
|
|
VolumeLabel = L"NO FILE SYSTEM INFO";
|
|
} else {
|
|
VolumeLabel = FileContext->Info->VolumeLabel;
|
|
if (*VolumeLabel == 0x0000) {
|
|
VolumeLabel = L"NO VOLUME LABEL";
|
|
}
|
|
}
|
|
|
|
TempStr = MenuEntry->HelpString;
|
|
MenuEntry->DisplayString = AllocateZeroPool (MAX_CHAR);
|
|
ASSERT (MenuEntry->DisplayString != NULL);
|
|
UnicodeSPrint (
|
|
MenuEntry->DisplayString,
|
|
MAX_CHAR,
|
|
L"%s, [%s]",
|
|
VolumeLabel,
|
|
TempStr
|
|
);
|
|
OptionNumber++;
|
|
InsertTailList (&mSecureBootFsOptionMenu.Head, &MenuEntry->Link);
|
|
}
|
|
}
|
|
|
|
if (NoSimpleFsHandles != 0) {
|
|
SecureBootSafeFreePool ((VOID**) &SimpleFsHandle);
|
|
}
|
|
|
|
//
|
|
// Remember how many file system options are here
|
|
//
|
|
mSecureBootFsOptionMenu.MenuNumber = OptionNumber;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Update the file explower page with the refershed file system.
|
|
|
|
@param CallbackData BSECURE_BOOT_MANAGER_CALLBACK_DATA instance
|
|
@param KeyValue Key value to identify the type of data to expect.
|
|
|
|
@retval TRUE Inform the caller to create a callback packet to exit file explorer.
|
|
@retval FALSE Indicate that there is no need to exit file explorer.
|
|
|
|
**/
|
|
BOOLEAN
|
|
UpdateEmFileExplorer (
|
|
IN SECURE_BOOT_MANAGER_CALLBACK_DATA *CallbackData,
|
|
IN UINT16 KeyValue
|
|
)
|
|
{
|
|
UINT16 FileOptionMask;
|
|
ENROLL_MENU_ENTRY *NewMenuEntry;
|
|
EM_FILE_CONTEXT *NewFileContext;
|
|
EFI_STATUS Status;
|
|
|
|
|
|
FileOptionMask = (UINT16) (ENROLL_FILE_OPTION_MASK & KeyValue);
|
|
NewMenuEntry = NULL;
|
|
|
|
if (EM_UNKNOWN_CONTEXT == CallbackData->EmDisplayContext) {
|
|
EmFindFileSystem ();
|
|
EmCreateMenuStringToken (CallbackData->HiiHandle, &mSecureBootFsOptionMenu);
|
|
UpdateEnrollMenuPage (CallbackData, &mSecureBootFsOptionMenu);
|
|
CallbackData->EmDisplayContext = EM_FILE_SYSTEM;
|
|
} else {
|
|
if (EM_FILE_SYSTEM == CallbackData->EmDisplayContext) {
|
|
NewMenuEntry = EmGetMenuEntry (&mSecureBootFsOptionMenu, FileOptionMask);
|
|
} else if (EM_DIRECTORY == CallbackData->EmDisplayContext) {
|
|
NewMenuEntry = EmGetMenuEntry (&mSecureBootDirectoryMenu, FileOptionMask);
|
|
}
|
|
if (NewMenuEntry == NULL) {
|
|
return FALSE;
|
|
}
|
|
NewFileContext = (EM_FILE_CONTEXT *) NewMenuEntry->VariableContext;
|
|
|
|
if (NewFileContext->IsDir ) {
|
|
CallbackData->EmDisplayContext = EM_DIRECTORY;
|
|
RemoveEntryList (&NewMenuEntry->Link);
|
|
EmFreeMenu (&mSecureBootDirectoryMenu);
|
|
SecureBootReclaimStrDepository (mSecureBootFileOptionStrDepository);
|
|
Status = EmFindFiles (CallbackData, NewMenuEntry);
|
|
EmDestroyMenuEntry (NewMenuEntry);
|
|
if (EFI_ERROR (Status)) {
|
|
return TRUE;
|
|
}
|
|
|
|
EmCreateMenuStringToken (CallbackData->HiiHandle, &mSecureBootDirectoryMenu);
|
|
UpdateEnrollMenuPage (CallbackData, &mSecureBootDirectoryMenu);
|
|
} else {
|
|
if (CallbackData->SecureBootMgrState == HashImageForm) {
|
|
EnrollHashImage (NewFileContext);
|
|
} else {
|
|
EnrollFile (CallbackData, NewMenuEntry, NewFileContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
Get the file format of enroll menu based on secure boot manager state.
|
|
|
|
@param[in] CallbackData Pointer to the secure boot manager private data
|
|
@param[out] FileFormat Returns the file format
|
|
|
|
@retval EFI_SUCCESS Success find the file system
|
|
@retval EFI_INVALID_PARAMETER Input parameter is NULL.
|
|
@retval EFI_OUT_OF_RESOURCES Fail to allocate memory.
|
|
@retval EFI_ABORTED User aborts the question dialog.
|
|
@retval EFI_NOT_FOUND File format is not found.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
GetEnrollMenuFileFormat (
|
|
IN SECURE_BOOT_MANAGER_CALLBACK_DATA *CallbackData,
|
|
OUT UINTN *FileFormat
|
|
)
|
|
{
|
|
CHAR16 *TitleStr;
|
|
CHAR16 **OptionStrArray;
|
|
UINT32 OptionCount;
|
|
UINT32 Index;
|
|
UINT32 SelectIndex;
|
|
UINTN MaxStrLen;
|
|
EFI_INPUT_KEY Key;
|
|
|
|
if (CallbackData == NULL || FileFormat == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
switch (CallbackData->SecureBootMgrState) {
|
|
|
|
case HashImageForm:
|
|
*FileFormat = EM_FILE_FORMAT_EFI;
|
|
break;
|
|
|
|
case PkForm:
|
|
case KekForm:
|
|
case DbtForm:
|
|
case DbrForm:
|
|
*FileFormat = EM_FILE_FORMAT_CER;
|
|
break;
|
|
|
|
case DbForm:
|
|
case DbxForm:
|
|
//
|
|
// Pop dialog for user to select file format.
|
|
//
|
|
OptionCount = sizeof (mEmFileFormatList) / sizeof (EM_FILE_FORMAT_INFO);
|
|
OptionStrArray = AllocatePool (OptionCount * sizeof (CHAR16 *));
|
|
if (OptionStrArray == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
MaxStrLen = 0;
|
|
for (Index = 0; Index < OptionCount; Index++) {
|
|
OptionStrArray[Index] = HiiGetString (CallbackData->HiiHandle, mEmFileFormatList[Index].StringId, NULL);
|
|
if (OptionStrArray[Index] != NULL) {
|
|
MaxStrLen = MAX (StrLen (OptionStrArray[Index]), MaxStrLen);
|
|
}
|
|
}
|
|
|
|
Key.UnicodeChar = CHAR_NULL;
|
|
TitleStr = HiiGetString (CallbackData->HiiHandle, STRING_TOKEN (STR_SELECT_SIGNATURE_FORMAT_MESSAGE), NULL);
|
|
SelectIndex = 0;
|
|
CallbackData->H2ODialog->OneOfOptionDialog (
|
|
OptionCount,
|
|
FALSE,
|
|
NULL,
|
|
&Key,
|
|
(UINT32) MaxStrLen,
|
|
TitleStr,
|
|
&SelectIndex,
|
|
OptionStrArray,
|
|
0
|
|
);
|
|
SecureBootSafeFreePool ((VOID **) &TitleStr);
|
|
for (Index = 0; Index < OptionCount; Index++) {
|
|
SecureBootSafeFreePool ((VOID **) &OptionStrArray[Index]);
|
|
}
|
|
FreePool (OptionStrArray);
|
|
if (Key.UnicodeChar != CHAR_CARRIAGE_RETURN) {
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
*FileFormat = mEmFileFormatList[SelectIndex].FileFormat;
|
|
break;
|
|
|
|
default:
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Initialize file system for Enroll menu
|
|
|
|
@param[in] CallbackData Pointer to SECURE_BOOT_MANAGER_CALLBACK_DATA instance
|
|
|
|
@retval EFI_SUCCESS Initialize enroll menu successful
|
|
@retval Other Fail to get the file format of enroll menu.
|
|
**/
|
|
EFI_STATUS
|
|
InitializeEnrollMenu (
|
|
IN SECURE_BOOT_MANAGER_CALLBACK_DATA *CallbackData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = GetEnrollMenuFileFormat (CallbackData, &CallbackData->EmFileFormat);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
CallbackData->EmDisplayContext = EM_UNKNOWN_CONTEXT;
|
|
UpdateEmFileExplorer (CallbackData, 0);
|
|
return EFI_SUCCESS;
|
|
}
|