/** @file | |
Blob verifier library that uses SEV hashes table. The hashes table holds the | |
allowed hashes of the kernel, initrd, and cmdline blobs. | |
Copyright (C) 2021, IBM Corporation | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <Library/BaseCryptLib.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/BlobVerifierLib.h> | |
/** | |
The SEV Hashes table must be in encrypted memory and has the table | |
and its entries described by | |
<GUID>|UINT16 <len>|<data> | |
With the whole table GUID being 9438d606-4f22-4cc9-b479-a793d411fd21 | |
The current possible table entries are for the kernel, the initrd | |
and the cmdline: | |
4de79437-abd2-427f-b835-d5b172d2045b kernel | |
44baf731-3a2f-4bd7-9af1-41e29169781d initrd | |
97d02dd8-bd20-4c94-aa78-e7714d36ab2a cmdline | |
The size of the entry is used to identify the hash, but the | |
expectation is that it will be 32 bytes of SHA-256. | |
**/ | |
#define SEV_HASH_TABLE_GUID \ | |
(GUID) { 0x9438d606, 0x4f22, 0x4cc9, { 0xb4, 0x79, 0xa7, 0x93, 0xd4, 0x11, 0xfd, 0x21 } } | |
#define SEV_KERNEL_HASH_GUID \ | |
(GUID) { 0x4de79437, 0xabd2, 0x427f, { 0xb8, 0x35, 0xd5, 0xb1, 0x72, 0xd2, 0x04, 0x5b } } | |
#define SEV_INITRD_HASH_GUID \ | |
(GUID) { 0x44baf731, 0x3a2f, 0x4bd7, { 0x9a, 0xf1, 0x41, 0xe2, 0x91, 0x69, 0x78, 0x1d } } | |
#define SEV_CMDLINE_HASH_GUID \ | |
(GUID) { 0x97d02dd8, 0xbd20, 0x4c94, { 0xaa, 0x78, 0xe7, 0x71, 0x4d, 0x36, 0xab, 0x2a } } | |
STATIC CONST EFI_GUID mSevKernelHashGuid = SEV_KERNEL_HASH_GUID; | |
STATIC CONST EFI_GUID mSevInitrdHashGuid = SEV_INITRD_HASH_GUID; | |
STATIC CONST EFI_GUID mSevCmdlineHashGuid = SEV_CMDLINE_HASH_GUID; | |
#pragma pack (1) | |
typedef struct { | |
GUID Guid; | |
UINT16 Len; | |
UINT8 Data[]; | |
} HASH_TABLE; | |
#pragma pack () | |
STATIC HASH_TABLE *mHashesTable; | |
STATIC UINT16 mHashesTableSize; | |
STATIC | |
CONST GUID * | |
FindBlobEntryGuid ( | |
IN CONST CHAR16 *BlobName | |
) | |
{ | |
if (StrCmp (BlobName, L"kernel") == 0) { | |
return &mSevKernelHashGuid; | |
} else if (StrCmp (BlobName, L"initrd") == 0) { | |
return &mSevInitrdHashGuid; | |
} else if (StrCmp (BlobName, L"cmdline") == 0) { | |
return &mSevCmdlineHashGuid; | |
} else { | |
return NULL; | |
} | |
} | |
/** | |
Verify blob from an external source. | |
@param[in] BlobName The name of the blob | |
@param[in] Buf The data of the blob | |
@param[in] BufSize The size of the blob in bytes | |
@retval EFI_SUCCESS The blob was verified successfully. | |
@retval EFI_ACCESS_DENIED The blob could not be verified, and therefore | |
should be considered non-secure. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
VerifyBlob ( | |
IN CONST CHAR16 *BlobName, | |
IN CONST VOID *Buf, | |
IN UINT32 BufSize | |
) | |
{ | |
CONST GUID *Guid; | |
INT32 Remaining; | |
HASH_TABLE *Entry; | |
if ((mHashesTable == NULL) || (mHashesTableSize == 0)) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: Verifier called but no hashes table discoverd in MEMFD\n", | |
__func__ | |
)); | |
return EFI_ACCESS_DENIED; | |
} | |
Guid = FindBlobEntryGuid (BlobName); | |
if (Guid == NULL) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: Unknown blob name \"%s\"\n", | |
__func__, | |
BlobName | |
)); | |
return EFI_ACCESS_DENIED; | |
} | |
// | |
// Remaining is INT32 to catch underflow in case Entry->Len has a | |
// very high UINT16 value | |
// | |
for (Entry = mHashesTable, Remaining = mHashesTableSize; | |
Remaining >= sizeof *Entry && Remaining >= Entry->Len; | |
Remaining -= Entry->Len, | |
Entry = (HASH_TABLE *)((UINT8 *)Entry + Entry->Len)) | |
{ | |
UINTN EntrySize; | |
EFI_STATUS Status; | |
UINT8 Hash[SHA256_DIGEST_SIZE]; | |
if (!CompareGuid (&Entry->Guid, Guid)) { | |
continue; | |
} | |
DEBUG ((DEBUG_INFO, "%a: Found GUID %g in table\n", __func__, Guid)); | |
EntrySize = Entry->Len - sizeof Entry->Guid - sizeof Entry->Len; | |
if (EntrySize != SHA256_DIGEST_SIZE) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: Hash has the wrong size %d != %d\n", | |
__func__, | |
EntrySize, | |
SHA256_DIGEST_SIZE | |
)); | |
return EFI_ACCESS_DENIED; | |
} | |
// | |
// Calculate the buffer's hash and verify that it is identical to the | |
// expected hash table entry | |
// | |
Sha256HashAll (Buf, BufSize, Hash); | |
if (CompareMem (Entry->Data, Hash, EntrySize) == 0) { | |
Status = EFI_SUCCESS; | |
DEBUG (( | |
DEBUG_INFO, | |
"%a: Hash comparison succeeded for \"%s\"\n", | |
__func__, | |
BlobName | |
)); | |
} else { | |
Status = EFI_ACCESS_DENIED; | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: Hash comparison failed for \"%s\"\n", | |
__func__, | |
BlobName | |
)); | |
} | |
return Status; | |
} | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: Hash GUID %g not found in table\n", | |
__func__, | |
Guid | |
)); | |
return EFI_ACCESS_DENIED; | |
} | |
/** | |
Locate the SEV hashes table. | |
This function always returns success, even if the table can't be found. The | |
subsequent VerifyBlob calls will fail if no table was found. | |
@retval RETURN_SUCCESS The hashes table is set up correctly, or there is no | |
hashes table | |
**/ | |
RETURN_STATUS | |
EFIAPI | |
BlobVerifierLibSevHashesConstructor ( | |
VOID | |
) | |
{ | |
HASH_TABLE *Ptr; | |
UINT32 Size; | |
mHashesTable = NULL; | |
mHashesTableSize = 0; | |
Ptr = (void *)(UINTN)FixedPcdGet64 (PcdQemuHashTableBase); | |
Size = FixedPcdGet32 (PcdQemuHashTableSize); | |
if ((Ptr == NULL) || (Size < sizeof *Ptr) || | |
!CompareGuid (&Ptr->Guid, &SEV_HASH_TABLE_GUID) || | |
(Ptr->Len < sizeof *Ptr) || (Ptr->Len > Size)) | |
{ | |
return RETURN_SUCCESS; | |
} | |
DEBUG (( | |
DEBUG_INFO, | |
"%a: Found injected hashes table in secure location\n", | |
__func__ | |
)); | |
mHashesTable = (HASH_TABLE *)Ptr->Data; | |
mHashesTableSize = Ptr->Len - sizeof Ptr->Guid - sizeof Ptr->Len; | |
DEBUG (( | |
DEBUG_VERBOSE, | |
"%a: mHashesTable=0x%p, Size=%u\n", | |
__func__, | |
mHashesTable, | |
mHashesTableSize | |
)); | |
return RETURN_SUCCESS; | |
} |