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.

Only 16-bit ports are allowed by the type system, which remove the need
for overflow check and error.

Signed-off-by: Mickaël Salaün <mic@digikod.net>
  • Loading branch information
l0kod committed Jun 10, 2024
1 parent 3d02895 commit ba349d2
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ pub use errors::{
HandleAccessesError, 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
115 changes: 113 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, PrivateAccess, PrivateRule, Rule, Ruleset,
RulesetCreated, TailoredCompatLevel, TryCompat, ABI,
};
use enumflags2::{bitflags, BitFlags};
use std::mem::zeroed;

/// Network access right.
///
Expand Down Expand Up @@ -86,3 +89,111 @@ 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,
// Only 16-bit port make sense for now.
port: u16,
allowed_access: BitFlags<AccessNet>,
compat_level: Option<CompatLevel>,
}

// If we need support for 32 or 64 ports, we'll add a new_32() or a new_64() method returning a
// Result with a potential overflow error.
impl NetPort {
/// Creates a new TCP port rule.
///
/// As defined by the Linux ABI, `port` with a value of `0` means that TCP bindings will be
/// allowed for a port range defined by `/proc/sys/net/ipv4/ip_local_port_range`.
pub fn new<A>(port: u16, 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 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 _
}

// 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(
&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 {}
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 ba349d2

Please sign in to comment.