// Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! Wrappers for passwd and group file access. use std::ffi::CStr; use std::mem; use std::ptr; use libc::{self, c_char, getgrnam_r, getpwnam_r, gid_t, uid_t}; use crate::{errno_result, Result}; /// Safe wrapper for getting a uid from a user name with `getpwnam_r(3)`. #[inline(always)] pub fn get_user_id(user_name: &CStr) -> Result { // libc::passwd is a C struct and can be safely initialized with zeroed memory. let mut passwd: libc::passwd = unsafe { mem::zeroed() }; let mut passwd_result: *mut libc::passwd = ptr::null_mut(); let mut buf = [0 as c_char; 256]; // For thread-safety, use the reentrant version of this function. This allows us to give it a // buffer on the stack (instead of a global buffer). Unlike most libc functions, the return // value of this doesn't really need to be checked, since the extra result pointer that is // passed in indicates whether or not the function succeeded. // // This call is safe as long as it behaves as described in the man page. We pass in valid // pointers to stack-allocated buffers, and the length check for the scratch buffer is correct. unsafe { handle_eintr_rc!(getpwnam_r( user_name.as_ptr(), &mut passwd, buf.as_mut_ptr(), buf.len(), &mut passwd_result )) }; if passwd_result.is_null() { errno_result() } else { Ok(passwd.pw_uid) } } /// Safe wrapper for getting a gid from a group name with `getgrnam_r(3)`. #[inline(always)] pub fn get_group_id(group_name: &CStr) -> Result { // libc::group is a C struct and can be safely initialized with zeroed memory. let mut group: libc::group = unsafe { mem::zeroed() }; let mut group_result: *mut libc::group = ptr::null_mut(); let mut buf = [0 as c_char; 256]; // For thread-safety, use the reentrant version of this function. This allows us to give it a // buffer on the stack (instead of a global buffer). Unlike most libc functions, the return // value of this doesn't really need to be checked, since the extra result pointer that is // passed in indicates whether or not the function succeeded. // // This call is safe as long as it behaves as described in the man page. We pass in valid // pointers to stack-allocated buffers, and the length check for the scratch buffer is correct. unsafe { handle_eintr_rc!(getgrnam_r( group_name.as_ptr(), &mut group, buf.as_mut_ptr(), buf.len(), &mut group_result )) }; if group_result.is_null() { errno_result() } else { Ok(group.gr_gid) } } #[cfg(test)] mod tests { use super::*; #[test] fn get_good_uid() { let root_name = CStr::from_bytes_with_nul(b"root\0").unwrap(); // root's uid should always exist, and should be 0. let root_uid = get_user_id(root_name).unwrap(); assert_eq!(root_uid, 0); } #[test] fn get_bad_uid() { let bad_name = CStr::from_bytes_with_nul(b"this better not be a user\0").unwrap(); // This user should give us an error. As a cruel joke, the getpwnam(3) man page allows // ENOENT, ESRCH, EBADF, EPERM, or even 0 to be set in errno if a user isn't found. So // instead of checking which error we got, just see that we did get one. let bad_uid_result = get_user_id(bad_name); assert!(bad_uid_result.is_err()); } #[test] fn get_good_gid() { let root_name = CStr::from_bytes_with_nul(b"root\0").unwrap(); // root's gid should always exist, and should be 0. let root_gid = get_group_id(root_name).unwrap(); assert_eq!(root_gid, 0); } #[test] fn get_bad_gid() { let bad_name = CStr::from_bytes_with_nul(b"this better not be a group\0").unwrap(); // This group should give us an error. As a cruel joke, the getgrnam(3) man page allows // ENOENT, ESRCH, EBADF, EPERM, or even 0 to be set in errno if a group isn't found. So // instead of checking which error we got, just see that we did get one. let bad_gid_result = get_group_id(bad_name); assert!(bad_gid_result.is_err()); } }