276 lines
7.3 KiB
Rust
276 lines
7.3 KiB
Rust
//! `shadow.h` implementation.
|
|
//!
|
|
//! Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getspnam.3.html>.
|
|
|
|
use core::{
|
|
cell::SyncUnsafeCell,
|
|
ops::{Deref, DerefMut},
|
|
pin::Pin,
|
|
ptr,
|
|
str::FromStr,
|
|
};
|
|
|
|
use alloc::{boxed::Box, string::String};
|
|
|
|
use crate::{
|
|
c_str::CStr,
|
|
fs::File,
|
|
header::fcntl,
|
|
io::{BufReader, Lines, prelude::*},
|
|
platform,
|
|
platform::types::{c_char, c_int, c_long, c_ulong, size_t},
|
|
};
|
|
|
|
use super::errno::*;
|
|
|
|
#[cfg(target_os = "linux")]
|
|
const SEPARATOR: char = ':';
|
|
|
|
#[cfg(target_os = "redox")]
|
|
const SEPARATOR: char = ';';
|
|
|
|
const SHADOW_FILE: &core::ffi::CStr = c"/etc/shadow";
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
struct DestBuffer {
|
|
ptr: *mut u8,
|
|
len: usize,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum MaybeAllocated {
|
|
Owned(Pin<Box<[u8]>>),
|
|
Borrowed(DestBuffer),
|
|
}
|
|
impl Deref for MaybeAllocated {
|
|
type Target = [u8];
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
match self {
|
|
MaybeAllocated::Owned(boxed) => boxed,
|
|
MaybeAllocated::Borrowed(dst) => unsafe {
|
|
core::slice::from_raw_parts(dst.ptr, dst.len)
|
|
},
|
|
}
|
|
}
|
|
}
|
|
impl DerefMut for MaybeAllocated {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
match self {
|
|
MaybeAllocated::Owned(boxed) => boxed,
|
|
MaybeAllocated::Borrowed(dst) => unsafe {
|
|
core::slice::from_raw_parts_mut(dst.ptr, dst.len)
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
static mut SHADOW_BUF: Option<MaybeAllocated> = None;
|
|
static mut SHADOW: spwd = spwd {
|
|
sp_namp: ptr::null_mut(),
|
|
sp_pwdp: ptr::null_mut(),
|
|
sp_lstchg: -1,
|
|
sp_min: -1,
|
|
sp_max: -1,
|
|
sp_warn: -1,
|
|
sp_inact: -1,
|
|
sp_expire: -1,
|
|
sp_flag: 0,
|
|
};
|
|
|
|
static LINE_READER: SyncUnsafeCell<Option<Lines<BufReader<File>>>> = SyncUnsafeCell::new(None);
|
|
|
|
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getspnam.3.html>.
|
|
#[repr(C)]
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct spwd {
|
|
pub sp_namp: *mut c_char,
|
|
pub sp_pwdp: *mut c_char,
|
|
pub sp_lstchg: c_long,
|
|
pub sp_min: c_long,
|
|
pub sp_max: c_long,
|
|
pub sp_warn: c_long,
|
|
pub sp_inact: c_long,
|
|
pub sp_expire: c_long,
|
|
pub sp_flag: c_ulong,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum Error {
|
|
EOF,
|
|
BufTooSmall,
|
|
Syntax,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct OwnedSpwd {
|
|
buffer: MaybeAllocated,
|
|
reference: spwd,
|
|
}
|
|
|
|
impl OwnedSpwd {
|
|
fn into_global(self) -> *mut spwd {
|
|
unsafe {
|
|
SHADOW_BUF = Some(self.buffer);
|
|
SHADOW = self.reference;
|
|
&raw mut SHADOW
|
|
}
|
|
}
|
|
}
|
|
fn to_long(s: &str) -> c_long {
|
|
c_long::from_str(s).unwrap_or(-1)
|
|
}
|
|
fn to_ulong(s: &str) -> c_ulong {
|
|
c_ulong::from_str(s).unwrap_or(0)
|
|
}
|
|
|
|
fn parse_spwd(line: String, destbuf: Option<DestBuffer>) -> Result<OwnedSpwd, Error> {
|
|
let mut parts = line.split(SEPARATOR);
|
|
|
|
let sp_namp_str = parts.next().ok_or(Error::Syntax)?;
|
|
let sp_pwdp_str = parts.next().ok_or(Error::Syntax)?;
|
|
|
|
//TODO: these are not implemented redox-users crate
|
|
let sp_lstchg = to_long(parts.next().unwrap_or(""));
|
|
let sp_min = to_long(parts.next().unwrap_or(""));
|
|
let sp_max = to_long(parts.next().unwrap_or(""));
|
|
let sp_warn = to_long(parts.next().unwrap_or(""));
|
|
let sp_inact = to_long(parts.next().unwrap_or(""));
|
|
let sp_expire = to_long(parts.next().unwrap_or(""));
|
|
let sp_flag = to_ulong(parts.next().unwrap_or(""));
|
|
|
|
let string_data_len = sp_namp_str.len() + 1 + sp_pwdp_str.len() + 1;
|
|
|
|
let mut buffer = match destbuf {
|
|
Some(buf) => {
|
|
if buf.len < string_data_len {
|
|
platform::ERRNO.set(ERANGE);
|
|
return Err(Error::BufTooSmall);
|
|
}
|
|
MaybeAllocated::Borrowed(buf)
|
|
}
|
|
None => {
|
|
let vec = vec![0; string_data_len];
|
|
MaybeAllocated::Owned(Box::into_pin(vec.into_boxed_slice()))
|
|
}
|
|
};
|
|
|
|
let (name_slice, rest) = buffer.split_at_mut(sp_namp_str.len() + 1);
|
|
name_slice[..sp_namp_str.len()].copy_from_slice(sp_namp_str.as_bytes());
|
|
name_slice[sp_namp_str.len()] = 0;
|
|
|
|
let (pwd_slice, _) = rest.split_at_mut(sp_pwdp_str.len() + 1);
|
|
pwd_slice[..sp_pwdp_str.len()].copy_from_slice(sp_pwdp_str.as_bytes());
|
|
pwd_slice[sp_pwdp_str.len()] = 0;
|
|
|
|
let sp_namp = name_slice.as_mut_ptr().cast::<c_char>();
|
|
let sp_pwdp = pwd_slice.as_mut_ptr().cast::<c_char>();
|
|
|
|
let reference = spwd {
|
|
sp_namp,
|
|
sp_pwdp,
|
|
sp_lstchg,
|
|
sp_min,
|
|
sp_max,
|
|
sp_warn,
|
|
sp_inact,
|
|
sp_expire,
|
|
sp_flag,
|
|
};
|
|
|
|
Ok(OwnedSpwd { buffer, reference })
|
|
}
|
|
|
|
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getspnam.3.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn getspnam(name: *const c_char) -> *mut spwd {
|
|
let Ok(db) = File::open(SHADOW_FILE.into(), fcntl::O_RDONLY) else {
|
|
return ptr::null_mut();
|
|
};
|
|
let c_name = unsafe { CStr::from_ptr(name) };
|
|
|
|
for line in BufReader::new(db).lines() {
|
|
let Ok(line) = line else { continue };
|
|
if line.starts_with(c_name.to_str().unwrap_or("\0"))
|
|
&& let Ok(pwd) = parse_spwd(line, None)
|
|
{
|
|
return pwd.into_global();
|
|
}
|
|
}
|
|
ptr::null_mut()
|
|
}
|
|
|
|
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getspnam.3.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn getspnam_r(
|
|
name: *const c_char,
|
|
result_buf: *mut spwd,
|
|
buffer: *mut c_char,
|
|
buflen: size_t,
|
|
result: *mut *mut spwd,
|
|
) -> c_int {
|
|
unsafe { *result = ptr::null_mut() };
|
|
|
|
let Ok(db) = File::open(SHADOW_FILE.into(), fcntl::O_RDONLY) else {
|
|
return ENOENT;
|
|
};
|
|
let c_name = unsafe { CStr::from_ptr(name).to_str().unwrap_or("\0") };
|
|
|
|
for line in BufReader::new(db).lines() {
|
|
let Ok(line) = line else { continue };
|
|
if line.starts_with(c_name) {
|
|
let dest_buf = Some(DestBuffer {
|
|
ptr: buffer.cast::<u8>(),
|
|
len: buflen,
|
|
});
|
|
return match parse_spwd(line, dest_buf) {
|
|
Ok(sp) => {
|
|
unsafe {
|
|
*result_buf = sp.reference;
|
|
*result = result_buf;
|
|
}
|
|
0
|
|
}
|
|
Err(Error::BufTooSmall) => ERANGE,
|
|
_ => ENOENT,
|
|
};
|
|
}
|
|
}
|
|
ENOENT
|
|
}
|
|
|
|
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getspnam.3.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn setspent() {
|
|
let line_reader = unsafe { &mut *LINE_READER.get() };
|
|
if let Ok(db) = File::open(SHADOW_FILE.into(), fcntl::O_RDONLY) {
|
|
*line_reader = Some(BufReader::new(db).lines());
|
|
}
|
|
}
|
|
|
|
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getspnam.3.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn endspent() {
|
|
unsafe {
|
|
*LINE_READER.get() = None;
|
|
}
|
|
}
|
|
|
|
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getspnam.3.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn getspent() -> *mut spwd {
|
|
let line_reader = unsafe { &mut *LINE_READER.get() };
|
|
if line_reader.is_none() {
|
|
unsafe {
|
|
setspent();
|
|
}
|
|
}
|
|
if let Some(lines) = line_reader
|
|
&& let Some(Ok(line)) = lines.next()
|
|
&& let Ok(sp) = parse_spwd(line, None)
|
|
{
|
|
return sp.into_global();
|
|
}
|
|
ptr::null_mut()
|
|
}
|