use std::io;
use super::plat;
use super::{observer_uid, read_pid_namespace_inode, BeatOrigin, RecvResult};
pub(crate) fn enable_credential_passing(fd: i32) -> io::Result<()> {
#[cfg(target_os = "linux")]
{
let (level, optname) = (plat::SOL_SOCKET, plat::SO_PASSCRED);
let one: i32 = 1;
let ret = unsafe {
plat::setsockopt(
fd,
level,
optname,
core::ptr::addr_of!(one) as *const core::ffi::c_void,
core::mem::size_of::<i32>() as u32,
)
};
if ret != 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
#[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))]
{
let (level, optname) = (plat::SOL_SOCKET, plat::LOCAL_CREDS);
let one: i32 = 1;
let ret = unsafe {
plat::setsockopt(
fd,
level,
optname,
core::ptr::addr_of!(one) as *const core::ffi::c_void,
core::mem::size_of::<i32>() as u32,
)
};
if ret != 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
{
let (level, optname) = (plat::SOL_SOCKET, plat::SO_RECVUCRED);
let one: i32 = 1;
let ret = unsafe {
plat::setsockopt(
fd,
level,
optname,
core::ptr::addr_of!(one) as *const core::ffi::c_void,
core::mem::size_of::<i32>() as u32,
)
};
if ret != 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
#[cfg(not(any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "illumos",
target_os = "solaris",
)))]
{
let _ = fd;
Ok(())
}
}
pub(crate) fn recv_authenticated(fd: i32) -> RecvResult {
#[cfg(all(
not(feature = "force-socketmode-fallback"),
any(
target_os = "linux",
target_os = "macos",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "illumos",
target_os = "solaris",
)
))]
{
let mut data = [0u8; 32];
#[repr(align(8))]
struct AncBuf([u8; plat::ANCILLARY_BUFFER_SIZE]);
let mut anc = AncBuf([0u8; plat::ANCILLARY_BUFFER_SIZE]);
let mut iov = plat::Iovec {
iov_base: data.as_mut_ptr() as *mut core::ffi::c_void,
iov_len: 32,
};
let mut mhdr = plat::msghdr_for_recv(
&mut iov,
anc.0.as_mut_ptr() as *mut core::ffi::c_void,
plat::ANCILLARY_BUFFER_SIZE,
);
let n = loop {
let ret = unsafe { plat::recvmsg(fd, &mut mhdr, 0) };
if ret < 0 {
let err = io::Error::last_os_error();
match err.kind() {
io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut => {
return RecvResult::WouldBlock;
}
io::ErrorKind::Interrupted => continue,
_ => return RecvResult::IoError(err),
}
}
break ret;
};
if plat::ctrl_truncated(&mhdr) {
return RecvResult::CtrlTruncated(io::Error::new(
io::ErrorKind::InvalidData,
"ancillary data truncated by kernel (ANCILLARY_BUFFER_SIZE too small)",
));
}
if n as usize != 32 {
return RecvResult::ShortRead;
}
let (peer_pid, peer_uid) = match plat::peer_pid_after_recv(fd, &mhdr) {
Some((pid, uid)) => (pid, uid),
None => {
return RecvResult::IoError(io::Error::new(
io::ErrorKind::InvalidData,
"kernel did not attach peer credentials",
));
}
};
let my_uid = observer_uid();
if peer_pid != 0 && peer_uid != my_uid {
return RecvResult::IoError(io::Error::new(
io::ErrorKind::PermissionDenied,
format!(
"peer credential UID mismatch: kernel reports uid {peer_uid}, expected uid {my_uid}"
),
));
}
let peer_pid_ns_inode = if peer_pid != 0 {
read_pid_namespace_inode(peer_pid)
} else {
None
};
RecvResult::Authenticated {
peer_pid,
peer_uid,
peer_pid_ns_inode,
origin: BeatOrigin::KernelAttested,
data,
}
}
#[cfg(any(
feature = "force-socketmode-fallback",
not(any(
target_os = "linux",
target_os = "macos",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "illumos",
target_os = "solaris",
))
))]
{
extern "C" {
fn recv(fd: i32, buf: *mut core::ffi::c_void, len: usize, flags: i32) -> isize;
}
let mut data = [0u8; 32];
let n = loop {
let ret = unsafe { recv(fd, data.as_mut_ptr() as *mut core::ffi::c_void, 32, 0) };
if ret < 0 {
let err = io::Error::last_os_error();
match err.kind() {
io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut => {
return RecvResult::WouldBlock;
}
io::ErrorKind::Interrupted => continue,
_ => return RecvResult::IoError(err),
}
}
break ret as isize;
};
if n as usize != 32 {
return RecvResult::ShortRead;
}
return RecvResult::Authenticated {
peer_pid: 0,
peer_uid: 0,
peer_pid_ns_inode: None,
origin: BeatOrigin::SocketModeOnly,
data,
};
}
}