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