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