use core::ptr::NonNull;
use uefi::{
Handle, Identify,
boot::{self, ScopedProtocol},
cstr16,
proto::{device_path::DevicePath, media::fs::SimpleFileSystem, shim::ShimLock},
runtime::VariableVendor,
};
use crate::{
BootResult,
boot::secure_boot::{SecureBootError, SecurityOverrideGuard, secure_boot_enabled},
system::{
fs::UefiFileSystem,
helper::{device_path_to_text, locate_protocol},
protos::ShimImageLoader,
variable::{get_variable, set_variable},
},
};
fn validate_from_device_path(
mut device_path: &DevicePath,
shim: &ScopedProtocol<ShimLock>,
) -> BootResult<()> {
let handle = boot::locate_device_path::<SimpleFileSystem>(&mut device_path)?;
let mut fs = UefiFileSystem::from_handle(handle)?;
let path = device_path_to_text(device_path)?;
let file_buffer = fs.read(&path)?;
Ok(shim.verify(&file_buffer)?)
}
fn shim_loaded() -> bool {
boot::get_handle_for_protocol::<ShimLock>().is_ok()
}
fn shim_is_recent() -> bool {
boot::get_handle_for_protocol::<ShimImageLoader>().is_ok()
}
fn shim_validate(
_ctx: Option<NonNull<u8>>,
device_path: Option<&DevicePath>,
file_buffer: Option<&mut [u8]>,
_file_size: usize,
) -> BootResult<()> {
let shim = locate_protocol::<ShimLock>()?;
if let Some(file_buffer) = file_buffer {
return Ok(shim.verify(file_buffer)?);
}
if let Some(device_path) = device_path {
return validate_from_device_path(device_path, &shim);
}
Err(SecureBootError::NoDevicePathOrFile.into())
}
fn shim_retain_protocol() -> BootResult<()> {
let vendor = VariableVendor(ShimLock::GUID);
if !matches!(
get_variable::<bool>(cstr16!("ShimRetainProtocol"), Some(vendor)),
Ok(true)
) {
set_variable::<bool>(
cstr16!("ShimRetainProtocol"),
Some(vendor),
None,
Some(true),
)?;
}
Ok(())
}
pub(crate) fn shim_load_image(
parent: Handle,
source: boot::LoadImageSource<'_>,
) -> BootResult<Handle> {
if !shim_loaded() || shim_is_recent() || !secure_boot_enabled() {
return Ok(boot::load_image(parent, source)?);
}
shim_retain_protocol()?;
let _guard = SecurityOverrideGuard::new(shim_validate, None);
let handle = boot::load_image(parent, source);
Ok(handle?)
}