1use crate::access_vector_cache::{FifoQueryCache, Locked, Query};
6use crate::policy::{AccessVector, AccessVectorComputer, SELINUX_AVD_FLAGS_PERMISSIVE};
7use crate::security_server::SecurityServer;
8use crate::{ClassPermission, FsNodeClass, NullessByteStr, Permission, SecurityId};
9
10#[cfg(target_os = "fuchsia")]
11use fuchsia_inspect_contrib::profile_duration;
12
13use std::num::NonZeroU64;
14use std::sync::Weak;
15
16#[derive(Clone, Debug, PartialEq)]
18pub struct PermissionCheckResult {
19 pub permit: bool,
21
22 pub audit: bool,
27
28 pub todo_bug: Option<NonZeroU64>,
31}
32
33pub struct PermissionCheck<'a> {
37 security_server: &'a SecurityServer,
38 access_vector_cache: &'a Locked<FifoQueryCache<Weak<SecurityServer>>>,
39}
40
41impl<'a> PermissionCheck<'a> {
42 pub(crate) fn new(
43 security_server: &'a SecurityServer,
44 access_vector_cache: &'a Locked<FifoQueryCache<Weak<SecurityServer>>>,
45 ) -> Self {
46 Self { security_server, access_vector_cache }
47 }
48
49 pub fn has_permission<P: ClassPermission + Into<Permission> + Clone + 'static>(
53 &self,
54 source_sid: SecurityId,
55 target_sid: SecurityId,
56 permission: P,
57 ) -> PermissionCheckResult {
58 has_permission(
59 self.security_server.is_enforcing(),
60 self.access_vector_cache,
61 self.security_server,
62 source_sid,
63 target_sid,
64 permission,
65 )
66 }
67
68 pub fn security_server(&self) -> &SecurityServer {
71 self.security_server
72 }
73
74 pub fn compute_new_fs_node_sid(
79 &self,
80 source_sid: SecurityId,
81 target_sid: SecurityId,
82 fs_node_class: FsNodeClass,
83 fs_node_name: NullessByteStr<'_>,
84 ) -> Result<SecurityId, anyhow::Error> {
85 if !fs_node_name.as_bytes().is_empty() {
87 if let Some(sid) = self.access_vector_cache.compute_new_fs_node_sid_with_name(
88 source_sid,
89 target_sid,
90 fs_node_class,
91 fs_node_name,
92 ) {
93 return Ok(sid);
94 }
95 }
96 self.access_vector_cache.compute_new_fs_node_sid(source_sid, target_sid, fs_node_class)
97 }
98}
99
100fn has_permission<P: ClassPermission + Into<Permission> + Clone + 'static>(
102 is_enforcing: bool,
103 query: &impl Query,
104 access_vector_computer: &impl AccessVectorComputer,
105 source_sid: SecurityId,
106 target_sid: SecurityId,
107 permission: P,
108) -> PermissionCheckResult {
109 #[cfg(target_os = "fuchsia")]
110 profile_duration!("libselinux.check_permission");
111 let target_class = permission.class();
112
113 let decision = query.compute_access_decision(source_sid, target_sid, target_class.into());
114
115 let mut result = if let Some(permission_access_vector) =
116 access_vector_computer.access_vector_from_permissions(&[permission])
117 {
118 let permit = permission_access_vector & decision.allow == permission_access_vector;
119
120 let audit = if permit {
121 permission_access_vector & decision.auditallow != AccessVector::NONE
122 } else {
123 permission_access_vector & decision.auditdeny != AccessVector::NONE
124 };
125 PermissionCheckResult { permit, audit, todo_bug: None }
126 } else {
127 PermissionCheckResult { permit: false, audit: true, todo_bug: None }
128 };
129
130 if !result.permit {
131 if !is_enforcing {
132 result.permit = true;
134 } else if decision.flags & SELINUX_AVD_FLAGS_PERMISSIVE != 0 {
135 result.permit = true;
138 } else if decision.todo_bug.is_some() {
139 result.permit = true;
142 result.todo_bug = decision.todo_bug;
143 }
144 }
145
146 result
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152 use crate::access_vector_cache::DenyAll;
153 use crate::policy::testing::{ACCESS_VECTOR_0001, ACCESS_VECTOR_0010};
154 use crate::policy::{AccessDecision, AccessVector, IoctlAccessDecision};
155 use crate::{AbstractObjectClass, ProcessPermission};
156
157 use std::any::Any;
158 use std::num::NonZeroU32;
159 use std::sync::atomic::{AtomicU32, Ordering};
160 use std::sync::LazyLock;
161
162 static A_TEST_SID: LazyLock<SecurityId> = LazyLock::new(unique_sid);
164
165 fn unique_sid() -> SecurityId {
167 static NEXT_ID: AtomicU32 = AtomicU32::new(1000);
168 SecurityId(NonZeroU32::new(NEXT_ID.fetch_add(1, Ordering::AcqRel)).unwrap())
169 }
170
171 fn access_vector_from_permission<P: ClassPermission + Into<Permission> + 'static>(
172 permission: P,
173 ) -> AccessVector {
174 let any = &permission as &dyn Any;
175 let permission_ref = match any.downcast_ref::<ProcessPermission>() {
176 Some(permission_ref) => permission_ref,
177 None => return AccessVector::NONE,
178 };
179
180 match permission_ref {
181 ProcessPermission::Fork => ACCESS_VECTOR_0001,
182 ProcessPermission::Transition => ACCESS_VECTOR_0010,
183 _ => AccessVector::NONE,
184 }
185 }
186
187 fn access_vector_from_permissions<
188 'a,
189 P: ClassPermission + Into<Permission> + Clone + 'static,
190 >(
191 permissions: &[P],
192 ) -> AccessVector {
193 let mut access_vector = AccessVector::NONE;
194 for permission in permissions {
195 access_vector |= access_vector_from_permission(permission.clone());
196 }
197 access_vector
198 }
199
200 #[derive(Default)]
201 pub struct DenyAllPermissions(DenyAll);
202
203 impl Query for DenyAllPermissions {
204 fn compute_access_decision(
205 &self,
206 source_sid: SecurityId,
207 target_sid: SecurityId,
208 target_class: AbstractObjectClass,
209 ) -> AccessDecision {
210 self.0.compute_access_decision(source_sid, target_sid, target_class)
211 }
212
213 fn compute_new_fs_node_sid(
214 &self,
215 _source_sid: SecurityId,
216 _target_sid: SecurityId,
217 _fs_node_class: FsNodeClass,
218 ) -> Result<SecurityId, anyhow::Error> {
219 unreachable!();
220 }
221
222 fn compute_new_fs_node_sid_with_name(
223 &self,
224 _source_sid: SecurityId,
225 _target_sid: SecurityId,
226 _fs_node_class: FsNodeClass,
227 _fs_node_name: NullessByteStr<'_>,
228 ) -> Option<SecurityId> {
229 unreachable!();
230 }
231
232 fn compute_ioctl_access_decision(
233 &self,
234 _source_sid: SecurityId,
235 _target_sid: SecurityId,
236 _target_class: AbstractObjectClass,
237 _ioctl_prefix: u8,
238 ) -> IoctlAccessDecision {
239 unreachable!()
242 }
243 }
244
245 impl AccessVectorComputer for DenyAllPermissions {
246 fn access_vector_from_permissions<
247 P: ClassPermission + Into<Permission> + Clone + 'static,
248 >(
249 &self,
250 permissions: &[P],
251 ) -> Option<AccessVector> {
252 Some(access_vector_from_permissions(permissions))
253 }
254 }
255
256 #[derive(Default)]
258 struct AllowAllPermissions;
259
260 impl Query for AllowAllPermissions {
261 fn compute_access_decision(
262 &self,
263 _source_sid: SecurityId,
264 _target_sid: SecurityId,
265 _target_class: AbstractObjectClass,
266 ) -> AccessDecision {
267 AccessDecision::allow(AccessVector::ALL)
268 }
269
270 fn compute_new_fs_node_sid(
271 &self,
272 _source_sid: SecurityId,
273 _target_sid: SecurityId,
274 _fs_node_class: FsNodeClass,
275 ) -> Result<SecurityId, anyhow::Error> {
276 unreachable!();
277 }
278
279 fn compute_new_fs_node_sid_with_name(
280 &self,
281 _source_sid: SecurityId,
282 _target_sid: SecurityId,
283 _fs_node_class: FsNodeClass,
284 _fs_node_name: NullessByteStr<'_>,
285 ) -> Option<SecurityId> {
286 unreachable!();
287 }
288
289 fn compute_ioctl_access_decision(
290 &self,
291 _source_sid: SecurityId,
292 _target_sid: SecurityId,
293 _target_class: AbstractObjectClass,
294 _ioctl_prefix: u8,
295 ) -> IoctlAccessDecision {
296 IoctlAccessDecision::ALLOW_ALL
297 }
298 }
299
300 impl AccessVectorComputer for AllowAllPermissions {
301 fn access_vector_from_permissions<
302 P: ClassPermission + Into<Permission> + Clone + 'static,
303 >(
304 &self,
305 permissions: &[P],
306 ) -> Option<AccessVector> {
307 Some(access_vector_from_permissions(permissions))
308 }
309 }
310
311 #[test]
312 fn has_permission_both() {
313 let deny_all: DenyAllPermissions = Default::default();
314 let allow_all: AllowAllPermissions = Default::default();
315
316 let permissions = [ProcessPermission::Fork, ProcessPermission::Transition];
319 for permission in &permissions {
320 assert_eq!(
322 PermissionCheckResult { permit: false, audit: true, todo_bug: None },
323 has_permission(
324 true,
325 &deny_all,
326 &deny_all,
327 *A_TEST_SID,
328 *A_TEST_SID,
329 permission.clone()
330 )
331 );
332 assert_eq!(
334 PermissionCheckResult { permit: true, audit: false, todo_bug: None },
335 has_permission(
336 true,
337 &allow_all,
338 &allow_all,
339 *A_TEST_SID,
340 *A_TEST_SID,
341 permission.clone()
342 )
343 );
344 }
345 }
346}