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