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 = 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 */ 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 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 */ 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 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 */ 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 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 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 */ 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