diff --git a/README.md b/README.md index 9aaeb3d..3d19d14 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,13 @@ on the ESP. The chain of trust is maintained by validating the signature on the Linux kernel and embedding a cryptographic hash of the initrd into the signed UKI. +The stub is available in a "thin" and a "fat" variant. The "thin" variant is +the one described above and is tailor made for NixOS. The "fat" variant aims to +work exactly like the `systemd-stub`---in fact, it's supposed to eventually +replace it. The "thin" variant is the default, you can build it from the stub +directory with `cargo build`. The "fat" variant needs to be enabled at build +time with `cargo build --no-default-features --features fat`. + The stub lives in `rust/stub`. ### Fwupd diff --git a/flake.nix b/flake.nix index b511a82..41765cb 100644 --- a/flake.nix +++ b/flake.nix @@ -59,6 +59,17 @@ } ); + flake.nixosModules.uki = moduleWithSystem ( + perSystem@{ config }: + { lib, ... }: { + imports = [ + ./nix/modules/uki.nix + ]; + + boot.loader.uki.stub = lib.mkDefault "${perSystem.config.packages.fatStub}/bin/lanzaboote_stub.efi"; + } + ); + systems = [ "x86_64-linux" @@ -81,7 +92,7 @@ craneLib = crane.lib.x86_64-linux.overrideToolchain uefi-rust-stable; # Build attributes for a Rust application. - buildRustApp = + buildRustApp = lib.makeOverridable ( { src , target ? null , doCheck ? true @@ -126,7 +137,8 @@ }); rustfmt = craneLib.cargoFmt (commonArgs // { inherit cargoArtifacts; }); - }; + } + ); stubCrane = buildRustApp { src = craneLib.cleanCargoSource ./rust/stub; @@ -134,7 +146,14 @@ doCheck = false; }; + fatStubCrane = stubCrane.override { + extraArgs = { + cargoExtraArgs = "--no-default-features --features fat"; + }; + }; + stub = stubCrane.package; + fatStub = fatStubCrane.package; toolCrane = buildRustApp { src = ./rust/tool; @@ -164,7 +183,7 @@ in { packages = { - inherit stub; + inherit stub fatStub; tool = wrappedTool; lzbt = wrappedTool; }; @@ -173,15 +192,27 @@ inherit (config.packages) tool; }; - checks = { - toolClippy = toolCrane.clippy; - stubClippy = stubCrane.clippy; - toolFmt = toolCrane.rustfmt; - stubFmt = stubCrane.rustfmt; - } // (import ./nix/tests/lanzaboote.nix { - inherit pkgs; - lanzabooteModule = self.nixosModules.lanzaboote; - }); + checks = + let + nixosLib = import (pkgs.path + "/nixos/lib") { }; + runTest = module: nixosLib.runTest { + imports = [ module ]; + hostPkgs = pkgs; + }; + in + { + toolClippy = toolCrane.clippy; + stubClippy = stubCrane.clippy; + fatStubClippy = fatStubCrane.clippy; + toolFmt = toolCrane.rustfmt; + stubFmt = stubCrane.rustfmt; + } // (import ./nix/tests/lanzaboote.nix { + inherit pkgs; + lanzabooteModule = self.nixosModules.lanzaboote; + }) // (import ./nix/tests/stub.nix { + inherit pkgs runTest; + ukiModule = self.nixosModules.uki; + }); pre-commit = { check.enable = true; @@ -193,9 +224,17 @@ }; devShells.default = pkgs.mkShell { - shellHook = '' - ${config.pre-commit.installationScript} - ''; + shellHook = + let + systemdUkify = pkgs.systemdMinimal.override { + withEfi = true; + withUkify = true; + }; + in + '' + ${config.pre-commit.installationScript} + export PATH=$PATH:${systemdUkify}/lib/systemd + ''; packages = let diff --git a/nix/modules/uki.nix b/nix/modules/uki.nix new file mode 100644 index 0000000..5c465b6 --- /dev/null +++ b/nix/modules/uki.nix @@ -0,0 +1,61 @@ +# This module introduces a simple boot loader installer that installs a UKI, +# leveraging bootspec. It is only designed to be useful in tests where +# rebuilding is unlikely/hard. + +{ config, lib, pkgs, ... }: + +let + cfg = config.boot.loader.uki; +in +{ + options.boot.loader.uki = { + enable = lib.mkEnableOption "UKI"; + + stub = lib.mkOption { + type = lib.types.path; + description = "Path to the UKI stub to use."; + }; + }; + + config = + let + systemdUkify = pkgs.systemdMinimal.override { + withEfi = true; + withUkify = true; + }; + in + lib.mkIf cfg.enable { + boot.bootspec.enable = true; + boot.loader.external = { + enable = true; + installHook = + let + bootspecNamespace = ''"org.nixos.bootspec.v1"''; + installer = pkgs.writeShellApplication { + name = "install-uki"; + runtimeInputs = with pkgs; [ jq systemd binutils ]; + text = '' + boot_json=/nix/var/nix/profiles/system-1-link/boot.json + kernel=$(jq -r '.${bootspecNamespace}.kernel' "$boot_json") + initrd=$(jq -r '.${bootspecNamespace}.initrd' "$boot_json") + init=$(jq -r '.${bootspecNamespace}.init' "$boot_json") + + ${systemdUkify}/lib/systemd/ukify \ + "$kernel" \ + "$initrd" \ + --stub=${cfg.stub} \ + --cmdline="init=$init ${builtins.toString config.boot.kernelParams}" \ + --os-release="@${config.system.build.etc}/etc/os-release" \ + --output=uki.efi + + esp=${config.boot.loader.efi.efiSysMountPoint} + + bootctl install --esp-path="$esp" + install uki.efi "$esp"/EFI/Linux/ + ''; + }; + in + "${installer}/bin/install-uki"; + }; + }; +} diff --git a/nix/tests/stub.nix b/nix/tests/stub.nix new file mode 100644 index 0000000..ee44b3f --- /dev/null +++ b/nix/tests/stub.nix @@ -0,0 +1,44 @@ +{ pkgs, runTest, ukiModule }: + +let + common = _: { + imports = [ ukiModule ]; + + virtualisation = { + useBootLoader = true; + useEFIBoot = true; + }; + + boot.loader.uki.enable = true; + boot.loader.efi = { + canTouchEfiVariables = true; + }; + }; +in +{ + # This test serves as a baseline to make sure that the custom boot installer + # script defined in the ukiModule works with the upstream systemd-stub. When + # this test fails something is very wrong. + systemd-stub = runTest { + name = "systemd-stub"; + nodes.machine = _: { + imports = [ common ]; + boot.loader.uki.stub = "${pkgs.systemd}/lib/systemd/boot/efi/linuxx64.efi.stub"; + }; + testScript = '' + machine.start() + print(machine.succeed("bootctl status")) + ''; + }; + + fatStub = runTest { + name = "fat-stub"; + nodes.machine = _: { + imports = [ common ]; + }; + testScript = '' + machine.start() + print(machine.succeed("bootctl status")) + ''; + }; +} diff --git a/rust/stub/Cargo.toml b/rust/stub/Cargo.toml index f06d981..363c501 100644 --- a/rust/stub/Cargo.toml +++ b/rust/stub/Cargo.toml @@ -16,10 +16,15 @@ bitflags = "2.2.1" log = { version = "0.4.17", default-features = false, features = [ "max_level_info", "release_max_level_warn" ]} # Use software implementation because the UEFI target seems to need it. -sha2 = { version = "0.10.6", default-features = false, features = ["force-soft"] } +sha2 = { version = "0.10.6", default-features = false, features = ["force-soft"], optional = true } # SHA1 for TPM TCG interface version 1. sha1_smol = "1.0.0" +[features] +default = [ "thin" ] +thin = ["dep:sha2"] +fat = [] + [profile.release] opt-level = "s" lto = true diff --git a/rust/stub/src/common.rs b/rust/stub/src/common.rs new file mode 100644 index 0000000..9c567b7 --- /dev/null +++ b/rust/stub/src/common.rs @@ -0,0 +1,35 @@ +use alloc::vec::Vec; +use uefi::{prelude::*, CStr16, CString16, Result}; + +use crate::linux_loader::InitrdLoader; +use crate::pe_loader::Image; +use crate::pe_section::pe_section_as_string; + +/// Extract a string, stored as UTF-8, from a PE section. +pub fn extract_string(pe_data: &[u8], section: &str) -> Result { + let string = pe_section_as_string(pe_data, section).ok_or(Status::INVALID_PARAMETER)?; + + Ok(CString16::try_from(string.as_str()).map_err(|_| Status::INVALID_PARAMETER)?) +} + +/// Boot the Linux kernel without checking the PE signature. +/// +/// We assume that the caller has made sure that the image is safe to +/// be loaded using other means. +pub fn boot_linux_unchecked( + handle: Handle, + system_table: SystemTable, + kernel_data: Vec, + kernel_cmdline: &CStr16, + initrd_data: Vec, +) -> uefi::Result<()> { + let kernel = + Image::load(system_table.boot_services(), &kernel_data).expect("Failed to load the kernel"); + + let mut initrd_loader = InitrdLoader::new(system_table.boot_services(), handle, initrd_data)?; + + let status = unsafe { kernel.start(handle, &system_table, kernel_cmdline) }; + + initrd_loader.uninstall(system_table.boot_services())?; + status.to_result() +} diff --git a/rust/stub/src/fat.rs b/rust/stub/src/fat.rs new file mode 100644 index 0000000..4a39f0a --- /dev/null +++ b/rust/stub/src/fat.rs @@ -0,0 +1,67 @@ +use alloc::vec::Vec; +use uefi::{prelude::*, CString16, Result}; + +use crate::common::{boot_linux_unchecked, extract_string}; +use crate::pe_section::pe_section; +use crate::uefi_helpers::booted_image_file; + +/// Extract bytes from a PE section. +pub fn extract_bytes(pe_data: &[u8], section: &str) -> Result> { + let bytes: Vec = pe_section(pe_data, section) + .ok_or(Status::INVALID_PARAMETER)? + .try_into() + .map_err(|_| Status::INVALID_PARAMETER)?; + + Ok(bytes) +} + +/// The configuration that is embedded at build time. +/// +/// After this stub is built, configuration need to be embedded into the binary by adding PE +/// sections. This struct represents that information. +struct EmbeddedConfiguration { + /// The kernel command-line. + cmdline: CString16, + + /// The kernel as raw bytes. + kernel: Vec, + + /// The initrd as raw bytes. + initrd: Vec, +} + +impl EmbeddedConfiguration { + fn new(file_data: &[u8]) -> Result { + Ok(Self { + kernel: extract_bytes(file_data, ".linux")?, + initrd: extract_bytes(file_data, ".initrd")?, + cmdline: extract_string(file_data, ".cmdline")?, + }) + } +} + +pub fn boot_linux(handle: Handle, mut system_table: SystemTable) -> Status { + uefi_services::init(&mut system_table).unwrap(); + + // SAFETY: We get a slice that represents our currently running + // image and then parse the PE data structures from it. This is + // safe, because we don't touch any data in the data sections that + // might conceivably change while we look at the slice. + let config = unsafe { + EmbeddedConfiguration::new( + booted_image_file(system_table.boot_services()) + .unwrap() + .as_slice(), + ) + .expect("Failed to extract configuration from binary.") + }; + + boot_linux_unchecked( + handle, + system_table, + config.kernel, + &config.cmdline, + config.initrd, + ) + .status() +} diff --git a/rust/stub/src/main.rs b/rust/stub/src/main.rs index 391f2b0..7beb836 100644 --- a/rust/stub/src/main.rs +++ b/rust/stub/src/main.rs @@ -4,6 +4,7 @@ extern crate alloc; +mod common; mod efivars; mod linux_loader; mod measure; @@ -13,19 +14,19 @@ mod tpm; mod uefi_helpers; mod unified_sections; -use alloc::vec::Vec; +#[cfg(feature = "fat")] +mod fat; + +#[cfg(feature = "thin")] +mod thin; + use efivars::{export_efi_variables, get_loader_features, EfiLoaderFeatures}; -use log::{info, warn}; +use log::info; use measure::measure_image; -use pe_loader::Image; -use pe_section::{pe_section, pe_section_as_string}; -use sha2::{Digest, Sha256}; use tpm::tpm_available; -use uefi::{prelude::*, proto::loaded_image::LoadedImage, CStr16, CString16, Result}; +use uefi::prelude::*; -use crate::{linux_loader::InitrdLoader, uefi_helpers::booted_image_file}; - -type Hash = sha2::digest::Output; +use crate::uefi_helpers::booted_image_file; /// Print the startup logo on boot. fn print_logo() { @@ -42,177 +43,12 @@ fn print_logo() { ); } -/// The configuration that is embedded at build time. -/// -/// After lanzaboote is built, lzbt needs to embed configuration -/// into the binary. This struct represents that information. -struct EmbeddedConfiguration { - /// The filename of the kernel to be booted. This filename is - /// relative to the root of the volume that contains the - /// lanzaboote binary. - kernel_filename: CString16, - - /// The cryptographic hash of the kernel. - kernel_hash: Hash, - - /// The filename of the initrd to be passed to the kernel. See - /// `kernel_filename` for how to interpret these filenames. - initrd_filename: CString16, - - /// The cryptographic hash of the initrd. This hash is computed - /// over the whole PE binary, not only the embedded initrd. - initrd_hash: Hash, - - /// The kernel command-line. - cmdline: CString16, -} - -/// Extract a string, stored as UTF-8, from a PE section. -fn extract_string(pe_data: &[u8], section: &str) -> Result { - let string = pe_section_as_string(pe_data, section).ok_or(Status::INVALID_PARAMETER)?; - - Ok(CString16::try_from(string.as_str()).map_err(|_| Status::INVALID_PARAMETER)?) -} - -/// Extract a Blake3 hash from a PE section. -fn extract_hash(pe_data: &[u8], section: &str) -> Result { - let array: [u8; 32] = pe_section(pe_data, section) - .ok_or(Status::INVALID_PARAMETER)? - .try_into() - .map_err(|_| Status::INVALID_PARAMETER)?; - - Ok(array.into()) -} - -impl EmbeddedConfiguration { - fn new(file_data: &[u8]) -> Result { - Ok(Self { - kernel_filename: extract_string(file_data, ".kernelp")?, - kernel_hash: extract_hash(file_data, ".kernelh")?, - - initrd_filename: extract_string(file_data, ".initrdp")?, - initrd_hash: extract_hash(file_data, ".initrdh")?, - - cmdline: extract_string(file_data, ".cmdline")?, - }) - } -} - -/// Boot the Linux kernel without checking the PE signature. -/// -/// We assume that the caller has made sure that the image is safe to -/// be loaded using other means. -fn boot_linux_unchecked( - handle: Handle, - system_table: SystemTable, - kernel_data: Vec, - kernel_cmdline: &CStr16, - initrd_data: Vec, -) -> uefi::Result<()> { - let kernel = - Image::load(system_table.boot_services(), &kernel_data).expect("Failed to load the kernel"); - - let mut initrd_loader = InitrdLoader::new(system_table.boot_services(), handle, initrd_data)?; - - let status = unsafe { kernel.start(handle, &system_table, kernel_cmdline) }; - - initrd_loader.uninstall(system_table.boot_services())?; - status.to_result() -} - -/// Boot the Linux kernel via the UEFI PE loader. -/// -/// This should only succeed when UEFI Secure Boot is off (or -/// broken...), because the Lanzaboote tool does not sign the kernel. -/// -/// In essence, we can use this routine to detect whether Secure Boot -/// is actually enabled. -fn boot_linux_uefi( - handle: Handle, - system_table: SystemTable, - kernel_data: Vec, - kernel_cmdline: &CStr16, - initrd_data: Vec, -) -> uefi::Result<()> { - let kernel_handle = system_table.boot_services().load_image( - handle, - uefi::table::boot::LoadImageSource::FromBuffer { - buffer: &kernel_data, - file_path: None, - }, - )?; - - let mut kernel_image = system_table - .boot_services() - .open_protocol_exclusive::(kernel_handle)?; - - unsafe { - kernel_image.set_load_options( - kernel_cmdline.as_ptr() as *const u8, - // This unwrap is "safe" in the sense that any - // command-line that doesn't fit 4G is surely broken. - u32::try_from(kernel_cmdline.num_bytes()).unwrap(), - ); - } - - let mut initrd_loader = InitrdLoader::new(system_table.boot_services(), handle, initrd_data)?; - - let status = system_table - .boot_services() - .start_image(kernel_handle) - .status(); - - initrd_loader.uninstall(system_table.boot_services())?; - status.to_result() -} - #[entry] fn main(handle: Handle, mut system_table: SystemTable) -> Status { uefi_services::init(&mut system_table).unwrap(); print_logo(); - // SAFETY: We get a slice that represents our currently running - // image and then parse the PE data structures from it. This is - // safe, because we don't touch any data in the data sections that - // might conceivably change while we look at the slice. - let config: EmbeddedConfiguration = unsafe { - EmbeddedConfiguration::new( - booted_image_file(system_table.boot_services()) - .unwrap() - .as_slice(), - ) - .expect("Failed to extract configuration from binary. Did you run lzbt?") - }; - - let kernel_data; - let initrd_data; - - { - let mut file_system = system_table - .boot_services() - .get_image_file_system(handle) - .expect("Failed to get file system handle"); - - kernel_data = file_system - .read(&*config.kernel_filename) - .expect("Failed to read kernel file into memory"); - initrd_data = file_system - .read(&*config.initrd_filename) - .expect("Failed to read initrd file into memory"); - } - - let is_kernel_hash_correct = Sha256::digest(&kernel_data) == config.kernel_hash; - let is_initrd_hash_correct = Sha256::digest(&initrd_data) == config.initrd_hash; - - if !is_kernel_hash_correct { - warn!("Hash mismatch for kernel!"); - } - - if !is_initrd_hash_correct { - warn!("Hash mismatch for initrd!"); - } - if tpm_available(system_table.boot_services()) { info!("TPM available, will proceed to measurements."); unsafe { @@ -237,46 +73,17 @@ fn main(handle: Handle, mut system_table: SystemTable) -> Status { } export_efi_variables(&system_table).expect("Failed to export stub EFI variables"); - if is_kernel_hash_correct && is_initrd_hash_correct { - boot_linux_unchecked( - handle, - system_table, - kernel_data, - &config.cmdline, - initrd_data, - ) - .status() - } else { - // There is no good way to detect whether Secure Boot is - // enabled. This is unfortunate, because we want to give the - // user a way to recover from hash mismatches when Secure Boot - // is off. - // - // So in case we get a hash mismatch, we will try to load the - // Linux image using LoadImage. What happens then depends on - // whether Secure Boot is enabled: - // - // **With Secure Boot**, the firmware will reject loading the - // image with status::SECURITY_VIOLATION. - // - // **Without Secure Boot**, the firmware will just load the - // Linux kernel. - // - // This is the behavior we want. A slight turd is that we - // increase the attack surface here by exposing the unverfied - // Linux image to the UEFI firmware. But in case the PE loader - // of the firmware is broken, we have little hope of security - // anyway. + let status; - warn!("Trying to continue as non-Secure Boot. This will fail when Secure Boot is enabled."); - - boot_linux_uefi( - handle, - system_table, - kernel_data, - &config.cmdline, - initrd_data, - ) - .status() + #[cfg(feature = "fat")] + { + status = fat::boot_linux(handle, system_table) } + + #[cfg(feature = "thin")] + { + status = thin::boot_linux(handle, system_table) + } + + status } diff --git a/rust/stub/src/thin.rs b/rust/stub/src/thin.rs new file mode 100644 index 0000000..d66e056 --- /dev/null +++ b/rust/stub/src/thin.rs @@ -0,0 +1,193 @@ +use alloc::vec::Vec; +use log::warn; +use sha2::{Digest, Sha256}; +use uefi::{prelude::*, proto::loaded_image::LoadedImage, CStr16, CString16, Result}; + +use crate::common::{boot_linux_unchecked, extract_string}; +use crate::pe_section::pe_section; +use crate::{linux_loader::InitrdLoader, uefi_helpers::booted_image_file}; + +type Hash = sha2::digest::Output; + +/// The configuration that is embedded at build time. +/// +/// After this stub is built, lzbt needs to embed configuration into the binary by adding PE +/// sections. This struct represents that information. +struct EmbeddedConfiguration { + /// The filename of the kernel to be booted. This filename is + /// relative to the root of the volume that contains the + /// lanzaboote binary. + kernel_filename: CString16, + + /// The cryptographic hash of the kernel. + kernel_hash: Hash, + + /// The filename of the initrd to be passed to the kernel. See + /// `kernel_filename` for how to interpret these filenames. + initrd_filename: CString16, + + /// The cryptographic hash of the initrd. This hash is computed + /// over the whole PE binary, not only the embedded initrd. + initrd_hash: Hash, + + /// The kernel command-line. + cmdline: CString16, +} + +/// Extract a SHA256 hash from a PE section. +fn extract_hash(pe_data: &[u8], section: &str) -> Result { + let array: [u8; 32] = pe_section(pe_data, section) + .ok_or(Status::INVALID_PARAMETER)? + .try_into() + .map_err(|_| Status::INVALID_PARAMETER)?; + + Ok(array.into()) +} + +impl EmbeddedConfiguration { + fn new(file_data: &[u8]) -> Result { + Ok(Self { + kernel_filename: extract_string(file_data, ".kernelp")?, + kernel_hash: extract_hash(file_data, ".kernelh")?, + + initrd_filename: extract_string(file_data, ".initrdp")?, + initrd_hash: extract_hash(file_data, ".initrdh")?, + + cmdline: extract_string(file_data, ".cmdline")?, + }) + } +} + +/// Boot the Linux kernel via the UEFI PE loader. +/// +/// This should only succeed when UEFI Secure Boot is off (or +/// broken...), because the Lanzaboote tool does not sign the kernel. +/// +/// In essence, we can use this routine to detect whether Secure Boot +/// is actually enabled. +fn boot_linux_uefi( + handle: Handle, + system_table: SystemTable, + kernel_data: Vec, + kernel_cmdline: &CStr16, + initrd_data: Vec, +) -> uefi::Result<()> { + let kernel_handle = system_table.boot_services().load_image( + handle, + uefi::table::boot::LoadImageSource::FromBuffer { + buffer: &kernel_data, + file_path: None, + }, + )?; + + let mut kernel_image = system_table + .boot_services() + .open_protocol_exclusive::(kernel_handle)?; + + unsafe { + kernel_image.set_load_options( + kernel_cmdline.as_ptr() as *const u8, + // This unwrap is "safe" in the sense that any + // command-line that doesn't fit 4G is surely broken. + u32::try_from(kernel_cmdline.num_bytes()).unwrap(), + ); + } + + let mut initrd_loader = InitrdLoader::new(system_table.boot_services(), handle, initrd_data)?; + + let status = system_table + .boot_services() + .start_image(kernel_handle) + .status(); + + initrd_loader.uninstall(system_table.boot_services())?; + status.to_result() +} + +pub fn boot_linux(handle: Handle, mut system_table: SystemTable) -> Status { + uefi_services::init(&mut system_table).unwrap(); + + // SAFETY: We get a slice that represents our currently running + // image and then parse the PE data structures from it. This is + // safe, because we don't touch any data in the data sections that + // might conceivably change while we look at the slice. + let config = unsafe { + EmbeddedConfiguration::new( + booted_image_file(system_table.boot_services()) + .unwrap() + .as_slice(), + ) + .expect("Failed to extract configuration from binary. Did you run lzbt?") + }; + + let kernel_data; + let initrd_data; + + { + let mut file_system = system_table + .boot_services() + .get_image_file_system(handle) + .expect("Failed to get file system handle"); + + kernel_data = file_system + .read(&*config.kernel_filename) + .expect("Failed to read kernel file into memory"); + initrd_data = file_system + .read(&*config.initrd_filename) + .expect("Failed to read initrd file into memory"); + } + + let is_kernel_hash_correct = Sha256::digest(&kernel_data) == config.kernel_hash; + let is_initrd_hash_correct = Sha256::digest(&initrd_data) == config.initrd_hash; + + if !is_kernel_hash_correct { + warn!("Hash mismatch for kernel!"); + } + + if !is_initrd_hash_correct { + warn!("Hash mismatch for initrd!"); + } + + if is_kernel_hash_correct && is_initrd_hash_correct { + boot_linux_unchecked( + handle, + system_table, + kernel_data, + &config.cmdline, + initrd_data, + ) + .status() + } else { + // There is no good way to detect whether Secure Boot is + // enabled. This is unfortunate, because we want to give the + // user a way to recover from hash mismatches when Secure Boot + // is off. + // + // So in case we get a hash mismatch, we will try to load the + // Linux image using LoadImage. What happens then depends on + // whether Secure Boot is enabled: + // + // **With Secure Boot**, the firmware will reject loading the + // image with status::SECURITY_VIOLATION. + // + // **Without Secure Boot**, the firmware will just load the + // Linux kernel. + // + // This is the behavior we want. A slight turd is that we + // increase the attack surface here by exposing the unverfied + // Linux image to the UEFI firmware. But in case the PE loader + // of the firmware is broken, we have little hope of security + // anyway. + + warn!("Trying to continue as non-Secure Boot. This will fail when Secure Boot is enabled."); + + boot_linux_uefi( + handle, + system_table, + kernel_data, + &config.cmdline, + initrd_data, + ) + .status() + } +}