-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Implementation of peer credentials for Unix sockets #75148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5bc8b18
ed20eff
a9ec61d
be2637a
cbcf387
40a8303
7c20be3
fa697df
72eef61
68ff495
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
//! Unix peer credentials. | ||
|
||
// NOTE: Code in this file is heavily based on work done in PR 13 from the tokio-uds repository on | ||
// GitHub. | ||
// | ||
// For reference, the link is here: https://github.com/tokio-rs/tokio-uds/pull/13 | ||
// Credit to Martin Habovštiak (GitHub username Kixunil) and contributors for this work. | ||
|
||
use libc::{gid_t, pid_t, uid_t}; | ||
|
||
/// Credentials for a UNIX process for credentials passing. | ||
#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")] | ||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] | ||
pub struct UCred { | ||
/// The UID part of the peer credential. This is the effective UID of the process at the domain | ||
/// socket's endpoint. | ||
pub uid: uid_t, | ||
/// The GID part of the peer credential. This is the effective GID of the process at the domain | ||
/// socket's endpoint. | ||
pub gid: gid_t, | ||
/// The PID part of the peer credential. This field is optional because the PID part of the | ||
/// peer credentials is not supported on every platform. On platforms where the mechanism to | ||
/// discover the PID exists, this field will be populated to the PID of the process at the | ||
/// domain socket's endpoint. Otherwise, it will be set to None. | ||
pub pid: Option<pid_t>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we get doc comments for each of the public fields? In particular the |
||
} | ||
|
||
#[cfg(any(target_os = "android", target_os = "linux"))] | ||
pub use self::impl_linux::peer_cred; | ||
|
||
#[cfg(any( | ||
target_os = "dragonfly", | ||
target_os = "freebsd", | ||
target_os = "ios", | ||
target_os = "macos", | ||
target_os = "openbsd" | ||
))] | ||
pub use self::impl_bsd::peer_cred; | ||
|
||
#[cfg(any(target_os = "linux", target_os = "android"))] | ||
pub mod impl_linux { | ||
use super::UCred; | ||
use crate::os::unix::io::AsRawFd; | ||
use crate::os::unix::net::UnixStream; | ||
use crate::{io, mem}; | ||
use libc::{c_void, getsockopt, socklen_t, ucred, SOL_SOCKET, SO_PEERCRED}; | ||
|
||
pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> { | ||
let ucred_size = mem::size_of::<ucred>(); | ||
|
||
// Trivial sanity checks. | ||
assert!(mem::size_of::<u32>() <= mem::size_of::<usize>()); | ||
assert!(ucred_size <= u32::MAX as usize); | ||
|
||
let mut ucred_size = ucred_size as socklen_t; | ||
let mut ucred: ucred = ucred { pid: 1, uid: 1, gid: 1 }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oooh, nice defensive programming using |
||
|
||
unsafe { | ||
let ret = getsockopt( | ||
socket.as_raw_fd(), | ||
SOL_SOCKET, | ||
SO_PEERCRED, | ||
&mut ucred as *mut ucred as *mut c_void, | ||
&mut ucred_size, | ||
); | ||
|
||
if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() { | ||
This comment was marked as resolved.
Sorry, something went wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. 😄 |
||
Ok(UCred { uid: ucred.uid, gid: ucred.gid, pid: Some(ucred.pid) }) | ||
} else { | ||
Err(io::Error::last_os_error()) | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[cfg(any( | ||
target_os = "dragonfly", | ||
target_os = "macos", | ||
target_os = "ios", | ||
target_os = "freebsd", | ||
target_os = "openbsd" | ||
))] | ||
pub mod impl_bsd { | ||
use super::UCred; | ||
use crate::io; | ||
use crate::os::unix::io::AsRawFd; | ||
use crate::os::unix::net::UnixStream; | ||
|
||
pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> { | ||
let mut cred = UCred { uid: 1, gid: 1, pid: None }; | ||
unsafe { | ||
let ret = libc::getpeereid(socket.as_raw_fd(), &mut cred.uid, &mut cred.gid); | ||
|
||
if ret == 0 { Ok(cred) } else { Err(io::Error::last_os_error()) } | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
use crate::os::unix::net::UnixStream; | ||
use libc::{getegid, geteuid}; | ||
|
||
#[test] | ||
#[cfg(any( | ||
target_os = "android", | ||
target_os = "linux", | ||
target_os = "dragonfly", | ||
target_os = "freebsd", | ||
target_os = "ios", | ||
target_os = "macos", | ||
target_os = "openbsd" | ||
))] | ||
fn test_socket_pair() { | ||
// Create two connected sockets and get their peer credentials. They should be equal. | ||
let (sock_a, sock_b) = UnixStream::pair().unwrap(); | ||
let (cred_a, cred_b) = (sock_a.peer_cred().unwrap(), sock_b.peer_cred().unwrap()); | ||
assert_eq!(cred_a, cred_b); | ||
|
||
// Check that the UID and GIDs match up. | ||
let uid = unsafe { geteuid() }; | ||
let gid = unsafe { getegid() }; | ||
assert_eq!(cred_a.uid, uid); | ||
assert_eq!(cred_a.gid, gid); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
something_t
types are not used instd
often (at all?), is this a good idea?Personally I'd love seeing use of newtypes as done in
nix
, but not sure if that should go tostd
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did a quick
grep
of the codebase and I can see a few places where these are used... I'll leave it as it is for now! If you would strongly like this to change feel free to follow up. 🙂