Skip to content

Commit

Permalink
net: Add NetPort rule
Browse files Browse the repository at this point in the history
The NetPort type enables us to create network port rules leveraging
Landlock ABI 4.

Signed-off-by: Mickaël Salaün <[email protected]>
  • Loading branch information
l0kod committed May 31, 2024
1 parent afd2999 commit 547190c
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 5 deletions.
11 changes: 11 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ where
PathBeneath(#[from] PathBeneathError),
#[error(transparent)]
Access(#[from] AccessError<T>),
#[error(transparent)]
NetPort(#[from] NetPortError),
}

#[derive(Debug, Error)]
Expand All @@ -147,6 +149,15 @@ pub enum PathBeneathError {
},
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum NetPortError {
/// For now, only 16-bit ports are supported.
#[error("port value is greater than 2^16 - 1")]
#[non_exhaustive]
PortOverflow,
}

#[derive(Debug, Error)]
// Exhaustive enum
pub enum AccessError<T>
Expand Down
5 changes: 3 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,11 @@ pub use compat::{CompatLevel, Compatible, ABI};
pub use enumflags2::{make_bitflags, BitFlags};
pub use errors::{
AccessError, AddRuleError, AddRulesError, CompatError, CreateRulesetError, HandleAccessError,
HandleAccessesError, PathBeneathError, PathFdError, RestrictSelfError, RulesetError,
HandleAccessesError, NetPortError, PathBeneathError, PathFdError, RestrictSelfError,
RulesetError,
};
pub use fs::{path_beneath_rules, AccessFs, PathBeneath, PathFd};
pub use net::AccessNet;
pub use net::{AccessNet, NetPort};
pub use ruleset::{
RestrictionStatus, Rule, Ruleset, RulesetAttr, RulesetCreated, RulesetCreatedAttr,
RulesetStatus,
Expand Down
142 changes: 140 additions & 2 deletions src/net.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use crate::compat::private::OptionCompatLevelMut;
use crate::{
uapi, Access, AddRuleError, AddRulesError, HandleAccessError, HandleAccessesError,
PrivateAccess, Ruleset, TryCompat, ABI,
uapi, Access, AddRuleError, AddRulesError, CompatError, CompatLevel, CompatResult, CompatState,
Compatible, HandleAccessError, HandleAccessesError, NetPortError, PrivateAccess, PrivateRule,
Ruleset, RulesetCreated, TailoredCompatLevel, TryCompat, ABI,
};
use enumflags2::{bitflags, BitFlags};
use std::mem::zeroed;

/// Network access right.
///
Expand Down Expand Up @@ -86,3 +89,138 @@ impl PrivateAccess for AccessNet {
HandleAccessesError::Net(error)
}
}

/// Landlock rule for a network port.
///
/// TODO: Add example, see PathBeneath
#[cfg_attr(test, derive(Debug))]
pub struct NetPort {
attr: uapi::landlock_net_port_attr,
port: u64,
allowed_access: BitFlags<AccessNet>,
compat_level: Option<CompatLevel>,
}

impl NetPort {
// FIXME: use type system to forbid ports > 16-bit
pub fn new<A>(port: u64, access: A) -> Self
where
A: Into<BitFlags<AccessNet>>,
{
NetPort {
// Invalid access-rights until as_ptr() is called.
attr: unsafe { zeroed() },
port,
allowed_access: access.into(),
compat_level: None,
}
}
}

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;
self.attr.allowed_access = self.allowed_access.bits();
&self.attr as *const _ as _
}

// TODO: Add tests
fn check_consistency(&self, ruleset: &RulesetCreated) -> Result<(), AddRulesError> {
// Checks that this rule doesn't contain a superset of the access-rights handled by the
// ruleset. This check is about requested access-rights but not actual access-rights.
// Indeed, we want to get a deterministic behavior, i.e. not based on the running kernel
// (which is handled by Ruleset and RulesetCreated).
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())
}
}
}

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>,
{
// Checks with our own compatibility level, if any.
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(
self,
_abi: ABI,
) -> Result<CompatResult<Self, AccessNet>, CompatError<AccessNet>> {
// TODO: check port 0?
if self.port > u16::MAX.into() {
return Err(CompatError::NetPort(NetPortError::PortOverflow));
}
Ok(CompatResult::Full(self))
}
}

#[cfg(test)]
fn port_overflow(abi: ABI) {
use crate::*;

let net_port = NetPort::new(65535, AccessNet::ConnectTcp);
net_port.try_compat_inner(abi).unwrap();

let mut compat_state = CompatState::Init;
let net_port = NetPort::new(65536, AccessNet::ConnectTcp);
assert!(matches!(
net_port
// FIXME:
.try_compat(abi, CompatLevel::BestEffort, &mut compat_state)
.unwrap_err(),
CompatError::NetPort(NetPortError::PortOverflow)
));
}

/*
#[test]
fn port_overflow_v3() {
// FIXME:
port_overflow(ABI::V3);
}
*/

#[test]
fn port_overflow_v4() {
port_overflow(ABI::V4);
}

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 {}
3 changes: 3 additions & 0 deletions src/ruleset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ pub struct RulesetCreated {
fd: RawFd,
no_new_privs: bool,
pub(crate) requested_handled_fs: BitFlags<AccessFs>,
pub(crate) requested_handled_net: BitFlags<AccessNet>,
compat: Compatibility,
}

Expand All @@ -605,6 +606,7 @@ impl RulesetCreated {
fd,
no_new_privs: true,
requested_handled_fs: ruleset.requested_handled_fs,
requested_handled_net: ruleset.requested_handled_net,
compat: ruleset.compat,
}
}
Expand Down Expand Up @@ -702,6 +704,7 @@ impl RulesetCreated {
},
no_new_privs: self.no_new_privs,
requested_handled_fs: self.requested_handled_fs,
requested_handled_net: self.requested_handled_net,
compat: self.compat,
})
}
Expand Down
4 changes: 3 additions & 1 deletion src/uapi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ mod landlock;

#[rustfmt::skip]
pub use self::landlock::{
landlock_net_port_attr,
landlock_path_beneath_attr,
landlock_ruleset_attr,
landlock_rule_type,
landlock_rule_type_LANDLOCK_RULE_NET_PORT,
landlock_rule_type_LANDLOCK_RULE_PATH_BENEATH,
landlock_ruleset_attr,
LANDLOCK_ACCESS_FS_EXECUTE,
LANDLOCK_ACCESS_FS_WRITE_FILE,
LANDLOCK_ACCESS_FS_READ_FILE,
Expand Down

0 comments on commit 547190c

Please sign in to comment.