]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/efi-loader.c
NEWS: fix typo
[thirdparty/systemd.git] / src / shared / efi-loader.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "efi-api.h"
5 #include "efi-loader.h"
6 #include "efivars.h"
7 #include "env-util.h"
8 #include "log.h"
9 #include "parse-util.h"
10 #include "path-util.h"
11 #include "stat-util.h"
12 #include "string-util.h"
13 #include "strv.h"
14 #include "time-util.h"
15 #include "tpm2-pcr.h"
16 #include "utf8.h"
17
18 #if ENABLE_EFI
19 static int read_usec(const char *variable, usec_t *ret) {
20 _cleanup_free_ char *j = NULL;
21 uint64_t x = 0;
22 int r;
23
24 assert(variable);
25 assert(ret);
26
27 r = efi_get_variable_string(variable, &j);
28 if (r < 0)
29 return r;
30
31 r = safe_atou64(j, &x);
32 if (r < 0)
33 return r;
34
35 *ret = x;
36 return 0;
37 }
38
39 static int get_device_part_uuid(const char *variable, sd_id128_t *ret) {
40 if (!is_efi_boot())
41 return -EOPNOTSUPP;
42
43 return efi_get_variable_id128(variable, ret);
44 }
45 #endif
46
47 int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader) {
48 #if ENABLE_EFI
49 uint64_t x, y;
50 int r;
51
52 assert(ret_firmware);
53 assert(ret_loader);
54
55 if (!is_efi_boot())
56 return -EOPNOTSUPP;
57
58 r = read_usec(EFI_LOADER_VARIABLE_STR("LoaderTimeInitUSec"), &x);
59 if (r < 0)
60 return log_debug_errno(r, "Failed to read LoaderTimeInitUSec: %m");
61
62 r = read_usec(EFI_LOADER_VARIABLE_STR("LoaderTimeExecUSec"), &y);
63 if (r < 0)
64 return log_debug_errno(r, "Failed to read LoaderTimeExecUSec: %m");
65
66 if (y == 0 || y < x || y - x > USEC_PER_HOUR)
67 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
68 "Bad LoaderTimeInitUSec=%"PRIu64", LoaderTimeExecUSec=%" PRIu64"; refusing.",
69 x, y);
70
71 *ret_firmware = x;
72 *ret_loader = y;
73 return 0;
74 #else
75 return -EOPNOTSUPP;
76 #endif
77 }
78
79 int efi_loader_get_device_part_uuid(sd_id128_t *ret) {
80 #if ENABLE_EFI
81 return get_device_part_uuid(EFI_LOADER_VARIABLE_STR("LoaderDevicePartUUID"), ret);
82 #else
83 return -EOPNOTSUPP;
84 #endif
85 }
86
87 int efi_stub_get_device_part_uuid(sd_id128_t *ret) {
88 #if ENABLE_EFI
89 return get_device_part_uuid(EFI_LOADER_VARIABLE_STR("StubDevicePartUUID"), ret);
90 #else
91 return -EOPNOTSUPP;
92 #endif
93 }
94
95 int efi_loader_get_entries(char ***ret) {
96 #if ENABLE_EFI
97 _cleanup_free_ char16_t *entries = NULL;
98 _cleanup_strv_free_ char **l = NULL;
99 size_t size;
100 int r;
101
102 assert(ret);
103
104 if (!is_efi_boot())
105 return -EOPNOTSUPP;
106
107 r = efi_get_variable(EFI_LOADER_VARIABLE_STR("LoaderEntries"), NULL, (void**) &entries, &size);
108 if (r < 0)
109 return r;
110
111 /* The variable contains a series of individually NUL terminated UTF-16 strings. We gracefully
112 * consider the final NUL byte optional (i.e. the last string may or may not end in a NUL byte). */
113
114 for (size_t i = 0, start = 0;; i++) {
115 _cleanup_free_ char *decoded = NULL;
116 bool end;
117
118 /* Is this the end of the variable's data? */
119 end = i * sizeof(char16_t) >= size;
120
121 /* Are we in the middle of a string? (i.e. not at the end of the variable, nor at a NUL terminator?) If
122 * so, let's go to the next entry. */
123 if (!end && entries[i] != 0)
124 continue;
125
126 /* Empty string at the end of variable? That's the trailer, we are done (i.e. we have a final
127 * NUL terminator). */
128 if (end && start == i)
129 break;
130
131 /* We reached the end of a string, let's decode it into UTF-8 */
132 decoded = utf16_to_utf8(entries + start, (i - start) * sizeof(char16_t));
133 if (!decoded)
134 return -ENOMEM;
135
136 if (efi_loader_entry_name_valid(decoded)) {
137 r = strv_consume(&l, TAKE_PTR(decoded));
138 if (r < 0)
139 return r;
140 } else
141 log_debug("Ignoring invalid loader entry '%s'.", decoded);
142
143 /* Exit the loop if we reached the end of the variable (i.e. we do not have a final NUL
144 * terminator) */
145 if (end)
146 break;
147
148 /* Continue after the NUL byte */
149 start = i + 1;
150 }
151
152 *ret = TAKE_PTR(l);
153 return 0;
154 #else
155 return -EOPNOTSUPP;
156 #endif
157 }
158
159 int efi_loader_get_features(uint64_t *ret) {
160 #if ENABLE_EFI
161 _cleanup_free_ void *v = NULL;
162 size_t s;
163 int r;
164
165 assert(ret);
166
167 if (!is_efi_boot()) {
168 *ret = 0;
169 return 0;
170 }
171
172 r = efi_get_variable(EFI_LOADER_VARIABLE_STR("LoaderFeatures"), NULL, &v, &s);
173 if (r == -ENOENT) {
174 _cleanup_free_ char *info = NULL;
175
176 /* The new (v240+) LoaderFeatures variable is not supported, let's see if it's systemd-boot at all */
177 r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderInfo"), &info);
178 if (r < 0) {
179 if (r != -ENOENT)
180 return r;
181
182 /* Variable not set, definitely means not systemd-boot */
183
184 } else if (first_word(info, "systemd-boot")) {
185
186 /* An older systemd-boot version. Let's hardcode the feature set, since it was pretty
187 * static in all its versions. */
188
189 *ret = EFI_LOADER_FEATURE_CONFIG_TIMEOUT |
190 EFI_LOADER_FEATURE_ENTRY_DEFAULT |
191 EFI_LOADER_FEATURE_ENTRY_ONESHOT;
192
193 return 0;
194 }
195
196 /* No features supported */
197 *ret = 0;
198 return 0;
199 }
200 if (r < 0)
201 return r;
202
203 if (s != sizeof(uint64_t))
204 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
205 "LoaderFeatures EFI variable doesn't have the right size.");
206
207 memcpy(ret, v, sizeof(uint64_t));
208 return 0;
209 #else
210 return -EOPNOTSUPP;
211 #endif
212 }
213
214 int efi_stub_get_features(uint64_t *ret) {
215 #if ENABLE_EFI
216 _cleanup_free_ void *v = NULL;
217 size_t s;
218 int r;
219
220 assert(ret);
221
222 if (!is_efi_boot()) {
223 *ret = 0;
224 return 0;
225 }
226
227 r = efi_get_variable(EFI_LOADER_VARIABLE_STR("StubFeatures"), NULL, &v, &s);
228 if (r == -ENOENT) {
229 _cleanup_free_ char *info = NULL;
230
231 /* The new (v252+) StubFeatures variable is not supported, let's see if it's systemd-stub at all */
232 r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("StubInfo"), &info);
233 if (r < 0) {
234 if (r != -ENOENT)
235 return r;
236
237 /* Variable not set, definitely means not systemd-stub */
238
239 } else if (first_word(info, "systemd-stub")) {
240
241 /* An older systemd-stub version. Let's hardcode the feature set, since it was pretty
242 * static in all its versions. */
243
244 *ret = EFI_STUB_FEATURE_REPORT_BOOT_PARTITION;
245 return 0;
246 }
247
248 /* No features supported */
249 *ret = 0;
250 return 0;
251 }
252 if (r < 0)
253 return r;
254
255 if (s != sizeof(uint64_t))
256 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
257 "StubFeatures EFI variable doesn't have the right size.");
258
259 memcpy(ret, v, sizeof(uint64_t));
260 return 0;
261 #else
262 return -EOPNOTSUPP;
263 #endif
264 }
265
266 int efi_measured_uki(int log_level) {
267 #if ENABLE_EFI
268 _cleanup_free_ char *pcr_string = NULL;
269 static int cached = -1;
270 unsigned pcr_nr;
271 int r;
272
273 if (cached >= 0)
274 return cached;
275
276 /* Checks if we are booted on a kernel with sd-stub which measured the kernel into PCR 11 on a TPM2
277 * chip. Or in other words, if we are running on a TPM enabled UKI. (TPM 1.2 situations are ignored.)
278 *
279 * Returns == 0 and > 0 depending on the result of the test. Returns -EREMOTE if we detected a stub
280 * being used, but it measured things into a different PCR than we are configured for in
281 * userspace. (i.e. we expect PCR 11 being used for this by both sd-stub and us) */
282
283 r = secure_getenv_bool("SYSTEMD_FORCE_MEASURE"); /* Give user a chance to override the variable test,
284 * for debugging purposes */
285 if (r >= 0)
286 return (cached = r);
287 if (r != -ENXIO)
288 log_debug_errno(r, "Failed to parse $SYSTEMD_FORCE_MEASURE, ignoring: %m");
289
290 if (!efi_has_tpm2())
291 return (cached = 0);
292
293 r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("StubPcrKernelImage"), &pcr_string);
294 if (r == -ENOENT)
295 return (cached = 0);
296 if (r < 0)
297 return log_full_errno(log_level, r,
298 "Failed to get StubPcrKernelImage EFI variable: %m");
299
300 r = safe_atou(pcr_string, &pcr_nr);
301 if (r < 0)
302 return log_full_errno(log_level, r,
303 "Failed to parse StubPcrKernelImage EFI variable: %s", pcr_string);
304 if (pcr_nr != TPM2_PCR_KERNEL_BOOT)
305 return log_full_errno(log_level, SYNTHETIC_ERRNO(EREMOTE),
306 "Kernel stub measured kernel image into PCR %u, which is different than expected %i.",
307 pcr_nr, TPM2_PCR_KERNEL_BOOT);
308
309 return (cached = 1);
310 #else
311 return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP), "Compiled without support for EFI");
312 #endif
313 }
314
315 int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
316 #if ENABLE_EFI
317 _cleanup_free_ char *v = NULL;
318 static struct stat cache_stat = {};
319 struct stat new_stat;
320 static usec_t cache;
321 uint64_t sec;
322 int r;
323
324 assert(ret);
325
326 /* stat() the EFI variable, to see if the mtime changed. If it did, we need to cache again. */
327 if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("LoaderConfigTimeoutOneShot")), &new_stat) < 0)
328 return -errno;
329
330 if (stat_inode_unmodified(&new_stat, &cache_stat)) {
331 *ret = cache;
332 return 0;
333 }
334
335 r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderConfigTimeoutOneShot"), &v);
336 if (r < 0)
337 return r;
338
339 r = safe_atou64(v, &sec);
340 if (r < 0)
341 return r;
342 if (sec > USEC_INFINITY / USEC_PER_SEC)
343 return -ERANGE;
344
345 cache_stat = new_stat;
346 *ret = cache = sec * USEC_PER_SEC; /* return in μs */
347 return 0;
348 #else
349 return -EOPNOTSUPP;
350 #endif
351 }
352
353 int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat) {
354 #if ENABLE_EFI
355 _cleanup_free_ char *v = NULL;
356 struct stat new_stat;
357 int r;
358
359 assert(cache);
360 assert(cache_stat);
361
362 /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
363 if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot")), &new_stat) < 0)
364 return -errno;
365
366 if (stat_inode_unmodified(&new_stat, cache_stat))
367 return 0;
368
369 r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot"), &v);
370 if (r < 0)
371 return r;
372
373 if (!efi_loader_entry_name_valid(v))
374 return -EINVAL;
375
376 *cache_stat = new_stat;
377 free_and_replace(*cache, v);
378
379 return 0;
380 #else
381 return -EOPNOTSUPP;
382 #endif
383 }
384
385 int efi_get_variable_id128(const char *variable, sd_id128_t *ret) {
386 #if ENABLE_EFI
387 int r;
388
389 assert(variable);
390
391 /* This is placed here (rather than in basic/efivars.c) because code in basic/ is not allowed to link
392 * against libsystemd.so */
393
394 _cleanup_free_ char *p = NULL;
395 r = efi_get_variable_string(variable, &p);
396 if (r < 0)
397 return r;
398
399 return sd_id128_from_string(p, ret);
400 #else
401 return -EOPNOTSUPP;
402 #endif
403 }
404
405 bool efi_loader_entry_name_valid(const char *s) {
406 if (!filename_is_valid(s)) /* Make sure entry names fit in filenames */
407 return false;
408
409 return in_charset(s, ALPHANUMERICAL "+-_.@");
410 }