summary refs log tree commit diff
diff options
context:
space:
mode:
authorStephen Barber <smbarber@chromium.org>2017-10-20 11:02:26 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-10-26 04:17:52 -0700
commit5bf651c0e41a051322d6a7dae4ac04efc70e6654 (patch)
tree74be6435852a18516ec7a7e88236bf4da47a3582
parent7a2fec45967f630c6a2a99dbdb5ca92ebca02449 (diff)
downloadcrosvm-5bf651c0e41a051322d6a7dae4ac04efc70e6654.tar
crosvm-5bf651c0e41a051322d6a7dae4ac04efc70e6654.tar.gz
crosvm-5bf651c0e41a051322d6a7dae4ac04efc70e6654.tar.bz2
crosvm-5bf651c0e41a051322d6a7dae4ac04efc70e6654.tar.lz
crosvm-5bf651c0e41a051322d6a7dae4ac04efc70e6654.tar.xz
crosvm-5bf651c0e41a051322d6a7dae4ac04efc70e6654.tar.zst
crosvm-5bf651c0e41a051322d6a7dae4ac04efc70e6654.zip
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 <smbarber@chromium.org>
Tested-by: Stephen Barber <smbarber@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
-rw-r--r--sys_util/src/lib.rs2
-rw-r--r--sys_util/src/passwd.rs119
2 files changed, 121 insertions, 0 deletions
diff --git a/sys_util/src/lib.rs b/sys_util/src/lib.rs
index 7610d4e..bf477b8 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 0000000..2f86946
--- /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<uid_t> {
+    // 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<gid_t> {
+    // 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());
+    }
+}