mod acl;
#[cfg(feature = "apparmor")]
mod apparmor;
pub mod caps;
mod caps_modify;
mod dac;
mod fsflags;
mod metadata;
mod mount;
#[cfg(feature = "selinux")]
mod selinux;
mod traversal;
use enum_map::{Enum, EnumMap};
use serde::Serialize;
use crate::state::SystemState;
pub use crate::operation::MetadataParams;
pub use acl::check_acl;
pub use caps::{
has_cap, CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_FOWNER, CAP_FSETID,
CAP_LINUX_IMMUTABLE, CAP_SYS_ADMIN,
};
pub use caps_modify::capability_modify;
pub use dac::check_dac;
pub use fsflags::check_fs_flags;
pub use metadata::check_metadata;
pub use mount::check_mount;
#[cfg(feature = "selinux")]
pub use selinux::operation_to_selinux_perm;
pub use traversal::check_traversal;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Enum, Serialize)]
#[non_exhaustive]
pub enum CoreLayer {
Mount,
FsFlags,
Traversal,
Dac,
Acl,
Metadata,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
#[non_exhaustive]
pub enum MacLayer {
SeLinux,
AppArmor,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(tag = "result")]
#[non_exhaustive]
pub enum LayerResult {
Pass {
detail: String,
warnings: Vec<String>,
},
Fail {
detail: String,
component_index: Option<usize>,
},
Degraded {
reason: String,
},
}
impl LayerResult {
#[must_use]
pub fn is_fail(&self) -> bool {
matches!(self, Self::Fail { .. })
}
#[must_use]
pub fn is_pass(&self) -> bool {
matches!(self, Self::Pass { .. })
}
#[must_use]
pub fn is_degraded(&self) -> bool {
matches!(self, Self::Degraded { .. })
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct CheckReport {
pub core_results: EnumMap<CoreLayer, LayerResult>,
pub mac_results: Vec<(MacLayer, LayerResult)>,
}
impl CheckReport {
#[must_use]
pub fn is_allowed(&self) -> bool {
let core_ok = self.core_results.values().all(|r| !r.is_fail());
let mac_ok = self.mac_results.iter().all(|(_, r)| !r.is_fail());
core_ok && mac_ok
}
#[must_use]
pub fn failed_layers(&self) -> Vec<CoreLayer> {
self.core_results
.iter()
.filter(|(_, r)| r.is_fail())
.map(|(l, _)| l)
.collect()
}
}
#[must_use]
pub fn run_checks(state: &SystemState, params: &MetadataParams) -> CheckReport {
let core_results = EnumMap::from_array([
check_mount(state),
check_fs_flags(state),
check_traversal(state),
run_dac_with_caps(state),
check_acl(state),
check_metadata(state, params),
]);
let mac_results = build_mac_results(state);
CheckReport {
core_results,
mac_results,
}
}
#[allow(clippy::vec_init_then_push)]
fn build_mac_results(state: &SystemState) -> Vec<(MacLayer, LayerResult)> {
let mut results: Vec<(MacLayer, LayerResult)> = Vec::with_capacity(2);
#[cfg(feature = "selinux")]
results.push((MacLayer::SeLinux, selinux::check_selinux(state)));
#[cfg(not(feature = "selinux"))]
results.push((MacLayer::SeLinux, selinux_stub(state)));
#[cfg(feature = "apparmor")]
results.push((MacLayer::AppArmor, apparmor::check_apparmor(state)));
#[cfg(not(feature = "apparmor"))]
results.push((MacLayer::AppArmor, apparmor_stub(state)));
results
}
#[cfg(not(feature = "selinux"))]
fn selinux_stub(state: &SystemState) -> LayerResult {
use crate::state::Probe;
match &state.mac_state.selinux {
Probe::Unknown => LayerResult::Degraded {
reason: "SELinux state not gathered".into(),
},
Probe::Inaccessible => LayerResult::Degraded {
reason: "SELinux state inaccessible".into(),
},
Probe::Known(_) => LayerResult::Degraded {
reason: "SELinux — not compiled in (rebuild with --features selinux)".into(),
},
}
}
#[cfg(not(feature = "apparmor"))]
fn apparmor_stub(state: &SystemState) -> LayerResult {
use crate::state::Probe;
match &state.mac_state.apparmor {
Probe::Unknown => LayerResult::Degraded {
reason: "AppArmor state not gathered".into(),
},
Probe::Inaccessible => LayerResult::Degraded {
reason: "AppArmor state inaccessible".into(),
},
Probe::Known(_) => LayerResult::Degraded {
reason: "AppArmor — not compiled in (rebuild with --features apparmor)".into(),
},
}
}
fn run_dac_with_caps(state: &SystemState) -> LayerResult {
let result = check_dac(state);
capability_modify(result, state)
}
#[cfg(test)]
#[path = "pipeline_tests.rs"]
mod tests;