| // Copyright 2024 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| pub mod local_cache; |
| pub mod permission_check; |
| pub mod policy; |
| pub mod security_server; |
| |
| pub use access_vector_cache::{AccessQueryArgs, DEFAULT_SHARED_SIZE, QueryCacheCapacity}; |
| pub use concurrent_access_cache::{AccessCacheStorage, ConcurrentAccessCache}; |
| pub use security_server::SecurityServer; |
| |
| mod access_vector_cache; |
| mod cache_stats; |
| mod concurrent_access_cache; |
| mod concurrent_cache; |
| mod exceptions_config; |
| mod kernel_permissions; |
| mod sid_table; |
| mod sync; |
| |
| /// Allow callers to use the kernel class & permission definitions. |
| pub use kernel_permissions::*; |
| |
| /// Numeric class Ids are provided to the userspace AVC surfaces (e.g. "create", "access", etc). |
| pub use policy::ClassId; |
| |
| pub use starnix_uapi::selinux::{InitialSid, ReferenceInitialSid, SecurityId, TaskAttrs}; |
| |
| use policy::arrays::FsUseType; |
| use strum::VariantArray as _; |
| use strum_macros::VariantArray; |
| |
| /// Identifies a specific class by its policy-defined Id, or as a kernel object class enum Id. |
| #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] |
| pub enum ObjectClass { |
| /// Refers to a well-known SELinux kernel object class (e.g. "process", "file", "capability"). |
| Kernel(KernelClass), |
| /// Refers to a policy-defined class by its policy-defined numeric Id. This is most commonly |
| /// used when handling queries from userspace, which refer to classes by-Id. |
| ClassId(ClassId), |
| } |
| |
| impl From<ClassId> for ObjectClass { |
| fn from(id: ClassId) -> Self { |
| Self::ClassId(id) |
| } |
| } |
| |
| impl<T: Into<KernelClass>> From<T> for ObjectClass { |
| fn from(class: T) -> Self { |
| Self::Kernel(class.into()) |
| } |
| } |
| |
| /// A borrowed byte slice that contains no `NUL` characters by truncating the input slice at the |
| /// first `NUL` (if any) upon construction. |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub struct NullessByteStr<'a>(&'a [u8]); |
| |
| impl<'a> NullessByteStr<'a> { |
| /// Returns a non-null-terminated representation of the security context string. |
| pub fn as_bytes(&self) -> &[u8] { |
| &self.0 |
| } |
| } |
| |
| impl<'a, S: AsRef<[u8]> + ?Sized> From<&'a S> for NullessByteStr<'a> { |
| /// Any `AsRef<[u8]>` can be processed into a [`NullessByteStr`]. The [`NullessByteStr`] will |
| /// retain everything up to (but not including) a null character, or else the complete byte |
| /// string. |
| fn from(s: &'a S) -> Self { |
| let value = s.as_ref(); |
| match value.iter().position(|c| *c == 0) { |
| Some(end) => Self(&value[..end]), |
| None => Self(value), |
| } |
| } |
| } |
| |
| #[derive(Clone, Debug, PartialEq)] |
| pub struct FileSystemMountSids { |
| pub context: Option<SecurityId>, |
| pub fs_context: Option<SecurityId>, |
| pub def_context: Option<SecurityId>, |
| pub root_context: Option<SecurityId>, |
| } |
| |
| #[derive(Clone, Debug, PartialEq)] |
| pub struct FileSystemLabel { |
| pub sid: SecurityId, |
| pub scheme: FileSystemLabelingScheme, |
| // Sids obtained by parsing the mount options of the FileSystem. |
| pub mount_sids: FileSystemMountSids, |
| } |
| |
| #[derive(Clone, Debug, PartialEq)] |
| pub enum FileSystemLabelingScheme { |
| /// This filesystem was mounted with "context=". |
| Mountpoint { sid: SecurityId }, |
| /// This filesystem has an "fs_use_xattr", "fs_use_task", or "fs_use_trans" entry in the |
| /// policy. If the `fs_use_type` is "fs_use_xattr" then the `default_sid` specifies the SID |
| /// with which to label `FsNode`s of files that do not have the "security.selinux" xattr. |
| FsUse { fs_use_type: FsUseType, default_sid: SecurityId }, |
| /// This filesystem has one or more "genfscon" statements associated with it in the policy. |
| /// If `supports_seclabel` is true then nodes in the filesystem may be dynamically relabeled. |
| GenFsCon { supports_seclabel: bool }, |
| } |
| |
| /// SELinux security context-related filesystem mount options. These options are documented in the |
| /// `context=context, fscontext=context, defcontext=context, and rootcontext=context` section of |
| /// the `mount(8)` manpage. |
| #[derive(Clone, Debug, Default, PartialEq)] |
| pub struct FileSystemMountOptions { |
| /// Specifies the effective security context to use for all nodes in the filesystem, and the |
| /// filesystem itself. If the filesystem already contains security attributes then these are |
| /// ignored. May not be combined with any of the other options. |
| pub context: Option<Vec<u8>>, |
| /// Specifies an effective security context to use for un-labeled nodes in the filesystem, |
| /// rather than falling-back to the policy-defined "file" context. |
| pub def_context: Option<Vec<u8>>, |
| /// The value of the `fscontext=[security-context]` mount option. This option is used to |
| /// label the filesystem (superblock) itself. |
| pub fs_context: Option<Vec<u8>>, |
| /// The value of the `rootcontext=[security-context]` mount option. This option is used to |
| /// (re)label the inode located at the filesystem mountpoint. |
| pub root_context: Option<Vec<u8>>, |
| } |
| |
| /// Status information parameter for the [`SeLinuxStatusPublisher`] interface. |
| pub struct SeLinuxStatus { |
| /// SELinux-wide enforcing vs. permissive mode bit. |
| pub is_enforcing: bool, |
| /// Number of times the policy has been changed since SELinux started. |
| pub change_count: u32, |
| /// Bit indicating whether operations unknown SELinux abstractions will be denied. |
| pub deny_unknown: bool, |
| } |
| |
| /// Interface for security server to interact with selinuxfs status file. |
| pub trait SeLinuxStatusPublisher: Send + Sync { |
| /// Sets the value part of the associated selinuxfs status file. |
| fn set_status(&mut self, policy_status: SeLinuxStatus); |
| } |
| |
| /// Reference policy capability Ids. |
| #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, VariantArray)] |
| pub enum PolicyCap { |
| NetworkPeerControls = 0, |
| OpenPerms = 1, |
| ExtendedSocketClass = 2, |
| AlwaysCheckNetwork = 3, |
| CgroupSeclabel = 4, |
| NnpNosuidTransition = 5, |
| GenfsSeclabelSymlinks = 6, |
| IoctlSkipCloexec = 7, |
| UserspaceInitialContext = 8, |
| NetlinkXperm = 9, |
| NetifWildcard = 10, |
| GenfsSeclabelWildcard = 11, |
| FunctionfsSeclabel = 12, |
| MemfdClass = 13, |
| } |
| |
| impl PolicyCap { |
| pub fn name(&self) -> &str { |
| match self { |
| Self::NetworkPeerControls => "network_peer_controls", |
| Self::OpenPerms => "open_perms", |
| Self::ExtendedSocketClass => "extended_socket_class", |
| Self::AlwaysCheckNetwork => "always_check_network", |
| Self::CgroupSeclabel => "cgroup_seclabel", |
| Self::NnpNosuidTransition => "nnp_nosuid_transition", |
| Self::GenfsSeclabelSymlinks => "genfs_seclabel_symlinks", |
| Self::IoctlSkipCloexec => "ioctl_skip_cloexec", |
| Self::UserspaceInitialContext => "userspace_initial_context", |
| Self::NetlinkXperm => "netlink_xperm", |
| Self::NetifWildcard => "netif_wildcard", |
| Self::GenfsSeclabelWildcard => "genfs_seclabel_wildcard", |
| Self::FunctionfsSeclabel => "functionfs_seclabel", |
| Self::MemfdClass => "memfd_class", |
| } |
| } |
| |
| pub fn by_name(name: &str) -> Option<Self> { |
| Self::VARIANTS.iter().find(|x| x.name() == name).copied() |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use std::num::NonZeroU32; |
| |
| #[test] |
| fn object_class_permissions() { |
| let test_class_id = ClassId::new(NonZeroU32::new(20).unwrap()); |
| assert_eq!(ObjectClass::ClassId(test_class_id), test_class_id.into()); |
| for variant in ProcessPermission::PERMISSIONS { |
| assert_eq!(KernelClass::Process, variant.class()); |
| assert_eq!("process", variant.class().name()); |
| assert_eq!(ObjectClass::Kernel(KernelClass::Process), variant.class().into()); |
| } |
| } |
| |
| #[test] |
| fn policy_capabilities() { |
| for capability in PolicyCap::VARIANTS { |
| assert_eq!(Some(*capability), PolicyCap::by_name(capability.name())); |
| } |
| } |
| |
| #[test] |
| fn nulless_byte_str_equivalence() { |
| let unterminated: NullessByteStr<'_> = b"u:object_r:test_valid_t:s0".into(); |
| let nul_terminated: NullessByteStr<'_> = b"u:object_r:test_valid_t:s0\0".into(); |
| let nul_containing: NullessByteStr<'_> = |
| b"u:object_r:test_valid_t:s0\0IGNORE THIS\0!\0".into(); |
| |
| for context in [nul_terminated, nul_containing] { |
| assert_eq!(unterminated, context); |
| assert_eq!(unterminated.as_bytes(), context.as_bytes()); |
| } |
| } |
| } |