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 | 128 ++++++++++++++---------------- 3 files changed, 99 insertions(+), 75 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 @@ -198,55 +198,43 @@ out: } /** - * efi_signature_verify_with_list - verify a signature with signature list - * @regs: List of regions to be authenticated - * @msg: Signature - * @signed_info: Pointer to PKCS7's signed_info - * @siglist: Signature list for certificates - * @valid_cert: x509 certificate that verifies this signature + * 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 * - * Signature pointed to by @signed_info against image pointed to by @regs - * is verified by signature list pointed to by @siglist. - * Signature database is a simple concatenation of one or more - * signature list(s). + * 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 signature is verified, false if not + * Return: true if found, false if not */ -static -bool efi_signature_verify_with_list(struct efi_image_regions *regs, - struct pkcs7_message *msg, - struct pkcs7_signed_info *signed_info, - struct efi_signature_store *siglist, - struct x509_certificate **valid_cert) +bool efi_signature_lookup_digest(struct efi_image_regions *regs, + struct efi_signature_store *db) { - struct x509_certificate *cert; + struct efi_signature_store *siglist; struct efi_sig_data *sig_data; - bool verified = false; + void *hash = NULL; + size_t size = 0; + bool found = false; - EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__, - regs, signed_info, siglist, valid_cert); + EFI_PRINT("%s: Enter, %p, %p\n", __func__, regs, db); - if (!signed_info) { - void *hash = NULL; - size_t size; + if (!regs || !db || !db->sig_data_list) + goto out; - EFI_PRINT("%s: unsigned image\n", __func__); - /* - * verify based on calculated hash value - * TODO: support other hash algorithms - */ + 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); - goto out; + break; } if (!efi_hash_regions(regs->reg, regs->num, &hash, &size)) { - EFI_PRINT("Digesting unsigned image failed\n"); - goto out; + EFI_PRINT("Digesting an image failed\n"); + break; } - /* go through the list */ for (sig_data = siglist->sig_data_list; sig_data; sig_data = sig_data->next) { #ifdef DEBUG @@ -254,18 +242,52 @@ bool efi_signature_verify_with_list(struct efi_image_regions *regs, print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, sig_data->data, sig_data->size, false); #endif - if ((sig_data->size == size) && + if (sig_data->size == size && !memcmp(sig_data->data, hash, size)) { - verified = true; + found = true; free(hash); goto out; } } + free(hash); - goto out; + hash = NULL; } - EFI_PRINT("%s: signed image\n", __func__); +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 + * @msg: Signature + * @signed_info: Pointer to PKCS7's signed_info + * @siglist: Signature list for certificates + * @valid_cert: x509 certificate that verifies this signature + * + * Signature pointed to by @signed_info against image pointed to by @regs + * is verified by signature list pointed to by @siglist. + * Signature database is a simple concatenation of one or more + * signature list(s). + * + * Return: true if signature is verified, false if not + */ +static +bool efi_signature_verify_with_list(struct efi_image_regions *regs, + struct pkcs7_message *msg, + struct pkcs7_signed_info *signed_info, + struct efi_signature_store *siglist, + struct x509_certificate **valid_cert) +{ + struct x509_certificate *cert; + struct efi_sig_data *sig_data; + bool verified = false; + + EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__, + regs, signed_info, siglist, valid_cert); + 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) - goto out; - - if (!db->sig_data_list) + if (!regs || !msg || !db || !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); -- 2.39.5