Skip to content

Commit

Permalink
ruleset: Move RulesetCreated methods to a new RulesetCreatedAttr trait
Browse files Browse the repository at this point in the history
This enables to make add_rule(), add_rules() and set_no_new_privs()
available from a RulesetCreated reference.

Implement AsMut<RulesetCreated>, required by RulesetCreatedAttr.

Users need to import the RulesetCreatedAttr trait to call such methods.

Closes landlock-lsm#19

Signed-off-by: Mickaël Salaün <mic@digikod.net>
  • Loading branch information
l0kod committed Nov 4, 2022
1 parent 0aaa555 commit 5934b34
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 36 deletions.
4 changes: 2 additions & 2 deletions examples/sandboxer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

use anyhow::{anyhow, bail};
use landlock::{
Access, AccessFs, BitFlags, PathBeneath, PathFd, PathFdError, Ruleset, RulesetError,
RulesetStatus, ABI,
Access, AccessFs, BitFlags, PathBeneath, PathFd, PathFdError, Ruleset, RulesetCreatedAttr,
RulesetError, RulesetStatus, ABI,
};
use std::env;
use std::ffi::OsStr;
Expand Down
5 changes: 3 additions & 2 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,13 @@ where
#[non_exhaustive]
pub enum PathBeneathError {
/// To check that access-rights are consistent with a file descriptor, a call to
/// [`RulesetCreated::add_rule()`](crate::RulesetCreated::add_rule)
/// [`RulesetCreatedAttr::add_rule()`](crate::RulesetCreatedAttr::add_rule)
/// looks at the file type with an `fstat()` system call.
#[error("failed to check file descriptor type: {source}")]
#[non_exhaustive]
StatCall { source: io::Error },
/// This error is returned by [`RulesetCreated::add_rule()`](crate::RulesetCreated::add_rule)
/// This error is returned by
/// [`RulesetCreatedAttr::add_rule()`](crate::RulesetCreatedAttr::add_rule)
/// if the related PathBeneath object is not set to best-effort,
/// and if its allowed access-rights contain directory-only ones
/// whereas the file descriptor doesn't point to a directory.
Expand Down
7 changes: 6 additions & 1 deletion src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::{AsRawFd, RawFd};
use std::path::Path;

#[cfg(test)]
use crate::RulesetCreatedAttr;
#[cfg(test)]
use strum::IntoEnumIterator;

Expand Down Expand Up @@ -425,7 +427,10 @@ fn path_fd() {
/// # Example
///
/// ```
/// use landlock::{ABI, Access, AccessFs, Ruleset, RulesetStatus, RulesetError, path_beneath_rules};
/// use landlock::{
/// ABI, Access, AccessFs, Ruleset, RulesetCreatedAttr, RulesetStatus, RulesetError,
/// path_beneath_rules,
/// };
///
/// fn restrict_thread() -> Result<(), RulesetError> {
/// let abi = ABI::V1;
Expand Down
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ pub use errors::{
HandleAccessesError, PathBeneathError, PathFdError, RestrictSelfError, RulesetError,
};
pub use fs::{path_beneath_rules, AccessFs, PathBeneath, PathFd};
pub use ruleset::{Access, RestrictionStatus, Rule, Ruleset, RulesetCreated, RulesetStatus};
pub use ruleset::{
Access, RestrictionStatus, Rule, Ruleset, RulesetCreated, RulesetCreatedAttr, RulesetStatus,
};
use ruleset::{PrivateAccess, PrivateRule};

#[cfg(test)]
Expand Down
111 changes: 81 additions & 30 deletions src/ruleset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ fn support_no_new_privs() -> bool {
///
/// ```
/// use landlock::{
/// Access, AccessFs, PathBeneath, PathFd, RestrictionStatus, Ruleset, RulesetError, ABI,
/// Access, AccessFs, PathBeneath, PathFd, RestrictionStatus, Ruleset, RulesetCreatedAttr,
/// RulesetError, ABI,
/// };
/// use std::os::unix::io::AsRawFd;
///
Expand Down Expand Up @@ -169,7 +170,7 @@ fn support_no_new_privs() -> bool {
/// ```
/// use landlock::{
/// Access, AccessFs, PathBeneath, PathFd, PathFdError, RestrictionStatus, Ruleset,
/// RulesetError, ABI,
/// RulesetCreatedAttr, RulesetError, ABI,
/// };
/// use thiserror::Error;
///
Expand Down Expand Up @@ -342,47 +343,29 @@ impl Compatible for Ruleset {
}
}

/// Ruleset created with [`Ruleset::create()`].
#[cfg_attr(test, derive(Debug))]
pub struct RulesetCreated {
fd: RawFd,
no_new_privs: bool,
pub(crate) requested_handled_fs: BitFlags<AccessFs>,
compat: Compatibility,
}

impl RulesetCreated {
fn new(ruleset: Ruleset, fd: RawFd) -> Self {
RulesetCreated {
fd,
no_new_privs: true,
requested_handled_fs: ruleset.requested_handled_fs,
compat: ruleset.compat,
}
}

pub trait RulesetCreatedAttr: Sized + AsRef<RulesetCreated> + AsMut<RulesetCreated> {
/// Attempts to add a new rule to the ruleset.
///
/// On error, returns a wrapped [`AddRulesError`].
pub fn add_rule<T, U>(mut self, rule: T) -> Result<Self, RulesetError>
fn add_rule<T, U>(mut self, rule: T) -> Result<Self, RulesetError>
where
T: Rule<U>,
U: Access,
{
let body = || -> Result<Self, AddRulesError> {
rule.check_consistency(&self)?;
let compat_rule = rule
.try_compat(&mut self.compat)
.try_compat(&mut self.as_mut().compat)
.map_err(AddRuleError::Compat)?;
match self.compat.abi {
match self.as_ref().compat.abi {
ABI::Unsupported => {
#[cfg(test)]
assert_eq!(self.compat.state, CompatState::Final);
assert_eq!(self.as_ref().compat.state, CompatState::Final);
Ok(self)
}
_ => match unsafe {
uapi::landlock_add_rule(
self.fd,
self.as_ref().fd,
compat_rule.get_type_id(),
compat_rule.as_ptr(),
compat_rule.get_flags(),
Expand Down Expand Up @@ -410,7 +393,7 @@ impl RulesetCreated {
/// ```
/// use landlock::{
/// Access, AccessFs, BitFlags, PathBeneath, PathFd, PathFdError, RestrictionStatus, Ruleset,
/// RulesetError, ABI,
/// RulesetCreatedAttr, RulesetError, ABI,
/// };
/// use std::env;
/// use std::ffi::OsStr;
Expand Down Expand Up @@ -473,7 +456,7 @@ impl RulesetCreated {
/// .restrict_self()?)
/// }
/// ```
pub fn add_rules<I, T, U, E>(mut self, rules: I) -> Result<Self, E>
fn add_rules<I, T, U, E>(mut self, rules: I) -> Result<Self, E>
where
I: IntoIterator<Item = Result<T, E>>,
T: Rule<U>,
Expand All @@ -489,10 +472,30 @@ impl RulesetCreated {

/// Configures the ruleset to call `prctl(2)` with the `PR_SET_NO_NEW_PRIVS` command
/// in [`restrict_self()`](RulesetCreated::restrict_self).
pub fn set_no_new_privs(mut self, no_new_privs: bool) -> Self {
self.no_new_privs = no_new_privs;
fn set_no_new_privs(mut self, no_new_privs: bool) -> Self {
self.as_mut().no_new_privs = no_new_privs;
self
}
}

/// Ruleset created with [`Ruleset::create()`].
#[cfg_attr(test, derive(Debug))]
pub struct RulesetCreated {
fd: RawFd,
no_new_privs: bool,
pub(crate) requested_handled_fs: BitFlags<AccessFs>,
compat: Compatibility,
}

impl RulesetCreated {
fn new(ruleset: Ruleset, fd: RawFd) -> Self {
RulesetCreated {
fd,
no_new_privs: true,
requested_handled_fs: ruleset.requested_handled_fs,
compat: ruleset.compat,
}
}

/// Attempts to restrict the calling thread with the ruleset
/// according to the best-effort configuration
Expand Down Expand Up @@ -575,6 +578,54 @@ impl AsRef<RulesetCreated> for RulesetCreated {
}
}

impl AsMut<RulesetCreated> for RulesetCreated {
fn as_mut(&mut self) -> &mut RulesetCreated {
self
}
}

impl RulesetCreatedAttr for RulesetCreated {}

impl RulesetCreatedAttr for &mut RulesetCreated {}

#[test]
fn ruleset_created_attr() {
let mut ruleset_created = Ruleset::from(ABI::Unsupported)
.handle_access(AccessFs::Execute)
.unwrap()
.create()
.unwrap();
let ruleset_created_ref = &mut ruleset_created;

// Can pass this reference to populate the ruleset...
ruleset_created_ref
.add_rule(PathBeneath::new(
PathFd::new("/usr").unwrap(),
AccessFs::Execute,
))
.unwrap()
.add_rule(PathBeneath::new(
PathFd::new("/etc").unwrap(),
AccessFs::Execute,
))
.unwrap();

// ...and finally restrict with the last rules (thanks to non-lexical lifetimes).
ruleset_created
.add_rule(PathBeneath::new(
PathFd::new("/tmp").unwrap(),
AccessFs::Execute,
))
.unwrap()
.add_rule(PathBeneath::new(
PathFd::new("/var").unwrap(),
AccessFs::Execute,
))
.unwrap()
.restrict_self()
.unwrap();
}

impl Compatible for RulesetCreated {
fn set_best_effort(mut self, best_effort: bool) -> Self {
self.compat.is_best_effort = best_effort;
Expand Down

0 comments on commit 5934b34

Please sign in to comment.