extern crate libc;
extern crate seccomp_sys;
use seccomp_sys::scmp_compare::*;
use seccomp_sys::*;
use std::convert::Into;
use std::error::Error;
use std::fmt;
pub type Cmp = scmp_arg_cmp;
#[derive(Debug, Clone, Copy)]
pub enum Op {
Ne,
Lt,
Le,
Eq,
Ge,
Gt,
MaskedEq,
}
impl Into<scmp_compare> for Op {
fn into(self) -> scmp_compare {
match self {
Op::Ne => SCMP_CMP_NE,
Op::Lt => SCMP_CMP_LT,
Op::Le => SCMP_CMP_LE,
Op::Eq => SCMP_CMP_EQ,
Op::Ge => SCMP_CMP_GE,
Op::Gt => SCMP_CMP_GT,
Op::MaskedEq => SCMP_CMP_MASKED_EQ,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum Action {
Allow,
Kill,
KillProcess,
Trap,
Errno(i32),
Trace(u32),
}
impl Into<u32> for Action {
fn into(self) -> u32 {
match self {
Action::Allow => SCMP_ACT_ALLOW,
Action::Kill => SCMP_ACT_KILL,
Action::KillProcess => SCMP_ACT_KILL_PROCESS,
Action::Trap => SCMP_ACT_TRAP,
Action::Errno(x) => SCMP_ACT_ERRNO(x as u32),
Action::Trace(x) => SCMP_ACT_TRACE(x),
}
}
}
pub struct Compare {
arg: libc::c_uint,
op: Option<Op>,
datum_a: Option<scmp_datum_t>,
datum_b: Option<scmp_datum_t>,
}
impl Compare {
pub fn arg(arg_num: u32) -> Self {
Compare {
arg: arg_num as libc::c_uint,
op: None,
datum_a: None,
datum_b: None,
}
}
pub fn using(mut self, op: Op) -> Self {
self.op = Some(op);
self
}
pub fn with(mut self, datum: u64) -> Self {
self.datum_a = Some(datum);
self
}
pub fn and(mut self, datum: u64) -> Self {
self.datum_b = Some(datum);
self
}
pub fn build(self) -> Option<Cmp> {
if self.op.is_some() && self.datum_a.is_some() {
Some(Cmp {
arg: self.arg,
op: self.op.unwrap().into(),
datum_a: self.datum_a.unwrap(),
datum_b: self.datum_b.unwrap_or(0),
})
} else {
None
}
}
}
#[derive(Debug)]
pub struct Rule {
action: Action,
syscall_nr: usize,
comparators: Vec<Cmp>,
}
impl Rule {
pub fn new(syscall_nr: usize, cmp: Cmp, action: Action) -> Rule {
Rule {
action,
syscall_nr,
comparators: vec![cmp],
}
}
pub fn add_comparison(&mut self, cmp: Cmp) {
self.comparators.push(cmp);
}
}
#[derive(Debug)]
pub struct SeccompError {
msg: String,
}
impl SeccompError {
fn new<T: Into<String>>(msg: T) -> Self {
SeccompError { msg: msg.into() }
}
}
impl fmt::Display for SeccompError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SeccompError: {}", self.msg)
}
}
impl Error for SeccompError {
fn description(&self) -> &str {
&self.msg
}
}
#[derive(Debug)]
pub struct Context {
int: *mut scmp_filter_ctx,
}
impl Context {
pub fn default(def_action: Action) -> Result<Context, SeccompError> {
let filter_ctx = unsafe { seccomp_init(def_action.into()) };
if filter_ctx.is_null() {
return Err(SeccompError::new("initialization failed"));
}
Ok(Context { int: filter_ctx })
}
pub fn add_rule(&mut self, rule: Rule) -> Result<(), SeccompError> {
let res = unsafe {
seccomp_rule_add_array(
self.int,
rule.action.into(),
rule.syscall_nr as i32,
rule.comparators.len() as u32,
rule.comparators.as_slice().as_ptr(),
)
};
if res != 0 {
Err(SeccompError::new(format!("failed to add rule {:?}", rule)))
} else {
Ok(())
}
}
pub fn load(&self) -> Result<(), SeccompError> {
let res = unsafe { seccomp_load(self.int) };
if res != 0 {
Err(SeccompError::new("failed to load filter into the kernel"))
} else {
Ok(())
}
}
}
impl Drop for Context {
fn drop(&mut self) {
unsafe { seccomp_release(self.int) }
}
}
#[test]
fn it_works() {
fn test() -> Result<(), Box<dyn Error>> {
let mut ctx = Context::default(Action::Allow)?;
ctx.add_rule(Rule::new(
105,
Compare::arg(0).using(Op::Eq).with(1000).build().unwrap(),
Action::Errno(libc::EPERM),
))?;
ctx.load()?;
let ret = unsafe { libc::setuid(1000) };
println!("ret = {}, uid = {}", ret, unsafe { libc::getuid() });
Ok(())
}
test().unwrap();
}