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