/rust/registry/src/index.crates.io-1949cf8c6b5b557f/landlock-0.4.5/src/flags.rs
Line | Count | Source |
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 | | |
36 | | use crate::compat::{Compatibility, ABI}; |
37 | | use crate::errors::SyscallFlagError; |
38 | | use crate::private; |
39 | | use 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. |
48 | | pub 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. |
60 | | pub(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 | 0 | fn try_compat( |
86 | 0 | self, |
87 | 0 | set: bool, |
88 | 0 | compat: &mut Compatibility, |
89 | 0 | ) -> Result<bool, SyscallFlagError<Self>> { |
90 | 0 | 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 | 0 | return Ok(false); |
94 | 0 | } |
95 | 0 | compat.try_compat_binary(compat.abi() >= self.since(), || { |
96 | 0 | SyscallFlagError::NotSupported { flag: self, set } |
97 | 0 | }) |
98 | 0 | } |
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)] |
119 | | pub 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 | | |
128 | | impl SyscallFlag for RestrictSelfFlag {} |
129 | | |
130 | | impl RestrictSelfFlag { |
131 | | /// Returns the effective state of this flag given a raw bitmask of |
132 | | /// applied flags. |
133 | 0 | 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 | 0 | if raw_flags & self.raw_bit() != 0 { |
139 | 0 | !self.default_value() |
140 | | } else { |
141 | 0 | self.default_value() |
142 | | } |
143 | 0 | } |
144 | | } |
145 | | |
146 | | impl SyscallFlagExt for RestrictSelfFlag { |
147 | 0 | fn default_value(self) -> bool { |
148 | 0 | match self { |
149 | | // Same-exec logging is enabled by default. |
150 | 0 | Self::LogSameExec => true, |
151 | | // New-exec logging is disabled by default. |
152 | 0 | Self::LogNewExec => false, |
153 | | // Subdomain logging is enabled by default. |
154 | 0 | Self::LogSubdomains => true, |
155 | | } |
156 | 0 | } |
157 | | |
158 | 0 | fn raw_bit(self) -> u32 { |
159 | 0 | match self { |
160 | 0 | Self::LogSameExec => uapi::LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF, |
161 | 0 | Self::LogNewExec => uapi::LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON, |
162 | 0 | Self::LogSubdomains => uapi::LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF, |
163 | | } |
164 | 0 | } |
165 | | |
166 | 0 | fn since(self) -> ABI { |
167 | 0 | match self { |
168 | 0 | Self::LogSameExec => ABI::V7, |
169 | 0 | Self::LogNewExec => ABI::V7, |
170 | 0 | Self::LogSubdomains => ABI::V7, |
171 | | } |
172 | 0 | } |
173 | | } |
174 | | |
175 | | #[cfg(test)] |
176 | | mod 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 | | } |