Skip to main content

selinux/
security_server.rs

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