use crate::checks::LayerResult;
use crate::operation::Operation;
use crate::state::mac::AppArmorState;
use crate::state::{Probe, SystemState};
pub(crate) enum ProfileMode {
Enforce,
Complain,
Unknown,
}
pub fn check_apparmor(state: &SystemState) -> LayerResult {
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(aa) => evaluate_apparmor_state(aa, state.operation),
}
}
fn evaluate_apparmor_state(aa: &AppArmorState, operation: Operation) -> LayerResult {
let label = &aa.profile_label;
if label.starts_with("unconfined") {
return LayerResult::Pass {
detail: format!("AppArmor: subject is unconfined ({label})"),
warnings: vec![],
};
}
let (profile_name, mode) = parse_profile_label(label);
match mode {
ProfileMode::Enforce => build_enforce_result(profile_name, operation),
ProfileMode::Complain => LayerResult::Pass {
detail: format!("AppArmor: profile '{profile_name}' in complain mode"),
warnings: vec![
"AppArmor is in complain mode — violations logged but not blocked".into(),
"deny rules in this profile are still enforced even in complain mode".into(),
],
},
ProfileMode::Unknown => LayerResult::Degraded {
reason: format!("AppArmor: cannot determine mode for profile '{profile_name}'"),
},
}
}
fn build_enforce_result(profile_name: &str, operation: Operation) -> LayerResult {
if operation.is_metadata() {
return LayerResult::Degraded {
reason: format!(
"AppArmor: profile '{profile_name}' in enforce mode — metadata ops require \
capability rules (chown/fowner/sys_admin) which cannot be queried \
without libapparmor",
),
};
}
LayerResult::Degraded {
reason: format!(
"AppArmor: profile '{profile_name}' in enforce mode — \
rebuild with libapparmor support for full policy query"
),
}
}
pub(crate) fn parse_profile_label(label: &str) -> (&str, ProfileMode) {
if let Some(pos) = label.rfind(" (") {
let name = &label[..pos];
let suffix = &label[pos + 2..];
let mode = if suffix.starts_with("enforce") {
ProfileMode::Enforce
} else if suffix.starts_with("complain") {
ProfileMode::Complain
} else {
ProfileMode::Unknown
};
(name, mode)
} else {
(label, ProfileMode::Unknown)
}
}
#[cfg(test)]
#[path = "apparmor_tests.rs"]
mod tests;