mirror of
				https://git.proxmox.com/git/efi-boot-shim
				synced 2025-11-04 05:38:22 +00:00 
			
		
		
		
	This is a preparation commit for removing the setup_console(1) calls from MokManager and shim so that we don't force the EFI console to switch to text-mode. This commit replaces all direct calls to Print / PrintAt with calls to the new helpers (no functional changes) so that we can delay calling setup_console(1) till the first Print call in a follow-up patch. Signed-off-by: Hans de Goede <hdegoede@redhat.com>
		
			
				
	
	
		
			227 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			227 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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"
 | 
						|
 | 
						|
static EFI_SYSTEM_TABLE *systab;
 | 
						|
 | 
						|
EFI_SYSTEM_TABLE *
 | 
						|
get_active_systab(void)
 | 
						|
{
 | 
						|
	if (systab)
 | 
						|
		return systab;
 | 
						|
	return ST;
 | 
						|
}
 | 
						|
 | 
						|
static typeof(systab->BootServices->LoadImage) system_load_image;
 | 
						|
static typeof(systab->BootServices->StartImage) system_start_image;
 | 
						|
static typeof(systab->BootServices->Exit) system_exit;
 | 
						|
static typeof(systab->BootServices->ExitBootServices) system_exit_boot_services;
 | 
						|
 | 
						|
static EFI_HANDLE last_loaded_image;
 | 
						|
 | 
						|
void
 | 
						|
unhook_system_services(void)
 | 
						|
{
 | 
						|
	if (!systab)
 | 
						|
		return;
 | 
						|
 | 
						|
	systab->BootServices->LoadImage = system_load_image;
 | 
						|
	systab->BootServices->StartImage = system_start_image;
 | 
						|
	systab->BootServices->ExitBootServices = system_exit_boot_services;
 | 
						|
	gBS = systab->BootServices;
 | 
						|
}
 | 
						|
 | 
						|
static EFI_STATUS EFIAPI
 | 
						|
load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle,
 | 
						|
	EFI_DEVICE_PATH *DevicePath, VOID *SourceBuffer,
 | 
						|
	UINTN SourceSize, EFI_HANDLE *ImageHandle)
 | 
						|
{
 | 
						|
	EFI_STATUS efi_status;
 | 
						|
 | 
						|
	unhook_system_services();
 | 
						|
	efi_status = gBS->LoadImage(BootPolicy, ParentImageHandle, DevicePath,
 | 
						|
				    SourceBuffer, SourceSize, ImageHandle);
 | 
						|
	hook_system_services(systab);
 | 
						|
	if (EFI_ERROR(efi_status))
 | 
						|
		last_loaded_image = NULL;
 | 
						|
	else
 | 
						|
		last_loaded_image = *ImageHandle;
 | 
						|
	return efi_status;
 | 
						|
}
 | 
						|
 | 
						|
static EFI_STATUS EFIAPI
 | 
						|
replacement_start_image(EFI_HANDLE image_handle, UINTN *exit_data_size, CHAR16 **exit_data)
 | 
						|
{
 | 
						|
	EFI_STATUS efi_status;
 | 
						|
	unhook_system_services();
 | 
						|
 | 
						|
	if (image_handle == last_loaded_image) {
 | 
						|
		loader_is_participating = 1;
 | 
						|
		uninstall_shim_protocols();
 | 
						|
	}
 | 
						|
	efi_status = gBS->StartImage(image_handle, exit_data_size, exit_data);
 | 
						|
	if (EFI_ERROR(efi_status)) {
 | 
						|
		if (image_handle == last_loaded_image) {
 | 
						|
			EFI_STATUS efi_status2 = install_shim_protocols();
 | 
						|
 | 
						|
			if (EFI_ERROR(efi_status2)) {
 | 
						|
				console_print(L"Something has gone seriously wrong: %r\n",
 | 
						|
					      efi_status2);
 | 
						|
				console_print(L"shim cannot continue, sorry.\n");
 | 
						|
				msleep(5000000);
 | 
						|
				gRT->ResetSystem(EfiResetShutdown,
 | 
						|
						 EFI_SECURITY_VIOLATION,
 | 
						|
						 0, NULL);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		hook_system_services(systab);
 | 
						|
		loader_is_participating = 0;
 | 
						|
	}
 | 
						|
	return efi_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 efi_status;
 | 
						|
		efi_status = gBS->ExitBootServices(image_key, map_key);
 | 
						|
		if (EFI_ERROR(efi_status))
 | 
						|
			hook_system_services(systab);
 | 
						|
		return efi_status;
 | 
						|
	}
 | 
						|
 | 
						|
	console_print(L"Bootloader has not verified loaded image.\n");
 | 
						|
	console_print(L"System is compromised.  halting.\n");
 | 
						|
	msleep(5000000);
 | 
						|
	gRT->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION, 0, NULL);
 | 
						|
	return EFI_SECURITY_VIOLATION;
 | 
						|
}
 | 
						|
 | 
						|
static EFI_STATUS EFIAPI
 | 
						|
do_exit(EFI_HANDLE ImageHandle, EFI_STATUS ExitStatus,
 | 
						|
	UINTN ExitDataSize, CHAR16 *ExitData)
 | 
						|
{
 | 
						|
	EFI_STATUS efi_status;
 | 
						|
 | 
						|
	shim_fini();
 | 
						|
 | 
						|
	efi_status = gBS->Exit(ImageHandle, ExitStatus,
 | 
						|
			       ExitDataSize, ExitData);
 | 
						|
	if (EFI_ERROR(efi_status)) {
 | 
						|
		EFI_STATUS efi_status2 = shim_init();
 | 
						|
 | 
						|
		if (EFI_ERROR(efi_status2)) {
 | 
						|
			console_print(L"Something has gone seriously wrong: %r\n",
 | 
						|
				      efi_status2);
 | 
						|
			console_print(L"shim cannot continue, sorry.\n");
 | 
						|
			msleep(5000000);
 | 
						|
			gRT->ResetSystem(EfiResetShutdown,
 | 
						|
					 EFI_SECURITY_VIOLATION, 0, NULL);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return efi_status;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
hook_system_services(EFI_SYSTEM_TABLE *local_systab)
 | 
						|
{
 | 
						|
	systab = local_systab;
 | 
						|
	gBS = systab->BootServices;
 | 
						|
 | 
						|
	/* We need to hook various calls to make this work... */
 | 
						|
 | 
						|
	/* We need LoadImage() hooked so that fallback.c can load shim
 | 
						|
	 * without having to fake LoadImage as well.  This allows it
 | 
						|
	 * to call the system LoadImage(), and have us track the output
 | 
						|
	 * and mark loader_is_participating in replacement_start_image.  This
 | 
						|
	 * means anything added by fallback has to be verified by the system
 | 
						|
	 * db, which we want to preserve anyway, since that's all launching
 | 
						|
	 * through BDS gives us. */
 | 
						|
	system_load_image = systab->BootServices->LoadImage;
 | 
						|
	systab->BootServices->LoadImage = load_image;
 | 
						|
 | 
						|
	/* 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 = replacement_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;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
unhook_exit(void)
 | 
						|
{
 | 
						|
	systab->BootServices->Exit = system_exit;
 | 
						|
	gBS = systab->BootServices;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
hook_exit(EFI_SYSTEM_TABLE *local_systab)
 | 
						|
{
 | 
						|
	systab = local_systab;
 | 
						|
	gBS = local_systab->BootServices;
 | 
						|
 | 
						|
	/* 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 = do_exit;
 | 
						|
}
 |