summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--TODO4
-rw-r--r--replacements.c147
-rw-r--r--replacements.h43
-rw-r--r--shim.c54
5 files changed, 241 insertions, 13 deletions
diff --git a/Makefile b/Makefile
index 6f4adf12..1752ef5b 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 c2809ab6..46946767 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 00000000..b05b2202
--- /dev/null
+++ b/replacements.c
@@ -0,0 +1,147 @@
+/*
+ * 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->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 00000000..806c0386
--- /dev/null
+++ b/replacements.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013 Red Hat, Inc <pjones@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.
+ */
+#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 a923e7e9..aaf2fc46 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;
}
/*