From 5934b34f34897caf7cb92a525dae9e3448bd4bd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Fri, 28 Oct 2022 19:16:03 +0100 Subject: [PATCH] ruleset: Move RulesetCreated methods to a new RulesetCreatedAttr trait MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This enables to make add_rule(), add_rules() and set_no_new_privs() available from a RulesetCreated reference. Implement AsMut, required by RulesetCreatedAttr. Users need to import the RulesetCreatedAttr trait to call such methods. Closes #19 Signed-off-by: Mickaël Salaün --- examples/sandboxer.rs | 4 +- src/errors.rs | 5 +- src/fs.rs | 7 ++- src/lib.rs | 4 +- src/ruleset.rs | 111 ++++++++++++++++++++++++++++++------------ 5 files changed, 95 insertions(+), 36 deletions(-) diff --git a/examples/sandboxer.rs b/examples/sandboxer.rs index 5d82573..1e9b816 100644 --- a/examples/sandboxer.rs +++ b/examples/sandboxer.rs @@ -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; diff --git a/src/errors.rs b/src/errors.rs index 56b49a1..537935c 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -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. diff --git a/src/fs.rs b/src/fs.rs index 855ce02..a392d19 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -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; @@ -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; diff --git a/src/lib.rs b/src/lib.rs index 47178c5..b26c90c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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)] diff --git a/src/ruleset.rs b/src/ruleset.rs index 85177f7..89a5679 100644 --- a/src/ruleset.rs +++ b/src/ruleset.rs @@ -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; /// @@ -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; /// @@ -342,29 +343,11 @@ 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, - 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 + AsMut { /// Attempts to add a new rule to the ruleset. /// /// On error, returns a wrapped [`AddRulesError`]. - pub fn add_rule(mut self, rule: T) -> Result + fn add_rule(mut self, rule: T) -> Result where T: Rule, U: Access, @@ -372,17 +355,17 @@ impl RulesetCreated { let body = || -> Result { 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(), @@ -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; @@ -473,7 +456,7 @@ impl RulesetCreated { /// .restrict_self()?) /// } /// ``` - pub fn add_rules(mut self, rules: I) -> Result + fn add_rules(mut self, rules: I) -> Result where I: IntoIterator>, T: Rule, @@ -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, + 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 @@ -575,6 +578,54 @@ impl AsRef for RulesetCreated { } } +impl AsMut 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;