master
c 215 lines 5.92 KB
Raw
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 */
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 */
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
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
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 */
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 (efilist->siglist_size < sizeof(*efilist)) {
98 break;
99 }
100 if (size < efilist->siglist_size) {
101 break;
102 }
103
104 if (uadd64_overflow(sizeof(*efilist), efilist->header_size, &start)) {
105 break;
106 }
107 if (efilist->sig_size <= sizeof(QemuUUID)) {
108 break;
109 }
110
111 if (qemu_uuid_is_equal(&efilist->guid_type, &EfiCertX509Guid)) {
112 if (start + efilist->sig_size != efilist->siglist_size) {
113 break;
114 }
115 uefi_vars_siglist_add_x509(siglist,
116 (QemuUUID *)(data + start),
117 data + start + sizeof(QemuUUID),
118 efilist->sig_size - sizeof(QemuUUID));
119
120 } else if (qemu_uuid_is_equal(&efilist->guid_type, &EfiCertSha256Guid)) {
121 if (efilist->sig_size != sizeof(QemuUUID) + 32) {
122 break;
123 }
124 if (start + efilist->sig_size > efilist->siglist_size) {
125 break;
126 }
127 while (start <= efilist->siglist_size - efilist->sig_size) {
128 uefi_vars_siglist_add_sha256(siglist,
129 (QemuUUID *)(data + start),
130 data + start + sizeof(QemuUUID));
131 start += efilist->sig_size;
132 }
133
134 } else {
135 QemuUUID be = qemu_uuid_bswap(efilist->guid_type);
136 char *str_uuid = qemu_uuid_unparse_strdup(&be);
137 warn_report("%s: unknown type (%s)", __func__, str_uuid);
138 g_free(str_uuid);
139 }
140
141 data += efilist->siglist_size;
142 size -= efilist->siglist_size;
143 }
144 }
145
146 uint64_t uefi_vars_siglist_blob_size(uefi_vars_siglist *siglist)
147 {
148 uefi_vars_cert *c;
149 uefi_vars_hash *h;
150 uint64_t size = 0;
151
152 QTAILQ_FOREACH(c, &siglist->x509, next) {
153 size += sizeof(efi_siglist) + sizeof(QemuUUID) + c->size;
154 }
155
156 if (!QTAILQ_EMPTY(&siglist->sha256)) {
157 size += sizeof(efi_siglist);
158 QTAILQ_FOREACH(h, &siglist->sha256, next) {
159 size += sizeof(QemuUUID) + 32;
160 }
161 }
162
163 return size;
164 }
165
166 /*
167 * Generate UEFI signature list.
168 */
169 void uefi_vars_siglist_blob_generate(uefi_vars_siglist *siglist,
170 void *data, uint64_t size)
171 {
172 uefi_vars_cert *c;
173 uefi_vars_hash *h;
174 efi_siglist *efilist;
175 uint64_t pos = 0, start;
176 uint32_t i;
177
178 QTAILQ_FOREACH(c, &siglist->x509, next) {
179 efilist = data + pos;
180 efilist->guid_type = EfiCertX509Guid;
181 efilist->sig_size = sizeof(QemuUUID) + c->size;
182 efilist->header_size = 0;
183
184 start = pos + sizeof(efi_siglist);
185 memcpy(data + start,
186 &c->owner, sizeof(QemuUUID));
187 memcpy(data + start + sizeof(QemuUUID),
188 c->data, c->size);
189
190 efilist->siglist_size = sizeof(efi_siglist) + efilist->sig_size;
191 pos += efilist->siglist_size;
192 }
193
194 if (!QTAILQ_EMPTY(&siglist->sha256)) {
195 efilist = data + pos;
196 efilist->guid_type = EfiCertSha256Guid;
197 efilist->sig_size = sizeof(QemuUUID) + 32;
198 efilist->header_size = 0;
199
200 i = 0;
201 start = pos + sizeof(efi_siglist);
202 QTAILQ_FOREACH(h, &siglist->sha256, next) {
203 memcpy(data + start + efilist->sig_size * i,
204 &h->owner, sizeof(QemuUUID));
205 memcpy(data + start + efilist->sig_size * i + sizeof(QemuUUID),
206 h->data, 32);
207 i++;
208 }
209
210 efilist->siglist_size = sizeof(efi_siglist) + efilist->sig_size * i;
211 pos += efilist->siglist_size;
212 }
213
214 assert(pos == size);
215 }