relibc/header/shadow/
mod.rs1use core::{
6 cell::SyncUnsafeCell,
7 ops::{Deref, DerefMut},
8 pin::Pin,
9 ptr,
10 str::FromStr,
11};
12
13use alloc::{boxed::Box, string::String};
14
15use crate::{
16 c_str::CStr,
17 fs::File,
18 header::fcntl,
19 io::{BufReader, Lines, prelude::*},
20 platform,
21 platform::types::{c_char, c_int, c_long, c_ulong, size_t},
22};
23
24use super::errno::*;
25
26#[cfg(target_os = "linux")]
28const SEPARATOR: char = ':';
29
30#[cfg(target_os = "redox")]
32const SEPARATOR: char = ';';
33
34const SHADOW_FILE: &core::ffi::CStr = c"/etc/shadow";
36
37#[derive(Clone, Copy, Debug)]
38struct DestBuffer {
39 ptr: *mut u8,
40 len: usize,
41}
42
43#[derive(Debug)]
44enum MaybeAllocated {
45 Owned(Pin<Box<[u8]>>),
46 Borrowed(DestBuffer),
47}
48impl Deref for MaybeAllocated {
49 type Target = [u8];
50
51 fn deref(&self) -> &Self::Target {
52 match self {
53 MaybeAllocated::Owned(boxed) => boxed,
54 MaybeAllocated::Borrowed(dst) => unsafe {
55 core::slice::from_raw_parts(dst.ptr, dst.len)
56 },
57 }
58 }
59}
60impl DerefMut for MaybeAllocated {
61 fn deref_mut(&mut self) -> &mut Self::Target {
62 match self {
63 MaybeAllocated::Owned(boxed) => boxed,
64 MaybeAllocated::Borrowed(dst) => unsafe {
65 core::slice::from_raw_parts_mut(dst.ptr, dst.len)
66 },
67 }
68 }
69}
70
71static mut SHADOW_BUF: Option<MaybeAllocated> = None;
73static mut SHADOW: spwd = spwd {
75 sp_namp: ptr::null_mut(),
76 sp_pwdp: ptr::null_mut(),
77 sp_lstchg: -1,
78 sp_min: -1,
79 sp_max: -1,
80 sp_warn: -1,
81 sp_inact: -1,
82 sp_expire: -1,
83 sp_flag: 0,
84};
85
86static LINE_READER: SyncUnsafeCell<Option<Lines<BufReader<File>>>> = SyncUnsafeCell::new(None);
88
89#[repr(C)]
91#[derive(Debug, Clone, Copy)]
92pub struct spwd {
93 pub sp_namp: *mut c_char,
94 pub sp_pwdp: *mut c_char,
95 pub sp_lstchg: c_long,
96 pub sp_min: c_long,
97 pub sp_max: c_long,
98 pub sp_warn: c_long,
99 pub sp_inact: c_long,
100 pub sp_expire: c_long,
101 pub sp_flag: c_ulong,
102}
103
104#[derive(Debug)]
105enum Error {
106 EOF,
107 BufTooSmall,
108 Syntax,
109}
110
111#[derive(Debug)]
112struct OwnedSpwd {
113 buffer: MaybeAllocated,
114 reference: spwd,
115}
116
117impl OwnedSpwd {
118 fn into_global(self) -> *mut spwd {
119 unsafe {
120 SHADOW_BUF = Some(self.buffer);
121 SHADOW = self.reference;
122 &raw mut SHADOW
123 }
124 }
125}
126fn to_long(s: &str) -> c_long {
127 c_long::from_str(s).unwrap_or(-1)
128}
129fn to_ulong(s: &str) -> c_ulong {
130 c_ulong::from_str(s).unwrap_or(0)
131}
132
133fn parse_spwd(line: String, destbuf: Option<DestBuffer>) -> Result<OwnedSpwd, Error> {
134 let mut parts = line.split(SEPARATOR);
135
136 let sp_namp_str = parts.next().ok_or(Error::Syntax)?;
137 let sp_pwdp_str = parts.next().ok_or(Error::Syntax)?;
138
139 let sp_lstchg = to_long(parts.next().unwrap_or(""));
141 let sp_min = to_long(parts.next().unwrap_or(""));
142 let sp_max = to_long(parts.next().unwrap_or(""));
143 let sp_warn = to_long(parts.next().unwrap_or(""));
144 let sp_inact = to_long(parts.next().unwrap_or(""));
145 let sp_expire = to_long(parts.next().unwrap_or(""));
146 let sp_flag = to_ulong(parts.next().unwrap_or(""));
147
148 let string_data_len = sp_namp_str.len() + 1 + sp_pwdp_str.len() + 1;
149
150 let mut buffer = match destbuf {
151 Some(buf) => {
152 if buf.len < string_data_len {
153 platform::ERRNO.set(ERANGE);
154 return Err(Error::BufTooSmall);
155 }
156 MaybeAllocated::Borrowed(buf)
157 }
158 None => {
159 let vec = vec![0; string_data_len];
160 MaybeAllocated::Owned(Box::into_pin(vec.into_boxed_slice()))
161 }
162 };
163
164 let (name_slice, rest) = buffer.split_at_mut(sp_namp_str.len() + 1);
165 name_slice[..sp_namp_str.len()].copy_from_slice(sp_namp_str.as_bytes());
166 name_slice[sp_namp_str.len()] = 0;
167
168 let (pwd_slice, _) = rest.split_at_mut(sp_pwdp_str.len() + 1);
169 pwd_slice[..sp_pwdp_str.len()].copy_from_slice(sp_pwdp_str.as_bytes());
170 pwd_slice[sp_pwdp_str.len()] = 0;
171
172 let sp_namp = name_slice.as_mut_ptr().cast::<c_char>();
173 let sp_pwdp = pwd_slice.as_mut_ptr().cast::<c_char>();
174
175 let reference = spwd {
176 sp_namp,
177 sp_pwdp,
178 sp_lstchg,
179 sp_min,
180 sp_max,
181 sp_warn,
182 sp_inact,
183 sp_expire,
184 sp_flag,
185 };
186
187 Ok(OwnedSpwd { buffer, reference })
188}
189
190#[unsafe(no_mangle)]
192pub unsafe extern "C" fn getspnam(name: *const c_char) -> *mut spwd {
193 let Ok(db) = File::open(SHADOW_FILE.into(), fcntl::O_RDONLY) else {
194 return ptr::null_mut();
195 };
196 let c_name = unsafe { CStr::from_ptr(name) };
197
198 for line in BufReader::new(db).lines() {
199 let Ok(line) = line else { continue };
200 if line.starts_with(c_name.to_str().unwrap_or("\0"))
201 && let Ok(pwd) = parse_spwd(line, None)
202 {
203 return pwd.into_global();
204 }
205 }
206 ptr::null_mut()
207}
208
209#[unsafe(no_mangle)]
211pub unsafe extern "C" fn getspnam_r(
212 name: *const c_char,
213 result_buf: *mut spwd,
214 buffer: *mut c_char,
215 buflen: size_t,
216 result: *mut *mut spwd,
217) -> c_int {
218 unsafe { *result = ptr::null_mut() };
219
220 let Ok(db) = File::open(SHADOW_FILE.into(), fcntl::O_RDONLY) else {
221 return ENOENT;
222 };
223 let c_name = unsafe { CStr::from_ptr(name).to_str().unwrap_or("\0") };
224
225 for line in BufReader::new(db).lines() {
226 let Ok(line) = line else { continue };
227 if line.starts_with(c_name) {
228 let dest_buf = Some(DestBuffer {
229 ptr: buffer.cast::<u8>(),
230 len: buflen,
231 });
232 return match parse_spwd(line, dest_buf) {
233 Ok(sp) => {
234 unsafe {
235 *result_buf = sp.reference;
236 *result = result_buf;
237 }
238 0
239 }
240 Err(Error::BufTooSmall) => ERANGE,
241 _ => ENOENT,
242 };
243 }
244 }
245 ENOENT
246}
247
248#[unsafe(no_mangle)]
250pub unsafe extern "C" fn setspent() {
251 let line_reader = unsafe { &mut *LINE_READER.get() };
252 if let Ok(db) = File::open(SHADOW_FILE.into(), fcntl::O_RDONLY) {
253 *line_reader = Some(BufReader::new(db).lines());
254 }
255}
256
257#[unsafe(no_mangle)]
259pub unsafe extern "C" fn endspent() {
260 unsafe {
261 *LINE_READER.get() = None;
262 }
263}
264
265#[unsafe(no_mangle)]
267pub unsafe extern "C" fn getspent() -> *mut spwd {
268 let line_reader = unsafe { &mut *LINE_READER.get() };
269 if line_reader.is_none() {
270 unsafe {
271 setspent();
272 }
273 }
274 if let Some(lines) = line_reader
275 && let Some(Ok(line)) = lines.next()
276 && let Ok(sp) = parse_spwd(line, None)
277 {
278 return sp.into_global();
279 }
280 ptr::null_mut()
281}