selinux/
security_server.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::access_vector_cache::{AccessVectorCache, CacheStats, Query};
6use crate::exceptions_config::ExceptionsConfig;
7use crate::permission_check::PermissionCheck;
8use crate::policy::metadata::HandleUnknown;
9use crate::policy::parser::PolicyData;
10
11use crate::policy::{
12    AccessDecision, AccessVector, AccessVectorComputer, ClassId, ClassPermissionId,
13    FsUseLabelAndType, FsUseType, Policy, XpermsAccessDecision, XpermsKind, parse_policy_by_value,
14};
15use crate::sid_table::SidTable;
16use crate::sync::RwLock;
17use crate::{
18    ClassPermission, FileSystemLabel, FileSystemLabelingScheme, FileSystemMountOptions,
19    FileSystemMountSids, FsNodeClass, InitialSid, KernelClass, KernelPermission, NullessByteStr,
20    ObjectClass, PolicyCap, SeLinuxStatus, SeLinuxStatusPublisher, SecurityId,
21};
22
23use anyhow::Context as _;
24use std::collections::HashMap;
25use std::ops::DerefMut;
26use std::sync::Arc;
27
28const ROOT_PATH: &'static str = "/";
29
30struct ActivePolicy {
31    /// Parsed policy structure.
32    parsed: Arc<Policy>,
33
34    /// The binary policy that was previously passed to `load_policy()`.
35    binary: PolicyData,
36
37    /// Allocates and maintains the mapping between `SecurityId`s (SIDs) and Security Contexts.
38    sid_table: SidTable,
39
40    /// Describes access checks that should be granted, with associated bug Ids.
41    exceptions: ExceptionsConfig,
42}
43
44#[derive(Default)]
45struct SeLinuxBooleans {
46    /// Active values for all of the booleans defined by the policy.
47    /// Entries are created at policy load for each policy-defined conditional.
48    active: HashMap<String, bool>,
49    /// Pending values for any booleans modified since the last commit.
50    pending: HashMap<String, bool>,
51}
52
53impl SeLinuxBooleans {
54    fn reset(&mut self, booleans: Vec<(String, bool)>) {
55        self.active = HashMap::from_iter(booleans);
56        self.pending.clear();
57    }
58    fn names(&self) -> Vec<String> {
59        self.active.keys().cloned().collect()
60    }
61    fn set_pending(&mut self, name: &str, value: bool) -> Result<(), ()> {
62        if !self.active.contains_key(name) {
63            return Err(());
64        }
65        self.pending.insert(name.into(), value);
66        Ok(())
67    }
68    fn get(&self, name: &str) -> Result<(bool, bool), ()> {
69        let active = self.active.get(name).ok_or(())?;
70        let pending = self.pending.get(name).unwrap_or(active);
71        Ok((*active, *pending))
72    }
73    fn commit_pending(&mut self) {
74        self.active.extend(self.pending.drain());
75    }
76}
77
78struct SecurityServerState {
79    /// Describes the currently active policy.
80    active_policy: Option<ActivePolicy>,
81
82    /// Holds active and pending states for each boolean defined by policy.
83    booleans: SeLinuxBooleans,
84
85    /// Write-only interface to the data stored in the selinuxfs status file.
86    status_publisher: Option<Box<dyn SeLinuxStatusPublisher>>,
87
88    /// True if hooks should enforce policy-based access decisions.
89    enforcing: bool,
90
91    /// Count of changes to the active policy.  Changes include both loads
92    /// of complete new policies, and modifications to a previously loaded
93    /// policy, e.g. by committing new values to conditional booleans in it.
94    policy_change_count: u32,
95}
96
97impl SecurityServerState {
98    fn deny_unknown(&self) -> bool {
99        self.active_policy
100            .as_ref()
101            .map_or(true, |p| p.parsed.handle_unknown() != HandleUnknown::Allow)
102    }
103    fn reject_unknown(&self) -> bool {
104        self.active_policy
105            .as_ref()
106            .map_or(false, |p| p.parsed.handle_unknown() == HandleUnknown::Reject)
107    }
108
109    fn expect_active_policy(&self) -> &ActivePolicy {
110        &self.active_policy.as_ref().expect("policy should be loaded")
111    }
112
113    fn expect_active_policy_mut(&mut self) -> &mut ActivePolicy {
114        self.active_policy.as_mut().expect("policy should be loaded")
115    }
116}
117
118pub(crate) struct SecurityServerBackend {
119    /// The mutable state of the security server.
120    state: RwLock<SecurityServerState>,
121}
122
123pub struct SecurityServer {
124    /// The access vector cache that is shared between threads subject to access control by this
125    /// security server.
126    access_vector_cache: AccessVectorCache,
127
128    /// A shared reference to the security server's state.
129    backend: Arc<SecurityServerBackend>,
130
131    /// Optional set of exceptions to apply to access checks, via `ExceptionsConfig`.
132    exceptions: Vec<String>,
133}
134
135impl SecurityServer {
136    /// Returns an instance with default configuration and no exceptions.
137    pub fn new_default() -> Arc<Self> {
138        Self::new(String::new(), Vec::new())
139    }
140
141    /// Returns an instance with the specified options and exceptions configured.
142    pub fn new(options: String, exceptions: Vec<String>) -> Arc<Self> {
143        // No options are currently supported.
144        assert_eq!(options, String::new());
145
146        let backend = Arc::new(SecurityServerBackend {
147            state: RwLock::new(SecurityServerState {
148                active_policy: None,
149                booleans: SeLinuxBooleans::default(),
150                status_publisher: None,
151                enforcing: false,
152                policy_change_count: 0,
153            }),
154        });
155
156        let access_vector_cache = AccessVectorCache::new(backend.clone());
157
158        Arc::new(Self { access_vector_cache, backend, exceptions })
159    }
160
161    /// Converts a shared pointer to [`SecurityServer`] to a [`PermissionCheck`] without consuming
162    /// the pointer.
163    pub fn as_permission_check<'a>(self: &'a Self) -> PermissionCheck<'a> {
164        PermissionCheck::new(self, &self.access_vector_cache)
165    }
166
167    /// Returns the security ID mapped to `security_context`, creating it if it does not exist.
168    ///
169    /// All objects with the same security context will have the same SID associated.
170    pub fn security_context_to_sid(
171        &self,
172        security_context: NullessByteStr<'_>,
173    ) -> Result<SecurityId, anyhow::Error> {
174        let mut locked_state = self.backend.state.write();
175        let active_policy = locked_state
176            .active_policy
177            .as_mut()
178            .ok_or_else(|| anyhow::anyhow!("no policy loaded"))?;
179        let context = active_policy
180            .parsed
181            .parse_security_context(security_context)
182            .map_err(anyhow::Error::from)?;
183        active_policy.sid_table.security_context_to_sid(&context).map_err(anyhow::Error::from)
184    }
185
186    /// Returns the Security Context string for the requested `sid`.
187    /// This is used only where Contexts need to be stringified to expose to userspace, as
188    /// is the case for e.g. the `/proc/*/attr/` filesystem and `security.selinux` extended
189    /// attribute values.
190    pub fn sid_to_security_context(&self, sid: SecurityId) -> Option<Vec<u8>> {
191        let locked_state = self.backend.state.read();
192        let active_policy = locked_state.active_policy.as_ref()?;
193        let context = active_policy.sid_table.try_sid_to_security_context(sid)?;
194        Some(active_policy.parsed.serialize_security_context(context))
195    }
196
197    /// Returns the Security Context for the requested `sid` with a terminating NUL.
198    pub fn sid_to_security_context_with_nul(&self, sid: SecurityId) -> Option<Vec<u8>> {
199        self.sid_to_security_context(sid).map(|mut context| {
200            context.push(0u8);
201            context
202        })
203    }
204
205    /// Applies the supplied policy to the security server.
206    pub fn load_policy(&self, binary_policy: Vec<u8>) -> Result<(), anyhow::Error> {
207        // Parse the supplied policy, and reject the load operation if it is
208        // malformed or invalid.
209        let unvalidated_policy = parse_policy_by_value(binary_policy)?;
210        let parsed = Arc::new(unvalidated_policy.validate()?);
211        let binary = parsed.binary().clone();
212
213        let exceptions = self.exceptions.iter().map(String::as_str).collect::<Vec<&str>>();
214        let exceptions = ExceptionsConfig::new(&parsed, &exceptions)?;
215
216        // Replace any existing policy and push update to `state.status_publisher`.
217        self.with_state_and_update_status(|state| {
218            let sid_table = if let Some(previous_active_policy) = &state.active_policy {
219                SidTable::new_from_previous(parsed.clone(), &previous_active_policy.sid_table)
220            } else {
221                SidTable::new(parsed.clone())
222            };
223
224            // TODO(b/324265752): Determine whether SELinux booleans need to be retained across
225            // policy (re)loads.
226            state.booleans.reset(
227                parsed
228                    .conditional_booleans()
229                    .iter()
230                    // TODO(b/324392507): Relax the UTF8 requirement on policy strings.
231                    .map(|(name, value)| (String::from_utf8((*name).to_vec()).unwrap(), *value))
232                    .collect(),
233            );
234
235            state.active_policy = Some(ActivePolicy { parsed, binary, sid_table, exceptions });
236            state.policy_change_count += 1;
237        });
238
239        // TODO: https://fxbug.dev/367585803 - move this cache-resetting into the
240        // closure passed to self.with_state_and_update_status.
241        self.access_vector_cache.reset();
242
243        Ok(())
244    }
245
246    /// Returns the active policy in binary form, or `None` if no policy has yet been loaded.
247    pub fn get_binary_policy(&self) -> Option<PolicyData> {
248        self.backend.state.read().active_policy.as_ref().map(|p| p.binary.clone())
249    }
250
251    /// Set to enforcing mode if `enforce` is true, permissive mode otherwise.
252    pub fn set_enforcing(&self, enforcing: bool) {
253        self.with_state_and_update_status(|state| state.enforcing = enforcing);
254    }
255
256    pub fn is_enforcing(&self) -> bool {
257        self.backend.state.read().enforcing
258    }
259
260    /// Returns true if the policy requires unknown class / permissions to be
261    /// denied. Defaults to true until a policy is loaded.
262    pub fn deny_unknown(&self) -> bool {
263        self.backend.state.read().deny_unknown()
264    }
265
266    /// Returns true if the policy requires unknown class / permissions to be
267    /// rejected. Defaults to false until a policy is loaded.
268    pub fn reject_unknown(&self) -> bool {
269        self.backend.state.read().reject_unknown()
270    }
271
272    /// Returns the list of names of boolean conditionals defined by the
273    /// loaded policy.
274    pub fn conditional_booleans(&self) -> Vec<String> {
275        self.backend.state.read().booleans.names()
276    }
277
278    /// Returns the active and pending values of a policy boolean, if it exists.
279    pub fn get_boolean(&self, name: &str) -> Result<(bool, bool), ()> {
280        self.backend.state.read().booleans.get(name)
281    }
282
283    /// Sets the pending value of a boolean, if it is defined in the policy.
284    pub fn set_pending_boolean(&self, name: &str, value: bool) -> Result<(), ()> {
285        self.backend.state.write().booleans.set_pending(name, value)
286    }
287
288    /// Commits all pending changes to conditional booleans.
289    pub fn commit_pending_booleans(&self) {
290        // TODO(b/324264149): Commit values into the stored policy itself.
291        self.with_state_and_update_status(|state| {
292            state.booleans.commit_pending();
293            state.policy_change_count += 1;
294        });
295    }
296
297    /// Returns whether a standard policy capability is enabled in the loaded policy.
298    pub fn is_policycap_enabled(&self, policy_cap: PolicyCap) -> bool {
299        let locked_state = self.backend.state.read();
300        let Some(policy) = &locked_state.active_policy else {
301            return false;
302        };
303        policy.parsed.has_policycap(policy_cap)
304    }
305
306    /// Returns a snapshot of the AVC usage statistics.
307    pub fn avc_cache_stats(&self) -> CacheStats {
308        self.access_vector_cache.cache_stats()
309    }
310
311    /// Returns the list of all class names.
312    pub fn class_names(&self) -> Result<Vec<Vec<u8>>, ()> {
313        let locked_state = self.backend.state.read();
314        let names = locked_state
315            .expect_active_policy()
316            .parsed
317            .classes()
318            .iter()
319            .map(|class| class.class_name.to_vec())
320            .collect();
321        Ok(names)
322    }
323
324    /// Returns the class identifier of a class, if it exists.
325    pub fn class_id_by_name(&self, name: &str) -> Result<ClassId, ()> {
326        let locked_state = self.backend.state.read();
327        Ok(locked_state
328            .expect_active_policy()
329            .parsed
330            .classes()
331            .iter()
332            .find(|class| class.class_name == name.as_bytes())
333            .ok_or(())?
334            .class_id)
335    }
336
337    /// Returns the set of permissions associated with a class. Each permission
338    /// is represented as a tuple of the permission ID (in the scope of its
339    /// associated class) and the permission name.
340    pub fn class_permissions_by_name(
341        &self,
342        name: &str,
343    ) -> Result<Vec<(ClassPermissionId, Vec<u8>)>, ()> {
344        let locked_state = self.backend.state.read();
345        locked_state.expect_active_policy().parsed.find_class_permissions_by_name(name)
346    }
347
348    /// Determines the appropriate [`FileSystemLabel`] for a mounted filesystem given this security
349    /// server's loaded policy, the name of the filesystem type ("ext4" or "tmpfs", for example),
350    /// and the security-relevant mount options passed for the mount operation.
351    pub fn resolve_fs_label(
352        &self,
353        fs_type: NullessByteStr<'_>,
354        mount_options: &FileSystemMountOptions,
355    ) -> FileSystemLabel {
356        let mut locked_state = self.backend.state.write();
357        let active_policy = locked_state.expect_active_policy_mut();
358
359        let mount_sids = FileSystemMountSids {
360            context: sid_from_mount_option(active_policy, &mount_options.context),
361            fs_context: sid_from_mount_option(active_policy, &mount_options.fs_context),
362            def_context: sid_from_mount_option(active_policy, &mount_options.def_context),
363            root_context: sid_from_mount_option(active_policy, &mount_options.root_context),
364        };
365        if let Some(mountpoint_sid) = mount_sids.context {
366            // `mount_options` has `context` set, so the file-system and the nodes it contains are
367            // labeled with that value, which is not modifiable. The `fs_context` option, if set,
368            // overrides the file-system label.
369            FileSystemLabel {
370                sid: mount_sids.fs_context.unwrap_or(mountpoint_sid),
371                scheme: FileSystemLabelingScheme::Mountpoint { sid: mountpoint_sid },
372                mount_sids,
373            }
374        } else if let Some(FsUseLabelAndType { context, use_type }) =
375            active_policy.parsed.fs_use_label_and_type(fs_type)
376        {
377            // There is an `fs_use` statement for this file-system type in the policy.
378            let fs_sid_from_policy =
379                active_policy.sid_table.security_context_to_sid(&context).unwrap();
380            let fs_sid = mount_sids.fs_context.unwrap_or(fs_sid_from_policy);
381            FileSystemLabel {
382                sid: fs_sid,
383                scheme: FileSystemLabelingScheme::FsUse {
384                    fs_use_type: use_type,
385                    default_sid: mount_sids.def_context.unwrap_or_else(|| InitialSid::File.into()),
386                },
387                mount_sids,
388            }
389        } else if let Some(context) =
390            active_policy.parsed.genfscon_label_for_fs_and_path(fs_type, ROOT_PATH.into(), None)
391        {
392            // There is a `genfscon` statement for this file-system type in the policy.
393            let genfscon_sid = active_policy.sid_table.security_context_to_sid(&context).unwrap();
394            let fs_sid = mount_sids.fs_context.unwrap_or(genfscon_sid);
395
396            // For relabeling to make sense with `genfscon` labeling they must ensure to persist the
397            // `FsNode` security state. That is implicitly the case for filesystems which persist all
398            // `FsNode`s in-memory (independent of the `DirEntry` cache), e.g. those whose contents are
399            // managed as a `SimpleDirectory` structure.
400            //
401            // TODO: https://fxbug.dev/362898792 - Replace this with a more graceful mechanism for
402            // deciding whether `genfscon` supports relabeling (as indicated by the "seclabel" tag
403            // reported by `mount`).
404            // Also consider storing the "genfs_seclabel_symlinks" setting in the resolved label.
405            let fs_type = fs_type.as_bytes();
406            let mut supports_seclabel = matches!(fs_type, b"sysfs" | b"tracefs" | b"pstore");
407            supports_seclabel |= matches!(fs_type, b"cgroup" | b"cgroup2")
408                && active_policy.parsed.has_policycap(PolicyCap::CgroupSeclabel);
409            supports_seclabel |= fs_type == b"functionfs"
410                && active_policy.parsed.has_policycap(PolicyCap::FunctionfsSeclabel);
411
412            FileSystemLabel {
413                sid: fs_sid,
414                scheme: FileSystemLabelingScheme::GenFsCon { supports_seclabel },
415                mount_sids,
416            }
417        } else {
418            // The name of the filesystem type was not recognized.
419            FileSystemLabel {
420                sid: mount_sids.fs_context.unwrap_or_else(|| InitialSid::Unlabeled.into()),
421                scheme: FileSystemLabelingScheme::FsUse {
422                    fs_use_type: FsUseType::Xattr,
423                    default_sid: mount_sids.def_context.unwrap_or_else(|| InitialSid::File.into()),
424                },
425                mount_sids,
426            }
427        }
428    }
429
430    /// If there is a genfscon statement for the given filesystem type, returns the
431    /// [`SecurityContext`] that should be used for a node in path `node_path`. When `node_path` is
432    /// the root path ("/") the label additionally corresponds to the `FileSystem` label.
433    pub fn genfscon_label_for_fs_and_path(
434        &self,
435        fs_type: NullessByteStr<'_>,
436        node_path: NullessByteStr<'_>,
437        class_id: Option<KernelClass>,
438    ) -> Option<SecurityId> {
439        let mut locked_state = self.backend.state.write();
440        let active_policy = locked_state.expect_active_policy_mut();
441        let security_context = active_policy.parsed.genfscon_label_for_fs_and_path(
442            fs_type,
443            node_path.into(),
444            class_id,
445        )?;
446        Some(active_policy.sid_table.security_context_to_sid(&security_context).unwrap())
447    }
448
449    /// Returns true if the `bounded_sid` is bounded by the `parent_sid`.
450    /// Bounds relationships are mostly enforced by policy tooling, so this only requires validating
451    /// that the policy entry for the `TypeId` of `bounded_sid` has the `TypeId` of `parent_sid`
452    /// specified in its `bounds`.
453    pub fn is_bounded_by(&self, bounded_sid: SecurityId, parent_sid: SecurityId) -> bool {
454        let locked_state = self.backend.state.read();
455        let active_policy = locked_state.expect_active_policy();
456        let bounded_type = active_policy.sid_table.sid_to_security_context(bounded_sid).type_();
457        let parent_type = active_policy.sid_table.sid_to_security_context(parent_sid).type_();
458        active_policy.parsed.is_bounded_by(bounded_type, parent_type)
459    }
460
461    /// Assign a [`SeLinuxStatusPublisher`] to be used for pushing updates to the security server's
462    /// policy status. This should be invoked exactly once when `selinuxfs` is initialized.
463    ///
464    /// # Panics
465    ///
466    /// This will panic on debug builds if it is invoked multiple times.
467    pub fn set_status_publisher(&self, status_holder: Box<dyn SeLinuxStatusPublisher>) {
468        self.with_state_and_update_status(|state| {
469            assert!(state.status_publisher.is_none());
470            state.status_publisher = Some(status_holder);
471        });
472    }
473
474    /// Runs the supplied function with locked `self`, and then updates the SELinux status file
475    /// associated with `self.state.status_publisher`, if any.
476    fn with_state_and_update_status(&self, f: impl FnOnce(&mut SecurityServerState)) {
477        let mut locked_state = self.backend.state.write();
478        f(locked_state.deref_mut());
479        let new_value = SeLinuxStatus {
480            is_enforcing: locked_state.enforcing,
481            change_count: locked_state.policy_change_count,
482            deny_unknown: locked_state.deny_unknown(),
483        };
484        if let Some(status_publisher) = &mut locked_state.status_publisher {
485            status_publisher.set_status(new_value);
486        }
487    }
488
489    /// Returns the security identifier (SID) with which to label a new object of `target_class`,
490    /// based on the specified source & target security SIDs.
491    /// For file-like classes the `compute_new_fs_node_sid*()` APIs should be used instead.
492    // TODO: Move this API to sit alongside the other `compute_*()` APIs.
493    pub fn compute_create_sid(
494        &self,
495        source_sid: SecurityId,
496        target_sid: SecurityId,
497        target_class: impl Into<ObjectClass>,
498    ) -> Result<SecurityId, anyhow::Error> {
499        self.backend.compute_create_sid(source_sid, target_sid, target_class)
500    }
501}
502
503impl SecurityServerBackend {
504    fn compute_create_sid(
505        &self,
506        source_sid: SecurityId,
507        target_sid: SecurityId,
508        target_class: impl Into<ObjectClass>,
509    ) -> Result<SecurityId, anyhow::Error> {
510        let mut locked_state = self.state.write();
511        let active_policy = locked_state.expect_active_policy_mut();
512
513        let source_context = active_policy.sid_table.sid_to_security_context(source_sid);
514        let target_context = active_policy.sid_table.sid_to_security_context(target_sid);
515
516        let security_context = active_policy.parsed.compute_create_context(
517            source_context,
518            target_context,
519            target_class.into(),
520        );
521
522        active_policy
523            .sid_table
524            .security_context_to_sid(&security_context)
525            .map_err(anyhow::Error::from)
526            .context("computing new security context from policy")
527    }
528}
529
530impl Query for SecurityServerBackend {
531    fn compute_access_decision(
532        &self,
533        source_sid: SecurityId,
534        target_sid: SecurityId,
535        target_class: ObjectClass,
536    ) -> AccessDecision {
537        let locked_state = self.state.read();
538
539        let active_policy = match &locked_state.active_policy {
540            Some(active_policy) => active_policy,
541            // All permissions are allowed when no policy is loaded, regardless of enforcing state.
542            None => return AccessDecision::allow(AccessVector::ALL),
543        };
544
545        let source_context = active_policy.sid_table.sid_to_security_context(source_sid);
546        let target_context = active_policy.sid_table.sid_to_security_context(target_sid);
547
548        let mut decision = active_policy.parsed.compute_access_decision(
549            &source_context,
550            &target_context,
551            target_class,
552        );
553
554        decision.todo_bug = active_policy.exceptions.lookup(
555            source_context.type_(),
556            target_context.type_(),
557            target_class,
558        );
559
560        decision
561    }
562
563    fn compute_new_fs_node_sid(
564        &self,
565        source_sid: SecurityId,
566        target_sid: SecurityId,
567        fs_node_class: FsNodeClass,
568    ) -> Result<SecurityId, anyhow::Error> {
569        self.compute_create_sid(source_sid, target_sid, fs_node_class)
570    }
571
572    fn compute_new_fs_node_sid_with_name(
573        &self,
574        source_sid: SecurityId,
575        target_sid: SecurityId,
576        fs_node_class: FsNodeClass,
577        fs_node_name: NullessByteStr<'_>,
578    ) -> Option<SecurityId> {
579        let mut locked_state = self.state.write();
580
581        // This interface will not be reached without a policy having been loaded.
582        let active_policy = locked_state.active_policy.as_mut().expect("Policy loaded");
583
584        let source_context = active_policy.sid_table.sid_to_security_context(source_sid);
585        let target_context = active_policy.sid_table.sid_to_security_context(target_sid);
586
587        let new_file_context = active_policy.parsed.compute_create_context_with_name(
588            source_context,
589            target_context,
590            fs_node_class,
591            fs_node_name,
592        )?;
593
594        active_policy.sid_table.security_context_to_sid(&new_file_context).ok()
595    }
596
597    fn compute_xperms_access_decision(
598        &self,
599        xperms_kind: XpermsKind,
600        source_sid: SecurityId,
601        target_sid: SecurityId,
602        target_class: ObjectClass,
603        xperms_prefix: u8,
604    ) -> XpermsAccessDecision {
605        let locked_state = self.state.read();
606
607        let active_policy = match &locked_state.active_policy {
608            Some(active_policy) => active_policy,
609            // All permissions are allowed when no policy is loaded, regardless of enforcing state.
610            None => return XpermsAccessDecision::ALLOW_ALL,
611        };
612
613        let source_context = active_policy.sid_table.sid_to_security_context(source_sid);
614        let target_context = active_policy.sid_table.sid_to_security_context(target_sid);
615
616        active_policy.parsed.compute_xperms_access_decision(
617            xperms_kind,
618            &source_context,
619            &target_context,
620            target_class,
621            xperms_prefix,
622        )
623    }
624}
625
626impl AccessVectorComputer for SecurityServer {
627    fn access_vector_from_permissions<
628        P: ClassPermission + Into<KernelPermission> + Clone + 'static,
629    >(
630        &self,
631        permissions: &[P],
632    ) -> Option<AccessVector> {
633        match &self.backend.state.read().active_policy {
634            Some(policy) => policy.parsed.access_vector_from_permissions(permissions),
635            None => Some(AccessVector::NONE),
636        }
637    }
638}
639
640/// Computes a [`SecurityId`] given a non-[`None`] value for one of the four
641/// "context" mount options (https://man7.org/linux/man-pages/man8/mount.8.html).
642fn sid_from_mount_option(
643    active_policy: &mut ActivePolicy,
644    mount_option: &Option<Vec<u8>>,
645) -> Option<SecurityId> {
646    if let Some(label) = mount_option.as_ref() {
647        Some(
648            if let Some(context) = active_policy.parsed.parse_security_context(label.into()).ok() {
649                active_policy.sid_table.security_context_to_sid(&context).unwrap()
650            } else {
651                // The mount option is present-but-not-valid: we use `Unlabeled`.
652                InitialSid::Unlabeled.into()
653            },
654        )
655    } else {
656        None
657    }
658}
659
660#[cfg(test)]
661mod tests {
662    use super::*;
663    use crate::permission_check::PermissionCheckResult;
664    use crate::{
665        CommonFsNodePermission, DirPermission, FileClass, FilePermission, ForClass, KernelClass,
666        ProcessPermission,
667    };
668    use std::num::NonZeroU64;
669
670    const TESTSUITE_BINARY_POLICY: &[u8] = include_bytes!("../testdata/policies/selinux_testsuite");
671    const TESTS_BINARY_POLICY: &[u8] =
672        include_bytes!("../testdata/micro_policies/security_server_tests_policy.pp");
673    const MINIMAL_BINARY_POLICY: &[u8] =
674        include_bytes!("../testdata/composite_policies/compiled/minimal_policy.pp");
675
676    fn security_server_with_tests_policy() -> Arc<SecurityServer> {
677        let policy_bytes = TESTS_BINARY_POLICY.to_vec();
678        let security_server = SecurityServer::new_default();
679        assert_eq!(
680            Ok(()),
681            security_server.load_policy(policy_bytes).map_err(|e| format!("{:?}", e))
682        );
683        security_server
684    }
685
686    #[test]
687    fn compute_access_vector_allows_all() {
688        let security_server = SecurityServer::new_default();
689        let sid1 = InitialSid::Kernel.into();
690        let sid2 = InitialSid::Unlabeled.into();
691        assert_eq!(
692            security_server
693                .backend
694                .compute_access_decision(sid1, sid2, KernelClass::Process.into())
695                .allow,
696            AccessVector::ALL
697        );
698    }
699
700    #[test]
701    fn loaded_policy_can_be_retrieved() {
702        let security_server = security_server_with_tests_policy();
703        assert_eq!(TESTS_BINARY_POLICY, security_server.get_binary_policy().unwrap().as_slice());
704    }
705
706    #[test]
707    fn loaded_policy_is_validated() {
708        let not_really_a_policy = "not a real policy".as_bytes().to_vec();
709        let security_server = SecurityServer::new_default();
710        assert!(security_server.load_policy(not_really_a_policy.clone()).is_err());
711    }
712
713    #[test]
714    fn enforcing_mode_is_reported() {
715        let security_server = SecurityServer::new_default();
716        assert!(!security_server.is_enforcing());
717
718        security_server.set_enforcing(true);
719        assert!(security_server.is_enforcing());
720    }
721
722    #[test]
723    fn without_policy_conditional_booleans_are_empty() {
724        let security_server = SecurityServer::new_default();
725        assert!(security_server.conditional_booleans().is_empty());
726    }
727
728    #[test]
729    fn conditional_booleans_can_be_queried() {
730        let policy_bytes = TESTSUITE_BINARY_POLICY.to_vec();
731        let security_server = SecurityServer::new_default();
732        assert_eq!(
733            Ok(()),
734            security_server.load_policy(policy_bytes).map_err(|e| format!("{:?}", e))
735        );
736
737        let booleans = security_server.conditional_booleans();
738        assert!(!booleans.is_empty());
739        let boolean = booleans[0].as_str();
740
741        assert!(security_server.get_boolean("this_is_not_a_valid_boolean_name").is_err());
742        assert!(security_server.get_boolean(boolean).is_ok());
743    }
744
745    #[test]
746    fn conditional_booleans_can_be_changed() {
747        let policy_bytes = TESTSUITE_BINARY_POLICY.to_vec();
748        let security_server = SecurityServer::new_default();
749        assert_eq!(
750            Ok(()),
751            security_server.load_policy(policy_bytes).map_err(|e| format!("{:?}", e))
752        );
753
754        let booleans = security_server.conditional_booleans();
755        assert!(!booleans.is_empty());
756        let boolean = booleans[0].as_str();
757
758        let (active, pending) = security_server.get_boolean(boolean).unwrap();
759        assert_eq!(active, pending, "Initially active and pending values should match");
760
761        security_server.set_pending_boolean(boolean, !active).unwrap();
762        let (active, pending) = security_server.get_boolean(boolean).unwrap();
763        assert!(active != pending, "Before commit pending should differ from active");
764
765        security_server.commit_pending_booleans();
766        let (final_active, final_pending) = security_server.get_boolean(boolean).unwrap();
767        assert_eq!(final_active, pending, "Pending value should be active after commit");
768        assert_eq!(final_active, final_pending, "Active and pending are the same after commit");
769    }
770
771    #[test]
772    fn parse_security_context_no_policy() {
773        let security_server = SecurityServer::new_default();
774        let error = security_server
775            .security_context_to_sid(b"unconfined_u:unconfined_r:unconfined_t:s0".into())
776            .expect_err("expected error");
777        let error_string = format!("{:?}", error);
778        assert!(error_string.contains("no policy"));
779    }
780
781    #[test]
782    fn compute_new_fs_node_sid_no_defaults() {
783        let security_server = SecurityServer::new_default();
784        let policy_bytes =
785            include_bytes!("../testdata/micro_policies/file_no_defaults_policy.pp").to_vec();
786        security_server.load_policy(policy_bytes).expect("binary policy loads");
787
788        let source_sid = security_server
789            .security_context_to_sid(b"user_u:unconfined_r:unconfined_t:s0-s1".into())
790            .expect("creating SID from security context should succeed");
791        let target_sid = security_server
792            .security_context_to_sid(b"file_u:object_r:file_t:s0".into())
793            .expect("creating SID from security context should succeed");
794
795        let computed_sid = security_server
796            .as_permission_check()
797            .compute_new_fs_node_sid(source_sid, target_sid, FileClass::File.into(), "".into())
798            .expect("new sid computed");
799        let computed_context = security_server
800            .sid_to_security_context(computed_sid)
801            .expect("computed sid associated with context");
802
803        // User and low security level should be copied from the source,
804        // and the role and type from the target.
805        assert_eq!(computed_context, b"user_u:object_r:file_t:s0");
806    }
807
808    #[test]
809    fn compute_new_fs_node_sid_source_defaults() {
810        let security_server = SecurityServer::new_default();
811        let policy_bytes =
812            include_bytes!("../testdata/micro_policies/file_source_defaults_policy.pp").to_vec();
813        security_server.load_policy(policy_bytes).expect("binary policy loads");
814
815        let source_sid = security_server
816            .security_context_to_sid(b"user_u:unconfined_r:unconfined_t:s0-s2:c0".into())
817            .expect("creating SID from security context should succeed");
818        let target_sid = security_server
819            .security_context_to_sid(b"file_u:object_r:file_t:s1-s3:c0".into())
820            .expect("creating SID from security context should succeed");
821
822        let computed_sid = security_server
823            .as_permission_check()
824            .compute_new_fs_node_sid(source_sid, target_sid, FileClass::File.into(), "".into())
825            .expect("new sid computed");
826        let computed_context = security_server
827            .sid_to_security_context(computed_sid)
828            .expect("computed sid associated with context");
829
830        // All fields should be copied from the source, but only the "low" part of the security
831        // range.
832        assert_eq!(computed_context, b"user_u:unconfined_r:unconfined_t:s0");
833    }
834
835    #[test]
836    fn compute_new_fs_node_sid_target_defaults() {
837        let security_server = SecurityServer::new_default();
838        let policy_bytes =
839            include_bytes!("../testdata/micro_policies/file_target_defaults_policy.pp").to_vec();
840        security_server.load_policy(policy_bytes).expect("binary policy loads");
841
842        let source_sid = security_server
843            .security_context_to_sid(b"user_u:unconfined_r:unconfined_t:s0-s2:c0".into())
844            .expect("creating SID from security context should succeed");
845        let target_sid = security_server
846            .security_context_to_sid(b"file_u:object_r:file_t:s1-s3:c0".into())
847            .expect("creating SID from security context should succeed");
848
849        let computed_sid = security_server
850            .as_permission_check()
851            .compute_new_fs_node_sid(source_sid, target_sid, FileClass::File.into(), "".into())
852            .expect("new sid computed");
853        let computed_context = security_server
854            .sid_to_security_context(computed_sid)
855            .expect("computed sid associated with context");
856
857        // User, role and type copied from target, with source's low security level.
858        assert_eq!(computed_context, b"file_u:object_r:file_t:s0");
859    }
860
861    #[test]
862    fn compute_new_fs_node_sid_range_source_low_default() {
863        let security_server = SecurityServer::new_default();
864        let policy_bytes =
865            include_bytes!("../testdata/micro_policies/file_range_source_low_policy.pp").to_vec();
866        security_server.load_policy(policy_bytes).expect("binary policy loads");
867
868        let source_sid = security_server
869            .security_context_to_sid(b"user_u:unconfined_r:unconfined_t:s0-s1:c0".into())
870            .expect("creating SID from security context should succeed");
871        let target_sid = security_server
872            .security_context_to_sid(b"file_u:object_r:file_t:s1".into())
873            .expect("creating SID from security context should succeed");
874
875        let computed_sid = security_server
876            .as_permission_check()
877            .compute_new_fs_node_sid(source_sid, target_sid, FileClass::File.into(), "".into())
878            .expect("new sid computed");
879        let computed_context = security_server
880            .sid_to_security_context(computed_sid)
881            .expect("computed sid associated with context");
882
883        // User and low security level copied from source, role and type as default.
884        assert_eq!(computed_context, b"user_u:object_r:file_t:s0");
885    }
886
887    #[test]
888    fn compute_new_fs_node_sid_range_source_low_high_default() {
889        let security_server = SecurityServer::new_default();
890        let policy_bytes =
891            include_bytes!("../testdata/micro_policies/file_range_source_low_high_policy.pp")
892                .to_vec();
893        security_server.load_policy(policy_bytes).expect("binary policy loads");
894
895        let source_sid = security_server
896            .security_context_to_sid(b"user_u:unconfined_r:unconfined_t:s0-s1:c0".into())
897            .expect("creating SID from security context should succeed");
898        let target_sid = security_server
899            .security_context_to_sid(b"file_u:object_r:file_t:s1".into())
900            .expect("creating SID from security context should succeed");
901
902        let computed_sid = security_server
903            .as_permission_check()
904            .compute_new_fs_node_sid(source_sid, target_sid, FileClass::File.into(), "".into())
905            .expect("new sid computed");
906        let computed_context = security_server
907            .sid_to_security_context(computed_sid)
908            .expect("computed sid associated with context");
909
910        // User and full security range copied from source, role and type as default.
911        assert_eq!(computed_context, b"user_u:object_r:file_t:s0-s1:c0");
912    }
913
914    #[test]
915    fn compute_new_fs_node_sid_range_source_high_default() {
916        let security_server = SecurityServer::new_default();
917        let policy_bytes =
918            include_bytes!("../testdata/micro_policies/file_range_source_high_policy.pp").to_vec();
919        security_server.load_policy(policy_bytes).expect("binary policy loads");
920
921        let source_sid = security_server
922            .security_context_to_sid(b"user_u:unconfined_r:unconfined_t:s0-s1:c0".into())
923            .expect("creating SID from security context should succeed");
924        let target_sid = security_server
925            .security_context_to_sid(b"file_u:object_r:file_t:s0".into())
926            .expect("creating SID from security context should succeed");
927
928        let computed_sid = security_server
929            .as_permission_check()
930            .compute_new_fs_node_sid(source_sid, target_sid, FileClass::File.into(), "".into())
931            .expect("new sid computed");
932        let computed_context = security_server
933            .sid_to_security_context(computed_sid)
934            .expect("computed sid associated with context");
935
936        // User and high security level copied from source, role and type as default.
937        assert_eq!(computed_context, b"user_u:object_r:file_t:s1:c0");
938    }
939
940    #[test]
941    fn compute_new_fs_node_sid_range_target_low_default() {
942        let security_server = SecurityServer::new_default();
943        let policy_bytes =
944            include_bytes!("../testdata/micro_policies/file_range_target_low_policy.pp").to_vec();
945        security_server.load_policy(policy_bytes).expect("binary policy loads");
946
947        let source_sid = security_server
948            .security_context_to_sid(b"user_u:unconfined_r:unconfined_t:s1".into())
949            .expect("creating SID from security context should succeed");
950        let target_sid = security_server
951            .security_context_to_sid(b"file_u:object_r:file_t:s0-s1:c0".into())
952            .expect("creating SID from security context should succeed");
953
954        let computed_sid = security_server
955            .as_permission_check()
956            .compute_new_fs_node_sid(source_sid, target_sid, FileClass::File.into(), "".into())
957            .expect("new sid computed");
958        let computed_context = security_server
959            .sid_to_security_context(computed_sid)
960            .expect("computed sid associated with context");
961
962        // User copied from source, low security level from target, role and type as default.
963        assert_eq!(computed_context, b"user_u:object_r:file_t:s0");
964    }
965
966    #[test]
967    fn compute_new_fs_node_sid_range_target_low_high_default() {
968        let security_server = SecurityServer::new_default();
969        let policy_bytes =
970            include_bytes!("../testdata/micro_policies/file_range_target_low_high_policy.pp")
971                .to_vec();
972        security_server.load_policy(policy_bytes).expect("binary policy loads");
973
974        let source_sid = security_server
975            .security_context_to_sid(b"user_u:unconfined_r:unconfined_t:s1".into())
976            .expect("creating SID from security context should succeed");
977        let target_sid = security_server
978            .security_context_to_sid(b"file_u:object_r:file_t:s0-s1:c0".into())
979            .expect("creating SID from security context should succeed");
980
981        let computed_sid = security_server
982            .as_permission_check()
983            .compute_new_fs_node_sid(source_sid, target_sid, FileClass::File.into(), "".into())
984            .expect("new sid computed");
985        let computed_context = security_server
986            .sid_to_security_context(computed_sid)
987            .expect("computed sid associated with context");
988
989        // User copied from source, full security range from target, role and type as default.
990        assert_eq!(computed_context, b"user_u:object_r:file_t:s0-s1:c0");
991    }
992
993    #[test]
994    fn compute_new_fs_node_sid_range_target_high_default() {
995        let security_server = SecurityServer::new_default();
996        let policy_bytes =
997            include_bytes!("../testdata/micro_policies/file_range_target_high_policy.pp").to_vec();
998        security_server.load_policy(policy_bytes).expect("binary policy loads");
999
1000        let source_sid = security_server
1001            .security_context_to_sid(b"user_u:unconfined_r:unconfined_t:s0".into())
1002            .expect("creating SID from security context should succeed");
1003        let target_sid = security_server
1004            .security_context_to_sid(b"file_u:object_r:file_t:s0-s1:c0".into())
1005            .expect("creating SID from security context should succeed");
1006
1007        let computed_sid = security_server
1008            .as_permission_check()
1009            .compute_new_fs_node_sid(source_sid, target_sid, FileClass::File.into(), "".into())
1010            .expect("new sid computed");
1011        let computed_context = security_server
1012            .sid_to_security_context(computed_sid)
1013            .expect("computed sid associated with context");
1014
1015        // User copied from source, high security level from target, role and type as default.
1016        assert_eq!(computed_context, b"user_u:object_r:file_t:s1:c0");
1017    }
1018
1019    #[test]
1020    fn compute_new_fs_node_sid_with_name() {
1021        let security_server = SecurityServer::new_default();
1022        let policy_bytes =
1023            include_bytes!("../testdata/composite_policies/compiled/type_transition_policy.pp")
1024                .to_vec();
1025        security_server.load_policy(policy_bytes).expect("binary policy loads");
1026
1027        let source_sid = security_server
1028            .security_context_to_sid(b"source_u:source_r:source_t:s0".into())
1029            .expect("creating SID from security context should succeed");
1030        let target_sid = security_server
1031            .security_context_to_sid(b"target_u:object_r:target_t:s0".into())
1032            .expect("creating SID from security context should succeed");
1033
1034        const SPECIAL_FILE_NAME: &[u8] = b"special_file";
1035        let computed_sid = security_server
1036            .as_permission_check()
1037            .compute_new_fs_node_sid(
1038                source_sid,
1039                target_sid,
1040                FileClass::File.into(),
1041                SPECIAL_FILE_NAME.into(),
1042            )
1043            .expect("new sid computed");
1044        let computed_context = security_server
1045            .sid_to_security_context(computed_sid)
1046            .expect("computed sid associated with context");
1047
1048        // New domain should be derived from the filename-specific rule.
1049        assert_eq!(computed_context, b"source_u:object_r:special_transition_t:s0");
1050
1051        let computed_sid = security_server
1052            .as_permission_check()
1053            .compute_new_fs_node_sid(
1054                source_sid,
1055                target_sid,
1056                FileClass::Character.into(),
1057                SPECIAL_FILE_NAME.into(),
1058            )
1059            .expect("new sid computed");
1060        let computed_context = security_server
1061            .sid_to_security_context(computed_sid)
1062            .expect("computed sid associated with context");
1063
1064        // New domain should be copied from the target, because the class does not match either the
1065        // filename-specific nor generic type transition rules.
1066        assert_eq!(computed_context, b"source_u:object_r:target_t:s0");
1067
1068        const OTHER_FILE_NAME: &[u8] = b"other_file";
1069        let computed_sid = security_server
1070            .as_permission_check()
1071            .compute_new_fs_node_sid(
1072                source_sid,
1073                target_sid,
1074                FileClass::File.into(),
1075                OTHER_FILE_NAME.into(),
1076            )
1077            .expect("new sid computed");
1078        let computed_context = security_server
1079            .sid_to_security_context(computed_sid)
1080            .expect("computed sid associated with context");
1081
1082        // New domain should be derived from the non-filename-specific rule, because the filename
1083        // does not match.
1084        assert_eq!(computed_context, b"source_u:object_r:transition_t:s0");
1085    }
1086
1087    #[test]
1088    fn permissions_are_fresh_after_different_policy_load() {
1089        let minimal_bytes = MINIMAL_BINARY_POLICY.to_vec();
1090        let allow_fork_bytes =
1091            include_bytes!("../testdata/composite_policies/compiled/allow_fork.pp").to_vec();
1092        let context = b"source_u:object_r:source_t:s0:c0";
1093
1094        let security_server = SecurityServer::new_default();
1095        security_server.set_enforcing(true);
1096
1097        let permission_check = security_server.as_permission_check();
1098
1099        // Load the minimal policy and get a SID for the context.
1100        assert_eq!(
1101            Ok(()),
1102            security_server.load_policy(minimal_bytes).map_err(|e| format!("{:?}", e))
1103        );
1104        let sid = security_server.security_context_to_sid(context.into()).unwrap();
1105
1106        // The minimal policy does not grant fork allowance.
1107        assert!(!permission_check.has_permission(sid, sid, ProcessPermission::Fork).permit);
1108
1109        // Load a policy that does grant fork allowance.
1110        assert_eq!(
1111            Ok(()),
1112            security_server.load_policy(allow_fork_bytes).map_err(|e| format!("{:?}", e))
1113        );
1114
1115        // The now-loaded "allow_fork" policy allows the context represented by `sid` to fork.
1116        assert!(permission_check.has_permission(sid, sid, ProcessPermission::Fork).permit);
1117    }
1118
1119    #[test]
1120    fn unknown_sids_are_effectively_unlabeled() {
1121        let with_unlabeled_access_domain_policy_bytes = include_bytes!(
1122            "../testdata/composite_policies/compiled/with_unlabeled_access_domain_policy.pp"
1123        )
1124        .to_vec();
1125        let with_additional_domain_policy_bytes = include_bytes!(
1126            "../testdata/composite_policies/compiled/with_additional_domain_policy.pp"
1127        )
1128        .to_vec();
1129        let allowed_type_context = b"source_u:object_r:allowed_t:s0:c0";
1130        let additional_type_context = b"source_u:object_r:additional_t:s0:c0";
1131
1132        let security_server = SecurityServer::new_default();
1133        security_server.set_enforcing(true);
1134
1135        // Load a policy, get a SID for a context that is valid for that policy, and verify
1136        // that a context that is not valid for that policy is not issued a SID.
1137        assert_eq!(
1138            Ok(()),
1139            security_server
1140                .load_policy(with_unlabeled_access_domain_policy_bytes.clone())
1141                .map_err(|e| format!("{:?}", e))
1142        );
1143        let allowed_type_sid =
1144            security_server.security_context_to_sid(allowed_type_context.into()).unwrap();
1145        assert!(security_server.security_context_to_sid(additional_type_context.into()).is_err());
1146
1147        // Load the policy that makes the second context valid, and verify that it is valid, and
1148        // verify that the first context remains valid (and unchanged).
1149        assert_eq!(
1150            Ok(()),
1151            security_server
1152                .load_policy(with_additional_domain_policy_bytes.clone())
1153                .map_err(|e| format!("{:?}", e))
1154        );
1155        let additional_type_sid =
1156            security_server.security_context_to_sid(additional_type_context.into()).unwrap();
1157        assert_eq!(
1158            allowed_type_sid,
1159            security_server.security_context_to_sid(allowed_type_context.into()).unwrap()
1160        );
1161
1162        let permission_check = security_server.as_permission_check();
1163
1164        // "allowed_t" is allowed the process getsched capability to "unlabeled_t" - but since
1165        // the currently-loaded policy defines "additional_t", the SID for "additional_t" does
1166        // not get treated as effectively unlabeled, and these permission checks are denied.
1167        assert!(
1168            !permission_check
1169                .has_permission(additional_type_sid, allowed_type_sid, ProcessPermission::GetSched)
1170                .permit
1171        );
1172        assert!(
1173            !permission_check
1174                .has_permission(additional_type_sid, allowed_type_sid, ProcessPermission::SetSched)
1175                .permit
1176        );
1177        assert!(
1178            !permission_check
1179                .has_permission(allowed_type_sid, additional_type_sid, ProcessPermission::GetSched)
1180                .permit
1181        );
1182        assert!(
1183            !permission_check
1184                .has_permission(allowed_type_sid, additional_type_sid, ProcessPermission::SetSched)
1185                .permit
1186        );
1187
1188        // We now flip back to the policy that does not recognize "additional_t"...
1189        assert_eq!(
1190            Ok(()),
1191            security_server
1192                .load_policy(with_unlabeled_access_domain_policy_bytes)
1193                .map_err(|e| format!("{:?}", e))
1194        );
1195
1196        // The now-loaded policy allows "allowed_t" the process getsched capability
1197        // to "unlabeled_t" and since the now-loaded policy does not recognize "additional_t",
1198        // "allowed_t" is now allowed the process getsched capability to "additional_t".
1199        assert!(
1200            permission_check
1201                .has_permission(allowed_type_sid, additional_type_sid, ProcessPermission::GetSched)
1202                .permit
1203        );
1204        assert!(
1205            !permission_check
1206                .has_permission(allowed_type_sid, additional_type_sid, ProcessPermission::SetSched)
1207                .permit
1208        );
1209
1210        // ... and the now-loaded policy also allows "unlabeled_t" the process
1211        // setsched capability to "allowed_t" and since the now-loaded policy does not recognize
1212        // "additional_t", "unlabeled_t" is now allowed the process setsched capability to
1213        // "allowed_t".
1214        assert!(
1215            !permission_check
1216                .has_permission(additional_type_sid, allowed_type_sid, ProcessPermission::GetSched)
1217                .permit
1218        );
1219        assert!(
1220            permission_check
1221                .has_permission(additional_type_sid, allowed_type_sid, ProcessPermission::SetSched)
1222                .permit
1223        );
1224
1225        // We also verify that we do not get a serialization for unrecognized "additional_t"...
1226        assert!(security_server.sid_to_security_context(additional_type_sid).is_none());
1227
1228        // ... but if we flip forward to the policy that recognizes "additional_t", then we see
1229        // the serialization succeed and return the original context string.
1230        assert_eq!(
1231            Ok(()),
1232            security_server
1233                .load_policy(with_additional_domain_policy_bytes)
1234                .map_err(|e| format!("{:?}", e))
1235        );
1236        assert_eq!(
1237            additional_type_context.to_vec(),
1238            security_server.sid_to_security_context(additional_type_sid).unwrap()
1239        );
1240    }
1241
1242    #[test]
1243    fn permission_check_permissive() {
1244        let security_server = security_server_with_tests_policy();
1245        security_server.set_enforcing(false);
1246        assert!(!security_server.is_enforcing());
1247
1248        let sid =
1249            security_server.security_context_to_sid("user0:object_r:type0:s0".into()).unwrap();
1250        let permission_check = security_server.as_permission_check();
1251
1252        // Test policy grants "type0" the process-fork permission to itself.
1253        // Since the permission is granted by policy, the check will not be audit logged.
1254        assert_eq!(
1255            permission_check.has_permission(sid, sid, ProcessPermission::Fork),
1256            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1257        );
1258
1259        // Test policy does not grant "type0" the process-getrlimit permission to itself, but
1260        // the security server is configured to be permissive. Because the permission was not
1261        // granted by the policy, the check will be audit logged.
1262        assert_eq!(
1263            permission_check.has_permission(sid, sid, ProcessPermission::GetRlimit),
1264            PermissionCheckResult { permit: true, audit: true, todo_bug: None }
1265        );
1266
1267        // Test policy is built with "deny unknown" behaviour, and has no "blk_file" class defined.
1268        // This permission should be treated like a defined permission that is not allowed to the
1269        // source, and both allowed and audited here.
1270        assert_eq!(
1271            permission_check.has_permission(
1272                sid,
1273                sid,
1274                CommonFsNodePermission::GetAttr.for_class(FileClass::Block)
1275            ),
1276            PermissionCheckResult { permit: true, audit: true, todo_bug: None }
1277        );
1278    }
1279
1280    #[test]
1281    fn permission_check_enforcing() {
1282        let security_server = security_server_with_tests_policy();
1283        security_server.set_enforcing(true);
1284        assert!(security_server.is_enforcing());
1285
1286        let sid =
1287            security_server.security_context_to_sid("user0:object_r:type0:s0".into()).unwrap();
1288        let permission_check = security_server.as_permission_check();
1289
1290        // Test policy grants "type0" the process-fork permission to itself.
1291        assert_eq!(
1292            permission_check.has_permission(sid, sid, ProcessPermission::Fork),
1293            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1294        );
1295
1296        // Test policy does not grant "type0" the process-getrlimit permission to itself.
1297        // Permission denials are audit logged in enforcing mode.
1298        assert_eq!(
1299            permission_check.has_permission(sid, sid, ProcessPermission::GetRlimit),
1300            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1301        );
1302
1303        // Test policy is built with "deny unknown" behaviour, and has no "blk_file" class defined.
1304        // This permission should therefore be denied, and the denial audited.
1305        assert_eq!(
1306            permission_check.has_permission(
1307                sid,
1308                sid,
1309                CommonFsNodePermission::GetAttr.for_class(FileClass::Block)
1310            ),
1311            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1312        );
1313    }
1314
1315    #[test]
1316    fn permissive_domain() {
1317        let security_server = security_server_with_tests_policy();
1318        security_server.set_enforcing(true);
1319        assert!(security_server.is_enforcing());
1320
1321        let permissive_sid = security_server
1322            .security_context_to_sid("user0:object_r:permissive_t:s0".into())
1323            .unwrap();
1324        let non_permissive_sid = security_server
1325            .security_context_to_sid("user0:object_r:non_permissive_t:s0".into())
1326            .unwrap();
1327
1328        let permission_check = security_server.as_permission_check();
1329
1330        // Test policy grants process-getsched permission to both of the test domains.
1331        assert_eq!(
1332            permission_check.has_permission(
1333                permissive_sid,
1334                permissive_sid,
1335                ProcessPermission::GetSched
1336            ),
1337            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1338        );
1339        assert_eq!(
1340            permission_check.has_permission(
1341                non_permissive_sid,
1342                non_permissive_sid,
1343                ProcessPermission::GetSched
1344            ),
1345            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1346        );
1347
1348        // Test policy does not grant process-getsched permission to the test domains on one another.
1349        // The permissive domain will be granted the permission, since it is marked permissive.
1350        assert_eq!(
1351            permission_check.has_permission(
1352                permissive_sid,
1353                non_permissive_sid,
1354                ProcessPermission::GetSched
1355            ),
1356            PermissionCheckResult { permit: true, audit: true, todo_bug: None }
1357        );
1358        assert_eq!(
1359            permission_check.has_permission(
1360                non_permissive_sid,
1361                permissive_sid,
1362                ProcessPermission::GetSched
1363            ),
1364            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1365        );
1366
1367        // Test policy has "deny unknown" behaviour and does not define the "blk_file" class, so
1368        // access to a permission on it will depend on whether the source is permissive.
1369        // The target domain is irrelevant, since the class/permission do not exist, so the non-
1370        // permissive SID is used for both checks.
1371        assert_eq!(
1372            permission_check.has_permission(
1373                permissive_sid,
1374                non_permissive_sid,
1375                CommonFsNodePermission::GetAttr.for_class(FileClass::Block)
1376            ),
1377            PermissionCheckResult { permit: true, audit: true, todo_bug: None }
1378        );
1379        assert_eq!(
1380            permission_check.has_permission(
1381                non_permissive_sid,
1382                non_permissive_sid,
1383                CommonFsNodePermission::GetAttr.for_class(FileClass::Block)
1384            ),
1385            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1386        );
1387    }
1388
1389    #[test]
1390    fn auditallow_and_dontaudit() {
1391        let security_server = security_server_with_tests_policy();
1392        security_server.set_enforcing(true);
1393        assert!(security_server.is_enforcing());
1394
1395        let audit_sid = security_server
1396            .security_context_to_sid("user0:object_r:test_audit_t:s0".into())
1397            .unwrap();
1398
1399        let permission_check = security_server.as_permission_check();
1400
1401        // Test policy grants the domain self-fork permission, and marks it audit-allow.
1402        assert_eq!(
1403            permission_check.has_permission(audit_sid, audit_sid, ProcessPermission::Fork),
1404            PermissionCheckResult { permit: true, audit: true, todo_bug: None }
1405        );
1406
1407        // Self-setsched permission is granted, and marked dont-audit, which takes no effect.
1408        assert_eq!(
1409            permission_check.has_permission(audit_sid, audit_sid, ProcessPermission::SetSched),
1410            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1411        );
1412
1413        // Self-getsched permission is denied, but marked dont-audit.
1414        assert_eq!(
1415            permission_check.has_permission(audit_sid, audit_sid, ProcessPermission::GetSched),
1416            PermissionCheckResult { permit: false, audit: false, todo_bug: None }
1417        );
1418
1419        // Self-getpgid permission is denied, with neither audit-allow nor dont-audit.
1420        assert_eq!(
1421            permission_check.has_permission(audit_sid, audit_sid, ProcessPermission::GetPgid),
1422            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1423        );
1424    }
1425
1426    #[test]
1427    fn access_checks_with_exceptions_config() {
1428        const EXCEPTIONS_CONFIG: &[&str] = &[
1429            // These statement should all be resolved.
1430            "todo_deny b/001 test_exception_source_t test_exception_target_t file",
1431            "todo_deny b/002 test_exception_other_t test_exception_target_t chr_file",
1432            "todo_deny b/003 test_exception_source_t test_exception_other_t anon_inode",
1433            "todo_deny b/004 test_exception_permissive_t test_exception_target_t file",
1434            "todo_permissive b/005 test_exception_todo_permissive_t",
1435            // These statements should not be resolved.
1436            "todo_deny b/101 test_undefined_source_t test_exception_target_t file",
1437            "todo_deny b/102 test_exception_source_t test_undefined_target_t file",
1438            "todo_permissive b/103 test_undefined_source_t",
1439        ];
1440        let exceptions_config = EXCEPTIONS_CONFIG.iter().map(|x| String::from(*x)).collect();
1441        let security_server = SecurityServer::new(String::new(), exceptions_config);
1442        security_server.set_enforcing(true);
1443
1444        const EXCEPTIONS_POLICY: &[u8] =
1445            include_bytes!("../testdata/composite_policies/compiled/exceptions_config_policy.pp");
1446        assert!(security_server.load_policy(EXCEPTIONS_POLICY.into()).is_ok());
1447
1448        let source_sid = security_server
1449            .security_context_to_sid("test_exception_u:object_r:test_exception_source_t:s0".into())
1450            .unwrap();
1451        let target_sid = security_server
1452            .security_context_to_sid("test_exception_u:object_r:test_exception_target_t:s0".into())
1453            .unwrap();
1454        let other_sid = security_server
1455            .security_context_to_sid("test_exception_u:object_r:test_exception_other_t:s0".into())
1456            .unwrap();
1457        let permissive_sid = security_server
1458            .security_context_to_sid(
1459                "test_exception_u:object_r:test_exception_permissive_t:s0".into(),
1460            )
1461            .unwrap();
1462        let unmatched_sid = security_server
1463            .security_context_to_sid(
1464                "test_exception_u:object_r:test_exception_unmatched_t:s0".into(),
1465            )
1466            .unwrap();
1467        let todo_permissive_sid = security_server
1468            .security_context_to_sid(
1469                "test_exception_u:object_r:test_exception_todo_permissive_t:s0".into(),
1470            )
1471            .unwrap();
1472
1473        let permission_check = security_server.as_permission_check();
1474
1475        // Source SID has no "process" permissions to target SID, and no exceptions.
1476        assert_eq!(
1477            permission_check.has_permission(source_sid, target_sid, ProcessPermission::GetPgid),
1478            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1479        );
1480
1481        // Source SID has no "file:entrypoint" permission to target SID, but there is an exception defined.
1482        assert_eq!(
1483            permission_check.has_permission(source_sid, target_sid, FilePermission::Entrypoint),
1484            PermissionCheckResult {
1485                permit: true,
1486                audit: true,
1487                todo_bug: Some(NonZeroU64::new(1).unwrap())
1488            }
1489        );
1490
1491        // Source SID has "file:execute_no_trans" permission to target SID.
1492        assert_eq!(
1493            permission_check.has_permission(source_sid, target_sid, FilePermission::ExecuteNoTrans),
1494            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1495        );
1496
1497        // Other SID has no "file:entrypoint" permissions to target SID, and the exception does not match "file" class.
1498        assert_eq!(
1499            permission_check.has_permission(other_sid, target_sid, FilePermission::Entrypoint),
1500            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1501        );
1502
1503        // Other SID has no "chr_file" permissions to target SID, but there is an exception defined.
1504        assert_eq!(
1505            permission_check.has_permission(
1506                other_sid,
1507                target_sid,
1508                CommonFsNodePermission::Read.for_class(FileClass::Character)
1509            ),
1510            PermissionCheckResult {
1511                permit: true,
1512                audit: true,
1513                todo_bug: Some(NonZeroU64::new(2).unwrap())
1514            }
1515        );
1516
1517        // Source SID has no "file:entrypoint" permissions to unmatched SID, and no exception is defined.
1518        assert_eq!(
1519            permission_check.has_permission(source_sid, unmatched_sid, FilePermission::Entrypoint),
1520            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1521        );
1522
1523        // Unmatched SID has no "file:entrypoint" permissions to target SID, and no exception is defined.
1524        assert_eq!(
1525            permission_check.has_permission(unmatched_sid, target_sid, FilePermission::Entrypoint),
1526            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1527        );
1528
1529        // Todo-deny exceptions are processed before the permissive bit is handled.
1530        assert_eq!(
1531            permission_check.has_permission(permissive_sid, target_sid, FilePermission::Entrypoint),
1532            PermissionCheckResult {
1533                permit: true,
1534                audit: true,
1535                todo_bug: Some(NonZeroU64::new(4).unwrap())
1536            }
1537        );
1538
1539        // Todo-permissive SID is not granted any permissions, so all permissions should be granted,
1540        // to all target domains and classes, and all grants should be associated with the bug.
1541        assert_eq!(
1542            permission_check.has_permission(
1543                todo_permissive_sid,
1544                target_sid,
1545                FilePermission::Entrypoint
1546            ),
1547            PermissionCheckResult {
1548                permit: true,
1549                audit: true,
1550                todo_bug: Some(NonZeroU64::new(5).unwrap())
1551            }
1552        );
1553        assert_eq!(
1554            permission_check.has_permission(
1555                todo_permissive_sid,
1556                todo_permissive_sid,
1557                FilePermission::Entrypoint
1558            ),
1559            PermissionCheckResult {
1560                permit: true,
1561                audit: true,
1562                todo_bug: Some(NonZeroU64::new(5).unwrap())
1563            }
1564        );
1565        assert_eq!(
1566            permission_check.has_permission(
1567                todo_permissive_sid,
1568                target_sid,
1569                FilePermission::Entrypoint
1570            ),
1571            PermissionCheckResult {
1572                permit: true,
1573                audit: true,
1574                todo_bug: Some(NonZeroU64::new(5).unwrap())
1575            }
1576        );
1577    }
1578
1579    #[test]
1580    fn handle_unknown() {
1581        let security_server = security_server_with_tests_policy();
1582
1583        let sid = security_server
1584            .security_context_to_sid("user0:object_r:type0:s0".into())
1585            .expect("Resolve Context to SID");
1586
1587        // Load a policy that is missing some elements, and marked handle_unknown=reject.
1588        // The policy should be rejected, since not all classes/permissions are defined.
1589        // Rejecting policy is not controlled by permissive vs enforcing.
1590        const REJECT_POLICY: &[u8] = include_bytes!(
1591            "../testdata/composite_policies/compiled/handle_unknown_policy-reject.pp"
1592        );
1593        assert!(security_server.load_policy(REJECT_POLICY.to_vec()).is_err());
1594
1595        security_server.set_enforcing(true);
1596
1597        // Load a policy that is missing some elements, and marked handle_unknown=deny.
1598        const DENY_POLICY: &[u8] =
1599            include_bytes!("../testdata/composite_policies/compiled/handle_unknown_policy-deny.pp");
1600        assert!(security_server.load_policy(DENY_POLICY.to_vec()).is_ok());
1601        let permission_check = security_server.as_permission_check();
1602
1603        // Check against undefined classes or permissions should deny access and audit.
1604        assert_eq!(
1605            permission_check.has_permission(sid, sid, ProcessPermission::GetSched),
1606            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1607        );
1608        assert_eq!(
1609            permission_check.has_permission(sid, sid, DirPermission::AddName),
1610            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1611        );
1612
1613        // Check that permissions that are defined are unaffected by handle-unknown.
1614        assert_eq!(
1615            permission_check.has_permission(sid, sid, DirPermission::Search),
1616            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1617        );
1618        assert_eq!(
1619            permission_check.has_permission(sid, sid, DirPermission::Reparent),
1620            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1621        );
1622
1623        // Load a policy that is missing some elements, and marked handle_unknown=allow.
1624        const ALLOW_POLICY: &[u8] = include_bytes!(
1625            "../testdata/composite_policies/compiled/handle_unknown_policy-allow.pp"
1626        );
1627        assert!(security_server.load_policy(ALLOW_POLICY.to_vec()).is_ok());
1628        let permission_check = security_server.as_permission_check();
1629
1630        // Check against undefined classes or permissions should grant access without audit.
1631        assert_eq!(
1632            permission_check.has_permission(sid, sid, ProcessPermission::GetSched),
1633            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1634        );
1635        assert_eq!(
1636            permission_check.has_permission(sid, sid, DirPermission::AddName),
1637            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1638        );
1639
1640        // Check that permissions that are defined are unaffected by handle-unknown.
1641        assert_eq!(
1642            permission_check.has_permission(sid, sid, DirPermission::Search),
1643            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1644        );
1645        assert_eq!(
1646            permission_check.has_permission(sid, sid, DirPermission::Reparent),
1647            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1648        );
1649    }
1650}