#[cfg(target_os = "linux")] use std::ffi::c_int; use std::path::PathBuf; use anyhow::Context; use sparse_05_common::messages::{Capabilities, OperatingSystem, TransportType}; #[derive(Debug)] pub struct SrvCapabilities { pub operating_system: OperatingSystem, pub docker_container: bool, pub docker_breakout: bool, pub setuid: bool, pub root: bool, pub service: bool, pub userent: Option, pub transport: TransportType, pub hostname: Option, } impl SrvCapabilities { pub fn to_capabilities(&self) -> Capabilities { Capabilities { cwd: PathBuf::from("/"), docker_breakout: self.docker_breakout, docker_container: self.docker_container, hostname: self.hostname.clone(), operating_system: self.operating_system.clone(), root: self.root, setuid: self.setuid, service: self.service, transport: self.transport, userent: self.userent.clone(), } } } #[cfg(target_os = "linux")] const CAP_SETUID: u32 = 1 << 7; #[cfg(target_os = "linux")] const CAP_NET_RAW: u32 = 1 << 13; #[cfg(target_os = "linux")] const SYS_CAPGET: i64 = 125; #[cfg(target_os = "linux")] #[allow(non_camel_case_types)] #[repr(C)] #[derive(Debug)] struct cap_user_header_t { version: u32, pid: c_int, } #[cfg(target_os = "linux")] #[allow(non_camel_case_types)] #[repr(C)] #[derive(Debug)] struct cap_user_data_t { effective: u32, permitted: u32, inheritable: u32, } #[cfg(any(target_os = "linux", target_os = "freebsd"))] 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 } })) } #[cfg(target_os = "windows")] fn get_username(_uid: u32) -> anyhow::Result> { Ok(std::env::var("USERNAME").ok()) } #[cfg(target_os = "linux")] fn get_current_capabilities() -> anyhow::Result { let mut header = cap_user_header_t { version: 0x20080522, pid: 0, }; let mut data = cap_user_data_t { effective: 0, permitted: 0, inheritable: 0, }; let oscapabilities = unsafe { libc::syscall(SYS_CAPGET, &mut header as *const _, &mut data as *mut _) }; if oscapabilities == -1 { return Err(std::io::Error::last_os_error())?; } let docker_container = std::fs::read_to_string("/proc/1/cgroup")? != "0::/\n"; let docker_breakout = false; 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)?; let hostname = std::fs::read_to_string("/etc/hostname") .map(|s| s.trim().to_string()) .ok(); Ok(SrvCapabilities { operating_system: OperatingSystem::Linux, docker_container, docker_breakout, setuid, root, service: false, userent, transport, hostname, }) } #[cfg(target_os = "windows")] fn get_current_capabilities() -> anyhow::Result { let userent = get_username(0)?; let hostname = std::env::var("COMPUTERNAME").ok(); let service_name = hostname.clone().map(|name| format!("{name}$")); Ok(SrvCapabilities { operating_system: OperatingSystem::Windows, docker_container: false, docker_breakout: false, setuid: false, service: userent.as_deref() == service_name.as_deref(), root: userent.as_deref() == Some("Administrator"), userent: userent.clone(), transport: TransportType::RawUdp, hostname, }) } #[cfg(target_os = "freebsd")] fn get_current_capabilities() -> anyhow::Result { let uid = unsafe { libc::getuid() }; let root = uid == 0; let userent = get_username(uid)?; let hostname = std::fs::read_to_string("/etc/rc.conf")? .split("\n") .map(|line| line.split("=").collect::>()) .find(|line| line.get(0) == Some(&"hostname")) .map(|line| line.get(1).map(|name| name.to_string())) .flatten(); Ok(SrvCapabilities { operating_system: OperatingSystem::FreeBSD, docker_container: false, docker_breakout: false, setuid: false, service: false, root, userent: userent.clone(), transport: TransportType::RawUdp, hostname, }) } pub fn get_capabilities() -> anyhow::Result { get_current_capabilities() }