Change:
already offers and methods to control the user identity of the child process (calling respectively). For complete control of Unix credentials, there should also be a way to call and the concept of the supplementary group list are; I would expect this not to be a portability concern.) This feature would eliminate a use of and an unsafe block (to call ) from the program I'm currently writing, which is a port to Rust of a setuid C program. For illustration, this function exists in the C version: <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --This issue has been assigned to via . <!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOT_END --
I would be prepared to consider a PR to add .
claim

Discussion:
#[cfg(target_os = "vxworks")] id: u16, ) -> &mut process::Command; <ins> /// Sets the supplementary group IDs for the calling process. Translates to /// a `setgroups` call in the child process. #[unstable(feature = "setgroups", issue = "38527", reason = "")] fn groups( &mut self, #[cfg(not(target_os = "vxworks"))] groups: &[u32], #[cfg(target_os = "vxworks")] groups: &[u16], ) -> &mut process::Command; </ins> /// Schedules a closure to be run just before the `exec` function is /// invoked. ///

self } <ins> fn groups( &mut self, #[cfg(not(target_os = "vxworks"))] groups: &[u32], #[cfg(target_os = "vxworks")] groups: &[u16], ) -> &mut process::Command { self.as_inner_mut().groups(groups); self } </ins> unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command where F: FnMut() -> io::Result<()> + Send + Sync + 'static,

gid: Option<gid_t>, saw_nul: bool, closures: Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>>, <ins> groups: Option<Box<[gid_t]>>, </ins> stdin: Option<Stdio>, stdout: Option<Stdio>, stderr: Option<Stdio>,

gid: None, saw_nul, closures: Vec::new(), <ins> groups: None, </ins> stdin: None, stdout: None, stderr: None,

pub fn gid(&mut self, id: gid_t) { self.gid = Some(id); } <ins> pub fn groups(&mut self, groups: &[gid_t]) { self.groups = Some(Box::from(groups)); } </ins> pub fn saw_nul(&self) -> bool { self.saw_nul

pub fn get_gid(&self) -> Option<gid_t> { self.gid } <ins> #[allow(dead_code)] pub fn get_groups(&self) -> Option<&[gid_t]> { self.groups.as_deref() } </ins> pub fn get_closures(&mut self) -> &mut Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>> { &mut self.closures

#[cfg(not(target_os = "l4re"))] { <ins> if let Some(_g) = self.get_groups() { //FIXME: Redox kernel does not support setgroups yet #[cfg(not(target_os = "redox"))] cvt(libc::setgroups(_g.len().try_into().unwrap(), _g.as_ptr()))?; } </ins> if let Some(u) = self.get_gid() { cvt(libc::setgid(u as gid_t))?; } if let Some(u) = self.get_uid() { // When dropping privileges from root, the `setgroups` call <del> // will remove any extraneous groups. If we don't call this, // then even though our uid has dropped, we may still have // groups that enable us to do super-user things. This will // fail if we aren't root, so don't bother checking the // return value, this is just done as an optimistic // privilege dropping function. </del> <ins> // will remove any extraneous groups. We only drop groups // if the current uid is 0 and we weren't given an explicit // set of groups. If we don't call this, then even though our // uid has dropped, we may still have groups that enable us to // do super-user things. </ins> //FIXME: Redox kernel does not support setgroups yet #[cfg(not(target_os = "redox"))] <del> let _ = libc::setgroups(0, ptr::null()); </del> <ins> if libc::getuid() == 0 && self.get_groups().is_none() { cvt(libc::setgroups(0, ptr::null()))?; } </ins> cvt(libc::setuid(u as uid_t))?; } }

|| self.get_uid().is_some() || (self.env_saw_path() && !self.program_is_path()) || !self.get_closures().is_empty() <ins> || self.get_groups().is_some() </ins> { return Ok(None); }

<ins> // run-pass // ignore-windows - this is a unix-specific test // ignore-cloudabi // ignore-emscripten // ignore-sgx #![feature(rustc_private)] #![feature(setgroups)] extern crate libc; use std::process::Command; use std::os::unix::process::CommandExt; fn main() { #[cfg(unix)] run() } #[cfg(unix)] fn run() { let max_ngroups = unsafe { libc::sysconf(libc::_SC_NGROUPS_MAX) }; let max_ngroups = max_ngroups as u32 + 1; let vec: Vec<u32> = (0..max_ngroups).collect(); let p = Command::new("/bin/id").groups(&vec[..]).spawn(); assert!(p.is_err()); } </ins>