From 41e6b23000b41ad52ea3c0f0e9ad4791d24281a4 Mon Sep 17 00:00:00 2001 From: slo1 <26316200+slo1@users.noreply.github.com> Date: Mon, 15 Jun 2020 21:39:34 -0700 Subject: [PATCH] Add setgroups to std::os::unix::process::CommandExt --- library/std/src/sys/unix/ext/process.rs | 18 ++++++++++++++++ .../src/sys/unix/process/process_common.rs | 9 ++++++++ .../std/src/sys/unix/process/process_unix.rs | 21 ++++++++++++------- src/test/ui/command/command-setgroups.rs | 19 +++++++++++++++++ 4 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 src/test/ui/command/command-setgroups.rs diff --git a/library/std/src/sys/unix/ext/process.rs b/library/std/src/sys/unix/ext/process.rs index f4c67b225e6..724b5dcca6a 100644 --- a/library/std/src/sys/unix/ext/process.rs +++ b/library/std/src/sys/unix/ext/process.rs @@ -39,6 +39,15 @@ pub trait CommandExt { #[cfg(target_os = "vxworks")] id: u16, ) -> &mut process::Command; + /// 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; + /// Schedules a closure to be run just before the `exec` function is /// invoked. /// @@ -149,6 +158,15 @@ impl CommandExt for process::Command { self } + 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 + } + unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command where F: FnMut() -> io::Result<()> + Send + Sync + 'static, diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index 372e5e6a5b3..a96d4aa6a45 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -87,6 +87,7 @@ pub struct Command { gid: Option, saw_nul: bool, closures: Vec io::Result<()> + Send + Sync>>, + groups: Option>, stdin: Option, stdout: Option, stderr: Option, @@ -148,6 +149,7 @@ impl Command { gid: None, saw_nul, closures: Vec::new(), + groups: None, stdin: None, stdout: None, stderr: None, @@ -183,6 +185,9 @@ impl Command { pub fn gid(&mut self, id: gid_t) { self.gid = Some(id); } + pub fn groups(&mut self, groups: &[gid_t]) { + self.groups = Some(Box::from(groups)); + } pub fn saw_nul(&self) -> bool { self.saw_nul @@ -226,6 +231,10 @@ impl Command { pub fn get_gid(&self) -> Option { self.gid } + #[allow(dead_code)] + pub fn get_groups(&self) -> Option<&[gid_t]> { + self.groups.as_deref() + } pub fn get_closures(&mut self) -> &mut Vec io::Result<()> + Send + Sync>> { &mut self.closures diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index ddcb404c60e..2746f87468d 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -183,20 +183,26 @@ impl Command { #[cfg(not(target_os = "l4re"))] { + 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()))?; + } 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 - // 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. + // 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. //FIXME: Redox kernel does not support setgroups yet #[cfg(not(target_os = "redox"))] - let _ = libc::setgroups(0, ptr::null()); + if libc::getuid() == 0 && self.get_groups().is_none() { + cvt(libc::setgroups(0, ptr::null()))?; + } cvt(libc::setuid(u as uid_t))?; } } @@ -287,6 +293,7 @@ impl Command { || self.get_uid().is_some() || (self.env_saw_path() && !self.program_is_path()) || !self.get_closures().is_empty() + || self.get_groups().is_some() { return Ok(None); } diff --git a/src/test/ui/command/command-setgroups.rs b/src/test/ui/command/command-setgroups.rs new file mode 100644 index 00000000000..978c9c44486 --- /dev/null +++ b/src/test/ui/command/command-setgroups.rs @@ -0,0 +1,19 @@ +// run-pass +// 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() { + let max_ngroups = unsafe { libc::sysconf(libc::_SC_NGROUPS_MAX) }; + let max_ngroups = max_ngroups as u32 + 1; + let vec: Vec = (0..max_ngroups).collect(); + let p = Command::new("/bin/id").groups(&vec[..]).spawn(); + assert!(p.is_err()); +}