diff --git a/Cargo.lock b/Cargo.lock index a845e2c42..222a29b8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,6 +155,7 @@ dependencies = [ "confy", "data-encoding", "dbus", + "directories", "lmdb-rkv", "nom", "ring", diff --git a/Cargo.toml b/Cargo.toml index 6e389fdc6..e634610cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,4 @@ dbus = "0.9.1" lmdb-rkv = "0.14.0" confy = "0.4.0" serde = { version = "1.0", features = ["derive"] } +directories = "2.0.2" diff --git a/python/Cargo.lock b/python/Cargo.lock index da64e60b9..6ba80a05f 100644 --- a/python/Cargo.lock +++ b/python/Cargo.lock @@ -165,6 +165,7 @@ dependencies = [ "confy", "data-encoding", "dbus", + "directories", "lmdb-rkv", "nom", "ring", diff --git a/python/examples/changeset.py b/python/examples/changeset.py index 8ab00c835..ceade9e8f 100644 --- a/python/examples/changeset.py +++ b/python/examples/changeset.py @@ -1,16 +1,42 @@ -from fapolicy_analyzer import * import pathlib +import itertools as it +from fapolicy_analyzer import * # config is loaded from $HOME/.config/fapolicy-analyzer/fapolicy-analyzer.toml + +# a system represents the state of the host sytems s1 = System() print(f"system1 has {len(s1.ancillary_trust())} trust entries") -xs = Changeset() -for p in pathlib.Path("/bin").iterdir(): - xs.add_trust(str(p)) -print(f"adding {xs.len()} trust entries") +# changeset represents changes to trust +xs1 = Changeset() +for p in it.islice(pathlib.Path("/bin").iterdir(), 5): + xs1.add_trust(str(p)) +print(f"adding {xs1.len()} trust entries") + +# changesets are inexpensive +xs2 = Changeset() +for p in it.islice(pathlib.Path("/bin").iterdir(), 5, 10): + xs2.add_trust(str(p)) +print(f"adding {xs2.len()} trust entries") -s2 = s1.apply_changeset(xs) +# changesets get applied to a system, producing a new system +s2 = s1.apply_changeset(xs1) print(f"system2 has {len(s2.ancillary_trust())} trust entries") -for t in s2.ancillary_trust(): - print(f"{t.status} {t.path} {t.size} {t.hash}") + +# the new system can have changes applied to it +s3 = s2.apply_changeset(xs2) +print(f"system3 has {len(s3.ancillary_trust())} trust entries") + +# a system is deployed, updating the fapolicyd ancillary trust +# s2.deploy() + +# the output is +# ================================== +# system1 has 0 trust entries +# adding 5 trust entries +# adding 5 trust entries +# applying changeset to current state... +# system2 has 5 trust entries +# applying changeset to current state... +# system3 has 10 trust entries diff --git a/python/examples/show_ancillary.py b/python/examples/show_ancillary.py new file mode 100644 index 000000000..8c030d962 --- /dev/null +++ b/python/examples/show_ancillary.py @@ -0,0 +1,8 @@ +from fapolicy_analyzer import * + +# config is loaded from $HOME/.config/fapolicy-analyzer/fapolicy-analyzer.toml +s1 = System() +for t in s1.ancillary_trust(): + print(f"{t.path} {t.size} {t.hash}") + +print(f"found {len(s1.ancillary_trust())} ancillary trust entries") diff --git a/python/src/app.rs b/python/src/app.rs index a6ccaf11e..1d8ffb931 100644 --- a/python/src/app.rs +++ b/python/src/app.rs @@ -29,8 +29,8 @@ impl From for State { impl PySystem { #[new] fn new() -> PySystem { - let conf = cfg::load(); - State::new(&conf.system).into() + let conf = cfg::All::default(); + State::new(&conf).into() } fn system_trust(&self) -> Vec { @@ -60,7 +60,7 @@ impl PySystem { } fn deploy(&self) { - deploy_app_state(&self.state); + deploy_app_state(&self.state).expect("deployment failed") } fn is_stale(&self) -> bool { diff --git a/src/app.rs b/src/app.rs index 77675da21..3c9764b20 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,8 +1,10 @@ +use directories::ProjectDirs; use serde::Deserialize; use serde::Serialize; use crate::api::{Trust, TrustSource}; -use crate::sys::Config; +use crate::cfg::All; +use crate::cfg::PROJECT_NAME; use crate::trust::load_trust_db; use crate::trust::Changeset; @@ -10,15 +12,15 @@ use crate::trust::Changeset; /// Carries along the configuration that provided the state. #[derive(Clone, Serialize, Deserialize)] pub struct State { - pub config: Config, + pub config: All, pub trust_db: Vec, } impl State { - pub fn new(cfg: &Config) -> State { + pub fn new(cfg: &All) -> State { State { config: cfg.clone(), - trust_db: load_trust_db(&cfg.trust_db_path), + trust_db: load_trust_db(&cfg.system.trust_db_path), } } @@ -43,3 +45,21 @@ impl State { } } } + +#[derive(Clone, Serialize, Deserialize)] +pub struct Config { + pub data_dir: String, +} + +impl Default for Config { + fn default() -> Self { + let proj_dirs = + ProjectDirs::from("rs", "", PROJECT_NAME).expect("failed to init project dirs"); + + let dd = proj_dirs.data_dir(); + + Self { + data_dir: dd.to_path_buf().into_os_string().into_string().unwrap(), + } + } +} diff --git a/src/cfg.rs b/src/cfg.rs index fc4db607b..3d89faf03 100644 --- a/src/cfg.rs +++ b/src/cfg.rs @@ -1,14 +1,20 @@ use serde::Deserialize; use serde::Serialize; +use crate::app; use crate::sys; +pub const PROJECT_NAME: &str = "fapolicy-analyzer"; + /// All configuration loaded from the user toml under XDG config dir #[derive(Clone, Serialize, Deserialize, Default)] pub struct All { pub system: sys::Config, + pub application: app::Config, } -pub fn load() -> All { - confy::load("fapolicy-analyzer").expect("unable to load user configuration") +impl All { + pub fn data_dir(&self) -> &str { + self.application.data_dir.as_str() + } } diff --git a/src/fapolicyd.rs b/src/fapolicyd.rs index 5354a6b5d..92b5b9fa5 100644 --- a/src/fapolicyd.rs +++ b/src/fapolicyd.rs @@ -18,9 +18,9 @@ pub fn reload_databases() { .write(true) .read(false) .open(FIFO_PIPE) - .unwrap() + .expect("unable to open fifo pipe") .write_all("1".as_bytes()) - .unwrap(); + .expect("unable to write fifo pipe"); } /// filtering logic as implemented by fapolicyd rpm backend diff --git a/src/sys.rs b/src/sys.rs index 0d9ab8e7a..5c363c958 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -1,21 +1,30 @@ use serde::Deserialize; use serde::Serialize; +use crate::api::TrustSource; use crate::app::State; use crate::fapolicyd; +use std::fs::File; +use std::io::Write; -pub fn deploy_app_state(_state: &State) { +pub fn deploy_app_state(state: &State) -> Result<(), String> { // todo;; back up trust file println!("backing up trust file..."); - // todo;; write changeset println!("writing changeset to disk..."); + let mut tf = File::create(&state.config.system.ancillary_trust_path) + .expect("unable to create ancillary trust"); + for t in &state.trust_db { + if t.source == TrustSource::Ancillary { + tf.write_all(format!("{} {} {}\n", t.path, t.size.to_string(), t.hash).as_bytes()) + .expect("unable to write ancillary trust file") + } + } - // todo;; signal fapolicyd update println!("signaling fapolicdy reload..."); + fapolicyd::reload_databases(); - // todo;; return new Application - println!("reloading app trust database..."); + Ok(()) } /// host system configuration information