| // Copyright 2017 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| extern crate libc; |
| |
| #[allow(dead_code)] |
| #[allow(non_camel_case_types)] |
| #[allow(non_snake_case)] |
| #[allow(non_upper_case_globals)] |
| mod libminijail; |
| |
| use libc::pid_t; |
| use std::ffi::CString; |
| use std::fmt; |
| use std::fs; |
| use std::io; |
| use std::os::unix::io::{AsRawFd, RawFd}; |
| use std::path::{Path, PathBuf}; |
| use std::ptr::{null, null_mut}; |
| |
| #[derive(Debug)] |
| pub enum Error { |
| // minijail failed to accept bind mount. |
| BindMount { |
| errno: i32, |
| src: PathBuf, |
| dst: PathBuf, |
| }, |
| // minijail failed to accept mount. |
| Mount { |
| errno: i32, |
| src: PathBuf, |
| dest: PathBuf, |
| fstype: String, |
| flags: usize, |
| data: String, |
| }, |
| /// 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. |
| StrToCString(String), |
| /// The path passed in didn't parse to a valid CString. |
| PathToCString(PathBuf), |
| /// Failed to call dup2 to set stdin, stdout, or stderr to /dev/null. |
| DupDevNull(i32), |
| /// Failed to set up /dev/null for FDs 0, 1, or 2. |
| OpenDevNull(io::Error), |
| /// Setting the specified alt-syscall table failed with errno. Is the table in the kernel? |
| SetAltSyscallTable { errno: i32, name: String }, |
| /// chroot failed with the provided errno. |
| SettingChrootDirectory(i32, PathBuf), |
| /// pivot_root failed with the provided errno. |
| SettingPivotRootDirectory(i32, PathBuf), |
| /// There is an entry in /proc/self/fd that isn't a valid PID. |
| ReadFdDirEntry(io::Error), |
| /// /proc/self/fd failed to open. |
| 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 { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match self { |
| &Error::BindMount { |
| ref src, |
| ref dst, |
| errno, |
| } => write!( |
| f, |
| "failed to accept bind mount {:?} -> {:?}: {}", |
| src, dst, errno |
| ), |
| &Error::Mount { |
| errno, |
| ref src, |
| ref dest, |
| ref fstype, |
| flags, |
| ref data, |
| } => write!( |
| f, |
| "failed to accept mount {:?} -> {:?} of type {:?} with flags 0x{:x} \ |
| and data {:?}: {}", |
| src, dest, fstype, flags, data, 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) |
| } |
| &Error::PathToCString(ref s) => { |
| write!(f, "failed to convert path into CString: {:?}", s) |
| } |
| &Error::DupDevNull(errno) => write!( |
| f, |
| "failed to call dup2 to set stdin, stdout, or stderr to /dev/null: {}", |
| errno |
| ), |
| &Error::OpenDevNull(ref e) => write!( |
| f, |
| "fail to open /dev/null for setting FDs 0, 1, or 2: {}", |
| e |
| ), |
| &Error::SetAltSyscallTable { ref name, errno } => { |
| write!(f, "failed to set alt-syscall table {:?}: {}", name, errno) |
| } |
| &Error::SettingChrootDirectory(errno, ref p) => { |
| write!(f, "failed to set chroot {:?}: {}", p, errno) |
| } |
| &Error::SettingPivotRootDirectory(errno, ref p) => { |
| write!(f, "failed to set pivot root {:?}: {}", p, errno) |
| } |
| &Error::ReadFdDirEntry(ref e) => { |
| write!(f, "failed to read an entry in /proc/self/fd: {}", e) |
| } |
| &Error::ReadFdDir(ref e) => write!(f, "failed to open /proc/self/fd: {}", e), |
| &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) |
| } |
| } |
| } |
| } |
| |
| pub type Result<T> = std::result::Result<T, Error>; |
| |
| /// Configuration to jail a process based on wrapping libminijail. |
| /// |
| /// Intentionally leave out everything related to `minijail_run`. Forking is |
| /// hard to reason about w.r.t. memory and resource safety. It is better to avoid |
| /// forking from rust code. Leave forking to the library user, who can make |
| /// an informed decision about when to fork to minimize risk. |
| /// # Examples |
| /// * Load seccomp policy - like "minijail0 -n -S myfilter.policy" |
| /// |
| /// ``` |
| /// # use std::path::Path; |
| /// # use io_jail::Minijail; |
| /// # fn seccomp_filter_test() -> Result<(), ()> { |
| /// let mut j = Minijail::new().map_err(|_| ())?; |
| /// j.no_new_privs(); |
| /// j.parse_seccomp_filters(Path::new("my_filter.policy")).map_err(|_| ())?; |
| /// j.use_seccomp_filter(); |
| /// unsafe { // `fork` will close all the programs FDs. |
| /// j.fork(None).map_err(|_| ())?; |
| /// } |
| /// # Ok(()) |
| /// # } |
| /// ``` |
| /// |
| /// * Keep stdin, stdout, and stderr open after jailing. |
| /// |
| /// ``` |
| /// # use io_jail::Minijail; |
| /// # use std::os::unix::io::RawFd; |
| /// # fn seccomp_filter_test() -> Result<(), ()> { |
| /// let j = Minijail::new().map_err(|_| ())?; |
| /// let preserve_fds: Vec<RawFd> = vec![0, 1, 2]; |
| /// unsafe { // `fork` will close all the programs FDs. |
| /// j.fork(Some(&preserve_fds)).map_err(|_| ())?; |
| /// } |
| /// # Ok(()) |
| /// # } |
| /// ``` |
| /// # Errors |
| /// The `fork` function might not return an error if it fails after forking. A |
| /// partial jail is not recoverable and will instead result in killing the |
| /// process. |
| pub struct Minijail { |
| jail: *mut libminijail::minijail, |
| } |
| |
| impl Minijail { |
| /// Creates a new jail configuration. |
| pub fn new() -> Result<Minijail> { |
| let j = unsafe { |
| // libminijail actually owns the minijail structure. It will live until we call |
| // minijail_destroy. |
| libminijail::minijail_new() |
| }; |
| if j.is_null() { |
| return Err(Error::CreatingMinijail); |
| } |
| Ok(Minijail { jail: j }) |
| } |
| |
| // The following functions are safe because they only set values in the |
| // struct already owned by minijail. The struct's lifetime is tied to |
| // `struct Minijail` so it is guaranteed to be valid |
| |
| pub fn change_uid(&mut self, uid: libc::uid_t) { |
| unsafe { |
| libminijail::minijail_change_uid(self.jail, uid); |
| } |
| } |
| pub fn change_gid(&mut self, gid: libc::gid_t) { |
| unsafe { |
| libminijail::minijail_change_gid(self.jail, gid); |
| } |
| } |
| pub fn set_supplementary_gids(&mut self, ids: &[libc::gid_t]) { |
| unsafe { |
| libminijail::minijail_set_supplementary_gids(self.jail, ids.len(), ids.as_ptr()); |
| } |
| } |
| pub fn keep_supplementary_gids(&mut self) { |
| unsafe { |
| libminijail::minijail_keep_supplementary_gids(self.jail); |
| } |
| } |
| pub fn use_seccomp(&mut self) { |
| unsafe { |
| libminijail::minijail_use_seccomp(self.jail); |
| } |
| } |
| pub fn no_new_privs(&mut self) { |
| unsafe { |
| libminijail::minijail_no_new_privs(self.jail); |
| } |
| } |
| pub fn use_seccomp_filter(&mut self) { |
| unsafe { |
| libminijail::minijail_use_seccomp_filter(self.jail); |
| } |
| } |
| pub fn set_seccomp_filter_tsync(&mut self) { |
| unsafe { |
| libminijail::minijail_set_seccomp_filter_tsync(self.jail); |
| } |
| } |
| pub fn parse_seccomp_filters(&mut self, path: &Path) -> Result<()> { |
| if !path.is_file() { |
| return Err(Error::SeccompPath(path.to_owned())); |
| } |
| |
| let pathstring = path |
| .as_os_str() |
| .to_str() |
| .ok_or(Error::PathToCString(path.to_owned()))?; |
| let filename = |
| CString::new(pathstring).map_err(|_| Error::PathToCString(path.to_owned()))?; |
| unsafe { |
| libminijail::minijail_parse_seccomp_filters(self.jail, filename.as_ptr()); |
| } |
| Ok(()) |
| } |
| pub fn log_seccomp_filter_failures(&mut self) { |
| unsafe { |
| libminijail::minijail_log_seccomp_filter_failures(self.jail); |
| } |
| } |
| pub fn use_caps(&mut self, capmask: u64) { |
| unsafe { |
| libminijail::minijail_use_caps(self.jail, capmask); |
| } |
| } |
| pub fn capbset_drop(&mut self, capmask: u64) { |
| unsafe { |
| libminijail::minijail_capbset_drop(self.jail, capmask); |
| } |
| } |
| pub fn set_ambient_caps(&mut self) { |
| unsafe { |
| libminijail::minijail_set_ambient_caps(self.jail); |
| } |
| } |
| pub fn reset_signal_mask(&mut self) { |
| unsafe { |
| libminijail::minijail_reset_signal_mask(self.jail); |
| } |
| } |
| pub fn run_as_init(&mut self) { |
| unsafe { |
| libminijail::minijail_run_as_init(self.jail); |
| } |
| } |
| pub fn namespace_pids(&mut self) { |
| unsafe { |
| libminijail::minijail_namespace_pids(self.jail); |
| } |
| } |
| pub fn namespace_user(&mut self) { |
| unsafe { |
| libminijail::minijail_namespace_user(self.jail); |
| } |
| } |
| pub fn namespace_user_disable_setgroups(&mut self) { |
| unsafe { |
| libminijail::minijail_namespace_user_disable_setgroups(self.jail); |
| } |
| } |
| pub fn namespace_vfs(&mut self) { |
| unsafe { |
| libminijail::minijail_namespace_vfs(self.jail); |
| } |
| } |
| pub fn skip_setting_securebits(&mut self, securebits_skip_mask: u64) { |
| unsafe { |
| libminijail::minijail_skip_setting_securebits(self.jail, securebits_skip_mask); |
| } |
| } |
| pub fn new_session_keyring(&mut self) { |
| unsafe { |
| libminijail::minijail_new_session_keyring(self.jail); |
| } |
| } |
| pub fn skip_remount_private(&mut self) { |
| unsafe { |
| libminijail::minijail_skip_remount_private(self.jail); |
| } |
| } |
| pub fn namespace_ipc(&mut self) { |
| unsafe { |
| libminijail::minijail_namespace_ipc(self.jail); |
| } |
| } |
| pub fn namespace_net(&mut self) { |
| unsafe { |
| libminijail::minijail_namespace_net(self.jail); |
| } |
| } |
| pub fn namespace_cgroups(&mut self) { |
| unsafe { |
| libminijail::minijail_namespace_cgroups(self.jail); |
| } |
| } |
| pub fn remount_proc_readonly(&mut self) { |
| unsafe { |
| libminijail::minijail_remount_proc_readonly(self.jail); |
| } |
| } |
| pub fn uidmap(&mut self, uid_map: &str) -> Result<()> { |
| let map_cstring = |
| CString::new(uid_map).map_err(|_| Error::StrToCString(uid_map.to_owned()))?; |
| unsafe { |
| libminijail::minijail_uidmap(self.jail, map_cstring.as_ptr()); |
| } |
| Ok(()) |
| } |
| pub fn gidmap(&mut self, gid_map: &str) -> Result<()> { |
| let map_cstring = |
| CString::new(gid_map).map_err(|_| Error::StrToCString(gid_map.to_owned()))?; |
| unsafe { |
| libminijail::minijail_gidmap(self.jail, map_cstring.as_ptr()); |
| } |
| Ok(()) |
| } |
| pub fn inherit_usergroups(&mut self) { |
| unsafe { |
| libminijail::minijail_inherit_usergroups(self.jail); |
| } |
| } |
| pub fn use_alt_syscall(&mut self, table_name: &str) -> Result<()> { |
| let table_name_string = |
| CString::new(table_name).map_err(|_| Error::StrToCString(table_name.to_owned()))?; |
| let ret = |
| unsafe { libminijail::minijail_use_alt_syscall(self.jail, table_name_string.as_ptr()) }; |
| if ret < 0 { |
| return Err(Error::SetAltSyscallTable { |
| errno: ret, |
| name: table_name.to_owned(), |
| }); |
| } |
| Ok(()) |
| } |
| pub fn enter_chroot(&mut self, dir: &Path) -> Result<()> { |
| let pathstring = dir |
| .as_os_str() |
| .to_str() |
| .ok_or(Error::PathToCString(dir.to_owned()))?; |
| let dirname = CString::new(pathstring).map_err(|_| Error::PathToCString(dir.to_owned()))?; |
| let ret = unsafe { libminijail::minijail_enter_chroot(self.jail, dirname.as_ptr()) }; |
| if ret < 0 { |
| return Err(Error::SettingChrootDirectory(ret, dir.to_owned())); |
| } |
| Ok(()) |
| } |
| pub fn enter_pivot_root(&mut self, dir: &Path) -> Result<()> { |
| let pathstring = dir |
| .as_os_str() |
| .to_str() |
| .ok_or(Error::PathToCString(dir.to_owned()))?; |
| let dirname = CString::new(pathstring).map_err(|_| Error::PathToCString(dir.to_owned()))?; |
| let ret = unsafe { libminijail::minijail_enter_pivot_root(self.jail, dirname.as_ptr()) }; |
| if ret < 0 { |
| return Err(Error::SettingPivotRootDirectory(ret, dir.to_owned())); |
| } |
| Ok(()) |
| } |
| pub fn mount(&mut self, src: &Path, dest: &Path, fstype: &str, flags: usize) -> Result<()> { |
| self.mount_with_data(src, dest, fstype, flags, "") |
| } |
| pub fn mount_with_data( |
| &mut self, |
| src: &Path, |
| dest: &Path, |
| fstype: &str, |
| flags: usize, |
| data: &str, |
| ) -> Result<()> { |
| let src_os = src |
| .as_os_str() |
| .to_str() |
| .ok_or(Error::PathToCString(src.to_owned()))?; |
| let src_path = CString::new(src_os).map_err(|_| Error::StrToCString(src_os.to_owned()))?; |
| let dest_os = dest |
| .as_os_str() |
| .to_str() |
| .ok_or(Error::PathToCString(dest.to_owned()))?; |
| let dest_path = |
| CString::new(dest_os).map_err(|_| Error::StrToCString(dest_os.to_owned()))?; |
| let fstype_string = |
| CString::new(fstype).map_err(|_| Error::StrToCString(fstype.to_owned()))?; |
| let data_string = CString::new(data).map_err(|_| Error::StrToCString(data.to_owned()))?; |
| let ret = unsafe { |
| libminijail::minijail_mount_with_data( |
| self.jail, |
| src_path.as_ptr(), |
| dest_path.as_ptr(), |
| fstype_string.as_ptr(), |
| flags as _, |
| data_string.as_ptr(), |
| ) |
| }; |
| if ret < 0 { |
| return Err(Error::Mount { |
| errno: ret, |
| src: src.to_owned(), |
| dest: dest.to_owned(), |
| fstype: fstype.to_owned(), |
| flags: flags, |
| data: data.to_owned(), |
| }); |
| } |
| Ok(()) |
| } |
| pub fn mount_tmp(&mut self) { |
| unsafe { |
| libminijail::minijail_mount_tmp(self.jail); |
| } |
| } |
| pub fn mount_tmp_size(&mut self, size: usize) { |
| unsafe { |
| libminijail::minijail_mount_tmp_size(self.jail, size); |
| } |
| } |
| pub fn mount_bind(&mut self, src: &Path, dest: &Path, writable: bool) -> Result<()> { |
| let src_os = src |
| .as_os_str() |
| .to_str() |
| .ok_or(Error::PathToCString(src.to_owned()))?; |
| let src_path = CString::new(src_os).map_err(|_| Error::StrToCString(src_os.to_owned()))?; |
| let dest_os = dest |
| .as_os_str() |
| .to_str() |
| .ok_or(Error::PathToCString(dest.to_owned()))?; |
| let dest_path = |
| CString::new(dest_os).map_err(|_| Error::StrToCString(dest_os.to_owned()))?; |
| let ret = unsafe { |
| libminijail::minijail_bind( |
| self.jail, |
| src_path.as_ptr(), |
| dest_path.as_ptr(), |
| writable as _, |
| ) |
| }; |
| if ret < 0 { |
| return Err(Error::BindMount { |
| errno: ret, |
| src: src.to_owned(), |
| dst: dest.to_owned(), |
| }); |
| } |
| Ok(()) |
| } |
| pub fn enter(&self) -> () { |
| unsafe { |
| libminijail::minijail_enter(self.jail); |
| } |
| } |
| |
| /// Forks and execs a child and puts it in the previously configured minijail. |
| /// 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 fn run(&self, cmd: &Path, inheritable_fds: &[RawFd], args: &[&str]) -> Result<pid_t> { |
| let cmd_os = cmd.to_str().ok_or(Error::PathToCString(cmd.to_owned()))?; |
| let cmd_cstr = CString::new(cmd_os).map_err(|_| Error::StrToCString(cmd_os.to_owned()))?; |
| |
| // Converts each incoming `args` string to a `CString`, and then puts each `CString` pointer |
| // into a null terminated array, suitable for use as an argv parameter to `execve`. |
| let mut args_cstr = Vec::with_capacity(args.len()); |
| let mut args_array = Vec::with_capacity(args.len()); |
| for &arg in args { |
| let arg_cstr = CString::new(arg).map_err(|_| Error::StrToCString(arg.to_owned()))?; |
| args_array.push(arg_cstr.as_ptr()); |
| args_cstr.push(arg_cstr); |
| } |
| args_array.push(null()); |
| |
| for fd in inheritable_fds { |
| let ret = unsafe { 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.contains(io_fd) { |
| let ret = unsafe { |
| libminijail::minijail_preserve_fd(self.jail, dev_null.as_raw_fd(), *io_fd) |
| }; |
| if ret < 0 { |
| return Err(Error::PreservingFd(ret)); |
| } |
| } |
| } |
| |
| unsafe { |
| libminijail::minijail_close_open_fds(self.jail); |
| } |
| |
| let mut pid = 0; |
| let ret = unsafe { |
| libminijail::minijail_run_pid_pipes( |
| self.jail, |
| cmd_cstr.as_ptr(), |
| args_array.as_ptr(), |
| &mut pid, |
| null_mut(), |
| null_mut(), |
| null_mut(), |
| ) |
| }; |
| if ret < 0 { |
| return Err(Error::ForkingMinijail(ret)); |
| } |
| Ok(pid) |
| } |
| |
| /// 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> { |
| // This test will fail during `cargo test` because the test harness always spawns a test |
| // thread. We will make an exception for that case because the tests for this module should |
| // always be run in a serial fashion using `--test-threads=1`. |
| #[cfg(not(test))] |
| { |
| 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) |
| } |
| } |
| |
| impl Drop for Minijail { |
| /// Frees the Minijail created in Minijail::new. |
| fn drop(&mut self) { |
| unsafe { |
| // Destroys the minijail's memory. It is safe to do here because all references to |
| // this object have been dropped. |
| libminijail::minijail_destroy(self.jail); |
| } |
| } |
| } |
| |
| // 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::*; |
| |
| #[test] |
| fn create_and_free() { |
| unsafe { |
| let j = libminijail::minijail_new(); |
| assert_ne!(std::ptr::null_mut(), j); |
| libminijail::minijail_destroy(j); |
| } |
| |
| let j = Minijail::new().unwrap(); |
| drop(j); |
| } |
| |
| #[test] |
| // Test that setting a seccomp filter with no-new-privs works as non-root. |
| // This is equivalent to minijail0 -n -S <seccomp_policy> |
| fn seccomp_no_new_privs() { |
| let mut j = Minijail::new().unwrap(); |
| j.no_new_privs(); |
| j.parse_seccomp_filters(Path::new("src/test_filter.policy")) |
| .unwrap(); |
| j.use_seccomp_filter(); |
| unsafe { |
| j.fork(None).unwrap(); |
| } |
| } |
| |
| #[test] |
| // Test that open FDs get closed and that FDs in the inherit list are left open. |
| fn close_fds() { |
| unsafe { |
| // Using libc to open/close FDs for testing. |
| const FILE_PATH: &[u8] = b"/dev/null\0"; |
| let j = Minijail::new().unwrap(); |
| let first = libc::open(FILE_PATH.as_ptr() as *const i8, libc::O_RDONLY); |
| assert!(first >= 0); |
| let second = libc::open(FILE_PATH.as_ptr() as *const i8, libc::O_RDONLY); |
| assert!(second >= 0); |
| let fds: Vec<RawFd> = vec![0, 1, 2, first]; |
| if j.fork(Some(&fds)).unwrap() == 0 { |
| assert!(libc::close(second) < 0); // Should fail as second should be closed already. |
| assert_eq!(libc::close(first), 0); // Should succeed as first should be untouched. |
| } |
| } |
| } |
| |
| #[test] |
| #[ignore] // privileged operation. |
| fn chroot() { |
| let mut j = Minijail::new().unwrap(); |
| j.enter_chroot(Path::new(".")).unwrap(); |
| unsafe { |
| j.fork(None).unwrap(); |
| } |
| } |
| |
| #[test] |
| #[ignore] // privileged operation. |
| fn namespace_vfs() { |
| let mut j = Minijail::new().unwrap(); |
| j.namespace_vfs(); |
| unsafe { |
| j.fork(None).unwrap(); |
| } |
| } |
| |
| #[test] |
| fn run() { |
| let j = Minijail::new().unwrap(); |
| j.run(Path::new("/bin/true"), &[], &[]).unwrap(); |
| } |
| } |