lanzaboote/rust/tool/tests/systemd_boot.rs

156 lines
5.2 KiB
Rust

use sha2::{Digest, Sha256};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::{fs, os::unix::prelude::MetadataExt};
use anyhow::Result;
use tempfile::tempdir;
mod common;
#[test]
fn keep_systemd_boot_binaries() -> Result<()> {
let esp = tempdir()?;
let tmpdir = tempdir()?;
let profiles = tempdir()?;
let generation_link = common::setup_generation_link(tmpdir.path(), profiles.path(), 1)
.expect("Failed to setup generation link");
let systemd_boot_path = systemd_boot_path(&esp);
let systemd_boot_fallback_path = systemd_boot_fallback_path(&esp);
let output0 = common::lanzaboote_install(0, esp.path(), vec![&generation_link])?;
assert!(output0.status.success());
// Use the modification time instead of a hash because the hash would be the same even if the
// file was overwritten.
let systemd_boot_mtime0 = mtime(&systemd_boot_path);
let systemd_boot_fallback_mtime0 = mtime(&systemd_boot_fallback_path);
let output1 = common::lanzaboote_install(0, esp.path(), vec![generation_link])?;
assert!(output1.status.success());
let systemd_boot_mtime1 = mtime(&systemd_boot_path);
let systemd_boot_fallback_mtime1 = mtime(&systemd_boot_fallback_path);
assert_eq!(
systemd_boot_mtime0, systemd_boot_mtime1,
"systemd-boot binary was modified on second install."
);
assert_eq!(
systemd_boot_fallback_mtime0, systemd_boot_fallback_mtime1,
"systemd-boot fallback binary was moidified on second install."
);
Ok(())
}
#[test]
fn overwrite_malformed_systemd_boot_binaries() -> Result<()> {
let esp = tempdir()?;
let tmpdir = tempdir()?;
let profiles = tempdir()?;
let generation_link = common::setup_generation_link(tmpdir.path(), profiles.path(), 1)
.expect("Failed to setup generation link");
let systemd_boot_path = systemd_boot_path(&esp);
let systemd_boot_fallback_path = systemd_boot_fallback_path(&esp);
let output0 = common::lanzaboote_install(0, esp.path(), vec![&generation_link])?;
assert!(output0.status.success());
// Make systemd-boot binaries malformed by truncating them.
fs::File::create(&systemd_boot_path)?;
fs::File::create(&systemd_boot_fallback_path)?;
let malformed_systemd_boot_hash = hash_file(&systemd_boot_path);
let malformed_systemd_boot_fallback_hash = hash_file(&systemd_boot_fallback_path);
let output1 = common::lanzaboote_install(0, esp.path(), vec![generation_link])?;
assert!(output1.status.success());
let systemd_boot_hash = hash_file(&systemd_boot_path);
let systemd_boot_fallback_hash = hash_file(&systemd_boot_fallback_path);
assert_ne!(
malformed_systemd_boot_hash, systemd_boot_hash,
"Malformed systemd-boot binaries were not replaced."
);
assert_ne!(
malformed_systemd_boot_fallback_hash, systemd_boot_fallback_hash,
"Maformed systemd-boot fallback binaries were not replaced."
);
Ok(())
}
#[test]
fn overwrite_unsigned_systemd_boot_binaries() -> Result<()> {
let esp = tempdir()?;
let tmpdir = tempdir()?;
let profiles = tempdir()?;
let generation_link = common::setup_generation_link(tmpdir.path(), profiles.path(), 1)
.expect("Failed to setup generation link");
let systemd_boot_path = systemd_boot_path(&esp);
let systemd_boot_fallback_path = systemd_boot_fallback_path(&esp);
let output0 = common::lanzaboote_install(0, esp.path(), vec![&generation_link])?;
assert!(output0.status.success());
remove_signature(&systemd_boot_path)?;
remove_signature(&systemd_boot_fallback_path)?;
assert!(!verify_signature(&systemd_boot_path)?);
assert!(!verify_signature(&systemd_boot_fallback_path)?);
let output1 = common::lanzaboote_install(0, esp.path(), vec![generation_link])?;
assert!(output1.status.success());
assert!(verify_signature(&systemd_boot_path)?);
assert!(verify_signature(&systemd_boot_fallback_path)?);
Ok(())
}
fn systemd_boot_path(esp: &tempfile::TempDir) -> PathBuf {
esp.path().join("EFI/systemd/systemd-bootx64.efi")
}
fn systemd_boot_fallback_path(esp: &tempfile::TempDir) -> PathBuf {
esp.path().join("EFI/BOOT/BOOTX64.EFI")
}
/// Look up the modification time (mtime) of a file.
fn mtime(path: &Path) -> i64 {
fs::metadata(path)
.expect("Failed to read modification time.")
.mtime()
}
fn hash_file(path: &Path) -> sha2::digest::Output<Sha256> {
Sha256::digest(fs::read(path).expect("Failed to read file to hash."))
}
/// Remove signature from a signed PE file.
pub fn remove_signature(path: &Path) -> Result<()> {
let output = Command::new("sbattach")
.arg("--remove")
.arg(path.as_os_str())
.output()?;
print!("{}", String::from_utf8(output.stdout)?);
print!("{}", String::from_utf8(output.stderr)?);
Ok(())
}
/// Verify signature of PE file.
pub fn verify_signature(path: &Path) -> Result<bool> {
let output = Command::new("sbverify")
.arg(path.as_os_str())
.arg("--cert")
.arg("tests/fixtures/uefi-keys/db.pem")
.output()?;
print!("{}", String::from_utf8(output.stdout)?);
print!("{}", String::from_utf8(output.stderr)?);
Ok(output.status.success())
}