#![deny(warnings)]
#![allow(unsafe_code)]
extern crate libc;
use std::ffi::{CStr, CString, OsStr, OsString};
use std::os::unix::ffi::OsStrExt;
#[cfg(target_os = "linux")]
use {
crate::DaemonError::{GetPasswdRecord, SetProcName},
libc::{PR_SET_NAME, prctl},
};
use crate::{DaemonError, Result};
#[cfg(not(target_os = "linux"))]
use crate::DaemonError::{GetPasswdRecord, SetProcName, UnsupportedOnOS};
use crate::DaemonError::InvalidProcName;
#[repr(C)]
#[allow(dead_code)]
struct FFIGroup {
gr_name: *const libc::c_char,
gr_passwd: *const libc::c_char,
gr_gid: libc::gid_t,
gr_mem: *const *const libc::c_char,
}
#[cfg(target_os = "linux")]
#[repr(C)]
#[allow(dead_code)]
struct FFIPasswd {
pw_name: *const libc::c_char,
pw_passwd: *const libc::c_char,
pw_uid: libc::uid_t,
pw_gid: libc::gid_t,
pw_gecos: *const libc::c_char,
pw_dir: *const libc::c_char,
pw_shell: *const libc::c_char,
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
#[repr(C)]
#[allow(dead_code)]
struct FFIPasswd {
pw_name: *const libc::c_char,
pw_passwd: *const libc::c_char,
pw_uid: libc::uid_t,
pw_gid: libc::gid_t,
pw_change: libc::time_t,
pw_class: *const libc::c_char,
pw_gecos: *const libc::c_char,
pw_dir: *const libc::c_char,
pw_shell: *const libc::c_char,
pw_expire: libc::time_t,
pw_fields: libc::c_int,
}
#[cfg(any(target_os = "openbsd", target_os = "netbsd"))]
#[repr(C)]
#[allow(dead_code)]
struct FFIPasswd {
pw_name: *const libc::c_char,
pw_passwd: *const libc::c_char,
pw_uid: libc::uid_t,
pw_gid: libc::gid_t,
pw_change: libc::time_t,
pw_class: *const libc::c_char,
pw_gecos: *const libc::c_char,
pw_dir: *const libc::c_char,
pw_shell: *const libc::c_char,
pw_expire: libc::time_t,
}
#[allow(dead_code)]
extern "C" {
fn getgrnam(name: *const libc::c_char) -> *const FFIGroup;
fn getgrgid(name: libc::gid_t) -> *const FFIGroup;
fn getpwnam(name: *const libc::c_char) -> *const FFIPasswd;
fn getpwuid(name: libc::uid_t) -> *const FFIPasswd;
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct GroupRecord {
pub gr_name: String,
pub gr_passwd: String,
pub gr_gid: u32,
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct PasswdRecord {
pub pw_name: String,
pub pw_passwd: String,
pub pw_uid: u32,
pub pw_gid: u32,
pub pw_gecos: String,
pub pw_dir: String,
pub pw_shell: String,
}
unsafe fn check_group_record(grp: *const FFIGroup) -> Result<GroupRecord> {
return if grp.is_null() {
Err(DaemonError::GetGrRecord)
} else {
let gr = &*grp;
let sgr = GroupRecord {
gr_name: CStr::from_ptr(gr.gr_name).to_string_lossy().to_string(),
gr_passwd: CStr::from_ptr(gr.gr_passwd).to_string_lossy().to_string(),
gr_gid: gr.gr_gid as u32,
};
Ok(sgr)
};
}
unsafe fn check_passwd_record(passwd: *const FFIPasswd) -> Result<PasswdRecord> {
return if passwd.is_null() {
Err(GetPasswdRecord)
} else {
let pw = &*passwd;
let pwr = PasswdRecord {
pw_name: CStr::from_ptr(pw.pw_name).to_string_lossy().to_string(),
pw_passwd: CStr::from_ptr(pw.pw_passwd).to_string_lossy().to_string(),
pw_uid: pw.pw_uid as u32,
pw_gid: pw.pw_gid as u32,
pw_gecos: CStr::from_ptr(pw.pw_gecos).to_string_lossy().to_string(),
pw_dir: CStr::from_ptr(pw.pw_dir).to_string_lossy().to_string(),
pw_shell: CStr::from_ptr(pw.pw_shell).to_string_lossy().to_string(),
};
Ok(pwr)
};
}
#[allow(dead_code)]
impl GroupRecord {
pub fn lookup_record_by_name(name: &str) -> Result<GroupRecord> {
let record_name = match CString::new(name) {
Ok(s) => s,
Err(_) => return Err(DaemonError::InvalidCstr),
};
unsafe {
let raw_grp = getgrnam(record_name.as_ptr());
return check_group_record(raw_grp);
};
}
pub fn lookup_record_by_id(gid: u32) -> Result<GroupRecord> {
let record_id = gid as libc::uid_t;
unsafe {
let raw_grp = getgrgid(record_id);
return check_group_record(raw_grp);
};
}
}
impl PasswdRecord {
pub fn lookup_record_by_name(name: &str) -> Result<PasswdRecord> {
let record_name = match CString::new(name) {
Ok(s) => s,
Err(_) => return Err(DaemonError::InvalidCstr),
};
unsafe {
let raw_passwd = getpwnam(record_name.as_ptr());
return check_passwd_record(raw_passwd);
};
}
pub fn lookup_record_by_id(uid: u32) -> Result<PasswdRecord> {
let record_id = uid as libc::uid_t;
unsafe {
let raw_passwd = getpwuid(record_id);
return check_passwd_record(raw_passwd);
};
}
}
#[cfg(target_os = "linux")]
pub fn set_proc_name(name: &OsStr) -> Result<()> {
let name_truncated = match CString::new(OsString::from(name).as_bytes()) {
Ok(procname) => procname,
Err(_) => return Err(InvalidProcName),
};
unsafe {
if prctl(PR_SET_NAME, name_truncated.as_bytes_with_nul()) < 0 {
Err(SetProcName)
} else {
Ok(())
}
}
}
#[cfg(not(target_os = "linux"))]
pub fn set_proc_name(name: &OsStr) -> Result<()> {
Err(UnsupportedOnOS)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_passwd_by_name() {
let root = PasswdRecord::lookup_record_by_name("root").unwrap();
assert_eq!(root.pw_uid, 0)
}
#[test]
fn test_passwd_by_uid() {
let root = PasswdRecord::lookup_record_by_id(0).unwrap();
assert_eq!(root.pw_name, "root")
}
#[test]
fn test_gr_by_name() {
let root = GroupRecord::lookup_record_by_name("root").unwrap();
assert_eq!(root.gr_gid, 0)
}
#[test]
fn test_gr_by_gid() {
let root = GroupRecord::lookup_record_by_id(0).unwrap();
assert_eq!(root.gr_name, "root")
}
}