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