]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/basic/efivars.c
pcrlock: process components outside of location window properly
[thirdparty/systemd.git] / src / basic / efivars.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <fcntl.h>
4#include <stdlib.h>
5#include <sys/stat.h>
6#include <time.h>
7#include <unistd.h>
8
9#include "alloc-util.h"
10#include "chattr-util.h"
11#include "efivars.h"
12#include "fd-util.h"
13#include "io-util.h"
14#include "log.h"
15#include "memory-util.h"
16#include "string-util.h"
17#include "time-util.h"
18#include "utf8.h"
19#include "virt.h"
20
21#if ENABLE_EFI
22
23/* Reads from efivarfs sometimes fail with EINTR. Retry that many times. */
24#define EFI_N_RETRIES_NO_DELAY 20
25#define EFI_N_RETRIES_TOTAL 25
26#define EFI_RETRY_DELAY (50 * USEC_PER_MSEC)
27
28int efi_get_variable(
29 const char *variable,
30 uint32_t *ret_attribute,
31 void **ret_value,
32 size_t *ret_size) {
33
34 _cleanup_close_ int fd = -EBADF;
35 _cleanup_free_ void *buf = NULL;
36 struct stat st;
37 usec_t begin = 0; /* Unnecessary initialization to appease gcc */
38 uint32_t a;
39 ssize_t n;
40
41 assert(variable);
42
43 const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
44
45 if (!ret_value && !ret_size && !ret_attribute) {
46 /* If caller is not interested in anything, just check if the variable exists and is
47 * readable. */
48 if (access(p, R_OK) < 0)
49 return -errno;
50
51 return 0;
52 }
53
54 if (DEBUG_LOGGING) {
55 log_debug("Reading EFI variable %s.", p);
56 begin = now(CLOCK_MONOTONIC);
57 }
58
59 fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
60 if (fd < 0)
61 return log_debug_errno(errno, "open(\"%s\") failed: %m", p);
62
63 if (fstat(fd, &st) < 0)
64 return log_debug_errno(errno, "fstat(\"%s\") failed: %m", p);
65 if (st.st_size < 4)
66 return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), "EFI variable %s is shorter than 4 bytes, refusing.", p);
67 if (st.st_size > 4*1024*1024 + 4)
68 return log_debug_errno(SYNTHETIC_ERRNO(E2BIG), "EFI variable %s is ridiculously large, refusing.", p);
69
70 if (ret_value || ret_attribute) {
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;
83 log_debug_errno(errno, "Reading from \"%s\" failed: %m", p);
84 if (errno != EINTR)
85 return -errno;
86 if (try >= EFI_N_RETRIES_TOTAL)
87 return -EBUSY;
88
89 if (try >= EFI_N_RETRIES_NO_DELAY)
90 (void) usleep_safe(EFI_RETRY_DELAY);
91 }
92
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
109 if (n != sizeof(a))
110 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
111 "Read %zi bytes from EFI variable %s, expected %zu.", n, p, sizeof(a));
112 }
113
114 if (ret_value) {
115 buf = malloc(st.st_size - 4 + 3);
116 if (!buf)
117 return -ENOMEM;
118
119 n = read(fd, buf, (size_t) st.st_size - 4);
120 if (n < 0)
121 return log_debug_errno(errno, "Failed to read value of EFI variable %s: %m", p);
122 assert(n <= st.st_size - 4);
123
124 /* Always NUL-terminate (3 bytes, to properly protect UTF-16, even if truncated in the middle
125 * of a character) */
126 ((char*) buf)[n] = 0;
127 ((char*) buf)[n + 1] = 0;
128 ((char*) buf)[n + 2] = 0;
129 } else
130 /* Assume that the reported size is accurate */
131 n = st.st_size - 4;
132
133 if (DEBUG_LOGGING) {
134 usec_t end = now(CLOCK_MONOTONIC);
135 if (end > begin + EFI_RETRY_DELAY)
136 log_debug("Detected slow EFI variable read access on %s: %s",
137 variable, FORMAT_TIMESPAN(end - begin, 1));
138 }
139
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)
150 *ret_size = n;
151
152 return 0;
153}
154
155int efi_get_variable_string(const char *variable, char **ret) {
156 _cleanup_free_ void *s = NULL;
157 size_t ss = 0;
158 char *x;
159 int r;
160
161 assert(variable);
162
163 r = efi_get_variable(variable, NULL, &s, &ss);
164 if (r < 0)
165 return r;
166
167 x = utf16_to_utf8(s, ss);
168 if (!x)
169 return -ENOMEM;
170
171 if (ret)
172 *ret = x;
173
174 return 0;
175}
176
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
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
208int efi_set_variable(const char *variable, const void *value, size_t size) {
209 static const uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
210
211 struct var {
212 uint32_t attr;
213 char buf[];
214 } _packed_ * _cleanup_free_ buf = NULL;
215 _cleanup_close_ int fd = -EBADF;
216 bool saved_flags_valid = false;
217 unsigned saved_flags;
218 int r;
219
220 assert(variable);
221 assert(value || size == 0);
222
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
229 const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
230
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. */
234
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);
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
267 buf->attr = attr;
268 memcpy(buf->buf, value, size);
269
270 r = loop_write(fd, buf, sizeof(uint32_t) + size);
271 if (r < 0)
272 goto finish;
273
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. */
276 if (futimens(fd, /* times = */ NULL) < 0)
277 log_debug_errno(errno, "Failed to update mtime/atime on %s, ignoring: %m", p);
278
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)
287 q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL);
288 else
289 q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL);
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
297int efi_set_variable_string(const char *variable, const char *value) {
298 _cleanup_free_ char16_t *u16 = NULL;
299
300 u16 = utf8_to_utf16(value, SIZE_MAX);
301 if (!u16)
302 return -ENOMEM;
303
304 return efi_set_variable(variable, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
305}
306
307bool is_efi_boot(void) {
308 static int cache = -1;
309
310 if (cache < 0) {
311 if (detect_container() > 0)
312 cache = false;
313 else {
314 cache = access("/sys/firmware/efi/", F_OK) >= 0;
315 if (!cache && errno != ENOENT)
316 log_debug_errno(errno, "Unable to test whether /sys/firmware/efi/ exists, assuming EFI not available: %m");
317 }
318 }
319
320 return cache;
321}
322
323static int read_flag(const char *variable) {
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
332 r = efi_get_variable(variable, NULL, &v, &s);
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) {
344 static int cache = -1;
345 int r;
346
347 if (cache < 0) {
348 r = read_flag(EFI_GLOBAL_VARIABLE_STR("SecureBoot"));
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 }
356
357 return cache > 0;
358}
359
360SecureBootMode efi_get_secure_boot_mode(void) {
361 static SecureBootMode cache = _SECURE_BOOT_INVALID;
362
363 if (cache != _SECURE_BOOT_INVALID)
364 return cache;
365
366 int secure = read_flag(EFI_GLOBAL_VARIABLE_STR("SecureBoot"));
367 if (secure < 0) {
368 if (secure != -ENOENT)
369 log_debug_errno(secure, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m");
370
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). */
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"));
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));
383}
384#endif
385
386char *efi_tilt_backslashes(char *s) {
387 return string_replace_char(s, '\\', '/');
388}