Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/compat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ pub enum ABI {
/// Sixth Landlock ABI, introduced with
/// [Linux 6.12](https://git.kernel.org/stable/c/e1b061b444fb01c237838f0d8238653afe6a8094).
V6 = 6,
/// Seventh Landlock ABI, introduced with
/// [Linux 6.15](https://git.kernel.org/stable/c/72885116069abdd05c245707c3989fc605632970).
V7 = 7,
}

// ABI should not be dynamically created (in other crates) according to the running kernel
Expand All @@ -94,8 +97,9 @@ impl From<i32> for ABI {
3 => ABI::V3,
4 => ABI::V4,
5 => ABI::V5,
6 => ABI::V6,
// Returns the greatest known ABI.
_ => ABI::V6,
_ => ABI::V7,
}
}
}
Expand Down
14 changes: 8 additions & 6 deletions src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,13 @@ impl AccessFs {
pub fn from_read(abi: ABI) -> BitFlags<Self> {
match abi {
ABI::Unsupported => BitFlags::EMPTY,
ABI::V1 | ABI::V2 | ABI::V3 | ABI::V4 | ABI::V5 | ABI::V6 => make_bitflags!(AccessFs::{
Execute
| ReadFile
| ReadDir
}),
ABI::V1 | ABI::V2 | ABI::V3 | ABI::V4 | ABI::V5 | ABI::V6 | ABI::V7 => {
make_bitflags!(AccessFs::{
Execute
| ReadFile
| ReadDir
})
}
}
}

Expand All @@ -140,7 +142,7 @@ impl AccessFs {
}),
ABI::V2 => Self::from_write(ABI::V1) | AccessFs::Refer,
ABI::V3 | ABI::V4 => Self::from_write(ABI::V2) | AccessFs::Truncate,
ABI::V5 | ABI::V6 => Self::from_write(ABI::V4) | AccessFs::IoctlDev,
ABI::V5 | ABI::V6 | ABI::V7 => Self::from_write(ABI::V4) | AccessFs::IoctlDev,
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ pub use errors::{
pub use fs::{path_beneath_rules, AccessFs, PathBeneath, PathFd};
pub use net::{AccessNet, NetPort};
pub use ruleset::{
RestrictionStatus, Rule, Ruleset, RulesetAttr, RulesetCreated, RulesetCreatedAttr,
RulesetStatus,
RestrictSelfFlag, RestrictionStatus, Rule, Ruleset, RulesetAttr, RulesetCreated,
RulesetCreatedAttr, RulesetStatus,
};
pub use scope::Scope;

Expand Down Expand Up @@ -123,6 +123,7 @@ mod private {

impl Sealed for crate::AccessFs {}
impl Sealed for crate::AccessNet {}
impl Sealed for crate::RestrictSelfFlag {}
impl Sealed for crate::Scope {}
}

Expand Down
2 changes: 1 addition & 1 deletion src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ 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,
ABI::V4 | ABI::V5 | ABI::V6 | ABI::V7 => AccessNet::BindTcp | AccessNet::ConnectTcp,
}
}
}
Expand Down
86 changes: 80 additions & 6 deletions src/ruleset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

use crate::compat::private::OptionCompatLevelMut;
use crate::{
uapi, AccessFs, AccessNet, AddRuleError, AddRulesError, BitFlags, CompatLevel, CompatState,
Compatibility, Compatible, CreateRulesetError, HandledAccess, LandlockStatus,
PrivateHandledAccess, RestrictSelfError, RulesetError, Scope, ScopeError, TryCompat,
uapi, Access, AccessFs, AccessNet, AddRuleError, AddRulesError, BitFlags, CompatLevel,
CompatState, Compatibility, Compatible, CreateRulesetError, HandledAccess, LandlockStatus,
PrivateHandledAccess, RestrictSelfError, RulesetError, Scope, ScopeError, TryCompat, ABI,
};
use enumflags2::bitflags;
use std::io::Error;
use std::mem::size_of_val;
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd};
Expand Down Expand Up @@ -50,6 +51,65 @@ pub enum RulesetStatus {
NotEnforced,
}

