]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/efivars.c
tree-wide: drop string.h when string-util.h or friends are included
[thirdparty/systemd.git] / src / basic / efivars.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <limits.h>
6 #include <linux/fs.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11
12 #include "sd-id128.h"
13
14 #include "alloc-util.h"
15 #include "chattr-util.h"
16 #include "efivars.h"
17 #include "fd-util.h"
18 #include "io-util.h"
19 #include "macro.h"
20 #include "stdio-util.h"
21 #include "strv.h"
22 #include "time-util.h"
23 #include "utf8.h"
24
25 #if ENABLE_EFI
26
27 char* efi_variable_path(sd_id128_t vendor, const char *name) {
28 char *p;
29
30 if (asprintf(&p,
31 "/sys/firmware/efi/efivars/%s-" SD_ID128_UUID_FORMAT_STR,
32 name, SD_ID128_FORMAT_VAL(vendor)) < 0)
33 return NULL;
34
35 return p;
36 }
37
38 int efi_get_variable(
39 sd_id128_t vendor,
40 const char *name,
41 uint32_t *ret_attribute,
42 void **ret_value,
43 size_t *ret_size) {
44
45 _cleanup_close_ int fd = -1;
46 _cleanup_free_ char *p = NULL;
47 _cleanup_free_ void *buf = NULL;
48 struct stat st;
49 uint32_t a;
50 ssize_t n;
51
52 assert(name);
53
54 p = efi_variable_path(vendor, name);
55 if (!p)
56 return -ENOMEM;
57
58 if (!ret_value && !ret_size && !ret_attribute) {
59 /* If caller is not interested in anything, just check if the variable exists and is readable
60 * to us. */
61 if (access(p, R_OK) < 0)
62 return -errno;
63
64 return 0;
65 }
66
67 fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
68 if (fd < 0)
69 return -errno;
70
71 if (fstat(fd, &st) < 0)
72 return -errno;
73 if (st.st_size < 4)
74 return -ENODATA;
75 if (st.st_size > 4*1024*1024 + 4)
76 return -E2BIG;
77
78 if (ret_value || ret_attribute) {
79 n = read(fd, &a, sizeof(a));
80 if (n < 0)
81 return -errno;
82 if (n != sizeof(a))
83 return -EIO;
84 }
85
86 if (ret_value) {
87 buf = malloc(st.st_size - 4 + 2);
88 if (!buf)
89 return -ENOMEM;
90
91 n = read(fd, buf, (size_t) st.st_size - 4);
92 if (n < 0)
93 return -errno;
94 if (n != st.st_size - 4)
95 return -EIO;
96
97 /* Always NUL terminate (2 bytes, to protect UTF-16) */
98 ((char*) buf)[st.st_size - 4] = 0;
99 ((char*) buf)[st.st_size - 4 + 1] = 0;
100 }
101
102 /* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
103 * with a smaller value. */
104
105 if (ret_attribute)
106 *ret_attribute = a;
107
108 if (ret_value)
109 *ret_value = TAKE_PTR(buf);
110
111 if (ret_size)
112 *ret_size = (size_t) st.st_size - 4;
113
114 return 0;
115 }
116
117 int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
118 _cleanup_free_ void *s = NULL;
119 size_t ss = 0;
120 int r;
121 char *x;
122
123 r = efi_get_variable(vendor, name, NULL, &s, &ss);
124 if (r < 0)
125 return r;
126
127 x = utf16_to_utf8(s, ss);
128 if (!x)
129 return -ENOMEM;
130
131 *p = x;
132 return 0;
133 }
134
135 int efi_set_variable(
136 sd_id128_t vendor,
137 const char *name,
138 const void *value,
139 size_t size) {
140
141 struct var {
142 uint32_t attr;
143 char buf[];
144 } _packed_ * _cleanup_free_ buf = NULL;
145 _cleanup_free_ char *p = NULL;
146 _cleanup_close_ int fd = -1;
147 bool saved_flags_valid = false;
148 unsigned saved_flags;
149 int r;
150
151 assert(name);
152 assert(value || size == 0);
153
154 p = efi_variable_path(vendor, name);
155 if (!p)
156 return -ENOMEM;
157
158 /* Newer efivarfs protects variables that are not in a whitelist with FS_IMMUTABLE_FL by default, to protect
159 * them for accidental removal and modification. We are not changing these variables accidentally however,
160 * hence let's unset the bit first. */
161
162 r = chattr_path(p, 0, FS_IMMUTABLE_FL, &saved_flags);
163 if (r < 0 && r != -ENOENT)
164 log_debug_errno(r, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p);
165
166 saved_flags_valid = r >= 0;
167
168 if (size == 0) {
169 if (unlink(p) < 0) {
170 r = -errno;
171 goto finish;
172 }
173
174 return 0;
175 }
176
177 fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
178 if (fd < 0) {
179 r = -errno;
180 goto finish;
181 }
182
183 buf = malloc(sizeof(uint32_t) + size);
184 if (!buf) {
185 r = -ENOMEM;
186 goto finish;
187 }
188
189 buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
190 memcpy(buf->buf, value, size);
191
192 r = loop_write(fd, buf, sizeof(uint32_t) + size, false);
193 if (r < 0)
194 goto finish;
195
196 r = 0;
197
198 finish:
199 if (saved_flags_valid) {
200 int q;
201
202 /* Restore the original flags field, just in case */
203 if (fd < 0)
204 q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL, NULL);
205 else
206 q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL, NULL);
207 if (q < 0)
208 log_debug_errno(q, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p);
209 }
210
211 return r;
212 }
213
214 int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *v) {
215 _cleanup_free_ char16_t *u16 = NULL;
216
217 u16 = utf8_to_utf16(v, strlen(v));
218 if (!u16)
219 return -ENOMEM;
220
221 return efi_set_variable(vendor, name, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
222 }
223
224 int efi_systemd_options_variable(char **line) {
225 const char *e;
226 int r;
227
228 assert(line);
229
230 /* For testing purposes it is sometimes useful to be able to override this */
231 e = secure_getenv("SYSTEMD_EFI_OPTIONS");
232 if (e) {
233 char *m;
234
235 m = strdup(e);
236 if (!m)
237 return -ENOMEM;
238
239 *line = m;
240 return 0;
241 }
242
243 r = efi_get_variable_string(EFI_VENDOR_SYSTEMD, "SystemdOptions", line);
244 if (r == -ENOENT)
245 return -ENODATA;
246
247 return r;
248 }
249 #endif