diff options
author | Mathieu Trudel-Lapierre <cyphermox@ubuntu.com> | 2017-08-04 12:10:50 -0400 |
---|---|---|
committer | Mathieu Trudel-Lapierre <cyphermox@ubuntu.com> | 2017-08-04 12:10:50 -0400 |
commit | bbfd2ab18f52600aa41f061b2da9a2afe2a9d6ac (patch) | |
tree | 56132d617fff7c4f05e67024ec872d88fcafa92d /replacements.c | |
download | efi-boot-shim-upstream/0.9+1474479173.6c180c6.tar.gz efi-boot-shim-upstream/0.9+1474479173.6c180c6.zip |
Import Upstream version 0.9+1474479173.6c180c6upstream/0.9+1474479173.6c180c6
Diffstat (limited to 'replacements.c')
-rw-r--r-- | replacements.c | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/replacements.c b/replacements.c new file mode 100644 index 00000000..01eda0e3 --- /dev/null +++ b/replacements.c @@ -0,0 +1,233 @@ +/* + * shim - trivial UEFI first-stage bootloader + * + * Copyright 2012 Red Hat, Inc <mjg@redhat.com> + * + * 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 <efi.h> +#include <efiapi.h> +#include <efilib.h> +#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->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) +{ + if (!systab) + return; + + 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 (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; +} + +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 +do_exit(EFI_HANDLE ImageHandle, EFI_STATUS ExitStatus, + UINTN ExitDataSize, CHAR16 *ExitData) +{ + EFI_STATUS status; + + shim_fini(); + + status = systab->BootServices->Exit(ImageHandle, ExitStatus, + ExitDataSize, ExitData); + if (EFI_ERROR(status)) { + EFI_STATUS status2 = shim_init(); + + if (EFI_ERROR(status2)) { + Print(L"Something has gone seriously wrong: %r\n", + status2); + Print(L"shim cannot continue, sorry.\n"); + systab->BootServices->Stall(5000000); + systab->RuntimeServices->ResetSystem( + EfiResetShutdown, + EFI_SECURITY_VIOLATION, 0, NULL); + } + } + return status; +} + +void +hook_system_services(EFI_SYSTEM_TABLE *local_systab) +{ + systab = 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; + 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; +} + +void +unhook_exit(void) +{ + systab->BootServices->Exit = system_exit; +} + +void +hook_exit(EFI_SYSTEM_TABLE *local_systab) +{ + systab = local_systab; + + /* 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 = do_exit; +} |