1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 *
4 * uefi vars device - pkcs7 verification
5 */
6 #include "qemu/osdep.h"
7 #include "qemu/error-report.h"
8 #include "system/dma.h"
9
10 #include <gnutls/gnutls.h>
11 #include <gnutls/pkcs7.h>
12 #include <gnutls/crypto.h>
13
14 #include "hw/uefi/var-service.h"
15
16 #define AUTHVAR_DIGEST_ALGO GNUTLS_DIG_SHA256
17 #define AUTHVAR_DIGEST_SIZE 32
18
19 /*
20 * Replicate the signed data for signature verification.
21 */
build_signed_data(mm_variable_access * va,void * data)22 static gnutls_datum_t *build_signed_data(mm_variable_access *va, void *data)
23 {
24 variable_auth_2 *auth = data;
25 uint64_t data_offset = sizeof(efi_time) + auth->hdr_length;
26 uint16_t *name = (void *)va + sizeof(mm_variable_access);
27 gnutls_datum_t *sdata;
28 uint64_t pos = 0;
29
30 sdata = g_new(gnutls_datum_t, 1);
31 sdata->size = (va->name_size - 2
32 + sizeof(QemuUUID)
33 + sizeof(va->attributes)
34 + sizeof(auth->timestamp)
35 + va->data_size - data_offset);
36 sdata->data = g_malloc(sdata->size);
37
38 /* Variable Name (without terminating \0) */
39 memcpy(sdata->data + pos, name, va->name_size - 2);
40 pos += va->name_size - 2;
41
42 /* Variable Namespace Guid */
43 memcpy(sdata->data + pos, &va->guid, sizeof(va->guid));
44 pos += sizeof(va->guid);
45
46 /* Attributes */
47 memcpy(sdata->data + pos, &va->attributes, sizeof(va->attributes));
48 pos += sizeof(va->attributes);
49
50 /* TimeStamp */
51 memcpy(sdata->data + pos, &auth->timestamp, sizeof(auth->timestamp));
52 pos += sizeof(auth->timestamp);
53
54 /* Variable Content */
55 memcpy(sdata->data + pos, data + data_offset, va->data_size - data_offset);
56 pos += va->data_size - data_offset;
57
58 assert(pos == sdata->size);
59 return sdata;
60 }
61
62 /*
63 * See WrapPkcs7Data() in edk2.
64 *
65 * UEFI spec allows pkcs7 signatures being used without the envelope which
66 * identifies them as pkcs7 signatures. openssl and gnutls will not parse them
67 * without the envelope though. So add it if needed.
68 */
wrap_pkcs7(gnutls_datum_t * pkcs7)69 static void wrap_pkcs7(gnutls_datum_t *pkcs7)
70 {
71 static uint8_t signed_data_oid[9] = {
72 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02
73 };
74 gnutls_datum_t wrap;
75
76 if (pkcs7->data[4] == 0x06 &&
77 pkcs7->data[5] == 0x09 &&
78 memcmp(pkcs7->data + 6, signed_data_oid, sizeof(signed_data_oid)) == 0 &&
79 pkcs7->data[15] == 0x0a &&
80 pkcs7->data[16] == 0x82) {
81 return;
82 }
83
84 wrap.size = pkcs7->size + 19;
85 wrap.data = g_malloc(wrap.size);
86
87 wrap.data[0] = 0x30;
88 wrap.data[1] = 0x82;
89 wrap.data[2] = (wrap.size - 4) >> 8;
90 wrap.data[3] = (wrap.size - 4) & 0xff;
91 wrap.data[4] = 0x06;
92 wrap.data[5] = 0x09;
93 memcpy(wrap.data + 6, signed_data_oid, sizeof(signed_data_oid));
94
95 wrap.data[15] = 0xa0;
96 wrap.data[16] = 0x82;
97 wrap.data[17] = pkcs7->size >> 8;
98 wrap.data[18] = pkcs7->size & 0xff;
99 memcpy(wrap.data + 19, pkcs7->data, pkcs7->size);
100
101 g_free(pkcs7->data);
102 *pkcs7 = wrap;
103 }
104
build_pkcs7(void * data)105 static gnutls_datum_t *build_pkcs7(void *data)
106 {
107 variable_auth_2 *auth = data;
108 gnutls_datum_t *pkcs7;
109
110 pkcs7 = g_new(gnutls_datum_t, 1);
111 pkcs7->size = auth->hdr_length - 24;
112 pkcs7->data = g_malloc(pkcs7->size);
113 memcpy(pkcs7->data, data + 16 + 24, pkcs7->size);
114
115 wrap_pkcs7(pkcs7);
116
117 return pkcs7;
118 }
119
120 /*
121 * Read UEFI signature database, store x509 all certificates found in
122 * gnutls_x509_trust_list_t.
123 */
build_trust_list_sb(uefi_variable * var)124 static gnutls_x509_trust_list_t build_trust_list_sb(uefi_variable *var)
125 {
126 gnutls_x509_trust_list_t tlist;
127 gnutls_datum_t cert_data;
128 gnutls_x509_crt_t cert;
129 uefi_vars_siglist siglist;
130 uefi_vars_cert *c;
131 int rc;
132
133 rc = gnutls_x509_trust_list_init(&tlist, 0);
134 if (rc < 0) {
135 warn_report("gnutls_x509_trust_list_init error: %s",
136 gnutls_strerror(rc));
137 return NULL;
138 }
139
140 uefi_vars_siglist_init(&siglist);
141 uefi_vars_siglist_parse(&siglist, var->data, var->data_size);
142
143 QTAILQ_FOREACH(c, &siglist.x509, next) {
144 cert_data.size = c->size;
145 cert_data.data = c->data;
146
147 rc = gnutls_x509_crt_init(&cert);
148 if (rc < 0) {
149 warn_report("gnutls_x509_crt_init error: %s", gnutls_strerror(rc));
150 break;
151 }
152 rc = gnutls_x509_crt_import(cert, &cert_data, GNUTLS_X509_FMT_DER);
153 if (rc < 0) {
154 warn_report("gnutls_x509_crt_import error: %s",
155 gnutls_strerror(rc));
156 gnutls_x509_crt_deinit(cert);
157 break;
158 }
159 rc = gnutls_x509_trust_list_add_cas(tlist, &cert, 1, 0);
160 if (rc < 0) {
161 warn_report("gnutls_x509_crt_import error: %s",
162 gnutls_strerror(rc));
163 gnutls_x509_crt_deinit(cert);
164 break;
165 }
166 }
167
168 uefi_vars_siglist_free(&siglist);
169
170 return tlist;
171 }
172
build_digest_authvar(gnutls_x509_crt_t signer,gnutls_x509_crt_t root,uint8_t * hash_digest)173 static int build_digest_authvar(gnutls_x509_crt_t signer,
174 gnutls_x509_crt_t root,
175 uint8_t *hash_digest)
176 {
177 char *cn;
178 size_t cn_size = 0;
179 uint8_t fp[AUTHVAR_DIGEST_SIZE];
180 size_t fp_size = sizeof(fp);
181 gnutls_hash_hd_t hash;
182 int rc;
183
184 /* get signer CN */
185 rc = gnutls_x509_crt_get_dn_by_oid(signer, GNUTLS_OID_X520_COMMON_NAME,
186 0, 0, NULL, &cn_size);
187 if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) {
188 warn_report("gnutls_x509_crt_get_dn_by_oid error #1: %s",
189 gnutls_strerror(rc));
190 return rc;
191 }
192
193 cn = g_malloc(cn_size);
194 rc = gnutls_x509_crt_get_dn_by_oid(signer, GNUTLS_OID_X520_COMMON_NAME,
195 0, 0, cn, &cn_size);
196 if (rc < 0) {
197 warn_report("gnutls_x509_crt_get_dn_by_oid error #2: %s",
198 gnutls_strerror(rc));
199 goto err;
200 }
201
202 /* get root certificate fingerprint */
203 rc = gnutls_x509_crt_get_fingerprint(root, AUTHVAR_DIGEST_ALGO,
204 fp, &fp_size);
205 if (rc < 0) {
206 warn_report("gnutls_x509_crt_get_fingerprint error: %s",
207 gnutls_strerror(rc));
208 goto err;
209 }
210
211 /* digest both items */
212 rc = gnutls_hash_init(&hash, AUTHVAR_DIGEST_ALGO);
213 if (rc < 0) {
214 warn_report("gnutls_hash_init error: %s",
215 gnutls_strerror(rc));
216 goto err;
217 }
218 rc = gnutls_hash(hash, cn, cn_size);
219 if (rc < 0) {
220 warn_report("gnutls_hash error: %s",
221 gnutls_strerror(rc));
222 goto err;
223 }
224 rc = gnutls_hash(hash, fp, fp_size);
225 if (rc < 0) {
226 warn_report("gnutls_hash error: %s",
227 gnutls_strerror(rc));
228 goto err;
229 }
230 gnutls_hash_deinit(hash, hash_digest);
231
232 return 0;
233
234 err:
235 g_free(cn);
236 return rc;
237 }
238
239 /*
240 * uefi spec 2.9, section 8.2.2
241 *
242 * For EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS variables which are
243 * NOT secure boot variables we should track the root certificate of the trust
244 * chain, and the subject CN of the signer certificate.
245 *
246 * So we'll go store a digest of these two items so we can verify this. Also
247 * create a gnutls_x509_trust_list_t with the root certificate, so
248 * gnutls_pkcs7_verify() will pass (assuming the signature is otherwise
249 * correct).
250 */
build_trust_list_authvar(gnutls_pkcs7_t pkcs7,uint8_t * hash_digest)251 static gnutls_x509_trust_list_t build_trust_list_authvar(gnutls_pkcs7_t pkcs7,
252 uint8_t *hash_digest)
253 {
254 gnutls_datum_t signer_data = { 0 };
255 gnutls_datum_t root_data = { 0 };
256 gnutls_x509_crt_t signer = NULL;
257 gnutls_x509_crt_t root = NULL;
258 gnutls_x509_trust_list_t tlist = NULL;
259 int n, rc;
260
261 n = gnutls_pkcs7_get_crt_count(pkcs7);
262
263 /* first is signer certificate */
264 rc = gnutls_pkcs7_get_crt_raw2(pkcs7, 0, &signer_data);
265 if (rc < 0) {
266 warn_report("gnutls_pkcs7_get_crt_raw2(0) error: %s",
267 gnutls_strerror(rc));
268 goto done;
269 }
270 rc = gnutls_x509_crt_init(&signer);
271 if (rc < 0) {
272 warn_report("gnutls_x509_crt_init error: %s", gnutls_strerror(rc));
273 goto done;
274 }
275 rc = gnutls_x509_crt_import(signer, &signer_data, GNUTLS_X509_FMT_DER);
276 if (rc < 0) {
277 warn_report("gnutls_x509_crt_import error: %s",
278 gnutls_strerror(rc));
279 gnutls_x509_crt_deinit(signer);
280 goto done;
281 }
282
283 /* last is root-of-trust certificate (can be identical to signer) */
284 rc = gnutls_pkcs7_get_crt_raw2(pkcs7, n - 1, &root_data);
285 if (rc < 0) {
286 warn_report("gnutls_pkcs7_get_crt_raw2(%d) error: %s",
287 n - 1, gnutls_strerror(rc));
288 goto done;
289 }
290 rc = gnutls_x509_crt_init(&root);
291 if (rc < 0) {
292 warn_report("gnutls_x509_crt_init error: %s", gnutls_strerror(rc));
293 goto done;
294 }
295 rc = gnutls_x509_crt_import(root, &root_data, GNUTLS_X509_FMT_DER);
296 if (rc < 0) {
297 warn_report("gnutls_x509_crt_import error: %s",
298 gnutls_strerror(rc));
299 goto done;
300 }
301
302 /* calc digest for signer CN + root cert */
303 rc = build_digest_authvar(signer, root, hash_digest);
304 if (rc < 0) {
305 goto done;
306 }
307
308 /* add root to trust list */
309 rc = gnutls_x509_trust_list_init(&tlist, 0);
310 if (rc < 0) {
311 warn_report("gnutls_x509_trust_list_init error: %s",
312 gnutls_strerror(rc));
313 goto done;
314 }
315 rc = gnutls_x509_trust_list_add_cas(tlist, &root, 1, 0);
316 if (rc < 0) {
317 warn_report("gnutls_x509_crt_import error: %s",
318 gnutls_strerror(rc));
319 gnutls_x509_trust_list_deinit(tlist, 1);
320 tlist = NULL;
321 goto done;
322 } else {
323 /* ownership passed to tlist */
324 root = NULL;
325 }
326
327 done:
328 if (signer_data.data) {
329 gnutls_free(signer_data.data);
330 }
331 if (root_data.data) {
332 gnutls_free(root_data.data);
333 }
334 if (signer) {
335 gnutls_x509_crt_deinit(signer);
336 }
337 if (root) {
338 gnutls_x509_crt_deinit(root);
339 }
340 return tlist;
341 }
342
free_datum(gnutls_datum_t * ptr)343 static void free_datum(gnutls_datum_t *ptr)
344 {
345 if (!ptr) {
346 return;
347 }
348 g_free(ptr->data);
349 g_free(ptr);
350 }
351
gnutls_log_stderr(int level,const char * msg)352 static void gnutls_log_stderr(int level, const char *msg)
353 {
354 if (strncmp(msg, "ASSERT:", 7) == 0) {
355 return;
356 }
357 fprintf(stderr, " %d: %s", level, msg);
358 }
359
360 /*
361 * pkcs7 signature verification (EFI_VARIABLE_AUTHENTICATION_2).
362 */
uefi_vars_check_pkcs7_2(uefi_variable * siglist,void ** digest,uint32_t * digest_size,mm_variable_access * va,void * data)363 efi_status uefi_vars_check_pkcs7_2(uefi_variable *siglist,
364 void **digest, uint32_t *digest_size,
365 mm_variable_access *va, void *data)
366 {
367 gnutls_x509_trust_list_t tlist = NULL;
368 gnutls_datum_t *signed_data = NULL;
369 gnutls_datum_t *pkcs7_data = NULL;
370 gnutls_pkcs7_t pkcs7 = NULL;
371 efi_status status = EFI_SECURITY_VIOLATION;
372 int rc;
373
374 if (0) {
375 /* gnutls debug logging */
376 static bool first = true;
377
378 if (first) {
379 first = false;
380 gnutls_global_set_log_function(gnutls_log_stderr);
381 gnutls_global_set_log_level(99);
382 }
383 }
384
385 signed_data = build_signed_data(va, data);
386 pkcs7_data = build_pkcs7(data);
387
388 rc = gnutls_pkcs7_init(&pkcs7);
389 if (rc < 0) {
390 warn_report("gnutls_pkcs7_init error: %s", gnutls_strerror(rc));
391 goto out;
392 }
393
394 rc = gnutls_pkcs7_import(pkcs7, pkcs7_data, GNUTLS_X509_FMT_DER);
395 if (rc < 0) {
396 warn_report("gnutls_pkcs7_import error: %s", gnutls_strerror(rc));
397 goto out;
398 }
399
400 if (siglist) {
401 /* secure boot variables */
402 tlist = build_trust_list_sb(siglist);
403 } else if (digest && digest_size) {
404 /* other authenticated variables */
405 *digest_size = AUTHVAR_DIGEST_SIZE;
406 *digest = g_malloc(*digest_size);
407 tlist = build_trust_list_authvar(pkcs7, *digest);
408 } else {
409 /* should not happen */
410 goto out;
411 }
412
413 rc = gnutls_pkcs7_verify(pkcs7, tlist,
414 NULL, 0,
415 0, signed_data,
416 GNUTLS_VERIFY_DISABLE_TIME_CHECKS |
417 GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS);
418 if (rc < 0) {
419 warn_report("gnutls_pkcs7_verify error: %s", gnutls_strerror(rc));
420 goto out;
421 }
422
423 /* check passed */
424 status = EFI_SUCCESS;
425
426 out:
427 free_datum(signed_data);
428 free_datum(pkcs7_data);
429 if (tlist) {
430 gnutls_x509_trust_list_deinit(tlist, 1);
431 }
432 if (pkcs7) {
433 gnutls_pkcs7_deinit(pkcs7);
434 }
435 return status;
436 }
437