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