blob: 118d4df041a850011b197177ac68569f716ea8da [file]
// Copyright 2026 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use super::{Auditable, common_cap, selinux_hooks};
use crate::vfs::socket::{Socket, SocketFile, SocketHandle};
use crate::vfs::{DirEntryHandle, DowncastedFile, FsNode};
use std::ops::Deref;
/// Opaque structure used by [`crate::vfs::FsNode`] to store security state.
///
/// Its inner state is only accessible via [`NonPrivateFsNode`], guaranteeing at
/// compile time that kernel-private [`FsNode`]s never have their security state
/// read or mutated.
#[derive(Debug, Default)]
pub struct FsNodeState {
selinux: selinux_hooks::FsNodeState,
common_cap: common_cap::FsNodeState,
}
impl FsNodeState {
#[cfg(test)]
pub(super) fn selinux_for_test(&self) -> &selinux_hooks::FsNodeState {
&self.selinux
}
}
/// Borrowed reference to an [`FsNode`] verified not to be kernel-private.
#[derive(Clone, Copy, Debug)]
pub(super) struct NonPrivateFsNode<'a> {
fs_node: &'a FsNode,
}
impl<'a> NonPrivateFsNode<'a> {
/// Returns `Some` if `fs_node` is not marked kernel-private.
pub(super) fn new(fs_node: &'a FsNode) -> Option<Self> {
(!fs_node.is_private()).then_some(Self { fs_node })
}
/// Asserts in debug builds that `fs_node` is not kernel-private, returning `None`
/// if the invariant is violated in release builds so callers can fail-safe.
pub(super) fn assert_from(fs_node: &'a FsNode) -> Option<Self> {
debug_assert!(!fs_node.is_private());
Self::new(fs_node)
}
/// Returns the underlying [`FsNode`] reference.
pub(super) fn as_fs_node(self) -> &'a FsNode {
self.fs_node
}
/// Returns the SELinux [`selinux_hooks::FsNodeState`] for this non-private node.
pub(super) fn selinux_state(self) -> &'a selinux_hooks::FsNodeState {
&self.fs_node.security_state.selinux
}
/// Returns the common capabilities [`common_cap::FsNodeState`] for this non-private node.
pub(super) fn common_cap_state(self) -> &'a common_cap::FsNodeState {
&self.fs_node.security_state.common_cap
}
}
impl Deref for NonPrivateFsNode<'_> {
type Target = FsNode;
fn deref(&self) -> &Self::Target {
self.fs_node
}
}
impl<'a> From<NonPrivateFsNode<'a>> for Auditable<'a> {
fn from(value: NonPrivateFsNode<'a>) -> Self {
Auditable::FsNode(value.fs_node)
}
}
/// Borrowed reference to a [`DirEntryHandle`] whose [`FsNode`] is verified not to be
/// kernel-private.
#[derive(Clone, Copy, Debug)]
pub(super) struct NonPrivateDirEntry<'a> {
dir_entry: &'a DirEntryHandle,
fs_node: NonPrivateFsNode<'a>,
}
impl<'a> NonPrivateDirEntry<'a> {
/// Returns `Some` if `dir_entry.node` is not marked kernel-private.
pub(super) fn new(dir_entry: &'a DirEntryHandle) -> Option<Self> {
let fs_node = NonPrivateFsNode::new(&dir_entry.node)?;
Some(Self { dir_entry, fs_node })
}
/// Asserts in debug builds that `dir_entry.node` is not kernel-private, returning `None`
/// if the invariant is violated in release builds so callers can fail-safe.
pub(super) fn assert_from(dir_entry: &'a DirEntryHandle) -> Option<Self> {
let fs_node = NonPrivateFsNode::assert_from(&dir_entry.node)?;
Some(Self { dir_entry, fs_node })
}
/// Returns the underlying [`DirEntryHandle`] reference.
pub(super) fn as_dir_entry(self) -> &'a DirEntryHandle {
self.dir_entry
}
/// Returns the verified [`NonPrivateFsNode`] for this directory entry.
pub(super) fn node(self) -> NonPrivateFsNode<'a> {
self.fs_node
}
}
impl Deref for NonPrivateDirEntry<'_> {
type Target = DirEntryHandle;
fn deref(&self) -> &Self::Target {
self.dir_entry
}
}
impl<'a> From<NonPrivateDirEntry<'a>> for Auditable<'a> {
fn from(value: NonPrivateDirEntry<'a>) -> Self {
value.dir_entry.into()
}
}
/// Borrowed reference to a [`Socket`] verified not to be backed by a kernel-private [`FsNode`].
#[derive(Clone, Copy)]
pub(super) struct NonPrivateSocket<'a> {
socket: &'a Socket,
fs_node: Option<NonPrivateFsNode<'a>>,
}
impl<'a> NonPrivateSocket<'a> {
/// Returns `Some` if `socket` is not backed by a kernel-private [`FsNode`].
pub(super) fn new(socket: &'a Socket) -> Option<Self> {
let fs_node = match socket.fs_node() {
Some(node) => Some(NonPrivateFsNode::new(node)?),
None => None,
};
Some(Self { socket, fs_node })
}
/// Returns the socket's [`NonPrivateFsNode`], if an [`FsNode`] has been attached.
pub(super) fn fs_node(self) -> Option<NonPrivateFsNode<'a>> {
self.fs_node
}
}
impl Deref for NonPrivateSocket<'_> {
type Target = Socket;
fn deref(&self) -> &Self::Target {
self.socket
}
}
/// Borrowed reference to a [`DowncastedFile<SocketFile>`] whose [`FsNode`] is verified not to
/// be kernel-private.
#[derive(Clone, Copy)]
pub(super) struct NonPrivateSocketFile<'a> {
socket_file: DowncastedFile<'a, SocketFile>,
fs_node: NonPrivateFsNode<'a>,
}
impl<'a> NonPrivateSocketFile<'a> {
/// Returns `Some` if `socket_file`'s [`FsNode`] is not marked kernel-private.
pub(super) fn new(socket_file: DowncastedFile<'a, SocketFile>) -> Option<Self> {
let fs_node = NonPrivateFsNode::new(socket_file.file().node())?;
Some(Self { socket_file, fs_node })
}
/// Asserts in debug builds that `socket_file`'s [`FsNode`] is not kernel-private,
/// returning `None` if the invariant is violated in release builds so callers can
/// fail-safe.
pub(super) fn assert_from(socket_file: DowncastedFile<'a, SocketFile>) -> Option<Self> {
let fs_node = NonPrivateFsNode::assert_from(socket_file.file().node())?;
Some(Self { socket_file, fs_node })
}
/// Returns the underlying [`SocketHandle`].
pub(super) fn socket(self) -> &'a SocketHandle {
self.socket_file.socket()
}
/// Returns the verified [`NonPrivateFsNode`] for this socket file.
pub(super) fn node(self) -> NonPrivateFsNode<'a> {
self.fs_node
}
}