diff options
-rw-r--r-- | io_jail/src/lib.rs | 83 | ||||
-rw-r--r-- | io_jail/src/libminijail.rs | 5 | ||||
-rw-r--r-- | seccomp/x86_64/block_device.policy | 3 | ||||
-rw-r--r-- | seccomp/x86_64/net_device.policy | 2 | ||||
-rw-r--r-- | seccomp/x86_64/rng_device.policy | 3 | ||||
-rw-r--r-- | seccomp/x86_64/vhost_net_device.policy | 2 | ||||
-rw-r--r-- | seccomp/x86_64/vhost_vsock_device.policy | 2 | ||||
-rw-r--r-- | seccomp/x86_64/wl_device.policy | 3 |
8 files changed, 100 insertions, 3 deletions
diff --git a/io_jail/src/lib.rs b/io_jail/src/lib.rs index 460c22d..ec077d1 100644 --- a/io_jail/src/lib.rs +++ b/io_jail/src/lib.rs @@ -10,6 +10,7 @@ extern crate libc; #[allow(non_upper_case_globals)] mod libminijail; +use libc::pid_t; use std::fmt; use std::ffi::CString; use std::fs; @@ -26,8 +27,14 @@ pub enum Error { src: PathBuf, dst: PathBuf, }, + /// Failure to count the number of threads in /proc/self/tasks. + CheckingMultiThreaded(io::Error), /// minjail_new failed, this is an allocation failure. CreatingMinijail, + /// minijail_fork failed with the given error code. + ForkingMinijail(i32), + /// Attempt to `fork` while already multithreaded. + ForkingWhileMultiThreaded, /// The seccomp policy path doesn't exist. SeccompPath(PathBuf), /// The string passed in didn't parse to a valid CString. @@ -50,6 +57,8 @@ pub enum Error { ReadFdDir(io::Error), /// An entry in /proc/self/fd is not an integer ProcFd(String), + /// Minijail refused to preserve an FD in the inherit list of `fork()`. + PreservingFd(i32), } impl fmt::Display for Error { @@ -66,9 +75,18 @@ impl fmt::Display for Error { dst, errno) } + &Error::CheckingMultiThreaded(ref e) => { + write!(f, "Failed to count the number of threads from /proc/self/tasks {}", e) + }, &Error::CreatingMinijail => { write!(f, "minjail_new failed due to an allocation failure") } + &Error::ForkingMinijail(ref e) => { + write!(f, "minijail_fork failed with error {}", e) + }, + &Error::ForkingWhileMultiThreaded => { + write!(f, "Attempt to call fork() while multithreaded") + }, &Error::SeccompPath(ref p) => write!(f, "missing seccomp policy path: {:?}", p), &Error::StrToCString(ref s) => { write!(f, "failed to convert string into CString: {:?}", s) @@ -102,6 +120,9 @@ impl fmt::Display for Error { &Error::ProcFd(ref s) => { write!(f, "an entry in /proc/self/fd is not an integer: {}", s) } + &Error::PreservingFd(ref e) => { + write!(f, "fork failed in minijail_preserve_fd with error {}", e) + }, } } } @@ -341,6 +362,54 @@ impl Minijail { Ok(()) } + /// Forks a child and puts it in the previously configured minijail. + /// `fork` is unsafe because it closes all open FD for this process. That + /// could cause a lot of trouble if not handled carefully. FDs 0, 1, and 2 + /// are overwritten with /dev/null FDs unless they are included in the + /// inheritable_fds list. + /// This Function may abort in the child on error because a partially + /// entered jail isn't recoverable. + pub unsafe fn fork(&self, inheritable_fds: Option<&[RawFd]>) -> Result<pid_t> { + if !is_single_threaded().map_err(Error::CheckingMultiThreaded)? { + return Err(Error::ForkingWhileMultiThreaded); + } + + if let Some(keep_fds) = inheritable_fds { + for fd in keep_fds { + let ret = libminijail::minijail_preserve_fd(self.jail, *fd, *fd); + if ret < 0 { + return Err(Error::PreservingFd(ret)); + } + } + } + + let dev_null = fs::OpenOptions::new() + .read(true) + .write(true) + .open("/dev/null") + .map_err(Error::OpenDevNull)?; + // Set stdin, stdout, and stderr to /dev/null unless they are in the inherit list. + // These will only be closed when this process exits. + for io_fd in &[libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO] { + if inheritable_fds.is_none() || !inheritable_fds.unwrap().contains(io_fd) { + let ret = libminijail::minijail_preserve_fd(self.jail, + dev_null.as_raw_fd(), + *io_fd); + if ret < 0 { + return Err(Error::PreservingFd(ret)); + } + } + } + + libminijail::minijail_close_open_fds(self.jail); + + let ret = libminijail::minijail_fork(self.jail); + if ret < 0 { + return Err(Error::ForkingMinijail(ret)); + } + Ok(ret as pid_t) + } + // Closing all open FDs could be unsafe if something is relying on an FD // that is closed unexpectedly. It is safe as long as it is called // immediately after forking and all needed FDs are in `inheritable_fds`. @@ -390,6 +459,20 @@ impl Drop for Minijail { } } +// Count the number of files in the directory specified by `path`. +fn count_dir_entries<P: AsRef<Path>>(path: P) -> io::Result<usize> { + Ok(fs::read_dir(path)?.count()) +} + +// Return true if the current thread is the only thread in the process. +fn is_single_threaded() -> io::Result<bool> { + match count_dir_entries("/proc/self/task") { + Ok(1) => Ok(true), + Ok(_) => Ok(false), + Err(e) => Err(e), + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/io_jail/src/libminijail.rs b/io_jail/src/libminijail.rs index 803a4df..fbc5dc8 100644 --- a/io_jail/src/libminijail.rs +++ b/io_jail/src/libminijail.rs @@ -74,6 +74,7 @@ extern "C" { pub fn minijail_enter_pivot_root(j: *mut minijail, dir: *const c_char) -> c_int; + pub fn minijail_fork(j: *mut minijail) -> pid_t; pub fn minijail_get_original_path(j: *mut minijail, chroot_path: *const c_char) -> *mut c_char; @@ -97,6 +98,10 @@ extern "C" { dest: *const c_char, writeable: c_int) -> c_int; + pub fn minijail_preserve_fd(j: *mut minijail, + parent_fd: c_int, + child_fd: c_int) + -> c_int; pub fn minijail_enter(j: *const minijail); pub fn minijail_run(j: *mut minijail, filename: *const c_char, diff --git a/seccomp/x86_64/block_device.policy b/seccomp/x86_64/block_device.policy index f1f31f8..581169a 100644 --- a/seccomp/x86_64/block_device.policy +++ b/seccomp/x86_64/block_device.policy @@ -3,6 +3,8 @@ # found in the LICENSE file. close: 1 +dup: 1 +dup2: 1 exit_group: 1 futex: 1 lseek: 1 @@ -20,6 +22,5 @@ sigaltstack: 1 clone: arg0 & 0x00010000 write: 1 eventfd2: 1 -dup: 1 poll: 1 getpid: 1 diff --git a/seccomp/x86_64/net_device.policy b/seccomp/x86_64/net_device.policy index e15a00e..23abcfe 100644 --- a/seccomp/x86_64/net_device.policy +++ b/seccomp/x86_64/net_device.policy @@ -3,6 +3,8 @@ # found in the LICENSE file. close: 1 +dup: 1 +dup2: 1 exit_group: 1 futex: 1 # Disallow mmap with PROT_EXEC set. The syntax here doesn't allow bit diff --git a/seccomp/x86_64/rng_device.policy b/seccomp/x86_64/rng_device.policy index a5e5bf7..429e94d 100644 --- a/seccomp/x86_64/rng_device.policy +++ b/seccomp/x86_64/rng_device.policy @@ -3,6 +3,8 @@ # found in the LICENSE file. close: 1 +dup: 1 +dup2: 1 exit_group: 1 futex: 1 # Disallow mmap with PROT_EXEC set. The syntax here doesn't allow bit @@ -19,6 +21,5 @@ sigaltstack: 1 clone: arg0 & 0x00010000 write: 1 eventfd2: 1 -dup: 1 poll: 1 getpid: 1 diff --git a/seccomp/x86_64/vhost_net_device.policy b/seccomp/x86_64/vhost_net_device.policy index 30f79d9..6e61bba 100644 --- a/seccomp/x86_64/vhost_net_device.policy +++ b/seccomp/x86_64/vhost_net_device.policy @@ -3,6 +3,8 @@ # found in the LICENSE file. close: 1 +dup: 1 +dup2: 1 exit_group: 1 futex: 1 # Whitelist vhost_net ioctls only. diff --git a/seccomp/x86_64/vhost_vsock_device.policy b/seccomp/x86_64/vhost_vsock_device.policy index 0310470..fe54042 100644 --- a/seccomp/x86_64/vhost_vsock_device.policy +++ b/seccomp/x86_64/vhost_vsock_device.policy @@ -3,6 +3,8 @@ # found in the LICENSE file. close: 1 +dup: 1 +dup2: 1 exit_group: 1 futex: 1 # Whitelist vhost_vsock ioctls only. diff --git a/seccomp/x86_64/wl_device.policy b/seccomp/x86_64/wl_device.policy index be404be..7f1ee1b 100644 --- a/seccomp/x86_64/wl_device.policy +++ b/seccomp/x86_64/wl_device.policy @@ -1,4 +1,6 @@ close: 1 +dup: 1 +dup2: 1 getpid: 1 exit_group: 1 futex: 1 @@ -18,7 +20,6 @@ sigaltstack: 1 clone: arg0 & 0x00010000 write: 1 eventfd2: 1 -dup: 1 # Used to connect to wayland. arg0 == AF_UNIX && arg1 == SOCK_STREAM|SOCK_CLOEXEC socket: arg0 == 1 && arg1 == 0x80001 && arg2 == 0 # arg1 == FIONBIO |