]>
Commit | Line | Data |
---|---|---|
aedb8470 PJ |
1 | // SPDX-License-Identifier: BSD-2-Clause-Patent |
2 | ||
22b58f24 MG |
3 | #include <efi.h> |
4 | #include <efilib.h> | |
5 | #include <string.h> | |
2d82a389 | 6 | #include <stdint.h> |
22b58f24 | 7 | |
dc62a3c4 | 8 | #include "shim.h" |
22b58f24 | 9 | |
8af7c4ca MG |
10 | typedef struct { |
11 | CHAR16 *VariableName; | |
12 | EFI_GUID *VendorGuid; | |
13 | VOID *Data; | |
14 | UINTN Size; | |
15 | } VARIABLE_RECORD; | |
16 | ||
17 | UINTN measuredcount = 0; | |
18 | VARIABLE_RECORD *measureddata = NULL; | |
19 | ||
22b58f24 MG |
20 | static BOOLEAN tpm_present(efi_tpm_protocol_t *tpm) |
21 | { | |
1c237633 | 22 | EFI_STATUS efi_status; |
22b58f24 MG |
23 | TCG_EFI_BOOT_SERVICE_CAPABILITY caps; |
24 | UINT32 flags; | |
25 | EFI_PHYSICAL_ADDRESS eventlog, lastevent; | |
26 | ||
27 | caps.Size = (UINT8)sizeof(caps); | |
9fdca5bb | 28 | efi_status = tpm->status_check(tpm, &caps, &flags, |
1c237633 PJ |
29 | &eventlog, &lastevent); |
30 | if (EFI_ERROR(efi_status) || | |
31 | caps.TPMDeactivatedFlag || !caps.TPMPresentFlag) | |
22b58f24 MG |
32 | return FALSE; |
33 | ||
34 | return TRUE; | |
35 | } | |
36 | ||
0baa9150 JMC |
37 | static EFI_STATUS tpm2_get_caps(efi_tpm2_protocol_t *tpm, |
38 | EFI_TCG2_BOOT_SERVICE_CAPABILITY *caps, | |
39 | BOOLEAN *old_caps) | |
22b58f24 | 40 | { |
1c237633 | 41 | EFI_STATUS efi_status; |
22b58f24 | 42 | |
0baa9150 | 43 | caps->Size = (UINT8)sizeof(*caps); |
22b58f24 | 44 | |
9fdca5bb | 45 | efi_status = tpm->get_capability(tpm, caps); |
1c237633 PJ |
46 | if (EFI_ERROR(efi_status)) |
47 | return efi_status; | |
0baa9150 JMC |
48 | |
49 | if (caps->StructureVersion.Major == 1 && | |
50 | caps->StructureVersion.Minor == 0) | |
51 | *old_caps = TRUE; | |
00197895 PJ |
52 | else |
53 | *old_caps = FALSE; | |
22b58f24 | 54 | |
0baa9150 JMC |
55 | return EFI_SUCCESS; |
56 | } | |
57 | ||
919c17a4 | 58 | static BOOLEAN tpm2_present(EFI_TCG2_BOOT_SERVICE_CAPABILITY *caps, |
0baa9150 JMC |
59 | BOOLEAN old_caps) |
60 | { | |
61 | TREE_BOOT_SERVICE_CAPABILITY *caps_1_0; | |
62 | ||
63 | if (old_caps) { | |
64 | caps_1_0 = (TREE_BOOT_SERVICE_CAPABILITY *)caps; | |
94c955bb | 65 | if (caps_1_0->TrEEPresentFlag) |
22b58f24 | 66 | return TRUE; |
22b58f24 MG |
67 | } |
68 | ||
0baa9150 JMC |
69 | if (caps->TPMPresentFlag) |
70 | return TRUE; | |
71 | ||
22b58f24 MG |
72 | return FALSE; |
73 | } | |
74 | ||
431b8a2e PJ |
75 | static EFI_STATUS tpm_locate_protocol(efi_tpm_protocol_t **tpm, |
76 | efi_tpm2_protocol_t **tpm2, | |
77 | BOOLEAN *old_caps_p, | |
78 | EFI_TCG2_BOOT_SERVICE_CAPABILITY *capsp) | |
22b58f24 | 79 | { |
1c237633 | 80 | EFI_STATUS efi_status; |
22b58f24 | 81 | |
431b8a2e PJ |
82 | *tpm = NULL; |
83 | *tpm2 = NULL; | |
1c237633 | 84 | efi_status = LibLocateProtocol(&EFI_TPM2_GUID, (VOID **)tpm2); |
22b58f24 | 85 | /* TPM 2.0 */ |
1c237633 | 86 | if (!EFI_ERROR(efi_status)) { |
0baa9150 | 87 | BOOLEAN old_caps; |
0baa9150 JMC |
88 | EFI_TCG2_BOOT_SERVICE_CAPABILITY caps; |
89 | ||
1c237633 PJ |
90 | efi_status = tpm2_get_caps(*tpm2, &caps, &old_caps); |
91 | if (EFI_ERROR(efi_status)) | |
92 | return efi_status; | |
431b8a2e PJ |
93 | |
94 | if (tpm2_present(&caps, old_caps)) { | |
95 | if (old_caps_p) | |
96 | *old_caps_p = old_caps; | |
97 | if (capsp) | |
98 | memcpy(capsp, &caps, sizeof(caps)); | |
0baa9150 | 99 | return EFI_SUCCESS; |
431b8a2e PJ |
100 | } |
101 | } else { | |
1c237633 PJ |
102 | efi_status = LibLocateProtocol(&EFI_TPM_GUID, (VOID **)tpm); |
103 | if (EFI_ERROR(efi_status)) | |
104 | return efi_status; | |
22b58f24 | 105 | |
431b8a2e | 106 | if (tpm_present(*tpm)) |
22b58f24 | 107 | return EFI_SUCCESS; |
431b8a2e PJ |
108 | } |
109 | ||
110 | return EFI_NOT_FOUND; | |
111 | } | |
112 | ||
113 | static EFI_STATUS tpm_log_event_raw(EFI_PHYSICAL_ADDRESS buf, UINTN size, | |
114 | UINT8 pcr, const CHAR8 *log, UINTN logsize, | |
115 | UINT32 type, CHAR8 *hash) | |
116 | { | |
1c237633 | 117 | EFI_STATUS efi_status; |
431b8a2e PJ |
118 | efi_tpm_protocol_t *tpm; |
119 | efi_tpm2_protocol_t *tpm2; | |
120 | BOOLEAN old_caps; | |
121 | EFI_TCG2_BOOT_SERVICE_CAPABILITY caps; | |
122 | ||
1c237633 PJ |
123 | efi_status = tpm_locate_protocol(&tpm, &tpm2, &old_caps, &caps); |
124 | if (EFI_ERROR(efi_status)) { | |
15a34804 PJ |
125 | #ifdef REQUIRE_TPM |
126 | perror(L"TPM logging failed: %r\n", efi_status); | |
1c237633 | 127 | return efi_status; |
15a34804 PJ |
128 | #else |
129 | if (efi_status != EFI_NOT_FOUND) { | |
130 | perror(L"TPM logging failed: %r\n", efi_status); | |
131 | return efi_status; | |
132 | } | |
133 | #endif | |
431b8a2e PJ |
134 | } else if (tpm2) { |
135 | EFI_TCG2_EVENT *event; | |
6fd8db6b CC |
136 | UINTN event_size = sizeof(*event) - sizeof(event->Event) + |
137 | logsize; | |
d3884fe8 | 138 | |
6fd8db6b | 139 | event = AllocatePool(event_size); |
22b58f24 MG |
140 | if (!event) { |
141 | perror(L"Unable to allocate event structure\n"); | |
142 | return EFI_OUT_OF_RESOURCES; | |
143 | } | |
144 | ||
145 | event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER); | |
146 | event->Header.HeaderVersion = 1; | |
147 | event->Header.PCRIndex = pcr; | |
8af7c4ca | 148 | event->Header.EventType = type; |
6fd8db6b | 149 | event->Size = event_size; |
8af7c4ca | 150 | CopyMem(event->Event, (VOID *)log, logsize); |
22f27375 MG |
151 | if (hash) { |
152 | /* TPM 2 systems will generate the appropriate hash | |
571bfc95 TL |
153 | themselves if we pass PE_COFF_IMAGE. In case that |
154 | fails we fall back to measuring without it. | |
22f27375 | 155 | */ |
9fdca5bb PJ |
156 | efi_status = tpm2->hash_log_extend_event(tpm2, |
157 | PE_COFF_IMAGE, buf, (UINT64) size, event); | |
571bfc95 TL |
158 | } |
159 | ||
1c237633 | 160 | if (!hash || EFI_ERROR(efi_status)) { |
9fdca5bb PJ |
161 | efi_status = tpm2->hash_log_extend_event(tpm2, |
162 | 0, buf, (UINT64) size, event); | |
22f27375 | 163 | } |
22b58f24 | 164 | FreePool(event); |
1c237633 | 165 | return efi_status; |
431b8a2e | 166 | } else if (tpm) { |
22b58f24 | 167 | TCG_PCR_EVENT *event; |
9c40fb7c | 168 | UINT32 eventnum = 0; |
22b58f24 MG |
169 | EFI_PHYSICAL_ADDRESS lastevent; |
170 | ||
1c237633 PJ |
171 | efi_status = LibLocateProtocol(&EFI_TPM_GUID, (VOID **)&tpm); |
172 | if (EFI_ERROR(efi_status)) | |
22b58f24 MG |
173 | return EFI_SUCCESS; |
174 | ||
175 | if (!tpm_present(tpm)) | |
176 | return EFI_SUCCESS; | |
177 | ||
8af7c4ca | 178 | event = AllocatePool(sizeof(*event) + logsize); |
22b58f24 MG |
179 | |
180 | if (!event) { | |
181 | perror(L"Unable to allocate event structure\n"); | |
182 | return EFI_OUT_OF_RESOURCES; | |
183 | } | |
184 | ||
185 | event->PCRIndex = pcr; | |
8af7c4ca MG |
186 | event->EventType = type; |
187 | event->EventSize = logsize; | |
188 | CopyMem(event->Event, (VOID *)log, logsize); | |
22f27375 MG |
189 | if (hash) { |
190 | /* TPM 1.2 devices require us to pass the Authenticode | |
191 | hash rather than allowing the firmware to attempt | |
192 | to calculate it */ | |
193 | CopyMem(event->digest, hash, sizeof(event->digest)); | |
9fdca5bb PJ |
194 | efi_status = tpm->log_extend_event(tpm, 0, 0, |
195 | TPM_ALG_SHA, event, &eventnum, &lastevent); | |
22f27375 | 196 | } else { |
9fdca5bb PJ |
197 | efi_status = tpm->log_extend_event(tpm, buf, |
198 | (UINT64)size, TPM_ALG_SHA, event, &eventnum, | |
199 | &lastevent); | |
22f27375 | 200 | } |
22b58f24 | 201 | FreePool(event); |
1c237633 | 202 | return efi_status; |
22b58f24 MG |
203 | } |
204 | ||
205 | return EFI_SUCCESS; | |
206 | } | |
431b8a2e | 207 | |
8af7c4ca MG |
208 | EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr, |
209 | const CHAR8 *description) | |
210 | { | |
211 | return tpm_log_event_raw(buf, size, pcr, description, | |
22f27375 MG |
212 | strlen(description) + 1, 0xd, NULL); |
213 | } | |
214 | ||
0a8f7ade JMC |
215 | EFI_STATUS tpm_log_pe(EFI_PHYSICAL_ADDRESS buf, UINTN size, |
216 | EFI_PHYSICAL_ADDRESS addr, EFI_DEVICE_PATH *path, | |
217 | UINT8 *sha1hash, UINT8 pcr) | |
22f27375 | 218 | { |
0a8f7ade JMC |
219 | EFI_IMAGE_LOAD_EVENT *ImageLoad = NULL; |
220 | EFI_STATUS efi_status; | |
221 | UINTN path_size = 0; | |
222 | ||
223 | if (path) | |
224 | path_size = DevicePathSize(path); | |
225 | ||
226 | ImageLoad = AllocateZeroPool(sizeof(*ImageLoad) + path_size); | |
227 | if (!ImageLoad) { | |
228 | perror(L"Unable to allocate image load event structure\n"); | |
229 | return EFI_OUT_OF_RESOURCES; | |
230 | } | |
231 | ||
232 | ImageLoad->ImageLocationInMemory = buf; | |
233 | ImageLoad->ImageLengthInMemory = size; | |
234 | ImageLoad->ImageLinkTimeAddress = addr; | |
235 | ||
236 | if (path_size > 0) { | |
237 | CopyMem(ImageLoad->DevicePath, path, path_size); | |
238 | ImageLoad->LengthOfDevicePath = path_size; | |
239 | } | |
240 | ||
241 | efi_status = tpm_log_event_raw(buf, size, pcr, (CHAR8 *)ImageLoad, | |
242 | sizeof(*ImageLoad) + path_size, | |
243 | EV_EFI_BOOT_SERVICES_APPLICATION, | |
a7f9911b | 244 | (CHAR8 *)sha1hash); |
0a8f7ade JMC |
245 | FreePool(ImageLoad); |
246 | ||
247 | return efi_status; | |
8af7c4ca MG |
248 | } |
249 | ||
250 | typedef struct { | |
251 | EFI_GUID VariableName; | |
252 | UINT64 UnicodeNameLength; | |
253 | UINT64 VariableDataLength; | |
254 | CHAR16 UnicodeName[1]; | |
255 | INT8 VariableData[1]; | |
9f80be9f | 256 | } __attribute__ ((packed)) EFI_VARIABLE_DATA_TREE; |
8af7c4ca MG |
257 | |
258 | static BOOLEAN tpm_data_measured(CHAR16 *VarName, EFI_GUID VendorGuid, UINTN VarSize, VOID *VarData) | |
259 | { | |
260 | UINTN i; | |
261 | ||
262 | for (i=0; i<measuredcount; i++) { | |
263 | if ((StrCmp (VarName, measureddata[i].VariableName) == 0) && | |
58df8d74 | 264 | (CompareGuid (&VendorGuid, measureddata[i].VendorGuid) == 0) && |
8af7c4ca MG |
265 | (VarSize == measureddata[i].Size) && |
266 | (CompareMem (VarData, measureddata[i].Data, VarSize) == 0)) { | |
267 | return TRUE; | |
268 | } | |
269 | } | |
270 | ||
271 | return FALSE; | |
272 | } | |
273 | ||
274 | static EFI_STATUS tpm_record_data_measurement(CHAR16 *VarName, EFI_GUID VendorGuid, UINTN VarSize, VOID *VarData) | |
275 | { | |
276 | if (measureddata == NULL) { | |
277 | measureddata = AllocatePool(sizeof(*measureddata)); | |
278 | } else { | |
279 | measureddata = ReallocatePool(measureddata, measuredcount * sizeof(*measureddata), | |
280 | (measuredcount + 1) * sizeof(*measureddata)); | |
281 | } | |
282 | ||
283 | if (measureddata == NULL) | |
284 | return EFI_OUT_OF_RESOURCES; | |
285 | ||
286 | measureddata[measuredcount].VariableName = AllocatePool(StrSize(VarName)); | |
287 | measureddata[measuredcount].VendorGuid = AllocatePool(sizeof(EFI_GUID)); | |
288 | measureddata[measuredcount].Data = AllocatePool(VarSize); | |
289 | ||
290 | if (measureddata[measuredcount].VariableName == NULL || | |
291 | measureddata[measuredcount].VendorGuid == NULL || | |
292 | measureddata[measuredcount].Data == NULL) { | |
293 | return EFI_OUT_OF_RESOURCES; | |
294 | } | |
295 | ||
296 | StrCpy(measureddata[measuredcount].VariableName, VarName); | |
297 | CopyMem(measureddata[measuredcount].VendorGuid, &VendorGuid, sizeof(EFI_GUID)); | |
298 | CopyMem(measureddata[measuredcount].Data, VarData, VarSize); | |
299 | measureddata[measuredcount].Size = VarSize; | |
300 | measuredcount++; | |
301 | ||
302 | return EFI_SUCCESS; | |
303 | } | |
304 | ||
305 | EFI_STATUS tpm_measure_variable(CHAR16 *VarName, EFI_GUID VendorGuid, UINTN VarSize, VOID *VarData) | |
306 | { | |
1c237633 | 307 | EFI_STATUS efi_status; |
8af7c4ca MG |
308 | UINTN VarNameLength; |
309 | EFI_VARIABLE_DATA_TREE *VarLog; | |
310 | UINT32 VarLogSize; | |
311 | ||
312 | /* Don't measure something that we've already measured */ | |
313 | if (tpm_data_measured(VarName, VendorGuid, VarSize, VarData)) | |
314 | return EFI_SUCCESS; | |
315 | ||
316 | VarNameLength = StrLen (VarName); | |
317 | VarLogSize = (UINT32)(sizeof (*VarLog) + | |
318 | VarNameLength * sizeof (*VarName) + | |
319 | VarSize - | |
320 | sizeof (VarLog->UnicodeName) - | |
321 | sizeof (VarLog->VariableData)); | |
322 | ||
323 | VarLog = (EFI_VARIABLE_DATA_TREE *) AllocateZeroPool (VarLogSize); | |
324 | if (VarLog == NULL) { | |
325 | return EFI_OUT_OF_RESOURCES; | |
326 | } | |
327 | ||
328 | CopyMem (&VarLog->VariableName, &VendorGuid, | |
329 | sizeof(VarLog->VariableName)); | |
330 | VarLog->UnicodeNameLength = VarNameLength; | |
331 | VarLog->VariableDataLength = VarSize; | |
332 | CopyMem (VarLog->UnicodeName, VarName, | |
333 | VarNameLength * sizeof (*VarName)); | |
334 | CopyMem ((CHAR16 *)VarLog->UnicodeName + VarNameLength, VarData, | |
335 | VarSize); | |
336 | ||
1c237633 PJ |
337 | efi_status = tpm_log_event_raw((EFI_PHYSICAL_ADDRESS)(intptr_t)VarLog, |
338 | VarLogSize, 7, (CHAR8 *)VarLog, VarLogSize, | |
339 | EV_EFI_VARIABLE_AUTHORITY, NULL); | |
8af7c4ca MG |
340 | |
341 | FreePool(VarLog); | |
342 | ||
1c237633 PJ |
343 | if (EFI_ERROR(efi_status)) |
344 | return efi_status; | |
8af7c4ca MG |
345 | |
346 | return tpm_record_data_measurement(VarName, VendorGuid, VarSize, | |
347 | VarData); | |
348 | } | |
431b8a2e PJ |
349 | |
350 | EFI_STATUS | |
351 | fallback_should_prefer_reset(void) | |
352 | { | |
1c237633 | 353 | EFI_STATUS efi_status; |
431b8a2e PJ |
354 | efi_tpm_protocol_t *tpm; |
355 | efi_tpm2_protocol_t *tpm2; | |
356 | ||
1c237633 PJ |
357 | efi_status = tpm_locate_protocol(&tpm, &tpm2, NULL, NULL); |
358 | if (EFI_ERROR(efi_status)) | |
431b8a2e PJ |
359 | return EFI_NOT_FOUND; |
360 | return EFI_SUCCESS; | |
361 | } |