]> git.proxmox.com Git - grub2.git/blob - grub-core/commands/efi/tpm.c
tpm: Fix bug in GRUB2 TPM module
[grub2.git] / grub-core / commands / efi / tpm.c
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2018 Free Software Foundation, Inc.
4 *
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * EFI TPM support code.
19 */
20
21 #include <grub/err.h>
22 #include <grub/i18n.h>
23 #include <grub/efi/api.h>
24 #include <grub/efi/efi.h>
25 #include <grub/efi/tpm.h>
26 #include <grub/mm.h>
27 #include <grub/tpm.h>
28 #include <grub/term.h>
29
30 typedef TCG_PCR_EVENT grub_tpm_event_t;
31
32 static grub_efi_guid_t tpm_guid = EFI_TPM_GUID;
33 static grub_efi_guid_t tpm2_guid = EFI_TPM2_GUID;
34
35 static grub_efi_handle_t *grub_tpm_handle;
36 static grub_uint8_t grub_tpm_version;
37
38 static grub_int8_t tpm1_present = -1;
39 static grub_int8_t tpm2_present = -1;
40
41 static grub_efi_boolean_t
42 grub_tpm1_present (grub_efi_tpm_protocol_t *tpm)
43 {
44 grub_efi_status_t status;
45 TCG_EFI_BOOT_SERVICE_CAPABILITY caps;
46 grub_uint32_t flags;
47 grub_efi_physical_address_t eventlog, lastevent;
48
49 if (tpm1_present != -1)
50 return (grub_efi_boolean_t) tpm1_present;
51
52 caps.Size = (grub_uint8_t) sizeof (caps);
53
54 status = efi_call_5 (tpm->status_check, tpm, &caps, &flags, &eventlog,
55 &lastevent);
56
57 if (status != GRUB_EFI_SUCCESS || caps.TPMDeactivatedFlag
58 || !caps.TPMPresentFlag)
59 return tpm1_present = 0;
60
61 return tpm1_present = 1;
62 }
63
64 static grub_efi_boolean_t
65 grub_tpm2_present (grub_efi_tpm2_protocol_t *tpm)
66 {
67 grub_efi_status_t status;
68 EFI_TCG2_BOOT_SERVICE_CAPABILITY caps;
69
70 caps.Size = (grub_uint8_t) sizeof (caps);
71
72 if (tpm2_present != -1)
73 return (grub_efi_boolean_t) tpm2_present;
74
75 status = efi_call_2 (tpm->get_capability, tpm, &caps);
76
77 if (status != GRUB_EFI_SUCCESS || !caps.TPMPresentFlag)
78 return tpm2_present = 0;
79
80 return tpm2_present = 1;
81 }
82
83 static grub_efi_boolean_t
84 grub_tpm_handle_find (grub_efi_handle_t *tpm_handle,
85 grub_efi_uint8_t *protocol_version)
86 {
87 grub_efi_handle_t *handles;
88 grub_efi_uintn_t num_handles;
89
90 if (grub_tpm_handle != NULL)
91 {
92 *tpm_handle = grub_tpm_handle;
93 *protocol_version = grub_tpm_version;
94 return 1;
95 }
96
97 handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &tpm_guid, NULL,
98 &num_handles);
99 if (handles && num_handles > 0)
100 {
101 grub_tpm_handle = handles[0];
102 *tpm_handle = handles[0];
103 grub_tpm_version = 1;
104 *protocol_version = 1;
105 return 1;
106 }
107
108 handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &tpm2_guid, NULL,
109 &num_handles);
110 if (handles && num_handles > 0)
111 {
112 grub_tpm_handle = handles[0];
113 *tpm_handle = handles[0];
114 grub_tpm_version = 2;
115 *protocol_version = 2;
116 return 1;
117 }
118
119 return 0;
120 }
121
122 static grub_err_t
123 grub_tpm1_execute (grub_efi_handle_t tpm_handle,
124 PassThroughToTPM_InputParamBlock *inbuf,
125 PassThroughToTPM_OutputParamBlock *outbuf)
126 {
127 grub_efi_status_t status;
128 grub_efi_tpm_protocol_t *tpm;
129 grub_uint32_t inhdrsize = sizeof (*inbuf) - sizeof (inbuf->TPMOperandIn);
130 grub_uint32_t outhdrsize =
131 sizeof (*outbuf) - sizeof (outbuf->TPMOperandOut);
132
133 tpm = grub_efi_open_protocol (tpm_handle, &tpm_guid,
134 GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
135
136 if (!grub_tpm1_present (tpm))
137 return 0;
138
139 /* UEFI TPM protocol takes the raw operand block, no param block header. */
140 status = efi_call_5 (tpm->pass_through_to_tpm, tpm,
141 inbuf->IPBLength - inhdrsize, inbuf->TPMOperandIn,
142 outbuf->OPBLength - outhdrsize, outbuf->TPMOperandOut);
143
144 switch (status)
145 {
146 case GRUB_EFI_SUCCESS:
147 return 0;
148 case GRUB_EFI_DEVICE_ERROR:
149 return grub_error (GRUB_ERR_IO, N_("Command failed"));
150 case GRUB_EFI_INVALID_PARAMETER:
151 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter"));
152 case GRUB_EFI_BUFFER_TOO_SMALL:
153 return grub_error (GRUB_ERR_BAD_ARGUMENT,
154 N_("Output buffer too small"));
155 case GRUB_EFI_NOT_FOUND:
156 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("TPM unavailable"));
157 default:
158 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("Unknown TPM error"));
159 }
160 }
161
162 static grub_err_t
163 grub_tpm2_execute (grub_efi_handle_t tpm_handle,
164 PassThroughToTPM_InputParamBlock *inbuf,
165 PassThroughToTPM_OutputParamBlock *outbuf)
166 {
167 grub_efi_status_t status;
168 grub_efi_tpm2_protocol_t *tpm;
169 grub_uint32_t inhdrsize = sizeof (*inbuf) - sizeof (inbuf->TPMOperandIn);
170 grub_uint32_t outhdrsize =
171 sizeof (*outbuf) - sizeof (outbuf->TPMOperandOut);
172
173 tpm = grub_efi_open_protocol (tpm_handle, &tpm2_guid,
174 GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
175
176 if (!grub_tpm2_present (tpm))
177 return 0;
178
179 /* UEFI TPM protocol takes the raw operand block, no param block header. */
180 status = efi_call_5 (tpm->submit_command, tpm,
181 inbuf->IPBLength - inhdrsize, inbuf->TPMOperandIn,
182 outbuf->OPBLength - outhdrsize, outbuf->TPMOperandOut);
183
184 switch (status)
185 {
186 case GRUB_EFI_SUCCESS:
187 return 0;
188 case GRUB_EFI_DEVICE_ERROR:
189 return grub_error (GRUB_ERR_IO, N_("Command failed"));
190 case GRUB_EFI_INVALID_PARAMETER:
191 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter"));
192 case GRUB_EFI_BUFFER_TOO_SMALL:
193 return grub_error (GRUB_ERR_BAD_ARGUMENT,
194 N_("Output buffer too small"));
195 case GRUB_EFI_NOT_FOUND:
196 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("TPM unavailable"));
197 default:
198 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("Unknown TPM error"));
199 }
200 }
201
202 grub_err_t
203 grub_tpm_execute (PassThroughToTPM_InputParamBlock *inbuf,
204 PassThroughToTPM_OutputParamBlock *outbuf)
205 {
206 grub_efi_handle_t tpm_handle;
207 grub_uint8_t protocol_version;
208
209 /* Absence of a TPM isn't a failure. */
210 if (!grub_tpm_handle_find (&tpm_handle, &protocol_version))
211 return 0;
212
213 if (protocol_version == 1)
214 return grub_tpm1_execute (tpm_handle, inbuf, outbuf);
215 else
216 return grub_tpm2_execute (tpm_handle, inbuf, outbuf);
217 }
218
219 static grub_err_t
220 grub_tpm1_log_event (grub_efi_handle_t tpm_handle, unsigned char *buf,
221 grub_size_t size, grub_uint8_t pcr,
222 const char *description)
223 {
224 grub_tpm_event_t *event;
225 grub_efi_status_t status;
226 grub_efi_tpm_protocol_t *tpm;
227 grub_efi_physical_address_t lastevent;
228 grub_uint32_t algorithm;
229 grub_uint32_t eventnum = 0;
230
231 tpm = grub_efi_open_protocol (tpm_handle, &tpm_guid,
232 GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
233
234 if (!grub_tpm1_present (tpm))
235 return 0;
236
237 event = grub_zalloc (sizeof (*event) + grub_strlen (description) + 1);
238 if (!event)
239 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
240 N_("cannot allocate TPM event buffer"));
241
242 event->PCRIndex = pcr;
243 event->EventType = EV_IPL;
244 event->EventSize = grub_strlen (description) + 1;
245 grub_memcpy (event->Event, description, event->EventSize);
246
247 algorithm = TCG_ALG_SHA;
248 status = efi_call_7 (tpm->log_extend_event, tpm, buf, (grub_uint64_t) size,
249 algorithm, event, &eventnum, &lastevent);
250
251 switch (status)
252 {
253 case GRUB_EFI_SUCCESS:
254 return 0;
255 case GRUB_EFI_DEVICE_ERROR:
256 return grub_error (GRUB_ERR_IO, N_("Command failed"));
257 case GRUB_EFI_INVALID_PARAMETER:
258 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter"));
259 case GRUB_EFI_BUFFER_TOO_SMALL:
260 return grub_error (GRUB_ERR_BAD_ARGUMENT,
261 N_("Output buffer too small"));
262 case GRUB_EFI_NOT_FOUND:
263 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("TPM unavailable"));
264 default:
265 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("Unknown TPM error"));
266 }
267 }
268
269 static grub_err_t
270 grub_tpm2_log_event (grub_efi_handle_t tpm_handle, unsigned char *buf,
271 grub_size_t size, grub_uint8_t pcr,
272 const char *description)
273 {
274 EFI_TCG2_EVENT *event;
275 grub_efi_status_t status;
276 grub_efi_tpm2_protocol_t *tpm;
277
278 tpm = grub_efi_open_protocol (tpm_handle, &tpm2_guid,
279 GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
280
281 if (!grub_tpm2_present (tpm))
282 return 0;
283
284 event =
285 grub_zalloc (sizeof (EFI_TCG2_EVENT) + grub_strlen (description) + 1);
286 if (!event)
287 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
288 N_("cannot allocate TPM event buffer"));
289
290 event->Header.HeaderSize = sizeof (EFI_TCG2_EVENT_HEADER);
291 event->Header.HeaderVersion = 1;
292 event->Header.PCRIndex = pcr;
293 event->Header.EventType = EV_IPL;
294 event->Size =
295 sizeof (*event) - sizeof (event->Event) + grub_strlen (description) + 1;
296 grub_memcpy (event->Event, description, grub_strlen (description) + 1);
297
298 status = efi_call_5 (tpm->hash_log_extend_event, tpm, 0, buf,
299 (grub_uint64_t) size, event);
300
301 switch (status)
302 {
303 case GRUB_EFI_SUCCESS:
304 return 0;
305 case GRUB_EFI_DEVICE_ERROR:
306 return grub_error (GRUB_ERR_IO, N_("Command failed"));
307 case GRUB_EFI_INVALID_PARAMETER:
308 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter"));
309 case GRUB_EFI_BUFFER_TOO_SMALL:
310 return grub_error (GRUB_ERR_BAD_ARGUMENT,
311 N_("Output buffer too small"));
312 case GRUB_EFI_NOT_FOUND:
313 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("TPM unavailable"));
314 default:
315 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("Unknown TPM error"));
316 }
317 }
318
319 grub_err_t
320 grub_tpm_log_event (unsigned char *buf, grub_size_t size, grub_uint8_t pcr,
321 const char *description)
322 {
323 grub_efi_handle_t tpm_handle;
324 grub_efi_uint8_t protocol_version;
325
326 if (!grub_tpm_handle_find (&tpm_handle, &protocol_version))
327 return 0;
328
329 if (protocol_version == 1)
330 return grub_tpm1_log_event (tpm_handle, buf, size, pcr, description);
331 else
332 return grub_tpm2_log_event (tpm_handle, buf, size, pcr, description);
333 }