Andrew Rioux f6428b92fe
feat: added FreeBSD support
to make use of it, create a FreeBSD VM with curl installed and install
rustup by default from rustup.rs, then run `cargo build -p sparse-05-server`
2024-09-19 02:44:31 -04:00

181 lines
4.9 KiB
Rust

#[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<String>,
pub transport: TransportType,
pub hostname: Option<String>,
}
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<Option<String>> {
let passwd = std::fs::read_to_string("/etc/passwd")?;
Ok(passwd.split("\n").find_map(|row| -> Option<String> {
let mut entries = row.split(":");
let name = entries.next()?;
entries.next()?;
let euid = entries.next()?.parse::<u32>().ok()?;
if euid == uid {
Some(name.to_string())
} else {
None
}
}))
}
#[cfg(target_os = "windows")]
fn get_username(_uid: u32) -> anyhow::Result<Option<String>> {
Ok(std::env::var("USERNAME").ok())
}
#[cfg(target_os = "linux")]
fn get_current_capabilities() -> anyhow::Result<SrvCapabilities> {
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<SrvCapabilities> {
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<SrvCapabilities> {
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::<Vec<_>>())
.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<SrvCapabilities> {
get_current_capabilities()
}