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 as sc;
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, LowerHex};
29use std::marker::PhantomData;
30use std::num::{NonZeroU32, NonZeroU64};
31use std::ops::Deref;
32use std::str::FromStr;
33use symbols::{find_class_by_name, find_common_symbol_by_name_bytes};
34use zerocopy::{
35 little_endian as le, FromBytes, Immutable, KnownLayout, Ref, SplitByteSlice, Unaligned,
36};
37
38pub const SUPPORTED_POLICY_VERSION: u32 = 33;
40
41#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
43pub struct UserId(NonZeroU32);
44
45#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
47pub struct RoleId(NonZeroU32);
48
49#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
51pub struct TypeId(NonZeroU32);
52
53#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
55pub struct SensitivityId(NonZeroU32);
56
57#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
59pub struct CategoryId(NonZeroU32);
60
61#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
64pub struct ClassId(NonZeroU32);
65
66impl ClassId {
67 pub fn new(id: NonZeroU32) -> Self {
69 Self(id)
70 }
71}
72
73impl Into<u32> for ClassId {
74 fn into(self) -> u32 {
75 self.0.into()
76 }
77}
78
79#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
81pub struct ClassPermissionId(NonZeroU32);
82
83impl Display for ClassPermissionId {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 write!(f, "{}", self.0)
86 }
87}
88
89#[derive(Debug, Clone, PartialEq)]
94pub struct AccessDecision {
95 pub allow: AccessVector,
96 pub auditallow: AccessVector,
97 pub auditdeny: AccessVector,
98 pub flags: u32,
99
100 pub todo_bug: Option<NonZeroU64>,
103}
104
105impl Default for AccessDecision {
106 fn default() -> Self {
107 Self::allow(AccessVector::NONE)
108 }
109}
110
111impl AccessDecision {
112 pub(super) const fn allow(allow: AccessVector) -> Self {
115 Self {
116 allow,
117 auditallow: AccessVector::NONE,
118 auditdeny: AccessVector::ALL,
119 flags: 0,
120 todo_bug: None,
121 }
122 }
123}
124
125pub(super) const SELINUX_AVD_FLAGS_PERMISSIVE: u32 = 1;
127
128#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
131pub struct AccessVector(u32);
132
133impl AccessVector {
134 pub const NONE: AccessVector = AccessVector(0);
135 pub const ALL: AccessVector = AccessVector(std::u32::MAX);
136
137 pub(super) fn from_class_permission_id(id: ClassPermissionId) -> Self {
138 Self((1 as u32) << (id.0.get() - 1))
139 }
140}
141
142impl FromStr for AccessVector {
143 type Err = <u32 as FromStr>::Err;
144
145 fn from_str(value: &str) -> Result<Self, Self::Err> {
146 Ok(AccessVector(u32::from_str_radix(value, 16)?))
148 }
149}
150
151impl LowerHex for AccessVector {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 LowerHex::fmt(&self.0, f)
154 }
155}
156
157impl std::ops::BitAnd for AccessVector {
158 type Output = Self;
159
160 fn bitand(self, rhs: Self) -> Self::Output {
161 AccessVector(self.0 & rhs.0)
162 }
163}
164
165impl std::ops::BitOr for AccessVector {
166 type Output = Self;
167
168 fn bitor(self, rhs: Self) -> Self::Output {
169 AccessVector(self.0 | rhs.0)
170 }
171}
172
173impl std::ops::BitAndAssign for AccessVector {
174 fn bitand_assign(&mut self, rhs: Self) {
175 self.0 &= rhs.0
176 }
177}
178
179impl std::ops::BitOrAssign for AccessVector {
180 fn bitor_assign(&mut self, rhs: Self) {
181 self.0 |= rhs.0
182 }
183}
184
185impl std::ops::SubAssign for AccessVector {
186 fn sub_assign(&mut self, rhs: Self) {
187 self.0 = self.0 ^ (self.0 & rhs.0);
188 }
189}
190
191impl std::ops::Sub for AccessVector {
192 type Output = Self;
193
194 fn sub(self, rhs: Self) -> Self::Output {
195 AccessVector(self.0 ^ (self.0 & rhs.0))
196 }
197}
198
199#[derive(Debug, Clone, PartialEq)]
209pub struct IoctlAccessDecision {
210 pub allow: XpermsBitmap,
211 pub auditallow: XpermsBitmap,
212 pub auditdeny: XpermsBitmap,
213}
214
215impl IoctlAccessDecision {
216 pub const DENY_ALL: Self = Self {
217 allow: XpermsBitmap::NONE,
218 auditallow: XpermsBitmap::NONE,
219 auditdeny: XpermsBitmap::ALL,
220 };
221 pub const ALLOW_ALL: Self = Self {
222 allow: XpermsBitmap::ALL,
223 auditallow: XpermsBitmap::NONE,
224 auditdeny: XpermsBitmap::ALL,
225 };
226}
227
228pub fn parse_policy_by_value(
255 binary_policy: Vec<u8>,
256) -> Result<(Unvalidated<ByValue<Vec<u8>>>, Vec<u8>), anyhow::Error> {
257 let (parsed_policy, binary_policy) =
258 ParsedPolicy::parse(ByValue::new(binary_policy)).context("parsing policy")?;
259 Ok((Unvalidated(parsed_policy), binary_policy))
260}
261
262pub fn parse_policy_by_reference<'a>(
269 binary_policy: &'a [u8],
270) -> Result<Unvalidated<ByRef<&'a [u8]>>, anyhow::Error> {
271 let (parsed_policy, _) =
272 ParsedPolicy::parse(ByRef::new(binary_policy)).context("parsing policy")?;
273 Ok(Unvalidated(parsed_policy))
274}
275
276pub struct ClassInfo<'a> {
278 pub class_name: &'a [u8],
280 pub class_id: ClassId,
282}
283
284#[derive(Debug)]
285pub struct Policy<PS: ParseStrategy>(PolicyIndex<PS>);
286
287impl<PS: ParseStrategy> Policy<PS> {
288 pub fn policy_version(&self) -> u32 {
290 self.0.parsed_policy().policy_version()
291 }
292
293 pub fn handle_unknown(&self) -> HandleUnknown {
296 self.0.parsed_policy().handle_unknown()
297 }
298
299 pub fn conditional_booleans<'a>(&'a self) -> Vec<(&'a [u8], bool)> {
300 self.0
301 .parsed_policy()
302 .conditional_booleans()
303 .iter()
304 .map(|boolean| (PS::deref_slice(&boolean.data), PS::deref(&boolean.metadata).active()))
305 .collect()
306 }
307
308 pub fn classes<'a>(&'a self) -> Vec<ClassInfo<'a>> {
310 self.0
311 .parsed_policy()
312 .classes()
313 .iter()
314 .map(|class| ClassInfo { class_name: class.name_bytes(), class_id: class.id() })
315 .collect()
316 }
317
318 pub(super) fn type_id_by_name(&self, name: &str) -> Option<TypeId> {
320 self.0.parsed_policy().type_by_name(name).map(|x| x.id())
321 }
322
323 pub fn find_class_permissions_by_name(
328 &self,
329 class_name: &str,
330 ) -> Result<Vec<(ClassPermissionId, Vec<u8>)>, ()> {
331 let class = find_class_by_name(self.0.parsed_policy().classes(), class_name).ok_or(())?;
332 let owned_permissions = class.permissions();
333
334 let mut result: Vec<_> = owned_permissions
335 .iter()
336 .map(|permission| (permission.id(), permission.name_bytes().to_vec()))
337 .collect();
338
339 if class.common_name_bytes().is_empty() {
341 return Ok(result);
342 }
343
344 let common_symbol_permissions = find_common_symbol_by_name_bytes(
345 self.0.parsed_policy().common_symbols(),
346 class.common_name_bytes(),
347 )
348 .ok_or(())?
349 .permissions();
350
351 result.append(
352 &mut common_symbol_permissions
353 .iter()
354 .map(|permission| (permission.id(), permission.name_bytes().to_vec()))
355 .collect(),
356 );
357
358 Ok(result)
359 }
360
361 pub fn fs_use_label_and_type(
364 &self,
365 fs_type: sc::NullessByteStr<'_>,
366 ) -> Option<FsUseLabelAndType> {
367 self.0.fs_use_label_and_type(fs_type)
368 }
369
370 pub fn genfscon_label_for_fs_and_path(
373 &self,
374 fs_type: sc::NullessByteStr<'_>,
375 node_path: sc::NullessByteStr<'_>,
376 class_id: Option<ClassId>,
377 ) -> Option<SecurityContext> {
378 self.0.genfscon_label_for_fs_and_path(fs_type, node_path, class_id)
379 }
380
381 pub fn initial_context(&self, id: sc::InitialSid) -> security_context::SecurityContext {
384 self.0.initial_context(id)
385 }
386
387 pub fn parse_security_context(
389 &self,
390 security_context: sc::NullessByteStr<'_>,
391 ) -> Result<security_context::SecurityContext, security_context::SecurityContextError> {
392 security_context::SecurityContext::parse(&self.0, security_context)
393 }
394
395 pub fn validate_security_context(
397 &self,
398 security_context: &SecurityContext,
399 ) -> Result<(), SecurityContextError> {
400 security_context.validate(&self.0)
401 }
402
403 pub fn serialize_security_context(&self, security_context: &SecurityContext) -> Vec<u8> {
405 security_context.serialize(&self.0)
406 }
407
408 pub fn compute_create_context_with_name(
416 &self,
417 source: &SecurityContext,
418 target: &SecurityContext,
419 class: impl Into<sc::ObjectClass>,
420 name: sc::NullessByteStr<'_>,
421 ) -> Option<SecurityContext> {
422 self.0.compute_create_context_with_name(source, target, class.into(), name)
423 }
424
425 pub fn compute_create_context(
443 &self,
444 source: &SecurityContext,
445 target: &SecurityContext,
446 class: impl Into<sc::ObjectClass>,
447 ) -> SecurityContext {
448 self.0.compute_create_context(source, target, class.into())
449 }
450
451 pub fn compute_access_decision(
467 &self,
468 source_context: &SecurityContext,
469 target_context: &SecurityContext,
470 object_class: impl Into<sc::ObjectClass>,
471 ) -> AccessDecision {
472 if let Some(target_class) = self.0.class(object_class.into()) {
473 self.0.parsed_policy().compute_access_decision(
474 source_context,
475 target_context,
476 target_class,
477 )
478 } else {
479 AccessDecision::allow(AccessVector::NONE)
480 }
481 }
482
483 pub fn compute_ioctl_access_decision(
487 &self,
488 source_context: &SecurityContext,
489 target_context: &SecurityContext,
490 object_class: impl Into<sc::ObjectClass>,
491 ioctl_prefix: u8,
492 ) -> IoctlAccessDecision {
493 if let Some(target_class) = self.0.class(object_class.into()) {
494 self.0.parsed_policy().compute_ioctl_access_decision(
495 source_context,
496 target_context,
497 target_class,
498 ioctl_prefix,
499 )
500 } else {
501 IoctlAccessDecision::DENY_ALL
502 }
503 }
504
505 pub fn is_bounded_by(&self, bounded_type: TypeId, parent_type: TypeId) -> bool {
506 let type_ = self.0.parsed_policy().type_(bounded_type);
507 type_.bounded_by() == Some(parent_type)
508 }
509
510 pub fn is_permissive(&self, type_: TypeId) -> bool {
512 self.0.parsed_policy().permissive_types().is_set(type_.0.get())
513 }
514}
515
516impl<PS: ParseStrategy> AccessVectorComputer for Policy<PS> {
517 fn access_vector_from_permissions<
518 P: sc::ClassPermission + Into<sc::KernelPermission> + Clone + 'static,
519 >(
520 &self,
521 permissions: &[P],
522 ) -> Option<AccessVector> {
523 let mut access_vector = AccessVector::NONE;
524 for permission in permissions {
525 if let Some(permission_info) = self.0.permission(&permission.clone().into()) {
526 access_vector |= AccessVector::from_class_permission_id(permission_info.id());
528 } else {
529 if self.0.parsed_policy().handle_unknown() != HandleUnknown::Allow {
531 return None;
532 }
533 }
534 }
535 Some(access_vector)
536 }
537}
538
539impl<PS: ParseStrategy> Validate for Policy<PS> {
540 type Error = anyhow::Error;
541
542 fn validate(&self) -> Result<(), Self::Error> {
543 self.0.parsed_policy().validate()
544 }
545}
546
547pub struct Unvalidated<PS: ParseStrategy>(ParsedPolicy<PS>);
549
550impl<PS: ParseStrategy> Unvalidated<PS> {
551 pub fn validate(self) -> Result<Policy<PS>, anyhow::Error> {
552 Validate::validate(&self.0).context("validating parsed policy")?;
553 let index = PolicyIndex::new(self.0).context("building index")?;
554 Ok(Policy(index))
555 }
556}
557
558pub trait AccessVectorComputer {
561 fn access_vector_from_permissions<
568 P: sc::ClassPermission + Into<sc::KernelPermission> + Clone + 'static,
569 >(
570 &self,
571 permissions: &[P],
572 ) -> Option<AccessVector>;
573}
574
575pub trait Parse<PS: ParseStrategy>: Sized {
577 type Error: Into<anyhow::Error>;
580
581 fn parse(bytes: PS) -> Result<(Self, PS), Self::Error>;
584}
585
586pub(super) trait ParseSlice<PS: ParseStrategy>: Sized {
588 type Error: Into<anyhow::Error>;
591
592 fn parse_slice(bytes: PS, count: usize) -> Result<(Self, PS), Self::Error>;
595}
596
597pub(super) trait Validate {
599 type Error: Into<anyhow::Error>;
602
603 fn validate(&self) -> Result<(), Self::Error>;
605}
606
607pub(super) trait ValidateArray<M, D> {
608 type Error: Into<anyhow::Error>;
611
612 fn validate_array<'a>(metadata: &'a M, data: &'a [D]) -> Result<(), Self::Error>;
614}
615
616pub(super) trait Counted {
618 fn count(&self) -> u32;
620}
621
622impl<T: Validate> Validate for Option<T> {
623 type Error = <T as Validate>::Error;
624
625 fn validate(&self) -> Result<(), Self::Error> {
626 match self {
627 Some(value) => value.validate(),
628 None => Ok(()),
629 }
630 }
631}
632
633impl Validate for le::U32 {
634 type Error = anyhow::Error;
635
636 fn validate(&self) -> Result<(), Self::Error> {
639 Ok(())
640 }
641}
642
643impl Validate for u8 {
644 type Error = anyhow::Error;
645
646 fn validate(&self) -> Result<(), Self::Error> {
649 Ok(())
650 }
651}
652
653impl Validate for [u8] {
654 type Error = anyhow::Error;
655
656 fn validate(&self) -> Result<(), Self::Error> {
659 Ok(())
660 }
661}
662
663impl<B: SplitByteSlice, T: Validate + FromBytes + KnownLayout + Immutable> Validate for Ref<B, T> {
664 type Error = <T as Validate>::Error;
665
666 fn validate(&self) -> Result<(), Self::Error> {
667 self.deref().validate()
668 }
669}
670
671impl<B: SplitByteSlice, T: Counted + FromBytes + KnownLayout + Immutable> Counted for Ref<B, T> {
672 fn count(&self) -> u32 {
673 self.deref().count()
674 }
675}
676
677#[derive(Clone, Debug, PartialEq)]
680struct Array<PS, M, D> {
681 metadata: M,
682 data: D,
683 _marker: PhantomData<PS>,
684}
685
686impl<PS: ParseStrategy, M: Counted + Parse<PS>, D: ParseSlice<PS>> Parse<PS> for Array<PS, M, D> {
687 type Error = anyhow::Error;
690
691 fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
693 let tail = bytes;
694
695 let (metadata, tail) = M::parse(tail).map_err(Into::<anyhow::Error>::into)?;
696
697 let (data, tail) =
698 D::parse_slice(tail, metadata.count() as usize).map_err(Into::<anyhow::Error>::into)?;
699
700 let array = Self { metadata, data, _marker: PhantomData };
701
702 Ok((array, tail))
703 }
704}
705
706impl<
707 T: Clone + Debug + FromBytes + KnownLayout + Immutable + PartialEq + Unaligned,
708 PS: ParseStrategy<Output<T> = T>,
709 > Parse<PS> for T
710{
711 type Error = anyhow::Error;
712
713 fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
714 let num_bytes = bytes.len();
715 let (data, tail) = PS::parse::<T>(bytes).ok_or(ParseError::MissingData {
716 type_name: std::any::type_name::<T>(),
717 type_size: std::mem::size_of::<T>(),
718 num_bytes,
719 })?;
720
721 Ok((data, tail))
722 }
723}
724
725macro_rules! array_type {
729 ($type_name:ident, $parse_strategy:ident, $metadata_type:ty, $data_type:ty, $metadata_type_name:expr, $data_type_name:expr) => {
730 #[doc = "An [`Array`] with [`"]
731 #[doc = $metadata_type_name]
732 #[doc = "`] metadata and [`"]
733 #[doc = $data_type_name]
734 #[doc = "`] data items."]
735 #[derive(Debug, PartialEq)]
736 pub(super) struct $type_name<$parse_strategy: crate::policy::parser::ParseStrategy>(
737 crate::policy::Array<PS, $metadata_type, $data_type>,
738 );
739
740 impl<PS: crate::policy::parser::ParseStrategy> std::ops::Deref for $type_name<PS> {
741 type Target = crate::policy::Array<PS, $metadata_type, $data_type>;
742
743 fn deref(&self) -> &Self::Target {
744 &self.0
745 }
746 }
747
748 impl<PS: crate::policy::parser::ParseStrategy> crate::policy::Parse<PS> for $type_name<PS>
749 where
750 crate::policy::Array<PS, $metadata_type, $data_type>: crate::policy::Parse<PS>,
751 {
752 type Error = <Array<PS, $metadata_type, $data_type> as crate::policy::Parse<PS>>::Error;
753
754 fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
755 let (array, tail) = Array::<PS, $metadata_type, $data_type>::parse(bytes)?;
756 Ok((Self(array), tail))
757 }
758 }
759 };
760
761 ($type_name:ident, $parse_strategy:ident, $metadata_type:ty, $data_type:ty) => {
762 array_type!(
763 $type_name,
764 $parse_strategy,
765 $metadata_type,
766 $data_type,
767 stringify!($metadata_type),
768 stringify!($data_type)
769 );
770 };
771}
772
773pub(super) use array_type;
774
775macro_rules! array_type_validate_deref_both {
776 ($type_name:ident) => {
777 impl<PS: crate::policy::parser::ParseStrategy> Validate for $type_name<PS> {
778 type Error = anyhow::Error;
779
780 fn validate(&self) -> Result<(), Self::Error> {
781 let metadata = PS::deref(&self.metadata);
782 metadata.validate()?;
783
784 let data = PS::deref_slice(&self.data);
785 data.validate()?;
786
787 Self::validate_array(metadata, data).map_err(Into::<anyhow::Error>::into)
788 }
789 }
790 };
791}
792
793pub(super) use array_type_validate_deref_both;
794
795macro_rules! array_type_validate_deref_data {
796 ($type_name:ident) => {
797 impl<PS: crate::policy::parser::ParseStrategy> Validate for $type_name<PS> {
798 type Error = anyhow::Error;
799
800 fn validate(&self) -> Result<(), Self::Error> {
801 let metadata = &self.metadata;
802 metadata.validate()?;
803
804 let data = PS::deref_slice(&self.data);
805 data.validate()?;
806
807 Self::validate_array(metadata, data)
808 }
809 }
810 };
811}
812
813pub(super) use array_type_validate_deref_data;
814
815macro_rules! array_type_validate_deref_metadata_data_vec {
816 ($type_name:ident) => {
817 impl<PS: crate::policy::parser::ParseStrategy> Validate for $type_name<PS> {
818 type Error = anyhow::Error;
819
820 fn validate(&self) -> Result<(), Self::Error> {
821 let metadata = PS::deref(&self.metadata);
822 metadata.validate()?;
823
824 let data = &self.data;
825 data.validate()?;
826
827 Self::validate_array(metadata, data.as_slice())
828 }
829 }
830 };
831}
832
833pub(super) use array_type_validate_deref_metadata_data_vec;
834
835macro_rules! array_type_validate_deref_none_data_vec {
836 ($type_name:ident) => {
837 impl<PS: crate::policy::parser::ParseStrategy> Validate for $type_name<PS> {
838 type Error = anyhow::Error;
839
840 fn validate(&self) -> Result<(), Self::Error> {
841 let metadata = &self.metadata;
842 metadata.validate()?;
843
844 let data = &self.data;
845 data.validate()?;
846
847 Self::validate_array(metadata, data.as_slice())
848 }
849 }
850 };
851}
852
853pub(super) use array_type_validate_deref_none_data_vec;
854
855impl<
856 B: Debug + SplitByteSlice + PartialEq,
857 T: Clone + Debug + FromBytes + KnownLayout + Immutable + PartialEq + Unaligned,
858 > Parse<ByRef<B>> for Ref<B, T>
859{
860 type Error = anyhow::Error;
861
862 fn parse(bytes: ByRef<B>) -> Result<(Self, ByRef<B>), Self::Error> {
863 let num_bytes = bytes.len();
864 let (data, tail) = ByRef::<B>::parse::<T>(bytes).ok_or(ParseError::MissingData {
865 type_name: std::any::type_name::<T>(),
866 type_size: std::mem::size_of::<T>(),
867 num_bytes,
868 })?;
869
870 Ok((data, tail))
871 }
872}
873
874impl<
875 B: Debug + SplitByteSlice + PartialEq,
876 T: Clone + Debug + FromBytes + Immutable + PartialEq + Unaligned,
877 > ParseSlice<ByRef<B>> for Ref<B, [T]>
878{
879 type Error = anyhow::Error;
882
883 fn parse_slice(bytes: ByRef<B>, count: usize) -> Result<(Self, ByRef<B>), Self::Error> {
886 let num_bytes = bytes.len();
887 let (data, tail) =
888 ByRef::<B>::parse_slice::<T>(bytes, count).ok_or(ParseError::MissingSliceData {
889 type_name: std::any::type_name::<T>(),
890 type_size: std::mem::size_of::<T>(),
891 num_items: count,
892 num_bytes,
893 })?;
894
895 Ok((data, tail))
896 }
897}
898
899impl<PS: ParseStrategy, T: Parse<PS>> ParseSlice<PS> for Vec<T> {
900 type Error = anyhow::Error;
903
904 fn parse_slice(bytes: PS, count: usize) -> Result<(Self, PS), Self::Error> {
906 let mut slice = Vec::with_capacity(count);
907 let mut tail = bytes;
908
909 for _ in 0..count {
910 let (item, next_tail) = T::parse(tail).map_err(Into::<anyhow::Error>::into)?;
911 slice.push(item);
912 tail = next_tail;
913 }
914
915 Ok((slice, tail))
916 }
917}
918
919#[cfg(test)]
920pub(super) mod testing {
921 use crate::policy::error::ValidateError;
922 use crate::policy::{AccessVector, ParseError};
923
924 pub const ACCESS_VECTOR_0001: AccessVector = AccessVector(0b0001u32);
925 pub const ACCESS_VECTOR_0010: AccessVector = AccessVector(0b0010u32);
926
927 pub(super) fn as_parse_error(error: anyhow::Error) -> ParseError {
929 error.downcast::<ParseError>().expect("parse error")
930 }
931
932 pub(super) fn as_validate_error(error: anyhow::Error) -> ValidateError {
934 error.downcast::<ValidateError>().expect("validate error")
935 }
936}
937
938#[cfg(test)]
939pub(super) mod tests {
940 use super::*;
941
942 use crate::policy::metadata::HandleUnknown;
943 use crate::policy::{parse_policy_by_reference, parse_policy_by_value, SecurityContext};
944 use crate::{FileClass, InitialSid, KernelClass};
945
946 use serde::Deserialize;
947 use std::ops::Shl;
948
949 fn is_explicitly_allowed<PS: ParseStrategy>(
957 policy: &Policy<PS>,
958 source_type: TypeId,
959 target_type: TypeId,
960 target_class: &str,
961 permission: &str,
962 ) -> bool {
963 let class = policy
964 .0
965 .parsed_policy()
966 .classes()
967 .iter()
968 .find(|class| class.name_bytes() == target_class.as_bytes())
969 .expect("class not found");
970 let class_permissions = policy
971 .find_class_permissions_by_name(target_class)
972 .expect("class permissions not found");
973 let (permission_id, _) = class_permissions
974 .iter()
975 .find(|(_, name)| permission.as_bytes() == name)
976 .expect("permission not found");
977 let permission_bit = AccessVector::from_class_permission_id(*permission_id);
978 let access_decision =
979 policy.0.parsed_policy().compute_explicitly_allowed(source_type, target_type, class);
980 permission_bit == access_decision.allow & permission_bit
981 }
982
983 #[derive(Debug, Deserialize)]
984 struct Expectations {
985 expected_policy_version: u32,
986 expected_handle_unknown: LocalHandleUnknown,
987 }
988
989 #[derive(Debug, Deserialize, PartialEq)]
990 #[serde(rename_all = "snake_case")]
991 enum LocalHandleUnknown {
992 Deny,
993 Reject,
994 Allow,
995 }
996
997 impl PartialEq<HandleUnknown> for LocalHandleUnknown {
998 fn eq(&self, other: &HandleUnknown) -> bool {
999 match self {
1000 LocalHandleUnknown::Deny => *other == HandleUnknown::Deny,
1001 LocalHandleUnknown::Reject => *other == HandleUnknown::Reject,
1002 LocalHandleUnknown::Allow => *other == HandleUnknown::Allow,
1003 }
1004 }
1005 }
1006
1007 fn xperms_bitmap_from_elements(elements: &[u8]) -> XpermsBitmap {
1010 let mut bitmap = [le::U32::ZERO; 8];
1011 for element in elements.iter() {
1012 let block_index = (*element as usize) / 32;
1013 let bit_index = ((*element as usize) % 32) as u32;
1014 let bitmask = le::U32::new(1).shl(bit_index);
1015 bitmap[block_index] = bitmap[block_index] | bitmask;
1016 }
1017 XpermsBitmap::new(bitmap)
1018 }
1019
1020 #[test]
1021 fn known_policies() {
1022 let policies_and_expectations = [
1023 [
1024 b"testdata/policies/emulator".to_vec(),
1025 include_bytes!("../../testdata/policies/emulator").to_vec(),
1026 include_bytes!("../../testdata/expectations/emulator").to_vec(),
1027 ],
1028 [
1029 b"testdata/policies/selinux_testsuite".to_vec(),
1030 include_bytes!("../../testdata/policies/selinux_testsuite").to_vec(),
1031 include_bytes!("../../testdata/expectations/selinux_testsuite").to_vec(),
1032 ],
1033 ];
1034
1035 for [policy_path, policy_bytes, expectations_bytes] in policies_and_expectations {
1036 let expectations = serde_json5::from_reader::<_, Expectations>(
1037 &mut std::io::Cursor::new(expectations_bytes),
1038 )
1039 .expect("deserialize expectations");
1040
1041 let (policy, returned_policy_bytes) =
1044 parse_policy_by_value(policy_bytes.clone()).expect("parse policy");
1045
1046 let policy = policy
1047 .validate()
1048 .with_context(|| {
1049 format!(
1050 "policy path: {:?}",
1051 std::str::from_utf8(policy_path.as_slice()).unwrap()
1052 )
1053 })
1054 .expect("validate policy");
1055
1056 assert_eq!(expectations.expected_policy_version, policy.policy_version());
1057 assert_eq!(expectations.expected_handle_unknown, policy.handle_unknown());
1058
1059 assert_eq!(policy_bytes, returned_policy_bytes);
1061
1062 let policy = parse_policy_by_reference(policy_bytes.as_slice()).expect("parse policy");
1064 let policy = policy.validate().expect("validate policy");
1065
1066 assert_eq!(expectations.expected_policy_version, policy.policy_version());
1067 assert_eq!(expectations.expected_handle_unknown, policy.handle_unknown());
1068 }
1069 }
1070
1071 #[test]
1072 fn policy_lookup() {
1073 let policy_bytes = include_bytes!("../../testdata/policies/selinux_testsuite");
1074 let (policy, _) = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1075 let policy = policy.validate().expect("validate selinux testsuite policy");
1076
1077 let unconfined_t = policy.type_id_by_name("unconfined_t").expect("look up type id");
1078
1079 assert!(is_explicitly_allowed(&policy, unconfined_t, unconfined_t, "process", "fork",));
1080 }
1081
1082 #[test]
1083 fn initial_contexts() {
1084 let policy_bytes = include_bytes!(
1085 "../../testdata/micro_policies/multiple_levels_and_categories_policy.pp"
1086 );
1087 let (policy, _) = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1088 let policy = policy.validate().expect("validate policy");
1089
1090 let kernel_context = policy.initial_context(InitialSid::Kernel);
1091 assert_eq!(
1092 policy.serialize_security_context(&kernel_context),
1093 b"user0:object_r:type0:s0:c0-s1:c0.c2,c4"
1094 )
1095 }
1096
1097 #[test]
1098 fn explicit_allow_type_type() {
1099 let policy_bytes =
1100 include_bytes!("../../testdata/micro_policies/allow_a_t_b_t_class0_perm0_policy.pp");
1101 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1102 .expect("parse policy")
1103 .validate()
1104 .expect("validate policy");
1105
1106 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1107 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1108
1109 assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1110 }
1111
1112 #[test]
1113 fn no_explicit_allow_type_type() {
1114 let policy_bytes =
1115 include_bytes!("../../testdata/micro_policies/no_allow_a_t_b_t_class0_perm0_policy.pp");
1116 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1117 .expect("parse policy")
1118 .validate()
1119 .expect("validate policy");
1120
1121 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1122 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1123
1124 assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1125 }
1126
1127 #[test]
1128 fn explicit_allow_type_attr() {
1129 let policy_bytes =
1130 include_bytes!("../../testdata/micro_policies/allow_a_t_b_attr_class0_perm0_policy.pp");
1131 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1132 .expect("parse policy")
1133 .validate()
1134 .expect("validate policy");
1135
1136 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1137 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1138
1139 assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1140 }
1141
1142 #[test]
1143 fn no_explicit_allow_type_attr() {
1144 let policy_bytes = include_bytes!(
1145 "../../testdata/micro_policies/no_allow_a_t_b_attr_class0_perm0_policy.pp"
1146 );
1147 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1148 .expect("parse policy")
1149 .validate()
1150 .expect("validate policy");
1151
1152 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1153 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1154
1155 assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1156 }
1157
1158 #[test]
1159 fn explicit_allow_attr_attr() {
1160 let policy_bytes = include_bytes!(
1161 "../../testdata/micro_policies/allow_a_attr_b_attr_class0_perm0_policy.pp"
1162 );
1163 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1164 .expect("parse policy")
1165 .validate()
1166 .expect("validate policy");
1167
1168 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1169 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1170
1171 assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1172 }
1173
1174 #[test]
1175 fn no_explicit_allow_attr_attr() {
1176 let policy_bytes = include_bytes!(
1177 "../../testdata/micro_policies/no_allow_a_attr_b_attr_class0_perm0_policy.pp"
1178 );
1179 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1180 .expect("parse policy")
1181 .validate()
1182 .expect("validate policy");
1183
1184 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1185 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1186
1187 assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1188 }
1189
1190 #[test]
1191 fn compute_explicitly_allowed_multiple_attributes() {
1192 let policy_bytes = include_bytes!("../../testdata/micro_policies/allow_a_t_a1_attr_class0_perm0_a2_attr_class0_perm1_policy.pp");
1193 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1194 .expect("parse policy")
1195 .validate()
1196 .expect("validate policy");
1197
1198 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1199
1200 let class = policy
1201 .0
1202 .parsed_policy()
1203 .classes()
1204 .iter()
1205 .find(|class| class.name_bytes() == b"class0")
1206 .expect("class not found");
1207 let raw_access_vector =
1208 policy.0.parsed_policy().compute_explicitly_allowed(a_t, a_t, class).allow.0;
1209
1210 assert_eq!(2, raw_access_vector.count_ones());
1215 }
1216
1217 #[test]
1218 fn compute_access_decision_with_constraints() {
1219 let policy_bytes =
1220 include_bytes!("../../testdata/micro_policies/allow_with_constraints_policy.pp");
1221 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1222 .expect("parse policy")
1223 .validate()
1224 .expect("validate policy");
1225
1226 let source_context: SecurityContext = policy
1227 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1228 .expect("create source security context");
1229
1230 let target_context_satisfied: SecurityContext = source_context.clone();
1231 let decision_satisfied = policy.compute_access_decision(
1232 &source_context,
1233 &target_context_satisfied,
1234 KernelClass::File,
1235 );
1236 assert_eq!(decision_satisfied.allow, AccessVector(7));
1240
1241 let target_context_unsatisfied: SecurityContext = policy
1242 .parse_security_context(b"user1:object_r:type0:s0:c0-s0:c0".into())
1243 .expect("create target security context failing some constraints");
1244 let decision_unsatisfied = policy.compute_access_decision(
1245 &source_context,
1246 &target_context_unsatisfied,
1247 KernelClass::File,
1248 );
1249 assert_eq!(decision_unsatisfied.allow, AccessVector(4));
1252 }
1253
1254 #[test]
1255 fn compute_ioctl_access_decision_explicitly_allowed() {
1256 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1257 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1258 .expect("parse policy")
1259 .validate()
1260 .expect("validate policy");
1261
1262 let source_context: SecurityContext = policy
1263 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1264 .expect("create source security context");
1265 let target_context_matched: SecurityContext = source_context.clone();
1266
1267 let decision_single = policy.compute_ioctl_access_decision(
1285 &source_context,
1286 &target_context_matched,
1287 KernelClass::File,
1288 0xab,
1289 );
1290
1291 let mut expected_auditdeny =
1292 xperms_bitmap_from_elements((0x0..=0xff).collect::<Vec<_>>().as_slice());
1293 expected_auditdeny -= &xperms_bitmap_from_elements(&[0xcd, 0xef]);
1294
1295 let expected_decision_single = IoctlAccessDecision {
1296 allow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1297 auditallow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1298 auditdeny: expected_auditdeny,
1299 };
1300 assert_eq!(decision_single, expected_decision_single);
1301
1302 let decision_range = policy.compute_ioctl_access_decision(
1303 &source_context,
1304 &target_context_matched,
1305 KernelClass::File,
1306 0x10,
1307 );
1308 let expected_decision_range = IoctlAccessDecision {
1309 allow: XpermsBitmap::ALL,
1310 auditallow: XpermsBitmap::ALL,
1311 auditdeny: XpermsBitmap::NONE,
1312 };
1313 assert_eq!(decision_range, expected_decision_range);
1314 }
1315
1316 #[test]
1317 fn compute_ioctl_access_decision_unmatched() {
1318 let policy_bytes =
1319 include_bytes!("../../testdata/micro_policies/allow_with_constraints_policy.pp");
1320 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1321 .expect("parse policy")
1322 .validate()
1323 .expect("validate policy");
1324
1325 let source_context: SecurityContext = policy
1326 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1327 .expect("create source security context");
1328
1329 let target_context_unmatched: SecurityContext = policy
1331 .parse_security_context(b"user0:object_r:type1:s0-s0".into())
1332 .expect("create source security context");
1333
1334 for prefix in 0x0..=0xff {
1335 let decision = policy.compute_ioctl_access_decision(
1336 &source_context,
1337 &target_context_unmatched,
1338 KernelClass::File,
1339 prefix,
1340 );
1341 assert_eq!(decision, IoctlAccessDecision::ALLOW_ALL);
1342 }
1343 }
1344
1345 #[test]
1346 fn compute_create_context_minimal() {
1347 let policy_bytes =
1348 include_bytes!("../../testdata/composite_policies/compiled/minimal_policy.pp");
1349 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1350 .expect("parse policy")
1351 .validate()
1352 .expect("validate policy");
1353 let source = policy
1354 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1355 .expect("valid source security context");
1356 let target = policy
1357 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1358 .expect("valid target security context");
1359
1360 let actual = policy.compute_create_context(&source, &target, FileClass::File);
1361 let expected: SecurityContext = policy
1362 .parse_security_context(b"source_u:object_r:target_t:s0:c0".into())
1363 .expect("valid expected security context");
1364
1365 assert_eq!(expected, actual);
1366 }
1367
1368 #[test]
1369 fn new_security_context_minimal() {
1370 let policy_bytes =
1371 include_bytes!("../../testdata/composite_policies/compiled/minimal_policy.pp");
1372 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1373 .expect("parse policy")
1374 .validate()
1375 .expect("validate policy");
1376 let source = policy
1377 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1378 .expect("valid source security context");
1379 let target = policy
1380 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1381 .expect("valid target security context");
1382
1383 let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1384
1385 assert_eq!(source, actual);
1386 }
1387
1388 #[test]
1389 fn compute_create_context_class_defaults() {
1390 let policy_bytes =
1391 include_bytes!("../../testdata/composite_policies/compiled/class_defaults_policy.pp");
1392 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1393 .expect("parse policy")
1394 .validate()
1395 .expect("validate policy");
1396 let source = policy
1397 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1398 .expect("valid source security context");
1399 let target = policy
1400 .parse_security_context(b"target_u:target_r:target_t:s1:c0-s1:c0.c1".into())
1401 .expect("valid target security context");
1402
1403 let actual = policy.compute_create_context(&source, &target, FileClass::File);
1404 let expected: SecurityContext = policy
1405 .parse_security_context(b"target_u:source_r:source_t:s1:c0-s1:c0.c1".into())
1406 .expect("valid expected security context");
1407
1408 assert_eq!(expected, actual);
1409 }
1410
1411 #[test]
1412 fn new_security_context_class_defaults() {
1413 let policy_bytes =
1414 include_bytes!("../../testdata/composite_policies/compiled/class_defaults_policy.pp");
1415 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1416 .expect("parse policy")
1417 .validate()
1418 .expect("validate policy");
1419 let source = policy
1420 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1421 .expect("valid source security context");
1422 let target = policy
1423 .parse_security_context(b"target_u:target_r:target_t:s1:c0-s1:c0.c1".into())
1424 .expect("valid target security context");
1425
1426 let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1427 let expected: SecurityContext = policy
1428 .parse_security_context(b"target_u:source_r:source_t:s1:c0-s1:c0.c1".into())
1429 .expect("valid expected security context");
1430
1431 assert_eq!(expected, actual);
1432 }
1433
1434 #[test]
1435 fn compute_create_context_role_transition() {
1436 let policy_bytes =
1437 include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy.pp");
1438 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1439 .expect("parse policy")
1440 .validate()
1441 .expect("validate policy");
1442 let source = policy
1443 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1444 .expect("valid source security context");
1445 let target = policy
1446 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1447 .expect("valid target security context");
1448
1449 let actual = policy.compute_create_context(&source, &target, FileClass::File);
1450 let expected: SecurityContext = policy
1451 .parse_security_context(b"source_u:transition_r:target_t:s0:c0".into())
1452 .expect("valid expected security context");
1453
1454 assert_eq!(expected, actual);
1455 }
1456
1457 #[test]
1458 fn new_security_context_role_transition() {
1459 let policy_bytes =
1460 include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy.pp");
1461 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1462 .expect("parse policy")
1463 .validate()
1464 .expect("validate policy");
1465 let source = policy
1466 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1467 .expect("valid source security context");
1468 let target = policy
1469 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1470 .expect("valid target security context");
1471
1472 let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1473 let expected: SecurityContext = policy
1474 .parse_security_context(b"source_u:transition_r:source_t:s0:c0-s2:c0.c1".into())
1475 .expect("valid expected security context");
1476
1477 assert_eq!(expected, actual);
1478 }
1479
1480 #[test]
1481 #[ignore]
1483 fn compute_create_context_role_transition_not_allowed() {
1484 let policy_bytes = include_bytes!(
1485 "../../testdata/composite_policies/compiled/role_transition_not_allowed_policy.pp"
1486 );
1487 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1488 .expect("parse policy")
1489 .validate()
1490 .expect("validate policy");
1491 let source = policy
1492 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1493 .expect("valid source security context");
1494 let target = policy
1495 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1496 .expect("valid target security context");
1497
1498 let actual = policy.compute_create_context(&source, &target, FileClass::File);
1499
1500 assert!(policy.validate_security_context(&actual).is_err());
1502 }
1503
1504 #[test]
1505 fn compute_create_context_type_transition() {
1506 let policy_bytes =
1507 include_bytes!("../../testdata/composite_policies/compiled/type_transition_policy.pp");
1508 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1509 .expect("parse policy")
1510 .validate()
1511 .expect("validate policy");
1512 let source = policy
1513 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1514 .expect("valid source security context");
1515 let target = policy
1516 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1517 .expect("valid target security context");
1518
1519 let actual = policy.compute_create_context(&source, &target, FileClass::File);
1520 let expected: SecurityContext = policy
1521 .parse_security_context(b"source_u:object_r:transition_t:s0:c0".into())
1522 .expect("valid expected security context");
1523
1524 assert_eq!(expected, actual);
1525 }
1526
1527 #[test]
1528 fn new_security_context_type_transition() {
1529 let policy_bytes =
1530 include_bytes!("../../testdata/composite_policies/compiled/type_transition_policy.pp");
1531 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1532 .expect("parse policy")
1533 .validate()
1534 .expect("validate policy");
1535 let source = policy
1536 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1537 .expect("valid source security context");
1538 let target = policy
1539 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1540 .expect("valid target security context");
1541
1542 let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1543 let expected: SecurityContext = policy
1544 .parse_security_context(b"source_u:source_r:transition_t:s0:c0-s2:c0.c1".into())
1545 .expect("valid expected security context");
1546
1547 assert_eq!(expected, actual);
1548 }
1549
1550 #[test]
1551 fn compute_create_context_range_transition() {
1552 let policy_bytes =
1553 include_bytes!("../../testdata/composite_policies/compiled/range_transition_policy.pp");
1554 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1555 .expect("parse policy")
1556 .validate()
1557 .expect("validate policy");
1558 let source = policy
1559 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1560 .expect("valid source security context");
1561 let target = policy
1562 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1563 .expect("valid target security context");
1564
1565 let actual = policy.compute_create_context(&source, &target, FileClass::File);
1566 let expected: SecurityContext = policy
1567 .parse_security_context(b"source_u:object_r:target_t:s1:c1-s2:c1.c2".into())
1568 .expect("valid expected security context");
1569
1570 assert_eq!(expected, actual);
1571 }
1572
1573 #[test]
1574 fn new_security_context_range_transition() {
1575 let policy_bytes =
1576 include_bytes!("../../testdata/composite_policies/compiled/range_transition_policy.pp");
1577 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1578 .expect("parse policy")
1579 .validate()
1580 .expect("validate policy");
1581 let source = policy
1582 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1583 .expect("valid source security context");
1584 let target = policy
1585 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1586 .expect("valid target security context");
1587
1588 let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1589 let expected: SecurityContext = policy
1590 .parse_security_context(b"source_u:source_r:source_t:s1:c1-s2:c1.c2".into())
1591 .expect("valid expected security context");
1592
1593 assert_eq!(expected, actual);
1594 }
1595}