diff --git a/Cargo.lock b/Cargo.lock index 2d07b9b..352e642 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -87,7 +96,7 @@ dependencies = [ "const-oid", "der", "digest", - "object", + "object 0.36.2", "rsa", "sha1", "sha2", @@ -101,6 +110,21 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object 0.32.2", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.1" @@ -113,6 +137,21 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "2.6.0" @@ -140,11 +179,15 @@ version = "0.1.0" dependencies = [ "authenticode", "clap", + "color-eyre", "digest", "fallible-iterator", + "fancy-regex", "hex", "libc", - "object", + "object 0.36.2", + "serde", + "serde_json", "sha1", "sha2", "thiserror", @@ -217,6 +260,33 @@ dependencies = [ "x509-cert", ] +[[package]] +name = "color-eyre" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + [[package]] name = "colorchoice" version = "1.0.2" @@ -305,12 +375,33 @@ dependencies = [ "termcolor", ] +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "fallible-iterator" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +[[package]] +name = "fancy-regex" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + [[package]] name = "flagset" version = "0.4.6" @@ -352,6 +443,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + [[package]] name = "hashbrown" version = "0.12.3" @@ -378,6 +475,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "humantime" @@ -388,6 +488,12 @@ dependencies = [ "quick-error", ] +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "indexmap" version = "1.9.3" @@ -404,6 +510,12 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "lazy_static" version = "1.5.0" @@ -510,6 +622,15 @@ dependencies = [ "libm", ] +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + [[package]] name = "object" version = "0.36.2" @@ -576,6 +697,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -585,6 +712,12 @@ dependencies = [ "base64ct", ] +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + [[package]] name = "pkcs1" version = "0.7.5" @@ -730,6 +863,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + [[package]] name = "ruzstd" version = "0.7.0" @@ -766,6 +905,18 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "serde_json" +version = "1.0.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "serde_yaml" version = "0.8.26" @@ -800,6 +951,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "signature" version = "2.2.0" @@ -901,6 +1061,16 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "tpmless-tpm2" version = "0.4.0" @@ -915,6 +1085,47 @@ dependencies = [ "thiserror", ] +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + [[package]] name = "twox-hash" version = "1.6.3" @@ -985,6 +1196,12 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index 0b0d91d..50446b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,11 +6,15 @@ edition = "2021" [dependencies] authenticode = { version = "0.4.3", features = ["object"] } clap = { version = "4.5.11", features = ["derive"] } +color-eyre = "0.6.3" digest = "0.10.7" fallible-iterator = "0.2.0" -hex = "0.4.3" +fancy-regex = "0.13.0" +hex = { version = "0.4.3", features = ["serde"] } libc = "0.2.155" object = "0.36.2" +serde = { version = "1.0.204", features = ["derive"] } +serde_json = "1.0.121" sha1 = "0.10.6" sha2 = "0.10.8" thiserror = "1.0.63" diff --git a/src/drop_ins.rs b/src/drop_ins.rs new file mode 100644 index 0000000..de1feff --- /dev/null +++ b/src/drop_ins.rs @@ -0,0 +1,86 @@ +use std::path::{Path, PathBuf}; + +use fancy_regex::Regex; +use serde::{de::Visitor, Deserialize}; + +#[derive(Deserialize)] +#[serde(tag = "type", rename_all = "kebab-case")] +enum Matcher { + RecordedHash { + #[serde(deserialize_with = "hex::serde::deserialize")] + hash: Vec, + }, + PathRegex { + #[serde(deserialize_with = "deserialize_regex")] + regex: Regex, + }, + Path { + path: PathBuf, + }, +} + +#[derive(Deserialize)] +pub struct DropIn { + matcher: Matcher, + path: PathBuf, +} + +#[derive(Deserialize)] +pub struct DropIns(Vec); + +pub trait FindDropIn { + fn find_drop_in(&self, path: &Path, hash: &[u8]) -> Option; +} + +impl FindDropIn for Option { + fn find_drop_in(&self, path: &Path, hash: &[u8]) -> Option { + match self { + Some(v) => v.find_drop_in(path, hash), + None => None, + } + } +} + +impl FindDropIn for DropIns { + fn find_drop_in(&self, path: &Path, hash: &[u8]) -> Option { + for drop_in in &self.0 { + if match &drop_in.matcher { + Matcher::RecordedHash { hash: matcher_hash } => matcher_hash == hash, + Matcher::PathRegex { + regex: matcher_regex, + } => path + .to_str() + .is_some_and(|path| matcher_regex.is_match(path).unwrap_or(false)), + Matcher::Path { path: matcher_path } => path == matcher_path, + } { + return Some(drop_in.path.clone()); + } + } + + None + } +} + +fn deserialize_regex<'de, D>(d: D) -> Result +where + D: serde::Deserializer<'de>, +{ + struct RegexVisitor; + impl<'v> Visitor<'v> for RegexVisitor { + type Value = Regex; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a regex string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + let regex = Regex::new(v); + regex.map_err(|err| serde::de::Error::custom(err)) + } + } + + d.deserialize_string(RegexVisitor) +} diff --git a/src/main.rs b/src/main.rs index 19ce55e..dd7b53c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,23 +2,27 @@ mod args; mod device_path; +mod drop_ins; mod find_mount_point; mod hash; mod util; use std::fs::OpenOptions; -use std::io; +use std::io::{self, BufReader}; use std::path::PathBuf; -use args::Args; use clap::Parser; -use device_path::traverse_device_path; +use color_eyre::eyre::{self, Context}; +use drop_ins::FindDropIn; use fallible_iterator::FallibleIterator; use thiserror::Error; use typed_path::{Utf8UnixEncoding, Utf8WindowsPathBuf}; use uefi_eventlog::parsed::ParsedEventData; use uefi_eventlog::EventType; +use crate::args::Args; +use crate::device_path::traverse_device_path; +use crate::drop_ins::DropIns; use crate::hash::*; #[derive(Error, Debug)] @@ -37,14 +41,32 @@ impl From for Error { } } -fn main() { +fn main() -> eyre::Result<()> { + color_eyre::install()?; + let args = Args::parse(); let hash_len = Hasher::from(args.algo).output_size(); - let mut file = OpenOptions::new().read(true).open(args.event_log).unwrap(); + let drop_ins: Option = match args.drop_ins { + Some(drop_ins_file) => Some( + serde_json::from_reader(BufReader::new( + OpenOptions::new() + .read(true) + .open(drop_ins_file) + .context("couldn't open drop-ins file")?, + )) + .context("couldn't parse drop-ins JSON")?, + ), + None => None, + }; - let fucking_settings = uefi_eventlog::ParseSettings::new(); - let mut events = uefi_eventlog::Parser::new(&mut file, &fucking_settings); + let mut file = OpenOptions::new() + .read(true) + .open(args.event_log) + .context("couldn't open event log")?; + + let settings = uefi_eventlog::ParseSettings::new(); + let mut events = uefi_eventlog::Parser::new(&mut file, &settings); let size = Hasher::from(args.algo).output_size(); @@ -70,10 +92,32 @@ fn main() { continue; } + let digest; + if let Some(event_digest) = event.digests.first() { + digest = event_digest.digest(); + } else { + eprintln!("ignoring event with no digest"); + continue; + } + 'measure_file: { match event.event { EventType::EFIBootServicesApplication => { - let event_data = event.parsed_data.unwrap().unwrap(); + let event_data = match event.parsed_data { + Some(event_data) => event_data, + None => { + eprintln!("no event data"); + break 'measure_file; + } + }; + let event_data = match event_data { + Ok(event_data) => event_data, + Err(err) => { + eprintln!("error while parsing event data: {err}"); + break 'measure_file; + } + }; + if let ParsedEventData::ImageLoadEvent { device_path, .. } = event_data && let Some(device_path) = device_path { @@ -127,9 +171,15 @@ fn main() { esp = PathBuf::from("/boot/efi"); } - let full_path = esp.join(unix_path.strip_prefix("/").unwrap_or(&unix_path)); + let mut full_path = + esp.join(unix_path.strip_prefix("/").unwrap_or(&unix_path)); eprintln!("measuring file {full_path:?}"); + if let Some(drop_in_path) = drop_ins.find_drop_in(&full_path, &digest) { + eprintln!("found drop in for file: {drop_in_path:?}"); + full_path = drop_in_path; + }; + let mut hasher = Hasher::from(args.algo); let hash = match hash_by_path(&full_path, &mut hasher, args.bits) { Ok(_) => { @@ -153,16 +203,15 @@ fn main() { } } - if let Some(digest) = event.digests.first() { - let digest = digest.digest(); - let encoded = hex::encode(digest.as_slice()); - eprintln!("applying digest: {encoded:0>len$}", len = hash_len * 2); - state.measure(&digest, args.algo.into()); - } + let encoded = hex::encode(digest.as_slice()); + eprintln!("applying digest: {encoded:0>len$}", len = hash_len * 2); + state.measure(&digest, args.algo.into()); } let hash = state.as_slice(); let hex = hex::encode(hash); println!("{hex:0>len$}", len = hash_len * 2); + + Ok(()) }