xref: /relibc/src/header/pwd/mod.rs (revision a2f2484e4559206922b0fcc16217598a6867c8a2)
1 //! pwd implementation for relibc
2 
3 use alloc::{boxed::Box, vec::Vec};
4 use core::{
5     ops::{Deref, DerefMut},
6     pin::Pin,
7     ptr,
8 };
9 
10 use crate::{
11     fs::File,
12     header::{errno, fcntl, string::strcmp},
13     io::{prelude::*, BufReader, SeekFrom},
14     platform::{self, types::*},
15 };
16 
17 #[cfg(target_os = "linux")]
18 mod linux;
19 #[cfg(target_os = "redox")]
20 mod redox;
21 
22 #[cfg(target_os = "linux")]
23 use self::linux as sys;
24 #[cfg(target_os = "redox")]
25 use self::redox as sys;
26 
27 #[repr(C)]
28 #[derive(Debug)]
29 pub struct passwd {
30     pw_name: *mut c_char,
31     pw_passwd: *mut c_char,
32     pw_uid: uid_t,
33     pw_gid: gid_t,
34     pw_gecos: *mut c_char,
35     pw_dir: *mut c_char,
36     pw_shell: *mut c_char,
37 }
38 
39 static mut PASSWD_BUF: Option<MaybeAllocated> = None;
40 static mut PASSWD: passwd = passwd {
41     pw_name: ptr::null_mut(),
42     pw_passwd: ptr::null_mut(),
43     pw_uid: 0,
44     pw_gid: 0,
45     pw_gecos: ptr::null_mut(),
46     pw_dir: ptr::null_mut(),
47     pw_shell: ptr::null_mut(),
48 };
49 
50 #[derive(Clone, Copy, Debug)]
51 struct DestBuffer {
52     ptr: *mut u8,
53     len: usize,
54 }
55 
56 #[derive(Debug)]
57 enum MaybeAllocated {
58     Owned(Pin<Box<[u8]>>),
59     Borrowed(DestBuffer),
60 }
61 impl Deref for MaybeAllocated {
62     type Target = [u8];
63 
64     fn deref(&self) -> &Self::Target {
65         match self {
66             MaybeAllocated::Owned(boxed) => boxed,
67             MaybeAllocated::Borrowed(dst) => unsafe {
68                 core::slice::from_raw_parts(dst.ptr, dst.len)
69             },
70         }
71     }
72 }
73 impl DerefMut for MaybeAllocated {
74     fn deref_mut(&mut self) -> &mut Self::Target {
75         match self {
76             MaybeAllocated::Owned(boxed) => boxed,
77             MaybeAllocated::Borrowed(dst) => unsafe {
78                 core::slice::from_raw_parts_mut(dst.ptr, dst.len)
79             },
80         }
81     }
82 }
83 
84 #[derive(Debug)]
85 struct OwnedPwd {
86     buffer: MaybeAllocated,
87     reference: passwd,
88 }
89 
90 impl OwnedPwd {
91     fn into_global(self) -> *mut passwd {
92         unsafe {
93             PASSWD_BUF = Some(self.buffer);
94             PASSWD = self.reference;
95             &mut PASSWD
96         }
97     }
98 }
99 
100 #[derive(Clone, Copy, Debug)]
101 enum Cause {
102     Eof,
103     Other,
104 }
105 
106 static mut READER: Option<BufReader<File>> = None;
107 
108 fn parsed<I, O>(buf: Option<I>) -> Option<O>
109 where
110     I: core::borrow::Borrow<[u8]>,
111     O: core::str::FromStr,
112 {
113     let buf = buf?;
114     let string = core::str::from_utf8(buf.borrow()).ok()?;
115     string.parse().ok()
116 }
117 
118 fn getpwent_r(
119     reader: &mut BufReader<File>,
120     destination: Option<DestBuffer>,
121 ) -> Result<OwnedPwd, Cause> {
122     let mut buf = Vec::new();
123     if reader
124         .read_until(b'\n', &mut buf)
125         .map_err(|_| Cause::Other)?
126         == 0
127     {
128         return Err(Cause::Eof);
129     }
130 
131     // Replace all occurences of ':' with terminating NUL byte
132     let mut start = 0;
133     while let Some(i) = memchr::memchr(b':', &buf[start..]) {
134         buf[start + i] = 0;
135         start += i + 1;
136     }
137 
138     // Place terminating NUL byte at the end, replace newline
139     let last = buf.last_mut();
140     if last == Some(&mut b'\n') {
141         *last.unwrap() = 0;
142     } else {
143         buf.push(0);
144     }
145 
146     let mut buf = match destination {
147         None => MaybeAllocated::Owned(Box::into_pin(buf.into_boxed_slice())),
148         Some(dst) => {
149             let mut new = MaybeAllocated::Borrowed(dst);
150             if new.len() < buf.len() {
151                 unsafe {
152                     platform::errno = errno::ERANGE;
153                 }
154                 return Err(Cause::Other);
155             }
156             new[..buf.len()].copy_from_slice(&buf);
157             new
158         }
159     };
160 
161     // Chop up the result into a valid structure
162     let passwd = sys::split(&mut buf).ok_or(Cause::Other)?;
163 
164     Ok(OwnedPwd {
165         buffer: buf,
166         reference: passwd,
167     })
168 }
169 
170 fn pwd_lookup<F>(mut matches: F, destination: Option<DestBuffer>) -> Result<OwnedPwd, Cause>
171 where
172     F: FnMut(&passwd) -> bool,
173 {
174     let file = match File::open(c_str!("/etc/passwd"), fcntl::O_RDONLY) {
175         Ok(file) => file,
176         Err(_) => return Err(Cause::Other),
177     };
178 
179     let mut reader = BufReader::new(file);
180 
181     loop {
182         let entry = getpwent_r(&mut reader, destination)?;
183 
184         if matches(&entry.reference) {
185             return Ok(entry);
186         }
187     }
188 }
189 
190 unsafe fn mux(
191     status: Result<OwnedPwd, Cause>,
192     out: *mut passwd,
193     result: *mut *mut passwd,
194 ) -> c_int {
195     match status {
196         Ok(owned) => {
197             *out = owned.reference;
198             *result = out;
199             0
200         }
201         Err(Cause::Eof) => {
202             *result = ptr::null_mut();
203             0
204         }
205         Err(Cause::Other) => {
206             *result = ptr::null_mut();
207             -1
208         }
209     }
210 }
211 
212 #[no_mangle]
213 pub unsafe extern "C" fn getpwnam_r(
214     name: *const c_char,
215     out: *mut passwd,
216     buf: *mut c_char,
217     size: size_t,
218     result: *mut *mut passwd,
219 ) -> c_int {
220     mux(
221         pwd_lookup(
222             |parts| strcmp(parts.pw_name, name) == 0,
223             Some(DestBuffer {
224                 ptr: buf as *mut u8,
225                 len: size,
226             }),
227         ),
228         out,
229         result,
230     )
231 }
232 
233 #[no_mangle]
234 pub unsafe extern "C" fn getpwuid_r(
235     uid: uid_t,
236     out: *mut passwd,
237     buf: *mut c_char,
238     size: size_t,
239     result: *mut *mut passwd,
240 ) -> c_int {
241     let slice = core::slice::from_raw_parts_mut(buf as *mut u8, size);
242     mux(
243         pwd_lookup(
244             |part| part.pw_uid == uid,
245             Some(DestBuffer {
246                 ptr: buf as *mut u8,
247                 len: size,
248             }),
249         ),
250         out,
251         result,
252     )
253 }
254 
255 #[no_mangle]
256 pub extern "C" fn getpwnam(name: *const c_char) -> *mut passwd {
257     pwd_lookup(|parts| unsafe { strcmp(parts.pw_name, name) } == 0, None)
258         .map(|res| res.into_global())
259         .unwrap_or(ptr::null_mut())
260 }
261 
262 #[no_mangle]
263 pub extern "C" fn getpwuid(uid: uid_t) -> *mut passwd {
264     pwd_lookup(|parts| parts.pw_uid == uid, None)
265         .map(|res| res.into_global())
266         .unwrap_or(ptr::null_mut())
267 }
268 
269 #[no_mangle]
270 pub extern "C" fn getpwent() -> *mut passwd {
271     let reader = match unsafe { &mut READER } {
272         Some(reader) => reader,
273         None => {
274             let file = match File::open(c_str!("/etc/passwd"), fcntl::O_RDONLY) {
275                 Ok(file) => file,
276                 Err(_) => return ptr::null_mut(),
277             };
278             let reader = BufReader::new(file);
279             unsafe {
280                 READER = Some(reader);
281                 READER.as_mut().unwrap()
282             }
283         }
284     };
285     getpwent_r(reader, None)
286         .map(|res| res.into_global())
287         .unwrap_or(ptr::null_mut())
288 }
289 
290 #[no_mangle]
291 pub extern "C" fn setpwent() {
292     if let Some(reader) = unsafe { &mut READER } {
293         let _ = reader.seek(SeekFrom::Start(0));
294     }
295 }
296 
297 #[no_mangle]
298 pub extern "C" fn endpwent() {
299     unsafe {
300         READER = None;
301     }
302 }
303