1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 //! Wrappers for passwd and group file access.
6
7 use std::ffi::CStr;
8 use std::mem;
9 use std::ptr;
10
11 use libc::{self, getgrnam_r, getpwnam_r, gid_t, uid_t};
12
13 use crate::{errno_result, Result};
14
15 /// Safe wrapper for getting a uid from a user name with `getpwnam_r(3)`.
16 #[inline(always)]
get_user_id(user_name: &CStr) -> Result<uid_t>17 pub fn get_user_id(user_name: &CStr) -> Result<uid_t> {
18 // libc::passwd is a C struct and can be safely initialized with zeroed memory.
19 let mut passwd: libc::passwd = unsafe { mem::zeroed() };
20 let mut passwd_result: *mut libc::passwd = ptr::null_mut();
21 let mut buf = [0; 256];
22
23 // For thread-safety, use the reentrant version of this function. This allows us to give it a
24 // buffer on the stack (instead of a global buffer). Unlike most libc functions, the return
25 // value of this doesn't really need to be checked, since the extra result pointer that is
26 // passed in indicates whether or not the function succeeded.
27 //
28 // This call is safe as long as it behaves as described in the man page. We pass in valid
29 // pointers to stack-allocated buffers, and the length check for the scratch buffer is correct.
30 unsafe {
31 handle_eintr_rc!(getpwnam_r(
32 user_name.as_ptr(),
33 &mut passwd,
34 buf.as_mut_ptr(),
35 buf.len(),
36 &mut passwd_result
37 ))
38 };
39
40 if passwd_result.is_null() {
41 errno_result()
42 } else {
43 Ok(passwd.pw_uid)
44 }
45 }
46
47 /// Safe wrapper for getting a gid from a group name with `getgrnam_r(3)`.
48 #[inline(always)]
get_group_id(group_name: &CStr) -> Result<gid_t>49 pub fn get_group_id(group_name: &CStr) -> Result<gid_t> {
50 // libc::group is a C struct and can be safely initialized with zeroed memory.
51 let mut group: libc::group = unsafe { mem::zeroed() };
52 let mut group_result: *mut libc::group = ptr::null_mut();
53 let mut buf = [0; 256];
54
55 // For thread-safety, use the reentrant version of this function. This allows us to give it a
56 // buffer on the stack (instead of a global buffer). Unlike most libc functions, the return
57 // value of this doesn't really need to be checked, since the extra result pointer that is
58 // passed in indicates whether or not the function succeeded.
59 //
60 // This call is safe as long as it behaves as described in the man page. We pass in valid
61 // pointers to stack-allocated buffers, and the length check for the scratch buffer is correct.
62 unsafe {
63 handle_eintr_rc!(getgrnam_r(
64 group_name.as_ptr(),
65 &mut group,
66 buf.as_mut_ptr(),
67 buf.len(),
68 &mut group_result
69 ))
70 };
71
72 if group_result.is_null() {
73 errno_result()
74 } else {
75 Ok(group.gr_gid)
76 }
77 }
78
79 #[cfg(test)]
80 mod tests {
81 use super::*;
82
83 #[test]
get_good_uid()84 fn get_good_uid() {
85 let root_name = CStr::from_bytes_with_nul(b"root\0").unwrap();
86
87 // root's uid should always exist, and should be 0.
88 let root_uid = get_user_id(root_name).unwrap();
89 assert_eq!(root_uid, 0);
90 }
91
92 #[test]
get_bad_uid()93 fn get_bad_uid() {
94 let bad_name = CStr::from_bytes_with_nul(b"this better not be a user\0").unwrap();
95
96 // This user should give us an error. As a cruel joke, the getpwnam(3) man page allows
97 // ENOENT, ESRCH, EBADF, EPERM, or even 0 to be set in errno if a user isn't found. So
98 // instead of checking which error we got, just see that we did get one.
99 let bad_uid_result = get_user_id(bad_name);
100 assert!(bad_uid_result.is_err());
101 }
102
103 #[test]
get_good_gid()104 fn get_good_gid() {
105 let root_name = CStr::from_bytes_with_nul(b"root\0").unwrap();
106
107 // root's gid should always exist, and should be 0.
108 let root_gid = get_group_id(root_name).unwrap();
109 assert_eq!(root_gid, 0);
110 }
111
112 #[test]
get_bad_gid()113 fn get_bad_gid() {
114 let bad_name = CStr::from_bytes_with_nul(b"this better not be a group\0").unwrap();
115
116 // This group should give us an error. As a cruel joke, the getgrnam(3) man page allows
117 // ENOENT, ESRCH, EBADF, EPERM, or even 0 to be set in errno if a group isn't found. So
118 // instead of checking which error we got, just see that we did get one.
119 let bad_gid_result = get_group_id(bad_name);
120 assert!(bad_gid_result.is_err());
121 }
122 }
123