Skip to main content

starnix_core/security/
hooks.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Security hooks mediating operations across the Starnix kernel.
6//!
7//! Hooks in this module are based on the Linux Security Modules (LSM)
8//! documentation. Signatures and semantics follow LSM specifications,
9//! adapted where necessary for Starnix architectural differences:
10//!
11//! - Explicit execution context: Hooks receive explicit [`CurrentTask`] or
12//!   [`Task`] references rather than accessing ambient state.
13//! - Idiomatic error handling: Hooks return [`Result`] (with [`Errno`])
14//!   rather than integer status codes.
15//! - Object lifetimes and ownership: Parameters use Starnix types
16//!   ([`Credentials`], [`FsNode`], [`FileObject`]) integrated with Starnix
17//!   ownership, locking, and reference counting.
18//!
19//! ## Hook Composition Patterns
20//!
21//! Hooks dispatch to security modules using four common LSM patterns:
22//!
23//! - **Sequential fail-closed**: Access checks query each active module in turn
24//!   ([`yama`], [`common_cap`], [`selinux_hooks`]), denying on first error
25//!   (e.g., [`ptrace_access_check`], [`ptrace_traceme`]).
26//! - **Primary LSM**: Labeling, policy decisions, and lifecycle dispatch only
27//!   to the primary module (SELinux), defaulting to a permissive no-op when
28//!   unconfigured (e.g., [`binder_transaction`], [`bprm_creds_for_exec`]).
29//! - **First-responder matching**: Attribute-specific hooks (such as security
30//!   xattrs) query modules until one claims the prefix, falling back to VFS
31//!   operations (e.g., [`fs_node_getsecurity`], [`fs_node_setsecurity`]).
32//! - **Lifecycle broadcasts**: State initialization and cleanup notify all
33//!   active modules unconditionally (e.g., [`socket_post_create`]).
34
35// TODO(https://github.com/rust-lang/rust/issues/39371): remove
36#![allow(non_upper_case_globals)]
37
38use super::selinux_hooks::audit::Auditable;
39use super::{
40    BpfMapState, BpfProgState, FileObjectState, FileSystemState, KernelState, PerfEventState,
41    common_cap, selinux_hooks, yama,
42};
43use crate::mm::{Mapping, MappingOptions, ProtectionFlags};
44use crate::perf::PerfEventFile;
45use crate::security::selinux_hooks::current_task_state;
46use crate::task::loader::ResolvedProgram;
47use crate::task::{CurrentTask, Kernel, Task};
48use crate::vfs::fs_args::MountParams;
49use crate::vfs::socket::{
50    Socket, SocketAddress, SocketDomain, SocketFile, SocketPeer, SocketProtocol,
51    SocketShutdownFlags, SocketType,
52};
53use crate::vfs::{
54    DirEntryHandle, DowncastedFile, FileHandle, FileObject, FileSystem, FileSystemHandle,
55    FileSystemOps, FsNode, FsStr, FsString, Mount, NamespaceNode, ValueOrSize, XattrOp,
56};
57use ebpf::MapFlags;
58use linux_uapi::{
59    perf_type_id, perf_type_id_PERF_TYPE_BREAKPOINT, perf_type_id_PERF_TYPE_HARDWARE,
60    perf_type_id_PERF_TYPE_HW_CACHE, perf_type_id_PERF_TYPE_RAW, perf_type_id_PERF_TYPE_SOFTWARE,
61    perf_type_id_PERF_TYPE_TRACEPOINT,
62};
63use selinux::{FileSystemMountOptions, InitialSid, SecurityPermission, SecurityServer, TaskAttrs};
64use starnix_logging::{CATEGORY_STARNIX_SECURITY, log_debug};
65use starnix_uapi::arc_key::WeakKey;
66use starnix_uapi::auth::{Credentials, PtraceAccessMode};
67use starnix_uapi::device_id::DeviceId;
68use starnix_uapi::errors::Errno;
69use starnix_uapi::file_mode::{Access, FileMode};
70use starnix_uapi::inotify_mask::InotifyMask;
71use starnix_uapi::mount_flags::MountFlags;
72use starnix_uapi::open_flags::OpenFlags;
73use starnix_uapi::signals::Signal;
74use starnix_uapi::syslog::SyslogAction;
75use starnix_uapi::unmount_flags::UnmountFlags;
76use starnix_uapi::user_address::UserAddress;
77use starnix_uapi::{bpf_cmd, error, rlimit};
78use std::ops::Range;
79use std::sync::Arc;
80use syncio::zxio_node_attr_has_t;
81use zerocopy::FromBytes;
82
83macro_rules! track_hook_duration {
84    ($cname:literal) => {
85        fuchsia_trace::duration!(CATEGORY_STARNIX_SECURITY, $cname);
86    };
87}
88
89/// Returns true if `socket` is backed by a kernel-private [`FsNode`].
90fn is_private_socket(socket: &Socket) -> bool {
91    socket.fs_node().is_some_and(|fs_node| fs_node.is_private())
92}
93
94bitflags::bitflags! {
95    /// The flags about which permissions should be checked when opening an FsNode. Used in the
96    /// `fs_node_permission()` hook.
97    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
98    pub struct PermissionFlags: u32 {
99        const EXEC = 1 as u32;
100        const WRITE = 2 as u32;
101        const READ = 4 as u32;
102        const APPEND = 8 as u32;
103
104        // Internal flag used to indicate that the check is being made on behalf of userspace e.g.
105        // via the `access()` syscall.
106        const ACCESS = 16 as u32;
107    }
108}
109
110impl PermissionFlags {
111    pub fn as_access(&self) -> Access {
112        let mut access = Access::empty();
113        if self.contains(PermissionFlags::READ) {
114            access |= Access::READ;
115        }
116        if self.contains(PermissionFlags::WRITE) {
117            // `APPEND` only modifies the behaviour of `WRITE` if set, so it is sufficient to only
118            // consider whether `WRITE` is set, to calculate the `Access` flags.
119            access |= Access::WRITE;
120        }
121        if self.contains(PermissionFlags::EXEC) {
122            access |= Access::EXEC;
123        }
124        access
125    }
126}
127
128impl From<Access> for PermissionFlags {
129    fn from(access: Access) -> Self {
130        // Note that `Access` doesn't have an `append` bit.
131        let mut permissions = PermissionFlags::empty();
132        if access.contains(Access::READ) {
133            permissions |= PermissionFlags::READ;
134        }
135        if access.contains(Access::WRITE) {
136            permissions |= PermissionFlags::WRITE;
137        }
138        if access.contains(Access::EXEC) {
139            permissions |= PermissionFlags::EXEC;
140        }
141        permissions
142    }
143}
144
145impl From<ProtectionFlags> for PermissionFlags {
146    fn from(protection_flags: ProtectionFlags) -> Self {
147        let mut flags = PermissionFlags::empty();
148        if protection_flags.contains(ProtectionFlags::READ) {
149            flags |= PermissionFlags::READ;
150        }
151        if protection_flags.contains(ProtectionFlags::WRITE) {
152            flags |= PermissionFlags::WRITE;
153        }
154        if protection_flags.contains(ProtectionFlags::EXEC) {
155            flags |= PermissionFlags::EXEC;
156        }
157        flags
158    }
159}
160
161impl From<OpenFlags> for PermissionFlags {
162    fn from(flags: OpenFlags) -> Self {
163        let mut permissions = PermissionFlags::empty();
164        if flags.can_read() {
165            permissions |= PermissionFlags::READ;
166        }
167        if flags.can_write() {
168            permissions |= PermissionFlags::WRITE;
169            if flags.contains(OpenFlags::APPEND) {
170                permissions |= PermissionFlags::APPEND;
171            }
172        }
173        permissions
174    }
175}
176
177impl From<MapFlags> for PermissionFlags {
178    fn from(bpf_flags: MapFlags) -> Self {
179        if bpf_flags.contains(MapFlags::SyscallReadOnly) {
180            PermissionFlags::READ
181        } else if bpf_flags.contains(MapFlags::SyscallWriteOnly) {
182            PermissionFlags::WRITE
183        } else {
184            PermissionFlags::READ | PermissionFlags::WRITE
185        }
186    }
187}
188
189/// The flags about the PerfEvent types.
190#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
191pub enum PerfEventType {
192    Hardware,
193    Software,
194    Tracepoint,
195    Raw,
196    HwCache,
197    Breakpoint,
198}
199
200// TODO(https://github.com/rust-lang/rust/issues/39371): remove
201#[allow(non_upper_case_globals)]
202impl TryFrom<perf_type_id> for PerfEventType {
203    type Error = Errno;
204
205    fn try_from(type_id: perf_type_id) -> Result<Self, Errno> {
206        match type_id {
207            perf_type_id_PERF_TYPE_HARDWARE => Ok(Self::Hardware),
208            perf_type_id_PERF_TYPE_SOFTWARE => Ok(Self::Software),
209            perf_type_id_PERF_TYPE_TRACEPOINT => Ok(Self::Tracepoint),
210            perf_type_id_PERF_TYPE_RAW => Ok(Self::Raw),
211            perf_type_id_PERF_TYPE_HW_CACHE => Ok(Self::HwCache),
212            perf_type_id_PERF_TYPE_BREAKPOINT => Ok(Self::Breakpoint),
213            _ => {
214                return error!(ENOTSUP);
215            }
216        }
217    }
218}
219
220/// The target of a `perf_event_open()` call.
221#[derive(PartialEq, Eq)]
222pub enum PerfEventTarget<'a> {
223    /// Monitor all tasks/activities.
224    AllTasks,
225    /// Only monitor the current task.
226    CurrentTask,
227    /// Only monitor a specific task.
228    Task(&'a Task),
229}
230
231/// Identifies which stage of the `perf_event_open()` syscall a permission check is being made
232/// for. The syscall calls the `check_perf_event_open_access` hook once for each stage that
233/// applies to the requested event.
234#[derive(Clone, Copy, Debug, PartialEq, Eq)]
235pub enum PerfEventOpenType {
236    /// Opening any perf_event.
237    Open,
238    /// Monitoring CPU-wide activity.
239    Cpu,
240    /// Monitoring kernel activity.
241    Kernel,
242    /// Monitoring tracepoint activity.
243    Tracepoint,
244}
245
246/// Executes the `hook` closure if SELinux is enabled, and has a policy loaded.
247/// If SELinux is not enabled, or has no policy loaded, then the `default` closure is executed,
248/// and its result returned.
249fn if_selinux_else<F, R, D>(task: &Task, hook: F, default: D) -> R
250where
251    F: FnOnce(&Arc<SecurityServer>) -> R,
252    D: Fn() -> R,
253{
254    if let Some(state) = task.kernel().security_state.state.as_ref() {
255        if state.has_policy() { hook(&state.server) } else { default() }
256    } else {
257        default()
258    }
259}
260
261/// Specialization of `if_selinux_else(...)` for hooks which return a `Result<..., Errno>`, that
262/// arranges to return a default `Ok(...)` result value if SELinux is not enabled, or not yet
263/// configured with a policy.
264fn if_selinux_else_default_ok<R, F>(task: &Task, hook: F) -> Result<R, Errno>
265where
266    F: FnOnce(&Arc<SecurityServer>) -> Result<R, Errno>,
267    R: Default,
268{
269    if_selinux_else(task, hook, || Ok(R::default()))
270}
271
272/// Returns the security state structure for the kernel, based on the supplied "selinux" argument
273/// contents.
274pub fn kernel_init_security(
275    enabled: bool,
276    options: String,
277    exceptions: Vec<String>,
278    inspect_node: &fuchsia_inspect::Node,
279) -> KernelState {
280    track_hook_duration!("security.hooks.kernel_init_security");
281    KernelState {
282        state: enabled
283            .then(|| selinux_hooks::kernel_init_security(options, exceptions, inspect_node)),
284    }
285}
286
287/// Checks whether the given `current_task` can become the binder context manager.
288/// Corresponds to the `binder_set_context_mgr` hook.
289pub fn binder_set_context_mgr(
290    current_task: &CurrentTask,
291    context_mgr_creds: &Credentials,
292) -> Result<(), Errno> {
293    track_hook_duration!("security.hooks.binder_set_context_mgr");
294    if_selinux_else_default_ok(current_task, |security_server| {
295        selinux_hooks::binder::binder_set_context_mgr(
296            security_server,
297            current_task,
298            context_mgr_creds,
299        )
300    })
301}
302
303/// Checks whether the given `current_task` can perform a transaction from `source_creds` to `target_creds`.
304/// Corresponds to the `binder_transaction` hook.
305pub fn binder_transaction(
306    current_task: &CurrentTask,
307    source_creds: &Credentials,
308    target_creds: &Credentials,
309) -> Result<(), Errno> {
310    track_hook_duration!("security.hooks.binder_transaction");
311    if_selinux_else_default_ok(current_task, |security_server| {
312        selinux_hooks::binder::binder_transaction(
313            security_server,
314            current_task,
315            source_creds,
316            target_creds,
317        )
318    })
319}
320
321/// Checks whether the given `current_task` can transfer Binder objects from `source_creds` to `target_creds`.
322/// Corresponds to the `binder_transfer_binder` hook.
323pub fn binder_transfer_binder(
324    current_task: &CurrentTask,
325    source_creds: &Credentials,
326    target_creds: &Credentials,
327) -> Result<(), Errno> {
328    track_hook_duration!("security.hooks.binder_transfer_binder");
329    if_selinux_else_default_ok(current_task, |security_server| {
330        selinux_hooks::binder::binder_transfer_binder(
331            security_server,
332            current_task,
333            source_creds,
334            target_creds,
335        )
336    })
337}
338
339/// Checks whether the receiving process with `target_creds` can receive `file` in a Binder transaction.
340/// Corresponds to the `binder_transfer_file` hook.
341pub fn binder_transfer_file(
342    current_task: &CurrentTask,
343    target_creds: &Credentials,
344    file: &FileObject,
345) -> Result<(), Errno> {
346    track_hook_duration!("security.hooks.binder_transfer_file");
347    if_selinux_else_default_ok(current_task, |security_server| {
348        selinux_hooks::binder::binder_transfer_file(
349            security_server,
350            current_task,
351            target_creds,
352            file,
353        )
354    })
355}
356
357/// Returns the serialized, NUL-terminated Security Context associated with the specified
358/// credentials, or an empty NUL-terminated string (`b"\0"`) if SELinux is not active.
359pub fn binder_get_context(current_task: &CurrentTask, source_creds: &Credentials) -> FsString {
360    track_hook_duration!("security.hooks.binder_get_context");
361    if_selinux_else(
362        current_task,
363        |security_server| {
364            selinux_hooks::binder::binder_get_context(&security_server, source_creds).into()
365        },
366        || FsString::from(b"\0"),
367    )
368}
369
370/// Consumes the mount options from the supplied `MountParams` and returns the security mount
371/// options for the given `MountParams`.
372/// Corresponds to the `sb_eat_lsm_opts` hook.
373pub fn sb_eat_lsm_opts(
374    kernel: &Kernel,
375    mount_params: &mut MountParams,
376) -> Result<FileSystemMountOptions, Errno> {
377    track_hook_duration!("security.hooks.sb_eat_lsm_opts");
378    if kernel.security_state.state.is_some() {
379        return selinux_hooks::superblock::sb_eat_lsm_opts(mount_params);
380    }
381    Ok(FileSystemMountOptions::default())
382}
383
384/// Returns security state to associate with a filesystem based on the supplied mount options.
385/// This sits somewhere between `fs_context_parse_param()` and `sb_set_mnt_opts()` in function.
386pub fn file_system_init_security(
387    mount_options: &FileSystemMountOptions,
388    ops: &dyn FileSystemOps,
389) -> Result<FileSystemState, Errno> {
390    track_hook_duration!("security.hooks.file_system_init_security");
391    Ok(FileSystemState {
392        state: selinux_hooks::superblock::file_system_init_security(mount_options, ops)?,
393    })
394}
395
396/// Gives the hooks subsystem an opportunity to note that the new `file_system` needs labeling, if
397/// SELinux is enabled, but no policy has yet been loaded.
398// TODO: https://fxbug.dev/366405587 - Merge this logic into `file_system_resolve_security()` and
399// remove this extra hook.
400pub fn file_system_post_init_security(kernel: &Kernel, file_system: &FileSystemHandle) {
401    track_hook_duration!("security.hooks.file_system_post_init_security");
402    if let Some(state) = &kernel.security_state.state {
403        if !state.has_policy() {
404            // TODO: https://fxbug.dev/367585803 - Revise locking to guard against a policy load
405            // sneaking in, in-between `has_policy()` and this `insert()`.
406            log_debug!("Queuing {} FileSystem for labeling", file_system.name());
407            state.pending_file_systems.lock().insert(WeakKey::from(&file_system));
408        }
409    }
410}
411
412/// Resolves the labeling scheme and arguments for the `file_system`, based on the loaded policy.
413/// If no policy has yet been loaded then no work is done, and the `file_system` will instead be
414/// labeled when a policy is first loaded.
415/// If the `file_system` was already labeled then no further work is done.
416pub fn file_system_resolve_security(
417    current_task: &CurrentTask,
418    file_system: &FileSystemHandle,
419) -> Result<(), Errno> {
420    track_hook_duration!("security.hooks.file_system_resolve_security");
421    if_selinux_else_default_ok(current_task, |security_server| {
422        selinux_hooks::superblock::file_system_resolve_security(
423            security_server,
424            current_task,
425            file_system,
426        )
427    })
428}
429
430/// Used to return an extended attribute name and value to apply to a [`crate::vfs::FsNode`].
431pub struct FsNodeSecurityXattr {
432    pub name: &'static FsStr,
433    pub value: FsString,
434}
435
436/// Checks whether the `current_task` is allowed to mmap `file` or memory using the given
437/// [`ProtectionFlags`] and [`MappingOptions`].
438/// Corresponds to the `mmap_file()` LSM hook.
439pub fn mmap_file(
440    current_task: &CurrentTask,
441    file: Option<&FileHandle>,
442    protection_flags: ProtectionFlags,
443    options: MappingOptions,
444) -> Result<(), Errno> {
445    track_hook_duration!("security.hooks.mmap_file");
446    if_selinux_else_default_ok(current_task, |security_server| {
447        selinux_hooks::file::mmap_file(
448            security_server,
449            current_task,
450            file,
451            protection_flags,
452            options,
453        )
454    })
455}
456
457/// Checks whether `current_task` is allowed to request setting the memory protection of
458/// `mapping` to `prot`.
459/// Corresponds to the `file_mprotect` LSM hook.
460pub fn file_mprotect(
461    current_task: &CurrentTask,
462    range: &Range<UserAddress>,
463    mapping: &Mapping,
464    prot: ProtectionFlags,
465) -> Result<(), Errno> {
466    track_hook_duration!("security.hooks.file_mprotect");
467    if_selinux_else_default_ok(current_task, |security_server| {
468        selinux_hooks::file::file_mprotect(security_server, current_task, range, mapping, prot)
469    })
470}
471
472/// Checks whether the `current_task` has the specified `permission_flags` to the `file`.
473/// Corresponds to the `file_permission()` LSM hook.
474pub fn file_permission(
475    current_task: &CurrentTask,
476    file: &FileObject,
477    permission_flags: PermissionFlags,
478) -> Result<(), Errno> {
479    track_hook_duration!("security.hooks.file_permission");
480    if_selinux_else_default_ok(current_task, |security_server| {
481        selinux_hooks::file::file_permission(security_server, current_task, file, permission_flags)
482    })
483}
484
485/// Checks whether the `current_task` is allowed to open `file`.
486/// Corresponds to the `file_open()` LSM hook.
487pub fn file_open(current_task: &CurrentTask, file: &FileObject) -> Result<(), Errno> {
488    track_hook_duration!("security.hooks.file_open");
489    if file.node().is_private() {
490        return Ok(());
491    }
492    if_selinux_else_default_ok(current_task, |security_server| {
493        selinux_hooks::file::file_open(security_server, current_task, file)
494    })
495}
496
497/// Called by the VFS to initialize the security state for an `FsNode` that is being linked at
498/// `dir_entry`.
499/// If the `FsNode` security state had already been initialized, or no policy is yet loaded, then
500/// this is a no-op.
501/// Corresponds to the `d_instantiate()` LSM hook.
502pub fn fs_node_init_with_dentry(
503    current_task: &CurrentTask,
504    dir_entry: &DirEntryHandle,
505) -> Result<(), Errno> {
506    track_hook_duration!("security.hooks.fs_node_init_with_dentry");
507    if dir_entry.node.is_private() {
508        return Ok(());
509    }
510    // TODO: https://fxbug.dev/367585803 - Don't use `if_selinux_else()` here, because the `has_policy()`
511    // check is racey, so doing non-trivial work in the "else" path is unsafe. Instead, call the SELinux
512    // hook implementation, and let it label, or queue, the `FsNode` based on the `FileSystem` label
513    // state, thereby ensuring safe ordering.
514    if let Some(state) = &current_task.kernel().security_state.state {
515        selinux_hooks::fs_node::fs_node_init_with_dentry(
516            &state.server,
517            current_task,
518            dir_entry,
519            /* read_xaddr = */
520            true,
521        )
522    } else {
523        Ok(())
524    }
525}
526
527pub fn fs_node_init_with_dentry_no_xattr(
528    current_task: &CurrentTask,
529    dir_entry: &DirEntryHandle,
530) -> Result<(), Errno> {
531    track_hook_duration!("security.hooks.fs_node_init_with_dentry_no_xattr");
532    if dir_entry.node.is_private() {
533        return Ok(());
534    }
535    // TODO: https://fxbug.dev/367585803 - Don't use `if_selinux_else()` here, because the `has_policy()`
536    // check is racey, so doing non-trivial work in the "else" path is unsafe. Instead, call the SELinux
537    // hook implementation, and let it label, or queue, the `FsNode` based on the `FileSystem` label
538    // state, thereby ensuring safe ordering.
539    if let Some(state) = &current_task.kernel().security_state.state {
540        selinux_hooks::fs_node::fs_node_init_with_dentry(
541            &state.server,
542            current_task,
543            dir_entry,
544            /* read_xaddr = */
545            false,
546        )
547    } else {
548        Ok(())
549    }
550}
551
552// Temporary work-around for lack of a `CurrentTask` during creation of `DirEntry`s for some initial
553// file-systems.
554// TODO: https://fxbug.dev/455771186 - Clean up with-DirEntry initialization and remove this.
555pub fn fs_node_init_with_dentry_deferred(kernel: &Kernel, dir_entry: &DirEntryHandle) {
556    track_hook_duration!("security.hooks.fs_node_init_with_dentry_no_xattr");
557    if dir_entry.node.is_private() {
558        return;
559    }
560    if kernel.security_state.state.is_some() {
561        selinux_hooks::fs_node::fs_node_init_with_dentry_deferred(dir_entry);
562    }
563}
564
565/// Applies the given label to the given node without checking any permissions.
566/// Used by file-system implementations to set the label for a node, for example when it has
567/// prefetched the label in the xattr rather than letting it get fetched by
568/// `fs_node_init_with_dentry` later. Calling this doesn't need to exclude the use of
569/// `fs_node_init_with_dentry`, it will just turn that call into a fast no-op.
570/// Corresponds to the `inode_notifysecctx` LSM hook.
571pub fn fs_node_notify_security_context(
572    current_task: &CurrentTask,
573    fs_node: &FsNode,
574    context: &FsStr,
575) -> Result<(), Errno> {
576    track_hook_duration!("security.hooks.fs_node_notify_security_context");
577    if fs_node.is_private() {
578        return Ok(());
579    }
580    if_selinux_else(
581        current_task,
582        |security_server| {
583            selinux_hooks::fs_node::fs_node_notify_security_context(
584                security_server,
585                fs_node,
586                context,
587            )
588        },
589        || error!(ENOTSUP),
590    )
591}
592
593/// Called by file-system implementations when creating the `FsNode` for a new file, to determine the
594/// correct label based on the `CurrentTask` and `parent` node, and the policy-defined transition
595/// rules, and to initialize the `FsNode`'s security state accordingly.
596/// If no policy has yet been loaded then this is a no-op; if the `FsNode` corresponds to an xattr-
597/// labeled file then it will receive the file-system's "default" label once a policy is loaded.
598/// Returns an extended attribute value to set on the newly-created file if the labeling scheme is
599/// `fs_use_xattr`. For other labeling schemes (e.g. `fs_use_trans`, mountpoint-labeling) a label
600/// is set on the `FsNode` security state, but no extended attribute is set nor returned.
601/// The `name` with which the new node is being created allows name-conditional `type_transition`
602/// rules to be applied when determining the label for the `new_node`.
603/// Corresponds to the `inode_init_security()` LSM hook.
604pub fn fs_node_init_on_create(
605    current_task: &CurrentTask,
606    new_node: &FsNode,
607    parent: &FsNode,
608    name: &FsStr,
609) -> Result<Option<FsNodeSecurityXattr>, Errno> {
610    track_hook_duration!("security.hooks.fs_node_init_on_create");
611    debug_assert!(!new_node.is_private());
612    debug_assert!(!parent.is_private());
613    if_selinux_else_default_ok(current_task, |security_server| {
614        selinux_hooks::fs_node::fs_node_init_on_create(
615            security_server,
616            current_task,
617            new_node,
618            Some(parent),
619            name,
620        )
621    })
622}
623
624/// Called by specialist file-system implementations before creating a new `FsNode`, to obtain the
625/// SID with which the code will be labeled, in advance.
626///
627/// The computed SID will be applied to the `fscreate_sid` field of the supplied `new_creds`, which
628/// may then be used with `CurrentTask::override_creds()` to later create the new node.
629///
630/// Corresponds to the `dentry_create_files_as()` LSM hook.
631pub fn dentry_create_files_as(
632    current_task: &CurrentTask,
633    parent: &FsNode,
634    new_node_mode: FileMode,
635    new_node_name: &FsStr,
636    new_creds: &mut Credentials,
637) -> Result<(), Errno> {
638    track_hook_duration!("security.hooks.dentry_create_files_as");
639    debug_assert!(!parent.is_private());
640    if_selinux_else_default_ok(current_task, |security_server| {
641        selinux_hooks::fs_node::dentry_create_files_as(
642            security_server,
643            current_task,
644            parent,
645            new_node_mode,
646            new_node_name,
647            new_creds,
648        )
649    })
650}
651
652/// Called on creation of anonymous [`crate::vfs::FsNode`]s. APIs that create file-descriptors that
653/// are not linked into any filesystem directory structure create anonymous nodes, labeled by this
654/// hook rather than `fs_node_init_on_create()` above.
655/// Corresponds to the `inode_init_security_anon()` LSM hook.
656pub fn fs_node_init_anon(
657    current_task: &CurrentTask,
658    new_node: &FsNode,
659    node_type: &str,
660) -> Result<(), Errno> {
661    track_hook_duration!("security.hooks.fs_node_init_anon");
662    if new_node.is_private() {
663        return Ok(());
664    }
665    if let Some(state) = current_task.kernel().security_state.state.as_ref() {
666        selinux_hooks::fs_node::fs_node_init_anon(&state.server, current_task, new_node, node_type)
667    } else {
668        Ok(())
669    }
670}
671
672/// Validate that `current_task` has permission to create a regular file in the `parent` directory,
673/// with the specified file `mode`.
674/// Corresponds to the `inode_create()` LSM hook.
675pub fn check_fs_node_create_access(
676    current_task: &CurrentTask,
677    parent: &FsNode,
678    mode: FileMode,
679    name: &FsStr,
680) -> Result<(), Errno> {
681    track_hook_duration!("security.hooks.check_fs_node_create_access");
682    debug_assert!(!parent.is_private());
683    if_selinux_else_default_ok(current_task, |security_server| {
684        selinux_hooks::fs_node::check_fs_node_create_access(
685            security_server,
686            current_task,
687            parent,
688            mode,
689            name,
690        )
691    })
692}
693
694/// Validate that `current_task` has permission to create a symlink to `old_path` in the `parent`
695/// directory.
696/// Corresponds to the `inode_symlink()` LSM hook.
697pub fn check_fs_node_symlink_access(
698    current_task: &CurrentTask,
699    parent: &FsNode,
700    name: &FsStr,
701    old_path: &FsStr,
702) -> Result<(), Errno> {
703    track_hook_duration!("security.hooks.check_fs_node_symlink_access");
704    debug_assert!(!parent.is_private());
705    if_selinux_else_default_ok(current_task, |security_server| {
706        selinux_hooks::fs_node::check_fs_node_symlink_access(
707            security_server,
708            current_task,
709            parent,
710            name,
711            old_path,
712        )
713    })
714}
715
716/// Validate that `current_task` has permission to create a new directory in the `parent` directory,
717/// with the specified file `mode`.
718/// Corresponds to the `inode_mkdir()` LSM hook.
719pub fn check_fs_node_mkdir_access(
720    current_task: &CurrentTask,
721    parent: &FsNode,
722    mode: FileMode,
723    name: &FsStr,
724) -> Result<(), Errno> {
725    track_hook_duration!("security.hooks.check_fs_node_mkdir_access");
726    debug_assert!(!parent.is_private());
727    if_selinux_else_default_ok(current_task, |security_server| {
728        selinux_hooks::fs_node::check_fs_node_mkdir_access(
729            security_server,
730            current_task,
731            parent,
732            mode,
733            name,
734        )
735    })
736}
737
738/// Validate that `current_task` has permission to create a new special file, socket or pipe, in the
739/// `parent` directory, and with the specified file `mode` and `device_id`.
740/// For consistency any calls to `mknod()` with a file `mode` specifying a regular file will be
741/// validated by `check_fs_node_create_access()` rather than by this hook.
742/// Corresponds to the `inode_mknod()` LSM hook.
743pub fn check_fs_node_mknod_access(
744    current_task: &CurrentTask,
745    parent: &FsNode,
746    mode: FileMode,
747    name: &FsStr,
748    device_id: DeviceId,
749) -> Result<(), Errno> {
750    track_hook_duration!("security.hooks.check_fs_node_mknod_access");
751    assert!(!mode.is_reg());
752    debug_assert!(!parent.is_private());
753
754    if_selinux_else_default_ok(current_task, |security_server| {
755        selinux_hooks::fs_node::check_fs_node_mknod_access(
756            security_server,
757            current_task,
758            parent,
759            mode,
760            name,
761            device_id,
762        )
763    })
764}
765
766/// Validate that `current_task` has  the permission to create a new hard link to a file.
767/// Corresponds to the `inode_link()` LSM hook.
768pub fn check_fs_node_link_access(
769    current_task: &CurrentTask,
770    parent: &FsNode,
771    child: &FsNode,
772) -> Result<(), Errno> {
773    track_hook_duration!("security.hooks.check_fs_node_link_access");
774    debug_assert!(!parent.is_private());
775    debug_assert!(!child.is_private());
776    if_selinux_else_default_ok(current_task, |security_server| {
777        selinux_hooks::fs_node::check_fs_node_link_access(
778            security_server,
779            current_task,
780            parent,
781            child,
782        )
783    })
784}
785
786/// Validate that `current_task` has the permission to remove a hard link to a file.
787/// Corresponds to the `inode_unlink()` LSM hook.
788pub fn check_fs_node_unlink_access(
789    current_task: &CurrentTask,
790    parent: &FsNode,
791    child: &FsNode,
792    name: &FsStr,
793) -> Result<(), Errno> {
794    track_hook_duration!("security.hooks.check_fs_node_unlink_access");
795    debug_assert!(!parent.is_private());
796    debug_assert!(!child.is_private());
797    if_selinux_else_default_ok(current_task, |security_server| {
798        selinux_hooks::fs_node::check_fs_node_unlink_access(
799            security_server,
800            current_task,
801            parent,
802            child,
803            name,
804        )
805    })
806}
807
808/// Validate that `current_task` has the permission to remove a directory.
809/// Corresponds to the `inode_rmdir()` LSM hook.
810pub fn check_fs_node_rmdir_access(
811    current_task: &CurrentTask,
812    parent: &FsNode,
813    child: &FsNode,
814    name: &FsStr,
815) -> Result<(), Errno> {
816    track_hook_duration!("security.hooks.check_fs_node_rmdir_access");
817    debug_assert!(!parent.is_private());
818    debug_assert!(!child.is_private());
819    if_selinux_else_default_ok(current_task, |security_server| {
820        selinux_hooks::fs_node::check_fs_node_rmdir_access(
821            security_server,
822            current_task,
823            parent,
824            child,
825            name,
826        )
827    })
828}
829
830/// Checks whether the `current_task` can rename the file or directory `moving_node`.
831/// If the rename replaces an existing node, `replaced_node` must contain a reference to the
832/// existing node.
833/// Corresponds to the `inode_rename()` LSM hook.
834pub fn check_fs_node_rename_access(
835    current_task: &CurrentTask,
836    old_parent: &FsNode,
837    moving_node: &FsNode,
838    new_parent: &FsNode,
839    replaced_node: Option<&FsNode>,
840    old_basename: &FsStr,
841    new_basename: &FsStr,
842) -> Result<(), Errno> {
843    track_hook_duration!("security.hooks.check_fs_node_rename_access");
844    debug_assert!(!old_parent.is_private());
845    debug_assert!(!moving_node.is_private());
846    debug_assert!(!new_parent.is_private());
847    debug_assert!(replaced_node.is_none_or(|node| !node.is_private()));
848    if_selinux_else_default_ok(current_task, |security_server| {
849        selinux_hooks::fs_node::check_fs_node_rename_access(
850            security_server,
851            current_task,
852            old_parent,
853            moving_node,
854            new_parent,
855            replaced_node,
856            old_basename,
857            new_basename,
858        )
859    })
860}
861
862/// Checks whether the `current_task` can read the symbolic link in `fs_node`.
863/// Corresponds to the `inode_readlink()` LSM hook.
864pub fn check_fs_node_read_link_access(
865    current_task: &CurrentTask,
866    fs_node: &FsNode,
867) -> Result<(), Errno> {
868    track_hook_duration!("security.hooks.check_fs_node_read_link_access");
869    if fs_node.is_private() {
870        return Ok(());
871    }
872    if_selinux_else_default_ok(current_task, |security_server| {
873        selinux_hooks::fs_node::check_fs_node_read_link_access(
874            security_server,
875            current_task,
876            fs_node,
877        )
878    })
879}
880
881/// Checks whether the `current_task` can access an inode.
882/// Corresponds to the `inode_permission()` LSM hook.
883pub fn fs_node_permission(
884    current_task: &CurrentTask,
885    fs_node: &FsNode,
886    permission_flags: PermissionFlags,
887    audit_context: Auditable<'_>,
888) -> Result<(), Errno> {
889    track_hook_duration!("security.hooks.fs_node_permission");
890    if fs_node.is_private() {
891        return Ok(());
892    }
893    if_selinux_else_default_ok(current_task, |security_server| {
894        selinux_hooks::fs_node::fs_node_permission(
895            security_server,
896            current_task,
897            fs_node,
898            permission_flags,
899            audit_context,
900        )
901    })
902}
903
904/// Returns whether the `current_task` can receive `file` via a socket IPC.
905/// Corresponds to the `file_receive()` LSM hook.
906pub fn file_receive(current_task: &CurrentTask, file: &FileObject) -> Result<(), Errno> {
907    track_hook_duration!("security.hooks.file_receive");
908    if_selinux_else_default_ok(current_task, |security_server| {
909        let receiving_sid = current_task_state(current_task).current_sid;
910        selinux_hooks::file::file_receive(security_server, current_task, receiving_sid, file)
911    })
912}
913
914/// Returns the security state for a new file object created by `current_task`.
915/// Corresponds to the `file_alloc_security()` LSM hook.
916pub fn file_alloc_security(current_task: &CurrentTask) -> FileObjectState {
917    track_hook_duration!("security.hooks.file_alloc_security");
918    FileObjectState { state: selinux_hooks::file::file_alloc_security(current_task) }
919}
920
921/// Returns the security context to be assigned to a BPM map object, based on the task that
922/// creates it.
923/// Corresponds to the `bpf_map_alloc_security()` LSM hook.
924pub fn bpf_map_alloc(current_task: &CurrentTask) -> BpfMapState {
925    track_hook_duration!("security.hooks.bpf_map_alloc");
926    BpfMapState { state: selinux_hooks::bpf::bpf_map_alloc(current_task) }
927}
928
929/// Returns the security context to be assigned to a BPM program object, based on the task
930/// that creates it.
931/// Corresponds to the `bpf_prog_alloc_security()` LSM hook.
932pub fn bpf_prog_alloc(current_task: &CurrentTask) -> BpfProgState {
933    track_hook_duration!("security.hooks.bpf_prog_alloc");
934    BpfProgState { state: selinux_hooks::bpf::bpf_prog_alloc(current_task) }
935}
936
937/// Returns whether `current_task` can issue an ioctl to `file`.
938/// Corresponds to the `file_ioctl()` LSM hook.
939pub fn check_file_ioctl_access(
940    current_task: &CurrentTask,
941    file: &FileObject,
942    request: u32,
943) -> Result<(), Errno> {
944    track_hook_duration!("security.hooks.check_file_ioctl_access");
945    if_selinux_else_default_ok(current_task, |security_server| {
946        selinux_hooks::file::check_file_ioctl_access(security_server, current_task, file, request)
947    })
948}
949
950/// Updates the supplied `new_creds` with the necessary FS and LSM credentials to correctly label
951/// a new `FsNode` on copy-up, to match the existing `fs_node`.
952///
953/// - fs_node: The "lower" filesystem node that is to be copied-up.
954/// - fs: The OverlayFS instance performing the copy-up operation.
955// TODO: https://fxbug.dev/398696739 - Revise this API to accept the overlay FsNode for which
956// copy-up is being performed, rather than separate "lower" `fs_node` and overlay `fs`.
957///
958/// Corresponds to the `security_inode_copy_up()` LSM hook.
959pub fn fs_node_copy_up(
960    current_task: &CurrentTask,
961    fs_node: &FsNode,
962    fs: &FileSystem,
963    new_creds: &mut Credentials,
964) {
965    if fs_node.is_private() {
966        return;
967    }
968    if_selinux_else(
969        current_task,
970        |_security_server| {
971            selinux_hooks::fs_node::fs_node_copy_up(current_task, fs_node, fs, new_creds)
972        },
973        || {},
974    )
975}
976
977/// This hook is called by the `flock` syscall. Returns whether `current_task` can perform
978/// a lock operation on the given file.
979///
980/// See also `check_file_fcntl_access()` for `lock` permission checks performed after an
981/// fcntl lock request.
982///
983/// Corresponds to the `file_lock()` LSM hook.
984pub fn check_file_lock_access(current_task: &CurrentTask, file: &FileObject) -> Result<(), Errno> {
985    track_hook_duration!("security.hooks.check_file_lock_access");
986    if_selinux_else_default_ok(current_task, |security_server| {
987        selinux_hooks::file::check_file_lock_access(security_server, current_task, file)
988    })
989}
990
991/// Returns whether `current_task` has the permissions to execute this fcntl syscall.
992/// Corresponds to the `file_fcntl()` LSM hook.
993pub fn check_file_fcntl_access(
994    current_task: &CurrentTask,
995    file: &FileObject,
996    fcntl_cmd: u32,
997    fcntl_arg: u64,
998) -> Result<(), Errno> {
999    track_hook_duration!("security.hooks.check_file_fcntl_access");
1000    if_selinux_else_default_ok(current_task, |security_server| {
1001        selinux_hooks::file::check_file_fcntl_access(
1002            security_server,
1003            current_task,
1004            file,
1005            fcntl_cmd,
1006            fcntl_arg,
1007        )
1008    })
1009}
1010
1011/// Checks whether `current_task` can set attributes on `node`.
1012/// Corresponds to the `inode_setattr()` LSM hook.
1013pub fn check_fs_node_setattr_access(
1014    current_task: &CurrentTask,
1015    node: &FsNode,
1016    attributes: &zxio_node_attr_has_t,
1017) -> Result<(), Errno> {
1018    track_hook_duration!("security.hooks.check_fs_node_setattr_access");
1019    if node.is_private() {
1020        return Ok(());
1021    }
1022    if_selinux_else_default_ok(current_task, |security_server| {
1023        selinux_hooks::fs_node::check_fs_node_setattr_access(
1024            security_server,
1025            current_task,
1026            node,
1027            attributes,
1028        )
1029    })
1030}
1031
1032/// Return the default initial `TaskAttrs` for kernel tasks.
1033/// Corresponds to the `task_alloc()` LSM hook, in the special case when current_task is null.
1034pub fn task_alloc_for_kernel() -> TaskAttrs {
1035    track_hook_duration!("security.hooks.task_alloc_for_kernel");
1036    TaskAttrs::for_kernel()
1037}
1038
1039/// Labels an [`crate::vfs::FsNode`], by attaching a pseudo-label to the `fs_node`, which allows
1040/// indirect resolution of the effective label. Makes the security attributes of `fs_node` track the
1041/// `task`'s security attributes, even if the task's security attributes change. Called for the
1042/// /proc/<pid> `FsNode`s when they are created.
1043/// Corresponds to the `task_to_inode` LSM hook.
1044pub fn task_to_fs_node(current_task: &CurrentTask, task: &Task, fs_node: &FsNode) {
1045    track_hook_duration!("security.hooks.task_to_fs_node");
1046    if fs_node.is_private() {
1047        return;
1048    }
1049    // The fs_node_init_with_task hook doesn't require any policy-specific information. Only check
1050    // if SElinux is enabled before running it.
1051    if current_task.kernel().security_state.state.is_some() {
1052        selinux_hooks::task::fs_node_init_with_task(task, &fs_node);
1053    }
1054}
1055
1056/// Returns `TaskAttrs` for a new `Task`, based on that of the provided `context`.
1057/// The effect is similar to combining the `task_alloc()` and `setprocattr()` LSM hooks, with the
1058/// difference that no access-checks are performed, and the "#<name>" syntax may be used to
1059/// have the `Task` assigned one of the "initial" Security Contexts, to allow components to be run
1060/// prior to a policy being loaded.
1061pub fn task_for_context(task: &Task, context: &FsStr) -> Result<TaskAttrs, Errno> {
1062    track_hook_duration!("security.hooks.task_for_context");
1063    Ok(if let Some(kernel_state) = task.kernel().security_state.state.as_ref() {
1064        selinux_hooks::task::task_alloc_from_context(&kernel_state.server, context)
1065    } else {
1066        Ok(TaskAttrs::for_selinux_disabled())
1067    }?)
1068}
1069
1070/// Returns true if there exits a `dontaudit` rule for `current_task` access to `fs_node`, which
1071/// includes the `audit_access` pseudo-permission.
1072/// This appears to be handled via additional options & flags in other hooks, by LSM.
1073pub fn has_dontaudit_access(current_task: &CurrentTask, fs_node: &FsNode) -> bool {
1074    track_hook_duration!("security.hooks.has_dontaudit_access");
1075    if fs_node.is_private() {
1076        return false;
1077    }
1078    if_selinux_else(
1079        current_task,
1080        |security_server| {
1081            selinux_hooks::fs_node::has_dontaudit_access(security_server, current_task, fs_node)
1082        },
1083        || false,
1084    )
1085}
1086
1087/// Returns true if a task has the specified `capability`.
1088/// Corresponds to the `capable()` LSM hook invoked with a no-audit flag set.
1089pub fn is_task_capable_noaudit(
1090    current_task: &CurrentTask,
1091    capability: starnix_uapi::auth::Capabilities,
1092) -> bool {
1093    check_task_capable_noaudit(current_task, capability).is_ok()
1094}
1095
1096/// Checks if a task with the given `creds` has the specified `capability` without generating
1097/// audit logs.
1098/// Corresponds to the `capable()` LSM hook invoked with a no-audit flag set.
1099pub fn check_creds_capable_noaudit(
1100    current_task: &CurrentTask,
1101    creds: &Credentials,
1102    capability: starnix_uapi::auth::Capabilities,
1103) -> Result<(), Errno> {
1104    track_hook_duration!("security.hooks.is_task_capable_noaudit");
1105    common_cap::creds_capable(creds, capability)?;
1106    let permitted = if_selinux_else(
1107        current_task,
1108        |security_server| {
1109            selinux_hooks::task::is_creds_capable_noaudit(
1110                &selinux_hooks::build_permission_check(current_task, security_server),
1111                &current_task,
1112                creds,
1113                capability,
1114            )
1115        },
1116        || true,
1117    );
1118    if permitted { Ok(()) } else { error!(EPERM) }
1119}
1120
1121/// Checks if a task has the specified `capability` without generating audit logs.
1122/// Corresponds to the `capable()` LSM hook invoked with a no-audit flag set.
1123pub fn check_task_capable_noaudit(
1124    current_task: &CurrentTask,
1125    capability: starnix_uapi::auth::Capabilities,
1126) -> Result<(), Errno> {
1127    check_creds_capable_noaudit(current_task, &**current_task.current_creds(), capability)
1128}
1129
1130/// Checks if a task has the specified `capability`.
1131/// Corresponds to the `capable()` LSM hook.
1132pub fn check_creds_capable(
1133    current_task: &CurrentTask,
1134    creds: &Credentials,
1135    capability: starnix_uapi::auth::Capabilities,
1136) -> Result<(), Errno> {
1137    track_hook_duration!("security.hooks.check_creds_capable");
1138    common_cap::creds_capable(creds, capability)?;
1139    if_selinux_else_default_ok(current_task, |security_server| {
1140        selinux_hooks::task::check_creds_capable(
1141            &selinux_hooks::build_permission_check(current_task, security_server),
1142            &current_task,
1143            creds,
1144            capability,
1145        )
1146    })
1147}
1148
1149/// Checks if a task has the specified `capability`.
1150/// Corresponds to the `capable()` LSM hook.
1151pub fn check_task_capable(
1152    current_task: &CurrentTask,
1153    capability: starnix_uapi::auth::Capabilities,
1154) -> Result<(), Errno> {
1155    check_creds_capable(current_task, &**current_task.current_creds(), capability)
1156}
1157
1158/// Checks if a task with the given `creds` has `CAP_SYS_PTRACE`, honoring `PTRACE_MODE_NOAUDIT`
1159/// if set in `mode`.
1160pub fn check_creds_ptrace_capable(
1161    current_task: &CurrentTask,
1162    creds: &Credentials,
1163    mode: PtraceAccessMode,
1164) -> Result<(), Errno> {
1165    if mode.contains(PtraceAccessMode::NOAUDIT) {
1166        check_creds_capable_noaudit(current_task, creds, starnix_uapi::auth::CAP_SYS_PTRACE)
1167    } else {
1168        check_creds_capable(current_task, creds, starnix_uapi::auth::CAP_SYS_PTRACE)
1169    }
1170}
1171
1172/// Checks if a task has `CAP_SYS_PTRACE`, honoring `PTRACE_MODE_NOAUDIT` if set in `mode`.
1173pub fn check_task_ptrace_capable(
1174    current_task: &CurrentTask,
1175    mode: PtraceAccessMode,
1176) -> Result<(), Errno> {
1177    check_creds_ptrace_capable(current_task, &**current_task.current_creds(), mode)
1178}
1179
1180/// Checks if creating a task is allowed.
1181/// Corresponds to the `task_alloc()` LSM hook, except this hook doesn't modify the task's label.
1182pub fn check_task_create_access(current_task: &CurrentTask) -> Result<(), Errno> {
1183    track_hook_duration!("security.hooks.check_task_create_access");
1184    if_selinux_else_default_ok(current_task, |security_server| {
1185        selinux_hooks::task::check_task_create_access(
1186            &selinux_hooks::build_permission_check(current_task, security_server),
1187            current_task,
1188        )
1189    })
1190}
1191
1192/// Checks if creating a socket is allowed.
1193/// Corresponds to the `socket_create()` LSM hook.
1194pub fn check_socket_create_access(
1195    current_task: &CurrentTask,
1196    domain: SocketDomain,
1197    socket_type: SocketType,
1198    protocol: SocketProtocol,
1199    kernel_private: bool,
1200) -> Result<(), Errno> {
1201    track_hook_duration!("security.hooks.socket_create");
1202    if kernel_private {
1203        return Ok(());
1204    }
1205    if_selinux_else_default_ok(current_task, |security_server| {
1206        selinux_hooks::socket::check_socket_create_access(
1207            &security_server,
1208            current_task,
1209            domain,
1210            socket_type,
1211            protocol,
1212        )
1213    })
1214}
1215
1216/// Sets the peer security context for each socket in the pair.
1217/// Corresponds to the `socket_socketpair()` LSM hook.
1218pub fn socket_socketpair(
1219    current_task: &CurrentTask,
1220    left: DowncastedFile<'_, SocketFile>,
1221    right: DowncastedFile<'_, SocketFile>,
1222) -> Result<(), Errno> {
1223    track_hook_duration!("security.hooks.socket_socketpair");
1224    if left.file().node().is_private() {
1225        debug_assert!(right.file().node().is_private());
1226        return Ok(());
1227    }
1228    if_selinux_else_default_ok(current_task, |_| {
1229        selinux_hooks::socket::socket_socketpair(left, right)
1230    })
1231}
1232
1233/// Computes and updates the socket security class associated with a new socket.
1234/// Corresponds to the `socket_post_create()` LSM hook.
1235pub fn socket_post_create(current_task: &CurrentTask, socket: &Socket) {
1236    track_hook_duration!("security.hooks.socket_post_create");
1237    if is_private_socket(socket) {
1238        return;
1239    }
1240    if let Some(state) = &current_task.kernel().security_state.state {
1241        selinux_hooks::socket::socket_post_create(&state.server, socket);
1242    }
1243}
1244
1245/// Checks if the `current_task` is allowed to perform a bind operation for this `socket`.
1246/// Corresponds to the `socket_bind()` LSM hook.
1247pub fn check_socket_bind_access(
1248    current_task: &CurrentTask,
1249    socket: &Socket,
1250    socket_address: &SocketAddress,
1251) -> Result<(), Errno> {
1252    track_hook_duration!("security.hooks.check_socket_bind_access");
1253    if is_private_socket(socket) {
1254        return Ok(());
1255    }
1256    if_selinux_else_default_ok(current_task, |security_server| {
1257        selinux_hooks::socket::check_socket_bind_access(
1258            &security_server,
1259            current_task,
1260            socket,
1261            socket_address,
1262        )
1263    })
1264}
1265
1266/// Checks if the `current_task` is allowed to initiate a connection with `socket`.
1267/// Corresponds to the `socket_connect()` LSM hook.
1268pub fn check_socket_connect_access(
1269    current_task: &CurrentTask,
1270    socket: DowncastedFile<'_, SocketFile>,
1271    socket_peer: &SocketPeer,
1272) -> Result<(), Errno> {
1273    track_hook_duration!("security.hooks.check_socket_connect_access");
1274    if socket.file().node().is_private() {
1275        return Ok(());
1276    }
1277    if_selinux_else_default_ok(current_task, |security_server| {
1278        selinux_hooks::socket::check_socket_connect_access(
1279            &security_server,
1280            current_task,
1281            socket,
1282            socket_peer,
1283        )
1284    })
1285}
1286
1287/// Checks if the `current_task` is allowed to listen on `socket_node`.
1288/// Corresponds to the `socket_listen()` LSM hook.
1289pub fn check_socket_listen_access(
1290    current_task: &CurrentTask,
1291    socket: &Socket,
1292    backlog: i32,
1293) -> Result<(), Errno> {
1294    track_hook_duration!("security.hooks.check_socket_listen_access");
1295    if is_private_socket(socket) {
1296        return Ok(());
1297    }
1298    if_selinux_else_default_ok(current_task, |security_server| {
1299        selinux_hooks::socket::check_socket_listen_access(
1300            &security_server,
1301            current_task,
1302            socket,
1303            backlog,
1304        )
1305    })
1306}
1307
1308/// Checks if the `current_task` is allowed to accept connections on `listening_socket`. Sets
1309/// the security label and SID for the accepted socket to match those of the listening socket.
1310/// Corresponds to the `socket_accept()` LSM hook.
1311pub fn socket_accept(
1312    current_task: &CurrentTask,
1313    listening_socket: DowncastedFile<'_, SocketFile>,
1314    accepted_socket: DowncastedFile<'_, SocketFile>,
1315) -> Result<(), Errno> {
1316    track_hook_duration!("security.hooks.check_socket_getname_access");
1317    if listening_socket.file().node().is_private() || accepted_socket.file().node().is_private() {
1318        return Ok(());
1319    }
1320    if_selinux_else_default_ok(current_task, |security_server| {
1321        selinux_hooks::socket::socket_accept(
1322            &security_server,
1323            current_task,
1324            listening_socket,
1325            accepted_socket,
1326        )
1327    })
1328}
1329
1330/// Checks if the `current_task` is allowed to get socket options on `socket`.
1331/// Corresponds to the `socket_getsockopt()` LSM hook.
1332pub fn check_socket_getsockopt_access(
1333    current_task: &CurrentTask,
1334    socket: &Socket,
1335    level: u32,
1336    optname: u32,
1337) -> Result<(), Errno> {
1338    track_hook_duration!("security.hooks.check_socket_getsockopt_access");
1339    if is_private_socket(socket) {
1340        return Ok(());
1341    }
1342    if_selinux_else_default_ok(current_task, |security_server| {
1343        selinux_hooks::socket::check_socket_getsockopt_access(
1344            &security_server,
1345            current_task,
1346            socket,
1347            level,
1348            optname,
1349        )
1350    })
1351}
1352
1353/// Checks if the `current_task` is allowed to set socket options on `socket`.
1354/// Corresponds to the `socket_setsockopt()` LSM hook.
1355pub fn check_socket_setsockopt_access(
1356    current_task: &CurrentTask,
1357    socket: &Socket,
1358    level: u32,
1359    optname: u32,
1360) -> Result<(), Errno> {
1361    track_hook_duration!("security.hooks.check_socket_setsockopt_access");
1362    if is_private_socket(socket) {
1363        return Ok(());
1364    }
1365    if_selinux_else_default_ok(current_task, |security_server| {
1366        selinux_hooks::socket::check_socket_setsockopt_access(
1367            &security_server,
1368            current_task,
1369            socket,
1370            level,
1371            optname,
1372        )
1373    })
1374}
1375
1376/// Checks if the `current_task` is allowed to send a message on `socket`.
1377/// Corresponds to the `socket_sendmsg()` LSM hook.
1378pub fn check_socket_sendmsg_access(
1379    current_task: &CurrentTask,
1380    socket: &Socket,
1381) -> Result<(), Errno> {
1382    track_hook_duration!("security.hooks.check_socket_sendmsg_access");
1383    if is_private_socket(socket) {
1384        return Ok(());
1385    }
1386    if_selinux_else_default_ok(current_task, |security_server| {
1387        selinux_hooks::socket::check_socket_sendmsg_access(&security_server, current_task, socket)
1388    })
1389}
1390
1391/// Checks if the `current_task` is allowed to receive a message on `socket`.
1392/// Corresponds to the `socket_recvmsg()` LSM hook.
1393pub fn check_socket_recvmsg_access(
1394    current_task: &CurrentTask,
1395    socket: &Socket,
1396) -> Result<(), Errno> {
1397    track_hook_duration!("security.hooks.check_socket_recvmsg_access");
1398    if is_private_socket(socket) {
1399        return Ok(());
1400    }
1401    if_selinux_else_default_ok(current_task, |security_server| {
1402        selinux_hooks::socket::check_socket_recvmsg_access(&security_server, current_task, socket)
1403    })
1404}
1405
1406/// Checks if the `current_task` is allowed to get the local name of `socket`.
1407/// Corresponds to the `socket_getsockname()` LSM hook.
1408pub fn check_socket_getsockname_access(
1409    current_task: &CurrentTask,
1410    socket: &Socket,
1411) -> Result<(), Errno> {
1412    track_hook_duration!("security.hooks.check_socket_getname_access");
1413    if is_private_socket(socket) {
1414        return Ok(());
1415    }
1416    if_selinux_else_default_ok(current_task, |security_server| {
1417        selinux_hooks::socket::check_socket_getname_access(&security_server, current_task, socket)
1418    })
1419}
1420
1421/// Checks if the `current_task` is allowed to get the remote name of `socket`.
1422/// Corresponds to the `socket_getpeername()` LSM hook.
1423pub fn check_socket_getpeername_access(
1424    current_task: &CurrentTask,
1425    socket: &Socket,
1426) -> Result<(), Errno> {
1427    track_hook_duration!("security.hooks.check_socket_getname_access");
1428    if is_private_socket(socket) {
1429        return Ok(());
1430    }
1431    if_selinux_else_default_ok(current_task, |security_server| {
1432        selinux_hooks::socket::check_socket_getname_access(&security_server, current_task, socket)
1433    })
1434}
1435
1436/// Checks if the `current_task` is allowed to shutdown `socket`.
1437/// Corresponds to the `socket_shutdown()` LSM hook.
1438pub fn check_socket_shutdown_access(
1439    current_task: &CurrentTask,
1440    socket: &Socket,
1441    how: SocketShutdownFlags,
1442) -> Result<(), Errno> {
1443    track_hook_duration!("security.hooks.check_socket_shutdown_access");
1444    if is_private_socket(socket) {
1445        return Ok(());
1446    }
1447    if_selinux_else_default_ok(current_task, |security_server| {
1448        selinux_hooks::socket::check_socket_shutdown_access(
1449            &security_server,
1450            current_task,
1451            socket,
1452            how,
1453        )
1454    })
1455}
1456
1457/// Returns the Security Context with which the [`crate::vfs::Socket`]'s peer is labeled.
1458/// Corresponds to the `socket_getpeersec_stream()` LSM hook.
1459pub fn socket_getpeersec_stream(
1460    current_task: &CurrentTask,
1461    socket: &Socket,
1462) -> Result<Vec<u8>, Errno> {
1463    track_hook_duration!("security.hooks.socket_getpeersec_stream");
1464    if is_private_socket(socket) {
1465        return Ok(Vec::default());
1466    }
1467    if_selinux_else_default_ok(current_task, |security_server| {
1468        selinux_hooks::socket::socket_getpeersec_stream(&security_server, current_task, socket)
1469    })
1470}
1471
1472/// Returns the Security Context with which the [`crate::vfs::Socket`]'s is labeled, to return to
1473/// the recipient via `SCM_SECURITY` auxiliary data, if `SO_PASSSEC` is set.
1474/// Corresponds to the `socket_getpeersec_dgram()` LSM hook.
1475pub fn socket_getpeersec_dgram(current_task: &CurrentTask, socket: &Socket) -> Vec<u8> {
1476    track_hook_duration!("security.hooks.socket_getpeersec_dgram");
1477    if is_private_socket(socket) {
1478        return Vec::default();
1479    }
1480    if_selinux_else(
1481        current_task,
1482        |security_server| {
1483            selinux_hooks::socket::socket_getpeersec_dgram(&security_server, current_task, socket)
1484        },
1485        Vec::default,
1486    )
1487}
1488
1489/// Checks if the Unix domain `sending_socket` is allowed to send a message to the
1490/// `receiving_socket`.
1491/// Corresponds to the `unix_may_send()` LSM hook.
1492pub fn unix_may_send(
1493    current_task: &CurrentTask,
1494    sending_socket: &Socket,
1495    receiving_socket: &Socket,
1496) -> Result<(), Errno> {
1497    track_hook_duration!("security.hooks.unix_may_send");
1498    if is_private_socket(sending_socket) || is_private_socket(receiving_socket) {
1499        return Ok(());
1500    }
1501    if_selinux_else_default_ok(current_task, |security_server| {
1502        selinux_hooks::socket::unix_may_send(
1503            &security_server,
1504            current_task,
1505            sending_socket,
1506            receiving_socket,
1507        )
1508    })
1509}
1510
1511/// Checks if the Unix domain `client_socket` is allowed to connect to `listening_socket`, and
1512/// initialises the peer information in the client and server sockets.
1513/// Corresponds to the `unix_stream_connect()` LSM hook.
1514pub fn unix_stream_connect(
1515    current_task: &CurrentTask,
1516    client_socket: &Socket,
1517    listening_socket: &Socket,
1518    server_socket: &Socket,
1519) -> Result<(), Errno> {
1520    track_hook_duration!("security.hooks.unix_stream_connect");
1521    if is_private_socket(client_socket) || is_private_socket(listening_socket) {
1522        return Ok(());
1523    }
1524    if_selinux_else_default_ok(current_task, |security_server| {
1525        selinux_hooks::socket::unix_stream_connect(
1526            &security_server,
1527            current_task,
1528            client_socket,
1529            listening_socket,
1530            server_socket,
1531        )
1532    })
1533}
1534
1535/// Checks if the `current_task` is allowed to send a message of `message_type` on the Netlink
1536/// `socket`.
1537/// Corresponds to the `netlink_send()` LSM hook.
1538pub fn check_netlink_send_access(
1539    current_task: &CurrentTask,
1540    socket: &Socket,
1541    message_type: u16,
1542) -> Result<(), Errno> {
1543    track_hook_duration!("security.hooks.check_netlink_send_access");
1544    if is_private_socket(socket) {
1545        return Ok(());
1546    }
1547    if_selinux_else_default_ok(current_task, |security_server| {
1548        selinux_hooks::netlink_socket::check_netlink_send_access(
1549            &security_server,
1550            current_task,
1551            socket,
1552            message_type,
1553        )
1554    })
1555}
1556
1557/// Checks if the `current_task` has permission to create a new TUN device.
1558/// Corresponds to the `tun_dev_create()` LSM hook.
1559pub fn check_tun_dev_create_access(current_task: &CurrentTask) -> Result<(), Errno> {
1560    track_hook_duration!("security.hooks.check_tun_dev_create_access");
1561    if_selinux_else_default_ok(current_task, |security_server| {
1562        selinux_hooks::socket::check_tun_dev_create_access(&security_server, current_task)
1563    })
1564}
1565
1566/// Updates credentials based on the executable file (e.g., SUID/SGID, capabilities).
1567///
1568/// Corresponds to the `bprm_creds_from_file` LSM hook.
1569pub fn bprm_creds_from_file(
1570    current_task: &CurrentTask,
1571    resolved_program: &mut ResolvedProgram,
1572) -> Result<(), Errno> {
1573    track_hook_duration!("security.hooks.bprm_creds_from_file");
1574    let mask_caps = {
1575        let state = current_task.read();
1576        state.no_new_privs() || state.is_ptraced_without_cap_sys_ptrace()
1577    };
1578
1579    let enable_suid = current_task.kernel().features.enable_suid && !mask_caps;
1580    if enable_suid {
1581        resolved_program.file.name().apply_suid_and_sgid(&mut resolved_program.creds);
1582    }
1583
1584    // On exec, the filesystem UIDs are always reset to the effective UIDs.
1585    resolved_program.creds.fsuid = resolved_program.creds.euid;
1586    resolved_program.creds.fsgid = resolved_program.creds.egid;
1587
1588    // The effective user ID of the process is copied to the saved set-
1589    // user-ID; similarly, the effective group ID is copied to the saved
1590    // set-group-ID. This copying takes place after any effective ID
1591    // changes that occur because of the set-user-ID and set-group-ID
1592    // mode bits.
1593    resolved_program.creds.saved_uid = resolved_program.creds.euid;
1594    resolved_program.creds.saved_gid = resolved_program.creds.egid;
1595
1596    common_cap::bprm_creds_from_file(current_task, resolved_program, mask_caps)
1597}
1598
1599/// Checks permissions for `execve`, updating [`ResolvedProgram`] with the post-exec
1600/// security state if allowed.
1601///
1602/// Corresponds to the `bprm_creds_for_exec()` LSM hook.
1603pub fn bprm_creds_for_exec(
1604    current_task: &CurrentTask,
1605    resolved_program: &mut ResolvedProgram,
1606) -> Result<(), Errno> {
1607    track_hook_duration!("security.hooks.bprm_creds_for_exec");
1608    if let Some(state) = &current_task.kernel().security_state.state {
1609        if state.has_policy() {
1610            return selinux_hooks::task::bprm_creds_for_exec(
1611                &state.server,
1612                current_task,
1613                resolved_program,
1614            );
1615        } else {
1616            // SELinux is enabled but not yet configured, so apply the "init" SID.
1617            let previous_sid = current_task.current_creds().security_state.current_sid;
1618            resolved_program.creds.security_state =
1619                TaskAttrs::for_transition(InitialSid::Init.into(), previous_sid);
1620        }
1621    }
1622    Ok(())
1623}
1624
1625/// Applies pre-commit security restrictions (such as revoking inaccessible file descriptors)
1626/// immediately before [`ResolvedProgram::creds`] are committed to the calling task.
1627///
1628/// Corresponds to the `bprm_committing_creds()` LSM hook.
1629pub fn bprm_committing_creds(
1630    current_task: &CurrentTask,
1631    resolved_program: &ResolvedProgram,
1632) -> Result<(), Errno> {
1633    track_hook_duration!("security.hooks.bprm_committing_creds");
1634    if_selinux_else_default_ok(current_task, |security_server| {
1635        selinux_hooks::task::bprm_committing_creds(security_server, current_task, resolved_program);
1636        Ok(())
1637    })
1638}
1639
1640/// Called immediately after new credentials have been applied to the process during `exec()`.
1641///
1642/// Corresponds to the `bprm_committed_creds()` LSM hook.
1643pub fn bprm_committed_creds(current_task: &CurrentTask) -> Result<(), Errno> {
1644    track_hook_duration!("security.hooks.bprm_committed_creds");
1645    if_selinux_else_default_ok(current_task, |security_server| {
1646        selinux_hooks::task::bprm_committed_creds(security_server, current_task);
1647        Ok(())
1648    })
1649}
1650
1651/// Checks if `source` may exercise the "getsched" permission on `target`.
1652/// Corresponds to the `task_getscheduler()` LSM hook.
1653pub fn check_task_getscheduler_access(source: &CurrentTask, target: &Task) -> Result<(), Errno> {
1654    track_hook_duration!("security.hooks.task_getscheduler");
1655    if_selinux_else_default_ok(source, |security_server| {
1656        selinux_hooks::task::check_getsched_access(
1657            &selinux_hooks::build_permission_check(source, security_server),
1658            &source,
1659            &target,
1660        )
1661    })
1662}
1663
1664/// Checks if setsched is allowed.
1665/// Corresponds to the `task_setscheduler()` LSM hook.
1666pub fn check_task_setscheduler_access(source: &CurrentTask, target: &Task) -> Result<(), Errno> {
1667    track_hook_duration!("security.hooks.task_setscheduler");
1668    if_selinux_else_default_ok(source, |security_server| {
1669        selinux_hooks::task::check_setsched_access(
1670            &selinux_hooks::build_permission_check(source, security_server),
1671            &source,
1672            &target,
1673        )
1674    })
1675}
1676
1677/// Checks if setting nice value is allowed.
1678/// Corresponds to the `task_setnice()` LSM hook.
1679pub fn check_task_setnice_access(source: &CurrentTask, target: &Task) -> Result<(), Errno> {
1680    track_hook_duration!("security.hooks.check_task_setnice_access");
1681    if_selinux_else_default_ok(source, |security_server| {
1682        selinux_hooks::task::check_setsched_access(
1683            &selinux_hooks::build_permission_check(source, security_server),
1684            &source,
1685            &target,
1686        )
1687    })
1688}
1689
1690/// Checks if getpgid is allowed.
1691/// Corresponds to the `task_getpgid()` LSM hook.
1692pub fn check_getpgid_access(source: &CurrentTask, target: &Task) -> Result<(), Errno> {
1693    track_hook_duration!("security.hooks.check_getpgid_access");
1694    if_selinux_else_default_ok(source, |security_server| {
1695        selinux_hooks::task::check_getpgid_access(
1696            &selinux_hooks::build_permission_check(source, security_server),
1697            &source,
1698            &target,
1699        )
1700    })
1701}
1702
1703/// Checks if setpgid is allowed.
1704/// Corresponds to the `task_setpgid()` LSM hook.
1705pub fn check_setpgid_access(source: &CurrentTask, target: &Task) -> Result<(), Errno> {
1706    track_hook_duration!("security.hooks.check_setpgid_access");
1707    if_selinux_else_default_ok(source, |security_server| {
1708        selinux_hooks::task::check_setpgid_access(
1709            &selinux_hooks::build_permission_check(source, security_server),
1710            &source,
1711            &target,
1712        )
1713    })
1714}
1715
1716/// Called when the current task queries the session Id of the `target` task.
1717/// Corresponds to the `task_getsid()` LSM hook.
1718pub fn check_task_getsid(source: &CurrentTask, target: &Task) -> Result<(), Errno> {
1719    track_hook_duration!("security.hooks.check_task_getsid");
1720    if_selinux_else_default_ok(source, |security_server| {
1721        selinux_hooks::task::check_task_getsid(
1722            &selinux_hooks::build_permission_check(source, security_server),
1723            &source,
1724            &target,
1725        )
1726    })
1727}
1728
1729/// Called when the current task queries the Linux capabilities of the `target` task.
1730/// Corresponds to the `capget()` LSM hook.
1731pub fn check_getcap_access(source: &CurrentTask, target: &Task) -> Result<(), Errno> {
1732    track_hook_duration!("security.hooks.check_getcap_access");
1733    if_selinux_else_default_ok(source, |security_server| {
1734        selinux_hooks::task::check_getcap_access(
1735            &selinux_hooks::build_permission_check(source, security_server),
1736            &source,
1737            &target,
1738        )
1739    })
1740}
1741
1742/// Called when the current task attempts to set the Linux capabilities of the `target`
1743/// task.
1744/// Corresponds to the `capset()` LSM hook.
1745pub fn check_setcap_access(source: &CurrentTask, target: &Task) -> Result<(), Errno> {
1746    track_hook_duration!("security.hooks.check_setcap_access");
1747    if_selinux_else_default_ok(source, |security_server| {
1748        selinux_hooks::task::check_setcap_access(
1749            &selinux_hooks::build_permission_check(source, security_server),
1750            &source,
1751            &target,
1752        )
1753    })
1754}
1755
1756/// Checks if sending a signal is allowed.
1757/// Corresponds to the `task_kill()` LSM hook.
1758pub fn check_signal_access(
1759    source: &CurrentTask,
1760    target: &Task,
1761    signal: Signal,
1762) -> Result<(), Errno> {
1763    track_hook_duration!("security.hooks.check_signal_access");
1764    if_selinux_else_default_ok(source, |security_server| {
1765        selinux_hooks::task::check_signal_access(
1766            &selinux_hooks::build_permission_check(source, security_server),
1767            &source,
1768            &target,
1769            signal,
1770        )
1771    })
1772}
1773
1774/// Checks if a particular syslog action is allowed.
1775/// Corresponds to the `task_syslog()` LSM hook.
1776pub fn check_syslog_access(source: &CurrentTask, action: SyslogAction) -> Result<(), Errno> {
1777    track_hook_duration!("security.hooks.check_syslog_access");
1778    if_selinux_else_default_ok(source, |security_server| {
1779        selinux_hooks::task::check_syslog_access(
1780            &selinux_hooks::build_permission_check(source, security_server),
1781            &source,
1782            action,
1783        )
1784    })
1785}
1786
1787/// Checks whether the `parent_tracer_task` is allowed to trace the `current_task`.
1788/// Corresponds to the `ptrace_traceme()` LSM hook.
1789pub fn ptrace_traceme(current_task: &CurrentTask, parent_tracer_task: &Task) -> Result<(), Errno> {
1790    track_hook_duration!("security.hooks.ptrace_traceme");
1791    yama::ptrace_traceme(current_task, parent_tracer_task)?;
1792    common_cap::ptrace_traceme(current_task, parent_tracer_task)?;
1793    if_selinux_else_default_ok(current_task, |security_server| {
1794        selinux_hooks::task::ptrace_traceme(
1795            &selinux_hooks::build_permission_check(current_task, security_server),
1796            current_task,
1797            parent_tracer_task,
1798        )
1799    })
1800}
1801
1802/// Checks whether the current `current_task` is allowed to trace `tracee_task`.
1803/// Corresponds to the `ptrace_access_check()` LSM hook.
1804pub fn ptrace_access_check(
1805    current_task: &CurrentTask,
1806    tracee_task: &Task,
1807    mode: PtraceAccessMode,
1808) -> Result<(), Errno> {
1809    track_hook_duration!("security.hooks.ptrace_access_check");
1810    yama::ptrace_access_check(current_task, tracee_task, mode)?;
1811    common_cap::ptrace_access_check(current_task, tracee_task, mode)?;
1812    if_selinux_else_default_ok(current_task, |security_server| {
1813        selinux_hooks::task::ptrace_access_check(
1814            &selinux_hooks::build_permission_check(current_task, security_server),
1815            current_task,
1816            tracee_task,
1817            mode,
1818        )
1819    })
1820}
1821
1822/// Called when the current task calls prlimit on a different task.
1823/// Corresponds to the `task_prlimit()` LSM hook.
1824pub fn task_prlimit(
1825    source: &CurrentTask,
1826    target: &Task,
1827    check_get_rlimit: bool,
1828    check_set_rlimit: bool,
1829) -> Result<(), Errno> {
1830    track_hook_duration!("security.hooks.task_prlimit");
1831    if_selinux_else_default_ok(source, |security_server| {
1832        selinux_hooks::task::task_prlimit(
1833            &selinux_hooks::build_permission_check(source, security_server),
1834            &source,
1835            &target,
1836            check_get_rlimit,
1837            check_set_rlimit,
1838        )
1839    })
1840}
1841
1842/// Called before `source` sets the resource limits of `target` from `old_limit` to `new_limit`.
1843/// Corresponds to the `security_task_setrlimit` hook.
1844pub fn task_setrlimit(
1845    source: &CurrentTask,
1846    target: &Task,
1847    old_limit: rlimit,
1848    new_limit: rlimit,
1849) -> Result<(), Errno> {
1850    track_hook_duration!("security.hooks.task_setrlimit");
1851    if_selinux_else_default_ok(source, |security_server| {
1852        selinux_hooks::task::task_setrlimit(
1853            &selinux_hooks::build_permission_check(source, security_server),
1854            &source,
1855            &target,
1856            old_limit,
1857            new_limit,
1858        )
1859    })
1860}
1861
1862/// Check permission before mounting `fs`.
1863/// Corresponds to the `sb_kern_mount()` LSM hook.
1864pub fn sb_kern_mount(current_task: &CurrentTask, fs: &FileSystem) -> Result<(), Errno> {
1865    track_hook_duration!("security.hooks.sb_kern_mount");
1866    if_selinux_else_default_ok(current_task, |security_server| {
1867        selinux_hooks::superblock::sb_kern_mount(
1868            &selinux_hooks::build_permission_check(current_task, security_server),
1869            current_task,
1870            fs,
1871        )
1872    })
1873}
1874
1875/// Check permission before mounting to `path`. `flags` contains the mount flags that determine the
1876/// kind of mount operation done, and therefore the permissions that the caller requires.
1877/// Corresponds to the `sb_mount()` LSM hook.
1878pub fn sb_mount(
1879    current_task: &CurrentTask,
1880    path: &NamespaceNode,
1881    flags: MountFlags,
1882) -> Result<(), Errno> {
1883    track_hook_duration!("security.hooks.sb_mount");
1884    if path.entry.node.is_private() {
1885        // Userspace may pass a path to an anonymous private file (e.g. via `/proc/self/fd/<N>`) to
1886        // `mount()`. Allow the LSM check to succeed and let the VFS reject the mount.
1887        return Ok(());
1888    }
1889    if_selinux_else_default_ok(current_task, |security_server| {
1890        selinux_hooks::superblock::sb_mount(
1891            &selinux_hooks::build_permission_check(current_task, security_server),
1892            current_task,
1893            path,
1894            flags,
1895        )
1896    })
1897}
1898
1899/// Checks permission before remounting `mount` with `new_mount_params`.
1900/// Corresponds to the `sb_remount()` LSM hook.
1901pub fn sb_remount(
1902    current_task: &CurrentTask,
1903    mount: &Mount,
1904    new_mount_options: FileSystemMountOptions,
1905) -> Result<(), Errno> {
1906    track_hook_duration!("security.hooks.sb_remount");
1907    if_selinux_else_default_ok(current_task, |security_server| {
1908        selinux_hooks::superblock::sb_remount(security_server, mount, new_mount_options)
1909    })
1910}
1911
1912/// Returns a `Display` implementation that Writes the LSM mount options of `fs` into `buf`.
1913/// Corresponds to the `sb_show_options` LSM hook.
1914pub fn sb_show_options<'a>(
1915    _kernel: &Kernel,
1916    fs: &'a FileSystem,
1917) -> Result<impl std::fmt::Display + 'a, Errno> {
1918    track_hook_duration!("security.hooks.sb_show_options");
1919    selinux_hooks::superblock::sb_show_options(fs)
1920}
1921
1922/// Checks if `current_task` has the permission to get the filesystem statistics of `fs`.
1923/// Corresponds to the `sb_statfs()` LSM hook.
1924pub fn sb_statfs(current_task: &CurrentTask, fs: &FileSystem) -> Result<(), Errno> {
1925    track_hook_duration!("security.hooks.sb_statfs");
1926    if_selinux_else_default_ok(current_task, |security_server| {
1927        selinux_hooks::superblock::sb_statfs(
1928            &selinux_hooks::build_permission_check(current_task, security_server),
1929            current_task,
1930            fs,
1931        )
1932    })
1933}
1934
1935/// Checks if `current_task` has the permission to unmount the filesystem mounted on
1936/// `node` using the unmount flags `flags`.
1937/// Corresponds to the `sb_umount()` LSM hook.
1938pub fn sb_umount(
1939    current_task: &CurrentTask,
1940    node: &NamespaceNode,
1941    flags: UnmountFlags,
1942) -> Result<(), Errno> {
1943    track_hook_duration!("security.hooks.sb_umount");
1944    if node.entry.node.is_private() {
1945        // Userspace may pass a path to an anonymous private file (e.g. via `/proc/self/fd/<N>`) to
1946        // `umount2()`. Allow the LSM check to succeed and let the VFS reject the unmount.
1947        return Ok(());
1948    }
1949    if_selinux_else_default_ok(current_task, |security_server| {
1950        selinux_hooks::superblock::sb_umount(
1951            &selinux_hooks::build_permission_check(current_task, security_server),
1952            current_task,
1953            node,
1954            flags,
1955        )
1956    })
1957}
1958
1959/// Checks if `current_task` has the permission to read file attributes for  `fs_node`.
1960/// Corresponds to the `inode_getattr()` hook.
1961pub fn check_fs_node_getattr_access(
1962    current_task: &CurrentTask,
1963    fs_node: &FsNode,
1964) -> Result<(), Errno> {
1965    track_hook_duration!("security.hooks.check_fs_node_getattr_access");
1966    if fs_node.is_private() {
1967        return Ok(());
1968    }
1969    if_selinux_else_default_ok(current_task, |security_server| {
1970        selinux_hooks::fs_node::check_fs_node_getattr_access(security_server, current_task, fs_node)
1971    })
1972}
1973
1974/// Checks whether `current_task` can set a watch on `fs_node`, to be notified of the events in
1975/// `mask`.
1976/// Corresponds to the `path_notify()` LSM hook.
1977pub fn path_notify(
1978    current_task: &CurrentTask,
1979    fs_node: &FsNode,
1980    mask: InotifyMask,
1981) -> Result<(), Errno> {
1982    track_hook_duration!("security.hooks.path_notify");
1983    if fs_node.is_private() {
1984        return Ok(());
1985    }
1986    if_selinux_else_default_ok(current_task, |security_server| {
1987        selinux_hooks::fs_node::path_notify(security_server, current_task, fs_node, mask)
1988    })
1989}
1990
1991/// Returns true if the security subsystem should skip capability checks on access to the named
1992/// attribute, false otherwise.
1993pub fn fs_node_xattr_skipcap(name: &FsStr) -> bool {
1994    common_cap::fs_node_xattr_skipcap(name) || selinux_hooks::fs_node::fs_node_xattr_skipcap(name)
1995}
1996
1997/// This is called by Starnix even for filesystems which support extended attributes, unlike Linux
1998/// LSM.
1999/// Partially corresponds to the `inode_setxattr()` LSM hook: It is equivalent to
2000/// `inode_setxattr()` for non-security xattrs, while `fs_node_setsecurity()` is always called for
2001/// security xattrs. See also [`fs_node_setsecurity()`].
2002pub fn check_fs_node_setxattr_access(
2003    current_task: &CurrentTask,
2004    fs_node: &FsNode,
2005    name: &FsStr,
2006    value: &FsStr,
2007    op: XattrOp,
2008) -> Result<(), Errno> {
2009    track_hook_duration!("security.hooks.check_fs_node_setxattr_access");
2010    if fs_node.is_private() {
2011        return Ok(());
2012    }
2013    common_cap::fs_node_setxattr(current_task, fs_node, name, value, op)?;
2014    if_selinux_else_default_ok(current_task, |security_server| {
2015        selinux_hooks::fs_node::check_fs_node_setxattr_access(
2016            security_server,
2017            current_task,
2018            fs_node,
2019            name,
2020            value,
2021            op,
2022        )
2023    })
2024}
2025
2026/// Corresponds to the `inode_getxattr()` LSM hook.
2027pub fn check_fs_node_getxattr_access(
2028    current_task: &CurrentTask,
2029    fs_node: &FsNode,
2030    name: &FsStr,
2031) -> Result<(), Errno> {
2032    track_hook_duration!("security.hooks.check_fs_node_getxattr_access");
2033    if fs_node.is_private() {
2034        return Ok(());
2035    }
2036    if_selinux_else_default_ok(current_task, |security_server| {
2037        selinux_hooks::fs_node::check_fs_node_getxattr_access(
2038            security_server,
2039            current_task,
2040            fs_node,
2041            name,
2042        )
2043    })
2044}
2045
2046/// Corresponds to the `inode_listxattr()` LSM hook.
2047pub fn check_fs_node_listxattr_access(
2048    current_task: &CurrentTask,
2049    fs_node: &FsNode,
2050) -> Result<(), Errno> {
2051    track_hook_duration!("security.hooks.check_fs_node_listxattr_access");
2052    if fs_node.is_private() {
2053        return Ok(());
2054    }
2055    if_selinux_else_default_ok(current_task, |security_server| {
2056        selinux_hooks::fs_node::check_fs_node_listxattr_access(
2057            security_server,
2058            current_task,
2059            fs_node,
2060        )
2061    })
2062}
2063
2064/// Corresponds to the `inode_removexattr()` LSM hook.
2065pub fn check_fs_node_removexattr_access(
2066    current_task: &CurrentTask,
2067    fs_node: &FsNode,
2068    name: &FsStr,
2069) -> Result<(), Errno> {
2070    track_hook_duration!("security.hooks.check_fs_node_removexattr_access");
2071    if fs_node.is_private() {
2072        return Ok(());
2073    }
2074    common_cap::fs_node_removexattr(current_task, fs_node, name)?;
2075    if_selinux_else_default_ok(current_task, |security_server| {
2076        selinux_hooks::fs_node::check_fs_node_removexattr_access(
2077            security_server,
2078            current_task,
2079            fs_node,
2080            name,
2081        )
2082    })
2083}
2084
2085/// Returns whether `fs_node` has privileges (e.g. file capabilities) that need to be removed when
2086/// modified.
2087///
2088/// Corresponds to the `inode_need_killpriv()` LSM hook.
2089pub fn fs_node_needs_killpriv(current_task: &CurrentTask, fs_node: &FsNode) -> Result<bool, Errno> {
2090    track_hook_duration!("security.hooks.fs_node_needs_killpriv");
2091    if fs_node.is_private() {
2092        return Ok(false);
2093    }
2094    common_cap::fs_node_needs_killpriv(current_task, fs_node)
2095}
2096
2097/// Removes privileges (e.g. file capabilities) from `fs_node` when its contents or ownership
2098/// are modified.
2099///
2100/// Corresponds to the `inode_killpriv()` LSM hook.
2101pub fn fs_node_killpriv(current_task: &CurrentTask, fs_node: &FsNode) -> Result<(), Errno> {
2102    track_hook_duration!("security.hooks.fs_node_killpriv");
2103    if fs_node.is_private() {
2104        return Ok(());
2105    }
2106    common_cap::fs_node_killpriv(current_task, fs_node)
2107}
2108
2109/// If SELinux is enabled and `fs_node` is in a filesystem without xattr support, returns the xattr
2110/// name for the security label associated with inode. Otherwise returns None.
2111///
2112/// This hook is called from the `listxattr` syscall.
2113///
2114/// Corresponds to the `inode_listsecurity()` LSM hook.
2115pub fn fs_node_listsecurity(current_task: &CurrentTask, fs_node: &FsNode) -> Option<FsString> {
2116    track_hook_duration!("security.hooks.fs_node_listsecurity");
2117    if fs_node.is_private() {
2118        return None;
2119    }
2120    if_selinux_else(
2121        current_task,
2122        |_| selinux_hooks::fs_node::fs_node_listsecurity(fs_node),
2123        || None,
2124    )
2125}
2126
2127/// Returns the value of the specified "security.*" attribute for `fs_node`.
2128/// If SELinux is enabled then requests for the "security.selinux" attribute will return the
2129/// Security Context corresponding to the SID with which `fs_node` has been labeled, even if the
2130/// node's file system does not generally support extended attributes.
2131/// If SELinux is not enabled, or the node is not labeled with a SID, then the call is delegated to
2132/// the [`crate::vfs::FsNodeOps`], so the returned value may not be a valid Security Context.
2133/// Corresponds to the `inode_getsecurity()` LSM hook.
2134pub fn fs_node_getsecurity(
2135    current_task: &CurrentTask,
2136    fs_node: &FsNode,
2137    name: &FsStr,
2138    max_size: usize,
2139) -> Result<ValueOrSize<FsString>, Errno> {
2140    track_hook_duration!("security.hooks.fs_node_getsecurity");
2141    if fs_node.is_private() {
2142        return fs_node.ops().get_xattr(fs_node, current_task, name, max_size);
2143    }
2144    let result = if_selinux_else(
2145        current_task,
2146        |security_server| {
2147            selinux_hooks::fs_node::fs_node_getsecurity(
2148                security_server,
2149                current_task,
2150                fs_node,
2151                name,
2152                max_size,
2153            )
2154            .transpose()
2155        },
2156        || None,
2157    );
2158    let result = result.or_else(|| {
2159        common_cap::fs_node_getsecurity(current_task, fs_node, name, max_size).transpose()
2160    });
2161    result.unwrap_or_else(|| fs_node.ops().get_xattr(fs_node, current_task, name, max_size))
2162}
2163
2164/// Called when an extended attribute with "security."-prefixed `name` is being set, after having
2165/// passed the discretionary and `check_fs_node_setxattr_access()` permission-checks.
2166/// This allows the LSM (e.g. SELinux) to update internal state as necessary for xattr changes.
2167///
2168/// Partially corresponds to the `inode_setsecurity()` and `inode_post_setxattr()` LSM hooks.
2169pub fn fs_node_setsecurity(
2170    current_task: &CurrentTask,
2171    fs_node: &FsNode,
2172    name: &FsStr,
2173    value: &FsStr,
2174    op: XattrOp,
2175) -> Result<(), Errno> {
2176    track_hook_duration!("security.hooks.fs_node_setsecurity");
2177    if fs_node.is_private() {
2178        return fs_node.ops().set_xattr(fs_node, current_task, name, value, op);
2179    }
2180    let result = if_selinux_else(
2181        current_task,
2182        |security_server| {
2183            selinux_hooks::fs_node::fs_node_setsecurity(
2184                security_server,
2185                current_task,
2186                fs_node,
2187                name,
2188                value,
2189                op,
2190            )
2191            .transpose()
2192        },
2193        || None,
2194    );
2195    let result = result.or_else(|| {
2196        common_cap::fs_node_setsecurity(current_task, fs_node, name, value, op).transpose()
2197    });
2198    result.unwrap_or_else(|| fs_node.ops().set_xattr(fs_node, current_task, name, value, op))
2199}
2200
2201/// Checks whether `current_task` can perform the given bpf `cmd`. This hook is called from the
2202/// `sys_bpf()` syscall after the attribute is copied into the kernel.
2203/// Corresponds to the `bpf()` LSM hook.
2204pub fn check_bpf_access<Attr: FromBytes>(
2205    current_task: &CurrentTask,
2206    cmd: bpf_cmd,
2207    attr: &Attr,
2208    attr_size: u32,
2209) -> Result<(), Errno> {
2210    track_hook_duration!("security.hooks.check_bpf_access");
2211    if_selinux_else_default_ok(current_task, |security_server| {
2212        selinux_hooks::bpf::check_bpf_access(security_server, current_task, cmd, attr, attr_size)
2213    })
2214}
2215
2216/// Checks whether `current_task` can create a bpf_map. This hook is called from the
2217/// `sys_bpf()` syscall when the kernel tries to generate and return a file descriptor for maps.
2218/// Corresponds to the `bpf_map()` LSM hook.
2219pub fn check_bpf_map_access(
2220    current_task: &CurrentTask,
2221    bpf_map_state: &BpfMapState,
2222    flags: PermissionFlags,
2223) -> Result<(), Errno> {
2224    track_hook_duration!("security.hooks.check_bpf_map_access");
2225    if_selinux_else_default_ok(current_task, |security_server| {
2226        let subject_sid = current_task_state(current_task).current_sid;
2227        selinux_hooks::bpf::check_bpf_map_access(
2228            security_server,
2229            current_task,
2230            subject_sid,
2231            bpf_map_state,
2232            flags,
2233        )
2234    })
2235}
2236
2237/// Checks whether `current_task` can create a bpf_program. This hook is called from the
2238/// `sys_bpf()` syscall when the kernel tries to generate and return a file descriptor for
2239/// programs.
2240/// Corresponds to the `bpf_prog()` LSM hook.
2241pub fn check_bpf_prog_access(
2242    current_task: &CurrentTask,
2243    bpf_program_state: &BpfProgState,
2244) -> Result<(), Errno> {
2245    track_hook_duration!("security.hooks.check_bpf_prog_access");
2246    if_selinux_else_default_ok(current_task, |security_server| {
2247        let subject_sid = current_task_state(current_task).current_sid;
2248        selinux_hooks::bpf::check_bpf_prog_access(
2249            security_server,
2250            current_task,
2251            subject_sid,
2252            bpf_program_state,
2253        )
2254    })
2255}
2256
2257/// Checks whether `current_task` has the correct permissions for the given stage of a
2258/// `perf_event_open()` call. Called by `perf_event_open()` once for each stage that applies to
2259/// the new event.
2260/// Corresponds to the `perf_event_open` LSM hook.
2261pub fn check_perf_event_open_access(
2262    current_task: &CurrentTask,
2263    perf_event_open_type: PerfEventOpenType,
2264) -> Result<(), Errno> {
2265    track_hook_duration!("security.hooks.check_perf_event_open_access");
2266    if_selinux_else_default_ok(current_task, |security_server| {
2267        selinux_hooks::perf_event::check_perf_event_open_access(
2268            security_server,
2269            current_task,
2270            perf_event_open_type,
2271        )
2272    })
2273}
2274
2275/// Returns the security context to be assigned to a PerfEventFileState, based on the task that
2276/// creates it.
2277/// Corresponds to the `perf_event_alloc` LSM hook.
2278pub fn perf_event_alloc(current_task: &CurrentTask) -> PerfEventState {
2279    track_hook_duration!("security.hooks.perf_event_alloc");
2280    PerfEventState { state: selinux_hooks::perf_event::perf_event_alloc(current_task) }
2281}
2282
2283/// Checks whether `current_task` has the correct permissions to read the given `perf_event_file`
2284/// Corresponds to the `perf_event_read` LSM hook.
2285pub fn check_perf_event_read_access(
2286    current_task: &CurrentTask,
2287    perf_event_file: &PerfEventFile,
2288) -> Result<(), Errno> {
2289    track_hook_duration!("security.hooks.check_perf_event_read_access");
2290    if_selinux_else_default_ok(current_task, |security_server| {
2291        selinux_hooks::perf_event::check_perf_event_read_access(
2292            security_server,
2293            current_task,
2294            perf_event_file,
2295        )
2296    })
2297}
2298
2299/// Checks whether `current_task` has the correct permissions to write to the given `perf_event_file`.
2300/// Corresponds to the `perf_event_write` LSM hook.
2301pub fn check_perf_event_write_access(
2302    current_task: &CurrentTask,
2303    perf_event_file: &PerfEventFile,
2304) -> Result<(), Errno> {
2305    track_hook_duration!("security.hooks.check_perf_event_write_access");
2306    if_selinux_else_default_ok(current_task, |security_server| {
2307        selinux_hooks::perf_event::check_perf_event_write_access(
2308            security_server,
2309            current_task,
2310            perf_event_file,
2311        )
2312    })
2313}
2314
2315/// Identifies one of the Security Context attributes associated with a task.
2316#[derive(Debug, Clone, Copy, PartialEq)]
2317pub enum ProcAttr {
2318    Current,
2319    Exec,
2320    FsCreate,
2321    KeyCreate,
2322    Previous,
2323    SockCreate,
2324}
2325
2326/// Returns the Security Context associated with the `name`ed entry for the specified `target` task.
2327/// Corresponds to the `getprocattr()` LSM hook.
2328pub fn get_procattr(
2329    current_task: &CurrentTask,
2330    target: &Task,
2331    attr: ProcAttr,
2332) -> Result<Vec<u8>, Errno> {
2333    track_hook_duration!("security.hooks.get_procattr");
2334    if_selinux_else(
2335        current_task,
2336        |security_server| {
2337            selinux_hooks::task::get_procattr(security_server, current_task, target, attr)
2338        },
2339        // If SELinux is disabled then there are no values to return.
2340        || error!(EINVAL),
2341    )
2342}
2343
2344/// Sets the Security Context associated with the `name`ed entry for the current task.
2345/// Corresponds to the `setprocattr()` LSM hook.
2346pub fn set_procattr(
2347    current_task: &CurrentTask,
2348    attr: ProcAttr,
2349    context: &[u8],
2350) -> Result<(), Errno> {
2351    track_hook_duration!("security.hooks.set_procattr");
2352    if_selinux_else(
2353        current_task,
2354        |security_server| {
2355            selinux_hooks::task::set_procattr(security_server, current_task, attr, context)
2356        },
2357        // If SELinux is disabled then no writes are accepted.
2358        || error!(EINVAL),
2359    )
2360}
2361
2362/// Returns true if SELinux is enabled on the kernel for this task.
2363pub fn fs_is_xattr_labeled(fs: FileSystemHandle) -> bool {
2364    fs.security_state.state.supports_xattr()
2365}
2366
2367/// Stashes a reference to the selinuxfs null file for later use by hooks that remap
2368/// inaccessible file descriptors to null.
2369pub fn selinuxfs_init_null(current_task: &CurrentTask, null_fs_node: &FileHandle) {
2370    // Note: No `if_selinux_...` guard because hook is invoked inside selinuxfs initialization code;
2371    // i.e., hook is only invoked when selinux is enabled.
2372    selinux_hooks::selinuxfs::selinuxfs_init_null(current_task, null_fs_node)
2373}
2374
2375/// Called by the "selinuxfs" when a policy has been successfully loaded, to allow policy-dependent
2376/// initialization to be completed. This includes resolving labeling schemes and state for
2377/// file-systems mounted prior to policy load (e.g. the "selinuxfs" itself), and initializing
2378/// security state for any file nodes they may already contain.
2379// TODO: https://fxbug.dev/362917997 - Remove this when SELinux LSM is modularized.
2380pub fn selinuxfs_policy_loaded(current_task: &CurrentTask) {
2381    track_hook_duration!("security.hooks.selinuxfs_policy_loaded");
2382    selinux_hooks::selinuxfs::selinuxfs_policy_loaded(current_task)
2383}
2384
2385/// Used by the "selinuxfs" module to access the SELinux administration API, if enabled.
2386// TODO: https://fxbug.dev/335397745 - Return a more restricted API, or ...
2387// TODO: https://fxbug.dev/362917997 - Remove this when SELinux LSM is modularized.
2388pub fn selinuxfs_get_admin_api(current_task: &CurrentTask) -> Option<Arc<SecurityServer>> {
2389    current_task.kernel().security_state.state.as_ref().map(|state| state.server.clone())
2390}
2391
2392/// Used by the "selinuxfs" module to perform checks on SELinux API file accesses.
2393// TODO: https://fxbug.dev/362917997 - Remove this when SELinux LSM is modularized.
2394pub fn selinuxfs_check_access(
2395    current_task: &CurrentTask,
2396    permission: SecurityPermission,
2397) -> Result<(), Errno> {
2398    track_hook_duration!("security.hooks.selinuxfs_check_access");
2399    if_selinux_else_default_ok(current_task, |security_server| {
2400        selinux_hooks::selinuxfs::selinuxfs_check_access(security_server, current_task, permission)
2401    })
2402}
2403
2404/// Marks the credentials as being used for an internal operation. All SELinux permission checks
2405/// will be skipped on this task.
2406pub fn creds_start_internal_operation(current_task: &CurrentTask) -> Arc<Credentials> {
2407    track_hook_duration!("security.hooks.creds_start_internal_operation");
2408    let mut creds = Credentials::clone(&current_task.current_creds());
2409    creds.security_state.internal_operation = true;
2410    creds.into()
2411}
2412
2413pub mod testing {
2414    use super::{Arc, KernelState, SecurityServer, selinux_hooks};
2415    use starnix_sync::LockDepMutex;
2416    use std::sync::OnceLock;
2417    use std::sync::atomic::AtomicU64;
2418
2419    /// Used by Starnix' `testing.rs` to create `KernelState` wrapping a test-
2420    /// supplied `SecurityServer`.
2421    pub fn kernel_state(security_server: Option<Arc<SecurityServer>>) -> KernelState {
2422        let state = security_server.map(|server| selinux_hooks::KernelState {
2423            server,
2424            pending_file_systems: LockDepMutex::default(),
2425            selinuxfs_null: OnceLock::default(),
2426            access_denial_count: AtomicU64::new(0u64),
2427            has_policy: false.into(),
2428            _inspect_node: fuchsia_inspect::Node::default(),
2429        });
2430        KernelState { state }
2431    }
2432}
2433
2434#[cfg(test)]
2435mod tests {
2436    use super::*;
2437    use crate::device::mem::new_null_file;
2438    use crate::mm::DumpPolicy;
2439    use crate::security;
2440    use crate::security::selinux_hooks::get_cached_sid;
2441    use crate::security::selinux_hooks::testing::{
2442        self, spawn_kernel_with_selinux_hooks_test_policy_and_run,
2443    };
2444    use crate::testing::{
2445        create_task, create_task_with_security_context, spawn_kernel_and_run,
2446        spawn_kernel_with_selinux_and_run,
2447    };
2448    use linux_uapi::XATTR_NAME_SELINUX;
2449    use selinux::InitialSid;
2450    use starnix_uapi::auth::{
2451        CAP_SYS_PTRACE, PTRACE_MODE_ATTACH, PTRACE_MODE_ATTACH_REALCREDS, PTRACE_MODE_NOAUDIT,
2452        PTRACE_MODE_READ_FSCREDS,
2453    };
2454    use starnix_uapi::signals::SIGTERM;
2455    use std::ffi::CString;
2456    use std::sync::atomic::Ordering;
2457
2458    const VALID_SECURITY_CONTEXT: &[u8] = b"u:object_r:test_valid_t:s0";
2459    const VALID_SECURITY_CONTEXT_WITH_NUL: &[u8] = b"u:object_r:test_valid_t:s0\0";
2460
2461    const DIFFERENT_VALID_SECURITY_CONTEXT: &[u8] = b"u:object_r:test_different_valid_t:s0";
2462    const DIFFERENT_VALID_SECURITY_CONTEXT_WITH_NUL: &[u8] =
2463        b"u:object_r:test_different_valid_t:s0\0";
2464
2465    const INVALID_SECURITY_CONTEXT_INTERNAL_NUL: &[u8] = b"u:object_r:test_valid_\0t:s0";
2466
2467    const INVALID_SECURITY_CONTEXT: &[u8] = b"not_a_u:object_r:test_valid_t:s0";
2468
2469    #[derive(Default, Debug, PartialEq)]
2470    enum TestHookResult {
2471        WasRun,
2472        WasNotRun,
2473        #[default]
2474        WasNotRunDefault,
2475    }
2476
2477    #[fuchsia::test]
2478    async fn if_selinux_else_disabled() {
2479        spawn_kernel_and_run(async |current_task| {
2480            assert!(current_task.kernel().security_state.state.is_none());
2481
2482            let check_result =
2483                if_selinux_else_default_ok(current_task, |_| Ok(TestHookResult::WasRun));
2484            assert_eq!(check_result, Ok(TestHookResult::WasNotRunDefault));
2485
2486            let run_else_result = if_selinux_else(
2487                current_task,
2488                |_| TestHookResult::WasRun,
2489                || TestHookResult::WasNotRun,
2490            );
2491            assert_eq!(run_else_result, TestHookResult::WasNotRun);
2492        })
2493        .await;
2494    }
2495
2496    #[fuchsia::test]
2497    async fn if_selinux_else_without_policy() {
2498        spawn_kernel_with_selinux_and_run(async |current_task, _security_server| {
2499            let check_result =
2500                if_selinux_else_default_ok(current_task, |_| Ok(TestHookResult::WasRun));
2501            assert_eq!(check_result, Ok(TestHookResult::WasNotRunDefault));
2502
2503            let run_else_result = if_selinux_else(
2504                current_task,
2505                |_| TestHookResult::WasRun,
2506                || TestHookResult::WasNotRun,
2507            );
2508            assert_eq!(run_else_result, TestHookResult::WasNotRun);
2509        })
2510        .await;
2511    }
2512
2513    #[fuchsia::test]
2514    async fn if_selinux_else_with_policy() {
2515        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, _security_server| {
2516            let check_result =
2517                if_selinux_else_default_ok(current_task, |_| Ok(TestHookResult::WasRun));
2518            assert_eq!(check_result, Ok(TestHookResult::WasRun));
2519
2520            let run_else_result = if_selinux_else(
2521                current_task,
2522                |_| TestHookResult::WasRun,
2523                || TestHookResult::WasNotRun,
2524            );
2525            assert_eq!(run_else_result, TestHookResult::WasRun);
2526        })
2527        .await;
2528    }
2529
2530    #[fuchsia::test]
2531    async fn task_create_access_allowed_for_selinux_disabled() {
2532        spawn_kernel_and_run(async |current_task| {
2533            assert!(current_task.kernel().security_state.state.is_none());
2534            assert_eq!(check_task_create_access(current_task), Ok(()));
2535        })
2536        .await;
2537    }
2538
2539    #[fuchsia::test]
2540    async fn task_create_access_allowed_for_permissive_mode() {
2541        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, security_server| {
2542            security_server.set_enforcing(false);
2543            assert_eq!(check_task_create_access(current_task), Ok(()));
2544        })
2545        .await;
2546    }
2547
2548    #[fuchsia::test]
2549    async fn exec_access_allowed_for_selinux_disabled() {
2550        spawn_kernel_and_run(async |current_task| {
2551            assert!(current_task.kernel().security_state.state.is_none());
2552            let executable = testing::create_test_file(current_task);
2553            let file = testing::open_test_file(current_task, &executable);
2554            let mut resolved_program = testing::make_resolved_program(current_task, file);
2555            assert_eq!(bprm_creds_for_exec(current_task, &mut resolved_program), Ok(()));
2556        })
2557        .await;
2558    }
2559
2560    #[fuchsia::test]
2561    async fn exec_access_allowed_for_permissive_mode() {
2562        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, security_server| {
2563            security_server.set_enforcing(false);
2564            let executable = testing::create_test_file(current_task);
2565            let file = testing::open_test_file(current_task, &executable);
2566            let mut resolved_program = testing::make_resolved_program(current_task, file);
2567            // Expect that access is granted.
2568            let result = bprm_creds_for_exec(current_task, &mut resolved_program);
2569            assert!(result.is_ok());
2570        })
2571        .await;
2572    }
2573
2574    #[fuchsia::test]
2575    async fn exec_no_state_update_for_selinux_disabled() {
2576        spawn_kernel_and_run(async |current_task| {
2577            let target_sid = InitialSid::Unlabeled.into();
2578
2579            assert!(selinux_hooks::current_task_state(current_task).current_sid != target_sid);
2580
2581            // Set exec_sid to cause the hook to apply a transition, to verify if it is updated or not.
2582            testing::mutate_attrs_for_test(current_task, |attrs| {
2583                attrs.exec_sid = Some(target_sid);
2584            });
2585
2586            let executable = testing::create_test_file(current_task);
2587            let file = testing::open_test_file(current_task, &executable);
2588            let mut resolved_program = testing::make_resolved_program(current_task, file);
2589
2590            let before_hook_sid = selinux_hooks::current_task_state(current_task).current_sid;
2591
2592            bprm_creds_for_exec(current_task, &mut resolved_program).unwrap();
2593            assert_eq!(resolved_program.creds.security_state.current_sid, before_hook_sid);
2594        })
2595        .await;
2596    }
2597
2598    #[fuchsia::test]
2599    async fn exec_initial_context_for_selinux_without_policy() {
2600        spawn_kernel_with_selinux_and_run(async |current_task, _security_server| {
2601            let elf_sid = InitialSid::Unlabeled.into();
2602            assert_ne!(selinux_hooks::current_task_state(current_task).current_sid, elf_sid);
2603            assert_ne!(
2604                selinux_hooks::current_task_state(current_task).current_sid,
2605                InitialSid::Init.into()
2606            );
2607
2608            // Set exec_sid to cause the hook to apply a transition, to verify if it is updated or not.
2609            testing::mutate_attrs_for_test(current_task, |attrs| {
2610                attrs.exec_sid = Some(elf_sid);
2611            });
2612
2613            let executable = testing::create_test_file(current_task);
2614            let file = testing::open_test_file(current_task, &executable);
2615            let mut resolved_program = testing::make_resolved_program(current_task, file);
2616
2617            bprm_creds_for_exec(current_task, &mut resolved_program).unwrap();
2618
2619            assert_eq!(resolved_program.creds.security_state.current_sid, InitialSid::Init.into());
2620        })
2621        .await;
2622    }
2623
2624    #[fuchsia::test]
2625    async fn exec_state_update_for_permissive_mode() {
2626        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, security_server| {
2627            security_server.set_enforcing(false);
2628            let elf_sid = security_server
2629                .security_context_to_sid(b"u:object_r:fork_no_t:s0".into())
2630                .expect("invalid security context");
2631
2632            assert_ne!(elf_sid, selinux_hooks::current_task_state(current_task).current_sid);
2633
2634            // Set exec_sid to cause the hook to apply a transition, to verify if it is updated or not.
2635            testing::mutate_attrs_for_test(current_task, |attrs| {
2636                attrs.exec_sid = Some(elf_sid);
2637            });
2638
2639            let executable = testing::create_test_file(current_task);
2640            let file = testing::open_test_file(current_task, &executable);
2641            let mut resolved_program = testing::make_resolved_program(current_task, file);
2642
2643            bprm_creds_for_exec(current_task, &mut resolved_program).unwrap();
2644            assert_eq!(resolved_program.creds.security_state.current_sid, elf_sid);
2645        })
2646        .await;
2647    }
2648
2649    #[fuchsia::test]
2650    async fn getsched_access_allowed_for_selinux_disabled() {
2651        spawn_kernel_and_run(async |current_task| {
2652            let another_task = create_task(&current_task.kernel(), "another-task");
2653            assert_eq!(check_task_getscheduler_access(current_task, &another_task), Ok(()));
2654        })
2655        .await;
2656    }
2657
2658    #[fuchsia::test]
2659    async fn getsched_access_allowed_for_permissive_mode() {
2660        spawn_kernel_with_selinux_and_run(async |current_task, _security_server| {
2661            let another_task = create_task(&current_task.kernel(), "another-task");
2662            assert_eq!(check_task_getscheduler_access(current_task, &another_task), Ok(()));
2663        })
2664        .await;
2665    }
2666
2667    #[fuchsia::test]
2668    async fn setsched_access_allowed_for_selinux_disabled() {
2669        spawn_kernel_and_run(async |current_task| {
2670            let another_task = create_task(&current_task.kernel(), "another-task");
2671            assert_eq!(check_task_setscheduler_access(current_task, &another_task), Ok(()));
2672        })
2673        .await;
2674    }
2675
2676    #[fuchsia::test]
2677    async fn setsched_access_allowed_for_permissive_mode() {
2678        spawn_kernel_with_selinux_and_run(async |current_task, _security_server| {
2679            let another_task = create_task(&current_task.kernel(), "another-task");
2680            assert_eq!(check_task_setscheduler_access(current_task, &another_task), Ok(()));
2681        })
2682        .await;
2683    }
2684
2685    #[fuchsia::test]
2686    async fn getpgid_access_allowed_for_selinux_disabled() {
2687        spawn_kernel_and_run(async |current_task| {
2688            let another_task = create_task(&current_task.kernel(), "another-task");
2689            assert_eq!(check_getpgid_access(current_task, &another_task), Ok(()));
2690        })
2691        .await;
2692    }
2693
2694    #[fuchsia::test]
2695    async fn getpgid_access_allowed_for_permissive_mode() {
2696        spawn_kernel_with_selinux_and_run(async |current_task, _security_server| {
2697            let another_task = create_task(&current_task.kernel(), "another-task");
2698            assert_eq!(check_getpgid_access(current_task, &another_task), Ok(()));
2699        })
2700        .await;
2701    }
2702
2703    #[fuchsia::test]
2704    async fn setpgid_access_allowed_for_selinux_disabled() {
2705        spawn_kernel_and_run(async |current_task| {
2706            let another_task = create_task(&current_task.kernel(), "another-task");
2707            assert_eq!(check_setpgid_access(current_task, &another_task), Ok(()));
2708        })
2709        .await;
2710    }
2711
2712    #[fuchsia::test]
2713    async fn setpgid_access_allowed_for_permissive_mode() {
2714        spawn_kernel_with_selinux_and_run(async |current_task, _security_server| {
2715            let another_task = create_task(&current_task.kernel(), "another-task");
2716            assert_eq!(check_setpgid_access(current_task, &another_task), Ok(()));
2717        })
2718        .await;
2719    }
2720
2721    #[fuchsia::test]
2722    async fn task_getsid_allowed_for_selinux_disabled() {
2723        spawn_kernel_and_run(async |current_task| {
2724            let another_task = create_task(&current_task.kernel(), "another-task");
2725            assert_eq!(check_task_getsid(current_task, &another_task), Ok(()));
2726        })
2727        .await;
2728    }
2729
2730    #[fuchsia::test]
2731    async fn task_getsid_allowed_for_permissive_mode() {
2732        spawn_kernel_with_selinux_and_run(async |current_task, _security_server| {
2733            let another_task = create_task(&current_task.kernel(), "another-task");
2734            assert_eq!(check_task_getsid(current_task, &another_task), Ok(()));
2735        })
2736        .await;
2737    }
2738
2739    #[fuchsia::test]
2740    async fn signal_access_allowed_for_selinux_disabled() {
2741        spawn_kernel_and_run(async |current_task| {
2742            let another_task = create_task(&current_task.kernel(), "another-task");
2743            assert_eq!(check_signal_access(current_task, &another_task, SIGTERM), Ok(()));
2744        })
2745        .await;
2746    }
2747
2748    #[fuchsia::test]
2749    async fn signal_access_allowed_for_permissive_mode() {
2750        spawn_kernel_with_selinux_and_run(async |current_task, _security_server| {
2751            let another_task = create_task(&current_task.kernel(), "another-task");
2752            assert_eq!(check_signal_access(current_task, &another_task, SIGTERM), Ok(()));
2753        })
2754        .await;
2755    }
2756
2757    #[fuchsia::test]
2758    async fn ptrace_traceme_access_allowed_for_selinux_disabled() {
2759        spawn_kernel_and_run(async |current_task| {
2760            let another_task = create_task(&current_task.kernel(), "another-task");
2761            assert_eq!(ptrace_traceme(current_task, &another_task), Ok(()));
2762        })
2763        .await;
2764    }
2765
2766    #[fuchsia::test]
2767    async fn ptrace_traceme_access_allowed_for_permissive_mode() {
2768        spawn_kernel_with_selinux_and_run(async |current_task, _security_server| {
2769            let another_task = create_task(&current_task.kernel(), "another-task");
2770            assert_eq!(ptrace_traceme(current_task, &another_task), Ok(()));
2771        })
2772        .await;
2773    }
2774
2775    #[fuchsia::test]
2776    async fn ptrace_attach_access_allowed_for_selinux_disabled() {
2777        spawn_kernel_and_run(async |current_task| {
2778            let another_task = create_task(&current_task.kernel(), "another-task");
2779            assert_eq!(
2780                ptrace_access_check(current_task, &another_task, PTRACE_MODE_ATTACH),
2781                Ok(())
2782            );
2783        })
2784        .await;
2785    }
2786
2787    #[fuchsia::test]
2788    async fn ptrace_attach_access_allowed_for_permissive_mode() {
2789        spawn_kernel_with_selinux_and_run(async |current_task, _security_server| {
2790            let another_task = create_task(&current_task.kernel(), "another-task");
2791            assert_eq!(
2792                ptrace_access_check(current_task, &another_task, PTRACE_MODE_ATTACH),
2793                Ok(())
2794            );
2795        })
2796        .await;
2797    }
2798
2799    #[fuchsia::test]
2800    async fn task_prlimit_access_allowed_for_selinux_disabled() {
2801        spawn_kernel_and_run(async |current_task| {
2802            let another_task = create_task(&current_task.kernel(), "another-task");
2803            assert_eq!(task_prlimit(current_task, &another_task, true, true), Ok(()));
2804        })
2805        .await;
2806    }
2807
2808    #[fuchsia::test]
2809    async fn task_prlimit_access_allowed_for_permissive_mode() {
2810        spawn_kernel_with_selinux_and_run(async |current_task, _security_server| {
2811            let another_task = create_task(&current_task.kernel(), "another-task");
2812            assert_eq!(task_prlimit(current_task, &another_task, true, true), Ok(()));
2813        })
2814        .await;
2815    }
2816
2817    #[fuchsia::test]
2818    async fn fs_node_task_to_fs_node_noop_selinux_disabled() {
2819        spawn_kernel_and_run(async |current_task| {
2820            let node = &testing::create_test_file(current_task).entry.node;
2821            task_to_fs_node(current_task, &current_task.task, &node);
2822            assert_eq!(None, selinux_hooks::get_cached_sid(node));
2823        })
2824        .await;
2825    }
2826
2827    #[fuchsia::test]
2828    async fn fs_node_setsecurity_selinux_disabled_only_sets_xattr() {
2829        spawn_kernel_and_run(async |current_task| {
2830            let node = &testing::create_test_file(current_task).entry.node;
2831
2832            fs_node_setsecurity(
2833                &current_task,
2834                &node,
2835                XATTR_NAME_SELINUX.to_bytes().into(),
2836                VALID_SECURITY_CONTEXT.into(),
2837                XattrOp::Set,
2838            )
2839            .expect("set_xattr(security.selinux) failed");
2840
2841            assert_eq!(None, selinux_hooks::get_cached_sid(node));
2842        })
2843        .await;
2844    }
2845
2846    #[fuchsia::test]
2847    async fn fs_node_setsecurity_selinux_without_policy_only_sets_xattr() {
2848        spawn_kernel_with_selinux_and_run(async |current_task, _security_server| {
2849            let node = &testing::create_test_file(current_task).entry.node;
2850            fs_node_setsecurity(
2851                &current_task,
2852                &node,
2853                XATTR_NAME_SELINUX.to_bytes().into(),
2854                VALID_SECURITY_CONTEXT.into(),
2855                XattrOp::Set,
2856            )
2857            .expect("set_xattr(security.selinux) failed");
2858
2859            assert_eq!(None, selinux_hooks::get_cached_sid(node));
2860        })
2861        .await;
2862    }
2863
2864    #[fuchsia::test]
2865    async fn fs_node_setsecurity_selinux_permissive_sets_xattr_and_label() {
2866        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, security_server| {
2867            security_server.set_enforcing(false);
2868            let expected_sid = security_server
2869                .security_context_to_sid(VALID_SECURITY_CONTEXT.into())
2870                .expect("no SID for VALID_SECURITY_CONTEXT");
2871            let node = &testing::create_test_file(&current_task).entry.node;
2872
2873            // Safeguard against a false positive by ensuring `expected_sid` is not already the file's label.
2874            assert_ne!(Some(expected_sid), selinux_hooks::get_cached_sid(node));
2875
2876            fs_node_setsecurity(
2877                &current_task,
2878                &node,
2879                XATTR_NAME_SELINUX.to_bytes().into(),
2880                VALID_SECURITY_CONTEXT.into(),
2881                XattrOp::Set,
2882            )
2883            .expect("set_xattr(security.selinux) failed");
2884
2885            // Verify that the SID now cached on the node is that SID
2886            // corresponding to VALID_SECURITY_CONTEXT.
2887            assert_eq!(Some(expected_sid), selinux_hooks::get_cached_sid(node));
2888        })
2889        .await;
2890    }
2891
2892    #[fuchsia::test]
2893    async fn fs_node_setsecurity_not_selinux_only_sets_xattr() {
2894        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, security_server| {
2895            let valid_security_context_sid = security_server
2896                .security_context_to_sid(VALID_SECURITY_CONTEXT.into())
2897                .expect("no SID for VALID_SECURITY_CONTEXT");
2898            let node = &testing::create_test_file(current_task).entry.node;
2899            // The label assigned to the test file on creation must differ from
2900            // VALID_SECURITY_CONTEXT, otherwise this test may return a false
2901            // positive.
2902            let whatever_sid = selinux_hooks::get_cached_sid(node);
2903            assert_ne!(Some(valid_security_context_sid), whatever_sid);
2904
2905            fs_node_setsecurity(
2906                &current_task,
2907                &node,
2908                "security.selinu!".into(), // Note: name != "security.selinux".
2909                VALID_SECURITY_CONTEXT.into(),
2910                XattrOp::Set,
2911            )
2912            .expect("set_xattr(security.selinux) failed");
2913
2914            // Verify that the node's SID (whatever it was) has not changed.
2915            assert_eq!(whatever_sid, selinux_hooks::get_cached_sid(node));
2916        })
2917        .await;
2918    }
2919
2920    #[fuchsia::test]
2921    async fn fs_node_setsecurity_selinux_enforcing_invalid_context_fails() {
2922        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, _security_server| {
2923            let node = &testing::create_test_file(current_task).entry.node;
2924
2925            let before_sid = selinux_hooks::get_cached_sid(node);
2926            assert_ne!(Some(InitialSid::Unlabeled.into()), before_sid);
2927
2928            assert!(
2929                check_fs_node_setxattr_access(
2930                    &current_task,
2931                    &node,
2932                    XATTR_NAME_SELINUX.to_bytes().into(),
2933                    "!".into(), // Note: Not a valid security context.
2934                    XattrOp::Set,
2935                )
2936                .is_err()
2937            );
2938
2939            assert_eq!(before_sid, selinux_hooks::get_cached_sid(node));
2940        })
2941        .await;
2942    }
2943
2944    #[fuchsia::test]
2945    async fn fs_node_setsecurity_selinux_permissive_invalid_context_sets_xattr_and_label() {
2946        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, security_server| {
2947            security_server.set_enforcing(false);
2948            let node = &testing::create_test_file(current_task).entry.node;
2949
2950            assert_ne!(Some(InitialSid::Unlabeled.into()), selinux_hooks::get_cached_sid(node));
2951
2952            fs_node_setsecurity(
2953                &current_task,
2954                &node,
2955                XATTR_NAME_SELINUX.to_bytes().into(),
2956                "!".into(), // Note: Not a valid security context.
2957                XattrOp::Set,
2958            )
2959            .expect("set_xattr(security.selinux) failed");
2960
2961            assert_eq!(Some(InitialSid::Unlabeled.into()), selinux_hooks::get_cached_sid(node));
2962        })
2963        .await;
2964    }
2965
2966    #[fuchsia::test]
2967    async fn fs_node_setsecurity_different_sid_for_different_context() {
2968        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, _security_server| {
2969            let node = &testing::create_test_file(current_task).entry.node;
2970
2971            fs_node_setsecurity(
2972                &current_task,
2973                &node,
2974                XATTR_NAME_SELINUX.to_bytes().into(),
2975                VALID_SECURITY_CONTEXT.into(),
2976                XattrOp::Set,
2977            )
2978            .expect("set_xattr(security.selinux) failed");
2979
2980            assert!(selinux_hooks::get_cached_sid(node).is_some());
2981
2982            let first_sid = selinux_hooks::get_cached_sid(node).unwrap();
2983            fs_node_setsecurity(
2984                &current_task,
2985                &node,
2986                XATTR_NAME_SELINUX.to_bytes().into(),
2987                DIFFERENT_VALID_SECURITY_CONTEXT.into(),
2988                XattrOp::Set,
2989            )
2990            .expect("set_xattr(security.selinux) failed");
2991
2992            assert!(selinux_hooks::get_cached_sid(node).is_some());
2993
2994            let second_sid = selinux_hooks::get_cached_sid(node).unwrap();
2995
2996            assert_ne!(first_sid, second_sid);
2997        })
2998        .await;
2999    }
3000
3001    #[fuchsia::test]
3002    async fn fs_node_getsecurity_returns_cached_context() {
3003        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, security_server| {
3004            let node = &testing::create_test_file(current_task).entry.node;
3005
3006            // Set a mismatched value in `node`'s "security.seliux" attribute.
3007            const TEST_VALUE: &str = "Something Random";
3008            node.ops()
3009                .set_xattr(
3010                    node,
3011                    current_task,
3012                    XATTR_NAME_SELINUX.to_bytes().into(),
3013                    TEST_VALUE.into(),
3014                    XattrOp::Set,
3015                )
3016                .expect("set_xattr(security.selinux) failed");
3017
3018            // Attach a valid SID to the `node`.
3019            let sid = security_server
3020                .security_context_to_sid(VALID_SECURITY_CONTEXT.into())
3021                .expect("security context to SID");
3022            selinux_hooks::set_cached_sid(&node, sid);
3023
3024            // Reading the security attribute should return the Security Context for the SID, rather than delegating.
3025            let result =
3026                fs_node_getsecurity(current_task, node, XATTR_NAME_SELINUX.to_bytes().into(), 4096);
3027            assert_eq!(
3028                result,
3029                Ok(ValueOrSize::Value(FsString::new(VALID_SECURITY_CONTEXT_WITH_NUL.into())))
3030            );
3031        })
3032        .await;
3033    }
3034
3035    #[fuchsia::test]
3036    async fn fs_node_getsecurity_delegates_to_get_xattr() {
3037        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, security_server| {
3038            let node = &testing::create_test_file(current_task).entry.node;
3039
3040            // Set an invalid value in `node`'s "security.selinux" attribute.
3041            // This requires SELinux to be in permissive mode, otherwise the "relabelto" permission check will fail.
3042            security_server.set_enforcing(false);
3043            const TEST_VALUE: &str = "Something Random";
3044            fs_node_setsecurity(
3045                &current_task,
3046                node,
3047                XATTR_NAME_SELINUX.to_bytes().into(),
3048                TEST_VALUE.into(),
3049                XattrOp::Set,
3050            )
3051            .expect("set_xattr(security.selinux) failed");
3052            security_server.set_enforcing(true);
3053
3054            // Reading the security attribute should pass-through to read the value from the file system.
3055            let result =
3056                fs_node_getsecurity(current_task, node, XATTR_NAME_SELINUX.to_bytes().into(), 4096);
3057            assert_eq!(result, Ok(ValueOrSize::Value(FsString::new(TEST_VALUE.into()))));
3058        })
3059        .await;
3060    }
3061
3062    #[fuchsia::test]
3063    async fn set_get_procattr() {
3064        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, _security_server| {
3065            assert_eq!(get_procattr(current_task, current_task, ProcAttr::Exec), Ok(Vec::new()));
3066
3067            assert_eq!(
3068                // Test policy allows "kernel_t" tasks to set the "exec" context.
3069                set_procattr(current_task, ProcAttr::Exec, VALID_SECURITY_CONTEXT.into()),
3070                Ok(())
3071            );
3072
3073            assert_eq!(
3074                // Test policy does not allow "kernel_t" tasks to set the "sockcreate" context.
3075                set_procattr(
3076                    current_task,
3077                    ProcAttr::SockCreate,
3078                    DIFFERENT_VALID_SECURITY_CONTEXT.into()
3079                ),
3080                error!(EACCES)
3081            );
3082
3083            assert_eq!(
3084                // It is never permitted to set the "previous" context.
3085                set_procattr(
3086                    current_task,
3087                    ProcAttr::Previous,
3088                    DIFFERENT_VALID_SECURITY_CONTEXT.into()
3089                ),
3090                error!(EINVAL)
3091            );
3092
3093            assert_eq!(
3094                // Cannot set an invalid context.
3095                set_procattr(current_task, ProcAttr::Exec, INVALID_SECURITY_CONTEXT.into()),
3096                error!(EINVAL)
3097            );
3098
3099            assert_eq!(
3100                get_procattr(current_task, current_task, ProcAttr::Exec),
3101                Ok(VALID_SECURITY_CONTEXT_WITH_NUL.into())
3102            );
3103
3104            assert!(get_procattr(current_task, current_task, ProcAttr::Current).is_ok());
3105        })
3106        .await;
3107    }
3108
3109    #[fuchsia::test]
3110    async fn set_get_procattr_with_nulls() {
3111        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, _security_server| {
3112            assert_eq!(get_procattr(current_task, current_task, ProcAttr::Exec), Ok(Vec::new()));
3113
3114            assert_eq!(
3115                // Setting a Context with a string with trailing null(s) should work, if the Context is valid.
3116                set_procattr(current_task, ProcAttr::Exec, VALID_SECURITY_CONTEXT_WITH_NUL.into()),
3117                Ok(())
3118            );
3119
3120            assert_eq!(
3121                // Nulls in the middle of an otherwise valid Context truncate it, rendering it invalid.
3122                set_procattr(
3123                    current_task,
3124                    ProcAttr::FsCreate,
3125                    INVALID_SECURITY_CONTEXT_INTERNAL_NUL.into()
3126                ),
3127                error!(EINVAL)
3128            );
3129
3130            assert_eq!(
3131                get_procattr(current_task, current_task, ProcAttr::Exec),
3132                Ok(VALID_SECURITY_CONTEXT_WITH_NUL.into())
3133            );
3134
3135            assert_eq!(
3136                get_procattr(current_task, current_task, ProcAttr::FsCreate),
3137                Ok(Vec::new())
3138            );
3139        })
3140        .await;
3141    }
3142
3143    #[fuchsia::test]
3144    async fn set_get_procattr_clear_context() {
3145        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, _security_server| {
3146            // Set up the "exec" and "fscreate" Contexts with valid values.
3147            assert_eq!(
3148                set_procattr(current_task, ProcAttr::Exec, VALID_SECURITY_CONTEXT.into()),
3149                Ok(())
3150            );
3151            assert_eq!(
3152                set_procattr(
3153                    current_task,
3154                    ProcAttr::FsCreate,
3155                    DIFFERENT_VALID_SECURITY_CONTEXT.into()
3156                ),
3157                Ok(())
3158            );
3159
3160            // Clear the "exec" context with a write containing a single null octet.
3161            assert_eq!(set_procattr(current_task, ProcAttr::Exec, b"\0"), Ok(()));
3162            assert_eq!(current_task.current_creds().security_state.exec_sid, None);
3163
3164            // Clear the "fscreate" context with a write containing a single newline.
3165            assert_eq!(set_procattr(current_task, ProcAttr::FsCreate, b"\x0a"), Ok(()));
3166            assert_eq!(current_task.current_creds().security_state.fscreate_sid, None);
3167        })
3168        .await;
3169    }
3170
3171    #[fuchsia::test]
3172    async fn set_get_procattr_setcurrent() {
3173        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, _security_server| {
3174            // Stash the initial "previous" context.
3175            let initial_previous =
3176                get_procattr(current_task, current_task, ProcAttr::Previous).unwrap();
3177
3178            assert_eq!(
3179                // Dynamically transition to a valid new context.
3180                set_procattr(current_task, ProcAttr::Current, VALID_SECURITY_CONTEXT.into()),
3181                Ok(())
3182            );
3183
3184            assert_eq!(
3185                // "current" should report the new context.
3186                get_procattr(current_task, current_task, ProcAttr::Current),
3187                Ok(VALID_SECURITY_CONTEXT_WITH_NUL.into())
3188            );
3189
3190            assert_eq!(
3191                // "prev" should continue to report the original context.
3192                get_procattr(current_task, current_task, ProcAttr::Previous),
3193                Ok(initial_previous.clone())
3194            );
3195
3196            assert_eq!(
3197                // Dynamically transition to a different valid context.
3198                set_procattr(
3199                    current_task,
3200                    ProcAttr::Current,
3201                    DIFFERENT_VALID_SECURITY_CONTEXT.into()
3202                ),
3203                Ok(())
3204            );
3205
3206            assert_eq!(
3207                // "current" should report the different new context.
3208                get_procattr(current_task, current_task, ProcAttr::Current),
3209                Ok(DIFFERENT_VALID_SECURITY_CONTEXT_WITH_NUL.into())
3210            );
3211
3212            assert_eq!(
3213                // "prev" should continue to report the original context.
3214                get_procattr(current_task, current_task, ProcAttr::Previous),
3215                Ok(initial_previous.clone())
3216            );
3217        })
3218        .await;
3219    }
3220
3221    #[fuchsia::test]
3222    async fn set_get_procattr_selinux_permissive() {
3223        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, security_server| {
3224            security_server.set_enforcing(false);
3225            assert_eq!(
3226                get_procattr(current_task, &current_task.task, ProcAttr::Exec),
3227                Ok(Vec::new())
3228            );
3229
3230            assert_eq!(
3231                // Test policy allows "kernel_t" tasks to set the "exec" context.
3232                set_procattr(current_task, ProcAttr::Exec, VALID_SECURITY_CONTEXT.into()),
3233                Ok(())
3234            );
3235
3236            assert_eq!(
3237                // Test policy does not allow "kernel_t" tasks to set the "fscreate" context, but
3238                // in permissive mode the setting will be allowed.
3239                set_procattr(
3240                    current_task,
3241                    ProcAttr::FsCreate,
3242                    DIFFERENT_VALID_SECURITY_CONTEXT.into()
3243                ),
3244                Ok(())
3245            );
3246
3247            assert_eq!(
3248                // Setting an invalid context should fail, even in permissive mode.
3249                set_procattr(current_task, ProcAttr::Exec, INVALID_SECURITY_CONTEXT.into()),
3250                error!(EINVAL)
3251            );
3252
3253            assert_eq!(
3254                get_procattr(current_task, &current_task.task, ProcAttr::Exec),
3255                Ok(VALID_SECURITY_CONTEXT_WITH_NUL.into())
3256            );
3257
3258            assert!(get_procattr(current_task, &current_task.task, ProcAttr::Current).is_ok());
3259        })
3260        .await;
3261    }
3262
3263    #[fuchsia::test]
3264    async fn set_get_procattr_selinux_disabled() {
3265        spawn_kernel_and_run(async |current_task| {
3266            assert_eq!(
3267                set_procattr(&current_task, ProcAttr::Exec, VALID_SECURITY_CONTEXT.into()),
3268                error!(EINVAL)
3269            );
3270
3271            assert_eq!(
3272                // Test policy allows "kernel_t" tasks to set the "exec" context.
3273                set_procattr(&current_task, ProcAttr::Exec, VALID_SECURITY_CONTEXT.into()),
3274                error!(EINVAL)
3275            );
3276
3277            assert_eq!(
3278                // Test policy does not allow "kernel_t" tasks to set the "fscreate" context.
3279                set_procattr(&current_task, ProcAttr::FsCreate, VALID_SECURITY_CONTEXT.into()),
3280                error!(EINVAL)
3281            );
3282
3283            assert_eq!(
3284                // Cannot set an invalid context.
3285                set_procattr(&current_task, ProcAttr::Exec, INVALID_SECURITY_CONTEXT.into()),
3286                error!(EINVAL)
3287            );
3288
3289            assert_eq!(
3290                get_procattr(&current_task, &current_task.task, ProcAttr::Current),
3291                error!(EINVAL)
3292            );
3293        })
3294        .await;
3295    }
3296
3297    #[fuchsia::test]
3298    async fn create_file_with_fscreate_sid() {
3299        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, security_server| {
3300            let sid =
3301                security_server.security_context_to_sid(VALID_SECURITY_CONTEXT.into()).unwrap();
3302            let source_node = &testing::create_test_file(current_task).entry.node;
3303
3304            fs_node_setsecurity(
3305                &current_task,
3306                &source_node,
3307                XATTR_NAME_SELINUX.to_bytes().into(),
3308                VALID_SECURITY_CONTEXT.into(),
3309                XattrOp::Set,
3310            )
3311            .expect("set_xattr(security.selinux) failed");
3312
3313            let mut creds = Credentials::clone(&current_task.current_creds());
3314            security::fs_node_copy_up(current_task, source_node, &source_node.fs(), &mut creds);
3315            let dir_entry = current_task
3316                .override_creds(creds.into(), || {
3317                    current_task
3318                        .fs()
3319                        .root()
3320                        .create_node(
3321                            &current_task,
3322                            "test_file2".into(),
3323                            FileMode::IFREG,
3324                            DeviceId::NONE,
3325                        )
3326                        .unwrap()
3327                })
3328                .entry;
3329
3330            assert_eq!(get_cached_sid(&dir_entry.node), Some(sid));
3331        })
3332        .await;
3333    }
3334
3335    #[fuchsia::test]
3336    async fn test_check_ptrace_access_mode_noaudit() {
3337        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, _security_server| {
3338            let tracer_task = create_task_with_security_context(
3339                &current_task.kernel(),
3340                "tracer_task",
3341                &CString::new(b"u:object_r:test_ptrace_tracer_no_t:s0").unwrap(),
3342            );
3343            let tracee_task = create_task_with_security_context(
3344                &current_task.kernel(),
3345                "target_task",
3346                &CString::new(b"u:object_r:test_ptrace_traced_t:s0").unwrap(),
3347            );
3348
3349            // 1. Same UID/GID and dumpable: fails in `selinux_hooks::task::ptrace_access_check`.
3350            let initial_denials = current_task.kernel().security_state.access_denial_count();
3351            assert_eq!(
3352                tracer_task.check_ptrace_access_mode(
3353                    PTRACE_MODE_ATTACH_REALCREDS | PTRACE_MODE_NOAUDIT,
3354                    &tracee_task
3355                ),
3356                error!(EACCES)
3357            );
3358            assert_eq!(current_task.kernel().security_state.access_denial_count(), initial_denials);
3359
3360            assert_eq!(
3361                tracer_task.check_ptrace_access_mode(PTRACE_MODE_ATTACH_REALCREDS, &tracee_task),
3362                error!(EACCES)
3363            );
3364            assert_eq!(
3365                current_task.kernel().security_state.access_denial_count(),
3366                initial_denials + 1
3367            );
3368
3369            // 2. Mismatched UID/GID (Step 3 of `CurrentTask::check_ptrace_access_mode`):
3370            //    fails in `security::check_task_ptrace_capable`.
3371            let orig_tracee_creds = Credentials::clone(&tracee_task.current_creds());
3372            let mut diff_uid_creds = orig_tracee_creds.clone();
3373            diff_uid_creds.uid = 1000;
3374            tracee_task.set_creds(diff_uid_creds);
3375
3376            let denials_before_uid = current_task.kernel().security_state.access_denial_count();
3377            assert_eq!(
3378                tracer_task.check_ptrace_access_mode(
3379                    PTRACE_MODE_READ_FSCREDS | PTRACE_MODE_NOAUDIT,
3380                    &tracee_task
3381                ),
3382                error!(EPERM)
3383            );
3384            assert_eq!(
3385                current_task.kernel().security_state.access_denial_count(),
3386                denials_before_uid
3387            );
3388
3389            assert_eq!(
3390                tracer_task.check_ptrace_access_mode(PTRACE_MODE_READ_FSCREDS, &tracee_task),
3391                error!(EPERM)
3392            );
3393            assert_eq!(
3394                current_task.kernel().security_state.access_denial_count(),
3395                denials_before_uid + 1
3396            );
3397            tracee_task.set_creds(orig_tracee_creds);
3398
3399            // 3. Non-dumpable target (Step 4 of `CurrentTask::check_ptrace_access_mode`):
3400            //    fails in `security::check_task_ptrace_capable`.
3401            tracee_task.mm().unwrap().dumpable.store(DumpPolicy::Disable);
3402            let denials_before_dumpable =
3403                current_task.kernel().security_state.access_denial_count();
3404            assert_eq!(
3405                tracer_task.check_ptrace_access_mode(
3406                    PTRACE_MODE_READ_FSCREDS | PTRACE_MODE_NOAUDIT,
3407                    &tracee_task
3408                ),
3409                error!(EPERM)
3410            );
3411            assert_eq!(
3412                current_task.kernel().security_state.access_denial_count(),
3413                denials_before_dumpable
3414            );
3415
3416            assert_eq!(
3417                tracer_task.check_ptrace_access_mode(PTRACE_MODE_READ_FSCREDS, &tracee_task),
3418                error!(EPERM)
3419            );
3420            assert_eq!(
3421                current_task.kernel().security_state.access_denial_count(),
3422                denials_before_dumpable + 1
3423            );
3424            tracee_task.mm().unwrap().dumpable.store(DumpPolicy::User);
3425
3426            // 4. `common_cap::check_ptrace_access` (when caller's capabilities are not a superset
3427            //    of target's permitted capabilities): fails in `security::check_creds_ptrace_capable`.
3428            let orig_caller_creds = Credentials::clone(&tracer_task.current_creds());
3429            let mut reduced_cap_creds = orig_caller_creds.clone();
3430            reduced_cap_creds.cap_effective = CAP_SYS_PTRACE;
3431            tracer_task.set_creds(reduced_cap_creds);
3432
3433            let denials_before_common_cap =
3434                current_task.kernel().security_state.access_denial_count();
3435            assert_eq!(
3436                tracer_task.check_ptrace_access_mode(
3437                    PTRACE_MODE_READ_FSCREDS | PTRACE_MODE_NOAUDIT,
3438                    &tracee_task
3439                ),
3440                error!(EPERM)
3441            );
3442            assert_eq!(
3443                current_task.kernel().security_state.access_denial_count(),
3444                denials_before_common_cap
3445            );
3446
3447            assert_eq!(
3448                tracer_task.check_ptrace_access_mode(PTRACE_MODE_READ_FSCREDS, &tracee_task),
3449                error!(EPERM)
3450            );
3451            assert_eq!(
3452                current_task.kernel().security_state.access_denial_count(),
3453                denials_before_common_cap + 1
3454            );
3455            tracer_task.set_creds(orig_caller_creds);
3456
3457            // 5. `yama::ptrace_access_check` (`SCOPE_RESTRICTED` and `SCOPE_ADMIN_ONLY`):
3458            //    fails in `security::check_task_ptrace_capable`.
3459            for scope in [yama::SCOPE_RESTRICTED, yama::SCOPE_ADMIN_ONLY] {
3460                current_task.kernel().ptrace_scope.store(scope, Ordering::Relaxed);
3461                let denials_before_yama =
3462                    current_task.kernel().security_state.access_denial_count();
3463                assert_eq!(
3464                    tracer_task.check_ptrace_access_mode(
3465                        PTRACE_MODE_ATTACH_REALCREDS | PTRACE_MODE_NOAUDIT,
3466                        &tracee_task
3467                    ),
3468                    error!(EPERM)
3469                );
3470                assert_eq!(
3471                    current_task.kernel().security_state.access_denial_count(),
3472                    denials_before_yama
3473                );
3474
3475                assert_eq!(
3476                    tracer_task
3477                        .check_ptrace_access_mode(PTRACE_MODE_ATTACH_REALCREDS, &tracee_task),
3478                    error!(EPERM)
3479                );
3480                assert_eq!(
3481                    current_task.kernel().security_state.access_denial_count(),
3482                    denials_before_yama + 1
3483                );
3484            }
3485            current_task.kernel().ptrace_scope.store(yama::SCOPE_CLASSIC, Ordering::Relaxed);
3486        })
3487        .await;
3488    }
3489
3490    #[fuchsia::test]
3491    async fn private_fs_node_bypasses_fs_node_hooks() {
3492        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, _security_server| {
3493            let private_file = new_null_file(current_task, OpenFlags::RDWR);
3494
3495            let node = private_file.node();
3496            assert!(node.is_private());
3497            assert_eq!(get_cached_sid(node), None);
3498            assert_eq!(fs_node_listsecurity(current_task, node), None);
3499            assert_eq!(check_fs_node_getattr_access(current_task, node), Ok(()));
3500            assert_eq!(
3501                fs_node_permission(
3502                    current_task,
3503                    node,
3504                    PermissionFlags::READ | PermissionFlags::WRITE,
3505                    (&*current_task).into(),
3506                ),
3507                Ok(())
3508            );
3509            assert_eq!(sb_mount(current_task, &private_file.name, MountFlags::empty()), Ok(()));
3510            assert_eq!(file_open(current_task, &private_file), Ok(()));
3511        })
3512        .await;
3513    }
3514
3515    #[fuchsia::test]
3516    async fn private_fs_node_file_enforces_fd_use() {
3517        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, security_server| {
3518            let private_file = new_null_file(current_task, OpenFlags::RDWR | OpenFlags::APPEND);
3519
3520            assert_eq!(
3521                file_permission(
3522                    current_task,
3523                    &private_file,
3524                    PermissionFlags::READ | PermissionFlags::WRITE,
3525                ),
3526                Ok(())
3527            );
3528            assert_eq!(
3529                check_file_fcntl_access(
3530                    current_task,
3531                    &private_file,
3532                    starnix_uapi::F_SETFL,
3533                    OpenFlags::RDWR.bits() as u64,
3534                ),
3535                Ok(())
3536            );
3537
3538            // Switching to a different domain without `fd { use }` should still deny FileObject
3539            // operations, even though `FsNode` permission checks are skipped.
3540            let other_sid = security_server
3541                .security_context_to_sid(DIFFERENT_VALID_SECURITY_CONTEXT.into())
3542                .expect("invalid security context");
3543            testing::mutate_attrs_for_test(current_task, |attrs| attrs.current_sid = other_sid);
3544            assert_eq!(
3545                file_permission(
3546                    current_task,
3547                    &private_file,
3548                    PermissionFlags::READ | PermissionFlags::WRITE,
3549                ),
3550                error!(EACCES)
3551            );
3552            assert_eq!(
3553                check_file_fcntl_access(
3554                    current_task,
3555                    &private_file,
3556                    starnix_uapi::F_SETFL,
3557                    OpenFlags::RDWR.bits() as u64,
3558                ),
3559                error!(EACCES)
3560            );
3561        })
3562        .await;
3563    }
3564
3565    fn create_private_test_socket(
3566        current_task: &CurrentTask,
3567        security_server: &SecurityServer,
3568    ) -> FileHandle {
3569        let task_sid = security_server
3570            .security_context_to_sid(b"u:object_r:test_socket_create_no_t:s0".into())
3571            .expect("invalid security context");
3572        testing::mutate_attrs_for_test(current_task, |attrs| attrs.current_sid = task_sid);
3573
3574        SocketFile::new_socket(
3575            current_task,
3576            SocketDomain::Unix,
3577            SocketType::Stream,
3578            OpenFlags::RDWR,
3579            SocketProtocol::IP,
3580            /* kernel_private = */ true,
3581        )
3582        .expect("kernel_private socket creation should bypass access check")
3583    }
3584
3585    #[fuchsia::test]
3586    async fn private_socket_bypasses_creation_and_labeling() {
3587        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, security_server| {
3588            let private_socket_file = create_private_test_socket(current_task, security_server);
3589
3590            let node = private_socket_file.node();
3591            assert!(node.is_private());
3592            assert_eq!(get_cached_sid(node), None);
3593
3594            let socket =
3595                Socket::get_from_file(&private_socket_file).expect("FileHandle should be a socket");
3596            assert!(is_private_socket(socket));
3597        })
3598        .await;
3599    }
3600
3601    #[fuchsia::test]
3602    async fn private_socket_bypasses_socket_ops_hooks() {
3603        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, security_server| {
3604            let private_socket_file = create_private_test_socket(current_task, security_server);
3605            let socket =
3606                Socket::get_from_file(&private_socket_file).expect("FileHandle should be a socket");
3607
3608            assert_eq!(check_socket_getsockname_access(current_task, socket), Ok(()));
3609            assert_eq!(check_socket_getpeername_access(current_task, socket), Ok(()));
3610            assert_eq!(
3611                check_socket_shutdown_access(current_task, socket, SocketShutdownFlags::READ),
3612                Ok(())
3613            );
3614            assert_eq!(socket_getpeersec_stream(current_task, socket), Ok(Vec::<u8>::new()));
3615            assert_eq!(socket_getpeersec_dgram(current_task, socket), Vec::<u8>::new());
3616        })
3617        .await;
3618    }
3619
3620    #[fuchsia::test]
3621    async fn private_socket_bypasses_connection_hooks() {
3622        spawn_kernel_with_selinux_hooks_test_policy_and_run(|current_task, security_server| {
3623            let private_socket_file = create_private_test_socket(current_task, security_server);
3624            let socket =
3625                Socket::get_from_file(&private_socket_file).expect("FileHandle should be a socket");
3626            let socket_downcasted = private_socket_file
3627                .downcast_file::<SocketFile>()
3628                .expect("downcast to SocketFile should succeed");
3629
3630            assert_eq!(
3631                socket_socketpair(current_task, socket_downcasted, socket_downcasted),
3632                Ok(())
3633            );
3634            assert_eq!(
3635                check_socket_connect_access(
3636                    current_task,
3637                    socket_downcasted,
3638                    &SocketPeer::Handle(socket.clone()),
3639                ),
3640                Ok(())
3641            );
3642            assert_eq!(socket_accept(current_task, socket_downcasted, socket_downcasted), Ok(()));
3643        })
3644        .await;
3645    }
3646}