1 | /** @file
|
---|
2 |
|
---|
3 | Blob verifier library that uses SEV hashes table. The hashes table holds the
|
---|
4 | allowed hashes of the kernel, initrd, and cmdline blobs.
|
---|
5 |
|
---|
6 | Copyright (C) 2021, IBM Corporation
|
---|
7 |
|
---|
8 | SPDX-License-Identifier: BSD-2-Clause-Patent
|
---|
9 | **/
|
---|
10 |
|
---|
11 | #include <Library/BaseCryptLib.h>
|
---|
12 | #include <Library/BaseLib.h>
|
---|
13 | #include <Library/BaseMemoryLib.h>
|
---|
14 | #include <Library/DebugLib.h>
|
---|
15 | #include <Library/BlobVerifierLib.h>
|
---|
16 |
|
---|
17 | /**
|
---|
18 | The SEV Hashes table must be in encrypted memory and has the table
|
---|
19 | and its entries described by
|
---|
20 |
|
---|
21 | <GUID>|UINT16 <len>|<data>
|
---|
22 |
|
---|
23 | With the whole table GUID being 9438d606-4f22-4cc9-b479-a793d411fd21
|
---|
24 |
|
---|
25 | The current possible table entries are for the kernel, the initrd
|
---|
26 | and the cmdline:
|
---|
27 |
|
---|
28 | 4de79437-abd2-427f-b835-d5b172d2045b kernel
|
---|
29 | 44baf731-3a2f-4bd7-9af1-41e29169781d initrd
|
---|
30 | 97d02dd8-bd20-4c94-aa78-e7714d36ab2a cmdline
|
---|
31 |
|
---|
32 | The size of the entry is used to identify the hash, but the
|
---|
33 | expectation is that it will be 32 bytes of SHA-256.
|
---|
34 | **/
|
---|
35 |
|
---|
36 | #define SEV_HASH_TABLE_GUID \
|
---|
37 | (GUID) { 0x9438d606, 0x4f22, 0x4cc9, { 0xb4, 0x79, 0xa7, 0x93, 0xd4, 0x11, 0xfd, 0x21 } }
|
---|
38 | #define SEV_KERNEL_HASH_GUID \
|
---|
39 | (GUID) { 0x4de79437, 0xabd2, 0x427f, { 0xb8, 0x35, 0xd5, 0xb1, 0x72, 0xd2, 0x04, 0x5b } }
|
---|
40 | #define SEV_INITRD_HASH_GUID \
|
---|
41 | (GUID) { 0x44baf731, 0x3a2f, 0x4bd7, { 0x9a, 0xf1, 0x41, 0xe2, 0x91, 0x69, 0x78, 0x1d } }
|
---|
42 | #define SEV_CMDLINE_HASH_GUID \
|
---|
43 | (GUID) { 0x97d02dd8, 0xbd20, 0x4c94, { 0xaa, 0x78, 0xe7, 0x71, 0x4d, 0x36, 0xab, 0x2a } }
|
---|
44 |
|
---|
45 | STATIC CONST EFI_GUID mSevKernelHashGuid = SEV_KERNEL_HASH_GUID;
|
---|
46 | STATIC CONST EFI_GUID mSevInitrdHashGuid = SEV_INITRD_HASH_GUID;
|
---|
47 | STATIC CONST EFI_GUID mSevCmdlineHashGuid = SEV_CMDLINE_HASH_GUID;
|
---|
48 |
|
---|
49 | #pragma pack (1)
|
---|
50 | typedef struct {
|
---|
51 | GUID Guid;
|
---|
52 | UINT16 Len;
|
---|
53 | UINT8 Data[];
|
---|
54 | } HASH_TABLE;
|
---|
55 | #pragma pack ()
|
---|
56 |
|
---|
57 | STATIC HASH_TABLE *mHashesTable;
|
---|
58 | STATIC UINT16 mHashesTableSize;
|
---|
59 |
|
---|
60 | STATIC
|
---|
61 | CONST GUID *
|
---|
62 | FindBlobEntryGuid (
|
---|
63 | IN CONST CHAR16 *BlobName
|
---|
64 | )
|
---|
65 | {
|
---|
66 | if (StrCmp (BlobName, L"kernel") == 0) {
|
---|
67 | return &mSevKernelHashGuid;
|
---|
68 | } else if (StrCmp (BlobName, L"initrd") == 0) {
|
---|
69 | return &mSevInitrdHashGuid;
|
---|
70 | } else if (StrCmp (BlobName, L"cmdline") == 0) {
|
---|
71 | return &mSevCmdlineHashGuid;
|
---|
72 | } else {
|
---|
73 | return NULL;
|
---|
74 | }
|
---|
75 | }
|
---|
76 |
|
---|
77 | /**
|
---|
78 | Verify blob from an external source.
|
---|
79 |
|
---|
80 | If a non-secure configuration is detected this function will enter a
|
---|
81 | dead loop to prevent a boot.
|
---|
82 |
|
---|
83 | @param[in] BlobName The name of the blob
|
---|
84 | @param[in] Buf The data of the blob
|
---|
85 | @param[in] BufSize The size of the blob in bytes
|
---|
86 | @param[in] FetchStatus The status of the previous blob fetch
|
---|
87 |
|
---|
88 | @retval EFI_SUCCESS The blob was verified successfully or was not
|
---|
89 | found in the hash table.
|
---|
90 | @retval EFI_ACCESS_DENIED Kernel hashes not supported, but the boot
|
---|
91 | can continue safely.
|
---|
92 | **/
|
---|
93 | EFI_STATUS
|
---|
94 | EFIAPI
|
---|
95 | VerifyBlob (
|
---|
96 | IN CONST CHAR16 *BlobName,
|
---|
97 | IN CONST VOID *Buf,
|
---|
98 | IN UINT32 BufSize,
|
---|
99 | IN EFI_STATUS FetchStatus
|
---|
100 | )
|
---|
101 | {
|
---|
102 | CONST GUID *Guid;
|
---|
103 | INT32 Remaining;
|
---|
104 | HASH_TABLE *Entry;
|
---|
105 |
|
---|
106 | // Enter a dead loop if the fetching of this blob
|
---|
107 | // failed. This prevents a malicious host from
|
---|
108 | // circumventing the following checks.
|
---|
109 | if (EFI_ERROR (FetchStatus)) {
|
---|
110 | DEBUG ((
|
---|
111 | DEBUG_ERROR,
|
---|
112 | "%a: Fetching blob failed.\n",
|
---|
113 | __func__
|
---|
114 | ));
|
---|
115 |
|
---|
116 | CpuDeadLoop ();
|
---|
117 | }
|
---|
118 |
|
---|
119 | if ((mHashesTable == NULL) || (mHashesTableSize == 0)) {
|
---|
120 | DEBUG ((
|
---|
121 | DEBUG_WARN,
|
---|
122 | "%a: Verifier called but no hashes table discoverd in MEMFD\n",
|
---|
123 | __func__
|
---|
124 | ));
|
---|
125 | return EFI_ACCESS_DENIED;
|
---|
126 | }
|
---|
127 |
|
---|
128 | Guid = FindBlobEntryGuid (BlobName);
|
---|
129 | if (Guid == NULL) {
|
---|
130 | DEBUG ((
|
---|
131 | DEBUG_ERROR,
|
---|
132 | "%a: Unknown blob name \"%s\"\n",
|
---|
133 | __func__,
|
---|
134 | BlobName
|
---|
135 | ));
|
---|
136 |
|
---|
137 | CpuDeadLoop ();
|
---|
138 | }
|
---|
139 |
|
---|
140 | //
|
---|
141 | // Remaining is INT32 to catch underflow in case Entry->Len has a
|
---|
142 | // very high UINT16 value
|
---|
143 | //
|
---|
144 | for (Entry = mHashesTable, Remaining = mHashesTableSize;
|
---|
145 | Remaining >= sizeof *Entry && Remaining >= Entry->Len;
|
---|
146 | Remaining -= Entry->Len,
|
---|
147 | Entry = (HASH_TABLE *)((UINT8 *)Entry + Entry->Len))
|
---|
148 | {
|
---|
149 | UINTN EntrySize;
|
---|
150 | EFI_STATUS Status;
|
---|
151 | UINT8 Hash[SHA256_DIGEST_SIZE];
|
---|
152 |
|
---|
153 | if (!CompareGuid (&Entry->Guid, Guid)) {
|
---|
154 | continue;
|
---|
155 | }
|
---|
156 |
|
---|
157 | DEBUG ((DEBUG_INFO, "%a: Found GUID %g in table\n", __func__, Guid));
|
---|
158 |
|
---|
159 | EntrySize = Entry->Len - sizeof Entry->Guid - sizeof Entry->Len;
|
---|
160 | if (EntrySize != SHA256_DIGEST_SIZE) {
|
---|
161 | DEBUG ((
|
---|
162 | DEBUG_WARN,
|
---|
163 | "%a: Hash has the wrong size %d != %d\n",
|
---|
164 | __func__,
|
---|
165 | EntrySize,
|
---|
166 | SHA256_DIGEST_SIZE
|
---|
167 | ));
|
---|
168 | return EFI_ACCESS_DENIED;
|
---|
169 | }
|
---|
170 |
|
---|
171 | //
|
---|
172 | // Calculate the buffer's hash and verify that it is identical to the
|
---|
173 | // expected hash table entry
|
---|
174 | //
|
---|
175 | Sha256HashAll (Buf, BufSize, Hash);
|
---|
176 |
|
---|
177 | if (CompareMem (Entry->Data, Hash, EntrySize) == 0) {
|
---|
178 | Status = EFI_SUCCESS;
|
---|
179 | DEBUG ((
|
---|
180 | DEBUG_INFO,
|
---|
181 | "%a: Hash comparison succeeded for \"%s\"\n",
|
---|
182 | __func__,
|
---|
183 | BlobName
|
---|
184 | ));
|
---|
185 | } else {
|
---|
186 | Status = EFI_ACCESS_DENIED;
|
---|
187 | DEBUG ((
|
---|
188 | DEBUG_ERROR,
|
---|
189 | "%a: Hash comparison failed for \"%s\"\n",
|
---|
190 | __func__,
|
---|
191 | BlobName
|
---|
192 | ));
|
---|
193 |
|
---|
194 | CpuDeadLoop ();
|
---|
195 | }
|
---|
196 |
|
---|
197 | return Status;
|
---|
198 | }
|
---|
199 |
|
---|
200 | //
|
---|
201 | // If the GUID is not in the hash table, execution can still continue.
|
---|
202 | // This blob will not be measured, but at least one blob must be.
|
---|
203 | //
|
---|
204 | DEBUG ((
|
---|
205 | DEBUG_ERROR,
|
---|
206 | "%a: Hash GUID %g not found in table\n",
|
---|
207 | __func__,
|
---|
208 | Guid
|
---|
209 | ));
|
---|
210 | return EFI_SUCCESS;
|
---|
211 | }
|
---|
212 |
|
---|
213 | /**
|
---|
214 | Locate the SEV hashes table.
|
---|
215 |
|
---|
216 | This function always returns success, even if the table can't be found. The
|
---|
217 | subsequent VerifyBlob calls will fail if no table was found.
|
---|
218 |
|
---|
219 | @retval RETURN_SUCCESS The hashes table is set up correctly, or there is no
|
---|
220 | hashes table
|
---|
221 | **/
|
---|
222 | RETURN_STATUS
|
---|
223 | EFIAPI
|
---|
224 | BlobVerifierLibSevHashesConstructor (
|
---|
225 | VOID
|
---|
226 | )
|
---|
227 | {
|
---|
228 | HASH_TABLE *Ptr;
|
---|
229 | UINT32 Size;
|
---|
230 |
|
---|
231 | mHashesTable = NULL;
|
---|
232 | mHashesTableSize = 0;
|
---|
233 |
|
---|
234 | Ptr = (void *)(UINTN)FixedPcdGet64 (PcdQemuHashTableBase);
|
---|
235 | Size = FixedPcdGet32 (PcdQemuHashTableSize);
|
---|
236 |
|
---|
237 | if ((Ptr == NULL) || (Size < sizeof *Ptr) ||
|
---|
238 | !CompareGuid (&Ptr->Guid, &SEV_HASH_TABLE_GUID) ||
|
---|
239 | (Ptr->Len < sizeof *Ptr) || (Ptr->Len > Size))
|
---|
240 | {
|
---|
241 | return RETURN_SUCCESS;
|
---|
242 | }
|
---|
243 |
|
---|
244 | DEBUG ((
|
---|
245 | DEBUG_INFO,
|
---|
246 | "%a: Found injected hashes table in secure location\n",
|
---|
247 | __func__
|
---|
248 | ));
|
---|
249 |
|
---|
250 | mHashesTable = (HASH_TABLE *)Ptr->Data;
|
---|
251 | mHashesTableSize = Ptr->Len - sizeof Ptr->Guid - sizeof Ptr->Len;
|
---|
252 |
|
---|
253 | DEBUG ((
|
---|
254 | DEBUG_VERBOSE,
|
---|
255 | "%a: mHashesTable=0x%p, Size=%u\n",
|
---|
256 | __func__,
|
---|
257 | mHashesTable,
|
---|
258 | mHashesTableSize
|
---|
259 | ));
|
---|
260 |
|
---|
261 | return RETURN_SUCCESS;
|
---|
262 | }
|
---|