efi/libstub: Take noinitrd cmdline argument into account for devpath initrd
authorArd Biesheuvel <ardb@kernel.org>
Tue, 4 Feb 2020 22:01:22 +0000 (22:01 +0000)
committerArd Biesheuvel <ardb@kernel.org>
Sun, 23 Feb 2020 20:59:42 +0000 (21:59 +0100)
One of the advantages of using what basically amounts to a callback
interface into the bootloader for loading the initrd is that it provides
a natural place for the bootloader or firmware to measure the initrd
contents while they are being passed to the kernel.

Unfortunately, this is not a guarantee that the initrd will in fact be
loaded and its /init invoked by the kernel, since the command line may
contain the 'noinitrd' option, in which case the initrd is ignored, but
this will not be reflected in the PCR that covers the initrd measurement.

This could be addressed by measuring the command line as well, and
including that PCR in the attestation policy, but this locks down the
command line completely, which may be too restrictive.

So let's take the noinitrd argument into account in the stub, too. This
forces any PCR that covers the initrd to assume a different value when
noinitrd is passed, allowing an attestation policy to disregard the
command line if there is no need to take its measurement into account
for other reasons.

As Peter points out, this would still require the agent that takes the
measurements to measure a separator event into the PCR in question at
ExitBootServices() time, to prevent replay attacks using the known
measurement from the TPM log.

Cc: Peter Jones <pjones@redhat.com>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
drivers/firmware/efi/libstub/arm-stub.c
drivers/firmware/efi/libstub/efi-stub-helper.c
drivers/firmware/efi/libstub/efistub.h
drivers/firmware/efi/libstub/x86-stub.c

index 11c673a7e95bc9c18519526b54d3b4d3017398ab..13559c7e6643ecb682c1f4a2afc1d8903ee841eb 100644 (file)
@@ -153,7 +153,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
        unsigned long image_size = 0;
        unsigned long dram_base;
        /* addr/point and size pairs for memory management*/
-       unsigned long initrd_addr;
+       unsigned long initrd_addr = 0;
        unsigned long initrd_size = 0;
        unsigned long fdt_addr = 0;  /* Original DTB */
        unsigned long fdt_size = 0;
@@ -268,18 +268,21 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
        if (!fdt_addr)
                pr_efi("Generating empty DTB\n");
 
-       max_addr = efi_get_max_initrd_addr(dram_base, image_addr);
-       status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size, max_addr);
-       if (status == EFI_SUCCESS) {
-               pr_efi("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n");
-       } else if (status == EFI_NOT_FOUND) {
-               status = efi_load_initrd(image, &initrd_addr, &initrd_size,
-                                        ULONG_MAX, max_addr);
-               if (status == EFI_SUCCESS)
-                       pr_efi("Loaded initrd from command line option\n");
+       if (!noinitrd()) {
+               max_addr = efi_get_max_initrd_addr(dram_base, image_addr);
+               status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size,
+                                                 max_addr);
+               if (status == EFI_SUCCESS) {
+                       pr_efi("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n");
+               } else if (status == EFI_NOT_FOUND) {
+                       status = efi_load_initrd(image, &initrd_addr, &initrd_size,
+                                                ULONG_MAX, max_addr);
+                       if (status == EFI_SUCCESS)
+                               pr_efi("Loaded initrd from command line option\n");
+               }
+               if (status != EFI_SUCCESS)
+                       pr_efi_err("Failed to load initrd!\n");
        }
-       if (status != EFI_SUCCESS)
-               pr_efi_err("Failed to load initrd!\n");
 
        efi_random_get_seed();
 
index 6017b968cef7da81a2c823168d0ead533c28bda0..b1da58141a4dc31024784e124a02670687b4c9a8 100644 (file)
@@ -14,6 +14,7 @@
 
 static bool __efistub_global efi_nochunk;
 static bool __efistub_global efi_nokaslr;
+static bool __efistub_global efi_noinitrd;
 static bool __efistub_global efi_quiet;
 static bool __efistub_global efi_novamap;
 static bool __efistub_global efi_nosoftreserve;
@@ -28,6 +29,10 @@ bool __pure nokaslr(void)
 {
        return efi_nokaslr;
 }
