Skip to content

Commit 62afd55

Browse files
authored
Add wrappers for SO_PEERCRED and SCM_CREDENTIALS (#851)
1 parent a3c3398 commit 62afd55

7 files changed

Lines changed: 140 additions & 4 deletions

File tree

src/backend/libc/net/sockopt.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ use crate::net::Protocol;
4444
target_env = "newlib"
4545
))]
4646
use crate::net::RawProtocol;
47-
#[cfg(linux_kernel)]
48-
use crate::net::SocketAddrV6;
4947
use crate::net::{Ipv4Addr, Ipv6Addr, SocketType};
5048
#[cfg(any(linux_kernel, target_os = "fuchsia"))]
5149
use crate::net::{SocketAddrAny, SocketAddrStorage, SocketAddrV4};
50+
#[cfg(linux_kernel)]
51+
use crate::net::{SocketAddrV6, UCred};
5252
use crate::utils::as_mut_ptr;
5353
#[cfg(feature = "alloc")]
5454
#[cfg(any(
@@ -948,6 +948,12 @@ pub(crate) fn get_tcp_cork(fd: BorrowedFd<'_>) -> io::Result<bool> {
948948
getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(to_bool)
949949
}
950950

951+
#[cfg(linux_kernel)]
952+
#[inline]
953+
pub(crate) fn get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> {
954+
getsockopt(fd, c::SOL_SOCKET as _, c::SO_PEERCRED)
955+
}
956+
951957
#[inline]
952958
fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
953959
c::ip_mreq {

src/backend/linux_raw/net/sockopt.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::fd::BorrowedFd;
1212
use crate::ffi::CStr;
1313
use crate::io;
1414
use crate::net::sockopt::Timeout;
15+
use crate::net::UCred;
1516
use crate::net::{
1617
AddressFamily, Ipv4Addr, Ipv6Addr, Protocol, RawProtocol, SocketAddrAny, SocketAddrStorage,
1718
SocketAddrV4, SocketAddrV6, SocketType,
@@ -793,6 +794,12 @@ pub(crate) fn set_tcp_cork(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
793794
pub(crate) fn get_tcp_cork(fd: BorrowedFd<'_>) -> io::Result<bool> {
794795
getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(to_bool)
795796
}
797+
798+
#[inline]
799+
pub(crate) fn get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> {
800+
getsockopt(fd, c::SOL_SOCKET as _, linux_raw_sys::net::SO_PEERCRED)
801+
}
802+
796803
#[inline]
797804
fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
798805
c::ip_mreq {

src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,8 @@ mod clockid;
335335
feature = "runtime",
336336
feature = "termios",
337337
feature = "thread",
338-
all(bsd, feature = "event")
338+
all(bsd, feature = "event"),
339+
all(linux_kernel, feature = "net")
339340
))]
340341
mod pid;
341342
#[cfg(any(feature = "process", feature = "thread"))]
@@ -378,6 +379,7 @@ mod timespec;
378379
feature = "time",
379380
target_arch = "x86",
380381
)
381-
)
382+
),
383+
all(linux_kernel, feature = "net")
382384
))]
383385
mod ugid;

src/net/send_recv/msg.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use crate::backend::{self, c};
66
use crate::fd::{AsFd, BorrowedFd, OwnedFd};
77
use crate::io::{self, IoSlice, IoSliceMut};
8+
#[cfg(linux_kernel)]
9+
use crate::net::UCred;
810

911
use core::iter::FusedIterator;
1012
use core::marker::PhantomData;
@@ -22,6 +24,11 @@ macro_rules! cmsg_space {
2224
$len * ::core::mem::size_of::<$crate::fd::BorrowedFd<'static>>(),
2325
)
2426
};
27+
(ScmCredentials($len:expr)) => {
28+
$crate::net::__cmsg_space(
29+
$len * ::core::mem::size_of::<$crate::net::UCred>(),
30+
)
31+
};
2532

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

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

5769
unsafe {
@@ -69,6 +81,9 @@ impl SendAncillaryMessage<'_, '_> {
6981
pub enum RecvAncillaryMessage<'a> {
7082
/// Received file descriptors.
7183
ScmRights(AncillaryIter<'a, OwnedFd>),
84+
/// Received process credentials.
85+
#[cfg(linux_kernel)]
86+
ScmCredentials(UCred),
7287
}
7388

7489
/// Buffer for sending ancillary messages with [`sendmsg`], [`sendmsg_v4`],
@@ -139,6 +154,13 @@ impl<'buf, 'slice, 'fd> SendAncillaryBuffer<'buf, 'slice, 'fd> {
139154
unsafe { slice::from_raw_parts(fds.as_ptr().cast::<u8>(), size_of_val(fds)) };
140155
self.push_ancillary(fds_bytes, c::SOL_SOCKET as _, c::SCM_RIGHTS as _)
141156
}
157+
#[cfg(linux_kernel)]
158+
SendAncillaryMessage::ScmCredentials(ucred) => {
159+
let ucred_bytes = unsafe {
160+
slice::from_raw_parts(&ucred as *const _ as *const u8, size_of_val(&ucred))
161+
};
162+
self.push_ancillary(ucred_bytes, c::SOL_SOCKET as _, c::SCM_CREDENTIALS as _)
163+
}
142164
}
143165
}
144166

