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`
181 lines
4.9 KiB
Rust
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()
|
|
}
|