Skip to main content

selinux/
lib.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
5pub mod local_cache;
6pub mod permission_check;
7pub mod policy;
8pub mod security_server;
9
10#[allow(unused_imports)]
11#[expect(dead_code)]
12mod new_policy;
13
14pub use access_vector_cache::{AccessQueryArgs, DEFAULT_SHARED_SIZE, QueryCacheCapacity};
15pub use concurrent_access_cache::{AccessCacheStorage, ConcurrentAccessCache};
16pub use security_server::{PolicySeqNo, SecurityServer};
17
18mod access_vector_cache;
19mod cache_stats;
20mod concurrent_access_cache;
21mod concurrent_cache;
22mod exceptions_config;
23mod kernel_permissions;
24mod sid_table;
25mod sync;
26
27/// Allow callers to use the kernel class & permission definitions.
28pub use kernel_permissions::*;
29
30/// Numeric class Ids are provided to the userspace AVC surfaces (e.g. "create", "access", etc).
31pub use policy::ClassId;
32
33pub use starnix_uapi::selinux::{InitialSid, ReferenceInitialSid, SecurityId, TaskAttrs};
34
35use policy::arrays::FsUseType;
36use strum::VariantArray as _;
37use strum_macros::VariantArray;
38
39/// Identifies a specific class by its policy-defined Id, or as a kernel object class enum Id.
40#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
41pub enum ObjectClass {
42    /// Refers to a well-known SELinux kernel object class (e.g. "process", "file", "capability").
43    Kernel(KernelClass),
44    /// Refers to a policy-defined class by its policy-defined numeric Id. This is most commonly
45    /// used when handling queries from userspace, which refer to classes by-Id.
46    ClassId(ClassId),
47}
48
49impl From<ClassId> for ObjectClass {
50    fn from(id: ClassId) -> Self {
51        Self::ClassId(id)
52    }
53}
54
55impl<T: Into<KernelClass>> From<T> for ObjectClass {
56    fn from(class: T) -> Self {
57        Self::Kernel(class.into())
58    }
59}
60
61/// A borrowed byte slice that contains no `NUL` characters by truncating the input slice at the
62/// first `NUL` (if any) upon construction.
63#[derive(Clone, Copy, Debug, PartialEq)]
64pub struct NullessByteStr<'a>(&'a [u8]);
65
66impl<'a> NullessByteStr<'a> {
67    /// Returns a non-null-terminated representation of the security context string.
68    pub fn as_bytes(&self) -> &[u8] {
69        &self.0
70    }
71}
72
73impl<'a, S: AsRef<[u8]> + ?Sized> From<&'a S> for NullessByteStr<'a> {
74    /// Any `AsRef<[u8]>` can be processed into a [`NullessByteStr`]. The [`NullessByteStr`] will
75    /// retain everything up to (but not including) a null character, or else the complete byte
76    /// string.
77    fn from(s: &'a S) -> Self {
78        let value = s.as_ref();
79        match value.iter().position(|c| *c == 0) {
80            Some(end) => Self(&value[..end]),
81            None => Self(value),
82        }
83    }
84}
85
86#[derive(Clone, Debug, PartialEq)]
87pub struct FileSystemMountSids {
88    pub context: Option<SecurityId>,
89    pub fs_context: Option<SecurityId>,
90    pub def_context: Option<SecurityId>,
91    pub root_context: Option<SecurityId>,
92}
93
94#[derive(Clone, Debug, PartialEq)]
95pub struct FileSystemLabel {
96    pub sid: SecurityId,
97    pub scheme: FileSystemLabelingScheme,
98    // Sids obtained by parsing the mount options of the FileSystem.
99    pub mount_sids: FileSystemMountSids,
100}
101
102#[derive(Clone, Debug, PartialEq)]
103pub enum FileSystemLabelingScheme {
104    /// This filesystem was mounted with "context=".
105    Mountpoint { sid: SecurityId },
106    /// This filesystem has an "fs_use_xattr", "fs_use_task", or "fs_use_trans" entry in the
107    /// policy. If the `fs_use_type` is "fs_use_xattr" then the `default_sid` specifies the SID
108    /// with which to label `FsNode`s of files that do not have the "security.selinux" xattr.
109    FsUse { fs_use_type: FsUseType, default_sid: SecurityId },
110    /// This filesystem has one or more "genfscon" statements associated with it in the policy.
111    /// If `supports_seclabel` is true then nodes in the filesystem may be dynamically relabeled.
112    GenFsCon { supports_seclabel: bool },
113}
114
115/// SELinux security context-related filesystem mount options. These options are documented in the
116/// `context=context, fscontext=context, defcontext=context, and rootcontext=context` section of
117/// the `mount(8)` manpage.
118#[derive(Clone, Debug, Default, PartialEq)]
119pub struct FileSystemMountOptions {
120    /// Specifies the effective security context to use for all nodes in the filesystem, and the
121    /// filesystem itself. If the filesystem already contains security attributes then these are
122    /// ignored. May not be combined with any of the other options.
123    pub context: Option<Vec<u8>>,
124    /// Specifies an effective security context to use for un-labeled nodes in the filesystem,
125    /// rather than falling-back to the policy-defined "file" context.
126    pub def_context: Option<Vec<u8>>,
127    /// The value of the `fscontext=[security-context]` mount option. This option is used to
128    /// label the filesystem (superblock) itself.
129    pub fs_context: Option<Vec<u8>>,
130    /// The value of the `rootcontext=[security-context]` mount option. This option is used to
131    /// (re)label the inode located at the filesystem mountpoint.
132    pub root_context: Option<Vec<u8>>,
133}
134
135/// Status information parameter for the [`SeLinuxStatusPublisher`] interface.
136pub struct SeLinuxStatus {
137    /// SELinux-wide enforcing vs. permissive mode  bit.
138    pub is_enforcing: bool,
139    /// Number of times the policy has been changed since SELinux started.
140    pub change_count: u32,
141    /// Bit indicating whether operations unknown SELinux abstractions will be denied.
142    pub deny_unknown: bool,
143}
144
145/// Interface for security server to interact with selinuxfs status file.
146pub trait SeLinuxStatusPublisher: Send + Sync {
147    /// Sets the value part of the associated selinuxfs status file.
148    fn set_status(&mut self, policy_status: SeLinuxStatus);
149}
150
151/// Reference policy capability Ids.
152#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, VariantArray)]
153pub enum PolicyCap {
154    NetworkPeerControls = 0,
155    OpenPerms = 1,
156    ExtendedSocketClass = 2,
157    AlwaysCheckNetwork = 3,
158    CgroupSeclabel = 4,
159    NnpNosuidTransition = 5,
160    GenfsSeclabelSymlinks = 6,
161    IoctlSkipCloexec = 7,
162    UserspaceInitialContext = 8,
163    NetlinkXperm = 9,
164    NetifWildcard = 10,
165    GenfsSeclabelWildcard = 11,
166    FunctionfsSeclabel = 12,
167    MemfdClass = 13,
168}
169
170impl PolicyCap {
171    pub fn name(&self) -> &str {
172        match self {
173            Self::NetworkPeerControls => "network_peer_controls",
174            Self::OpenPerms => "open_perms",
175            Self::ExtendedSocketClass => "extended_socket_class",
176            Self::AlwaysCheckNetwork => "always_check_network",
177            Self::CgroupSeclabel => "cgroup_seclabel",
178            Self::NnpNosuidTransition => "nnp_nosuid_transition",
179            Self::GenfsSeclabelSymlinks => "genfs_seclabel_symlinks",
180            Self::IoctlSkipCloexec => "ioctl_skip_cloexec",
181            Self::UserspaceInitialContext => "userspace_initial_context",
182            Self::NetlinkXperm => "netlink_xperm",
183            Self::NetifWildcard => "netif_wildcard",
184            Self::GenfsSeclabelWildcard => "genfs_seclabel_wildcard",
185            Self::FunctionfsSeclabel => "functionfs_seclabel",
186            Self::MemfdClass => "memfd_class",
187        }
188    }
189
190    pub fn by_name(name: &str) -> Option<Self> {
191        Self::VARIANTS.iter().find(|x| x.name() == name).copied()
192    }
193}
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198    use std::num::NonZeroU32;
199
200    #[test]
201    fn object_class_permissions() {
202        let test_class_id = ClassId::new(NonZeroU32::new(20).unwrap());
203        assert_eq!(ObjectClass::ClassId(test_class_id), test_class_id.into());
204        for variant in ProcessPermission::PERMISSIONS {
205            assert_eq!(KernelClass::Process, variant.class());
206            assert_eq!("process", variant.class().name());
207            assert_eq!(ObjectClass::Kernel(KernelClass::Process), variant.class().into());
208        }
209    }
210
211    #[test]
212    fn policy_capabilities() {
213        for capability in PolicyCap::VARIANTS {
214            assert_eq!(Some(*capability), PolicyCap::by_name(capability.name()));
215        }
216    }
217
218    #[test]
219    fn nulless_byte_str_equivalence() {
220        let unterminated: NullessByteStr<'_> = b"u:object_r:test_valid_t:s0".into();
221        let nul_terminated: NullessByteStr<'_> = b"u:object_r:test_valid_t:s0\0".into();
222        let nul_containing: NullessByteStr<'_> =
223            b"u:object_r:test_valid_t:s0\0IGNORE THIS\0!\0".into();
224
225        for context in [nul_terminated, nul_containing] {
226            assert_eq!(unterminated, context);
227            assert_eq!(unterminated.as_bytes(), context.as_bytes());
228        }
229    }
230}