fix seccomp and add tracing-mode project feature
This commit is contained in:
145
src/main.rs
145
src/main.rs
@ -1,38 +1,149 @@
|
||||
use std::{ffi::CString, ptr::null};
|
||||
use nix::{
|
||||
libc::{c_long, EPERM, ORIG_RAX},
|
||||
sys::signal::Signal,
|
||||
unistd::{getpid, getppid},
|
||||
};
|
||||
use std::{
|
||||
ffi::{c_void, CString},
|
||||
mem::size_of,
|
||||
};
|
||||
use std::io::Write;
|
||||
|
||||
use clap::*;
|
||||
|
||||
use libseccomp::*;
|
||||
use nix::libc::EPERM;
|
||||
|
||||
#[derive(clap::Parser, Debug)]
|
||||
struct Args {
|
||||
#[cfg(feature = "tracing-mode")]
|
||||
#[clap(short, long)]
|
||||
log_failed_to: Option<String>,
|
||||
|
||||
command: Vec<String>,
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
log::info!("restrict myself by set_no_new_privs...");
|
||||
nix::sys::prctl::set_no_new_privs()?;
|
||||
let args = Args::parse();
|
||||
|
||||
log::info!("create filter context...");
|
||||
log::trace!("args parsed: {args:?}");
|
||||
|
||||
let mut filter = ScmpFilterContext::new_filter(ScmpAction::Errno(EPERM))?;
|
||||
|
||||
log::info!("add architecture to filter context...");
|
||||
filter.add_arch(ScmpArch::X8664)?;
|
||||
#[cfg(feature = "tracing-mode")]
|
||||
let tracing = args.log_failed_to != None;
|
||||
|
||||
let mut default_action = ScmpAction::Errno(EPERM);
|
||||
|
||||
#[cfg(feature = "tracing-mode")]
|
||||
if let Some(log_fail_to) = args.log_failed_to {
|
||||
use nix::sys::{
|
||||
ptrace::Options,
|
||||
wait::{waitpid, WaitStatus},
|
||||
};
|
||||
log::trace!("tracing mode. forking into a tracer...");
|
||||
|
||||
let fork_result = match unsafe { nix::unistd::fork() } {
|
||||
Err(err) => panic!("failed to fork a child: {}", err),
|
||||
Ok(pid) => pid,
|
||||
};
|
||||
|
||||
log::trace!("fork result: {fork_result:?}");
|
||||
|
||||
if let nix::unistd::ForkResult::Parent { child } = fork_result {
|
||||
// i'm parent, setting up ptrace
|
||||
|
||||
log::trace!("waiting for child to be ready...");
|
||||
waitpid(child, None)?;
|
||||
|
||||
let mut output = match std::fs::OpenOptions::new().append(true).write(true).create(true).open(log_fail_to) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
nix::sys::ptrace::kill(child)?;
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
nix::sys::ptrace::setoptions(child, Options::PTRACE_O_TRACESECCOMP)?;
|
||||
nix::sys::ptrace::cont(child, None)?;
|
||||
log::trace!("child is ready");
|
||||
|
||||
loop {
|
||||
log::debug!("parent: waitpid...");
|
||||
match waitpid(child, None)? {
|
||||
WaitStatus::Exited(pid, ret) => {
|
||||
log::info!("child {pid} exited with return code {ret}");
|
||||
break;
|
||||
}
|
||||
WaitStatus::PtraceEvent(pid, sig,_) => {
|
||||
let syscall_nr = nix::sys::ptrace::read_user(
|
||||
pid,
|
||||
(size_of::<c_long>() * ORIG_RAX as usize) as *mut c_void,
|
||||
)? as i32;
|
||||
let syscall = ScmpSyscall::from(syscall_nr);
|
||||
let syscall_name = syscall.get_name().unwrap_or(format!("syscall({syscall_nr})"));
|
||||
log::info!("parent: child {pid} received signal {sig:?} syscall: {syscall_name}({syscall_nr})");
|
||||
|
||||
writeln!(output, "{} {}", pid.as_raw(), syscall_name);
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
nix::sys::ptrace::cont(child, None);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let command: Vec<_> = args
|
||||
.command
|
||||
.iter()
|
||||
.map(|s| CString::new(s.as_str()).unwrap())
|
||||
.collect();
|
||||
log::trace!("command: {command:?}");
|
||||
let executable = CString::new(args.command[0].clone()).unwrap();
|
||||
log::trace!("executable: {executable:?}");
|
||||
let env: Vec<CString> = std::env::vars()
|
||||
.into_iter()
|
||||
.map(|(k, v)| format!("{k}={v}"))
|
||||
.map(|s| CString::new(s).unwrap())
|
||||
.collect();
|
||||
|
||||
#[cfg(feature = "tracing-mode")]
|
||||
if tracing {
|
||||
// i'm child. trace me
|
||||
log::debug!("child: traceme");
|
||||
nix::sys::ptrace::traceme()?;
|
||||
|
||||
let me = getpid();
|
||||
log::info!("waiting for parent to be ready...");
|
||||
nix::sys::signal::kill(me, nix::sys::signal::Signal::SIGSTOP)?;
|
||||
log::info!("parent is ready");
|
||||
|
||||
let parent = getppid();
|
||||
default_action = ScmpAction::Trace(parent.as_raw() as u16);
|
||||
log::info!("set default action to {default_action:?}");
|
||||
}
|
||||
|
||||
log::trace!("create filter context...");
|
||||
let mut filter = ScmpFilterContext::new_filter(default_action)?;
|
||||
|
||||
filter.add_rule(ScmpAction::Allow, ScmpSyscall::from(nix::libc::SYS_execve as i32))?;
|
||||
|
||||
x2t_sandbox_rulegen::generate! {
|
||||
log::info!("accepting {}", syscall_name);
|
||||
log::trace!("accepting {}({})", syscall_name, syscall_nr);
|
||||
};
|
||||
|
||||
log::info!("load filter into kernel...");
|
||||
log::debug!("restrict myself by set_no_new_privs...");
|
||||
nix::sys::prctl::set_no_new_privs()?;
|
||||
|
||||
log::info!("loading filter into kernel...");
|
||||
if let Err(err) = filter.load() {
|
||||
log::error!("failed to load filter into kernel: {err}");
|
||||
return Err(err.into());
|
||||
}
|
||||
log::trace!("loaded");
|
||||
|
||||
let args: Vec<_> = std::env::args().map(|s| CString::new(s).unwrap()).collect();
|
||||
let command = std::env::args().next().unwrap();
|
||||
let command = CString::new(command).unwrap();
|
||||
let env: Vec<CString> = Vec::new();
|
||||
|
||||
log::info!("executing {:?}", args);
|
||||
if let Err(err) = nix::unistd::execve(&command, args.as_slice(), env.as_slice()) {
|
||||
log::debug!("executing {:?}", args.command);
|
||||
if let Err(err) = nix::unistd::execve(&executable, command.as_slice(), env.as_slice()) {
|
||||
panic!("failed to execve for {err}");
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user