diff mbox series

[1/1] efi_loader: clean up uefi secure boot image verification logic

Message ID 20220125144629.712580-1-ilias.apalodimas@linaro.org
State New
Headers show
Series [1/1] efi_loader: clean up uefi secure boot image verification logic | expand

Commit Message

Ilias Apalodimas Jan. 25, 2022, 2:46 p.m. UTC
We currently distinguish between signed and non signed PE/COFF executables
while trying to authenticate signatures and/or sha256 hashes in db and dbx.
That code duplication can be avoided.  When checking for sha256 hashes,
in db,  we don't really care if the image is signed or not.  The sequence
for verifying the binary can be
 1. Is the sha256 of the image in dbx,  reject the image
 2. Certification checking
  2a. Is the image signed with a certificate that's found in db and
      not in dbx
  2b. The image carries a cert which is signed by a cert in db (and
      not in dbx) and the image can be verified against the former
 3. Is the sha256 of the image in db

So weed out the 'special' case,  which is handling unsigned images.

While at it move the checking of a hash outside the certificate
verification loop which isn't really needed and check for the hash
only once.  Also allow a mix of signatures and hashes in db instead
of explicitly breaking out the SHA256 verification loop if a signature
happens to be the present before the hash.

It's worth noting that (2a) only works if the certificate is self
signed,  but that can be fixed in a following patch.

Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
---
changes since RFC:
- added a comment indication sha256 approval is the last resort case
- reworded the commit message, based on Takahiro's proposal

 lib/efi_loader/efi_image_loader.c | 89 +++++++------------------------
 lib/efi_loader/efi_signature.c    |  2 +-
 2 files changed, 20 insertions(+), 71 deletions(-)

Comments

Ilias Apalodimas Jan. 27, 2022, 9:47 p.m. UTC | #1
Heinrich,

On Tue, 25 Jan 2022 at 16:46, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>
> We currently distinguish between signed and non signed PE/COFF executables
> while trying to authenticate signatures and/or sha256 hashes in db and dbx.
> That code duplication can be avoided.  When checking for sha256 hashes,
> in db,  we don't really care if the image is signed or not.  The sequence
> for verifying the binary can be
>  1. Is the sha256 of the image in dbx,  reject the image
>  2. Certification checking
>   2a. Is the image signed with a certificate that's found in db and
>       not in dbx
>   2b. The image carries a cert which is signed by a cert in db (and
>       not in dbx) and the image can be verified against the former
>  3. Is the sha256 of the image in db
>
> So weed out the 'special' case,  which is handling unsigned images.
>
> While at it move the checking of a hash outside the certificate
> verification loop which isn't really needed and check for the hash
> only once.  Also allow a mix of signatures and hashes in db instead
> of explicitly breaking out the SHA256 verification loop if a signature
> happens to be the present before the hash.
>
> It's worth noting that (2a) only works if the certificate is self
> signed,  but that can be fixed in a following patch.

I've found more unsupported cases in our existing code while testing.
Please ignore this patchset I'll send updates which supersede it

