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