From 7bef357b794c5890442683a4061c79c6f9148c48 Mon Sep 17 00:00:00 2001 From: hocine Date: Wed, 12 Mar 2025 09:58:36 +0100 Subject: [PATCH 1/8] feat: add group support to user credentials and implement equality checks for SGroups --- rar-common/src/database/actor.rs | 19 + rar-common/src/database/finder.rs | 888 ++++++++++++++++++++++++++++- rar-common/src/database/mod.rs | 3 +- rar-common/src/database/structs.rs | 58 +- src/chsr/cli/mod.rs | 3 +- src/chsr/cli/process/json.rs | 4 +- src/sr/main.rs | 8 + 7 files changed, 951 insertions(+), 32 deletions(-) diff --git a/rar-common/src/database/actor.rs b/rar-common/src/database/actor.rs index 8d63b4f2..d74c122c 100644 --- a/rar-common/src/database/actor.rs +++ b/rar-common/src/database/actor.rs @@ -67,6 +67,15 @@ impl fmt::Display for SGroupType { } impl SGroupType { + + pub fn fetch_eq(&self, other: &Self) -> bool { + let uid = self.fetch_id(); + let ouid = other.fetch_id(); + match (uid, ouid) { + (Some(uid), Some(ouid)) => uid == ouid, + _ => false, + } + } pub(super) fn fetch_id(&self) -> Option { match &self.0 { SGenericActorType::Id(id) => Some(*id), @@ -110,6 +119,16 @@ impl SGroups { pub fn is_empty(&self) -> bool { self.len() == 0 } + + pub fn fetch_eq(&self, other: &Self) -> bool { + match (self, other) { + (SGroups::Single(group), SGroups::Single(ogroup)) => group.fetch_eq(ogroup), + (SGroups::Multiple(groups), SGroups::Multiple(ogroups)) => { + groups.iter().all(|group| ogroups.iter().any(|ogroup| group.fetch_eq(ogroup))) + } + _ => false, + } + } } impl<'de> Deserialize<'de> for SGenericActorType { diff --git a/rar-common/src/database/finder.rs b/rar-common/src/database/finder.rs index 83dc38d8..c7f7d134 100644 --- a/rar-common/src/database/finder.rs +++ b/rar-common/src/database/finder.rs @@ -23,7 +23,10 @@ use strum::EnumIs; use crate::database::{ actor::SActor, options::{Opt, OptStack}, - structs::{SCommand, SCommands, SConfig, SRole, STask, SUserChooser, SetBehavior}, + structs::{ + SCommand, SCommands, SConfig, SGroupschooser, SRole, STask, SUserChooser, + SetBehavior + }, }; use crate::util::{capabilities_are_exploitable, final_path, parse_conf_command}; use crate::{ @@ -775,14 +778,57 @@ impl TaskMatcher for Rc> { // Set gid processing let setgid = &self.as_ref().borrow().cred.setgid; + let setgid_result: Option = match setgid { + Some(SGroupschooser::Group(s)) => Some(s.clone()), + Some(SGroupschooser::StructChooser(m)) => { + match cmd_opt.as_ref().and_then(|cmd| cmd.group.as_ref()) { + None => { + debug!( + "No group specified in the command, fallback used : {:?}", + m.fallback + ); + Some(m.fallback.clone()) + } + Some(ggroup) => { + debug!("Group specified in the command : {:?}", ggroup); + if ggroup.fetch_eq(&m.fallback) { + debug!("The group specified in the command matches the fallback !"); + Some(m.fallback.clone()) + } else if m.sub.iter().any(|s| s.fetch_eq(ggroup)) { + return Err(MatchError::NoMatch( + "The group is forbidden in sub.".into(), + )); + } else if m.add.iter().any(|s| s.fetch_eq(ggroup)) { + Some(ggroup.clone()) + } else { + match m.default { + SetBehavior::None => { + return Err(MatchError::NoMatch( + "No default behavior applicable.".into(), + )); + } + SetBehavior::All => { + debug!("All groups are accepted."); + Some(ggroup.clone()) + } + } + } + } + } + } + None => None, + }; // Calculate setuid and setgid minimum - score.setuser_min = - get_setuid_min(setuid_result.as_ref(), setgid.as_ref(), &score.security_min); + score.setuser_min = get_setuid_min( + setuid_result.as_ref(), + setgid_result.as_ref(), + &score.security_min, + ); // Update task settings settings.setuid = setuid_result.clone(); - settings.setgroups = setgid.clone(); + settings.setgroups = setgid_result.clone(); settings.caps = capset; // Get options stack from the task @@ -1171,7 +1217,7 @@ mod tests { database::{ make_weak_config, options::{EnvBehavior, PathBehavior, SAuthentication, SBounding, SPrivileged}, - structs::{IdTask, RoleGetter, SCredentials, SSetuidSet}, + structs::{IdTask, RoleGetter, SCredentials, SSetuidSet,SSetgidSet}, versionning::Versioning, }, rc_refcell, @@ -1667,7 +1713,6 @@ mod tests { }; task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); - // Création des credentials avec l'utilisateur correspondant au `fallback` let cred = Cred::builder().user_name("root").group_name("root").build(); // Commande de test @@ -1714,9 +1759,8 @@ mod tests { }; task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); - // Création des credentials sans spécifier d'utilisateur let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), // Utilisateur non spécifié + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -1764,9 +1808,8 @@ mod tests { }; task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); - // Création des credentials avec l'utilisateur correspondant à l'ajout let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), // Même nom que l'ajout + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -1815,9 +1858,8 @@ mod tests { }; task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); - // Création des credentials avec l'utilisateur correspondant à l'ajout let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), // Même nom que l'ajout + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -1864,9 +1906,9 @@ mod tests { }; task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); - // Création des credentials avec l'utilisateur correspondant à l'ajout + let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), // Même nom que l'ajout + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -1889,7 +1931,7 @@ mod tests { } #[test] - //echec + fn test_setuid_all_valid() { // Configuration de test let config = setup_test_config(1); // Un seul rôle pour simplifier @@ -1914,9 +1956,8 @@ mod tests { }; task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); - // Création des credentials avec l'utilisateur correspondant à l'ajout let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), // Même nom que l'ajout + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -1933,7 +1974,7 @@ mod tests { assert!(result.is_ok()); let result = result.unwrap(); - // Vérification que l'utilisateur assigné est bien celui de l'ajout + // Vérification que l'utilisateur assigné est autorisé assert_eq!(result.settings.setuid, Some(SUserType::from("root"))); println!("Test réussi : L'utilisateur spécifié correspond bien à l'ajout."); @@ -1962,9 +2003,8 @@ mod tests { }; task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); - // Création des credentials avec l'utilisateur correspondant à l'ajout let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), // Même nom que l'ajout + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2013,9 +2053,8 @@ mod tests { }; task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); - // Création des credentials avec l'utilisateur correspondant à l'ajout let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), // Même nom que l'ajout + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2061,9 +2100,8 @@ mod tests { }; task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); - // Création des credentials avec l'utilisateur correspondant à l'ajout let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), // Même nom que l'ajout + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2305,4 +2343,806 @@ mod tests { " Test réussi : L'utilisateur spécifié correspond bien à l'ajout pour la tâche 3." ); } + + + + + + + + + + + + + + + #[test] + fn test_setgid_fallback_single_valid() { + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_uid()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::None, + add: vec![], + sub: vec![], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(fallback_group.clone()) + .build(); + + // Exécution du match + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_ok()); + let result = result.unwrap(); + + // Vérification que le groupe assigné est bien celui de fallback + assert_eq!(result.settings.setgroups, Some(fallback_group.clone())); + + println!("Test successful: The specified user correctly matches the fallback."); + } + + #[test] + fn test_setgid_fallback_multiple_valid() { + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::Multiple(vec![ + SGroupType::from(get_non_root_uid()), + SGroupType::from("aitbelkacem"), + ]); let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::None, + add: vec![], + sub: vec![], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(SGroups::Multiple(vec![ + SGroupType::from(get_non_root_uid()), + SGroupType::from("aitbelkacem"), + ])) + .build(); + + // Exécution du match + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_ok()); + let result = result.unwrap(); + + // Vérification que le groupe assigné est bien celui de fallaback + assert_eq!(result.settings.setgroups, Some(fallback_group.clone())); + + println!("Test successful: The specified user correctly matches the fallback."); + } + + + #[test] + fn test_setgid_fallback_nonarg_valid() { + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_uid()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::None, + add: vec![], + sub: vec![], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let result = config.matches(&cred, &None, &command); + // Vérification que le match est réussi + assert!(result.is_ok()); + let result = result.unwrap(); + + // Vérification que le groupe assigné est bien celui de fallback + assert_eq!(result.settings.setgroups, Some(fallback_group.clone())); + + println!("Test successful: The specified group correctly matches the fallback when no valid group is provided."); + } + + #[test] + fn test_setgid_add_single_valid() { + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_uid()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::None, + add: vec![SGroups::from("root")], + sub: vec![], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(SGroups::from("root")) + .build(); + + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_ok()); + let result = result.unwrap(); + + // Vérification que le groupe assigné est bien celui de l'ajout + assert_eq!( + result.settings.setgroups, + Some(SGroups::from("root")) + ); + println!("Test réussi : Le groupe spécifié correspond bien à l'ajout."); + } + + #[test] + fn test_setgid_add_multiple_valid() { + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_uid()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::None, + add: vec![ + SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from("aitbelkacem"), + ]), + ], + sub: vec![], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from("aitbelkacem"), + ])) + .build(); + + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_ok()); + let result = result.unwrap(); + + // Vérification que le groupe assigné est bien celui de l'ajout + assert_eq!( + result.settings.setgroups, + Some(SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from("aitbelkacem"), + ])) + ); + println!("Test réussi : Le groupe spécifié correspond bien à l'ajout."); + } + + #[test] + fn test_setgid_add_sub_invalid(){ + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_uid()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::None, + add: vec![SGroups::from("root")], + sub: vec![SGroups::from("root")], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(SGroups::from("root")) + .build(); + + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_err()); + let result = result.unwrap_err(); + + // Vérification que l'erreur est bien de type `NoMatch` + assert!(result.is_no_match()); + + println!("Test réussi : Le groupe spécifié ne correspond pas "); + + } + + #[test] + fn test_setgid_all_sub_single_invalid(){ + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_uid()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::All, + add: vec![], + sub: vec![SGroups::from("root")], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(SGroups::from("root")) + .build(); + + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_err()); + let result = result.unwrap_err(); + + // Vérification que l'erreur est bien de type `NoMatch` + assert!(result.is_no_match()); + + println!("Test réussi : Le groupe spécifié ne correspond pas "); + + } + + #[test] + fn test_setgid_all_sub_multiple_invalid(){ + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_uid()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::All, + add: vec![], + sub: vec![SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from("aitbelkacem"), + ])], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from("aitbelkacem"), + ])) + .build(); + + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_err()); + let result = result.unwrap_err(); + + // Vérification que l'erreur est bien de type `NoMatch` + assert!(result.is_no_match()); + + println!("Test réussi : Le groupe spécifié ne correspond pas "); + + } + + #[test] + fn test_setgid_all_valid(){ + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_uid()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::All, + add: vec![], + sub: vec![], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(SGroups::from("root")) + .build(); + + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_ok()); + let result = result.unwrap(); + + // Vérification que le groupe assigné est autorisé + assert_eq!(result.settings.setgroups, Some(SGroups::from("root"))); + + println!("Test réussi : Le groupe spécifié correspond bien à l'ajout."); + + } + + #[test] + fn test_setgid_none_invalid(){ + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_uid()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::None, + add: vec![], + sub: vec![], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(SGroups::from("root")) + .build(); + + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_err()); + let result = result.unwrap_err(); + + // Vérification que l'erreur est bien de type `NoMatch` + assert!(result.is_no_match()); + + println!("Test réussi : Le groupe spécifié ne correspond pas "); + + } + + #[test] + fn test_setgid_all_add_single_valid(){ + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_uid()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::All, + add: vec![SGroups::from("root")], + sub: vec![], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(SGroups::from("root")) + .build(); + + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_ok()); + let result = result.unwrap(); + + // Vérification que le groupe assigné est bien celui de l'ajout + assert_eq!(result.settings.setgroups, Some(SGroups::from("root"))); + + println!("Test réussi : Le groupe spécifié correspond bien à l'ajout."); + + } + + #[test] + fn test_setgid_all_add_multiple_valid(){ + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_uid()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::All, + add: vec![SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from("aitbelkacem"), + ])], + sub: vec![], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from("aitbelkacem"), + ])) + .build(); + + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_ok()); + let result = result.unwrap(); + + // Vérification que le groupe assigné est bien celui de l'ajout + assert_eq!(result.settings.setgroups, Some(SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from("aitbelkacem"), + ]))); + + println!("Test réussi : Le groupe spécifié correspond bien à l'ajout."); + } + + #[test] + fn test_setgid_none_add_single_invalid() { + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::None); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_uid()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::None, + add: vec![SGroups::from("root")], + sub: vec![], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .user(SUserType::from("aitbelkacem")) + .build(); + + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_err()); + let result = result.unwrap_err(); + + // Vérification que l'erreur est bien de type `NoMatch` + assert!(result.is_no_match()); + + println!("Test réussi : Le groupe spécifié ne correspond pas "); + } + + #[test] + fn test_setgid_none_add_multiple_invvalid(){ + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_uid()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::None, + add: vec![SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from("aitbelkacem"), + ])], + sub: vec![], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(SGroups::Multiple(vec![ + SGroupType::from(get_non_root_uid()), + SGroupType::from("root"), + ])) + .build(); + + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_err()); + let result = result.unwrap_err(); + + // Vérification que l'erreur est bien de type `NoMatch` + assert!(result.is_no_match()); + + println!("Test réussi : Le groupe spécifié ne correspond pas "); + + } } diff --git a/rar-common/src/database/mod.rs b/rar-common/src/database/mod.rs index d68035f1..96533fc0 100644 --- a/rar-common/src/database/mod.rs +++ b/rar-common/src/database/mod.rs @@ -5,7 +5,7 @@ use crate::save_settings; use crate::util::{toggle_lock_config, ImmutableLock}; use crate::version::PACKAGE_VERSION; -use actor::SUserType; +use actor::{ SGroups, SUserType}; use bon::{builder, Builder}; use chrono::Duration; use linked_hash_set::LinkedHashSet; @@ -36,6 +36,7 @@ pub struct FilterMatcher { pub env_behavior: Option, #[builder(into)] pub user: Option, + pub group:Option, } pub fn make_weak_config(config: &Rc>) { diff --git a/rar-common/src/database/structs.rs b/rar-common/src/database/structs.rs index a4aafbaf..8387d14d 100644 --- a/rar-common/src/database/structs.rs +++ b/rar-common/src/database/structs.rs @@ -135,7 +135,7 @@ pub struct SCredentials { pub setuid: Option, #[serde(skip_serializing_if = "Option::is_none")] #[builder(into)] - pub setgid: Option, + pub setgid: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub capabilities: Option, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -201,6 +201,54 @@ pub enum SetBehavior { #[default] None, } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(untagged)] +pub enum SGroupschooser { + Group (SGroups), + StructChooser (SSetgidSet), +} + +impl From for SGroupschooser { + fn from(group: SGroups) -> Self { + SGroupschooser::Group(group) + } +} + +impl From for SGroupschooser { + fn from(set: SSetgidSet) -> Self { + SGroupschooser::StructChooser(set) + } +} + +impl From<&str> for SGroupschooser { + fn from(name: &str) -> Self { + SGroupschooser::Group(name.into()) + } +} + +impl From for SGroupschooser { + fn from(id: u32) -> Self { + SGroupschooser::Group(id.into()) + } +} + +#[derive(Serialize, Deserialize, Debug, Clone, Builder, PartialEq, Eq)] +pub struct SSetgidSet { + #[builder(start_fn, into)] + pub fallback: SGroups, + #[serde(rename = "default", default, skip_serializing_if = "is_default")] + #[builder(start_fn)] + pub default: SetBehavior, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + #[builder(default, with = FromIterator::from_iter)] + pub add: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + #[builder(default, with = FromIterator::from_iter)] + pub sub: Vec, +} + + + #[derive(PartialEq, Eq, Debug, Builder)] pub struct SCapabilities { @@ -839,7 +887,8 @@ mod tests { assert!( matches!(cred.setuid.as_ref().unwrap(), SUserChooser::ChooserStruct(set) if set == &setuidstruct) ); - assert_eq!(*cred.setgid.as_ref().unwrap(), ["setgid1".into()]); + assert_eq!(*cred.setgid.as_ref().unwrap(), SGroupschooser::Group(SGroups::from("setgid1"))); + let capabilities = cred.capabilities.as_ref().unwrap(); assert_eq!(capabilities.default_behavior, SetBehavior::All); assert!(capabilities.add.has(Cap::NET_BIND_SERVICE)); @@ -1077,7 +1126,8 @@ mod tests { cred.setuid.as_ref().unwrap(), &SUserChooser::from(SUserType::from("setuid1")) ); - assert_eq!(cred.setgid.as_ref().unwrap(), &SGroups::from(["setgid1"])); + assert_eq!(*cred.setgid.as_ref().unwrap(), SGroupschooser::Group(SGroups::from("setgid1"))); + let capabilities = cred.capabilities.as_ref().unwrap(); assert_eq!(capabilities.default_behavior, SetBehavior::None); assert!(capabilities.add.has(Cap::NET_BIND_SERVICE)); @@ -1111,7 +1161,7 @@ mod tests { .sub(["user3".into()]) .build(), )) - .setgid(["setgid1"]) + .setgid(SGroupschooser::Group(SGroups::from("setgid1"))) .capabilities( SCapabilities::builder(SetBehavior::All) .add_cap(Cap::NET_BIND_SERVICE) diff --git a/src/chsr/cli/mod.rs b/src/chsr/cli/mod.rs index edf76386..0f0c7b77 100644 --- a/src/chsr/cli/mod.rs +++ b/src/chsr/cli/mod.rs @@ -44,6 +44,7 @@ mod tests { use rar_common::{ database::{ actor::SActor, + actor::SGroups, options::*, read_json_config, structs::{SCredentials, *}, @@ -203,7 +204,7 @@ mod tests { .cred( SCredentials::builder() .setuid("user1") - .setgid(["group1", "group2"]) + .setgid(SGroupschooser::Group(SGroups::from(["setgid1","setgid2"]))) .capabilities( SCapabilities::builder(SetBehavior::All) .add_cap(Cap::LINUX_IMMUTABLE) diff --git a/src/chsr/cli/process/json.rs b/src/chsr/cli/process/json.rs index 4fe165ca..2ad57c31 100644 --- a/src/chsr/cli/process/json.rs +++ b/src/chsr/cli/process/json.rs @@ -10,7 +10,7 @@ use rar_common::database::{ EnvBehavior, EnvKey, Opt, OptStack, OptType, PathBehavior, SEnvOptions, SPathOptions, STimeout, }, - structs::{IdTask, RoleGetter, SCapabilities, SCommand, SRole, STask, SUserChooser}, + structs::{IdTask, RoleGetter, SCapabilities, SCommand, SRole, STask, SUserChooser,SGroupschooser}, }; use super::perform_on_target_opt; @@ -323,7 +323,7 @@ pub fn cred_set( task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::Actor(setuid)); } if let Some(setgid) = cred_setgid { - task.as_ref().borrow_mut().cred.setgid = Some(setgid); + task.as_ref().borrow_mut().cred.setgid = Some(SGroupschooser::Group(setgid.clone())); } Ok(true) } diff --git a/src/sr/main.rs b/src/sr/main.rs index 1a1d684c..4c85e947 100644 --- a/src/sr/main.rs +++ b/src/sr/main.rs @@ -69,6 +69,8 @@ const USAGE: &str = formatcp!( {BOLD}-u, --user {RST} Specify the user to execute the command as + {BOLD} -g --group {RST} + Specify the group to execute the command as {BOLD}-i, --info{RST} Display rights of executor @@ -143,13 +145,18 @@ where let mut role = None; let mut task = None; let mut user: Option = None; + let mut group: Option = None; let mut env = None; + while let Some(arg) = iter.next() { // matches only first options match arg.as_ref() { "-u" | "--user" => { user = iter.next().map(|s| escape_parser_string(s).as_str().into()); } + "-g" | "--group" => { + group = iter.next().map(|s| escape_parser_string(s).as_str().into()); + } "-S" | "--stdin" => { args.stdin = true; } @@ -190,6 +197,7 @@ where .maybe_task(task) .maybe_env_behavior(env) .maybe_user(user) + .maybe_group(group) .build(), ); for arg in iter { From 2fa9b6ecc215db6e92c9680754f6f407d932df60 Mon Sep 17 00:00:00 2001 From: hocine Date: Wed, 12 Mar 2025 10:23:36 +0100 Subject: [PATCH 2/8] test: concurrency issue with path test --- rar-common/src/database/options.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rar-common/src/database/options.rs b/rar-common/src/database/options.rs index 0dce33c4..e2186713 100644 --- a/rar-common/src/database/options.rs +++ b/rar-common/src/database/options.rs @@ -2010,7 +2010,7 @@ mod tests { #[test] fn test_safe_path() { let path = std::env::var("PATH").unwrap(); - std::env::set_var("PATH", "/sys:./proc:/tmp:/bin"); + let config = SConfig::builder() .role( SRole::builder("test") @@ -2031,6 +2031,7 @@ mod tests { ) .build(); let options = OptStack::from_task(config.task("test", 1).unwrap()); + std::env::set_var("PATH", "/sys:./proc:/tmp:/bin"); let res = options.calculate_path(); assert_eq!(res, "/tmp:/bin"); From 42f75d8ea1c3192973a568fda26b1ae19a5a2639 Mon Sep 17 00:00:00 2001 From: hocine Date: Wed, 12 Mar 2025 10:23:45 +0100 Subject: [PATCH 3/8] feat: replace get_non_root_uid with get_non_root_gid for group handling --- rar-common/src/database/finder.rs | 76 ++++++++++++++++++------------- 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/rar-common/src/database/finder.rs b/rar-common/src/database/finder.rs index 5612ffc7..73ee8602 100644 --- a/rar-common/src/database/finder.rs +++ b/rar-common/src/database/finder.rs @@ -1238,6 +1238,20 @@ mod tests { .unwrap(); } + fn get_non_root_gid(nth : usize) -> Option { + // list all users + let passwd = fs::read_to_string("/etc/group").unwrap(); + let passwd: Vec<&str> = passwd.split('\n').collect(); + return passwd + .iter() + .map(|line| { + let line: Vec<&str> = line.split(':').collect(); + line[2].parse::().unwrap() + }) + .filter(|uid| *uid != 0) + .nth(nth); + } + #[test] fn test_find_from_envpath() { let needle = PathBuf::from("ls"); @@ -2372,7 +2386,7 @@ mod tests { task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); // Définition du `setgid` avec un `fallback` - let fallback_group = SGroups::from(get_non_root_uid()); + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::None, @@ -2427,8 +2441,8 @@ mod tests { // Définition du `setgid` avec un `fallback` let fallback_group = SGroups::Multiple(vec![ - SGroupType::from(get_non_root_uid()), - SGroupType::from("aitbelkacem"), + SGroupType::from(get_non_root_gid(0).unwrap()), + SGroupType::from(get_non_root_gid(1).unwrap()), ]); let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::None, @@ -2451,8 +2465,8 @@ mod tests { // Exécution du match let filter_matcher = FilterMatcher::builder() .group(SGroups::Multiple(vec![ - SGroupType::from(get_non_root_uid()), - SGroupType::from("aitbelkacem"), + SGroupType::from(get_non_root_gid(0).unwrap()), + SGroupType::from(get_non_root_gid(1).unwrap()), ])) .build(); @@ -2486,7 +2500,7 @@ mod tests { task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); // Définition du `setgid` avec un `fallback` - let fallback_group = SGroups::from(get_non_root_uid()); + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::None, @@ -2535,7 +2549,7 @@ mod tests { task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); // Définition du `setgid` avec un `fallback` - let fallback_group = SGroups::from(get_non_root_uid()); + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::None, @@ -2590,14 +2604,14 @@ mod tests { task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); // Définition du `setgid` avec un `fallback` - let fallback_group = SGroups::from(get_non_root_uid()); + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::None, add: vec![ SGroups::Multiple(vec![ - SGroupType::from("root"), - SGroupType::from("aitbelkacem"), + SGroupType::from(0), + SGroupType::from(get_non_root_gid(1).unwrap()), ]), ], sub: vec![], @@ -2618,8 +2632,8 @@ mod tests { // Exécution du match let filter_matcher = FilterMatcher::builder() .group(SGroups::Multiple(vec![ - SGroupType::from("root"), - SGroupType::from("aitbelkacem"), + SGroupType::from(0), + SGroupType::from(get_non_root_gid(1).unwrap()), ])) .build(); @@ -2633,8 +2647,8 @@ mod tests { assert_eq!( result.settings.setgroups, Some(SGroups::Multiple(vec![ - SGroupType::from("root"), - SGroupType::from("aitbelkacem"), + SGroupType::from(0), + SGroupType::from(get_non_root_gid(1).unwrap()), ])) ); println!("Test réussi : Le groupe spécifié correspond bien à l'ajout."); @@ -2656,7 +2670,7 @@ mod tests { task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); // Définition du `setgid` avec un `fallback` - let fallback_group = SGroups::from(get_non_root_uid()); + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::None, @@ -2710,7 +2724,7 @@ mod tests { task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); // Définition du `setgid` avec un `fallback` - let fallback_group = SGroups::from(get_non_root_uid()); + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::All, @@ -2764,14 +2778,14 @@ mod tests { task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); // Définition du `setgid` avec un `fallback` - let fallback_group = SGroups::from(get_non_root_uid()); + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::All, add: vec![], sub: vec![SGroups::Multiple(vec![ SGroupType::from("root"), - SGroupType::from("aitbelkacem"), + SGroupType::from(get_non_root_gid(1).unwrap()), ])], }; task.as_ref().borrow_mut().cred.setgid = @@ -2791,7 +2805,7 @@ mod tests { let filter_matcher = FilterMatcher::builder() .group(SGroups::Multiple(vec![ SGroupType::from("root"), - SGroupType::from("aitbelkacem"), + SGroupType::from(get_non_root_gid(1).unwrap()), ])) .build(); @@ -2824,7 +2838,7 @@ mod tests { task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); // Définition du `setgid` avec un `fallback` - let fallback_group = SGroups::from(get_non_root_uid()); + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::All, @@ -2878,7 +2892,7 @@ mod tests { task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); // Définition du `setgid` avec un `fallback` - let fallback_group = SGroups::from(get_non_root_uid()); + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::None, @@ -2932,7 +2946,7 @@ mod tests { task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); // Définition du `setgid` avec un `fallback` - let fallback_group = SGroups::from(get_non_root_uid()); + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::All, @@ -2986,13 +3000,13 @@ mod tests { task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); // Définition du `setgid` avec un `fallback` - let fallback_group = SGroups::from(get_non_root_uid()); + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::All, add: vec![SGroups::Multiple(vec![ SGroupType::from("root"), - SGroupType::from("aitbelkacem"), + SGroupType::from(get_non_root_gid(1).unwrap()), ])], sub: vec![], }; @@ -3013,7 +3027,7 @@ mod tests { let filter_matcher = FilterMatcher::builder() .group(SGroups::Multiple(vec![ SGroupType::from("root"), - SGroupType::from("aitbelkacem"), + SGroupType::from(get_non_root_gid(1).unwrap()), ])) .build(); @@ -3026,7 +3040,7 @@ mod tests { // Vérification que le groupe assigné est bien celui de l'ajout assert_eq!(result.settings.setgroups, Some(SGroups::Multiple(vec![ SGroupType::from("root"), - SGroupType::from("aitbelkacem"), + SGroupType::from(get_non_root_gid(1).unwrap()), ]))); println!("Test réussi : Le groupe spécifié correspond bien à l'ajout."); @@ -3048,7 +3062,7 @@ mod tests { task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::None); // Définition du `setgid` avec un `fallback` - let fallback_group = SGroups::from(get_non_root_uid()); + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::None, @@ -3070,7 +3084,7 @@ mod tests { // Exécution du match let filter_matcher = FilterMatcher::builder() - .user(SUserType::from("aitbelkacem")) + .user(SUserType::from(get_non_root_gid(1).unwrap())) .build(); let result = config.matches(&cred, &Some(filter_matcher), &command); @@ -3101,13 +3115,13 @@ mod tests { task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); // Définition du `setgid` avec un `fallback` - let fallback_group = SGroups::from(get_non_root_uid()); + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::None, add: vec![SGroups::Multiple(vec![ SGroupType::from("root"), - SGroupType::from("aitbelkacem"), + SGroupType::from(get_non_root_gid(1).unwrap()), ])], sub: vec![], }; @@ -3127,7 +3141,7 @@ mod tests { // Exécution du match let filter_matcher = FilterMatcher::builder() .group(SGroups::Multiple(vec![ - SGroupType::from(get_non_root_uid()), + SGroupType::from(get_non_root_gid(0).unwrap()), SGroupType::from("root"), ])) .build(); From 8315b23f856c93ec8f6ea04198f2abe21b3b8e4a Mon Sep 17 00:00:00 2001 From: hocine Date: Wed, 12 Mar 2025 15:30:01 +0100 Subject: [PATCH 4/8] refactor: remove debug print statements from calculate_path and environment handling --- rar-common/src/database/options.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/rar-common/src/database/options.rs b/rar-common/src/database/options.rs index e2186713..9d10deff 100644 --- a/rar-common/src/database/options.rs +++ b/rar-common/src/database/options.rs @@ -766,7 +766,6 @@ impl OptStack { fn calculate_path(&self) -> String { let path = self.get_final_path(); let default = LinkedHashSet::new(); - println!("path: {:?}", path); if let Some(add) = path.add { let final_add = add.difference(path.sub.as_ref().unwrap_or(&default)).fold( "".to_string(), @@ -967,7 +966,6 @@ impl OptStack { I: Iterator, { let env = self.get_final_env(opt_filter); - println!("env: {:?}", env); if env.default_behavior.is_keep() { warn!("Keeping environment variables is dangerous operation, it can lead to security vulnerabilities. Please consider using delete instead. From 9929cf1fa386df797701b4cc9ca2a87642379af6 Mon Sep 17 00:00:00 2001 From: hocine Date: Wed, 12 Mar 2025 15:32:06 +0100 Subject: [PATCH 5/8] tests: add tests and fix -g --- rar-common/src/database/actor.rs | 15 +- rar-common/src/database/finder.rs | 531 ++++++++++++++++++++--------- rar-common/src/database/mod.rs | 4 +- rar-common/src/database/options.rs | 2 +- rar-common/src/database/structs.rs | 17 +- src/chsr/cli/mod.rs | 4 +- src/chsr/cli/process/json.rs | 7 +- src/sr/main.rs | 14 +- 8 files changed, 412 insertions(+), 182 deletions(-) diff --git a/rar-common/src/database/actor.rs b/rar-common/src/database/actor.rs index f63a0e3c..b8007b55 100644 --- a/rar-common/src/database/actor.rs +++ b/rar-common/src/database/actor.rs @@ -67,7 +67,6 @@ impl fmt::Display for SGroupType { } impl SGroupType { - pub fn fetch_eq(&self, other: &Self) -> bool { let uid = self.fetch_id(); let ouid = other.fetch_id(); @@ -123,9 +122,9 @@ impl SGroups { pub fn fetch_eq(&self, other: &Self) -> bool { match (self, other) { (SGroups::Single(group), SGroups::Single(ogroup)) => group.fetch_eq(ogroup), - (SGroups::Multiple(groups), SGroups::Multiple(ogroups)) => { - groups.iter().all(|group| ogroups.iter().any(|ogroup| group.fetch_eq(ogroup))) - } + (SGroups::Multiple(groups), SGroups::Multiple(ogroups)) => groups + .iter() + .all(|group| ogroups.iter().any(|ogroup| group.fetch_eq(ogroup))), _ => false, } } @@ -500,4 +499,12 @@ mod tests { let groups = SGroups::Multiple(vec![]); assert!(groups.is_empty()); } + + #[test] + fn test_fetch_eq_sgroupstype_false() { + let group1 = SGroupType::from("unkown"); + let group2 = SGroupType::from("unkown2"); + + assert!(!group1.fetch_eq(&group2)); + } } diff --git a/rar-common/src/database/finder.rs b/rar-common/src/database/finder.rs index 73ee8602..a8a31407 100644 --- a/rar-common/src/database/finder.rs +++ b/rar-common/src/database/finder.rs @@ -24,8 +24,7 @@ use crate::database::{ actor::SActor, options::{Opt, OptStack}, structs::{ - SCommand, SCommands, SConfig, SGroupschooser, SRole, STask, SUserChooser, - SetBehavior + SCommand, SCommands, SConfig, SGroupschooser, SRole, STask, SUserChooser, SetBehavior, }, }; use crate::util::{capabilities_are_exploitable, final_path, parse_conf_command}; @@ -1207,7 +1206,7 @@ impl TaskMatcher for Rc> { #[cfg(test)] mod tests { - use std::fs; + use std::{fs, vec}; use capctl::Cap; use test_log::test; @@ -1216,7 +1215,7 @@ mod tests { database::{ make_weak_config, options::{EnvBehavior, PathBehavior, SAuthentication, SBounding, SPrivileged}, - structs::{IdTask, RoleGetter, SCredentials, SSetuidSet,SSetgidSet}, + structs::{IdTask, RoleGetter, SCredentials, SSetgidSet, SSetuidSet}, versionning::Versioning, }, rc_refcell, @@ -1238,7 +1237,7 @@ mod tests { .unwrap(); } - fn get_non_root_gid(nth : usize) -> Option { + fn get_non_root_gid(nth: usize) -> Option { // list all users let passwd = fs::read_to_string("/etc/group").unwrap(); let passwd: Vec<&str> = passwd.split('\n').collect(); @@ -1773,7 +1772,7 @@ mod tests { task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -1822,7 +1821,7 @@ mod tests { task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -1872,7 +1871,7 @@ mod tests { task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -1919,9 +1918,8 @@ mod tests { }; task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); - let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -1970,7 +1968,7 @@ mod tests { task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2017,7 +2015,7 @@ mod tests { task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2067,7 +2065,7 @@ mod tests { task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2114,7 +2112,7 @@ mod tests { task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::ChooserStruct(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2357,19 +2355,6 @@ mod tests { ); } - - - - - - - - - - - - - #[test] fn test_setgid_fallback_single_valid() { // Configuration de test @@ -2443,7 +2428,8 @@ mod tests { let fallback_group = SGroups::Multiple(vec![ SGroupType::from(get_non_root_gid(0).unwrap()), SGroupType::from(get_non_root_gid(1).unwrap()), - ]); let chooser_struct = SSetgidSet { + ]); + let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::None, add: vec![], @@ -2453,7 +2439,7 @@ mod tests { Some(SGroupschooser::StructChooser(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2464,11 +2450,11 @@ mod tests { // Exécution du match let filter_matcher = FilterMatcher::builder() - .group(SGroups::Multiple(vec![ - SGroupType::from(get_non_root_gid(0).unwrap()), - SGroupType::from(get_non_root_gid(1).unwrap()), - ])) - .build(); + .group(SGroups::Multiple(vec![ + SGroupType::from(get_non_root_gid(0).unwrap()), + SGroupType::from(get_non_root_gid(1).unwrap()), + ])) + .build(); // Exécution du match let result = config.matches(&cred, &Some(filter_matcher), &command); @@ -2483,7 +2469,6 @@ mod tests { println!("Test successful: The specified user correctly matches the fallback."); } - #[test] fn test_setgid_fallback_nonarg_valid() { // Configuration de test @@ -2510,9 +2495,8 @@ mod tests { task.as_ref().borrow_mut().cred.setgid = Some(SGroupschooser::StructChooser(chooser_struct)); - let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2560,7 +2544,7 @@ mod tests { Some(SGroupschooser::StructChooser(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2571,8 +2555,8 @@ mod tests { // Exécution du match let filter_matcher = FilterMatcher::builder() - .group(SGroups::from("root")) - .build(); + .group(SGroups::from("root")) + .build(); let result = config.matches(&cred, &Some(filter_matcher), &command); @@ -2581,10 +2565,7 @@ mod tests { let result = result.unwrap(); // Vérification que le groupe assigné est bien celui de l'ajout - assert_eq!( - result.settings.setgroups, - Some(SGroups::from("root")) - ); + assert_eq!(result.settings.setgroups, Some(SGroups::from("root"))); println!("Test réussi : Le groupe spécifié correspond bien à l'ajout."); } @@ -2608,19 +2589,17 @@ mod tests { let chooser_struct = SSetgidSet { fallback: fallback_group.clone(), default: SetBehavior::None, - add: vec![ - SGroups::Multiple(vec![ - SGroupType::from(0), - SGroupType::from(get_non_root_gid(1).unwrap()), - ]), - ], + add: vec![SGroups::Multiple(vec![ + SGroupType::from(0), + SGroupType::from(get_non_root_gid(1).unwrap()), + ])], sub: vec![], }; task.as_ref().borrow_mut().cred.setgid = Some(SGroupschooser::StructChooser(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2631,11 +2610,11 @@ mod tests { // Exécution du match let filter_matcher = FilterMatcher::builder() - .group(SGroups::Multiple(vec![ - SGroupType::from(0), - SGroupType::from(get_non_root_gid(1).unwrap()), - ])) - .build(); + .group(SGroups::Multiple(vec![ + SGroupType::from(0), + SGroupType::from(get_non_root_gid(1).unwrap()), + ])) + .build(); let result = config.matches(&cred, &Some(filter_matcher), &command); @@ -2655,7 +2634,7 @@ mod tests { } #[test] - fn test_setgid_add_sub_invalid(){ + fn test_setgid_add_sub_invalid() { // Configuration de test let config = setup_test_config(1); // Un seul rôle pour simplifier let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); @@ -2681,7 +2660,7 @@ mod tests { Some(SGroupschooser::StructChooser(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2692,24 +2671,23 @@ mod tests { // Exécution du match let filter_matcher = FilterMatcher::builder() - .group(SGroups::from("root")) - .build(); + .group(SGroups::from("root")) + .build(); let result = config.matches(&cred, &Some(filter_matcher), &command); - // Vérification que le match est réussi - assert!(result.is_err()); - let result = result.unwrap_err(); - - // Vérification que l'erreur est bien de type `NoMatch` - assert!(result.is_no_match()); - - println!("Test réussi : Le groupe spécifié ne correspond pas "); - + // Vérification que le match est réussi + assert!(result.is_err()); + let result = result.unwrap_err(); + + // Vérification que l'erreur est bien de type `NoMatch` + assert!(result.is_no_match()); + + println!("Test réussi : Le groupe spécifié ne correspond pas "); } #[test] - fn test_setgid_all_sub_single_invalid(){ + fn test_setgid_all_sub_single_invalid() { // Configuration de test let config = setup_test_config(1); // Un seul rôle pour simplifier let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); @@ -2735,7 +2713,7 @@ mod tests { Some(SGroupschooser::StructChooser(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2746,24 +2724,23 @@ mod tests { // Exécution du match let filter_matcher = FilterMatcher::builder() - .group(SGroups::from("root")) - .build(); + .group(SGroups::from("root")) + .build(); let result = config.matches(&cred, &Some(filter_matcher), &command); - // Vérification que le match est réussi - assert!(result.is_err()); - let result = result.unwrap_err(); - - // Vérification que l'erreur est bien de type `NoMatch` - assert!(result.is_no_match()); - - println!("Test réussi : Le groupe spécifié ne correspond pas "); - + // Vérification que le match est réussi + assert!(result.is_err()); + let result = result.unwrap_err(); + + // Vérification que l'erreur est bien de type `NoMatch` + assert!(result.is_no_match()); + + println!("Test réussi : Le groupe spécifié ne correspond pas "); } #[test] - fn test_setgid_all_sub_multiple_invalid(){ + fn test_setgid_all_sub_multiple_invalid() { // Configuration de test let config = setup_test_config(1); // Un seul rôle pour simplifier let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); @@ -2792,7 +2769,7 @@ mod tests { Some(SGroupschooser::StructChooser(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2803,27 +2780,26 @@ mod tests { // Exécution du match let filter_matcher = FilterMatcher::builder() - .group(SGroups::Multiple(vec![ - SGroupType::from("root"), - SGroupType::from(get_non_root_gid(1).unwrap()), - ])) - .build(); + .group(SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from(get_non_root_gid(1).unwrap()), + ])) + .build(); let result = config.matches(&cred, &Some(filter_matcher), &command); - // Vérification que le match est réussi - assert!(result.is_err()); - let result = result.unwrap_err(); - - // Vérification que l'erreur est bien de type `NoMatch` - assert!(result.is_no_match()); - - println!("Test réussi : Le groupe spécifié ne correspond pas "); - + // Vérification que le match est réussi + assert!(result.is_err()); + let result = result.unwrap_err(); + + // Vérification que l'erreur est bien de type `NoMatch` + assert!(result.is_no_match()); + + println!("Test réussi : Le groupe spécifié ne correspond pas "); } #[test] - fn test_setgid_all_valid(){ + fn test_setgid_all_valid() { // Configuration de test let config = setup_test_config(1); // Un seul rôle pour simplifier let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); @@ -2849,7 +2825,7 @@ mod tests { Some(SGroupschooser::StructChooser(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2860,24 +2836,23 @@ mod tests { // Exécution du match let filter_matcher = FilterMatcher::builder() - .group(SGroups::from("root")) - .build(); + .group(SGroups::from("root")) + .build(); let result = config.matches(&cred, &Some(filter_matcher), &command); - // Vérification que le match est réussi - assert!(result.is_ok()); - let result = result.unwrap(); - - // Vérification que le groupe assigné est autorisé - assert_eq!(result.settings.setgroups, Some(SGroups::from("root"))); - - println!("Test réussi : Le groupe spécifié correspond bien à l'ajout."); + // Vérification que le match est réussi + assert!(result.is_ok()); + let result = result.unwrap(); + + // Vérification que le groupe assigné est autorisé + assert_eq!(result.settings.setgroups, Some(SGroups::from("root"))); + println!("Test réussi : Le groupe spécifié correspond bien à l'ajout."); } #[test] - fn test_setgid_none_invalid(){ + fn test_setgid_none_invalid() { // Configuration de test let config = setup_test_config(1); // Un seul rôle pour simplifier let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); @@ -2903,7 +2878,7 @@ mod tests { Some(SGroupschooser::StructChooser(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2914,24 +2889,23 @@ mod tests { // Exécution du match let filter_matcher = FilterMatcher::builder() - .group(SGroups::from("root")) - .build(); + .group(SGroups::from("root")) + .build(); let result = config.matches(&cred, &Some(filter_matcher), &command); - // Vérification que le match est réussi - assert!(result.is_err()); - let result = result.unwrap_err(); - - // Vérification que l'erreur est bien de type `NoMatch` - assert!(result.is_no_match()); - - println!("Test réussi : Le groupe spécifié ne correspond pas "); - + // Vérification que le match est réussi + assert!(result.is_err()); + let result = result.unwrap_err(); + + // Vérification que l'erreur est bien de type `NoMatch` + assert!(result.is_no_match()); + + println!("Test réussi : Le groupe spécifié ne correspond pas "); } #[test] - fn test_setgid_all_add_single_valid(){ + fn test_setgid_all_add_single_valid() { // Configuration de test let config = setup_test_config(1); // Un seul rôle pour simplifier let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); @@ -2957,7 +2931,7 @@ mod tests { Some(SGroupschooser::StructChooser(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -2968,24 +2942,23 @@ mod tests { // Exécution du match let filter_matcher = FilterMatcher::builder() - .group(SGroups::from("root")) - .build(); + .group(SGroups::from("root")) + .build(); let result = config.matches(&cred, &Some(filter_matcher), &command); - // Vérification que le match est réussi - assert!(result.is_ok()); - let result = result.unwrap(); - - // Vérification que le groupe assigné est bien celui de l'ajout - assert_eq!(result.settings.setgroups, Some(SGroups::from("root"))); - - println!("Test réussi : Le groupe spécifié correspond bien à l'ajout."); + // Vérification que le match est réussi + assert!(result.is_ok()); + let result = result.unwrap(); + // Vérification que le groupe assigné est bien celui de l'ajout + assert_eq!(result.settings.setgroups, Some(SGroups::from("root"))); + + println!("Test réussi : Le groupe spécifié correspond bien à l'ajout."); } #[test] - fn test_setgid_all_add_multiple_valid(){ + fn test_setgid_all_add_multiple_valid() { // Configuration de test let config = setup_test_config(1); // Un seul rôle pour simplifier let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); @@ -3014,7 +2987,7 @@ mod tests { Some(SGroupschooser::StructChooser(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -3025,25 +2998,28 @@ mod tests { // Exécution du match let filter_matcher = FilterMatcher::builder() - .group(SGroups::Multiple(vec![ - SGroupType::from("root"), - SGroupType::from(get_non_root_gid(1).unwrap()), - ])) - .build(); + .group(SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from(get_non_root_gid(1).unwrap()), + ])) + .build(); let result = config.matches(&cred, &Some(filter_matcher), &command); - // Vérification que le match est réussi - assert!(result.is_ok()); - let result = result.unwrap(); - - // Vérification que le groupe assigné est bien celui de l'ajout - assert_eq!(result.settings.setgroups, Some(SGroups::Multiple(vec![ - SGroupType::from("root"), - SGroupType::from(get_non_root_gid(1).unwrap()), - ]))); - - println!("Test réussi : Le groupe spécifié correspond bien à l'ajout."); + // Vérification que le match est réussi + assert!(result.is_ok()); + let result = result.unwrap(); + + // Vérification que le groupe assigné est bien celui de l'ajout + assert_eq!( + result.settings.setgroups, + Some(SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from(get_non_root_gid(1).unwrap()), + ])) + ); + + println!("Test réussi : Le groupe spécifié correspond bien à l'ajout."); } #[test] @@ -3073,7 +3049,7 @@ mod tests { Some(SGroupschooser::StructChooser(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -3100,7 +3076,178 @@ mod tests { } #[test] - fn test_setgid_none_add_multiple_invvalid(){ + fn test_setgid_none_add_multiple_invvalid() { + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::None, + add: vec![SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from(get_non_root_gid(1).unwrap()), + ])], + sub: vec![], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(SGroups::Multiple(vec![ + SGroupType::from(get_non_root_gid(0).unwrap()), + SGroupType::from("root"), + ])) + .build(); + + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_err()); + let result = result.unwrap_err(); + + // Vérification que l'erreur est bien de type `NoMatch` + assert!(result.is_no_match()); + + println!("Test réussi : Le groupe spécifié ne correspond pas "); + } + + #[test] + fn test_setgid_add_multiple_invalid() { + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::None, + add: vec![SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from(get_non_root_gid(1).unwrap()), + ])], + sub: vec![], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(SGroups::from("root")) + .build(); + + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_err()); + let result = result.unwrap_err(); + + // Vérification que l'erreur est bien de type `NoMatch` + assert!(result.is_no_match()); + + println!("Test réussi : Le groupe spécifié ne correspond pas "); + } + + #[test] + fn test_setgid_add_single_invalid() { + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::None, + add: vec![SGroups::from("root")], + sub: vec![], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from(get_non_root_gid(0).unwrap()), + ])) + .build(); + + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi + assert!(result.is_err()); + let result = result.unwrap_err(); + + // Vérification que l'erreur est bien de type `NoMatch` + assert!(result.is_no_match()); + + println!("Test réussi : Le groupe spécifié ne correspond pas "); + } + + #[test] + fn test_setgid_add_diff_invalid() { // Configuration de test let config = setup_test_config(1); // Un seul rôle pour simplifier let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); @@ -3129,7 +3276,7 @@ mod tests { Some(SGroupschooser::StructChooser(chooser_struct)); let cred = Cred { - user: User::from_name("root").unwrap().unwrap(), + user: User::from_name("root").unwrap().unwrap(), groups: vec![Group::from_name("root").unwrap().unwrap()], ppid: Pid::from_raw(0), tty: None, @@ -3140,15 +3287,74 @@ mod tests { // Exécution du match let filter_matcher = FilterMatcher::builder() - .group(SGroups::Multiple(vec![ - SGroupType::from(get_non_root_gid(0).unwrap()), - SGroupType::from("root"), - ])) - .build(); + .group(SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from(get_non_root_gid(0).unwrap()), + ])) + .build(); let result = config.matches(&cred, &Some(filter_matcher), &command); - // Vérification que le match est réussi + // Vérification que le match est réussi + assert!(result.is_err()); + let result = result.unwrap_err(); + + // Vérification que l'erreur est bien de type `NoMatch` + assert!(result.is_no_match()); + + println!("Test réussi : Le groupe spécifié ne correspond pas "); + } + + #[test] + fn test_setgid_add_list_or_invalid() { + // Configuration de test + let config = setup_test_config(1); // Un seul rôle pour simplifier + let role = setup_test_role(1, Some(config.as_ref().borrow().roles[0].clone()), None); + let task = role.as_ref().borrow().tasks[0].clone(); + + // Ajout d'un acteur autorisé + role.as_ref() + .borrow_mut() + .actors + .push(SActor::user("root").build()); + + task.as_ref().borrow_mut().commands.default_behavior = Some(SetBehavior::All); + + // Définition du `setgid` avec un `fallback` + let fallback_group = SGroups::from(get_non_root_gid(0).unwrap()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::None, + add: vec![ + SGroups::from("root"), + SGroups::from(get_non_root_gid(1).unwrap()), + ], + sub: vec![], + }; + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::StructChooser(chooser_struct)); + + let cred = Cred { + user: User::from_name("root").unwrap().unwrap(), + groups: vec![Group::from_name("root").unwrap().unwrap()], + ppid: Pid::from_raw(0), + tty: None, + }; + + // Commande de test + let command = vec!["/bin/ls".to_string(), "-l".to_string(), "-a".to_string()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .group(SGroups::Multiple(vec![ + SGroupType::from("root"), + SGroupType::from(get_non_root_gid(1).unwrap()), + ])) + .build(); + + let result = config.matches(&cred, &Some(filter_matcher), &command); + + // Vérification que le match est réussi assert!(result.is_err()); let result = result.unwrap_err(); @@ -3156,6 +3362,5 @@ mod tests { assert!(result.is_no_match()); println!("Test réussi : Le groupe spécifié ne correspond pas "); - } } diff --git a/rar-common/src/database/mod.rs b/rar-common/src/database/mod.rs index 58c81856..80f41673 100644 --- a/rar-common/src/database/mod.rs +++ b/rar-common/src/database/mod.rs @@ -5,7 +5,7 @@ use crate::save_settings; use crate::util::{toggle_lock_config, ImmutableLock}; use crate::version::PACKAGE_VERSION; -use actor::{ SGroups, SUserType}; +use actor::{SGroups, SUserType}; use bon::{builder, Builder}; use chrono::Duration; use linked_hash_set::LinkedHashSet; @@ -36,7 +36,7 @@ pub struct FilterMatcher { pub env_behavior: Option, #[builder(into)] pub user: Option, - pub group:Option, + pub group: Option, } pub fn make_weak_config(config: &Rc>) { diff --git a/rar-common/src/database/options.rs b/rar-common/src/database/options.rs index 9d10deff..6f559f93 100644 --- a/rar-common/src/database/options.rs +++ b/rar-common/src/database/options.rs @@ -2008,7 +2008,7 @@ mod tests { #[test] fn test_safe_path() { let path = std::env::var("PATH").unwrap(); - + let config = SConfig::builder() .role( SRole::builder("test") diff --git a/rar-common/src/database/structs.rs b/rar-common/src/database/structs.rs index ad4d0ffc..34a3a637 100644 --- a/rar-common/src/database/structs.rs +++ b/rar-common/src/database/structs.rs @@ -204,8 +204,8 @@ pub enum SetBehavior { #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(untagged)] pub enum SGroupschooser { - Group (SGroups), - StructChooser (SSetgidSet), + Group(SGroups), + StructChooser(SSetgidSet), } impl From for SGroupschooser { @@ -247,9 +247,6 @@ pub struct SSetgidSet { pub sub: Vec, } - - - #[derive(PartialEq, Eq, Debug, Builder)] pub struct SCapabilities { #[builder(start_fn)] @@ -904,7 +901,10 @@ mod tests { assert!( matches!(cred.setuid.as_ref().unwrap(), SUserChooser::ChooserStruct(set) if set == &setuidstruct) ); - assert_eq!(*cred.setgid.as_ref().unwrap(), SGroupschooser::Group(SGroups::from("setgid1"))); + assert_eq!( + *cred.setgid.as_ref().unwrap(), + SGroupschooser::Group(SGroups::from("setgid1")) + ); let capabilities = cred.capabilities.as_ref().unwrap(); assert_eq!(capabilities.default_behavior, SetBehavior::All); @@ -1159,7 +1159,10 @@ mod tests { cred.setuid.as_ref().unwrap(), &SUserChooser::from(SUserType::from("setuid1")) ); - assert_eq!(*cred.setgid.as_ref().unwrap(), SGroupschooser::Group(SGroups::from("setgid1"))); + assert_eq!( + *cred.setgid.as_ref().unwrap(), + SGroupschooser::Group(SGroups::from("setgid1")) + ); let capabilities = cred.capabilities.as_ref().unwrap(); assert_eq!(capabilities.default_behavior, SetBehavior::None); diff --git a/src/chsr/cli/mod.rs b/src/chsr/cli/mod.rs index a9429f55..f83cd04b 100644 --- a/src/chsr/cli/mod.rs +++ b/src/chsr/cli/mod.rs @@ -205,7 +205,9 @@ mod tests { .cred( SCredentials::builder() .setuid("user1") - .setgid(SGroupschooser::Group(SGroups::from(["setgid1","setgid2"]))) + .setgid(SGroupschooser::Group(SGroups::from([ + "setgid1", "setgid2", + ]))) .capabilities( SCapabilities::builder(SetBehavior::All) .add_cap(Cap::LINUX_IMMUTABLE) diff --git a/src/chsr/cli/process/json.rs b/src/chsr/cli/process/json.rs index e0d42ac4..f7443870 100644 --- a/src/chsr/cli/process/json.rs +++ b/src/chsr/cli/process/json.rs @@ -10,7 +10,9 @@ use rar_common::database::{ EnvBehavior, EnvKey, Opt, OptStack, OptType, PathBehavior, SEnvOptions, SPathOptions, STimeout, }, - structs::{IdTask, RoleGetter, SCapabilities, SCommand, SRole, STask, SUserChooser,SGroupschooser}, + structs::{ + IdTask, RoleGetter, SCapabilities, SCommand, SGroupschooser, SRole, STask, SUserChooser, + }, }; use super::perform_on_target_opt; @@ -323,7 +325,8 @@ pub fn cred_set( task.as_ref().borrow_mut().cred.setuid = Some(SUserChooser::Actor(setuid)); } if let Some(setgid) = cred_setgid { - task.as_ref().borrow_mut().cred.setgid = Some(SGroupschooser::Group(setgid.clone())); + task.as_ref().borrow_mut().cred.setgid = + Some(SGroupschooser::Group(setgid.clone())); } Ok(true) } diff --git a/src/sr/main.rs b/src/sr/main.rs index 4c85e947..d7271d0f 100644 --- a/src/sr/main.rs +++ b/src/sr/main.rs @@ -9,7 +9,7 @@ use nix::{ unistd::{getgroups, getuid, isatty, Group, User}, }; use rar_common::database::{ - actor::{SGroups, SUserType}, + actor::{SGroupType, SGroups, SUserType}, finder::{Cred, TaskMatch, TaskMatcher}, options::EnvBehavior, FilterMatcher, @@ -155,7 +155,17 @@ where user = iter.next().map(|s| escape_parser_string(s).as_str().into()); } "-g" | "--group" => { - group = iter.next().map(|s| escape_parser_string(s).as_str().into()); + group = iter + .next() + .map(|s| { + SGroups::Multiple( + s.as_ref() + .split(',') + .map(|g| g.into()) + .collect::>(), + ) + }) + .into(); } "-S" | "--stdin" => { args.stdin = true; From 7a69f1c0a811b150361cd1eebc3f2ab0553dd7f2 Mon Sep 17 00:00:00 2001 From: LeChatP Date: Mon, 24 Mar 2025 14:09:02 +0100 Subject: [PATCH 6/8] fix: integer conversion. --- rar-common/src/database/options.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rar-common/src/database/options.rs b/rar-common/src/database/options.rs index 6f559f93..9c50595e 100644 --- a/rar-common/src/database/options.rs +++ b/rar-common/src/database/options.rs @@ -557,7 +557,7 @@ fn tz_is_safe(tzval: &str) -> bool { } // Reject extra long TZ values (even if not a path). - if tzval.len() >= PATH_MAX.try_into().unwrap() { + if tzval.len() >= >::try_into(PATH_MAX).unwrap() { return false; } From da5797ab6670489399bbb84f5c7f4451524c5ffa Mon Sep 17 00:00:00 2001 From: LeChatP Date: Mon, 24 Mar 2025 14:22:10 +0100 Subject: [PATCH 7/8] test: testing struct from --- rar-common/src/database/finder.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/rar-common/src/database/finder.rs b/rar-common/src/database/finder.rs index a8a31407..7bc3c39f 100644 --- a/rar-common/src/database/finder.rs +++ b/rar-common/src/database/finder.rs @@ -3363,4 +3363,29 @@ mod tests { println!("Test réussi : Le groupe spécifié ne correspond pas "); } + + #[test] + fn test_sgroupschooser_from() { + let sgroup = SGroups::from(get_non_root_gid(0).unwrap()); + let sgroupschooser = SGroupschooser::from(sgroup.clone()); + assert_eq!(sgroupschooser, SGroupschooser::Group(sgroup.clone())); + let chooser_struct = SSetgidSet { + fallback: sgroup.clone(), + default: SetBehavior::None, + add: vec![ + SGroups::from("root"), + SGroups::from(get_non_root_gid(1).unwrap()), + ], + sub: vec![], + }; + let sgroupschooser = SGroupschooser::from(chooser_struct.clone()); + assert_eq!(sgroupschooser, SGroupschooser::StructChooser(chooser_struct)); + let group = "grp"; + let sgroupschooser = SGroupschooser::from(group); + assert_eq!(sgroupschooser, SGroupschooser::Group(group.into())); + let group = 0; + let sgroupschooser = SGroupschooser::from(group); + assert_eq!(sgroupschooser, SGroupschooser::Group(group.into())); + + } } From 41c3c8576b3a616245236c49f9684408029450cc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 13:23:31 +0000 Subject: [PATCH 8/8] Format Rust code using rustfmt --- rar-common/src/database/finder.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rar-common/src/database/finder.rs b/rar-common/src/database/finder.rs index 7bc3c39f..ba0717e2 100644 --- a/rar-common/src/database/finder.rs +++ b/rar-common/src/database/finder.rs @@ -3379,13 +3379,15 @@ mod tests { sub: vec![], }; let sgroupschooser = SGroupschooser::from(chooser_struct.clone()); - assert_eq!(sgroupschooser, SGroupschooser::StructChooser(chooser_struct)); + assert_eq!( + sgroupschooser, + SGroupschooser::StructChooser(chooser_struct) + ); let group = "grp"; let sgroupschooser = SGroupschooser::from(group); assert_eq!(sgroupschooser, SGroupschooser::Group(group.into())); let group = 0; let sgroupschooser = SGroupschooser::from(group); assert_eq!(sgroupschooser, SGroupschooser::Group(group.into())); - } }