shitbox/pw/src/lib.rs

207 lines
6.0 KiB
Rust

#![warn(clippy::all, clippy::pedantic)]
#![allow(clippy::missing_errors_doc, clippy::must_use_candidate)]
//! Wraps certain libc functions around groups and users
use std::{
error::Error,
ffi::{c_int, CStr, CString},
io, num,
};
/// Gets the current username of this process
/// # Errors
/// The user name must be valid utf8
pub fn get_username<'a>() -> Result<&'a str, std::str::Utf8Error> {
let user = unsafe {
let uid = libc::getuid();
let pw = libc::getpwuid(uid);
let name = (*pw).pw_name;
CStr::from_ptr(name)
};
user.to_str()
}
/// Gets the current effective user name of this process
/// # Errors
/// The user name must be valid utf8
pub fn get_eusername<'a>() -> Result<&'a str, std::str::Utf8Error> {
let user = unsafe {
let uid = libc::geteuid();
let pw = libc::getpwuid(uid);
let name = (*pw).pw_name;
CStr::from_ptr(name)
};
user.to_str()
}
pub fn get_username_for_uid<'a>(uid: u32) -> Result<&'a str, std::str::Utf8Error> {
let user = unsafe {
let pw = libc::getpwuid(uid);
let name = (*pw).pw_name;
CStr::from_ptr(name)
};
user.to_str()
}
/// Gets the uid associated with the given name
#[must_use]
pub fn get_uid_for_name(name: &str) -> Option<u32> {
let Ok(user) = CString::new(name.as_bytes()) else { return None };
unsafe {
let pw = libc::getpwnam(user.as_ptr());
if pw.is_null() {
None
} else {
Some((*pw).pw_uid)
}
}
}
/// Gets the gid for the main group associated with the given user
#[must_use]
pub fn get_pgid_for_name(name: &str) -> Option<u32> {
let Ok(user) = CString::new(name.as_bytes()) else { return None };
unsafe {
let pw = libc::getpwnam(user.as_ptr());
if pw.is_null() {
None
} else {
Some((*pw).pw_gid)
}
}
}
/// Gets the main group name for the current process
/// # Errors
/// The name must be valid utf8
pub fn get_grpname<'a>() -> Result<&'a str, std::str::Utf8Error> {
let group = unsafe {
let gid = libc::getgid();
let gr = libc::getgrgid(gid);
let name = (*gr).gr_name;
CStr::from_ptr(name)
};
group.to_str()
}
pub fn get_gid_for_groupname(groupname: &str) -> Option<u32> {
let Ok(grp) = CString::new(groupname.as_bytes()) else { return None };
unsafe { Some((*libc::getgrnam(grp.as_ptr())).gr_gid) }
}
pub fn get_groupname_for_gid<'a>(gid: u32) -> Result<&'a str, std::str::Utf8Error> {
let group = unsafe {
let gr = libc::getgrgid(gid);
let name = (*gr).gr_name;
CStr::from_ptr(name)
};
group.to_str()
}
/// Gets the effective group for the current process
/// # Errors
/// The name must be valid utf8
pub fn get_egrpname<'a>() -> Result<&'a str, std::str::Utf8Error> {
let group = unsafe {
let gid = libc::getegid();
let gr = libc::getgrgid(gid);
let name = (*gr).gr_name;
CStr::from_ptr(name)
};
group.to_str()
}
/// Gets the group ids for all groups the current effective user belongs to
/// # Errors
/// Can only error if unable to make a valid usize from the number
/// of groups returned via `libc::getgroups`
pub fn get_gids() -> Result<Vec<u32>, num::TryFromIntError> {
let mut buf: Vec<libc::gid_t>;
unsafe {
buf = vec![0; 1];
let num = libc::getgroups(0, buf.as_mut_ptr());
buf = vec![0; num.try_into()?];
let _num = libc::getgroups(num, buf.as_mut_ptr());
buf.push(libc::getegid());
};
buf.dedup();
Ok(buf)
}
/// Gets the group ids for all groups the named user belongs to
/// # Errors
/// Errors if a valid `CString` cannot be made
/// Errors if a user belongs to more than 1024 groups
#[allow(clippy::cast_sign_loss)]
pub fn get_gids_for_name(name: &str) -> Result<Vec<u32>, Box<dyn Error>> {
let gid = match get_pgid_for_name(name) {
Some(g) => g as libc::gid_t,
None => {
return Err(Box::new(io::Error::new(
io::ErrorKind::Other,
"cannot get primary group",
)))
}
};
let mut buf: Vec<libc::gid_t> = vec![0; 1024];
let name = CString::new(name.as_bytes())?;
let mut ct: c_int = 1024;
unsafe {
let res = libc::getgrouplist(name.as_ptr(), gid, buf.as_mut_ptr(), &mut ct);
if res < 0 {
return Err(Box::new(io::Error::new(
io::ErrorKind::Other,
"too many groups",
)));
}
}
Ok(buf[0..ct as usize].to_vec())
}
/// Gets a list of group names for the current effective user
/// # Errors
/// Bubbles up errors from `get_gids`
/// Can error if a group name is not valid utf8
pub fn get_group_names() -> Result<Vec<String>, Box<dyn Error>> {
let gids = get_gids()?;
let mut names = vec![];
for id in gids {
let name = unsafe {
let gr = libc::getgrgid(id);
if gr.is_null() {
id.to_string()
} else {
let name = (*gr).gr_name;
// if we don't take ownership here the OS can (and will)
// reuse the memory
CStr::from_ptr(name).to_str()?.to_string()
}
};
names.push(name);
}
Ok(names)
}
/// Gets a list of group names for the given user name
/// # Errors
/// Bubbles up errors from `get_gids_for_name`
/// Can error if a group name is not valid utf8
pub fn get_group_names_for_name(name: &str) -> Result<Vec<String>, Box<dyn Error>> {
let gids = get_gids_for_name(name)?;
let mut names = vec![];
for id in gids {
let name = unsafe {
let gr = libc::getgrgid(id);
if gr.is_null() {
id.to_string()
} else {
let name = (*gr).gr_name;
// if we don't take ownership here the OS can (and will)
// reuse the memory
CStr::from_ptr(name).to_str()?.to_string()
}
};
names.push(name);
}
Ok(names)
}