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