bootmgr_rs_core/boot/
secure_boot.rs

1// SPDX-FileCopyrightText: 2025 some100 <ootinnyoo@outlook.com>
2// SPDX-License-Identifier: MIT
3
4//! Secure Boot support module.
5//!
6//! Secure Boot interacts with the loading of images through the `SecurityArch` and
7//! `Security2Arch` protocol. In firmware, if any of these two are published, `LoadImage`
8//! must use those protocols on every image that is loaded. The `Security2Arch` protocol
9//! takes priority.
10//!
11//! Internally, the `FileAuthentication` and `FileAuthenticationState` methods are used for
12//! verifying images. These methods can also be replaced with our own custom made ones, mainly
13//! also using Shim.
14//!
15//! Even though Shim is the main consumer of this type of module, the overall architecture is
16//! very pluggable and custom validators not simply delegating to Shim can be used as well.
17//!
18//! This hooks onto `SecurityArch` and `Security2Arch` in order to replace their
19//! authenticators with custom ones using Shim or any other validator.
20//!
21//! These hooks are temporary and should be uninstalled after the image is loaded. This is done
22//! automatically through the `SecurityOverrideGuard` struct.
23
24use core::cell::Cell;
25use core::ptr::NonNull;
26
27use thiserror::Error;
28use uefi::{cstr16, proto::device_path::DevicePath, runtime::VariableVendor};
29
30use crate::{
31    BootResult, boot::secure_boot::security_override::SecurityOverrideInner,
32    system::variable::get_variable,
33};
34
35pub mod security_hooks;
36pub mod security_override;
37pub mod shim;
38
39/// An `Error` that may result from validating an image with Secure Boot.
40#[derive(Error, Debug)]
41pub enum SecureBootError {
42    /// Neither a device path nor a file buffer were specified to the image.
43    #[error("DevicePath and file buffer were both None")]
44    NoDevicePathOrFile,
45
46    /// A validator was not installed, but the security hooks were installed.
47    #[error("Validator was not installed")]
48    NoValidator,
49}
50
51/// The function signature for a validator.
52pub(super) type Validator = fn(
53    ctx: Option<NonNull<u8>>,
54    device_path: Option<&DevicePath>,
55    file_buffer: Option<&mut [u8]>,
56    file_size: usize,
57) -> BootResult<()>;
58
59/// An instance of [`SecurityOverrideInner`] that lasts for the lifetime of the program.
60///
61/// This is mandatory mainly due to the security hooks. We cannot provide arbitrary context to the security hooks, as the
62/// function signature is constant and decided by the UEFI spec. Without a global static state, the security hooks cannot
63/// possibly know of the existence of what custom validator we have installed, or where the original security validators
64/// are.
65///
66/// To partially counter the risk that a singular static state brings, this static is not exposed to anywhere other than
67/// [`SecurityOverrideGuard`]. This may need to be changed more than once in case `LoadImage` fails, and the override
68/// is still installed.
69static SECURITY_OVERRIDE: SecurityOverride = SecurityOverride {
70    inner: Cell::new(None),
71};
72
73/// The security override, for installing a custom validator.
74struct SecurityOverride {
75    /// The inner [`SecurityOverrideInner`] wrapped around a [`Cell`] for safety.
76    inner: Cell<Option<SecurityOverrideInner>>,
77}
78
79impl SecurityOverride {
80    /// Return a copy of the inner [`SecurityOverrideInner`].
81    ///
82    /// # Panics
83    ///
84    /// This will panic if the [`Cell`] is not yet initialized.
85    /// However, this is not possible since the [`Cell`] is always initalized at the start
86    /// of the program as a static. Therefore, this method cannot actually panic.
87    const fn get(&self) -> SecurityOverrideInner {
88        self.inner
89            .get()
90            .expect("The static Cell should always be initialized at the start of the program")
91    }
92}
93
94// SAFETY: uefi is a single threaded environment there is no notion of thread safety
95unsafe impl Sync for SecurityOverride {}
96
97/// A guard for [`SecurityOverride`]. When created, it will install a validator. When the
98/// override is eventually dropped, the validator will be uninstalled.
99pub(super) struct SecurityOverrideGuard;
100
101impl SecurityOverrideGuard {
102    /// Create a new [`SecurityOverrideGuard`]. Installs a validator and returns the guard.
103    ///
104    /// When the returned guard is dropped, the security override is automatically uninstalled.
105    pub(super) fn new(validator: Validator, validator_ctx: Option<NonNull<u8>>) -> Self {
106        install_security_override(validator, validator_ctx);
107        Self
108    }
109}
110
111impl Drop for SecurityOverrideGuard {
112    fn drop(&mut self) {
113        uninstall_security_override();
114    }
115}
116
117/// Tests if secure boot is enabled through a UEFI variable.
118#[must_use = "Has no effect if the result is unused"]
119fn secure_boot_enabled() -> bool {
120    matches!(
121        get_variable::<bool>(cstr16!("SecureBoot"), Some(VariableVendor::GLOBAL_VARIABLE)),
122        Ok(true)
123    )
124}
125
126/// Installs a security override given a [`Validator`] and optionally a `validator_ctx`.
127///
128/// You should use the [`SecurityOverrideGuard`] to safely ensure the override is dropped.
129fn install_security_override(validator: Validator, validator_ctx: Option<NonNull<u8>>) {
130    let security_override = &SECURITY_OVERRIDE;
131
132    security_override
133        .inner
134        .set(Some(SecurityOverrideInner::new(validator, validator_ctx)));
135}
136
137/// Uninstalls the security override. Should be used after installing the security override.
138///
139/// You should use the [`SecurityOverrideGuard`] to safely ensure the override is dropped.
140fn uninstall_security_override() {
141    let security_override = &SECURITY_OVERRIDE;
142
143    security_override.get().uninstall_validator();
144    security_override.inner.take();
145}