+bool __pure noinitrd(void)
+{
+       return efi_noinitrd;
+}
 bool __pure is_quiet(void)
 {
        return efi_quiet;
@@ -87,6 +92,8 @@ efi_status_t efi_parse_options(char const *cmdline)
                        efi_nokaslr = true;
                } else if (!strcmp(param, "quiet")) {
                        efi_quiet = true;
+               } else if (!strcmp(param, "noinitrd")) {
+                       efi_noinitrd = true;
                } else if (!strcmp(param, "efi") && val) {
                        efi_nochunk = parse_option_str(val, "nochunk");
                        efi_novamap = parse_option_str(val, "novamap");
index b58cb2c4474e16e098c6927762172bc68bcdda7e..2e5e79edb4d76c85e8a2f3503ec29dc9f7aab103 100644 (file)
@@ -33,6 +33,7 @@
 
 extern bool __pure nochunk(void);
 extern bool __pure nokaslr(void);
+extern bool __pure noinitrd(void);
 extern bool __pure is_quiet(void);
 extern bool __pure novamap(void);
 
index 16bf4ed21f1f30e5c645b1dc2c40a52ae1f2533c..7d4866471f86f0fa4da305af99219900e78b674b 100644 (file)
@@ -421,15 +421,18 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
        if (status != EFI_SUCCESS)
                goto fail2;
 
-       status = efi_load_initrd(image, &ramdisk_addr, &ramdisk_size,
-                                hdr->initrd_addr_max,
-                                above4g ? ULONG_MAX : hdr->initrd_addr_max);
-       if (status != EFI_SUCCESS)
-               goto fail2;
-       hdr->ramdisk_image = ramdisk_addr & 0xffffffff;
-       hdr->ramdisk_size  = ramdisk_size & 0xffffffff;
-       boot_params->ext_ramdisk_image = (u64)ramdisk_addr >> 32;
-       boot_params->ext_ramdisk_size  = (u64)ramdisk_size >> 32;
+       if (!noinitrd()) {
+               status = efi_load_initrd(image, &ramdisk_addr, &ramdisk_size,
+                                        hdr->initrd_addr_max,
+                                        above4g ? ULONG_MAX
+                                                : hdr->initrd_addr_max);
+               if (status != EFI_SUCCESS)
+                       goto fail2;
+               hdr->ramdisk_image = ramdisk_addr & 0xffffffff;
+               hdr->ramdisk_size  = ramdisk_size & 0xffffffff;
+               boot_params->ext_ramdisk_image = (u64)ramdisk_addr >> 32;
+               boot_params->ext_ramdisk_size  = (u64)ramdisk_size >> 32;
+       }
 
        efi_stub_entry(handle, sys_table, boot_params);
        /* not reached */
@@ -699,14 +702,9 @@ struct boot_params *efi_main(efi_handle_t handle,
 {
        unsigned long bzimage_addr = (unsigned long)startup_32;
        struct setup_header *hdr = &boot_params->hdr;
-       unsigned long max_addr = hdr->initrd_addr_max;
-       unsigned long initrd_addr, initrd_size;
        efi_status_t status;
        unsigned long cmdline_paddr;
 
-       if (hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G)
-               max_addr = ULONG_MAX;
-
        sys_table = sys_table_arg;
 
        /* Check if we were booted by the EFI firmware */
@@ -746,15 +744,23 @@ struct boot_params *efi_main(efi_handle_t handle,
         * permit an initrd loaded from the LINUX_EFI_INITRD_MEDIA_GUID device
         * path to supersede it.
         */
-       status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size, max_addr);
-       if (status == EFI_SUCCESS) {
-               hdr->ramdisk_image              = (u32)initrd_addr;
-               hdr->ramdisk_size               = (u32)initrd_size;
-               boot_params->ext_ramdisk_image  = (u64)initrd_addr >> 32;
-               boot_params->ext_ramdisk_size   = (u64)initrd_size >> 32;
-       } else if (status != EFI_NOT_FOUND) {
-               efi_printk("efi_load_initrd_dev_path() failed!\n");
-               goto fail;
+       if (!noinitrd()) {
+               unsigned long addr, size;
+               unsigned long max_addr = hdr->initrd_addr_max;
+
+               if (hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G)
+                       max_addr = ULONG_MAX;
+
+               status = efi_load_initrd_dev_path(&addr, &size, max_addr);
+               if (status == EFI_SUCCESS) {
+                       hdr->ramdisk_image              = (u32)addr;
+                       hdr->ramdisk_size               = (u32)size;
+                       boot_params->ext_ramdisk_image  = (u64)addr >> 32;
+                       boot_params->ext_ramdisk_size   = (u64)size >> 32;
+               } else if (status != EFI_NOT_FOUND) {
+                       efi_printk("efi_load_initrd_dev_path() failed!\n");
+                       goto fail;
+               }
        }
 
        /*