From 7926dfb579cb17efc62ede2ce6d5c0a6f7e2f855 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Wed, 8 Jul 2020 14:01:57 +0900 Subject: [PATCH] efi_loader: image_loader: add digest-based verification for signed image In case that a type of certificate in "db" or "dbx" is EFI_CERT_X509_SHA256_GUID, it is actually not a certificate which contains a public key for RSA decryption, but a digest of image to be loaded. If the value matches to a value calculated from a given binary image, it is granted for loading. With this patch, common digest check code, which used to be used for unsigned image verification, will be extracted from efi_signature_verify_with_sigdb() into efi_signature_lookup_digest(), and extra step for digest check will be added to efi_image_authenticate(). Signed-off-by: AKASHI Takahiro --- include/efi_loader.h | 2 + lib/efi_loader/efi_image_loader.c | 44 ++++++++-- lib/efi_loader/efi_signature.c | 134 ++++++++++++++---------------- 3 files changed, 102 insertions(+), 78 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 2f9fb112b3..ceabbaadd0 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -765,6 +765,8 @@ struct efi_signature_store { struct x509_certificate; struct pkcs7_message; +bool efi_signature_lookup_digest(struct efi_image_regions *regs, + struct efi_signature_store *db); bool efi_signature_verify_one(struct efi_image_regions *regs, struct pkcs7_message *msg, struct efi_signature_store *db); diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 058359fc25..b7cf26046e 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -448,16 +448,16 @@ static bool efi_image_unsigned_authenticate(struct efi_image_regions *regs) } /* try black-list first */ - if (efi_signature_verify_one(regs, NULL, dbx)) { - EFI_PRINT("Image is not signed and rejected by \"dbx\"\n"); + if (efi_signature_lookup_digest(regs, dbx)) { + EFI_PRINT("Image is not signed and its digest found in \"dbx\"\n"); goto out; } /* try white-list */ - if (efi_signature_verify_one(regs, NULL, db)) + if (efi_signature_lookup_digest(regs, db)) ret = true; else - EFI_PRINT("Image is not signed and not found in \"db\" or \"dbx\"\n"); + EFI_PRINT("Image is not signed and its digest not found in \"db\" or \"dbx\"\n"); out: efi_sigstore_free(db); @@ -605,6 +605,25 @@ static bool efi_image_authenticate(void *efi, size_t efi_size) continue; } + /* + * NOTE: + * UEFI specification defines two signature types possible + * in signature database: + * a. x509 certificate, where a signature in image is + * a message digest encrypted by RSA public key + * (EFI_CERT_X509_GUID) + * b. bare hash value of message digest + * (EFI_CERT_SHAxxx_GUID) + * + * efi_signature_verify() handles case (a), while + * efi_signature_lookup_digest() handles case (b). + * + * There is a third type: + * c. message digest of a certificate + * (EFI_CERT_X509_SHAAxxx_GUID) + * This type of signature is used only in revocation list + * (dbx) and handled as part of efi_signatgure_verify(). + */ /* try black-list first */ if (efi_signature_verify_one(regs, msg, dbx)) { EFI_PRINT("Signature was rejected by \"dbx\"\n"); @@ -616,11 +635,22 @@ static bool efi_image_authenticate(void *efi, size_t efi_size) goto err; } - /* try white-list */ - if (!efi_signature_verify_with_sigdb(regs, msg, db, dbx)) { - EFI_PRINT("Signature was not verified by \"db\"\n"); + if (efi_signature_lookup_digest(regs, dbx)) { + EFI_PRINT("Image's digest was found in \"dbx\"\n"); goto err; } + + /* try white-list */ + if (efi_signature_verify_with_sigdb(regs, msg, db, dbx)) + continue; + + debug("Signature was not verified by \"db\"\n"); + + if (efi_signature_lookup_digest(regs, db)) + continue; + + debug("Image's digest was not found in \"db\" or \"dbx\"\n"); + goto err; } ret = true; diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c index 52392d139a..fc0314e6d4 100644 --- a/lib/efi_loader/efi_signature.c +++ b/lib/efi_loader/efi_signature.c @@ -197,6 +197,68 @@ out: return verified; } +/** + * efi_signature_lookup_digest - search for an image's digest in sigdb + * @regs: List of regions to be authenticated + * @db: Signature database for trusted certificates + * + * A message digest of image pointed to by @regs is calculated and + * its hash value is compared to entries in signature database pointed + * to by @db. + * + * Return: true if found, false if not + */ +bool efi_signature_lookup_digest(struct efi_image_regions *regs, + struct efi_signature_store *db) +{ + struct efi_signature_store *siglist; + struct efi_sig_data *sig_data; + void *hash = NULL; + size_t size = 0; + bool found = false; + + EFI_PRINT("%s: Enter, %p, %p\n", __func__, regs, db); + + if (!regs || !db || !db->sig_data_list) + goto out; + + for (siglist = db; siglist; siglist = siglist->next) { + /* TODO: support other hash algorithms */ + if (guidcmp(&siglist->sig_type, &efi_guid_sha256)) { + EFI_PRINT("Digest algorithm is not supported: %pUl\n", + &siglist->sig_type); + break; + } + + if (!efi_hash_regions(regs->reg, regs->num, &hash, &size)) { + EFI_PRINT("Digesting an image failed\n"); + break; + } + + for (sig_data = siglist->sig_data_list; sig_data; + sig_data = sig_data->next) { +#ifdef DEBUG + EFI_PRINT("Msg digest in database:\n"); + print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, + sig_data->data, sig_data->size, false); +#endif + if (sig_data->size == size && + !memcmp(sig_data->data, hash, size)) { + found = true; + free(hash); + goto out; + } + } + + free(hash); + hash = NULL; + } + +out: + EFI_PRINT("%s: Exit, found: %d\n", __func__, found); + return found; +} + /** * efi_signature_verify_with_list - verify a signature with signature list * @regs: List of regions to be authenticated @@ -226,46 +288,6 @@ bool efi_signature_verify_with_list(struct efi_image_regions *regs, EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__, regs, signed_info, siglist, valid_cert); - if (!signed_info) { - void *hash = NULL; - size_t size; - - EFI_PRINT("%s: unsigned image\n", __func__); - /* - * verify based on calculated hash value - * TODO: support other hash algorithms - */ - if (guidcmp(&siglist->sig_type, &efi_guid_sha256)) { - EFI_PRINT("Digest algorithm is not supported: %pUl\n", - &siglist->sig_type); - goto out; - } - - if (!efi_hash_regions(regs->reg, regs->num, &hash, &size)) { - EFI_PRINT("Digesting unsigned image failed\n"); - goto out; - } - - /* go through the list */ - for (sig_data = siglist->sig_data_list; sig_data; - sig_data = sig_data->next) { -#ifdef DEBUG - EFI_PRINT("Msg digest in database:\n"); - print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, - sig_data->data, sig_data->size, false); -#endif - if ((sig_data->size == size) && - !memcmp(sig_data->data, hash, size)) { - verified = true; - free(hash); - goto out; - } - } - free(hash); - goto out; - } - - EFI_PRINT("%s: signed image\n", __func__); if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509)) { EFI_PRINT("Signature type is not supported: %pUl\n", &siglist->sig_type); @@ -412,19 +434,6 @@ bool efi_signature_verify_one(struct efi_image_regions *regs, if (!db->sig_data_list) goto out; - /* for unsigned image */ - if (!msg) { - EFI_PRINT("%s: Verify unsigned image with db\n", __func__); - for (siglist = db; siglist; siglist = siglist->next) - if (efi_signature_verify_with_list(regs, NULL, NULL, - siglist, &cert)) { - verified = true; - break; - } - goto out; - } - - /* for signed image or variable */ EFI_PRINT("%s: Verify signed image with db\n", __func__); for (sinfo = msg->signed_infos; sinfo; sinfo = sinfo->next) { EFI_PRINT("Signed Info: digest algo: %s, pkey algo: %s\n", @@ -468,26 +477,9 @@ bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs, EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__, regs, msg, db, dbx); - if (!db) + if (!regs || !msg || !db || !db->sig_data_list) goto out; - if (!db->sig_data_list) - goto out; - - /* for unsigned image */ - if (!msg) { - EFI_PRINT("%s: Verify unsigned image with db\n", __func__); - for (siglist = db; siglist; siglist = siglist->next) - if (efi_signature_verify_with_list(regs, NULL, NULL, - siglist, &cert)) { - verified = true; - break; - } - goto out; - } - - /* for signed image or variable */ - EFI_PRINT("%s: Verify signed image with db\n", __func__); for (info = msg->signed_infos; info; info = info->next) { EFI_PRINT("Signed Info: digest algo: %s, pkey algo: %s\n", info->sig->hash_algo, info->sig->pkey_algo);