Skip to main content

starnix_core/security/
hooks.rs

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