diff options
author | Michael Brown <mcb30@ipxe.org> | 2023-05-19 14:03:12 +0100 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2023-05-19 14:03:12 +0100 |
commit | 6e85f25ca66f806ed6d87e503b09fe01df4f7831 (patch) | |
tree | 5c55c739fcacbdb0feebcf3ddc0a9bdc5605fda7 | |
parent | bacfb468216ad6a139e63510cb28d4044dd121de (diff) | |
download | ipxe-6e85f25ca66f806ed6d87e503b09fe01df4f7831.tar.gz |
WIP - shim unlocker
-rw-r--r-- | src/image/efi_image.c | 23 | ||||
-rw-r--r-- | src/include/ipxe/efi/Protocol/ShimLock.h | 29 | ||||
-rw-r--r-- | src/include/ipxe/efi/efi.h | 1 | ||||
-rw-r--r-- | src/include/ipxe/efi/efi_image.h | 3 | ||||
-rw-r--r-- | src/include/ipxe/efi/efi_shim.h | 29 | ||||
-rw-r--r-- | src/include/ipxe/errfile.h | 1 | ||||
-rw-r--r-- | src/interface/efi/efi_debug.c | 2 | ||||
-rw-r--r-- | src/interface/efi/efi_guid.c | 5 | ||||
-rw-r--r-- | src/interface/efi/efi_shim.c | 144 | ||||
-rw-r--r-- | src/usr/shimmgmt.c | 2 |
10 files changed, 225 insertions, 14 deletions
diff --git a/src/image/efi_image.c b/src/image/efi_image.c index 42a724ee0..13f08de28 100644 --- a/src/image/efi_image.c +++ b/src/image/efi_image.c @@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/efi/efi_pxe.h> #include <ipxe/efi/efi_driver.h> #include <ipxe/efi/efi_image.h> +#include <ipxe/efi/efi_shim.h> #include <ipxe/image.h> #include <ipxe/init.h> #include <ipxe/features.h> @@ -56,16 +57,6 @@ FEATURE ( FEATURE_IMAGE, "EFI", DHCP_EB_FEATURE_EFI, 1 ); "Could not start image" ) #define EEFI_START( efirc ) EPLATFORM ( EINFO_EEFI_START, efirc ) -/** EFI shim image */ -struct image_tag efi_shim __image_tag = { - .name = "SHIM", -}; - -/** EIF shim crutch image */ -struct image_tag efi_shim_crutch __image_tag = { - .name = "SHIMCRUTCH", -}; - /** * Create device path for image * @@ -165,6 +156,7 @@ static int efi_image_exec ( struct image *image ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev; EFI_DEVICE_PATH_PROTOCOL *path; + struct efi_shim_unlocker unlocker; union { EFI_LOADED_IMAGE_PROTOCOL *image; void *interface; @@ -242,6 +234,14 @@ static int efi_image_exec ( struct image *image ) { goto err_cmdline; } + /* Install shim unlocker (if using a shim) */ + if ( shim && + ( ( rc = efi_shim_install ( &unlocker ) ) != 0 ) ) { + DBGC ( image, "EFIIMAGE %s could not install shim unlocker: " + "%s\n", image->name, strerror ( rc ) ); + goto err_shim_install; + } + /* Attempt loading image */ handle = NULL; if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, path, @@ -336,6 +336,9 @@ static int efi_image_exec ( struct image *image ) { if ( rc != 0 ) bs->UnloadImage ( handle ); err_load_image: + if ( shim ) + efi_shim_uninstall ( &unlocker ); + err_shim_install: free ( cmdline ); err_cmdline: free ( path ); diff --git a/src/include/ipxe/efi/Protocol/ShimLock.h b/src/include/ipxe/efi/Protocol/ShimLock.h new file mode 100644 index 000000000..726081949 --- /dev/null +++ b/src/include/ipxe/efi/Protocol/ShimLock.h @@ -0,0 +1,29 @@ +#ifndef _IPXE_EFI_SHIM_LOCK_PROTOCOL_H +#define _IPXE_EFI_SHIM_LOCK_PROTOCOL_H + +/** @file + * + * EFI "shim lock" protocol + * + */ + +FILE_LICENCE ( BSD3 ); + +#define EFI_SHIM_LOCK_PROTOCOL_GUID \ + { 0x605dab50, 0xe046, 0x4300, \ + { 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } } + +typedef +EFI_STATUS +(*EFI_SHIM_LOCK_VERIFY) ( + IN VOID *buffer, + IN UINT32 size + ); + +typedef struct _EFI_SHIM_LOCK_PROTOCOL { + EFI_SHIM_LOCK_VERIFY Verify; + VOID *Reserved1; + VOID *Reserved2; +} EFI_SHIM_LOCK_PROTOCOL; + +#endif /*_IPXE_EFI_SHIM_LOCK_PROTOCOL_H */ diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index 1dd0d4453..e0e2db608 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -197,6 +197,7 @@ extern EFI_GUID efi_pci_io_protocol_guid; extern EFI_GUID efi_pci_root_bridge_io_protocol_guid; extern EFI_GUID efi_pxe_base_code_protocol_guid; extern EFI_GUID efi_serial_io_protocol_guid; +extern EFI_GUID efi_shim_lock_protocol_guid; extern EFI_GUID efi_simple_file_system_protocol_guid; extern EFI_GUID efi_simple_network_protocol_guid; extern EFI_GUID efi_simple_pointer_protocol_guid; diff --git a/src/include/ipxe/efi/efi_image.h b/src/include/ipxe/efi/efi_image.h index c9766b924..0fc0402b1 100644 --- a/src/include/ipxe/efi/efi_image.h +++ b/src/include/ipxe/efi/efi_image.h @@ -11,9 +11,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/image.h> -extern struct image_tag efi_shim __image_tag; -extern struct image_tag efi_shim_crutch __image_tag; - extern struct image_type efi_image_type[] __image_type ( PROBE_NORMAL ); /** diff --git a/src/include/ipxe/efi/efi_shim.h b/src/include/ipxe/efi/efi_shim.h new file mode 100644 index 000000000..ec7803d66 --- /dev/null +++ b/src/include/ipxe/efi/efi_shim.h @@ -0,0 +1,29 @@ +#ifndef _IPXE_EFI_SHIM_H +#define _IPXE_EFI_SHIM_H + +/** @file + * + * UEFI shim handling + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/image.h> +#include <ipxe/efi/efi.h> + +/** A shim unlocker */ +struct efi_shim_unlocker { + /** Protocol installation event */ + EFI_EVENT event; + /** Protocol notification registration token */ + void *token; +}; + +extern struct image_tag efi_shim __image_tag; +extern struct image_tag efi_shim_crutch __image_tag; + +extern int efi_shim_install ( struct efi_shim_unlocker *unlocker ); +extern void efi_shim_uninstall ( struct efi_shim_unlocker *unlocker ); + +#endif /* _IPXE_EFI_SHIM_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index e6fd8524e..021f6a72e 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -404,6 +404,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_dhe ( ERRFILE_OTHER | 0x005a0000 ) #define ERRFILE_efi_cmdline ( ERRFILE_OTHER | 0x005b0000 ) #define ERRFILE_efi_rng ( ERRFILE_OTHER | 0x005c0000 ) +#define ERRFILE_efi_shim ( ERRFILE_OTHER | 0x005d0000 ) /** @} */ diff --git a/src/interface/efi/efi_debug.c b/src/interface/efi/efi_debug.c index 967bb6182..02cbf9fa4 100644 --- a/src/interface/efi/efi_debug.c +++ b/src/interface/efi/efi_debug.c @@ -143,6 +143,8 @@ static struct efi_well_known_guid efi_well_known_guids[] = { "PxeBaseCode" }, { &efi_serial_io_protocol_guid, "SerialIo" }, + { &efi_shim_lock_protocol_guid, + "ShimLock" }, { &efi_simple_file_system_protocol_guid, "SimpleFileSystem" }, { &efi_simple_network_protocol_guid, diff --git a/src/interface/efi/efi_guid.c b/src/interface/efi/efi_guid.c index 663585dc2..25c342ffb 100644 --- a/src/interface/efi/efi_guid.c +++ b/src/interface/efi/efi_guid.c @@ -54,6 +54,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/Protocol/PciRootBridgeIo.h> #include <ipxe/efi/Protocol/PxeBaseCode.h> #include <ipxe/efi/Protocol/SerialIo.h> +#include <ipxe/efi/Protocol/ShimLock.h> #include <ipxe/efi/Protocol/SimpleFileSystem.h> #include <ipxe/efi/Protocol/SimpleNetwork.h> #include <ipxe/efi/Protocol/SimplePointer.h> @@ -227,6 +228,10 @@ EFI_GUID efi_pxe_base_code_protocol_guid EFI_GUID efi_serial_io_protocol_guid = EFI_SERIAL_IO_PROTOCOL_GUID; +/** Shim lock protocol GUID */ +EFI_GUID efi_shim_lock_protocol_guid + = EFI_SHIM_LOCK_PROTOCOL_GUID; + /** Simple file system protocol GUID */ EFI_GUID efi_simple_file_system_protocol_guid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; diff --git a/src/interface/efi/efi_shim.c b/src/interface/efi/efi_shim.c new file mode 100644 index 000000000..b0caf227d --- /dev/null +++ b/src/interface/efi/efi_shim.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2022 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <string.h> +#include <errno.h> +#include <ipxe/image.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_shim.h> +#include <ipxe/efi/Protocol/ShimLock.h> + +/** @file + * + * UEFI shim handling + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** EFI shim image */ +struct image_tag efi_shim __image_tag = { + .name = "SHIM", +}; + +/** EFI shim crutch image */ +struct image_tag efi_shim_crutch __image_tag = { + .name = "SHIMCRUTCH", +}; + +/** + * Unlock UEFI shim + * + * @v event Event + * @v context Event context + * + * The UEFI shim is gradually becoming less capable of directly + * executing a kernel image, due to an ever increasing list of + * assumptions that it will only ever be used in conjunction with a + * second stage loader such as GRUB. + * + * For example: shim will erroneously complain if the image that it + * loads and executes does not call in to the "shim lock protocol" + * before calling ExitBootServices(), even if there is no valid reason + * for it to have done so. + * + * Reduce the Secure Boot attack surface by removing, where possible, + * this spurious requirement for the use of an additional second stage + * loader. + */ +static EFIAPI void efi_shim_unlock ( EFI_EVENT event __unused, void *context ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_GUID *protocol = &efi_shim_lock_protocol_guid; + struct efi_shim_unlocker *unlocker = context; + union { + EFI_SHIM_LOCK_PROTOCOL *lock; + void *interface; + } u; + uint8_t empty[0]; + EFI_STATUS efirc; + + /* Process all new instances of the shim lock protocol */ + while ( 1 ) { + + /* Get next instance */ + if ( ( efirc = bs->LocateProtocol ( protocol, unlocker->token, + &u.interface ) ) != 0 ) + break; + + /* Call shim lock protocol with empty buffer */ + u.lock->Verify ( empty, sizeof ( empty ) ); + DBGC ( unlocker, "SHIM unlocked %p\n", u.interface ); + } +} + +/** + * Install UEFI shim unlocker + * + * @v unlocker Shim unlocker + * @ret rc Return status code + */ +int efi_shim_install ( struct efi_shim_unlocker *unlocker ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_GUID *protocol = &efi_shim_lock_protocol_guid; + EFI_STATUS efirc; + int rc; + + /* Create event */ + if ( ( efirc = bs->CreateEvent ( EVT_NOTIFY_SIGNAL, TPL_CALLBACK, + efi_shim_unlock, unlocker, + &unlocker->event ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( unlocker, "SHIM could not create event: %s\n", + strerror ( rc ) ); + goto err_create_event; + } + + /* Register for protocol installations */ + if ( ( efirc = bs->RegisterProtocolNotify ( protocol, unlocker->event, + &unlocker->token ) ) != 0){ + rc = -EEFI ( efirc ); + DBGC ( unlocker, "SHIM could not register for protocols: %s\n", + strerror ( rc ) ); + goto err_register_notify; + } + + return 0; + + err_register_notify: + bs->CloseEvent ( unlocker->event ); + err_create_event: + return rc; +} + +/** + * Uninstall UEFI shim unlocker + * + * @v unlocker Shim unlocker + */ +void efi_shim_uninstall ( struct efi_shim_unlocker *unlocker ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + bs->CloseEvent ( unlocker->event ); +} diff --git a/src/usr/shimmgmt.c b/src/usr/shimmgmt.c index 4a5966deb..ecffd01d9 100644 --- a/src/usr/shimmgmt.c +++ b/src/usr/shimmgmt.c @@ -24,7 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/efi.h> -#include <ipxe/efi/efi_image.h> +#include <ipxe/efi/efi_shim.h> #include <usr/shimmgmt.h> /** @file |