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