]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/efivars.c
tree-wide: use -EBADF for fd initialization
[thirdparty/systemd.git] / src / basic / efivars.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
0bb2f0f1
ZJS
2
3#include <errno.h>
4#include <fcntl.h>
5#include <limits.h>
6#include <linux/fs.h>
0bb2f0f1 7#include <stdlib.h>
0bb2f0f1
ZJS
8#include <sys/stat.h>
9#include <unistd.h>
10
11#include "sd-id128.h"
12
13#include "alloc-util.h"
14#include "chattr-util.h"
15#include "efivars.h"
16#include "fd-util.h"
209b2592 17#include "fileio.h"
0bb2f0f1
ZJS
18#include "io-util.h"
19#include "macro.h"
37e4637a 20#include "memory-util.h"
0bb2f0f1
ZJS
21#include "stdio-util.h"
22#include "strv.h"
23#include "time-util.h"
24#include "utf8.h"
c7d26acc 25#include "virt.h"
0bb2f0f1
ZJS
26
27#if ENABLE_EFI
28
7229ec02 29/* Reads from efivarfs sometimes fail with EINTR. Retry that many times. */
eee9b30a
ZJS
30#define EFI_N_RETRIES_NO_DELAY 20
31#define EFI_N_RETRIES_TOTAL 25
7229ec02
ZJS
32#define EFI_RETRY_DELAY (50 * USEC_PER_MSEC)
33
0bb2f0f1 34int efi_get_variable(
e6f055cb 35 const char *variable,
0bb2f0f1
ZJS
36 uint32_t *ret_attribute,
37 void **ret_value,
38 size_t *ret_size) {
39
254d1313 40 _cleanup_close_ int fd = -EBADF;
0bb2f0f1
ZJS
41 _cleanup_free_ void *buf = NULL;
42 struct stat st;
aff81b18 43 usec_t begin = 0; /* Unnecessary initialization to appease gcc */
0bb2f0f1
ZJS
44 uint32_t a;
45 ssize_t n;
46
e6f055cb 47 assert(variable);
0bb2f0f1 48
e6f055cb 49 const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
0bb2f0f1
ZJS
50
51 if (!ret_value && !ret_size && !ret_attribute) {
7229ec02
ZJS
52 /* If caller is not interested in anything, just check if the variable exists and is
53 * readable. */
0bb2f0f1
ZJS
54 if (access(p, R_OK) < 0)
55 return -errno;
56
57 return 0;
58 }
59
84190644
LP
60 if (DEBUG_LOGGING) {
61 log_debug("Reading EFI variable %s.", p);
698564d1 62 begin = now(CLOCK_MONOTONIC);
84190644 63 }
698564d1 64
0bb2f0f1
ZJS
65 fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
66 if (fd < 0)
7229ec02 67 return log_debug_errno(errno, "open(\"%s\") failed: %m", p);
0bb2f0f1
ZJS
68
69 if (fstat(fd, &st) < 0)
84190644 70 return log_debug_errno(errno, "fstat(\"%s\") failed: %m", p);
0bb2f0f1 71 if (st.st_size < 4)
84190644 72 return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), "EFI variable %s is shorter than 4 bytes, refusing.", p);
0bb2f0f1 73 if (st.st_size > 4*1024*1024 + 4)
84190644 74 return log_debug_errno(SYNTHETIC_ERRNO(E2BIG), "EFI variable %s is ridiculously large, refusing.", p);
0bb2f0f1
ZJS
75
76 if (ret_value || ret_attribute) {
7229ec02
ZJS
77 /* The kernel ratelimits reads from the efivarfs because EFI is inefficient, and we'll
78 * occasionally fail with EINTR here. A slowdown is better than a failure for us, so
79 * retry a few times and eventually fail with -EBUSY.
80 *
81 * See https://github.com/torvalds/linux/blob/master/fs/efivarfs/file.c#L75
82 * and
83 * https://github.com/torvalds/linux/commit/bef3efbeb897b56867e271cdbc5f8adaacaeb9cd.
84 */
85 for (unsigned try = 0;; try++) {
86 n = read(fd, &a, sizeof(a));
87 if (n >= 0)
88 break;
84190644 89 log_debug_errno(errno, "Reading from \"%s\" failed: %m", p);
7229ec02
ZJS
90 if (errno != EINTR)
91 return -errno;
eee9b30a 92 if (try >= EFI_N_RETRIES_TOTAL)
7229ec02 93 return -EBUSY;
c75e7da0 94
eee9b30a
ZJS
95 if (try >= EFI_N_RETRIES_NO_DELAY)
96 (void) usleep(EFI_RETRY_DELAY);
7229ec02
ZJS
97 }
98
0bb2f0f1 99 if (n != sizeof(a))
84190644
LP
100 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
101 "Read %zi bytes from EFI variable %s, expected %zu.", n, p, sizeof(a));
0bb2f0f1
ZJS
102 }
103
104 if (ret_value) {
c75e7da0 105 buf = malloc(st.st_size - 4 + 3);
0bb2f0f1
ZJS
106 if (!buf)
107 return -ENOMEM;
108
109 n = read(fd, buf, (size_t) st.st_size - 4);
110 if (n < 0)
84190644 111 return log_debug_errno(errno, "Failed to read value of EFI variable %s: %m", p);
35b9eb0a 112 assert(n <= st.st_size - 4);
0bb2f0f1 113
e6f055cb
ZJS
114 /* Always NUL-terminate (3 bytes, to properly protect UTF-16, even if truncated in the middle
115 * of a character) */
861f1789
LP
116 ((char*) buf)[n] = 0;
117 ((char*) buf)[n + 1] = 0;
c75e7da0 118 ((char*) buf)[n + 2] = 0;
35b9eb0a
ZJS
119 } else
120 /* Assume that the reported size is accurate */
121 n = st.st_size - 4;
0bb2f0f1 122
698564d1 123 if (DEBUG_LOGGING) {
5291f26d 124 usec_t end = now(CLOCK_MONOTONIC);
698564d1 125 if (end > begin + EFI_RETRY_DELAY)
e6f055cb 126 log_debug("Detected slow EFI variable read access on %s: %s",
5291f26d 127 variable, FORMAT_TIMESPAN(end - begin, 1));
698564d1
LP
128 }
129
0bb2f0f1
ZJS
130 /* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
131 * with a smaller value. */
132
133 if (ret_attribute)
134 *ret_attribute = a;
135
136 if (ret_value)
137 *ret_value = TAKE_PTR(buf);
138
139 if (ret_size)
35b9eb0a 140 *ret_size = n;
0bb2f0f1
ZJS
141
142 return 0;
143}
144
187513fd 145int efi_get_variable_string(const char *variable, char **ret) {
0bb2f0f1
ZJS
146 _cleanup_free_ void *s = NULL;
147 size_t ss = 0;
148 int r;
149 char *x;
150
e6f055cb 151 r = efi_get_variable(variable, NULL, &s, &ss);
0bb2f0f1
ZJS
152 if (r < 0)
153 return r;
154
155 x = utf16_to_utf8(s, ss);
156 if (!x)
157 return -ENOMEM;
158
187513fd 159 *ret = x;
0bb2f0f1
ZJS
160 return 0;
161}
162
37e4637a
AH
163static int efi_verify_variable(const char *variable, uint32_t attr, const void *value, size_t size) {
164 _cleanup_free_ void *buf = NULL;
165 size_t n;
166 uint32_t a;
167 int r;
168
169 assert(variable);
170 assert(value || size == 0);
171
172 r = efi_get_variable(variable, &a, &buf, &n);
173 if (r < 0)
174 return r;
175
176 return a == attr && memcmp_nn(buf, n, value, size) == 0;
177}
178
e6f055cb 179int efi_set_variable(const char *variable, const void *value, size_t size) {
0bb2f0f1
ZJS
180 struct var {
181 uint32_t attr;
182 char buf[];
183 } _packed_ * _cleanup_free_ buf = NULL;
254d1313 184 _cleanup_close_ int fd = -EBADF;
37e4637a 185 uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
0bb2f0f1
ZJS
186 bool saved_flags_valid = false;
187 unsigned saved_flags;
188 int r;
189
e6f055cb 190 assert(variable);
0bb2f0f1
ZJS
191 assert(value || size == 0);
192
e6f055cb 193 const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
0bb2f0f1 194
37e4637a
AH
195 /* size 0 means removal, empty variable would not be enough for that */
196 if (size > 0 && efi_verify_variable(variable, attr, value, size) > 0) {
197 log_debug("Variable '%s' is already in wanted state, skipping write.", variable);
198 return 0;
199 }
200
6b000af4
LP
201 /* Newer efivarfs protects variables that are not in an allow list with FS_IMMUTABLE_FL by default,
202 * to protect them for accidental removal and modification. We are not changing these variables
203 * accidentally however, hence let's unset the bit first. */
0bb2f0f1
ZJS
204
205 r = chattr_path(p, 0, FS_IMMUTABLE_FL, &saved_flags);
206 if (r < 0 && r != -ENOENT)
207 log_debug_errno(r, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p);
208
209 saved_flags_valid = r >= 0;
210
211 if (size == 0) {
212 if (unlink(p) < 0) {
213 r = -errno;
214 goto finish;
215 }
216
217 return 0;
218 }
219
220 fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
221 if (fd < 0) {
222 r = -errno;
223 goto finish;
224 }
225
226 buf = malloc(sizeof(uint32_t) + size);
227 if (!buf) {
228 r = -ENOMEM;
229 goto finish;
230 }
231
37e4637a 232 buf->attr = attr;
0bb2f0f1
ZJS
233 memcpy(buf->buf, value, size);
234
235 r = loop_write(fd, buf, sizeof(uint32_t) + size, false);
236 if (r < 0)
237 goto finish;
238
d197c403
LP
239 /* For some reason efivarfs doesn't update mtime automatically. Let's do it manually then. This is
240 * useful for processes that cache EFI variables to detect when changes occurred. */
241 if (futimens(fd, (struct timespec[2]) {
242 { .tv_nsec = UTIME_NOW },
243 { .tv_nsec = UTIME_NOW }
244 }) < 0)
245 log_debug_errno(errno, "Failed to update mtime/atime on %s, ignoring: %m", p);
246
0bb2f0f1
ZJS
247 r = 0;
248
249finish:
250 if (saved_flags_valid) {
251 int q;
252
253 /* Restore the original flags field, just in case */
254 if (fd < 0)
255 q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL, NULL);
256 else
257 q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL, NULL);
258 if (q < 0)
259 log_debug_errno(q, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p);
260 }
261
262 return r;
263}
264
e6f055cb 265int efi_set_variable_string(const char *variable, const char *value) {
0bb2f0f1
ZJS
266 _cleanup_free_ char16_t *u16 = NULL;
267
e6f055cb 268 u16 = utf8_to_utf16(value, strlen(value));
0bb2f0f1
ZJS
269 if (!u16)
270 return -ENOMEM;
271
e6f055cb 272 return efi_set_variable(variable, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
0bb2f0f1
ZJS
273}
274
c7d26acc 275bool is_efi_boot(void) {
f46ba939 276 static int cache = -1;
c7d26acc 277
f46ba939
LP
278 if (cache < 0) {
279 if (detect_container() > 0)
280 cache = false;
5b4c1630 281 else {
f46ba939 282 cache = access("/sys/firmware/efi/", F_OK) >= 0;
5b4c1630
LP
283 if (!cache && errno != ENOENT)
284 log_debug_errno(errno, "Unable to test whether /sys/firmware/efi/ exists, assuming EFI not available: %m");
285 }
f46ba939
LP
286 }
287
288 return cache;
c7d26acc
AP
289}
290
e6f055cb 291static int read_flag(const char *variable) {
c7d26acc
AP
292 _cleanup_free_ void *v = NULL;
293 uint8_t b;
294 size_t s;
295 int r;
296
297 if (!is_efi_boot()) /* If this is not an EFI boot, assume the queried flags are zero */
298 return 0;
299
e6f055cb 300 r = efi_get_variable(variable, NULL, &v, &s);
c7d26acc
AP
301 if (r < 0)
302 return r;
303
304 if (s != 1)
305 return -EINVAL;
306
307 b = *(uint8_t *)v;
308 return !!b;
309}
310
311bool is_efi_secure_boot(void) {
f46ba939 312 static int cache = -1;
3e09ad57 313 int r;
f46ba939 314
3e09ad57
LP
315 if (cache < 0) {
316 r = read_flag(EFI_GLOBAL_VARIABLE(SecureBoot));
317 if (r == -ENOENT)
318 cache = false;
319 else if (r < 0)
320 log_debug_errno(r, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m");
321 else
322 cache = r;
323 }
f46ba939
LP
324
325 return cache > 0;
c7d26acc
AP
326}
327
c4964512
JJ
328SecureBootMode efi_get_secure_boot_mode(void) {
329 static SecureBootMode cache = _SECURE_BOOT_INVALID;
f46ba939 330
c4964512
JJ
331 if (cache != _SECURE_BOOT_INVALID)
332 return cache;
f46ba939 333
c4964512
JJ
334 int secure = read_flag(EFI_GLOBAL_VARIABLE(SecureBoot));
335 if (secure < 0) {
336 if (secure != -ENOENT)
bc5eb900
LP
337 log_debug_errno(secure, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m");
338
c4964512
JJ
339 return (cache = SECURE_BOOT_UNSUPPORTED);
340 }
341
342 /* We can assume false for all these if they are abscent (AuditMode and
343 * DeployedMode may not exist on older firmware). */
344 int audit = read_flag(EFI_GLOBAL_VARIABLE(AuditMode));
345 int deployed = read_flag(EFI_GLOBAL_VARIABLE(DeployedMode));
346 int setup = read_flag(EFI_GLOBAL_VARIABLE(SetupMode));
347 log_debug("Secure boot variables: SecureBoot=%d AuditMode=%d DeployedMode=%d SetupMode=%d",
348 secure, audit, deployed, setup);
349
350 return (cache = decode_secure_boot_mode(secure, audit > 0, deployed > 0, setup > 0));
c7d26acc
AP
351}
352
187513fd 353static int read_efi_options_variable(char **ret) {
53aa0d02
ZJS
354 int r;
355
484f4e5b 356 /* In SecureBoot mode this is probably not what you want. As your cmdline is cryptographically signed
db811444 357 * like when using Type #2 EFI Unified Kernel Images (https://uapi-group.org/specifications/specs/boot_loader_specification)
484f4e5b
LP
358 * The user's intention is then that the cmdline should not be modified. You want to make sure that
359 * the system starts up as exactly specified in the signed artifact.
360 *
209b2592
FB
361 * (NB: For testing purposes, we still check the $SYSTEMD_EFI_OPTIONS env var before accessing this
362 * cache, even when in SecureBoot mode.) */
484f4e5b 363 if (is_efi_secure_boot()) {
484f4e5b
LP
364 /* Let's be helpful with the returned error and check if the variable exists at all. If it
365 * does, let's return a recognizable error (EPERM), and if not ENODATA. */
366
e6f055cb 367 if (access(EFIVAR_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), F_OK) < 0)
a0fa2683 368 return errno == ENOENT ? -ENODATA : -errno;
484f4e5b
LP
369
370 return -EPERM;
371 }
372
187513fd 373 r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(SystemdOptions), ret);
53aa0d02
ZJS
374 if (r == -ENOENT)
375 return -ENODATA;
ad2d6880
ZJS
376 return r;
377}
378
379int cache_efi_options_variable(void) {
380 _cleanup_free_ char *line = NULL;
381 int r;
382
383 r = read_efi_options_variable(&line);
209b2592
FB
384 if (r < 0)
385 return r;
53aa0d02 386
e6f055cb
ZJS
387 return write_string_file(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), line,
388 WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755);
209b2592
FB
389}
390
187513fd 391int systemd_efi_options_variable(char **ret) {
209b2592 392 const char *e;
209b2592
FB
393 int r;
394
ad2d6880
ZJS
395 /* Returns the contents of the variable for current boot from the cache. */
396
187513fd 397 assert(ret);
209b2592
FB
398
399 /* For testing purposes it is sometimes useful to be able to override this */
400 e = secure_getenv("SYSTEMD_EFI_OPTIONS");
401 if (e) {
402 char *m;
403
404 m = strdup(e);
405 if (!m)
406 return -ENOMEM;
407
187513fd 408 *ret = m;
209b2592
FB
409 return 0;
410 }
411
187513fd 412 r = read_one_line_file(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), ret);
209b2592
FB
413 if (r == -ENOENT)
414 return -ENODATA;
53aa0d02
ZJS
415 return r;
416}
ad2d6880
ZJS
417
418static inline int compare_stat_mtime(const struct stat *a, const struct stat *b) {
419 return CMP(timespec_load(&a->st_mtim), timespec_load(&b->st_mtim));
420}
421
187513fd 422int systemd_efi_options_efivarfs_if_newer(char **ret) {
ad2d6880
ZJS
423 struct stat a = {}, b;
424 int r;
425
426 if (stat(EFIVAR_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), &a) < 0 && errno != ENOENT)
427 return log_debug_errno(errno, "Failed to stat EFI variable SystemdOptions: %m");
428
429 if (stat(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), &b) < 0) {
2f092762 430 if (errno != ENOENT)
ad2d6880
ZJS
431 log_debug_errno(errno, "Failed to stat "EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions))": %m");
432 } else if (compare_stat_mtime(&a, &b) > 0)
433 log_debug("Variable SystemdOptions in evifarfs is newer than in cache.");
434 else {
435 log_debug("Variable SystemdOptions in cache is up to date.");
187513fd 436 *ret = NULL;
ad2d6880
ZJS
437 return 0;
438 }
439
187513fd 440 r = read_efi_options_variable(ret);
ad2d6880 441 if (r < 0)
afd1a45a
LP
442 return log_debug_errno(r, "Failed to read SystemdOptions EFI variable: %m");
443
83fe0be1 444 return 0;
ad2d6880 445}
0bb2f0f1 446#endif