1#![allow(unused_imports)]
7
8use std::{
9 fs::File,
10 io::{BufRead, BufReader, Read},
11 mem::{ManuallyDrop, MaybeUninit},
12 process::{Command, Stdio},
13 ptr::{null, null_mut},
14 str::FromStr,
15 string::String,
16 vec::Vec,
17 ffi::CString,
18};
19
20use super::errno;
21
22#[cfg(target_os = "linux")]
23use libc::{gid_t, uid_t, sysconf, getpwnam_r, getgrnam_r, getpwuid_r, getgrgid_r, getgrouplist, strlen};
24
25#[cfg(target_os = "windows")]
26use winapi::{
27 um::{
28 wincred::NERR_BASE,
29 lmaccess::{
30 NetUserGetLocalGroups,
31 NetGroupGetInfo,
32 LPLOCALGROUP_USERS_INFO_0,
33 LOCALGROUP_USERS_INFO_0,
34 LPUSER_INFO_0,
35 LPGROUP_INFO_0
36 },
37 lmapibuf::NetApiBufferFree
38 },
39 ctypes::c_void
40};
41
42#[cfg(target_os = "linux")]
44#[derive(Clone)]
45pub struct GroupEntry {
46 pub groupname: String,
47 pub gid: gid_t,
48 pub list: Vec<String>,
49}
50
51#[cfg(target_os = "linux")]
53#[derive(Clone)]
54pub struct PasswdEntry {
55 pub username: String,
56 pub password_in_shadow: bool,
57 pub uid: uid_t,
58 pub gid: gid_t,
59 pub gecos: String,
60 pub home_dir: String,
61 pub shell: String,
62}
63
64#[cfg(target_os = "linux")]
65impl PasswdEntry {
66 pub fn parse_entry<T: ToString>(entry: &T) -> PasswdEntry {
68 let entry_str = entry.to_string();
69 let tokenized_entry: Vec<_> = entry_str.split(':').collect();
70
71 PasswdEntry {
72 username: tokenized_entry[0].to_string(),
73 password_in_shadow: tokenized_entry[1] == "x",
74 uid: tokenized_entry[2].parse::<uid_t>().unwrap(),
75 gid: tokenized_entry[3].parse::<gid_t>().unwrap(),
76 gecos: tokenized_entry[4].to_string(),
77 home_dir: tokenized_entry[5].to_string(),
78 shell: tokenized_entry[6].to_string(),
79 }
80 }
81
82 pub fn get_entry_from_passwd<T: ToString>(name: &T) -> Result<PasswdEntry, i32> {
84 let username = match CString::new(name.to_string()) {
85 Ok(s) => s,
86 _ => return Err(-1),
87 };
88
89 unsafe {
90 let mut pass = MaybeUninit::zeroed().assume_init();
91 let mut pass_ptr = MaybeUninit::zeroed().assume_init();
92 let mut buf = vec![0i8; sysconf(libc::_SC_GETPW_R_SIZE_MAX) as usize];
93 let mut res ;
94 let tmp;
95 res = getpwnam_r(username.as_ptr() as *const i8, &mut pass, buf.as_mut_ptr(), buf.len(), &mut pass_ptr);
96
97 while res != 0 && errno() == libc::ERANGE {
98 let mut nb = vec![0i8; sysconf(libc::_SC_GETPW_R_SIZE_MAX) as usize];
99 buf.append(&mut nb);
100 res = getpwnam_r(username.as_ptr() as *const i8, &mut pass, buf.as_mut_ptr(), buf.len(), &mut pass_ptr);
101 }
102
103 if res != 0 {
104 return Err(errno());
105 }
106
107 tmp = pass_ptr.as_mut().unwrap();
108
109 Ok(PasswdEntry {
110 username: String::from_raw_parts(tmp.pw_name as *mut u8, libc::strlen(tmp.pw_name), libc::strlen(tmp.pw_name)),
111 uid: tmp.pw_uid,
112 gid: tmp.pw_gid,
113 password_in_shadow: *tmp.pw_passwd == 'x' as i8,
114 gecos: String::from_raw_parts(tmp.pw_gecos as *mut u8, libc::strlen(tmp.pw_gecos), libc::strlen(tmp.pw_gecos)),
115 home_dir: String::from_raw_parts(tmp.pw_dir as *mut u8, libc::strlen(tmp.pw_dir), libc::strlen(tmp.pw_dir)),
116 shell: String::from_raw_parts(tmp.pw_shell as *mut u8, libc::strlen(tmp.pw_shell), libc::strlen(tmp.pw_shell)),
117 }.clone())
118 }
119 }
120
121 pub fn get_entry_from_passwd_by_uid(uid: uid_t) -> Result<PasswdEntry, i32> {
123 unsafe {
124 let mut pass = MaybeUninit::zeroed().assume_init();
125 let mut pass_ptr = MaybeUninit::zeroed().assume_init();
126 let mut buf = vec![0i8; sysconf(libc::_SC_GETPW_R_SIZE_MAX) as usize];
127 let mut res;
128 let tmp;
129 res = getpwuid_r(uid, &mut pass, buf.as_mut_ptr(), buf.len(), &mut pass_ptr);
130
131 while res != 0 && errno() == libc::ERANGE {
132 let mut nb = vec![0i8; sysconf(libc::_SC_GETPW_R_SIZE_MAX) as usize];
133 buf.append(&mut nb);
134 res = getpwuid_r(uid, &mut pass, buf.as_mut_ptr(), buf.len(), &mut pass_ptr);
135 }
136
137 if res != 0 {
138 return Err(errno());
139 }
140
141 tmp = pass_ptr.as_mut().unwrap();
142
143 Ok(PasswdEntry {
144 username: String::from_raw_parts(tmp.pw_name as *mut u8, libc::strlen(tmp.pw_name), libc::strlen(tmp.pw_name)),
145 uid: tmp.pw_uid,
146 gid: tmp.pw_gid,
147 password_in_shadow: *tmp.pw_passwd == 'x' as i8,
148 gecos: String::from_raw_parts(tmp.pw_gecos as *mut u8, libc::strlen(tmp.pw_gecos), libc::strlen(tmp.pw_gecos)),
149 home_dir: String::from_raw_parts(tmp.pw_dir as *mut u8, libc::strlen(tmp.pw_dir), libc::strlen(tmp.pw_dir)),
150 shell: String::from_raw_parts(tmp.pw_shell as *mut u8, libc::strlen(tmp.pw_shell), libc::strlen(tmp.pw_shell)),
151 }.clone())
152 }
153 }
154}
155
156#[cfg(target_os = "linux")]
157impl GroupEntry {
158 pub fn get_entry_from_group<T: ToString>(name: &T) -> Result<GroupEntry, i32> {
160 let groupname = match CString::new(name.to_string()) {
161 Ok(s) => s,
162 _ => return Err(-1),
163 };
164
165 unsafe {
166 let mut pass = MaybeUninit::zeroed().assume_init();
167 let mut pass_ptr = MaybeUninit::zeroed().assume_init();
168 let mut buf = vec![0i8; sysconf(libc::_SC_GETPW_R_SIZE_MAX) as usize];
169 let mut res ;
170 let mut ret;
171 let tmp;
172 res = getgrnam_r(groupname.as_ptr() as *const i8, &mut pass, buf.as_mut_ptr(), buf.len(), &mut pass_ptr);
173
174 while res != 0 && errno() == libc::ERANGE {
175 let mut nb = vec![0i8; sysconf(libc::_SC_GETPW_R_SIZE_MAX) as usize];
176 buf.append(&mut nb);
177 res = getgrnam_r(groupname.as_ptr() as *const i8, &mut pass, buf.as_mut_ptr(), buf.len(), &mut pass_ptr);
178 }
179
180 if res != 0 {
181 return Err(errno());
182 }
183
184 tmp = pass_ptr.as_mut().unwrap();
185
186 ret = GroupEntry {
187 groupname: String::from_raw_parts(tmp.gr_name as *mut u8, libc::strlen(tmp.gr_name), libc::strlen(tmp.gr_name)),
188 gid: tmp.gr_gid,
189 list: Vec::new(),
190 };
191
192 let mut i = 0;
193 while tmp.gr_mem.offset(i).read() != null_mut() {
194 let tmp_tmp = tmp.gr_mem.offset(i).read() as *mut i8;
195 ret.list.push(String::from_raw_parts(tmp_tmp as *mut u8, libc::strlen(tmp_tmp), libc::strlen(tmp_tmp)));
196 i += 1;
197 }
198
199 Ok(ret.clone())
200 }
201 }
202
203 pub fn get_entry_by_gid(gid: gid_t) -> Result<GroupEntry, i32> {
205 unsafe {
206 let mut pass = MaybeUninit::zeroed().assume_init();
207 let mut pass_ptr = MaybeUninit::zeroed().assume_init();
208 let mut buf = vec![0i8; sysconf(libc::_SC_GETPW_R_SIZE_MAX) as usize];
209 let mut res ;
210 let mut ret;
211 let tmp;
212 res = getgrgid_r(gid, &mut pass, buf.as_mut_ptr(), buf.len(), &mut pass_ptr);
213
214 while res != 0 && errno() == libc::ERANGE {
215 let mut nb = vec![0i8; sysconf(libc::_SC_GETPW_R_SIZE_MAX) as usize];
216 buf.append(&mut nb);
217 res = getgrgid_r(gid, &mut pass, buf.as_mut_ptr(), buf.len(), &mut pass_ptr);
218 }
219
220 if res != 0 {
221 return Err(errno());
222 }
223
224 tmp = pass_ptr.as_mut().unwrap();
225
226 ret = GroupEntry {
227 groupname: String::from_raw_parts(tmp.gr_name as *mut u8, libc::strlen(tmp.gr_name), libc::strlen(tmp.gr_name)),
228 gid: tmp.gr_gid,
229 list: Vec::new(),
230 };
231
232 let mut i = 0;
233 while tmp.gr_mem.offset(i).read() != null_mut() {
234 let tmp_tmp = tmp.gr_mem.offset(i).read() as *mut i8;
235 ret.list.push(String::from_raw_parts(tmp_tmp as *mut u8, libc::strlen(tmp_tmp), libc::strlen(tmp_tmp)));
236 i += 1;
237 }
238
239 Ok(ret.clone())
240 }
241 }
242}
243
244pub fn user_exists<T: ToString>(n: &T) -> Result<bool, i32> {
246 let name = n.to_string();
247 #[cfg(target_os = "linux")]
248 unsafe {
249 let username = match CString::new(name) {
250 Ok(s) => s,
251 _ => return Err(-1),
252 };
253
254 let mut pass = MaybeUninit::zeroed().assume_init();
255 let mut pass_ptr = MaybeUninit::zeroed().assume_init();
256 let mut buf = vec![0i8; sysconf(libc::_SC_GETPW_R_SIZE_MAX) as usize];
257 let mut res ;
258 res = getpwnam_r(username.as_ptr() as *const i8, &mut pass, buf.as_mut_ptr(), buf.len(), &mut pass_ptr);
259
260 while res != 0 && errno() == libc::ERANGE {
261 let mut nb = vec![0i8; sysconf(libc::_SC_GETPW_R_SIZE_MAX) as usize];
262 buf.append(&mut nb);
263 res = getpwnam_r(username.as_ptr() as *const i8, &mut pass, buf.as_mut_ptr(), buf.len(), &mut pass_ptr);
264 }
265
266 if res == 0 {
267 Ok(true)
268 } else {
269 match errno() {
270 0 | libc::ENOENT | libc::ESRCH | libc::EBADF | libc::EPERM => Ok(false),
271 _ => Err(errno()),
272 }
273 }
274 }
275 #[cfg(target_os = "windows")]
276 unsafe {
277 let mut user: LPUSER_INFO_0 = null_mut();
278 let uname_utf16 = name.encode_utf16().collect::<Vec<u16>>();
279
280 match NetGroupGetInfo(null(), uname_utf16.as_ptr(), 0, *&mut user as *mut *mut u8) {
281 0 => { NetApiBufferFree(user as *mut c_void); Ok(true) },
282 2220 => Ok(false), _ => Err(errno()),
284 }
285 }
286}
287
288pub fn group_exists<T: ToString>(n: &T) -> Result<bool, i32> {
290 let name = n.to_string();
291 #[cfg(target_os = "linux")]
292 unsafe {
293 let groupname = match CString::new(name) {
294 Ok(s) => s,
295 _ => return Err(-1),
296 };
297
298 let mut pass = MaybeUninit::zeroed().assume_init();
299 let mut pass_ptr = MaybeUninit::zeroed().assume_init();
300 let mut buf = vec![0i8; sysconf(libc::_SC_GETPW_R_SIZE_MAX) as usize];
301 let mut res ;
302 res = getgrnam_r(groupname.as_ptr() as *const i8, &mut pass, buf.as_mut_ptr(), buf.len(), &mut pass_ptr);
303
304 while res != 0 && errno() == libc::ERANGE {
305 let mut nb = vec![0i8; sysconf(libc::_SC_GETPW_R_SIZE_MAX) as usize];
306 buf.append(&mut nb);
307 res = getgrnam_r(groupname.as_ptr() as *const i8, &mut pass, buf.as_mut_ptr(), buf.len(), &mut pass_ptr);
308 }
309
310 if res == 0 {
311 Ok(true)
312 } else {
313 match errno() {
314 0 | libc::ENOENT | libc::ESRCH | libc::EBADF | libc::EPERM => Ok(false),
315 _ => Err(errno()),
316 }
317 }
318 }
319 #[cfg(target_os = "windows")]
320 unsafe {
321 let mut group: LPGROUP_INFO_0 = null_mut();
322 let gname_utf16 = name.encode_utf16().collect::<Vec<u16>>();
323
324 match NetGroupGetInfo(null(), gname_utf16.as_ptr(), 0, *&mut group as *mut *mut u8) {
325 0 => { NetApiBufferFree(group as *mut c_void); Ok(true) },
326 2220 => Ok(false),
327 _ => Err(errno()),
328 }
329 }
330}
331
332pub fn user_is_in_group<A: ToString, B: ToString>(u: &A, g: &B) -> Result<bool, i32> {
337 user_exists(u)?;
338 group_exists(g)?;
339 #[cfg(target_os = "linux")]
340 {
341 let group = GroupEntry::get_entry_from_group(g)?;
342 Ok(group.list.contains(&g.to_string()))
343 }
344 #[cfg(target_os = "windows")]
345 unsafe {
346 let uname = u.to_string();
347 let gname = g.to_string();
348 let mut groups: LPLOCALGROUP_USERS_INFO_0 = null_mut();
349 let mut uname_utf16 = uname.encode_utf16().collect::<Vec<u16>>();
350 let mut gname_utf16 = gname.encode_utf16().collect::<Vec<u16>>();
351 let mut num_entries = 0;
352 let mut tmp = 0;
353 uname_utf16.push(0);
354 gname_utf16.push(0);
355
356 let status = NetUserGetLocalGroups(null(), uname_utf16.as_ptr(), 0, 1, *&mut groups as *mut *mut u8, u32::MAX, &mut num_entries, &mut tmp);
357 let entries = ManuallyDrop::new(Vec::<LOCALGROUP_USERS_INFO_0>::from_raw_parts(groups, num_entries as usize, num_entries as usize));
358
359 if status == 0 {
360 let mut strlen = 0;
361 for c in gname_utf16.iter() {
362 if *c == 0 {
363 break;
364 }
365 strlen += 1;
366 }
367
368 'groups: for raw_entry in (*entries).iter() {
369 let mut i = 0;
370 while *raw_entry.lgrui0_name.offset(i) != 0 {
371 i += 1;
372 }
373
374 let entry = ManuallyDrop::new(Vec::from_raw_parts(raw_entry.lgrui0_name, i as usize, i as usize));
375 if i != strlen {
376 continue;
377 }
378
379 for j in 0..i {
380 if entry[j as usize] != gname_utf16[j as usize] {
381 continue 'groups;
382 }
383 }
384
385 NetApiBufferFree(groups as *mut c_void);
386 return Ok(true);
387 }
388
389 NetApiBufferFree(groups as *mut c_void);
390 return Ok(false);
391 } else {
392 Err(errno())
393 }
394 }
395}
396
397pub fn user_is_admin<T: ToString>(name: &T) -> Result<bool, i32> {
405
406 #[cfg(target_os = "linux")]
407 {
408 if name.to_string() == "root" {
409 Ok(true)
410 } else {
411 if user_exists(name).unwrap_or(false) {
412 let cmd = Command::new("sudo")
413 .args(&["-l", "-U", &format!("{}", name.to_string())]).stderr(Stdio::null()).stdout(Stdio::piped())
414 .output().expect("Sudo failed to run.");
415 let mensaje = format!("User {} is not allowed to run sudo", name.to_string());
416
417 Ok(!String::from_utf8_lossy(&cmd.stdout).contains(&mensaje))
418 } else {
419 Err(errno())
420 }
421 }
422 }
423 #[cfg(target_os = "windows")]
424 {
425 user_is_in_group(name, &"Administrators")
426 }
427}