summary refs log blame commit diff
path: root/sys_util/src/passwd.rs
blob: 19a72cb7d3b6b6fe30211a92d160a1019d9c73ba (plain) (tree)
1
2
3
4
5
6
7
8
9
10









                                                                         
                                                               
 
                                  
















                                                                                                   






                                    
























                                                                                                   






                                    




















































                                                                                               
// 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<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_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<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_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());
    }
}