Thanks
/Ilias
>
> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> ---
> changes since RFC:
> - added a comment indication sha256 approval is the last resort case
> - reworded the commit message, based on Takahiro's proposal
>
>  lib/efi_loader/efi_image_loader.c | 89 +++++++------------------------
>  lib/efi_loader/efi_signature.c    |  2 +-
>  2 files changed, 20 insertions(+), 71 deletions(-)
>
> diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
> index 255613eb72ba..90594acf9623 100644
> --- a/lib/efi_loader/efi_image_loader.c
> +++ b/lib/efi_loader/efi_image_loader.c
> @@ -516,63 +516,17 @@ err:
>  }
>
>  #ifdef CONFIG_EFI_SECURE_BOOT
> -/**
> - * efi_image_unsigned_authenticate() - authenticate unsigned image with
> - * SHA256 hash
> - * @regs:      List of regions to be verified
> - *
> - * If an image is not signed, it doesn't have a signature. In this case,
> - * its message digest is calculated and it will be compared with one of
> - * hash values stored in signature databases.
> - *
> - * Return:     true if authenticated, false if not
> - */
> -static bool efi_image_unsigned_authenticate(struct efi_image_regions *regs)
> -{
> -       struct efi_signature_store *db = NULL, *dbx = NULL;
> -       bool ret = false;
> -
> -       dbx = efi_sigstore_parse_sigdb(L"dbx");
> -       if (!dbx) {
> -               EFI_PRINT("Getting signature database(dbx) failed\n");
> -               goto out;
> -       }
> -
> -       db = efi_sigstore_parse_sigdb(L"db");
> -       if (!db) {
> -               EFI_PRINT("Getting signature database(db) failed\n");
> -               goto out;
> -       }
> -
> -       /* try black-list first */
> -       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_lookup_digest(regs, db))
> -               ret = true;
> -       else
> -               EFI_PRINT("Image is not signed and its digest not found in \"db\" or \"dbx\"\n");
> -
> -out:
> -       efi_sigstore_free(db);
> -       efi_sigstore_free(dbx);
> -
> -       return ret;
> -}
> -
>  /**
>   * efi_image_authenticate() - verify a signature of signed image
>   * @efi:       Pointer to image
>   * @efi_size:  Size of @efi
>   *
> - * A signed image should have its signature stored in a table of its PE header.
> + * An image should have its signature stored in the image certificate table
> + *
>   * So if an image is signed and only if if its signature is verified using
> - * signature databases, an image is authenticated.
> - * If an image is not signed, its validity is checked by using
> - * efi_image_unsigned_authenticated().
> + * signature databases or if it's sha256 is found in db an image is
> + * authenticated.
> + *
>   * TODO:
>   * When AuditMode==0, if the image's signature is not found in
>   * the authorized database, or is found in the forbidden database,
> @@ -608,14 +562,7 @@ static bool efi_image_authenticate(void *efi, size_t efi_size)
>         if (!efi_image_parse(new_efi, efi_size, &regs, &wincerts,
>                              &wincerts_len)) {
>                 EFI_PRINT("Parsing PE executable image failed\n");
> -               goto err;
> -       }
> -
> -       if (!wincerts) {
> -               /* The image is not signed */
> -               ret = efi_image_unsigned_authenticate(regs);
> -
> -               goto err;
> +               goto out;
>         }
>
>         /*
> @@ -624,18 +571,18 @@ static bool efi_image_authenticate(void *efi, size_t efi_size)
>         db = efi_sigstore_parse_sigdb(L"db");
>         if (!db) {
>                 EFI_PRINT("Getting signature database(db) failed\n");
> -               goto err;
> +               goto out;
>         }
>
>         dbx = efi_sigstore_parse_sigdb(L"dbx");
>         if (!dbx) {
>                 EFI_PRINT("Getting signature database(dbx) failed\n");
> -               goto err;
> +               goto out;
>         }
>
>         if (efi_signature_lookup_digest(regs, dbx)) {
>                 EFI_PRINT("Image's digest was found in \"dbx\"\n");
> -               goto err;
> +               goto out;
>         }
>
>         /*
> @@ -729,20 +676,22 @@ static bool efi_image_authenticate(void *efi, size_t efi_size)
>                 /* try white-list */
>                 if (efi_signature_verify(regs, msg, db, dbx)) {
>                         ret = true;
> -                       break;
> +                       goto out;
>                 }
>
>                 EFI_PRINT("Signature was not verified by \"db\"\n");
>
> -               if (efi_signature_lookup_digest(regs, db)) {
> -                       ret = true;
> -                       break;
> -               }
> -
> -               EFI_PRINT("Image's digest was not found in \"db\" or \"dbx\"\n");
>         }
>
> -err:
> +       /*
> +        * This our last resort,  the image is not found in dbx and is not
> +        * authenticated by any certs in db.  Try the image hash in db
> +        */
> +       if (efi_signature_lookup_digest(regs, db))
> +               ret = true;
> +       else
> +               EFI_PRINT("Image's digest was not found in \"db\" or \"dbx\"\n");
> +out:
>         efi_sigstore_free(db);
>         efi_sigstore_free(dbx);
>         pkcs7_free_message(msg);
> diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c
> index 3243e2c60de0..8fa82f8cea4c 100644
> --- a/lib/efi_loader/efi_signature.c
> +++ b/lib/efi_loader/efi_signature.c
> @@ -176,7 +176,7 @@ bool efi_signature_lookup_digest(struct efi_image_regions *regs,
>                 if (guidcmp(&siglist->sig_type, &efi_guid_sha256)) {
>                         EFI_PRINT("Digest algorithm is not supported: %pUs\n",
>                                   &siglist->sig_type);
> -                       break;
> +                       continue;
>                 }
>
>                 if (!efi_hash_regions(regs->reg, regs->num, &hash, &size)) {
> --
> 2.30.2
>
diff mbox series

Patch

diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
index 255613eb72ba..90594acf9623 100644
--- a/lib/efi_loader/efi_image_loader.c
+++ b/lib/efi_loader/efi_image_loader.c
@@ -516,63 +516,17 @@  err:
 }
 
 #ifdef CONFIG_EFI_SECURE_BOOT
-/**
- * efi_image_unsigned_authenticate() - authenticate unsigned image with
- * SHA256 hash
- * @regs:	List of regions to be verified
- *
- * If an image is not signed, it doesn't have a signature. In this case,
- * its message digest is calculated and it will be compared with one of
- * hash values stored in signature databases.
- *
- * Return:	true if authenticated, false if not
- */
-static bool efi_image_unsigned_authenticate(struct efi_image_regions *regs)
-{
-	struct efi_signature_store *db = NULL, *dbx = NULL;
-	bool ret = false;
-
-	dbx = efi_sigstore_parse_sigdb(L"dbx");
-	if (!dbx) {
-		EFI_PRINT("Getting signature database(dbx) failed\n");
-		goto out;
-	}
-
-	db = efi_sigstore_parse_sigdb(L"db");
-	if (!db) {
-		EFI_PRINT("Getting signature database(db) failed\n");
-		goto out;
-	}
-
-	/* try black-list first */
-	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_lookup_digest(regs, db))
-		ret = true;
-	else
-		EFI_PRINT("Image is not signed and its digest not found in \"db\" or \"dbx\"\n");
-
-out:
-	efi_sigstore_free(db);
-	efi_sigstore_free(dbx);
-
-	return ret;
-}
-
 /**
  * efi_image_authenticate() - verify a signature of signed image
  * @efi:	Pointer to image
  * @efi_size:	Size of @efi
  *
- * A signed image should have its signature stored in a table of its PE header.
+ * An image should have its signature stored in the image certificate table
+ *
  * So if an image is signed and only if if its signature is verified using
- * signature databases, an image is authenticated.
- * If an image is not signed, its validity is checked by using
- * efi_image_unsigned_authenticated().
+ * signature databases or if it's sha256 is found in db an image is
+ * authenticated.
+ *
  * TODO:
  * When AuditMode==0, if the image's signature is not found in
  * the authorized database, or is found in the forbidden database,
@@ -608,14 +562,7 @@  static bool efi_image_authenticate(void *efi, size_t efi_size)
 	if (!efi_image_parse(new_efi, efi_size, &regs, &wincerts,
 			     &wincerts_len)) {
 		EFI_PRINT("Parsing PE executable image failed\n");
-		goto err;
-	}
-
-	if (!wincerts) {
-		/* The image is not signed */
-		ret = efi_image_unsigned_authenticate(regs);
-
-		goto err;
+		goto out;
 	}
 
 	/*
@@ -624,18 +571,18 @@  static bool efi_image_authenticate(void *efi, size_t efi_size)
 	db = efi_sigstore_parse_sigdb(L"db");
 	if (!db) {
 		EFI_PRINT("Getting signature database(db) failed\n");
-		goto err;
+		goto out;
 	}
 
 	dbx = efi_sigstore_parse_sigdb(L"dbx");
 	if (!dbx) {
 		EFI_PRINT("Getting signature database(dbx) failed\n");
-		goto err;
+		goto out;
 	}
 
 	if (efi_signature_lookup_digest(regs, dbx)) {
 		EFI_PRINT("Image's digest was found in \"dbx\"\n");
-		goto err;
+		goto out;
 	}
 
 	/*
@@ -729,20 +676,22 @@  static bool efi_image_authenticate(void *efi, size_t efi_size)
 		/* try white-list */
 		if (efi_signature_verify(regs, msg, db, dbx)) {
 			ret = true;
-			break;
+			goto out;
 		}
 
 		EFI_PRINT("Signature was not verified by \"db\"\n");
 
