1pub 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
27pub use kernel_permissions::*;
29
30pub 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#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
41pub enum ObjectClass {
42 Kernel(KernelClass),
44 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#[derive(Clone, Copy, Debug, PartialEq)]
64pub struct NullessByteStr<'a>(&'a [u8]);
65
66impl<'a> NullessByteStr<'a> {
67 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 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 pub mount_sids: FileSystemMountSids,
100}
101
102#[derive(Clone, Debug, PartialEq)]
103pub enum FileSystemLabelingScheme {
104 Mountpoint { sid: SecurityId },
106 FsUse { fs_use_type: FsUseType, default_sid: SecurityId },
110 GenFsCon { supports_seclabel: bool },
113}
114
115#[derive(Clone, Debug, Default, PartialEq)]
119pub struct FileSystemMountOptions {
120 pub context: Option<Vec<u8>>,
124 pub def_context: Option<Vec<u8>>,
127 pub fs_context: Option<Vec<u8>>,
130 pub root_context: Option<Vec<u8>>,
133}
134
135pub struct SeLinuxStatus {
137 pub is_enforcing: bool,
139 pub change_count: u32,
141 pub deny_unknown: bool,
143}
144
145pub trait SeLinuxStatusPublisher: Send + Sync {
147 fn set_status(&mut self, policy_status: SeLinuxStatus);
149}
150
151#[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}