1use crate::access_vector_cache::{AccessVectorCache, Query};
6use crate::policy::{AccessVector, KernelAccessDecision, SELINUX_AVD_FLAGS_PERMISSIVE, XpermsKind};
7use crate::security_server::SecurityServer;
8use crate::{
9 ClassPermission, FdPermission, FsNodeClass, KernelClass, KernelPermission, NullessByteStr,
10 SecurityId,
11};
12
13use std::num::NonZeroU32;
14
15pub use crate::local_cache::PerThreadCache;
16
17#[derive(Clone, Debug, PartialEq)]
19pub struct PermissionCheckResult {
20 pub granted: bool,
22
23 pub audit: bool,
28
29 pub permissive: bool,
32
33 pub todo_bug: Option<NonZeroU32>,
36}
37
38impl PermissionCheckResult {
39 pub fn permit(&self) -> bool {
41 self.granted || self.permissive
42 }
43}
44
45pub struct PermissionCheck<'a> {
49 security_server: &'a SecurityServer,
50 access_vector_cache: &'a AccessVectorCache,
51 local_cache: &'a PerThreadCache,
52}
53
54impl<'a> PermissionCheck<'a> {
55 pub(crate) fn new(
56 security_server: &'a SecurityServer,
57 access_vector_cache: &'a AccessVectorCache,
58 local_cache: &'a PerThreadCache,
59 ) -> Self {
60 Self { security_server, access_vector_cache, local_cache }
61 }
62
63 pub fn has_permission<P: ClassPermission + Into<KernelPermission> + Clone + 'static>(
67 &self,
68 source_sid: SecurityId,
69 target_sid: SecurityId,
70 permission: P,
71 ) -> PermissionCheckResult {
72 has_permission(
73 self.security_server.is_enforcing(),
74 self.local_cache,
75 self.access_vector_cache,
76 source_sid,
77 target_sid,
78 permission.into(),
79 )
80 }
81
82 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 self.local_cache.check_xperm(
107 xperms_kind,
108 source_sid,
109 target_sid,
110 permission.clone(),
111 xperm,
112 || {
113 has_extended_permission(
114 self.security_server.is_enforcing(),
115 self.access_vector_cache,
116 xperms_kind,
117 source_sid,
118 target_sid,
119 permission.into(),
120 xperm,
121 )
122 },
123 )
124 }
125
126 pub fn security_server(&self) -> &SecurityServer {
129 self.security_server
130 }
131
132 pub fn compute_new_fs_node_sid(
137 &self,
138 source_sid: SecurityId,
139 target_sid: SecurityId,
140 fs_node_class: FsNodeClass,
141 fs_node_name: NullessByteStr<'_>,
142 ) -> Result<SecurityId, anyhow::Error> {
143 if !fs_node_name.as_bytes().is_empty() {
145 if let Some(sid) = self.access_vector_cache.compute_new_fs_node_sid_with_name(
146 source_sid,
147 target_sid,
148 fs_node_class,
149 fs_node_name,
150 ) {
151 return Ok(sid);
152 }
153 }
154 self.access_vector_cache.compute_create_sid(source_sid, target_sid, fs_node_class.into())
155 }
156
157 pub fn compute_create_sid(
160 &self,
161 source_sid: SecurityId,
162 target_sid: SecurityId,
163 target_class: KernelClass,
164 ) -> Result<SecurityId, anyhow::Error> {
165 self.access_vector_cache.compute_create_sid(source_sid, target_sid, target_class)
166 }
167
168 pub fn compute_access_decision(
170 &self,
171 source_sid: SecurityId,
172 target_sid: SecurityId,
173 target_class: KernelClass,
174 ) -> KernelAccessDecision {
175 self.local_cache.lookup_access_decision(source_sid, target_sid, target_class, || {
176 self.access_vector_cache.compute_access_decision(source_sid, target_sid, target_class)
177 })
178 }
179}
180
181fn has_permission(
183 is_enforcing: bool,
184 local_cache: &PerThreadCache,
185 query: &impl Query,
186 source_sid: SecurityId,
187 target_sid: SecurityId,
188 permission: KernelPermission,
189) -> PermissionCheckResult {
190 let permission_access_vector = permission.as_access_vector();
191
192 if permission == KernelPermission::Fd(FdPermission::Use) {
193 return local_cache.lookup_fd_use(source_sid, target_sid, || {
195 let decision = query.compute_access_decision(source_sid, target_sid, KernelClass::Fd);
196 access_decision_to_permission_check_result(
197 is_enforcing,
198 permission_access_vector,
199 decision,
200 )
201 });
202 }
203
204 let decision =
205 local_cache.lookup_access_decision(source_sid, target_sid, permission.class(), || {
206 query.compute_access_decision(source_sid, target_sid, permission.class())
207 });
208 let result = access_decision_to_permission_check_result(
209 is_enforcing,
210 permission_access_vector,
211 decision,
212 );
213
214 result
215}
216
217fn access_decision_to_permission_check_result(
218 is_enforcing: bool,
219 permission_access_vector: AccessVector,
220 decision: KernelAccessDecision,
221) -> PermissionCheckResult {
222 let permissive = decision.flags & SELINUX_AVD_FLAGS_PERMISSIVE != 0;
223 let granted = permission_access_vector & decision.allow == permission_access_vector;
224 let audit = permission_access_vector & decision.audit != AccessVector::NONE;
225 let mut result = PermissionCheckResult { granted, audit, permissive, todo_bug: None };
226
227 if !result.granted {
228 if !is_enforcing {
229 result.permissive = true;
230 } else if decision.todo_bug.is_some() {
231 result.granted = true;
232 result.todo_bug = decision.todo_bug;
233 }
234 }
235
236 result
237}
238
239fn has_extended_permission(
241 is_enforcing: bool,
242 query: &impl Query,
243 xperms_kind: XpermsKind,
244 source_sid: SecurityId,
245 target_sid: SecurityId,
246 permission: KernelPermission,
247 xperm: u16,
248) -> PermissionCheckResult {
249 let [xperms_postfix, xperms_prefix] = xperm.to_le_bytes();
250 let xperms_decision = query.compute_xperms_access_decision(
251 xperms_kind,
252 source_sid,
253 target_sid,
254 permission,
255 xperms_prefix,
256 );
257
258 let granted = xperms_decision.allow.contains(xperms_postfix);
259 let audit = xperms_decision.audit.contains(xperms_postfix);
260 let permissive = xperms_decision.permissive;
261 let mut result = PermissionCheckResult { granted, audit, permissive, todo_bug: None };
262
263 if !result.granted {
264 if !is_enforcing {
265 result.permissive = true;
266 } else if xperms_decision.has_todo {
267 let base_decision =
270 query.compute_access_decision(source_sid, target_sid, permission.class());
271 result.todo_bug = base_decision.todo_bug;
272 }
273 }
274
275 result
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281 use crate::access_vector_cache::KernelXpermsAccessDecision;
282 use crate::policy::{
283 AccessDecision, AccessVector, AccessVectorComputer, KernelAccessDecision, XpermsBitmap,
284 };
285 use crate::{CommonFsNodePermission, FileClass, ForClass, KernelClass, ProcessPermission};
286
287 use std::num::NonZeroU32;
288 use std::sync::LazyLock;
289 use std::sync::atomic::{AtomicU32, Ordering};
290
291 static A_TEST_SID: LazyLock<SecurityId> = LazyLock::new(unique_sid);
293
294 fn unique_sid() -> SecurityId {
296 static NEXT_ID: AtomicU32 = AtomicU32::new(1000);
297 SecurityId(NonZeroU32::new(NEXT_ID.fetch_add(1, Ordering::AcqRel)).unwrap())
298 }
299
300 fn access_decision_to_kernel_access_decision(
302 _class: KernelClass,
303 decision: AccessDecision,
304 ) -> KernelAccessDecision {
305 KernelAccessDecision {
306 allow: decision.allow,
307 audit: (decision.allow & decision.auditallow) | (!decision.allow & decision.auditdeny),
308 todo_bug: decision.todo_bug,
309 flags: decision.flags,
310 }
311 }
312
313 #[derive(Default)]
314 pub struct DenyAllPermissions;
315
316 impl Query for DenyAllPermissions {
317 fn compute_access_decision(
318 &self,
319 _source_sid: SecurityId,
320 _target_sid: SecurityId,
321 _target_class: KernelClass,
322 ) -> KernelAccessDecision {
323 KernelAccessDecision {
324 allow: AccessVector::NONE,
325 audit: AccessVector::ALL,
326 flags: 0,
327 todo_bug: None,
328 }
329 }
330
331 fn compute_create_sid(
332 &self,
333 _source_sid: SecurityId,
334 _target_sid: SecurityId,
335 _target_class: KernelClass,
336 ) -> Result<SecurityId, anyhow::Error> {
337 unreachable!();
338 }
339
340 fn compute_new_fs_node_sid_with_name(
341 &self,
342 _source_sid: SecurityId,
343 _target_sid: SecurityId,
344 _fs_node_class: FsNodeClass,
345 _fs_node_name: NullessByteStr<'_>,
346 ) -> Option<SecurityId> {
347 unreachable!();
348 }
349
350 fn compute_xperms_access_decision(
351 &self,
352 _xperms_kind: XpermsKind,
353 _source_sid: SecurityId,
354 _target_sid: SecurityId,
355 _permission: KernelPermission,
356 _xperms_prefix: u8,
357 ) -> KernelXpermsAccessDecision {
358 KernelXpermsAccessDecision {
359 allow: XpermsBitmap::NONE,
360 audit: XpermsBitmap::ALL,
361 permissive: false,
362 has_todo: false,
363 }
364 }
365 }
366
367 impl AccessVectorComputer for DenyAllPermissions {
368 fn access_decision_to_kernel_access_decision(
369 &self,
370 class: KernelClass,
371 av: AccessDecision,
372 ) -> KernelAccessDecision {
373 access_decision_to_kernel_access_decision(class, av)
374 }
375 }
376
377 #[derive(Default)]
379 struct AllowAllPermissions;
380
381 impl Query for AllowAllPermissions {
382 fn compute_access_decision(
383 &self,
384 _source_sid: SecurityId,
385 _target_sid: SecurityId,
386 _target_class: KernelClass,
387 ) -> KernelAccessDecision {
388 KernelAccessDecision {
389 allow: AccessVector::ALL,
390 audit: AccessVector::NONE,
391 flags: 0,
392 todo_bug: None,
393 }
394 }
395
396 fn compute_create_sid(
397 &self,
398 _source_sid: SecurityId,
399 _target_sid: SecurityId,
400 _target_class: KernelClass,
401 ) -> Result<SecurityId, anyhow::Error> {
402 unreachable!();
403 }
404
405 fn compute_new_fs_node_sid_with_name(
406 &self,
407 _source_sid: SecurityId,
408 _target_sid: SecurityId,
409 _fs_node_class: FsNodeClass,
410 _fs_node_name: NullessByteStr<'_>,
411 ) -> Option<SecurityId> {
412 unreachable!();
413 }
414
415 fn compute_xperms_access_decision(
416 &self,
417 _xperms_kind: XpermsKind,
418 _source_sid: SecurityId,
419 _target_sid: SecurityId,
420 _permission: KernelPermission,
421 _xperms_prefix: u8,
422 ) -> KernelXpermsAccessDecision {
423 KernelXpermsAccessDecision {
424 allow: XpermsBitmap::ALL,
425 audit: XpermsBitmap::NONE,
426 permissive: false,
427 has_todo: false,
428 }
429 }
430 }
431
432 impl AccessVectorComputer for AllowAllPermissions {
433 fn access_decision_to_kernel_access_decision(
434 &self,
435 class: KernelClass,
436 av: AccessDecision,
437 ) -> KernelAccessDecision {
438 access_decision_to_kernel_access_decision(class, av)
439 }
440 }
441
442 #[test]
443 fn has_permission_both() {
444 let deny_all = DenyAllPermissions::default();
445 let allow_all = AllowAllPermissions::default();
446
447 let permissions = [ProcessPermission::Fork, ProcessPermission::Transition];
450 for permission in permissions {
451 let local_cache1 = PerThreadCache::default();
452 let result = has_permission(
454 true,
455 &local_cache1,
456 &deny_all,
457 *A_TEST_SID,
458 *A_TEST_SID,
459 permission.into(),
460 );
461 assert_eq!(
462 result,
463 PermissionCheckResult {
464 granted: false,
465 audit: true,
466 permissive: false,
467 todo_bug: None
468 }
469 );
470 assert!(!result.permit());
471
472 let local_cache2 = PerThreadCache::default();
473 let result = has_permission(
475 true,
476 &local_cache2,
477 &allow_all,
478 *A_TEST_SID,
479 *A_TEST_SID,
480 permission.into(),
481 );
482 assert_eq!(
483 result,
484 PermissionCheckResult {
485 granted: true,
486 audit: false,
487 permissive: false,
488 todo_bug: None
489 }
490 );
491 assert!(result.permit());
492 }
493 }
494
495 #[test]
496 fn has_ioctl_permission_enforcing() {
497 let deny_all = DenyAllPermissions::default();
498 let allow_all = AllowAllPermissions::default();
499 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
500
501 let result = has_extended_permission(
503 true,
504 &deny_all,
505 XpermsKind::Ioctl,
506 *A_TEST_SID,
507 *A_TEST_SID,
508 permission.into(),
509 0xabcd,
510 );
511 assert_eq!(
512 result,
513 PermissionCheckResult {
514 granted: false,
515 audit: true,
516 permissive: false,
517 todo_bug: None
518 }
519 );
520 assert!(!result.permit());
521
522 let result = has_extended_permission(
524 true,
525 &allow_all,
526 XpermsKind::Ioctl,
527 *A_TEST_SID,
528 *A_TEST_SID,
529 permission.into(),
530 0xabcd,
531 );
532 assert_eq!(
533 result,
534 PermissionCheckResult {
535 granted: true,
536 audit: false,
537 permissive: false,
538 todo_bug: None
539 }
540 );
541 assert!(result.permit());
542 }
543
544 #[test]
545 fn has_ioctl_permission_not_enforcing() {
546 let deny_all = DenyAllPermissions::default();
547 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
548
549 let result = has_extended_permission(
552 false,
553 &deny_all,
554 XpermsKind::Ioctl,
555 *A_TEST_SID,
556 *A_TEST_SID,
557 permission,
558 0xabcd,
559 );
560 assert_eq!(
561 result,
562 PermissionCheckResult { granted: false, audit: true, permissive: true, todo_bug: None }
563 );
564 assert!(result.permit());
565 }
566}