use crate::compat::private::OptionCompatLevelMut;
use crate::{
uapi, Access, AddRuleError, AddRulesError, CompatError, CompatLevel, CompatResult, CompatState,
Compatible, HandleAccessError, HandleAccessesError, HandledAccess, PrivateHandledAccess,
PrivateRule, Rule, Ruleset, RulesetCreated, TailoredCompatLevel, TryCompat, ABI,
};
use enumflags2::{bitflags, BitFlags};
use std::mem::zeroed;
#[bitflags]
#[repr(u64)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum AccessNet {
BindTcp = uapi::LANDLOCK_ACCESS_NET_BIND_TCP as u64,
ConnectTcp = uapi::LANDLOCK_ACCESS_NET_CONNECT_TCP as u64,
}
impl Access for AccessNet {
fn from_all(abi: ABI) -> BitFlags<Self> {
match abi {
ABI::Unsupported | ABI::V1 | ABI::V2 | ABI::V3 => BitFlags::EMPTY,
ABI::V4 | ABI::V5 | ABI::V6 => AccessNet::BindTcp | AccessNet::ConnectTcp,
}
}
}
impl HandledAccess for AccessNet {}
impl PrivateHandledAccess for AccessNet {
fn ruleset_handle_access(
ruleset: &mut Ruleset,
access: BitFlags<Self>,
) -> Result<(), HandleAccessesError> {
ruleset.requested_handled_net |= access;
ruleset.actual_handled_net |= match access
.try_compat(
ruleset.compat.abi(),
ruleset.compat.level,
&mut ruleset.compat.state,
)
.map_err(HandleAccessError::Compat)?
{
Some(a) => a,
None => return Ok(()),
};
Ok(())
}
fn into_add_rules_error(error: AddRuleError<Self>) -> AddRulesError {
AddRulesError::Net(error)
}
fn into_handle_accesses_error(error: HandleAccessError<Self>) -> HandleAccessesError {
HandleAccessesError::Net(error)
}
}
#[derive(Debug)]
pub struct NetPort {
attr: uapi::landlock_net_port_attr,
port: u16,
allowed_access: BitFlags<AccessNet>,
compat_level: Option<CompatLevel>,
}
impl NetPort {
pub fn new<A>(port: u16, access: A) -> Self
where
A: Into<BitFlags<AccessNet>>,
{
NetPort {
attr: unsafe { zeroed() },
port,
allowed_access: access.into(),
compat_level: None,
}
}
}
impl Rule<AccessNet> for NetPort {}
impl PrivateRule<AccessNet> for NetPort {
const TYPE_ID: uapi::landlock_rule_type = uapi::landlock_rule_type_LANDLOCK_RULE_NET_PORT;
fn as_ptr(&mut self) -> *const libc::c_void {
self.attr.port = self.port as u64;
self.attr.allowed_access = self.allowed_access.bits();
&self.attr as *const _ as _
}
fn check_consistency(&self, ruleset: &RulesetCreated) -> Result<(), AddRulesError> {
if ruleset.requested_handled_net.contains(self.allowed_access) {
Ok(())
} else {
Err(AddRuleError::UnhandledAccess {
access: self.allowed_access,
incompatible: self.allowed_access & !ruleset.requested_handled_net,
}
.into())
}
}
}
#[test]
fn net_port_check_consistency() {
use crate::*;
let bind = AccessNet::BindTcp;
let bind_connect = bind | AccessNet::ConnectTcp;
assert!(matches!(
Ruleset::from(ABI::Unsupported)
.handle_access(bind)
.unwrap()
.create()
.unwrap()
.add_rule(NetPort::new(1, bind_connect))
.unwrap_err(),
RulesetError::AddRules(AddRulesError::Net(AddRuleError::UnhandledAccess { access, incompatible }))
if access == bind_connect && incompatible == AccessNet::ConnectTcp
));
}
impl TryCompat<AccessNet> for NetPort {
fn try_compat_children<L>(
mut self,
abi: ABI,
parent_level: L,
compat_state: &mut CompatState,
) -> Result<Option<Self>, CompatError<AccessNet>>
where
L: Into<CompatLevel>,
{
self.allowed_access = match self.allowed_access.try_compat(
abi,
self.tailored_compat_level(parent_level),
compat_state,
)? {
Some(a) => a,
None => return Ok(None),
};
Ok(Some(self))
}
fn try_compat_inner(
&mut self,
_abi: ABI,
) -> Result<CompatResult<AccessNet>, CompatError<AccessNet>> {
Ok(CompatResult::Full)
}
}
impl OptionCompatLevelMut for NetPort {
fn as_option_compat_level_mut(&mut self) -> &mut Option<CompatLevel> {
&mut self.compat_level
}
}
impl OptionCompatLevelMut for &mut NetPort {
fn as_option_compat_level_mut(&mut self) -> &mut Option<CompatLevel> {
&mut self.compat_level
}
}
impl Compatible for NetPort {}
impl Compatible for &mut NetPort {}