chiark / gitweb /
efi: never call qsort on potentially NULL arrays
[elogind.git] / src / shared / efivars.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <unistd.h>
23 #include <string.h>
24 #include <fcntl.h>
25 #include <ctype.h>
26
27 #include "acpi-fpdt.h"
28 #include "util.h"
29 #include "utf8.h"
30 #include "efivars.h"
31
32 #ifdef ENABLE_EFI
33
34 bool is_efi_boot(void) {
35         return access("/sys/firmware/efi", F_OK) >= 0;
36 }
37
38 static int read_flag(const char *varname) {
39         int r;
40         void *v;
41         size_t s;
42         uint8_t b;
43
44         r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, NULL, &v, &s);
45         if (r < 0)
46                 return r;
47
48         if (s != 1) {
49                 r = -EINVAL;
50                 goto finish;
51         }
52
53         b = *(uint8_t *)v;
54         r = b > 0;
55 finish:
56         free(v);
57         return r;
58 }
59
60 int is_efi_secure_boot(void) {
61         return read_flag("SecureBoot");
62 }
63
64 int is_efi_secure_boot_setup_mode(void) {
65         return read_flag("SetupMode");
66 }
67
68 int efi_get_variable(
69                 sd_id128_t vendor,
70                 const char *name,
71                 uint32_t *attribute,
72                 void **value,
73                 size_t *size) {
74
75         _cleanup_close_ int fd = -1;
76         _cleanup_free_ char *p = NULL;
77         uint32_t a;
78         ssize_t n;
79         struct stat st;
80         void *r;
81
82         assert(name);
83         assert(value);
84         assert(size);
85
86         if (asprintf(&p,
87                      "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
88                      name, SD_ID128_FORMAT_VAL(vendor)) < 0)
89                 return -ENOMEM;
90
91         fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
92         if (fd < 0)
93                 return -errno;
94
95         if (fstat(fd, &st) < 0)
96                 return -errno;
97         if (st.st_size < 4)
98                 return -EIO;
99         if (st.st_size > 4*1024*1024 + 4)
100                 return -E2BIG;
101
102         n = read(fd, &a, sizeof(a));
103         if (n < 0)
104                 return -errno;
105         if (n != sizeof(a))
106                 return -EIO;
107
108         r = malloc(st.st_size - 4 + 2);
109         if (!r)
110                 return -ENOMEM;
111
112         n = read(fd, r, (size_t) st.st_size - 4);
113         if (n < 0) {
114                 free(r);
115                 return -errno;
116         }
117         if (n != (ssize_t) st.st_size - 4) {
118                 free(r);
119                 return -EIO;
120         }
121
122         /* Always NUL terminate (2 bytes, to protect UTF-16) */
123         ((char*) r)[st.st_size - 4] = 0;
124         ((char*) r)[st.st_size - 4 + 1] = 0;
125
126         *value = r;
127         *size = (size_t) st.st_size - 4;
128
129         if (attribute)
130                 *attribute = a;
131
132         return 0;
133 }
134
135 int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
136         _cleanup_free_ void *s = NULL;
137         size_t ss;
138         int r;
139         char *x;
140
141         r = efi_get_variable(vendor, name, NULL, &s, &ss);
142         if (r < 0)
143                 return r;
144
145         x = utf16_to_utf8(s, ss);
146         if (!x)
147                 return -ENOMEM;
148
149         *p = x;
150         return 0;
151 }
152
153 static size_t utf16_size(const uint16_t *s) {
154         size_t l = 0;
155
156         while (s[l] > 0)
157                 l++;
158
159         return (l+1) * sizeof(uint16_t);
160 }
161
162 static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) {
163         struct uuid {
164                 uint32_t u1;
165                 uint16_t u2;
166                 uint16_t u3;
167                 uint8_t u4[8];
168         } _packed_;
169         const struct uuid *uuid = guid;
170
171         id128->bytes[0] = (uuid->u1 >> 24) & 0xff;
172         id128->bytes[1] = (uuid->u1 >> 16) & 0xff;
173         id128->bytes[2] = (uuid->u1 >> 8) & 0xff;
174         id128->bytes[3] = (uuid->u1) & 0xff;
175         id128->bytes[4] = (uuid->u2 >> 8) & 0xff;
176         id128->bytes[5] = (uuid->u2) & 0xff;
177         id128->bytes[6] = (uuid->u3 >> 8) & 0xff;
178         id128->bytes[7] = (uuid->u3) & 0xff;
179         memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4));
180 }
181
182 int efi_get_boot_option(
183                 uint16_t id,
184                 char **title,
185                 sd_id128_t *part_uuid,
186                 char **path) {
187
188         struct boot_option {
189                 uint32_t attr;
190                 uint16_t path_len;
191                 uint16_t title[];
192         } _packed_;
193
194         struct drive_path {
195                 uint32_t part_nr;
196                 uint64_t part_start;
197                 uint64_t part_size;
198                 char signature[16];
199                 uint8_t mbr_type;
200                 uint8_t signature_type;
201         } _packed_;
202
203         struct device_path {
204                 uint8_t type;
205                 uint8_t sub_type;
206                 uint16_t length;
207                 union {
208                         uint16_t path[0];
209                         struct drive_path drive;
210                 };
211         } _packed_;
212
213         char boot_id[9];
214         _cleanup_free_ uint8_t *buf = NULL;
215         size_t l;
216         struct boot_option *header;
217         size_t title_size;
218         char *s = NULL;
219         char *p = NULL;
220         sd_id128_t p_uuid = SD_ID128_NULL;
221         int err;
222
223         snprintf(boot_id, sizeof(boot_id), "Boot%04X", id);
224         err = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l);
225         if (err < 0)
226                 return err;
227         if (l < sizeof(struct boot_option))
228                 return -ENOENT;
229
230         header = (struct boot_option *)buf;
231         title_size = utf16_size(header->title);
232         if (title_size > l - offsetof(struct boot_option, title))
233                 return -EINVAL;
234
235         if (title) {
236                 s = utf16_to_utf8(header->title, title_size);
237                 if (!s) {
238                         err = -ENOMEM;
239                         goto err;
240                 }
241         }
242
243         if (header->path_len > 0) {
244                 uint8_t *dbuf;
245                 size_t dnext;
246
247                 dbuf = buf + offsetof(struct boot_option, title) + title_size;
248                 dnext = 0;
249                 while (dnext < header->path_len) {
250                         struct device_path *dpath;
251
252                         dpath = (struct device_path *)(dbuf + dnext);
253                         if (dpath->length < 4)
254                                 break;
255
256                         /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */
257                         if (dpath->type == 0x7f && dpath->sub_type == 0xff)
258                                 break;
259
260                         dnext += dpath->length;
261
262                         /* Type 0x04 – Media Device Path */
263                         if (dpath->type != 0x04)
264                                 continue;
265
266                         /* Sub-Type 1 – Hard Drive */
267                         if (dpath->sub_type == 0x01) {
268                                 /* 0x02 – GUID Partition Table */
269                                 if (dpath->drive.mbr_type != 0x02)
270                                         continue;
271
272                                 /* 0x02 – GUID signature */
273                                 if (dpath->drive.signature_type != 0x02)
274                                         continue;
275
276                                 if (part_uuid)
277                                         efi_guid_to_id128(dpath->drive.signature, &p_uuid);
278                                 continue;
279                         }
280
281                         /* Sub-Type 4 – File Path */
282                         if (dpath->sub_type == 0x04 && !p && path) {
283                                 p = utf16_to_utf8(dpath->path, dpath->length-4);
284                                 continue;
285                         }
286                 }
287         }
288
289         if (title)
290                 *title = s;
291         if (part_uuid)
292                 *part_uuid = p_uuid;
293         if (path)
294                 *path = p;
295
296         return 0;
297 err:
298         free(s);
299         free(p);
300         return err;
301 }
302
303 int efi_get_boot_order(uint16_t **order) {
304         void *buf;
305         size_t l;
306         int r;
307
308         r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l);
309         if (r < 0)
310                 return r;
311
312         if (l <= 0) {
313                 free(buf);
314                 return -ENOENT;
315         }
316
317         if ((l % sizeof(uint16_t) > 0) ||
318             (l / sizeof(uint16_t) > INT_MAX)) {
319                 free(buf);
320                 return -EINVAL;
321         }
322
323         *order = buf;
324         return (int) (l / sizeof(uint16_t));
325 }
326
327 static int boot_id_hex(const char s[4]) {
328         int i;
329         int id = 0;
330
331         for (i = 0; i < 4; i++)
332                 if (s[i] >= '0' && s[i] <= '9')
333                         id |= (s[i] - '0') << (3 - i) * 4;
334                 else if (s[i] >= 'A' && s[i] <= 'F')
335                         id |= (s[i] - 'A' + 10) << (3 - i) * 4;
336                 else
337                         return -1;
338
339         return id;
340 }
341
342 static int cmp_uint16(const void *_a, const void *_b) {
343         const uint16_t *a = _a, *b = _b;
344
345         return (int)*a - (int)*b;
346 }
347
348 int efi_get_boot_options(uint16_t **options) {
349         _cleanup_closedir_ DIR *dir = NULL;
350         struct dirent *de;
351         uint16_t *list = NULL;
352         int count = 0, r;
353
354         assert(options);
355
356         dir = opendir("/sys/firmware/efi/efivars/");
357         if (!dir)
358                 return -errno;
359
360         FOREACH_DIRENT(de, dir, r = -errno; goto fail) {
361                 int id;
362                 uint16_t *t;
363
364                 if (strncmp(de->d_name, "Boot", 4) != 0)
365                         continue;
366
367                 if (strlen(de->d_name) != 45)
368                         continue;
369
370                 if (strcmp(de->d_name + 8, "-8be4df61-93ca-11d2-aa0d-00e098032b8c") != 0)
371                         continue;
372
373                 id = boot_id_hex(de->d_name + 4);
374                 if (id < 0)
375                         continue;
376
377                 t = realloc(list, (count + 1) * sizeof(uint16_t));
378                 if (!t) {
379                         r = -ENOMEM;
380                         goto fail;
381                 }
382
383                 list = t;
384                 list[count ++] = id;
385         }
386
387         if (list)
388                 qsort(list, count, sizeof(uint16_t), cmp_uint16);
389
390         *options = list;
391         return count;
392
393 fail:
394         free(list);
395         return r;
396 }
397
398 static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
399         _cleanup_free_ char *j = NULL;
400         int r;
401         uint64_t x;
402
403         assert(name);
404         assert(u);
405
406         r = efi_get_variable_string(EFI_VENDOR_LOADER, name, &j);
407         if (r < 0)
408                 return r;
409
410         r = safe_atou64(j, &x);
411         if (r < 0)
412                 return r;
413
414         *u = x;
415         return 0;
416 }
417
418 int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
419         uint64_t x, y;
420         int r;
421
422         assert(firmware);
423         assert(loader);
424
425         r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
426         if (r < 0)
427                 return r;
428
429         r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeExecUSec", &y);
430         if (r < 0)
431                 return r;
432
433         if (y == 0 || y < x)
434                 return -EIO;
435
436         if (y > USEC_PER_HOUR)
437                 return -EIO;
438
439         *firmware = x;
440         *loader = y;
441
442         return 0;
443 }
444
445 int efi_loader_get_device_part_uuid(sd_id128_t *u) {
446         _cleanup_free_ char *p = NULL;
447         int r, parsed[16];
448         unsigned i;
449
450         assert(u);
451
452         r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p);
453         if (r < 0)
454                 return r;
455
456         if (sscanf(p, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
457                    &parsed[0], &parsed[1], &parsed[2], &parsed[3],
458                    &parsed[4], &parsed[5], &parsed[6], &parsed[7],
459                    &parsed[8], &parsed[9], &parsed[10], &parsed[11],
460                    &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16)
461                 return -EIO;
462
463         for (i = 0; i < ELEMENTSOF(parsed); i++)
464                 u->bytes[i] = parsed[i];
465
466         return 0;
467 }
468
469 #endif