diff --git a/Makefile b/Makefile index 6f4adf1..1752ef5 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ EFI_LDS = elf_$(ARCH)_efi.lds DEFAULT_LOADER := \\\\grub.efi CFLAGS = -ggdb -O0 -fno-stack-protector -fno-strict-aliasing -fpic \ -fshort-wchar -Wall -Werror -mno-red-zone -maccumulate-outgoing-args \ - -mno-mmx -mno-sse \ + -mno-mmx -mno-sse -fno-builtin \ "-DDEFAULT_LOADER=L\"$(DEFAULT_LOADER)\"" \ "-DDEFAULT_LOADER_CHAR=\"$(DEFAULT_LOADER)\"" \ $(EFI_INCLUDES) @@ -36,9 +36,9 @@ LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L$(EFI_PATH VERSION = 0.4 TARGET = shim.efi MokManager.efi.signed fallback.efi.signed -OBJS = shim.o netboot.o cert.o dbx.o +OBJS = shim.o netboot.o cert.o replacements.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key -SOURCES = shim.c shim.h netboot.c include/PeImage.h include/wincert.h +SOURCES = shim.c shim.h netboot.c include/PeImage.h include/wincert.h replacements.c replacements.h MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o MOK_SOURCES = MokManager.c shim.h console_control.h PasswordCrypt.c PasswordCrypt.h crypt_blowfish.c crypt_blowfish.h FALLBACK_OBJS = fallback.o diff --git a/TODO b/TODO index c2809ab..4694676 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,3 @@ -Hardening startimage: -- Don't allow non-participating bootloaders/kernels to call - ExitBootServices(), but trap in StartImage() so we can let them do - that. Versioned protocol: - Make shim and the bootloaders using it express how enlightened they are to one another, so we can stop earlier without tricks like diff --git a/replacements.c b/replacements.c new file mode 100644 index 0000000..b05b220 --- /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; +} diff --git a/replacements.h b/replacements.h new file mode 100644 index 0000000..806c038 --- /dev/null +++ b/replacements.h @@ -0,0 +1,43 @@ +/* + * Copyright 2013 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. + */ +#ifndef SHIM_REPLACEMENTS_H +#define SHIM_REPLACEMENTS_H 1 + +typedef enum { + VERIFIED_BY_NOTHING, + VERIFIED_BY_CERT, + VERIFIED_BY_HASH +} verification_method_t; + +extern verification_method_t verification_method; +extern int loader_is_participating; + +extern void hook_system_services(EFI_SYSTEM_TABLE *local_systab); + +#endif /* SHIM_REPLACEMENTS_H */ diff --git a/shim.c b/shim.c index a923e7e..aaf2fc4 100644 --- a/shim.c +++ b/shim.c @@ -40,6 +40,7 @@ #include "shim.h" #include "netboot.h" #include "shim_cert.h" +#include "replacements.h" #include "ucs2.h" #include "guid.h" @@ -75,9 +76,15 @@ UINT32 vendor_dbx_size; UINT8 *vendor_cert; UINT8 *vendor_dbx; +/* + * indicator of how an image has been verified + */ +verification_method_t verification_method; +int loader_is_participating; + #define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }} -static UINT8 insecure_mode; +UINT8 insecure_mode; typedef enum { DATA_FOUND, @@ -370,6 +377,12 @@ static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert, return EFI_SUCCESS; } +static void update_verification_method(verification_method_t method) +{ + if (verification_method == VERIFIED_BY_NOTHING) + verification_method = method; +} + /* * Check whether the binary signature or hash are present in db or MokList */ @@ -380,19 +393,34 @@ static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert, EFI_GUID shim_var = SHIM_LOCK_GUID; if (check_db_hash(L"db", secure_var, sha256hash, SHA256_DIGEST_SIZE, - EFI_CERT_SHA256_GUID) == DATA_FOUND) + EFI_CERT_SHA256_GUID) == DATA_FOUND) { + update_verification_method(VERIFIED_BY_HASH); return EFI_SUCCESS; + } if (check_db_hash(L"db", secure_var, sha1hash, SHA1_DIGEST_SIZE, - EFI_CERT_SHA1_GUID) == DATA_FOUND) + EFI_CERT_SHA1_GUID) == DATA_FOUND) { + verification_method = VERIFIED_BY_HASH; + update_verification_method(VERIFIED_BY_HASH); return EFI_SUCCESS; + } if (check_db_hash(L"MokList", shim_var, sha256hash, SHA256_DIGEST_SIZE, - EFI_CERT_SHA256_GUID) == DATA_FOUND) + EFI_CERT_SHA256_GUID) == DATA_FOUND) { + verification_method = VERIFIED_BY_HASH; + update_verification_method(VERIFIED_BY_HASH); return EFI_SUCCESS; - if (check_db_cert(L"db", secure_var, cert, sha256hash) == DATA_FOUND) + } + if (check_db_cert(L"db", secure_var, cert, sha256hash) == DATA_FOUND) { + verification_method = VERIFIED_BY_CERT; + update_verification_method(VERIFIED_BY_CERT); return EFI_SUCCESS; - if (check_db_cert(L"MokList", shim_var, cert, sha256hash) == DATA_FOUND) + } + if (check_db_cert(L"MokList", shim_var, cert, sha256hash) == DATA_FOUND) { + verification_method = VERIFIED_BY_CERT; + update_verification_method(VERIFIED_BY_CERT); return EFI_SUCCESS; + } + update_verification_method(VERIFIED_BY_NOTHING); return EFI_ACCESS_DENIED; } @@ -1169,6 +1197,8 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size) EFI_STATUS status; PE_COFF_LOADER_IMAGE_CONTEXT context; + loader_is_participating = 1; + if (!secure_mode()) return EFI_SUCCESS; @@ -1262,6 +1292,8 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) goto done; } + loader_is_participating = 0; + /* * The binary is trusted and relocated. Run it */ @@ -1391,6 +1423,8 @@ static EFI_STATUS check_mok_sb (void) if (status != EFI_SUCCESS) return EFI_ACCESS_DENIED; + insecure_mode = 0; + /* * Delete and ignore the variable if it's been set from or could be * modified by the OS @@ -1500,6 +1534,8 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) UINTN verbose_check_size; EFI_GUID global_var = EFI_GLOBAL_VARIABLE; + verification_method = VERIFIED_BY_NOTHING; + vendor_cert_size = cert_table.vendor_cert_size; vendor_dbx_size = cert_table.vendor_dbx_size; vendor_cert = (UINT8 *)&cert_table + cert_table.vendor_cert_offset; @@ -1541,6 +1577,12 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) if (insecure_mode) { Print(L"Booting in insecure mode\n"); uefi_call_wrapper(BS->Stall, 1, 2000000); + } else { + /* + * Install our hooks for ExitBootServices() and StartImage() + */ + hook_system_services(systab); + loader_is_participating = 0; } /*