1pub mod arrays;
6pub mod error;
7pub mod index;
8pub mod metadata;
9pub mod parsed_policy;
10pub mod parser;
11pub mod view;
12
13mod constraints;
14mod extensible_bitmap;
15mod security_context;
16mod symbols;
17
18pub use arrays::{FsUseType, XpermsBitmap};
19pub use index::FsUseLabelAndType;
20pub use parser::PolicyCursor;
21pub use security_context::{SecurityContext, SecurityContextError};
22
23use crate::{
24 ClassPermission, KernelClass, KernelPermission, NullessByteStr, ObjectClass, PolicyCap,
25};
26use index::PolicyIndex;
27use metadata::HandleUnknown;
28use parsed_policy::ParsedPolicy;
29use parser::PolicyData;
30use symbols::{find_class_by_name, find_common_symbol_by_name_bytes};
31
32use anyhow::Context as _;
33use std::fmt::{Debug, Display, LowerHex};
34use std::num::{NonZeroU8, NonZeroU32, NonZeroU64};
35use std::ops::Deref;
36use std::str::FromStr;
37use std::sync::Arc;
38use zerocopy::{
39 FromBytes, Immutable, KnownLayout, Ref, SplitByteSlice, Unaligned, little_endian as le,
40};
41
42#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
44pub struct UserId(NonZeroU32);
45
46#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
48pub struct RoleId(NonZeroU32);
49
50#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
52pub struct TypeId(NonZeroU32);
53
54#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
56pub struct SensitivityId(NonZeroU32);
57
58#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
60pub struct CategoryId(NonZeroU32);
61
62#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
65pub struct ClassId(NonZeroU32);
66
67impl ClassId {
68 pub fn new(id: NonZeroU32) -> Self {
70 Self(id)
71 }
72}
73
74impl Into<u32> for ClassId {
75 fn into(self) -> u32 {
76 self.0.into()
77 }
78}
79
80#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
82pub struct ClassPermissionId(NonZeroU8);
83
84impl Display for ClassPermissionId {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 write!(f, "{}", self.0)
87 }
88}
89
90#[derive(Debug, Clone, PartialEq)]
95pub struct AccessDecision {
96 pub allow: AccessVector,
97 pub auditallow: AccessVector,
98 pub auditdeny: AccessVector,
99 pub flags: u32,
100
101 pub todo_bug: Option<NonZeroU64>,
104}
105
106impl Default for AccessDecision {
107 fn default() -> Self {
108 Self::allow(AccessVector::NONE)
109 }
110}
111
112impl AccessDecision {
113 pub(super) const fn allow(allow: AccessVector) -> Self {
116 Self {
117 allow,
118 auditallow: AccessVector::NONE,
119 auditdeny: AccessVector::ALL,
120 flags: 0,
121 todo_bug: None,
122 }
123 }
124}
125
126pub(super) const SELINUX_AVD_FLAGS_PERMISSIVE: u32 = 1;
128
129#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
132pub struct AccessVector(u32);
133
134impl AccessVector {
135 pub const NONE: AccessVector = AccessVector(0);
136 pub const ALL: AccessVector = AccessVector(std::u32::MAX);
137
138 pub(super) fn from_class_permission_id(id: ClassPermissionId) -> Self {
139 Self((1 as u32) << (id.0.get() - 1))
140 }
141}
142
143impl Debug for AccessVector {
144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145 write!(f, "AccessVector({:0>8x})", self)
146 }
147}
148
149impl FromStr for AccessVector {
150 type Err = <u32 as FromStr>::Err;
151
152 fn from_str(value: &str) -> Result<Self, Self::Err> {
153 Ok(AccessVector(u32::from_str_radix(value, 16)?))
155 }
156}
157
158impl LowerHex for AccessVector {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 LowerHex::fmt(&self.0, f)
161 }
162}
163
164impl std::ops::BitAnd for AccessVector {
165 type Output = Self;
166
167 fn bitand(self, rhs: Self) -> Self::Output {
168 AccessVector(self.0 & rhs.0)
169 }
170}
171
172impl std::ops::BitOr for AccessVector {
173 type Output = Self;
174
175 fn bitor(self, rhs: Self) -> Self::Output {
176 AccessVector(self.0 | rhs.0)
177 }
178}
179
180impl std::ops::BitAndAssign for AccessVector {
181 fn bitand_assign(&mut self, rhs: Self) {
182 self.0 &= rhs.0
183 }
184}
185
186impl std::ops::BitOrAssign for AccessVector {
187 fn bitor_assign(&mut self, rhs: Self) {
188 self.0 |= rhs.0
189 }
190}
191
192impl std::ops::SubAssign for AccessVector {
193 fn sub_assign(&mut self, rhs: Self) {
194 self.0 = self.0 ^ (self.0 & rhs.0);
195 }
196}
197
198impl std::ops::Sub for AccessVector {
199 type Output = Self;
200
201 fn sub(self, rhs: Self) -> Self::Output {
202 AccessVector(self.0 ^ (self.0 & rhs.0))
203 }
204}
205
206#[derive(Clone, Debug, Eq, Hash, PartialEq)]
209pub enum XpermsKind {
210 Ioctl,
211 Nlmsg,
212}
213
214#[derive(Debug, Clone, PartialEq)]
219pub struct XpermsAccessDecision {
220 pub allow: XpermsBitmap,
221 pub auditallow: XpermsBitmap,
222 pub auditdeny: XpermsBitmap,
223}
224
225impl XpermsAccessDecision {
226 pub const DENY_ALL: Self = Self {
227 allow: XpermsBitmap::NONE,
228 auditallow: XpermsBitmap::NONE,
229 auditdeny: XpermsBitmap::ALL,
230 };
231 pub const ALLOW_ALL: Self = Self {
232 allow: XpermsBitmap::ALL,
233 auditallow: XpermsBitmap::NONE,
234 auditdeny: XpermsBitmap::ALL,
235 };
236}
237
238pub fn parse_policy_by_value(binary_policy: Vec<u8>) -> Result<Unvalidated, anyhow::Error> {
247 let policy_data = Arc::new(binary_policy);
248 let policy = ParsedPolicy::parse(policy_data).context("parsing policy")?;
249 Ok(Unvalidated(policy))
250}
251
252pub struct ClassInfo {
254 pub class_name: Box<[u8]>,
256 pub class_id: ClassId,
258}
259
260#[derive(Debug)]
261pub struct Policy(PolicyIndex);
262
263impl Policy {
264 pub fn policy_version(&self) -> u32 {
266 self.0.parsed_policy().policy_version()
267 }
268
269 pub fn binary(&self) -> &PolicyData {
270 &self.0.parsed_policy().data
271 }
272
273 pub fn handle_unknown(&self) -> HandleUnknown {
276 self.0.parsed_policy().handle_unknown()
277 }
278
279 pub fn conditional_booleans<'a>(&'a self) -> Vec<(&'a [u8], bool)> {
280 self.0
281 .parsed_policy()
282 .conditional_booleans()
283 .iter()
284 .map(|boolean| (boolean.data.as_slice(), boolean.metadata.active()))
285 .collect()
286 }
287
288 pub fn classes(&self) -> Vec<ClassInfo> {
290 self.0
291 .parsed_policy()
292 .classes()
293 .into_iter()
294 .map(|class| ClassInfo {
295 class_name: Box::<[u8]>::from(class.name_bytes()),
296 class_id: class.id(),
297 })
298 .collect()
299 }
300
301 pub(super) fn type_id_by_name(&self, name: &str) -> Option<TypeId> {
303 self.0.parsed_policy().type_id_by_name(name)
304 }
305
306 pub fn find_class_permissions_by_name(
311 &self,
312 class_name: &str,
313 ) -> Result<Vec<(ClassPermissionId, Vec<u8>)>, ()> {
314 let classes = self.0.parsed_policy().classes();
315 let class = find_class_by_name(&classes, class_name).ok_or(())?;
316 let owned_permissions = class.permissions();
317
318 let mut result: Vec<_> = owned_permissions
319 .iter()
320 .map(|permission| (permission.id(), permission.name_bytes().to_vec()))
321 .collect();
322
323 if class.common_name_bytes().is_empty() {
325 return Ok(result);
326 }
327
328 let common_symbol_permissions = find_common_symbol_by_name_bytes(
329 self.0.parsed_policy().common_symbols(),
330 class.common_name_bytes(),
331 )
332 .ok_or(())?
333 .permissions();
334
335 result.append(
336 &mut common_symbol_permissions
337 .iter()
338 .map(|permission| (permission.id(), permission.name_bytes().to_vec()))
339 .collect(),
340 );
341
342 Ok(result)
343 }
344
345 pub fn fs_use_label_and_type(&self, fs_type: NullessByteStr<'_>) -> Option<FsUseLabelAndType> {
348 self.0.fs_use_label_and_type(fs_type)
349 }
350
351 pub fn genfscon_label_for_fs_and_path(
354 &self,
355 fs_type: NullessByteStr<'_>,
356 node_path: NullessByteStr<'_>,
357 class_id: Option<KernelClass>,
358 ) -> Option<SecurityContext> {
359 self.0.genfscon_label_for_fs_and_path(fs_type, node_path, class_id)
360 }
361
362 pub fn initial_context(&self, id: crate::InitialSid) -> security_context::SecurityContext {
365 self.0.initial_context(id)
366 }
367
368 pub fn parse_security_context(
370 &self,
371 security_context: NullessByteStr<'_>,
372 ) -> Result<security_context::SecurityContext, security_context::SecurityContextError> {
373 security_context::SecurityContext::parse(&self.0, security_context)
374 }
375
376 pub fn validate_security_context(
378 &self,
379 security_context: &SecurityContext,
380 ) -> Result<(), SecurityContextError> {
381 security_context.validate(&self.0)
382 }
383
384 pub fn serialize_security_context(&self, security_context: &SecurityContext) -> Vec<u8> {
386 security_context.serialize(&self.0)
387 }
388
389 pub fn compute_create_context_with_name(
397 &self,
398 source: &SecurityContext,
399 target: &SecurityContext,
400 class: impl Into<ObjectClass>,
401 name: NullessByteStr<'_>,
402 ) -> Option<SecurityContext> {
403 self.0.compute_create_context_with_name(source, target, class.into(), name)
404 }
405
406 pub fn compute_create_context(
424 &self,
425 source: &SecurityContext,
426 target: &SecurityContext,
427 class: impl Into<ObjectClass>,
428 ) -> SecurityContext {
429 self.0.compute_create_context(source, target, class.into())
430 }
431
432 pub fn compute_access_decision(
442 &self,
443 source_context: &SecurityContext,
444 target_context: &SecurityContext,
445 object_class: impl Into<ObjectClass>,
446 ) -> AccessDecision {
447 if let Some(target_class) = self.0.class(object_class.into()) {
448 self.0.parsed_policy().compute_access_decision(
449 source_context,
450 target_context,
451 &target_class,
452 )
453 } else {
454 AccessDecision::allow(AccessVector::NONE)
455 }
456 }
457
458 pub fn compute_xperms_access_decision(
462 &self,
463 xperms_kind: XpermsKind,
464 source_context: &SecurityContext,
465 target_context: &SecurityContext,
466 object_class: impl Into<ObjectClass>,
467 xperms_prefix: u8,
468 ) -> XpermsAccessDecision {
469 if let Some(target_class) = self.0.class(object_class.into()) {
470 self.0.parsed_policy().compute_xperms_access_decision(
471 xperms_kind,
472 source_context,
473 target_context,
474 &target_class,
475 xperms_prefix,
476 )
477 } else {
478 XpermsAccessDecision::DENY_ALL
479 }
480 }
481
482 pub fn is_bounded_by(&self, bounded_type: TypeId, parent_type: TypeId) -> bool {
483 self.0.parsed_policy().type_(bounded_type).bounded_by() == Some(parent_type)
484 }
485
486 pub fn is_permissive(&self, type_: TypeId) -> bool {
488 self.0.parsed_policy().permissive_types().is_set(type_.0.get())
489 }
490
491 pub fn has_policycap(&self, policy_cap: PolicyCap) -> bool {
493 self.0.parsed_policy().has_policycap(policy_cap)
494 }
495}
496
497impl AccessVectorComputer for Policy {
498 fn kernel_permissions_to_access_vector<
499 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
500 >(
501 &self,
502 permissions: &[P],
503 ) -> Option<AccessVector> {
504 let mut access_vector = AccessVector::NONE;
505 for permission in permissions {
506 if let Some(permission_access_vector) =
507 self.0.kernel_permission_to_access_vector(permission.clone())
508 {
509 access_vector |= permission_access_vector;
510 } else {
511 if self.0.parsed_policy().handle_unknown() != HandleUnknown::Allow {
513 return None;
514 }
515 }
516 }
517 Some(access_vector)
518 }
519}
520
521pub struct Unvalidated(ParsedPolicy);
523
524impl Unvalidated {
525 pub fn validate(self) -> Result<Policy, anyhow::Error> {
526 self.0.validate().context("validating parsed policy")?;
527 let index = PolicyIndex::new(self.0).context("building index")?;
528 Ok(Policy(index))
529 }
530}
531
532pub trait AccessVectorComputer {
535 fn kernel_permissions_to_access_vector<
542 P: ClassPermission + Into<KernelPermission> + Clone + 'static,
543 >(
544 &self,
545 permissions: &[P],
546 ) -> Option<AccessVector>;
547}
548
549pub trait Parse: Sized {
551 type Error: Into<anyhow::Error>;
554
555 fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error>;
558}
559
560pub(super) trait ParseSlice: Sized {
562 type Error: Into<anyhow::Error>;
565
566 fn parse_slice<'a>(
569 bytes: PolicyCursor<'a>,
570 count: usize,
571 ) -> Result<(Self, PolicyCursor<'a>), Self::Error>;
572}
573
574pub(super) struct PolicyValidationContext {
576 #[allow(unused)]
578 pub(super) data: PolicyData,
579}
580
581pub(super) trait Validate {
583 type Error: Into<anyhow::Error>;
586
587 fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error>;
589}
590
591pub(super) trait ValidateArray<M, D> {
592 type Error: Into<anyhow::Error>;
595
596 fn validate_array(
598 context: &PolicyValidationContext,
599 metadata: &M,
600 items: &[D],
601 ) -> Result<(), Self::Error>;
602}
603
604pub(super) trait Counted {
606 fn count(&self) -> u32;
608}
609
610impl<T: Validate> Validate for Option<T> {
611 type Error = <T as Validate>::Error;
612
613 fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
614 match self {
615 Some(value) => value.validate(context),
616 None => Ok(()),
617 }
618 }
619}
620
621impl Validate for le::U32 {
622 type Error = anyhow::Error;
623
624 fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
627 Ok(())
628 }
629}
630
631impl Validate for u8 {
632 type Error = anyhow::Error;
633
634 fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
637 Ok(())
638 }
639}
640
641impl Validate for [u8] {
642 type Error = anyhow::Error;
643
644 fn validate(&self, _context: &PolicyValidationContext) -> Result<(), Self::Error> {
647 Ok(())
648 }
649}
650
651impl<B: SplitByteSlice, T: Validate + FromBytes + KnownLayout + Immutable> Validate for Ref<B, T> {
652 type Error = <T as Validate>::Error;
653
654 fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
655 self.deref().validate(context)
656 }
657}
658
659impl<B: SplitByteSlice, T: Counted + FromBytes + KnownLayout + Immutable> Counted for Ref<B, T> {
660 fn count(&self) -> u32 {
661 self.deref().count()
662 }
663}
664
665#[derive(Clone, Debug, PartialEq)]
668struct Array<M, D> {
669 metadata: M,
670 data: D,
671}
672
673impl<M: Counted + Parse, D: ParseSlice> Parse for Array<M, D> {
674 type Error = anyhow::Error;
677
678 fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
680 let tail = bytes;
681
682 let (metadata, tail) = M::parse(tail).map_err(Into::<anyhow::Error>::into)?;
683
684 let (data, tail) =
685 D::parse_slice(tail, metadata.count() as usize).map_err(Into::<anyhow::Error>::into)?;
686
687 let array = Self { metadata, data };
688
689 Ok((array, tail))
690 }
691}
692
693impl<T: Clone + Debug + FromBytes + KnownLayout + Immutable + PartialEq + Unaligned> Parse for T {
694 type Error = anyhow::Error;
695
696 fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
697 bytes.parse::<T>().map_err(anyhow::Error::from)
698 }
699}
700
701macro_rules! array_type {
705 ($type_name:ident, $metadata_type:ty, $data_type:ty, $metadata_type_name:expr, $data_type_name:expr) => {
706 #[doc = "An [`Array`] with [`"]
707 #[doc = $metadata_type_name]
708 #[doc = "`] metadata and [`"]
709 #[doc = $data_type_name]
710 #[doc = "`] data items."]
711 #[derive(Debug, PartialEq)]
712 pub(super) struct $type_name(super::Array<$metadata_type, $data_type>);
713
714 impl std::ops::Deref for $type_name {
715 type Target = super::Array<$metadata_type, $data_type>;
716
717 fn deref(&self) -> &Self::Target {
718 &self.0
719 }
720 }
721
722 impl super::Parse for $type_name
723 where
724 super::Array<$metadata_type, $data_type>: super::Parse,
725 {
726 type Error = <Array<$metadata_type, $data_type> as super::Parse>::Error;
727
728 fn parse<'a>(bytes: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
729 let (array, tail) = Array::<$metadata_type, $data_type>::parse(bytes)?;
730 Ok((Self(array), tail))
731 }
732 }
733 };
734
735 ($type_name:ident, $metadata_type:ty, $data_type:ty) => {
736 array_type!(
737 $type_name,
738 $metadata_type,
739 $data_type,
740 stringify!($metadata_type),
741 stringify!($data_type)
742 );
743 };
744}
745
746pub(super) use array_type;
747
748macro_rules! array_type_validate_deref_both {
749 ($type_name:ident) => {
750 impl Validate for $type_name {
751 type Error = anyhow::Error;
752
753 fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
754 let metadata = &self.metadata;
755 metadata.validate(context)?;
756
757 let items = &self.data;
758 items.validate(context)?;
759
760 Self::validate_array(context, metadata, items).map_err(Into::<anyhow::Error>::into)
761 }
762 }
763 };
764}
765
766pub(super) use array_type_validate_deref_both;
767
768macro_rules! array_type_validate_deref_data {
769 ($type_name:ident) => {
770 impl Validate for $type_name {
771 type Error = anyhow::Error;
772
773 fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
774 let metadata = &self.metadata;
775 metadata.validate(context)?;
776
777 let items = &self.data;
778 items.validate(context)?;
779
780 Self::validate_array(context, metadata, items)
781 }
782 }
783 };
784}
785
786pub(super) use array_type_validate_deref_data;
787
788macro_rules! array_type_validate_deref_metadata_data_vec {
789 ($type_name:ident) => {
790 impl Validate for $type_name {
791 type Error = anyhow::Error;
792
793 fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
794 let metadata = &self.metadata;
795 metadata.validate(context)?;
796
797 let items = &self.data;
798 items.validate(context)?;
799
800 Self::validate_array(context, metadata, items.as_slice())
801 }
802 }
803 };
804}
805
806pub(super) use array_type_validate_deref_metadata_data_vec;
807
808macro_rules! array_type_validate_deref_none_data_vec {
809 ($type_name:ident) => {
810 impl Validate for $type_name {
811 type Error = anyhow::Error;
812
813 fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
814 let metadata = &self.metadata;
815 metadata.validate(context)?;
816
817 let items = &self.data;
818 items.validate(context)?;
819
820 Self::validate_array(context, metadata, items.as_slice())
821 }
822 }
823 };
824}
825
826pub(super) use array_type_validate_deref_none_data_vec;
827
828impl<T: Parse> ParseSlice for Vec<T> {
829 type Error = anyhow::Error;
832
833 fn parse_slice<'a>(
835 bytes: PolicyCursor<'a>,
836 count: usize,
837 ) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
838 let mut slice = Vec::with_capacity(count);
839 let mut tail = bytes;
840
841 for _ in 0..count {
842 let (item, next_tail) = T::parse(tail).map_err(Into::<anyhow::Error>::into)?;
843 slice.push(item);
844 tail = next_tail;
845 }
846
847 Ok((slice, tail))
848 }
849}
850
851#[cfg(test)]
852pub(super) mod testing {
853 use super::AccessVector;
854 use super::error::{ParseError, ValidateError};
855
856 pub const ACCESS_VECTOR_0001: AccessVector = AccessVector(0b0001u32);
857 pub const ACCESS_VECTOR_0010: AccessVector = AccessVector(0b0010u32);
858
859 pub(super) fn as_parse_error(error: anyhow::Error) -> ParseError {
861 error.downcast::<ParseError>().expect("parse error")
862 }
863
864 pub(super) fn as_validate_error(error: anyhow::Error) -> ValidateError {
866 error.downcast::<ValidateError>().expect("validate error")
867 }
868}
869
870#[cfg(test)]
871pub(super) mod tests {
872 use super::arrays::XpermsBitmap;
873 use super::metadata::HandleUnknown;
874 use super::security_context::SecurityContext;
875 use super::symbols::find_class_by_name;
876 use super::{
877 AccessVector, Policy, TypeId, XpermsAccessDecision, XpermsKind, parse_policy_by_value,
878 };
879 use crate::{FileClass, InitialSid, KernelClass};
880
881 use anyhow::Context as _;
882 use serde::Deserialize;
883 use std::ops::{Deref, Shl};
884 use zerocopy::little_endian as le;
885
886 fn is_explicitly_allowed(
894 policy: &Policy,
895 source_type: TypeId,
896 target_type: TypeId,
897 target_class: &str,
898 permission: &str,
899 ) -> bool {
900 let classes = policy.0.parsed_policy().classes();
901 let class = classes
902 .iter()
903 .find(|class| class.name_bytes() == target_class.as_bytes())
904 .expect("class not found");
905 let class_permissions = policy
906 .find_class_permissions_by_name(target_class)
907 .expect("class permissions not found");
908 let (permission_id, _) = class_permissions
909 .iter()
910 .find(|(_, name)| permission.as_bytes() == name)
911 .expect("permission not found");
912 let permission_bit = AccessVector::from_class_permission_id(*permission_id);
913 let access_decision =
914 policy.0.parsed_policy().compute_explicitly_allowed(source_type, target_type, class);
915 permission_bit == access_decision.allow & permission_bit
916 }
917
918 #[derive(Debug, Deserialize)]
919 struct Expectations {
920 expected_policy_version: u32,
921 expected_handle_unknown: LocalHandleUnknown,
922 }
923
924 #[derive(Debug, Deserialize, PartialEq)]
925 #[serde(rename_all = "snake_case")]
926 enum LocalHandleUnknown {
927 Deny,
928 Reject,
929 Allow,
930 }
931
932 impl PartialEq<HandleUnknown> for LocalHandleUnknown {
933 fn eq(&self, other: &HandleUnknown) -> bool {
934 match self {
935 LocalHandleUnknown::Deny => *other == HandleUnknown::Deny,
936 LocalHandleUnknown::Reject => *other == HandleUnknown::Reject,
937 LocalHandleUnknown::Allow => *other == HandleUnknown::Allow,
938 }
939 }
940 }
941
942 fn xperms_bitmap_from_elements(elements: &[u8]) -> XpermsBitmap {
945 let mut bitmap = [le::U32::ZERO; 8];
946 for element in elements {
947 let block_index = (*element as usize) / 32;
948 let bit_index = ((*element as usize) % 32) as u32;
949 let bitmask = le::U32::new(1).shl(bit_index);
950 bitmap[block_index] = bitmap[block_index] | bitmask;
951 }
952 XpermsBitmap::new(bitmap)
953 }
954
955 #[test]
956 fn known_policies() {
957 let policies_and_expectations = [
958 [
959 b"testdata/policies/emulator".to_vec(),
960 include_bytes!("../../testdata/policies/emulator").to_vec(),
961 include_bytes!("../../testdata/expectations/emulator").to_vec(),
962 ],
963 [
964 b"testdata/policies/selinux_testsuite".to_vec(),
965 include_bytes!("../../testdata/policies/selinux_testsuite").to_vec(),
966 include_bytes!("../../testdata/expectations/selinux_testsuite").to_vec(),
967 ],
968 ];
969
970 for [policy_path, policy_bytes, expectations_bytes] in policies_and_expectations {
971 let expectations = serde_json5::from_reader::<_, Expectations>(
972 &mut std::io::Cursor::new(expectations_bytes),
973 )
974 .expect("deserialize expectations");
975
976 let unvalidated_policy =
979 parse_policy_by_value(policy_bytes.clone()).expect("parse policy");
980
981 let policy = unvalidated_policy
982 .validate()
983 .with_context(|| {
984 format!(
985 "policy path: {:?}",
986 std::str::from_utf8(policy_path.as_slice()).unwrap()
987 )
988 })
989 .expect("validate policy");
990
991 assert_eq!(expectations.expected_policy_version, policy.policy_version());
992 assert_eq!(expectations.expected_handle_unknown, policy.handle_unknown());
993
994 let binary_policy = policy.binary().clone();
996 assert_eq!(&policy_bytes, binary_policy.deref());
997 }
998 }
999
1000 #[test]
1001 fn policy_lookup() {
1002 let policy_bytes = include_bytes!("../../testdata/policies/selinux_testsuite");
1003 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1004 let policy = policy.validate().expect("validate selinux testsuite policy");
1005
1006 let unconfined_t = policy.type_id_by_name("unconfined_t").expect("look up type id");
1007
1008 assert!(is_explicitly_allowed(&policy, unconfined_t, unconfined_t, "process", "fork",));
1009 }
1010
1011 #[test]
1012 fn initial_contexts() {
1013 let policy_bytes = include_bytes!(
1014 "../../testdata/micro_policies/multiple_levels_and_categories_policy.pp"
1015 );
1016 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1017 let policy = policy.validate().expect("validate policy");
1018
1019 let kernel_context = policy.initial_context(InitialSid::Kernel);
1020 assert_eq!(
1021 policy.serialize_security_context(&kernel_context),
1022 b"user0:object_r:type0:s0:c0-s1:c0.c2,c4"
1023 )
1024 }
1025
1026 #[test]
1027 fn explicit_allow_type_type() {
1028 let policy_bytes =
1029 include_bytes!("../../testdata/micro_policies/allow_a_t_b_t_class0_perm0_policy.pp");
1030 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1031 let policy = policy.validate().expect("validate policy");
1032
1033 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1034 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1035
1036 assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1037 }
1038
1039 #[test]
1040 fn no_explicit_allow_type_type() {
1041 let policy_bytes =
1042 include_bytes!("../../testdata/micro_policies/no_allow_a_t_b_t_class0_perm0_policy.pp");
1043 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1044 let policy = policy.validate().expect("validate policy");
1045
1046 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1047 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1048
1049 assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1050 }
1051
1052 #[test]
1053 fn explicit_allow_type_attr() {
1054 let policy_bytes =
1055 include_bytes!("../../testdata/micro_policies/allow_a_t_b_attr_class0_perm0_policy.pp");
1056 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1057 let policy = policy.validate().expect("validate policy");
1058
1059 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1060 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1061
1062 assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1063 }
1064
1065 #[test]
1066 fn no_explicit_allow_type_attr() {
1067 let policy_bytes = include_bytes!(
1068 "../../testdata/micro_policies/no_allow_a_t_b_attr_class0_perm0_policy.pp"
1069 );
1070 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1071 let policy = policy.validate().expect("validate policy");
1072
1073 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1074 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1075
1076 assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1077 }
1078
1079 #[test]
1080 fn explicit_allow_attr_attr() {
1081 let policy_bytes = include_bytes!(
1082 "../../testdata/micro_policies/allow_a_attr_b_attr_class0_perm0_policy.pp"
1083 );
1084 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1085 let policy = policy.validate().expect("validate policy");
1086
1087 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1088 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1089
1090 assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1091 }
1092
1093 #[test]
1094 fn no_explicit_allow_attr_attr() {
1095 let policy_bytes = include_bytes!(
1096 "../../testdata/micro_policies/no_allow_a_attr_b_attr_class0_perm0_policy.pp"
1097 );
1098 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1099 let policy = policy.validate().expect("validate policy");
1100
1101 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1102 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1103
1104 assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1105 }
1106
1107 #[test]
1108 fn compute_explicitly_allowed_multiple_attributes() {
1109 let policy_bytes = include_bytes!(
1110 "../../testdata/micro_policies/allow_a_t_a1_attr_class0_perm0_a2_attr_class0_perm1_policy.pp"
1111 );
1112 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1113 let policy = policy.validate().expect("validate policy");
1114
1115 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1116
1117 let classes = policy.0.parsed_policy().classes();
1118 let class =
1119 classes.iter().find(|class| class.name_bytes() == b"class0").expect("class not found");
1120 let raw_access_vector =
1121 policy.0.parsed_policy().compute_explicitly_allowed(a_t, a_t, class).allow.0;
1122
1123 assert_eq!(2, raw_access_vector.count_ones());
1128 }
1129
1130 #[test]
1131 fn compute_access_decision_with_constraints() {
1132 let policy_bytes =
1133 include_bytes!("../../testdata/micro_policies/allow_with_constraints_policy.pp");
1134 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1135 let policy = policy.validate().expect("validate policy");
1136
1137 let source_context: SecurityContext = policy
1138 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1139 .expect("create source security context");
1140
1141 let target_context_satisfied: SecurityContext = source_context.clone();
1142 let decision_satisfied = policy.compute_access_decision(
1143 &source_context,
1144 &target_context_satisfied,
1145 KernelClass::File,
1146 );
1147 assert_eq!(decision_satisfied.allow, AccessVector(7));
1151
1152 let target_context_unsatisfied: SecurityContext = policy
1153 .parse_security_context(b"user1:object_r:type0:s0:c0-s0:c0".into())
1154 .expect("create target security context failing some constraints");
1155 let decision_unsatisfied = policy.compute_access_decision(
1156 &source_context,
1157 &target_context_unsatisfied,
1158 KernelClass::File,
1159 );
1160 assert_eq!(decision_unsatisfied.allow, AccessVector(4));
1163 }
1164
1165 #[test]
1166 fn compute_ioctl_access_decision_explicitly_allowed() {
1167 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1168 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1169 let policy = policy.validate().expect("validate policy");
1170
1171 let source_context: SecurityContext = policy
1172 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1173 .expect("create source security context");
1174 let target_context_matched: SecurityContext = source_context.clone();
1175
1176 let decision_single = policy.compute_xperms_access_decision(
1194 XpermsKind::Ioctl,
1195 &source_context,
1196 &target_context_matched,
1197 KernelClass::File,
1198 0xab,
1199 );
1200
1201 let mut expected_auditdeny =
1202 xperms_bitmap_from_elements((0x0..=0xff).collect::<Vec<_>>().as_slice());
1203 expected_auditdeny -= &xperms_bitmap_from_elements(&[0xcd, 0xef]);
1204
1205 let expected_decision_single = XpermsAccessDecision {
1206 allow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1207 auditallow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1208 auditdeny: expected_auditdeny,
1209 };
1210 assert_eq!(decision_single, expected_decision_single);
1211
1212 let decision_range = policy.compute_xperms_access_decision(
1213 XpermsKind::Ioctl,
1214 &source_context,
1215 &target_context_matched,
1216 KernelClass::File,
1217 0x10,
1218 );
1219 let expected_decision_range = XpermsAccessDecision {
1220 allow: XpermsBitmap::ALL,
1221 auditallow: XpermsBitmap::ALL,
1222 auditdeny: XpermsBitmap::NONE,
1223 };
1224 assert_eq!(decision_range, expected_decision_range);
1225 }
1226
1227 #[test]
1228 fn compute_ioctl_access_decision_denied() {
1229 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1230 let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1231 let class_id = find_class_by_name(&unvalidated.0.classes(), "class_one_ioctl")
1232 .expect("look up class_one_ioctl")
1233 .id();
1234 let policy = unvalidated.validate().expect("validate policy");
1235 let source_context: SecurityContext = policy
1236 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1237 .expect("create source security context");
1238 let target_context_matched: SecurityContext = source_context.clone();
1239
1240 let decision_single = policy.compute_xperms_access_decision(
1244 XpermsKind::Ioctl,
1245 &source_context,
1246 &target_context_matched,
1247 class_id,
1248 0xdb,
1249 );
1250
1251 let expected_decision = XpermsAccessDecision {
1252 allow: XpermsBitmap::NONE,
1253 auditallow: XpermsBitmap::NONE,
1254 auditdeny: XpermsBitmap::ALL,
1255 };
1256 assert_eq!(decision_single, expected_decision);
1257 }
1258
1259 #[test]
1260 fn compute_ioctl_access_decision_unmatched() {
1261 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1262 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1263 let policy = policy.validate().expect("validate policy");
1264
1265 let source_context: SecurityContext = policy
1266 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1267 .expect("create source security context");
1268
1269 let target_context_unmatched: SecurityContext = policy
1271 .parse_security_context(b"user0:object_r:type1:s0-s0".into())
1272 .expect("create source security context");
1273
1274 for prefix in 0x0..=0xff {
1275 let decision = policy.compute_xperms_access_decision(
1276 XpermsKind::Ioctl,
1277 &source_context,
1278 &target_context_unmatched,
1279 KernelClass::File,
1280 prefix,
1281 );
1282 assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1283 }
1284 }
1285
1286 #[test]
1287 fn compute_ioctl_earlier_redundant_prefixful_not_coalesced_into_prefixless() {
1288 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1289 let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1290 let class_id = find_class_by_name(
1291 &unvalidated.0.classes(),
1292 "class_earlier_redundant_prefixful_not_coalesced_into_prefixless",
1293 )
1294 .expect("look up class_earlier_redundant_prefixful_not_coalesced_into_prefixless")
1295 .id();
1296 let policy = unvalidated.validate().expect("validate policy");
1297 let source_context: SecurityContext = policy
1298 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1299 .expect("create source security context");
1300 let target_context_matched: SecurityContext = source_context.clone();
1301
1302 let decision = policy.compute_xperms_access_decision(
1307 XpermsKind::Ioctl,
1308 &source_context,
1309 &target_context_matched,
1310 class_id,
1311 0x7f,
1312 );
1313 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1314 let decision = policy.compute_xperms_access_decision(
1315 XpermsKind::Ioctl,
1316 &source_context,
1317 &target_context_matched,
1318 class_id,
1319 0x80,
1320 );
1321 assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1322 let decision = policy.compute_xperms_access_decision(
1323 XpermsKind::Ioctl,
1324 &source_context,
1325 &target_context_matched,
1326 class_id,
1327 0x81,
1328 );
1329 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1330 }
1331
1332 #[test]
1333 fn compute_ioctl_later_redundant_prefixful_not_coalesced_into_prefixless() {
1334 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1335 let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1336 let class_id = find_class_by_name(
1337 &unvalidated.0.classes(),
1338 "class_later_redundant_prefixful_not_coalesced_into_prefixless",
1339 )
1340 .expect("look up class_later_redundant_prefixful_not_coalesced_into_prefixless")
1341 .id();
1342 let policy = unvalidated.validate().expect("validate policy");
1343 let source_context: SecurityContext = policy
1344 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1345 .expect("create source security context");
1346 let target_context_matched: SecurityContext = source_context.clone();
1347
1348 let decision = policy.compute_xperms_access_decision(
1353 XpermsKind::Ioctl,
1354 &source_context,
1355 &target_context_matched,
1356 class_id,
1357 0x8f,
1358 );
1359 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1360 let decision = policy.compute_xperms_access_decision(
1361 XpermsKind::Ioctl,
1362 &source_context,
1363 &target_context_matched,
1364 class_id,
1365 0x90,
1366 );
1367 assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1368 let decision = policy.compute_xperms_access_decision(
1369 XpermsKind::Ioctl,
1370 &source_context,
1371 &target_context_matched,
1372 class_id,
1373 0x91,
1374 );
1375 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1376 }
1377
1378 #[test]
1379 fn compute_ioctl_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless() {
1380 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1381 let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1382 let class_id = find_class_by_name(
1383 &unvalidated.0.classes(),
1384 "class_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless",
1385 )
1386 .expect("look up class_earlier_and_later_redundant_prefixful_not_coalesced_into_prefixless")
1387 .id();
1388 let policy = unvalidated.validate().expect("validate policy");
1389 let source_context: SecurityContext = policy
1390 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1391 .expect("create source security context");
1392 let target_context_matched: SecurityContext = source_context.clone();
1393
1394 let decision = policy.compute_xperms_access_decision(
1400 XpermsKind::Ioctl,
1401 &source_context,
1402 &target_context_matched,
1403 class_id,
1404 0x9f,
1405 );
1406 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1407 let decision = policy.compute_xperms_access_decision(
1408 XpermsKind::Ioctl,
1409 &source_context,
1410 &target_context_matched,
1411 class_id,
1412 0xa0,
1413 );
1414 assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1415 let decision = policy.compute_xperms_access_decision(
1416 XpermsKind::Ioctl,
1417 &source_context,
1418 &target_context_matched,
1419 class_id,
1420 0xa1,
1421 );
1422 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1423 }
1424
1425 #[test]
1426 fn compute_ioctl_prefixfuls_that_coalesce_to_prefixless() {
1427 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1428 let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1429 let class_id = find_class_by_name(
1430 &unvalidated.0.classes(),
1431 "class_prefixfuls_that_coalesce_to_prefixless",
1432 )
1433 .expect("look up class_prefixfuls_that_coalesce_to_prefixless")
1434 .id();
1435 let policy = unvalidated.validate().expect("validate policy");
1436 let source_context: SecurityContext = policy
1437 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1438 .expect("create source security context");
1439 let target_context_matched: SecurityContext = source_context.clone();
1440
1441 let decision = policy.compute_xperms_access_decision(
1447 XpermsKind::Ioctl,
1448 &source_context,
1449 &target_context_matched,
1450 class_id,
1451 0xaf,
1452 );
1453 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1454 let decision = policy.compute_xperms_access_decision(
1455 XpermsKind::Ioctl,
1456 &source_context,
1457 &target_context_matched,
1458 class_id,
1459 0xb0,
1460 );
1461 assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1462 let decision = policy.compute_xperms_access_decision(
1463 XpermsKind::Ioctl,
1464 &source_context,
1465 &target_context_matched,
1466 class_id,
1467 0xb1,
1468 );
1469 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1470 }
1471
1472 #[test]
1473 fn compute_ioctl_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless() {
1474 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1475 let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1476 let class_id = find_class_by_name(
1477 &unvalidated.0.classes(),
1478 "class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless",
1479 )
1480 .expect("look up class_prefixfuls_that_coalesce_to_prefixless_just_before_prefixless")
1481 .id();
1482 let policy = unvalidated.validate().expect("validate policy");
1483 let source_context: SecurityContext = policy
1484 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1485 .expect("create source security context");
1486 let target_context_matched: SecurityContext = source_context.clone();
1487
1488 let decision = policy.compute_xperms_access_decision(
1495 XpermsKind::Ioctl,
1496 &source_context,
1497 &target_context_matched,
1498 class_id,
1499 0xbf,
1500 );
1501 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1502 let decision = policy.compute_xperms_access_decision(
1503 XpermsKind::Ioctl,
1504 &source_context,
1505 &target_context_matched,
1506 class_id,
1507 0xc0,
1508 );
1509 assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1510 let decision = policy.compute_xperms_access_decision(
1511 XpermsKind::Ioctl,
1512 &source_context,
1513 &target_context_matched,
1514 class_id,
1515 0xc1,
1516 );
1517 assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1518 let decision = policy.compute_xperms_access_decision(
1519 XpermsKind::Ioctl,
1520 &source_context,
1521 &target_context_matched,
1522 class_id,
1523 0xc2,
1524 );
1525 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1526 }
1527
1528 #[test]
1529 fn compute_ioctl_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless() {
1530 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1531 let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1532 let class_id = find_class_by_name(
1533 &unvalidated.0.classes(),
1534 "class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless",
1535 )
1536 .expect("look up class_prefixless_just_before_prefixfuls_that_coalesce_to_prefixless")
1537 .id();
1538 let policy = unvalidated.validate().expect("validate policy");
1539 let source_context: SecurityContext = policy
1540 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1541 .expect("create source security context");
1542 let target_context_matched: SecurityContext = source_context.clone();
1543
1544 let decision = policy.compute_xperms_access_decision(
1551 XpermsKind::Ioctl,
1552 &source_context,
1553 &target_context_matched,
1554 class_id,
1555 0xd5,
1556 );
1557 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1558 let decision = policy.compute_xperms_access_decision(
1559 XpermsKind::Ioctl,
1560 &source_context,
1561 &target_context_matched,
1562 class_id,
1563 0xd6,
1564 );
1565 assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1566 let decision = policy.compute_xperms_access_decision(
1567 XpermsKind::Ioctl,
1568 &source_context,
1569 &target_context_matched,
1570 class_id,
1571 0xd7,
1572 );
1573 assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1574 let decision = policy.compute_xperms_access_decision(
1575 XpermsKind::Ioctl,
1576 &source_context,
1577 &target_context_matched,
1578 class_id,
1579 0xd8,
1580 );
1581 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1582 }
1583
1584 #[test]
1595 fn compute_ioctl_ridiculous_permission_ordering() {
1596 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1597 let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1598 let class_id =
1599 find_class_by_name(&unvalidated.0.classes(), "class_ridiculous_permission_ordering")
1600 .expect("look up class_ridiculous_permission_ordering")
1601 .id();
1602 let policy = unvalidated.validate().expect("validate policy");
1603 let source_context: SecurityContext = policy
1604 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1605 .expect("create source security context");
1606 let target_context_matched: SecurityContext = source_context.clone();
1607
1608 let decision = policy.compute_xperms_access_decision(
1613 XpermsKind::Ioctl,
1614 &source_context,
1615 &target_context_matched,
1616 class_id,
1617 0x00,
1618 );
1619 assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1620 let decision = policy.compute_xperms_access_decision(
1621 XpermsKind::Ioctl,
1622 &source_context,
1623 &target_context_matched,
1624 class_id,
1625 0x01,
1626 );
1627 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1628 let decision = policy.compute_xperms_access_decision(
1629 XpermsKind::Ioctl,
1630 &source_context,
1631 &target_context_matched,
1632 class_id,
1633 0xbf,
1634 );
1635 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1636 let decision = policy.compute_xperms_access_decision(
1637 XpermsKind::Ioctl,
1638 &source_context,
1639 &target_context_matched,
1640 class_id,
1641 0xc0,
1642 );
1643 assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1644 let decision = policy.compute_xperms_access_decision(
1645 XpermsKind::Ioctl,
1646 &source_context,
1647 &target_context_matched,
1648 class_id,
1649 0xce,
1650 );
1651 assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1652 let decision = policy.compute_xperms_access_decision(
1653 XpermsKind::Ioctl,
1654 &source_context,
1655 &target_context_matched,
1656 class_id,
1657 0xcf,
1658 );
1659 assert_eq!(
1660 decision,
1661 XpermsAccessDecision {
1662 allow: xperms_bitmap_from_elements((0x0..=0xf2).collect::<Vec<_>>().as_slice()),
1663 auditallow: XpermsBitmap::NONE,
1664 auditdeny: XpermsBitmap::ALL,
1665 }
1666 );
1667 let decision = policy.compute_xperms_access_decision(
1668 XpermsKind::Ioctl,
1669 &source_context,
1670 &target_context_matched,
1671 class_id,
1672 0xd0,
1673 );
1674 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1675 let decision = policy.compute_xperms_access_decision(
1676 XpermsKind::Ioctl,
1677 &source_context,
1678 &target_context_matched,
1679 class_id,
1680 0xe9,
1681 );
1682 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1683 let decision = policy.compute_xperms_access_decision(
1684 XpermsKind::Ioctl,
1685 &source_context,
1686 &target_context_matched,
1687 class_id,
1688 0xf0,
1689 );
1690 assert_eq!(
1691 decision,
1692 XpermsAccessDecision {
1693 allow: xperms_bitmap_from_elements(&[0x01]),
1694 auditallow: XpermsBitmap::NONE,
1695 auditdeny: XpermsBitmap::ALL,
1696 }
1697 );
1698 let decision = policy.compute_xperms_access_decision(
1699 XpermsKind::Ioctl,
1700 &source_context,
1701 &target_context_matched,
1702 class_id,
1703 0xf1,
1704 );
1705 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1706 let decision = policy.compute_xperms_access_decision(
1707 XpermsKind::Ioctl,
1708 &source_context,
1709 &target_context_matched,
1710 class_id,
1711 0xfc,
1712 );
1713 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1714 let decision = policy.compute_xperms_access_decision(
1715 XpermsKind::Ioctl,
1716 &source_context,
1717 &target_context_matched,
1718 class_id,
1719 0xfd,
1720 );
1721 assert_eq!(
1722 decision,
1723 XpermsAccessDecision {
1724 allow: xperms_bitmap_from_elements((0xfa..=0xfd).collect::<Vec<_>>().as_slice()),
1725 auditallow: XpermsBitmap::NONE,
1726 auditdeny: XpermsBitmap::ALL,
1727 }
1728 );
1729 let decision = policy.compute_xperms_access_decision(
1730 XpermsKind::Ioctl,
1731 &source_context,
1732 &target_context_matched,
1733 class_id,
1734 0xfe,
1735 );
1736 assert_eq!(decision, XpermsAccessDecision::DENY_ALL);
1737 }
1738
1739 #[test]
1740 fn compute_nlmsg_access_decision_explicitly_allowed() {
1741 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1742 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1743 let policy = policy.validate().expect("validate policy");
1744
1745 let source_context: SecurityContext = policy
1746 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1747 .expect("create source security context");
1748 let target_context_matched: SecurityContext = source_context.clone();
1749
1750 let decision_single = policy.compute_xperms_access_decision(
1768 XpermsKind::Nlmsg,
1769 &source_context,
1770 &target_context_matched,
1771 KernelClass::NetlinkRouteSocket,
1772 0xab,
1773 );
1774
1775 let mut expected_auditdeny =
1776 xperms_bitmap_from_elements((0x0..=0xff).collect::<Vec<_>>().as_slice());
1777 expected_auditdeny -= &xperms_bitmap_from_elements(&[0xcd, 0xef]);
1778
1779 let expected_decision_single = XpermsAccessDecision {
1780 allow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1781 auditallow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1782 auditdeny: expected_auditdeny,
1783 };
1784 assert_eq!(decision_single, expected_decision_single);
1785
1786 let decision_range = policy.compute_xperms_access_decision(
1787 XpermsKind::Nlmsg,
1788 &source_context,
1789 &target_context_matched,
1790 KernelClass::NetlinkRouteSocket,
1791 0x10,
1792 );
1793 let expected_decision_range = XpermsAccessDecision {
1794 allow: XpermsBitmap::ALL,
1795 auditallow: XpermsBitmap::ALL,
1796 auditdeny: XpermsBitmap::NONE,
1797 };
1798 assert_eq!(decision_range, expected_decision_range);
1799 }
1800
1801 #[test]
1802 fn compute_nlmsg_access_decision_unmatched() {
1803 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1804 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1805 let policy = policy.validate().expect("validate policy");
1806
1807 let source_context: SecurityContext = policy
1808 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1809 .expect("create source security context");
1810
1811 let target_context_unmatched: SecurityContext = policy
1813 .parse_security_context(b"user0:object_r:type1:s0-s0".into())
1814 .expect("create source security context");
1815
1816 for prefix in 0x0..=0xff {
1817 let decision = policy.compute_xperms_access_decision(
1818 XpermsKind::Nlmsg,
1819 &source_context,
1820 &target_context_unmatched,
1821 KernelClass::NetlinkRouteSocket,
1822 prefix,
1823 );
1824 assert_eq!(decision, XpermsAccessDecision::ALLOW_ALL);
1825 }
1826 }
1827
1828 #[test]
1829 fn compute_ioctl_grant_does_not_cause_nlmsg_deny() {
1830 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1831 let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1832 let class_id = find_class_by_name(
1833 &unvalidated.0.classes(),
1834 "class_ioctl_grant_does_not_cause_nlmsg_deny",
1835 )
1836 .expect("look up class_ioctl_grant_does_not_cause_nlmsg_deny")
1837 .id();
1838 let policy = unvalidated.validate().expect("validate policy");
1839 let source_context: SecurityContext = policy
1840 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1841 .expect("create source security context");
1842 let target_context_matched: SecurityContext = source_context.clone();
1843
1844 let ioctl_decision = policy.compute_xperms_access_decision(
1848 XpermsKind::Ioctl,
1849 &source_context,
1850 &target_context_matched,
1851 class_id,
1852 0x00,
1853 );
1854 assert_eq!(
1855 ioctl_decision,
1856 XpermsAccessDecision {
1857 allow: xperms_bitmap_from_elements(&[0x0002]),
1858 auditallow: XpermsBitmap::NONE,
1859 auditdeny: XpermsBitmap::ALL,
1860 }
1861 );
1862 let nlmsg_decision = policy.compute_xperms_access_decision(
1863 XpermsKind::Nlmsg,
1864 &source_context,
1865 &target_context_matched,
1866 class_id,
1867 0x00,
1868 );
1869 assert_eq!(nlmsg_decision, XpermsAccessDecision::ALLOW_ALL);
1870 }
1871
1872 #[test]
1873 fn compute_nlmsg_grant_does_not_cause_ioctl_deny() {
1874 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1875 let unvalidated = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1876 let class_id = find_class_by_name(
1877 &unvalidated.0.classes(),
1878 "class_nlmsg_grant_does_not_cause_ioctl_deny",
1879 )
1880 .expect("look up class_nlmsg_grant_does_not_cause_ioctl_deny")
1881 .id();
1882 let policy = unvalidated.validate().expect("validate policy");
1883 let source_context: SecurityContext = policy
1884 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1885 .expect("create source security context");
1886 let target_context_matched: SecurityContext = source_context.clone();
1887
1888 let nlmsg_decision = policy.compute_xperms_access_decision(
1892 XpermsKind::Nlmsg,
1893 &source_context,
1894 &target_context_matched,
1895 class_id,
1896 0x00,
1897 );
1898 assert_eq!(
1899 nlmsg_decision,
1900 XpermsAccessDecision {
1901 allow: xperms_bitmap_from_elements(&[0x0003]),
1902 auditallow: XpermsBitmap::NONE,
1903 auditdeny: XpermsBitmap::ALL,
1904 }
1905 );
1906 let ioctl_decision = policy.compute_xperms_access_decision(
1907 XpermsKind::Ioctl,
1908 &source_context,
1909 &target_context_matched,
1910 class_id,
1911 0x00,
1912 );
1913 assert_eq!(ioctl_decision, XpermsAccessDecision::ALLOW_ALL);
1914 }
1915
1916 #[test]
1917 fn compute_create_context_minimal() {
1918 let policy_bytes =
1919 include_bytes!("../../testdata/composite_policies/compiled/minimal_policy.pp");
1920 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1921 let policy = policy.validate().expect("validate policy");
1922 let source = policy
1923 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1924 .expect("valid source security context");
1925 let target = policy
1926 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1927 .expect("valid target security context");
1928
1929 let actual = policy.compute_create_context(&source, &target, FileClass::File);
1930 let expected: SecurityContext = policy
1931 .parse_security_context(b"source_u:object_r:target_t:s0:c0".into())
1932 .expect("valid expected security context");
1933
1934 assert_eq!(expected, actual);
1935 }
1936
1937 #[test]
1938 fn new_security_context_minimal() {
1939 let policy_bytes =
1940 include_bytes!("../../testdata/composite_policies/compiled/minimal_policy.pp");
1941 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1942 let policy = policy.validate().expect("validate policy");
1943 let source = policy
1944 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1945 .expect("valid source security context");
1946 let target = policy
1947 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1948 .expect("valid target security context");
1949
1950 let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1951
1952 assert_eq!(source, actual);
1953 }
1954
1955 #[test]
1956 fn compute_create_context_class_defaults() {
1957 let policy_bytes =
1958 include_bytes!("../../testdata/composite_policies/compiled/class_defaults_policy.pp");
1959 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1960 let policy = policy.validate().expect("validate policy");
1961 let source = policy
1962 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1963 .expect("valid source security context");
1964 let target = policy
1965 .parse_security_context(b"target_u:target_r:target_t:s1:c0-s1:c0.c1".into())
1966 .expect("valid target security context");
1967
1968 let actual = policy.compute_create_context(&source, &target, FileClass::File);
1969 let expected: SecurityContext = policy
1970 .parse_security_context(b"target_u:source_r:source_t:s1:c0-s1:c0.c1".into())
1971 .expect("valid expected security context");
1972
1973 assert_eq!(expected, actual);
1974 }
1975
1976 #[test]
1977 fn new_security_context_class_defaults() {
1978 let policy_bytes =
1979 include_bytes!("../../testdata/composite_policies/compiled/class_defaults_policy.pp");
1980 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1981 let policy = policy.validate().expect("validate policy");
1982 let source = policy
1983 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1984 .expect("valid source security context");
1985 let target = policy
1986 .parse_security_context(b"target_u:target_r:target_t:s1:c0-s1:c0.c1".into())
1987 .expect("valid target security context");
1988
1989 let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1990 let expected: SecurityContext = policy
1991 .parse_security_context(b"target_u:source_r:source_t:s1:c0-s1:c0.c1".into())
1992 .expect("valid expected security context");
1993
1994 assert_eq!(expected, actual);
1995 }
1996
1997 #[test]
1998 fn compute_create_context_role_transition() {
1999 let policy_bytes =
2000 include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy.pp");
2001 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2002 let policy = policy.validate().expect("validate policy");
2003 let source = policy
2004 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
2005 .expect("valid source security context");
2006 let target = policy
2007 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
2008 .expect("valid target security context");
2009
2010 let actual = policy.compute_create_context(&source, &target, FileClass::File);
2011 let expected: SecurityContext = policy
2012 .parse_security_context(b"source_u:transition_r:target_t:s0:c0".into())
2013 .expect("valid expected security context");
2014
2015 assert_eq!(expected, actual);
2016 }
2017
2018 #[test]
2019 fn new_security_context_role_transition() {
2020 let policy_bytes =
2021 include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy.pp");
2022 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2023 let policy = policy.validate().expect("validate policy");
2024 let source = policy
2025 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
2026 .expect("valid source security context");
2027 let target = policy
2028 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
2029 .expect("valid target security context");
2030
2031 let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
2032 let expected: SecurityContext = policy
2033 .parse_security_context(b"source_u:transition_r:source_t:s0:c0-s2:c0.c1".into())
2034 .expect("valid expected security context");
2035
2036 assert_eq!(expected, actual);
2037 }
2038
2039 #[test]
2040 #[ignore]
2042 fn compute_create_context_role_transition_not_allowed() {
2043 let policy_bytes = include_bytes!(
2044 "../../testdata/composite_policies/compiled/role_transition_not_allowed_policy.pp"
2045 );
2046 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2047 let policy = policy.validate().expect("validate policy");
2048 let source = policy
2049 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
2050 .expect("valid source security context");
2051 let target = policy
2052 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
2053 .expect("valid target security context");
2054
2055 let actual = policy.compute_create_context(&source, &target, FileClass::File);
2056
2057 assert!(policy.validate_security_context(&actual).is_err());
2059 }
2060
2061 #[test]
2062 fn compute_create_context_type_transition() {
2063 let policy_bytes =
2064 include_bytes!("../../testdata/composite_policies/compiled/type_transition_policy.pp");
2065 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2066 let policy = policy.validate().expect("validate policy");
2067 let source = policy
2068 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
2069 .expect("valid source security context");
2070 let target = policy
2071 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
2072 .expect("valid target security context");
2073
2074 let actual = policy.compute_create_context(&source, &target, FileClass::File);
2075 let expected: SecurityContext = policy
2076 .parse_security_context(b"source_u:object_r:transition_t:s0:c0".into())
2077 .expect("valid expected security context");
2078
2079 assert_eq!(expected, actual);
2080 }
2081
2082 #[test]
2083 fn new_security_context_type_transition() {
2084 let policy_bytes =
2085 include_bytes!("../../testdata/composite_policies/compiled/type_transition_policy.pp");
2086 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2087 let policy = policy.validate().expect("validate policy");
2088 let source = policy
2089 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
2090 .expect("valid source security context");
2091 let target = policy
2092 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
2093 .expect("valid target security context");
2094
2095 let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
2096 let expected: SecurityContext = policy
2097 .parse_security_context(b"source_u:source_r:transition_t:s0:c0-s2:c0.c1".into())
2098 .expect("valid expected security context");
2099
2100 assert_eq!(expected, actual);
2101 }
2102
2103 #[test]
2104 fn compute_create_context_range_transition() {
2105 let policy_bytes =
2106 include_bytes!("../../testdata/composite_policies/compiled/range_transition_policy.pp");
2107 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2108 let policy = policy.validate().expect("validate policy");
2109 let source = policy
2110 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
2111 .expect("valid source security context");
2112 let target = policy
2113 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
2114 .expect("valid target security context");
2115
2116 let actual = policy.compute_create_context(&source, &target, FileClass::File);
2117 let expected: SecurityContext = policy
2118 .parse_security_context(b"source_u:object_r:target_t:s1:c1-s2:c1.c2".into())
2119 .expect("valid expected security context");
2120
2121 assert_eq!(expected, actual);
2122 }
2123
2124 #[test]
2125 fn new_security_context_range_transition() {
2126 let policy_bytes =
2127 include_bytes!("../../testdata/composite_policies/compiled/range_transition_policy.pp");
2128 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2129 let policy = policy.validate().expect("validate policy");
2130 let source = policy
2131 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
2132 .expect("valid source security context");
2133 let target = policy
2134 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
2135 .expect("valid target security context");
2136
2137 let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
2138 let expected: SecurityContext = policy
2139 .parse_security_context(b"source_u:source_r:source_t:s1:c1-s2:c1.c2".into())
2140 .expect("valid expected security context");
2141
2142 assert_eq!(expected, actual);
2143 }
2144
2145 #[test]
2146 fn access_vector_formats() {
2147 assert_eq!(format!("{:x}", AccessVector::NONE), "0");
2148 assert_eq!(format!("{:x}", AccessVector::ALL), "ffffffff");
2149 assert_eq!(format!("{:?}", AccessVector::NONE), "AccessVector(00000000)");
2150 assert_eq!(format!("{:?}", AccessVector::ALL), "AccessVector(ffffffff)");
2151 }
2152
2153 #[test]
2154 fn policy_genfscon_root_path() {
2155 let policy_bytes =
2156 include_bytes!("../../testdata/composite_policies/compiled/genfscon_policy.pp");
2157 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2158 let policy = policy.validate().expect("validate selinux policy");
2159
2160 {
2161 let context = policy.genfscon_label_for_fs_and_path(
2162 "fs_with_path_rules".into(),
2163 "/".into(),
2164 None,
2165 );
2166 assert_eq!(
2167 policy.serialize_security_context(&context.unwrap()),
2168 b"system_u:object_r:fs_with_path_rules_t:s0"
2169 )
2170 }
2171 {
2172 let context = policy.genfscon_label_for_fs_and_path(
2173 "fs_2_with_path_rules".into(),
2174 "/".into(),
2175 None,
2176 );
2177 assert_eq!(
2178 policy.serialize_security_context(&context.unwrap()),
2179 b"system_u:object_r:fs_2_with_path_rules_t:s0"
2180 )
2181 }
2182 }
2183
2184 #[test]
2185 fn policy_genfscon_subpaths() {
2186 let policy_bytes =
2187 include_bytes!("../../testdata/composite_policies/compiled/genfscon_policy.pp");
2188 let policy = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
2189 let policy = policy.validate().expect("validate selinux policy");
2190
2191 let path_label_expectations = [
2192 ("/a1/", "system_u:object_r:fs_with_path_rules_a1_t:s0"),
2196 ("/a1/b", "system_u:object_r:fs_with_path_rules_a1_t:s0"),
2197 ("/a1/b/c", "system_u:object_r:fs_with_path_rules_a1_b_c_t:s0"),
2198 ("/a2/", "system_u:object_r:fs_with_path_rules_t:s0"),
2201 ("/a2/b/c/d", "system_u:object_r:fs_with_path_rules_a2_b_t:s0"),
2202 ("/a3/b/c/d", "system_u:object_r:fs_with_path_rules_a3_t:s0"),
2205 ];
2206 for (path, expected_label) in path_label_expectations {
2207 let context = policy.genfscon_label_for_fs_and_path(
2208 "fs_with_path_rules".into(),
2209 path.into(),
2210 None,
2211 );
2212 assert_eq!(
2213 policy.serialize_security_context(&context.unwrap()),
2214 expected_label.as_bytes()
2215 )
2216 }
2217 }
2218}