Skip to main content

starnix_core/security/
non_private.rs

1// Copyright 2026 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
5use super::{Auditable, common_cap, selinux_hooks};
6use crate::vfs::socket::{Socket, SocketFile, SocketHandle};
7use crate::vfs::{DirEntryHandle, DowncastedFile, FsNode};
8use std::ops::Deref;
9
10/// Opaque structure used by [`crate::vfs::FsNode`] to store security state.
11///
12/// Its inner state is only accessible via [`NonPrivateFsNode`], guaranteeing at
13/// compile time that kernel-private [`FsNode`]s never have their security state
14/// read or mutated.
15#[derive(Debug, Default)]
16pub struct FsNodeState {
17    selinux: selinux_hooks::FsNodeState,
18    common_cap: common_cap::FsNodeState,
19}
20
21impl FsNodeState {
22    #[cfg(test)]
23    pub(super) fn selinux_for_test(&self) -> &selinux_hooks::FsNodeState {
24        &self.selinux
25    }
26}
27
28/// Borrowed reference to an [`FsNode`] verified not to be kernel-private.
29#[derive(Clone, Copy, Debug)]
30pub(super) struct NonPrivateFsNode<'a> {
31    fs_node: &'a FsNode,
32}
33
34impl<'a> NonPrivateFsNode<'a> {
35    /// Returns `Some` if `fs_node` is not marked kernel-private.
36    pub(super) fn new(fs_node: &'a FsNode) -> Option<Self> {
37        (!fs_node.is_private()).then_some(Self { fs_node })
38    }
39
40    /// Asserts in debug builds that `fs_node` is not kernel-private, returning `None`
41    /// if the invariant is violated in release builds so callers can fail-safe.
42    pub(super) fn assert_from(fs_node: &'a FsNode) -> Option<Self> {
43        debug_assert!(!fs_node.is_private());
44        Self::new(fs_node)
45    }
46
47    /// Returns the underlying [`FsNode`] reference.
48    pub(super) fn as_fs_node(self) -> &'a FsNode {
49        self.fs_node
50    }
51
52    /// Returns the SELinux [`selinux_hooks::FsNodeState`] for this non-private node.
53    pub(super) fn selinux_state(self) -> &'a selinux_hooks::FsNodeState {
54        &self.fs_node.security_state.selinux
55    }
56
57    /// Returns the common capabilities [`common_cap::FsNodeState`] for this non-private node.
58    pub(super) fn common_cap_state(self) -> &'a common_cap::FsNodeState {
59        &self.fs_node.security_state.common_cap
60    }
61}
62
63impl Deref for NonPrivateFsNode<'_> {
64    type Target = FsNode;
65
66    fn deref(&self) -> &Self::Target {
67        self.fs_node
68    }
69}
70
71impl<'a> From<NonPrivateFsNode<'a>> for Auditable<'a> {
72    fn from(value: NonPrivateFsNode<'a>) -> Self {
73        Auditable::FsNode(value.fs_node)
74    }
75}
76
77/// Borrowed reference to a [`DirEntryHandle`] whose [`FsNode`] is verified not to be
78/// kernel-private.
79#[derive(Clone, Copy, Debug)]
80pub(super) struct NonPrivateDirEntry<'a> {
81    dir_entry: &'a DirEntryHandle,
82    fs_node: NonPrivateFsNode<'a>,
83}
84
85impl<'a> NonPrivateDirEntry<'a> {
86    /// Returns `Some` if `dir_entry.node` is not marked kernel-private.
87    pub(super) fn new(dir_entry: &'a DirEntryHandle) -> Option<Self> {
88        let fs_node = NonPrivateFsNode::new(&dir_entry.node)?;
89        Some(Self { dir_entry, fs_node })
90    }
91
92    /// Asserts in debug builds that `dir_entry.node` is not kernel-private, returning `None`
93    /// if the invariant is violated in release builds so callers can fail-safe.
94    pub(super) fn assert_from(dir_entry: &'a DirEntryHandle) -> Option<Self> {
95        let fs_node = NonPrivateFsNode::assert_from(&dir_entry.node)?;
96        Some(Self { dir_entry, fs_node })
97    }
98
99    /// Returns the underlying [`DirEntryHandle`] reference.
100    pub(super) fn as_dir_entry(self) -> &'a DirEntryHandle {
101        self.dir_entry
102    }
103
104    /// Returns the verified [`NonPrivateFsNode`] for this directory entry.
105    pub(super) fn node(self) -> NonPrivateFsNode<'a> {
106        self.fs_node
107    }
108}
109
110impl Deref for NonPrivateDirEntry<'_> {
111    type Target = DirEntryHandle;
112
113    fn deref(&self) -> &Self::Target {
114        self.dir_entry
115    }
116}
117
118impl<'a> From<NonPrivateDirEntry<'a>> for Auditable<'a> {
119    fn from(value: NonPrivateDirEntry<'a>) -> Self {
120        value.dir_entry.into()
121    }
122}
123
124/// Borrowed reference to a [`Socket`] verified not to be backed by a kernel-private [`FsNode`].
125#[derive(Clone, Copy)]
126pub(super) struct NonPrivateSocket<'a> {
127    socket: &'a Socket,
128    fs_node: Option<NonPrivateFsNode<'a>>,
129}
130
131impl<'a> NonPrivateSocket<'a> {
132    /// Returns `Some` if `socket` is not backed by a kernel-private [`FsNode`].
133    pub(super) fn new(socket: &'a Socket) -> Option<Self> {
134        let fs_node = match socket.fs_node() {
135            Some(node) => Some(NonPrivateFsNode::new(node)?),
136            None => None,
137        };
138        Some(Self { socket, fs_node })
139    }
140
141    /// Returns the socket's [`NonPrivateFsNode`], if an [`FsNode`] has been attached.
142    pub(super) fn fs_node(self) -> Option<NonPrivateFsNode<'a>> {
143        self.fs_node
144    }
145}
146
147impl Deref for NonPrivateSocket<'_> {
148    type Target = Socket;
149
150    fn deref(&self) -> &Self::Target {
151        self.socket
152    }
153}
154
155/// Borrowed reference to a [`DowncastedFile<SocketFile>`] whose [`FsNode`] is verified not to
156/// be kernel-private.
157#[derive(Clone, Copy)]
158pub(super) struct NonPrivateSocketFile<'a> {
159    socket_file: DowncastedFile<'a, SocketFile>,
160    fs_node: NonPrivateFsNode<'a>,
161}
162
163impl<'a> NonPrivateSocketFile<'a> {
164    /// Returns `Some` if `socket_file`'s [`FsNode`] is not marked kernel-private.
165    pub(super) fn new(socket_file: DowncastedFile<'a, SocketFile>) -> Option<Self> {
166        let fs_node = NonPrivateFsNode::new(socket_file.file().node())?;
167        Some(Self { socket_file, fs_node })
168    }
169
170    /// Asserts in debug builds that `socket_file`'s [`FsNode`] is not kernel-private,
171    /// returning `None` if the invariant is violated in release builds so callers can
172    /// fail-safe.
173    pub(super) fn assert_from(socket_file: DowncastedFile<'a, SocketFile>) -> Option<Self> {
174        let fs_node = NonPrivateFsNode::assert_from(socket_file.file().node())?;
175        Some(Self { socket_file, fs_node })
176    }
177
178    /// Returns the underlying [`SocketHandle`].
179    pub(super) fn socket(self) -> &'a SocketHandle {
180        self.socket_file.socket()
181    }
182
183    /// Returns the verified [`NonPrivateFsNode`] for this socket file.
184    pub(super) fn node(self) -> NonPrivateFsNode<'a> {
185        self.fs_node
186    }
187}