• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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