Merge pull request #182 from nix-community/fat-uki

stub: add fat variant
This commit is contained in:
nikstur 2023-06-01 22:43:53 +02:00 committed by GitHub
commit da24357977
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 488 additions and 230 deletions

View File

@ -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 signature on the Linux kernel and embedding a cryptographic hash of
the initrd into the signed UKI. 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`. The stub lives in `rust/stub`.
### Fwupd ### Fwupd

View File

@ -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 = [ systems = [
"x86_64-linux" "x86_64-linux"
@ -81,7 +92,7 @@
craneLib = crane.lib.x86_64-linux.overrideToolchain uefi-rust-stable; craneLib = crane.lib.x86_64-linux.overrideToolchain uefi-rust-stable;
# Build attributes for a Rust application. # Build attributes for a Rust application.
buildRustApp = buildRustApp = lib.makeOverridable (
{ src { src
, target ? null , target ? null
, doCheck ? true , doCheck ? true
@ -126,7 +137,8 @@
}); });
rustfmt = craneLib.cargoFmt (commonArgs // { inherit cargoArtifacts; }); rustfmt = craneLib.cargoFmt (commonArgs // { inherit cargoArtifacts; });
}; }
);
stubCrane = buildRustApp { stubCrane = buildRustApp {
src = craneLib.cleanCargoSource ./rust/stub; src = craneLib.cleanCargoSource ./rust/stub;
@ -134,7 +146,14 @@
doCheck = false; doCheck = false;
}; };
fatStubCrane = stubCrane.override {
extraArgs = {
cargoExtraArgs = "--no-default-features --features fat";
};
};
stub = stubCrane.package; stub = stubCrane.package;
fatStub = fatStubCrane.package;
toolCrane = buildRustApp { toolCrane = buildRustApp {
src = ./rust/tool; src = ./rust/tool;
@ -164,7 +183,7 @@
in in
{ {
packages = { packages = {
inherit stub; inherit stub fatStub;
tool = wrappedTool; tool = wrappedTool;
lzbt = wrappedTool; lzbt = wrappedTool;
}; };
@ -173,14 +192,26 @@
inherit (config.packages) tool; inherit (config.packages) tool;
}; };
checks = { checks =
let
nixosLib = import (pkgs.path + "/nixos/lib") { };
runTest = module: nixosLib.runTest {
imports = [ module ];
hostPkgs = pkgs;
};
in
{
toolClippy = toolCrane.clippy; toolClippy = toolCrane.clippy;
stubClippy = stubCrane.clippy; stubClippy = stubCrane.clippy;
fatStubClippy = fatStubCrane.clippy;
toolFmt = toolCrane.rustfmt; toolFmt = toolCrane.rustfmt;
stubFmt = stubCrane.rustfmt; stubFmt = stubCrane.rustfmt;
} // (import ./nix/tests/lanzaboote.nix { } // (import ./nix/tests/lanzaboote.nix {
inherit pkgs; inherit pkgs;
lanzabooteModule = self.nixosModules.lanzaboote; lanzabooteModule = self.nixosModules.lanzaboote;
}) // (import ./nix/tests/stub.nix {
inherit pkgs runTest;
ukiModule = self.nixosModules.uki;
}); });
pre-commit = { pre-commit = {
@ -193,8 +224,16 @@
}; };
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
shellHook = '' shellHook =
let
systemdUkify = pkgs.systemdMinimal.override {
withEfi = true;
withUkify = true;
};
in
''
${config.pre-commit.installationScript} ${config.pre-commit.installationScript}
export PATH=$PATH:${systemdUkify}/lib/systemd
''; '';
packages = packages =

61
nix/modules/uki.nix Normal file
View File

@ -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";
};
};
}

44
nix/tests/stub.nix Normal file
View File

@ -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"))
'';
};
}

View File

@ -16,10 +16,15 @@ bitflags = "2.3.1"
log = { version = "0.4.18", default-features = false, features = [ "max_level_info", "release_max_level_warn" ]} log = { version = "0.4.18", default-features = false, features = [ "max_level_info", "release_max_level_warn" ]}
# Use software implementation because the UEFI target seems to need it. # 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 for TPM TCG interface version 1.
sha1_smol = "1.0.0" sha1_smol = "1.0.0"
[features]
default = [ "thin" ]
thin = ["dep:sha2"]
fat = []
[profile.release] [profile.release]
opt-level = "s" opt-level = "s"
lto = true lto = true

35
rust/stub/src/common.rs Normal file
View File

@ -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<CString16> {
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<Boot>,
kernel_data: Vec<u8>,
kernel_cmdline: &CStr16,
initrd_data: Vec<u8>,
) -> 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()
}

67
rust/stub/src/fat.rs Normal file
View File

@ -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<Vec<u8>> {
let bytes: Vec<u8> = 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<u8>,
/// The initrd as raw bytes.
initrd: Vec<u8>,
}
impl EmbeddedConfiguration {
fn new(file_data: &[u8]) -> Result<Self> {
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<Boot>) -> 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()
}

View File

@ -4,6 +4,7 @@
extern crate alloc; extern crate alloc;
mod common;
mod efivars; mod efivars;
mod linux_loader; mod linux_loader;
mod measure; mod measure;
@ -13,19 +14,19 @@ mod tpm;
mod uefi_helpers; mod uefi_helpers;
mod unified_sections; 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 efivars::{export_efi_variables, get_loader_features, EfiLoaderFeatures};
use log::{info, warn}; use log::info;
use measure::measure_image; 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 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}; use crate::uefi_helpers::booted_image_file;
type Hash = sha2::digest::Output<Sha256>;
/// Print the startup logo on boot. /// Print the startup logo on boot.
fn print_logo() { 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<CString16> {
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<Hash> {
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<Self> {
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<Boot>,
kernel_data: Vec<u8>,
kernel_cmdline: &CStr16,
initrd_data: Vec<u8>,
) -> 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<Boot>,
kernel_data: Vec<u8>,
kernel_cmdline: &CStr16,
initrd_data: Vec<u8>,
) -> 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::<LoadedImage>(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] #[entry]
fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status { fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
uefi_services::init(&mut system_table).unwrap(); uefi_services::init(&mut system_table).unwrap();
print_logo(); 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()) { if tpm_available(system_table.boot_services()) {
info!("TPM available, will proceed to measurements."); info!("TPM available, will proceed to measurements.");
unsafe { unsafe {
@ -237,46 +73,17 @@ fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
} }
export_efi_variables(&system_table).expect("Failed to export stub EFI variables"); export_efi_variables(&system_table).expect("Failed to export stub EFI variables");
if is_kernel_hash_correct && is_initrd_hash_correct { let status;
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."); #[cfg(feature = "fat")]
{
status = fat::boot_linux(handle, system_table)
}
boot_linux_uefi( #[cfg(feature = "thin")]
handle, {
system_table, status = thin::boot_linux(handle, system_table)
kernel_data,
&config.cmdline,
initrd_data,
)
.status()
} }
status
} }

193
rust/stub/src/thin.rs Normal file
View File

@ -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<Sha256>;
/// 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<Hash> {
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<Self> {
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<Boot>,
kernel_data: Vec<u8>,
kernel_cmdline: &CStr16,
initrd_data: Vec<u8>,
) -> 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::<LoadedImage>(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<Boot>) -> 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()
}
}