1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
//! Contains a [`RuleSet`] for allowing syscalls that may be dangerous.
use std::collections::{HashMap, HashSet};
//use libseccomp::scmp_cmp;
use syscalls::x86_64::Sysno;
use crate::x86_64::{Rule, RuleSet};
use super::YesReally;
// const CLONE_PARENT: u64 = libc::CLONE_PARENT as u64;
// const CLONE_THREAD: u64 = libc::CLONE_THREAD as u64;
/// Allows `clone` and `sleep` syscalls, which allow creating new threads and processes, and
/// pausing them.
///
/// # Security
/// This is in the danger zone not because it's dangerous but because it can be misused: Threads
/// do not provide isolation from each other. You can still access other threads' memory and
/// potentially get them to do operations that are not allowed in the current thread's seccomp
/// context.
pub struct Threads {
allowed: HashSet<Sysno>,
}
impl Threads {
/// Create a new [`Threads`] ruleset with nothing allowed by default.
#[must_use]
pub fn nothing() -> Threads {
Threads {
allowed: HashSet::new(),
}
}
/// Allow creating new threads and processes.
#[must_use]
pub fn allow_create(mut self) -> Threads {
self.allowed.extend([Sysno::clone, Sysno::clone3]);
self
}
/// Allow sleeping on the current thread
///
/// # Security considerations
/// An attacker with arbitrary code execution and access to a high resolution timer can mount
/// timing attacks (e.g. spectre).
#[must_use]
pub fn allow_sleep(mut self) -> YesReally<Threads> {
self.allowed
.extend([Sysno::clock_nanosleep, Sysno::nanosleep]);
YesReally::new(self)
}
}
impl RuleSet for Threads {
fn simple_rules(&self) -> Vec<Sysno> {
self.allowed.iter().copied().collect()
}
fn conditional_rules(&self) -> HashMap<Sysno, Vec<Rule>> {
// let mut rules = HashMap::new();
// let clone = Rule::new(Sysno::clone)
// .and_condition(scmp_cmp!($arg2 & CLONE_THREAD == CLONE_THREAD));
// rules.entry(Sysno::clone)
// .or_insert_with(Vec::new)
// .push(clone);
// let clone3 = Rule::new(Sysno::clone3)
// .and_condition(scmp_cmp!($arg2 & CLONE_THREAD == CLONE_THREAD));
// rules.entry(Sysno::clone3)
// .or_insert_with(Vec::new)
// .push(clone3);
// rules
HashMap::new()
}
fn name(&self) -> &'static str {
"Threads"
}
}
/// [`ForkAndExec`] is in the danger zone because it can be used to start another process,
/// including more privileged ones. That process will still be under seccomp's restrictions (see
/// `tests/inherit_filters.rs`) but depending on your filter it could still do bad things.
///
/// Note that this also allows the `clone` syscall.
pub struct ForkAndExec;
impl RuleSet for ForkAndExec {
fn simple_rules(&self) -> Vec<Sysno> {
vec![
Sysno::fork,
Sysno::vfork,
Sysno::execve,
Sysno::execveat,
Sysno::wait4,
Sysno::waitid,
Sysno::clone,
Sysno::clone3,
]
}
fn conditional_rules(&self) -> HashMap<Sysno, Vec<Rule>> {
// TODO: figure out if there's something reasonable we can do with this. same as with
// Threads
// let mut custom = HashMap::new();
//
// let clone = Rule::new(Sysno::clone)
// .and_condition(scmp_cmp!($arg2 & CLONE_PARENT == CLONE_PARENT));
// custom.entry(Sysno::clone)
// .or_insert_with(Vec::new)
// .push(clone);
// let clone3 = Rule::new(Sysno::clone3)
// .and_condition(scmp_cmp!($arg2 & CLONE_PARENT == CLONE_PARENT));
// custom.entry(Sysno::clone3)
// .or_insert_with(Vec::new)
// .push(clone3);
//
// custom
HashMap::new()
}
fn name(&self) -> &'static str {
"ForkAndExec"
}
}