Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/backend/libc/net/sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ use crate::net::Protocol;
target_env = "newlib"
))]
use crate::net::RawProtocol;
#[cfg(linux_kernel)]
use crate::net::SocketAddrV6;
use crate::net::{Ipv4Addr, Ipv6Addr, SocketType};
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
use crate::net::{SocketAddrAny, SocketAddrStorage, SocketAddrV4};
#[cfg(linux_kernel)]
use crate::net::{SocketAddrV6, UCred};
use crate::utils::as_mut_ptr;
#[cfg(feature = "alloc")]
#[cfg(any(
Expand Down Expand Up @@ -948,6 +948,12 @@ pub(crate) fn get_tcp_cork(fd: BorrowedFd<'_>) -> io::Result<bool> {
getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(to_bool)
}

#[cfg(linux_kernel)]
#[inline]
pub(crate) fn get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> {
getsockopt(fd, c::SOL_SOCKET as _, c::SO_PEERCRED)
}

#[inline]
fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
c::ip_mreq {
Expand Down
7 changes: 7 additions & 0 deletions src/backend/linux_raw/net/sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::fd::BorrowedFd;
use crate::ffi::CStr;
use crate::io;
use crate::net::sockopt::Timeout;
use crate::net::UCred;
use crate::net::{
AddressFamily, Ipv4Addr, Ipv6Addr, Protocol, RawProtocol, SocketAddrAny, SocketAddrStorage,
SocketAddrV4, SocketAddrV6, SocketType,
Expand Down Expand Up @@ -793,6 +794,12 @@ pub(crate) fn set_tcp_cork(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
pub(crate) fn get_tcp_cork(fd: BorrowedFd<'_>) -> io::Result<bool> {
getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(to_bool)
}

#[inline]
pub(crate) fn get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> {
getsockopt(fd, c::SOL_SOCKET as _, linux_raw_sys::net::SO_PEERCRED)
}

#[inline]
fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
c::ip_mreq {
Expand Down
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,8 @@ mod clockid;
feature = "runtime",
feature = "termios",
feature = "thread",
all(bsd, feature = "event")
all(bsd, feature = "event"),
all(linux_kernel, feature = "net")
))]
mod pid;
#[cfg(any(feature = "process", feature = "thread"))]
Expand Down Expand Up @@ -378,6 +379,7 @@ mod timespec;
feature = "time",
target_arch = "x86",
)
)
),
all(linux_kernel, feature = "net")
))]
mod ugid;
31 changes: 31 additions & 0 deletions src/net/send_recv/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use crate::backend::{self, c};
use crate::fd::{AsFd, BorrowedFd, OwnedFd};
use crate::io::{self, IoSlice, IoSliceMut};
#[cfg(linux_kernel)]
use crate::net::UCred;

use core::iter::FusedIterator;
use core::marker::PhantomData;
Expand All @@ -22,6 +24,11 @@ macro_rules! cmsg_space {
$len * ::core::mem::size_of::<$crate::fd::BorrowedFd<'static>>(),
)
};
(ScmCredentials($len:expr)) => {
$crate::net::__cmsg_space(
$len * ::core::mem::size_of::<$crate::net::UCred>(),
)
};

