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_else(|| 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) =
865 ByRef::<B>::parse::<T>(bytes).ok_or_else(|| ParseError::MissingData {
866 type_name: std::any::type_name::<T>(),
867 type_size: std::mem::size_of::<T>(),
868 num_bytes,
869 })?;
870
871 Ok((data, tail))
872 }
873}
874
875impl<
876 B: Debug + SplitByteSlice + PartialEq,
877 T: Clone + Debug + FromBytes + Immutable + PartialEq + Unaligned,
878 > ParseSlice<ByRef<B>> for Ref<B, [T]>
879{
880 type Error = anyhow::Error;
883
884 fn parse_slice(bytes: ByRef<B>, count: usize) -> Result<(Self, ByRef<B>), Self::Error> {
887 let num_bytes = bytes.len();
888 let (data, tail) = ByRef::<B>::parse_slice::<T>(bytes, count).ok_or_else(|| {
889 ParseError::MissingSliceData {
890 type_name: std::any::type_name::<T>(),
891 type_size: std::mem::size_of::<T>(),
892 num_items: count,
893 num_bytes,
894 }
895 })?;
896
897 Ok((data, tail))
898 }
899}
900
901impl<PS: ParseStrategy, T: Parse<PS>> ParseSlice<PS> for Vec<T> {
902 type Error = anyhow::Error;
905
906 fn parse_slice(bytes: PS, count: usize) -> Result<(Self, PS), Self::Error> {
908 let mut slice = Vec::with_capacity(count);
909 let mut tail = bytes;
910
911 for _ in 0..count {
912 let (item, next_tail) = T::parse(tail).map_err(Into::<anyhow::Error>::into)?;
913 slice.push(item);
914 tail = next_tail;
915 }
916
917 Ok((slice, tail))
918 }
919}
920
921#[cfg(test)]
922pub(super) mod testing {
923 use crate::policy::error::ValidateError;
924 use crate::policy::{AccessVector, ParseError};
925
926 pub const ACCESS_VECTOR_0001: AccessVector = AccessVector(0b0001u32);
927 pub const ACCESS_VECTOR_0010: AccessVector = AccessVector(0b0010u32);
928
929 pub(super) fn as_parse_error(error: anyhow::Error) -> ParseError {
931 error.downcast::<ParseError>().expect("parse error")
932 }
933
934 pub(super) fn as_validate_error(error: anyhow::Error) -> ValidateError {
936 error.downcast::<ValidateError>().expect("validate error")
937 }
938}
939
940#[cfg(test)]
941pub(super) mod tests {
942 use super::*;
943
944 use crate::policy::metadata::HandleUnknown;
945 use crate::policy::{parse_policy_by_reference, parse_policy_by_value, SecurityContext};
946 use crate::{FileClass, InitialSid, KernelClass};
947
948 use serde::Deserialize;
949 use std::ops::Shl;
950
951 fn is_explicitly_allowed<PS: ParseStrategy>(
959 policy: &Policy<PS>,
960 source_type: TypeId,
961 target_type: TypeId,
962 target_class: &str,
963 permission: &str,
964 ) -> bool {
965 let class = policy
966 .0
967 .parsed_policy()
968 .classes()
969 .iter()
970 .find(|class| class.name_bytes() == target_class.as_bytes())
971 .expect("class not found");
972 let class_permissions = policy
973 .find_class_permissions_by_name(target_class)
974 .expect("class permissions not found");
975 let (permission_id, _) = class_permissions
976 .iter()
977 .find(|(_, name)| permission.as_bytes() == name)
978 .expect("permission not found");
979 let permission_bit = AccessVector::from_class_permission_id(*permission_id);
980 let access_decision =
981 policy.0.parsed_policy().compute_explicitly_allowed(source_type, target_type, class);
982 permission_bit == access_decision.allow & permission_bit
983 }
984
985 #[derive(Debug, Deserialize)]
986 struct Expectations {
987 expected_policy_version: u32,
988 expected_handle_unknown: LocalHandleUnknown,
989 }
990
991 #[derive(Debug, Deserialize, PartialEq)]
992 #[serde(rename_all = "snake_case")]
993 enum LocalHandleUnknown {
994 Deny,
995 Reject,
996 Allow,
997 }
998
999 impl PartialEq<HandleUnknown> for LocalHandleUnknown {
1000 fn eq(&self, other: &HandleUnknown) -> bool {
1001 match self {
1002 LocalHandleUnknown::Deny => *other == HandleUnknown::Deny,
1003 LocalHandleUnknown::Reject => *other == HandleUnknown::Reject,
1004 LocalHandleUnknown::Allow => *other == HandleUnknown::Allow,
1005 }
1006 }
1007 }
1008
1009 fn xperms_bitmap_from_elements(elements: &[u8]) -> XpermsBitmap {
1012 let mut bitmap = [le::U32::ZERO; 8];
1013 for element in elements.iter() {
1014 let block_index = (*element as usize) / 32;
1015 let bit_index = ((*element as usize) % 32) as u32;
1016 let bitmask = le::U32::new(1).shl(bit_index);
1017 bitmap[block_index] = bitmap[block_index] | bitmask;
1018 }
1019 XpermsBitmap::new(bitmap)
1020 }
1021
1022 #[test]
1023 fn known_policies() {
1024 let policies_and_expectations = [
1025 [
1026 b"testdata/policies/emulator".to_vec(),
1027 include_bytes!("../../testdata/policies/emulator").to_vec(),
1028 include_bytes!("../../testdata/expectations/emulator").to_vec(),
1029 ],
1030 [
1031 b"testdata/policies/selinux_testsuite".to_vec(),
1032 include_bytes!("../../testdata/policies/selinux_testsuite").to_vec(),
1033 include_bytes!("../../testdata/expectations/selinux_testsuite").to_vec(),
1034 ],
1035 ];
1036
1037 for [policy_path, policy_bytes, expectations_bytes] in policies_and_expectations {
1038 let expectations = serde_json5::from_reader::<_, Expectations>(
1039 &mut std::io::Cursor::new(expectations_bytes),
1040 )
1041 .expect("deserialize expectations");
1042
1043 let (policy, returned_policy_bytes) =
1046 parse_policy_by_value(policy_bytes.clone()).expect("parse policy");
1047
1048 let policy = policy
1049 .validate()
1050 .with_context(|| {
1051 format!(
1052 "policy path: {:?}",
1053 std::str::from_utf8(policy_path.as_slice()).unwrap()
1054 )
1055 })
1056 .expect("validate policy");
1057
1058 assert_eq!(expectations.expected_policy_version, policy.policy_version());
1059 assert_eq!(expectations.expected_handle_unknown, policy.handle_unknown());
1060
1061 assert_eq!(policy_bytes, returned_policy_bytes);
1063
1064 let policy = parse_policy_by_reference(policy_bytes.as_slice()).expect("parse policy");
1066 let policy = policy.validate().expect("validate policy");
1067
1068 assert_eq!(expectations.expected_policy_version, policy.policy_version());
1069 assert_eq!(expectations.expected_handle_unknown, policy.handle_unknown());
1070 }
1071 }
1072
1073 #[test]
1074 fn policy_lookup() {
1075 let policy_bytes = include_bytes!("../../testdata/policies/selinux_testsuite");
1076 let (policy, _) = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1077 let policy = policy.validate().expect("validate selinux testsuite policy");
1078
1079 let unconfined_t = policy.type_id_by_name("unconfined_t").expect("look up type id");
1080
1081 assert!(is_explicitly_allowed(&policy, unconfined_t, unconfined_t, "process", "fork",));
1082 }
1083
1084 #[test]
1085 fn initial_contexts() {
1086 let policy_bytes = include_bytes!(
1087 "../../testdata/micro_policies/multiple_levels_and_categories_policy.pp"
1088 );
1089 let (policy, _) = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1090 let policy = policy.validate().expect("validate policy");
1091
1092 let kernel_context = policy.initial_context(InitialSid::Kernel);
1093 assert_eq!(
1094 policy.serialize_security_context(&kernel_context),
1095 b"user0:object_r:type0:s0:c0-s1:c0.c2,c4"
1096 )
1097 }
1098
1099 #[test]
1100 fn explicit_allow_type_type() {
1101 let policy_bytes =
1102 include_bytes!("../../testdata/micro_policies/allow_a_t_b_t_class0_perm0_policy.pp");
1103 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1104 .expect("parse policy")
1105 .validate()
1106 .expect("validate policy");
1107
1108 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1109 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1110
1111 assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1112 }
1113
1114 #[test]
1115 fn no_explicit_allow_type_type() {
1116 let policy_bytes =
1117 include_bytes!("../../testdata/micro_policies/no_allow_a_t_b_t_class0_perm0_policy.pp");
1118 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1119 .expect("parse policy")
1120 .validate()
1121 .expect("validate policy");
1122
1123 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1124 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1125
1126 assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1127 }
1128
1129 #[test]
1130 fn explicit_allow_type_attr() {
1131 let policy_bytes =
1132 include_bytes!("../../testdata/micro_policies/allow_a_t_b_attr_class0_perm0_policy.pp");
1133 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1134 .expect("parse policy")
1135 .validate()
1136 .expect("validate policy");
1137
1138 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1139 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1140
1141 assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1142 }
1143
1144 #[test]
1145 fn no_explicit_allow_type_attr() {
1146 let policy_bytes = include_bytes!(
1147 "../../testdata/micro_policies/no_allow_a_t_b_attr_class0_perm0_policy.pp"
1148 );
1149 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1150 .expect("parse policy")
1151 .validate()
1152 .expect("validate policy");
1153
1154 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1155 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1156
1157 assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1158 }
1159
1160 #[test]
1161 fn explicit_allow_attr_attr() {
1162 let policy_bytes = include_bytes!(
1163 "../../testdata/micro_policies/allow_a_attr_b_attr_class0_perm0_policy.pp"
1164 );
1165 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1166 .expect("parse policy")
1167 .validate()
1168 .expect("validate policy");
1169
1170 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1171 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1172
1173 assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1174 }
1175
1176 #[test]
1177 fn no_explicit_allow_attr_attr() {
1178 let policy_bytes = include_bytes!(
1179 "../../testdata/micro_policies/no_allow_a_attr_b_attr_class0_perm0_policy.pp"
1180 );
1181 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1182 .expect("parse policy")
1183 .validate()
1184 .expect("validate policy");
1185
1186 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1187 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1188
1189 assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1190 }
1191
1192 #[test]
1193 fn compute_explicitly_allowed_multiple_attributes() {
1194 let policy_bytes = include_bytes!("../../testdata/micro_policies/allow_a_t_a1_attr_class0_perm0_a2_attr_class0_perm1_policy.pp");
1195 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1196 .expect("parse policy")
1197 .validate()
1198 .expect("validate policy");
1199
1200 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1201
1202 let class = policy
1203 .0
1204 .parsed_policy()
1205 .classes()
1206 .iter()
1207 .find(|class| class.name_bytes() == b"class0")
1208 .expect("class not found");
1209 let raw_access_vector =
1210 policy.0.parsed_policy().compute_explicitly_allowed(a_t, a_t, class).allow.0;
1211
1212 assert_eq!(2, raw_access_vector.count_ones());
1217 }
1218
1219 #[test]
1220 fn compute_access_decision_with_constraints() {
1221 let policy_bytes =
1222 include_bytes!("../../testdata/micro_policies/allow_with_constraints_policy.pp");
1223 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1224 .expect("parse policy")
1225 .validate()
1226 .expect("validate policy");
1227
1228 let source_context: SecurityContext = policy
1229 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1230 .expect("create source security context");
1231
1232 let target_context_satisfied: SecurityContext = source_context.clone();
1233 let decision_satisfied = policy.compute_access_decision(
1234 &source_context,
1235 &target_context_satisfied,
1236 KernelClass::File,
1237 );
1238 assert_eq!(decision_satisfied.allow, AccessVector(7));
1242
1243 let target_context_unsatisfied: SecurityContext = policy
1244 .parse_security_context(b"user1:object_r:type0:s0:c0-s0:c0".into())
1245 .expect("create target security context failing some constraints");
1246 let decision_unsatisfied = policy.compute_access_decision(
1247 &source_context,
1248 &target_context_unsatisfied,
1249 KernelClass::File,
1250 );
1251 assert_eq!(decision_unsatisfied.allow, AccessVector(4));
1254 }
1255
1256 #[test]
1257 fn compute_ioctl_access_decision_explicitly_allowed() {
1258 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_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 let target_context_matched: SecurityContext = source_context.clone();
1268
1269 let decision_single = policy.compute_ioctl_access_decision(
1287 &source_context,
1288 &target_context_matched,
1289 KernelClass::File,
1290 0xab,
1291 );
1292
1293 let mut expected_auditdeny =
1294 xperms_bitmap_from_elements((0x0..=0xff).collect::<Vec<_>>().as_slice());
1295 expected_auditdeny -= &xperms_bitmap_from_elements(&[0xcd, 0xef]);
1296
1297 let expected_decision_single = IoctlAccessDecision {
1298 allow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1299 auditallow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1300 auditdeny: expected_auditdeny,
1301 };
1302 assert_eq!(decision_single, expected_decision_single);
1303
1304 let decision_range = policy.compute_ioctl_access_decision(
1305 &source_context,
1306 &target_context_matched,
1307 KernelClass::File,
1308 0x10,
1309 );
1310 let expected_decision_range = IoctlAccessDecision {
1311 allow: XpermsBitmap::ALL,
1312 auditallow: XpermsBitmap::ALL,
1313 auditdeny: XpermsBitmap::NONE,
1314 };
1315 assert_eq!(decision_range, expected_decision_range);
1316 }
1317
1318 #[test]
1319 fn compute_ioctl_access_decision_unmatched() {
1320 let policy_bytes =
1321 include_bytes!("../../testdata/micro_policies/allow_with_constraints_policy.pp");
1322 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1323 .expect("parse policy")
1324 .validate()
1325 .expect("validate policy");
1326
1327 let source_context: SecurityContext = policy
1328 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1329 .expect("create source security context");
1330
1331 let target_context_unmatched: SecurityContext = policy
1333 .parse_security_context(b"user0:object_r:type1:s0-s0".into())
1334 .expect("create source security context");
1335
1336 for prefix in 0x0..=0xff {
1337 let decision = policy.compute_ioctl_access_decision(
1338 &source_context,
1339 &target_context_unmatched,
1340 KernelClass::File,
1341 prefix,
1342 );
1343 assert_eq!(decision, IoctlAccessDecision::ALLOW_ALL);
1344 }
1345 }
1346
1347 #[test]
1348 fn compute_create_context_minimal() {
1349 let policy_bytes =
1350 include_bytes!("../../testdata/composite_policies/compiled/minimal_policy.pp");
1351 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1352 .expect("parse policy")
1353 .validate()
1354 .expect("validate policy");
1355 let source = policy
1356 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1357 .expect("valid source security context");
1358 let target = policy
1359 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1360 .expect("valid target security context");
1361
1362 let actual = policy.compute_create_context(&source, &target, FileClass::File);
1363 let expected: SecurityContext = policy
1364 .parse_security_context(b"source_u:object_r:target_t:s0:c0".into())
1365 .expect("valid expected security context");
1366
1367 assert_eq!(expected, actual);
1368 }
1369
1370 #[test]
1371 fn new_security_context_minimal() {
1372 let policy_bytes =
1373 include_bytes!("../../testdata/composite_policies/compiled/minimal_policy.pp");
1374 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1375 .expect("parse policy")
1376 .validate()
1377 .expect("validate policy");
1378 let source = policy
1379 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1380 .expect("valid source security context");
1381 let target = policy
1382 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1383 .expect("valid target security context");
1384
1385 let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1386
1387 assert_eq!(source, actual);
1388 }
1389
1390 #[test]
1391 fn compute_create_context_class_defaults() {
1392 let policy_bytes =
1393 include_bytes!("../../testdata/composite_policies/compiled/class_defaults_policy.pp");
1394 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1395 .expect("parse policy")
1396 .validate()
1397 .expect("validate policy");
1398 let source = policy
1399 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1400 .expect("valid source security context");
1401 let target = policy
1402 .parse_security_context(b"target_u:target_r:target_t:s1:c0-s1:c0.c1".into())
1403 .expect("valid target security context");
1404
1405 let actual = policy.compute_create_context(&source, &target, FileClass::File);
1406 let expected: SecurityContext = policy
1407 .parse_security_context(b"target_u:source_r:source_t:s1:c0-s1:c0.c1".into())
1408 .expect("valid expected security context");
1409
1410 assert_eq!(expected, actual);
1411 }
1412
1413 #[test]
1414 fn new_security_context_class_defaults() {
1415 let policy_bytes =
1416 include_bytes!("../../testdata/composite_policies/compiled/class_defaults_policy.pp");
1417 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1418 .expect("parse policy")
1419 .validate()
1420 .expect("validate policy");
1421 let source = policy
1422 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1423 .expect("valid source security context");
1424 let target = policy
1425 .parse_security_context(b"target_u:target_r:target_t:s1:c0-s1:c0.c1".into())
1426 .expect("valid target security context");
1427
1428 let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1429 let expected: SecurityContext = policy
1430 .parse_security_context(b"target_u:source_r:source_t:s1:c0-s1:c0.c1".into())
1431 .expect("valid expected security context");
1432
1433 assert_eq!(expected, actual);
1434 }
1435
1436 #[test]
1437 fn compute_create_context_role_transition() {
1438 let policy_bytes =
1439 include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy.pp");
1440 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1441 .expect("parse policy")
1442 .validate()
1443 .expect("validate policy");
1444 let source = policy
1445 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1446 .expect("valid source security context");
1447 let target = policy
1448 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1449 .expect("valid target security context");
1450
1451 let actual = policy.compute_create_context(&source, &target, FileClass::File);
1452 let expected: SecurityContext = policy
1453 .parse_security_context(b"source_u:transition_r:target_t:s0:c0".into())
1454 .expect("valid expected security context");
1455
1456 assert_eq!(expected, actual);
1457 }
1458
1459 #[test]
1460 fn new_security_context_role_transition() {
1461 let policy_bytes =
1462 include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy.pp");
1463 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1464 .expect("parse policy")
1465 .validate()
1466 .expect("validate policy");
1467 let source = policy
1468 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1469 .expect("valid source security context");
1470 let target = policy
1471 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1472 .expect("valid target security context");
1473
1474 let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1475 let expected: SecurityContext = policy
1476 .parse_security_context(b"source_u:transition_r:source_t:s0:c0-s2:c0.c1".into())
1477 .expect("valid expected security context");
1478
1479 assert_eq!(expected, actual);
1480 }
1481
1482 #[test]
1483 #[ignore]
1485 fn compute_create_context_role_transition_not_allowed() {
1486 let policy_bytes = include_bytes!(
1487 "../../testdata/composite_policies/compiled/role_transition_not_allowed_policy.pp"
1488 );
1489 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1490 .expect("parse policy")
1491 .validate()
1492 .expect("validate policy");
1493 let source = policy
1494 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1495 .expect("valid source security context");
1496 let target = policy
1497 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1498 .expect("valid target security context");
1499
1500 let actual = policy.compute_create_context(&source, &target, FileClass::File);
1501
1502 assert!(policy.validate_security_context(&actual).is_err());
1504 }
1505
1506 #[test]
1507 fn compute_create_context_type_transition() {
1508 let policy_bytes =
1509 include_bytes!("../../testdata/composite_policies/compiled/type_transition_policy.pp");
1510 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1511 .expect("parse policy")
1512 .validate()
1513 .expect("validate policy");
1514 let source = policy
1515 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1516 .expect("valid source security context");
1517 let target = policy
1518 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1519 .expect("valid target security context");
1520
1521 let actual = policy.compute_create_context(&source, &target, FileClass::File);
1522 let expected: SecurityContext = policy
1523 .parse_security_context(b"source_u:object_r:transition_t:s0:c0".into())
1524 .expect("valid expected security context");
1525
1526 assert_eq!(expected, actual);
1527 }
1528
1529 #[test]
1530 fn new_security_context_type_transition() {
1531 let policy_bytes =
1532 include_bytes!("../../testdata/composite_policies/compiled/type_transition_policy.pp");
1533 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1534 .expect("parse policy")
1535 .validate()
1536 .expect("validate policy");
1537 let source = policy
1538 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1539 .expect("valid source security context");
1540 let target = policy
1541 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1542 .expect("valid target security context");
1543
1544 let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1545 let expected: SecurityContext = policy
1546 .parse_security_context(b"source_u:source_r:transition_t:s0:c0-s2:c0.c1".into())
1547 .expect("valid expected security context");
1548
1549 assert_eq!(expected, actual);
1550 }
1551
1552 #[test]
1553 fn compute_create_context_range_transition() {
1554 let policy_bytes =
1555 include_bytes!("../../testdata/composite_policies/compiled/range_transition_policy.pp");
1556 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1557 .expect("parse policy")
1558 .validate()
1559 .expect("validate policy");
1560 let source = policy
1561 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1562 .expect("valid source security context");
1563 let target = policy
1564 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1565 .expect("valid target security context");
1566
1567 let actual = policy.compute_create_context(&source, &target, FileClass::File);
1568 let expected: SecurityContext = policy
1569 .parse_security_context(b"source_u:object_r:target_t:s1:c1-s2:c1.c2".into())
1570 .expect("valid expected security context");
1571
1572 assert_eq!(expected, actual);
1573 }
1574
1575 #[test]
1576 fn new_security_context_range_transition() {
1577 let policy_bytes =
1578 include_bytes!("../../testdata/composite_policies/compiled/range_transition_policy.pp");
1579 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1580 .expect("parse policy")
1581 .validate()
1582 .expect("validate policy");
1583 let source = policy
1584 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1585 .expect("valid source security context");
1586 let target = policy
1587 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1588 .expect("valid target security context");
1589
1590 let actual = policy.compute_create_context(&source, &target, KernelClass::Process);
1591 let expected: SecurityContext = policy
1592 .parse_security_context(b"source_u:source_r:source_t:s1:c1-s2:c1.c2".into())
1593 .expect("valid expected security context");
1594
1595 assert_eq!(expected, actual);
1596 }
1597}