]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/measure.c
repart: remove duplicate word in --help
[thirdparty/systemd.git] / src / boot / efi / measure.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #if ENABLE_TPM
4
5 #include "macro-fundamental.h"
6 #include "measure.h"
7 #include "memory-util-fundamental.h"
8 #include "proto/cc-measurement.h"
9 #include "proto/tcg.h"
10 #include "tpm2-pcr.h"
11 #include "util.h"
12
13 static EFI_STATUS tpm2_measure_to_pcr_and_tagged_event_log(
14 EFI_TCG2_PROTOCOL *tcg,
15 uint32_t pcrindex,
16 EFI_PHYSICAL_ADDRESS buffer,
17 uint64_t buffer_size,
18 uint32_t event_id,
19 const char16_t *description) {
20
21 _cleanup_free_ struct event {
22 EFI_TCG2_EVENT tcg_event;
23 EFI_TCG2_TAGGED_EVENT tcg_tagged_event;
24 } _packed_ *event = NULL;
25 size_t desc_len, event_size;
26
27 assert(tcg);
28 assert(description);
29
30 desc_len = strsize16(description);
31 event_size = offsetof(EFI_TCG2_EVENT, Event) + offsetof(EFI_TCG2_TAGGED_EVENT, Event) + desc_len;
32
33 event = xmalloc(event_size);
34 *event = (struct event) {
35 .tcg_event = (EFI_TCG2_EVENT) {
36 .Size = event_size,
37 .Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER),
38 .Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION,
39 .Header.PCRIndex = pcrindex,
40 .Header.EventType = EV_EVENT_TAG,
41 },
42 .tcg_tagged_event = {
43 .EventId = event_id,
44 .EventSize = desc_len,
45 },
46 };
47 memcpy(event->tcg_tagged_event.Event, description, desc_len);
48
49 return tcg->HashLogExtendEvent(
50 tcg,
51 0,
52 buffer, buffer_size,
53 &event->tcg_event);
54 }
55
56 static EFI_STATUS tpm2_measure_to_pcr_and_event_log(
57 EFI_TCG2_PROTOCOL *tcg,
58 uint32_t pcrindex,
59 EFI_PHYSICAL_ADDRESS buffer,
60 uint64_t buffer_size,
61 const char16_t *description) {
62
63 _cleanup_free_ EFI_TCG2_EVENT *tcg_event = NULL;
64 size_t desc_len;
65
66 assert(tcg);
67 assert(description);
68
69 /* NB: We currently record everything as EV_IPL. Which sucks, because it makes it hard to
70 * recognize from the event log which of the events are ours. Measurement logs are kinda API hence
71 * this is hard to change for existing, established events. But for future additions, let's use
72 * EV_EVENT_TAG instead, with a tag of our choosing that makes clear what precisely we are measuring
73 * here. */
74
75 desc_len = strsize16(description);
76 tcg_event = xmalloc(offsetof(EFI_TCG2_EVENT, Event) + desc_len);
77 *tcg_event = (EFI_TCG2_EVENT) {
78 .Size = offsetof(EFI_TCG2_EVENT, Event) + desc_len,
79 .Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER),
80 .Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION,
81 .Header.PCRIndex = pcrindex,
82 .Header.EventType = EV_IPL,
83 };
84
85 memcpy(tcg_event->Event, description, desc_len);
86
87 return tcg->HashLogExtendEvent(
88 tcg,
89 0,
90 buffer, buffer_size,
91 tcg_event);
92 }
93
94 static EFI_STATUS cc_measure_to_mr_and_event_log(
95 EFI_CC_MEASUREMENT_PROTOCOL *cc,
96 uint32_t pcrindex,
97 EFI_PHYSICAL_ADDRESS buffer,
98 uint64_t buffer_size,
99 const char16_t *description) {
100
101 _cleanup_free_ EFI_CC_EVENT *event = NULL;
102 uint32_t mr;
103 EFI_STATUS err;
104 size_t desc_len;
105
106 assert(cc);
107 assert(description);
108
109 /* MapPcrToMrIndex service provides callers information on
110 * how the TPM PCR registers are mapped to the CC measurement
111 * registers (MR) in the vendor implementation. */
112 err = cc->MapPcrToMrIndex(cc, pcrindex, &mr);
113 if (err != EFI_SUCCESS)
114 return EFI_NOT_FOUND;
115
116 desc_len = strsize16(description);
117 event = xmalloc(offsetof(EFI_CC_EVENT, Event) + desc_len);
118 *event = (EFI_CC_EVENT) {
119 .Size = offsetof(EFI_CC_EVENT, Event) + desc_len,
120 .Header.HeaderSize = sizeof(EFI_CC_EVENT_HEADER),
121 .Header.HeaderVersion = EFI_CC_EVENT_HEADER_VERSION,
122 .Header.MrIndex = mr,
123 .Header.EventType = EV_IPL,
124 };
125
126 memcpy(event->Event, description, desc_len);
127
128 return cc->HashLogExtendEvent(
129 cc,
130 0,
131 buffer,
132 buffer_size,
133 event);
134 }
135
136 static EFI_CC_MEASUREMENT_PROTOCOL *cc_interface_check(void) {
137 EFI_CC_BOOT_SERVICE_CAPABILITY capability = {
138 .Size = sizeof(capability),
139 };
140 EFI_STATUS err;
141 EFI_CC_MEASUREMENT_PROTOCOL *cc;
142
143 err = BS->LocateProtocol(MAKE_GUID_PTR(EFI_CC_MEASUREMENT_PROTOCOL), NULL, (void **) &cc);
144 if (err != EFI_SUCCESS)
145 return NULL;
146
147 err = cc->GetCapability(cc, &capability);
148 if (err != EFI_SUCCESS)
149 return NULL;
150
151 if (!(capability.SupportedEventLogs & EFI_CC_EVENT_LOG_FORMAT_TCG_2))
152 return NULL;
153
154 return cc;
155 }
156
157 static EFI_TCG2_PROTOCOL *tcg2_interface_check(void) {
158 EFI_TCG2_BOOT_SERVICE_CAPABILITY capability = {
159 .Size = sizeof(capability),
160 };
161 EFI_STATUS err;
162 EFI_TCG2_PROTOCOL *tcg;
163
164 err = BS->LocateProtocol(MAKE_GUID_PTR(EFI_TCG2_PROTOCOL), NULL, (void **) &tcg);
165 if (err != EFI_SUCCESS)
166 return NULL;
167
168 err = tcg->GetCapability(tcg, &capability);
169 if (err != EFI_SUCCESS)
170 return NULL;
171
172 if (capability.StructureVersion.Major == 1 &&
173 capability.StructureVersion.Minor == 0) {
174 EFI_TCG_BOOT_SERVICE_CAPABILITY *caps_1_0 =
175 (EFI_TCG_BOOT_SERVICE_CAPABILITY*) &capability;
176 if (caps_1_0->TPMPresentFlag)
177 return tcg;
178 }
179
180 if (!capability.TPMPresentFlag)
181 return NULL;
182
183 return tcg;
184 }
185
186 bool tpm_present(void) {
187 return tcg2_interface_check();
188 }
189
190 static EFI_STATUS tcg2_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured) {
191 EFI_TCG2_PROTOCOL *tpm2;
192 EFI_STATUS err = EFI_SUCCESS;
193
194 assert(ret_measured);
195
196 tpm2 = tcg2_interface_check();
197 if (tpm2)
198 err = tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description);
199
200 *ret_measured = tpm2 && (err == EFI_SUCCESS);
201
202 return err;
203 }
204
205 static EFI_STATUS cc_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured) {
206 EFI_CC_MEASUREMENT_PROTOCOL *cc;
207 EFI_STATUS err = EFI_SUCCESS;
208
209 assert(ret_measured);
210
211 cc = cc_interface_check();
212 if (cc)
213 err = cc_measure_to_mr_and_event_log(cc, pcrindex, buffer, buffer_size, description);
214
215 *ret_measured = cc && (err == EFI_SUCCESS);
216
217 return err;
218 }
219
220 EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured) {
221 EFI_STATUS err;
222 bool tpm_ret_measured, cc_ret_measured;
223
224 assert(description || pcrindex == UINT32_MAX);
225
226 /* If EFI_SUCCESS is returned, will initialize ret_measured to true if we actually measured
227 * something, or false if measurement was turned off. */
228
229 if (pcrindex == UINT32_MAX) { /* PCR disabled? */
230 if (ret_measured)
231 *ret_measured = false;
232
233 return EFI_SUCCESS;
234 }
235
236 /* Measure into both CC and TPM if both are available to avoid a problem like CVE-2021-42299 */
237 err = cc_log_event(pcrindex, buffer, buffer_size, description, &cc_ret_measured);
238 if (err != EFI_SUCCESS)
239 return err;
240
241 err = tcg2_log_event(pcrindex, buffer, buffer_size, description, &tpm_ret_measured);
242
243 if (err == EFI_SUCCESS && ret_measured)
244 *ret_measured = tpm_ret_measured || cc_ret_measured;
245
246 return err;
247 }
248
249 EFI_STATUS tpm_log_tagged_event(
250 uint32_t pcrindex,
251 EFI_PHYSICAL_ADDRESS buffer,
252 size_t buffer_size,
253 uint32_t event_id,
254 const char16_t *description,
255 bool *ret_measured) {
256
257 EFI_TCG2_PROTOCOL *tpm2;
258 EFI_STATUS err;
259
260 assert(description || pcrindex == UINT32_MAX);
261 assert(event_id > 0);
262
263 /* If EFI_SUCCESS is returned, will initialize ret_measured to true if we actually measured
264 * something, or false if measurement was turned off. */
265
266 tpm2 = tcg2_interface_check();
267 if (!tpm2 || pcrindex == UINT32_MAX) { /* PCR disabled? */
268 if (ret_measured)
269 *ret_measured = false;
270
271 return EFI_SUCCESS;
272 }
273
274 err = tpm2_measure_to_pcr_and_tagged_event_log(tpm2, pcrindex, buffer, buffer_size, event_id, description);
275 if (err == EFI_SUCCESS && ret_measured)
276 *ret_measured = true;
277
278 return err;
279 }
280
281 EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char *description, bool *ret_measured) {
282 _cleanup_free_ char16_t *c = NULL;
283
284 if (description)
285 c = xstr8_to_16(description);
286
287 return tpm_log_event(pcrindex, buffer, buffer_size, c, ret_measured);
288 }
289
290 EFI_STATUS tpm_log_load_options(const char16_t *load_options, bool *ret_measured) {
291 bool measured = false;
292 EFI_STATUS err;
293
294 /* Measures a load options string into the TPM2, i.e. the kernel command line */
295
296 err = tpm_log_event(
297 TPM2_PCR_KERNEL_CONFIG,
298 POINTER_TO_PHYSICAL_ADDRESS(load_options),
299 strsize16(load_options),
300 load_options,
301 &measured);
302 if (err != EFI_SUCCESS)
303 return log_error_status(
304 err,
305 "Unable to add load options (i.e. kernel command) line measurement to PCR %i: %m",
306 TPM2_PCR_KERNEL_CONFIG);
307
308 if (ret_measured)
309 *ret_measured = measured;
310
311 return EFI_SUCCESS;
312 }
313
314 #endif