-		if (efi_signature_lookup_digest(regs, db)) {
-			ret = true;
-			break;
-		}
-
-		EFI_PRINT("Image's digest was not found in \"db\" or \"dbx\"\n");
 	}
 
-err:
+	/*
+	 * This our last resort,  the image is not found in dbx and is not
+	 * authenticated by any certs in db.  Try the image hash in db
+	 */
+	if (efi_signature_lookup_digest(regs, db))
+		ret = true;
+	else
+		EFI_PRINT("Image's digest was not found in \"db\" or \"dbx\"\n");
+out:
 	efi_sigstore_free(db);
 	efi_sigstore_free(dbx);
 	pkcs7_free_message(msg);
diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c
index 3243e2c60de0..8fa82f8cea4c 100644
--- a/lib/efi_loader/efi_signature.c
+++ b/lib/efi_loader/efi_signature.c
@@ -176,7 +176,7 @@  bool efi_signature_lookup_digest(struct efi_image_regions *regs,
 		if (guidcmp(&siglist->sig_type, &efi_guid_sha256)) {
 			EFI_PRINT("Digest algorithm is not supported: %pUs\n",
 				  &siglist->sig_type);
-			break;
+			continue;
 		}
 
 		if (!efi_hash_regions(regs->reg, regs->num, &hash, &size)) {