diff --git a/src/backend/libc/net/sockopt.rs b/src/backend/libc/net/sockopt.rs index 39e4ee93b..34d7fda3c 100644 --- a/src/backend/libc/net/sockopt.rs +++ b/src/backend/libc/net/sockopt.rs @@ -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( @@ -948,6 +948,12 @@ pub(crate) fn get_tcp_cork(fd: BorrowedFd<'_>) -> io::Result { 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 { + getsockopt(fd, c::SOL_SOCKET as _, c::SO_PEERCRED) +} + #[inline] fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq { c::ip_mreq { diff --git a/src/backend/linux_raw/net/sockopt.rs b/src/backend/linux_raw/net/sockopt.rs index a4bd321d1..a3238f21f 100644 --- a/src/backend/linux_raw/net/sockopt.rs +++ b/src/backend/linux_raw/net/sockopt.rs @@ -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, @@ -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 { getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(to_bool) } + +#[inline] +pub(crate) fn get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result { + 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 { diff --git a/src/lib.rs b/src/lib.rs index 9cc254b07..7bf178097 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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"))] @@ -378,6 +379,7 @@ mod timespec; feature = "time", target_arch = "x86", ) - ) + ), + all(linux_kernel, feature = "net") ))] mod ugid; diff --git a/src/net/send_recv/msg.rs b/src/net/send_recv/msg.rs index 0409bb0af..213c93ae4 100644 --- a/src/net/send_recv/msg.rs +++ b/src/net/send_recv/msg.rs @@ -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; @@ -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)*),+)) => { @@ -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<'_, '_> { @@ -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 { @@ -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`], @@ -139,6 +154,13 @@ impl<'buf, 'slice, 'fd> SendAncillaryBuffer<'buf, 'slice, 'fd> { unsafe { slice::from_raw_parts(fds.as_ptr().cast::(), 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 _) + } } } @@ -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::() { + let ucred = payload.as_ptr().cast::().read_unaligned(); + Some(RecvAncillaryMessage::ScmCredentials(ucred)) + } else { + None + } + } _ => None, } } diff --git a/src/net/sockopt.rs b/src/net/sockopt.rs index 30e37862e..f43a75e1c 100644 --- a/src/net/sockopt.rs +++ b/src/net/sockopt.rs @@ -1346,6 +1346,18 @@ pub fn get_tcp_cork(fd: Fd) -> io::Result { 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: Fd) -> io::Result { + backend::net::sockopt::get_socket_peercred(fd.as_fd()) +} + #[test] fn test_sizes() { use c::c_int; diff --git a/src/net/types.rs b/src/net/types.rs index 53116f9c8..e31cdbcd4 100644 --- a/src/net/types.rs +++ b/src/net/types.rs @@ -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; @@ -1361,4 +1380,7 @@ fn test_sizes() { let t: Option = Some(Protocol::from_raw(RawProtocol::new(4567).unwrap())); assert_eq!(4567_u32, transmute::, u32>(t)); } + + #[cfg(linux_kernel)] + assert_eq_size!(UCred, libc::ucred); } diff --git a/tests/net/unix.rs b/tests/net/unix.rs index c80c1f2bb..c0f1bfc49 100644 --- a/tests/net/unix.rs +++ b/tests/net/unix.rs @@ -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"), + }; +}