//! OpenBSD pledge(2) and unveil(2) wrappers. use std::ffi::{CString, c_char}; use std::path::Path; unsafe extern "C" { fn pledge(promises: *const c_char, execpromises: *const c_char) -> i32; fn unveil(path: *const c_char, permissions: *const c_char) -> i32; } /// Valid pledge promises on OpenBSD. /// See `pledgereq[]` in `/usr/src/sys/kern/kern_pledge.c`. const VALID_PROMISES: &[&str] = &[ "audio", "bpf", "chown", "cpath", "disklabel", "dns", "dpath", "drm", "error", "exec", "fattr", "flock", "getpw", "id", "inet", "mcast", "pf", "proc", "prot_exec", "ps", "recvfd", "route", "rpath", "sendfd", "settime", "stdio", "tape", "tmppath", "tty", "unix", "unveil", "video", "vminfo", "vmm", "wpath", "wroute", ]; /// Valid unveil permission characters. const VALID_PERMS: &[u8] = b"rwcx"; /// Restrict the process to the given pledge promises. pub fn do_pledge(promises: &str) { for word in promises.split_whitespace() { if !VALID_PROMISES.contains(&word) { log::error!("pledge: unknown promise: {word}"); std::process::exit(1); } } let c = CString::new(promises).unwrap_or_else(|_| { log::error!("pledge: promises contain NUL byte"); std::process::exit(1); }); let ret = unsafe { pledge(c.as_ptr(), std::ptr::null()) }; if ret != 0 { let err = std::io::Error::last_os_error(); log::error!("pledge failed: {err}"); std::process::exit(1); } log::debug!("pledge applied"); } /// Add a path to the unveil whitelist with the given permissions. /// Permissions: "r" read, "w" write, "c" create, "x" execute. pub fn do_unveil(path: &Path, perms: &str) { if perms.is_empty() || !perms.as_bytes().iter().all(|b| VALID_PERMS.contains(b)) { log::error!("unveil: invalid permissions"); std::process::exit(1); } let p = CString::new(path.as_os_str().as_encoded_bytes()).unwrap_or_else( |_| { log::error!("unveil: path contains NUL byte"); std::process::exit(1); }, ); let f = CString::new(perms).unwrap_or_else(|_| { log::error!("unveil: permissions contain NUL byte"); std::process::exit(1); }); let ret = unsafe { unveil(p.as_ptr(), f.as_ptr()) }; if ret != 0 { let err = std::io::Error::last_os_error(); log::error!("unveil failed: {err}"); std::process::exit(1); } log::debug!("unveil: path added"); } /// Lock the unveil list — no further unveil calls allowed. pub fn unveil_lock() { let ret = unsafe { unveil(std::ptr::null(), std::ptr::null()) }; if ret != 0 { let err = std::io::Error::last_os_error(); log::error!("unveil lock failed: {err}"); std::process::exit(1); } log::debug!("unveil locked"); }