use std::collections::{HashSet, HashMap};
use std::fs::File;
use std::os::unix::io::AsRawFd;
use libseccomp::*;
use syscalls::Sysno;
use crate::{RuleSet, Rule};
use super::YesReally;
const IO_READ_SYSCALLS: &[Sysno] = &[Sysno::read, Sysno::readv, Sysno::preadv, Sysno::preadv2, Sysno::pread64, Sysno::lseek];
const IO_WRITE_SYSCALLS: &[Sysno] = &[Sysno::write, Sysno::writev, Sysno::pwritev, Sysno::pwritev2, Sysno::pwrite64,
Sysno::fsync, Sysno::fdatasync, Sysno::lseek];
const IO_OPEN_SYSCALLS: &[Sysno] = &[Sysno::open, Sysno::openat, Sysno::openat2];
const IO_IOCTL_SYSCALLS: &[Sysno] = &[Sysno::ioctl, Sysno::fcntl];
const IO_METADATA_SYSCALLS: &[Sysno] = &[Sysno::stat, Sysno::fstat, Sysno::newfstatat,
Sysno::lstat, Sysno::statx,
Sysno::getdents, Sysno::getdents64,
Sysno::getcwd];
const IO_CLOSE_SYSCALLS: &[Sysno] = &[Sysno::close, Sysno::close_range];
pub struct SystemIO {
allowed: HashSet<Sysno>,
custom: HashMap<Sysno, Vec<Rule>>,
}
impl SystemIO {
#[must_use]
pub fn nothing() -> SystemIO {
SystemIO {
allowed: HashSet::new(),
custom: HashMap::new(),
}
}
#[must_use]
pub fn everything() -> SystemIO {
SystemIO::nothing()
.allow_read()
.allow_write()
.allow_open().yes_really()
.allow_metadata()
.allow_close()
}
#[must_use]
pub fn allow_read(mut self) -> SystemIO {
self.allowed.extend(IO_READ_SYSCALLS);
self
}
#[must_use]
pub fn allow_write(mut self) -> SystemIO {
self.allowed.extend(IO_WRITE_SYSCALLS);
self
}
#[must_use]
pub fn allow_open(mut self) -> YesReally<SystemIO> {
self.allowed.extend(IO_OPEN_SYSCALLS);
YesReally::new(self)
}
#[must_use]
pub fn allow_open_readonly(mut self) -> SystemIO {
const O_WRONLY: u64 = libc::O_WRONLY as u64;
const O_RDWR: u64 = libc::O_RDWR as u64;
const O_APPEND: u64 = libc::O_APPEND as u64;
const O_CREAT: u64 = libc::O_CREAT as u64;
const O_EXCL: u64 = libc::O_EXCL as u64;
const WRITECREATE: u64 = O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_EXCL;
let rule = Rule::new(Sysno::open)
.and_condition(scmp_cmp!($arg1 & WRITECREATE == 0));
self.custom.entry(Sysno::open)
.or_insert_with(Vec::new)
.push(rule);
let rule = Rule::new(Sysno::openat)
.and_condition(scmp_cmp!($arg2 & WRITECREATE == 0));
self.custom.entry(Sysno::openat)
.or_insert_with(Vec::new)
.push(rule);
self
}
#[must_use]
pub fn allow_metadata(mut self) -> SystemIO {
self.allowed.extend(IO_METADATA_SYSCALLS);
self
}
#[must_use]
pub fn allow_ioctl(mut self) -> SystemIO {
self.allowed.extend(IO_IOCTL_SYSCALLS);
self
}
#[must_use]
pub fn allow_close(mut self) -> SystemIO {
self.allowed.extend(IO_CLOSE_SYSCALLS);
self
}
#[must_use]
pub fn allow_stdin(mut self) -> SystemIO {
let rule = Rule::new(Sysno::read)
.and_condition(scmp_cmp!($arg0 == 0));
self.custom.entry(Sysno::read)
.or_insert_with(Vec::new)
.push(rule);
self
}
#[must_use]
pub fn allow_stdout(mut self) -> SystemIO {
let rule = Rule::new(Sysno::write)
.and_condition(scmp_cmp!($arg0 == 1));
self.custom.entry(Sysno::write)
.or_insert_with(Vec::new)
.push(rule);
self
}
#[must_use]
pub fn allow_stderr(mut self) -> SystemIO {
let rule = Rule::new(Sysno::write)
.and_condition(scmp_cmp!($arg0 == 2));
self.custom.entry(Sysno::write)
.or_insert_with(Vec::new)
.push(rule);
self
}
#[must_use]
pub fn allow_file_read(mut self, file: &File) -> SystemIO {
let fd = file.as_raw_fd();
for &syscall in IO_READ_SYSCALLS {
let rule = Rule::new(syscall)
.and_condition(scmp_cmp!($arg0 == fd.try_into().expect("fd provided was negative")));
self.custom.entry(syscall)
.or_insert_with(Vec::new)
.push(rule);
}
for &syscall in IO_METADATA_SYSCALLS {
let rule = Rule::new(syscall)
.and_condition(scmp_cmp!($arg0 == fd.try_into().expect("fd provided was negative")));
self.custom.entry(syscall)
.or_insert_with(Vec::new)
.push(rule);
}
self
}
#[must_use]
pub fn allow_file_write(mut self, file: &File) -> SystemIO {
let fd = file.as_raw_fd();
let rule = Rule::new(Sysno::write)
.and_condition(scmp_cmp!($arg0 == fd.try_into().expect("fd provided was negative")));
self.custom.entry(Sysno::write)
.or_insert_with(Vec::new)
.push(rule);
self
}
}
impl RuleSet for SystemIO {
fn simple_rules(&self) -> Vec<syscalls::Sysno> {
self.allowed.iter().copied().collect()
}
fn conditional_rules(&self) -> HashMap<syscalls::Sysno, Vec<Rule>> {
self.custom.clone()
}
fn name(&self) -> &'static str {
"SystemIO"
}
}