1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include "alloc-util.h"
4 #include "efi-loader.h"
5 #include "parse-util.h"
6 #include "path-util.h"
7 #include "stat-util.h"
8 #include "strv.h"
9 #include "utf8.h"
10 
11 #if ENABLE_EFI
12 
read_usec(const char * variable,usec_t * ret)13 static int read_usec(const char *variable, usec_t *ret) {
14         _cleanup_free_ char *j = NULL;
15         uint64_t x = 0;
16         int r;
17 
18         assert(variable);
19         assert(ret);
20 
21         r = efi_get_variable_string(variable, &j);
22         if (r < 0)
23                 return r;
24 
25         r = safe_atou64(j, &x);
26         if (r < 0)
27                 return r;
28 
29         *ret = x;
30         return 0;
31 }
32 
efi_loader_get_boot_usec(usec_t * ret_firmware,usec_t * ret_loader)33 int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader) {
34         uint64_t x, y;
35         int r;
36 
37         assert(ret_firmware);
38         assert(ret_loader);
39 
40         if (!is_efi_boot())
41                 return -EOPNOTSUPP;
42 
43         r = read_usec(EFI_LOADER_VARIABLE(LoaderTimeInitUSec), &x);
44         if (r < 0)
45                 return log_debug_errno(r, "Failed to read LoaderTimeInitUSec: %m");
46 
47         r = read_usec(EFI_LOADER_VARIABLE(LoaderTimeExecUSec), &y);
48         if (r < 0)
49                 return log_debug_errno(r, "Failed to read LoaderTimeExecUSec: %m");
50 
51         if (y == 0 || y < x || y - x > USEC_PER_HOUR)
52                 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
53                                        "Bad LoaderTimeInitUSec=%"PRIu64", LoaderTimeExecUSec=%" PRIu64"; refusing.",
54                                        x, y);
55 
56         *ret_firmware = x;
57         *ret_loader = y;
58         return 0;
59 }
60 
efi_loader_get_device_part_uuid(sd_id128_t * ret)61 int efi_loader_get_device_part_uuid(sd_id128_t *ret) {
62         _cleanup_free_ char *p = NULL;
63         int r, parsed[16];
64 
65         if (!is_efi_boot())
66                 return -EOPNOTSUPP;
67 
68         r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderDevicePartUUID), &p);
69         if (r < 0)
70                 return r;
71 
72         if (sscanf(p, SD_ID128_UUID_FORMAT_STR,
73                    &parsed[0], &parsed[1], &parsed[2], &parsed[3],
74                    &parsed[4], &parsed[5], &parsed[6], &parsed[7],
75                    &parsed[8], &parsed[9], &parsed[10], &parsed[11],
76                    &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16)
77                 return -EIO;
78 
79         if (ret)
80                 for (unsigned i = 0; i < ELEMENTSOF(parsed); i++)
81                         ret->bytes[i] = parsed[i];
82 
83         return 0;
84 }
85 
efi_loader_get_entries(char *** ret)86 int efi_loader_get_entries(char ***ret) {
87         _cleanup_free_ char16_t *entries = NULL;
88         _cleanup_strv_free_ char **l = NULL;
89         size_t size;
90         int r;
91 
92         assert(ret);
93 
94         if (!is_efi_boot())
95                 return -EOPNOTSUPP;
96 
97         r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntries), NULL, (void**) &entries, &size);
98         if (r < 0)
99                 return r;
100 
101         /* The variable contains a series of individually NUL terminated UTF-16 strings. */
102 
103         for (size_t i = 0, start = 0;; i++) {
104                 _cleanup_free_ char *decoded = NULL;
105                 bool end;
106 
107                 /* Is this the end of the variable's data? */
108                 end = i * sizeof(char16_t) >= size;
109 
110                 /* Are we in the middle of a string? (i.e. not at the end of the variable, nor at a NUL terminator?) If
111                  * so, let's go to the next entry. */
112                 if (!end && entries[i] != 0)
113                         continue;
114 
115                 /* We reached the end of a string, let's decode it into UTF-8 */
116                 decoded = utf16_to_utf8(entries + start, (i - start) * sizeof(char16_t));
117                 if (!decoded)
118                         return -ENOMEM;
119 
120                 if (efi_loader_entry_name_valid(decoded)) {
121                         r = strv_consume(&l, TAKE_PTR(decoded));
122                         if (r < 0)
123                                 return r;
124                 } else
125                         log_debug("Ignoring invalid loader entry '%s'.", decoded);
126 
127                 /* We reached the end of the variable */
128                 if (end)
129                         break;
130 
131                 /* Continue after the NUL byte */
132                 start = i + 1;
133         }
134 
135         *ret = TAKE_PTR(l);
136         return 0;
137 }
138 
efi_loader_get_features(uint64_t * ret)139 int efi_loader_get_features(uint64_t *ret) {
140         _cleanup_free_ void *v = NULL;
141         size_t s;
142         int r;
143 
144         assert(ret);
145 
146         if (!is_efi_boot()) {
147                 *ret = 0;
148                 return 0;
149         }
150 
151         r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderFeatures), NULL, &v, &s);
152         if (r == -ENOENT) {
153                 _cleanup_free_ char *info = NULL;
154 
155                 /* The new (v240+) LoaderFeatures variable is not supported, let's see if it's systemd-boot at all */
156                 r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderInfo), &info);
157                 if (r < 0) {
158                         if (r != -ENOENT)
159                                 return r;
160 
161                         /* Variable not set, definitely means not systemd-boot */
162 
163                 } else if (first_word(info, "systemd-boot")) {
164 
165                         /* An older systemd-boot version. Let's hardcode the feature set, since it was pretty
166                          * static in all its versions. */
167 
168                         *ret = EFI_LOADER_FEATURE_CONFIG_TIMEOUT |
169                                 EFI_LOADER_FEATURE_ENTRY_DEFAULT |
170                                 EFI_LOADER_FEATURE_ENTRY_ONESHOT;
171 
172                         return 0;
173                 }
174 
175                 /* No features supported */
176                 *ret = 0;
177                 return 0;
178         }
179         if (r < 0)
180                 return r;
181 
182         if (s != sizeof(uint64_t))
183                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
184                                        "LoaderFeatures EFI variable doesn't have the right size.");
185 
186         memcpy(ret, v, sizeof(uint64_t));
187         return 0;
188 }
189 
efi_loader_get_config_timeout_one_shot(usec_t * ret)190 int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
191         _cleanup_free_ char *v = NULL;
192         static struct stat cache_stat = {};
193         struct stat new_stat;
194         static usec_t cache;
195         uint64_t sec;
196         int r;
197 
198         assert(ret);
199 
200         /* stat() the EFI variable, to see if the mtime changed. If it did, we need to cache again. */
201         if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot)), &new_stat) < 0)
202                 return -errno;
203 
204         if (stat_inode_unmodified(&new_stat, &cache_stat)) {
205                 *ret = cache;
206                 return 0;
207         }
208 
209         r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot), &v);
210         if (r < 0)
211                 return r;
212 
213         r = safe_atou64(v, &sec);
214         if (r < 0)
215                 return r;
216         if (sec > USEC_INFINITY / USEC_PER_SEC)
217                 return -ERANGE;
218 
219         cache_stat = new_stat;
220         *ret = cache = sec * USEC_PER_SEC; /* return in µs */
221         return 0;
222 }
223 
efi_loader_update_entry_one_shot_cache(char ** cache,struct stat * cache_stat)224 int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat) {
225         _cleanup_free_ char *v = NULL;
226         struct stat new_stat;
227         int r;
228 
229         assert(cache);
230         assert(cache_stat);
231 
232         /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
233         if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderEntryOneShot)), &new_stat) < 0)
234                 return -errno;
235 
236         if (stat_inode_unmodified(&new_stat, cache_stat))
237                 return 0;
238 
239         r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryOneShot), &v);
240         if (r < 0)
241                 return r;
242 
243         if (!efi_loader_entry_name_valid(v))
244                 return -EINVAL;
245 
246         *cache_stat = new_stat;
247         free_and_replace(*cache, v);
248 
249         return 0;
250 }
251 
252 #endif
253 
efi_loader_entry_name_valid(const char * s)254 bool efi_loader_entry_name_valid(const char *s) {
255         if (!filename_is_valid(s)) /* Make sure entry names fit in filenames */
256                 return false;
257 
258         return in_charset(s, ALPHANUMERICAL "+-_.");
259 }
260