如何通过libc :: getgroups为用户获取组?

时间:2018-06-15 20:20:51

标签: rust libc

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

打印

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

我的假设是它会显示9组的载体。

2 个答案:

答案 0 :(得分:2)

您需要通过添加虚拟元素来更改groups的大小,而不仅仅是其容量。像这样:

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

答案 1 :(得分:2)

  • 您的所有代码都没有检查错误! seteuidgetgroups可以失败,但您的代码会忽略这种可能性。您正在尝试调试失败,但甚至没有花时间检查您正在调用的函数是否成功。

  • 在传递v.as_mut_ptr()时要非常小心。空Vec 有空指针。在这种情况下它很好,因为手册页说它只关心计数。

  • 您不会处理第一次和第二次通话之间小组数量发生变化的情况。

  • You don't need to pass in references to the println arguments

有了这个,因为你只在u32中存储Vec,你可以通过相应地调整大小和容量来避免用虚拟值填充它:

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

但是,我不会重写所有这些,我会依赖现有的工作。 nix crate提供了很好的包装器:

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