diff --git a/experiment/seccomp/Cargo.lock b/experiment/seccomp/Cargo.lock index d3af855d6..9ee70dd61 100644 --- a/experiment/seccomp/Cargo.lock +++ b/experiment/seccomp/Cargo.lock @@ -266,10 +266,42 @@ dependencies = [ "nix 0.27.1", "prctl", "syscall-numbers", + "syscalls", "thiserror", "tokio", ] +[[package]] +name = "serde" +version = "1.0.208" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.208" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -312,6 +344,16 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "084e382bf467cd3381fdec080d883505792ee0d16a004b1b090abf2db5dc2a29" +[[package]] +name = "syscalls" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d0e35dc7d73976a53c7e6d7d177ef804a0c0ee774ec77bcc520c2216fd7cbe" +dependencies = [ + "serde", + "serde_repr", +] + [[package]] name = "thiserror" version = "1.0.58" diff --git a/experiment/seccomp/Cargo.toml b/experiment/seccomp/Cargo.toml index 56c4fae40..0b5bbc8e2 100644 --- a/experiment/seccomp/Cargo.toml +++ b/experiment/seccomp/Cargo.toml @@ -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"]} \ No newline at end of file diff --git a/experiment/seccomp/src/instruction/arch.rs b/experiment/seccomp/src/instruction/arch.rs index 521a0828b..2883f5daa 100644 --- a/experiment/seccomp/src/instruction/arch.rs +++ b/experiment/seccomp/src/instruction/arch.rs @@ -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 { let arch = match arc { Arch::X86 => AUDIT_ARCH_X86_64, + Arch::AArch64 => AUDIT_ARCH_AARCH64 }; vec![ diff --git a/experiment/seccomp/src/main.rs b/experiment/seccomp/src/main.rs index 9fd04bdae..15ab5572e 100644 --- a/experiment/seccomp/src/main.rs +++ b/experiment/seccomp/src/main.rs @@ -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(sock: OwnedFd, fd: &F) -> nix::Result<()> { let fd = fd.as_raw_fd(); @@ -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() diff --git a/experiment/seccomp/src/seccomp.rs b/experiment/seccomp/src/seccomp.rs index bc45741df..5d502e85e 100644 --- a/experiment/seccomp/src/seccomp.rs +++ b/experiment/seccomp/src/seccomp.rs @@ -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 { @@ -198,3 +200,69 @@ struct Filters { pub len: c_ushort, pub filter: *const Instruction, } + +fn get_syscall_number(arc: &Arch, name: &str) -> Option { + 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 +} +impl From for Vec { + 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, + } + } +} \ No newline at end of file