1pub mod permission_check;
6pub mod policy;
7pub mod security_server;
8
9pub use security_server::SecurityServer;
10
11mod access_cache;
12mod access_vector_cache;
13mod exceptions_config;
14mod kernel_permissions;
15mod sid_table;
16mod sync;
17
18pub use kernel_permissions::*;
20
21pub use policy::ClassId;
23
24pub use starnix_uapi::selinux::{InitialSid, ReferenceInitialSid, SecurityId, TaskAttrs};
25
26use policy::arrays::FsUseType;
27use strum::VariantArray as _;
28use strum_macros::VariantArray;
29
30#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
32pub enum ObjectClass {
33 Kernel(KernelClass),
35 ClassId(ClassId),
38}
39
40impl From<ClassId> for ObjectClass {
41 fn from(id: ClassId) -> Self {
42 Self::ClassId(id)
43 }
44}
45
46impl<T: Into<KernelClass>> From<T> for ObjectClass {
47 fn from(class: T) -> Self {
48 Self::Kernel(class.into())
49 }
50}
51
52#[derive(Clone, Copy, Debug, PartialEq)]
55pub struct NullessByteStr<'a>(&'a [u8]);
56
57impl<'a> NullessByteStr<'a> {
58 pub fn as_bytes(&self) -> &[u8] {
60 &self.0
61 }
62}
63
64impl<'a, S: AsRef<[u8]> + ?Sized> From<&'a S> for NullessByteStr<'a> {
65 fn from(s: &'a S) -> Self {
69 let value = s.as_ref();
70 match value.iter().position(|c| *c == 0) {
71 Some(end) => Self(&value[..end]),
72 None => Self(value),
73 }
74 }
75}
76
77#[derive(Clone, Debug, PartialEq)]
78pub struct FileSystemMountSids {
79 pub context: Option<SecurityId>,
80 pub fs_context: Option<SecurityId>,
81 pub def_context: Option<SecurityId>,
82 pub root_context: Option<SecurityId>,
83}
84
85#[derive(Clone, Debug, PartialEq)]
86pub struct FileSystemLabel {
87 pub sid: SecurityId,
88 pub scheme: FileSystemLabelingScheme,
89 pub mount_sids: FileSystemMountSids,
91}
92
93#[derive(Clone, Debug, PartialEq)]
94pub enum FileSystemLabelingScheme {
95 Mountpoint { sid: SecurityId },
97 FsUse { fs_use_type: FsUseType, default_sid: SecurityId },
101 GenFsCon { supports_seclabel: bool },
104}
105
106#[derive(Clone, Debug, Default, PartialEq)]
110pub struct FileSystemMountOptions {
111 pub context: Option<Vec<u8>>,
115 pub def_context: Option<Vec<u8>>,
118 pub fs_context: Option<Vec<u8>>,
121 pub root_context: Option<Vec<u8>>,
124}
125
126pub struct SeLinuxStatus {
128 pub is_enforcing: bool,
130 pub change_count: u32,
132 pub deny_unknown: bool,
134}
135
136pub trait SeLinuxStatusPublisher: Send + Sync {
138 fn set_status(&mut self, policy_status: SeLinuxStatus);
140}
141
142#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, VariantArray)]
144pub enum PolicyCap {
145 NetworkPeerControls = 0,
146 OpenPerms = 1,
147 ExtendedSocketClass = 2,
148 AlwaysCheckNetwork = 3,
149 CgroupSeclabel = 4,
150 NnpNosuidTransition = 5,
151 GenfsSeclabelSymlinks = 6,
152 IoctlSkipCloexec = 7,
153 UserspaceInitialContext = 8,
154 NetlinkXperm = 9,
155 NetifWildcard = 10,
156 GenfsSeclabelWildcard = 11,
157 FunctionfsSeclabel = 12,
158 MemfdClass = 13,
159}
160
161impl PolicyCap {
162 pub fn name(&self) -> &str {
163 match self {
164 Self::NetworkPeerControls => "network_peer_controls",
165 Self::OpenPerms => "open_perms",
166 Self::ExtendedSocketClass => "extended_socket_class",
167 Self::AlwaysCheckNetwork => "always_check_network",
168 Self::CgroupSeclabel => "cgroup_seclabel",
169 Self::NnpNosuidTransition => "nnp_nosuid_transition",
170 Self::GenfsSeclabelSymlinks => "genfs_seclabel_symlinks",
171 Self::IoctlSkipCloexec => "ioctl_skip_cloexec",
172 Self::UserspaceInitialContext => "userspace_initial_context",
173 Self::NetlinkXperm => "netlink_xperm",
174 Self::NetifWildcard => "netif_wildcard",
175 Self::GenfsSeclabelWildcard => "genfs_seclabel_wildcard",
176 Self::FunctionfsSeclabel => "functionfs_seclabel",
177 Self::MemfdClass => "memfd_class",
178 }
179 }
180
181 pub fn by_name(name: &str) -> Option<Self> {
182 Self::VARIANTS.iter().find(|x| x.name() == name).copied()
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use std::num::NonZeroU32;
190
191 #[test]
192 fn object_class_permissions() {
193 let test_class_id = ClassId::new(NonZeroU32::new(20).unwrap());
194 assert_eq!(ObjectClass::ClassId(test_class_id), test_class_id.into());
195 for variant in ProcessPermission::VARIANTS {
196 assert_eq!(KernelClass::Process, variant.class());
197 assert_eq!("process", variant.class().name());
198 let permission = (*variant).into();
199 assert_eq!(KernelPermission::Process(*variant), permission);
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}