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