diff --git a/CHANGELOG.md b/CHANGELOG.md index e13d0ac..5d82c09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [Unreleased] - ReleaseDate +### Changed +- Dropped the `users` crate and fixed erroneous user lookups + ([Issue](https://github.com/fubarnetes/rctl/pull/5), + [PR](https://github.com/fubarnetes/rctl/pull/5)) + ## [0.0.5] - 2018-12-20 ### Added - This Changelog diff --git a/Cargo.toml b/Cargo.toml index 2ed1d64..cc5315c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,4 +21,3 @@ libc = "0.2" nix = "0.11" number_prefix = "0.2" sysctl = "0.2" -users = "0.7" diff --git a/src/lib.rs b/src/lib.rs index 8c88664..19f2bc5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,7 +44,6 @@ extern crate libc; extern crate nix; extern crate number_prefix; extern crate sysctl; -extern crate users; pub use nix::sys::signal::Signal; use number_prefix::{binary_prefix, Prefix, Prefixed, Standalone}; @@ -89,6 +88,12 @@ pub enum ParseError { #[fail(display = "Invalid Rule syntax: '{}'", _0)] InvalidRuleSyntax(String), + + #[fail( + display = "An interior Nul byte was found while attempting to construct a CString: {}", + _0 + )] + CStringError(#[cause] NulError), } #[derive(Debug, Fail)] @@ -117,33 +122,80 @@ mod subject { use super::ParseError; use libc; use std::fmt; - use users; - use users::{get_user_by_name, get_user_by_uid}; /// Represents a user subject #[derive(Clone, Debug, PartialEq, Eq, Hash)] - pub struct User(pub users::uid_t); + pub struct User(pub libc::uid_t); impl User { pub fn from_uid(uid: libc::uid_t) -> User { - User(uid as users::uid_t) + User(uid) } pub fn from_name(name: &str) -> Result { - let uid = get_user_by_name(name) - .ok_or_else(|| ParseError::UnknownUser(name.into()))? - .uid(); + // From getpwent(3): + // + // The functions getpwent_r(), getpwnam_r(), and getpwuid_r() + // are thread-safe versions of getpwent(), getpwnam(), and + // getpwuid(), respectively. + // + // The caller must provide storage for the results of the search in + // the pwd, buffer, bufsize, and result arguments. + let c_name = std::ffi::CString::new(name).map_err(ParseError::CStringError)?; + let mut passwd: libc::passwd = unsafe { std::mem::zeroed() }; + const PWBUFLEN: usize = 2048; + let mut buf: Vec = vec![0; PWBUFLEN]; + let mut result: *mut libc::passwd = std::ptr::null_mut(); + + unsafe { + libc::getpwnam_r( + c_name.as_ptr(), + &mut passwd, + buf.as_mut_ptr(), + buf.len(), + &mut result, + ); + } + + // If an entry is not found or an error occurs, result will be set + // to NULL. + if result.is_null() { + return Err(ParseError::UnknownUser(name.into())); + } - Ok(User::from_uid(uid)) + // When these functions are successful, the pwd argument will be + // filled-in, and a pointer to that argument will be stored in + // result. + assert_eq!(result, &mut passwd as *mut libc::passwd); + Ok(User::from_uid(passwd.pw_uid)) } } impl fmt::Display for User { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match get_user_by_uid(self.0) { - Some(user) => write!(f, "user:{}", user.name()), - None => write!(f, "user:{}", self.0), + let mut passwd: libc::passwd = unsafe { std::mem::zeroed() }; + const PWBUFLEN: usize = 2048; + let mut buf: Vec = vec![0; PWBUFLEN]; + let mut result: *mut libc::passwd = std::ptr::null_mut(); + + unsafe { + libc::getpwuid_r( + self.0, + &mut passwd, + buf.as_mut_ptr(), + buf.len(), + &mut result, + ); } + + // If an entry is not found or an error occurs, result will be set + // to NULL. + if result.is_null() { + return write!(f, "user:{}", self.0); + } + + let name = unsafe { std::ffi::CStr::from_ptr(passwd.pw_name) }; + write!(f, "user:{}", name.to_string_lossy()) } } @@ -204,6 +256,7 @@ mod subject { #[cfg(test)] mod tests { use super::*; + use std::process::Command; #[test] fn display_jail_name() { @@ -230,6 +283,24 @@ mod subject { "loginclass:test".to_string() ); } + + #[test] + fn from_name() { + let nobody = User::from_name("nobody").expect("no nobody user"); + + // Get the UID using the `getent` command. + let uid_nobody = Command::new("id") + .args(&["-u", "nobody"]) + .output() + .expect("failed to run `id -u nobody`"); + + let uid_nobody: libc::uid_t = String::from_utf8_lossy(&uid_nobody.stdout) + .trim() + .parse() + .expect("Could not parse UID"); + + assert_eq!(User::from_uid(uid_nobody), nobody); + } } }