diff --git a/src/cmd/bootstrap/mod.rs b/src/cmd/bootstrap/mod.rs index 1ecef5d..2d765d1 100644 --- a/src/cmd/bootstrap/mod.rs +++ b/src/cmd/bootstrap/mod.rs @@ -246,20 +246,7 @@ impl BootstrapCmd for dyn Cmd { let mut path = PathBuf::from(prefix); let binpath = self.path(); match binpath { - Some(crate::Path::Bin) => path.push("bin"), - Some(crate::Path::Sbin) => path.push("sbin"), - Some(crate::Path::UsrBin) => { - if usr { - path.push("usr"); - } - path.push("bin"); - } - Some(crate::Path::UsrSbin) => { - if usr { - path.push("usr"); - } - path.push("sbin"); - } + Some(p) => path.push(p.to_str(usr)), None => return None, } path.push(self.name()); diff --git a/src/cmd/which/mod.rs b/src/cmd/which/mod.rs index 354289d..943c6a3 100644 --- a/src/cmd/which/mod.rs +++ b/src/cmd/which/mod.rs @@ -62,7 +62,23 @@ fn which(command: &str, path: &[&str]) -> Option { if let Ok(exe) = File::open(&file) { if let Ok(meta) = exe.metadata() { let mode = meta.mode(); - if mode & 0o111 != 0 { + let myuid = unsafe { + libc::geteuid() + }; + let mygroups = crate::pw::get_gids(); + // we own the file and it has u+x + if myuid == meta.uid() && mode & 0o100 != 0 { + return Some(format!("{}", file.display())); + // file has ug+x + } else if mode & 0o110 != 0 { + if let Ok(groups) = mygroups { + // one of our groups owns the file + if groups.contains(&meta.gid()) { + return Some(format!("{}", file.display())); + } + } + // the file has uga+x + } else if mode & 0o111 != 0 { return Some(format!("{}", file.display())); } } diff --git a/src/lib.rs b/src/lib.rs index 1ec2520..4afdae4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,8 @@ use std::{env, error::Error, path::PathBuf, process, string::ToString}; pub mod cmd; pub use cmd::Cmd; pub mod math; +/// User and group related functionality +pub mod pw; #[derive(Debug, Clone, Copy)] pub enum Path { @@ -13,6 +15,19 @@ pub enum Path { UsrSbin, } +impl Path { + pub fn to_str(&self, usr: bool) -> &'static str { + match self { + Self::Bin => "bin", + Self::UsrBin if usr => "usr/bin", + Self::UsrBin => "bin", + Self::Sbin => "sbin", + Self::UsrSbin if usr => "usr/sbin", + Self::UsrSbin => "sbin", + } + } +} + #[must_use] pub fn progname() -> Option { env::args() diff --git a/src/pw/mod.rs b/src/pw/mod.rs new file mode 100644 index 0000000..4884be1 --- /dev/null +++ b/src/pw/mod.rs @@ -0,0 +1,70 @@ +use std::{error::Error, ffi::CStr, num}; + +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() +} + +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_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_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() +} + +pub fn get_gids() -> Result, num::TryFromIntError> { + let mut buf: Vec; + 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) +} + +pub fn get_group_names() -> Result, Box> { + let gids = get_gids()?; + let mut names = vec![]; + for id in gids { + let name = unsafe { + let gr = libc::getgrgid(id); + 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) +}