]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/efivars.c
tree-wide: Add more socket units (#37991)
[thirdparty/systemd.git] / src / basic / efivars.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
0bb2f0f1 2
0bb2f0f1 3#include <fcntl.h>
0bb2f0f1 4#include <stdlib.h>
0bb2f0f1 5#include <sys/stat.h>
0c15577a 6#include <time.h>
0bb2f0f1
ZJS
7#include <unistd.h>
8
0bb2f0f1
ZJS
9#include "alloc-util.h"
10#include "chattr-util.h"
11#include "efivars.h"
12#include "fd-util.h"
13#include "io-util.h"
93a1f792 14#include "log.h"
37e4637a 15#include "memory-util.h"
0c15577a 16#include "string-util.h"
0bb2f0f1
ZJS
17#include "time-util.h"
18#include "utf8.h"
c7d26acc 19#include "virt.h"
0bb2f0f1
ZJS
20
21#if ENABLE_EFI
22
7229ec02 23/* Reads from efivarfs sometimes fail with EINTR. Retry that many times. */
eee9b30a
ZJS
24#define EFI_N_RETRIES_NO_DELAY 20
25#define EFI_N_RETRIES_TOTAL 25
7229ec02
ZJS
26#define EFI_RETRY_DELAY (50 * USEC_PER_MSEC)
27
0bb2f0f1 28int efi_get_variable(
e6f055cb 29 const char *variable,
0bb2f0f1
ZJS
30 uint32_t *ret_attribute,
31 void **ret_value,
32 size_t *ret_size) {
33
254d1313 34 _cleanup_close_ int fd = -EBADF;
0bb2f0f1
ZJS
35 _cleanup_free_ void *buf = NULL;
36 struct stat st;
aff81b18 37 usec_t begin = 0; /* Unnecessary initialization to appease gcc */
0bb2f0f1
ZJS
38 uint32_t a;
39 ssize_t n;
40
e6f055cb 41 assert(variable);
0bb2f0f1 42
e6f055cb 43 const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
0bb2f0f1
ZJS
44
45 if (!ret_value && !ret_size && !ret_attribute) {
7229ec02
ZJS
46 /* If caller is not interested in anything, just check if the variable exists and is
47 * readable. */
0bb2f0f1
ZJS
48 if (access(p, R_OK) < 0)
49 return -errno;
50
51 return 0;
52 }
53
84190644
LP
54 if (DEBUG_LOGGING) {
55 log_debug("Reading EFI variable %s.", p);
698564d1 56 begin = now(CLOCK_MONOTONIC);
84190644 57 }
698564d1 58
0bb2f0f1
ZJS
59 fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
60 if (fd < 0)
7229ec02 61 return log_debug_errno(errno, "open(\"%s\") failed: %m", p);
0bb2f0f1
ZJS
62
63 if (fstat(fd, &st) < 0)
84190644 64 return log_debug_errno(errno, "fstat(\"%s\") failed: %m", p);
0bb2f0f1 65 if (st.st_size < 4)
84190644 66 return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), "EFI variable %s is shorter than 4 bytes, refusing.", p);
0bb2f0f1 67 if (st.st_size > 4*1024*1024 + 4)
84190644 68 return log_debug_errno(SYNTHETIC_ERRNO(E2BIG), "EFI variable %s is ridiculously large, refusing.", p);
0bb2f0f1
ZJS
69
70 if (ret_value || ret_attribute) {
7229ec02
ZJS
71 /* The kernel ratelimits reads from the efivarfs because EFI is inefficient, and we'll
72 * occasionally fail with EINTR here. A slowdown is better than a failure for us, so
73 * retry a few times and eventually fail with -EBUSY.
74 *
75 * See https://github.com/torvalds/linux/blob/master/fs/efivarfs/file.c#L75
76 * and
77 * https://github.com/torvalds/linux/commit/bef3efbeb897b56867e271cdbc5f8adaacaeb9cd.
78 */
79 for (unsigned try = 0;; try++) {
80 n = read(fd, &a, sizeof(a));
81 if (n >= 0)
82 break;
84190644 83 log_debug_errno(errno, "Reading from \"%s\" failed: %m", p);
7229ec02
ZJS
84 if (errno != EINTR)
85 return -errno;
eee9b30a 86 if (try >= EFI_N_RETRIES_TOTAL)
7229ec02 87 return -EBUSY;
c75e7da0 88
eee9b30a 89 if (try >= EFI_N_RETRIES_NO_DELAY)
4251512e 90 (void) usleep_safe(EFI_RETRY_DELAY);
7229ec02
ZJS
91 }
92
6013dee9 93 /* Unfortunately kernel reports EOF if there's an inconsistency between efivarfs var list
94 * and what's actually stored in firmware, c.f. #34304. A zero size env var is not allowed in
95 * efi and hence the variable doesn't really exist in the backing store as long as it is zero
96 * sized, and the kernel calls this "uncommitted". Hence we translate EOF back to ENOENT here,
97 * as with kernel behavior before
98 * https://github.com/torvalds/linux/commit/3fab70c165795431f00ddf9be8b84ddd07bd1f8f
99 *
100 * If the kernel changes behaviour (to flush dentries on resume), we can drop
101 * this at some point in the future. But note that the commit is 11
102 * years old at this point so we'll need to deal with the current behaviour for
103 * a long time.
104 */
105 if (n == 0)
106 return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
107 "EFI variable %s is uncommitted", p);
108
0bb2f0f1 109 if (n != sizeof(a))
84190644
LP
110 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
111 "Read %zi bytes from EFI variable %s, expected %zu.", n, p, sizeof(a));
0bb2f0f1
ZJS
112 }
113
114 if (ret_value) {
c75e7da0 115 buf = malloc(st.st_size - 4 + 3);
0bb2f0f1
ZJS
116 if (!buf)
117 return -ENOMEM;
118
119 n = read(fd, buf, (size_t) st.st_size - 4);
120 if (n < 0)
84190644 121 return log_debug_errno(errno, "Failed to read value of EFI variable %s: %m", p);
35b9eb0a 122 assert(n <= st.st_size - 4);
0bb2f0f1 123
e6f055cb
ZJS
124 /* Always NUL-terminate (3 bytes, to properly protect UTF-16, even if truncated in the middle
125 * of a character) */
861f1789
LP
126 ((char*) buf)[n] = 0;
127 ((char*) buf)[n + 1] = 0;
c75e7da0 128 ((char*) buf)[n + 2] = 0;
35b9eb0a
ZJS
129 } else
130 /* Assume that the reported size is accurate */
131 n = st.st_size - 4;
0bb2f0f1 132
698564d1 133 if (DEBUG_LOGGING) {
5291f26d 134 usec_t end = now(CLOCK_MONOTONIC);
698564d1 135 if (end > begin + EFI_RETRY_DELAY)
e6f055cb 136 log_debug("Detected slow EFI variable read access on %s: %s",
5291f26d 137 variable, FORMAT_TIMESPAN(end - begin, 1));
698564d1
LP
138 }
139
0bb2f0f1
ZJS
140 /* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
141 * with a smaller value. */
142
143 if (ret_attribute)
144 *ret_attribute = a;
145
146 if (ret_value)
147 *ret_value = TAKE_PTR(buf);
148
149 if (ret_size)
35b9eb0a 150 *ret_size = n;
0bb2f0f1
ZJS
151
152 return 0;
153}
154
187513fd 155int efi_get_variable_string(const char *variable, char **ret) {
0bb2f0f1
ZJS
156 _cleanup_free_ void *s = NULL;
157 size_t ss = 0;
0bb2f0f1 158 char *x;
c8d60ae7
LP
159 int r;
160
161 assert(variable);
0bb2f0f1 162
e6f055cb 163 r = efi_get_variable(variable, NULL, &s, &ss);
0bb2f0f1
ZJS
164 if (r < 0)
165 return r;
166
167 x = utf16_to_utf8(s, ss);
168 if (!x)
169 return -ENOMEM;
170
c8d60ae7
LP
171 if (ret)
172 *ret = x;
173
0bb2f0f1
ZJS
174 return 0;
175}
176
c8d60ae7
LP
177int efi_get_variable_path(const char *variable, char **ret) {
178 int r;
179
180 assert(variable);
181
182 r = efi_get_variable_string(variable, ret);
183 if (r < 0)
184 return r;
185
186 if (ret)
187 efi_tilt_backslashes(*ret);
188
189 return r;
190}
191
37e4637a
AH
192static int efi_verify_variable(const char *variable, uint32_t attr, const void *value, size_t size) {
193 _cleanup_free_ void *buf = NULL;
194 size_t n;
195 uint32_t a;
196 int r;
197
198 assert(variable);
199 assert(value || size == 0);
200
201 r = efi_get_variable(variable, &a, &buf, &n);
202 if (r < 0)
203 return r;
204
205 return a == attr && memcmp_nn(buf, n, value, size) == 0;
206}
207
e6f055cb 208int efi_set_variable(const char *variable, const void *value, size_t size) {
80d1d9f5
MY
209 static const uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
210
0bb2f0f1
ZJS
211 struct var {
212 uint32_t attr;
213 char buf[];
214 } _packed_ * _cleanup_free_ buf = NULL;
254d1313 215 _cleanup_close_ int fd = -EBADF;
0bb2f0f1
ZJS
216 bool saved_flags_valid = false;
217 unsigned saved_flags;
218 int r;
219
e6f055cb 220 assert(variable);
0bb2f0f1
ZJS
221 assert(value || size == 0);
222
37e4637a
AH
223 /* size 0 means removal, empty variable would not be enough for that */
224 if (size > 0 && efi_verify_variable(variable, attr, value, size) > 0) {
225 log_debug("Variable '%s' is already in wanted state, skipping write.", variable);
226 return 0;
227 }
228
80d1d9f5
MY
229 const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
230
6b000af4
LP
231 /* Newer efivarfs protects variables that are not in an allow list with FS_IMMUTABLE_FL by default,
232 * to protect them for accidental removal and modification. We are not changing these variables
233 * accidentally however, hence let's unset the bit first. */
0bb2f0f1 234
a997f338
YW
235 r = chattr_full(AT_FDCWD, p,
236 /* value = */ 0,
237 /* mask = */ FS_IMMUTABLE_FL,
238 /* ret_previous = */ &saved_flags,
239 /* ret_final = */ NULL,
240 /* flags = */ 0);
0bb2f0f1
ZJS
241 if (r < 0 && r != -ENOENT)
242 log_debug_errno(r, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p);
243
244 saved_flags_valid = r >= 0;
245
246 if (size == 0) {
247 if (unlink(p) < 0) {
248 r = -errno;
249 goto finish;
250 }
251
252 return 0;
253 }
254
255 fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
256 if (fd < 0) {
257 r = -errno;
258 goto finish;
259 }
260
261 buf = malloc(sizeof(uint32_t) + size);
262 if (!buf) {
263 r = -ENOMEM;
264 goto finish;
265 }
266
37e4637a 267 buf->attr = attr;
0bb2f0f1
ZJS
268 memcpy(buf->buf, value, size);
269
e22c60a9 270 r = loop_write(fd, buf, sizeof(uint32_t) + size);
0bb2f0f1
ZJS
271 if (r < 0)
272 goto finish;
273
d197c403
LP
274 /* For some reason efivarfs doesn't update mtime automatically. Let's do it manually then. This is
275 * useful for processes that cache EFI variables to detect when changes occurred. */
cce82462 276 if (futimens(fd, /* times = */ NULL) < 0)
d197c403
LP
277 log_debug_errno(errno, "Failed to update mtime/atime on %s, ignoring: %m", p);
278
0bb2f0f1
ZJS
279 r = 0;
280
281finish:
282 if (saved_flags_valid) {
283 int q;
284
285 /* Restore the original flags field, just in case */
286 if (fd < 0)
a997f338 287 q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL);
0bb2f0f1 288 else
a997f338 289 q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL);
0bb2f0f1
ZJS
290 if (q < 0)
291 log_debug_errno(q, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p);
292 }
293
294 return r;
295}
296
e6f055cb 297int efi_set_variable_string(const char *variable, const char *value) {
0bb2f0f1
ZJS
298 _cleanup_free_ char16_t *u16 = NULL;
299
ba091282 300 u16 = utf8_to_utf16(value, SIZE_MAX);
0bb2f0f1
ZJS
301 if (!u16)
302 return -ENOMEM;
303
e6f055cb 304 return efi_set_variable(variable, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
0bb2f0f1
ZJS
305}
306
c7d26acc 307bool is_efi_boot(void) {
f46ba939 308 static int cache = -1;
c7d26acc 309
f46ba939
LP
310 if (cache < 0) {
311 if (detect_container() > 0)
312 cache = false;
5b4c1630 313 else {
f46ba939 314 cache = access("/sys/firmware/efi/", F_OK) >= 0;
5b4c1630
LP
315 if (!cache && errno != ENOENT)
316 log_debug_errno(errno, "Unable to test whether /sys/firmware/efi/ exists, assuming EFI not available: %m");
317 }
f46ba939
LP
318 }
319
320 return cache;
c7d26acc
AP
321}
322
e6f055cb 323static int read_flag(const char *variable) {
c7d26acc
AP
324 _cleanup_free_ void *v = NULL;
325 uint8_t b;
326 size_t s;
327 int r;
328
329 if (!is_efi_boot()) /* If this is not an EFI boot, assume the queried flags are zero */
330 return 0;
331
e6f055cb 332 r = efi_get_variable(variable, NULL, &v, &s);
c7d26acc
AP
333 if (r < 0)
334 return r;
335
336 if (s != 1)
337 return -EINVAL;
338
339 b = *(uint8_t *)v;
340 return !!b;
341}
342
343bool is_efi_secure_boot(void) {
f46ba939 344 static int cache = -1;
3e09ad57 345 int r;
f46ba939 346
3e09ad57 347 if (cache < 0) {
d5c12da9 348 r = read_flag(EFI_GLOBAL_VARIABLE_STR("SecureBoot"));
3e09ad57
LP
349 if (r == -ENOENT)
350 cache = false;
351 else if (r < 0)
352 log_debug_errno(r, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m");
353 else
354 cache = r;
355 }
f46ba939
LP
356
357 return cache > 0;
c7d26acc
AP
358}
359
c4964512
JJ
360SecureBootMode efi_get_secure_boot_mode(void) {
361 static SecureBootMode cache = _SECURE_BOOT_INVALID;
f46ba939 362
c4964512
JJ
363 if (cache != _SECURE_BOOT_INVALID)
364 return cache;
f46ba939 365
d5c12da9 366 int secure = read_flag(EFI_GLOBAL_VARIABLE_STR("SecureBoot"));
c4964512
JJ
367 if (secure < 0) {
368 if (secure != -ENOENT)
bc5eb900
LP
369 log_debug_errno(secure, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m");
370
c4964512
JJ
371 return (cache = SECURE_BOOT_UNSUPPORTED);
372 }
373
374 /* We can assume false for all these if they are abscent (AuditMode and
375 * DeployedMode may not exist on older firmware). */
d5c12da9
DDM
376 int audit = read_flag(EFI_GLOBAL_VARIABLE_STR("AuditMode"));
377 int deployed = read_flag(EFI_GLOBAL_VARIABLE_STR("DeployedMode"));
378 int setup = read_flag(EFI_GLOBAL_VARIABLE_STR("SetupMode"));
c4964512
JJ
379 log_debug("Secure boot variables: SecureBoot=%d AuditMode=%d DeployedMode=%d SetupMode=%d",
380 secure, audit, deployed, setup);
381
382 return (cache = decode_secure_boot_mode(secure, audit > 0, deployed > 0, setup > 0));
c7d26acc 383}
0bb2f0f1 384#endif
0c15577a
DDM
385
386char *efi_tilt_backslashes(char *s) {
387 return string_replace_char(s, '\\', '/');
388}