about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-01-22 19:00:11 +0000
committerbors <bors@rust-lang.org>2021-01-22 19:00:11 +0000
commit22ddcd1a13082b7be0fc99b720677efd2b733816 (patch)
tree2f1353a2d02e579821b1e90e37a0cce9efe4c5cd
parentb814b639836aa76b5c6deaa585367150bb2debf4 (diff)
parenta4db851302166616bb5fcbc89f240537a8164d49 (diff)
downloadrust-22ddcd1a13082b7be0fc99b720677efd2b733816.tar.gz
rust-22ddcd1a13082b7be0fc99b720677efd2b733816.zip
Auto merge of #72160 - slo1:libstd-setgroups, r=KodrAus
Add setgroups to std::os::unix::process::CommandExt

Should fix #38527. I'm not sure groups is the greatest name though.
-rw-r--r--library/std/src/sys/unix/ext/process.rs18
-rw-r--r--library/std/src/sys/unix/process/process_common.rs9
-rw-r--r--library/std/src/sys/unix/process/process_unix.rs21
-rw-r--r--src/test/ui/command/command-setgroups.rs26
4 files changed, 67 insertions, 7 deletions
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<F>(&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<gid_t>,
     saw_nul: bool,
     closures: Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>>,
+    groups: Option<Box<[gid_t]>>,
     stdin: Option<Stdio>,
     stdout: Option<Stdio>,
     stderr: Option<Stdio>,
@@ -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<gid_t> {
         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<Box<dyn FnMut() -> 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..28f2bfdd3a7
--- /dev/null
+++ b/src/test/ui/command/command-setgroups.rs
@@ -0,0 +1,26 @@
+// 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());
+}