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