| 1 | // NOTE: Code in this file is heavily based on work done in PR 13 from the tokio-uds repository on |
| 2 | // GitHub. |
| 3 | // |
| 4 | // For reference, the link is here: https://github.com/tokio-rs/tokio-uds/pull/13 |
| 5 | // Credit to Martin Habovštiak (GitHub username Kixunil) and contributors for this work. |
| 6 | |
| 7 | use libc::{gid_t, pid_t, uid_t}; |
| 8 | |
| 9 | /// Credentials for a UNIX process for credentials passing. |
| 10 | #[unstable (feature = "peer_credentials_unix_socket" , issue = "42839" , reason = "unstable" )] |
| 11 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq)] |
| 12 | pub struct UCred { |
| 13 | /// The UID part of the peer credential. This is the effective UID of the process at the domain |
| 14 | /// socket's endpoint. |
| 15 | pub uid: uid_t, |
| 16 | /// The GID part of the peer credential. This is the effective GID of the process at the domain |
| 17 | /// socket's endpoint. |
| 18 | pub gid: gid_t, |
| 19 | /// The PID part of the peer credential. This field is optional because the PID part of the |
| 20 | /// peer credentials is not supported on every platform. On platforms where the mechanism to |
| 21 | /// discover the PID exists, this field will be populated to the PID of the process at the |
| 22 | /// domain socket's endpoint. Otherwise, it will be set to None. |
| 23 | pub pid: Option<pid_t>, |
| 24 | } |
| 25 | |
| 26 | #[cfg (target_vendor = "apple" )] |
| 27 | pub(super) use self::impl_apple::peer_cred; |
| 28 | #[cfg (any( |
| 29 | target_os = "dragonfly" , |
| 30 | target_os = "freebsd" , |
| 31 | target_os = "openbsd" , |
| 32 | target_os = "netbsd" , |
| 33 | target_os = "nto" |
| 34 | ))] |
| 35 | pub(super) use self::impl_bsd::peer_cred; |
| 36 | #[cfg (any(target_os = "android" , target_os = "linux" , target_os = "cygwin" ))] |
| 37 | pub(super) use self::impl_linux::peer_cred; |
| 38 | |
| 39 | #[cfg (any(target_os = "linux" , target_os = "android" , target_os = "cygwin" ))] |
| 40 | mod impl_linux { |
| 41 | use libc::{SO_PEERCRED, SOL_SOCKET, c_void, getsockopt, socklen_t, ucred}; |
| 42 | |
| 43 | use super::UCred; |
| 44 | use crate::io; |
| 45 | use crate::os::unix::io::AsRawFd; |
| 46 | use crate::os::unix::net::UnixStream; |
| 47 | |
| 48 | pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> { |
| 49 | let ucred_size = size_of::<ucred>(); |
| 50 | |
| 51 | // Trivial sanity checks. |
| 52 | assert!(size_of::<u32>() <= size_of::<usize>()); |
| 53 | assert!(ucred_size <= u32::MAX as usize); |
| 54 | |
| 55 | let mut ucred_size = ucred_size as socklen_t; |
| 56 | let mut ucred: ucred = ucred { pid: 1, uid: 1, gid: 1 }; |
| 57 | |
| 58 | unsafe { |
| 59 | let ret = getsockopt( |
| 60 | socket.as_raw_fd(), |
| 61 | SOL_SOCKET, |
| 62 | SO_PEERCRED, |
| 63 | (&raw mut ucred) as *mut c_void, |
| 64 | &mut ucred_size, |
| 65 | ); |
| 66 | |
| 67 | if ret == 0 && ucred_size as usize == size_of::<ucred>() { |
| 68 | Ok(UCred { uid: ucred.uid, gid: ucred.gid, pid: Some(ucred.pid) }) |
| 69 | } else { |
| 70 | Err(io::Error::last_os_error()) |
| 71 | } |
| 72 | } |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | #[cfg (any( |
| 77 | target_os = "dragonfly" , |
| 78 | target_os = "freebsd" , |
| 79 | target_os = "openbsd" , |
| 80 | target_os = "netbsd" , |
| 81 | target_os = "nto" , |
| 82 | ))] |
| 83 | mod impl_bsd { |
| 84 | use super::UCred; |
| 85 | use crate::io; |
| 86 | use crate::os::unix::io::AsRawFd; |
| 87 | use crate::os::unix::net::UnixStream; |
| 88 | |
| 89 | pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> { |
| 90 | let mut cred = UCred { uid: 1, gid: 1, pid: None }; |
| 91 | unsafe { |
| 92 | let ret = libc::getpeereid(socket.as_raw_fd(), &mut cred.uid, &mut cred.gid); |
| 93 | |
| 94 | if ret == 0 { Ok(cred) } else { Err(io::Error::last_os_error()) } |
| 95 | } |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | #[cfg (target_vendor = "apple" )] |
| 100 | mod impl_apple { |
| 101 | use libc::{LOCAL_PEERPID, SOL_LOCAL, c_void, getpeereid, getsockopt, pid_t, socklen_t}; |
| 102 | |
| 103 | use super::UCred; |
| 104 | use crate::io; |
| 105 | use crate::os::unix::io::AsRawFd; |
| 106 | use crate::os::unix::net::UnixStream; |
| 107 | |
| 108 | pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> { |
| 109 | let mut cred = UCred { uid: 1, gid: 1, pid: None }; |
| 110 | unsafe { |
| 111 | let ret = getpeereid(socket.as_raw_fd(), &mut cred.uid, &mut cred.gid); |
| 112 | |
| 113 | if ret != 0 { |
| 114 | return Err(io::Error::last_os_error()); |
| 115 | } |
| 116 | |
| 117 | let mut pid: pid_t = 1; |
| 118 | let mut pid_size = size_of::<pid_t>() as socklen_t; |
| 119 | |
| 120 | let ret = getsockopt( |
| 121 | socket.as_raw_fd(), |
| 122 | SOL_LOCAL, |
| 123 | LOCAL_PEERPID, |
| 124 | (&raw mut pid) as *mut c_void, |
| 125 | &mut pid_size, |
| 126 | ); |
| 127 | |
| 128 | if ret == 0 && pid_size as usize == size_of::<pid_t>() { |
| 129 | cred.pid = Some(pid); |
| 130 | Ok(cred) |
| 131 | } else { |
| 132 | Err(io::Error::last_os_error()) |
| 133 | } |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | |