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 and thread synchronization rather than access
107/// restrictions.
108///
109/// Each flag is set through a dedicated boolean method on
110/// [`RulesetCreatedAttr`](crate::RulesetCreatedAttr) or
111/// [`RestrictSelfAttr`](crate::RestrictSelfAttr). The polarity mapping
112/// (e.g., `log_same_exec(false)` maps to `LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF`)
113/// is handled internally.
114#[derive(Copy, Clone, Debug, PartialEq, Eq)]
115#[non_exhaustive]
116pub enum RestrictSelfFlag {
117 /// Same-exec logging (see [`RulesetCreatedAttr::log_same_exec()`](crate::RulesetCreatedAttr::log_same_exec)).
118 LogSameExec,
119 /// New-exec logging (see [`RulesetCreatedAttr::log_new_exec()`](crate::RulesetCreatedAttr::log_new_exec)).
120 LogNewExec,
121 /// Subdomain logging (see [`RestrictSelfAttr::log_subdomains()`](crate::RestrictSelfAttr::log_subdomains)).
122 LogSubdomains,
123 /// Apply the restriction to all threads of the process (see
124 /// [`RestrictSelfAttr::all_threads()`](crate::RestrictSelfAttr::all_threads)).
125 AllThreads,
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 // Only the calling thread is restricted by default.
156 Self::AllThreads => false,
157 }
158 }
159
160 fn raw_bit(self) -> u32 {
161 match self {
162 Self::LogSameExec => uapi::LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF,
163 Self::LogNewExec => uapi::LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON,
164 Self::LogSubdomains => uapi::LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF,
165 Self::AllThreads => uapi::LANDLOCK_RESTRICT_SELF_TSYNC,
166 }
167 }
168
169 fn since(self) -> ABI {
170 match self {
171 Self::LogSameExec => ABI::V7,
172 Self::LogNewExec => ABI::V7,
173 Self::LogSubdomains => ABI::V7,
174 Self::AllThreads => ABI::V8,
175 }
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use crate::uapi;
183
184 #[test]
185 fn restrict_self_flag_raw_bit() {
186 assert_eq!(
187 RestrictSelfFlag::LogSameExec.raw_bit(),
188 uapi::LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF,
189 );
190 assert_eq!(
191 RestrictSelfFlag::LogNewExec.raw_bit(),
192 uapi::LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON,
193 );
194 assert_eq!(
195 RestrictSelfFlag::LogSubdomains.raw_bit(),
196 uapi::LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF,
197 );
198 assert_eq!(
199 RestrictSelfFlag::AllThreads.raw_bit(),
200 uapi::LANDLOCK_RESTRICT_SELF_TSYNC,
201 );
202 }
203
204 #[test]
205 fn restrict_self_flag_default_value() {
206 assert!(RestrictSelfFlag::LogSameExec.default_value());
207 assert!(!RestrictSelfFlag::LogNewExec.default_value());
208 assert!(RestrictSelfFlag::LogSubdomains.default_value());
209 assert!(!RestrictSelfFlag::AllThreads.default_value());
210 }
211
212 #[test]
213 fn restrict_self_flag_since() {
214 assert_eq!(RestrictSelfFlag::LogSameExec.since(), ABI::V7);
215 assert_eq!(RestrictSelfFlag::LogNewExec.since(), ABI::V7);
216 assert_eq!(RestrictSelfFlag::LogSubdomains.since(), ABI::V7);
217 assert_eq!(RestrictSelfFlag::AllThreads.since(), ABI::V8);
218 }
219}