diff --git a/rar-common/src/database/actor.rs b/rar-common/src/database/actor.rs index d4f194c6..b8007b55 100644 --- a/rar-common/src/database/actor.rs +++ b/rar-common/src/database/actor.rs @@ -67,6 +67,14 @@ 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 +118,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 { @@ -481,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 9c197f75..ba0717e2 100644 --- a/rar-common/src/database/finder.rs +++ b/rar-common/src/database/finder.rs @@ -23,7 +23,9 @@ 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::{ @@ -774,14 +776,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 @@ -1161,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; @@ -1170,7 +1215,7 @@ mod tests { database::{ make_weak_config, options::{EnvBehavior, PathBehavior, SAuthentication, SBounding, SPrivileged}, - structs::{IdTask, RoleGetter, SCredentials, SSetuidSet}, + structs::{IdTask, RoleGetter, SCredentials, SSetgidSet, SSetuidSet}, versionning::Versioning, }, rc_refcell, @@ -1192,6 +1237,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"); @@ -1666,7 +1725,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 @@ -1713,9 +1771,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, @@ -1763,9 +1820,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, @@ -1814,9 +1870,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, @@ -1863,9 +1918,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, @@ -1888,7 +1942,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 @@ -1913,9 +1967,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, @@ -1932,7 +1985,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."); @@ -1961,9 +2014,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, @@ -2012,9 +2064,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, @@ -2060,9 +2111,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, @@ -2304,4 +2354,1040 @@ 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_gid(0).unwrap()); + 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_gid(0).unwrap()), + SGroupType::from(get_non_root_gid(1).unwrap()), + ]); + 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_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); + + // 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_gid(0).unwrap()); + 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_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::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_gid(0).unwrap()); + 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()), + ])], + 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(0), + 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(0), + SGroupType::from(get_non_root_gid(1).unwrap()), + ])) + ); + 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_gid(0).unwrap()); + 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_gid(0).unwrap()); + 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_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(get_non_root_gid(1).unwrap()), + ])], + }; + 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(); + + // 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_gid(0).unwrap()); + 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_gid(0).unwrap()); + 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_gid(0).unwrap()); + 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_gid(0).unwrap()); + let chooser_struct = SSetgidSet { + fallback: fallback_group.clone(), + default: SetBehavior::All, + 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("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."); + } + + #[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_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()]; + + // Exécution du match + let filter_matcher = FilterMatcher::builder() + .user(SUserType::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 "); + } + + #[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_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); + 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("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_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(); + + // 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_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())); + } } diff --git a/rar-common/src/database/mod.rs b/rar-common/src/database/mod.rs index 9b993474..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::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/options.rs b/rar-common/src/database/options.rs index 0dce33c4..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; } @@ -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. @@ -2010,7 +2008,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 +2029,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"); diff --git a/rar-common/src/database/structs.rs b/rar-common/src/database/structs.rs index 1456a807..34a3a637 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,51 @@ 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 { @@ -856,7 +901,11 @@ 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)); @@ -1110,7 +1159,11 @@ 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)); @@ -1144,7 +1197,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 dbc0a0cf..f83cd04b 100644 --- a/src/chsr/cli/mod.rs +++ b/src/chsr/cli/mod.rs @@ -45,6 +45,7 @@ mod tests { use rar_common::{ database::{ actor::SActor, + actor::SGroups, options::*, read_json_config, structs::{SCredentials, *}, @@ -204,7 +205,9 @@ 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 0bf9d6d7..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}, + 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(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..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, @@ -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,28 @@ 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| { + SGroups::Multiple( + s.as_ref() + .split(',') + .map(|g| g.into()) + .collect::>(), + ) + }) + .into(); + } "-S" | "--stdin" => { args.stdin = true; } @@ -190,6 +207,7 @@ where .maybe_task(task) .maybe_env_behavior(env) .maybe_user(user) + .maybe_group(group) .build(), ); for arg in iter {