use std::path::Path; use std::process::{Command,Stdio}; use libcitadel::{BlockDev, ResourceImage, CommandLine, ImageHeader, Partition, LoopDevice}; use libcitadel::verity::Verity; pub fn setup_rootfs() -> Result<(), Box> { let mut p = choose_boot_partiton(true, CommandLine::revert_rootfs())?; if CommandLine::noverity() { setup_partition_unverified(&p) } else { setup_partition_verified(&mut p) } } pub fn setup_rootfs_resource(rootfs: &ResourceImage) -> Result<(), Box> { if CommandLine::noverity() { setup_resource_unverified(&rootfs) } else { setup_resource_verified(&rootfs) } } fn setup_resource_unverified(img: &ResourceImage) -> Result<(), Box> { if img.is_compressed() { img.decompress(false)?; } let loopdev = LoopDevice::create(img.path(), Some(4096), true)?; info!("Loop device created: {}", loopdev); setup_linear_mapping(loopdev.device()) } fn setup_resource_verified(img: &ResourceImage) -> Result<(), Box> { let _ = img.setup_verity_device()?; Ok(()) } fn setup_partition_unverified(p: &Partition) -> Result<(), Box> { info!("Creating /dev/mapper/rootfs device with linear device mapping of partition (no verity)"); setup_linear_mapping(p.path()) } fn setup_partition_verified(p: &mut Partition) -> Result<(), Box> { info!("Creating /dev/mapper/rootfs dm-verity device"); if !CommandLine::nosignatures() { if !p.has_public_key() { return Result::Err( format!( "no public key available for channel {}", p.metainfo().channel() ) .into(), ); } if !p.is_signature_valid() { p.write_status(ImageHeader::STATUS_BAD_SIG)?; return Result::Err("signature verification failed on partition".into()); } info!("Image signature is valid for channel {}", p.metainfo().channel()); } Verity::setup_partition(p)?; Ok(()) } fn setup_linear_mapping(blockdev: &Path) -> Result<(), Box> { let dev = BlockDev::open_ro(blockdev)?; let table = format!("0 {} linear {} 0", dev.nsectors()?, blockdev.display()); info!("/usr/sbin/dmsetup create rootfs --table '{}'", table); let ok = Command::new("/usr/sbin/dmsetup") .args(&["create", "rootfs", "--table", &table]) .stderr(Stdio::inherit()) .status() .expect("unable to execute /usr/sbin/dmsetup") .success(); if !ok { return Result::Err( "failed to set up linear identity mapping with /usr/sbin/dmsetup".into(), ); } Ok(()) } fn is_revertible_partition(best: &Option, partition: &Partition) -> bool { if !is_bootable(partition) { return false; } match best { Some(p) => p.path() != partition.path(), None => true, } } fn choose_revert_partition(best: Option) -> Option { for p in Partition::rootfs_partitions().unwrap_or(Vec::new()) { if is_revertible_partition(&best, &p) { return Some(p); } } best } fn choose_boot_partiton(scan: bool, revert_rootfs: bool, ) -> Result> { let mut partitions = Partition::rootfs_partitions()?; if scan { for p in &mut partitions { p.boot_scan()?; } } let mut best = None; for p in partitions { best = compare_boot_partitions(best, p); } if revert_rootfs { best = choose_revert_partition(best); } best.ok_or_else(|| format_err!("No partition found to boot from").into()) } fn compare_boot_partitions(a: Option, b: Partition) -> Option { if !is_bootable(&b) { return a; } // b is bootable, so if a is None, then just return b let a = match a { Some(partition) => partition, None => return Some(b), }; // First partition with FLAG_PREFER_BOOT trumps everything if a.is_preferred() { return Some(a); } if b.is_preferred() { return Some(b); } // Compare versions and channels let meta_a = a.metainfo(); let meta_b = b.metainfo(); let ver_a = meta_a.version(); let ver_b = meta_b.version(); // Compare versions only if channels match if a.metainfo().channel() == b.metainfo().channel() { if ver_a > ver_b { return Some(a); } else if ver_b > ver_a { return Some(b); } } // choose NEW over GOOD if versions are the same or // if versions cannot be compared because channels differ if b.is_new() && a.is_good() { return Some(b); } Some(a) } fn is_bootable(p: &Partition) -> bool { if !p.is_initialized() { return false; } // signatures enabled so not bootable without pubkey if signatures_enabled() && !p.has_public_key() { return false; } if p.is_new() || p.is_good() { return true; } // If signatures are disabled then don't disqualify an // image which failed a prior signature verification if !signatures_enabled() && p.is_sig_failed() { return true; } false } fn signatures_enabled() -> bool { !(CommandLine::nosignatures() || CommandLine::noverity()) }