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.
This commit is contained in:
bors 2021-01-22 19:00:11 +00:00
commit 22ddcd1a13
4 changed files with 67 additions and 7 deletions

View File

@ -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,

View File

@ -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

View File

@ -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);
}

View File

@ -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());
}