Auto merge of #75148 - joechrisellis:master, r=Amanieu
Implementation of peer credentials for Unix sockets The code in `ucred.rs` is based on the work done in [PR 13](https://github.com/tokio-rs/tokio-uds/pull/13) in the tokio-uds repository on GitHub. This commit is effectively a port to the stdlib, so credit to Martin Habovštiak (`@Kixunil)` and contributors for the meat of this work. 🥇 Happy to make changes as needed. 🙂
This commit is contained in:
commit
a874956d94
@ -37,6 +37,18 @@
|
||||
pub mod raw;
|
||||
pub mod thread;
|
||||
|
||||
#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
pub mod ucred;
|
||||
|
||||
/// A prelude for conveniently writing platform-specific code.
|
||||
///
|
||||
/// Includes all extension traits, and some important type definitions.
|
||||
|
@ -30,6 +30,29 @@ mod libc {
|
||||
use crate::sys_common::{self, AsInner, FromInner, IntoInner};
|
||||
use crate::time::Duration;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
use crate::os::unix::ucred;
|
||||
|
||||
#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
pub use ucred::UCred;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "android",
|
||||
@ -405,6 +428,34 @@ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) })
|
||||
}
|
||||
|
||||
/// Gets the peer credentials for this Unix domain socket.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(peer_credentials_unix_socket)]
|
||||
/// use std::os::unix::net::UnixStream;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let socket = UnixStream::connect("/tmp/sock")?;
|
||||
/// let peer_cred = socket.peer_cred().expect("Couldn't get peer credentials");
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
pub fn peer_cred(&self) -> io::Result<UCred> {
|
||||
ucred::peer_cred(self)
|
||||
}
|
||||
|
||||
/// Sets the read timeout for the socket.
|
||||
///
|
||||
/// If the provided value is [`None`], then [`read`] calls will block
|
||||
|
97
library/std/src/sys/unix/ext/ucred.rs
Normal file
97
library/std/src/sys/unix/ext/ucred.rs
Normal file
@ -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>,
|
||||
}
|
||||
|
||||
#[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 };
|
||||
|
||||
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>() {
|
||||
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()) }
|
||||
}
|
||||
}
|
||||
}
|
25
library/std/src/sys/unix/ext/ucred/tests.rs
Normal file
25
library/std/src/sys/unix/ext/ucred/tests.rs
Normal file
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user