xref: /qemu/hw/uefi/var-service-siglist.c (revision 50aa3d0984d8a4a9c39d34e2f81e8a70674462e4)
1 /*
2  * SPDX-License-Identifier: GPL-2.0-or-later
3  *
4  * uefi vars device - parse and generate efi signature databases
5  */
6 
7 #include "qemu/osdep.h"
8 #include "qemu/error-report.h"
9 #include "system/dma.h"
10 
11 #include "hw/uefi/var-service.h"
12 
13 /*
14  * Add x509 certificate to list (with duplicate check).
15  */
uefi_vars_siglist_add_x509(uefi_vars_siglist * siglist,QemuUUID * owner,void * data,uint64_t size)16 static void uefi_vars_siglist_add_x509(uefi_vars_siglist *siglist,
17                                        QemuUUID *owner,
18                                        void *data, uint64_t size)
19 {
20     uefi_vars_cert *c;
21 
22     QTAILQ_FOREACH(c, &siglist->x509, next) {
23         if (c->size != size) {
24             continue;
25         }
26         if (memcmp(c->data, data, size) != 0) {
27             continue;
28         }
29         return;
30     }
31 
32     c = g_malloc(sizeof(*c) + size);
33     c->owner = *owner;
34     c->size = size;
35     memcpy(c->data, data, size);
36     QTAILQ_INSERT_TAIL(&siglist->x509, c, next);
37 }
38 
39 /*
40  * Add sha256 hash to list (with duplicate check).
41  */
uefi_vars_siglist_add_sha256(uefi_vars_siglist * siglist,QemuUUID * owner,void * data)42 static void uefi_vars_siglist_add_sha256(uefi_vars_siglist *siglist,
43                                          QemuUUID *owner,
44                                          void *data)
45 {
46     uefi_vars_hash *h;
47 
48     QTAILQ_FOREACH(h, &siglist->sha256, next) {
49         if (memcmp(h->data, data, 32) != 0) {
50             continue;
51         }
52         return;
53     }
54 
55     h = g_malloc(sizeof(*h) + 32);
56     h->owner = *owner;
57     memcpy(h->data, data, 32);
58     QTAILQ_INSERT_TAIL(&siglist->sha256, h, next);
59 }
60 
uefi_vars_siglist_init(uefi_vars_siglist * siglist)61 void uefi_vars_siglist_init(uefi_vars_siglist *siglist)
62 {
63     memset(siglist, 0, sizeof(*siglist));
64     QTAILQ_INIT(&siglist->x509);
65     QTAILQ_INIT(&siglist->sha256);
66 }
67 
uefi_vars_siglist_free(uefi_vars_siglist * siglist)68 void uefi_vars_siglist_free(uefi_vars_siglist *siglist)
69 {
70     uefi_vars_cert *c, *cs;
71     uefi_vars_hash *h, *hs;
72 
73     QTAILQ_FOREACH_SAFE(c, &siglist->x509, next, cs) {
74         QTAILQ_REMOVE(&siglist->x509, c, next);
75         g_free(c);
76     }
77     QTAILQ_FOREACH_SAFE(h, &siglist->sha256, next, hs) {
78         QTAILQ_REMOVE(&siglist->sha256, h, next);
79         g_free(h);
80     }
81 }
82 
83 /*
84  * Parse UEFI signature list.
85  */
uefi_vars_siglist_parse(uefi_vars_siglist * siglist,void * data,uint64_t size)86 void uefi_vars_siglist_parse(uefi_vars_siglist *siglist,
87                              void *data, uint64_t size)
88 {
89     efi_siglist *efilist;
90     uint64_t start;
91 
92     while (size) {
93         if (size < sizeof(*efilist)) {
94             break;
95         }
96         efilist = data;
97         if (size < efilist->siglist_size) {
98             break;
99         }
100 
101         if (uadd64_overflow(sizeof(*efilist), efilist->header_size, &start)) {
102             break;
103         }
104         if (efilist->sig_size <= sizeof(QemuUUID)) {
105             break;
106         }
107 
108         if (qemu_uuid_is_equal(&efilist->guid_type, &EfiCertX509Guid)) {
109             if (start + efilist->sig_size != efilist->siglist_size) {
110                 break;
111             }
112             uefi_vars_siglist_add_x509(siglist,
113                                        (QemuUUID *)(data + start),
114                                        data + start + sizeof(QemuUUID),
115                                        efilist->sig_size - sizeof(QemuUUID));
116 
117         } else if (qemu_uuid_is_equal(&efilist->guid_type, &EfiCertSha256Guid)) {
118             if (efilist->sig_size != sizeof(QemuUUID) + 32) {
119                 break;
120             }
121             if (start + efilist->sig_size > efilist->siglist_size) {
122                 break;
123             }
124             while (start <= efilist->siglist_size - efilist->sig_size) {
125                 uefi_vars_siglist_add_sha256(siglist,
126                                              (QemuUUID *)(data + start),
127                                              data + start + sizeof(QemuUUID));
128                 start += efilist->sig_size;
129             }
130 
131         } else {
132             QemuUUID be = qemu_uuid_bswap(efilist->guid_type);
133             char *str_uuid = qemu_uuid_unparse_strdup(&be);
134             warn_report("%s: unknown type (%s)", __func__, str_uuid);
135             g_free(str_uuid);
136         }
137 
138         data += efilist->siglist_size;
139         size -= efilist->siglist_size;
140     }
141 }
142 
uefi_vars_siglist_blob_size(uefi_vars_siglist * siglist)143 uint64_t uefi_vars_siglist_blob_size(uefi_vars_siglist *siglist)
144 {
145     uefi_vars_cert *c;
146     uefi_vars_hash *h;
147     uint64_t size = 0;
148 
149     QTAILQ_FOREACH(c, &siglist->x509, next) {
150         size += sizeof(efi_siglist) + sizeof(QemuUUID) + c->size;
151     }
152 
153     if (!QTAILQ_EMPTY(&siglist->sha256)) {
154         size += sizeof(efi_siglist);
155         QTAILQ_FOREACH(h, &siglist->sha256, next) {
156             size += sizeof(QemuUUID) + 32;
157         }
158     }
159 
160     return size;
161 }
162 
163 /*
164  * Generate UEFI signature list.
165  */
uefi_vars_siglist_blob_generate(uefi_vars_siglist * siglist,void * data,uint64_t size)166 void uefi_vars_siglist_blob_generate(uefi_vars_siglist *siglist,
167                                      void *data, uint64_t size)
168 {
169     uefi_vars_cert *c;
170     uefi_vars_hash *h;
171     efi_siglist *efilist;
172     uint64_t pos = 0, start;
173     uint32_t i;
174 
175     QTAILQ_FOREACH(c, &siglist->x509, next) {
176         efilist = data + pos;
177         efilist->guid_type = EfiCertX509Guid;
178         efilist->sig_size = sizeof(QemuUUID) + c->size;
179         efilist->header_size = 0;
180 
181         start = pos + sizeof(efi_siglist);
182         memcpy(data + start,
183                &c->owner, sizeof(QemuUUID));
184         memcpy(data + start + sizeof(QemuUUID),
185                c->data, c->size);
186 
187         efilist->siglist_size = sizeof(efi_siglist) + efilist->sig_size;
188         pos += efilist->siglist_size;
189     }
190 
191     if (!QTAILQ_EMPTY(&siglist->sha256)) {
192         efilist = data + pos;
193         efilist->guid_type = EfiCertSha256Guid;
194         efilist->sig_size = sizeof(QemuUUID) + 32;
195         efilist->header_size = 0;
196 
197         i = 0;
198         start = pos + sizeof(efi_siglist);
199         QTAILQ_FOREACH(h, &siglist->sha256, next) {
200             memcpy(data + start + efilist->sig_size * i,
201                    &h->owner, sizeof(QemuUUID));
202             memcpy(data + start + efilist->sig_size * i + sizeof(QemuUUID),
203                    h->data, 32);
204             i++;
205         }
206 
207         efilist->siglist_size = sizeof(efi_siglist) + efilist->sig_size * i;
208         pos += efilist->siglist_size;
209     }
210 
211     assert(pos == size);
212 }
213