None of your code checks for errors! seteuid
and getgroups
can fail but your code ignores that possibility. You are attempting to debug a failure but haven't even spent the time to check that the functions you are calling are succeeding.
Be very careful about passing in v.as_mut_ptr()
. An empty Vec
does not have a NULL pointer. In this case it's fine because the man page says it only cares about the count.
You don't handle the case where the number of groups changes in size between the first and second call.
You don't need to pass in references to the println
arguments.
With that out of the way, since you are only storing u32
in the Vec
, you can avoid filling it with dummy values by adjusting the size and capacity accordingly:
extern crate libc;
#[derive(Debug)]
struct Info {
guid: u32,
ruid: u32,
groups: Vec<u32>,
num_groups: usize,
}
fn example(guid: u32) -> Info {
unsafe {
let ruid = libc::getuid();
if -1 == libc::seteuid(guid) {
panic!("seteuid")
}
let mut groups = Vec::new();
let mut attempts = 0;
loop {
let num_groups = libc::getgroups(groups.capacity() as i32, groups.as_mut_ptr());
if -1 == num_groups {
panic!("getgroups")
}
let num_groups = num_groups as usize;
if num_groups <= groups.capacity() {
groups.set_len(num_groups);
return Info {
guid,
ruid,
groups,
num_groups,
};
}
groups.reserve_exact(num_groups);
attempts += 1;
if attempts >= 3 {
panic!("Unstable amount of groups")
}
}
}
}
fn main() {
println!("{:?}", example(unsafe { libc::getuid() }));
}
However, I wouldn't rewrite all this and I'd rely on the existing work. The nix crate provides nice wrappers:
extern crate nix;
use nix::unistd::{self, Uid};
use std::u32;
fn example(guid: Uid) -> nix::Result<()> {
let ruid = unistd::getuid();
let no_change = Uid::from_raw(u32::MAX);
unistd::setresuid(no_change, guid, no_change)?;
let groups = nix::unistd::getgroups()?;
println!(
"real user id {} as user id {}, as user groups {:?}",
ruid, guid, groups
);
Ok(())
}
fn main() {
println!("{:?}", example(Uid::current()));
}