1use core::cell::SyncUnsafeCell;
11use core::ffi::CStr;
12use core::mem::{align_of, zeroed};
13use core::ptr::{addr_of_mut, copy_nonoverlapping, null, null_mut, write};
14use core::str;
15use core::str::FromStr;
16use errno::{set_errno, Errno};
17use libc::{c_char, c_int, c_void, gid_t, group, passwd, size_t, uid_t};
18use rustix::path::DecInt;
19use std::ffi::OsStr;
20use std::os::unix::ffi::OsStrExt;
21use std::process::Command;
22
23#[no_mangle]
24unsafe extern "C" fn getpwnam_r(
25 name: *const c_char,
26 pwd: *mut passwd,
27 buf: *mut c_char,
28 buflen: usize,
29 result: *mut *mut passwd,
30) -> c_int {
31 libc!(libc::getpwnam_r(name, pwd, buf, buflen, result));
32
33 let name = OsStr::from_bytes(CStr::from_ptr(name).to_bytes());
34 let mut command = Command::new("getent");
35 command.arg("passwd").arg(name);
36 getpw_r(command, pwd, buf, buflen, result)
37}
38
39#[no_mangle]
40unsafe extern "C" fn getpwuid_r(
41 uid: uid_t,
42 pwd: *mut passwd,
43 buf: *mut c_char,
44 buflen: usize,
45 result: *mut *mut passwd,
46) -> c_int {
47 libc!(libc::getpwuid_r(uid, pwd, buf, buflen, result));
48
49 let dec_int = DecInt::new(uid);
50 let name = OsStr::from_bytes(dec_int.as_bytes());
51 let mut command = Command::new("getent");
52 command.arg("passwd").arg(name);
53 getpw_r(command, pwd, buf, buflen, result)
54}
55
56#[no_mangle]
57unsafe extern "C" fn getgrnam_r(
58 name: *const c_char,
59 grp: *mut group,
60 buf: *mut c_char,
61 buflen: usize,
62 result: *mut *mut group,
63) -> c_int {
64 libc!(libc::getgrnam_r(name, grp, buf, buflen, result));
65
66 let name = OsStr::from_bytes(CStr::from_ptr(name).to_bytes());
67 let mut command = Command::new("getent");
68 command.arg("group").arg(name);
69 getgr_r(command, grp, buf, buflen, result)
70}
71
72#[no_mangle]
73unsafe extern "C" fn getgrgid_r(
74 gid: gid_t,
75 grp: *mut group,
76 buf: *mut c_char,
77 buflen: usize,
78 result: *mut *mut group,
79) -> c_int {
80 libc!(libc::getgrgid_r(gid, grp, buf, buflen, result));
81
82 let dec_int = DecInt::new(gid);
83 let name = OsStr::from_bytes(dec_int.as_bytes());
84 let mut command = Command::new("getent");
85 command.arg("group").arg(name);
86 getgr_r(command, grp, buf, buflen, result)
87}
88
89unsafe fn getpw_r(
90 command: Command,
91 pwd: *mut passwd,
92 buf: *mut c_char,
93 buflen: usize,
94 result: *mut *mut passwd,
95) -> c_int {
96 let mut command = command;
97 let output = match command.output() {
98 Ok(output) => output,
99 Err(_err) => return parse_error(result.cast()),
100 };
101
102 match output.status.code() {
103 Some(0) => {}
104 Some(2) => return success(result.cast(), null_mut()),
105 Some(r) => panic!("unexpected exit status from `getent passwd`: {}", r),
106 None => return parse_error(result.cast()),
107 }
108
109 let stdout = match str::from_utf8(&output.stdout) {
110 Ok(stdout) => stdout,
111 Err(_err) => return parse_error(result.cast()),
112 };
113 let stdout = match stdout.strip_suffix('\n') {
114 Some(stdout) => stdout,
115 None => return parse_error(result.cast()),
116 };
117
118 let mut parts = stdout.split(':');
119 let mut buf = buf;
120 let mut buflen = buflen;
121
122 let part = match parts.next() {
123 Some(part) => part,
124 None => return parse_error(result.cast()),
125 };
126 if part.len() > buflen {
127 return buffer_exhausted(result.cast());
128 }
129 let pw_name = buf;
130 copy_nonoverlapping(part.as_ptr(), buf.cast(), part.len());
131 buf = buf.add(part.len());
132 write(buf, 0);
133 buf = buf.add(1);
134 buflen -= part.len() + 1;
135
136 let part = match parts.next() {
137 Some(part) => part,
138 None => return parse_error(result.cast()),
139 };
140 if part.len() > buflen {
141 return buffer_exhausted(result.cast());
142 }
143 let pw_passwd = buf;
144 copy_nonoverlapping(part.as_ptr(), buf.cast(), part.len());
145 buf = buf.add(part.len());
146 write(buf, 0);
147 buf = buf.add(1);
148 buflen -= part.len() + 1;
149
150 let part = match parts.next() {
151 Some(part) => part,
152 None => return parse_error(result.cast()),
153 };
154 let pw_uid = match part.parse() {
155 Ok(pw_uid) => pw_uid,
156 Err(_err) => return parse_error(result.cast()),
157 };
158
159 let part = match parts.next() {
160 Some(part) => part,
161 None => return parse_error(result.cast()),
162 };
163 let pw_gid = match part.parse() {
164 Ok(pw_gid) => pw_gid,
165 Err(_err) => return parse_error(result.cast()),
166 };
167
168 let part = match parts.next() {
169 Some(part) => part,
170 None => return parse_error(result.cast()),
171 };
172 if part.len() > buflen {
173 return buffer_exhausted(result.cast());
174 }
175 let pw_gecos = buf;
176 copy_nonoverlapping(part.as_ptr(), buf.cast(), part.len());
177 buf = buf.add(part.len());
178 write(buf, 0);
179 buf = buf.add(1);
180 buflen -= part.len() + 1;
181
182 let part = match parts.next() {
183 Some(part) => part,
184 None => return parse_error(result.cast()),
185 };
186 if part.len() > buflen {
187 return buffer_exhausted(result.cast());
188 }
189 let pw_dir = buf;
190 copy_nonoverlapping(part.as_ptr(), buf.cast(), part.len());
191 buf = buf.add(part.len());
192 write(buf, 0);
193 buf = buf.add(1);
194 buflen -= part.len() + 1;
195
196 let part = match parts.next() {
197 Some(part) => part,
198 None => return parse_error(result.cast()),
199 };
200 if part.len() > buflen {
201 return buffer_exhausted(result.cast());
202 }
203 let pw_shell = buf;
204 copy_nonoverlapping(part.as_ptr(), buf.cast(), part.len());
205 buf = buf.add(part.len());
206 write(buf, 0);
207
208 write(
209 pwd,
210 passwd {
211 pw_name,
212 pw_passwd,
213 pw_uid,
214 pw_gid,
215 pw_gecos,
216 pw_dir,
217 pw_shell,
218 },
219 );
220 success(result.cast(), pwd.cast())
221}
222
223unsafe fn getgr_r(
224 command: Command,
225 grp: *mut group,
226 buf: *mut c_char,
227 buflen: usize,
228 result: *mut *mut group,
229) -> c_int {
230 let mut command = command;
231 let output = match command.output() {
232 Ok(output) => output,
233 Err(_err) => return parse_error(result.cast()),
234 };
235
236 match output.status.code() {
237 Some(0) => {}
238 Some(2) => return success(result.cast(), null_mut()),
239 Some(r) => panic!("unexpected exit status from `getent group`: {}", r),
240 None => return parse_error(result.cast()),
241 }
242
243 let stdout = match str::from_utf8(&output.stdout) {
244 Ok(stdout) => stdout,
245 Err(_err) => return parse_error(result.cast()),
246 };
247 let stdout = match stdout.strip_suffix('\n') {
248 Some(stdout) => stdout,
249 None => return parse_error(result.cast()),
250 };
251
252 let mut parts = stdout.split(':');
253 let mut buf = buf;
254 let mut buflen = buflen;
255
256 let part = match parts.next() {
257 Some(part) => part,
258 None => return parse_error(result.cast()),
259 };
260 if part.len() > buflen {
261 return buffer_exhausted(result.cast());
262 }
263 let gr_name = buf;
264 copy_nonoverlapping(part.as_ptr(), buf.cast(), part.len());
265 buf = buf.add(part.len());
266 write(buf, 0);
267 buf = buf.add(1);
268 buflen -= part.len() + 1;
269
270 let part = match parts.next() {
271 Some(part) => part,
272 None => return parse_error(result.cast()),
273 };
274 if part.len() > buflen {
275 return buffer_exhausted(result.cast());
276 }
277 let gr_passwd = buf;
278 copy_nonoverlapping(part.as_ptr(), buf.cast(), part.len());
279 buf = buf.add(part.len());
280 write(buf, 0);
281 buf = buf.add(1);
282 buflen -= part.len() + 1;
283
284 let part = match parts.next() {
285 Some(part) => part,
286 None => return parse_error(result.cast()),
287 };
288 let gr_gid = match part.parse() {
289 Ok(pw_gid) => pw_gid,
290 Err(_err) => return parse_error(result.cast()),
291 };
292
293 let part = match parts.next() {
294 Some(part) => part,
295 None => return parse_error(result.cast()),
296 };
297 if part.len() > buflen {
298 return buffer_exhausted(result.cast());
299 }
300
301 let num_members = if part.is_empty() {
302 0
303 } else {
304 part.split(',').count()
305 };
306 let pad = align_of::<*const c_char>() - (buf.addr()) % align_of::<*const c_char>();
307 buf = buf.add(pad);
308 let gr_mem = buf.cast::<*mut c_char>();
309 buf = gr_mem.add(num_members + 1).cast::<c_char>();
310
311 let mut cur_mem = gr_mem;
312 if num_members != 0 {
313 for member in part.split(',') {
314 *cur_mem = buf;
315 cur_mem = cur_mem.add(1);
316 copy_nonoverlapping(member.as_ptr(), buf.cast(), member.len());
317 buf = buf.add(member.len());
318 write(buf, 0);
319 buf = buf.add(1);
320 }
321 }
322 write(cur_mem, null_mut());
323
324 write(
325 grp,
326 group {
327 gr_name,
328 gr_passwd,
329 gr_gid,
330 gr_mem,
331 },
332 );
333 success(result.cast(), grp.cast())
334}
335
336#[cold]
337unsafe fn buffer_exhausted(result: *mut *mut c_void) -> c_int {
338 *result = null_mut();
339 set_errno(Errno(libc::ERANGE));
342 libc::ERANGE
343}
344
345#[cold]
346unsafe fn parse_error(result: *mut *mut c_void) -> c_int {
347 *result = null_mut();
348 set_errno(Errno(libc::EIO));
350 libc::EIO
351}
352
353unsafe fn success(result: *mut *mut c_void, value: *mut c_void) -> c_int {
354 *result = value;
355 set_errno(Errno(0));
358 0
359}
360
361struct StaticPasswd {
362 record: passwd,
363 buf: *mut c_char,
364 len: usize,
365}
366unsafe impl Sync for StaticPasswd {}
369static STATIC_PASSWD: SyncUnsafeCell<StaticPasswd> = SyncUnsafeCell::new(StaticPasswd {
370 record: passwd {
371 pw_name: null_mut(),
372 pw_passwd: null_mut(),
373 pw_uid: 0,
374 pw_gid: 0,
375 pw_gecos: null_mut(),
376 pw_dir: null_mut(),
377 pw_shell: null_mut(),
378 },
379 buf: null_mut(),
380 len: 0,
381});
382
383struct StaticGroup {
384 record: group,
385 buf: *mut c_char,
386 len: usize,
387}
388unsafe impl Sync for StaticGroup {}
391static STATIC_GROUP: SyncUnsafeCell<StaticGroup> = SyncUnsafeCell::new(StaticGroup {
392 record: group {
393 gr_name: null_mut(),
394 gr_passwd: null_mut(),
395 gr_gid: 0,
396 gr_mem: null_mut(),
397 },
398 buf: null_mut(),
399 len: 0,
400});
401
402#[no_mangle]
403unsafe extern "C" fn getpwnam(name: *const c_char) -> *mut libc::passwd {
404 libc!(libc::getpwnam(name));
405
406 let static_passwd = &mut *STATIC_PASSWD.get();
407 let mut ptr: *mut libc::passwd = &mut static_passwd.record;
408
409 loop {
410 if static_passwd.len == 0 {
411 static_passwd.len = 1024;
412 } else {
413 static_passwd.len *= 2;
414 libc::free(static_passwd.buf.cast());
415 }
416
417 static_passwd.buf = libc::malloc(static_passwd.len).cast();
418 if static_passwd.buf.is_null() {
419 static_passwd.len = 0;
420 set_errno(Errno(libc::ENOMEM));
421 return null_mut();
422 }
423
424 let r = getpwnam_r(
425 name,
426 &mut static_passwd.record,
427 static_passwd.buf,
428 static_passwd.len,
429 &mut ptr,
430 );
431 if r == 0 {
432 return ptr;
433 }
434 if r != libc::ERANGE {
435 return null_mut();
436 }
437 }
438}
439
440#[no_mangle]
441unsafe extern "C" fn getpwuid(uid: uid_t) -> *mut libc::passwd {
442 libc!(libc::getpwuid(uid));
443
444 let static_passwd = &mut *STATIC_PASSWD.get();
445 let mut ptr: *mut libc::passwd = &mut static_passwd.record;
446
447 loop {
448 if static_passwd.len == 0 {
449 static_passwd.len = 1024;
450 } else {
451 static_passwd.len *= 2;
452 libc::free(static_passwd.buf.cast());
453 }
454
455 static_passwd.buf = libc::malloc(static_passwd.len).cast();
456 if static_passwd.buf.is_null() {
457 static_passwd.len = 0;
458 set_errno(Errno(libc::ENOMEM));
459 return null_mut();
460 }
461
462 let r = getpwuid_r(
463 uid,
464 &mut static_passwd.record,
465 static_passwd.buf,
466 static_passwd.len,
467 &mut ptr,
468 );
469 if r == 0 {
470 return ptr;
471 }
472 if r != libc::ERANGE {
473 return null_mut();
474 }
475 }
476}
477
478#[no_mangle]
479unsafe extern "C" fn getgrnam(name: *const c_char) -> *mut libc::group {
480 libc!(libc::getgrnam(name));
481
482 let static_group = &mut *STATIC_GROUP.get();
483 let mut ptr: *mut libc::group = &mut static_group.record;
484
485 loop {
486 if static_group.len == 0 {
487 static_group.len = 1024;
488 } else {
489 static_group.len *= 2;
490 libc::free(static_group.buf.cast());
491 }
492
493 static_group.buf = libc::malloc(static_group.len).cast();
494 if static_group.buf.is_null() {
495 static_group.len = 0;
496 set_errno(Errno(libc::ENOMEM));
497 return null_mut();
498 }
499
500 let r = getgrnam_r(
501 name,
502 &mut static_group.record,
503 static_group.buf,
504 static_group.len,
505 &mut ptr,
506 );
507 if r == 0 {
508 return ptr;
509 }
510 if r != libc::ERANGE {
511 return null_mut();
512 }
513 }
514}
515
516#[no_mangle]
517unsafe extern "C" fn getgrgid(gid: gid_t) -> *mut libc::group {
518 libc!(libc::getgrgid(gid));
519
520 let static_group = &mut *STATIC_GROUP.get();
521 let mut ptr: *mut libc::group = &mut static_group.record;
522
523 loop {
524 if static_group.len == 0 {
525 static_group.len = 1024;
526 } else {
527 static_group.len *= 2;
528 libc::free(static_group.buf.cast());
529 }
530
531 static_group.buf = libc::malloc(static_group.len).cast();
532 if static_group.buf.is_null() {
533 static_group.len = 0;
534 set_errno(Errno(libc::ENOMEM));
535 return null_mut();
536 }
537
538 let r = getgrgid_r(
539 gid,
540 &mut static_group.record,
541 static_group.buf,
542 static_group.len,
543 &mut ptr,
544 );
545 if r == 0 {
546 return ptr;
547 }
548 if r != libc::ERANGE {
549 return null_mut();
550 }
551 }
552}
553
554#[no_mangle]
555unsafe extern "C" fn getgrouplist(
556 user: *const c_char,
557 group: gid_t,
558 groups: *mut gid_t,
559 ngroups: *mut c_int,
560) -> c_int {
561 libc!(libc::getgrouplist(user, group, groups, ngroups));
562
563 let user = OsStr::from_bytes(CStr::from_ptr(user).to_bytes());
564 let mut groups = groups;
565
566 let mut command = Command::new("getent");
567 command.arg("initgroups").arg(user);
568
569 let output = match command.output() {
570 Ok(output) => output,
571 Err(_err) => return -1,
572 };
573
574 match output.status.code() {
575 Some(0) => {}
576 Some(r) => panic!("unexpected exit status from `getent initgroups`: {}", r),
577 None => return -1,
578 }
579
580 let stdout = match str::from_utf8(&output.stdout) {
581 Ok(stdout) => stdout,
582 Err(_err) => return -1,
583 };
584 let stdout = match stdout.strip_suffix('\n') {
585 Some(stdout) => stdout,
586 None => return -1,
587 };
588
589 let mut parts = stdout.split_ascii_whitespace();
590 match parts.next() {
591 Some(part) => {
592 if part != user {
593 return -1;
594 }
595 }
596 None => return -1,
597 };
598
599 let ngroups_in = ngroups.read();
600 let mut ngroups_out = 0;
601
602 if ngroups_out == ngroups_in {
603 return -1;
604 }
605 ngroups_out += 1;
606 groups.write(group);
607 groups = groups.add(1);
608
609 for part in parts {
610 let gid: u32 = match part.parse() {
611 Ok(gid) => gid,
612 Err(_) => return -1,
613 };
614 if gid == group {
615 continue;
616 }
617 if ngroups_out == ngroups_in {
618 return -1;
619 }
620 ngroups_out += 1;
621 groups.write(gid);
622 groups = groups.add(1);
623 }
624
625 ngroups.write(ngroups_out);
626 ngroups_out
627}
628
629#[no_mangle]
630unsafe extern "C" fn getservbyport_r(
631 port: c_int,
632 proto: *const c_char,
633 result_buf: *mut libc::servent,
634 buf: *mut c_char,
635 buflen: size_t,
636 result: *mut *mut libc::servent,
637) -> c_int {
638 let mut command = Command::new("getent");
643 command
644 .arg("services")
645 .arg(DecInt::new(u16::from_be(port as u16)).as_ref());
646 getserv_r(command, null(), proto, result_buf, buf, buflen, result)
647}
648
649#[no_mangle]
650unsafe extern "C" fn getservbyname_r(
651 name: *const c_char,
652 proto: *const c_char,
653 result_buf: *mut libc::servent,
654 buf: *mut c_char,
655 buflen: size_t,
656 result: *mut *mut libc::servent,
657) -> c_int {
658 let arg_name = OsStr::from_bytes(CStr::from_ptr(name).to_bytes());
663 let mut command = Command::new("getent");
664 command.arg("services").arg(arg_name);
665 getserv_r(command, name, proto, result_buf, buf, buflen, result)
666}
667
668unsafe fn getserv_r(
669 command: Command,
670 name: *const c_char,
671 proto: *const c_char,
672 result_buf: *mut libc::servent,
673 buf: *mut c_char,
674 buflen: size_t,
675 result: *mut *mut libc::servent,
676) -> c_int {
677 static mut STATIC_SERVENT_ALIASES: *mut c_char = null_mut();
681 let s_aliases = &mut *addr_of_mut!(STATIC_SERVENT_ALIASES);
682
683 let mut command = command;
684 let output = match command.output() {
685 Ok(output) => output,
686 Err(_err) => {
687 *result = null_mut();
688 return libc::EIO;
689 }
690 };
691
692 match output.status.code() {
693 Some(0) => {}
694 Some(2) => {
695 *result = null_mut();
696 return libc::ENOENT;
697 }
698 Some(r) => panic!("unexpected exit status from `getent services`: {}", r),
699 None => {
700 *result = null_mut();
701 return libc::EIO;
702 }
703 }
704
705 let stdout = match str::from_utf8(&output.stdout) {
706 Ok(stdout) => stdout,
707 Err(_err) => {
708 *result = null_mut();
709 return libc::EIO;
710 }
711 };
712 let stdout = match stdout.strip_suffix('\n') {
713 Some(stdout) => stdout,
714 None => {
715 *result = null_mut();
716 return libc::EIO;
717 }
718 };
719
720 let mut parts = stdout.split_ascii_whitespace();
722
723 let s_name = match parts.next() {
724 Some(check_name) => {
725 if name.is_null() {
726 if check_name.len() > buflen {
727 return libc::ERANGE;
728 }
729 copy_nonoverlapping(check_name.as_ptr(), buf.cast(), check_name.len());
730 buf.add(check_name.len()).write(0);
731 buf
732 } else {
733 name.cast_mut()
734 }
735 }
736 None => {
737 *result = null_mut();
738 return libc::EIO;
739 }
740 };
741
742 let port_protocol = match parts.next() {
743 Some(port_protocol) => port_protocol,
744 None => {
745 *result = null_mut();
746 return libc::EIO;
747 }
748 };
749
750 let (port, protocol) = match port_protocol.split_once('/') {
755 Some(port_protocol) => port_protocol,
756 None => {
757 *result = null_mut();
758 return libc::EIO;
759 }
760 };
761
762 let s_port: i32 = match u16::from_str(port) {
764 Ok(port) => port.to_be().into(),
765 Err(_) => {
766 *result = null_mut();
767 return libc::EIO;
768 }
769 };
770
771 let s_proto = if !proto.is_null() {
774 if protocol.as_bytes() != CStr::from_ptr(proto).to_bytes() {
775 *result = null_mut();
776 return libc::EIO;
777 }
778 proto
779 } else if protocol == "tcp" {
780 c"tcp".as_ptr()
781 } else if protocol == "udp" {
782 c"udp".as_ptr()
783 } else {
784 return libc::EINVAL;
785 }
786 .cast_mut();
787
788 *result_buf = libc::servent {
789 s_name,
790 s_aliases,
791 s_port,
792 s_proto,
793 };
794 *result = result_buf;
795 0
796}
797
798static mut STATIC_SERVENT: libc::servent = unsafe { zeroed() };
801
802#[no_mangle]
803unsafe extern "C" fn getservbyname(
804 name: *const c_char,
805 proto: *const c_char,
806) -> *mut libc::servent {
807 libc!(libc::getservbyname(name, proto));
808
809 let mut result = null_mut();
810 if getservbyname_r(
811 name,
812 proto,
813 addr_of_mut!(STATIC_SERVENT),
814 null_mut(),
815 0,
816 &mut result,
817 ) == 0
818 {
819 result
820 } else {
821 null_mut()
822 }
823}
824
825#[no_mangle]
826unsafe extern "C" fn getservbyport(port: c_int, proto: *const c_char) -> *mut libc::servent {
827 libc!(libc::getservbyport(port, proto));
828
829 let mut buf = [0; 32];
830 let mut result = null_mut();
831 if getservbyport_r(
832 port,
833 proto,
834 addr_of_mut!(STATIC_SERVENT),
835 buf.as_mut_ptr(),
836 buf.len(),
837 &mut result,
838 ) == 0
839 {
840 result
841 } else {
842 null_mut()
843 }
844}