uds_fork/
credentials.rs

1#![allow(
2    clippy::match_ref_pats, // looks more optimized with long array
3    clippy::needless_borrowed_reference,
4)]
5
6use std::os::fd::AsFd;
7use std::{io, fmt};
8use std::num::NonZeroU32;
9use std::io::ErrorKind::*;
10#[cfg(any(
11    target_os="linux", target_os="android",
12    target_os="freebsd", target_os="dragonfly", target_vendor="apple",
13    target_os="openbsd", target_os="netbsd"
14))]
15use std::mem;
16#[cfg(any(target_os="illumos", target_os="solaris"))]
17use std::ptr;
18
19#[cfg(any(
20    target_os="linux", target_os="android",
21    target_os="freebsd", target_os="dragonfly", target_vendor="apple",
22    target_os="openbsd", target_os="netbsd"
23))]
24use libc::{getsockopt, c_void, socklen_t};
25#[cfg(any(target_os="linux", target_os="android"))]
26use libc::{pid_t, uid_t, gid_t, getpid, getuid, geteuid, getgid, getegid};
27#[cfg(any(target_os="linux", target_os="android"))]
28use libc::{ucred, SOL_SOCKET, SO_PEERCRED, SO_PEERSEC};
29#[cfg(any(target_os="freebsd", target_os="dragonfly", target_vendor="apple"))]
30use libc::{xucred, XUCRED_VERSION, LOCAL_PEERCRED};
31#[cfg(target_vendor="apple")]
32use libc::SOL_LOCAL; // Apple is for once the one that does the right thing!
33#[cfg(target_os="openbsd")]
34use libc::{sockpeercred, SOL_SOCKET, SO_PEERCRED};
35#[cfg(target_os="netbsd")]
36use libc::{unpcbid, LOCAL_PEEREID};
37#[cfg(any(target_os="illumos", target_os="solaris"))]
38use libc::{getpeerucred, ucred_free, ucred_t};
39#[cfg(any(target_os="illumos", target_os="solaris"))]
40use libc::{ucred_geteuid, ucred_getegid, ucred_getpid, ucred_getgroups, uid_t, gid_t, pid_t};
41
42/// Credentials to be sent with `send_ancillary()`.
43///
44/// Only on Linux (& Android) does one need to send credentials, and on other
45/// operating systems this struct is ignored.
46#[derive(Clone,Copy, PartialEq,Eq, Debug)]
47#[allow(unused)] // not used yet
48pub enum SendCredentials 
49{
50    Effective,
51    Real,
52    Custom{ pid: u32, uid: u32, gid: u32 }
53}
54
55#[cfg(any(target_os="linux", target_os="android"))]
56impl SendCredentials 
57{
58    pub 
59    fn into_raw(self) -> ucred 
60    {
61        let mut ucred: ucred = unsafe { mem::zeroed() };
62
63        let (pid, uid, gid) = 
64            match self 
65            {
66                SendCredentials::Effective => 
67                    unsafe { (getpid(), geteuid(), getegid()) },
68                SendCredentials::Real => 
69                    unsafe { (getpid(), getuid(), getgid()) },
70                SendCredentials::Custom{pid, uid, gid} => 
71                    (pid as pid_t, uid as uid_t, gid as gid_t),
72            };
73
74        ucred.pid = pid;
75        ucred.uid = uid;
76        ucred.gid = gid;
77
78        return ucred;
79    }
80}
81
82
83
84#[cfg(any(target_os="linux", target_os="android"))]
85pub 
86fn selinux_context<FD: AsFd>(fd: FD, buffer: &mut[u8]) -> Result<usize, io::Error> 
87{
88    let ptr = buffer.as_mut_ptr() as *mut c_void;
89    let mut capacity = buffer.len().min(socklen_t::max_value() as usize) as socklen_t;
90
91    let res = 
92        unsafe 
93        {
94            use std::os::fd::AsRawFd;
95
96            getsockopt(fd.as_fd().as_raw_fd(), SOL_SOCKET, SO_PEERSEC, ptr, &mut capacity)    
97        };
98    
99    return 
100        match res 
101        {
102            -1 => Err(io::Error::last_os_error()),
103            _ => Ok(capacity as usize),
104        };
105}
106
107#[cfg(not(any(target_os="linux", target_os="android")))]
108pub 
109fn selinux_context<FD: AsFd>(_fd: FD,  _buffer: &mut[u8]) -> Result<usize, io::Error> 
110{
111    Err(io::Error::new(Other, "not available"))
112}
113
114
115
116/// Credentials of the peer process when it called `connect()`, `accept()` or `pair()`.
117///
118/// User and group IDs can be misleading if the peer side of the socket
119/// has been transfered to another process or the peer has changed privileges.  
120/// PID is almost impossible to use correctly, as the peer might have
121/// terminated and the pid reused, or as for euid, the socket has been sent
122/// to another process (via fd-passing or forking).
123///
124/// What information is received varies from OS to OS:
125///
126/// * Linux, OpenBSD and NetBSD provides process ID, effective user ID
127///   and effective group id.
128/// * macOS, FreeBSD and DragonFly BSD provides effective user ID
129///   and group memberships. (The first group is also the effective group ID.)
130///   [FreeBSD 13+ will also provide process ID](https://www.freebsd.org/cgi/man.cgi?query=unix&sektion=0&manpath=FreeBSD+13-current&format=html).
131/// * Illumos and Solaris provide [more than one could possibly want](https://illumos.org/man/3C/ucred)
132///   (the `LinuxLike` variant is most likely returned).
133///
134/// Current limitations of this crate:
135///
136/// * Illumos and Solaris provides enough information to fill out
137///   both variants, but obviously only one can be returned.
138/// * FreeBSD 13 will also provide pid, but this crate doesn't detect that.
139#[derive(Clone,Copy, PartialEq,Eq)]
140pub enum ConnCredentials 
141{
142    LinuxLike{ pid: NonZeroU32, euid: u32, egid: u32 },
143    MacOsLike{ euid: u32, number_of_groups: u8, groups: [u32; 16/*what libc uses for all OSes*/] },
144}
145
146impl ConnCredentials 
147{
148    /// Get the process ID of the initial peer of a connection.
149    ///
150    /// This is currently only available on Linux & Android,
151    /// but will in the future also be available on OpenBSD and NetBSD,
152    /// and possibly also FreeBSD and Solaris.
153    pub 
154    fn pid(&self) -> Option<NonZeroU32> 
155    {
156        match self 
157        {
158            &ConnCredentials::LinuxLike{ pid, .. } => Some(pid),
159            &ConnCredentials::MacOsLike{ .. } => None,
160        }
161    }
162    /// Get the effective user ID of the initial peer of a connection.
163    ///
164    /// This is provided by any supported OS.
165    pub 
166    fn euid(&self) -> u32 
167    {
168        match self 
169        {
170            &ConnCredentials::LinuxLike{ euid, .. } => euid,
171            &ConnCredentials::MacOsLike{ euid, .. } => euid,
172        }
173    }
174    /// Get the effective group ID of the initial peer of a connection.
175    ///
176    /// * On Linux, Android, OpenBSD and NetBSD,
177    ///   `egid` from the `LinuxLike` variant is returned.
178    /// * On FreeBSD, DragonFly BSD, macOS & other Apple platforms,
179    ///   `groups[0]` from the `MacOsLike` variant is returned
180    ///   (except in the unlikely case that `number_of_groups` is zero).
181    // Sources for that the first group is egid: `<sys/ucred.h>` for
182    // [macOS](https://github.com/apple/darwin-xnu/blob/cc0ca6d1af34cf5daee3673d1b0d770538f19ca5/bsd/sys/ucred.h#L140),
183    // [FreeBSD](https://svnweb.freebsd.org/base/stable/11/sys/sys/ucred.h?revision=331722&view=markup#l93),
184    // [DragonFly BSD](http://gitweb.dragonflybsd.org/dragonfly.git/blob/91dc43dd1215cf13344c65a8f9478bfd31b95814:/sys/sys/ucred.h#l77).
185    // Used by the implementation of `getpeereid()` for
186    // [FreeBSD](https://svnweb.freebsd.org/base/head/lib/libc/gen/getpeereid.c?view=markup),
187    // [DragonFly BSD](http://gitweb.dragonflybsd.org/dragonfly.git/blob/HEAD:/lib/libc/gen/getpeereid.c#l77),
188    // [macOS](https://opensource.apple.com/source/Libc/Libc-1082.50.1/gen/FreeBSD/getpeereid.c.auto.html)
189    // TODO remove None case before 0.2
190    pub 
191    fn egid(&self) -> Option<u32> 
192    {
193        return 
194            match self 
195            {
196                &ConnCredentials::LinuxLike{ egid, .. } => 
197                    Some(egid),
198                &ConnCredentials::MacOsLike{ number_of_groups: 1..=255, groups, .. } => 
199                    Some(groups[0]),
200                &ConnCredentials::MacOsLike{ number_of_groups: 0, .. } => 
201                    None,
202            };
203    }
204
205    /// Get the groups that the initial peer of a connection was a mamber of.
206    ///
207    /// This is only available on FreeBSD and macOS (in the future also
208    /// DragonFly BSD), and an empty slice is returned on other OSes.
209    pub 
210    fn groups(&self) -> &[u32] 
211    {
212        return
213            match self 
214            {
215                &ConnCredentials::LinuxLike{ .. } => 
216                    &[],
217                &ConnCredentials::MacOsLike{ number_of_groups: n @ 0..=15, ref groups, .. } => 
218                    &groups[..(n as usize)],
219                &ConnCredentials::MacOsLike{ number_of_groups: 16..=255, ref groups, .. } => 
220                    groups,
221            };
222    }
223}
224impl fmt::Debug for ConnCredentials 
225{
226    fn fmt(&self,  fmtr: &mut fmt::Formatter) -> Result<(), fmt::Error> 
227    {
228        let mut repr = fmtr.debug_struct("ConnCredentials");
229
230        match self 
231        {
232            &ConnCredentials::LinuxLike{ ref pid, ref euid, ref egid } => 
233            {
234                repr.field("pid", pid);
235                repr.field("euid", euid);
236                repr.field("egid", egid);
237            }
238            &ConnCredentials::MacOsLike{ ref euid, number_of_groups, ref groups } => 
239            {
240                repr.field("euid", euid);
241                let number_of_groups = (number_of_groups as usize).min(groups.len());
242                repr.field("groups", &&groups[..number_of_groups]);
243            }
244        }
245
246        return repr.finish();
247    }
248}
249
250
251#[cfg(any(target_os="linux", target_os="android"))]
252pub 
253fn peer_credentials<FD: AsFd>(conn: FD) -> Result<ConnCredentials, io::Error> 
254{
255    use std::os::fd::AsRawFd;
256
257    let mut ucred: ucred = unsafe { mem::zeroed() };
258
259    let ptr = &mut ucred as *mut ucred as *mut c_void;
260    let mut size = mem::size_of::<ucred>() as socklen_t;
261
262    let res = 
263        unsafe
264        { 
265            getsockopt(conn.as_fd().as_raw_fd(), SOL_SOCKET, SO_PEERCRED, ptr, &mut size)
266        };
267
268    return 
269        if res == -1 
270        {
271            Err(io::Error::last_os_error())
272        } 
273        else if let Some(pid) = NonZeroU32::new(ucred.pid as u32) 
274        {
275            Ok(ConnCredentials::LinuxLike{ pid, euid: ucred.uid as u32, egid: ucred.gid as u32 })
276        } 
277        else 
278        {
279            Err(io::Error::new(NotConnected, "socket is not a connection"))
280        };
281    
282}
283
284#[cfg(any(target_os="freebsd", target_os="dragonfly", target_vendor="apple"))]
285pub 
286fn peer_credentials<FD: AsFd>(conn: FD) -> Result<ConnCredentials, io::Error> 
287{
288    let mut xucred: xucred = unsafe { mem::zeroed() };
289    xucred.cr_version = XUCRED_VERSION;
290    xucred.cr_ngroups = xucred.cr_groups.len() as _;
291
292    // initialize to values that don't signify root, to reduce severity of bugs
293    xucred.cr_uid = !0;
294    for group_slot in &mut xucred.cr_groups 
295    {
296        *group_slot = !0;
297    }
298
299    #[cfg(any(target_os="freebsd", target_os="dragonfly"))]
300    const PEERCRED_SOCKET_LEVEL: i32 = 0; // yes literal zero: not SOL_SOCKET, and SOL_LOCAL is not a thing
301
302    #[cfg(target_vendor="apple")]
303    use SOL_LOCAL as PEERCRED_SOCKET_LEVEL;
304
305    unsafe 
306    {
307        use std::os::fd::AsRawFd;
308
309        let ptr = &mut xucred as *mut xucred as *mut c_void;
310        let mut size = mem::size_of::<xucred>() as socklen_t;
311
312        match getsockopt(conn.as_fd().as_raw_fd(), PEERCRED_SOCKET_LEVEL, LOCAL_PEERCRED, ptr, &mut size) 
313        {
314            -1 => 
315                Err(io::Error::last_os_error()),
316            _ if xucred.cr_version != XUCRED_VERSION => 
317            {
318                Err(io::Error::new(InvalidData, "unknown version of peer credentials"))
319            },
320            _ => 
321            {
322                let mut groups = [u32::max_value(); 16]; // set all unused group slots to ~0
323                let filled_groups = xucred.cr_groups.iter().take(xucred.cr_ngroups as usize);
324                for (&src, dst) in filled_groups.zip(&mut groups) 
325                {
326                    *dst = src.into();
327                }
328
329                return Ok(
330                    ConnCredentials::MacOsLike 
331                    {
332                        euid: xucred.cr_uid.into(),
333                        number_of_groups: xucred.cr_ngroups as u8,
334                        groups: groups,
335                    }
336                )
337            }
338        }
339    }
340}
341
342#[cfg(target_os="openbsd")]
343pub fn peer_credentials<FD: AsFd>(conn: FD) -> Result<ConnCredentials, io::Error> 
344{
345    let mut sockpeercred: sockpeercred = unsafe { mem::zeroed() };
346    unsafe 
347    {
348        let ptr = &mut sockpeercred as *mut sockpeercred as *mut c_void;
349        let mut size = mem::size_of::<sockpeercred>() as socklen_t;
350
351        if getsockopt(conn.as_fd().as_raw_fd(), SOL_SOCKET, SO_PEERCRED, ptr, &mut size) == -1 
352        {
353            return Err(io::Error::last_os_error());
354        } 
355        else if let Some(pid) = NonZeroU32::new(sockpeercred.pid as u32) 
356        {
357            return 
358                Ok(
359                    ConnCredentials::LinuxLike 
360                    {
361                        pid,
362                        euid: sockpeercred.uid as u32,
363                        egid: sockpeercred.gid as u32,
364                    }
365                );
366        } 
367        else 
368        {
369            return Err(io::Error::new(InvalidData, "the returned pid is zero"));
370        }
371    }
372}
373
374#[cfg(target_os="netbsd")]
375pub fn peer_credentials<FD: AsFd>(conn: FD) -> Result<ConnCredentials, io::Error> 
376{
377    let mut unpcbid: unpcbid = unsafe { mem::zeroed() };
378    unsafe 
379    {
380        let ptr = &mut unpcbid as *mut unpcbid as *mut c_void;
381        let mut size = mem::size_of::<unpcbid>() as socklen_t;
382
383        // `man unix` describes it as a socket-level option, but 0 is what works
384        if getsockopt(conn.as_fd().as_raw_fd(), 0, LOCAL_PEEREID, ptr, &mut size) == -1 
385        {
386            return Err(io::Error::last_os_error());
387        } 
388        else if let Some(pid) = NonZeroU32::new(unpcbid.unp_pid as u32) 
389        {
390            return Ok(
391                ConnCredentials::LinuxLike 
392                {
393                    pid,
394                    euid: unpcbid.unp_euid as u32,
395                    egid: unpcbid.unp_egid as u32,
396                }
397            );
398        } 
399        else 
400        {
401            return Err(io::Error::new(InvalidData, "the returned pid is zero"));
402        }
403    }
404}
405
406#[cfg(any(target_os="illumos", target_os="solaris"))]
407pub 
408fn peer_credentials<FD: AsFd>(conn: FD) -> Result<ConnCredentials, io::Error> 
409{
410    struct UcredAlloc(*mut ucred_t);
411
412    impl Drop for UcredAlloc 
413    {
414        fn drop(&mut self) 
415        {
416            unsafe 
417            {
418                if self.0 != ptr::null_mut() 
419                {
420                    ucred_free(self.0);
421                }
422            }
423        }
424    }
425    unsafe 
426    {
427        let mut ucred = UcredAlloc(ptr::null_mut());
428        if getpeerucred(conn, &mut ucred.0) == -1 
429        {
430            return Err(io::Error::last_os_error());
431        } 
432        else if ucred.0 == ptr::null_mut() 
433        {
434            return Err(io::Error::new(NotConnected, "socket is not a connection"));
435        } 
436        else 
437        {
438            let euid = ucred_geteuid(ucred.0 as *const _);
439            let egid = ucred_getegid(ucred.0 as *const _);
440            let pid = ucred_getpid(ucred.0 as *const _);
441            let mut groups_ptr: *const gid_t = ptr::null_mut();
442            let ngroups = ucred_getgroups(ucred.0 as *const _, &mut groups_ptr);
443
444            // https://illumos.org/man/3C/ucred says -1 is returned on error,
445            // but the types in libc are u32
446            if euid != -1i32 as uid_t  &&  egid != -1i32 as gid_t &&  pid != -1i32 as pid_t  &&  pid != 0 
447            {
448                return Ok(
449                    ConnCredentials::LinuxLike 
450                    {
451                        pid: NonZeroU32::new(pid as u32).unwrap(), // already checked
452                        euid: euid as u32,
453                        egid: egid as u32,
454                    }
455                );
456            } 
457            else if euid != -1i32 as uid_t  &&  ngroups > 0  &&  groups_ptr != ptr::null() 
458            {
459                let mut groups = [u32::max_value(); 16];
460                let number_of_groups = ngroups.min(16) as u8;
461
462                
463                for (i, group) in groups[0..number_of_groups].iter_mut().enumerate()
464                {
465                    *group = *groups_ptr.offset(i as isize);
466                    //groups[i as usize] = *groups_ptr.offset(i as isize);
467                }
468
469                return Ok(
470                    ConnCredentials::MacOsLike 
471                    {
472                        euid: euid as u32,
473                        number_of_groups,
474                        groups,
475                    }
476                );
477            } 
478            else if euid != -1i32 as uid_t  &&  egid != -1i32 as gid_t 
479            {
480                let mut groups = [u32::max_value(); 16];
481                groups[0] = egid as u32;
482
483                return Ok(
484                    ConnCredentials::MacOsLike 
485                    {
486                        euid: euid as u32,
487                        number_of_groups: 1,
488                        groups,
489                    }
490                );
491            } 
492            else 
493            {
494                return Err(io::Error::new(Other, "Not enough information was available"));
495            }
496        }
497    }
498}
499
500#[cfg(not(any(
501    target_os="linux", target_os="android",
502    target_os="freebsd", target_os="dragonfly", target_vendor="apple",
503    target_os="openbsd", target_os="netbsd",
504    target_os="illumos", target_os="solaris",
505)))]
506pub 
507fn peer_credentials<FD: AsFd>(_conn: FD) -> Result<ConnCredentials, io::Error> 
508{
509    Err(io::Error::new(Other, "Not available"))
510}
511
512
513
514#[cfg(any(target_os="linux", target_os="android"))]
515pub type RawReceivedCredentials = libc::ucred;
516
517
518/// Process credentials received through `recv_ancillary()`.
519///
520/// What information is returned varies from OS to OS:
521///
522/// * On Linux (& Android) the information has to be explicitly sent by the
523///   peer through `send_ancillary()` or `sendmsg()`, but is validated by the
524///   kernel.  
525///   Peer chooses whether to send effective or real uid or gid, unless root
526///   in which case it can send whatever it wants.
527/// * On FreeBSD, NetBSD, DragonFly BSD, Illumos and likely macOS it is provided
528///   by the OS automatically when the socket option is set.
529/// * OpenBSD doesn't appear to support receiving credentials.
530#[derive(Clone,Copy, PartialEq,Eq,Hash, Debug)]
531pub struct ReceivedCredentials 
532{
533    #[cfg(any(target_os="linux", target_os="android", target_os="dragonfly"))]
534    pid: u32,
535    #[cfg(any(target_os="linux", target_os="android"))]
536    uid: u32,
537    #[cfg(any(target_os="linux", target_os="android"))]
538    gid: u32,
539
540    #[cfg(any(
541        target_os="freebsd", target_os="netbsd", target_os="dragonfly",
542        target_os="illumos", target_os="solaris", target_os="macos",
543    ))]
544    real_uid: u32,
545    #[cfg(any(
546        target_os="freebsd", target_os="netbsd", target_os="dragonfly",
547        target_os="illumos", target_os="solaris", target_os="macos",
548    ))]
549    effective_uid: u32,
550    #[cfg(any(
551        target_os="freebsd", target_os="netbsd", target_os="dragonfly",
552        target_os="illumos", target_os="solaris", target_os="macos",
553    ))]
554    real_gid: u32,
555    #[cfg(any(
556        target_os="freebsd", target_os="netbsd",
557        target_os="illumos", target_os="solaris", target_os="macos",
558    ))]
559    effective_gid: u32,
560    #[cfg(any(
561        target_os="freebsd", target_os="netbsd", target_os="dragonfly",
562        target_os="illumos", target_os="solaris", target_os="macos",
563    ))]
564    groups: [u32; 5],
565}
566
567#[allow(unused)] // TODO
568impl ReceivedCredentials 
569{
570    #[cfg(any(target_os="linux", target_os="android"))]
571    pub(crate) 
572    fn from_raw(creds: libc::ucred) -> Self 
573    {
574        ReceivedCredentials 
575        {
576            pid: creds.pid as u32,
577            uid: creds.uid as u32,
578            gid: creds.gid as u32,
579        }
580    }
581
582    /// The pid of the peer.
583    ///
584    /// This information is only available on Linux, Android and DragonFly BSD.
585    pub 
586    fn pid(&self) -> Option<u32> 
587    {
588        #[cfg(any(target_os="linux", target_os="android", target_os="dragonfly"))] 
589        {
590            Some(self.pid)
591        }
592        #[cfg(not(any(target_os="linux", target_os="android", target_os="dragonfly")))] 
593        {
594            None
595        }
596    }
597
598    pub 
599    fn effective_or_sent_uid(&self) -> u32 
600    {
601        #[cfg(any(target_os="linux", target_os="android"))] 
602        {
603            self.uid
604        }
605
606        #[cfg(any(
607            target_os="freebsd", target_os="netbsd", target_os="dragonfly",
608            target_os="illumos", target_os="solaris", target_os="macos",
609        ))] 
610        {
611            self.effective_uid
612        }
613
614        #[cfg(not(any(
615            target_os="linux", target_os="android",
616            target_os="freebsd", target_os="netbsd", target_os="dragonfly",
617            target_os="illumos", target_os="solaris", target_os="macos",
618        )))] 
619        {
620            unreachable!("struct cannot be created on unsupported OSes")
621        }
622    }
623
624    pub 
625    fn real_or_sent_uid(&self) -> u32 
626    {
627        #[cfg(any(target_os="linux", target_os="android"))] 
628        {
629            self.uid
630        }
631        #[cfg(any(
632            target_os="freebsd", target_os="netbsd", target_os="dragonfly",
633            target_os="illumos", target_os="solaris", target_os="macos",
634        ))] 
635        {
636            self.real_uid
637        }
638        #[cfg(not(any(
639            target_os="linux", target_os="android",
640            target_os="freebsd", target_os="netbsd", target_os="dragonfly",
641            target_os="illumos", target_os="solaris", target_os="macos",
642        )))] 
643        {
644            unreachable!("struct cannot be created on unsupported OSes")
645        }
646    }
647    pub fn effective_or_sent_gid(&self) -> Option<u32> {
648        #[cfg(any(target_os="linux", target_os="android"))] {
649            Some(self.gid)
650        }
651        #[cfg(any(
652            target_os="freebsd", target_os="netbsd",
653            target_os="illumos", target_os="solaris", target_os="macos",
654        ))] {
655            Some(self.effective_gid)
656        }
657        #[cfg(not(any(
658            target_os="linux", target_os="android",
659            target_os="freebsd", target_os="netbsd",
660            target_os="illumos", target_os="solaris", target_os="macos",
661        )))] {
662            None
663        }
664    }
665    
666    pub fn real_or_sent_gid(&self) -> u32 
667    {
668        #[cfg(any(target_os="linux", target_os="android"))] 
669        {
670            self.gid
671        }
672        #[cfg(any(
673            target_os="freebsd", target_os="netbsd", target_os="dragonfly",
674            target_os="illumos", target_os="solaris", target_os="macos",
675        ))] 
676        {
677            self.real_gid
678        }
679        #[cfg(not(any(
680            target_os="linux", target_os="android",
681            target_os="freebsd", target_os="netbsd", target_os="dragonfly",
682            target_os="illumos", target_os="solaris", target_os="macos",
683        )))] 
684        {
685            unreachable!("struct cannot be created on unsupported OSes")
686        }
687    }
688    /// Get the peer's group memberships.
689    ///
690    /// This information is only available on macOS, the BSDs and and Illumos.
691    /// On other operating systems an empty slice is returned.
692    pub 
693    fn groups(&self) -> &[u32] 
694    {
695        #[cfg(any(
696            target_os="freebsd", target_os="netbsd", target_os="dragonfly",
697            target_os="illumos", target_os="solaris", target_os="macos",
698        ))] 
699        {
700            &self.groups[..]
701        }
702        #[cfg(not(any(
703            target_os="freebsd", target_os="netbsd", target_os="dragonfly",
704            target_os="illumos", target_os="solaris", target_os="macos",
705        )))] 
706        {
707            &[]
708        }
709    }
710}