diff options
author | Jan Janssen <medhefgo@web.de> | 2022-01-07 11:15:28 +0100 |
---|---|---|
committer | Jan Janssen <medhefgo@web.de> | 2022-01-10 16:40:16 +0100 |
commit | 661615a0afacee3545cde0a48286c0fef983f8fe (patch) | |
tree | 461f2ebc3066c9d873428db05f270f69c15c5df0 /src/boot | |
parent | boot: Add TPM to status info (diff) | |
download | systemd-661615a0afacee3545cde0a48286c0fef983f8fe.tar.xz systemd-661615a0afacee3545cde0a48286c0fef983f8fe.zip |
boot: Add BitLocker TPM key sealing workaround
Fixes: #21891
Diffstat (limited to 'src/boot')
-rw-r--r-- | src/boot/efi/boot.c | 122 |
1 files changed, 106 insertions, 16 deletions
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 32923c23ed..4dec054536 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -85,6 +85,7 @@ typedef struct { BOOLEAN editor; BOOLEAN auto_entries; BOOLEAN auto_firmware; + BOOLEAN reboot_for_bitlocker; BOOLEAN force_menu; BOOLEAN use_saved_entry; BOOLEAN use_saved_entry_efivar; @@ -496,6 +497,7 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) { ps_bool(L" editor: %s\n", config->editor); ps_bool(L" auto-entries: %s\n", config->auto_entries); ps_bool(L" auto-firmware: %s\n", config->auto_firmware); + ps_bool(L" reboot-for-bitlocker: %s\n", config->reboot_for_bitlocker); ps_string(L" random-seed-mode: %s\n", random_seed_modes_table[config->random_seed_mode]); switch (config->console_mode) { @@ -1142,6 +1144,13 @@ static void config_defaults_load_from_file(Config *config, CHAR8 *content) { continue; } + if (strcmpa((CHAR8 *)"reboot-for-bitlocker", key) == 0) { + err = parse_boolean(value, &config->reboot_for_bitlocker); + if (EFI_ERROR(err)) + log_error_stall(L"Error parsing 'reboot-for-bitlocker' config option: %a", value); + continue; + } + if (strcmpa((CHAR8 *)"console-mode", key) == 0) { if (strcmpa((CHAR8 *)"auto", value) == 0) config->console_mode = CONSOLE_MODE_AUTO; @@ -1512,6 +1521,7 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) { .editor = TRUE, .auto_entries = TRUE, .auto_firmware = TRUE, + .reboot_for_bitlocker = TRUE, .random_seed_mode = RANDOM_SEED_WITH_SYSTEM_TOKEN, .idx_default_efivar = IDX_INVALID, .console_mode = CONSOLE_MODE_KEEP, @@ -1860,7 +1870,7 @@ static BOOLEAN is_sd_boot(EFI_FILE *root_dir, const CHAR16 *loader_path) { return CompareMem(content, magic, sizeof(magic)) == 0; } -static BOOLEAN config_entry_add_loader_auto( +static ConfigEntry *config_entry_add_loader_auto( Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, @@ -1871,7 +1881,6 @@ static BOOLEAN config_entry_add_loader_auto( const CHAR16 *loader) { EFI_FILE_HANDLE handle; - ConfigEntry *entry; EFI_STATUS err; assert(config); @@ -1882,7 +1891,7 @@ static BOOLEAN config_entry_add_loader_auto( assert(loader || loaded_image_path); if (!config->auto_entries) - return FALSE; + return NULL; if (loaded_image_path) { loader = L"\\EFI\\BOOT\\BOOT" EFI_MACHINE_TYPE_NAME ".efi"; @@ -1895,20 +1904,16 @@ static BOOLEAN config_entry_add_loader_auto( if (StriCmp(loader, loaded_image_path) == 0 || is_sd_boot(root_dir, loader) || is_sd_boot(root_dir, L"\\EFI\\BOOT\\GRUB" EFI_MACHINE_TYPE_NAME L".EFI")) - return FALSE; + return NULL; } /* check existence */ err = root_dir->Open(root_dir, &handle, (CHAR16*) loader, EFI_FILE_MODE_READ, 0ULL); if (EFI_ERROR(err)) - return FALSE; + return NULL; handle->Close(handle); - entry = config_entry_add_loader(config, device, LOADER_AUTO, id, key, title, loader, NULL); - if (!entry) - return FALSE; - - return TRUE; + return config_entry_add_loader(config, device, LOADER_AUTO, id, key, title, loader, NULL); } static void config_entry_add_osx(Config *config) { @@ -1943,6 +1948,83 @@ static void config_entry_add_osx(Config *config) { } } +static EFI_STATUS boot_windows_bitlocker(void) { + _cleanup_freepool_ EFI_HANDLE *handles = NULL; + UINTN n_handles; + EFI_STATUS err; + + /* BitLocker key cannot be sealed without a TPM present. */ + if (!tpm_present()) + return EFI_NOT_FOUND; + + err = BS->LocateHandleBuffer(ByProtocol, &BlockIoProtocol, NULL, &n_handles, &handles); + if (EFI_ERROR(err)) + return err; + + /* Look for BitLocker magic string on all block drives. */ + BOOLEAN found = FALSE; + for (UINTN i = 0; i < n_handles; i++) { + EFI_BLOCK_IO *block_io; + err = BS->HandleProtocol(handles[i], &BlockIoProtocol, (void **) &block_io); + if (EFI_ERROR(err) || block_io->Media->BlockSize < 512) + continue; + + CHAR8 buf[block_io->Media->BlockSize]; + block_io->ReadBlocks(block_io, block_io->Media->MediaId, 0, sizeof(buf), buf); + if (EFI_ERROR(err)) + continue; + + if (CompareMem(buf + 3, "-FVE-FS-", STRLEN("-FVE-FS-")) == 0) { + found = TRUE; + break; + } + } + + /* If no BitLocker drive was found, we can just chainload bootmgfw.efi directly. */ + if (!found) + return EFI_NOT_FOUND; + + _cleanup_freepool_ UINT16 *boot_order = NULL; + UINTN boot_order_size; + + /* There can be gaps in Boot#### entries. Instead of iterating over the full + * EFI var list or UINT16 namespace, just look for "Windows Boot Manager" in BootOrder. */ + err = efivar_get_raw(EFI_GLOBAL_GUID, L"BootOrder", (CHAR8 **) &boot_order, &boot_order_size); + if (EFI_ERROR(err) || boot_order_size % sizeof(UINT16) != 0) + return err; + + for (UINTN i = 0; i < boot_order_size / sizeof(UINT16); i++) { + _cleanup_freepool_ CHAR8 *buf = NULL; + CHAR16 name[sizeof(L"Boot0000")]; + UINTN buf_size; + + SPrint(name, sizeof(name), L"Boot%04x", boot_order[i]); + err = efivar_get_raw(EFI_GLOBAL_GUID, name, &buf, &buf_size); + if (EFI_ERROR(err)) + continue; + + /* Boot#### are EFI_LOAD_OPTION. But we really are only interested + * for the description, which is at this offset. */ + UINTN offset = sizeof(UINT32) + sizeof(UINT16); + if (buf_size < offset + sizeof(CHAR16)) + continue; + + if (streq((CHAR16 *) (buf + offset), L"Windows Boot Manager")) { + err = efivar_set_raw( + EFI_GLOBAL_GUID, + L"BootNext", + boot_order + i, + sizeof(boot_order[i]), + EFI_VARIABLE_NON_VOLATILE); + if (EFI_ERROR(err)) + return err; + return RT->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, NULL); + } + } + + return EFI_NOT_FOUND; +} + static void config_entry_add_windows(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir) { #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) _cleanup_freepool_ CHAR8 *bcd = NULL; @@ -1962,9 +2044,12 @@ static void config_entry_add_windows(Config *config, EFI_HANDLE *device, EFI_FIL if (!EFI_ERROR(err)) title = get_bcd_title((UINT8 *) bcd, len); - config_entry_add_loader_auto(config, device, root_dir, NULL, - L"auto-windows", 'w', title ?: L"Windows Boot Manager", - L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi"); + ConfigEntry *e = config_entry_add_loader_auto(config, device, root_dir, NULL, + L"auto-windows", 'w', title ?: L"Windows Boot Manager", + L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi"); + + if (config->reboot_for_bitlocker) + e->call = boot_windows_bitlocker; #endif } @@ -2161,6 +2246,10 @@ static EFI_STATUS image_start( assert(config); assert(entry); + /* If this loader entry has a special way to boot, try that first. */ + if (entry->call) + (void) entry->call(); + path = FileDevicePath(entry->device, entry->loader); if (!path) return log_error_status_stall(EFI_INVALID_PARAMETER, L"Error getting device path."); @@ -2244,7 +2333,7 @@ static void config_write_entries_to_variable(Config *config) { static void save_selected_entry(const Config *config, const ConfigEntry *entry) { assert(config); assert(entry); - assert(!entry->call); + assert(entry->loader || !entry->call); /* Always export the selected boot entry to the system in a volatile var. */ (void) efivar_set(LOADER_GUID, L"LoaderEntrySelected", entry->id, 0); @@ -2432,8 +2521,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { break; } - /* run special entry like "reboot" */ - if (entry->call) { + /* Run special entry like "reboot" now. Those that have a loader + * will be handled by image_start() instead. */ + if (entry->call && !entry->loader) { entry->call(); continue; } |