diff mbox series

[v2,11/17] efi_loader: image_loader: add digest-based verification for signed image

Message ID 20200609050947.17861-12-takahiro.akashi@linaro.org
State Superseded
Headers show
Series efi_loader: rework/improve UEFI secure boot code | expand

Commit Message

AKASHI Takahiro June 9, 2020, 5:09 a.m. UTC
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 <takahiro.akashi at linaro.org>
---
 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 mbox series

Patch

diff --git a/include/efi_loader.h b/include/efi_loader.h
index 9f49a8a349fa..a95f9b339027 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -762,6 +762,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 222396b49eda..584a841116df 100644
--- a/lib/efi_loader/efi_image_loader.c
+++ b/lib/efi_loader/efi_image_loader.c
@@ -449,16 +449,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);
@@ -606,6 +606,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");
@@ -617,11 +636,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 f76c24a6fa25..a87cbe520f56 100644
--- a/lib/efi_loader/efi_signature.c
+++ b/lib/efi_loader/efi_signature.c
@@ -199,55 +199,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
@@ -255,18 +243,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);
@@ -413,19 +435,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",
@@ -469,26 +478,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);