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

AKASHI Takahiro takahiro.akashi at linaro.org
Thu Feb 10 08:36:35 CET 2022


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 at 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


More information about the U-Boot mailing list