use landlock::{
ABI, Access, AccessFs, AccessNet, AddRulesError, BitFlags, CreateRulesetError,
HandleAccessesError, NetPort, PathBeneath, PathFd, Ruleset, RulesetAttr, RulesetCreated,
RulesetCreatedAttr, RulesetError, path_beneath_rules,
};
use crate::{FilePermissions, NetworkPermissions};
pub struct InsideRuleset {
abi: ABI,
ruleset: RulesetCreated,
}
impl InsideRuleset {
pub fn try_new(abi: ABI) -> Result<Self, NewInsideRulesetError> {
match Ruleset::default().handle_access(AccessFs::from_all(abi)) {
Ok(ruleset) => match ruleset.handle_access(AccessNet::from_all(abi)) {
Ok(ruleset) => match ruleset.create() {
Ok(ruleset) => Ok(InsideRuleset { abi, ruleset }),
Err(RulesetError::CreateRuleset(e)) => Err(e.into()),
Err(_) => unreachable!(),
},
Err(RulesetError::HandleAccesses(e)) => Err(e.into()),
Err(_) => unreachable!(),
},
Err(RulesetError::HandleAccesses(e)) => Err(e.into()),
Err(_) => unreachable!(),
}
}
pub fn allow_file(
mut self,
path: AllowPath,
permissions: FilePermissions,
) -> Result<Self, AddRulesError> {
let access_fs = if let Some(access_fs) = self.access_fs(permissions) {
access_fs
} else {
return Ok(self);
};
let ruleset = self
.ruleset
.add_rule(PathBeneath::new(path.0, access_fs))
.map_err(|e| match e {
RulesetError::AddRules(e) => e,
_ => unreachable!(),
})?;
self.ruleset = ruleset;
Ok(self)
}
pub fn allow_files<T: AsRef<std::path::Path>>(
mut self,
paths: impl Iterator<Item = T>,
permissions: FilePermissions,
) -> Result<Self, AddRulesError> {
let access_fs = if let Some(access_fs) = self.access_fs(permissions) {
access_fs
} else {
return Ok(self);
};
self.ruleset = self
.ruleset
.add_rules(path_beneath_rules(paths, access_fs))
.map_err(|e| match e {
RulesetError::AddRules(e) => e,
_ => unreachable!(),
})?;
Ok(self)
}
pub fn allow_port(
mut self,
port: u16,
network_permissions: NetworkPermissions,
) -> Result<Self, AddRulesError> {
let access_net = if let Some(network_permissions) = self.access_net(network_permissions) {
network_permissions
} else {
return Ok(self);
};
self.ruleset = self
.ruleset
.add_rule(NetPort::new(port, access_net))
.map_err(|e| match e {
RulesetError::AddRules(e) => e,
_ => unreachable!(),
})?;
Ok(self)
}
pub fn restrict(self) -> Result<landlock::RestrictionStatus, RulesetError> {
self.ruleset.restrict_self()
}
fn access_fs(&self, file_permissions: FilePermissions) -> Option<BitFlags<AccessFs, u64>> {
let access_fs = match file_permissions {
FilePermissions::NONE => {
return None;
}
FilePermissions::READ => AccessFs::from_read(self.abi),
FilePermissions::WRITE => AccessFs::from_write(self.abi),
FilePermissions::EXECUTE => AccessFs::Execute.into(),
FilePermissions::READ_WRITE => AccessFs::from_all(self.abi),
FilePermissions::READ_EXECUTE => AccessFs::from_read(self.abi) | AccessFs::Execute,
FilePermissions::WRITE_EXECUTE => AccessFs::from_write(self.abi) | AccessFs::Execute,
FilePermissions::ALL => AccessFs::from_all(self.abi) | AccessFs::Execute,
_ => unreachable!(),
};
Some(access_fs)
}
fn access_net(
&self,
network_permissions: NetworkPermissions,
) -> Option<BitFlags<AccessNet, u64>> {
let access_net = match network_permissions {
NetworkPermissions::NONE => return None,
NetworkPermissions::CONNECT_TCP => AccessNet::ConnectTcp.into(),
NetworkPermissions::BIND_TCP => AccessNet::BindTcp.into(),
NetworkPermissions::ALL => AccessNet::from_all(self.abi),
_ => unreachable!(),
};
Some(access_net)
}
}
#[derive(thiserror::Error, Debug)]
pub enum NewInsideRulesetError {
#[error(transparent)]
HandleAccess(#[from] HandleAccessesError),
#[error(transparent)]
CreateRuleset(#[from] CreateRulesetError),
}
#[derive(Debug)]
pub struct AllowPath(PathFd);
impl AllowPath {
pub fn try_new(path: impl AsRef<std::path::Path>) -> Result<Self, landlock::PathFdError> {
let path = PathFd::new(path)?;
Ok(AllowPath(path))
}
}