rework secure boot override

This commit is contained in:
2023-11-16 19:14:31 +01:00
parent 29b63e1848
commit 7655b589d6
5 changed files with 102 additions and 104 deletions

View File

@ -1,14 +1,13 @@
use core::{ffi::c_void, mem::MaybeUninit};
use uefi::{
guid,
prelude::BootServices,
proto::device_path::{build, DevicePath},
Identify, Status,
Guid, Identify, Status,
};
const LOADFILE2_GUID: uefi::Guid = guid!("4006c0c1-fcb3-403e-996d-4a6c8724e06d");
const LINUX_INITRD_DEVICE_GUID: uefi::Guid = guid!("5568e427-68fc-4f3d-ac74-ca555231cc68");
const LOADFILE2_GUID: Guid = Guid::parse_or_panic("4006c0c1-fcb3-403e-996d-4a6c8724e06d");
const LINUX_INITRD_DEVICE_GUID: Guid = Guid::parse_or_panic("5568e427-68fc-4f3d-ac74-ca555231cc68");
/// Extends the LoadFile2 interface with initrd buffer location and size
pub struct Loader {
@ -71,7 +70,7 @@ pub fn make_initrd_device_path<'a>(buf: &'a mut [MaybeUninit<u8>; 256]) -> &'a D
/// Copies data if valid
fn initrd_loadfile2(
this: *const Loader,
_proto: *const c_void,
_file_path: *const c_void,
_boot_policy: bool,
size: *mut usize,
buf: *mut c_void,
@ -83,7 +82,7 @@ fn initrd_loadfile2(
*size = (*this).size;
return Status::BUFFER_TOO_SMALL;
}
core::ptr::copy_nonoverlapping((*this).buf, buf, (*this).size);
(*this).buf.copy_to(buf, (*this).size);
}
Status::SUCCESS
}

View File

