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