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