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