use core::{ffi::c_void, ptr::NonNull};
use uefi::{
Handle, Status,
proto::device_path::{DevicePath, FfiDevicePath},
};
use crate::{
BootResult,
boot::secure_boot::{SecureBootError, Validator, secure_boot_enabled},
system::protos::{Security2ArchProtocol, SecurityArchProtocol},
};
type AuthState = unsafe extern "efiapi" fn(
this: *const SecurityArchProtocol,
auth_status: u32,
file: *const FfiDevicePath,
) -> Status;
type Authentication = unsafe extern "efiapi" fn(
this: *const Security2ArchProtocol,
device_path: *const FfiDevicePath,
file_buffer: *mut c_void,
file_size: usize,
boot_policy: u8,
) -> Status;
#[derive(Clone, Copy, Default)]
pub(super) struct SecurityOverrideInner {
pub(super) security: Option<Handle>,
pub(super) security2: Option<Handle>,
pub(super) original_hook: Option<AuthState>,
pub(super) original_hook2: Option<Authentication>,
pub(super) validator: Option<Validator>,
pub(super) validator_ctx: Option<NonNull<u8>>,
}
impl SecurityOverrideInner {
pub(super) fn new(validator: Validator, validator_ctx: Option<NonNull<u8>>) -> Self {
let mut security_override = Self::default();
security_override.install_validator(validator, validator_ctx);
security_override
}
pub(super) fn install_validator(
&mut self,
validator: Validator,
validator_ctx: Option<NonNull<u8>>,
) {
if self.should_skip_install(validator, validator_ctx) {
return;
}
self.install_security1_hook();
self.install_security2_hook();
self.validator = Some(validator);
self.validator_ctx = validator_ctx;
}
pub(super) fn uninstall_validator(&self) {
self.uninstall_security1_hook();
self.uninstall_security2_hook();
}
fn should_skip_install(
&self,
validator: Validator,
validator_ctx: Option<NonNull<u8>>,
) -> bool {
if let Some(security_validator) = self.validator {
if core::ptr::fn_addr_eq(validator, security_validator)
&& self.validator_ctx == validator_ctx
{
return true;
}
self.uninstall_validator();
}
if !secure_boot_enabled() {
return true;
}
false
}
pub(super) fn call_validator(
&self,
device_path: Option<&DevicePath>,
file_buffer: Option<&mut [u8]>,
) -> BootResult<()> {
self.validator.map_or_else(
|| Err(SecureBootError::NoValidator.into()),
|validator| {
let validator_ctx = self.validator_ctx;
let file_size = file_buffer
.as_ref()
.map_or(0, |file_buffer| file_buffer.len());
validator(validator_ctx, device_path, file_buffer, file_size)
},
)
}
pub(super) unsafe fn call_original_hook(
&self,
this: *const SecurityArchProtocol,
auth_status: u32,
file: *const FfiDevicePath,
) -> Status {
self.original_hook
.map_or(Status::SUCCESS, |original_hook| unsafe {
original_hook(this, auth_status, file)
})
}
pub(super) unsafe fn call_original_hook2(
&self,
this: *const Security2ArchProtocol,
device_path: *const FfiDevicePath,
file_buffer: *mut c_void,
file_size: usize,
boot_policy: u8,
) -> Status {
self.original_hook2
.map_or(Status::SUCCESS, |original_hook2| unsafe {
original_hook2(this, device_path, file_buffer, file_size, boot_policy)
})
}
}