WIP - shim unlocker

This commit is contained in:
Michael Brown
2023-05-19 14:03:12 +01:00
parent bacfb46821
commit 6e85f25ca6
10 changed files with 225 additions and 14 deletions

View File

@ -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 );

View File

@ -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 */

View File

@ -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;

View File

@ -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 );
/**

View File

@ -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 */

View File

@ -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 )
/** @} */

View File

@ -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,

View File

@ -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;

View File

@ -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 );
}

View File

@ -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