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 let result = has_permission(
73 self.local_cache,
74 self.access_vector_cache,
75 source_sid,
76 target_sid,
77 permission.into(),
78 );
79 self.apply_enforcement(result)
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 let result = 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.access_vector_cache,
115 xperms_kind,
116 source_sid,
117 target_sid,
118 permission.into(),
119 xperm,
120 )
121 },
122 );
123 self.apply_enforcement(result)
124 }
125
126 fn apply_enforcement(&self, mut result: PermissionCheckResult) -> PermissionCheckResult {
127 if !result.granted {
128 if !self.security_server.is_enforcing() {
129 result.permissive = true;
130 result.todo_bug = None;
131 } else if result.todo_bug.is_some() {
132 result.granted = true;
133 }
134 } else {
135 result.todo_bug = None;
136 }
137 result
138 }
139
140 pub fn security_server(&self) -> &SecurityServer {
143 self.security_server
144 }
145
146 pub fn compute_new_fs_node_sid(
151 &self,
152 source_sid: SecurityId,
153 target_sid: SecurityId,
154 fs_node_class: FsNodeClass,
155 fs_node_name: NullessByteStr<'_>,
156 ) -> Result<SecurityId, anyhow::Error> {
157 if !fs_node_name.as_bytes().is_empty() {
159 if let Some(sid) = self.access_vector_cache.compute_new_fs_node_sid_with_name(
160 source_sid,
161 target_sid,
162 fs_node_class,
163 fs_node_name,
164 ) {
165 return Ok(sid);
166 }
167 }
168 self.access_vector_cache.compute_create_sid(source_sid, target_sid, fs_node_class.into())
169 }
170
171 pub fn compute_create_sid(
174 &self,
175 source_sid: SecurityId,
176 target_sid: SecurityId,
177 target_class: KernelClass,
178 ) -> Result<SecurityId, anyhow::Error> {
179 self.access_vector_cache.compute_create_sid(source_sid, target_sid, target_class)
180 }
181
182 pub fn compute_access_decision(
184 &self,
185 source_sid: SecurityId,
186 target_sid: SecurityId,
187 target_class: KernelClass,
188 ) -> KernelAccessDecision {
189 self.local_cache.lookup_access_decision(source_sid, target_sid, target_class, || {
190 self.access_vector_cache.compute_access_decision(source_sid, target_sid, target_class)
191 })
192 }
193}
194
195fn has_permission(
197 local_cache: &PerThreadCache,
198 query: &impl Query,
199 source_sid: SecurityId,
200 target_sid: SecurityId,
201 permission: KernelPermission,
202) -> PermissionCheckResult {
203 let permission_access_vector = permission.as_access_vector();
204
205 if permission == KernelPermission::Fd(FdPermission::Use) {
206 return local_cache.lookup_fd_use(source_sid, target_sid, || {
208 let decision = query.compute_access_decision(source_sid, target_sid, KernelClass::Fd);
209 access_decision_to_permission_check_result(permission_access_vector, decision)
210 });
211 }
212
213 let decision =
214 local_cache.lookup_access_decision(source_sid, target_sid, permission.class(), || {
215 query.compute_access_decision(source_sid, target_sid, permission.class())
216 });
217 access_decision_to_permission_check_result(permission_access_vector, decision)
218}
219
220fn access_decision_to_permission_check_result(
221 permission_access_vector: AccessVector,
222 decision: KernelAccessDecision,
223) -> PermissionCheckResult {
224 let permissive = decision.flags & SELINUX_AVD_FLAGS_PERMISSIVE != 0;
225 let granted = permission_access_vector & decision.allow == permission_access_vector;
226 let audit = permission_access_vector & decision.audit != AccessVector::NONE;
227 PermissionCheckResult { granted, audit, permissive, todo_bug: decision.todo_bug }
228}
229
230fn has_extended_permission(
232 query: &impl Query,
233 xperms_kind: XpermsKind,
234 source_sid: SecurityId,
235 target_sid: SecurityId,
236 permission: KernelPermission,
237 xperm: u16,
238) -> PermissionCheckResult {
239 let [xperms_postfix, xperms_prefix] = xperm.to_le_bytes();
240 let xperms_decision = query.compute_xperms_access_decision(
241 xperms_kind,
242 source_sid,
243 target_sid,
244 permission,
245 xperms_prefix,
246 );
247
248 let granted = xperms_decision.allow.contains(xperms_postfix);
249 let audit = xperms_decision.audit.contains(xperms_postfix);
250 let permissive = xperms_decision.permissive;
251 let mut result = PermissionCheckResult { granted, audit, permissive, todo_bug: None };
252
253 if !result.permit() && xperms_decision.has_todo {
254 let base_decision =
257 query.compute_access_decision(source_sid, target_sid, permission.class());
258 result.todo_bug = base_decision.todo_bug;
259 }
260
261 result
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267 use crate::access_vector_cache::KernelXpermsAccessDecision;
268 use crate::policy::{
269 AccessDecision, AccessVector, AccessVectorComputer, KernelAccessDecision, XpermsBitmap,
270 };
271 use crate::{CommonFsNodePermission, FileClass, ForClass, KernelClass, ProcessPermission};
272
273 use std::num::NonZeroU32;
274 use std::sync::LazyLock;
275 use std::sync::atomic::{AtomicU32, Ordering};
276
277 static A_TEST_SID: LazyLock<SecurityId> = LazyLock::new(unique_sid);
279
280 fn unique_sid() -> SecurityId {
282 static NEXT_ID: AtomicU32 = AtomicU32::new(1000);
283 SecurityId(NonZeroU32::new(NEXT_ID.fetch_add(1, Ordering::AcqRel)).unwrap())
284 }
285
286 fn access_decision_to_kernel_access_decision(
288 _class: KernelClass,
289 decision: AccessDecision,
290 ) -> KernelAccessDecision {
291 KernelAccessDecision {
292 allow: decision.allow,
293 audit: (decision.allow & decision.auditallow) | (!decision.allow & decision.auditdeny),
294 todo_bug: decision.todo_bug,
295 flags: decision.flags,
296 }
297 }
298
299 #[derive(Default)]
300 pub struct DenyAllPermissions;
301
302 impl Query for DenyAllPermissions {
303 fn compute_access_decision(
304 &self,
305 _source_sid: SecurityId,
306 _target_sid: SecurityId,
307 _target_class: KernelClass,
308 ) -> KernelAccessDecision {
309 KernelAccessDecision {
310 allow: AccessVector::NONE,
311 audit: AccessVector::ALL,
312 flags: 0,
313 todo_bug: None,
314 }
315 }
316
317 fn compute_create_sid(
318 &self,
319 _source_sid: SecurityId,
320 _target_sid: SecurityId,
321 _target_class: KernelClass,
322 ) -> Result<SecurityId, anyhow::Error> {
323 unreachable!();
324 }
325
326 fn compute_new_fs_node_sid_with_name(
327 &self,
328 _source_sid: SecurityId,
329 _target_sid: SecurityId,
330 _fs_node_class: FsNodeClass,
331 _fs_node_name: NullessByteStr<'_>,
332 ) -> Option<SecurityId> {
333 unreachable!();
334 }
335
336 fn compute_xperms_access_decision(
337 &self,
338 _xperms_kind: XpermsKind,
339 _source_sid: SecurityId,
340 _target_sid: SecurityId,
341 _permission: KernelPermission,
342 _xperms_prefix: u8,
343 ) -> KernelXpermsAccessDecision {
344 KernelXpermsAccessDecision {
345 allow: XpermsBitmap::NONE,
346 audit: XpermsBitmap::ALL,
347 permissive: false,
348 has_todo: false,
349 }
350 }
351 }
352
353 impl AccessVectorComputer for DenyAllPermissions {
354 fn access_decision_to_kernel_access_decision(
355 &self,
356 class: KernelClass,
357 av: AccessDecision,
358 ) -> KernelAccessDecision {
359 access_decision_to_kernel_access_decision(class, av)
360 }
361 }
362
363 #[derive(Default)]
365 struct AllowAllPermissions;
366
367 impl Query for AllowAllPermissions {
368 fn compute_access_decision(
369 &self,
370 _source_sid: SecurityId,
371 _target_sid: SecurityId,
372 _target_class: KernelClass,
373 ) -> KernelAccessDecision {
374 KernelAccessDecision {
375 allow: AccessVector::ALL,
376 audit: AccessVector::NONE,
377 flags: 0,
378 todo_bug: None,
379 }
380 }
381
382 fn compute_create_sid(
383 &self,
384 _source_sid: SecurityId,
385 _target_sid: SecurityId,
386 _target_class: KernelClass,
387 ) -> Result<SecurityId, anyhow::Error> {
388 unreachable!();
389 }
390
391 fn compute_new_fs_node_sid_with_name(
392 &self,
393 _source_sid: SecurityId,
394 _target_sid: SecurityId,
395 _fs_node_class: FsNodeClass,
396 _fs_node_name: NullessByteStr<'_>,
397 ) -> Option<SecurityId> {
398 unreachable!();
399 }
400
401 fn compute_xperms_access_decision(
402 &self,
403 _xperms_kind: XpermsKind,
404 _source_sid: SecurityId,
405 _target_sid: SecurityId,
406 _permission: KernelPermission,
407 _xperms_prefix: u8,
408 ) -> KernelXpermsAccessDecision {
409 KernelXpermsAccessDecision {
410 allow: XpermsBitmap::ALL,
411 audit: XpermsBitmap::NONE,
412 permissive: false,
413 has_todo: false,
414 }
415 }
416 }
417
418 impl AccessVectorComputer for AllowAllPermissions {
419 fn access_decision_to_kernel_access_decision(
420 &self,
421 class: KernelClass,
422 av: AccessDecision,
423 ) -> KernelAccessDecision {
424 access_decision_to_kernel_access_decision(class, av)
425 }
426 }
427
428 #[test]
429 fn has_permission_both() {
430 let deny_all = DenyAllPermissions::default();
431 let allow_all = AllowAllPermissions::default();
432
433 let permissions = [ProcessPermission::Fork, ProcessPermission::Transition];
436 for permission in permissions {
437 let local_cache1 = PerThreadCache::default();
438 let result = has_permission(
440 &local_cache1,
441 &deny_all,
442 *A_TEST_SID,
443 *A_TEST_SID,
444 permission.into(),
445 );
446 assert_eq!(
447 result,
448 PermissionCheckResult {
449 granted: false,
450 audit: true,
451 permissive: false,
452 todo_bug: None
453 }
454 );
455 assert!(!result.permit());
456
457 let local_cache2 = PerThreadCache::default();
458 let result = has_permission(
460 &local_cache2,
461 &allow_all,
462 *A_TEST_SID,
463 *A_TEST_SID,
464 permission.into(),
465 );
466 assert_eq!(
467 result,
468 PermissionCheckResult {
469 granted: true,
470 audit: false,
471 permissive: false,
472 todo_bug: None
473 }
474 );
475 assert!(result.permit());
476 }
477 }
478
479 #[test]
480 fn has_ioctl_permission_enforcing() {
481 let deny_all = DenyAllPermissions::default();
482 let allow_all = AllowAllPermissions::default();
483 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
484
485 let result = has_extended_permission(
487 &deny_all,
488 XpermsKind::Ioctl,
489 *A_TEST_SID,
490 *A_TEST_SID,
491 permission.into(),
492 0xabcd,
493 );
494 assert_eq!(
495 result,
496 PermissionCheckResult {
497 granted: false,
498 audit: true,
499 permissive: false,
500 todo_bug: None
501 }
502 );
503 assert!(!result.permit());
504
505 let result = has_extended_permission(
507 &allow_all,
508 XpermsKind::Ioctl,
509 *A_TEST_SID,
510 *A_TEST_SID,
511 permission.into(),
512 0xabcd,
513 );
514 assert_eq!(
515 result,
516 PermissionCheckResult {
517 granted: true,
518 audit: false,
519 permissive: false,
520 todo_bug: None
521 }
522 );
523 assert!(result.permit());
524 }
525
526 fn security_server_with_tests_policy() -> std::sync::Arc<SecurityServer> {
527 const POLICY: &[u8] =
528 include_bytes!("../testdata/micro_policies/security_server_tests_policy");
529 let security_server = SecurityServer::new_default();
530 assert!(security_server.load_policy(POLICY.into()).is_ok());
531 security_server
532 }
533
534 #[test]
535 fn has_ioctl_permission_not_enforcing() {
536 let security_server = security_server_with_tests_policy();
537 let enforcing_values = [true, false];
538 for enforcing in enforcing_values {
539 security_server.set_enforcing(enforcing);
540
541 let sid =
542 security_server.security_context_to_sid("user0:object_r:type0:s0".into()).unwrap();
543 let local_cache = PerThreadCache::default();
544 let permission_check = security_server.as_permission_check(&local_cache);
545
546 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
547
548 let result = permission_check.has_extended_permission(
552 XpermsKind::Ioctl,
553 sid,
554 sid,
555 permission,
556 0xabcd,
557 );
558 assert_eq!(
559 result,
560 PermissionCheckResult {
561 granted: false,
562 audit: true,
563 permissive: !enforcing,
564 todo_bug: None
565 }
566 );
567 assert_eq!(result.permit(), !enforcing);
568 }
569 }
570}