Skip to main content

landlock/
flags.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3//! Syscall flag types for `landlock_restrict_self()` and future syscall flags.
4//!
5//! This module provides a compatibility mechanism for individual syscall flags,
6//! parallel to but simpler than the [`Access`](crate::Access) /
7//! [`TryCompat`](crate::TryCompat) machinery.
8//!
9//! Key differences from the [`Access`](crate::Access) pattern:
10//!
11//! - Access types ([`AccessFs`](crate::AccessFs), [`AccessNet`](crate::AccessNet),
12//!   [`Scope`](crate::Scope)) operate on **sets** of rights passed as
13//!   [`BitFlags<T>`](enumflags2::BitFlags), where the compat result can be Full,
14//!   Partial, or No depending on which bits are supported.  They use the
15//!   [`TryCompat`](crate::TryCompat) trait and the full
16//!   [`AccessError`](crate::AccessError) hierarchy.
17//!
18//! - Syscall flags are set **individually** through boolean setter methods
19//!   (e.g., [`log_same_exec()`](crate::RulesetCreatedAttr::log_same_exec)), so
20//!   the compat result is binary: supported or not.  There is no Partial case,
21//!   no set-level validation, and no need for an
22//!   [`AccessError`](crate::AccessError)-like hierarchy.  The
23//!   [`try_compat()`](SyscallFlagExt::try_compat) default method on
24//!   [`SyscallFlagExt`] handles the compat dispatch directly, using
25//!   [`Compatibility::try_compat_binary()`](crate::compat::Compatibility::try_compat_binary)
26//!   factored out from the No branch of
27//!   [`TryCompat::try_compat()`](crate::TryCompat::try_compat).
28//!
29//! - Access types implement the [`Access`](crate::Access) trait (which requires
30//!   [`BitFlag`](enumflags2::BitFlag)) and are used with
31//!   [`BitFlags<T>`](enumflags2::BitFlags) throughout the builder.  Syscall
32//!   flags are plain enums (no `#[bitflags]`); the raw UAPI bitmask is built
33//!   internally by OR-ing the constants returned by
34//!   [`raw_bit()`](SyscallFlagExt::raw_bit) into a `u32`.
35
36use crate::compat::{Compatibility, ABI};
37use crate::errors::SyscallFlagError;
38use crate::private;
39use crate::uapi;
40
41/// Marker trait for syscall flag types used in compatibility checks.
42///
43/// This is the syscall-flag equivalent of the [`Access`](crate::Access) trait,
44/// but without the [`BitFlag`](enumflags2::BitFlag) requirement since syscall
45/// flags are set individually rather than as sets.
46///
47/// This trait is sealed and cannot be implemented outside of this crate.
48pub trait SyscallFlag: Copy + core::fmt::Debug + private::Sealed {}
49
50/// Internal extension providing compatibility logic for syscall flags.
51///
52/// This is the syscall-flag equivalent of [`TryCompat`](crate::TryCompat),
53/// simplified for single-flag boolean setters where the compat result is
54/// binary (supported or not) rather than Full/Partial/No.
55///
56/// Implementors provide [`default_value()`](Self::default_value),
57/// [`raw_bit()`](Self::raw_bit), and [`since()`](Self::since); the
58/// [`try_compat()`](Self::try_compat) default method handles the compat
59/// state update and level dispatch.
60pub(crate) trait SyscallFlagExt: SyscallFlag {
61    /// Returns the kernel's default state for this flag.
62    fn default_value(self) -> bool;
63
64    /// Returns the raw UAPI constant for this flag.
65    fn raw_bit(self) -> u32;
66
67    /// Returns the minimum ABI version that supports this flag.
68    fn since(self) -> ABI;
69
70    /// Checks compatibility and returns whether the non-default bit should
71    /// be applied.
72    ///
73    /// Returns `Ok(true)` if [`raw_bit()`](Self::raw_bit) should be set in
74    /// the actual flags.  Returns `Ok(false)` if the flag should be cleared
75    /// to its default state, either because `set == default_value()` or
76    /// because the kernel does not support the flag and the compat level
77    /// permits dropping it.  Returns `Err` if the flag is unsupported with
78    /// [`CompatLevel::HardRequirement`](crate::CompatLevel::HardRequirement).
79    ///
80    /// Setting to the default value never requires a compat check.
81    ///
82    /// This mirrors the compat dispatch in
83    /// [`TryCompat::try_compat()`](crate::TryCompat::try_compat) but without
84    /// the Partial case (a single flag is either fully supported or not).
85    fn try_compat(
86        self,
87        set: bool,
88        compat: &mut Compatibility,
89    ) -> Result<bool, SyscallFlagError<Self>> {
90        if set == self.default_value() {
91            // Setting to the default value is always safe; no compat check
92            // needed and the caller should clear the bit.
93            return Ok(false);
94        }
95        compat.try_compat_binary(compat.abi() >= self.since(), || {
96            SyscallFlagError::NotSupported { flag: self, set }
97        })
98    }
99}
100
101/// Identifies a configuration flag for the `landlock_restrict_self()` syscall.
102///
103/// Unlike access rights ([`AccessFs`](crate::AccessFs), [`AccessNet`](crate::AccessNet))
104/// and scopes ([`Scope`](crate::Scope)), these flags are not passed to
105/// `landlock_create_ruleset()` but to `landlock_restrict_self()`.  They control
106/// audit logging behavior rather than access restrictions.
107///
108/// Each flag is set through a dedicated boolean method on
109/// [`RulesetCreatedAttr`](crate::RulesetCreatedAttr) or
110/// [`RestrictSelfAttr`](crate::RestrictSelfAttr).  The polarity mapping
111/// (e.g., `log_same_exec(false)` maps to `LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF`)
112/// is handled internally.
113#[derive(Copy, Clone, Debug, PartialEq, Eq)]
114#[non_exhaustive]
115// All variants intentionally share the "Log" prefix to match the kernel's
116// LANDLOCK_RESTRICT_SELF_LOG_* naming convention.  Future non-logging flags
117// (e.g., TSYNC) will break the shared prefix, removing the need for this allow.
118#[allow(clippy::enum_variant_names)]
119pub enum RestrictSelfFlag {
120    /// Same-exec logging (see [`RulesetCreatedAttr::log_same_exec()`](crate::RulesetCreatedAttr::log_same_exec)).
121    LogSameExec,
122    /// New-exec logging (see [`RulesetCreatedAttr::log_new_exec()`](crate::RulesetCreatedAttr::log_new_exec)).
123    LogNewExec,
124    /// Subdomain logging (see [`RestrictSelfAttr::log_subdomains()`](crate::RestrictSelfAttr::log_subdomains)).
125    LogSubdomains,
126}
127
128impl SyscallFlag for RestrictSelfFlag {}
129
130impl RestrictSelfFlag {
131    /// Returns the effective state of this flag given a raw bitmask of
132    /// applied flags.
133    pub(crate) fn is_set(self, raw_flags: u32) -> bool {
134        // Each flag's raw_bit() returns either an OFF or ON UAPI constant
135        // depending on the kernel's default for that flag.  When the bit
136        // is set in raw_flags, the kernel applies the opposite of the
137        // default; when unset, the default applies.
138        if raw_flags & self.raw_bit() != 0 {
139            !self.default_value()
140        } else {
141            self.default_value()
142        }
143    }
144}
145
146impl SyscallFlagExt for RestrictSelfFlag {
147    fn default_value(self) -> bool {
148        match self {
149            // Same-exec logging is enabled by default.
150            Self::LogSameExec => true,
151            // New-exec logging is disabled by default.
152            Self::LogNewExec => false,
153            // Subdomain logging is enabled by default.
154            Self::LogSubdomains => true,
155        }
156    }
157
158    fn raw_bit(self) -> u32 {
159        match self {
160            Self::LogSameExec => uapi::LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF,
161            Self::LogNewExec => uapi::LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON,
162            Self::LogSubdomains => uapi::LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF,
163        }
164    }
165
166    fn since(self) -> ABI {
167        match self {
168            Self::LogSameExec => ABI::V7,
169            Self::LogNewExec => ABI::V7,
170            Self::LogSubdomains => ABI::V7,
171        }
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178    use crate::uapi;
179
180    #[test]
181    fn restrict_self_flag_raw_bit() {
182        assert_eq!(
183            RestrictSelfFlag::LogSameExec.raw_bit(),
184            uapi::LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF,
185        );
186        assert_eq!(
187            RestrictSelfFlag::LogNewExec.raw_bit(),
188            uapi::LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON,
189        );
190        assert_eq!(
191            RestrictSelfFlag::LogSubdomains.raw_bit(),
192            uapi::LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF,
193        );
194    }
195
196    #[test]
197    fn restrict_self_flag_default_value() {
198        assert!(RestrictSelfFlag::LogSameExec.default_value());
199        assert!(!RestrictSelfFlag::LogNewExec.default_value());
200        assert!(RestrictSelfFlag::LogSubdomains.default_value());
201    }
202
203    #[test]
204    fn restrict_self_flag_since() {
205        assert_eq!(RestrictSelfFlag::LogSameExec.since(), ABI::V7);
206        assert_eq!(RestrictSelfFlag::LogNewExec.since(), ABI::V7);
207        assert_eq!(RestrictSelfFlag::LogSubdomains.since(), ABI::V7);
208    }
209}