]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/efi-loader.c
Merge pull request #26044 from DaanDeMeyer/repart-sector-size
[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 "tpm-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_stub_measured(void) {
242 _cleanup_free_ char *pcr_string = NULL;
243 unsigned pcr_nr;
244 int r;
245
246 /* Checks if we are booted on a kernel with sd-stub which measured the kernel into PCR 11. Or in
247 * other words, if we are running on a TPM enabled UKI.
248 *
249 * Returns == 0 and > 0 depending on the result of the test. Returns -EREMOTE if we detected a stub
250 * being used, but it measured things into a different PCR than we are configured for in
251 * userspace. (i.e. we expect PCR 11 being used for this by both sd-stub and us) */
252
253 r = getenv_bool_secure("SYSTEMD_FORCE_MEASURE"); /* Give user a chance to override the variable test,
254 * for debugging purposes */
255 if (r >= 0)
256 return r;
257 if (r != -ENXIO)
258 log_debug_errno(r, "Failed to parse $SYSTEMD_FORCE_MEASURE, ignoring: %m");
259
260 if (!is_efi_boot())
261 return 0;
262
263 r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubPcrKernelImage), &pcr_string);
264 if (r == -ENOENT)
265 return 0;
266 if (r < 0)
267 return r;
268
269 r = safe_atou(pcr_string, &pcr_nr);
270 if (r < 0)
271 return log_debug_errno(r, "Failed to parse StubPcrKernelImage EFI variable: %s", pcr_string);
272 if (pcr_nr != TPM_PCR_INDEX_KERNEL_IMAGE)
273 return log_debug_errno(SYNTHETIC_ERRNO(EREMOTE), "Kernel stub measured kernel image into PCR %u, which is different than expected %u.", pcr_nr, TPM_PCR_INDEX_KERNEL_IMAGE);
274
275 return 1;
276 }
277
278 int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
279 _cleanup_free_ char *v = NULL;
280 static struct stat cache_stat = {};
281 struct stat new_stat;
282 static usec_t cache;
283 uint64_t sec;
284 int r;
285
286 assert(ret);
287
288 /* stat() the EFI variable, to see if the mtime changed. If it did, we need to cache again. */
289 if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot)), &new_stat) < 0)
290 return -errno;
291
292 if (stat_inode_unmodified(&new_stat, &cache_stat)) {
293 *ret = cache;
294 return 0;
295 }
296
297 r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot), &v);
298 if (r < 0)
299 return r;
300
301 r = safe_atou64(v, &sec);
302 if (r < 0)
303 return r;
304 if (sec > USEC_INFINITY / USEC_PER_SEC)
305 return -ERANGE;
306
307 cache_stat = new_stat;
308 *ret = cache = sec * USEC_PER_SEC; /* return in µs */
309 return 0;
310 }
311
312 int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat) {
313 _cleanup_free_ char *v = NULL;
314 struct stat new_stat;
315 int r;
316
317 assert(cache);
318 assert(cache_stat);
319
320 /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
321 if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderEntryOneShot)), &new_stat) < 0)
322 return -errno;
323
324 if (stat_inode_unmodified(&new_stat, cache_stat))
325 return 0;
326
327 r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryOneShot), &v);
328 if (r < 0)
329 return r;
330
331 if (!efi_loader_entry_name_valid(v))
332 return -EINVAL;
333
334 *cache_stat = new_stat;
335 free_and_replace(*cache, v);
336
337 return 0;
338 }
339
340 #endif
341
342 bool efi_loader_entry_name_valid(const char *s) {
343 if (!filename_is_valid(s)) /* Make sure entry names fit in filenames */
344 return false;
345
346 return in_charset(s, ALPHANUMERICAL "+-_.");
347 }