From: Ana Bobillo <[email protected]>

efi_image_authenticate() walks the certificate table of an image by
dwLength, rounded up to a multiple of 8, without checking it:

- a dwLength of 0, or one that wraps around to 0 when rounded up,
  does not advance the walk, so U-Boot hangs on such an image;
- a dwLength larger than the rest of the table makes
  pkcs7_parse_message() read past the end of the table and of the
  image.

As EDK2's DxeImageVerificationLib does, reject an image whose
certificate table holds a certificate shorter than its header or
longer than the rest of the table.

Fixes: 4540dabdcaca ("efi_loader: image_loader: support image authentication")
Fixes: 1a44b7059c18 ("efi_loader: image_loader: add a check against certificate 
type of authenticode")
Signed-off-by: Ana Bobillo <[email protected]>
---
 lib/efi_loader/efi_image_loader.c | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/lib/efi_loader/efi_image_loader.c 
b/lib/efi_loader/efi_image_loader.c
index c0677b440e8..c4f64f102e8 100644
--- a/lib/efi_loader/efi_image_loader.c
+++ b/lib/efi_loader/efi_image_loader.c
@@ -822,10 +822,27 @@ static bool efi_image_authenticate(void *efi, size_t 
efi_size)
        for (wincert = wincerts, wincerts_end = (u8 *)wincerts + wincerts_len;
             (u8 *)wincert < wincerts_end;
             wincert = (WIN_CERTIFICATE *)
-                       ((u8 *)wincert + ALIGN(wincert->dwLength, 8))) {
-               if ((u8 *)wincert + sizeof(*wincert) >= wincerts_end)
+                       ((u8 *)wincert + ALIGN((size_t)wincert->dwLength, 8))) {
+               size_t remaining = wincerts_end - (u8 *)wincert;
+
+               if (sizeof(*wincert) >= remaining)
                        break;
 
+               /*
+                * dwLength includes the WIN_CERTIFICATE header. A shorter one
+                * would not advance the walk, and one longer than the rest of
+                * the certificate table would make the signature run past its
+                * end. Like EDK2, reject an image with such a corrupted
+                * certificate table.
+                */
+               if (wincert->dwLength < sizeof(*wincert) ||
+                   wincert->dwLength > remaining) {
+                       log_err("Invalid certificate length: %u\n",
+                               wincert->dwLength);
+                       ret = false;
+                       goto out;
+               }
+
                if (wincert->dwLength <= sizeof(*wincert)) {
                        log_debug("dwLength too small: %u < %zu\n",
                                  wincert->dwLength, sizeof(*wincert));

Reply via email to