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, FsNodeClass, KernelClass, KernelPermission, NullessByteStr, SecurityId,
10};
11
12use std::num::NonZeroU32;
13
14#[derive(Clone, Debug, PartialEq)]
16pub struct PermissionCheckResult {
17 pub granted: bool,
19
20 pub audit: bool,
25
26 pub permissive: bool,
29
30 pub todo_bug: Option<NonZeroU32>,
33}
34
35impl PermissionCheckResult {
36 pub fn permit(&self) -> bool {
38 self.granted || self.permissive
39 }
40}
41
42pub struct PermissionCheck<'a> {
46 security_server: &'a SecurityServer,
47 access_vector_cache: &'a AccessVectorCache,
48}
49
50impl<'a> PermissionCheck<'a> {
51 pub(crate) fn new(
52 security_server: &'a SecurityServer,
53 access_vector_cache: &'a AccessVectorCache,
54 ) -> Self {
55 Self { security_server, access_vector_cache }
56 }
57
58 pub fn has_permission<P: ClassPermission + Into<KernelPermission> + Clone + 'static>(
62 &self,
63 source_sid: SecurityId,
64 target_sid: SecurityId,
65 permission: P,
66 ) -> PermissionCheckResult {
67 has_permission(
68 self.security_server.is_enforcing(),
69 self.access_vector_cache,
70 source_sid,
71 target_sid,
72 permission.into(),
73 )
74 }
75
76 pub fn has_extended_permission<
90 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
91 >(
92 &self,
93 xperms_kind: XpermsKind,
94 source_sid: SecurityId,
95 target_sid: SecurityId,
96 permission: P,
97 xperm: u16,
98 ) -> PermissionCheckResult {
99 has_extended_permission(
100 self.security_server.is_enforcing(),
101 self.access_vector_cache,
102 xperms_kind,
103 source_sid,
104 target_sid,
105 permission.into(),
106 xperm,
107 )
108 }
109
110 pub fn security_server(&self) -> &SecurityServer {
113 self.security_server
114 }
115
116 pub fn compute_new_fs_node_sid(
121 &self,
122 source_sid: SecurityId,
123 target_sid: SecurityId,
124 fs_node_class: FsNodeClass,
125 fs_node_name: NullessByteStr<'_>,
126 ) -> Result<SecurityId, anyhow::Error> {
127 if !fs_node_name.as_bytes().is_empty() {
129 if let Some(sid) = self.access_vector_cache.compute_new_fs_node_sid_with_name(
130 source_sid,
131 target_sid,
132 fs_node_class,
133 fs_node_name,
134 ) {
135 return Ok(sid);
136 }
137 }
138 self.access_vector_cache.compute_create_sid(source_sid, target_sid, fs_node_class.into())
139 }
140
141 pub fn compute_create_sid(
144 &self,
145 source_sid: SecurityId,
146 target_sid: SecurityId,
147 target_class: KernelClass,
148 ) -> Result<SecurityId, anyhow::Error> {
149 self.access_vector_cache.compute_create_sid(source_sid, target_sid, target_class)
150 }
151
152 pub fn compute_access_decision(
154 &self,
155 source_sid: SecurityId,
156 target_sid: SecurityId,
157 target_class: KernelClass,
158 ) -> KernelAccessDecision {
159 self.access_vector_cache.compute_access_decision(source_sid, target_sid, target_class)
160 }
161}
162
163fn has_permission(
165 is_enforcing: bool,
166 query: &impl Query,
167 source_sid: SecurityId,
168 target_sid: SecurityId,
169 permission: KernelPermission,
170) -> PermissionCheckResult {
171 let target_class = permission.class();
172 let permission_access_vector = permission.as_access_vector();
173
174 let decision = query.compute_access_decision(source_sid, target_sid, target_class.into());
175 let permissive = decision.flags & SELINUX_AVD_FLAGS_PERMISSIVE != 0;
176 let granted = permission_access_vector & decision.allow == permission_access_vector;
177 let audit = permission_access_vector & decision.audit != AccessVector::NONE;
178 let mut result = PermissionCheckResult { granted, audit, permissive, todo_bug: None };
179
180 if !result.granted {
181 if !is_enforcing {
182 result.permissive = true;
184 } else if decision.todo_bug.is_some() {
185 result.granted = true;
192 result.todo_bug = decision.todo_bug;
193 }
194 }
195
196 result
197}
198
199fn has_extended_permission(
201 is_enforcing: bool,
202 query: &impl Query,
203 xperms_kind: XpermsKind,
204 source_sid: SecurityId,
205 target_sid: SecurityId,
206 permission: KernelPermission,
207 xperm: u16,
208) -> PermissionCheckResult {
209 let [xperms_postfix, xperms_prefix] = xperm.to_le_bytes();
210 let xperms_decision = query.compute_xperms_access_decision(
211 xperms_kind,
212 source_sid,
213 target_sid,
214 permission,
215 xperms_prefix,
216 );
217
218 let granted = xperms_decision.allow.contains(xperms_postfix);
219 let audit = xperms_decision.audit.contains(xperms_postfix);
220 let permissive = xperms_decision.permissive;
221 let mut result = PermissionCheckResult { granted, audit, permissive, todo_bug: None };
222
223 if !result.granted {
224 if !is_enforcing {
225 result.permissive = true;
226 } else if xperms_decision.has_todo {
227 let base_decision =
230 query.compute_access_decision(source_sid, target_sid, permission.class());
231 result.todo_bug = base_decision.todo_bug;
232 }
233 }
234
235 result
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241 use crate::access_vector_cache::KernelXpermsAccessDecision;
242 use crate::policy::{
243 AccessDecision, AccessVector, AccessVectorComputer, KernelAccessDecision, XpermsBitmap,
244 };
245 use crate::{CommonFsNodePermission, FileClass, ForClass, KernelClass, ProcessPermission};
246
247 use std::num::NonZeroU32;
248 use std::sync::LazyLock;
249 use std::sync::atomic::{AtomicU32, Ordering};
250
251 static A_TEST_SID: LazyLock<SecurityId> = LazyLock::new(unique_sid);
253
254 fn unique_sid() -> SecurityId {
256 static NEXT_ID: AtomicU32 = AtomicU32::new(1000);
257 SecurityId(NonZeroU32::new(NEXT_ID.fetch_add(1, Ordering::AcqRel)).unwrap())
258 }
259
260 fn access_decision_to_kernel_access_decision(
262 _class: KernelClass,
263 decision: AccessDecision,
264 ) -> KernelAccessDecision {
265 KernelAccessDecision {
266 allow: decision.allow,
267 audit: (decision.allow & decision.auditallow) | (!decision.allow & decision.auditdeny),
268 todo_bug: decision.todo_bug,
269 flags: decision.flags,
270 }
271 }
272
273 #[derive(Default)]
274 pub struct DenyAllPermissions;
275
276 impl Query for DenyAllPermissions {
277 fn compute_access_decision(
278 &self,
279 _source_sid: SecurityId,
280 _target_sid: SecurityId,
281 _target_class: KernelClass,
282 ) -> KernelAccessDecision {
283 KernelAccessDecision {
284 allow: AccessVector::NONE,
285 audit: AccessVector::ALL,
286 flags: 0,
287 todo_bug: None,
288 }
289 }
290
291 fn compute_create_sid(
292 &self,
293 _source_sid: SecurityId,
294 _target_sid: SecurityId,
295 _target_class: KernelClass,
296 ) -> Result<SecurityId, anyhow::Error> {
297 unreachable!();
298 }
299
300 fn compute_new_fs_node_sid_with_name(
301 &self,
302 _source_sid: SecurityId,
303 _target_sid: SecurityId,
304 _fs_node_class: FsNodeClass,
305 _fs_node_name: NullessByteStr<'_>,
306 ) -> Option<SecurityId> {
307 unreachable!();
308 }
309
310 fn compute_xperms_access_decision(
311 &self,
312 _xperms_kind: XpermsKind,
313 _source_sid: SecurityId,
314 _target_sid: SecurityId,
315 _permission: KernelPermission,
316 _xperms_prefix: u8,
317 ) -> KernelXpermsAccessDecision {
318 KernelXpermsAccessDecision {
319 allow: XpermsBitmap::NONE,
320 audit: XpermsBitmap::ALL,
321 permissive: false,
322 has_todo: false,
323 }
324 }
325 }
326
327 impl AccessVectorComputer for DenyAllPermissions {
328 fn access_decision_to_kernel_access_decision(
329 &self,
330 class: KernelClass,
331 av: AccessDecision,
332 ) -> KernelAccessDecision {
333 access_decision_to_kernel_access_decision(class, av)
334 }
335 }
336
337 #[derive(Default)]
339 struct AllowAllPermissions;
340
341 impl Query for AllowAllPermissions {
342 fn compute_access_decision(
343 &self,
344 _source_sid: SecurityId,
345 _target_sid: SecurityId,
346 _target_class: KernelClass,
347 ) -> KernelAccessDecision {
348 KernelAccessDecision {
349 allow: AccessVector::ALL,
350 audit: AccessVector::NONE,
351 flags: 0,
352 todo_bug: None,
353 }
354 }
355
356 fn compute_create_sid(
357 &self,
358 _source_sid: SecurityId,
359 _target_sid: SecurityId,
360 _target_class: KernelClass,
361 ) -> Result<SecurityId, anyhow::Error> {
362 unreachable!();
363 }
364
365 fn compute_new_fs_node_sid_with_name(
366 &self,
367 _source_sid: SecurityId,
368 _target_sid: SecurityId,
369 _fs_node_class: FsNodeClass,
370 _fs_node_name: NullessByteStr<'_>,
371 ) -> Option<SecurityId> {
372 unreachable!();
373 }
374
375 fn compute_xperms_access_decision(
376 &self,
377 _xperms_kind: XpermsKind,
378 _source_sid: SecurityId,
379 _target_sid: SecurityId,
380 _permission: KernelPermission,
381 _xperms_prefix: u8,
382 ) -> KernelXpermsAccessDecision {
383 KernelXpermsAccessDecision {
384 allow: XpermsBitmap::ALL,
385 audit: XpermsBitmap::NONE,
386 permissive: false,
387 has_todo: false,
388 }
389 }
390 }
391
392 impl AccessVectorComputer for AllowAllPermissions {
393 fn access_decision_to_kernel_access_decision(
394 &self,
395 class: KernelClass,
396 av: AccessDecision,
397 ) -> KernelAccessDecision {
398 access_decision_to_kernel_access_decision(class, av)
399 }
400 }
401
402 #[test]
403 fn has_permission_both() {
404 let deny_all = DenyAllPermissions::default();
405 let allow_all = AllowAllPermissions::default();
406
407 let permissions = [ProcessPermission::Fork, ProcessPermission::Transition];
410 for permission in permissions {
411 let result = has_permission(
413 true,
414 &deny_all,
415 *A_TEST_SID,
416 *A_TEST_SID,
417 permission.into(),
418 );
419 assert_eq!(
420 result,
421 PermissionCheckResult {
422 granted: false,
423 audit: true,
424 permissive: false,
425 todo_bug: None
426 }
427 );
428 assert!(!result.permit());
429
430 let result = has_permission(
432 true,
433 &allow_all,
434 *A_TEST_SID,
435 *A_TEST_SID,
436 permission.into(),
437 );
438 assert_eq!(
439 result,
440 PermissionCheckResult {
441 granted: true,
442 audit: false,
443 permissive: false,
444 todo_bug: None
445 }
446 );
447 assert!(result.permit());
448 }
449 }
450
451 #[test]
452 fn has_ioctl_permission_enforcing() {
453 let deny_all = DenyAllPermissions::default();
454 let allow_all = AllowAllPermissions::default();
455 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
456
457 let result = has_extended_permission(
459 true,
460 &deny_all,
461 XpermsKind::Ioctl,
462 *A_TEST_SID,
463 *A_TEST_SID,
464 permission.into(),
465 0xabcd,
466 );
467 assert_eq!(
468 result,
469 PermissionCheckResult {
470 granted: false,
471 audit: true,
472 permissive: false,
473 todo_bug: None
474 }
475 );
476 assert!(!result.permit());
477
478 let result = has_extended_permission(
480 true,
481 &allow_all,
482 XpermsKind::Ioctl,
483 *A_TEST_SID,
484 *A_TEST_SID,
485 permission.into(),
486 0xabcd,
487 );
488 assert_eq!(
489 result,
490 PermissionCheckResult {
491 granted: true,
492 audit: false,
493 permissive: false,
494 todo_bug: None
495 }
496 );
497 assert!(result.permit());
498 }
499
500 #[test]
501 fn has_ioctl_permission_not_enforcing() {
502 let deny_all = DenyAllPermissions::default();
503 let permission = CommonFsNodePermission::Ioctl.for_class(FileClass::File);
504
505 let result = has_extended_permission(
508 false,
509 &deny_all,
510 XpermsKind::Ioctl,
511 *A_TEST_SID,
512 *A_TEST_SID,
513 permission,
514 0xabcd,
515 );
516 assert_eq!(
517 result,
518 PermissionCheckResult { granted: false, audit: true, permissive: true, todo_bug: None }
519 );
520 assert!(result.permit());
521 }
522}