Skip to main content

selinux/
permission_check.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::kernel::{AccessVectorCache, KernelAccessDecision, Query};
6use crate::policy::{AccessVector, SELINUX_AVD_FLAGS_PERMISSIVE, XpermsKind};
7use crate::security_server::{PolicySeqNo, SecurityServer};
8use crate::{ClassPermission, FdPermission, KernelClass, KernelPermission, SecurityId};
9
10use std::num::NonZeroU32;
11
12pub use crate::kernel::PerThreadCache;
13
14/// Result of a permission lookup between two Security Contexts.
15#[derive(Clone, Debug, PartialEq)]
16pub struct PermissionCheckResult {
17    /// True if the specified permissions are granted by policy.
18    pub granted: bool,
19
20    /// True if details of the check should be audit logged. Audit logs are by default only output
21    /// when the policy defines that the permissions should be denied (whether or not the check is
22    /// "permissive"), but may be suppressed for some denials ("dontaudit"), or for some allowed
23    /// permissions ("auditallow").
24    pub audit: bool,
25
26    /// True if the access should be granted because either the security server is running in
27    /// permissive mode, or the subject domain is marked as permissive.
28    pub permissive: bool,
29
30    /// If the [`KernelAccessDecision`] indicates that permission denials should not be enforced then
31    /// [`PermissionCheckResult::permit`] will be true, and this field will hold the Id of the bug to
32    /// reference in audit logging.
33    pub todo_bug: Option<NonZeroU32>,
34}
35
36impl PermissionCheckResult {
37    /// Returns true if the request was granted, or was made in permissive mode.
38    pub fn permit(&self) -> bool {
39        self.granted || self.permissive
40    }
41}
42
43/// Implements the [`PermissionCheck::has_permission`] API, based on supplied [`SecurityServer`] and
44/// [`AccessVectorCache`] implementations.
45// TODO: https://fxbug.dev/362699811 - Revise the traits to avoid direct dependencies on `SecurityServer`.
46pub struct PermissionCheck<'a> {
47    security_server: &'a SecurityServer,
48    access_vector_cache: &'a AccessVectorCache,
49    local_cache: &'a PerThreadCache,
50}
51
52impl<'a> PermissionCheck<'a> {
53    pub(crate) fn new(
54        security_server: &'a SecurityServer,
55        access_vector_cache: &'a AccessVectorCache,
56        local_cache: &'a PerThreadCache,
57    ) -> Self {
58        Self { security_server, access_vector_cache, local_cache }
59    }
60
61    /// Returns whether the `source_sid` has the specified `permission` on `target_sid`.
62    /// The result indicates both whether `permission` is `permit`ted, and whether the caller
63    /// should `audit` log the query.
64    pub fn has_permission<P: ClassPermission + Into<KernelPermission> + Clone + 'static>(
65        &self,
66        source_sid: SecurityId,
67        target_sid: SecurityId,
68        permission: P,
69    ) -> PermissionCheckResult {
70        let policy_seqno = self.security_server.policy_seqno();
71        let result = has_permission(
72            self.local_cache,
73            self.access_vector_cache,
74            policy_seqno,
75            source_sid,
76            target_sid,
77            permission.into(),
78        );
79        self.apply_enforcement(result)
80    }
81
82    /// Returns whether the `source_sid` has both a base permission (i.e. `ioctl` or `nlmsg`) and
83    /// the specified extended permission on `target_sid`, and whether the decision should be
84    /// audited.
85    ///
86    /// A request is allowed if the base permission is `allow`ed and either the numeric extended
87    /// permission of this `xperms_kind` is included in an `allowxperm` statement, or extended
88    /// permissions of this kind are not filtered for this domain.
89    ///
90    /// A granted request is audited if the base permission is `auditallow` and the extended
91    /// permission is `auditallowxperm`.
92    ///
93    /// A denied request is audited if the base permission is `dontaudit` or the extended
94    /// permission is `dontauditxperm`.
95    pub fn has_extended_permission<
96        P: ClassPermission + Into<KernelPermission> + Clone + 'static,
97    >(
98        &self,
99        xperms_kind: XpermsKind,
100        source_sid: SecurityId,
101        target_sid: SecurityId,
102        permission: P,
103        xperm: u16,
104    ) -> PermissionCheckResult {
105        let permission: KernelPermission = permission.into();
106        let policy_seqno = self.security_server.policy_seqno();
107        let result = self.local_cache.check_xperm(
108            policy_seqno,
109            xperms_kind,
110            source_sid,
111            target_sid,
112            permission,
113            xperm,
114            || {
115                has_extended_permission(
116                    self.access_vector_cache,
117                    xperms_kind,
118                    source_sid,
119                    target_sid,
120                    permission,
121                    xperm,
122                )
123            },
124        );
125        self.apply_enforcement(result)
126    }
127
128    fn apply_enforcement(&self, mut result: PermissionCheckResult) -> PermissionCheckResult {
129        if !result.granted {
130            if !self.security_server.is_enforcing() {
131                result.permissive = true;
132                result.todo_bug = None;
133            } else if result.todo_bug.is_some() {
134                result.granted = true;
135            }
136        } else {
137            result.todo_bug = None;
138        }
139        result
140    }
141
142    // TODO: https://fxbug.dev/362699811 - Remove this once `SecurityServer` APIs such as `sid_to_security_context()`
143    // are exposed via a trait rather than directly by that implementation.
144    pub fn security_server(&self) -> &SecurityServer {
145        self.security_server
146    }
147
148    /// Returns the SID with which to label a new `target_class` instance created by `source_sid`
149    /// in a container labeled `target_sid`, taking into account role, type, and optional filename
150    /// transition rules.
151    /// Callers pass an empty slice (`&[]`) for `name` to express nameless transitions.
152    pub fn compute_create_sid(
153        &self,
154        source_sid: SecurityId,
155        target_sid: SecurityId,
156        target_class: KernelClass,
157        name: &[u8],
158    ) -> Result<SecurityId, anyhow::Error> {
159        self.access_vector_cache.compute_create_sid(source_sid, target_sid, target_class, name)
160    }
161
162    /// Returns the raw [`KernelAccessDecision`] for a specified source, target and class.
163    pub fn compute_access_decision(
164        &self,
165        source_sid: SecurityId,
166        target_sid: SecurityId,
167        target_class: KernelClass,
168    ) -> KernelAccessDecision {
169        self.local_cache.lookup_access_decision(
170            self.security_server.policy_seqno(),
171            source_sid,
172            target_sid,
173            target_class,
174            || {
175                self.access_vector_cache.compute_access_decision(
176                    source_sid,
177                    target_sid,
178                    target_class,
179                )
180            },
181        )
182    }
183}
184
185/// Internal implementation of the [`PermissionCheck::has_permission`] API, in terms of the [`Query`] trait.
186fn has_permission(
187    local_cache: &PerThreadCache,
188    query: &impl Query,
189    policy_seqno: PolicySeqNo,
190    source_sid: SecurityId,
191    target_sid: SecurityId,
192    permission: KernelPermission,
193) -> PermissionCheckResult {
194    let permission_access_vector = permission.as_access_vector();
195
196    if permission == KernelPermission::Fd(FdPermission::Use) {
197        // fd use checks are cached separately.
198        return local_cache.lookup_fd_use(policy_seqno, source_sid, target_sid, || {
199            let decision = query.compute_access_decision(source_sid, target_sid, KernelClass::Fd);
200            access_decision_to_permission_check_result(permission_access_vector, decision)
201        });
202    }
203
204    let decision = local_cache.lookup_access_decision(
205        policy_seqno,
206        source_sid,
207        target_sid,
208        permission.class(),
209        || query.compute_access_decision(source_sid, target_sid, permission.class()),
210    );
211    access_decision_to_permission_check_result(permission_access_vector, decision)
212}
213
214fn access_decision_to_permission_check_result(
215    permission_access_vector: AccessVector,
216    decision: KernelAccessDecision,
217) -> PermissionCheckResult {
218    let permissive = decision.flags & SELINUX_AVD_FLAGS_PERMISSIVE != 0;
219    let granted = permission_access_vector & decision.allow == permission_access_vector;
220    let audit = permission_access_vector & decision.audit != AccessVector::NONE;
221    PermissionCheckResult { granted, audit, permissive, todo_bug: decision.todo_bug }
222}
223
224/// Internal implementation of the [`PermissionCheck::has_extended_permission`] API, in terms of the [`Query`] trait.
225fn has_extended_permission(
226    query: &impl Query,
227    xperms_kind: XpermsKind,
228    source_sid: SecurityId,
229    target_sid: SecurityId,
230    permission: KernelPermission,
231    xperm: u16,
232) -> PermissionCheckResult {
233    let [xperms_postfix, xperms_prefix] = xperm.to_le_bytes();
234    let xperms_decision = query.compute_xperms_access_decision(
235        xperms_kind,
236        source_sid,
237        target_sid,
238        permission,
239        xperms_prefix,
240    );
241
242    let granted = xperms_decision.allow.contains(xperms_postfix);
243    let audit = xperms_decision.audit.contains(xperms_postfix);
244    let permissive = xperms_decision.permissive;
245    let mut result = PermissionCheckResult { granted, audit, permissive, todo_bug: None };
246
247    if !result.permit() && xperms_decision.has_todo {
248        // A todo_bug applies to this entry. Look up the base decision for details.
249        // This will re-compute the base decision if it is not cached.
250        let base_decision =
251            query.compute_access_decision(source_sid, target_sid, permission.class());
252        result.todo_bug = base_decision.todo_bug;
253    }
254
255    result
256}
257
258#[cfg(test)]
259mod tests {
260    use super::*;
261    use crate::kernel::KernelXpermsAccessDecision;
262    use crate::policy::{AccessVector, XpermsBitmap};
263    use crate::{CommonFsNodePermission, FileClass, ForClass, KernelClass, ProcessPermission};
264
265    use std::num::NonZeroU32;
266    use std::sync::LazyLock;
267    use std::sync::atomic::{AtomicU32, Ordering};
268
269    /// SID to use where any value will do.
270    static A_TEST_SID: LazyLock<SecurityId> = LazyLock::new(unique_sid);
271
272    /// Returns a new [`SecurityId`] with unique id.
273    fn unique_sid() -> SecurityId {
274        static NEXT_ID: AtomicU32 = AtomicU32::new(1000);
275        SecurityId(NonZeroU32::new(NEXT_ID.fetch_add(1, Ordering::AcqRel)).unwrap())
276    }
277
278    /// [`Query`] that denies all [`AccessVector`]s.
279    #[derive(Default)]
280    struct DenyAllPermissions;
281
282    impl Query for DenyAllPermissions {
283        fn compute_access_decision(
284            &self,
285            _source_sid: SecurityId,
286            _target_sid: SecurityId,
287            _target_class: KernelClass,
288        ) -> KernelAccessDecision {
289            KernelAccessDecision {
290                allow: AccessVector::NONE,
291                audit: AccessVector::ALL,
292                flags: 0,
293                todo_bug: None,
294            }
295        }
296
297        fn compute_create_sid(
298            &self,
299            _source_sid: SecurityId,
300            _target_sid: SecurityId,
301            _target_class: KernelClass,
302            _name: &[u8],
303        ) -> Result<SecurityId, anyhow::Error> {
304            unreachable!();
305        }
306
307        fn compute_xperms_access_decision(
308            &self,
309            _xperms_kind: XpermsKind,
310            _source_sid: SecurityId,
311            _target_sid: SecurityId,
312            _permission: KernelPermission,
313            _xperms_prefix: u8,
314        ) -> KernelXpermsAccessDecision {
315            KernelXpermsAccessDecision {
316                allow: XpermsBitmap::NONE,
317                audit: XpermsBitmap::ALL,
318                permissive: false,
319                has_todo: false,
320            }
321        }
322    }
323
324    /// [`Query`] that permits all [`AccessVector`]s.
325    #[derive(Default)]
326    struct AllowAllPermissions;
327
328    impl Query for AllowAllPermissions {
329        fn compute_access_decision(
330            &self,
331            _source_sid: SecurityId,
332            _target_sid: SecurityId,
333            _target_class: KernelClass,
334        ) -> KernelAccessDecision {
335            KernelAccessDecision {
336                allow: AccessVector::ALL,
337                audit: AccessVector::NONE,
338                flags: 0,
339                todo_bug: None,
340            }
341        }
342
343        fn compute_create_sid(
344            &self,
345            _source_sid: SecurityId,
346            _target_sid: SecurityId,
347            _target_class: KernelClass,
348            _name: &[u8],
349        ) -> Result<SecurityId, anyhow::Error> {
350            unreachable!();
351        }
352
353        fn compute_xperms_access_decision(
354            &self,
355            _xperms_kind: XpermsKind,
356            _source_sid: SecurityId,
357            _target_sid: SecurityId,
358            _permission: KernelPermission,
359            _xperms_prefix: u8,
360        ) -> KernelXpermsAccessDecision {
361            KernelXpermsAccessDecision {
362                allow: XpermsBitmap::ALL,
363                audit: XpermsBitmap::NONE,
364                permissive: false,
365                has_todo: false,
366            }
367        }
368    }
369
370    #[test]
371    fn has_permission_both() {
372        let deny_all = DenyAllPermissions::default();
373        let allow_all = AllowAllPermissions::default();
374
375        // Use permissions that are mapped to access vector bits in
376        // `access_vector_from_permission`.
377        let permissions = [ProcessPermission::Fork, ProcessPermission::Transition];
378        for permission in permissions {
379            let local_cache1 = PerThreadCache::default();
380            // DenyAllPermissions denies.
381            let result = has_permission(
382                &local_cache1,
383                &deny_all,
384                PolicySeqNo::INITIAL,
385                *A_TEST_SID,
386                *A_TEST_SID,
387                permission.into(),
388            );
389            assert_eq!(
390                result,
391                PermissionCheckResult {
392                    granted: false,
393                    audit: true,
394                    permissive: false,
395                    todo_bug: None
396                }
397            );
398            assert!(!result.permit());
399
400            let local_cache2 = PerThreadCache::default();
401            // AllowAllPermissions allows.
402            let result = has_permission(
403                &local_cache2,
404                &allow_all,
405                PolicySeqNo::INITIAL,
406                *A_TEST_SID,
407                *A_TEST_SID,
408                permission.into(),
409            );
410            assert_eq!(
411                result,
412                PermissionCheckResult {
413                    granted: true,
414                    audit: false,
415                    permissive: false,
416                    todo_bug: None
417                }
418            );
419            assert!(result.permit());
420        }
421    }
422
423    #[test]
424    fn has_ioctl_permission_enforcing() {
425        let deny_all = DenyAllPermissions::default();
426        let allow_all = AllowAllPermissions::default();
427        let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
428
429        // DenyAllPermissions denies.
430        let result = has_extended_permission(
431            &deny_all,
432            XpermsKind::Ioctl,
433            *A_TEST_SID,
434            *A_TEST_SID,
435            permission.into(),
436            0xabcd,
437        );
438        assert_eq!(
439            result,
440            PermissionCheckResult {
441                granted: false,
442                audit: true,
443                permissive: false,
444                todo_bug: None
445            }
446        );
447        assert!(!result.permit());
448
449        // AllowAllPermissions allows.
450        let result = has_extended_permission(
451            &allow_all,
452            XpermsKind::Ioctl,
453            *A_TEST_SID,
454            *A_TEST_SID,
455            permission.into(),
456            0xabcd,
457        );
458        assert_eq!(
459            result,
460            PermissionCheckResult {
461                granted: true,
462                audit: false,
463                permissive: false,
464                todo_bug: None
465            }
466        );
467        assert!(result.permit());
468    }
469
470    fn security_server_with_tests_policy() -> std::sync::Arc<SecurityServer> {
471        const POLICY: &[u8] =
472            include_bytes!("../testdata/micro_policies/security_server_tests_policy");
473        let security_server = SecurityServer::new_default();
474        assert!(security_server.load_policy(POLICY.into()).is_ok());
475        security_server
476    }
477
478    #[test]
479    fn has_ioctl_permission_not_enforcing() {
480        let security_server = security_server_with_tests_policy();
481        let enforcing_values = [true, false];
482        for enforcing in enforcing_values {
483            security_server.set_enforcing(enforcing);
484
485            let sid =
486                security_server.security_context_to_sid("user0:object_r:type0:s0".into()).unwrap();
487            let local_cache = PerThreadCache::default();
488            let permission_check = security_server.as_permission_check(&local_cache);
489
490            let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
491
492            // The test policy does not grant the permission, but when the security server
493            // is not in enforcing mode the permission will still be granted.
494            // Because the permission was not granted by policy, the check will be audit logged.
495            let result = permission_check.has_extended_permission(
496                XpermsKind::Ioctl,
497                sid,
498                sid,
499                permission,
500                0xabcd,
501            );
502            assert_eq!(
503                result,
504                PermissionCheckResult {
505                    granted: false,
506                    audit: true,
507                    permissive: !enforcing,
508                    todo_bug: None
509                }
510            );
511            assert_eq!(result.permit(), !enforcing);
512        }
513    }
514}