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