From 836337d9b895da32bcbc451c84bc3a7865a15963 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Mon, 18 Apr 2022 22:16:49 +0800 Subject: [PATCH 32/32] Use grub_loader_set_ex() for secureboot chainloader This is required as many distributions, including SUSE, has been shipping a variation to load and start image using native functions than calling out efi protocols when secure boot is enabled and shim lock is used to verify image. Signed-off-by: Michael Chang --- grub-core/loader/efi/chainloader.c | 100 +++++++++++++++++++---------- 1 file changed, 66 insertions(+), 34 deletions(-) diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index b3e1e89302..48d69c7795 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -53,10 +53,6 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_dl_t my_mod; -static grub_ssize_t fsize; -static grub_ssize_t cmdline_len; -static grub_efi_handle_t dev_handle; - #ifdef SUPPORT_SECURE_BOOT static grub_efi_boolean_t debug_secureboot = 0; static grub_efi_status_t (*entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table); @@ -76,8 +72,6 @@ grub_chainloader_unload (void *context) b = grub_efi_system_table->boot_services; efi_call_1 (b->unload_image, image_handle); - dev_handle = 0; - grub_dl_unref (my_mod); return GRUB_ERR_NONE; } @@ -254,6 +248,17 @@ struct pe_coff_loader_image_context struct grub_pe32_header_no_msdos_stub *pe_hdr; }; +struct grub_secureboot_chainloader_context +{ + grub_efi_physical_address_t address; + grub_efi_uintn_t pages; + grub_ssize_t fsize; + grub_efi_device_path_t *file_path; + grub_efi_char16_t *cmdline; + grub_ssize_t cmdline_len; + grub_efi_handle_t dev_handle; +}; + typedef struct pe_coff_loader_image_context pe_coff_loader_image_context_t; struct grub_efi_shim_lock @@ -477,11 +482,13 @@ grub_efi_get_media_file_path (grub_efi_device_path_t *dp) } static grub_efi_boolean_t -handle_image (void *data, grub_efi_uint32_t datasize) +handle_image (struct grub_secureboot_chainloader_context *load_context) { grub_efi_boot_services_t *b; grub_efi_loaded_image_t *li, li_bak; grub_efi_status_t efi_status; + void *data = (void *)(unsigned long)load_context->address; + grub_efi_uint32_t datasize = load_context->fsize; char *buffer = NULL; char *buffer_aligned = NULL; grub_efi_uint32_t i, size; @@ -571,10 +578,10 @@ handle_image (void *data, grub_efi_uint32_t datasize) grub_memcpy (&li_bak, li, sizeof (grub_efi_loaded_image_t)); li->image_base = buffer_aligned; li->image_size = context.image_size; - li->load_options = cmdline; - li->load_options_size = cmdline_len; - li->file_path = grub_efi_get_media_file_path (file_path); - li->device_handle = dev_handle; + li->load_options = load_context->cmdline; + li->load_options_size = load_context->cmdline_len; + li->file_path = grub_efi_get_media_file_path (load_context->file_path); + li->device_handle = load_context->dev_handle; if (li->file_path) { grub_printf ("file path: "); @@ -605,26 +612,27 @@ error_exit: } static grub_err_t -grub_secureboot_chainloader_unload (void) +grub_secureboot_chainloader_unload (void* context) { grub_efi_boot_services_t *b; + struct grub_secureboot_chainloader_context *sb_context = (struct grub_secureboot_chainloader_context *)context; b = grub_efi_system_table->boot_services; - efi_call_2 (b->free_pages, address, pages); - grub_free (file_path); - grub_free (cmdline); - cmdline = 0; - file_path = 0; - dev_handle = 0; + efi_call_2 (b->free_pages, sb_context->address, sb_context->pages); + grub_free (sb_context->file_path); + grub_free (sb_context->cmdline); + grub_free (sb_context); grub_dl_unref (my_mod); return GRUB_ERR_NONE; } static grub_err_t -grub_secureboot_chainloader_boot (void) +grub_secureboot_chainloader_boot (void *context) { - handle_image ((void *)address, fsize); + struct grub_secureboot_chainloader_context *sb_context = (struct grub_secureboot_chainloader_context *)context; + + handle_image (sb_context); grub_loader_unset (); return grub_errno; } @@ -635,6 +643,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { grub_file_t file = 0; + grub_ssize_t size; grub_efi_status_t status; grub_efi_boot_services_t *b; grub_device_t dev = 0; @@ -646,6 +655,8 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_efi_uintn_t pages = 0; grub_efi_char16_t *cmdline = NULL; grub_efi_handle_t image_handle = NULL; + grub_ssize_t cmdline_len = 0; + grub_efi_handle_t dev_handle = 0; if (argc == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); @@ -653,8 +664,6 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_dl_ref (my_mod); - dev_handle = 0; - b = grub_efi_system_table->boot_services; if (argc > 1) @@ -732,14 +741,14 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_printf ("file path: "); grub_efi_print_device_path (file_path); - fsize = grub_file_size (file); - if (!fsize) + size = grub_file_size (file); + if (!size) { grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), filename); goto fail; } - pages = (((grub_efi_uintn_t) fsize + ((1 << 12) - 1)) >> 12); + pages = (((grub_efi_uintn_t) size + ((1 << 12) - 1)) >> 12); status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ANY_PAGES, GRUB_EFI_LOADER_CODE, @@ -753,7 +762,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), } boot_image = (void *) ((grub_addr_t) address); - if (grub_file_read (file, boot_image, fsize) != fsize) + if (grub_file_read (file, boot_image, size) != size) { if (grub_errno == GRUB_ERR_NONE) grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), @@ -763,7 +772,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), } #if defined (__i386__) || defined (__x86_64__) - if (fsize >= (grub_ssize_t) sizeof (struct grub_macho_fat_header)) + if (size >= (grub_ssize_t) sizeof (struct grub_macho_fat_header)) { struct grub_macho_fat_header *head = boot_image; if (head->magic @@ -786,30 +795,42 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), > ~grub_cpu_to_le32 (archs[i].size) || grub_cpu_to_le32 (archs[i].offset) + grub_cpu_to_le32 (archs[i].size) - > (grub_size_t) fsize) + > (grub_size_t) size) { grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), filename); goto fail; } boot_image = (char *) boot_image + grub_cpu_to_le32 (archs[i].offset); - fsize = grub_cpu_to_le32 (archs[i].size); + size = grub_cpu_to_le32 (archs[i].size); } } #endif #ifdef SUPPORT_SECURE_BOOT /* FIXME is secure boot possible also with universal binaries? */ - if (debug_secureboot || (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED && grub_secure_validate ((void *)address, fsize))) + if (debug_secureboot || (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED && grub_secure_validate ((void *)address, size))) { + struct grub_secureboot_chainloader_context *sb_context; + + sb_context = grub_malloc (sizeof (*sb_context)); + if (!sb_context) + goto fail; + sb_context->cmdline = cmdline; + sb_context->cmdline_len = cmdline_len; + sb_context->fsize = size; + sb_context->dev_handle = dev_handle; + sb_context->address = address; + sb_context->pages = pages; + sb_context->file_path = file_path; grub_file_close (file); - grub_loader_set (grub_secureboot_chainloader_boot, grub_secureboot_chainloader_unload, 0); + grub_loader_set_ex (grub_secureboot_chainloader_boot, grub_secureboot_chainloader_unload, sb_context, 0); return 0; } #endif status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path, - boot_image, fsize, + boot_image, size, &image_handle); #ifdef SUPPORT_SECURE_BOOT if (status == GRUB_EFI_SECURITY_VIOLATION && grub_efi_get_secureboot () != GRUB_EFI_SECUREBOOT_MODE_ENABLED) @@ -817,10 +838,21 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), /* If it failed with security violation while not in secure boot mode, the firmware might be broken. We try to workaround on that by forcing the SB method! (bsc#887793) */ + struct grub_secureboot_chainloader_context *sb_context; + grub_dprintf ("chain", "Possible firmware flaw! Security violation while not in secure boot mode.\n"); + sb_context = grub_malloc (sizeof (*sb_context)); + if (!sb_context) + goto fail; + sb_context->cmdline = cmdline; + sb_context->cmdline_len = cmdline_len; + sb_context->fsize = size; + sb_context->dev_handle = dev_handle; + sb_context->address = address; + sb_context->pages = pages; grub_file_close (file); - grub_loader_set (grub_secureboot_chainloader_boot, - grub_secureboot_chainloader_unload, 0); + grub_loader_set_ex (grub_secureboot_chainloader_boot, + grub_secureboot_chainloader_unload, sb_context, 0); return 0; } #endif -- 2.34.1