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