From 5bf651c0e41a051322d6a7dae4ac04efc70e6654 Mon Sep 17 00:00:00 2001 From: Stephen Barber Date: Fri, 20 Oct 2017 11:02:26 -0700 Subject: [PATCH] sys_util: add get_user_id and get_group_id functions Add safe wrappers for getpwnam_r and getgrnam_r. BUG=none TEST=./build_test Change-Id: I737b4d264334ed788884a7320f5649cfc2266709 Reviewed-on: https://chromium-review.googlesource.com/733730 Commit-Ready: Stephen Barber Tested-by: Stephen Barber Reviewed-by: Dylan Reid --- sys_util/src/lib.rs | 2 + sys_util/src/passwd.rs | 119 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 sys_util/src/passwd.rs diff --git a/sys_util/src/lib.rs b/sys_util/src/lib.rs index 7610d4e141..bf477b8d30 100644 --- a/sys_util/src/lib.rs +++ b/sys_util/src/lib.rs @@ -28,6 +28,7 @@ mod signal; mod fork; mod signalfd; mod sock_ctrl_msg; +mod passwd; pub use mmap::*; pub use shm::*; @@ -45,6 +46,7 @@ pub use fork::*; pub use signalfd::*; pub use ioctl::*; pub use sock_ctrl_msg::*; +pub use passwd::*; pub use mmap::Error as MmapError; pub use guest_memory::Error as GuestMemoryError; diff --git a/sys_util/src/passwd.rs b/sys_util/src/passwd.rs new file mode 100644 index 0000000000..2f86946bd6 --- /dev/null +++ b/sys_util/src/passwd.rs @@ -0,0 +1,119 @@ +// 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; +use libc::{c_char, gid_t, uid_t, getgrnam_r, getpwnam_r}; + +use {Result, errno_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!(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!(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()); + } +}