/// Flags to use when applying ruleset restrictions
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[bitflags]
#[repr(u32)]
#[non_exhaustive]
pub enum RestrictSelfFlag {
/// Disables logging of denied accesses originating
/// from the thread creating the Landlock domain, as
/// well as its children, as long as they continue
/// running the same executable code (i.e., without
/// an intervening execve(2) call). This is intended
/// for programs that execute unknown code without
/// invoking execve(2), such as script interpreters.
/// Programs that only sandbox themselves should not
/// set this flag, so users can be notified of
/// unauthorized access attempts via system logs.
LogSameExecOff = uapi::LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF,
/// Enables logging of denied accesses after an
/// execve(2) call, providing visibility into
/// unauthorized access attempts by newly executed
/// programs within the created Landlock domain.
/// This flag is recommended only when all potential
/// executables in the domain are expected to comply
/// with the access restrictions, as excessive audit
/// log entries could make it more difficult to
/// identify critical events.
LogNewExecOn = uapi::LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON,
/// Disables logging of denied accesses originating
/// from nested Landlock domains created by the caller
/// or its descendants. This flag should be set
/// according to runtime configuration, not hardcoded,
/// to avoid suppressing important security events.
/// It is useful for container runtimes or sandboxing
/// tools that may launch programs which themselves
/// create Landlock domains and could otherwise
/// generate excessive logs. Unlike
/// LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF, this flag
/// only affects future nested domains, not the one
/// being created. It can also be used with a ruleset_fd
/// value of -1 to mute subdomain logs without creating
/// a domain.
LogSubdomainsOff = uapi::LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF,
}

impl Access for RestrictSelfFlag {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Syscall flags are not the same semantic as access rights.

fn from_all(abi: ABI) -> BitFlags<Self> {
match abi {
ABI::Unsupported | ABI::V1 | ABI::V2 | ABI::V3 | ABI::V4 | ABI::V5 | ABI::V6 => {
BitFlags::EMPTY
}
ABI::V7 => {
RestrictSelfFlag::LogSameExecOff
| RestrictSelfFlag::LogNewExecOn
| RestrictSelfFlag::LogSubdomainsOff
}
}
}
}