@ -8,7 +8,7 @@ use uefi::{
use crate::{
initrd::{install_initrd, make_loader},
pe::BootSections,
secureboot::install_security_override,
secureboot::run_in_security_override,
unicode::convert_8_to_16,
};
@ -20,33 +20,30 @@ pub fn start_linux(
boot_services: &BootServices,
sections: BootSections,
) -> Result {
// Override Secure Boot loader
let uninstall = install_security_override(boot_services);
// Load kernel as image from memory
// Load kernel as image from memory ignoring secure boot
// Image is already trusted (signed) by being part of this EFI program
let handle = unsafe {
boot_services
.load_image(
image_handle,
LoadImageSource::FromBuffer {
buffer: &*slice_from_raw_parts::<u8>(
sections.linux.pointer,
sections.linux.size as usize,
),
file_path: None,
},
)
.unwrap()
run_in_security_override(boot_services, || {
boot_services
.load_image(
image_handle,
LoadImageSource::FromBuffer {
buffer: &*slice_from_raw_parts::<u8>(
sections.linux.pointer,
sections.linux.size as usize,
),
file_path: None,
},
)
.unwrap()
})
};
// Remove secure boot override
uninstall();
let mut linux_image = boot_services
.open_protocol_exclusive::<LoadedImage>(handle)
.unwrap();
// Set cmdline for kernel image
// Set cmdline for kernel image (expected as utf16)
let (cmdline_utf16, cmdline_utf16_size) = convert_8_to_16(
boot_services,
sections.cmdline.pointer,
@ -62,7 +59,7 @@ pub fn start_linux(
);
// Install virtual initrd device
// Buffer is used to build the device path
// Buffer is used to build the device path, also needs to exist here for lifespan reasons
let mut buf = [MaybeUninit::<u8>::uninit(); 256];
install_initrd(boot_services, &mut buf, loader);

View File

@ -1,51 +1,33 @@
use core::ffi::c_void;
use log::info;
use uefi::{guid, prelude::BootServices, proto::Protocol, Identify};
use uefi::{prelude::BootServices, proto::unsafe_protocol};
#[unsafe_protocol("A46423E3-4617-49F1-B9FF-D1BFA9115839")]
struct SecurityArch {
pub handler: *const c_void,
}
unsafe impl Identify for SecurityArch {
const GUID: uefi::Guid = guid!("A46423E3-4617-49F1-B9FF-D1BFA9115839");
}
impl Protocol for SecurityArch {}
#[unsafe_protocol("94ab2f58-1438-4ef1-9152-18941a3a0e68")]
struct Security2Arch {
pub handler: *const c_void,
}
unsafe impl Identify for Security2Arch {
const GUID: uefi::Guid = guid!("94ab2f58-1438-4ef1-9152-18941a3a0e68");
}
impl Protocol for Security2Arch {}
/// Most basic security handler, always returns true
/// The ultimate yes-man
fn security_handler() -> bool {
true
}
/// Stores default security handlers to restore them when uninstalling
struct DefaultSecurityHandlers {
pub security1: Option<*mut *const c_void>,
pub security1_handler: Option<*const c_void>,
pub security2: Option<*mut *const c_void>,
pub security2_handler: Option<*const c_void>,
}
/// Secure Boot override to load kernel image from memory
/// Runs a function with a blind secure boot validator
/// Plagiarized from systemd-stub's "hack"
/// Returns an override uninstaller to restore default handlers
pub fn install_security_override(boot_services: &BootServices) -> impl Fn() -> () {
let mut defaults = DefaultSecurityHandlers {
security1: None,
security1_handler: None,
security2: None,
security2_handler: None,
};
/// There must be a better way
pub fn run_in_security_override<R, T: Fn() -> R>(boot_services: &BootServices, function: T) -> R {
// Backup storage
let mut df_security1 = None;
let mut df_security1_handler = None;
let mut df_security2 = None;
let mut df_security2_handler = None;
// Obtain SecurityArch protocol and replace handler
boot_services
.get_handle_for_protocol::<SecurityArch>()
@ -53,8 +35,8 @@ pub fn install_security_override(boot_services: &BootServices) -> impl Fn() -> (
boot_services
.open_protocol_exclusive::<SecurityArch>(h)
.and_then(|mut security| {
defaults.security1 = Some(core::ptr::addr_of_mut!(security.handler));
defaults.security1_handler = Some(security.handler);
df_security1 = Some(core::ptr::addr_of_mut!(security.handler));
df_security1_handler = Some(security.handler);
security.handler = security_handler as *const c_void;
Ok(())
})
@ -73,8 +55,8 @@ pub fn install_security_override(boot_services: &BootServices) -> impl Fn() -> (
boot_services
.open_protocol_exclusive::<Security2Arch>(h)
.and_then(|mut security| {
defaults.security2 = Some(core::ptr::addr_of_mut!(security.handler));
defaults.security2_handler = Some(security.handler);
df_security2 = Some(core::ptr::addr_of_mut!(security.handler));
df_security2_handler = Some(security.handler);
security.handler = security_handler as *const c_void;
Ok(())
})
@ -87,20 +69,13 @@ pub fn install_security_override(boot_services: &BootServices) -> impl Fn() -> (
info!("security2 not found");
});
// Returns restore function
return move || {
// Restore default SecurityArch handler
defaults.security1.and_then(|s1| {
defaults
.security1_handler
.and_then(|s1h| Some(unsafe { *s1 = s1h }))
});
let x = function();
// Restore default SecurityArch2 handler
defaults.security2.and_then(|s2| {
defaults
.security1_handler
.and_then(|s2h| Some(unsafe { *s2 = s2h }))
});
};
// Restore default SecurityArch handler
df_security1.and_then(|s1| df_security1_handler.and_then(|s1h| Some(unsafe { *s1 = s1h })));
// Restore default SecurityArch2 handler
df_security2.and_then(|s2| df_security2_handler.and_then(|s2h| Some(unsafe { *s2 = s2h })));
x
}