// Combo Rules
(($($($x:tt)*),+)) => {
Expand All @@ -43,6 +50,9 @@ pub fn __cmsg_space(len: usize) -> usize {
pub enum SendAncillaryMessage<'slice, 'fd> {
/// Send file descriptors.
ScmRights(&'slice [BorrowedFd<'fd>]),
/// Send process credentials.
#[cfg(linux_kernel)]
ScmCredentials(UCred),
}

impl SendAncillaryMessage<'_, '_> {
Expand All @@ -52,6 +62,8 @@ impl SendAncillaryMessage<'_, '_> {
pub fn size(&self) -> usize {
let total_bytes = match self {
Self::ScmRights(slice) => size_of_val(*slice),
#[cfg(linux_kernel)]
Self::ScmCredentials(ucred) => size_of_val(ucred),
};

unsafe {
Expand All @@ -69,6 +81,9 @@ impl SendAncillaryMessage<'_, '_> {
pub enum RecvAncillaryMessage<'a> {
/// Received file descriptors.
ScmRights(AncillaryIter<'a, OwnedFd>),
/// Received process credentials.
#[cfg(linux_kernel)]
ScmCredentials(UCred),
}

/// Buffer for sending ancillary messages with [`sendmsg`], [`sendmsg_v4`],
Expand Down Expand Up @@ -139,6 +154,13 @@ impl<'buf, 'slice, 'fd> SendAncillaryBuffer<'buf, 'slice, 'fd> {
unsafe { slice::from_raw_parts(fds.as_ptr().cast::<u8>(), size_of_val(fds)) };
self.push_ancillary(fds_bytes, c::SOL_SOCKET as _, c::SCM_RIGHTS as _)
}
#[cfg(linux_kernel)]
SendAncillaryMessage::ScmCredentials(ucred) => {
let ucred_bytes = unsafe {
slice::from_raw_parts(&ucred as *const _ as *const u8, size_of_val(&ucred))
};
self.push_ancillary(ucred_bytes, c::SOL_SOCKET as _, c::SCM_CREDENTIALS as _)
}
}
}

Expand Down Expand Up @@ -316,6 +338,15 @@ impl<'buf> AncillaryDrain<'buf> {

Some(RecvAncillaryMessage::ScmRights(fds))
}
#[cfg(linux_kernel)]
(c::SOL_SOCKET, c::SCM_CREDENTIALS) => {
if payload_len >= size_of::<UCred>() {
let ucred = payload.as_ptr().cast::<UCred>().read_unaligned();
Some(RecvAncillaryMessage::ScmCredentials(ucred))
} else {
None
}
}
_ => None,
}
}
Expand Down
12 changes: 12 additions & 0 deletions src/net/sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,18 @@ pub fn get_tcp_cork<Fd: AsFd>(fd: Fd) -> io::Result<bool> {
backend::net::sockopt::get_tcp_cork(fd.as_fd())
}

/// Get credentials of Unix domain socket peer process
///
/// # References
/// - [Linux `unix`]
///
/// [Linux `unix`]: https://man7.org/linux/man-pages/man7/unix.7.html
#[cfg(linux_kernel)]
#[doc(alias = "SO_PEERCRED")]
pub fn get_socket_peercred<Fd: AsFd>(fd: Fd) -> io::Result<super::UCred> {
backend::net::sockopt::get_socket_peercred(fd.as_fd())
}

#[test]
fn test_sizes() {
use c::c_int;
Expand Down
22 changes: 22 additions & 0 deletions src/net/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1336,6 +1336,25 @@ bitflags! {
}
}

/// UNIX credentials of socket peer, for use with [`get_socket_peercred`]
/// [`SendAncillaryMessage::ScmCredentials`] and
/// [`RecvAncillaryMessage::ScmCredentials`].
///
/// [`get_socket_peercred`]: crate::net::sockopt::get_socket_peercred
/// [`SendAncillaryMessage::ScmCredentials`]: crate::net::SendAncillaryMessage::ScmCredentials
/// [`RecvAncillaryMessage::ScmCredentials`]: crate::net::RecvAncillaryMessage::ScmCredentials
#[cfg(linux_kernel)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[repr(C)]
pub struct UCred {
/// Process ID of peer
pub pid: crate::pid::Pid,
/// User ID of peer
pub uid: crate::ugid::Uid,
/// Group ID of peer
pub gid: crate::ugid::Gid,
}

#[test]
fn test_sizes() {
use c::c_int;
Expand All @@ -1361,4 +1380,7 @@ fn test_sizes() {
let t: Option<Protocol> = Some(Protocol::from_raw(RawProtocol::new(4567).unwrap()));
assert_eq!(4567_u32, transmute::<Option<Protocol>, u32>(t));
}

#[cfg(linux_kernel)]
assert_eq_size!(UCred, libc::ucred);
}
56 changes: 56 additions & 0 deletions tests/net/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,3 +449,59 @@ fn test_unix_msg_with_scm_rights() {
client.join().unwrap();
server.join().unwrap();
}

#[cfg(all(feature = "process", linux_kernel))]
#[test]
fn test_unix_peercred() {
use rustix::io::{IoSlice, IoSliceMut};
use rustix::net::{
recvmsg, sendmsg, sockopt, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags,
SendAncillaryBuffer, SendAncillaryMessage, SendFlags, SocketFlags,
};
use rustix::process::{getgid, getpid, getuid};

let (send_sock, recv_sock) = rustix::net::socketpair(
AddressFamily::UNIX,
SocketType::STREAM,
SocketFlags::CLOEXEC,
None,
)
.unwrap();

sockopt::set_socket_passcred(&recv_sock, true).unwrap();

let ucred = sockopt::get_socket_peercred(&send_sock).unwrap();
assert_eq!(ucred.pid, getpid());
assert_eq!(ucred.uid, getuid());
assert_eq!(ucred.gid, getgid());

let msg = SendAncillaryMessage::ScmCredentials(ucred);
let mut space = vec![0; msg.size()];
let mut cmsg_buffer = SendAncillaryBuffer::new(&mut space);
assert!(cmsg_buffer.push(msg));

sendmsg(
&send_sock,
&[IoSlice::new(b"cred")],
&mut cmsg_buffer,
SendFlags::empty(),
)
.unwrap();

let mut cmsg_space = vec![0; rustix::cmsg_space!(ScmCredentials(1))];
let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut cmsg_space);

let mut buffer = vec![0; BUFFER_SIZE];
recvmsg(
&recv_sock,
&mut [IoSliceMut::new(&mut buffer)],
&mut cmsg_buffer,
RecvFlags::empty(),
)
.unwrap();

match cmsg_buffer.drain().next().unwrap() {
RecvAncillaryMessage::ScmCredentials(ucred2) => assert_eq!(ucred2, ucred),
_ => panic!("Unexpected ancilliary message"),
};
}