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