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