1pub mod arrays;
6pub mod error;
7pub mod index;
8pub mod metadata;
9pub mod parsed_policy;
10pub mod parser;
11
12mod constraints;
13mod extensible_bitmap;
14mod security_context;
15mod symbols;
16
17pub use arrays::{FsUseType, XpermsBitmap};
18pub use index::FsUseLabelAndType;
19pub use security_context::{SecurityContext, SecurityContextError};
20
21use crate::{self as sc, FsNodeClass, NullessByteStr, ObjectClass};
22use anyhow::Context as _;
23use error::ParseError;
24use index::PolicyIndex;
25use metadata::HandleUnknown;
26use parsed_policy::ParsedPolicy;
27use parser::{ByRef, ByValue, ParseStrategy};
28use std::fmt::{Debug, Display};
29use std::marker::PhantomData;
30use std::num::{NonZeroU32, NonZeroU64};
31use std::ops::Deref;
32use symbols::{find_class_by_name, find_common_symbol_by_name_bytes};
33use zerocopy::{
34 little_endian as le, FromBytes, Immutable, KnownLayout, Ref, SplitByteSlice, Unaligned,
35};
36
37pub const SUPPORTED_POLICY_VERSION: u32 = 33;
39
40#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
42pub struct UserId(NonZeroU32);
43
44#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
46pub struct RoleId(NonZeroU32);
47
48#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
50pub struct TypeId(NonZeroU32);
51
52#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
54pub struct SensitivityId(NonZeroU32);
55
56#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
58pub struct CategoryId(NonZeroU32);
59
60#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
62pub struct ClassId(NonZeroU32);
63
64impl Into<u32> for ClassId {
65 fn into(self) -> u32 {
66 self.0.into()
67 }
68}
69
70#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
72pub struct ClassPermissionId(NonZeroU32);
73
74impl Display for ClassPermissionId {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 write!(f, "{}", self.0)
77 }
78}
79
80#[derive(Debug, Clone, PartialEq)]
85pub struct AccessDecision {
86 pub allow: AccessVector,
87 pub auditallow: AccessVector,
88 pub auditdeny: AccessVector,
89 pub flags: u32,
90
91 pub todo_bug: Option<NonZeroU64>,
94}
95
96impl Default for AccessDecision {
97 fn default() -> Self {
98 Self::allow(AccessVector::NONE)
99 }
100}
101
102impl AccessDecision {
103 pub(super) const fn allow(allow: AccessVector) -> Self {
106 Self {
107 allow,
108 auditallow: AccessVector::NONE,
109 auditdeny: AccessVector::ALL,
110 flags: 0,
111 todo_bug: None,
112 }
113 }
114}
115
116pub(super) const SELINUX_AVD_FLAGS_PERMISSIVE: u32 = 1;
118
119#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
122pub struct AccessVector(u32);
123
124impl AccessVector {
125 pub const NONE: AccessVector = AccessVector(0);
126 pub const ALL: AccessVector = AccessVector(std::u32::MAX);
127
128 pub(super) fn from_class_permission_id(id: ClassPermissionId) -> Self {
129 Self((1 as u32) << (id.0.get() - 1))
130 }
131}
132
133impl std::ops::BitAnd for AccessVector {
134 type Output = Self;
135
136 fn bitand(self, rhs: Self) -> Self::Output {
137 AccessVector(self.0 & rhs.0)
138 }
139}
140
141impl std::ops::BitOr for AccessVector {
142 type Output = Self;
143
144 fn bitor(self, rhs: Self) -> Self::Output {
145 AccessVector(self.0 | rhs.0)
146 }
147}
148
149impl std::ops::BitAndAssign for AccessVector {
150 fn bitand_assign(&mut self, rhs: Self) {
151 self.0 &= rhs.0
152 }
153}
154
155impl std::ops::BitOrAssign for AccessVector {
156 fn bitor_assign(&mut self, rhs: Self) {
157 self.0 |= rhs.0
158 }
159}
160
161impl std::ops::SubAssign for AccessVector {
162 fn sub_assign(&mut self, rhs: Self) {
163 self.0 = self.0 ^ (self.0 & rhs.0);
164 }
165}
166
167#[derive(Debug, Clone, PartialEq)]
177pub struct IoctlAccessDecision {
178 pub allow: XpermsBitmap,
179 pub auditallow: XpermsBitmap,
180 pub auditdeny: XpermsBitmap,
181}
182
183impl IoctlAccessDecision {
184 pub const DENY_ALL: Self = Self {
185 allow: XpermsBitmap::NONE,
186 auditallow: XpermsBitmap::NONE,
187 auditdeny: XpermsBitmap::ALL,
188 };
189 pub const ALLOW_ALL: Self = Self {
190 allow: XpermsBitmap::ALL,
191 auditallow: XpermsBitmap::NONE,
192 auditdeny: XpermsBitmap::ALL,
193 };
194}
195
196pub fn parse_policy_by_value(
223 binary_policy: Vec<u8>,
224) -> Result<(Unvalidated<ByValue<Vec<u8>>>, Vec<u8>), anyhow::Error> {
225 let (parsed_policy, binary_policy) =
226 ParsedPolicy::parse(ByValue::new(binary_policy)).context("parsing policy")?;
227 Ok((Unvalidated(parsed_policy), binary_policy))
228}
229
230pub fn parse_policy_by_reference<'a>(
237 binary_policy: &'a [u8],
238) -> Result<Unvalidated<ByRef<&'a [u8]>>, anyhow::Error> {
239 let (parsed_policy, _) =
240 ParsedPolicy::parse(ByRef::new(binary_policy)).context("parsing policy")?;
241 Ok(Unvalidated(parsed_policy))
242}
243
244pub struct ClassInfo<'a> {
246 pub class_name: &'a [u8],
248 pub class_id: ClassId,
250}
251
252#[derive(Debug)]
253pub struct Policy<PS: ParseStrategy>(PolicyIndex<PS>);
254
255impl<PS: ParseStrategy> Policy<PS> {
256 pub fn policy_version(&self) -> u32 {
258 self.0.parsed_policy().policy_version()
259 }
260
261 pub fn handle_unknown(&self) -> HandleUnknown {
264 self.0.parsed_policy().handle_unknown()
265 }
266
267 pub fn conditional_booleans<'a>(&'a self) -> Vec<(&'a [u8], bool)> {
268 self.0
269 .parsed_policy()
270 .conditional_booleans()
271 .iter()
272 .map(|boolean| (PS::deref_slice(&boolean.data), PS::deref(&boolean.metadata).active()))
273 .collect()
274 }
275
276 pub fn classes<'a>(&'a self) -> Vec<ClassInfo<'a>> {
278 self.0
279 .parsed_policy()
280 .classes()
281 .iter()
282 .map(|class| ClassInfo { class_name: class.name_bytes(), class_id: class.id() })
283 .collect()
284 }
285
286 pub(super) fn type_id_by_name(&self, name: &str) -> Option<TypeId> {
288 self.0.parsed_policy().type_by_name(name).map(|x| x.id())
289 }
290
291 pub fn find_class_permissions_by_name(
296 &self,
297 class_name: &str,
298 ) -> Result<Vec<(ClassPermissionId, Vec<u8>)>, ()> {
299 let class = find_class_by_name(self.0.parsed_policy().classes(), class_name).ok_or(())?;
300 let owned_permissions = class.permissions();
301
302 let mut result: Vec<_> = owned_permissions
303 .iter()
304 .map(|permission| (permission.id(), permission.name_bytes().to_vec()))
305 .collect();
306
307 if class.common_name_bytes().is_empty() {
309 return Ok(result);
310 }
311
312 let common_symbol_permissions = find_common_symbol_by_name_bytes(
313 self.0.parsed_policy().common_symbols(),
314 class.common_name_bytes(),
315 )
316 .ok_or(())?
317 .permissions();
318
319 result.append(
320 &mut common_symbol_permissions
321 .iter()
322 .map(|permission| (permission.id(), permission.name_bytes().to_vec()))
323 .collect(),
324 );
325
326 Ok(result)
327 }
328
329 pub fn fs_use_label_and_type(&self, fs_type: NullessByteStr<'_>) -> Option<FsUseLabelAndType> {
332 self.0.fs_use_label_and_type(fs_type)
333 }
334
335 pub fn genfscon_label_for_fs_and_path(
338 &self,
339 fs_type: NullessByteStr<'_>,
340 node_path: NullessByteStr<'_>,
341 class_id: Option<ClassId>,
342 ) -> Option<SecurityContext> {
343 self.0.genfscon_label_for_fs_and_path(fs_type, node_path, class_id)
344 }
345
346 pub fn initial_context(&self, id: sc::InitialSid) -> security_context::SecurityContext {
349 self.0.initial_context(id)
350 }
351
352 pub fn parse_security_context(
354 &self,
355 security_context: NullessByteStr<'_>,
356 ) -> Result<security_context::SecurityContext, security_context::SecurityContextError> {
357 security_context::SecurityContext::parse(&self.0, security_context)
358 }
359
360 pub fn validate_security_context(
362 &self,
363 security_context: &SecurityContext,
364 ) -> Result<(), SecurityContextError> {
365 security_context.validate(&self.0)
366 }
367
368 pub fn serialize_security_context(&self, security_context: &SecurityContext) -> Vec<u8> {
370 security_context.serialize(&self.0)
371 }
372
373 pub fn new_file_security_context(
378 &self,
379 source: &SecurityContext,
380 target: &SecurityContext,
381 class: &FsNodeClass,
382 ) -> SecurityContext {
383 self.0.new_file_security_context(source, target, class)
384 }
385
386 pub fn new_file_security_context_by_name(
392 &self,
393 source: &SecurityContext,
394 target: &SecurityContext,
395 class: &FsNodeClass,
396 name: NullessByteStr<'_>,
397 ) -> Option<SecurityContext> {
398 self.0.new_file_security_context_by_name(source, target, class, name)
399 }
400
401 pub fn new_security_context(
410 &self,
411 source: &SecurityContext,
412 target: &SecurityContext,
413 class: &ObjectClass,
414 ) -> SecurityContext {
415 self.0.new_security_context(
416 source,
417 target,
418 class,
419 source.role(),
420 source.type_(),
421 source.low_level(),
422 source.high_level(),
423 )
424 }
425
426 pub fn compute_access_decision(
442 &self,
443 source_context: &SecurityContext,
444 target_context: &SecurityContext,
445 object_class: &sc::ObjectClass,
446 ) -> AccessDecision {
447 if let Some(target_class) = self.0.class(&object_class) {
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_access_decision_custom(
465 &self,
466 source_context: &SecurityContext,
467 target_context: &SecurityContext,
468 target_class_name: &str,
469 ) -> AccessDecision {
470 self.0.parsed_policy().compute_access_decision_custom(
471 source_context,
472 target_context,
473 target_class_name,
474 )
475 }
476
477 pub fn compute_ioctl_access_decision(
481 &self,
482 source_context: &SecurityContext,
483 target_context: &SecurityContext,
484 object_class: &sc::ObjectClass,
485 ioctl_prefix: u8,
486 ) -> IoctlAccessDecision {
487 if let Some(target_class) = self.0.class(&object_class) {
488 self.0.parsed_policy().compute_ioctl_access_decision(
489 source_context,
490 target_context,
491 target_class,
492 ioctl_prefix,
493 )
494 } else {
495 IoctlAccessDecision::DENY_ALL
496 }
497 }
498
499 pub fn compute_ioctl_access_decision_custom(
504 &self,
505 source_context: &SecurityContext,
506 target_context: &SecurityContext,
507 target_class_name: &str,
508 ioctl_prefix: u8,
509 ) -> IoctlAccessDecision {
510 self.0.parsed_policy().compute_ioctl_access_decision_custom(
511 source_context,
512 target_context,
513 target_class_name,
514 ioctl_prefix,
515 )
516 }
517
518 pub fn is_bounded_by(&self, bounded_type: TypeId, parent_type: TypeId) -> bool {
519 let type_ = self.0.parsed_policy().type_(bounded_type);
520 type_.bounded_by() == Some(parent_type)
521 }
522
523 pub fn is_permissive(&self, type_: TypeId) -> bool {
525 self.0.parsed_policy().permissive_types().is_set(type_.0.get())
526 }
527}
528
529impl<PS: ParseStrategy> AccessVectorComputer for Policy<PS> {
530 fn access_vector_from_permissions<
531 P: sc::ClassPermission + Into<sc::Permission> + Clone + 'static,
532 >(
533 &self,
534 permissions: &[P],
535 ) -> Option<AccessVector> {
536 let mut access_vector = AccessVector::NONE;
537 for permission in permissions {
538 if let Some(permission_info) = self.0.permission(&permission.clone().into()) {
539 access_vector |= AccessVector::from_class_permission_id(permission_info.id());
541 } else {
542 if self.0.parsed_policy().handle_unknown() != HandleUnknown::Allow {
544 return None;
545 }
546 }
547 }
548 Some(access_vector)
549 }
550}
551
552impl<PS: ParseStrategy> Validate for Policy<PS> {
553 type Error = anyhow::Error;
554
555 fn validate(&self) -> Result<(), Self::Error> {
556 self.0.parsed_policy().validate()
557 }
558}
559
560pub struct Unvalidated<PS: ParseStrategy>(ParsedPolicy<PS>);
562
563impl<PS: ParseStrategy> Unvalidated<PS> {
564 pub fn validate(self) -> Result<Policy<PS>, anyhow::Error> {
565 Validate::validate(&self.0).context("validating parsed policy")?;
566 let index = PolicyIndex::new(self.0).context("building index")?;
567 Ok(Policy(index))
568 }
569}
570
571pub trait AccessVectorComputer {
574 fn access_vector_from_permissions<
581 P: sc::ClassPermission + Into<sc::Permission> + Clone + 'static,
582 >(
583 &self,
584 permissions: &[P],
585 ) -> Option<AccessVector>;
586}
587
588pub trait Parse<PS: ParseStrategy>: Sized {
590 type Error: Into<anyhow::Error>;
593
594 fn parse(bytes: PS) -> Result<(Self, PS), Self::Error>;
597}
598
599pub(super) trait ParseSlice<PS: ParseStrategy>: Sized {
601 type Error: Into<anyhow::Error>;
604
605 fn parse_slice(bytes: PS, count: usize) -> Result<(Self, PS), Self::Error>;
608}
609
610pub(super) trait Validate {
612 type Error: Into<anyhow::Error>;
615
616 fn validate(&self) -> Result<(), Self::Error>;
618}
619
620pub(super) trait ValidateArray<M, D> {
621 type Error: Into<anyhow::Error>;
624
625 fn validate_array<'a>(metadata: &'a M, data: &'a [D]) -> Result<(), Self::Error>;
627}
628
629pub(super) trait Counted {
631 fn count(&self) -> u32;
633}
634
635impl<T: Validate> Validate for Option<T> {
636 type Error = <T as Validate>::Error;
637
638 fn validate(&self) -> Result<(), Self::Error> {
639 match self {
640 Some(value) => value.validate(),
641 None => Ok(()),
642 }
643 }
644}
645
646impl Validate for le::U32 {
647 type Error = anyhow::Error;
648
649 fn validate(&self) -> Result<(), Self::Error> {
652 Ok(())
653 }
654}
655
656impl Validate for u8 {
657 type Error = anyhow::Error;
658
659 fn validate(&self) -> Result<(), Self::Error> {
662 Ok(())
663 }
664}
665
666impl Validate for [u8] {
667 type Error = anyhow::Error;
668
669 fn validate(&self) -> Result<(), Self::Error> {
672 Ok(())
673 }
674}
675
676impl<B: SplitByteSlice, T: Validate + FromBytes + KnownLayout + Immutable> Validate for Ref<B, T> {
677 type Error = <T as Validate>::Error;
678
679 fn validate(&self) -> Result<(), Self::Error> {
680 self.deref().validate()
681 }
682}
683
684impl<B: SplitByteSlice, T: Counted + FromBytes + KnownLayout + Immutable> Counted for Ref<B, T> {
685 fn count(&self) -> u32 {
686 self.deref().count()
687 }
688}
689
690#[derive(Clone, Debug, PartialEq)]
693struct Array<PS, M, D> {
694 metadata: M,
695 data: D,
696 _marker: PhantomData<PS>,
697}
698
699impl<PS: ParseStrategy, M: Counted + Parse<PS>, D: ParseSlice<PS>> Parse<PS> for Array<PS, M, D> {
700 type Error = anyhow::Error;
703
704 fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
706 let tail = bytes;
707
708 let (metadata, tail) = M::parse(tail).map_err(Into::<anyhow::Error>::into)?;
709
710 let (data, tail) =
711 D::parse_slice(tail, metadata.count() as usize).map_err(Into::<anyhow::Error>::into)?;
712
713 let array = Self { metadata, data, _marker: PhantomData };
714
715 Ok((array, tail))
716 }
717}
718
719impl<
720 T: Clone + Debug + FromBytes + KnownLayout + Immutable + PartialEq + Unaligned,
721 PS: ParseStrategy<Output<T> = T>,
722 > Parse<PS> for T
723{
724 type Error = anyhow::Error;
725
726 fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
727 let num_bytes = bytes.len();
728 let (data, tail) = PS::parse::<T>(bytes).ok_or(ParseError::MissingData {
729 type_name: std::any::type_name::<T>(),
730 type_size: std::mem::size_of::<T>(),
731 num_bytes,
732 })?;
733
734 Ok((data, tail))
735 }
736}
737
738macro_rules! array_type {
742 ($type_name:ident, $parse_strategy:ident, $metadata_type:ty, $data_type:ty, $metadata_type_name:expr, $data_type_name:expr) => {
743 #[doc = "An [`Array`] with [`"]
744 #[doc = $metadata_type_name]
745 #[doc = "`] metadata and [`"]
746 #[doc = $data_type_name]
747 #[doc = "`] data items."]
748 #[derive(Debug, PartialEq)]
749 pub(super) struct $type_name<$parse_strategy: crate::policy::parser::ParseStrategy>(
750 crate::policy::Array<PS, $metadata_type, $data_type>,
751 );
752
753 impl<PS: crate::policy::parser::ParseStrategy> std::ops::Deref for $type_name<PS> {
754 type Target = crate::policy::Array<PS, $metadata_type, $data_type>;
755
756 fn deref(&self) -> &Self::Target {
757 &self.0
758 }
759 }
760
761 impl<PS: crate::policy::parser::ParseStrategy> crate::policy::Parse<PS> for $type_name<PS>
762 where
763 crate::policy::Array<PS, $metadata_type, $data_type>: crate::policy::Parse<PS>,
764 {
765 type Error = <Array<PS, $metadata_type, $data_type> as crate::policy::Parse<PS>>::Error;
766
767 fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
768 let (array, tail) = Array::<PS, $metadata_type, $data_type>::parse(bytes)?;
769 Ok((Self(array), tail))
770 }
771 }
772 };
773
774 ($type_name:ident, $parse_strategy:ident, $metadata_type:ty, $data_type:ty) => {
775 array_type!(
776 $type_name,
777 $parse_strategy,
778 $metadata_type,
779 $data_type,
780 stringify!($metadata_type),
781 stringify!($data_type)
782 );
783 };
784}
785
786pub(super) use array_type;
787
788macro_rules! array_type_validate_deref_both {
789 ($type_name:ident) => {
790 impl<PS: crate::policy::parser::ParseStrategy> Validate for $type_name<PS> {
791 type Error = anyhow::Error;
792
793 fn validate(&self) -> Result<(), Self::Error> {
794 let metadata = PS::deref(&self.metadata);
795 metadata.validate()?;
796
797 let data = PS::deref_slice(&self.data);
798 data.validate()?;
799
800 Self::validate_array(metadata, data).map_err(Into::<anyhow::Error>::into)
801 }
802 }
803 };
804}
805
806pub(super) use array_type_validate_deref_both;
807
808macro_rules! array_type_validate_deref_data {
809 ($type_name:ident) => {
810 impl<PS: crate::policy::parser::ParseStrategy> Validate for $type_name<PS> {
811 type Error = anyhow::Error;
812
813 fn validate(&self) -> Result<(), Self::Error> {
814 let metadata = &self.metadata;
815 metadata.validate()?;
816
817 let data = PS::deref_slice(&self.data);
818 data.validate()?;
819
820 Self::validate_array(metadata, data)
821 }
822 }
823 };
824}
825
826pub(super) use array_type_validate_deref_data;
827
828macro_rules! array_type_validate_deref_metadata_data_vec {
829 ($type_name:ident) => {
830 impl<PS: crate::policy::parser::ParseStrategy> Validate for $type_name<PS> {
831 type Error = anyhow::Error;
832
833 fn validate(&self) -> Result<(), Self::Error> {
834 let metadata = PS::deref(&self.metadata);
835 metadata.validate()?;
836
837 let data = &self.data;
838 data.validate()?;
839
840 Self::validate_array(metadata, data.as_slice())
841 }
842 }
843 };
844}
845
846pub(super) use array_type_validate_deref_metadata_data_vec;
847
848macro_rules! array_type_validate_deref_none_data_vec {
849 ($type_name:ident) => {
850 impl<PS: crate::policy::parser::ParseStrategy> Validate for $type_name<PS> {
851 type Error = anyhow::Error;
852
853 fn validate(&self) -> Result<(), Self::Error> {
854 let metadata = &self.metadata;
855 metadata.validate()?;
856
857 let data = &self.data;
858 data.validate()?;
859
860 Self::validate_array(metadata, data.as_slice())
861 }
862 }
863 };
864}
865
866pub(super) use array_type_validate_deref_none_data_vec;
867
868impl<
869 B: Debug + SplitByteSlice + PartialEq,
870 T: Clone + Debug + FromBytes + KnownLayout + Immutable + PartialEq + Unaligned,
871 > Parse<ByRef<B>> for Ref<B, T>
872{
873 type Error = anyhow::Error;
874
875 fn parse(bytes: ByRef<B>) -> Result<(Self, ByRef<B>), Self::Error> {
876 let num_bytes = bytes.len();
877 let (data, tail) = ByRef::<B>::parse::<T>(bytes).ok_or(ParseError::MissingData {
878 type_name: std::any::type_name::<T>(),
879 type_size: std::mem::size_of::<T>(),
880 num_bytes,
881 })?;
882
883 Ok((data, tail))
884 }
885}
886
887impl<
888 B: Debug + SplitByteSlice + PartialEq,
889 T: Clone + Debug + FromBytes + Immutable + PartialEq + Unaligned,
890 > ParseSlice<ByRef<B>> for Ref<B, [T]>
891{
892 type Error = anyhow::Error;
895
896 fn parse_slice(bytes: ByRef<B>, count: usize) -> Result<(Self, ByRef<B>), Self::Error> {
899 let num_bytes = bytes.len();
900 let (data, tail) =
901 ByRef::<B>::parse_slice::<T>(bytes, count).ok_or(ParseError::MissingSliceData {
902 type_name: std::any::type_name::<T>(),
903 type_size: std::mem::size_of::<T>(),
904 num_items: count,
905 num_bytes,
906 })?;
907
908 Ok((data, tail))
909 }
910}
911
912impl<PS: ParseStrategy, T: Parse<PS>> ParseSlice<PS> for Vec<T> {
913 type Error = anyhow::Error;
916
917 fn parse_slice(bytes: PS, count: usize) -> Result<(Self, PS), Self::Error> {
919 let mut slice = Vec::with_capacity(count);
920 let mut tail = bytes;
921
922 for _ in 0..count {
923 let (item, next_tail) = T::parse(tail).map_err(Into::<anyhow::Error>::into)?;
924 slice.push(item);
925 tail = next_tail;
926 }
927
928 Ok((slice, tail))
929 }
930}
931
932#[cfg(test)]
933pub(super) mod testing {
934 use crate::policy::error::ValidateError;
935 use crate::policy::{AccessVector, ParseError};
936
937 pub const ACCESS_VECTOR_0001: AccessVector = AccessVector(0b0001u32);
938 pub const ACCESS_VECTOR_0010: AccessVector = AccessVector(0b0010u32);
939
940 pub(super) fn as_parse_error(error: anyhow::Error) -> ParseError {
942 error.downcast::<ParseError>().expect("parse error")
943 }
944
945 pub(super) fn as_validate_error(error: anyhow::Error) -> ValidateError {
947 error.downcast::<ValidateError>().expect("validate error")
948 }
949}
950
951#[cfg(test)]
952pub(super) mod tests {
953 use super::*;
954
955 use crate::policy::metadata::HandleUnknown;
956 use crate::policy::{parse_policy_by_reference, parse_policy_by_value, SecurityContext};
957 use crate::{
958 ClassPermission as _, FileClass, InitialSid, ObjectClass, Permission, ProcessPermission,
959 };
960
961 use serde::Deserialize;
962 use std::ops::Shl;
963
964 fn is_explicitly_allowed<PS: ParseStrategy>(
971 policy: &Policy<PS>,
972 source_type: TypeId,
973 target_type: TypeId,
974 permission: sc::Permission,
975 ) -> Result<bool, &'static str> {
976 let object_class = permission.class();
977 let target_class = policy.0.class(&object_class).ok_or("class lookup failed")?;
978 let permission = policy.0.permission(&permission).ok_or("permission lookup failed")?;
979 let access_decision = policy.0.parsed_policy().compute_explicitly_allowed(
980 source_type,
981 target_type,
982 target_class,
983 );
984 let permission_bit = AccessVector::from_class_permission_id(permission.id());
985 Ok(permission_bit == access_decision.allow & permission_bit)
986 }
987
988 fn is_explicitly_allowed_custom<PS: ParseStrategy>(
995 policy: &Policy<PS>,
996 source_type: TypeId,
997 target_type: TypeId,
998 target_class_name: &str,
999 permission_name: &str,
1000 ) -> Result<bool, &'static str> {
1001 let (permission_id, _) = policy
1002 .find_class_permissions_by_name(target_class_name)
1003 .or(Err("class name lookup failed"))?
1004 .into_iter()
1005 .find(|(_, name)| name == permission_name.as_bytes())
1006 .ok_or("permission name lookup failed")?;
1007 let access_decision = policy.0.parsed_policy().compute_explicitly_allowed_custom(
1008 source_type,
1009 target_type,
1010 target_class_name,
1011 );
1012 let permission_bit = AccessVector::from_class_permission_id(permission_id);
1013 Ok(permission_bit == access_decision.allow & permission_bit)
1014 }
1015
1016 #[derive(Debug, Deserialize)]
1017 struct Expectations {
1018 expected_policy_version: u32,
1019 expected_handle_unknown: LocalHandleUnknown,
1020 }
1021
1022 #[derive(Debug, Deserialize, PartialEq)]
1023 #[serde(rename_all = "snake_case")]
1024 enum LocalHandleUnknown {
1025 Deny,
1026 Reject,
1027 Allow,
1028 }
1029
1030 impl PartialEq<HandleUnknown> for LocalHandleUnknown {
1031 fn eq(&self, other: &HandleUnknown) -> bool {
1032 match self {
1033 LocalHandleUnknown::Deny => *other == HandleUnknown::Deny,
1034 LocalHandleUnknown::Reject => *other == HandleUnknown::Reject,
1035 LocalHandleUnknown::Allow => *other == HandleUnknown::Allow,
1036 }
1037 }
1038 }
1039
1040 fn xperms_bitmap_from_elements(elements: &[u8]) -> XpermsBitmap {
1043 let mut bitmap = [le::U32::ZERO; 8];
1044 for element in elements.iter() {
1045 let block_index = (*element as usize) / 32;
1046 let bit_index = ((*element as usize) % 32) as u32;
1047 let bitmask = le::U32::new(1).shl(bit_index);
1048 bitmap[block_index] = bitmap[block_index] | bitmask;
1049 }
1050 XpermsBitmap::new(bitmap)
1051 }
1052
1053 #[test]
1054 fn known_policies() {
1055 let policies_and_expectations = [
1056 [
1057 b"testdata/policies/emulator".to_vec(),
1058 include_bytes!("../../testdata/policies/emulator").to_vec(),
1059 include_bytes!("../../testdata/expectations/emulator").to_vec(),
1060 ],
1061 [
1062 b"testdata/policies/selinux_testsuite".to_vec(),
1063 include_bytes!("../../testdata/policies/selinux_testsuite").to_vec(),
1064 include_bytes!("../../testdata/expectations/selinux_testsuite").to_vec(),
1065 ],
1066 ];
1067
1068 for [policy_path, policy_bytes, expectations_bytes] in policies_and_expectations {
1069 let expectations = serde_json5::from_reader::<_, Expectations>(
1070 &mut std::io::Cursor::new(expectations_bytes),
1071 )
1072 .expect("deserialize expectations");
1073
1074 let (policy, returned_policy_bytes) =
1077 parse_policy_by_value(policy_bytes.clone()).expect("parse policy");
1078
1079 let policy = policy
1080 .validate()
1081 .with_context(|| {
1082 format!(
1083 "policy path: {:?}",
1084 std::str::from_utf8(policy_path.as_slice()).unwrap()
1085 )
1086 })
1087 .expect("validate policy");
1088
1089 assert_eq!(expectations.expected_policy_version, policy.policy_version());
1090 assert_eq!(expectations.expected_handle_unknown, policy.handle_unknown());
1091
1092 assert_eq!(policy_bytes, returned_policy_bytes);
1094
1095 let policy = parse_policy_by_reference(policy_bytes.as_slice()).expect("parse policy");
1097 let policy = policy.validate().expect("validate policy");
1098
1099 assert_eq!(expectations.expected_policy_version, policy.policy_version());
1100 assert_eq!(expectations.expected_handle_unknown, policy.handle_unknown());
1101 }
1102 }
1103
1104 #[test]
1105 fn policy_lookup() {
1106 let policy_bytes = include_bytes!("../../testdata/policies/selinux_testsuite");
1107 let (policy, _) = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1108 let policy = policy.validate().expect("validate selinux testsuite policy");
1109
1110 let unconfined_t = policy.type_id_by_name("unconfined_t").expect("look up type id");
1111
1112 assert!(is_explicitly_allowed(
1113 &policy,
1114 unconfined_t,
1115 unconfined_t,
1116 Permission::Process(ProcessPermission::Fork),
1117 )
1118 .expect("check for `allow unconfined_t unconfined_t:process fork;"));
1119 }
1120
1121 #[test]
1122 fn initial_contexts() {
1123 let policy_bytes = include_bytes!(
1124 "../../testdata/micro_policies/multiple_levels_and_categories_policy.pp"
1125 );
1126 let (policy, _) = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1127 let policy = policy.validate().expect("validate policy");
1128
1129 let kernel_context = policy.initial_context(InitialSid::Kernel);
1130 assert_eq!(
1131 policy.serialize_security_context(&kernel_context),
1132 b"user0:object_r:type0:s0:c0-s1:c0.c2,c4"
1133 )
1134 }
1135
1136 #[test]
1137 fn explicit_allow_type_type() {
1138 let policy_bytes =
1139 include_bytes!("../../testdata/micro_policies/allow_a_t_b_t_class0_perm0_policy.pp");
1140 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1141 .expect("parse policy")
1142 .validate()
1143 .expect("validate policy");
1144
1145 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1146 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1147
1148 assert!(is_explicitly_allowed_custom(&policy, a_t, b_t, "class0", "perm0")
1149 .expect("query well-formed"));
1150 }
1151
1152 #[test]
1153 fn no_explicit_allow_type_type() {
1154 let policy_bytes =
1155 include_bytes!("../../testdata/micro_policies/no_allow_a_t_b_t_class0_perm0_policy.pp");
1156 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1157 .expect("parse policy")
1158 .validate()
1159 .expect("validate policy");
1160
1161 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1162 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1163
1164 assert!(!is_explicitly_allowed_custom(&policy, a_t, b_t, "class0", "perm0")
1165 .expect("query well-formed"));
1166 }
1167
1168 #[test]
1169 fn explicit_allow_type_attr() {
1170 let policy_bytes =
1171 include_bytes!("../../testdata/micro_policies/allow_a_t_b_attr_class0_perm0_policy.pp");
1172 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1173 .expect("parse policy")
1174 .validate()
1175 .expect("validate policy");
1176
1177 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1178 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1179
1180 assert!(is_explicitly_allowed_custom(&policy, a_t, b_t, "class0", "perm0")
1181 .expect("query well-formed"));
1182 }
1183
1184 #[test]
1185 fn no_explicit_allow_type_attr() {
1186 let policy_bytes = include_bytes!(
1187 "../../testdata/micro_policies/no_allow_a_t_b_attr_class0_perm0_policy.pp"
1188 );
1189 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1190 .expect("parse policy")
1191 .validate()
1192 .expect("validate policy");
1193
1194 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1195 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1196
1197 assert!(!is_explicitly_allowed_custom(&policy, a_t, b_t, "class0", "perm0")
1198 .expect("query well-formed"));
1199 }
1200
1201 #[test]
1202 fn explicit_allow_attr_attr() {
1203 let policy_bytes = include_bytes!(
1204 "../../testdata/micro_policies/allow_a_attr_b_attr_class0_perm0_policy.pp"
1205 );
1206 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1207 .expect("parse policy")
1208 .validate()
1209 .expect("validate policy");
1210
1211 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1212 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1213
1214 assert!(is_explicitly_allowed_custom(&policy, a_t, b_t, "class0", "perm0")
1215 .expect("query well-formed"));
1216 }
1217
1218 #[test]
1219 fn no_explicit_allow_attr_attr() {
1220 let policy_bytes = include_bytes!(
1221 "../../testdata/micro_policies/no_allow_a_attr_b_attr_class0_perm0_policy.pp"
1222 );
1223 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1224 .expect("parse policy")
1225 .validate()
1226 .expect("validate policy");
1227
1228 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1229 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1230
1231 assert!(!is_explicitly_allowed_custom(&policy, a_t, b_t, "class0", "perm0")
1232 .expect("query well-formed"));
1233 }
1234
1235 #[test]
1236 fn compute_explicitly_allowed_multiple_attributes() {
1237 let policy_bytes = include_bytes!("../../testdata/micro_policies/allow_a_t_a1_attr_class0_perm0_a2_attr_class0_perm1_policy.pp");
1238 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1239 .expect("parse policy")
1240 .validate()
1241 .expect("validate policy");
1242
1243 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1244
1245 let raw_access_vector =
1246 policy.0.parsed_policy().compute_explicitly_allowed_custom(a_t, a_t, "class0").allow.0;
1247
1248 assert_eq!(2, raw_access_vector.count_ones());
1253 }
1254
1255 #[test]
1256 fn compute_access_decision_with_constraints() {
1257 let policy_bytes =
1258 include_bytes!("../../testdata/micro_policies/allow_with_constraints_policy.pp");
1259 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1260 .expect("parse policy")
1261 .validate()
1262 .expect("validate policy");
1263
1264 let source_context: SecurityContext = policy
1265 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1266 .expect("create source security context");
1267
1268 let target_context_satisfied: SecurityContext = source_context.clone();
1269 let decision_satisfied = policy.compute_access_decision(
1270 &source_context,
1271 &target_context_satisfied,
1272 &ObjectClass::File,
1273 );
1274 assert_eq!(decision_satisfied.allow, AccessVector(7));
1278
1279 let target_context_unsatisfied: SecurityContext = policy
1280 .parse_security_context(b"user1:object_r:type0:s0:c0-s0:c0".into())
1281 .expect("create target security context failing some constraints");
1282 let decision_unsatisfied = policy.compute_access_decision(
1283 &source_context,
1284 &target_context_unsatisfied,
1285 &ObjectClass::File,
1286 );
1287 assert_eq!(decision_unsatisfied.allow, AccessVector(4));
1290 }
1291
1292 #[test]
1293 fn compute_access_decision_custom_with_mlsconstrain() {
1294 let policy_bytes =
1295 include_bytes!("../../testdata/micro_policies/allow_with_constraints_policy.pp");
1296 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1297 .expect("parse policy")
1298 .validate()
1299 .expect("validate policy");
1300
1301 let source_context: SecurityContext = policy
1302 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1303 .expect("create source security context");
1304
1305 let target_context_satisfied: SecurityContext = source_context.clone();
1306 let decision_satisfied = policy.compute_access_decision_custom(
1307 &source_context,
1308 &target_context_satisfied,
1309 "class_mlsconstrain",
1310 );
1311 assert_eq!(decision_satisfied.allow, AccessVector(3));
1315
1316 let target_context_unsatisfied: SecurityContext = policy
1317 .parse_security_context(b"user1:object_r:type0:s0:c0-s0:c0".into())
1318 .expect("create target security context failing a constraint");
1319 let decision_unsatisfied = policy.compute_access_decision_custom(
1320 &source_context,
1321 &target_context_unsatisfied,
1322 "class_mlsconstrain",
1323 );
1324 assert_eq!(decision_unsatisfied.allow, AccessVector(2));
1327 }
1328
1329 #[test]
1330 fn compute_access_decision_custom_with_constrain() {
1331 let policy_bytes =
1332 include_bytes!("../../testdata/micro_policies/allow_with_constraints_policy.pp");
1333 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1334 .expect("parse policy")
1335 .validate()
1336 .expect("validate policy");
1337
1338 let source_context: SecurityContext = policy
1339 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1340 .expect("create source security context");
1341
1342 let target_context_satisfied: SecurityContext = source_context.clone();
1343 let decision_satisfied = policy.compute_access_decision_custom(
1344 &source_context,
1345 &target_context_satisfied,
1346 "class_mlsconstrain",
1347 );
1348 assert_eq!(decision_satisfied.allow, AccessVector(3));
1352
1353 let target_context_unsatisfied: SecurityContext = policy
1354 .parse_security_context(b"user1:object_r:type0:s0:c0-s0:c0".into())
1355 .expect("create target security context failing a constraint");
1356 let decision_unsatisfied = policy.compute_access_decision_custom(
1357 &source_context,
1358 &target_context_unsatisfied,
1359 "class_constrain",
1360 );
1361 assert_eq!(decision_unsatisfied.allow, AccessVector(2));
1364 }
1365
1366 #[test]
1367 fn compute_ioctl_access_decision_explicitly_allowed() {
1368 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1369 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1370 .expect("parse policy")
1371 .validate()
1372 .expect("validate policy");
1373
1374 let source_context: SecurityContext = policy
1375 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1376 .expect("create source security context");
1377 let target_context_matched: SecurityContext = source_context.clone();
1378
1379 let decision_single = policy.compute_ioctl_access_decision(
1397 &source_context,
1398 &target_context_matched,
1399 &ObjectClass::File,
1400 0xab,
1401 );
1402
1403 let mut expected_auditdeny =
1404 xperms_bitmap_from_elements((0x0..=0xff).collect::<Vec<_>>().as_slice());
1405 expected_auditdeny -= &xperms_bitmap_from_elements(&[0xcd, 0xef]);
1406
1407 let expected_decision_single = IoctlAccessDecision {
1408 allow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1409 auditallow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1410 auditdeny: expected_auditdeny,
1411 };
1412 assert_eq!(decision_single, expected_decision_single);
1413
1414 let decision_range = policy.compute_ioctl_access_decision(
1415 &source_context,
1416 &target_context_matched,
1417 &ObjectClass::File,
1418 0x10,
1419 );
1420 let expected_decision_range = IoctlAccessDecision {
1421 allow: XpermsBitmap::ALL,
1422 auditallow: XpermsBitmap::ALL,
1423 auditdeny: XpermsBitmap::NONE,
1424 };
1425 assert_eq!(decision_range, expected_decision_range);
1426 }
1427
1428 #[test]
1429 fn compute_ioctl_access_decision_unmatched() {
1430 let policy_bytes =
1431 include_bytes!("../../testdata/micro_policies/allow_with_constraints_policy.pp");
1432 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1433 .expect("parse policy")
1434 .validate()
1435 .expect("validate policy");
1436
1437 let source_context: SecurityContext = policy
1438 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1439 .expect("create source security context");
1440
1441 let target_context_unmatched: SecurityContext = policy
1443 .parse_security_context(b"user0:object_r:type1:s0-s0".into())
1444 .expect("create source security context");
1445
1446 for prefix in 0x0..=0xff {
1447 let decision = policy.compute_ioctl_access_decision(
1448 &source_context,
1449 &target_context_unmatched,
1450 &ObjectClass::File,
1451 prefix,
1452 );
1453 assert_eq!(decision, IoctlAccessDecision::ALLOW_ALL);
1454 }
1455 }
1456
1457 #[test]
1458 fn compute_ioctl_access_decision_custom() {
1459 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1460 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1461 .expect("parse policy")
1462 .validate()
1463 .expect("validate policy");
1464
1465 let source_context: SecurityContext = policy
1466 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1467 .expect("create source security context");
1468
1469 let target_context: SecurityContext = source_context.clone();
1473 let decision = policy.compute_ioctl_access_decision_custom(
1474 &source_context,
1475 &target_context,
1476 "class_two_ioctls_same_range",
1477 0x12,
1478 );
1479
1480 let expected_decision = IoctlAccessDecision {
1481 allow: xperms_bitmap_from_elements(&[0x34, 0x56]),
1482 auditallow: XpermsBitmap::NONE,
1483 auditdeny: XpermsBitmap::ALL,
1484 };
1485 assert_eq!(decision, expected_decision);
1486 }
1487
1488 #[test]
1489 fn new_file_security_context_minimal() {
1490 let policy_bytes =
1491 include_bytes!("../../testdata/composite_policies/compiled/minimal_policy.pp");
1492 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1493 .expect("parse policy")
1494 .validate()
1495 .expect("validate policy");
1496 let source = policy
1497 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1498 .expect("valid source security context");
1499 let target = policy
1500 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1501 .expect("valid target security context");
1502
1503 let actual = policy.new_file_security_context(&source, &target, &FileClass::File.into());
1504 let expected: SecurityContext = policy
1505 .parse_security_context(b"source_u:object_r:target_t:s0:c0".into())
1506 .expect("valid expected security context");
1507
1508 assert_eq!(expected, actual);
1509 }
1510
1511 #[test]
1512 fn new_security_context_minimal() {
1513 let policy_bytes =
1514 include_bytes!("../../testdata/composite_policies/compiled/minimal_policy.pp");
1515 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1516 .expect("parse policy")
1517 .validate()
1518 .expect("validate policy");
1519 let source = policy
1520 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1521 .expect("valid source security context");
1522 let target = policy
1523 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1524 .expect("valid target security context");
1525
1526 let actual = policy.new_security_context(&source, &target, &ObjectClass::Process);
1527
1528 assert_eq!(source, actual);
1529 }
1530
1531 #[test]
1532 fn new_file_security_context_class_defaults() {
1533 let policy_bytes =
1534 include_bytes!("../../testdata/composite_policies/compiled/class_defaults_policy.pp");
1535 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1536 .expect("parse policy")
1537 .validate()
1538 .expect("validate policy");
1539 let source = policy
1540 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1541 .expect("valid source security context");
1542 let target = policy
1543 .parse_security_context(b"target_u:target_r:target_t:s1:c0-s1:c0.c1".into())
1544 .expect("valid target security context");
1545
1546 let actual = policy.new_file_security_context(&source, &target, &FileClass::File.into());
1547 let expected: SecurityContext = policy
1548 .parse_security_context(b"target_u:source_r:source_t:s1:c0-s1:c0.c1".into())
1549 .expect("valid expected security context");
1550
1551 assert_eq!(expected, actual);
1552 }
1553
1554 #[test]
1555 fn new_security_context_class_defaults() {
1556 let policy_bytes =
1557 include_bytes!("../../testdata/composite_policies/compiled/class_defaults_policy.pp");
1558 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1559 .expect("parse policy")
1560 .validate()
1561 .expect("validate policy");
1562 let source = policy
1563 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1564 .expect("valid source security context");
1565 let target = policy
1566 .parse_security_context(b"target_u:target_r:target_t:s1:c0-s1:c0.c1".into())
1567 .expect("valid target security context");
1568
1569 let actual = policy.new_security_context(&source, &target, &ObjectClass::Process);
1570 let expected: SecurityContext = policy
1571 .parse_security_context(b"target_u:source_r:source_t:s1:c0-s1:c0.c1".into())
1572 .expect("valid expected security context");
1573
1574 assert_eq!(expected, actual);
1575 }
1576
1577 #[test]
1578 fn new_file_security_context_role_transition() {
1579 let policy_bytes =
1580 include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy.pp");
1581 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1582 .expect("parse policy")
1583 .validate()
1584 .expect("validate policy");
1585 let source = policy
1586 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1587 .expect("valid source security context");
1588 let target = policy
1589 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1590 .expect("valid target security context");
1591
1592 let actual = policy.new_file_security_context(&source, &target, &FileClass::File.into());
1593 let expected: SecurityContext = policy
1594 .parse_security_context(b"source_u:transition_r:target_t:s0:c0".into())
1595 .expect("valid expected security context");
1596
1597 assert_eq!(expected, actual);
1598 }
1599
1600 #[test]
1601 fn new_security_context_role_transition() {
1602 let policy_bytes =
1603 include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy.pp");
1604 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1605 .expect("parse policy")
1606 .validate()
1607 .expect("validate policy");
1608 let source = policy
1609 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1610 .expect("valid source security context");
1611 let target = policy
1612 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1613 .expect("valid target security context");
1614
1615 let actual = policy.new_security_context(&source, &target, &ObjectClass::Process);
1616 let expected: SecurityContext = policy
1617 .parse_security_context(b"source_u:transition_r:source_t:s0:c0-s2:c0.c1".into())
1618 .expect("valid expected security context");
1619
1620 assert_eq!(expected, actual);
1621 }
1622
1623 #[test]
1624 #[ignore]
1626 fn new_file_security_context_role_transition_not_allowed() {
1627 let policy_bytes = include_bytes!(
1628 "../../testdata/composite_policies/compiled/role_transition_not_allowed_policy.pp"
1629 );
1630 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1631 .expect("parse policy")
1632 .validate()
1633 .expect("validate policy");
1634 let source = policy
1635 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1636 .expect("valid source security context");
1637 let target = policy
1638 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1639 .expect("valid target security context");
1640
1641 let actual = policy.new_file_security_context(&source, &target, &FileClass::File.into());
1642
1643 assert!(policy.validate_security_context(&actual).is_err());
1645 }
1646
1647 #[test]
1648 fn new_file_security_context_type_transition() {
1649 let policy_bytes =
1650 include_bytes!("../../testdata/composite_policies/compiled/type_transition_policy.pp");
1651 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1652 .expect("parse policy")
1653 .validate()
1654 .expect("validate policy");
1655 let source = policy
1656 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1657 .expect("valid source security context");
1658 let target = policy
1659 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1660 .expect("valid target security context");
1661
1662 let actual = policy.new_file_security_context(&source, &target, &FileClass::File.into());
1663 let expected: SecurityContext = policy
1664 .parse_security_context(b"source_u:object_r:transition_t:s0:c0".into())
1665 .expect("valid expected security context");
1666
1667 assert_eq!(expected, actual);
1668 }
1669
1670 #[test]
1671 fn new_security_context_type_transition() {
1672 let policy_bytes =
1673 include_bytes!("../../testdata/composite_policies/compiled/type_transition_policy.pp");
1674 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1675 .expect("parse policy")
1676 .validate()
1677 .expect("validate policy");
1678 let source = policy
1679 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1680 .expect("valid source security context");
1681 let target = policy
1682 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1683 .expect("valid target security context");
1684
1685 let actual = policy.new_security_context(&source, &target, &ObjectClass::Process);
1686 let expected: SecurityContext = policy
1687 .parse_security_context(b"source_u:source_r:transition_t:s0:c0-s2:c0.c1".into())
1688 .expect("valid expected security context");
1689
1690 assert_eq!(expected, actual);
1691 }
1692
1693 #[test]
1694 fn new_file_security_context_range_transition() {
1695 let policy_bytes =
1696 include_bytes!("../../testdata/composite_policies/compiled/range_transition_policy.pp");
1697 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1698 .expect("parse policy")
1699 .validate()
1700 .expect("validate policy");
1701 let source = policy
1702 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1703 .expect("valid source security context");
1704 let target = policy
1705 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1706 .expect("valid target security context");
1707
1708 let actual = policy.new_file_security_context(&source, &target, &FileClass::File.into());
1709 let expected: SecurityContext = policy
1710 .parse_security_context(b"source_u:object_r:target_t:s1:c1-s2:c1.c2".into())
1711 .expect("valid expected security context");
1712
1713 assert_eq!(expected, actual);
1714 }
1715
1716 #[test]
1717 fn new_security_context_range_transition() {
1718 let policy_bytes =
1719 include_bytes!("../../testdata/composite_policies/compiled/range_transition_policy.pp");
1720 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1721 .expect("parse policy")
1722 .validate()
1723 .expect("validate policy");
1724 let source = policy
1725 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1726 .expect("valid source security context");
1727 let target = policy
1728 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1729 .expect("valid target security context");
1730
1731 let actual = policy.new_security_context(&source, &target, &ObjectClass::Process);
1732 let expected: SecurityContext = policy
1733 .parse_security_context(b"source_u:source_r:source_t:s1:c1-s2:c1.c2".into())
1734 .expect("valid expected security context");
1735
1736 assert_eq!(expected, actual);
1737 }
1738}