1use std::fs;
2use std::io::Write;
3use std::path::{Path, PathBuf};
4
5use pathrs::flags::OpenFlags;
6use pathrs::procfs::{ProcfsBase, ProcfsHandle};
7
8#[derive(Debug, thiserror::Error)]
9pub enum AppArmorError {
10 #[error("failed to apply AppArmor profile")]
11 ActivateProfile {
12 path: PathBuf,
13 profile: String,
14 source: std::io::Error,
15 },
16 #[error(transparent)]
17 Pathrs(#[from] pathrs::error::Error),
18}
19
20type Result<T> = std::result::Result<T, AppArmorError>;
21
22const ENABLED_PARAMETER_PATH: &str = "/sys/module/apparmor/parameters/enabled";
23
24pub fn is_enabled() -> std::result::Result<bool, std::io::Error> {
26 let aa_enabled = fs::read_to_string(ENABLED_PARAMETER_PATH)?;
27 Ok(aa_enabled.starts_with('Y'))
28}
29
30pub fn apply_profile(profile: &str) -> Result<()> {
32 if profile.is_empty() {
33 return Ok(());
34 }
35
36 activate_profile(Path::new("attr/apparmor/exec"), profile)
39 .or_else(|_| activate_profile(Path::new("attr/exec"), profile))
41}
42
43fn activate_profile(subpath: &Path, profile: &str) -> Result<()> {
44 ProcfsHandle::new()?
45 .open(
46 ProcfsBase::ProcSelf,
47 subpath,
48 OpenFlags::O_WRONLY | OpenFlags::O_CLOEXEC,
49 )?
50 .write_all(format!("exec {profile}").as_bytes())
51 .map_err(|err| AppArmorError::ActivateProfile {
52 path: PathBuf::from("/proc/self").join(subpath),
53 profile: profile.to_owned(),
54 source: err,
55 })
56}