1#![recursion_limit = "512"]
6
7use starnix_sync::LockEqualOrBefore;
8
9use seq_lock::{SeqLock, SeqLockable, WriteSize};
10
11use selinux::policy::metadata::POLICYDB_VERSION_MAX;
12use selinux::policy::{AccessDecision, AccessVector};
13use selinux::{
14 ClassId, InitialSid, PolicyCap, SeLinuxStatus, SeLinuxStatusPublisher, SecurityId,
15 SecurityPermission, SecurityServer,
16};
17use starnix_core::device::mem::DevNull;
18use starnix_core::mm::memory::MemoryObject;
19use starnix_core::security;
20use starnix_core::task::{CurrentTask, Kernel};
21use starnix_core::vfs::buffers::{InputBuffer, OutputBuffer};
22use starnix_core::vfs::pseudo::simple_directory::{SimpleDirectory, SimpleDirectoryMutator};
23use starnix_core::vfs::pseudo::simple_file::{
24 BytesFile, BytesFileOps, SimpleFileNode, parse_unsigned_file,
25};
26use starnix_core::vfs::pseudo::vec_directory::{VecDirectory, VecDirectoryEntry};
27use starnix_core::vfs::{
28 CacheMode, DirEntry, DirectoryEntryType, DirentSink, FileObject, FileOps, FileSystem,
29 FileSystemHandle, FileSystemOps, FileSystemOptions, FsNode, FsNodeHandle, FsNodeInfo,
30 FsNodeOps, FsStr, FsString, MemoryRegularNode, NamespaceNode, emit_dotdot,
31 fileops_impl_directory, fileops_impl_noop_sync, fileops_impl_seekable,
32 fileops_impl_unbounded_seek, fs_node_impl_dir_readonly, fs_node_impl_not_dir,
33};
34use starnix_logging::{
35 __track_stub_inner, BugRef, impossible_error, log_error, log_info, track_stub,
36};
37use starnix_sync::{FileOpsCore, Locked, Mutex, Unlocked};
38use starnix_types::vfs::default_statfs;
39use starnix_uapi::auth::FsCred;
40use starnix_uapi::device_id::DeviceId;
41use starnix_uapi::errors::Errno;
42use starnix_uapi::file_mode::mode;
43use starnix_uapi::open_flags::OpenFlags;
44use starnix_uapi::{AUDIT_AVC, SELINUX_MAGIC, errno, error, statfs};
45use std::borrow::Cow;
46use std::num::{NonZeroU32, NonZeroU64};
47use std::ops::Deref;
48use std::str::FromStr;
49use std::sync::{Arc, OnceLock, Weak};
50use strum::VariantArray as _;
51use zerocopy::{Immutable, IntoBytes};
52use zx::{self as zx, HandleBased as _};
53
54const SELINUX_STATUS_VERSION: u32 = 1;
56
57#[derive(IntoBytes, Copy, Clone, Immutable)]
61#[repr(C, align(4))]
62struct SeLinuxStatusHeader {
63 version: u32,
65}
66
67impl Default for SeLinuxStatusHeader {
68 fn default() -> Self {
69 Self { version: SELINUX_STATUS_VERSION }
70 }
71}
72
73#[derive(IntoBytes, Copy, Clone, Default, Immutable)]
77#[repr(C, align(4))]
78struct SeLinuxStatusValue {
79 enforcing: u32,
81 policyload: u32,
83 deny_unknown: u32,
85}
86
87unsafe impl SeqLockable for SeLinuxStatusValue {
90 const WRITE_SIZE: WriteSize = WriteSize::Four;
91 const HAS_INLINE_SEQUENCE: bool = false;
92 const VMO_NAME: &'static [u8] = b"starnix:selinux";
93}
94
95type StatusSeqLock = SeqLock<SeLinuxStatusHeader, SeLinuxStatusValue>;
96
97struct StatusPublisher(StatusSeqLock);
98
99impl StatusPublisher {
100 pub fn new_default() -> Result<Self, zx::Status> {
101 let seq_lock = StatusSeqLock::new_default()?;
102 Ok(StatusPublisher(seq_lock))
103 }
104}
105
106impl SeLinuxStatusPublisher for StatusPublisher {
107 fn set_status(&mut self, policy_status: SeLinuxStatus) {
108 self.0.set_value(SeLinuxStatusValue {
109 enforcing: policy_status.is_enforcing as u32,
110 policyload: policy_status.change_count,
111 deny_unknown: policy_status.deny_unknown as u32,
112 })
113 }
114}
115
116struct SeLinuxFs;
117impl FileSystemOps for SeLinuxFs {
118 fn statfs(
119 &self,
120 _locked: &mut Locked<FileOpsCore>,
121 _fs: &FileSystem,
122 _current_task: &CurrentTask,
123 ) -> Result<statfs, Errno> {
124 Ok(default_statfs(SELINUX_MAGIC))
125 }
126 fn name(&self) -> &'static FsStr {
127 "selinuxfs".into()
128 }
129}
130
131impl SeLinuxFs {
135 fn new_fs<L>(
136 locked: &mut Locked<L>,
137 current_task: &CurrentTask,
138 options: FileSystemOptions,
139 ) -> Result<FileSystemHandle, Errno>
140 where
141 L: LockEqualOrBefore<FileOpsCore>,
142 {
143 let security_server = security::selinuxfs_get_admin_api(current_task)
145 .ok_or_else(|| errno!(ENODEV, "selinuxfs"))?;
146
147 let kernel = current_task.kernel();
148 let fs = FileSystem::new(locked, kernel, CacheMode::Permanent, SeLinuxFs, options)?;
149 let root = SimpleDirectory::new();
150 fs.create_root(fs.allocate_ino(), root.clone());
151 let dir = SimpleDirectoryMutator::new(fs.clone(), root);
152
153 dir.subdir("avc", 0o555, |dir| {
155 dir.entry(
156 "cache_stats",
157 AvcCacheStatsFile::new_node(security_server.clone()),
158 mode!(IFREG, 0o444),
159 );
160 });
161 dir.entry("checkreqprot", CheckReqProtApi::new_node(), mode!(IFREG, 0o644));
162 dir.entry("class", ClassDirectory::new(security_server.clone()), mode!(IFDIR, 0o555));
163 dir.entry(
164 "deny_unknown",
165 DenyUnknownFile::new_node(security_server.clone()),
166 mode!(IFREG, 0o444),
167 );
168 dir.entry(
169 "reject_unknown",
170 RejectUnknownFile::new_node(security_server.clone()),
171 mode!(IFREG, 0o444),
172 );
173 dir.subdir("initial_contexts", 0o555, |dir| {
174 for initial_sid in InitialSid::all_variants() {
175 dir.entry(
176 initial_sid.name(),
177 InitialContextFile::new_node(security_server.clone(), *initial_sid),
178 mode!(IFREG, 0o444),
179 );
180 }
181 });
182 dir.entry("mls", BytesFile::new_node(b"1".to_vec()), mode!(IFREG, 0o444));
183 dir.entry("policy", PolicyFile::new_node(security_server.clone()), mode!(IFREG, 0o600));
184 dir.subdir("policy_capabilities", 0o555, |dir| {
185 for capability in PolicyCap::VARIANTS {
186 dir.entry(
187 capability.name(),
188 PolicyCapFile::new_node(security_server.clone(), *capability),
189 mode!(IFREG, 0o444),
190 );
191 }
192 });
193 dir.entry(
194 "policyvers",
195 BytesFile::new_node(format!("{}", POLICYDB_VERSION_MAX).into_bytes()),
196 mode!(IFREG, 0o444),
197 );
198
199 let status_holder = StatusPublisher::new_default().expect("selinuxfs status seqlock");
203 let status_file = status_holder
204 .0
205 .get_readonly_vmo()
206 .duplicate_handle(zx::Rights::SAME_RIGHTS)
207 .map_err(impossible_error)?;
208 dir.entry(
209 "status",
210 MemoryRegularNode::from_memory(Arc::new(MemoryObject::from(status_file))),
211 mode!(IFREG, 0o444),
212 );
213 security_server.set_status_publisher(Box::new(status_holder));
214
215 dir.entry(
217 "access",
218 AccessApi::new_node(security_server.clone(), current_task.kernel()),
219 mode!(IFREG, 0o666),
220 );
221 dir.entry("context", ContextApi::new_node(security_server.clone()), mode!(IFREG, 0o666));
222 dir.entry("create", CreateApi::new_node(security_server.clone()), mode!(IFREG, 0o666));
223 dir.entry("member", MemberApi::new_node(), mode!(IFREG, 0o666));
224 dir.entry("relabel", RelabelApi::new_node(), mode!(IFREG, 0o666));
225 dir.entry("user", UserApi::new_node(), mode!(IFREG, 0o666));
226 dir.entry("load", LoadApi::new_node(security_server.clone()), mode!(IFREG, 0o600));
227 dir.entry(
228 "commit_pending_bools",
229 CommitBooleansApi::new_node(security_server.clone()),
230 mode!(IFREG, 0o200),
231 );
232
233 dir.entry("booleans", BooleansDirectory::new(security_server.clone()), mode!(IFDIR, 0o555));
235 dir.entry("enforce", EnforceApi::new_node(security_server), mode!(IFREG, 0o644));
237
238 let null_ops: Box<dyn FsNodeOps> = (NullFileNode).into();
240 let mut info = FsNodeInfo::new(mode!(IFCHR, 0o666), FsCred::root());
241 info.rdev = DeviceId::NULL;
242 let null_fs_node = fs.create_node_and_allocate_node_id(null_ops, info);
243 dir.node("null".into(), null_fs_node.clone());
244
245 let null_ops: Box<dyn FileOps> = Box::new(DevNull);
249 let null_flags = OpenFlags::empty();
250 let null_name =
251 NamespaceNode::new_anonymous(DirEntry::new(null_fs_node, None, "null".into()));
252 let null_file_object =
253 FileObject::new(locked, current_task, null_ops, null_name, null_flags)
254 .expect("create file object for just-created selinuxfs/null");
255 security::selinuxfs_init_null(current_task, &null_file_object);
256
257 Ok(fs)
258 }
259}
260
261struct LoadApi {
264 security_server: Arc<SecurityServer>,
265}
266
267impl LoadApi {
268 fn new_node(security_server: Arc<SecurityServer>) -> impl FsNodeOps {
269 SeLinuxApi::new_node(move || Ok(Self { security_server: security_server.clone() }))
270 }
271}
272
273impl SeLinuxApiOps for LoadApi {
274 fn api_write_permission() -> SecurityPermission {
275 SecurityPermission::LoadPolicy
276 }
277 fn api_write_with_task(
278 &self,
279 locked: &mut Locked<FileOpsCore>,
280 current_task: &CurrentTask,
281 data: Vec<u8>,
282 ) -> Result<(), Errno> {
283 log_info!("Loading {} byte policy", data.len());
284 self.security_server.load_policy(data).map_err(|error| {
285 log_error!("Policy load error: {}", error);
286 errno!(EINVAL)
287 })?;
288
289 security::selinuxfs_policy_loaded(locked, current_task);
291
292 Ok(())
293 }
294}
295
296struct PolicyFile {
299 security_server: Arc<SecurityServer>,
300}
301
302impl PolicyFile {
303 fn new_node(security_server: Arc<SecurityServer>) -> impl FsNodeOps {
304 SimpleFileNode::new(move |_, _| Ok(Self { security_server: security_server.clone() }))
305 }
306}
307
308impl FileOps for PolicyFile {
309 fileops_impl_seekable!();
310 fileops_impl_noop_sync!();
311
312 fn read(
313 &self,
314 _locked: &mut Locked<FileOpsCore>,
315 _file: &FileObject,
316 _current_task: &CurrentTask,
317 offset: usize,
318 data: &mut dyn OutputBuffer,
319 ) -> Result<usize, Errno> {
320 let policy = self.security_server.get_binary_policy().ok_or_else(|| errno!(EINVAL))?;
321 let policy_bytes: &[u8] = policy.deref();
322
323 if offset >= policy_bytes.len() {
324 return Ok(0);
325 }
326
327 data.write(&policy_bytes[offset..])
328 }
329
330 fn write(
331 &self,
332 _locked: &mut Locked<FileOpsCore>,
333 _file: &FileObject,
334 _current_task: &CurrentTask,
335 _offset: usize,
336 _data: &mut dyn InputBuffer,
337 ) -> Result<usize, Errno> {
338 error!(EACCES)
339 }
340}
341
342struct EnforceApi {
344 security_server: Arc<SecurityServer>,
345}
346
347impl EnforceApi {
348 fn new_node(security_server: Arc<SecurityServer>) -> impl FsNodeOps {
349 SeLinuxApi::new_node(move || Ok(Self { security_server: security_server.clone() }))
350 }
351}
352
353impl SeLinuxApiOps for EnforceApi {
354 fn api_write_permission() -> SecurityPermission {
355 SecurityPermission::SetEnforce
356 }
357
358 fn api_write(&self, data: Vec<u8>) -> Result<(), Errno> {
359 let enforce = parse_unsigned_file::<u32>(&data)? != 0;
361 self.security_server.set_enforcing(enforce);
362 Ok(())
363 }
364
365 fn api_read(&self) -> Result<Cow<'_, [u8]>, Errno> {
366 Ok(self.security_server.is_enforcing().then_some(b"1").unwrap_or(b"0").into())
367 }
368}
369
370struct DenyUnknownFile {
373 security_server: Arc<SecurityServer>,
374}
375
376impl DenyUnknownFile {
377 fn new_node(security_server: Arc<SecurityServer>) -> impl FsNodeOps {
378 BytesFile::new_node(Self { security_server })
379 }
380}
381
382impl BytesFileOps for DenyUnknownFile {
383 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
384 Ok(format!("{}", self.security_server.deny_unknown() as u32).into_bytes().into())
385 }
386}
387
388struct RejectUnknownFile {
391 security_server: Arc<SecurityServer>,
392}
393
394impl RejectUnknownFile {
395 fn new_node(security_server: Arc<SecurityServer>) -> impl FsNodeOps {
396 BytesFile::new_node(Self { security_server })
397 }
398}
399
400impl BytesFileOps for RejectUnknownFile {
401 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
402 Ok(format!("{}", self.security_server.reject_unknown() as u32).into_bytes().into())
403 }
404}
405
406struct CreateApi {
409 security_server: Arc<SecurityServer>,
410 result: OnceLock<SecurityId>,
411}
412
413impl CreateApi {
414 fn new_node(security_server: Arc<SecurityServer>) -> impl FsNodeOps {
415 SeLinuxApi::new_node(move || {
416 Ok(Self { security_server: security_server.clone(), result: OnceLock::new() })
417 })
418 }
419}
420
421impl SeLinuxApiOps for CreateApi {
422 fn api_write_permission() -> SecurityPermission {
423 SecurityPermission::ComputeCreate
424 }
425
426 fn api_write(&self, data: Vec<u8>) -> Result<(), Errno> {
427 if self.result.get().is_some() {
428 return error!(EBUSY);
430 }
431
432 let data = str::from_utf8(&data).map_err(|_| errno!(EINVAL))?;
433
434 let mut parts = data.split_whitespace();
436
437 let scontext = parts.next().ok_or_else(|| errno!(EINVAL))?;
439 let scontext = self
440 .security_server
441 .security_context_to_sid(scontext.into())
442 .map_err(|_| errno!(EINVAL))?;
443
444 let tcontext = parts.next().ok_or_else(|| errno!(EINVAL))?;
446 let tcontext = self
447 .security_server
448 .security_context_to_sid(tcontext.into())
449 .map_err(|_| errno!(EINVAL))?;
450
451 let tclass = parts.next().ok_or_else(|| errno!(EINVAL))?;
454 let tclass = u32::from_str(tclass).map_err(|_| errno!(EINVAL))?;
455 let tclass = ClassId::new(NonZeroU32::new(tclass).ok_or_else(|| errno!(EINVAL))?);
456
457 let tname = parts.next();
460 if tname.is_some() {
461 track_stub!(TODO("https://fxbug.dev/361552580"), "selinux create with name");
462 return error!(ENOTSUP);
463 }
464
465 if parts.next().is_some() {
467 return error!(EINVAL);
468 }
469
470 let result = self
471 .security_server
472 .compute_create_sid_raw(scontext, tcontext, tclass)
473 .map_err(|_| errno!(EINVAL))?;
474 self.result.set(result).map_err(|_| errno!(EINVAL))?;
475
476 Ok(())
477 }
478
479 fn api_read(&self) -> Result<Cow<'_, [u8]>, Errno> {
480 let maybe_context = self
481 .result
482 .get()
483 .map(|sid| self.security_server.sid_to_security_context_with_nul(*sid).unwrap());
484 let context = maybe_context.unwrap_or_else(|| Vec::new());
485 Ok(context.into())
486 }
487}
488
489struct MemberApi;
492
493impl MemberApi {
494 fn new_node() -> impl FsNodeOps {
495 SeLinuxApi::new_node(|| Ok(Self {}))
496 }
497}
498
499impl SeLinuxApiOps for MemberApi {
500 fn api_write_permission() -> SecurityPermission {
501 SecurityPermission::ComputeMember
502 }
503 fn api_write(&self, _data: Vec<u8>) -> Result<(), Errno> {
504 track_stub!(TODO("https://fxbug.dev/399069170"), "selinux member");
505 error!(ENOTSUP)
506 }
507 fn api_read(&self) -> Result<Cow<'_, [u8]>, Errno> {
508 error!(ENOTSUP)
509 }
510}
511
512struct RelabelApi;
515
516impl RelabelApi {
517 fn new_node() -> impl FsNodeOps {
518 SeLinuxApi::new_node(|| Ok(Self {}))
519 }
520}
521
522impl SeLinuxApiOps for RelabelApi {
523 fn api_write_permission() -> SecurityPermission {
524 SecurityPermission::ComputeRelabel
525 }
526 fn api_write(&self, _data: Vec<u8>) -> Result<(), Errno> {
527 track_stub!(TODO("https://fxbug.dev/399069766"), "selinux relabel");
528 error!(ENOTSUP)
529 }
530 fn api_read(&self) -> Result<Cow<'_, [u8]>, Errno> {
531 error!(ENOTSUP)
532 }
533}
534
535struct UserApi;
537
538impl UserApi {
539 fn new_node() -> impl FsNodeOps {
540 SeLinuxApi::new_node(|| Ok(Self {}))
541 }
542}
543
544impl SeLinuxApiOps for UserApi {
545 fn api_write_permission() -> SecurityPermission {
546 SecurityPermission::ComputeUser
547 }
548 fn api_write(&self, _data: Vec<u8>) -> Result<(), Errno> {
549 track_stub!(TODO("https://fxbug.dev/411433214"), "selinux user");
550 error!(ENOTSUP)
551 }
552 fn api_read(&self) -> Result<Cow<'_, [u8]>, Errno> {
553 error!(ENOTSUP)
554 }
555}
556
557struct CheckReqProtApi;
558
559impl CheckReqProtApi {
560 fn new_node() -> impl FsNodeOps {
561 SeLinuxApi::new_node(|| Ok(Self {}))
562 }
563}
564
565impl SeLinuxApiOps for CheckReqProtApi {
566 fn api_write_permission() -> SecurityPermission {
567 SecurityPermission::SetCheckReqProt
568 }
569
570 fn api_write(&self, _data: Vec<u8>) -> Result<(), Errno> {
571 error!(ENOTSUP)
574 }
575
576 fn api_read(&self) -> Result<Cow<'_, [u8]>, Errno> {
577 Ok(b"0".into())
578 }
579}
580
581struct ContextApi {
585 security_server: Arc<SecurityServer>,
586 context_sid: Mutex<Option<SecurityId>>,
588}
589
590impl ContextApi {
591 fn new_node(security_server: Arc<SecurityServer>) -> impl FsNodeOps {
592 SeLinuxApi::new_node(move || {
593 Ok(Self { security_server: security_server.clone(), context_sid: Mutex::default() })
594 })
595 }
596}
597
598impl SeLinuxApiOps for ContextApi {
599 fn api_write_permission() -> SecurityPermission {
600 SecurityPermission::CheckContext
601 }
602
603 fn api_write(&self, data: Vec<u8>) -> Result<(), Errno> {
604 let mut context_sid = self.context_sid.lock();
606 if context_sid.is_some() {
607 return error!(EBUSY);
608 }
609
610 *context_sid = Some(
613 self.security_server
614 .security_context_to_sid(data.as_slice().into())
615 .map_err(|_| errno!(EINVAL))?,
616 );
617
618 Ok(())
619 }
620
621 fn api_read(&self) -> Result<Cow<'_, [u8]>, Errno> {
622 let maybe_sid = *self.context_sid.lock();
628 let result = maybe_sid
629 .and_then(|sid| self.security_server.sid_to_security_context_with_nul(sid))
630 .unwrap_or_default();
631 Ok(result.into())
632 }
633}
634
635struct InitialContextFile {
637 security_server: Arc<SecurityServer>,
638 initial_sid: InitialSid,
639}
640
641impl InitialContextFile {
642 fn new_node(security_server: Arc<SecurityServer>, initial_sid: InitialSid) -> impl FsNodeOps {
643 BytesFile::new_node(Self { security_server, initial_sid })
644 }
645}
646
647impl BytesFileOps for InitialContextFile {
648 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
649 let sid = self.initial_sid.into();
650 if let Some(context) = self.security_server.sid_to_security_context_with_nul(sid) {
651 Ok(context.into())
652 } else {
653 Ok(self.initial_sid.name().as_bytes().into())
657 }
658 }
659}
660
661struct PolicyCapFile {
665 security_server: Arc<SecurityServer>,
666 policy_cap: PolicyCap,
667}
668
669impl PolicyCapFile {
670 fn new_node(security_server: Arc<SecurityServer>, initial_sid: PolicyCap) -> impl FsNodeOps {
671 BytesFile::new_node(Self { security_server, policy_cap: initial_sid })
672 }
673}
674
675impl BytesFileOps for PolicyCapFile {
676 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
677 if self.security_server.is_policycap_enabled(self.policy_cap) {
678 Ok(b"1".into())
679 } else {
680 Ok(b"0".into())
681 }
682 }
683}
684
685struct AccessDecisionAndDecided {
690 decision: AccessDecision,
691 decided: AccessVector,
692}
693
694struct AccessApi {
695 security_server: Arc<SecurityServer>,
696 result: OnceLock<AccessDecisionAndDecided>,
697
698 kernel: Weak<Kernel>,
700}
701
702impl AccessApi {
703 fn new_node(security_server: Arc<SecurityServer>, kernel: &Arc<Kernel>) -> impl FsNodeOps {
704 let kernel = Arc::downgrade(kernel);
705 SeLinuxApi::new_node(move || {
706 Ok(Self {
707 security_server: security_server.clone(),
708 result: OnceLock::default(),
709 kernel: kernel.clone(),
710 })
711 })
712 }
713}
714
715impl SeLinuxApiOps for AccessApi {
716 fn api_write_permission() -> SecurityPermission {
717 SecurityPermission::ComputeAv
718 }
719
720 fn api_write(&self, data: Vec<u8>) -> Result<(), Errno> {
721 if self.result.get().is_some() {
722 return error!(EBUSY);
724 }
725
726 let data = str::from_utf8(&data).map_err(|_| errno!(EINVAL))?;
727
728 let mut parts = data.split_whitespace();
730
731 let scontext_str = parts.next().ok_or_else(|| errno!(EINVAL))?;
733 let scontext = self
734 .security_server
735 .security_context_to_sid(scontext_str.into())
736 .map_err(|_| errno!(EINVAL))?;
737
738 let tcontext_str = parts.next().ok_or_else(|| errno!(EINVAL))?;
740 let tcontext = self
741 .security_server
742 .security_context_to_sid(tcontext_str.into())
743 .map_err(|_| errno!(EINVAL))?;
744
745 let tclass = parts.next().ok_or_else(|| errno!(EINVAL))?;
748 let tclass_id = u32::from_str(tclass).map_err(|_| errno!(EINVAL))?;
749 let tclass = ClassId::new(NonZeroU32::new(tclass_id).ok_or_else(|| errno!(EINVAL))?).into();
750
751 let requested = if let Some(requested) = parts.next() {
753 AccessVector::from_str(requested).map_err(|_| errno!(EINVAL))?
754 } else {
755 AccessVector::ALL
756 };
757
758 let mut decision =
762 self.security_server.compute_access_decision_raw(scontext, tcontext, tclass);
763
764 let mut decided = AccessVector::ALL;
768
769 let Some(kernel) = self.kernel.upgrade() else {
772 return error!(EINVAL);
773 };
774 if let Some(todo_bug) = decision.todo_bug {
775 let denied = AccessVector::ALL - decision.allow;
776 let audited_denied = denied & decision.auditdeny;
777
778 let requested_has_audited_denial = audited_denied & requested != AccessVector::NONE;
779
780 if requested_has_audited_denial {
781 __track_stub_inner(
785 BugRef::from(NonZeroU64::new(todo_bug.get() as u64).unwrap()),
786 "Enforce SELinuxFS access API",
787 None,
788 std::panic::Location::caller(),
789 );
790 let audit_message = format!(
791 "avc: todo_deny {{ ACCESS_API }} bug={todo_bug} scontext={scontext_str:?} tcontext={tcontext_str:?} tclass={tclass_id} requested={requested:?}",
792 );
793 kernel.audit_logger().audit_log(AUDIT_AVC as u16, || audit_message);
794 } else {
795 decided -= audited_denied;
800 }
801
802 decision.allow = AccessVector::ALL;
805 }
806
807 self.result
808 .set(AccessDecisionAndDecided { decision, decided })
809 .map_err(|_| errno!(EINVAL))?;
810
811 Ok(())
812 }
813
814 fn api_read(&self) -> Result<Cow<'_, [u8]>, Errno> {
815 let Some(AccessDecisionAndDecided { decision, decided }) = self.result.get() else {
816 return Ok(Vec::new().into());
817 };
818
819 let allowed = decision.allow;
820 let auditallow = decision.auditallow;
821 let auditdeny = decision.auditdeny;
822 let flags = decision.flags;
823
824 const SEQNO: u32 = 1;
827
828 let result =
831 format!("{allowed:x} {decided:x} {auditallow:x} {auditdeny:x} {SEQNO} {flags:x}");
832 Ok(result.into_bytes().into())
833 }
834}
835
836struct NullFileNode;
837
838impl FsNodeOps for NullFileNode {
839 fs_node_impl_not_dir!();
840
841 fn create_file_ops(
842 &self,
843 _locked: &mut Locked<FileOpsCore>,
844 _node: &FsNode,
845 _current_task: &CurrentTask,
846 _flags: OpenFlags,
847 ) -> Result<Box<dyn FileOps>, Errno> {
848 Ok(Box::new(DevNull))
849 }
850}
851
852#[derive(Clone)]
853struct BooleansDirectory {
854 security_server: Arc<SecurityServer>,
855}
856
857impl BooleansDirectory {
858 fn new(security_server: Arc<SecurityServer>) -> Self {
859 Self { security_server }
860 }
861}
862
863impl FsNodeOps for BooleansDirectory {
864 fs_node_impl_dir_readonly!();
865
866 fn create_file_ops(
867 &self,
868 _locked: &mut Locked<FileOpsCore>,
869 _node: &FsNode,
870 _current_task: &CurrentTask,
871 _flags: OpenFlags,
872 ) -> Result<Box<dyn FileOps>, Errno> {
873 Ok(Box::new(self.clone()))
874 }
875
876 fn lookup(
877 &self,
878 _locked: &mut Locked<FileOpsCore>,
879 node: &FsNode,
880 current_task: &CurrentTask,
881 name: &FsStr,
882 ) -> Result<FsNodeHandle, Errno> {
883 let utf8_name = String::from_utf8(name.to_vec()).map_err(|_| errno!(ENOENT))?;
884 if self.security_server.conditional_booleans().contains(&utf8_name) {
885 Ok(node.fs().create_node_and_allocate_node_id(
886 BooleanFile::new_node(self.security_server.clone(), utf8_name),
887 FsNodeInfo::new(mode!(IFREG, 0o644), current_task.current_fscred()),
888 ))
889 } else {
890 error!(ENOENT)
891 }
892 }
893}
894
895impl FileOps for BooleansDirectory {
896 fileops_impl_directory!();
897 fileops_impl_noop_sync!();
898 fileops_impl_unbounded_seek!();
899
900 fn readdir(
901 &self,
902 _locked: &mut Locked<FileOpsCore>,
903 file: &FileObject,
904 _current_task: &CurrentTask,
905 sink: &mut dyn DirentSink,
906 ) -> Result<(), Errno> {
907 emit_dotdot(file, sink)?;
908
909 let iter_offset = sink.offset() - 2;
912 for name in self.security_server.conditional_booleans().iter().skip(iter_offset as usize) {
913 sink.add(
914 file.fs.allocate_ino(),
915 sink.offset() + 1,
916 DirectoryEntryType::REG,
917 FsString::from(name.as_bytes()).as_ref(),
918 )?;
919 }
920
921 Ok(())
922 }
923}
924
925struct BooleanFile {
926 security_server: Arc<SecurityServer>,
927 name: String,
928}
929
930impl BooleanFile {
931 fn new_node(security_server: Arc<SecurityServer>, name: String) -> impl FsNodeOps {
932 BytesFile::new_node(BooleanFile { security_server, name })
933 }
934}
935
936impl BytesFileOps for BooleanFile {
937 fn write(&self, _current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
938 let value = parse_unsigned_file::<u32>(&data)? != 0;
939 self.security_server.set_pending_boolean(&self.name, value).map_err(|_| errno!(EIO))
940 }
941
942 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
943 let (active, pending) =
947 self.security_server.get_boolean(&self.name).map_err(|_| errno!(EIO))?;
948 Ok(format!("{} {}", active as u32, pending as u32).into_bytes().into())
949 }
950}
951
952struct CommitBooleansApi {
953 security_server: Arc<SecurityServer>,
954}
955
956impl CommitBooleansApi {
957 fn new_node(security_server: Arc<SecurityServer>) -> impl FsNodeOps {
958 SeLinuxApi::new_node(move || {
959 Ok(CommitBooleansApi { security_server: security_server.clone() })
960 })
961 }
962}
963
964impl SeLinuxApiOps for CommitBooleansApi {
965 fn api_write_permission() -> SecurityPermission {
966 SecurityPermission::SetBool
967 }
968
969 fn api_write(&self, data: Vec<u8>) -> Result<(), Errno> {
970 let commit = parse_unsigned_file::<u32>(&data)? != 0;
974
975 if commit {
976 self.security_server.commit_pending_booleans();
977 }
978 Ok(())
979 }
980}
981
982struct ClassDirectory {
983 security_server: Arc<SecurityServer>,
984}
985
986impl ClassDirectory {
987 fn new(security_server: Arc<SecurityServer>) -> Self {
988 Self { security_server }
989 }
990}
991
992impl FsNodeOps for ClassDirectory {
993 fs_node_impl_dir_readonly!();
994
995 fn create_file_ops(
997 &self,
998 _locked: &mut Locked<FileOpsCore>,
999 _node: &FsNode,
1000 _current_task: &CurrentTask,
1001 _flags: OpenFlags,
1002 ) -> Result<Box<dyn FileOps>, Errno> {
1003 Ok(VecDirectory::new_file(
1004 self.security_server
1005 .class_names()
1006 .map_err(|_| errno!(ENOENT))?
1007 .iter()
1008 .map(|class_name| VecDirectoryEntry {
1009 entry_type: DirectoryEntryType::DIR,
1010 name: class_name.clone().into(),
1011 inode: None,
1012 })
1013 .collect(),
1014 ))
1015 }
1016
1017 fn lookup(
1018 &self,
1019 _locked: &mut Locked<FileOpsCore>,
1020 node: &FsNode,
1021 _current_task: &CurrentTask,
1022 name: &FsStr,
1023 ) -> Result<FsNodeHandle, Errno> {
1024 let id: u32 = self
1025 .security_server
1026 .class_id_by_name(&name.to_string())
1027 .map_err(|_| errno!(EINVAL))?
1028 .into();
1029
1030 let fs = node.fs();
1031 let dir = SimpleDirectory::new();
1032 dir.edit(&fs, |dir| {
1033 let index_bytes = format!("{}", id).into_bytes();
1034 dir.entry("index", BytesFile::new_node(index_bytes), mode!(IFREG, 0o444));
1035 dir.entry(
1036 "perms",
1037 PermsDirectory::new(self.security_server.clone(), name.to_string()),
1038 mode!(IFDIR, 0o555),
1039 );
1040 });
1041 Ok(dir.into_node(&fs, 0o555))
1042 }
1043}
1044
1045struct PermsDirectory {
1047 security_server: Arc<SecurityServer>,
1048 class_name: String,
1049}
1050
1051impl PermsDirectory {
1052 fn new(security_server: Arc<SecurityServer>, class_name: String) -> Self {
1053 Self { security_server, class_name }
1054 }
1055}
1056
1057impl FsNodeOps for PermsDirectory {
1058 fs_node_impl_dir_readonly!();
1059
1060 fn create_file_ops(
1062 &self,
1063 _locked: &mut Locked<FileOpsCore>,
1064 _node: &FsNode,
1065 _current_task: &CurrentTask,
1066 _flags: OpenFlags,
1067 ) -> Result<Box<dyn FileOps>, Errno> {
1068 Ok(VecDirectory::new_file(
1069 self.security_server
1070 .class_permissions_by_name(&self.class_name)
1071 .map_err(|_| errno!(ENOENT))?
1072 .iter()
1073 .map(|(_permission_id, permission_name)| VecDirectoryEntry {
1074 entry_type: DirectoryEntryType::DIR,
1075 name: permission_name.clone().into(),
1076 inode: None,
1077 })
1078 .collect(),
1079 ))
1080 }
1081
1082 fn lookup(
1083 &self,
1084 _locked: &mut Locked<FileOpsCore>,
1085 node: &FsNode,
1086 current_task: &CurrentTask,
1087 name: &FsStr,
1088 ) -> Result<FsNodeHandle, Errno> {
1089 let found_permission_id = self
1090 .security_server
1091 .class_permissions_by_name(&(self.class_name))
1092 .map_err(|_| errno!(ENOENT))?
1093 .iter()
1094 .find(|(_permission_id, permission_name)| permission_name == name)
1095 .ok_or_else(|| errno!(ENOENT))?
1096 .0;
1097
1098 Ok(node.fs().create_node_and_allocate_node_id(
1099 BytesFile::new_node(format!("{}", found_permission_id).into_bytes()),
1100 FsNodeInfo::new(mode!(IFREG, 0o444), current_task.current_fscred()),
1101 ))
1102 }
1103}
1104
1105struct AvcCacheStatsFile {
1107 security_server: Arc<SecurityServer>,
1108}
1109
1110impl AvcCacheStatsFile {
1111 fn new_node(security_server: Arc<SecurityServer>) -> impl FsNodeOps {
1112 BytesFile::new_node(Self { security_server })
1113 }
1114}
1115
1116impl BytesFileOps for AvcCacheStatsFile {
1117 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
1118 let stats = self.security_server.avc_cache_stats();
1119 Ok(format!(
1120 "lookups hits misses allocations reclaims frees\n{} {} {} {} {} {}\n",
1121 stats.lookups, stats.hits, stats.misses, stats.allocs, stats.reclaims, stats.frees
1122 )
1123 .into_bytes()
1124 .into())
1125 }
1126}
1127
1128struct SeLinuxApi<T: SeLinuxApiOps + Sync + Send + 'static> {
1155 ops: T,
1156}
1157
1158impl<T: SeLinuxApiOps + Sync + Send + 'static> SeLinuxApi<T> {
1159 fn new_node<F>(create_ops: F) -> impl FsNodeOps
1162 where
1163 F: Fn() -> Result<T, Errno> + Send + Sync + 'static,
1164 {
1165 SimpleFileNode::new(move |_, _| create_ops().map(|ops| SeLinuxApi { ops }))
1166 }
1167}
1168
1169trait SeLinuxApiOps {
1171 fn api_write_permission() -> SecurityPermission;
1173
1174 fn api_write_ignores_offset() -> bool {
1176 false
1177 }
1178
1179 fn api_write(&self, _data: Vec<u8>) -> Result<(), Errno> {
1181 error!(EINVAL)
1182 }
1183
1184 fn api_read(&self) -> Result<Cow<'_, [u8]>, Errno> {
1186 error!(EINVAL)
1187 }
1188
1189 fn api_write_with_task(
1191 &self,
1192 _locked: &mut Locked<FileOpsCore>,
1193 _current_task: &CurrentTask,
1194 data: Vec<u8>,
1195 ) -> Result<(), Errno> {
1196 self.api_write(data)
1197 }
1198}
1199
1200impl<T: SeLinuxApiOps + Sync + Send + 'static> FileOps for SeLinuxApi<T> {
1201 fileops_impl_seekable!();
1202 fileops_impl_noop_sync!();
1203
1204 fn writes_update_seek_offset(&self) -> bool {
1205 false
1206 }
1207
1208 fn read(
1209 &self,
1210 _locked: &mut Locked<FileOpsCore>,
1211 _file: &FileObject,
1212 _current_task: &CurrentTask,
1213 offset: usize,
1214 data: &mut dyn OutputBuffer,
1215 ) -> Result<usize, Errno> {
1216 let response = self.ops.api_read()?;
1217 data.write(&response[offset..])
1218 }
1219
1220 fn write(
1221 &self,
1222 locked: &mut Locked<FileOpsCore>,
1223 _file: &FileObject,
1224 current_task: &CurrentTask,
1225 offset: usize,
1226 data: &mut dyn InputBuffer,
1227 ) -> Result<usize, Errno> {
1228 if offset != 0 && !T::api_write_ignores_offset() {
1229 return error!(EINVAL);
1230 }
1231 security::selinuxfs_check_access(current_task, T::api_write_permission())?;
1232 let data = data.read_all()?;
1233 let data_len = data.len();
1234 self.ops.api_write_with_task(locked, current_task, data)?;
1235 Ok(data_len)
1236 }
1237}
1238
1239pub fn selinux_fs(
1241 locked: &mut Locked<Unlocked>,
1242 current_task: &CurrentTask,
1243 options: FileSystemOptions,
1244) -> Result<FileSystemHandle, Errno> {
1245 struct SeLinuxFsHandle(FileSystemHandle);
1246
1247 Ok(current_task
1248 .kernel()
1249 .expando
1250 .get_or_try_init(|| Ok(SeLinuxFsHandle(SeLinuxFs::new_fs(locked, current_task, options)?)))?
1251 .0
1252 .clone())
1253}
1254
1255#[cfg(test)]
1256mod tests {
1257 use super::*;
1258 use fuchsia_runtime;
1259 use selinux::SecurityServer;
1260 use zerocopy::{FromBytes, KnownLayout};
1261
1262 #[fuchsia::test]
1263 fn status_vmo_has_correct_size_and_rights() {
1264 const STATUS_T_SIZE: usize = size_of::<u32>() * 5;
1267
1268 let status_holder = StatusPublisher::new_default().unwrap();
1269 let status_vmo = status_holder.0.get_readonly_vmo();
1270
1271 let content_size = status_vmo.get_content_size().unwrap() as usize;
1273 assert_eq!(content_size, STATUS_T_SIZE);
1274 let actual_size = status_vmo.get_size().unwrap() as usize;
1275 assert!(actual_size >= STATUS_T_SIZE);
1276
1277 let rights = status_vmo.basic_info().unwrap().rights;
1279 assert_eq!((rights & zx::Rights::MAP), zx::Rights::MAP);
1280 assert_eq!((rights & zx::Rights::READ), zx::Rights::READ);
1281 assert_eq!((rights & zx::Rights::GET_PROPERTY), zx::Rights::GET_PROPERTY);
1282 assert_eq!((rights & zx::Rights::WRITE), zx::Rights::NONE);
1283 assert_eq!((rights & zx::Rights::RESIZE), zx::Rights::NONE);
1284 }
1285
1286 #[derive(KnownLayout, FromBytes)]
1287 #[repr(C, align(4))]
1288 struct TestSeLinuxStatusT {
1289 version: u32,
1290 sequence: u32,
1291 enforcing: u32,
1292 policyload: u32,
1293 deny_unknown: u32,
1294 }
1295
1296 fn with_status_t<R>(
1297 status_vmo: &Arc<zx::Vmo>,
1298 do_test: impl FnOnce(&TestSeLinuxStatusT) -> R,
1299 ) -> R {
1300 let flags = zx::VmarFlags::PERM_READ
1301 | zx::VmarFlags::ALLOW_FAULTS
1302 | zx::VmarFlags::REQUIRE_NON_RESIZABLE;
1303 let map_addr = fuchsia_runtime::vmar_root_self()
1304 .map(0, status_vmo, 0, size_of::<TestSeLinuxStatusT>(), flags)
1305 .unwrap();
1306 #[allow(
1307 clippy::undocumented_unsafe_blocks,
1308 reason = "Force documented unsafe blocks in Starnix"
1309 )]
1310 let mapped_status = unsafe { &mut *(map_addr as *mut TestSeLinuxStatusT) };
1311 let result = do_test(mapped_status);
1312 #[allow(
1313 clippy::undocumented_unsafe_blocks,
1314 reason = "Force documented unsafe blocks in Starnix"
1315 )]
1316 unsafe {
1317 fuchsia_runtime::vmar_root_self()
1318 .unmap(map_addr, size_of::<TestSeLinuxStatusT>())
1319 .unwrap()
1320 };
1321 result
1322 }
1323
1324 #[fuchsia::test]
1325 fn status_file_layout() {
1326 let security_server = SecurityServer::new_default();
1327 let status_holder = StatusPublisher::new_default().unwrap();
1328 let status_vmo = status_holder.0.get_readonly_vmo();
1329 security_server.set_status_publisher(Box::new(status_holder));
1330 security_server.set_enforcing(false);
1331 let mut seq_no: u32 = 0;
1332 with_status_t(&status_vmo, |status| {
1333 assert_eq!(status.version, SELINUX_STATUS_VERSION);
1334 assert_eq!(status.enforcing, 0);
1335 seq_no = status.sequence;
1336 assert_eq!(seq_no % 2, 0);
1337 });
1338 security_server.set_enforcing(true);
1339 with_status_t(&status_vmo, |status| {
1340 assert_eq!(status.version, SELINUX_STATUS_VERSION);
1341 assert_eq!(status.enforcing, 1);
1342 assert_ne!(status.sequence, seq_no);
1343 seq_no = status.sequence;
1344 assert_eq!(seq_no % 2, 0);
1345 });
1346 }
1347
1348 #[fuchsia::test]
1349 fn status_accurate_directly_following_set_status_publisher() {
1350 let security_server = SecurityServer::new_default();
1351 let status_holder = StatusPublisher::new_default().unwrap();
1352 let status_vmo = status_holder.0.get_readonly_vmo();
1353
1354 assert_eq!(false, security_server.is_enforcing());
1357 security_server.set_enforcing(true);
1358
1359 security_server.set_status_publisher(Box::new(status_holder));
1360 with_status_t(&status_vmo, |status| {
1361 assert_eq!(status.enforcing, 1);
1364 });
1365 }
1366}