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_mut_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_mut_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_mut_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    /// Returns the [`SecurityId`] with which to label an [`FsNode`] in a filesystem of `fs_type`,
428    /// at the specified filesystem-relative `node_path`.  Callers are responsible for ensuring that
429    /// this API is never called prior to a policy first being loaded, or for a filesystem that is
430    /// not configured to be `genfscon`-labeled.
431    pub fn genfscon_label_for_fs_and_path(
432        &self,
433        fs_type: NullessByteStr<'_>,
434        node_path: NullessByteStr<'_>,
435        class_id: Option<KernelClass>,
436    ) -> Result<SecurityId, anyhow::Error> {
437        self.backend.compute_sid(|active_policy| {
438            active_policy
439                .parsed
440                .genfscon_label_for_fs_and_path(fs_type, node_path.into(), class_id)
441                .ok_or_else(|| {
442                    anyhow::anyhow!("Genfscon label requested for non-genfscon labeled filesystem")
443                })
444        })
445    }
446
447    /// Returns true if the `bounded_sid` is bounded by the `parent_sid`.
448    /// Bounds relationships are mostly enforced by policy tooling, so this only requires validating
449    /// that the policy entry for the `TypeId` of `bounded_sid` has the `TypeId` of `parent_sid`
450    /// specified in its `bounds`.
451    pub fn is_bounded_by(&self, bounded_sid: SecurityId, parent_sid: SecurityId) -> bool {
452        let locked_state = self.backend.state.read();
453        let active_policy = locked_state.expect_active_policy();
454        let bounded_type = active_policy.sid_table.sid_to_security_context(bounded_sid).type_();
455        let parent_type = active_policy.sid_table.sid_to_security_context(parent_sid).type_();
456        active_policy.parsed.is_bounded_by(bounded_type, parent_type)
457    }
458
459    /// Assign a [`SeLinuxStatusPublisher`] to be used for pushing updates to the security server's
460    /// policy status. This should be invoked exactly once when `selinuxfs` is initialized.
461    ///
462    /// # Panics
463    ///
464    /// This will panic on debug builds if it is invoked multiple times.
465    pub fn set_status_publisher(&self, status_holder: Box<dyn SeLinuxStatusPublisher>) {
466        self.with_mut_state_and_update_status(|state| {
467            assert!(state.status_publisher.is_none());
468            state.status_publisher = Some(status_holder);
469        });
470    }
471
472    /// Locks the security server state for modification and calls the supplied function to update
473    /// it.  Once the update is complete, the configured `SeLinuxStatusPublisher` (if any) is called
474    /// to update the userspace-facing "status" file to reflect the new state.
475    fn with_mut_state_and_update_status(&self, f: impl FnOnce(&mut SecurityServerState)) {
476        let mut locked_state = self.backend.state.write();
477        f(locked_state.deref_mut());
478        let new_value = SeLinuxStatus {
479            is_enforcing: locked_state.enforcing,
480            change_count: locked_state.policy_change_count,
481            deny_unknown: locked_state.deny_unknown(),
482        };
483        if let Some(status_publisher) = &mut locked_state.status_publisher {
484            status_publisher.set_status(new_value);
485        }
486    }
487
488    /// Returns the security identifier (SID) with which to label a new object of `target_class`,
489    /// based on the specified source & target security SIDs.
490    /// For file-like classes the `compute_new_fs_node_sid*()` APIs should be used instead.
491    // TODO: Move this API to sit alongside the other `compute_*()` APIs.
492    pub fn compute_create_sid(
493        &self,
494        source_sid: SecurityId,
495        target_sid: SecurityId,
496        target_class: ObjectClass,
497    ) -> Result<SecurityId, anyhow::Error> {
498        self.backend.compute_create_sid(source_sid, target_sid, target_class)
499    }
500}
501
502impl SecurityServerBackend {
503    fn compute_create_sid(
504        &self,
505        source_sid: SecurityId,
506        target_sid: SecurityId,
507        target_class: ObjectClass,
508    ) -> Result<SecurityId, anyhow::Error> {
509        self.compute_sid(|active_policy| {
510            let source_context = active_policy.sid_table.sid_to_security_context(source_sid);
511            let target_context = active_policy.sid_table.sid_to_security_context(target_sid);
512
513            Ok(active_policy.parsed.compute_create_context(
514                source_context,
515                target_context,
516                target_class,
517            ))
518        })
519        .context("computing new security context from policy")
520    }
521
522    /// Helper for call-sites that need to compute a `SecurityContext` and assign a SID to it.
523    fn compute_sid(
524        &self,
525        compute_context: impl Fn(&ActivePolicy) -> Result<SecurityContext, anyhow::Error>,
526    ) -> Result<SecurityId, anyhow::Error> {
527        // Initially assume that the computed context will most likely already have a SID assigned,
528        // so that the operation can be completed without any modification of the SID table.
529        let readable_state = self.state.read();
530        let policy_change_count = readable_state.policy_change_count;
531        let policy_state = readable_state
532            .active_policy
533            .as_ref()
534            .ok_or_else(|| anyhow::anyhow!("no policy loaded"))?;
535        let context = compute_context(policy_state)?;
536        if let Some(sid) = policy_state.sid_table.security_context_to_existing_sid(&context) {
537            return Ok(sid);
538        }
539        std::mem::drop(readable_state);
540
541        // Since the computed context was not found in the table, re-try the operation with the
542        // policy state write-locked to allow for the SID table to be updated. In the rare case of
543        // a new policy having been loaded in-between the read- and write-locked stages, the
544        // `context` is re-computed using the new policy state.
545        let mut writable_state = self.state.write();
546        let needs_recompute = policy_change_count != writable_state.policy_change_count;
547        let policy_state = writable_state.active_policy.as_mut().unwrap();
548        let context = if needs_recompute { compute_context(policy_state)? } else { context };
549        policy_state.sid_table.security_context_to_sid(&context).map_err(anyhow::Error::from)
550    }
551}
552
553impl Query for SecurityServerBackend {
554    fn compute_access_decision(
555        &self,
556        source_sid: SecurityId,
557        target_sid: SecurityId,
558        target_class: ObjectClass,
559    ) -> AccessDecision {
560        let locked_state = self.state.read();
561
562        let active_policy = match &locked_state.active_policy {
563            Some(active_policy) => active_policy,
564            // All permissions are allowed when no policy is loaded, regardless of enforcing state.
565            None => return AccessDecision::allow(AccessVector::ALL),
566        };
567
568        let source_context = active_policy.sid_table.sid_to_security_context(source_sid);
569        let target_context = active_policy.sid_table.sid_to_security_context(target_sid);
570
571        let mut decision = active_policy.parsed.compute_access_decision(
572            &source_context,
573            &target_context,
574            target_class,
575        );
576
577        decision.todo_bug = active_policy.exceptions.lookup(
578            source_context.type_(),
579            target_context.type_(),
580            target_class,
581        );
582
583        decision
584    }
585
586    fn compute_create_sid(
587        &self,
588        source_sid: SecurityId,
589        target_sid: SecurityId,
590        target_class: ObjectClass,
591    ) -> Result<SecurityId, anyhow::Error> {
592        self.compute_create_sid(source_sid, target_sid, target_class)
593    }
594
595    fn compute_new_fs_node_sid_with_name(
596        &self,
597        source_sid: SecurityId,
598        target_sid: SecurityId,
599        fs_node_class: FsNodeClass,
600        fs_node_name: NullessByteStr<'_>,
601    ) -> Option<SecurityId> {
602        let mut locked_state = self.state.write();
603
604        // This interface will not be reached without a policy having been loaded.
605        let active_policy = locked_state.active_policy.as_mut().expect("Policy loaded");
606
607        let source_context = active_policy.sid_table.sid_to_security_context(source_sid);
608        let target_context = active_policy.sid_table.sid_to_security_context(target_sid);
609
610        let new_file_context = active_policy.parsed.compute_create_context_with_name(
611            source_context,
612            target_context,
613            fs_node_class,
614            fs_node_name,
615        )?;
616
617        active_policy.sid_table.security_context_to_sid(&new_file_context).ok()
618    }
619
620    fn compute_xperms_access_decision(
621        &self,
622        xperms_kind: XpermsKind,
623        source_sid: SecurityId,
624        target_sid: SecurityId,
625        target_class: ObjectClass,
626        xperms_prefix: u8,
627    ) -> XpermsAccessDecision {
628        let locked_state = self.state.read();
629
630        let active_policy = match &locked_state.active_policy {
631            Some(active_policy) => active_policy,
632            // All permissions are allowed when no policy is loaded, regardless of enforcing state.
633            None => return XpermsAccessDecision::ALLOW_ALL,
634        };
635
636        let source_context = active_policy.sid_table.sid_to_security_context(source_sid);
637        let target_context = active_policy.sid_table.sid_to_security_context(target_sid);
638
639        active_policy.parsed.compute_xperms_access_decision(
640            xperms_kind,
641            &source_context,
642            &target_context,
643            target_class,
644            xperms_prefix,
645        )
646    }
647}
648
649impl AccessVectorComputer for SecurityServer {
650    fn kernel_permissions_to_access_vector<
651        P: ClassPermission + Into<KernelPermission> + Clone + 'static,
652    >(
653        &self,
654        permissions: &[P],
655    ) -> Option<AccessVector> {
656        match &self.backend.state.read().active_policy {
657            Some(policy) => policy.parsed.kernel_permissions_to_access_vector(permissions),
658            None => Some(AccessVector::NONE),
659        }
660    }
661}
662
663/// Computes a [`SecurityId`] given a non-[`None`] value for one of the four
664/// "context" mount options (https://man7.org/linux/man-pages/man8/mount.8.html).
665fn sid_from_mount_option(
666    active_policy: &mut ActivePolicy,
667    mount_option: &Option<Vec<u8>>,
668) -> Option<SecurityId> {
669    if let Some(label) = mount_option.as_ref() {
670        Some(
671            if let Some(context) = active_policy.parsed.parse_security_context(label.into()).ok() {
672                active_policy.sid_table.security_context_to_sid(&context).unwrap()
673            } else {
674                // The mount option is present-but-not-valid: we use `Unlabeled`.
675                InitialSid::Unlabeled.into()
676            },
677        )
678    } else {
679        None
680    }
681}
682
683#[cfg(test)]
684mod tests {
685    use super::*;
686    use crate::permission_check::PermissionCheckResult;
687    use crate::{
688        CommonFsNodePermission, DirPermission, FileClass, FilePermission, ForClass, KernelClass,
689        ProcessPermission,
690    };
691    use std::num::NonZeroU64;
692
693    const TESTSUITE_BINARY_POLICY: &[u8] = include_bytes!("../testdata/policies/selinux_testsuite");
694    const TESTS_BINARY_POLICY: &[u8] =
695        include_bytes!("../testdata/micro_policies/security_server_tests_policy.pp");
696    const MINIMAL_BINARY_POLICY: &[u8] =
697        include_bytes!("../testdata/composite_policies/compiled/minimal_policy.pp");
698
699    fn security_server_with_tests_policy() -> Arc<SecurityServer> {
700        let policy_bytes = TESTS_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        security_server
707    }
708
709    #[test]
710    fn compute_access_vector_allows_all() {
711        let security_server = SecurityServer::new_default();
712        let sid1 = InitialSid::Kernel.into();
713        let sid2 = InitialSid::Unlabeled.into();
714        assert_eq!(
715            security_server
716                .backend
717                .compute_access_decision(sid1, sid2, KernelClass::Process.into())
718                .allow,
719            AccessVector::ALL
720        );
721    }
722
723    #[test]
724    fn loaded_policy_can_be_retrieved() {
725        let security_server = security_server_with_tests_policy();
726        assert_eq!(TESTS_BINARY_POLICY, security_server.get_binary_policy().unwrap().as_slice());
727    }
728
729    #[test]
730    fn loaded_policy_is_validated() {
731        let not_really_a_policy = "not a real policy".as_bytes().to_vec();
732        let security_server = SecurityServer::new_default();
733        assert!(security_server.load_policy(not_really_a_policy.clone()).is_err());
734    }
735
736    #[test]
737    fn enforcing_mode_is_reported() {
738        let security_server = SecurityServer::new_default();
739        assert!(!security_server.is_enforcing());
740
741        security_server.set_enforcing(true);
742        assert!(security_server.is_enforcing());
743    }
744
745    #[test]
746    fn without_policy_conditional_booleans_are_empty() {
747        let security_server = SecurityServer::new_default();
748        assert!(security_server.conditional_booleans().is_empty());
749    }
750
751    #[test]
752    fn conditional_booleans_can_be_queried() {
753        let policy_bytes = TESTSUITE_BINARY_POLICY.to_vec();
754        let security_server = SecurityServer::new_default();
755        assert_eq!(
756            Ok(()),
757            security_server.load_policy(policy_bytes).map_err(|e| format!("{:?}", e))
758        );
759
760        let booleans = security_server.conditional_booleans();
761        assert!(!booleans.is_empty());
762        let boolean = booleans[0].as_str();
763
764        assert!(security_server.get_boolean("this_is_not_a_valid_boolean_name").is_err());
765        assert!(security_server.get_boolean(boolean).is_ok());
766    }
767
768    #[test]
769    fn conditional_booleans_can_be_changed() {
770        let policy_bytes = TESTSUITE_BINARY_POLICY.to_vec();
771        let security_server = SecurityServer::new_default();
772        assert_eq!(
773            Ok(()),
774            security_server.load_policy(policy_bytes).map_err(|e| format!("{:?}", e))
775        );
776
777        let booleans = security_server.conditional_booleans();
778        assert!(!booleans.is_empty());
779        let boolean = booleans[0].as_str();
780
781        let (active, pending) = security_server.get_boolean(boolean).unwrap();
782        assert_eq!(active, pending, "Initially active and pending values should match");
783
784        security_server.set_pending_boolean(boolean, !active).unwrap();
785        let (active, pending) = security_server.get_boolean(boolean).unwrap();
786        assert!(active != pending, "Before commit pending should differ from active");
787
788        security_server.commit_pending_booleans();
789        let (final_active, final_pending) = security_server.get_boolean(boolean).unwrap();
790        assert_eq!(final_active, pending, "Pending value should be active after commit");
791        assert_eq!(final_active, final_pending, "Active and pending are the same after commit");
792    }
793
794    #[test]
795    fn parse_security_context_no_policy() {
796        let security_server = SecurityServer::new_default();
797        let error = security_server
798            .security_context_to_sid(b"unconfined_u:unconfined_r:unconfined_t:s0".into())
799            .expect_err("expected error");
800        let error_string = format!("{:?}", error);
801        assert!(error_string.contains("no policy"));
802    }
803
804    #[test]
805    fn compute_new_fs_node_sid_no_defaults() {
806        let security_server = SecurityServer::new_default();
807        let policy_bytes =
808            include_bytes!("../testdata/micro_policies/file_no_defaults_policy.pp").to_vec();
809        security_server.load_policy(policy_bytes).expect("binary policy loads");
810
811        let source_sid = security_server
812            .security_context_to_sid(b"user_u:unconfined_r:unconfined_t:s0-s1".into())
813            .expect("creating SID from security context should succeed");
814        let target_sid = security_server
815            .security_context_to_sid(b"file_u:object_r:file_t:s0".into())
816            .expect("creating SID from security context should succeed");
817
818        let computed_sid = security_server
819            .as_permission_check()
820            .compute_new_fs_node_sid(source_sid, target_sid, FileClass::File.into(), "".into())
821            .expect("new sid computed");
822        let computed_context = security_server
823            .sid_to_security_context(computed_sid)
824            .expect("computed sid associated with context");
825
826        // User and low security level should be copied from the source,
827        // and the role and type from the target.
828        assert_eq!(computed_context, b"user_u:object_r:file_t:s0");
829    }
830
831    #[test]
832    fn compute_new_fs_node_sid_source_defaults() {
833        let security_server = SecurityServer::new_default();
834        let policy_bytes =
835            include_bytes!("../testdata/micro_policies/file_source_defaults_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-s2: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-s3:c0".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        // All fields should be copied from the source, but only the "low" part of the security
854        // range.
855        assert_eq!(computed_context, b"user_u:unconfined_r:unconfined_t:s0");
856    }
857
858    #[test]
859    fn compute_new_fs_node_sid_target_defaults() {
860        let security_server = SecurityServer::new_default();
861        let policy_bytes =
862            include_bytes!("../testdata/micro_policies/file_target_defaults_policy.pp").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-s2: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-s3:c0".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, role and type copied from target, with source's low security level.
881        assert_eq!(computed_context, b"file_u:object_r:file_t:s0");
882    }
883
884    #[test]
885    fn compute_new_fs_node_sid_range_source_low_default() {
886        let security_server = SecurityServer::new_default();
887        let policy_bytes =
888            include_bytes!("../testdata/micro_policies/file_range_source_low_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:s1".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 low security level copied from source, role and type as default.
907        assert_eq!(computed_context, b"user_u:object_r:file_t:s0");
908    }
909
910    #[test]
911    fn compute_new_fs_node_sid_range_source_low_high_default() {
912        let security_server = SecurityServer::new_default();
913        let policy_bytes =
914            include_bytes!("../testdata/micro_policies/file_range_source_low_high_policy.pp")
915                .to_vec();
916        security_server.load_policy(policy_bytes).expect("binary policy loads");
917
918        let source_sid = security_server
919            .security_context_to_sid(b"user_u:unconfined_r:unconfined_t:s0-s1:c0".into())
920            .expect("creating SID from security context should succeed");
921        let target_sid = security_server
922            .security_context_to_sid(b"file_u:object_r:file_t:s1".into())
923            .expect("creating SID from security context should succeed");
924
925        let computed_sid = security_server
926            .as_permission_check()
927            .compute_new_fs_node_sid(source_sid, target_sid, FileClass::File.into(), "".into())
928            .expect("new sid computed");
929        let computed_context = security_server
930            .sid_to_security_context(computed_sid)
931            .expect("computed sid associated with context");
932
933        // User and full security range copied from source, role and type as default.
934        assert_eq!(computed_context, b"user_u:object_r:file_t:s0-s1:c0");
935    }
936
937    #[test]
938    fn compute_new_fs_node_sid_range_source_high_default() {
939        let security_server = SecurityServer::new_default();
940        let policy_bytes =
941            include_bytes!("../testdata/micro_policies/file_range_source_high_policy.pp").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:s0-s1:c0".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".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 and high security level copied from source, role and type as default.
960        assert_eq!(computed_context, b"user_u:object_r:file_t:s1:c0");
961    }
962
963    #[test]
964    fn compute_new_fs_node_sid_range_target_low_default() {
965        let security_server = SecurityServer::new_default();
966        let policy_bytes =
967            include_bytes!("../testdata/micro_policies/file_range_target_low_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:s1".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, low security level from target, role and type as default.
986        assert_eq!(computed_context, b"user_u:object_r:file_t:s0");
987    }
988
989    #[test]
990    fn compute_new_fs_node_sid_range_target_low_high_default() {
991        let security_server = SecurityServer::new_default();
992        let policy_bytes =
993            include_bytes!("../testdata/micro_policies/file_range_target_low_high_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"user_u:unconfined_r:unconfined_t:s1".into())
999            .expect("creating SID from security context should succeed");
1000        let target_sid = security_server
1001            .security_context_to_sid(b"file_u:object_r:file_t:s0-s1:c0".into())
1002            .expect("creating SID from security context should succeed");
1003
1004        let computed_sid = security_server
1005            .as_permission_check()
1006            .compute_new_fs_node_sid(source_sid, target_sid, FileClass::File.into(), "".into())
1007            .expect("new sid computed");
1008        let computed_context = security_server
1009            .sid_to_security_context(computed_sid)
1010            .expect("computed sid associated with context");
1011
1012        // User copied from source, full security range from target, role and type as default.
1013        assert_eq!(computed_context, b"user_u:object_r:file_t:s0-s1:c0");
1014    }
1015
1016    #[test]
1017    fn compute_new_fs_node_sid_range_target_high_default() {
1018        let security_server = SecurityServer::new_default();
1019        let policy_bytes =
1020            include_bytes!("../testdata/micro_policies/file_range_target_high_policy.pp").to_vec();
1021        security_server.load_policy(policy_bytes).expect("binary policy loads");
1022
1023        let source_sid = security_server
1024            .security_context_to_sid(b"user_u:unconfined_r:unconfined_t:s0".into())
1025            .expect("creating SID from security context should succeed");
1026        let target_sid = security_server
1027            .security_context_to_sid(b"file_u:object_r:file_t:s0-s1:c0".into())
1028            .expect("creating SID from security context should succeed");
1029
1030        let computed_sid = security_server
1031            .as_permission_check()
1032            .compute_new_fs_node_sid(source_sid, target_sid, FileClass::File.into(), "".into())
1033            .expect("new sid computed");
1034        let computed_context = security_server
1035            .sid_to_security_context(computed_sid)
1036            .expect("computed sid associated with context");
1037
1038        // User copied from source, high security level from target, role and type as default.
1039        assert_eq!(computed_context, b"user_u:object_r:file_t:s1:c0");
1040    }
1041
1042    #[test]
1043    fn compute_new_fs_node_sid_with_name() {
1044        let security_server = SecurityServer::new_default();
1045        let policy_bytes =
1046            include_bytes!("../testdata/composite_policies/compiled/type_transition_policy.pp")
1047                .to_vec();
1048        security_server.load_policy(policy_bytes).expect("binary policy loads");
1049
1050        let source_sid = security_server
1051            .security_context_to_sid(b"source_u:source_r:source_t:s0".into())
1052            .expect("creating SID from security context should succeed");
1053        let target_sid = security_server
1054            .security_context_to_sid(b"target_u:object_r:target_t:s0".into())
1055            .expect("creating SID from security context should succeed");
1056
1057        const SPECIAL_FILE_NAME: &[u8] = b"special_file";
1058        let computed_sid = security_server
1059            .as_permission_check()
1060            .compute_new_fs_node_sid(
1061                source_sid,
1062                target_sid,
1063                FileClass::File.into(),
1064                SPECIAL_FILE_NAME.into(),
1065            )
1066            .expect("new sid computed");
1067        let computed_context = security_server
1068            .sid_to_security_context(computed_sid)
1069            .expect("computed sid associated with context");
1070
1071        // New domain should be derived from the filename-specific rule.
1072        assert_eq!(computed_context, b"source_u:object_r:special_transition_t:s0");
1073
1074        let computed_sid = security_server
1075            .as_permission_check()
1076            .compute_new_fs_node_sid(
1077                source_sid,
1078                target_sid,
1079                FileClass::Character.into(),
1080                SPECIAL_FILE_NAME.into(),
1081            )
1082            .expect("new sid computed");
1083        let computed_context = security_server
1084            .sid_to_security_context(computed_sid)
1085            .expect("computed sid associated with context");
1086
1087        // New domain should be copied from the target, because the class does not match either the
1088        // filename-specific nor generic type transition rules.
1089        assert_eq!(computed_context, b"source_u:object_r:target_t:s0");
1090
1091        const OTHER_FILE_NAME: &[u8] = b"other_file";
1092        let computed_sid = security_server
1093            .as_permission_check()
1094            .compute_new_fs_node_sid(
1095                source_sid,
1096                target_sid,
1097                FileClass::File.into(),
1098                OTHER_FILE_NAME.into(),
1099            )
1100            .expect("new sid computed");
1101        let computed_context = security_server
1102            .sid_to_security_context(computed_sid)
1103            .expect("computed sid associated with context");
1104
1105        // New domain should be derived from the non-filename-specific rule, because the filename
1106        // does not match.
1107        assert_eq!(computed_context, b"source_u:object_r:transition_t:s0");
1108    }
1109
1110    #[test]
1111    fn permissions_are_fresh_after_different_policy_load() {
1112        let minimal_bytes = MINIMAL_BINARY_POLICY.to_vec();
1113        let allow_fork_bytes =
1114            include_bytes!("../testdata/composite_policies/compiled/allow_fork.pp").to_vec();
1115        let context = b"source_u:object_r:source_t:s0:c0";
1116
1117        let security_server = SecurityServer::new_default();
1118        security_server.set_enforcing(true);
1119
1120        let permission_check = security_server.as_permission_check();
1121
1122        // Load the minimal policy and get a SID for the context.
1123        assert_eq!(
1124            Ok(()),
1125            security_server.load_policy(minimal_bytes).map_err(|e| format!("{:?}", e))
1126        );
1127        let sid = security_server.security_context_to_sid(context.into()).unwrap();
1128
1129        // The minimal policy does not grant fork allowance.
1130        assert!(!permission_check.has_permission(sid, sid, ProcessPermission::Fork).permit);
1131
1132        // Load a policy that does grant fork allowance.
1133        assert_eq!(
1134            Ok(()),
1135            security_server.load_policy(allow_fork_bytes).map_err(|e| format!("{:?}", e))
1136        );
1137
1138        // The now-loaded "allow_fork" policy allows the context represented by `sid` to fork.
1139        assert!(permission_check.has_permission(sid, sid, ProcessPermission::Fork).permit);
1140    }
1141
1142    #[test]
1143    fn unknown_sids_are_effectively_unlabeled() {
1144        let with_unlabeled_access_domain_policy_bytes = include_bytes!(
1145            "../testdata/composite_policies/compiled/with_unlabeled_access_domain_policy.pp"
1146        )
1147        .to_vec();
1148        let with_additional_domain_policy_bytes = include_bytes!(
1149            "../testdata/composite_policies/compiled/with_additional_domain_policy.pp"
1150        )
1151        .to_vec();
1152        let allowed_type_context = b"source_u:object_r:allowed_t:s0:c0";
1153        let additional_type_context = b"source_u:object_r:additional_t:s0:c0";
1154
1155        let security_server = SecurityServer::new_default();
1156        security_server.set_enforcing(true);
1157
1158        // Load a policy, get a SID for a context that is valid for that policy, and verify
1159        // that a context that is not valid for that policy is not issued a SID.
1160        assert_eq!(
1161            Ok(()),
1162            security_server
1163                .load_policy(with_unlabeled_access_domain_policy_bytes.clone())
1164                .map_err(|e| format!("{:?}", e))
1165        );
1166        let allowed_type_sid =
1167            security_server.security_context_to_sid(allowed_type_context.into()).unwrap();
1168        assert!(security_server.security_context_to_sid(additional_type_context.into()).is_err());
1169
1170        // Load the policy that makes the second context valid, and verify that it is valid, and
1171        // verify that the first context remains valid (and unchanged).
1172        assert_eq!(
1173            Ok(()),
1174            security_server
1175                .load_policy(with_additional_domain_policy_bytes.clone())
1176                .map_err(|e| format!("{:?}", e))
1177        );
1178        let additional_type_sid =
1179            security_server.security_context_to_sid(additional_type_context.into()).unwrap();
1180        assert_eq!(
1181            allowed_type_sid,
1182            security_server.security_context_to_sid(allowed_type_context.into()).unwrap()
1183        );
1184
1185        let permission_check = security_server.as_permission_check();
1186
1187        // "allowed_t" is allowed the process getsched capability to "unlabeled_t" - but since
1188        // the currently-loaded policy defines "additional_t", the SID for "additional_t" does
1189        // not get treated as effectively unlabeled, and these permission checks are denied.
1190        assert!(
1191            !permission_check
1192                .has_permission(additional_type_sid, allowed_type_sid, ProcessPermission::GetSched)
1193                .permit
1194        );
1195        assert!(
1196            !permission_check
1197                .has_permission(additional_type_sid, allowed_type_sid, ProcessPermission::SetSched)
1198                .permit
1199        );
1200        assert!(
1201            !permission_check
1202                .has_permission(allowed_type_sid, additional_type_sid, ProcessPermission::GetSched)
1203                .permit
1204        );
1205        assert!(
1206            !permission_check
1207                .has_permission(allowed_type_sid, additional_type_sid, ProcessPermission::SetSched)
1208                .permit
1209        );
1210
1211        // We now flip back to the policy that does not recognize "additional_t"...
1212        assert_eq!(
1213            Ok(()),
1214            security_server
1215                .load_policy(with_unlabeled_access_domain_policy_bytes)
1216                .map_err(|e| format!("{:?}", e))
1217        );
1218
1219        // The now-loaded policy allows "allowed_t" the process getsched capability
1220        // to "unlabeled_t" and since the now-loaded policy does not recognize "additional_t",
1221        // "allowed_t" is now allowed the process getsched capability to "additional_t".
1222        assert!(
1223            permission_check
1224                .has_permission(allowed_type_sid, additional_type_sid, ProcessPermission::GetSched)
1225                .permit
1226        );
1227        assert!(
1228            !permission_check
1229                .has_permission(allowed_type_sid, additional_type_sid, ProcessPermission::SetSched)
1230                .permit
1231        );
1232
1233        // ... and the now-loaded policy also allows "unlabeled_t" the process
1234        // setsched capability to "allowed_t" and since the now-loaded policy does not recognize
1235        // "additional_t", "unlabeled_t" is now allowed the process setsched capability to
1236        // "allowed_t".
1237        assert!(
1238            !permission_check
1239                .has_permission(additional_type_sid, allowed_type_sid, ProcessPermission::GetSched)
1240                .permit
1241        );
1242        assert!(
1243            permission_check
1244                .has_permission(additional_type_sid, allowed_type_sid, ProcessPermission::SetSched)
1245                .permit
1246        );
1247
1248        // We also verify that we do not get a serialization for unrecognized "additional_t"...
1249        assert!(security_server.sid_to_security_context(additional_type_sid).is_none());
1250
1251        // ... but if we flip forward to the policy that recognizes "additional_t", then we see
1252        // the serialization succeed and return the original context string.
1253        assert_eq!(
1254            Ok(()),
1255            security_server
1256                .load_policy(with_additional_domain_policy_bytes)
1257                .map_err(|e| format!("{:?}", e))
1258        );
1259        assert_eq!(
1260            additional_type_context.to_vec(),
1261            security_server.sid_to_security_context(additional_type_sid).unwrap()
1262        );
1263    }
1264
1265    #[test]
1266    fn permission_check_permissive() {
1267        let security_server = security_server_with_tests_policy();
1268        security_server.set_enforcing(false);
1269        assert!(!security_server.is_enforcing());
1270
1271        let sid =
1272            security_server.security_context_to_sid("user0:object_r:type0:s0".into()).unwrap();
1273        let permission_check = security_server.as_permission_check();
1274
1275        // Test policy grants "type0" the process-fork permission to itself.
1276        // Since the permission is granted by policy, the check will not be audit logged.
1277        assert_eq!(
1278            permission_check.has_permission(sid, sid, ProcessPermission::Fork),
1279            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1280        );
1281
1282        // Test policy does not grant "type0" the process-getrlimit permission to itself, but
1283        // the security server is configured to be permissive. Because the permission was not
1284        // granted by the policy, the check will be audit logged.
1285        assert_eq!(
1286            permission_check.has_permission(sid, sid, ProcessPermission::GetRlimit),
1287            PermissionCheckResult { permit: true, audit: true, todo_bug: None }
1288        );
1289
1290        // Test policy is built with "deny unknown" behaviour, and has no "blk_file" class defined.
1291        // This permission should be treated like a defined permission that is not allowed to the
1292        // source, and both allowed and audited here.
1293        assert_eq!(
1294            permission_check.has_permission(
1295                sid,
1296                sid,
1297                CommonFsNodePermission::GetAttr.for_class(FileClass::Block)
1298            ),
1299            PermissionCheckResult { permit: true, audit: true, todo_bug: None }
1300        );
1301    }
1302
1303    #[test]
1304    fn permission_check_enforcing() {
1305        let security_server = security_server_with_tests_policy();
1306        security_server.set_enforcing(true);
1307        assert!(security_server.is_enforcing());
1308
1309        let sid =
1310            security_server.security_context_to_sid("user0:object_r:type0:s0".into()).unwrap();
1311        let permission_check = security_server.as_permission_check();
1312
1313        // Test policy grants "type0" the process-fork permission to itself.
1314        assert_eq!(
1315            permission_check.has_permission(sid, sid, ProcessPermission::Fork),
1316            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1317        );
1318
1319        // Test policy does not grant "type0" the process-getrlimit permission to itself.
1320        // Permission denials are audit logged in enforcing mode.
1321        assert_eq!(
1322            permission_check.has_permission(sid, sid, ProcessPermission::GetRlimit),
1323            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1324        );
1325
1326        // Test policy is built with "deny unknown" behaviour, and has no "blk_file" class defined.
1327        // This permission should therefore be denied, and the denial audited.
1328        assert_eq!(
1329            permission_check.has_permission(
1330                sid,
1331                sid,
1332                CommonFsNodePermission::GetAttr.for_class(FileClass::Block)
1333            ),
1334            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1335        );
1336    }
1337
1338    #[test]
1339    fn permissive_domain() {
1340        let security_server = security_server_with_tests_policy();
1341        security_server.set_enforcing(true);
1342        assert!(security_server.is_enforcing());
1343
1344        let permissive_sid = security_server
1345            .security_context_to_sid("user0:object_r:permissive_t:s0".into())
1346            .unwrap();
1347        let non_permissive_sid = security_server
1348            .security_context_to_sid("user0:object_r:non_permissive_t:s0".into())
1349            .unwrap();
1350
1351        let permission_check = security_server.as_permission_check();
1352
1353        // Test policy grants process-getsched permission to both of the test domains.
1354        assert_eq!(
1355            permission_check.has_permission(
1356                permissive_sid,
1357                permissive_sid,
1358                ProcessPermission::GetSched
1359            ),
1360            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1361        );
1362        assert_eq!(
1363            permission_check.has_permission(
1364                non_permissive_sid,
1365                non_permissive_sid,
1366                ProcessPermission::GetSched
1367            ),
1368            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1369        );
1370
1371        // Test policy does not grant process-getsched permission to the test domains on one another.
1372        // The permissive domain will be granted the permission, since it is marked permissive.
1373        assert_eq!(
1374            permission_check.has_permission(
1375                permissive_sid,
1376                non_permissive_sid,
1377                ProcessPermission::GetSched
1378            ),
1379            PermissionCheckResult { permit: true, audit: true, todo_bug: None }
1380        );
1381        assert_eq!(
1382            permission_check.has_permission(
1383                non_permissive_sid,
1384                permissive_sid,
1385                ProcessPermission::GetSched
1386            ),
1387            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1388        );
1389
1390        // Test policy has "deny unknown" behaviour and does not define the "blk_file" class, so
1391        // access to a permission on it will depend on whether the source is permissive.
1392        // The target domain is irrelevant, since the class/permission do not exist, so the non-
1393        // permissive SID is used for both checks.
1394        assert_eq!(
1395            permission_check.has_permission(
1396                permissive_sid,
1397                non_permissive_sid,
1398                CommonFsNodePermission::GetAttr.for_class(FileClass::Block)
1399            ),
1400            PermissionCheckResult { permit: true, audit: true, todo_bug: None }
1401        );
1402        assert_eq!(
1403            permission_check.has_permission(
1404                non_permissive_sid,
1405                non_permissive_sid,
1406                CommonFsNodePermission::GetAttr.for_class(FileClass::Block)
1407            ),
1408            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1409        );
1410    }
1411
1412    #[test]
1413    fn auditallow_and_dontaudit() {
1414        let security_server = security_server_with_tests_policy();
1415        security_server.set_enforcing(true);
1416        assert!(security_server.is_enforcing());
1417
1418        let audit_sid = security_server
1419            .security_context_to_sid("user0:object_r:test_audit_t:s0".into())
1420            .unwrap();
1421
1422        let permission_check = security_server.as_permission_check();
1423
1424        // Test policy grants the domain self-fork permission, and marks it audit-allow.
1425        assert_eq!(
1426            permission_check.has_permission(audit_sid, audit_sid, ProcessPermission::Fork),
1427            PermissionCheckResult { permit: true, audit: true, todo_bug: None }
1428        );
1429
1430        // Self-setsched permission is granted, and marked dont-audit, which takes no effect.
1431        assert_eq!(
1432            permission_check.has_permission(audit_sid, audit_sid, ProcessPermission::SetSched),
1433            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1434        );
1435
1436        // Self-getsched permission is denied, but marked dont-audit.
1437        assert_eq!(
1438            permission_check.has_permission(audit_sid, audit_sid, ProcessPermission::GetSched),
1439            PermissionCheckResult { permit: false, audit: false, todo_bug: None }
1440        );
1441
1442        // Self-getpgid permission is denied, with neither audit-allow nor dont-audit.
1443        assert_eq!(
1444            permission_check.has_permission(audit_sid, audit_sid, ProcessPermission::GetPgid),
1445            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1446        );
1447    }
1448
1449    #[test]
1450    fn access_checks_with_exceptions_config() {
1451        const EXCEPTIONS_CONFIG: &[&str] = &[
1452            // These statement should all be resolved.
1453            "todo_deny b/001 test_exception_source_t test_exception_target_t file",
1454            "todo_deny b/002 test_exception_other_t test_exception_target_t chr_file",
1455            "todo_deny b/003 test_exception_source_t test_exception_other_t anon_inode",
1456            "todo_deny b/004 test_exception_permissive_t test_exception_target_t file",
1457            "todo_permissive b/005 test_exception_todo_permissive_t",
1458            // These statements should not be resolved.
1459            "todo_deny b/101 test_undefined_source_t test_exception_target_t file",
1460            "todo_deny b/102 test_exception_source_t test_undefined_target_t file",
1461            "todo_permissive b/103 test_undefined_source_t",
1462        ];
1463        let exceptions_config = EXCEPTIONS_CONFIG.iter().map(|x| String::from(*x)).collect();
1464        let security_server = SecurityServer::new(String::new(), exceptions_config);
1465        security_server.set_enforcing(true);
1466
1467        const EXCEPTIONS_POLICY: &[u8] =
1468            include_bytes!("../testdata/composite_policies/compiled/exceptions_config_policy.pp");
1469        assert!(security_server.load_policy(EXCEPTIONS_POLICY.into()).is_ok());
1470
1471        let source_sid = security_server
1472            .security_context_to_sid("test_exception_u:object_r:test_exception_source_t:s0".into())
1473            .unwrap();
1474        let target_sid = security_server
1475            .security_context_to_sid("test_exception_u:object_r:test_exception_target_t:s0".into())
1476            .unwrap();
1477        let other_sid = security_server
1478            .security_context_to_sid("test_exception_u:object_r:test_exception_other_t:s0".into())
1479            .unwrap();
1480        let permissive_sid = security_server
1481            .security_context_to_sid(
1482                "test_exception_u:object_r:test_exception_permissive_t:s0".into(),
1483            )
1484            .unwrap();
1485        let unmatched_sid = security_server
1486            .security_context_to_sid(
1487                "test_exception_u:object_r:test_exception_unmatched_t:s0".into(),
1488            )
1489            .unwrap();
1490        let todo_permissive_sid = security_server
1491            .security_context_to_sid(
1492                "test_exception_u:object_r:test_exception_todo_permissive_t:s0".into(),
1493            )
1494            .unwrap();
1495
1496        let permission_check = security_server.as_permission_check();
1497
1498        // Source SID has no "process" permissions to target SID, and no exceptions.
1499        assert_eq!(
1500            permission_check.has_permission(source_sid, target_sid, ProcessPermission::GetPgid),
1501            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1502        );
1503
1504        // Source SID has no "file:entrypoint" permission to target SID, but there is an exception defined.
1505        assert_eq!(
1506            permission_check.has_permission(source_sid, target_sid, FilePermission::Entrypoint),
1507            PermissionCheckResult {
1508                permit: true,
1509                audit: true,
1510                todo_bug: Some(NonZeroU64::new(1).unwrap())
1511            }
1512        );
1513
1514        // Source SID has "file:execute_no_trans" permission to target SID.
1515        assert_eq!(
1516            permission_check.has_permission(source_sid, target_sid, FilePermission::ExecuteNoTrans),
1517            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1518        );
1519
1520        // Other SID has no "file:entrypoint" permissions to target SID, and the exception does not match "file" class.
1521        assert_eq!(
1522            permission_check.has_permission(other_sid, target_sid, FilePermission::Entrypoint),
1523            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1524        );
1525
1526        // Other SID has no "chr_file" permissions to target SID, but there is an exception defined.
1527        assert_eq!(
1528            permission_check.has_permission(
1529                other_sid,
1530                target_sid,
1531                CommonFsNodePermission::Read.for_class(FileClass::Character)
1532            ),
1533            PermissionCheckResult {
1534                permit: true,
1535                audit: true,
1536                todo_bug: Some(NonZeroU64::new(2).unwrap())
1537            }
1538        );
1539
1540        // Source SID has no "file:entrypoint" permissions to unmatched SID, and no exception is defined.
1541        assert_eq!(
1542            permission_check.has_permission(source_sid, unmatched_sid, FilePermission::Entrypoint),
1543            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1544        );
1545
1546        // Unmatched SID has no "file:entrypoint" permissions to target SID, and no exception is defined.
1547        assert_eq!(
1548            permission_check.has_permission(unmatched_sid, target_sid, FilePermission::Entrypoint),
1549            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1550        );
1551
1552        // Todo-deny exceptions are processed before the permissive bit is handled.
1553        assert_eq!(
1554            permission_check.has_permission(permissive_sid, target_sid, FilePermission::Entrypoint),
1555            PermissionCheckResult {
1556                permit: true,
1557                audit: true,
1558                todo_bug: Some(NonZeroU64::new(4).unwrap())
1559            }
1560        );
1561
1562        // Todo-permissive SID is not granted any permissions, so all permissions should be granted,
1563        // to all target domains and classes, and all grants should be associated with the bug.
1564        assert_eq!(
1565            permission_check.has_permission(
1566                todo_permissive_sid,
1567                target_sid,
1568                FilePermission::Entrypoint
1569            ),
1570            PermissionCheckResult {
1571                permit: true,
1572                audit: true,
1573                todo_bug: Some(NonZeroU64::new(5).unwrap())
1574            }
1575        );
1576        assert_eq!(
1577            permission_check.has_permission(
1578                todo_permissive_sid,
1579                todo_permissive_sid,
1580                FilePermission::Entrypoint
1581            ),
1582            PermissionCheckResult {
1583                permit: true,
1584                audit: true,
1585                todo_bug: Some(NonZeroU64::new(5).unwrap())
1586            }
1587        );
1588        assert_eq!(
1589            permission_check.has_permission(
1590                todo_permissive_sid,
1591                target_sid,
1592                FilePermission::Entrypoint
1593            ),
1594            PermissionCheckResult {
1595                permit: true,
1596                audit: true,
1597                todo_bug: Some(NonZeroU64::new(5).unwrap())
1598            }
1599        );
1600    }
1601
1602    #[test]
1603    fn handle_unknown() {
1604        let security_server = security_server_with_tests_policy();
1605
1606        let sid = security_server
1607            .security_context_to_sid("user0:object_r:type0:s0".into())
1608            .expect("Resolve Context to SID");
1609
1610        // Load a policy that is missing some elements, and marked handle_unknown=reject.
1611        // The policy should be rejected, since not all classes/permissions are defined.
1612        // Rejecting policy is not controlled by permissive vs enforcing.
1613        const REJECT_POLICY: &[u8] = include_bytes!(
1614            "../testdata/composite_policies/compiled/handle_unknown_policy-reject.pp"
1615        );
1616        assert!(security_server.load_policy(REJECT_POLICY.to_vec()).is_err());
1617
1618        security_server.set_enforcing(true);
1619
1620        // Load a policy that is missing some elements, and marked handle_unknown=deny.
1621        const DENY_POLICY: &[u8] =
1622            include_bytes!("../testdata/composite_policies/compiled/handle_unknown_policy-deny.pp");
1623        assert!(security_server.load_policy(DENY_POLICY.to_vec()).is_ok());
1624        let permission_check = security_server.as_permission_check();
1625
1626        // Check against undefined classes or permissions should deny access and audit.
1627        assert_eq!(
1628            permission_check.has_permission(sid, sid, ProcessPermission::GetSched),
1629            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1630        );
1631        assert_eq!(
1632            permission_check.has_permission(sid, sid, DirPermission::AddName),
1633            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1634        );
1635
1636        // Check that permissions that are defined are unaffected by handle-unknown.
1637        assert_eq!(
1638            permission_check.has_permission(sid, sid, DirPermission::Search),
1639            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1640        );
1641        assert_eq!(
1642            permission_check.has_permission(sid, sid, DirPermission::Reparent),
1643            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1644        );
1645
1646        // Load a policy that is missing some elements, and marked handle_unknown=allow.
1647        const ALLOW_POLICY: &[u8] = include_bytes!(
1648            "../testdata/composite_policies/compiled/handle_unknown_policy-allow.pp"
1649        );
1650        assert!(security_server.load_policy(ALLOW_POLICY.to_vec()).is_ok());
1651        let permission_check = security_server.as_permission_check();
1652
1653        // Check against undefined classes or permissions should grant access without audit.
1654        assert_eq!(
1655            permission_check.has_permission(sid, sid, ProcessPermission::GetSched),
1656            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1657        );
1658        assert_eq!(
1659            permission_check.has_permission(sid, sid, DirPermission::AddName),
1660            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1661        );
1662
1663        // Check that permissions that are defined are unaffected by handle-unknown.
1664        assert_eq!(
1665            permission_check.has_permission(sid, sid, DirPermission::Search),
1666            PermissionCheckResult { permit: true, audit: false, todo_bug: None }
1667        );
1668        assert_eq!(
1669            permission_check.has_permission(sid, sid, DirPermission::Reparent),
1670            PermissionCheckResult { permit: false, audit: true, todo_bug: None }
1671        );
1672    }
1673}