diff --git a/.dockerignore b/.dockerignore index 88cd458..3b574bf 100644 --- a/.dockerignore +++ b/.dockerignore @@ -45,16 +45,17 @@ !minijail/util.c !minijail/util.h +!Cargo.lock +!Cargo.toml !README.md +!policies/*.frequency +!policies/*.policy +!policies/base/*.policy !src/**/* -!Cargo.toml -!Cargo.lock !tools/Main.runtimeconfig.json !tools/Release.rsp !tools/java.base.aotcfg !tools/mkroot -!tools/omegajail-setup -!tools/omegajail-container-wrapper !tools/omegajail-cgroups-wrapper -!policies/*.policy -!policies/*.frequency +!tools/omegajail-container-wrapper +!tools/omegajail-setup diff --git a/.gitignore b/.gitignore index 0cd5ca2..bc254b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,11 @@ # artifacts *.o -*.bpf *.stamp -java-compile -omegajail -stdio-mux .mypy_cache/ +out/ # test artifacts smoketest/run/ -util_test # mkroot artifacts tools/.remote-file-cache/ @@ -18,5 +14,4 @@ rootfs/ # Added by cargo - /target diff --git a/Dockerfile.distrib b/Dockerfile.distrib index 6ba4463..ed562cd 100644 --- a/Dockerfile.distrib +++ b/Dockerfile.distrib @@ -26,7 +26,9 @@ COPY ./src/ ./src/ COPY ./minijail/ ./minijail/ COPY Makefile ./ COPY tools/omegajail-setup ./tools/ +COPY tools/omegajail-container-wrapper ./tools/ COPY tools/omegajail-cgroups-wrapper ./tools/ +COPY ./policies/base/*.policy ./policies/base/ COPY ./policies/*.policy ./policies/*.frequency ./policies/ ARG OMEGAJAIL_RELEASE diff --git a/Makefile b/Makefile index 5100072..e36c61a 100644 --- a/Makefile +++ b/Makefile @@ -1,49 +1,59 @@ -BINARIES := omegajail java-compile -POLICIES := policies/gcc.bpf policies/cpp.bpf policies/ghc.bpf policies/hs.bpf \ - policies/javac.bpf policies/java.bpf policies/fpc.bpf policies/pas.bpf \ - policies/pyc.bpf policies/py.bpf policies/ruby.bpf policies/lua.bpf \ - policies/csc.bpf policies/cs.bpf policies/js.bpf policies/karel.bpf \ - policies/cpp-asan.bpf policies/clang.bpf \ - policies/go.bpf policies/go-build.bpf \ - policies/rustc.bpf policies/rs.bpf - -MINIJAIL_SOURCE_FILES := $(addprefix minijail/,\ - $(cd minijail && git ls-tree --name-only HEAD -- *.c *.c)) +BINARIES := out/bin/omegajail out/bin/java-compile +POLICIES := $(wildcard policies/*.policy) +POLICY_NOTIFY_BINARIES := $(addprefix out/policies/,$(patsubst %.policy,%.bpf,$(notdir $(POLICIES)))) +POLICY_SIGSYS_BINARIES := $(addprefix out/policies/sigsys/,$(patsubst %.policy,%.bpf,$(notdir $(POLICIES)))) + MKROOT_SOURCE_FILES := Dockerfile.rootfs tools/mkroot tools/java.base.aotcfg \ tools/Main.runtimeconfig.json tools/Release.rsp OMEGAJAIL_RELEASE ?= $(shell git describe --tags) DESTDIR ?= /var/lib/omegajail .PHONY: all -all: ${BINARIES} ${POLICIES} +all: $(BINARIES) $(POLICY_NOTIFY_BINARIES) $(POLICY_SIGSYS_BINARIES) + +out/bin: + mkdir -p "$@" + +out/policies: + mkdir -p "$@" + +out/policies/sigsys: out/policies + mkdir -p "$@" minijail/constants.json: $(MAKE) OUT=${PWD}/minijail -C minijail constants.json -omegajail: $(shell find src/ -name '*.rs') - cargo build --release --bin=$@ - cp target/release/$@ $@ +out/bin/omegajail: $(shell find src/ -name '*.rs') | out/bin + cargo build --release --bin=omegajail + cp target/release/omegajail $@ -java-compile: src/java_compile.rs - cargo build --release --bin=$@ - cp target/release/$@ $@ +out/bin/java-compile: src/java_compile.rs | out/bin + cargo build --release --bin=java-compile + cp target/release/java-compile $@ -policies/%.bpf: policies/%.policy policies/omegajail.policy | minijail/constants.json +out/policies/%.bpf: policies/%.policy policies/base/omegajail.policy | minijail/constants.json out/policies ./minijail/tools/compile_seccomp_policy.py \ --use-kill-process \ --default-action=user-notify \ --arch-json=minijail/constants.json \ $< $@ +out/policies/sigsys/%.bpf: policies/%.policy policies/base/omegajail.policy | minijail/constants.json out/policies/sigsys + ./minijail/tools/compile_seccomp_policy.py \ + --use-kill-process \ + --arch-json=minijail/constants.json \ + $< $@ + .PHONY: install -install: ${BINARIES} tools/omegajail-setup ${POLICIES} - install -d $(DESTDIR)/bin $(DESTDIR)/policies - install -t $(DESTDIR)/bin ${BINARIES} tools/omegajail-setup tools/omegajail-cgroups-wrapper - install -t $(DESTDIR)/policies -m 0644 ${POLICIES} +install: $(BINARIES) $(POLICY_NOTIFY_BINARIES) $(POLICY_SIGSYS_BINARIES) tools/omegajail-setup tools/omegajail-cgroups-wrapper + install -d $(DESTDIR)/bin $(DESTDIR)/policies $(DESTDIR)/policies/sigsys + install -t $(DESTDIR)/bin $(BINARIES) tools/omegajail-setup tools/omegajail-cgroups-wrapper + install -t $(DESTDIR)/policies -m 0644 $(POLICY_NOTIFY_BINARIES) + install -t $(DESTDIR)/policies/sigsys -m 0644 $(POLICY_SIGSYS_BINARIES) .PHONY: clean clean: - rm -f ${BINARIES} ${POLICIES} *.o + rm -rf out/ sudo rm -rf rootfs $(MAKE) OUT=${PWD}/minijail -C minijail clean @@ -89,7 +99,7 @@ smoketest-docker: .omegajail-builder-rootfs-runtime-debug.stamp . touch $@ -rootfs: .omegajail-builder-rootfs-runtime.stamp .omegajail-builder-rootfs-setup.stamp ${BINARIES} tools/omegajail-setup ${POLICIES} +rootfs: .omegajail-builder-rootfs-runtime.stamp .omegajail-builder-rootfs-setup.stamp $(BINARIES) tools/omegajail-setup $(POLICY_NOTIFY_BINARIES) $(POLICY_SIGSYS_BINARIES) sudo rm -rf $@ ".$@.tmp" mkdir ".$@.tmp" $(MAKE) DESTDIR=".$@.tmp" install || (sudo rm -rf ".$@.tmp" ; exit 1) diff --git a/policies/omegajail.policy b/policies/base/omegajail.policy similarity index 100% rename from policies/omegajail.policy rename to policies/base/omegajail.policy diff --git a/policies/cpp.policy b/policies/cpp.policy index ac20009..70ad7ff 100644 --- a/policies/cpp.policy +++ b/policies/cpp.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy @frequency ./cpp.frequency # Exit diff --git a/policies/cs.policy b/policies/cs.policy index a3de8ef..fc55076 100644 --- a/policies/cs.policy +++ b/policies/cs.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy @frequency ./cs.frequency # Exit diff --git a/policies/csc.policy b/policies/csc.policy index 8dff187..13bdafd 100644 --- a/policies/csc.policy +++ b/policies/csc.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy @frequency ./csc.frequency # Exit diff --git a/policies/fpc.policy b/policies/fpc.policy index e3ee976..90af9f7 100644 --- a/policies/fpc.policy +++ b/policies/fpc.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy @frequency ./fpc.frequency # Exit diff --git a/policies/gcc.policy b/policies/gcc.policy index e38213f..4bb3e61 100644 --- a/policies/gcc.policy +++ b/policies/gcc.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy @frequency ./gcc.frequency # Exit diff --git a/policies/ghc.policy b/policies/ghc.policy index e5dbd1f..470eef4 100644 --- a/policies/ghc.policy +++ b/policies/ghc.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy @frequency ./ghc.frequency # Exit diff --git a/policies/go.policy b/policies/go.policy index 2e8da23..320bbce 100644 --- a/policies/go.policy +++ b/policies/go.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy # Exit {exit, exit_group}: allow diff --git a/policies/hs.policy b/policies/hs.policy index 40f26de..556f25a 100644 --- a/policies/hs.policy +++ b/policies/hs.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy @frequency ./hs.frequency # Exit diff --git a/policies/java.policy b/policies/java.policy index aea8c9f..77224e7 100644 --- a/policies/java.policy +++ b/policies/java.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy @frequency ./java.frequency # Exit diff --git a/policies/javac.policy b/policies/javac.policy index 7a47444..ac8ec84 100644 --- a/policies/javac.policy +++ b/policies/javac.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy @frequency ./javac.frequency # Exit diff --git a/policies/js.policy b/policies/js.policy index ba3ebc7..d066eb9 100644 --- a/policies/js.policy +++ b/policies/js.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy @frequency ./js.frequency # Exit diff --git a/policies/karel.policy b/policies/karel.policy index 887b1c7..ddb196e 100644 --- a/policies/karel.policy +++ b/policies/karel.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy @frequency ./karel.frequency # Exit diff --git a/policies/lua.policy b/policies/lua.policy index 8e0e5a7..aa81a86 100644 --- a/policies/lua.policy +++ b/policies/lua.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy @frequency ./lua.frequency # Exit diff --git a/policies/pas.policy b/policies/pas.policy index 1fdb81f..8c2fbef 100644 --- a/policies/pas.policy +++ b/policies/pas.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy @frequency ./pas.frequency # Exit diff --git a/policies/py.policy b/policies/py.policy index 7a8acf4..0712d41 100644 --- a/policies/py.policy +++ b/policies/py.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy @frequency ./py.frequency # Exit diff --git a/policies/ruby.policy b/policies/ruby.policy index 1f33cc5..6242664 100644 --- a/policies/ruby.policy +++ b/policies/ruby.policy @@ -1,4 +1,4 @@ -@include ./omegajail.policy +@include ./base/omegajail.policy @frequency ./ruby.frequency # Exit diff --git a/src/args.rs b/src/args.rs index 0db6d57..241fa0a 100644 --- a/src/args.rs +++ b/src/args.rs @@ -141,6 +141,11 @@ pub struct Args { #[clap(long, value_name = "SOURCE:TARGET")] pub bind: Vec, + /// Allows downgrading to the SIGSYS-based seccomp filter that doesn't provide correct SYSACLL + /// information always + #[clap(long)] + pub allow_sigsys_fallback: bool, + /// Any additional arguments to the executable pub extra_args: Vec, } diff --git a/src/jail/child.rs b/src/jail/child.rs index 6174ec4..3cba907 100644 --- a/src/jail/child.rs +++ b/src/jail/child.rs @@ -3,13 +3,14 @@ use std::io::{ErrorKind, Read}; use std::os::unix::net::UnixStream; use anyhow::{anyhow, bail, Context, Result}; +use nix::errno::Errno; use nix::sys::resource::{setrlimit, Resource}; use nix::sys::signal::{sigprocmask, SigSet, SigmaskHow}; use nix::unistd::execve; use crate::jail::options::JailOptions; use crate::jail::{write_message, SendSeccompFDEvent}; -use crate::sys::{seccomp_set_mode_filter_with_listener, SendFile}; +use crate::sys::{seccomp_set_mode_filter, seccomp_set_mode_filter_with_listener, SendFile}; pub(crate) fn run( mut child_sock: UnixStream, @@ -103,12 +104,37 @@ fn setup_signal_handlers() -> Result<()> { } fn setup_seccomp_bpf(child_sock: &mut UnixStream, opts: &JailOptions) -> Result<()> { - let fd = seccomp_set_mode_filter_with_listener(&opts.seccomp_bpf_filter_contents) - .context("seccomp_set_mode_filter_with_listener")?; - let write_message_result = write_message(child_sock, SendSeccompFDEvent {}); - let send_result = child_sock.send_file(fd); - write_message_result.context("write parent setup done event")?; - send_result.context("send seccomp fd")?; + match seccomp_set_mode_filter_with_listener(&opts.seccomp_bpf_filter_notify_contents) { + Ok(fd) => { + let write_message_result = + write_message(child_sock, SendSeccompFDEvent { fd_available: true }); + let send_result = child_sock.send_file(fd); + write_message_result.context("write parent setup done event")?; + send_result.context("send seccomp fd")?; - Ok(()) + return Ok(()); + } + Err(err) => { + if opts.allow_sigsys_fallback { + match err.downcast_ref::() { + Some(&Errno::ENOSYS) => { + seccomp_set_mode_filter(&opts.seccomp_bpf_filter_sigsys_contents) + .context("seccomp_set_mode_filter")?; + write_message( + child_sock, + SendSeccompFDEvent { + fd_available: false, + }, + ) + .context("write parent setup done event")?; + + return Ok(()); + } + Some(&_) | None => {} + } + } + + return Err(err.context("seccomp_set_mode_filter_with_listener")); + } + }; } diff --git a/src/jail/child_init.rs b/src/jail/child_init.rs index 7f2a66b..04f5745 100644 --- a/src/jail/child_init.rs +++ b/src/jail/child_init.rs @@ -383,7 +383,7 @@ fn wait_child( let _ = kill(child, Signal::SIGKILL); None } - Ok(seccomp_fd) => Some(seccomp_fd), + Ok(seccomp_fd) => seccomp_fd, }; let override_status = match wait_read_seccomp_notification(child, deadline, seccomp_fd) { Err(err) => { @@ -420,9 +420,14 @@ fn wait_child( status } -fn wait_receive_seccomp_fd(jail_sock: &mut UnixStream) -> Result { - read_message::(jail_sock).context("wait for seccomp fd message")?; - Ok(jail_sock.recv_file().context("receive seccomp fd")?) +fn wait_receive_seccomp_fd(jail_sock: &mut UnixStream) -> Result> { + let event = + read_message::(jail_sock).context("wait for seccomp fd message")?; + if event.fd_available { + Ok(Some(jail_sock.recv_file().context("receive seccomp fd")?)) + } else { + Ok(None) + } } fn wait_read_seccomp_notification( diff --git a/src/jail/mod.rs b/src/jail/mod.rs index 53ec9bd..6cdb550 100644 --- a/src/jail/mod.rs +++ b/src/jail/mod.rs @@ -66,7 +66,9 @@ pub use crate::sys::WaitidStatus as JailResult; struct ParentSetupDoneEvent {} #[derive(Serialize, Deserialize, Debug)] -struct SendSeccompFDEvent {} +struct SendSeccompFDEvent { + fd_available: bool, +} #[derive(Serialize, Deserialize, Debug)] struct SetupCgroupRequest {} @@ -409,7 +411,8 @@ mod tests { ], env: vec![], // allows everything _except_ `mount(2)`. - seccomp_bpf_filter_contents: base64::decode("IAAAAAQAAAAVAAEAPgAAwAYAAAAAAAAAIAAAAAAAAAAVAAIBpQAAAAYAAAAAAP9/BgAAAAAA/38GAAAAAADAfw==")?, + seccomp_bpf_filter_notify_contents: base64::decode("IAAAAAQAAAAVAAEAPgAAwAYAAAAAAAAAIAAAAAAAAAAVAAIBpQAAAAYAAAAAAP9/BgAAAAAA/38GAAAAAADAfw==")?, + seccomp_bpf_filter_sigsys_contents: base64::decode("IAAAAAQAAAAVAAEAPgAAwAYAAAAAAAAAIAAAAAAAAAAVAAIBpQAAAAYAAAAAAP9/BgAAAAAA/38GAAAAAADAfw==")?, seccomp_profile_name: String::from("test"), meta: None, @@ -423,6 +426,7 @@ mod tests { memory_limit: Some(32 * 1024 * 1024), use_cgroups_for_memory_limit: false, vm_memory_size_in_bytes: 0u64, + allow_sigsys_fallback: false, }; let jail = Jail::new(options)?; diff --git a/src/jail/options.rs b/src/jail/options.rs index a9bf019..973b9c1 100644 --- a/src/jail/options.rs +++ b/src/jail/options.rs @@ -57,7 +57,8 @@ pub(crate) struct JailOptions { pub mounts: Vec, pub args: Vec, pub env: Vec, - pub seccomp_bpf_filter_contents: Vec, + pub seccomp_bpf_filter_notify_contents: Vec, + pub seccomp_bpf_filter_sigsys_contents: Vec, pub seccomp_profile_name: String, pub meta: Option, @@ -71,6 +72,7 @@ pub(crate) struct JailOptions { pub memory_limit: Option, pub use_cgroups_for_memory_limit: bool, pub vm_memory_size_in_bytes: u64, + pub allow_sigsys_fallback: bool, } impl JailOptions { @@ -752,11 +754,18 @@ impl JailOptions { execve_args.extend(args.extra_args); - let mut seccomp_bpf_filter_contents = vec![]; - let bpf_filter_path = root.join(format!("policies/{}.bpf", seccomp_profile_name)); + let mut seccomp_bpf_filter_notify_contents = vec![]; + let mut bpf_filter_path = root.join(format!("policies/{}.bpf", seccomp_profile_name)); File::open(&bpf_filter_path) .with_context(|| format!("open {:?}", &bpf_filter_path))? - .read_to_end(&mut seccomp_bpf_filter_contents) + .read_to_end(&mut seccomp_bpf_filter_notify_contents) + .with_context(|| format!("read {:?}", &bpf_filter_path))?; + + let mut seccomp_bpf_filter_sigsys_contents = vec![]; + bpf_filter_path = root.join(format!("policies/sigsys/{}.bpf", seccomp_profile_name)); + File::open(&bpf_filter_path) + .with_context(|| format!("open {:?}", &bpf_filter_path))? + .read_to_end(&mut seccomp_bpf_filter_sigsys_contents) .with_context(|| format!("read {:?}", &bpf_filter_path))?; let (time_limit, wall_time_limit) = match args.time_limit { @@ -778,7 +787,8 @@ impl JailOptions { .map(|s| CString::new(s.clone())) .try_collect()?, env: env.iter().map(|s| CString::new(*s)).try_collect()?, - seccomp_bpf_filter_contents: seccomp_bpf_filter_contents, + seccomp_bpf_filter_notify_contents: seccomp_bpf_filter_notify_contents, + seccomp_bpf_filter_sigsys_contents: seccomp_bpf_filter_sigsys_contents, seccomp_profile_name: seccomp_profile_name, meta: args.meta.map(|s| PathBuf::from(s)), @@ -799,6 +809,7 @@ impl JailOptions { Some(m) => Some(m), None => None, }, + allow_sigsys_fallback: args.allow_sigsys_fallback, }) } } diff --git a/src/sys.rs b/src/sys.rs index b80d934..66bdd8e 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -3,7 +3,7 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::os::unix::net::UnixStream; use std::time::Duration; -use anyhow::{bail, Context, Result}; +use anyhow::{bail, Context, Error, Result}; use nix::errno::Errno; use nix::ioctl_readwrite; use nix::sched::CloneFlags; @@ -17,9 +17,9 @@ use serde::{Deserialize, Serialize}; /// /// Returns an [`std::io::Error`] with the stringified error if the result of the function call is /// negative, and the result of the function as-is otherwise. -fn check_err(num: T) -> Result { - if num < T::default() { - bail!(std::io::Error::last_os_error().to_string()); +fn check_err(num: libc::c_long) -> Result { + if num < 0 { + return Err(Error::new(Errno::from_i32(num.try_into()?))); } Ok(num) } @@ -210,6 +210,36 @@ pub(crate) fn set_no_new_privs() -> Result<()> { Ok(()) } +pub(crate) fn seccomp_set_mode_filter(filter: &[u8]) -> Result<()> { + const SECCOMP_SET_MODE_FILTER: i32 = 1; + + const SECCOMP_FILTER_FLAG_TSYNC: u64 = 1 << 0; + const SECCOMP_FILTER_FLAG_TSYNC_ESRCH: u64 = 1 << 4; + + #[repr(C)] + struct SockFprog { + len: u16, + filter: *const libc::c_void, + } + + let fprog = SockFprog { + len: (filter.len() / 8).try_into()?, + filter: filter.as_ptr() as *const _, + }; + + check_err(unsafe { + libc::syscall( + libc::SYS_seccomp, + SECCOMP_SET_MODE_FILTER, + SECCOMP_FILTER_FLAG_TSYNC + | SECCOMP_FILTER_FLAG_TSYNC_ESRCH, + &fprog as *const _ as *const libc::c_void, + ) + }).context("seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC|SECCOMP_FILTER_FLAG_TSYNC_ESRCH)")?; + + Ok(()) +} + pub(crate) fn seccomp_set_mode_filter_with_listener(filter: &[u8]) -> Result { const SECCOMP_SET_MODE_FILTER: i32 = 1;