lanzaboote/rust/stub/src/main.rs

205 lines
6.3 KiB
Rust
Raw Normal View History

2022-11-21 05:44:04 -05:00
#![no_main]
#![no_std]
#![feature(abi_efiapi)]
2022-11-22 18:25:52 -05:00
#![feature(negative_impls)]
2022-11-21 05:44:04 -05:00
extern crate alloc;
2022-11-21 10:22:44 -05:00
2022-11-22 18:25:52 -05:00
mod linux_loader;
2022-11-22 10:18:12 -05:00
mod pe_section;
2022-11-22 10:24:09 -05:00
mod uefi_helpers;
2022-11-22 10:18:12 -05:00
use pe_section::{pe_section, pe_section_as_string};
use sha2::{Digest, Sha256};
use uefi::{
prelude::*,
proto::{
2022-11-24 06:18:23 -05:00
console::text::Output,
2022-11-22 10:18:12 -05:00
loaded_image::LoadedImage,
media::file::{File, FileAttribute, FileMode, RegularFile},
},
CString16, Result,
};
2022-11-21 10:22:44 -05:00
use crate::{
linux_loader::InitrdLoader,
uefi_helpers::{booted_image_cmdline, booted_image_file, read_all},
};
2022-11-22 10:18:12 -05:00
type Hash = sha2::digest::Output<Sha256>;
/// Print the startup logo on boot.
fn print_logo(output: &mut Output) -> Result<()> {
output.clear()?;
2022-11-21 10:22:44 -05:00
output.output_string(cstr16!(
"
_ _ _\r
| | | | | |\r
| | __ _ _ __ ______ _| |__ ___ ___ | |_ ___\r
| |/ _` | '_ \\|_ / _` | '_ \\ / _ \\ / _ \\| __/ _ \\\r
| | (_| | | | |/ / (_| | |_) | (_) | (_) | || __/\r
|_|\\__,_|_| |_/___\\__,_|_.__/ \\___/ \\___/ \\__\\___|\r
2022-11-22 10:18:12 -05:00
\r
2022-11-21 10:22:44 -05:00
"
))
2022-11-21 10:22:44 -05:00
}
2022-11-21 05:44:04 -05:00
/// The configuration that is embedded at build time.
///
/// After lanzaboote is built, lanzatool 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,
}
/// Extract a filename from a PE section. The filename is stored as UTF-8.
fn extract_filename(file_data: &[u8], section: &str) -> Result<CString16> {
let filename = pe_section_as_string(file_data, section).ok_or(Status::INVALID_PARAMETER)?;
Ok(CString16::try_from(filename.as_str()).map_err(|_| Status::INVALID_PARAMETER)?)
}
/// Extract a Blake3 hash from a PE section.
fn extract_hash(file_data: &[u8], section: &str) -> Result<Hash> {
let array: [u8; 32] = pe_section(file_data, section)
.ok_or(Status::INVALID_PARAMETER)?
.try_into()
.map_err(|_| Status::INVALID_PARAMETER)?;
Ok(array.into())
}
2022-11-23 11:57:43 -05:00
impl EmbeddedConfiguration {
fn new(file: &mut RegularFile) -> Result<Self> {
file.set_position(0)?;
let file_data = read_all(file)?;
Ok(Self {
kernel_filename: extract_filename(&file_data, ".kernelp")?,
kernel_hash: extract_hash(&file_data, ".kernelh")?,
initrd_filename: extract_filename(&file_data, ".initrdp")?,
initrd_hash: extract_hash(&file_data, ".initrdh")?,
})
}
}
2022-11-21 05:44:04 -05:00
#[entry]
fn main(handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
2022-11-21 05:44:04 -05:00
uefi_services::init(&mut system_table).unwrap();
print_logo(system_table.stdout()).unwrap();
2022-11-21 09:36:39 -05:00
2022-11-23 11:57:43 -05:00
let config: EmbeddedConfiguration =
EmbeddedConfiguration::new(&mut booted_image_file(system_table.boot_services()).unwrap())
2022-11-25 20:47:21 -05:00
.expect("Failed to extract configuration from binary. Did you run lanzatool?");
let kernel_data;
let initrd_data;
{
let mut file_system = system_table
.boot_services()
.get_image_file_system(handle)
2022-11-25 20:47:21 -05:00
.expect("Failed to get file system handle");
let mut root = file_system
.open_volume()
.expect("Failed to find ESP root directory");
let mut kernel_file = root
.open(
&config.kernel_filename,
FileMode::Read,
FileAttribute::empty(),
)
2022-11-25 20:47:21 -05:00
.expect("Failed to open kernel file for reading")
.into_regular_file()
2022-11-25 20:47:21 -05:00
.expect("Kernel is not a regular file");
kernel_data = read_all(&mut kernel_file).expect("Failed to read kernel file into memory");
let mut initrd_file = root
.open(
&config.initrd_filename,
FileMode::Read,
FileAttribute::empty(),
)
.expect("Failed to open initrd for reading")
.into_regular_file()
.expect("Initrd is not a regular file");
initrd_data = read_all(&mut initrd_file).expect("Failed to read kernel file into memory");
}
if Sha256::digest(&kernel_data) != config.kernel_hash {
system_table
.stdout()
.output_string(cstr16!("Hash mismatch for kernel. Refusing to load!\r\n"))
.unwrap();
return Status::SECURITY_VIOLATION;
}
if Sha256::digest(&initrd_data) != config.initrd_hash {
system_table
.stdout()
.output_string(cstr16!("Hash mismatch for initrd. Refusing to load!\r\n"))
.unwrap();
return Status::SECURITY_VIOLATION;
}
2022-11-25 20:47:21 -05:00
let kernel_cmdline =
booted_image_cmdline(system_table.boot_services()).expect("Failed to fetch command line");
2022-11-21 19:39:05 -05:00
2022-11-25 20:47:21 -05:00
let kernel_handle = {
system_table
.boot_services()
.load_image(
handle,
uefi::table::boot::LoadImageSource::FromBuffer {
buffer: &kernel_data,
file_path: None,
},
)
.expect("UEFI refused to load the kernel image. It may not be signed or it may not have an EFI stub.")
};
2022-11-21 19:39:05 -05:00
let mut kernel_image = system_table
.boot_services()
.open_protocol_exclusive::<LoadedImage>(kernel_handle)
2022-11-25 20:47:21 -05:00
.expect("Failed to open the LoadedImage protocol");
unsafe {
kernel_image.set_load_options(
kernel_cmdline.as_ptr() as *const u8,
u32::try_from(kernel_cmdline.len()).unwrap(),
);
}
let mut initrd_loader = InitrdLoader::new(system_table.boot_services(), handle, initrd_data)
2022-11-25 20:47:21 -05:00
.expect("Failed to load the initrd. It may not be there or it is not signed");
let status = system_table
.boot_services()
.start_image(kernel_handle)
.status();
initrd_loader
2022-11-23 06:13:45 -05:00
.uninstall(system_table.boot_services())
2022-11-25 20:47:21 -05:00
.expect("Failed to uninstall the initrd protocols");
2022-11-22 18:25:52 -05:00
status
2022-11-21 05:44:04 -05:00
}