From cbef697a96d9fcd7e0949e4cf772955b32a92290 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 1 Oct 2013 14:03:16 -0400 Subject: Harden shim against non-participating bootloaders. It works like this: during startup of shim, we hook into the system's ExitBootServices() and StartImage(). If the system's StartImage() is called, we automatically unhook, because we're chainloading to something the system can verify. When shim's verify is called, we record what kind of certificate the image was verified against. If the call /succeeds/, we remove our hooks. If ExitBootServices() is called, we check how the bootloader verified whatever it is loading. If it was verified by its hash, we unhook everything and call the system's EBS(). If it was verified by certificate, we check if it has called shim_verify(). If it has, we unhook everything and call the system's EBS() If the bootloader has not verified anything, and is itself verified by a certificate, we display a security violation warning and halt the machine. --- replacements.c | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 replacements.c (limited to 'replacements.c') diff --git a/replacements.c b/replacements.c new file mode 100644 index 00000000..b05b2202 --- /dev/null +++ b/replacements.c @@ -0,0 +1,147 @@ +/* + * shim - trivial UEFI first-stage bootloader + * + * Copyright 2012 Red Hat, Inc + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Significant portions of this code are derived from Tianocore + * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel + * Corporation. + */ + +/* Chemical agents lend themselves to covert use in sabotage against + * which it is exceedingly difficult to visualize any really effective + * defense... I will not dwell upon this use of CBW because, as one + * pursues the possibilities of such covert uses, one discovers that the + * scenarios resemble that in which the components of a nuclear weapon + * are smuggled into New York City and assembled in the basement of the + * Empire State Building. + * In other words, once the possibility is recognized to exist, about + * all that one can do is worry about it. + * -- Dr. Ivan L Bennett, Jr., testifying before the Subcommittee on + * National Security Policy and Scientific Developments, November 20, + * 1969. + */ + +#include +#include +#include +#include "shim.h" +#include "replacements.h" + +/* oh for fuck's sakes.*/ +#ifndef EFI_SECURITY_VIOLATION +#define EFI_SECURITY_VIOLATION 26 +#endif + +static EFI_SYSTEM_TABLE *systab; + +static typeof(systab->BootServices->StartImage) system_start_image; +static typeof(systab->BootServices->Exit) system_exit; +static typeof(systab->BootServices->ExitBootServices) system_exit_boot_services; + +extern UINT8 insecure_mode; + +static void +unhook_system_services(void) +{ + if (insecure_mode) + return; + systab->BootServices->Exit = system_exit; + systab->BootServices->StartImage = system_start_image; + systab->BootServices->ExitBootServices = system_exit_boot_services; +} + +static EFI_STATUS EFIAPI +start_image(EFI_HANDLE image_handle, UINTN *exit_data_size, CHAR16 **exit_data) +{ + EFI_STATUS status; + unhook_system_services(); + status = systab->BootServices->StartImage(image_handle, exit_data_size, exit_data); + if (EFI_ERROR(status)) + hook_system_services(systab); + return status; +} + +static EFI_STATUS EFIAPI +exit_boot_services(EFI_HANDLE image_key, UINTN map_key) +{ + if (loader_is_participating || verification_method == VERIFIED_BY_HASH) { + unhook_system_services(); + EFI_STATUS status; + status = systab->BootServices->ExitBootServices(image_key, map_key); + if (status != EFI_SUCCESS) + hook_system_services(systab); + return status; + } + + Print(L"Bootloader has not verified loaded image.\n"); + Print(L"System is compromised. halting.\n"); + systab->BootServices->Stall(5000000); + systab->RuntimeServices->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION, 0, NULL); + return EFI_SECURITY_VIOLATION; +} + +static EFI_STATUS EFIAPI +exit(EFI_HANDLE ImageHandle, EFI_STATUS ExitStatus, + UINTN ExitDataSize, CHAR16 *ExitData) +{ + EFI_STATUS status; + unhook_system_services(); + + status = systab->BootServices->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData); + if (EFI_ERROR(status)) + hook_system_services(systab); + return status; +} + + +void +hook_system_services(EFI_SYSTEM_TABLE *local_systab) +{ + if (insecure_mode) + return; + systab = local_systab; + + /* We need to hook various calls to make this work... */ + + /* we need StartImage() so that we can allow chain booting to an + * image trusted by the firmware */ + system_start_image = systab->BootServices->StartImage; + systab->BootServices->StartImage = start_image; + + /* we need to hook ExitBootServices() so a) we can enforce the policy + * and b) we can unwrap when we're done. */ + system_exit_boot_services = systab->BootServices->ExitBootServices; + systab->BootServices->ExitBootServices = exit_boot_services; + + /* we need to hook Exit() so that we can allow users to quit the + * bootloader and still e.g. start a new one or run an internal + * shell. */ + system_exit = systab->BootServices->Exit; + systab->BootServices->Exit = exit; +} -- cgit v1.2.3 From 98a9957866b31d0ca457961932a9f997068f3ab3 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 4 Oct 2013 15:31:48 -0400 Subject: Unhook system services as we exit. If we never find a valid thing to boot, we need to undo the weird things we've done. Signed-off-by: Peter Jones --- replacements.c | 2 +- replacements.h | 1 + shim.c | 14 ++++++++++---- 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'replacements.c') diff --git a/replacements.c b/replacements.c index b05b2202..bac5e5d7 100644 --- a/replacements.c +++ b/replacements.c @@ -66,7 +66,7 @@ static typeof(systab->BootServices->ExitBootServices) system_exit_boot_services; extern UINT8 insecure_mode; -static void +void unhook_system_services(void) { if (insecure_mode) diff --git a/replacements.h b/replacements.h index 806c0386..5b57bc25 100644 --- a/replacements.h +++ b/replacements.h @@ -39,5 +39,6 @@ extern verification_method_t verification_method; extern int loader_is_participating; extern void hook_system_services(EFI_SYSTEM_TABLE *local_systab); +extern void unhook_system_services(void); #endif /* SHIM_REPLACEMENTS_H */ diff --git a/shim.c b/shim.c index 502a91dd..c8759a58 100644 --- a/shim.c +++ b/shim.c @@ -1767,18 +1767,24 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) efi_status = init_grub(image_handle); +#if defined(OVERRIDE_SECURITY_POLICY) + /* + * Clean up the security protocol hook + */ + security_policy_uninstall(); +#endif + /* * If we're back here then clean everything up before exiting */ uefi_call_wrapper(BS->UninstallProtocolInterface, 3, handle, &shim_lock_guid, &shim_lock_interface); -#if defined(OVERRIDE_SECURITY_POLICY) + /* - * Clean up the security protocol hook + * Remove our hooks from system services. */ - security_policy_uninstall(); -#endif + unhook_system_services(); /* * Free the space allocated for the alternative 2nd stage loader -- cgit v1.2.3 From e60f118155e55f33c3ea9045abc2c43851d82516 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 19 Nov 2013 10:20:34 -0500 Subject: Clarify meaning of insecure_mode insecure_mode was intended to indicate that the user had explicity disabled checks with mokutil, which means it wasn't the opposite of secure_mode(). Change the names to clarify this and don't show the insecure mode message unless the user has explicitly enabled that mode. Signed-off-by: Matthew Garrett --- replacements.c | 6 ------ shim.c | 12 ++++++------ 2 files changed, 6 insertions(+), 12 deletions(-) (limited to 'replacements.c') diff --git a/replacements.c b/replacements.c index bac5e5d7..5ea5c327 100644 --- a/replacements.c +++ b/replacements.c @@ -64,13 +64,9 @@ static typeof(systab->BootServices->StartImage) system_start_image; static typeof(systab->BootServices->Exit) system_exit; static typeof(systab->BootServices->ExitBootServices) system_exit_boot_services; -extern UINT8 insecure_mode; - void unhook_system_services(void) { - if (insecure_mode) - return; systab->BootServices->Exit = system_exit; systab->BootServices->StartImage = system_start_image; systab->BootServices->ExitBootServices = system_exit_boot_services; @@ -123,8 +119,6 @@ exit(EFI_HANDLE ImageHandle, EFI_STATUS ExitStatus, void hook_system_services(EFI_SYSTEM_TABLE *local_systab) { - if (insecure_mode) - return; systab = local_systab; /* We need to hook various calls to make this work... */ diff --git a/shim.c b/shim.c index 9ae1936b..524f5fc0 100644 --- a/shim.c +++ b/shim.c @@ -85,7 +85,7 @@ int loader_is_participating; #define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }} -UINT8 insecure_mode; +UINT8 user_insecure_mode; UINT8 ignore_db; typedef enum { @@ -456,7 +456,7 @@ static BOOLEAN secure_mode (void) UINT8 *Data; UINT8 sb, setupmode; - if (insecure_mode) + if (user_insecure_mode) return FALSE; status = get_variable(L"SecureBoot", &Data, &len, global_var); @@ -1534,7 +1534,7 @@ static EFI_STATUS check_mok_sb (void) UINTN MokSBStateSize = 0; UINT32 attributes; - insecure_mode = 0; + user_insecure_mode = 0; ignore_db = 0; status = get_variable_attr(L"MokSBState", &MokSBState, &MokSBStateSize, @@ -1555,7 +1555,7 @@ static EFI_STATUS check_mok_sb (void) status = EFI_ACCESS_DENIED; } else { if (*(UINT8 *)MokSBState == 1) { - insecure_mode = 1; + user_insecure_mode = 1; } } @@ -1753,10 +1753,10 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) /* * Tell the user that we're in insecure mode if necessary */ - if (!secure_mode()) { + if (user_insecure_mode) { Print(L"Booting in insecure mode\n"); uefi_call_wrapper(BS->Stall, 1, 2000000); - } else { + } else if (secure_mode()) { /* * Install our hooks for ExitBootServices() and StartImage() */ -- cgit v1.2.3 From cf90edfff585cfb6d51f0b4992c7f4fc9195c4c2 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 14 Feb 2014 17:48:01 -0500 Subject: Allow fallback to use the system's LoadImage/StartImage . Track use of the system's LoadImage(), and when the next StartImage() call is for an image the system verified, allow that to count as participating, since it has been verified by the system's db. Signed-off-by: Peter Jones --- replacements.c | 68 +++++++++++++++++++++++++++++++++++++++++++++- replacements.h | 3 +++ shim.c | 85 +++++++++++++++++++++++++++++++++++----------------------- 3 files changed, 121 insertions(+), 35 deletions(-) (limited to 'replacements.c') diff --git a/replacements.c b/replacements.c index 5ea5c327..48dc4378 100644 --- a/replacements.c +++ b/replacements.c @@ -60,26 +60,82 @@ static EFI_SYSTEM_TABLE *systab; +static typeof(systab->BootServices->LoadImage) system_load_image; static typeof(systab->BootServices->StartImage) system_start_image; static typeof(systab->BootServices->Exit) system_exit; static typeof(systab->BootServices->ExitBootServices) system_exit_boot_services; +static EFI_HANDLE last_loaded_image; + void unhook_system_services(void) { systab->BootServices->Exit = system_exit; + systab->BootServices->LoadImage = system_load_image; systab->BootServices->StartImage = system_start_image; systab->BootServices->ExitBootServices = system_exit_boot_services; } +static EFI_STATUS EFIAPI +load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle, + EFI_DEVICE_PATH *DevicePath, VOID *SourceBuffer, + UINTN SourceSize, EFI_HANDLE *ImageHandle) +{ + EFI_STATUS status; + unhook_system_services(); + + status = systab->BootServices->LoadImage(BootPolicy, + ParentImageHandle, DevicePath, + SourceBuffer, SourceSize, ImageHandle); + hook_system_services(systab); + if (EFI_ERROR(status)) + last_loaded_image = NULL; + else + last_loaded_image = *ImageHandle; + return status; +} + static EFI_STATUS EFIAPI start_image(EFI_HANDLE image_handle, UINTN *exit_data_size, CHAR16 **exit_data) { EFI_STATUS status; unhook_system_services(); + + /* We have to uninstall shim's protocol here, because if we're + * On the fallback.efi path, then our call pathway is: + * + * shim->fallback->shim->grub + * ^ ^ ^ + * | | \- gets protocol #0 + * | \- installs its protocol (#1) + * \- installs its protocol (#0) + * and if we haven't removed this, then grub will get the *first* + * shim's protocol, but it'll get the second shim's systab + * replacements. So even though it will participate and verify + * the kernel, the systab never finds out. + */ + if (image_handle == last_loaded_image) { + loader_is_participating = 1; + uninstall_shim_protocols(); + } status = systab->BootServices->StartImage(image_handle, exit_data_size, exit_data); - if (EFI_ERROR(status)) + if (EFI_ERROR(status)) { + if (image_handle == last_loaded_image) { + EFI_STATUS status2 = install_shim_protocols(); + + if (EFI_ERROR(status2)) { + Print(L"Something has gone seriously wrong: %d\n", + status2); + Print(L"shim cannot continue, sorry.\n"); + systab->BootServices->Stall(5000000); + systab->RuntimeServices->ResetSystem( + EfiResetShutdown, + EFI_SECURITY_VIOLATION, 0, NULL); + } + } hook_system_services(systab); + loader_is_participating = 0; + } return status; } @@ -123,6 +179,16 @@ hook_system_services(EFI_SYSTEM_TABLE *local_systab) /* We need to hook various calls to make this work... */ + /* We need LoadImage() hooked so that fallback.c can load shim + * without having to fake LoadImage as well. This allows it + * to call the system LoadImage(), and have us track the output + * and mark loader_is_participating in start_image. This means + * anything added by fallback has to be verified by the system db, + * which we want to preserve anyway, since that's all launching + * through BDS gives us. */ + system_load_image = systab->BootServices->LoadImage; + systab->BootServices->LoadImage = load_image; + /* we need StartImage() so that we can allow chain booting to an * image trusted by the firmware */ system_start_image = systab->BootServices->StartImage; diff --git a/replacements.h b/replacements.h index 5b57bc25..bd094240 100644 --- a/replacements.h +++ b/replacements.h @@ -41,4 +41,7 @@ extern int loader_is_participating; extern void hook_system_services(EFI_SYSTEM_TABLE *local_systab); extern void unhook_system_services(void); +extern EFI_STATUS install_shim_protocols(void); +extern void uninstall_shim_protocols(void); + #endif /* SHIM_REPLACEMENTS_H */ diff --git a/shim.c b/shim.c index cf93d654..0e18d387 100644 --- a/shim.c +++ b/shim.c @@ -1707,11 +1707,56 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) return EFI_SUCCESS; } -EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) +static SHIM_LOCK shim_lock_interface; +static EFI_HANDLE shim_lock_handle; + +EFI_STATUS +install_shim_protocols(void) +{ + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + EFI_STATUS efi_status; + /* + * Install the protocol + */ + efi_status = uefi_call_wrapper(BS->InstallProtocolInterface, 4, + &shim_lock_handle, &shim_lock_guid, + EFI_NATIVE_INTERFACE, &shim_lock_interface); + if (EFI_ERROR(efi_status)) { + console_error(L"Could not install security protocol", + efi_status); + return efi_status; + } + +#if defined(OVERRIDE_SECURITY_POLICY) + /* + * Install the security protocol hook + */ + security_policy_install(shim_verify); +#endif + + return EFI_SUCCESS; +} + +void +uninstall_shim_protocols(void) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; - static SHIM_LOCK shim_lock_interface; - EFI_HANDLE handle = NULL; +#if defined(OVERRIDE_SECURITY_POLICY) + /* + * Clean up the security protocol hook + */ + security_policy_uninstall(); +#endif + + /* + * If we're back here then clean everything up before exiting + */ + uefi_call_wrapper(BS->UninstallProtocolInterface, 3, shim_lock_handle, + &shim_lock_guid, &shim_lock_interface); +} + +EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) +{ EFI_STATUS efi_status; verification_method = VERIFIED_BY_NOTHING; @@ -1768,24 +1813,9 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) } } - /* - * Install the protocol - */ - efi_status = uefi_call_wrapper(BS->InstallProtocolInterface, 4, - &handle, &shim_lock_guid, EFI_NATIVE_INTERFACE, - &shim_lock_interface); - if (EFI_ERROR(efi_status)) { - console_error(L"Could not install security protocol", - efi_status); + efi_status = install_shim_protocols(); + if (EFI_ERROR(efi_status)) return efi_status; - } - -#if defined(OVERRIDE_SECURITY_POLICY) - /* - * Install the security protocol hook - */ - security_policy_install(shim_verify); -#endif /* * Enter MokManager if necessary @@ -1810,20 +1840,7 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) efi_status = init_grub(image_handle); -#if defined(OVERRIDE_SECURITY_POLICY) - /* - * Clean up the security protocol hook - */ - security_policy_uninstall(); -#endif - - /* - * If we're back here then clean everything up before exiting - */ - uefi_call_wrapper(BS->UninstallProtocolInterface, 3, handle, - &shim_lock_guid, &shim_lock_interface); - - + uninstall_shim_protocols(); /* * Remove our hooks from system services. */ -- cgit v1.2.3 From 277127d1b34778076c07556b11c6cbf917bf6252 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 12 Aug 2014 10:54:05 -0400 Subject: unhook_system_services: bail on systab == NULL Prevent unhook_system_services() from dereferencing a NULL systab, which may occur if hook_system_services() has never been called. Signed-off-by: Ard Biesheuvel --- replacements.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'replacements.c') diff --git a/replacements.c b/replacements.c index 48dc4378..5dfa3556 100644 --- a/replacements.c +++ b/replacements.c @@ -70,6 +70,9 @@ static EFI_HANDLE last_loaded_image; void unhook_system_services(void) { + if (!systab) + return; + systab->BootServices->Exit = system_exit; systab->BootServices->LoadImage = system_load_image; systab->BootServices->StartImage = system_start_image; -- cgit v1.2.3 From 1042fd7c725ebde7d7064c23523a61605617a7ae Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 27 Aug 2014 16:40:57 -0400 Subject: Don't name something exit(). On aarch64 due to some terrifying include chain we wind up with Cryptlib's definition of exit here. I'm not a glutton for punishment, so I'm just changing the name so it's not coliding. Signed-off-by: Peter Jones --- replacements.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'replacements.c') diff --git a/replacements.c b/replacements.c index 5dfa3556..f7623d9d 100644 --- a/replacements.c +++ b/replacements.c @@ -162,7 +162,7 @@ exit_boot_services(EFI_HANDLE image_key, UINTN map_key) } static EFI_STATUS EFIAPI -exit(EFI_HANDLE ImageHandle, EFI_STATUS ExitStatus, +do_exit(EFI_HANDLE ImageHandle, EFI_STATUS ExitStatus, UINTN ExitDataSize, CHAR16 *ExitData) { EFI_STATUS status; @@ -206,5 +206,5 @@ hook_system_services(EFI_SYSTEM_TABLE *local_systab) * bootloader and still e.g. start a new one or run an internal * shell. */ system_exit = systab->BootServices->Exit; - systab->BootServices->Exit = exit; + systab->BootServices->Exit = do_exit; } -- cgit v1.2.3