diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index d95bc9b15c9c4..d5132375a257a 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -21,6 +21,13 @@ pub trait CommandExt: Sealed { /// Sets the child process's user ID. This translates to a /// `setuid` call in the child process. Failure in the `setuid` /// call will cause the spawn to fail. + /// + /// # Notes + /// + /// This will also trigger a call to `setgroups(0, NULL)` in the child + /// process if no groups have been specified. + /// This removes supplementary groups that might have given the child + /// unwanted permissions. #[stable(feature = "rust1", since = "1.0.0")] fn uid( &mut self, diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 3d305cd7310fd..625d0526a59b6 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -304,14 +304,22 @@ impl Command { if let Some(u) = self.get_uid() { // When dropping privileges from root, the `setgroups` call // will remove any extraneous groups. We only drop groups - // if the current uid is 0 and we weren't given an explicit + // if we have CAP_SETGID 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"))] - if libc::getuid() == 0 && self.get_groups().is_none() { - cvt(libc::setgroups(0, ptr::null()))?; + if self.get_groups().is_none() { + let res = cvt(libc::setgroups(0, ptr::null())); + if let Err(e) = res { + // Here we ignore the case of not having CAP_SETGID. + // An alternative would be to require CAP_SETGID (in + // addition to CAP_SETUID) for setting the UID. + if e.raw_os_error() != Some(libc::EPERM) { + return Err(e.into()); + } + } } cvt(libc::setuid(u as uid_t))?; }