Re: [RFC PATCH 1/2] efi_loader: fix dual signed image certification

AKASHI Takahiro
2022-02-10T07:36:48Z

On Thu, Feb 10, 2022 at 09:13:34AM +0200, Ilias Apalodimas wrote:
> On Thu, Feb 10, 2022 at 02:13:48PM +0900, AKASHI Takahiro wrote:
> > Hi Ilias,
> >
> > Thank you for reviewing the logic.
> >
> > On Fri, Feb 04, 2022 at 09:32:01AM +0200, Ilias Apalodimas wrote:
> > > The EFI spec allows for images to carry multiple signatures. Currently
> > > we don't adhere to the verification process for such images.
> >
> > In this patch, you're trying to do three things:
> > * remove efi_image_unsigned_authenticate()
> > * pull efi_signature_lookup_digest() out of a while loop
> > * change the logic of authentication
> >
> > I'd prefer to see those changes in separate patches for better reviewing.
>
> I tried both and the current one seemed easier to review. Heinrich any
> preference?

Those three changes are basically independent from each other.
Such changes should be in speparate patchs.
I believe it is what Heinrich always requires me to do.

> >
> > > The spec says:
> > > "Multiple signatures are allowed to exist in the binary's certificate
> > > table (as per PE/COFF Section "Attribute Certificate Table"). Only one
> > > hash or signature is required to be present in db in order to pass
> > > validation, so long as neither the SHA-256 hash of the binary nor any
> > > present signature is reflected in dbx."
> >
> > I have some concern about what the last phrase, "neither the SHA-256 hash
> > of the binary nor any present signature is reflected in dbx" means.
> > See the comment below.
> >
> > > With our current implementation signing the image with two certificates
> > > and inserting both of them in db and one of them dbx doesn't always reject
> > > the image. The rejection depends on the order that the image was signed
> > > and the order the certificates are read (and checked) in db.
> > >
> > > While at it move the sha256 hash verification outside the signature
> > > checking loop, since it only needs to run once per image and get simplify
> > > the logic for authenticating an unsigned imahe using sha256 hashes.
> > >
> > > Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> > > ---
> > > lib/efi_loader/efi_image_loader.c | 88 +++++++------------------------
> > > 1 file changed, 18 insertions(+), 70 deletions(-)
> > >
> > > diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
> > > index f41cfa4fccd5..5df35939f702 100644
> > > --- a/lib/efi_loader/efi_image_loader.c
> > > +++ b/lib/efi_loader/efi_image_loader.c
> > > @@ -516,53 +516,6 @@ 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(u"dbx");
> > > - if (!dbx) {
> > > - EFI_PRINT("Getting signature database(dbx) failed\n");
> > > - goto out;
> > > - }
> > > -
> > > - db = efi_sigstore_parse_sigdb(u"db");
> > > - if (!db) {
> > > - EFI_PRINT("Getting signature database(db) failed\n");
> > > - goto out;
> > > - }
> > > -
> > > - /* try black-list first */
> > > - if (efi_signature_lookup_digest(regs, dbx, true)) {
> > > - 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, false))
> > > - 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
> > > @@ -608,14 +561,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 +570,18 @@ static bool efi_image_authenticate(void *efi, size_t efi_size)
> > > db = efi_sigstore_parse_sigdb(u"db");
> > > if (!db) {
> > > EFI_PRINT("Getting signature database(db) failed\n");
> > > - goto err;
> > > + goto out;
> > > }
> > >
> > > dbx = efi_sigstore_parse_sigdb(u"dbx");
> > > if (!dbx) {
> > > EFI_PRINT("Getting signature database(dbx) failed\n");
> > > - goto err;
> > > + goto out;
> > > }
> > >
> > > if (efi_signature_lookup_digest(regs, dbx, true)) {
> > > EFI_PRINT("Image's digest was found in \"dbx\"\n");
> > > - goto err;
> > > + goto out;
> > > }
> > >
> > > /*
> > > @@ -678,7 +624,8 @@ static bool efi_image_authenticate(void *efi, size_t efi_size)
> > > if (guidcmp(auth, &efi_guid_cert_type_pkcs7)) {
> > > EFI_PRINT("Certificate type not supported: %pUs\n",
> > > auth);
> > > - continue;
> > > + ret = false;
> > > + goto out;
> >
> > Why should we break the loop here?
>
> We were trying to reject signature verification that we don't support,
> since the equivalent cert might be in dbx. But I am not 100% sure taht's
> what we want here.
>
> >
> > > }
> > >
> > > auth += sizeof(efi_guid_t);
> > > @@ -686,7 +633,8 @@ static bool efi_image_authenticate(void *efi, size_t efi_size)
> > > } else if (wincert->wCertificateType
> > > != WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
> > > EFI_PRINT("Certificate type not supported\n");
> > > - continue;
> > > + ret = false;
> > > + goto out;
> > > }
> > >
> > > msg = pkcs7_parse_message(auth, auth_size);
> > > @@ -717,32 +665,32 @@ static bool efi_image_authenticate(void *efi, size_t efi_size)
> > > */
> > > /* try black-list first */
> > > if (efi_signature_verify_one(regs, msg, dbx)) {
> > > + ret = false;
> > > EFI_PRINT("Signature was rejected by \"dbx\"\n");
> > > - continue;
> > > + goto out;
> >
> > If we go to "out" here, we have no chance to verify some cases:
> > 1) An image has two signatures, for instance, one signed by SHA1 cert
> > and the other signed by SHA256 cert. A user wants to reject SHA1 cert
> > and put the cert in dbx.
>
> I am not sure I am following, what does he gain be rejecting the SHA1
> portion only? Avoid potential collisions?

I will reply to Heinrich's comment later.

-Takahiro Akashi

> > But this image can and should yet be verified by SHA256 cert.
>
> Why should it be verified? My understanding of the EFI spec is that any match
> in dbx of any certificate in the signing chain of the signature being verified means
> reject the image.
>
> > 2) A user knows that a given image is safe for some reason even though
> > he or she doesn't trust the certficate which is used for signing
> > the image.
> >
> > -Takahiro Akashi
> >
> > > }
> > >
> > > if (!efi_signature_check_signers(msg, dbx)) {
> > > + ret = false;
> > > EFI_PRINT("Signer(s) in \"dbx\"\n");
> > > - continue;
> > > + goto out;
> > > }
> > >
> > > /* try white-list */
> > > if (efi_signature_verify(regs, msg, db, dbx)) {
> > > ret = true;
> > > - break;
> > > + continue;
> > > }
> > >
> > > EFI_PRINT("Signature was not verified by \"db\"\n");
> > > + }
> > >
> > > - if (efi_signature_lookup_digest(regs, db, false)) {
> > > - ret = true;
> > > - break;
> > > - }
> > >
> > > - EFI_PRINT("Image's digest was not found in \"db\" or \"dbx\"\n");
> > > - }
> > > + /* last resort try the image sha256 hash in db */
> > > + if (!ret && efi_signature_lookup_digest(regs, db, false))
> > > + ret = true;
> > >
> > > -err:
> > > +out:
> > > efi_sigstore_free(db);
> > > efi_sigstore_free(dbx);
> > > pkcs7_free_message(msg);
> > > --
> > > 2.32.0
> > >
>
> Thanks
> /Ilias