1pub mod local_cache;
6pub mod permission_check;
7pub mod policy;
8pub mod security_server;
9
10pub use security_server::SecurityServer;
11
12mod access_vector_cache;
13mod cache_stats;
14mod concurrent_access_cache;
15mod concurrent_cache;
16mod exceptions_config;
17mod kernel_permissions;
18mod sid_table;
19mod sync;
20
21pub use kernel_permissions::*;
23
24pub use policy::ClassId;
26
27pub use starnix_uapi::selinux::{InitialSid, ReferenceInitialSid, SecurityId, TaskAttrs};
28
29use policy::arrays::FsUseType;
30use strum::VariantArray as _;
31use strum_macros::VariantArray;
32
33#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
35pub enum ObjectClass {
36 Kernel(KernelClass),
38 ClassId(ClassId),
41}
42
43impl From<ClassId> for ObjectClass {
44 fn from(id: ClassId) -> Self {
45 Self::ClassId(id)
46 }
47}
48
49impl<T: Into<KernelClass>> From<T> for ObjectClass {
50 fn from(class: T) -> Self {
51 Self::Kernel(class.into())
52 }
53}
54
55#[derive(Clone, Copy, Debug, PartialEq)]
58pub struct NullessByteStr<'a>(&'a [u8]);
59
60impl<'a> NullessByteStr<'a> {
61 pub fn as_bytes(&self) -> &[u8] {
63 &self.0
64 }
65}
66
67impl<'a, S: AsRef<[u8]> + ?Sized> From<&'a S> for NullessByteStr<'a> {
68 fn from(s: &'a S) -> Self {
72 let value = s.as_ref();
73 match value.iter().position(|c| *c == 0) {
74 Some(end) => Self(&value[..end]),
75 None => Self(value),
76 }
77 }
78}
79
80#[derive(Clone, Debug, PartialEq)]
81pub struct FileSystemMountSids {
82 pub context: Option<SecurityId>,
83 pub fs_context: Option<SecurityId>,
84 pub def_context: Option<SecurityId>,
85 pub root_context: Option<SecurityId>,
86}
87
88#[derive(Clone, Debug, PartialEq)]
89pub struct FileSystemLabel {
90 pub sid: SecurityId,
91 pub scheme: FileSystemLabelingScheme,
92 pub mount_sids: FileSystemMountSids,
94}
95
96#[derive(Clone, Debug, PartialEq)]
97pub enum FileSystemLabelingScheme {
98 Mountpoint { sid: SecurityId },
100 FsUse { fs_use_type: FsUseType, default_sid: SecurityId },
104 GenFsCon { supports_seclabel: bool },
107}
108
109#[derive(Clone, Debug, Default, PartialEq)]
113pub struct FileSystemMountOptions {
114 pub context: Option<Vec<u8>>,
118 pub def_context: Option<Vec<u8>>,
121 pub fs_context: Option<Vec<u8>>,
124 pub root_context: Option<Vec<u8>>,
127}
128
129pub struct SeLinuxStatus {
131 pub is_enforcing: bool,
133 pub change_count: u32,
135 pub deny_unknown: bool,
137}
138
139pub trait SeLinuxStatusPublisher: Send + Sync {
141 fn set_status(&mut self, policy_status: SeLinuxStatus);
143}
144
145#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, VariantArray)]
147pub enum PolicyCap {
148 NetworkPeerControls = 0,
149 OpenPerms = 1,
150 ExtendedSocketClass = 2,
151 AlwaysCheckNetwork = 3,
152 CgroupSeclabel = 4,
153 NnpNosuidTransition = 5,
154 GenfsSeclabelSymlinks = 6,
155 IoctlSkipCloexec = 7,
156 UserspaceInitialContext = 8,
157 NetlinkXperm = 9,
158 NetifWildcard = 10,
159 GenfsSeclabelWildcard = 11,
160 FunctionfsSeclabel = 12,
161 MemfdClass = 13,
162}
163
164impl PolicyCap {
165 pub fn name(&self) -> &str {
166 match self {
167 Self::NetworkPeerControls => "network_peer_controls",
168 Self::OpenPerms => "open_perms",
169 Self::ExtendedSocketClass => "extended_socket_class",
170 Self::AlwaysCheckNetwork => "always_check_network",
171 Self::CgroupSeclabel => "cgroup_seclabel",
172 Self::NnpNosuidTransition => "nnp_nosuid_transition",
173 Self::GenfsSeclabelSymlinks => "genfs_seclabel_symlinks",
174 Self::IoctlSkipCloexec => "ioctl_skip_cloexec",
175 Self::UserspaceInitialContext => "userspace_initial_context",
176 Self::NetlinkXperm => "netlink_xperm",
177 Self::NetifWildcard => "netif_wildcard",
178 Self::GenfsSeclabelWildcard => "genfs_seclabel_wildcard",
179 Self::FunctionfsSeclabel => "functionfs_seclabel",
180 Self::MemfdClass => "memfd_class",
181 }
182 }
183
184 pub fn by_name(name: &str) -> Option<Self> {
185 Self::VARIANTS.iter().find(|x| x.name() == name).copied()
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192 use std::num::NonZeroU32;
193
194 #[test]
195 fn object_class_permissions() {
196 let test_class_id = ClassId::new(NonZeroU32::new(20).unwrap());
197 assert_eq!(ObjectClass::ClassId(test_class_id), test_class_id.into());
198 for variant in ProcessPermission::PERMISSIONS {
199 assert_eq!(KernelClass::Process, variant.class());
200 assert_eq!("process", variant.class().name());
201 assert_eq!(ObjectClass::Kernel(KernelClass::Process), variant.class().into());
202 }
203 }
204
205 #[test]
206 fn policy_capabilities() {
207 for capability in PolicyCap::VARIANTS {
208 assert_eq!(Some(*capability), PolicyCap::by_name(capability.name()));
209 }
210 }
211
212 #[test]
213 fn nulless_byte_str_equivalence() {
214 let unterminated: NullessByteStr<'_> = b"u:object_r:test_valid_t:s0".into();
215 let nul_terminated: NullessByteStr<'_> = b"u:object_r:test_valid_t:s0\0".into();
216 let nul_containing: NullessByteStr<'_> =
217 b"u:object_r:test_valid_t:s0\0IGNORE THIS\0!\0".into();
218
219 for context in [nul_terminated, nul_containing] {
220 assert_eq!(unterminated, context);
221 assert_eq!(unterminated.as_bytes(), context.as_bytes());
222 }
223 }
224}