1#![allow(clippy::needless_range_loop)]
39
40use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
41
42use spin::Mutex;
43
44use super::AccessType;
45use crate::error::KernelError;
46
47const MAX_POLICY_RULES: usize = 64;
53
54const MAX_TRANSITIONS: usize = 32;
56
57const MAX_ROLES: usize = 8;
59
60const MAX_ROLE_TYPES: usize = 16;
62
63const MAX_USER_ROLES: usize = 16;
65
66const MAX_USER_ASSIGNED_ROLES: usize = 8;
68
69const MAX_PROCESS_LABELS: usize = 64;
71
72const MAX_PERMISSIONS: usize = 3;
74
75const MAX_TOKENS: usize = 128;
77
78const MAX_PARSED_RULES: usize = 32;
80
81const MAX_PARSED_TRANSITIONS: usize = 16;
83
84const MAX_PARSED_ROLES: usize = 8;
86
87const MAX_PARSED_USER_ROLES: usize = 8;
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub struct MlsLevel {
101 pub sensitivity: u16,
103 pub categories: u64,
105}
106
107impl MlsLevel {
108 pub const fn new(sensitivity: u16, categories: u64) -> Self {
110 Self {
111 sensitivity,
112 categories,
113 }
114 }
115
116 pub const fn default_level() -> Self {
118 Self {
119 sensitivity: 0,
120 categories: 0,
121 }
122 }
123
124 pub fn dominates(&self, other: &MlsLevel) -> bool {
126 self.sensitivity >= other.sensitivity
127 && (self.categories & other.categories) == other.categories
128 }
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
139pub struct SecurityLabel {
140 pub type_name: &'static str,
142 pub role: &'static str,
144 pub level: MlsLevel,
146}
147
148impl SecurityLabel {
149 pub const fn new(type_name: &'static str, role: &'static str, level: MlsLevel) -> Self {
151 Self {
152 type_name,
153 role,
154 level,
155 }
156 }
157
158 pub const fn simple(type_name: &'static str, role: &'static str) -> Self {
160 Self::new(type_name, role, MlsLevel::default_level())
161 }
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq)]
170pub enum PolicyAction {
171 Allow,
172 Deny,
173}
174
175#[derive(Debug, Clone, Copy)]
179pub struct PolicyRule {
180 pub source_type: &'static str,
182 pub target_type: &'static str,
184 pub permissions: [Permission; MAX_PERMISSIONS],
186 pub perm_count: u8,
188 pub action: PolicyAction,
190 pub enabled: bool,
192}
193
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
196pub enum Permission {
197 Read,
198 Write,
199 Execute,
200}
201
202impl PolicyRule {
203 pub const fn new(
205 source_type: &'static str,
206 target_type: &'static str,
207 permissions: [Permission; MAX_PERMISSIONS],
208 perm_count: u8,
209 action: PolicyAction,
210 ) -> Self {
211 Self {
212 source_type,
213 target_type,
214 permissions,
215 perm_count,
216 action,
217 enabled: true,
218 }
219 }
220
221 pub fn from_perms(
225 source_type: &'static str,
226 target_type: &'static str,
227 perms: &[Permission],
228 action: PolicyAction,
229 ) -> Self {
230 let mut permissions = [Permission::Read; MAX_PERMISSIONS];
231 let count = perms.len().min(MAX_PERMISSIONS);
232 let mut i = 0;
233 while i < count {
234 permissions[i] = perms[i];
235 i += 1;
236 }
237 Self {
238 source_type,
239 target_type,
240 permissions,
241 perm_count: count as u8,
242 action,
243 enabled: true,
244 }
245 }
246
247 pub fn from_legacy(source: &'static str, target: &'static str, allowed: u8) -> Self {
249 let mut permissions = [Permission::Read; MAX_PERMISSIONS];
250 let mut count: u8 = 0;
251 if allowed & 0x1 != 0 {
252 permissions[count as usize] = Permission::Read;
253 count += 1;
254 }
255 if allowed & 0x2 != 0 {
256 permissions[count as usize] = Permission::Write;
257 count += 1;
258 }
259 if allowed & 0x4 != 0 {
260 permissions[count as usize] = Permission::Execute;
261 count += 1;
262 }
263 Self {
264 source_type: source,
265 target_type: target,
266 permissions,
267 perm_count: count,
268 action: PolicyAction::Allow,
269 enabled: true,
270 }
271 }
272
273 fn has_permission(&self, perm: Permission) -> bool {
275 let mut i = 0;
276 while i < self.perm_count as usize {
277 if matches!(
278 (&self.permissions[i], &perm),
279 (Permission::Read, Permission::Read)
280 | (Permission::Write, Permission::Write)
281 | (Permission::Execute, Permission::Execute)
282 ) {
283 return true;
284 }
285 i += 1;
286 }
287 false
288 }
289
290 pub fn allows(&self, access: AccessType) -> bool {
292 if !self.enabled {
293 return false;
294 }
295
296 let perm = match access {
297 AccessType::Read => Permission::Read,
298 AccessType::Write => Permission::Write,
299 AccessType::Execute => Permission::Execute,
300 };
301
302 let has_perm = self.has_permission(perm);
303
304 match self.action {
305 PolicyAction::Allow => has_perm,
306 PolicyAction::Deny => false, }
308 }
309
310 pub fn denies(&self, access: AccessType) -> bool {
312 if !self.enabled {
313 return false;
314 }
315
316 let perm = match access {
317 AccessType::Read => Permission::Read,
318 AccessType::Write => Permission::Write,
319 AccessType::Execute => Permission::Execute,
320 };
321
322 match self.action {
323 PolicyAction::Deny => self.has_permission(perm),
324 PolicyAction::Allow => false,
325 }
326 }
327}
328
329#[derive(Debug, Clone, Copy)]
339pub struct DomainTransition {
340 pub source_type: &'static str,
342 pub target_type: &'static str,
344 pub class: &'static str,
346 pub new_type: &'static str,
348}
349
350impl DomainTransition {
351 pub const fn new(
353 source_type: &'static str,
354 target_type: &'static str,
355 class: &'static str,
356 new_type: &'static str,
357 ) -> Self {
358 Self {
359 source_type,
360 target_type,
361 class,
362 new_type,
363 }
364 }
365}
366
367#[derive(Debug, Clone, Copy)]
375pub struct Role {
376 pub name: &'static str,
378 pub allowed_types: [&'static str; MAX_ROLE_TYPES],
380 pub type_count: usize,
382}
383
384impl Role {
385 pub fn from_types(name: &'static str, types: &[&'static str]) -> Self {
387 let mut allowed_types = [""; MAX_ROLE_TYPES];
388 let count = types.len().min(MAX_ROLE_TYPES);
389 let mut i = 0;
390 while i < count {
391 allowed_types[i] = types[i];
392 i += 1;
393 }
394 Self {
395 name,
396 allowed_types,
397 type_count: count,
398 }
399 }
400
401 pub fn allows_type(&self, type_name: &str) -> bool {
403 let mut i = 0;
404 while i < self.type_count {
405 if str_eq(self.allowed_types[i], type_name) {
406 return true;
407 }
408 i += 1;
409 }
410 false
411 }
412}
413
414#[derive(Debug, Clone, Copy)]
420struct RoleEntry {
421 name: &'static str,
422 role: Role,
423}
424
425#[derive(Debug, Clone, Copy)]
427struct UserRoleEntry {
428 username: &'static str,
429 roles: [&'static str; MAX_USER_ASSIGNED_ROLES],
430 role_count: usize,
431}
432
433#[derive(Debug, Clone, Copy)]
435struct ProcessLabelEntry {
436 pid: u64,
437 label: SecurityLabel,
438}
439
440mod parser;
445use parser::PolicyParser;
446
447struct PolicyDatabase {
455 rules: [Option<PolicyRule>; MAX_POLICY_RULES],
457 rule_count: usize,
459 transitions: [Option<DomainTransition>; MAX_TRANSITIONS],
461 transition_count: usize,
463 roles: [Option<RoleEntry>; MAX_ROLES],
465 role_count: usize,
467 user_roles: [Option<UserRoleEntry>; MAX_USER_ROLES],
469 user_role_count: usize,
471 process_labels: [Option<ProcessLabelEntry>; MAX_PROCESS_LABELS],
473 process_label_count: usize,
475}
476
477impl PolicyDatabase {
478 const fn new() -> Self {
480 Self {
481 rules: [const { None }; MAX_POLICY_RULES],
482 rule_count: 0,
483 transitions: [const { None }; MAX_TRANSITIONS],
484 transition_count: 0,
485 roles: [const { None }; MAX_ROLES],
486 role_count: 0,
487 user_roles: [const { None }; MAX_USER_ROLES],
488 user_role_count: 0,
489 process_labels: [const { None }; MAX_PROCESS_LABELS],
490 process_label_count: 0,
491 }
492 }
493
494 fn find_role(&self, name: &str) -> Option<&Role> {
496 for i in 0..self.role_count {
497 if let Some(entry) = &self.roles[i] {
498 if str_eq(entry.name, name) {
499 return Some(&entry.role);
500 }
501 }
502 }
503 None
504 }
505
506 fn insert_role(&mut self, role: Role) {
508 for i in 0..self.role_count {
510 if let Some(entry) = &mut self.roles[i] {
511 if str_eq(entry.name, role.name) {
512 entry.role = role;
513 return;
514 }
515 }
516 }
517 if self.role_count < MAX_ROLES {
519 self.roles[self.role_count] = Some(RoleEntry {
520 name: role.name,
521 role,
522 });
523 self.role_count += 1;
524 }
525 }
526
527 fn has_roles(&self) -> bool {
529 self.role_count > 0
530 }
531
532 fn find_user_roles(&self, username: &str) -> Option<&UserRoleEntry> {
534 for i in 0..self.user_role_count {
535 if let Some(entry) = &self.user_roles[i] {
536 if str_eq(entry.username, username) {
537 return Some(entry);
538 }
539 }
540 }
541 None
542 }
543
544 fn insert_user_roles(&mut self, entry: UserRoleEntry) {
546 for i in 0..self.user_role_count {
548 if let Some(existing) = &mut self.user_roles[i] {
549 if str_eq(existing.username, entry.username) {
550 *existing = entry;
551 return;
552 }
553 }
554 }
555 if self.user_role_count < MAX_USER_ROLES {
557 self.user_roles[self.user_role_count] = Some(entry);
558 self.user_role_count += 1;
559 }
560 }
561
562 fn find_process_label(&self, pid: u64) -> Option<SecurityLabel> {
564 for i in 0..self.process_label_count {
565 if let Some(entry) = &self.process_labels[i] {
566 if entry.pid == pid {
567 return Some(entry.label);
568 }
569 }
570 }
571 None
572 }
573
574 fn insert_process_label(&mut self, pid: u64, label: SecurityLabel) {
576 for i in 0..self.process_label_count {
578 if let Some(entry) = &mut self.process_labels[i] {
579 if entry.pid == pid {
580 entry.label = label;
581 return;
582 }
583 }
584 }
585 if self.process_label_count < MAX_PROCESS_LABELS {
587 self.process_labels[self.process_label_count] = Some(ProcessLabelEntry { pid, label });
588 self.process_label_count += 1;
589 }
590 }
591}
592
593static POLICY_DB: Mutex<PolicyDatabase> = Mutex::new(PolicyDatabase::new());
599static POLICY_COUNT: AtomicUsize = AtomicUsize::new(0);
600static MAC_ENABLED: AtomicBool = AtomicBool::new(false);
601
602fn with_policy_db<R, F: FnOnce(&mut PolicyDatabase) -> R>(f: F) -> R {
604 let mut guard = POLICY_DB.lock();
605 f(&mut guard)
606}
607
608#[inline]
614fn str_eq(a: &str, b: &str) -> bool {
615 a.as_bytes() == b.as_bytes()
616}
617
618pub fn add_policy_rule(rule: PolicyRule) -> Result<(), KernelError> {
624 with_policy_db(|db| {
625 if db.rule_count >= MAX_POLICY_RULES {
626 return Err(KernelError::ResourceExhausted {
627 resource: "MAC policy rules",
628 });
629 }
630 db.rules[db.rule_count] = Some(rule);
631 db.rule_count += 1;
632 POLICY_COUNT.store(db.rule_count, Ordering::Relaxed);
633 Ok(())
634 })
635}
636
637pub fn add_rule(
641 source: &'static str,
642 target: &'static str,
643 allowed: u8,
644) -> Result<(), KernelError> {
645 add_policy_rule(PolicyRule::from_legacy(source, target, allowed))
646}
647
648pub fn add_transition(transition: DomainTransition) -> Result<(), KernelError> {
650 with_policy_db(|db| {
651 if db.transition_count >= MAX_TRANSITIONS {
652 return Err(KernelError::ResourceExhausted {
653 resource: "MAC domain transitions",
654 });
655 }
656 db.transitions[db.transition_count] = Some(transition);
657 db.transition_count += 1;
658 Ok(())
659 })
660}
661
662pub fn add_role(role: Role) {
664 with_policy_db(|db| {
665 db.insert_role(role);
666 })
667}
668
669pub fn assign_user_roles_static(username: &'static str, roles: &[&'static str]) {
671 let mut assigned = [""; MAX_USER_ASSIGNED_ROLES];
672 let count = roles.len().min(MAX_USER_ASSIGNED_ROLES);
673 let mut i = 0;
674 while i < count {
675 assigned[i] = roles[i];
676 i += 1;
677 }
678 with_policy_db(|db| {
679 db.insert_user_roles(UserRoleEntry {
680 username,
681 roles: assigned,
682 role_count: count,
683 });
684 })
685}
686
687pub fn set_process_label(pid: u64, label: SecurityLabel) {
689 with_policy_db(|db| {
690 db.insert_process_label(pid, label);
691 })
692}
693
694pub fn get_process_label(pid: u64) -> Option<SecurityLabel> {
696 with_policy_db(|db| db.find_process_label(pid))
697}
698
699pub fn check_access(source: &str, target: &str, access: AccessType) -> bool {
708 if !MAC_ENABLED.load(Ordering::Acquire) {
709 return true; }
711
712 with_policy_db(|db| {
713 for i in 0..db.rule_count {
715 if let Some(rule) = &db.rules[i] {
716 if str_eq(rule.source_type, source)
717 && str_eq(rule.target_type, target)
718 && rule.denies(access)
719 {
720 return false;
721 }
722 }
723 }
724
725 for i in 0..db.rule_count {
727 if let Some(rule) = &db.rules[i] {
728 if str_eq(rule.source_type, source)
729 && str_eq(rule.target_type, target)
730 && rule.allows(access)
731 {
732 return true;
733 }
734 }
735 }
736
737 false
739 })
740}
741
742pub fn check_access_full(
750 subject: &SecurityLabel,
751 object: &SecurityLabel,
752 access: AccessType,
753) -> bool {
754 if !check_access(subject.type_name, object.type_name, access) {
756 return false;
757 }
758
759 match access {
761 AccessType::Read => {
762 if !subject.level.dominates(&object.level) {
764 return false;
765 }
766 }
767 AccessType::Write => {
768 if !object.level.dominates(&subject.level) {
770 return false;
771 }
772 }
773 AccessType::Execute => {
774 if subject.level.sensitivity != object.level.sensitivity {
776 return false;
777 }
778 }
779 }
780
781 with_policy_db(|db| {
783 if let Some(role) = db.find_role(subject.role) {
784 role.allows_type(subject.type_name)
785 } else {
786 !db.has_roles()
788 }
789 })
790}
791
792pub fn lookup_transition(
796 source_type: &str,
797 target_type: &str,
798 class: &str,
799) -> Option<&'static str> {
800 with_policy_db(|db| {
801 for i in 0..db.transition_count {
802 if let Some(t) = &db.transitions[i] {
803 if str_eq(t.source_type, source_type)
804 && str_eq(t.target_type, target_type)
805 && str_eq(t.class, class)
806 {
807 return Some(t.new_type);
808 }
809 }
810 }
811 None
812 })
813}
814
815pub fn user_has_role(username: &str, role_name: &str) -> bool {
817 with_policy_db(|db| {
818 if let Some(entry) = db.find_user_roles(username) {
819 let mut i = 0;
820 while i < entry.role_count {
821 if str_eq(entry.roles[i], role_name) {
822 return true;
823 }
824 i += 1;
825 }
826 false
827 } else {
828 false
829 }
830 })
831}
832
833pub fn role_allows_type(role_name: &str, type_name: &str) -> bool {
835 with_policy_db(|db| {
836 if let Some(role) = db.find_role(role_name) {
837 role.allows_type(type_name)
838 } else {
839 false
840 }
841 })
842}
843
844pub fn check_file_access(_path: &str, access: AccessType, pid: u64) -> Result<(), KernelError> {
854 let source = get_type_for_pid(pid);
856
857 let target = "file_t";
859
860 if !check_access(source, target, access) {
862 crate::security::audit::log_permission_denied(pid, 0, "file_access");
863 return Err(KernelError::PermissionDenied {
864 operation: "file_access",
865 });
866 }
867
868 let required_flags = match access {
871 AccessType::Read => crate::cap::Rights::READ.to_flags(),
872 AccessType::Write => crate::cap::Rights::WRITE.to_flags(),
873 AccessType::Execute => crate::cap::Rights::EXECUTE.to_flags(),
874 };
875
876 if let Some(cap_space_guard) = crate::cap::kernel_cap_space().try_read() {
878 if let Some(ref _cap_space) = *cap_space_guard {
879 if pid > 1 {
883 crate::security::audit::log_capability_op(pid, required_flags as u64, 0);
885 }
886 }
887 }
888
889 if let Some(process_label) = get_process_label(pid) {
891 let file_label = SecurityLabel::simple(target, "object_r");
892 if !check_access_full(&process_label, &file_label, access) {
893 crate::security::audit::log_permission_denied(pid, 0, "file_access_mls");
894 return Err(KernelError::PermissionDenied {
895 operation: "file_access_mls",
896 });
897 }
898 }
899
900 Ok(())
901}
902
903pub fn check_ipc_access(access: AccessType, pid: u64) -> Result<(), KernelError> {
908 let source = get_type_for_pid(pid);
909
910 let target = "system_t";
912
913 if !check_access(source, target, access) {
914 crate::security::audit::log_permission_denied(pid, 0, "ipc_access");
915 return Err(KernelError::PermissionDenied {
916 operation: "ipc_access",
917 });
918 }
919
920 if pid > 1 {
922 let required_flags = match access {
923 AccessType::Read => crate::cap::Rights::READ.to_flags(),
924 AccessType::Write => crate::cap::Rights::WRITE.to_flags(),
925 AccessType::Execute => crate::cap::Rights::EXECUTE.to_flags(),
926 };
927 crate::security::audit::log_capability_op(pid, required_flags as u64, 0);
928 }
929
930 Ok(())
931}
932
933pub fn enable() {
939 MAC_ENABLED.store(true, Ordering::Release);
940 println!("[MAC] Mandatory Access Control enabled");
941}
942
943pub fn disable() {
945 MAC_ENABLED.store(false, Ordering::Release);
946 println!("[MAC] Mandatory Access Control disabled");
947}
948
949pub fn is_enabled() -> bool {
951 MAC_ENABLED.load(Ordering::Acquire)
952}
953
954pub fn get_policy_count() -> usize {
956 POLICY_COUNT.load(Ordering::Relaxed)
957}
958
959pub fn load_policy(policy_text: &'static str) -> Result<(), KernelError> {
970 let parsed = PolicyParser::parse(policy_text)?;
971
972 for i in 0..parsed.rule_count {
973 if let Some(rule) = parsed.rules[i] {
974 add_policy_rule(rule)?;
975 }
976 }
977 for i in 0..parsed.transition_count {
978 if let Some(transition) = parsed.transitions[i] {
979 add_transition(transition)?;
980 }
981 }
982 for i in 0..parsed.role_count {
983 if let Some(role) = parsed.roles[i] {
984 add_role(role);
985 }
986 }
987 for i in 0..parsed.user_role_count {
988 if let Some(entry) = &parsed.user_roles[i] {
989 with_policy_db(|db| {
990 db.insert_user_roles(*entry);
991 });
992 }
993 }
994
995 Ok(())
996}
997
998const DEFAULT_POLICY: &str = "
1002# System domain - full access
1003allow system_t system_t { read write execute };
1004allow system_t user_t { read write execute };
1005allow system_t file_t { read write execute };
1006
1007# User domain - limited access
1008allow user_t user_t { read write execute };
1009allow user_t file_t { read write };
1010
1011# Driver domain
1012allow driver_t system_t { read };
1013allow driver_t device_t { read write execute };
1014
1015# Init process
1016allow init_t system_t { read write execute };
1017allow init_t user_t { read write execute };
1018allow init_t file_t { read write execute };
1019
1020# Domain transitions
1021type_transition user_t init_t : process system_t ;
1022
1023# Roles
1024role system_r types { system_t init_t driver_t };
1025role user_r types { user_t };
1026role admin_r types { system_t user_t init_t driver_t };
1027
1028# User-role assignments
1029user root roles { admin_r system_r };
1030user default roles { user_r };
1031";
1032
1033fn load_default_policy() -> Result<(), KernelError> {
1035 load_policy(DEFAULT_POLICY)?;
1036
1037 println!(
1038 "[MAC] Loaded {} default policy rules",
1039 POLICY_COUNT.load(Ordering::Relaxed)
1040 );
1041 Ok(())
1042}
1043
1044pub fn init() -> Result<(), KernelError> {
1050 println!("[MAC] Initializing Mandatory Access Control...");
1051
1052 load_default_policy()?;
1056
1057 set_process_label(0, SecurityLabel::simple("system_t", "system_r"));
1059 set_process_label(1, SecurityLabel::simple("init_t", "system_r"));
1060
1061 enable();
1063
1064 println!(
1065 "[MAC] MAC system initialized with {} rules",
1066 POLICY_COUNT.load(Ordering::Relaxed)
1067 );
1068 Ok(())
1069}
1070
1071fn get_type_for_pid(pid: u64) -> &'static str {
1079 if let Some(label) = get_process_label(pid) {
1081 return label.type_name;
1082 }
1083
1084 match pid {
1086 0 => "system_t",
1087 1 => "init_t",
1088 _ => "user_t",
1089 }
1090}
1091
1092#[cfg(test)]
1097mod tests {
1098 use super::*;
1099
1100 #[test]
1101 fn test_policy_rule() {
1102 let rule = PolicyRule::from_legacy("user_t", "file_t", 0x3); assert!(rule.allows(AccessType::Read));
1104 assert!(rule.allows(AccessType::Write));
1105 assert!(!rule.allows(AccessType::Execute));
1106 }
1107
1108 #[test]
1109 fn test_add_rule() {
1110 let rule = PolicyRule::new(
1111 "test_t",
1112 "test_t",
1113 [Permission::Read, Permission::Write, Permission::Execute],
1114 3,
1115 PolicyAction::Allow,
1116 );
1117 assert!(add_policy_rule(rule).is_ok());
1118 }
1119
1120 #[test]
1121 fn test_mls_dominance() {
1122 let high = MlsLevel::new(3, 0b111);
1123 let low = MlsLevel::new(1, 0b001);
1124 let mid = MlsLevel::new(2, 0b011);
1125
1126 assert!(high.dominates(&low));
1127 assert!(high.dominates(&mid));
1128 assert!(!low.dominates(&high));
1129 assert!(!low.dominates(&mid)); assert!(mid.dominates(&low)); }
1132
1133 #[test]
1134 fn test_deny_overrides_allow() {
1135 let allow_rule = PolicyRule::from_perms(
1136 "deny_test_t",
1137 "deny_target_t",
1138 &[Permission::Read, Permission::Write],
1139 PolicyAction::Allow,
1140 );
1141 let deny_rule = PolicyRule::from_perms(
1142 "deny_test_t",
1143 "deny_target_t",
1144 &[Permission::Write],
1145 PolicyAction::Deny,
1146 );
1147 let _ = add_policy_rule(allow_rule);
1148 let _ = add_policy_rule(deny_rule);
1149
1150 let was_enabled = MAC_ENABLED.load(Ordering::Acquire);
1153 MAC_ENABLED.store(true, Ordering::Release);
1154
1155 let result = check_access("deny_test_t", "deny_target_t", AccessType::Write);
1157
1158 MAC_ENABLED.store(was_enabled, Ordering::Release);
1160
1161 assert!(!result);
1162 }
1163
1164 #[test]
1165 fn test_policy_parser() {
1166 let policy: &'static str =
1167 "allow src_t dst_t { read write }; deny src_t dst_t { execute };";
1168 let result = PolicyParser::parse(policy);
1169 assert!(result.is_ok());
1170 let parsed = result.unwrap();
1171 assert_eq!(parsed.rule_count, 2);
1172 assert_eq!(parsed.rules[0].unwrap().action, PolicyAction::Allow);
1173 assert_eq!(parsed.rules[1].unwrap().action, PolicyAction::Deny);
1174 }
1175
1176 #[test]
1177 fn test_domain_transition_parse() {
1178 let policy: &'static str = "type_transition user_t init_t : process system_t ;";
1179 let result = PolicyParser::parse(policy);
1180 assert!(result.is_ok());
1181 let parsed = result.unwrap();
1182 assert_eq!(parsed.transition_count, 1);
1183 assert_eq!(parsed.transitions[0].unwrap().new_type, "system_t");
1184 }
1185
1186 #[test]
1187 fn test_rbac_parse() {
1188 let policy: &'static str =
1189 "role admin_r types { system_t user_t }; user root roles { admin_r };";
1190 let result = PolicyParser::parse(policy);
1191 assert!(result.is_ok());
1192 let parsed = result.unwrap();
1193 assert_eq!(parsed.role_count, 1);
1194 assert!(parsed.roles[0].unwrap().allows_type("system_t"));
1195 assert_eq!(parsed.user_role_count, 1);
1196 assert_eq!(parsed.user_roles[0].unwrap().username, "root");
1197 }
1198}