]> git.proxmox.com Git - efi-boot-shim.git/blob - tpm.c
New upstream version 15.7
[efi-boot-shim.git] / tpm.c
1 // SPDX-License-Identifier: BSD-2-Clause-Patent
2 #include "shim.h"
3
4 typedef struct {
5 CHAR16 *VariableName;
6 EFI_GUID *VendorGuid;
7 VOID *Data;
8 UINTN Size;
9 } VARIABLE_RECORD;
10
11 UINTN measuredcount = 0;
12 VARIABLE_RECORD *measureddata = NULL;
13 static BOOLEAN tpm_defective = FALSE;
14
15 static BOOLEAN tpm_present(efi_tpm_protocol_t *tpm)
16 {
17 EFI_STATUS efi_status;
18 TCG_EFI_BOOT_SERVICE_CAPABILITY caps;
19 UINT32 flags;
20 EFI_PHYSICAL_ADDRESS eventlog, lastevent;
21
22 if (tpm_defective)
23 return FALSE;
24
25 caps.Size = (UINT8)sizeof(caps);
26 efi_status = tpm->status_check(tpm, &caps, &flags,
27 &eventlog, &lastevent);
28 if (EFI_ERROR(efi_status) ||
29 caps.TPMDeactivatedFlag || !caps.TPMPresentFlag)
30 return FALSE;
31
32 return TRUE;
33 }
34
35 static EFI_STATUS tpm2_get_caps(efi_tpm2_protocol_t *tpm,
36 EFI_TCG2_BOOT_SERVICE_CAPABILITY *caps,
37 BOOLEAN *old_caps)
38 {
39 EFI_STATUS efi_status;
40
41 caps->Size = (UINT8)sizeof(*caps);
42
43 efi_status = tpm->get_capability(tpm, caps);
44 if (EFI_ERROR(efi_status))
45 return efi_status;
46
47 if (caps->StructureVersion.Major == 1 &&
48 caps->StructureVersion.Minor == 0)
49 *old_caps = TRUE;
50 else
51 *old_caps = FALSE;
52
53 return EFI_SUCCESS;
54 }
55
56 static BOOLEAN tpm2_present(EFI_TCG2_BOOT_SERVICE_CAPABILITY *caps,
57 BOOLEAN old_caps)
58 {
59 TREE_BOOT_SERVICE_CAPABILITY *caps_1_0;
60
61 if (old_caps) {
62 caps_1_0 = (TREE_BOOT_SERVICE_CAPABILITY *)caps;
63 if (caps_1_0->TrEEPresentFlag)
64 return TRUE;
65 }
66
67 if (caps->TPMPresentFlag)
68 return TRUE;
69
70 return FALSE;
71 }
72
73 static EFI_STATUS tpm_locate_protocol(efi_tpm_protocol_t **tpm,
74 efi_tpm2_protocol_t **tpm2,
75 BOOLEAN *old_caps_p,
76 EFI_TCG2_BOOT_SERVICE_CAPABILITY *capsp)
77 {
78 EFI_STATUS efi_status;
79
80 *tpm = NULL;
81 *tpm2 = NULL;
82 efi_status = LibLocateProtocol(&EFI_TPM2_GUID, (VOID **)tpm2);
83 /* TPM 2.0 */
84 if (!EFI_ERROR(efi_status)) {
85 BOOLEAN old_caps;
86 EFI_TCG2_BOOT_SERVICE_CAPABILITY caps;
87
88 efi_status = tpm2_get_caps(*tpm2, &caps, &old_caps);
89 if (EFI_ERROR(efi_status))
90 return efi_status;
91
92 if (tpm2_present(&caps, old_caps)) {
93 if (old_caps_p)
94 *old_caps_p = old_caps;
95 if (capsp)
96 memcpy(capsp, &caps, sizeof(caps));
97 return EFI_SUCCESS;
98 }
99 } else {
100 efi_status = LibLocateProtocol(&EFI_TPM_GUID, (VOID **)tpm);
101 if (EFI_ERROR(efi_status))
102 return efi_status;
103
104 if (tpm_present(*tpm))
105 return EFI_SUCCESS;
106 }
107
108 return EFI_NOT_FOUND;
109 }
110
111 static EFI_STATUS cc_log_event_raw(EFI_PHYSICAL_ADDRESS buf, UINTN size,
112 UINT8 pcr, const CHAR8 *log, UINTN logsize,
113 UINT32 type, BOOLEAN is_pe_image)
114 {
115 EFI_STATUS efi_status;
116 EFI_CC_EVENT *event;
117 efi_cc_protocol_t *cc;
118 EFI_CC_MR_INDEX mr;
119 uint64_t flags = is_pe_image ? EFI_CC_FLAG_PE_COFF_IMAGE : 0;
120
121 efi_status = LibLocateProtocol(&EFI_CC_MEASUREMENT_PROTOCOL_GUID,
122 (VOID **)&cc);
123 if (EFI_ERROR(efi_status) || !cc)
124 return EFI_SUCCESS;
125
126 efi_status = cc->map_pcr_to_mr_index(cc, pcr, &mr);
127 if (EFI_ERROR(efi_status))
128 return EFI_NOT_FOUND;
129
130 UINTN event_size = sizeof(*event) - sizeof(event->Event) + logsize;
131
132 event = AllocatePool(event_size);
133 if (!event) {
134 perror(L"Unable to allocate event structure\n");
135 return EFI_OUT_OF_RESOURCES;
136 }
137
138 event->Header.HeaderSize = sizeof(EFI_CC_EVENT_HEADER);
139 event->Header.HeaderVersion = EFI_CC_EVENT_HEADER_VERSION;
140 event->Header.MrIndex = mr;
141 event->Header.EventType = type;
142 event->Size = event_size;
143 CopyMem(event->Event, (VOID *)log, logsize);
144 efi_status = cc->hash_log_extend_event(cc, flags, buf, (UINT64)size,
145 event);
146 FreePool(event);
147 return efi_status;
148 }
149
150 static EFI_STATUS tpm_log_event_raw(EFI_PHYSICAL_ADDRESS buf, UINTN size,
151 UINT8 pcr, const CHAR8 *log, UINTN logsize,
152 UINT32 type, CHAR8 *hash)
153 {
154 EFI_STATUS efi_status;
155 efi_tpm_protocol_t *tpm;
156 efi_tpm2_protocol_t *tpm2;
157 BOOLEAN old_caps;
158 EFI_TCG2_BOOT_SERVICE_CAPABILITY caps;
159
160 /* CC guest like TDX or SEV will measure the buffer and log the event,
161 extend the result into a specific CC MR like TCG's PCR. It could
162 coexists with TCG's TPM 1.2 and TPM 2.
163 */
164 efi_status = cc_log_event_raw(buf, size, pcr, log, logsize, type,
165 (hash != NULL));
166 if (EFI_ERROR(efi_status))
167 return efi_status;
168
169 efi_status = tpm_locate_protocol(&tpm, &tpm2, &old_caps, &caps);
170 if (EFI_ERROR(efi_status)) {
171 #ifdef REQUIRE_TPM
172 perror(L"TPM logging failed: %r\n", efi_status);
173 return efi_status;
174 #else
175 if (efi_status != EFI_NOT_FOUND) {
176 perror(L"TPM logging failed: %r\n", efi_status);
177 return efi_status;
178 }
179 #endif
180 } else if (tpm2) {
181 EFI_TCG2_EVENT *event;
182 UINTN event_size = sizeof(*event) - sizeof(event->Event) +
183 logsize;
184
185 event = AllocatePool(event_size);
186 if (!event) {
187 perror(L"Unable to allocate event structure\n");
188 return EFI_OUT_OF_RESOURCES;
189 }
190
191 event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER);
192 event->Header.HeaderVersion = 1;
193 event->Header.PCRIndex = pcr;
194 event->Header.EventType = type;
195 event->Size = event_size;
196 CopyMem(event->Event, (VOID *)log, logsize);
197 if (hash) {
198 /* TPM 2 systems will generate the appropriate hash
199 themselves if we pass PE_COFF_IMAGE. In case that
200 fails we fall back to measuring without it.
201 */
202 efi_status = tpm2->hash_log_extend_event(tpm2,
203 PE_COFF_IMAGE, buf, (UINT64) size, event);
204 }
205
206 if (!hash || EFI_ERROR(efi_status)) {
207 efi_status = tpm2->hash_log_extend_event(tpm2,
208 0, buf, (UINT64) size, event);
209 }
210 FreePool(event);
211 return efi_status;
212 } else if (tpm) {
213 TCG_PCR_EVENT *event;
214 UINT32 eventnum = 0;
215 EFI_PHYSICAL_ADDRESS lastevent;
216
217 efi_status = LibLocateProtocol(&EFI_TPM_GUID, (VOID **)&tpm);
218 if (EFI_ERROR(efi_status))
219 return EFI_SUCCESS;
220
221 if (!tpm_present(tpm))
222 return EFI_SUCCESS;
223
224 event = AllocatePool(sizeof(*event) + logsize);
225
226 if (!event) {
227 perror(L"Unable to allocate event structure\n");
228 return EFI_OUT_OF_RESOURCES;
229 }
230
231 event->PCRIndex = pcr;
232 event->EventType = type;
233 event->EventSize = logsize;
234 CopyMem(event->Event, (VOID *)log, logsize);
235 if (hash) {
236 /* TPM 1.2 devices require us to pass the Authenticode
237 hash rather than allowing the firmware to attempt
238 to calculate it */
239 CopyMem(event->digest, hash, sizeof(event->digest));
240 efi_status = tpm->log_extend_event(tpm, 0, 0,
241 TPM_ALG_SHA, event, &eventnum, &lastevent);
242 } else {
243 efi_status = tpm->log_extend_event(tpm, buf,
244 (UINT64)size, TPM_ALG_SHA, event, &eventnum,
245 &lastevent);
246 }
247 if (efi_status == EFI_UNSUPPORTED) {
248 perror(L"Could not write TPM event: %r. Considering "
249 "the TPM as defective.\n", efi_status);
250 tpm_defective = TRUE;
251 efi_status = EFI_SUCCESS;
252 }
253 FreePool(event);
254 return efi_status;
255 }
256
257 return EFI_SUCCESS;
258 }
259
260 EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr,
261 const CHAR8 *description)
262 {
263 return tpm_log_event_raw(buf, size, pcr, description,
264 strlen(description) + 1, EV_IPL, NULL);
265 }
266
267 EFI_STATUS tpm_log_pe(EFI_PHYSICAL_ADDRESS buf, UINTN size,
268 EFI_PHYSICAL_ADDRESS addr, EFI_DEVICE_PATH *path,
269 UINT8 *sha1hash, UINT8 pcr)
270 {
271 EFI_IMAGE_LOAD_EVENT *ImageLoad = NULL;
272 EFI_STATUS efi_status;
273 UINTN path_size = 0;
274
275 if (path)
276 path_size = DevicePathSize(path);
277
278 ImageLoad = AllocateZeroPool(sizeof(*ImageLoad) + path_size);
279 if (!ImageLoad) {
280 perror(L"Unable to allocate image load event structure\n");
281 return EFI_OUT_OF_RESOURCES;
282 }
283
284 ImageLoad->ImageLocationInMemory = buf;
285 ImageLoad->ImageLengthInMemory = size;
286 ImageLoad->ImageLinkTimeAddress = addr;
287
288 if (path_size > 0) {
289 CopyMem(ImageLoad->DevicePath, path, path_size);
290 ImageLoad->LengthOfDevicePath = path_size;
291 }
292
293 efi_status = tpm_log_event_raw(buf, size, pcr, (CHAR8 *)ImageLoad,
294 sizeof(*ImageLoad) + path_size,
295 EV_EFI_BOOT_SERVICES_APPLICATION,
296 (CHAR8 *)sha1hash);
297 FreePool(ImageLoad);
298
299 return efi_status;
300 }
301
302 typedef struct {
303 EFI_GUID VariableName;
304 UINT64 UnicodeNameLength;
305 UINT64 VariableDataLength;
306 CHAR16 UnicodeName[1];
307 INT8 VariableData[1];
308 } __attribute__ ((packed)) EFI_VARIABLE_DATA_TREE;
309
310 static BOOLEAN tpm_data_measured(CHAR16 *VarName, EFI_GUID VendorGuid, UINTN VarSize, VOID *VarData)
311 {
312 UINTN i;
313
314 for (i=0; i<measuredcount; i++) {
315 if ((StrCmp (VarName, measureddata[i].VariableName) == 0) &&
316 (CompareGuid (&VendorGuid, measureddata[i].VendorGuid) == 0) &&
317 (VarSize == measureddata[i].Size) &&
318 (CompareMem (VarData, measureddata[i].Data, VarSize) == 0)) {
319 return TRUE;
320 }
321 }
322
323 return FALSE;
324 }
325
326 static EFI_STATUS tpm_record_data_measurement(CHAR16 *VarName, EFI_GUID VendorGuid, UINTN VarSize, VOID *VarData)
327 {
328 if (measureddata == NULL) {
329 measureddata = AllocatePool(sizeof(*measureddata));
330 } else {
331 measureddata = ReallocatePool(measureddata, measuredcount * sizeof(*measureddata),
332 (measuredcount + 1) * sizeof(*measureddata));
333 }
334
335 if (measureddata == NULL)
336 return EFI_OUT_OF_RESOURCES;
337
338 measureddata[measuredcount].VariableName = AllocatePool(StrSize(VarName));
339 measureddata[measuredcount].VendorGuid = AllocatePool(sizeof(EFI_GUID));
340 measureddata[measuredcount].Data = AllocatePool(VarSize);
341
342 if (measureddata[measuredcount].VariableName == NULL ||
343 measureddata[measuredcount].VendorGuid == NULL ||
344 measureddata[measuredcount].Data == NULL) {
345 return EFI_OUT_OF_RESOURCES;
346 }
347
348 StrCpy(measureddata[measuredcount].VariableName, VarName);
349 CopyMem(measureddata[measuredcount].VendorGuid, &VendorGuid, sizeof(EFI_GUID));
350 CopyMem(measureddata[measuredcount].Data, VarData, VarSize);
351 measureddata[measuredcount].Size = VarSize;
352 measuredcount++;
353
354 return EFI_SUCCESS;
355 }
356
357 EFI_STATUS tpm_measure_variable(CHAR16 *VarName, EFI_GUID VendorGuid, UINTN VarSize, VOID *VarData)
358 {
359 EFI_STATUS efi_status;
360 UINTN VarNameLength;
361 EFI_VARIABLE_DATA_TREE *VarLog;
362 UINT32 VarLogSize;
363
364 /* Don't measure something that we've already measured */
365 if (tpm_data_measured(VarName, VendorGuid, VarSize, VarData))
366 return EFI_SUCCESS;
367
368 VarNameLength = StrLen (VarName);
369 VarLogSize = (UINT32)(sizeof (*VarLog) +
370 VarNameLength * sizeof (*VarName) +
371 VarSize -
372 sizeof (VarLog->UnicodeName) -
373 sizeof (VarLog->VariableData));
374
375 VarLog = (EFI_VARIABLE_DATA_TREE *) AllocateZeroPool (VarLogSize);
376 if (VarLog == NULL) {
377 return EFI_OUT_OF_RESOURCES;
378 }
379
380 CopyMem (&VarLog->VariableName, &VendorGuid,
381 sizeof(VarLog->VariableName));
382 VarLog->UnicodeNameLength = VarNameLength;
383 VarLog->VariableDataLength = VarSize;
384 CopyMem (VarLog->UnicodeName, VarName,
385 VarNameLength * sizeof (*VarName));
386 CopyMem ((CHAR16 *)VarLog->UnicodeName + VarNameLength, VarData,
387 VarSize);
388
389 efi_status = tpm_log_event_raw((EFI_PHYSICAL_ADDRESS)(intptr_t)VarLog,
390 VarLogSize, 7, (CHAR8 *)VarLog, VarLogSize,
391 EV_EFI_VARIABLE_AUTHORITY, NULL);
392
393 FreePool(VarLog);
394
395 if (EFI_ERROR(efi_status))
396 return efi_status;
397
398 return tpm_record_data_measurement(VarName, VendorGuid, VarSize,
399 VarData);
400 }
401
402 EFI_STATUS
403 fallback_should_prefer_reset(void)
404 {
405 EFI_STATUS efi_status;
406 efi_tpm_protocol_t *tpm;
407 efi_tpm2_protocol_t *tpm2;
408
409 efi_status = tpm_locate_protocol(&tpm, &tpm2, NULL, NULL);
410 if (EFI_ERROR(efi_status))
411 return EFI_NOT_FOUND;
412 return EFI_SUCCESS;
413 }
414
415 #ifdef SHIM_UNIT_TEST
416 static void DESTRUCTOR
417 tpm_clean_up_measurements(void)
418 {
419 for (UINTN i = 0; i < measuredcount; i++) {
420 VARIABLE_RECORD *vr = &measureddata[i];
421
422 if (vr->VariableName)
423 FreePool(vr->VariableName);
424 if (vr->VendorGuid)
425 FreePool(vr->VendorGuid);
426 if (vr->Data)
427 FreePool(vr->Data);
428 }
429 if (measureddata)
430 FreePool(measureddata);
431
432 measuredcount = 0;
433 measureddata = NULL;
434 }
435 #endif