xref: /qemu/hw/uefi/var-service-pkcs7.c (revision 50aa3d0984d8a4a9c39d34e2f81e8a70674462e4)
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