| /** @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. | |
| If a non-secure configuration is detected this function will enter a | |
| dead loop to prevent a boot. | |
| @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 | |
| @param[in] FetchStatus The status of the previous blob fetch | |
| @retval EFI_SUCCESS The blob was verified successfully or was not | |
| found in the hash table. | |
| @retval EFI_ACCESS_DENIED Kernel hashes not supported, but the boot | |
| can continue safely. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| VerifyBlob ( | |
| IN CONST CHAR16 *BlobName, | |
| IN CONST VOID *Buf, | |
| IN UINT32 BufSize, | |
| IN EFI_STATUS FetchStatus | |
| ) | |
| { | |
| CONST GUID *Guid; | |
| INT32 Remaining; | |
| HASH_TABLE *Entry; | |
| // Enter a dead loop if the fetching of this blob | |
| // failed. This prevents a malicious host from | |
| // circumventing the following checks. | |
| if (EFI_ERROR (FetchStatus)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: Fetching blob failed.\n", | |
| __func__ | |
| )); | |
| CpuDeadLoop (); | |
| } | |
| if ((mHashesTable == NULL) || (mHashesTableSize == 0)) { | |
| DEBUG (( | |
| DEBUG_WARN, | |
| "%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 | |
| )); | |
| CpuDeadLoop (); | |
| } | |
| // | |
| // 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_WARN, | |
| "%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 | |
| )); | |
| CpuDeadLoop (); | |
| } | |
| return Status; | |
| } | |
| // | |
| // If the GUID is not in the hash table, execution can still continue. | |
| // This blob will not be measured, but at least one blob must be. | |
| // | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: Hash GUID %g not found in table\n", | |
| __func__, | |
| Guid | |
| )); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| 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; | |
| } |