1
0
mirror of https://github.com/systemd/systemd.git synced 2025-02-14 05:57:40 +03:00

Merge pull request #22043 from medhefgo/boot-bitlocker

boot: Add BitLocker TPM key sealing workaround
This commit is contained in:
Luca Boccassi 2022-01-12 13:05:30 +00:00 committed by GitHub
commit e80b51dad2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 137 additions and 16 deletions

View File

@ -196,6 +196,28 @@
by using the <keycap>f</keycap> key.</para></listitem>
</varlistentry>
<varlistentry>
<term>reboot-for-bitlocker</term>
<listitem><para>Work around BitLocker requiring a recovery key when the boot loader was
updated (enabled by default).</para>
<para>Try to detect BitLocker encrypted drives along with an active TPM. If both are found
and Windows Boot Manager is selected in the boot menu, set the <literal>BootNext</literal>
EFI variable and restart the system. The firmware will then start Windows Boot Manager
directly, leaving the TPM PCRs in expected states so that Windows can unseal the encryption
key. This allows systemd-boot to be updated without having to provide the recovery key for
BitLocker drive unlocking.</para>
<para>Note that the PCRs that Windows uses can be configured with the
<literal>Configure TPM platform validation profile for native UEFI firmware configurations</literal>
group policy under <literal>Computer Configuration\Administrative Templates\Windows Components\BitLocker Drive Encryption</literal>.
When secure boot is enabled, changing this to PCRs <literal>0,2,7,11</literal> should be safe.
The TPM key protector needs to be removed and then added back for the PCRs on an already
encrypted drive to change. If PCR 4 is not measured, this setting can be disabled to speed
up booting into Windows.</para></listitem>
</varlistentry>
<varlistentry>
<term>random-seed-mode</term>

View File

@ -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;
@ -461,6 +462,7 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) {
Print(L" OS indications: %lu\n", get_os_indications_supported());
Print(L" secure boot: %s (%s)\n", yes_no(IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED)), secure_boot_mode_to_string(secure));
ps_bool(L" shim: %s\n", shim_loaded());
ps_bool(L" TPM: %s\n", tpm_present());
Print(L" console mode: %d/%d (%lu x %lu)\n", ST->ConOut->Mode->Mode, ST->ConOut->Mode->MaxMode - 1LL, x_max, y_max);
Print(L"\n--- Press any key to continue. ---\n\n");
@ -495,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) {
@ -1141,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;
@ -1510,6 +1520,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,
@ -1867,7 +1878,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,
@ -1878,7 +1889,6 @@ static BOOLEAN config_entry_add_loader_auto(
const CHAR16 *loader) {
EFI_FILE_HANDLE handle;
ConfigEntry *entry;
EFI_STATUS err;
assert(config);
@ -1889,7 +1899,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";
@ -1902,20 +1912,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) {
@ -1950,6 +1956,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;
@ -1969,9 +2052,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
}
@ -2168,6 +2254,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.");
@ -2251,7 +2341,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);
@ -2441,8 +2531,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;
}

View File

@ -135,6 +135,10 @@ static EFI_TCG2 * tcg2_interface_check(void) {
return tcg;
}
BOOLEAN tpm_present(void) {
return tcg2_interface_check() || tcg1_interface_check();
}
EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) {
EFI_TCG *tpm1;
EFI_TCG2 *tpm2;

View File

@ -5,11 +5,15 @@
#if ENABLE_TPM
BOOLEAN tpm_present(void);
EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description);
EFI_STATUS tpm_log_load_options(const CHAR16 *cmdline);
#else
static inline BOOLEAN tpm_present(void) {
return FALSE;
}
static inline EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) {
return EFI_SUCCESS;
}