@@ -316,6 +338,15 @@ impl<'buf> AncillaryDrain<'buf> {
316338

317339
Some(RecvAncillaryMessage::ScmRights(fds))
318340
}
341+
#[cfg(linux_kernel)]
342+
(c::SOL_SOCKET, c::SCM_CREDENTIALS) => {
343+
if payload_len >= size_of::<UCred>() {
344+
let ucred = payload.as_ptr().cast::<UCred>().read_unaligned();
345+
Some(RecvAncillaryMessage::ScmCredentials(ucred))
346+
} else {
347+
None
348+
}
349+
}
319350
_ => None,
320351
}
321352
}

src/net/sockopt.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,6 +1346,18 @@ pub fn get_tcp_cork<Fd: AsFd>(fd: Fd) -> io::Result<bool> {
13461346
backend::net::sockopt::get_tcp_cork(fd.as_fd())
13471347
}
13481348

1349+
/// Get credentials of Unix domain socket peer process
1350+
///
1351+
/// # References
1352+
/// - [Linux `unix`]
1353+
///
1354+
/// [Linux `unix`]: https://man7.org/linux/man-pages/man7/unix.7.html
1355+
#[cfg(linux_kernel)]
1356+
#[doc(alias = "SO_PEERCRED")]
1357+
pub fn get_socket_peercred<Fd: AsFd>(fd: Fd) -> io::Result<super::UCred> {
1358+
backend::net::sockopt::get_socket_peercred(fd.as_fd())
1359+
}
1360+
13491361
#[test]
13501362
fn test_sizes() {
13511363
use c::c_int;

src/net/types.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,25 @@ bitflags! {
13361336
}
13371337
}
13381338

1339+
/// UNIX credentials of socket peer, for use with [`get_socket_peercred`]
1340+
/// [`SendAncillaryMessage::ScmCredentials`] and
1341+
/// [`RecvAncillaryMessage::ScmCredentials`].
1342+
///
1343+
/// [`get_socket_peercred`]: crate::net::sockopt::get_socket_peercred
1344+
/// [`SendAncillaryMessage::ScmCredentials`]: crate::net::SendAncillaryMessage::ScmCredentials
1345+
/// [`RecvAncillaryMessage::ScmCredentials`]: crate::net::RecvAncillaryMessage::ScmCredentials
1346+
#[cfg(linux_kernel)]
1347+
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1348+
#[repr(C)]
1349+
pub struct UCred {
1350+
/// Process ID of peer
1351+
pub pid: crate::pid::Pid,
1352+
/// User ID of peer
1353+
pub uid: crate::ugid::Uid,
1354+
/// Group ID of peer
1355+
pub gid: crate::ugid::Gid,
1356+
}
1357+
13391358
#[test]
13401359
fn test_sizes() {
13411360
use c::c_int;
@@ -1361,4 +1380,7 @@ fn test_sizes() {
13611380
let t: Option<Protocol> = Some(Protocol::from_raw(RawProtocol::new(4567).unwrap()));
13621381
assert_eq!(4567_u32, transmute::<Option<Protocol>, u32>(t));
13631382
}
1383+
1384+
#[cfg(linux_kernel)]
1385+
assert_eq_size!(UCred, libc::ucred);
13641386
}

tests/net/unix.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,3 +449,59 @@ fn test_unix_msg_with_scm_rights() {
449449
client.join().unwrap();
450450
server.join().unwrap();
451451
}
452+
453+
#[cfg(all(feature = "process", linux_kernel))]
454+
#[test]
455+
fn test_unix_peercred() {
456+
use rustix::io::{IoSlice, IoSliceMut};
457+
use rustix::net::{
458+
recvmsg, sendmsg, sockopt, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags,
459+
SendAncillaryBuffer, SendAncillaryMessage, SendFlags, SocketFlags,
460+
};
461+
use rustix::process::{getgid, getpid, getuid};
462+
463+
let (send_sock, recv_sock) = rustix::net::socketpair(
464+
AddressFamily::UNIX,
465+
SocketType::STREAM,
466+
SocketFlags::CLOEXEC,
467+
None,
468+
)
469+
.unwrap();
470+
471+
sockopt::set_socket_passcred(&recv_sock, true).unwrap();
472+
473+
let ucred = sockopt::get_socket_peercred(&send_sock).unwrap();
474+
assert_eq!(ucred.pid, getpid());
475+
assert_eq!(ucred.uid, getuid());
476+
assert_eq!(ucred.gid, getgid());
477+
478+
let msg = SendAncillaryMessage::ScmCredentials(ucred);
479+
let mut space = vec![0; msg.size()];
480+
let mut cmsg_buffer = SendAncillaryBuffer::new(&mut space);
481+
assert!(cmsg_buffer.push(msg));
482+
483+
sendmsg(
484+
&send_sock,
485+
&[IoSlice::new(b"cred")],
486+
&mut cmsg_buffer,
487+
SendFlags::empty(),
488+
)
489+
.unwrap();
490+
491+
let mut cmsg_space = vec![0; rustix::cmsg_space!(ScmCredentials(1))];
492+
let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut cmsg_space);
493+
494+
let mut buffer = vec![0; BUFFER_SIZE];
495+
recvmsg(
496+
&recv_sock,
497+
&mut [IoSliceMut::new(&mut buffer)],
498+
&mut cmsg_buffer,
499+
RecvFlags::empty(),
500+
)
501+
.unwrap();
502+
503+
match cmsg_buffer.drain().next().unwrap() {
504+
RecvAncillaryMessage::ScmCredentials(ucred2) => assert_eq!(ucred2, ucred),
505+
_ => panic!("Unexpected ancilliary message"),
506+
};
507+
}

0 commit comments

Comments
 (0)