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