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