use std::os::unix::io::{FromRawFd, OwnedFd, RawFd};
use std::sync::Arc;
use crate::netlink::{proxy, state::NetlinkState};
use crate::seccomp::notif::{read_child_mem, write_child_mem, NotifAction, OnInjectSuccess};
use crate::sys::structs::SeccompNotif;
const AF_NETLINK: u64 = 16;
const NETLINK_ROUTE: u64 = 0;
fn tgid_of(tid: i32) -> i32 {
let path = format!("/proc/{}/status", tid);
if let Ok(s) = std::fs::read_to_string(&path) {
for line in s.lines() {
if let Some(rest) = line.strip_prefix("Tgid:") {
if let Ok(v) = rest.trim().parse::<i32>() {
return v;
}
}
}
}
tid
}
fn read_struct<T: Copy>(
notif_fd: RawFd,
id: u64,
pid: u32,
addr: usize,
) -> Option<T> {
let bytes = read_child_mem(notif_fd, id, pid, addr as u64, std::mem::size_of::<T>()).ok()?;
Some(unsafe { std::ptr::read_unaligned(bytes.as_ptr() as *const T) })
}
pub async fn handle_socket(
notif: &SeccompNotif,
state: &Arc<NetlinkState>,
) -> NotifAction {
let domain = notif.data.args[0];
let protocol = notif.data.args[2];
if domain != AF_NETLINK {
return NotifAction::Continue;
}
if protocol != NETLINK_ROUTE {
return NotifAction::Errno(libc::EAFNOSUPPORT);
}
let mut fds = [0i32; 2];
let rc = unsafe {
libc::socketpair(
libc::AF_UNIX,
libc::SOCK_SEQPACKET | libc::SOCK_CLOEXEC,
0,
fds.as_mut_ptr(),
)
};
if rc != 0 {
return NotifAction::Errno(libc::ENOMEM);
}
let flags = unsafe { libc::fcntl(fds[0], libc::F_GETFL) };
if flags < 0
|| unsafe { libc::fcntl(fds[0], libc::F_SETFL, flags | libc::O_NONBLOCK) } < 0
{
unsafe {
libc::close(fds[0]);
libc::close(fds[1]);
}
return NotifAction::Errno(libc::ENOMEM);
}
let responder_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) };
let child_fd = unsafe { OwnedFd::from_raw_fd(fds[1]) };
let tgid = tgid_of(notif.pid as i32);
proxy::spawn_responder(responder_fd, tgid as u32);
let state = Arc::clone(state);
NotifAction::InjectFdSendTracked {
srcfd: child_fd,
newfd_flags: libc::O_CLOEXEC as u32,
on_success: OnInjectSuccess::new(move |child_fd_num| {
state.register(tgid, child_fd_num);
}),
}
}
pub async fn handle_netlink_recvmsg(
notif: &SeccompNotif,
state: &Arc<NetlinkState>,
notif_fd: RawFd,
) -> NotifAction {
let fd = notif.data.args[0] as i32;
let tgid = tgid_of(notif.pid as i32);
if !state.is_cookie(tgid, fd) {
return NotifAction::Continue;
}
let nr = notif.data.nr as i64;
let sockaddr_nl_len: usize = 12;
let zeros = [0u8; 12];
let pid = notif.pid;
let id = notif.id;
if nr == libc::SYS_recvmsg {
let msghdr_ptr = notif.data.args[1] as usize;
if let Some(hdr) = read_struct::<libc::msghdr>(notif_fd, id, pid, msghdr_ptr) {
if !hdr.msg_name.is_null() && (hdr.msg_namelen as usize) >= sockaddr_nl_len {
let _ = write_child_mem(notif_fd, id, pid, hdr.msg_name as u64, &zeros);
}
}
} else if nr == libc::SYS_recvfrom {
let src_addr = notif.data.args[4] as u64;
let addrlen_ptr = notif.data.args[5] as u64;
if src_addr != 0 && addrlen_ptr != 0 {
if let Ok(b) = read_child_mem(notif_fd, id, pid, addrlen_ptr, 4) {
let cap = u32::from_ne_bytes(b.try_into().unwrap_or([0; 4])) as usize;
if cap >= sockaddr_nl_len {
let _ = write_child_mem(notif_fd, id, pid, src_addr, &zeros);
}
}
}
}
NotifAction::Continue
}
pub async fn handle_bind(
notif: &SeccompNotif,
state: &Arc<NetlinkState>,
) -> NotifAction {
let fd = notif.data.args[0] as i32;
let tgid = tgid_of(notif.pid as i32);
if state.is_cookie(tgid, fd) {
return NotifAction::ReturnValue(0);
}
NotifAction::Continue
}
pub async fn handle_close(
notif: &SeccompNotif,
state: &Arc<NetlinkState>,
) -> NotifAction {
let fd = notif.data.args[0] as i32;
let tgid = tgid_of(notif.pid as i32);
if state.is_cookie(tgid, fd) {
state.unregister(tgid, fd);
}
NotifAction::Continue
}
pub async fn handle_getsockname(
notif: &SeccompNotif,
state: &Arc<NetlinkState>,
notif_fd: RawFd,
) -> NotifAction {
let fd = notif.data.args[0] as i32;
let tgid = tgid_of(notif.pid as i32);
if !state.is_cookie(tgid, fd) {
return NotifAction::Continue;
}
let mut addr = [0u8; 12];
let nl_family = libc::AF_NETLINK as u16;
addr[0..2].copy_from_slice(&nl_family.to_ne_bytes());
addr[4..8].copy_from_slice(&(tgid as u32).to_ne_bytes());
let addr_ptr = notif.data.args[1] as u64;
let addrlen_ptr = notif.data.args[2] as u64;
let pid = notif.pid;
let id = notif.id;
let cur = match read_child_mem(notif_fd, id, pid, addrlen_ptr, 4) {
Ok(b) => u32::from_ne_bytes(b.try_into().unwrap_or([0; 4])) as usize,
Err(_) => return NotifAction::Errno(libc::EFAULT),
};
let to_write = cur.min(addr.len());
if write_child_mem(notif_fd, id, pid, addr_ptr, &addr[..to_write]).is_err() {
return NotifAction::Errno(libc::EFAULT);
}
let actual = (addr.len() as u32).to_ne_bytes();
let _ = write_child_mem(notif_fd, id, pid, addrlen_ptr, &actual);
NotifAction::ReturnValue(0)
}