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