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"
    }
}