From b7b327f856b3782f28be561d612d66ff406c7789 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Mon, 14 Nov 2022 14:18:26 +0100 Subject: [PATCH 1/5] boot: Fix memory leak --- src/boot/efi/boot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 76023b14ca..76c756c5cf 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -2658,7 +2658,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { EFI_LOADED_IMAGE_PROTOCOL *loaded_image; _cleanup_(file_closep) EFI_FILE *root_dir = NULL; _cleanup_(config_free) Config config = {}; - char16_t *loaded_image_path; + _cleanup_free_ char16_t *loaded_image_path = NULL; EFI_STATUS err; uint64_t init_usec; bool menu = false; From af7ef648cddeb96da525de2410565d166f75cc96 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Mon, 14 Nov 2022 14:37:13 +0100 Subject: [PATCH 2/5] boot: Do not require a loaded image path If the device path to text protocol is not available (looking angrily at Apple) we would fail to boot because we cannot get the loaded image path. As this is only used for cosmetic purposes, we can just silently continue. Fixes: #25363 --- src/boot/efi/boot.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 76c756c5cf..ac9d11aacc 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -472,7 +472,6 @@ static void print_status(Config *config, char16_t *loaded_image_path) { _cleanup_free_ char16_t *device_part_uuid = NULL; assert(config); - assert(loaded_image_path); clear_screen(COLOR_NORMAL); console_query_mode(&x_max, &y_max); @@ -620,7 +619,6 @@ static bool menu_run( assert(config); assert(chosen_entry); - assert(loaded_image_path); EFI_STATUS err; UINTN visible_max = 0; @@ -1479,7 +1477,7 @@ static void config_entry_add_type1( entry->loader = xstra_to_path(value); /* do not add an entry for ourselves */ - if (loaded_image_path && strcaseeq16(entry->loader, loaded_image_path)) { + if (strcaseeq16(entry->loader, loaded_image_path)) { entry->type = LOADER_UNDEFINED; break; } @@ -1909,12 +1907,11 @@ static ConfigEntry *config_entry_add_loader_auto( assert(root_dir); assert(id); assert(title); - assert(loader || loaded_image_path); if (!config->auto_entries) return NULL; - if (loaded_image_path) { + if (!loader) { loader = L"\\EFI\\BOOT\\BOOT" EFI_MACHINE_TYPE_NAME ".efi"; /* We are trying to add the default EFI loader here, @@ -2563,7 +2560,6 @@ static void export_variables( char16_t uuid[37]; assert(loaded_image); - assert(loaded_image_path); efivar_set_time_usec(LOADER_GUID, L"LoaderTimeInitUSec", init_usec); efivar_set(LOADER_GUID, L"LoaderInfo", L"systemd-boot " GIT_VERSION, 0); @@ -2592,7 +2588,6 @@ static void config_load_all_entries( assert(config); assert(loaded_image); - assert(loaded_image_path); assert(root_dir); config_load_defaults(config, root_dir); @@ -2684,9 +2679,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { if (err != EFI_SUCCESS) return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err); - err = device_path_to_str(loaded_image->FilePath, &loaded_image_path); - if (err != EFI_SUCCESS) - return log_error_status_stall(err, L"Error getting loaded image path: %r", err); + (void) device_path_to_str(loaded_image->FilePath, &loaded_image_path); export_variables(loaded_image, loaded_image_path, init_usec); From 679007044fbbcf82c66cf20b99f2f5086b7df6b4 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Mon, 14 Nov 2022 15:24:32 +0100 Subject: [PATCH 3/5] boot: Manually convert filepaths if needed The conversion of a filepath device path to text is needed for the stub loader to find credential files. --- src/boot/efi/util.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c index 5547d288de..57436dbf0c 100644 --- a/src/boot/efi/util.c +++ b/src/boot/efi/util.c @@ -772,19 +772,51 @@ EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DE EFI_STATUS device_path_to_str(const EFI_DEVICE_PATH *dp, char16_t **ret) { EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *dp_to_text; EFI_STATUS err; + _cleanup_free_ char16_t *str = NULL; assert(dp); assert(ret); err = BS->LocateProtocol(&(EFI_GUID) EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID, NULL, (void **) &dp_to_text); - if (err != EFI_SUCCESS) - return err; + if (err != EFI_SUCCESS) { + /* If the device path to text protocol is not available we can still do a best-effort attempt + * to convert it ourselves if we are given filepath-only device path. */ - char16_t *str = dp_to_text->ConvertDevicePathToText(dp, false, false); + size_t size = 0; + for (const EFI_DEVICE_PATH *node = dp; !IsDevicePathEnd(node); + node = NextDevicePathNode(node)) { + + if (DevicePathType(node) != MEDIA_DEVICE_PATH || + DevicePathSubType(node) != MEDIA_FILEPATH_DP) + return err; + + size_t path_size = DevicePathNodeLength(node); + if (path_size <= offsetof(FILEPATH_DEVICE_PATH, PathName) || path_size % sizeof(char16_t)) + return EFI_INVALID_PARAMETER; + path_size -= offsetof(FILEPATH_DEVICE_PATH, PathName); + + _cleanup_free_ char16_t *old = str; + str = xmalloc(size + path_size); + if (old) { + memcpy(str, old, size); + str[size / sizeof(char16_t) - 1] = '\\'; + } + + memcpy(str + (size / sizeof(char16_t)), + ((uint8_t *) node) + offsetof(FILEPATH_DEVICE_PATH, PathName), + path_size); + size += path_size; + } + + *ret = TAKE_PTR(str); + return EFI_SUCCESS; + } + + str = dp_to_text->ConvertDevicePathToText(dp, false, false); if (!str) return EFI_OUT_OF_RESOURCES; - *ret = str; + *ret = TAKE_PTR(str); return EFI_SUCCESS; } From 5489c13bae119dc5f6e65be8d7f241aa7d54c023 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Tue, 15 Nov 2022 18:22:38 +0100 Subject: [PATCH 4/5] boot: Rework security arch override This simplifies the caller interface for security arch overrides by only having to pass a validator and an optional context. --- src/boot/efi/linux.c | 61 ++++++++------------ src/boot/efi/secure-boot.c | 105 +++++++++++++++++++++++++++-------- src/boot/efi/secure-boot.h | 26 +++------ src/boot/efi/shim.c | 110 ++++++++++++------------------------- 4 files changed, 148 insertions(+), 154 deletions(-) diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c index 75b9507709..dd7eb48c8c 100644 --- a/src/boot/efi/linux.c +++ b/src/boot/efi/linux.c @@ -20,35 +20,26 @@ #define STUB_PAYLOAD_GUID \ { 0x55c5d1f8, 0x04cd, 0x46b5, { 0x8a, 0x20, 0xe5, 0x6c, 0xbb, 0x30, 0x52, 0xd0 } } -static EFIAPI EFI_STATUS security_hook( - const SecurityOverride *this, uint32_t authentication_status, const EFI_DEVICE_PATH *file) { +typedef struct { + const void *addr; + size_t len; + const EFI_DEVICE_PATH *device_path; +} ValidationContext; - assert(this); - assert(this->hook == security_hook); +static bool validate_payload( + const void *ctx, const EFI_DEVICE_PATH *device_path, const void *file_buffer, size_t file_size) { - if (file == this->payload_device_path) - return EFI_SUCCESS; + const ValidationContext *payload = ASSERT_PTR(ctx); - return this->original_security->FileAuthenticationState( - this->original_security, authentication_status, file); -} + if (device_path != payload->device_path) + return false; -static EFIAPI EFI_STATUS security2_hook( - const SecurityOverride *this, - const EFI_DEVICE_PATH *device_path, - void *file_buffer, - size_t file_size, - BOOLEAN boot_policy) { + /* Security arch (1) protocol does not provide a file buffer. Instead we are supposed to fetch the payload + * ourselves, which is not needed as we already have everything in memory and the device paths match. */ + if (file_buffer && (file_buffer != payload->addr || file_size != payload->len)) + return false; - assert(this); - assert(this->hook == security2_hook); - - if (file_buffer == this->payload && file_size == this->payload_len && - device_path == this->payload_device_path) - return EFI_SUCCESS; - - return this->original_security2->FileAuthentication( - this->original_security2, device_path, file_buffer, file_size, boot_policy); + return true; } static EFI_STATUS load_image(EFI_HANDLE parent, const void *source, size_t len, EFI_HANDLE *ret_image) { @@ -79,19 +70,13 @@ static EFI_STATUS load_image(EFI_HANDLE parent, const void *source, size_t len, /* We want to support unsigned kernel images as payload, which is safe to do under secure boot * because it is embedded in this stub loader (and since it is already running it must be trusted). */ - SecurityOverride security_override = { - .hook = security_hook, - .payload = source, - .payload_len = len, - .payload_device_path = &payload_device_path.payload.Header, - }, security2_override = { - .hook = security2_hook, - .payload = source, - .payload_len = len, - .payload_device_path = &payload_device_path.payload.Header, - }; - - install_security_override(&security_override, &security2_override); + install_security_override( + validate_payload, + &(ValidationContext) { + .addr = source, + .len = len, + .device_path = &payload_device_path.payload.Header, + }); EFI_STATUS ret = BS->LoadImage( /*BootPolicy=*/false, @@ -101,7 +86,7 @@ static EFI_STATUS load_image(EFI_HANDLE parent, const void *source, size_t len, len, ret_image); - uninstall_security_override(&security_override, &security2_override); + uninstall_security_override(); return ret; } diff --git a/src/boot/efi/secure-boot.c b/src/boot/efi/secure-boot.c index 171b2c96b3..0e615c55e0 100644 --- a/src/boot/efi/secure-boot.c +++ b/src/boot/efi/secure-boot.c @@ -127,10 +127,60 @@ out_deallocate: return err; } -static EFI_STATUS install_security_override_one(EFI_GUID guid, SecurityOverride *override) { +static struct SecurityOverride { + /* Our own security arch instances that we register onto original_handle, thereby replacing the + * firmware provided instances. */ + EFI_SECURITY_ARCH_PROTOCOL override; + EFI_SECURITY2_ARCH_PROTOCOL override2; + + /* These are saved so we can uninstall our own instance later. */ + EFI_HANDLE original_handle, original_handle2; + EFI_SECURITY_ARCH_PROTOCOL *original_security; + EFI_SECURITY2_ARCH_PROTOCOL *original_security2; + + security_validator_t validator; + const void *validator_ctx; +} security_override; + +static EFIAPI EFI_STATUS security_hook( + const EFI_SECURITY_ARCH_PROTOCOL *this, + uint32_t authentication_status, + const EFI_DEVICE_PATH *file) { + + assert(security_override.validator); + assert(security_override.original_security); + + if (security_override.validator(security_override.validator_ctx, file, NULL, 0)) + return EFI_SUCCESS; + + return security_override.original_security->FileAuthenticationState( + security_override.original_security, authentication_status, file); +} + +static EFIAPI EFI_STATUS security2_hook( + const EFI_SECURITY2_ARCH_PROTOCOL *this, + const EFI_DEVICE_PATH *device_path, + void *file_buffer, + size_t file_size, + BOOLEAN boot_policy) { + + assert(security_override.validator); + assert(security_override.original_security2); + + if (security_override.validator(security_override.validator_ctx, device_path, file_buffer, file_size)) + return EFI_SUCCESS; + + return security_override.original_security2->FileAuthentication( + security_override.original_security2, device_path, file_buffer, file_size, boot_policy); +} + +static EFI_STATUS install_security_override_one( + EFI_GUID guid, void *override, EFI_HANDLE *ret_original_handle, void **ret_original_security) { EFI_STATUS err; assert(override); + assert(ret_original_handle); + assert(ret_original_security); _cleanup_free_ EFI_HANDLE *handles = NULL; size_t n_handles = 0; @@ -152,8 +202,8 @@ static EFI_STATUS install_security_override_one(EFI_GUID guid, SecurityOverride if (err != EFI_SUCCESS) return log_error_status_stall(err, u"Error overriding security arch protocol: %r", err); - override->original = security; - override->original_handle = handles[0]; + *ret_original_security = security; + *ret_original_handle = handles[0]; return EFI_SUCCESS; } @@ -161,35 +211,46 @@ static EFI_STATUS install_security_override_one(EFI_GUID guid, SecurityOverride * Specification) with the provided override instances. If not running in secure boot or the protocols are * not available nothing happens. The override instances are provided with the necessary info to undo this * in uninstall_security_override(). */ -void install_security_override(SecurityOverride *override, SecurityOverride *override2) { - assert(override); - assert(override2); +void install_security_override(security_validator_t validator, const void *validator_ctx) { + assert(validator); if (!secure_boot_enabled()) return; - (void) install_security_override_one((EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID, override); - (void) install_security_override_one((EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID, override2); + security_override = (struct SecurityOverride) { + { .FileAuthenticationState = security_hook, }, + { .FileAuthentication = security2_hook, }, + .validator = validator, + .validator_ctx = validator_ctx, + }; + + (void) install_security_override_one( + (EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID, + &security_override.override, + &security_override.original_handle, + (void **) &security_override.original_security); + (void) install_security_override_one( + (EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID, + &security_override.override2, + &security_override.original_handle2, + (void **) &security_override.original_security2); } -void uninstall_security_override(SecurityOverride *override, SecurityOverride *override2) { - assert(override); - assert(override2); - +void uninstall_security_override(void) { /* We use assert_se here to guarantee the system is not in a weird state in the unlikely case of an * error restoring the original protocols. */ - if (override->original_handle) + if (security_override.original_handle) assert_se(BS->ReinstallProtocolInterface( - override->original_handle, - &(EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID, - override, - override->original) == EFI_SUCCESS); + security_override.original_handle, + &(EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID, + &security_override.override, + security_override.original_security) == EFI_SUCCESS); - if (override2->original_handle) + if (security_override.original_handle2) assert_se(BS->ReinstallProtocolInterface( - override2->original_handle, - &(EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID, - override2, - override2->original) == EFI_SUCCESS); + security_override.original_handle2, + &(EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID, + &security_override.override2, + security_override.original_security2) == EFI_SUCCESS); } diff --git a/src/boot/efi/secure-boot.h b/src/boot/efi/secure-boot.h index 91b6770edb..e98de81c2a 100644 --- a/src/boot/efi/secure-boot.h +++ b/src/boot/efi/secure-boot.h @@ -17,23 +17,11 @@ SecureBootMode secure_boot_mode(void); EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path); -typedef struct { - void *hook; +typedef bool (*security_validator_t)( + const void *ctx, + const EFI_DEVICE_PATH *device_path, + const void *file_buffer, + size_t file_size); - /* End of EFI_SECURITY_ARCH(2)_PROTOCOL. The rest is our own protocol instance data. */ - - EFI_HANDLE original_handle; - union { - void *original; - EFI_SECURITY_ARCH_PROTOCOL *original_security; - EFI_SECURITY2_ARCH_PROTOCOL *original_security2; - }; - - /* Used by the stub to identify the embedded image. */ - const void *payload; - size_t payload_len; - const EFI_DEVICE_PATH *payload_device_path; -} SecurityOverride; - -void install_security_override(SecurityOverride *override, SecurityOverride *override2); -void uninstall_security_override(SecurityOverride *override, SecurityOverride *override2); +void install_security_override(security_validator_t validator, const void *validator_ctx); +void uninstall_security_override(void); diff --git a/src/boot/efi/shim.c b/src/boot/efi/shim.c index 3ae058cb84..ac224336bc 100644 --- a/src/boot/efi/shim.c +++ b/src/boot/efi/shim.c @@ -23,7 +23,7 @@ #endif struct ShimLock { - EFI_STATUS __sysv_abi__ (*shim_verify) (void *buffer, uint32_t size); + EFI_STATUS __sysv_abi__ (*shim_verify) (const void *buffer, uint32_t size); /* context is actually a struct for the PE header, but it isn't needed so void is sufficient just do define the interface * see shim.c/shim.h and PeHeader.h in the github shim repo */ @@ -41,79 +41,45 @@ bool shim_loaded(void) { return BS->LocateProtocol((EFI_GUID*) SHIM_LOCK_GUID, NULL, (void**) &shim_lock) == EFI_SUCCESS; } -static bool shim_validate(void *data, uint32_t size) { - struct ShimLock *shim_lock; - - if (!data) - return false; - - if (BS->LocateProtocol((EFI_GUID*) SHIM_LOCK_GUID, NULL, (void**) &shim_lock) != EFI_SUCCESS) - return false; - - if (!shim_lock) - return false; - - return shim_lock->shim_verify(data, size) == EFI_SUCCESS; -} - -static EFIAPI EFI_STATUS security2_hook( - const SecurityOverride *this, - const EFI_DEVICE_PATH *device_path, - void *file_buffer, - UINTN file_size, - BOOLEAN boot_policy) { - - assert(this); - assert(this->hook == security2_hook); - - if (shim_validate(file_buffer, file_size)) - return EFI_SUCCESS; - - return this->original_security2->FileAuthentication( - this->original_security2, device_path, file_buffer, file_size, boot_policy); -} - -static EFIAPI EFI_STATUS security_hook( - const SecurityOverride *this, - uint32_t authentication_status, - const EFI_DEVICE_PATH *device_path) { +static bool shim_validate( + const void *ctx, const EFI_DEVICE_PATH *device_path, const void *file_buffer, size_t file_size) { EFI_STATUS err; + _cleanup_free_ char *file_buffer_owned = NULL; - assert(this); - assert(this->hook == security_hook); + if (!file_buffer) { + if (!device_path) + return false; - if (!device_path) - return this->original_security->FileAuthenticationState( - this->original_security, authentication_status, device_path); + EFI_HANDLE device_handle; + EFI_DEVICE_PATH *file_dp = (EFI_DEVICE_PATH *) device_path; + err = BS->LocateDevicePath(&FileSystemProtocol, &file_dp, &device_handle); + if (err != EFI_SUCCESS) + return false; - EFI_HANDLE device_handle; - EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *) device_path; - err = BS->LocateDevicePath(&FileSystemProtocol, &dp, &device_handle); + _cleanup_(file_closep) EFI_FILE *root = NULL; + err = open_volume(device_handle, &root); + if (err != EFI_SUCCESS) + return false; + + _cleanup_free_ char16_t *dp_str = NULL; + err = device_path_to_str(file_dp, &dp_str); + if (err != EFI_SUCCESS) + return false; + + err = file_read(root, dp_str, 0, 0, &file_buffer_owned, &file_size); + if (err != EFI_SUCCESS) + return false; + + file_buffer = file_buffer_owned; + } + + struct ShimLock *shim_lock; + err = BS->LocateProtocol((EFI_GUID *) SHIM_LOCK_GUID, NULL, (void **) &shim_lock); if (err != EFI_SUCCESS) - return err; + return false; - _cleanup_(file_closep) EFI_FILE *root = NULL; - err = open_volume(device_handle, &root); - if (err != EFI_SUCCESS) - return err; - - _cleanup_free_ char16_t *dp_str = NULL; - err = device_path_to_str(dp, &dp_str); - if (err != EFI_SUCCESS) - return err; - - char *file_buffer; - size_t file_size; - err = file_read(root, dp_str, 0, 0, &file_buffer, &file_size); - if (err != EFI_SUCCESS) - return err; - - if (shim_validate(file_buffer, file_size)) - return EFI_SUCCESS; - - return this->original_security->FileAuthenticationState( - this->original_security, authentication_status, device_path); + return shim_lock->shim_verify(file_buffer, file_size) == EFI_SUCCESS; } EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path, EFI_HANDLE *ret_image) { @@ -122,20 +88,14 @@ EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path bool have_shim = shim_loaded(); - SecurityOverride security_override = { - .hook = security_hook, - }, security2_override = { - .hook = security2_hook, - }; - if (have_shim) - install_security_override(&security_override, &security2_override); + install_security_override(shim_validate, NULL); EFI_STATUS ret = BS->LoadImage( /*BootPolicy=*/false, parent, (EFI_DEVICE_PATH *) device_path, NULL, 0, ret_image); if (have_shim) - uninstall_security_override(&security_override, &security2_override); + uninstall_security_override(); return ret; } From 967a868563996e928f1fade5bcafc82a7219742b Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Tue, 15 Nov 2022 18:53:02 +0100 Subject: [PATCH 5/5] boot: Replace firmware security hooks directly For some firmware, replacing their own security arch instance with our override using ReinstallProtocolInterface() is not enough as they will not use it. This commit goes back to how this was done before by directly modifying the security protocols. Fixes: #25336 --- src/boot/efi/secure-boot.c | 119 +++++++++++++------------------------ 1 file changed, 40 insertions(+), 79 deletions(-) diff --git a/src/boot/efi/secure-boot.c b/src/boot/efi/secure-boot.c index 0e615c55e0..65457bf423 100644 --- a/src/boot/efi/secure-boot.c +++ b/src/boot/efi/secure-boot.c @@ -128,15 +128,10 @@ out_deallocate: } static struct SecurityOverride { - /* Our own security arch instances that we register onto original_handle, thereby replacing the - * firmware provided instances. */ - EFI_SECURITY_ARCH_PROTOCOL override; - EFI_SECURITY2_ARCH_PROTOCOL override2; - - /* These are saved so we can uninstall our own instance later. */ - EFI_HANDLE original_handle, original_handle2; - EFI_SECURITY_ARCH_PROTOCOL *original_security; - EFI_SECURITY2_ARCH_PROTOCOL *original_security2; + EFI_SECURITY_ARCH_PROTOCOL *security; + EFI_SECURITY2_ARCH_PROTOCOL *security2; + EFI_SECURITY_FILE_AUTHENTICATION_STATE original_hook; + EFI_SECURITY2_FILE_AUTHENTICATION original_hook2; security_validator_t validator; const void *validator_ctx; @@ -148,13 +143,13 @@ static EFIAPI EFI_STATUS security_hook( const EFI_DEVICE_PATH *file) { assert(security_override.validator); - assert(security_override.original_security); + assert(security_override.security); + assert(security_override.original_hook); if (security_override.validator(security_override.validator_ctx, file, NULL, 0)) return EFI_SUCCESS; - return security_override.original_security->FileAuthenticationState( - security_override.original_security, authentication_status, file); + return security_override.original_hook(security_override.security, authentication_status, file); } static EFIAPI EFI_STATUS security2_hook( @@ -165,92 +160,58 @@ static EFIAPI EFI_STATUS security2_hook( BOOLEAN boot_policy) { assert(security_override.validator); - assert(security_override.original_security2); + assert(security_override.security2); + assert(security_override.original_hook2); if (security_override.validator(security_override.validator_ctx, device_path, file_buffer, file_size)) return EFI_SUCCESS; - return security_override.original_security2->FileAuthentication( - security_override.original_security2, device_path, file_buffer, file_size, boot_policy); + return security_override.original_hook2( + security_override.security2, device_path, file_buffer, file_size, boot_policy); } -static EFI_STATUS install_security_override_one( - EFI_GUID guid, void *override, EFI_HANDLE *ret_original_handle, void **ret_original_security) { +/* This replaces the platform provided security arch protocols hooks (defined in the UEFI Platform + * Initialization Specification) with our own that uses the given validator to decide if a image is to be + * trusted. If not running in secure boot or the protocols are not available nothing happens. The override + * must be removed with uninstall_security_override() after LoadImage() has been called. + * + * This is a hack as we do not own the security protocol instances and modifying them is not an official part + * of their spec. But there is little else we can do to circumvent secure boot short of implementing our own + * PE loader. We could replace the firmware instances with our own instance using + * ReinstallProtocolInterface(), but some firmware will still use the old ones. */ +void install_security_override(security_validator_t validator, const void *validator_ctx) { EFI_STATUS err; - assert(override); - assert(ret_original_handle); - assert(ret_original_security); - - _cleanup_free_ EFI_HANDLE *handles = NULL; - size_t n_handles = 0; - - err = BS->LocateHandleBuffer(ByProtocol, &guid, NULL, &n_handles, &handles); - if (err != EFI_SUCCESS) - /* No security arch protocol around? */ - return err; - - /* There should only ever be one security arch protocol instance, but let's be paranoid here. */ - assert(n_handles == 1); - - void *security = NULL; - err = BS->LocateProtocol(&guid, NULL, &security); - if (err != EFI_SUCCESS) - return log_error_status_stall(err, u"Error getting security arch protocol: %r", err); - - err = BS->ReinstallProtocolInterface(handles[0], &guid, security, override); - if (err != EFI_SUCCESS) - return log_error_status_stall(err, u"Error overriding security arch protocol: %r", err); - - *ret_original_security = security; - *ret_original_handle = handles[0]; - return EFI_SUCCESS; -} - -/* This replaces the platform provided security arch protocols (defined in the UEFI Platform Initialization - * Specification) with the provided override instances. If not running in secure boot or the protocols are - * not available nothing happens. The override instances are provided with the necessary info to undo this - * in uninstall_security_override(). */ -void install_security_override(security_validator_t validator, const void *validator_ctx) { assert(validator); if (!secure_boot_enabled()) return; security_override = (struct SecurityOverride) { - { .FileAuthenticationState = security_hook, }, - { .FileAuthentication = security2_hook, }, .validator = validator, .validator_ctx = validator_ctx, }; - (void) install_security_override_one( - (EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID, - &security_override.override, - &security_override.original_handle, - (void **) &security_override.original_security); - (void) install_security_override_one( - (EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID, - &security_override.override2, - &security_override.original_handle2, - (void **) &security_override.original_security2); + EFI_SECURITY_ARCH_PROTOCOL *security = NULL; + err = BS->LocateProtocol(&(EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID, NULL, (void **) &security); + if (err == EFI_SUCCESS) { + security_override.security = security; + security_override.original_hook = security->FileAuthenticationState; + security->FileAuthenticationState = security_hook; + } + + EFI_SECURITY2_ARCH_PROTOCOL *security2 = NULL; + err = BS->LocateProtocol(&(EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID, NULL, (void **) &security2); + if (err == EFI_SUCCESS) { + security_override.security2 = security2; + security_override.original_hook2 = security2->FileAuthentication; + security2->FileAuthentication = security2_hook; + } } void uninstall_security_override(void) { - /* We use assert_se here to guarantee the system is not in a weird state in the unlikely case of an - * error restoring the original protocols. */ - - if (security_override.original_handle) - assert_se(BS->ReinstallProtocolInterface( - security_override.original_handle, - &(EFI_GUID) EFI_SECURITY_ARCH_PROTOCOL_GUID, - &security_override.override, - security_override.original_security) == EFI_SUCCESS); - - if (security_override.original_handle2) - assert_se(BS->ReinstallProtocolInterface( - security_override.original_handle2, - &(EFI_GUID) EFI_SECURITY2_ARCH_PROTOCOL_GUID, - &security_override.override2, - security_override.original_security2) == EFI_SUCCESS); + if (security_override.original_hook) + security_override.security->FileAuthenticationState = security_override.original_hook; + if (security_override.original_hook2) + security_override.security2->FileAuthentication = security_override.original_hook2; }