starnix_core/security/
hooks.rs

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