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
extern crate libc;
extern crate seccomp_sys;
#[macro_use]
extern crate log;
extern crate strum;
#[macro_use]
extern crate strum_macros;

use seccomp_sys::*;

mod error;
pub use error::{Error, Result};

mod syscalls;
pub use syscalls::Syscall;

#[derive(Debug, Clone)]
pub enum Action {
    Kill,
    Trap,
    Errno(u16),
    Trace(u16),
    Allow,
}

impl Into<u32> for Action {
    fn into(self) -> u32 {
        use self::Action::*;
        match self {
            Kill => SCMP_ACT_KILL,
            Trap => SCMP_ACT_TRAP,
            Errno(e) => SCMP_ACT_ERRNO(e.into()),
            Trace(t) => SCMP_ACT_TRACE(t.into()),
            Allow => SCMP_ACT_ALLOW,
        }
    }
}

pub struct Context {
    ctx: *mut scmp_filter_ctx,
}

impl Context {
    pub fn init() -> Result<Context> {
        Context::init_with_action(Action::Kill)
    }

    pub fn init_with_action(default_action: Action) -> Result<Context> {
        let ctx = unsafe { seccomp_init(default_action.into()) };

        if ctx.is_null() {
            return Err(Error::from("seccomp_init returned null".to_string()));
        }

        Ok(Context { ctx })
    }

    #[inline]
    pub fn allow_syscall(&mut self, syscall: Syscall) -> Result<()> {
        self.set_action_for_syscall(Action::Allow, syscall)
    }

    #[inline]
    pub fn set_action_for_syscall(&mut self, action: Action, syscall: Syscall) -> Result<()> {
        debug!("seccomp: setting action={:?} syscall={:?}", action, syscall);
        let ret = unsafe { seccomp_rule_add(self.ctx, action.into(), syscall.into_i32(), 0) };

        if ret != 0 {
            Err(Error::from("seccomp_rule_add returned error".to_string()))
        } else {
            Ok(())
        }
    }

    pub fn load(&self) -> Result<()> {
        debug!("seccomp: loading policy");
        let ret = unsafe { seccomp_load(self.ctx) };

        if ret != 0 {
            Err(Error::from("seccomp_load returned error".to_string()))
        } else {
            Ok(())
        }
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe { seccomp_release(self.ctx) };
    }
}

#[cfg(test)]
mod tests {
    use super::syscalls::Syscall;
    use super::{Action, Context};
    use libc;

    // this test isn't fully stable yet
    #[test]
    #[ignore]
    fn it_works() {
        let mut ctx = Context::init_with_action(Action::Errno(69)).unwrap();
        ctx.allow_syscall(Syscall::futex).unwrap();
        ctx.load().unwrap();
        assert_eq!(unsafe { libc::getpid() }, -69);
    }

    #[test]
    fn from_name() {
        use crate::syscalls::Syscall;

        let cases = vec![
            ("open", Some(Syscall::open)),
            ("setgid", Some(Syscall::setgid)),
            ("nothing", None),
            ("", None),
        ];

        for (name, rhs) in cases {
            let lhs = Syscall::from_name(name);
            assert_eq!(lhs, rhs);
        }
    }
}