Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

seccomp: Update experiment seccomp program #2901

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions experiment/seccomp/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions experiment/seccomp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ prctl = "1.0.0"
anyhow = "1.0"
tokio = { version = "1", features = ["full"] }
syscall-numbers = "3.1.1"
syscalls = { version = "0.6.18", features = ["std", "serde", "aarch64", "x86_64"]}
4 changes: 3 additions & 1 deletion experiment/seccomp/src/instruction/arch.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use crate::instruction::Instruction;
use crate::instruction::*;

#[derive(PartialEq, Debug)]
pub enum Arch {
X86,
X86,AArch64
}

pub fn gen_validate(arc: &Arch) -> Vec<Instruction> {
let arch = match arc {
Arch::X86 => AUDIT_ARCH_X86_64,
Arch::AArch64 => AUDIT_ARCH_AARCH64
};

vec![
Expand Down
55 changes: 20 additions & 35 deletions experiment/seccomp/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,18 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::slice;

use anyhow::Result;
use nix::{
libc,
sys::{
signal::Signal,
socket::{
self, ControlMessage, ControlMessageOwned, MsgFlags, SockFlag, SockType, UnixAddr,
},
stat::Mode,
wait::{self, WaitStatus},
use nix::{libc, sys::{
signal::Signal,
socket::{
self, ControlMessage, ControlMessageOwned, MsgFlags, SockFlag, SockType, UnixAddr,
},
unistd::{close, mkdir},
};
stat::Mode,
wait::{self, WaitStatus},
}, unistd::{close, mkdir}};

use syscall_numbers::x86_64;
use syscalls::syscall_args;
use seccomp::seccomp::{InstructionData, Rule};

fn send_fd<F: AsRawFd>(sock: OwnedFd, fd: &F) -> nix::Result<()> {
let fd = fd.as_raw_fd();
Expand Down Expand Up @@ -90,30 +89,16 @@ async fn main() -> Result<()> {
)?;

let _ = prctl::set_no_new_privileges(true);

let mut bpf_prog = instruction::gen_validate(&Arch::X86);
bpf_prog.append(&mut vec![
// A: Check if syscall is getcwd
Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0),
Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, libc::SYS_getcwd as u32), // If false, go to B
Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
// B: Check if syscall is write and it is writing to stderr(fd=2)
Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0),
Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 3, libc::SYS_write as u32), // If false, go to C
// Load the file descriptor
Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_args_offset().into()),
// Check if args is stderr
Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, libc::STDERR_FILENO as u32), // If false, go to C
Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
// C: Check if syscall is mkdir and if so, return seccomp notify
Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0),
Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, libc::SYS_mkdir as u32), // If false, go to D
Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF),
// D: Pass
Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
]);

let seccomp = Seccomp { filters: bpf_prog };
let inst_data = InstructionData{
arc: Arch::X86,
def_action: SECCOMP_RET_KILL_PROCESS,
rule_arr: vec![
Rule::new("getcwd".parse()?, 0, syscall_args!(),false),
Rule::new("write".parse()?,1, syscall_args!(libc::STDERR_FILENO as usize), false),
Rule::new("mkdir".parse()?,0, syscall_args!(), true)
]
};
let mut seccomp = Seccomp {filters: Vec::from(inst_data)};

tokio::spawn(async move {
tokio::signal::ctrl_c()
Expand Down
72 changes: 70 additions & 2 deletions experiment/seccomp/src/seccomp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ use std::{
},
};

use std::str::FromStr;
use nix::{
errno::Errno,
ioctl_readwrite, ioctl_write_ptr, libc,
libc::{SECCOMP_FILTER_FLAG_NEW_LISTENER, SECCOMP_SET_MODE_FILTER},
unistd,
};

use crate::instruction::{Instruction, SECCOMP_IOC_MAGIC};
use syscalls::{SyscallArgs};
use crate::instruction::{*};
use crate::instruction::{Arch, Instruction, SECCOMP_IOC_MAGIC};

#[derive(Debug, thiserror::Error)]
pub enum SeccompError {
Expand Down Expand Up @@ -198,3 +200,69 @@ struct Filters {
pub len: c_ushort,
pub filter: *const Instruction,
}

fn get_syscall_number(arc: &Arch, name: &str) -> Option<u64> {
if arc == &Arch::X86 {
match syscalls::x86_64::Sysno::from_str(name) {
Ok(syscall) => Some(syscall as u64),
Err(_) => None,
}
} else {
match syscalls::aarch64::Sysno::from_str(name) {
Ok(syscall) => Some(syscall as u64),
Err(_) => None,
}
}

}

#[derive(Debug)]
pub struct InstructionData {
pub arc: Arch,
pub def_action: u32,
pub rule_arr: Vec<Rule>
}
impl From<InstructionData> for Vec<Instruction> {
fn from(inst_data: InstructionData) -> Self {
let mut bpf_prog = gen_validate(&inst_data.arc);
for rule in &inst_data.rule_arr {
bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, 0)]);
bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1,
get_syscall_number(&inst_data.arc, &rule.syscall).unwrap() as c_uint)]);

// TODO: Checks for only one argument, but supports two or more arguments
if rule.arg_cnt != 0 {
bpf_prog.append(&mut vec![Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_args_offset().into())]);
bpf_prog.append(&mut vec![Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, rule.args.arg0 as c_uint)]);
}

if rule.is_notify {
bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF)]);
} else {
bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, inst_data.def_action)]);
}
}

bpf_prog.append(&mut vec![Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)]);
return bpf_prog;
}
}

#[derive(Debug)]
pub struct Rule {
pub syscall: String,
pub arg_cnt: u8,
pub args: SyscallArgs,
pub is_notify: bool
}

impl Rule {
pub fn new(syscall: String, arg_cnt: u8, args: SyscallArgs, is_notify: bool) -> Self {
Self {
syscall,
arg_cnt,
args,
is_notify,
}
}
}