selinux/
security_server.rs

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