impl From<CompatState> for RulesetStatus {
fn from(state: CompatState) -> Self {
match state {
Expand Down Expand Up @@ -746,14 +806,17 @@ impl RulesetCreated {
}
}

/// Attempts to restrict the calling thread with the ruleset
/// Attempts to restrict the calling thread with the ruleset and flags
/// according to the best-effort configuration
/// (see [`RulesetCreated::set_compatibility()`] and [`CompatLevel::BestEffort`]).
/// Call `prctl(2)` with the `PR_SET_NO_NEW_PRIVS`
/// according to the ruleset configuration.
///
/// On error, returns a wrapped [`RestrictSelfError`].
pub fn restrict_self(mut self) -> Result<RestrictionStatus, RulesetError> {
pub fn restrict_self_with_flags(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change, we need a smoother interface.

mut self,
flags: BitFlags<RestrictSelfFlag>,
) -> Result<RestrictionStatus, RulesetError> {
let mut body = || -> Result<RestrictionStatus, RestrictSelfError> {
// Enforce no_new_privs even if something failed with SoftRequirement. The rationale is
// that no_new_privs should not be an issue on its own if it is not explicitly
Expand Down Expand Up @@ -807,7 +870,7 @@ impl RulesetCreated {
assert!(self.fd.is_some());
// Does not consume ruleset FD, which will be automatically closed after this block.
let fd = self.fd.as_ref().map(|f| f.as_raw_fd()).unwrap_or(-1);
match unsafe { uapi::landlock_restrict_self(fd, 0) } {
match unsafe { uapi::landlock_restrict_self(fd, flags.bits()) } {
0 => {
self.compat.update(CompatState::Full);
Ok(RestrictionStatus {
Expand All @@ -827,6 +890,17 @@ impl RulesetCreated {
Ok(body()?)
}

/// Attempts to restrict the calling thread with the ruleset
/// according to the best-effort configuration
/// (see [`RulesetCreated::set_compatibility()`] and [`CompatLevel::BestEffort`]).
/// Call `prctl(2)` with the `PR_SET_NO_NEW_PRIVS`
/// according to the ruleset configuration.
///
/// On error, returns a wrapped [`RestrictSelfError`].
pub fn restrict_self(self) -> Result<RestrictionStatus, RulesetError> {
return self.restrict_self_with_flags(BitFlags::<RestrictSelfFlag>::empty());
}

/// Creates a new `RulesetCreated` instance by duplicating the underlying file descriptor.
/// Rule modification will affect both `RulesetCreated` instances simultaneously.
///
Expand Down
2 changes: 1 addition & 1 deletion src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl Access for Scope {
fn from_all(abi: ABI) -> BitFlags<Self> {
match abi {
ABI::Unsupported | ABI::V1 | ABI::V2 | ABI::V3 | ABI::V4 | ABI::V5 => BitFlags::EMPTY,
ABI::V6 => Scope::AbstractUnixSocket | Scope::Signal,
ABI::V6 | ABI::V7 => Scope::AbstractUnixSocket | Scope::Signal,
}
}
}
6 changes: 5 additions & 1 deletion src/uapi/landlock_all.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
/* automatically generated by rust-bindgen 0.72.0 */
/* automatically generated by rust-bindgen 0.72.1 */

pub const LANDLOCK_CREATE_RULESET_VERSION: u32 = 1;
pub const LANDLOCK_CREATE_RULESET_ERRATA: u32 = 2;
pub const LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF: u32 = 1;
pub const LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON: u32 = 2;
pub const LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF: u32 = 4;
pub const LANDLOCK_ACCESS_FS_EXECUTE: u32 = 1;
pub const LANDLOCK_ACCESS_FS_WRITE_FILE: u32 = 2;
pub const LANDLOCK_ACCESS_FS_READ_FILE: u32 = 4;
Expand Down
6 changes: 5 additions & 1 deletion src/uapi/landlock_i686.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
/* automatically generated by rust-bindgen 0.72.0 */
/* automatically generated by rust-bindgen 0.72.1 */

pub const LANDLOCK_CREATE_RULESET_VERSION: u32 = 1;
pub const LANDLOCK_CREATE_RULESET_ERRATA: u32 = 2;
pub const LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF: u32 = 1;
pub const LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON: u32 = 2;
pub const LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF: u32 = 4;
pub const LANDLOCK_ACCESS_FS_EXECUTE: u32 = 1;
pub const LANDLOCK_ACCESS_FS_WRITE_FILE: u32 = 2;
pub const LANDLOCK_ACCESS_FS_READ_FILE: u32 = 4;
Expand Down
6 changes: 5 additions & 1 deletion src/uapi/landlock_x86_64.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
/* automatically generated by rust-bindgen 0.72.0 */
/* automatically generated by rust-bindgen 0.72.1 */

pub const LANDLOCK_CREATE_RULESET_VERSION: u32 = 1;
pub const LANDLOCK_CREATE_RULESET_ERRATA: u32 = 2;
pub const LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF: u32 = 1;
pub const LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON: u32 = 2;
pub const LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF: u32 = 4;
pub const LANDLOCK_ACCESS_FS_EXECUTE: u32 = 1;
pub const LANDLOCK_ACCESS_FS_WRITE_FILE: u32 = 2;
pub const LANDLOCK_ACCESS_FS_READ_FILE: u32 = 4;
Expand Down
3 changes: 3 additions & 0 deletions src/uapi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ pub use self::landlock::{
landlock_rule_type_LANDLOCK_RULE_NET_PORT,
landlock_rule_type_LANDLOCK_RULE_PATH_BENEATH,
landlock_ruleset_attr,
LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF,
LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON,
LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF,
LANDLOCK_ACCESS_FS_EXECUTE,
LANDLOCK_ACCESS_FS_WRITE_FILE,
LANDLOCK_ACCESS_FS_READ_FILE,
Expand Down