0
extern crate libc;

fn example(guid: u32) {
    unsafe {
        let ruid = libc::getuid();
        libc::seteuid(guid);
        let mut v = vec![0; 0];
        let num_groups = libc::getgroups(0, v.as_mut_ptr());
        let mut groups = Vec::with_capacity(num_groups as usize);
        libc::getgroups(num_groups, groups.as_mut_ptr());
        println!(
            "real user id {} as user id {}, as user groups {:?}, numgroups {}",
            &ruid, &guid, &groups, &num_groups
        );
    }
}

This prints

real user id 1000 as user id 1000, as user groups [], numgroups 9

My assumptions was that it would show a vector of 9 groups.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
user1685095
  • 5,787
  • 9
  • 51
  • 100

2 Answers2

2

You need to change the size of groups by adding dummy elements, not just its capacity. Something like this:

unsafe {
    let ruid = libc::getuid();
    let guid = libc::getgid();
    let num_groups = libc::getgroups(0, ::std::ptr::null_mut());
    let mut groups = vec![0; num_groups as usize];
    libc::getgroups(num_groups, groups.as_mut_ptr());
    println!(
        "real user id {} as user id {}, as user groups {:?}, numgroups {}",
        &ruid, &guid, &groups, &num_groups
    );
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Florian Weimer
  • 32,022
  • 3
  • 48
  • 92
2
  • 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()));
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366