1 //! Integration with the C library’s users and groups.
2 //!
3 //! This module uses `extern` functions and types from `libc` that integrate
4 //! with the system’s C library, which integrates with the OS itself to get user
5 //! and group information. It’s where the “core” user handling is done.
6 //!
7 //!
8 //! ## Name encoding rules
9 //!
10 //! Under Unix, usernames and group names are considered to be
11 //! null-terminated, UTF-8 strings. These are `CString`s in Rust, although in
12 //! this library, they are just `String` values. Why?
13 //!
14 //! The reason is that any user or group values with invalid `CString` data
15 //! can instead just be assumed to not exist:
16 //!
17 //! - If you try to search for a user with a null character in their name,
18 //!   such a user could not exist anyway — so it’s OK to return `None`.
19 //! - If the OS returns user information with a null character in a field,
20 //!   then that field will just be truncated instead, which is valid behaviour
21 //!   for a `CString`.
22 //!
23 //! The downside is that we use `from_utf8_lossy` instead, which has a small
24 //! runtime penalty when it calculates and scans the length of the string for
25 //! invalid characters. However, this should not be a problem when dealing with
26 //! usernames of a few bytes each.
27 //!
28 //! In short, if you want to check for null characters in user fields, your
29 //! best bet is to check for them yourself before passing strings into any
30 //! functions.
31 
32 use std::ffi::{CStr, CString, OsStr, OsString};
33 use std::fmt;
34 use std::mem;
35 use std::io;
36 use std::os::unix::ffi::OsStrExt;
37 use std::ptr;
38 use std::sync::Arc;
39 
40 #[cfg(feature = "logging")]
41 extern crate log;
42 #[cfg(feature = "logging")]
43 use self::log::trace;
44 
45 use libc::{c_char, uid_t, gid_t, c_int};
46 use libc::passwd as c_passwd;
47 use libc::group as c_group;
48 
49 
50 /// Information about a particular user.
51 ///
52 /// For more information, see the [module documentation](index.html).
53 #[derive(Clone)]
54 pub struct User {
55     uid: uid_t,
56     primary_group: gid_t,
57     extras: os::UserExtras,
58     pub(crate) name_arc: Arc<OsStr>,
59 }
60 
61 impl User {
62 
63     /// Create a new `User` with the given user ID, name, and primary
64     /// group ID, with the rest of the fields filled with dummy values.
65     ///
66     /// This method does not actually create a new user on the system — it
67     /// should only be used for comparing users in tests.
68     ///
69     /// # Examples
70     ///
71     /// ```
72     /// use users::User;
73     ///
74     /// let user = User::new(501, "stevedore", 100);
75     /// ```
new<S: AsRef<OsStr> + ?Sized>(uid: uid_t, name: &S, primary_group: gid_t) -> Self76     pub fn new<S: AsRef<OsStr> + ?Sized>(uid: uid_t, name: &S, primary_group: gid_t) -> Self {
77         let name_arc = Arc::from(name.as_ref());
78         let extras = os::UserExtras::default();
79 
80         Self { uid, name_arc, primary_group, extras }
81     }
82 
83     /// Returns this user’s ID.
84     ///
85     /// # Examples
86     ///
87     /// ```
88     /// use users::User;
89     ///
90     /// let user = User::new(501, "stevedore", 100);
91     /// assert_eq!(user.uid(), 501);
92     /// ```
uid(&self) -> uid_t93     pub fn uid(&self) -> uid_t {
94         self.uid
95     }
96 
97     /// Returns this user’s name.
98     ///
99     /// # Examples
100     ///
101     /// ```
102     /// use std::ffi::OsStr;
103     /// use users::User;
104     ///
105     /// let user = User::new(501, "stevedore", 100);
106     /// assert_eq!(user.name(), OsStr::new("stevedore"));
107     /// ```
name(&self) -> &OsStr108     pub fn name(&self) -> &OsStr {
109         &*self.name_arc
110     }
111 
112     /// Returns the ID of this user’s primary group.
113     ///
114     /// # Examples
115     ///
116     /// ```
117     /// use users::User;
118     ///
119     /// let user = User::new(501, "stevedore", 100);
120     /// assert_eq!(user.primary_group_id(), 100);
121     /// ```
primary_group_id(&self) -> gid_t122     pub fn primary_group_id(&self) -> gid_t {
123         self.primary_group
124     }
125 
126     /// Returns a list of groups this user is a member of. This involves
127     /// loading the groups list, as it is _not_ contained within this type.
128     ///
129     /// # libc functions used
130     ///
131     /// - [`getgrouplist`](https://docs.rs/libc/*/libc/fn.getgrouplist.html)
132     ///
133     /// # Examples
134     ///
135     /// ```no_run
136     /// use users::User;
137     ///
138     /// let user = User::new(501, "stevedore", 100);
139     /// for group in user.groups().expect("User not found") {
140     ///     println!("User is in group: {:?}", group.name());
141     /// }
142     /// ```
groups(&self) -> Option<Vec<Group>>143     pub fn groups(&self) -> Option<Vec<Group>> {
144         get_user_groups(self.name(), self.primary_group_id())
145     }
146 }
147 
148 impl fmt::Debug for User {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result149     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
150         if f.alternate() {
151             f.debug_struct("User")
152              .field("uid", &self.uid)
153              .field("name_arc", &self.name_arc)
154              .field("primary_group", &self.primary_group)
155              .field("extras", &self.extras)
156              .finish()
157         }
158         else {
159             write!(f, "User({}, {})", self.uid(), self.name().to_string_lossy())
160         }
161     }
162 }
163 
164 
165 /// Information about a particular group.
166 ///
167 /// For more information, see the [module documentation](index.html).
168 #[derive(Clone)]
169 pub struct Group {
170     gid: gid_t,
171     extras: os::GroupExtras,
172     pub(crate) name_arc: Arc<OsStr>,
173 }
174 
175 impl Group {
176 
177     /// Create a new `Group` with the given group ID and name, with the
178     /// rest of the fields filled in with dummy values.
179     ///
180     /// This method does not actually create a new group on the system — it
181     /// should only be used for comparing groups in tests.
182     ///
183     /// # Examples
184     ///
185     /// ```
186     /// use users::Group;
187     ///
188     /// let group = Group::new(102, "database");
189     /// ```
new<S: AsRef<OsStr> + ?Sized>(gid: gid_t, name: &S) -> Self190     pub fn new<S: AsRef<OsStr> + ?Sized>(gid: gid_t, name: &S) -> Self {
191         let name_arc = Arc::from(name.as_ref());
192         let extras = os::GroupExtras::default();
193 
194         Self { gid, name_arc, extras }
195     }
196 
197     /// Returns this group’s ID.
198     ///
199     /// # Examples
200     ///
201     /// ```
202     /// use users::Group;
203     ///
204     /// let group = Group::new(102, "database");
205     /// assert_eq!(group.gid(), 102);
206     /// ```
gid(&self) -> gid_t207     pub fn gid(&self) -> gid_t {
208         self.gid
209     }
210 
211     /// Returns this group’s name.
212     ///
213     /// # Examples
214     ///
215     /// ```
216     /// use std::ffi::OsStr;
217     /// use users::Group;
218     ///
219     /// let group = Group::new(102, "database");
220     /// assert_eq!(group.name(), OsStr::new("database"));
221     /// ```
name(&self) -> &OsStr222     pub fn name(&self) -> &OsStr {
223         &*self.name_arc
224     }
225 }
226 
227 impl fmt::Debug for Group {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result228     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
229         if f.alternate() {
230             f.debug_struct("Group")
231              .field("gid", &self.gid)
232              .field("name_arc", &self.name_arc)
233              .field("extras", &self.extras)
234              .finish()
235         }
236         else {
237             write!(f, "Group({}, {})", self.gid(), self.name().to_string_lossy())
238         }
239     }
240 }
241 
242 
243 /// Reads data from a `*char` field in `c_passwd` or `g_group`. The return
244 /// type will be an `Arc<OsStr>` if the text is meant to be shared in a cache,
245 /// or a plain `OsString` if it’s not.
246 ///
247 /// The underlying buffer is managed by the C library, not by us, so we *need*
248 /// to move data out of it before the next user gets read.
from_raw_buf<'a, T>(p: *const c_char) -> T where T: From<&'a OsStr>249 unsafe fn from_raw_buf<'a, T>(p: *const c_char) -> T
250 where T: From<&'a OsStr>
251 {
252     T::from(OsStr::from_bytes(CStr::from_ptr(p).to_bytes()))
253 }
254 
255 /// Reads data from the `c_passwd` and returns it as a `User`.
passwd_to_user(passwd: c_passwd) -> User256 unsafe fn passwd_to_user(passwd: c_passwd) -> User {
257     #[cfg(feature = "logging")]
258     trace!("Loading user with uid {}", passwd.pw_uid);
259 
260     let name = from_raw_buf(passwd.pw_name);
261 
262     User {
263         uid:           passwd.pw_uid,
264         name_arc:      name,
265         primary_group: passwd.pw_gid,
266         extras:        os::UserExtras::from_passwd(passwd),
267     }
268 }
269 
270 /// Reads data from the `c_group` and returns it as a `Group`.
struct_to_group(group: c_group) -> Group271 unsafe fn struct_to_group(group: c_group) -> Group {
272     #[cfg(feature = "logging")]
273     trace!("Loading group with gid {}", group.gr_gid);
274 
275     let name = from_raw_buf(group.gr_name);
276 
277     Group {
278         gid:      group.gr_gid,
279         name_arc: name,
280         extras:   os::GroupExtras::from_struct(group),
281     }
282 }
283 
284 /// Expand a list of group members to a vector of strings.
285 ///
286 /// The list of members is, in true C fashion, a pointer to a pointer of
287 /// characters, terminated by a null pointer. We check `members[0]`, then
288 /// `members[1]`, and so on, until that null pointer is reached. It doesn’t
289 /// specify whether we should expect a null pointer or a pointer to a null
290 /// pointer, so we check for both here!
members(groups: *mut *mut c_char) -> Vec<OsString>291 unsafe fn members(groups: *mut *mut c_char) -> Vec<OsString> {
292     let mut members = Vec::new();
293 
294     for i in 0.. {
295         let username = groups.offset(i);
296 
297         if username.is_null() || (*username).is_null() {
298             break;
299         }
300         else {
301             members.push(from_raw_buf(*username));
302         }
303     }
304 
305     members
306 }
307 
308 
309 /// Searches for a `User` with the given ID in the system’s user database.
310 /// Returns it if one is found, otherwise returns `None`.
311 ///
312 /// # libc functions used
313 ///
314 /// - [`getpwuid_r`](https://docs.rs/libc/*/libc/fn.getpwuid_r.html)
315 ///
316 /// # Examples
317 ///
318 /// ```
319 /// use users::get_user_by_uid;
320 ///
321 /// match get_user_by_uid(501) {
322 ///     Some(user) => println!("Found user {:?}", user.name()),
323 ///     None       => println!("User not found"),
324 /// }
325 /// ```
get_user_by_uid(uid: uid_t) -> Option<User>326 pub fn get_user_by_uid(uid: uid_t) -> Option<User> {
327     let mut passwd = unsafe { mem::zeroed::<c_passwd>() };
328     let mut buf = vec![0; 2048];
329     let mut result = ptr::null_mut::<c_passwd>();
330 
331     #[cfg(feature = "logging")]
332     trace!("Running getpwuid_r for user #{}", uid);
333 
334     loop {
335         let r = unsafe {
336             libc::getpwuid_r(uid, &mut passwd, buf.as_mut_ptr(), buf.len(), &mut result)
337         };
338 
339         if r != libc::ERANGE {
340             break;
341         }
342 
343         let newsize = buf.len().checked_mul(2)?;
344         buf.resize(newsize, 0);
345     }
346 
347     if result.is_null() {
348         // There is no such user, or an error has occurred.
349         // errno gets set if there’s an error.
350         return None;
351     }
352 
353     if result != &mut passwd {
354         // The result of getpwuid_r should be its input passwd.
355         return None;
356     }
357 
358     let user = unsafe { passwd_to_user(result.read()) };
359     Some(user)
360 }
361 
362 /// Searches for a `User` with the given username in the system’s user database.
363 /// Returns it if one is found, otherwise returns `None`.
364 ///
365 /// # libc functions used
366 ///
367 /// - [`getpwnam_r`](https://docs.rs/libc/*/libc/fn.getpwnam_r.html)
368 ///
369 /// # Examples
370 ///
371 /// ```
372 /// use users::get_user_by_name;
373 ///
374 /// match get_user_by_name("stevedore") {
375 ///     Some(user) => println!("Found user #{}", user.uid()),
376 ///     None       => println!("User not found"),
377 /// }
378 /// ```
get_user_by_name<S: AsRef<OsStr> + ?Sized>(username: &S) -> Option<User>379 pub fn get_user_by_name<S: AsRef<OsStr> + ?Sized>(username: &S) -> Option<User> {
380     let username = match CString::new(username.as_ref().as_bytes()) {
381         Ok(u)  => u,
382         Err(_) => {
383             // The username that was passed in contained a null character,
384             // which will match no usernames.
385             return None;
386         }
387     };
388 
389     let mut passwd = unsafe { mem::zeroed::<c_passwd>() };
390     let mut buf = vec![0; 2048];
391     let mut result = ptr::null_mut::<c_passwd>();
392 
393     #[cfg(feature = "logging")]
394     trace!("Running getpwnam_r for user {:?}", username.as_ref());
395 
396     loop {
397         let r = unsafe {
398             libc::getpwnam_r(username.as_ptr(), &mut passwd, buf.as_mut_ptr(), buf.len(), &mut result)
399         };
400 
401         if r != libc::ERANGE {
402             break;
403         }
404 
405         let newsize = buf.len().checked_mul(2)?;
406         buf.resize(newsize, 0);
407     }
408 
409     if result.is_null() {
410         // There is no such user, or an error has occurred.
411         // errno gets set if there’s an error.
412         return None;
413     }
414 
415     if result != &mut passwd {
416         // The result of getpwnam_r should be its input passwd.
417         return None;
418     }
419 
420     let user = unsafe { passwd_to_user(result.read()) };
421     Some(user)
422 }
423 
424 /// Searches for a `Group` with the given ID in the system’s group database.
425 /// Returns it if one is found, otherwise returns `None`.
426 ///
427 /// # libc functions used
428 ///
429 /// - [`getgrgid_r`](https://docs.rs/libc/*/libc/fn.getgrgid_r.html)
430 ///
431 /// # Examples
432 ///
433 /// ```
434 /// use users::get_group_by_gid;
435 ///
436 /// match get_group_by_gid(102) {
437 ///     Some(group) => println!("Found group {:?}", group.name()),
438 ///     None        => println!("Group not found"),
439 /// }
440 /// ```
get_group_by_gid(gid: gid_t) -> Option<Group>441 pub fn get_group_by_gid(gid: gid_t) -> Option<Group> {
442     let mut passwd = unsafe { mem::zeroed::<c_group>() };
443     let mut buf = vec![0; 2048];
444     let mut result = ptr::null_mut::<c_group>();
445 
446     #[cfg(feature = "logging")]
447     trace!("Running getgruid_r for group #{}", gid);
448 
449     loop {
450         let r = unsafe {
451             libc::getgrgid_r(gid, &mut passwd, buf.as_mut_ptr(), buf.len(), &mut result)
452         };
453 
454         if r != libc::ERANGE {
455             break;
456         }
457 
458         let newsize = buf.len().checked_mul(2)?;
459         buf.resize(newsize, 0);
460     }
461 
462     if result.is_null() {
463         // There is no such group, or an error has occurred.
464         // errno gets set if there’s an error.
465         return None;
466     }
467 
468     if result != &mut passwd {
469         // The result of getgrgid_r should be its input struct.
470         return None;
471     }
472 
473     let group = unsafe { struct_to_group(result.read()) };
474     Some(group)
475 }
476 
477 /// Searches for a `Group` with the given group name in the system’s group database.
478 /// Returns it if one is found, otherwise returns `None`.
479 ///
480 /// # libc functions used
481 ///
482 /// - [`getgrnam_r`](https://docs.rs/libc/*/libc/fn.getgrnam_r.html)
483 ///
484 /// # Examples
485 ///
486 /// ```
487 /// use users::get_group_by_name;
488 ///
489 /// match get_group_by_name("db-access") {
490 ///     Some(group) => println!("Found group #{}", group.gid()),
491 ///     None        => println!("Group not found"),
492 /// }
493 /// ```
get_group_by_name<S: AsRef<OsStr> + ?Sized>(groupname: &S) -> Option<Group>494 pub fn get_group_by_name<S: AsRef<OsStr> + ?Sized>(groupname: &S) -> Option<Group> {
495     let groupname = match CString::new(groupname.as_ref().as_bytes()) {
496         Ok(u)  => u,
497         Err(_) => {
498             // The groupname that was passed in contained a null character,
499             // which will match no usernames.
500             return None;
501         }
502     };
503 
504     let mut group = unsafe { mem::zeroed::<c_group>() };
505     let mut buf = vec![0; 2048];
506     let mut result = ptr::null_mut::<c_group>();
507 
508     #[cfg(feature = "logging")]
509     trace!("Running getgrnam_r for group {:?}", groupname.as_ref());
510 
511     loop {
512         let r = unsafe {
513             libc::getgrnam_r(groupname.as_ptr(), &mut group, buf.as_mut_ptr(), buf.len(), &mut result)
514         };
515 
516         if r != libc::ERANGE {
517             break;
518         }
519 
520         let newsize = buf.len().checked_mul(2)?;
521         buf.resize(newsize, 0);
522     }
523 
524     if result.is_null() {
525         // There is no such group, or an error has occurred.
526         // errno gets set if there’s an error.
527         return None;
528     }
529 
530     if result != &mut group {
531         // The result of getgrnam_r should be its input struct.
532         return None;
533     }
534 
535     let group = unsafe { struct_to_group(result.read()) };
536     Some(group)
537 }
538 
539 /// Returns the user ID for the user running the process.
540 ///
541 /// # libc functions used
542 ///
543 /// - [`getuid`](https://docs.rs/libc/*/libc/fn.getuid.html)
544 ///
545 /// # Examples
546 ///
547 /// ```
548 /// use users::get_current_uid;
549 ///
550 /// println!("The ID of the current user is {}", get_current_uid());
551 /// ```
get_current_uid() -> uid_t552 pub fn get_current_uid() -> uid_t {
553     #[cfg(feature = "logging")]
554     trace!("Running getuid");
555 
556     unsafe { libc::getuid() }
557 }
558 
559 /// Returns the username of the user running the process.
560 ///
561 /// This function to return `None` if the current user does not exist, which
562 /// could happed if they were deleted after the program started running.
563 ///
564 /// # libc functions used
565 ///
566 /// - [`getuid`](https://docs.rs/libc/*/libc/fn.getuid.html)
567 /// - [`getpwuid_r`](https://docs.rs/libc/*/libc/fn.getpwuid_r.html)
568 ///
569 /// # Examples
570 ///
571 /// ```
572 /// use users::get_current_username;
573 ///
574 /// match get_current_username() {
575 ///     Some(uname) => println!("Running as user with name {:?}", uname),
576 ///     None        => println!("The current user does not exist!"),
577 /// }
578 /// ```
get_current_username() -> Option<OsString>579 pub fn get_current_username() -> Option<OsString> {
580     let uid = get_current_uid();
581     let user = get_user_by_uid(uid)?;
582 
583     Some(OsString::from(&*user.name_arc))
584 }
585 
586 /// Returns the user ID for the effective user running the process.
587 ///
588 /// # libc functions used
589 ///
590 /// - [`geteuid`](https://docs.rs/libc/*/libc/fn.geteuid.html)
591 ///
592 /// # Examples
593 ///
594 /// ```
595 /// use users::get_effective_uid;
596 ///
597 /// println!("The ID of the effective user is {}", get_effective_uid());
598 /// ```
get_effective_uid() -> uid_t599 pub fn get_effective_uid() -> uid_t {
600     #[cfg(feature = "logging")]
601     trace!("Running geteuid");
602 
603     unsafe { libc::geteuid() }
604 }
605 
606 /// Returns the username of the effective user running the process.
607 ///
608 /// # libc functions used
609 ///
610 /// - [`geteuid`](https://docs.rs/libc/*/libc/fn.geteuid.html)
611 /// - [`getpwuid_r`](https://docs.rs/libc/*/libc/fn.getpwuid_r.html)
612 ///
613 /// # Examples
614 ///
615 /// ```
616 /// use users::get_effective_username;
617 ///
618 /// match get_effective_username() {
619 ///     Some(uname) => println!("Running as effective user with name {:?}", uname),
620 ///     None        => println!("The effective user does not exist!"),
621 /// }
622 /// ```
get_effective_username() -> Option<OsString>623 pub fn get_effective_username() -> Option<OsString> {
624     let uid = get_effective_uid();
625     let user = get_user_by_uid(uid)?;
626 
627     Some(OsString::from(&*user.name_arc))
628 }
629 
630 /// Returns the group ID for the user running the process.
631 ///
632 /// # libc functions used
633 ///
634 /// - [`getgid`](https://docs.rs/libc/*/libc/fn.getgid.html)
635 ///
636 /// # Examples
637 ///
638 /// ```
639 /// use users::get_current_gid;
640 ///
641 /// println!("The ID of the current group is {}", get_current_gid());
642 /// ```
get_current_gid() -> gid_t643 pub fn get_current_gid() -> gid_t {
644     #[cfg(feature = "logging")]
645     trace!("Running getgid");
646 
647     unsafe { libc::getgid() }
648 }
649 
650 /// Returns the groupname of the user running the process.
651 ///
652 /// # libc functions used
653 ///
654 /// - [`getgid`](https://docs.rs/libc/*/libc/fn.getgid.html)
655 /// - [`getgrgid`](https://docs.rs/libc/*/libc/fn.getgrgid.html)
656 ///
657 /// # Examples
658 ///
659 /// ```
660 /// use users::get_current_groupname;
661 ///
662 /// match get_current_groupname() {
663 ///     Some(gname) => println!("Running as group with name {:?}", gname),
664 ///     None        => println!("The current group does not exist!"),
665 /// }
666 /// ```
get_current_groupname() -> Option<OsString>667 pub fn get_current_groupname() -> Option<OsString> {
668     let gid = get_current_gid();
669     let group = get_group_by_gid(gid)?;
670 
671     Some(OsString::from(&*group.name_arc))
672 }
673 
674 /// Returns the group ID for the effective user running the process.
675 ///
676 /// # libc functions used
677 ///
678 /// - [`getegid`](https://docs.rs/libc/*/libc/fn.getegid.html)
679 ///
680 /// # Examples
681 ///
682 /// ```
683 /// use users::get_effective_gid;
684 ///
685 /// println!("The ID of the effective group is {}", get_effective_gid());
686 /// ```
get_effective_gid() -> gid_t687 pub fn get_effective_gid() -> gid_t {
688     #[cfg(feature = "logging")]
689     trace!("Running getegid");
690 
691     unsafe { libc::getegid() }
692 }
693 
694 /// Returns the groupname of the effective user running the process.
695 ///
696 /// # libc functions used
697 ///
698 /// - [`getegid`](https://docs.rs/libc/*/libc/fn.getegid.html)
699 /// - [`getgrgid`](https://docs.rs/libc/*/libc/fn.getgrgid.html)
700 ///
701 /// # Examples
702 ///
703 /// ```
704 /// use users::get_effective_groupname;
705 ///
706 /// match get_effective_groupname() {
707 ///     Some(gname) => println!("Running as effective group with name {:?}", gname),
708 ///     None        => println!("The effective group does not exist!"),
709 /// }
710 /// ```
get_effective_groupname() -> Option<OsString>711 pub fn get_effective_groupname() -> Option<OsString> {
712     let gid = get_effective_gid();
713     let group = get_group_by_gid(gid)?;
714 
715     Some(OsString::from(&*group.name_arc))
716 }
717 
718 /// Returns the group access list for the current process.
719 ///
720 /// # libc functions used
721 ///
722 /// - [`getgroups`](https://docs.rs/libc/*/libc/fn.getgroups.html)
723 ///
724 /// # Errors
725 ///
726 /// This function will return `Err` when an I/O error occurs during the
727 /// `getgroups` call.
728 ///
729 /// # Examples
730 ///
731 /// ```no_run
732 /// use users::group_access_list;
733 ///
734 /// for group in group_access_list().expect("Error looking up groups") {
735 ///     println!("Process can access group #{} ({:?})", group.gid(), group.name());
736 /// }
737 /// ```
group_access_list() -> io::Result<Vec<Group>>738 pub fn group_access_list() -> io::Result<Vec<Group>> {
739     let mut buff: Vec<gid_t> = vec![0; 1024];
740 
741     #[cfg(feature = "logging")]
742     trace!("Running getgroups");
743 
744     let res = unsafe {
745         libc::getgroups(1024, buff.as_mut_ptr())
746     };
747 
748     if res < 0 {
749         Err(io::Error::last_os_error())
750     }
751     else {
752         let mut groups = buff.into_iter()
753                              .filter_map(get_group_by_gid)
754                              .collect::<Vec<_>>();
755         groups.dedup_by_key(|i| i.gid());
756         Ok(groups)
757     }
758 }
759 
760 /// Returns groups for a provided user name and primary group id.
761 ///
762 /// # libc functions used
763 ///
764 /// - [`getgrouplist`](https://docs.rs/libc/*/libc/fn.getgrouplist.html)
765 ///
766 /// # Examples
767 ///
768 /// ```no_run
769 /// use users::get_user_groups;
770 ///
771 /// for group in get_user_groups("stevedore", 1001).expect("Error looking up groups") {
772 ///     println!("User is a member of group #{} ({:?})", group.gid(), group.name());
773 /// }
774 /// ```
get_user_groups<S: AsRef<OsStr> + ?Sized>(username: &S, gid: gid_t) -> Option<Vec<Group>>775 pub fn get_user_groups<S: AsRef<OsStr> + ?Sized>(username: &S, gid: gid_t) -> Option<Vec<Group>> {
776     // MacOS uses i32 instead of gid_t in getgrouplist for unknown reasons
777     #[cfg(all(unix, target_os="macos"))]
778     let mut buff: Vec<i32> = vec![0; 1024];
779     #[cfg(all(unix, not(target_os="macos")))]
780     let mut buff: Vec<gid_t> = vec![0; 1024];
781 
782     let name = CString::new(username.as_ref().as_bytes()).unwrap();
783     let mut count = buff.len() as c_int;
784 
785     #[cfg(feature = "logging")]
786     trace!("Running getgrouplist for user {:?} and group #{}", username.as_ref(), gid);
787 
788     // MacOS uses i32 instead of gid_t in getgrouplist for unknown reasons
789     #[cfg(all(unix, target_os="macos"))]
790     let res = unsafe {
791         libc::getgrouplist(name.as_ptr(), gid as i32, buff.as_mut_ptr(), &mut count)
792     };
793 
794     #[cfg(all(unix, not(target_os="macos")))]
795     let res = unsafe {
796         libc::getgrouplist(name.as_ptr(), gid, buff.as_mut_ptr(), &mut count)
797     };
798 
799     if res < 0 {
800         None
801     }
802     else {
803         buff.dedup();
804         buff.into_iter()
805             .filter_map(|i| get_group_by_gid(i as gid_t))
806             .collect::<Vec<_>>()
807             .into()
808     }
809 }
810 
811 
812 
813 /// An iterator over every user present on the system.
814 struct AllUsers;
815 
816 /// Creates a new iterator over every user present on the system.
817 ///
818 /// # libc functions used
819 ///
820 /// - [`getpwent`](https://docs.rs/libc/*/libc/fn.getpwent.html)
821 /// - [`setpwent`](https://docs.rs/libc/*/libc/fn.setpwent.html)
822 /// - [`endpwent`](https://docs.rs/libc/*/libc/fn.endpwent.html)
823 ///
824 /// # Safety
825 ///
826 /// This constructor is marked as `unsafe`, which is odd for a crate
827 /// that’s meant to be a safe interface. It *has* to be unsafe because
828 /// we cannot guarantee that the underlying C functions,
829 /// `getpwent`/`setpwent`/`endpwent` that iterate over the system’s
830 /// `passwd` entries, are called in a thread-safe manner.
831 ///
832 /// These functions [modify a global
833 /// state](http://man7.org/linux/man-pages/man3/getpwent.3.html#ATTRIBUTES),
834 /// and if any are used at the same time, the state could be reset,
835 /// resulting in a data race. We cannot even place it behind an internal
836 /// `Mutex`, as there is nothing stopping another `extern` function
837 /// definition from calling it!
838 ///
839 /// So to iterate all users, construct the iterator inside an `unsafe`
840 /// block, then make sure to not make a new instance of it until
841 /// iteration is over.
842 ///
843 /// # Examples
844 ///
845 /// ```
846 /// use users::all_users;
847 ///
848 /// let iter = unsafe { all_users() };
849 /// for user in iter {
850 ///     println!("User #{} ({:?})", user.uid(), user.name());
851 /// }
852 /// ```
all_users() -> impl Iterator<Item=User>853 pub unsafe fn all_users() -> impl Iterator<Item=User> {
854     #[cfg(feature = "logging")]
855     trace!("Running setpwent");
856 
857     #[cfg(not(target_os = "android"))]
858     libc::setpwent();
859     AllUsers
860 }
861 
862 impl Drop for AllUsers {
863     #[cfg(target_os = "android")]
drop(&mut self)864     fn drop(&mut self) {
865         // nothing to do here
866     }
867 
868     #[cfg(not(target_os = "android"))]
drop(&mut self)869     fn drop(&mut self) {
870         #[cfg(feature = "logging")]
871         trace!("Running endpwent");
872 
873         unsafe { libc::endpwent() };
874     }
875 }
876 
877 impl Iterator for AllUsers {
878     type Item = User;
879 
880     #[cfg(target_os = "android")]
next(&mut self) -> Option<User>881     fn next(&mut self) -> Option<User> {
882         None
883     }
884 
885     #[cfg(not(target_os = "android"))]
next(&mut self) -> Option<User>886     fn next(&mut self) -> Option<User> {
887         #[cfg(feature = "logging")]
888         trace!("Running getpwent");
889 
890         let result = unsafe { libc::getpwent() };
891 
892         if result.is_null() {
893             None
894         }
895         else {
896             let user = unsafe { passwd_to_user(result.read()) };
897             Some(user)
898         }
899     }
900 }
901 
902 
903 
904 /// OS-specific extensions to users and groups.
905 ///
906 /// Every OS has a different idea of what data a user or a group comes with.
907 /// Although they all provide a *username*, some OS’ users have an *actual name*
908 /// too, or a set of permissions or directories or timestamps associated with
909 /// them.
910 ///
911 /// This module provides extension traits for users and groups that allow
912 /// implementors of this library to access this data *as long as a trait is
913 /// available*, which requires the OS they’re using to support this data.
914 ///
915 /// It’s the same method taken by `Metadata` in the standard Rust library,
916 /// which has a few cross-platform fields and many more OS-specific fields:
917 /// traits in `std::os` provides access to any data that is not guaranteed to
918 /// be there in the actual struct.
919 pub mod os {
920 
921     /// Extensions to users and groups for Unix platforms.
922     ///
923     /// Although the `passwd` struct is common among Unix systems, its actual
924     /// format can vary. See the definitions in the `base` module to check which
925     /// fields are actually present.
926     #[cfg(any(target_os = "linux", target_os = "android", target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd", target_os = "solaris"))]
927     pub mod unix {
928         use std::ffi::{OsStr, OsString};
929         use std::path::{Path, PathBuf};
930 
931         use super::super::{c_passwd, c_group, members, from_raw_buf, Group};
932 
933         /// Unix-specific extensions for `User`s.
934         pub trait UserExt {
935 
936             /// Returns a path to this user’s home directory.
home_dir(&self) -> &Path937             fn home_dir(&self) -> &Path;
938 
939             /// Sets this user value’s home directory to the given string.
940             /// Can be used to construct test users, which by default come with a
941             /// dummy home directory string.
with_home_dir<S: AsRef<OsStr> + ?Sized>(self, home_dir: &S) -> Self942             fn with_home_dir<S: AsRef<OsStr> + ?Sized>(self, home_dir: &S) -> Self;
943 
944             /// Returns a path to this user’s shell.
shell(&self) -> &Path945             fn shell(&self) -> &Path;
946 
947             /// Sets this user’s shell path to the given string.
948             /// Can be used to construct test users, which by default come with a
949             /// dummy shell field.
with_shell<S: AsRef<OsStr> + ?Sized>(self, shell: &S) -> Self950             fn with_shell<S: AsRef<OsStr> + ?Sized>(self, shell: &S) -> Self;
951 
952             /// Returns the user’s encrypted password.
password(&self) -> &OsStr953             fn password(&self) -> &OsStr;
954 
955             /// Sets this user’s password to the given string.
956             /// Can be used to construct tests users, which by default come with a
957             /// dummy password field.
with_password<S: AsRef<OsStr> + ?Sized>(self, password: &S) -> Self958             fn with_password<S: AsRef<OsStr> + ?Sized>(self, password: &S) -> Self;
959         }
960 
961         /// Unix-specific extensions for `Group`s.
962         pub trait GroupExt {
963 
964             /// Returns a slice of the list of users that are in this group as
965             /// their non-primary group.
members(&self) -> &[OsString]966             fn members(&self) -> &[OsString];
967 
968             /// Adds a new member to this group.
add_member<S: AsRef<OsStr> + ?Sized>(self, name: &S) -> Self969             fn add_member<S: AsRef<OsStr> + ?Sized>(self, name: &S) -> Self;
970         }
971 
972         /// Unix-specific fields for `User`s.
973         #[derive(Clone, Debug)]
974         pub struct UserExtras {
975 
976             /// The path to the user’s home directory.
977             pub home_dir: PathBuf,
978 
979             /// The path to the user’s shell.
980             pub shell: PathBuf,
981 
982             /// The user’s encrypted password.
983             pub password: OsString,
984         }
985 
986         impl Default for UserExtras {
default() -> Self987             fn default() -> Self {
988                 Self {
989                     home_dir: "/var/empty".into(),
990                     shell:    "/bin/false".into(),
991                     password: "*".into(),
992                 }
993             }
994         }
995 
996         impl UserExtras {
997             /// Extract the OS-specific fields from the C `passwd` struct that
998             /// we just read.
from_passwd(passwd: c_passwd) -> Self999             pub(crate) unsafe fn from_passwd(passwd: c_passwd) -> Self {
1000                 #[cfg(target_os = "android")]
1001                 {
1002                     Default::default()
1003                 }
1004                 #[cfg(not(target_os = "android"))]
1005                 {
1006                     let home_dir = from_raw_buf::<OsString>(passwd.pw_dir).into();
1007                     let shell    = from_raw_buf::<OsString>(passwd.pw_shell).into();
1008                     let password = from_raw_buf::<OsString>(passwd.pw_passwd);
1009 
1010                     Self { home_dir, shell, password }
1011                 }
1012             }
1013         }
1014 
1015         #[cfg(any(target_os = "linux", target_os = "android", target_os = "solaris"))]
1016         use super::super::User;
1017 
1018         #[cfg(any(target_os = "linux", target_os = "android", target_os = "solaris"))]
1019         impl UserExt for User {
home_dir(&self) -> &Path1020             fn home_dir(&self) -> &Path {
1021                 Path::new(&self.extras.home_dir)
1022             }
1023 
with_home_dir<S: AsRef<OsStr> + ?Sized>(mut self, home_dir: &S) -> Self1024             fn with_home_dir<S: AsRef<OsStr> + ?Sized>(mut self, home_dir: &S) -> Self {
1025                 self.extras.home_dir = home_dir.into();
1026                 self
1027             }
1028 
shell(&self) -> &Path1029             fn shell(&self) -> &Path {
1030                 Path::new(&self.extras.shell)
1031             }
1032 
with_shell<S: AsRef<OsStr> + ?Sized>(mut self, shell: &S) -> Self1033             fn with_shell<S: AsRef<OsStr> + ?Sized>(mut self, shell: &S) -> Self {
1034                 self.extras.shell = shell.into();
1035                 self
1036             }
1037 
password(&self) -> &OsStr1038             fn password(&self) -> &OsStr {
1039                 &self.extras.password
1040             }
1041 
with_password<S: AsRef<OsStr> + ?Sized>(mut self, password: &S) -> Self1042             fn with_password<S: AsRef<OsStr> + ?Sized>(mut self, password: &S) -> Self {
1043                 self.extras.password = password.into();
1044                 self
1045             }
1046         }
1047 
1048         /// Unix-specific fields for `Group`s.
1049         #[derive(Clone, Default, Debug)]
1050         pub struct GroupExtras {
1051 
1052             /// Vector of usernames that are members of this group.
1053             pub members: Vec<OsString>,
1054         }
1055 
1056         impl GroupExtras {
1057             /// Extract the OS-specific fields from the C `group` struct that
1058             /// we just read.
from_struct(group: c_group) -> Self1059             pub(crate) unsafe fn from_struct(group: c_group) -> Self {
1060                 Self { members: members(group.gr_mem) }
1061             }
1062         }
1063 
1064         impl GroupExt for Group {
members(&self) -> &[OsString]1065             fn members(&self) -> &[OsString] {
1066                 &*self.extras.members
1067             }
1068 
add_member<S: AsRef<OsStr> + ?Sized>(mut self, member: &S) -> Self1069             fn add_member<S: AsRef<OsStr> + ?Sized>(mut self, member: &S) -> Self {
1070                 self.extras.members.push(member.into());
1071                 self
1072             }
1073         }
1074     }
1075 
1076     /// Extensions to users and groups for BSD platforms.
1077     ///
1078     /// These platforms have `change` and `expire` fields in their `passwd`
1079     /// C structs.
1080     #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd"))]
1081     pub mod bsd {
1082         use std::ffi::OsStr;
1083         use std::path::Path;
1084         use libc::time_t;
1085         use super::super::{c_passwd, User};
1086 
1087         /// BSD-specific fields for `User`s.
1088         #[derive(Clone, Debug)]
1089         pub struct UserExtras {
1090 
1091             /// Fields specific to Unix, rather than just BSD. (This struct is
1092             /// a superset, so it has to have all the other fields in it, too).
1093             pub extras: super::unix::UserExtras,
1094 
1095             /// Password change time.
1096             pub change: time_t,
1097 
1098             /// Password expiry time.
1099             pub expire: time_t,
1100         }
1101 
1102         impl UserExtras {
1103             /// Extract the OS-specific fields from the C `passwd` struct that
1104             /// we just read.
from_passwd(passwd: c_passwd) -> Self1105             pub(crate) unsafe fn from_passwd(passwd: c_passwd) -> Self {
1106                 Self {
1107                     change: passwd.pw_change,
1108                     expire: passwd.pw_expire,
1109                     extras: super::unix::UserExtras::from_passwd(passwd),
1110                 }
1111             }
1112         }
1113 
1114         impl super::unix::UserExt for User {
home_dir(&self) -> &Path1115             fn home_dir(&self) -> &Path {
1116                 Path::new(&self.extras.extras.home_dir)
1117             }
1118 
with_home_dir<S: AsRef<OsStr> + ?Sized>(mut self, home_dir: &S) -> Self1119             fn with_home_dir<S: AsRef<OsStr> + ?Sized>(mut self, home_dir: &S) -> Self {
1120                 self.extras.extras.home_dir = home_dir.into();
1121                 self
1122             }
1123 
shell(&self) -> &Path1124             fn shell(&self) -> &Path {
1125                 Path::new(&self.extras.extras.shell)
1126             }
1127 
with_shell<S: AsRef<OsStr> + ?Sized>(mut self, shell: &S) -> Self1128             fn with_shell<S: AsRef<OsStr> + ?Sized>(mut self, shell: &S) -> Self {
1129                 self.extras.extras.shell = shell.into();
1130                 self
1131             }
1132 
password(&self) -> &OsStr1133             fn password(&self) -> &OsStr {
1134                 &self.extras.extras.password
1135             }
1136 
with_password<S: AsRef<OsStr> + ?Sized>(mut self, password: &S) -> Self1137             fn with_password<S: AsRef<OsStr> + ?Sized>(mut self, password: &S) -> Self {
1138                 self.extras.extras.password = password.into();
1139                 self
1140             }
1141         }
1142 
1143         /// BSD-specific accessors for `User`s.
1144         pub trait UserExt {
1145 
1146             /// Returns this user’s password change timestamp.
password_change_time(&self) -> time_t1147             fn password_change_time(&self) -> time_t;
1148 
1149             /// Returns this user’s password expiry timestamp.
password_expire_time(&self) -> time_t1150             fn password_expire_time(&self) -> time_t;
1151         }
1152 
1153         impl UserExt for User {
password_change_time(&self) -> time_t1154             fn password_change_time(&self) -> time_t {
1155                 self.extras.change
1156             }
1157 
password_expire_time(&self) -> time_t1158             fn password_expire_time(&self) -> time_t {
1159                 self.extras.expire
1160             }
1161         }
1162 
1163         impl Default for UserExtras {
default() -> Self1164             fn default() -> Self {
1165                 Self {
1166                     extras: super::unix::UserExtras::default(),
1167                     change: 0,
1168                     expire: 0,
1169                 }
1170             }
1171         }
1172     }
1173 
1174     /// Any extra fields on a `User` specific to the current platform.
1175     #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd"))]
1176     pub type UserExtras = bsd::UserExtras;
1177 
1178     /// Any extra fields on a `User` specific to the current platform.
1179     #[cfg(any(target_os = "linux", target_os = "android", target_os = "solaris"))]
1180     pub type UserExtras = unix::UserExtras;
1181 
1182     /// Any extra fields on a `Group` specific to the current platform.
1183     #[cfg(any(target_os = "linux", target_os = "android", target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd", target_os = "solaris"))]
1184     pub type GroupExtras = unix::GroupExtras;
1185 }
1186 
1187 
1188 #[cfg(test)]
1189 mod test {
1190     use super::*;
1191 
1192     #[test]
uid()1193     fn uid() {
1194         get_current_uid();
1195     }
1196 
1197     #[test]
username()1198     fn username() {
1199         let uid = get_current_uid();
1200         assert_eq!(&*get_current_username().unwrap(), &*get_user_by_uid(uid).unwrap().name());
1201     }
1202 
1203     #[test]
uid_for_username()1204     fn uid_for_username() {
1205         let uid = get_current_uid();
1206         let user = get_user_by_uid(uid).unwrap();
1207         assert_eq!(user.uid, uid);
1208     }
1209 
1210     #[test]
username_for_uid_for_username()1211     fn username_for_uid_for_username() {
1212         let uid = get_current_uid();
1213         let user = get_user_by_uid(uid).unwrap();
1214         let user2 = get_user_by_uid(user.uid).unwrap();
1215         assert_eq!(user2.uid, uid);
1216     }
1217 
1218     #[test]
user_info()1219     fn user_info() {
1220         use base::os::unix::UserExt;
1221 
1222         let uid = get_current_uid();
1223         let user = get_user_by_uid(uid).unwrap();
1224         // Not a real test but can be used to verify correct results
1225         // Use with --nocapture on test executable to show output
1226         println!("HOME={:?}, SHELL={:?}, PASSWD={:?}",
1227             user.home_dir(), user.shell(), user.password());
1228     }
1229 
1230     #[test]
user_by_name()1231     fn user_by_name() {
1232         // We cannot really test for arbitrary user as they might not exist on the machine
1233         // Instead the name of the current user is used
1234         let name = get_current_username().unwrap();
1235         let user_by_name = get_user_by_name(&name);
1236         assert!(user_by_name.is_some());
1237         assert_eq!(user_by_name.unwrap().name(), &*name);
1238 
1239         // User names containing '\0' cannot be used (for now)
1240         let user = get_user_by_name("user\0");
1241         assert!(user.is_none());
1242     }
1243 
1244     #[test]
user_get_groups()1245     fn user_get_groups() {
1246         let uid = get_current_uid();
1247         let user = get_user_by_uid(uid).unwrap();
1248         let groups = user.groups().unwrap();
1249         println!("Groups: {:?}", groups);
1250         assert!(groups.len() > 0);
1251     }
1252 
1253     #[test]
group_by_name()1254     fn group_by_name() {
1255         // We cannot really test for arbitrary groups as they might not exist on the machine
1256         // Instead the primary group of the current user is used
1257         let cur_uid = get_current_uid();
1258         let cur_user = get_user_by_uid(cur_uid).unwrap();
1259         let cur_group = get_group_by_gid(cur_user.primary_group).unwrap();
1260         let group_by_name = get_group_by_name(&cur_group.name());
1261 
1262         assert!(group_by_name.is_some());
1263         assert_eq!(group_by_name.unwrap().name(), cur_group.name());
1264 
1265         // Group names containing '\0' cannot be used (for now)
1266         let group = get_group_by_name("users\0");
1267         assert!(group.is_none());
1268     }
1269 }
1270