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};
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)]
63pub struct ClassId(NonZeroU32);
64
65impl ClassId {
66 pub fn new(id: NonZeroU32) -> Self {
68 Self(id)
69 }
70}
71
72impl Into<u32> for ClassId {
73 fn into(self) -> u32 {
74 self.0.into()
75 }
76}
77
78#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
80pub struct ClassPermissionId(NonZeroU32);
81
82impl Display for ClassPermissionId {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 write!(f, "{}", self.0)
85 }
86}
87
88#[derive(Debug, Clone, PartialEq)]
93pub struct AccessDecision {
94 pub allow: AccessVector,
95 pub auditallow: AccessVector,
96 pub auditdeny: AccessVector,
97 pub flags: u32,
98
99 pub todo_bug: Option<NonZeroU64>,
102}
103
104impl Default for AccessDecision {
105 fn default() -> Self {
106 Self::allow(AccessVector::NONE)
107 }
108}
109
110impl AccessDecision {
111 pub(super) const fn allow(allow: AccessVector) -> Self {
114 Self {
115 allow,
116 auditallow: AccessVector::NONE,
117 auditdeny: AccessVector::ALL,
118 flags: 0,
119 todo_bug: None,
120 }
121 }
122}
123
124pub(super) const SELINUX_AVD_FLAGS_PERMISSIVE: u32 = 1;
126
127#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
130pub struct AccessVector(u32);
131
132impl AccessVector {
133 pub const NONE: AccessVector = AccessVector(0);
134 pub const ALL: AccessVector = AccessVector(std::u32::MAX);
135
136 pub(super) fn from_class_permission_id(id: ClassPermissionId) -> Self {
137 Self((1 as u32) << (id.0.get() - 1))
138 }
139}
140
141impl std::ops::BitAnd for AccessVector {
142 type Output = Self;
143
144 fn bitand(self, rhs: Self) -> Self::Output {
145 AccessVector(self.0 & rhs.0)
146 }
147}
148
149impl std::ops::BitOr for AccessVector {
150 type Output = Self;
151
152 fn bitor(self, rhs: Self) -> Self::Output {
153 AccessVector(self.0 | rhs.0)
154 }
155}
156
157impl std::ops::BitAndAssign for AccessVector {
158 fn bitand_assign(&mut self, rhs: Self) {
159 self.0 &= rhs.0
160 }
161}
162
163impl std::ops::BitOrAssign for AccessVector {
164 fn bitor_assign(&mut self, rhs: Self) {
165 self.0 |= rhs.0
166 }
167}
168
169impl std::ops::SubAssign for AccessVector {
170 fn sub_assign(&mut self, rhs: Self) {
171 self.0 = self.0 ^ (self.0 & rhs.0);
172 }
173}
174
175#[derive(Debug, Clone, PartialEq)]
185pub struct IoctlAccessDecision {
186 pub allow: XpermsBitmap,
187 pub auditallow: XpermsBitmap,
188 pub auditdeny: XpermsBitmap,
189}
190
191impl IoctlAccessDecision {
192 pub const DENY_ALL: Self = Self {
193 allow: XpermsBitmap::NONE,
194 auditallow: XpermsBitmap::NONE,
195 auditdeny: XpermsBitmap::ALL,
196 };
197 pub const ALLOW_ALL: Self = Self {
198 allow: XpermsBitmap::ALL,
199 auditallow: XpermsBitmap::NONE,
200 auditdeny: XpermsBitmap::ALL,
201 };
202}
203
204pub fn parse_policy_by_value(
231 binary_policy: Vec<u8>,
232) -> Result<(Unvalidated<ByValue<Vec<u8>>>, Vec<u8>), anyhow::Error> {
233 let (parsed_policy, binary_policy) =
234 ParsedPolicy::parse(ByValue::new(binary_policy)).context("parsing policy")?;
235 Ok((Unvalidated(parsed_policy), binary_policy))
236}
237
238pub fn parse_policy_by_reference<'a>(
245 binary_policy: &'a [u8],
246) -> Result<Unvalidated<ByRef<&'a [u8]>>, anyhow::Error> {
247 let (parsed_policy, _) =
248 ParsedPolicy::parse(ByRef::new(binary_policy)).context("parsing policy")?;
249 Ok(Unvalidated(parsed_policy))
250}
251
252pub struct ClassInfo<'a> {
254 pub class_name: &'a [u8],
256 pub class_id: ClassId,
258}
259
260#[derive(Debug)]
261pub struct Policy<PS: ParseStrategy>(PolicyIndex<PS>);
262
263impl<PS: ParseStrategy> Policy<PS> {
264 pub fn policy_version(&self) -> u32 {
266 self.0.parsed_policy().policy_version()
267 }
268
269 pub fn handle_unknown(&self) -> HandleUnknown {
272 self.0.parsed_policy().handle_unknown()
273 }
274
275 pub fn conditional_booleans<'a>(&'a self) -> Vec<(&'a [u8], bool)> {
276 self.0
277 .parsed_policy()
278 .conditional_booleans()
279 .iter()
280 .map(|boolean| (PS::deref_slice(&boolean.data), PS::deref(&boolean.metadata).active()))
281 .collect()
282 }
283
284 pub fn classes<'a>(&'a self) -> Vec<ClassInfo<'a>> {
286 self.0
287 .parsed_policy()
288 .classes()
289 .iter()
290 .map(|class| ClassInfo { class_name: class.name_bytes(), class_id: class.id() })
291 .collect()
292 }
293
294 pub(super) fn type_id_by_name(&self, name: &str) -> Option<TypeId> {
296 self.0.parsed_policy().type_by_name(name).map(|x| x.id())
297 }
298
299 pub fn find_class_permissions_by_name(
304 &self,
305 class_name: &str,
306 ) -> Result<Vec<(ClassPermissionId, Vec<u8>)>, ()> {
307 let class = find_class_by_name(self.0.parsed_policy().classes(), class_name).ok_or(())?;
308 let owned_permissions = class.permissions();
309
310 let mut result: Vec<_> = owned_permissions
311 .iter()
312 .map(|permission| (permission.id(), permission.name_bytes().to_vec()))
313 .collect();
314
315 if class.common_name_bytes().is_empty() {
317 return Ok(result);
318 }
319
320 let common_symbol_permissions = find_common_symbol_by_name_bytes(
321 self.0.parsed_policy().common_symbols(),
322 class.common_name_bytes(),
323 )
324 .ok_or(())?
325 .permissions();
326
327 result.append(
328 &mut common_symbol_permissions
329 .iter()
330 .map(|permission| (permission.id(), permission.name_bytes().to_vec()))
331 .collect(),
332 );
333
334 Ok(result)
335 }
336
337 pub fn fs_use_label_and_type(
340 &self,
341 fs_type: sc::NullessByteStr<'_>,
342 ) -> Option<FsUseLabelAndType> {
343 self.0.fs_use_label_and_type(fs_type)
344 }
345
346 pub fn genfscon_label_for_fs_and_path(
349 &self,
350 fs_type: sc::NullessByteStr<'_>,
351 node_path: sc::NullessByteStr<'_>,
352 class_id: Option<ClassId>,
353 ) -> Option<SecurityContext> {
354 self.0.genfscon_label_for_fs_and_path(fs_type, node_path, class_id)
355 }
356
357 pub fn initial_context(&self, id: sc::InitialSid) -> security_context::SecurityContext {
360 self.0.initial_context(id)
361 }
362
363 pub fn parse_security_context(
365 &self,
366 security_context: sc::NullessByteStr<'_>,
367 ) -> Result<security_context::SecurityContext, security_context::SecurityContextError> {
368 security_context::SecurityContext::parse(&self.0, security_context)
369 }
370
371 pub fn validate_security_context(
373 &self,
374 security_context: &SecurityContext,
375 ) -> Result<(), SecurityContextError> {
376 security_context.validate(&self.0)
377 }
378
379 pub fn serialize_security_context(&self, security_context: &SecurityContext) -> Vec<u8> {
381 security_context.serialize(&self.0)
382 }
383
384 pub fn new_file_security_context(
389 &self,
390 source: &SecurityContext,
391 target: &SecurityContext,
392 class: impl Into<sc::FsNodeClass>,
393 ) -> SecurityContext {
394 self.0.new_file_security_context(source, target, class.into())
395 }
396
397 pub fn new_file_security_context_by_name(
403 &self,
404 source: &SecurityContext,
405 target: &SecurityContext,
406 class: impl Into<sc::FsNodeClass>,
407 name: sc::NullessByteStr<'_>,
408 ) -> Option<SecurityContext> {
409 self.0.new_file_security_context_by_name(source, target, class.into(), name)
410 }
411
412 pub fn new_security_context(
421 &self,
422 source: &SecurityContext,
423 target: &SecurityContext,
424 class: impl Into<sc::ObjectClass>,
425 ) -> SecurityContext {
426 self.0.new_security_context(
427 source,
428 target,
429 class.into(),
430 source.role(),
431 source.type_(),
432 source.low_level(),
433 source.high_level(),
434 )
435 }
436
437 pub fn compute_access_decision(
453 &self,
454 source_context: &SecurityContext,
455 target_context: &SecurityContext,
456 object_class: impl Into<sc::ObjectClass>,
457 ) -> AccessDecision {
458 if let Some(target_class) = self.0.class(object_class.into()) {
459 self.0.parsed_policy().compute_access_decision(
460 source_context,
461 target_context,
462 target_class,
463 )
464 } else {
465 AccessDecision::allow(AccessVector::NONE)
466 }
467 }
468
469 pub fn compute_ioctl_access_decision(
473 &self,
474 source_context: &SecurityContext,
475 target_context: &SecurityContext,
476 object_class: impl Into<sc::ObjectClass>,
477 ioctl_prefix: u8,
478 ) -> IoctlAccessDecision {
479 if let Some(target_class) = self.0.class(object_class.into()) {
480 self.0.parsed_policy().compute_ioctl_access_decision(
481 source_context,
482 target_context,
483 target_class,
484 ioctl_prefix,
485 )
486 } else {
487 IoctlAccessDecision::DENY_ALL
488 }
489 }
490
491 pub fn is_bounded_by(&self, bounded_type: TypeId, parent_type: TypeId) -> bool {
492 let type_ = self.0.parsed_policy().type_(bounded_type);
493 type_.bounded_by() == Some(parent_type)
494 }
495
496 pub fn is_permissive(&self, type_: TypeId) -> bool {
498 self.0.parsed_policy().permissive_types().is_set(type_.0.get())
499 }
500}
501
502impl<PS: ParseStrategy> AccessVectorComputer for Policy<PS> {
503 fn access_vector_from_permissions<
504 P: sc::ClassPermission + Into<sc::KernelPermission> + Clone + 'static,
505 >(
506 &self,
507 permissions: &[P],
508 ) -> Option<AccessVector> {
509 let mut access_vector = AccessVector::NONE;
510 for permission in permissions {
511 if let Some(permission_info) = self.0.permission(&permission.clone().into()) {
512 access_vector |= AccessVector::from_class_permission_id(permission_info.id());
514 } else {
515 if self.0.parsed_policy().handle_unknown() != HandleUnknown::Allow {
517 return None;
518 }
519 }
520 }
521 Some(access_vector)
522 }
523}
524
525impl<PS: ParseStrategy> Validate for Policy<PS> {
526 type Error = anyhow::Error;
527
528 fn validate(&self) -> Result<(), Self::Error> {
529 self.0.parsed_policy().validate()
530 }
531}
532
533pub struct Unvalidated<PS: ParseStrategy>(ParsedPolicy<PS>);
535
536impl<PS: ParseStrategy> Unvalidated<PS> {
537 pub fn validate(self) -> Result<Policy<PS>, anyhow::Error> {
538 Validate::validate(&self.0).context("validating parsed policy")?;
539 let index = PolicyIndex::new(self.0).context("building index")?;
540 Ok(Policy(index))
541 }
542}
543
544pub trait AccessVectorComputer {
547 fn access_vector_from_permissions<
554 P: sc::ClassPermission + Into<sc::KernelPermission> + Clone + 'static,
555 >(
556 &self,
557 permissions: &[P],
558 ) -> Option<AccessVector>;
559}
560
561pub trait Parse<PS: ParseStrategy>: Sized {
563 type Error: Into<anyhow::Error>;
566
567 fn parse(bytes: PS) -> Result<(Self, PS), Self::Error>;
570}
571
572pub(super) trait ParseSlice<PS: ParseStrategy>: Sized {
574 type Error: Into<anyhow::Error>;
577
578 fn parse_slice(bytes: PS, count: usize) -> Result<(Self, PS), Self::Error>;
581}
582
583pub(super) trait Validate {
585 type Error: Into<anyhow::Error>;
588
589 fn validate(&self) -> Result<(), Self::Error>;
591}
592
593pub(super) trait ValidateArray<M, D> {
594 type Error: Into<anyhow::Error>;
597
598 fn validate_array<'a>(metadata: &'a M, data: &'a [D]) -> Result<(), Self::Error>;
600}
601
602pub(super) trait Counted {
604 fn count(&self) -> u32;
606}
607
608impl<T: Validate> Validate for Option<T> {
609 type Error = <T as Validate>::Error;
610
611 fn validate(&self) -> Result<(), Self::Error> {
612 match self {
613 Some(value) => value.validate(),
614 None => Ok(()),
615 }
616 }
617}
618
619impl Validate for le::U32 {
620 type Error = anyhow::Error;
621
622 fn validate(&self) -> Result<(), Self::Error> {
625 Ok(())
626 }
627}
628
629impl Validate for u8 {
630 type Error = anyhow::Error;
631
632 fn validate(&self) -> Result<(), Self::Error> {
635 Ok(())
636 }
637}
638
639impl Validate for [u8] {
640 type Error = anyhow::Error;
641
642 fn validate(&self) -> Result<(), Self::Error> {
645 Ok(())
646 }
647}
648
649impl<B: SplitByteSlice, T: Validate + FromBytes + KnownLayout + Immutable> Validate for Ref<B, T> {
650 type Error = <T as Validate>::Error;
651
652 fn validate(&self) -> Result<(), Self::Error> {
653 self.deref().validate()
654 }
655}
656
657impl<B: SplitByteSlice, T: Counted + FromBytes + KnownLayout + Immutable> Counted for Ref<B, T> {
658 fn count(&self) -> u32 {
659 self.deref().count()
660 }
661}
662
663#[derive(Clone, Debug, PartialEq)]
666struct Array<PS, M, D> {
667 metadata: M,
668 data: D,
669 _marker: PhantomData<PS>,
670}
671
672impl<PS: ParseStrategy, M: Counted + Parse<PS>, D: ParseSlice<PS>> Parse<PS> for Array<PS, M, D> {
673 type Error = anyhow::Error;
676
677 fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
679 let tail = bytes;
680
681 let (metadata, tail) = M::parse(tail).map_err(Into::<anyhow::Error>::into)?;
682
683 let (data, tail) =
684 D::parse_slice(tail, metadata.count() as usize).map_err(Into::<anyhow::Error>::into)?;
685
686 let array = Self { metadata, data, _marker: PhantomData };
687
688 Ok((array, tail))
689 }
690}
691
692impl<
693 T: Clone + Debug + FromBytes + KnownLayout + Immutable + PartialEq + Unaligned,
694 PS: ParseStrategy<Output<T> = T>,
695 > Parse<PS> for T
696{
697 type Error = anyhow::Error;
698
699 fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
700 let num_bytes = bytes.len();
701 let (data, tail) = PS::parse::<T>(bytes).ok_or(ParseError::MissingData {
702 type_name: std::any::type_name::<T>(),
703 type_size: std::mem::size_of::<T>(),
704 num_bytes,
705 })?;
706
707 Ok((data, tail))
708 }
709}
710
711macro_rules! array_type {
715 ($type_name:ident, $parse_strategy:ident, $metadata_type:ty, $data_type:ty, $metadata_type_name:expr, $data_type_name:expr) => {
716 #[doc = "An [`Array`] with [`"]
717 #[doc = $metadata_type_name]
718 #[doc = "`] metadata and [`"]
719 #[doc = $data_type_name]
720 #[doc = "`] data items."]
721 #[derive(Debug, PartialEq)]
722 pub(super) struct $type_name<$parse_strategy: crate::policy::parser::ParseStrategy>(
723 crate::policy::Array<PS, $metadata_type, $data_type>,
724 );
725
726 impl<PS: crate::policy::parser::ParseStrategy> std::ops::Deref for $type_name<PS> {
727 type Target = crate::policy::Array<PS, $metadata_type, $data_type>;
728
729 fn deref(&self) -> &Self::Target {
730 &self.0
731 }
732 }
733
734 impl<PS: crate::policy::parser::ParseStrategy> crate::policy::Parse<PS> for $type_name<PS>
735 where
736 crate::policy::Array<PS, $metadata_type, $data_type>: crate::policy::Parse<PS>,
737 {
738 type Error = <Array<PS, $metadata_type, $data_type> as crate::policy::Parse<PS>>::Error;
739
740 fn parse(bytes: PS) -> Result<(Self, PS), Self::Error> {
741 let (array, tail) = Array::<PS, $metadata_type, $data_type>::parse(bytes)?;
742 Ok((Self(array), tail))
743 }
744 }
745 };
746
747 ($type_name:ident, $parse_strategy:ident, $metadata_type:ty, $data_type:ty) => {
748 array_type!(
749 $type_name,
750 $parse_strategy,
751 $metadata_type,
752 $data_type,
753 stringify!($metadata_type),
754 stringify!($data_type)
755 );
756 };
757}
758
759pub(super) use array_type;
760
761macro_rules! array_type_validate_deref_both {
762 ($type_name:ident) => {
763 impl<PS: crate::policy::parser::ParseStrategy> Validate for $type_name<PS> {
764 type Error = anyhow::Error;
765
766 fn validate(&self) -> Result<(), Self::Error> {
767 let metadata = PS::deref(&self.metadata);
768 metadata.validate()?;
769
770 let data = PS::deref_slice(&self.data);
771 data.validate()?;
772
773 Self::validate_array(metadata, data).map_err(Into::<anyhow::Error>::into)
774 }
775 }
776 };
777}
778
779pub(super) use array_type_validate_deref_both;
780
781macro_rules! array_type_validate_deref_data {
782 ($type_name:ident) => {
783 impl<PS: crate::policy::parser::ParseStrategy> Validate for $type_name<PS> {
784 type Error = anyhow::Error;
785
786 fn validate(&self) -> Result<(), Self::Error> {
787 let metadata = &self.metadata;
788 metadata.validate()?;
789
790 let data = PS::deref_slice(&self.data);
791 data.validate()?;
792
793 Self::validate_array(metadata, data)
794 }
795 }
796 };
797}
798
799pub(super) use array_type_validate_deref_data;
800
801macro_rules! array_type_validate_deref_metadata_data_vec {
802 ($type_name:ident) => {
803 impl<PS: crate::policy::parser::ParseStrategy> Validate for $type_name<PS> {
804 type Error = anyhow::Error;
805
806 fn validate(&self) -> Result<(), Self::Error> {
807 let metadata = PS::deref(&self.metadata);
808 metadata.validate()?;
809
810 let data = &self.data;
811 data.validate()?;
812
813 Self::validate_array(metadata, data.as_slice())
814 }
815 }
816 };
817}
818
819pub(super) use array_type_validate_deref_metadata_data_vec;
820
821macro_rules! array_type_validate_deref_none_data_vec {
822 ($type_name:ident) => {
823 impl<PS: crate::policy::parser::ParseStrategy> Validate for $type_name<PS> {
824 type Error = anyhow::Error;
825
826 fn validate(&self) -> Result<(), Self::Error> {
827 let metadata = &self.metadata;
828 metadata.validate()?;
829
830 let data = &self.data;
831 data.validate()?;
832
833 Self::validate_array(metadata, data.as_slice())
834 }
835 }
836 };
837}
838
839pub(super) use array_type_validate_deref_none_data_vec;
840
841impl<
842 B: Debug + SplitByteSlice + PartialEq,
843 T: Clone + Debug + FromBytes + KnownLayout + Immutable + PartialEq + Unaligned,
844 > Parse<ByRef<B>> for Ref<B, T>
845{
846 type Error = anyhow::Error;
847
848 fn parse(bytes: ByRef<B>) -> Result<(Self, ByRef<B>), Self::Error> {
849 let num_bytes = bytes.len();
850 let (data, tail) = ByRef::<B>::parse::<T>(bytes).ok_or(ParseError::MissingData {
851 type_name: std::any::type_name::<T>(),
852 type_size: std::mem::size_of::<T>(),
853 num_bytes,
854 })?;
855
856 Ok((data, tail))
857 }
858}
859
860impl<
861 B: Debug + SplitByteSlice + PartialEq,
862 T: Clone + Debug + FromBytes + Immutable + PartialEq + Unaligned,
863 > ParseSlice<ByRef<B>> for Ref<B, [T]>
864{
865 type Error = anyhow::Error;
868
869 fn parse_slice(bytes: ByRef<B>, count: usize) -> Result<(Self, ByRef<B>), Self::Error> {
872 let num_bytes = bytes.len();
873 let (data, tail) =
874 ByRef::<B>::parse_slice::<T>(bytes, count).ok_or(ParseError::MissingSliceData {
875 type_name: std::any::type_name::<T>(),
876 type_size: std::mem::size_of::<T>(),
877 num_items: count,
878 num_bytes,
879 })?;
880
881 Ok((data, tail))
882 }
883}
884
885impl<PS: ParseStrategy, T: Parse<PS>> ParseSlice<PS> for Vec<T> {
886 type Error = anyhow::Error;
889
890 fn parse_slice(bytes: PS, count: usize) -> Result<(Self, PS), Self::Error> {
892 let mut slice = Vec::with_capacity(count);
893 let mut tail = bytes;
894
895 for _ in 0..count {
896 let (item, next_tail) = T::parse(tail).map_err(Into::<anyhow::Error>::into)?;
897 slice.push(item);
898 tail = next_tail;
899 }
900
901 Ok((slice, tail))
902 }
903}
904
905#[cfg(test)]
906pub(super) mod testing {
907 use crate::policy::error::ValidateError;
908 use crate::policy::{AccessVector, ParseError};
909
910 pub const ACCESS_VECTOR_0001: AccessVector = AccessVector(0b0001u32);
911 pub const ACCESS_VECTOR_0010: AccessVector = AccessVector(0b0010u32);
912
913 pub(super) fn as_parse_error(error: anyhow::Error) -> ParseError {
915 error.downcast::<ParseError>().expect("parse error")
916 }
917
918 pub(super) fn as_validate_error(error: anyhow::Error) -> ValidateError {
920 error.downcast::<ValidateError>().expect("validate error")
921 }
922}
923
924#[cfg(test)]
925pub(super) mod tests {
926 use super::*;
927
928 use crate::policy::metadata::HandleUnknown;
929 use crate::policy::{parse_policy_by_reference, parse_policy_by_value, SecurityContext};
930 use crate::{FileClass, InitialSid, KernelClass};
931
932 use serde::Deserialize;
933 use std::ops::Shl;
934
935 fn is_explicitly_allowed<PS: ParseStrategy>(
943 policy: &Policy<PS>,
944 source_type: TypeId,
945 target_type: TypeId,
946 target_class: &str,
947 permission: &str,
948 ) -> bool {
949 let class = policy
950 .0
951 .parsed_policy()
952 .classes()
953 .iter()
954 .find(|class| class.name_bytes() == target_class.as_bytes())
955 .expect("class not found");
956 let class_permissions = policy
957 .find_class_permissions_by_name(target_class)
958 .expect("class permissions not found");
959 let (permission_id, _) = class_permissions
960 .iter()
961 .find(|(_, name)| permission.as_bytes() == name)
962 .expect("permission not found");
963 let permission_bit = AccessVector::from_class_permission_id(*permission_id);
964 let access_decision =
965 policy.0.parsed_policy().compute_explicitly_allowed(source_type, target_type, class);
966 permission_bit == access_decision.allow & permission_bit
967 }
968
969 #[derive(Debug, Deserialize)]
970 struct Expectations {
971 expected_policy_version: u32,
972 expected_handle_unknown: LocalHandleUnknown,
973 }
974
975 #[derive(Debug, Deserialize, PartialEq)]
976 #[serde(rename_all = "snake_case")]
977 enum LocalHandleUnknown {
978 Deny,
979 Reject,
980 Allow,
981 }
982
983 impl PartialEq<HandleUnknown> for LocalHandleUnknown {
984 fn eq(&self, other: &HandleUnknown) -> bool {
985 match self {
986 LocalHandleUnknown::Deny => *other == HandleUnknown::Deny,
987 LocalHandleUnknown::Reject => *other == HandleUnknown::Reject,
988 LocalHandleUnknown::Allow => *other == HandleUnknown::Allow,
989 }
990 }
991 }
992
993 fn xperms_bitmap_from_elements(elements: &[u8]) -> XpermsBitmap {
996 let mut bitmap = [le::U32::ZERO; 8];
997 for element in elements.iter() {
998 let block_index = (*element as usize) / 32;
999 let bit_index = ((*element as usize) % 32) as u32;
1000 let bitmask = le::U32::new(1).shl(bit_index);
1001 bitmap[block_index] = bitmap[block_index] | bitmask;
1002 }
1003 XpermsBitmap::new(bitmap)
1004 }
1005
1006 #[test]
1007 fn known_policies() {
1008 let policies_and_expectations = [
1009 [
1010 b"testdata/policies/emulator".to_vec(),
1011 include_bytes!("../../testdata/policies/emulator").to_vec(),
1012 include_bytes!("../../testdata/expectations/emulator").to_vec(),
1013 ],
1014 [
1015 b"testdata/policies/selinux_testsuite".to_vec(),
1016 include_bytes!("../../testdata/policies/selinux_testsuite").to_vec(),
1017 include_bytes!("../../testdata/expectations/selinux_testsuite").to_vec(),
1018 ],
1019 ];
1020
1021 for [policy_path, policy_bytes, expectations_bytes] in policies_and_expectations {
1022 let expectations = serde_json5::from_reader::<_, Expectations>(
1023 &mut std::io::Cursor::new(expectations_bytes),
1024 )
1025 .expect("deserialize expectations");
1026
1027 let (policy, returned_policy_bytes) =
1030 parse_policy_by_value(policy_bytes.clone()).expect("parse policy");
1031
1032 let policy = policy
1033 .validate()
1034 .with_context(|| {
1035 format!(
1036 "policy path: {:?}",
1037 std::str::from_utf8(policy_path.as_slice()).unwrap()
1038 )
1039 })
1040 .expect("validate policy");
1041
1042 assert_eq!(expectations.expected_policy_version, policy.policy_version());
1043 assert_eq!(expectations.expected_handle_unknown, policy.handle_unknown());
1044
1045 assert_eq!(policy_bytes, returned_policy_bytes);
1047
1048 let policy = parse_policy_by_reference(policy_bytes.as_slice()).expect("parse policy");
1050 let policy = policy.validate().expect("validate policy");
1051
1052 assert_eq!(expectations.expected_policy_version, policy.policy_version());
1053 assert_eq!(expectations.expected_handle_unknown, policy.handle_unknown());
1054 }
1055 }
1056
1057 #[test]
1058 fn policy_lookup() {
1059 let policy_bytes = include_bytes!("../../testdata/policies/selinux_testsuite");
1060 let (policy, _) = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1061 let policy = policy.validate().expect("validate selinux testsuite policy");
1062
1063 let unconfined_t = policy.type_id_by_name("unconfined_t").expect("look up type id");
1064
1065 assert!(is_explicitly_allowed(&policy, unconfined_t, unconfined_t, "process", "fork",));
1066 }
1067
1068 #[test]
1069 fn initial_contexts() {
1070 let policy_bytes = include_bytes!(
1071 "../../testdata/micro_policies/multiple_levels_and_categories_policy.pp"
1072 );
1073 let (policy, _) = parse_policy_by_value(policy_bytes.to_vec()).expect("parse policy");
1074 let policy = policy.validate().expect("validate policy");
1075
1076 let kernel_context = policy.initial_context(InitialSid::Kernel);
1077 assert_eq!(
1078 policy.serialize_security_context(&kernel_context),
1079 b"user0:object_r:type0:s0:c0-s1:c0.c2,c4"
1080 )
1081 }
1082
1083 #[test]
1084 fn explicit_allow_type_type() {
1085 let policy_bytes =
1086 include_bytes!("../../testdata/micro_policies/allow_a_t_b_t_class0_perm0_policy.pp");
1087 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1088 .expect("parse policy")
1089 .validate()
1090 .expect("validate policy");
1091
1092 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1093 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1094
1095 assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1096 }
1097
1098 #[test]
1099 fn no_explicit_allow_type_type() {
1100 let policy_bytes =
1101 include_bytes!("../../testdata/micro_policies/no_allow_a_t_b_t_class0_perm0_policy.pp");
1102 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1103 .expect("parse policy")
1104 .validate()
1105 .expect("validate policy");
1106
1107 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1108 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1109
1110 assert!(!is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1111 }
1112
1113 #[test]
1114 fn explicit_allow_type_attr() {
1115 let policy_bytes =
1116 include_bytes!("../../testdata/micro_policies/allow_a_t_b_attr_class0_perm0_policy.pp");
1117 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1118 .expect("parse policy")
1119 .validate()
1120 .expect("validate policy");
1121
1122 let a_t = policy.type_id_by_name("a_t").expect("look up type id");
1123 let b_t = policy.type_id_by_name("b_t").expect("look up type id");
1124
1125 assert!(is_explicitly_allowed(&policy, a_t, b_t, "class0", "perm0"));
1126 }
1127
1128 #[test]
1129 fn no_explicit_allow_type_attr() {
1130 let policy_bytes = include_bytes!(
1131 "../../testdata/micro_policies/no_allow_a_t_b_attr_class0_perm0_policy.pp"
1132 );
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 explicit_allow_attr_attr() {
1146 let policy_bytes = include_bytes!(
1147 "../../testdata/micro_policies/allow_a_attr_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 no_explicit_allow_attr_attr() {
1162 let policy_bytes = include_bytes!(
1163 "../../testdata/micro_policies/no_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 compute_explicitly_allowed_multiple_attributes() {
1178 let policy_bytes = include_bytes!("../../testdata/micro_policies/allow_a_t_a1_attr_class0_perm0_a2_attr_class0_perm1_policy.pp");
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
1186 let class = policy
1187 .0
1188 .parsed_policy()
1189 .classes()
1190 .iter()
1191 .find(|class| class.name_bytes() == b"class0")
1192 .expect("class not found");
1193 let raw_access_vector =
1194 policy.0.parsed_policy().compute_explicitly_allowed(a_t, a_t, class).allow.0;
1195
1196 assert_eq!(2, raw_access_vector.count_ones());
1201 }
1202
1203 #[test]
1204 fn compute_access_decision_with_constraints() {
1205 let policy_bytes =
1206 include_bytes!("../../testdata/micro_policies/allow_with_constraints_policy.pp");
1207 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1208 .expect("parse policy")
1209 .validate()
1210 .expect("validate policy");
1211
1212 let source_context: SecurityContext = policy
1213 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1214 .expect("create source security context");
1215
1216 let target_context_satisfied: SecurityContext = source_context.clone();
1217 let decision_satisfied = policy.compute_access_decision(
1218 &source_context,
1219 &target_context_satisfied,
1220 KernelClass::File,
1221 );
1222 assert_eq!(decision_satisfied.allow, AccessVector(7));
1226
1227 let target_context_unsatisfied: SecurityContext = policy
1228 .parse_security_context(b"user1:object_r:type0:s0:c0-s0:c0".into())
1229 .expect("create target security context failing some constraints");
1230 let decision_unsatisfied = policy.compute_access_decision(
1231 &source_context,
1232 &target_context_unsatisfied,
1233 KernelClass::File,
1234 );
1235 assert_eq!(decision_unsatisfied.allow, AccessVector(4));
1238 }
1239
1240 #[test]
1241 fn compute_ioctl_access_decision_explicitly_allowed() {
1242 let policy_bytes = include_bytes!("../../testdata/micro_policies/allowxperm_policy.pp");
1243 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1244 .expect("parse policy")
1245 .validate()
1246 .expect("validate policy");
1247
1248 let source_context: SecurityContext = policy
1249 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1250 .expect("create source security context");
1251 let target_context_matched: SecurityContext = source_context.clone();
1252
1253 let decision_single = policy.compute_ioctl_access_decision(
1271 &source_context,
1272 &target_context_matched,
1273 KernelClass::File,
1274 0xab,
1275 );
1276
1277 let mut expected_auditdeny =
1278 xperms_bitmap_from_elements((0x0..=0xff).collect::<Vec<_>>().as_slice());
1279 expected_auditdeny -= &xperms_bitmap_from_elements(&[0xcd, 0xef]);
1280
1281 let expected_decision_single = IoctlAccessDecision {
1282 allow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1283 auditallow: xperms_bitmap_from_elements(&[0xcd, 0xef]),
1284 auditdeny: expected_auditdeny,
1285 };
1286 assert_eq!(decision_single, expected_decision_single);
1287
1288 let decision_range = policy.compute_ioctl_access_decision(
1289 &source_context,
1290 &target_context_matched,
1291 KernelClass::File,
1292 0x10,
1293 );
1294 let expected_decision_range = IoctlAccessDecision {
1295 allow: XpermsBitmap::ALL,
1296 auditallow: XpermsBitmap::ALL,
1297 auditdeny: XpermsBitmap::NONE,
1298 };
1299 assert_eq!(decision_range, expected_decision_range);
1300 }
1301
1302 #[test]
1303 fn compute_ioctl_access_decision_unmatched() {
1304 let policy_bytes =
1305 include_bytes!("../../testdata/micro_policies/allow_with_constraints_policy.pp");
1306 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1307 .expect("parse policy")
1308 .validate()
1309 .expect("validate policy");
1310
1311 let source_context: SecurityContext = policy
1312 .parse_security_context(b"user0:object_r:type0:s0-s0".into())
1313 .expect("create source security context");
1314
1315 let target_context_unmatched: SecurityContext = policy
1317 .parse_security_context(b"user0:object_r:type1:s0-s0".into())
1318 .expect("create source security context");
1319
1320 for prefix in 0x0..=0xff {
1321 let decision = policy.compute_ioctl_access_decision(
1322 &source_context,
1323 &target_context_unmatched,
1324 KernelClass::File,
1325 prefix,
1326 );
1327 assert_eq!(decision, IoctlAccessDecision::ALLOW_ALL);
1328 }
1329 }
1330
1331 #[test]
1332 fn new_file_security_context_minimal() {
1333 let policy_bytes =
1334 include_bytes!("../../testdata/composite_policies/compiled/minimal_policy.pp");
1335 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1336 .expect("parse policy")
1337 .validate()
1338 .expect("validate policy");
1339 let source = policy
1340 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1341 .expect("valid source security context");
1342 let target = policy
1343 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1344 .expect("valid target security context");
1345
1346 let actual = policy.new_file_security_context(&source, &target, FileClass::File);
1347 let expected: SecurityContext = policy
1348 .parse_security_context(b"source_u:object_r:target_t:s0:c0".into())
1349 .expect("valid expected security context");
1350
1351 assert_eq!(expected, actual);
1352 }
1353
1354 #[test]
1355 fn new_security_context_minimal() {
1356 let policy_bytes =
1357 include_bytes!("../../testdata/composite_policies/compiled/minimal_policy.pp");
1358 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1359 .expect("parse policy")
1360 .validate()
1361 .expect("validate policy");
1362 let source = policy
1363 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1364 .expect("valid source security context");
1365 let target = policy
1366 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1367 .expect("valid target security context");
1368
1369 let actual = policy.new_security_context(&source, &target, KernelClass::Process);
1370
1371 assert_eq!(source, actual);
1372 }
1373
1374 #[test]
1375 fn new_file_security_context_class_defaults() {
1376 let policy_bytes =
1377 include_bytes!("../../testdata/composite_policies/compiled/class_defaults_policy.pp");
1378 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1379 .expect("parse policy")
1380 .validate()
1381 .expect("validate policy");
1382 let source = policy
1383 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1384 .expect("valid source security context");
1385 let target = policy
1386 .parse_security_context(b"target_u:target_r:target_t:s1:c0-s1:c0.c1".into())
1387 .expect("valid target security context");
1388
1389 let actual = policy.new_file_security_context(&source, &target, FileClass::File);
1390 let expected: SecurityContext = policy
1391 .parse_security_context(b"target_u:source_r:source_t:s1:c0-s1:c0.c1".into())
1392 .expect("valid expected security context");
1393
1394 assert_eq!(expected, actual);
1395 }
1396
1397 #[test]
1398 fn new_security_context_class_defaults() {
1399 let policy_bytes =
1400 include_bytes!("../../testdata/composite_policies/compiled/class_defaults_policy.pp");
1401 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1402 .expect("parse policy")
1403 .validate()
1404 .expect("validate policy");
1405 let source = policy
1406 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1407 .expect("valid source security context");
1408 let target = policy
1409 .parse_security_context(b"target_u:target_r:target_t:s1:c0-s1:c0.c1".into())
1410 .expect("valid target security context");
1411
1412 let actual = policy.new_security_context(&source, &target, KernelClass::Process);
1413 let expected: SecurityContext = policy
1414 .parse_security_context(b"target_u:source_r:source_t:s1:c0-s1:c0.c1".into())
1415 .expect("valid expected security context");
1416
1417 assert_eq!(expected, actual);
1418 }
1419
1420 #[test]
1421 fn new_file_security_context_role_transition() {
1422 let policy_bytes =
1423 include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy.pp");
1424 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1425 .expect("parse policy")
1426 .validate()
1427 .expect("validate policy");
1428 let source = policy
1429 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1430 .expect("valid source security context");
1431 let target = policy
1432 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1433 .expect("valid target security context");
1434
1435 let actual = policy.new_file_security_context(&source, &target, FileClass::File);
1436 let expected: SecurityContext = policy
1437 .parse_security_context(b"source_u:transition_r:target_t:s0:c0".into())
1438 .expect("valid expected security context");
1439
1440 assert_eq!(expected, actual);
1441 }
1442
1443 #[test]
1444 fn new_security_context_role_transition() {
1445 let policy_bytes =
1446 include_bytes!("../../testdata/composite_policies/compiled/role_transition_policy.pp");
1447 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1448 .expect("parse policy")
1449 .validate()
1450 .expect("validate policy");
1451 let source = policy
1452 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1453 .expect("valid source security context");
1454 let target = policy
1455 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1456 .expect("valid target security context");
1457
1458 let actual = policy.new_security_context(&source, &target, KernelClass::Process);
1459 let expected: SecurityContext = policy
1460 .parse_security_context(b"source_u:transition_r:source_t:s0:c0-s2:c0.c1".into())
1461 .expect("valid expected security context");
1462
1463 assert_eq!(expected, actual);
1464 }
1465
1466 #[test]
1467 #[ignore]
1469 fn new_file_security_context_role_transition_not_allowed() {
1470 let policy_bytes = include_bytes!(
1471 "../../testdata/composite_policies/compiled/role_transition_not_allowed_policy.pp"
1472 );
1473 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1474 .expect("parse policy")
1475 .validate()
1476 .expect("validate policy");
1477 let source = policy
1478 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1479 .expect("valid source security context");
1480 let target = policy
1481 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1482 .expect("valid target security context");
1483
1484 let actual = policy.new_file_security_context(&source, &target, FileClass::File);
1485
1486 assert!(policy.validate_security_context(&actual).is_err());
1488 }
1489
1490 #[test]
1491 fn new_file_security_context_type_transition() {
1492 let policy_bytes =
1493 include_bytes!("../../testdata/composite_policies/compiled/type_transition_policy.pp");
1494 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1495 .expect("parse policy")
1496 .validate()
1497 .expect("validate policy");
1498 let source = policy
1499 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1500 .expect("valid source security context");
1501 let target = policy
1502 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1503 .expect("valid target security context");
1504
1505 let actual = policy.new_file_security_context(&source, &target, FileClass::File);
1506 let expected: SecurityContext = policy
1507 .parse_security_context(b"source_u:object_r:transition_t:s0:c0".into())
1508 .expect("valid expected security context");
1509
1510 assert_eq!(expected, actual);
1511 }
1512
1513 #[test]
1514 fn new_security_context_type_transition() {
1515 let policy_bytes =
1516 include_bytes!("../../testdata/composite_policies/compiled/type_transition_policy.pp");
1517 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1518 .expect("parse policy")
1519 .validate()
1520 .expect("validate policy");
1521 let source = policy
1522 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1523 .expect("valid source security context");
1524 let target = policy
1525 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1526 .expect("valid target security context");
1527
1528 let actual = policy.new_security_context(&source, &target, KernelClass::Process);
1529 let expected: SecurityContext = policy
1530 .parse_security_context(b"source_u:source_r:transition_t:s0:c0-s2:c0.c1".into())
1531 .expect("valid expected security context");
1532
1533 assert_eq!(expected, actual);
1534 }
1535
1536 #[test]
1537 fn new_file_security_context_range_transition() {
1538 let policy_bytes =
1539 include_bytes!("../../testdata/composite_policies/compiled/range_transition_policy.pp");
1540 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1541 .expect("parse policy")
1542 .validate()
1543 .expect("validate policy");
1544 let source = policy
1545 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1546 .expect("valid source security context");
1547 let target = policy
1548 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1549 .expect("valid target security context");
1550
1551 let actual = policy.new_file_security_context(&source, &target, FileClass::File);
1552 let expected: SecurityContext = policy
1553 .parse_security_context(b"source_u:object_r:target_t:s1:c1-s2:c1.c2".into())
1554 .expect("valid expected security context");
1555
1556 assert_eq!(expected, actual);
1557 }
1558
1559 #[test]
1560 fn new_security_context_range_transition() {
1561 let policy_bytes =
1562 include_bytes!("../../testdata/composite_policies/compiled/range_transition_policy.pp");
1563 let policy = parse_policy_by_reference(policy_bytes.as_slice())
1564 .expect("parse policy")
1565 .validate()
1566 .expect("validate policy");
1567 let source = policy
1568 .parse_security_context(b"source_u:source_r:source_t:s0:c0-s2:c0.c1".into())
1569 .expect("valid source security context");
1570 let target = policy
1571 .parse_security_context(b"target_u:target_r:target_t:s1:c1".into())
1572 .expect("valid target security context");
1573
1574 let actual = policy.new_security_context(&source, &target, KernelClass::Process);
1575 let expected: SecurityContext = policy
1576 .parse_security_context(b"source_u:source_r:source_t:s1:c1-s2:c1.c2".into())
1577 .expect("valid expected security context");
1578
1579 assert_eq!(expected, actual);
1580 }
1581}