From f260b6f8c66856d21a8f022c582aae9b5b46f573 Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Sun, 30 Apr 2023 19:19:37 +0200 Subject: [PATCH] feat: pack the TPM2 PCR files as CPIOs if they are present --- rust/stub/src/cpio.rs | 2 +- rust/stub/src/initrd.rs | 2 +- rust/stub/src/main.rs | 47 +++++++++++++++++++++++---- rust/stub/src/measure.rs | 15 ++++++--- rust/stub/src/unified_sections.rs | 53 ++++++++++++++++++------------- 5 files changed, 85 insertions(+), 34 deletions(-) diff --git a/rust/stub/src/cpio.rs b/rust/stub/src/cpio.rs index 1fd31231..1da2c0b1 100644 --- a/rust/stub/src/cpio.rs +++ b/rust/stub/src/cpio.rs @@ -286,7 +286,7 @@ pub fn pack_cpio( pub fn pack_cpio_literal( boot_services: &BootServices, - data: &Vec, + data: &[u8], target_dir_prefix: &str, target_filename: &CStr16, dir_mode: u32, diff --git a/rust/stub/src/initrd.rs b/rust/stub/src/initrd.rs index 7bf9e1ef..b1705171 100644 --- a/rust/stub/src/initrd.rs +++ b/rust/stub/src/initrd.rs @@ -11,7 +11,7 @@ pub enum CompanionInitrd { } pub fn export_pcr_efi_variables(runtime_services: &RuntimeServices, - initrds: Vec) -> uefi::Result { + initrds: &Vec) -> uefi::Result { // Do we have kernel parameters that were measured if initrds.iter().any(|e| match e { CompanionInitrd::Credentials(_) => true, diff --git a/rust/stub/src/main.rs b/rust/stub/src/main.rs index 97c36b59..5600433d 100644 --- a/rust/stub/src/main.rs +++ b/rust/stub/src/main.rs @@ -31,6 +31,7 @@ use uefi::{ CStr16, CString16, Result, }; use uefi_helpers::SystemdLoaderFeatures; +use unified_sections::UnifiedSection; use crate::{ linux_loader::InitrdLoader, @@ -257,12 +258,11 @@ fn main(handle: Handle, mut system_table: SystemTable) -> Status { } } - unsafe { - // Iterate over unified sections and measure them - let _ = measure_image(&system_table, booted_image_file( + // Iterate over unified sections and measure them + let (_, discovered_unified_sections) = unsafe { measure_image(&system_table, booted_image_file( system_table.boot_services() - ).unwrap()).expect("Failed to measure the image"); - } + ).unwrap()).expect("Failed to measure the image") + }; export_efi_variables(&system_table) .expect("Failed to export stub EFI variables"); @@ -307,7 +307,42 @@ fn main(handle: Handle, mut system_table: SystemTable) -> Status { } // Let's export any StubPcr EFI variable we might need. - let _ = initrd::export_pcr_efi_variables(&system_table.runtime_services(), initrds); + let _ = initrd::export_pcr_efi_variables(&system_table.runtime_services(), &initrds); + + // Pack relevant left CPIOs: + // - TPM2 PCR signatures + // - TPM2 PCR public keys + for uf_section in discovered_unified_sections { + match uf_section { + UnifiedSection::PcrSig(pcrsig_data) => { + if let Ok(pcrsig_initrd) = cpio::pack_cpio_literal(system_table.boot_services(), + pcrsig_data, + ".extra", + cstr16!("tpm2-pcr-signature.json"), + 0o555, + 0o444, + // Do not perform any measurement. + uefi::proto::tcg::PcrIndex(u32::MAX), + "") { + initrds.push(CompanionInitrd::PcrSignature(pcrsig_initrd)); + } + } + UnifiedSection::PcrPkey(pcrpkey_data) => { + if let Ok(pcrpkey_initrd) = cpio::pack_cpio_literal(system_table.boot_services(), + pcrpkey_data, + ".extra", + cstr16!("tpm2-pcr-public-key.pem"), + 0o555, + 0o444, + // Do not perform any measurement. + uefi::proto::tcg::PcrIndex(u32::MAX), + "") { + initrds.push(CompanionInitrd::PcrPublicKey(pcrpkey_initrd)); + } + } + _ => continue, + } + } if is_kernel_hash_correct && is_initrd_hash_correct { boot_linux_unchecked( diff --git a/rust/stub/src/measure.rs b/rust/stub/src/measure.rs index ce0402c6..217395fa 100644 --- a/rust/stub/src/measure.rs +++ b/rust/stub/src/measure.rs @@ -1,5 +1,5 @@ use uefi::{table::{runtime::VariableAttributes, Boot, SystemTable}, cstr16, proto::tcg::PcrIndex}; - +use alloc::vec::Vec; use crate::{uefi_helpers::{PeInMemory, SD_LOADER}, pe_section::pe_section_data, unified_sections::UnifiedSection, tpm::tpm_log_event_ascii}; /// This is the TPM PCR where lanzastub extends its payload into, before using them. @@ -20,7 +20,7 @@ pub const TPM_PCR_INDEX_VOLUME_KEY: PcrIndex = PcrIndex(15); pub unsafe fn measure_image( system_table: &SystemTable, - image: PeInMemory) -> uefi::Result { + image: PeInMemory) -> uefi::Result<(u32, Vec)> { let runtime_services = system_table.runtime_services(); let boot_services = system_table.boot_services(); @@ -29,9 +29,12 @@ pub unsafe fn measure_image( .map_err(|_err| uefi::Status::LOAD_ERROR)?; let mut measurements = 0; + // We would like to use core::mem::variant_count + // https://github.com/rust-lang/rust/issues/73662 + let mut unified_sections = Vec::with_capacity(8); for section in pe.sections { let section_name = section.name().map_err(|_err| uefi::Status::UNSUPPORTED)?; - if let Ok(unified_section) = UnifiedSection::try_from(section_name) { + if let Ok(unified_section) = UnifiedSection::from_section_table(&pe_binary, §ion) { // UNSTABLE: && in the previous if is an unstable feature // https://github.com/rust-lang/rust/issues/53667 if unified_section.should_be_measured() { @@ -42,6 +45,10 @@ pub unsafe fn measure_image( } } } + // We would like to use push_within_capacity and err out correctly + // if the capacity is exceeded. + // https://github.com/rust-lang/rust/issues/100486 + unified_sections.push(unified_section); } } @@ -56,5 +63,5 @@ pub unsafe fn measure_image( )?; } - Ok(measurements) + Ok((measurements, unified_sections)) } diff --git a/rust/stub/src/unified_sections.rs b/rust/stub/src/unified_sections.rs index 38dd86f8..b56a0320 100644 --- a/rust/stub/src/unified_sections.rs +++ b/rust/stub/src/unified_sections.rs @@ -1,42 +1,51 @@ +use goblin::pe::{section_table::SectionTable, PE}; + +use crate::pe_section::pe_section_data; + /// List of PE sections that have a special meaning with respect to /// UKI specification. /// This is the canonical order in which they are measured into TPM /// PCR 11. /// !!! DO NOT REORDER !!! -pub enum UnifiedSection { +pub enum UnifiedSection<'a> { Linux, OsRel, CmdLine, Initrd, Splash, DTB, - PcrSig, - PcrPkey -} - -impl TryFrom<&str> for UnifiedSection { - type Error = uefi::Error; - fn try_from(value: &str) -> Result { - Ok(match value { - ".linux" => Self::Linux, - ".osrel" => Self::OsRel, - ".cmdline" => Self::CmdLine, - ".initrd" => Self::Initrd, - ".splash" => Self::Splash, - ".dtb" => Self::DTB, - ".pcrsig" => Self::PcrSig, - ".pcrpkey" => Self::PcrPkey, - _ => return Err(uefi::Status::INVALID_PARAMETER.into()) - }) - } + // We only need to store the data for those for now, + // because we need to pack them as CPIOs. + PcrSig(&'a [u8]), + PcrPkey(&'a [u8]) } -impl UnifiedSection { +impl<'a> UnifiedSection<'a> { /// Whether this section should be measured into TPM. pub fn should_be_measured(&self) -> bool { match self { - UnifiedSection::PcrSig => false, + UnifiedSection::PcrSig(_) => false, _ => true } } + + pub fn from_section_table(pe: &'a [u8], section: &SectionTable) -> uefi::Result { + if let Some(data) = pe_section_data(pe, §ion) { + Ok(match section.name().unwrap() { + ".linux" => Self::Linux, + ".osrel" => Self::OsRel, + ".cmdline" => Self::CmdLine, + ".initrd" => Self::Initrd, + ".splash" => Self::Splash, + ".dtb" => Self::DTB, + ".pcrsig" => Self::PcrSig(data), + ".pcrpkey" => Self::PcrPkey(data), + _ => return Err(uefi::Status::INVALID_PARAMETER.into()) + }) + } else { + // No data in the section is equivalent to missing section. + Err(uefi::Status::INVALID_PARAMETER.into()) + } + } + }