lanzatool, lanzaboote: don't wrap initrd as PE

... because we check its integrity using the embedded blake3 hash. So
there is no need for the LoadImage hack anymore.
This commit is contained in:
Julian Stecklina 2022-11-28 02:39:36 +01:00
parent 1739ffde26
commit 401c3b8c1c
4 changed files with 7 additions and 80 deletions

View File

@ -5,7 +5,7 @@
//! because we read the initrd multiple times. The code needs to be //! because we read the initrd multiple times. The code needs to be
//! restructured to solve this. //! restructured to solve this.
use core::{ffi::c_void, ops::Range, pin::Pin, ptr::slice_from_raw_parts_mut}; use core::{ffi::c_void, pin::Pin, ptr::slice_from_raw_parts_mut};
use alloc::{boxed::Box, vec::Vec}; use alloc::{boxed::Box, vec::Vec};
use uefi::{ use uefi::{
@ -14,7 +14,6 @@ use uefi::{
device_path::{DevicePath, FfiDevicePath}, device_path::{DevicePath, FfiDevicePath},
Protocol, Protocol,
}, },
table::boot::LoadImageSource,
unsafe_guid, Handle, Identify, Result, ResultExt, Status, unsafe_guid, Handle, Identify, Result, ResultExt, Status,
}; };
@ -115,70 +114,12 @@ pub struct InitrdLoader {
registered: bool, registered: bool,
} }
/// Returns the data range of the initrd in the PE binary.
///
/// The initrd has to be embedded in the file as a .initrd PE section.
fn initrd_location(initrd_efi: &[u8]) -> Result<Range<usize>> {
let pe_binary = goblin::pe::PE::parse(initrd_efi).map_err(|_| Status::INVALID_PARAMETER)?;
pe_binary
.sections
.iter()
.find(|s| s.name().unwrap() == ".initrd")
.map(|s| {
let section_start: usize = s.pointer_to_raw_data.try_into().unwrap();
let section_size: usize = s.size_of_raw_data.try_into().unwrap();
Range {
start: section_start,
end: section_start + section_size,
}
})
.ok_or_else(|| Status::END_OF_FILE.into())
}
/// Check the signature of the initrd.
///
/// For this to work, the initrd needs to be a PE binary. We misuse
/// [`BootServices::load_image`] for this.
fn initrd_verify(boot_services: &BootServices, initrd_efi: &[u8]) -> Result<()> {
let initrd_handle = boot_services.load_image(
boot_services.image_handle(),
LoadImageSource::FromBuffer {
buffer: &initrd_efi,
file_path: None,
},
)?;
// If we get here, the security policy allowed loading the
// image. This means that it was signed with an acceptable key in
// the Secure Boot scenario.
boot_services.unload_image(initrd_handle)?;
Ok(())
}
impl InitrdLoader { impl InitrdLoader {
/// Create a new [`InitrdLoader`]. /// Create a new [`InitrdLoader`].
/// ///
/// `handle` is the handle where the protocols are registered /// `handle` is the handle where the protocols are registered
/// on. `file` is the file that is served to Linux. /// on. `file` is the file that is served to Linux.
pub fn new( pub fn new(boot_services: &BootServices, handle: Handle, initrd_data: Vec<u8>) -> Result<Self> {
boot_services: &BootServices,
handle: Handle,
mut initrd_data: Vec<u8>,
) -> Result<Self> {
initrd_verify(boot_services, &initrd_data)?;
let range = initrd_location(&initrd_data)?;
// Remove the PE wrapper from the initrd. We do this in place
// to avoid having to keep the initrd in memory twice.
initrd_data.drain(0..range.start);
initrd_data.resize(range.end - range.start, 0);
initrd_data.shrink_to_fit();
let mut proto = Box::pin(LoadFile2Protocol { let mut proto = Box::pin(LoadFile2Protocol {
load_file: raw_load_file, load_file: raw_load_file,
initrd_data, initrd_data,

View File

@ -59,14 +59,11 @@ impl Commands {
fn install(args: InstallCommand) -> Result<()> { fn install(args: InstallCommand) -> Result<()> {
let lanzaboote_stub = let lanzaboote_stub =
std::env::var("LANZABOOTE_STUB").context("Failed to read LANZABOOTE_STUB env variable")?; std::env::var("LANZABOOTE_STUB").context("Failed to read LANZABOOTE_STUB env variable")?;
let initrd_stub = std::env::var("LANZABOOTE_INITRD_STUB")
.context("Failed to read LANZABOOTE_INITRD_STUB env variable")?;
let key_pair = KeyPair::new(&args.public_key, &args.private_key); let key_pair = KeyPair::new(&args.public_key, &args.private_key);
install::Installer::new( install::Installer::new(
PathBuf::from(lanzaboote_stub), PathBuf::from(lanzaboote_stub),
PathBuf::from(initrd_stub),
key_pair, key_pair,
args.pki_bundle, args.pki_bundle,
args.auto_enroll, args.auto_enroll,

View File

@ -14,7 +14,6 @@ use crate::signature::KeyPair;
pub struct Installer { pub struct Installer {
lanzaboote_stub: PathBuf, lanzaboote_stub: PathBuf,
initrd_stub: PathBuf,
key_pair: KeyPair, key_pair: KeyPair,
_pki_bundle: Option<PathBuf>, _pki_bundle: Option<PathBuf>,
_auto_enroll: bool, _auto_enroll: bool,
@ -25,7 +24,6 @@ pub struct Installer {
impl Installer { impl Installer {
pub fn new( pub fn new(
lanzaboote_stub: PathBuf, lanzaboote_stub: PathBuf,
initrd_stub: PathBuf,
key_pair: KeyPair, key_pair: KeyPair,
_pki_bundle: Option<PathBuf>, _pki_bundle: Option<PathBuf>,
_auto_enroll: bool, _auto_enroll: bool,
@ -34,7 +32,6 @@ impl Installer {
) -> Self { ) -> Self {
Self { Self {
lanzaboote_stub, lanzaboote_stub,
initrd_stub,
key_pair, key_pair,
_pki_bundle, _pki_bundle,
_auto_enroll, _auto_enroll,
@ -94,8 +91,6 @@ impl Installer {
if let Some(initrd_secrets_script) = &bootspec.initrd_secrets { if let Some(initrd_secrets_script) = &bootspec.initrd_secrets {
append_initrd_secrets(initrd_secrets_script, &initrd_location)?; append_initrd_secrets(initrd_secrets_script, &initrd_location)?;
} }
let wrapped_initrd = pe::wrap_initrd(&secure_temp_dir, &self.initrd_stub, &initrd_location)
.context("Failed to assemble stub")?;
println!("Sign and copy files to EFI system partition..."); println!("Sign and copy files to EFI system partition...");
@ -107,11 +102,15 @@ impl Installer {
(&systemd_boot, &esp_paths.efi_fallback), (&systemd_boot, &esp_paths.efi_fallback),
(&systemd_boot, &esp_paths.systemd_boot), (&systemd_boot, &esp_paths.systemd_boot),
(&bootspec.kernel, &esp_paths.kernel), (&bootspec.kernel, &esp_paths.kernel),
(&wrapped_initrd, &esp_paths.initrd),
] ]
.into_iter() .into_iter()
.try_for_each(|(from, to)| install_signed(&self.key_pair, from, to))?; .try_for_each(|(from, to)| install_signed(&self.key_pair, from, to))?;
// The initrd doesn't need to be signed. Lanzaboote has its
// hash embedded and will refuse loading it when the has
// mismatches.
copy(&initrd_location, &esp_paths.initrd).context("Failed to copy initrd to ESP")?;
let lanzaboote_image = pe::lanzaboote_image( let lanzaboote_image = pe::lanzaboote_image(
&secure_temp_dir, &secure_temp_dir,
&self.lanzaboote_stub, &self.lanzaboote_stub,

View File

@ -76,16 +76,6 @@ fn file_hash(file: &Path) -> Result<blake3::Hash> {
Ok(blake3::hash(&fs::read(file)?)) Ok(blake3::hash(&fs::read(file)?))
} }
/// Wrap an initrd into a PE binary.
///
/// This is required for lanzaboote to verify the signature of the
/// initrd.
pub fn wrap_initrd(target_dir: &TempDir, initrd_stub: &Path, initrd: &Path) -> Result<PathBuf> {
let initrd_offs = stub_offset(initrd_stub)?;
let sections = vec![s(".initrd", initrd, initrd_offs)];
wrap_in_pe(target_dir, "wrapped-initrd.exe", initrd_stub, sections)
}
/// Take a PE binary stub and attach sections to it. /// Take a PE binary stub and attach sections to it.
/// ///
/// The result is then written to a new file. Returns the filename of /// The result is then written to a new file. Returns the filename of