• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #[allow(dead_code)]
6 #[allow(non_camel_case_types)]
7 #[allow(non_snake_case)]
8 #[allow(non_upper_case_globals)]
9 mod libminijail;
10 
11 use libc::pid_t;
12 use std::ffi::CString;
13 use std::fmt::{self, Display};
14 use std::fs;
15 use std::io;
16 use std::os::unix::io::{AsRawFd, RawFd};
17 use std::path::{Path, PathBuf};
18 use std::ptr::{null, null_mut};
19 
20 #[derive(Debug)]
21 pub enum Error {
22     // minijail failed to accept bind mount.
23     BindMount {
24         errno: i32,
25         src: PathBuf,
26         dst: PathBuf,
27     },
28     // minijail failed to accept mount.
29     Mount {
30         errno: i32,
31         src: PathBuf,
32         dest: PathBuf,
33         fstype: String,
34         flags: usize,
35         data: String,
36     },
37     /// Failure to count the number of threads in /proc/self/tasks.
38     CheckingMultiThreaded(io::Error),
39     /// minjail_new failed, this is an allocation failure.
40     CreatingMinijail,
41     /// minijail_fork failed with the given error code.
42     ForkingMinijail(i32),
43     /// Attempt to `fork` while already multithreaded.
44     ForkingWhileMultiThreaded,
45     /// The seccomp policy path doesn't exist.
46     SeccompPath(PathBuf),
47     /// The string passed in didn't parse to a valid CString.
48     StrToCString(String),
49     /// The path passed in didn't parse to a valid CString.
50     PathToCString(PathBuf),
51     /// Failed to call dup2 to set stdin, stdout, or stderr to /dev/null.
52     DupDevNull(i32),
53     /// Failed to set up /dev/null for FDs 0, 1, or 2.
54     OpenDevNull(io::Error),
55     /// Setting the specified alt-syscall table failed with errno. Is the table in the kernel?
56     SetAltSyscallTable { errno: i32, name: String },
57     /// chroot failed with the provided errno.
58     SettingChrootDirectory(i32, PathBuf),
59     /// pivot_root failed with the provided errno.
60     SettingPivotRootDirectory(i32, PathBuf),
61     /// There is an entry in /proc/self/fd that isn't a valid PID.
62     ReadFdDirEntry(io::Error),
63     /// /proc/self/fd failed to open.
64     ReadFdDir(io::Error),
65     /// An entry in /proc/self/fd is not an integer
66     ProcFd(String),
67     /// Minijail refused to preserve an FD in the inherit list of `fork()`.
68     PreservingFd(i32),
69 }
70 
71 impl Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result72     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73         use self::Error::*;
74 
75         match self {
76             BindMount { src, dst, errno } => write!(
77                 f,
78                 "failed to accept bind mount {} -> {}: {}",
79                 src.display(),
80                 dst.display(),
81                 io::Error::from_raw_os_error(*errno),
82             ),
83             Mount {
84                 errno,
85                 src,
86                 dest,
87                 fstype,
88                 flags,
89                 data,
90             } => write!(
91                 f,
92                 "failed to accept mount {} -> {} of type {:?} with flags 0x{:x} \
93                  and data {:?}: {}",
94                 src.display(),
95                 dest.display(),
96                 fstype,
97                 flags,
98                 data,
99                 io::Error::from_raw_os_error(*errno),
100             ),
101             CheckingMultiThreaded(e) => write!(
102                 f,
103                 "Failed to count the number of threads from /proc/self/tasks {}",
104                 e
105             ),
106             CreatingMinijail => write!(f, "minjail_new failed due to an allocation failure"),
107             ForkingMinijail(e) => write!(f, "minijail_fork failed with error {}", e),
108             ForkingWhileMultiThreaded => write!(f, "Attempt to call fork() while multithreaded"),
109             SeccompPath(p) => write!(f, "missing seccomp policy path: {}", p.display()),
110             StrToCString(s) => write!(f, "failed to convert string into CString: {}", s),
111             PathToCString(s) => write!(f, "failed to convert path into CString: {}", s.display()),
112             DupDevNull(errno) => write!(
113                 f,
114                 "failed to call dup2 to set stdin, stdout, or stderr to /dev/null: {}",
115                 io::Error::from_raw_os_error(*errno),
116             ),
117             OpenDevNull(e) => write!(
118                 f,
119                 "fail to open /dev/null for setting FDs 0, 1, or 2: {}",
120                 e,
121             ),
122             SetAltSyscallTable { name, errno } => write!(
123                 f,
124                 "failed to set alt-syscall table {}: {}",
125                 name,
126                 io::Error::from_raw_os_error(*errno),
127             ),
128             SettingChrootDirectory(errno, p) => write!(
129                 f,
130                 "failed to set chroot {}: {}",
131                 p.display(),
132                 io::Error::from_raw_os_error(*errno),
133             ),
134             SettingPivotRootDirectory(errno, p) => write!(
135                 f,
136                 "failed to set pivot root {}: {}",
137                 p.display(),
138                 io::Error::from_raw_os_error(*errno),
139             ),
140             ReadFdDirEntry(e) => write!(f, "failed to read an entry in /proc/self/fd: {}", e),
141             ReadFdDir(e) => write!(f, "failed to open /proc/self/fd: {}", e),
142             ProcFd(s) => write!(f, "an entry in /proc/self/fd is not an integer: {}", s),
143             PreservingFd(e) => write!(f, "fork failed in minijail_preserve_fd with error {}", e),
144         }
145     }
146 }
147 
148 impl std::error::Error for Error {}
149 
150 pub type Result<T> = std::result::Result<T, Error>;
151 
152 /// Configuration to jail a process based on wrapping libminijail.
153 ///
154 /// Intentionally leave out everything related to `minijail_run`.  Forking is
155 /// hard to reason about w.r.t. memory and resource safety.  It is better to avoid
156 /// forking from rust code.  Leave forking to the library user, who can make
157 /// an informed decision about when to fork to minimize risk.
158 /// # Examples
159 /// * Load seccomp policy - like "minijail0 -n -S myfilter.policy"
160 ///
161 /// ```
162 /// # use std::path::Path;
163 /// # use io_jail::Minijail;
164 /// # fn seccomp_filter_test() -> Result<(), ()> {
165 ///       let mut j = Minijail::new().map_err(|_| ())?;
166 ///       j.no_new_privs();
167 ///       j.parse_seccomp_filters(Path::new("my_filter.policy")).map_err(|_| ())?;
168 ///       j.use_seccomp_filter();
169 ///       unsafe { // `fork` will close all the programs FDs.
170 ///           j.fork(None).map_err(|_| ())?;
171 ///       }
172 /// #     Ok(())
173 /// # }
174 /// ```
175 ///
176 /// * Keep stdin, stdout, and stderr open after jailing.
177 ///
178 /// ```
179 /// # use io_jail::Minijail;
180 /// # use std::os::unix::io::RawFd;
181 /// # fn seccomp_filter_test() -> Result<(), ()> {
182 ///       let j = Minijail::new().map_err(|_| ())?;
183 ///       let preserve_fds: Vec<RawFd> = vec![0, 1, 2];
184 ///       unsafe { // `fork` will close all the programs FDs.
185 ///           j.fork(Some(&preserve_fds)).map_err(|_| ())?;
186 ///       }
187 /// #     Ok(())
188 /// # }
189 /// ```
190 /// # Errors
191 /// The `fork` function might not return an error if it fails after forking. A
192 /// partial jail is not recoverable and will instead result in killing the
193 /// process.
194 pub struct Minijail {
195     jail: *mut libminijail::minijail,
196 }
197 
198 impl Minijail {
199     /// Creates a new jail configuration.
new() -> Result<Minijail>200     pub fn new() -> Result<Minijail> {
201         let j = unsafe {
202             // libminijail actually owns the minijail structure. It will live until we call
203             // minijail_destroy.
204             libminijail::minijail_new()
205         };
206         if j.is_null() {
207             return Err(Error::CreatingMinijail);
208         }
209         Ok(Minijail { jail: j })
210     }
211 
212     // The following functions are safe because they only set values in the
213     // struct already owned by minijail.  The struct's lifetime is tied to
214     // `struct Minijail` so it is guaranteed to be valid
215 
change_uid(&mut self, uid: libc::uid_t)216     pub fn change_uid(&mut self, uid: libc::uid_t) {
217         unsafe {
218             libminijail::minijail_change_uid(self.jail, uid);
219         }
220     }
change_gid(&mut self, gid: libc::gid_t)221     pub fn change_gid(&mut self, gid: libc::gid_t) {
222         unsafe {
223             libminijail::minijail_change_gid(self.jail, gid);
224         }
225     }
set_supplementary_gids(&mut self, ids: &[libc::gid_t])226     pub fn set_supplementary_gids(&mut self, ids: &[libc::gid_t]) {
227         unsafe {
228             libminijail::minijail_set_supplementary_gids(self.jail, ids.len(), ids.as_ptr());
229         }
230     }
keep_supplementary_gids(&mut self)231     pub fn keep_supplementary_gids(&mut self) {
232         unsafe {
233             libminijail::minijail_keep_supplementary_gids(self.jail);
234         }
235     }
use_seccomp(&mut self)236     pub fn use_seccomp(&mut self) {
237         unsafe {
238             libminijail::minijail_use_seccomp(self.jail);
239         }
240     }
no_new_privs(&mut self)241     pub fn no_new_privs(&mut self) {
242         unsafe {
243             libminijail::minijail_no_new_privs(self.jail);
244         }
245     }
use_seccomp_filter(&mut self)246     pub fn use_seccomp_filter(&mut self) {
247         unsafe {
248             libminijail::minijail_use_seccomp_filter(self.jail);
249         }
250     }
set_seccomp_filter_tsync(&mut self)251     pub fn set_seccomp_filter_tsync(&mut self) {
252         unsafe {
253             libminijail::minijail_set_seccomp_filter_tsync(self.jail);
254         }
255     }
parse_seccomp_filters(&mut self, path: &Path) -> Result<()>256     pub fn parse_seccomp_filters(&mut self, path: &Path) -> Result<()> {
257         if !path.is_file() {
258             return Err(Error::SeccompPath(path.to_owned()));
259         }
260 
261         let pathstring = path
262             .as_os_str()
263             .to_str()
264             .ok_or(Error::PathToCString(path.to_owned()))?;
265         let filename =
266             CString::new(pathstring).map_err(|_| Error::PathToCString(path.to_owned()))?;
267         unsafe {
268             libminijail::minijail_parse_seccomp_filters(self.jail, filename.as_ptr());
269         }
270         Ok(())
271     }
log_seccomp_filter_failures(&mut self)272     pub fn log_seccomp_filter_failures(&mut self) {
273         unsafe {
274             libminijail::minijail_log_seccomp_filter_failures(self.jail);
275         }
276     }
use_caps(&mut self, capmask: u64)277     pub fn use_caps(&mut self, capmask: u64) {
278         unsafe {
279             libminijail::minijail_use_caps(self.jail, capmask);
280         }
281     }
capbset_drop(&mut self, capmask: u64)282     pub fn capbset_drop(&mut self, capmask: u64) {
283         unsafe {
284             libminijail::minijail_capbset_drop(self.jail, capmask);
285         }
286     }
set_ambient_caps(&mut self)287     pub fn set_ambient_caps(&mut self) {
288         unsafe {
289             libminijail::minijail_set_ambient_caps(self.jail);
290         }
291     }
reset_signal_mask(&mut self)292     pub fn reset_signal_mask(&mut self) {
293         unsafe {
294             libminijail::minijail_reset_signal_mask(self.jail);
295         }
296     }
run_as_init(&mut self)297     pub fn run_as_init(&mut self) {
298         unsafe {
299             libminijail::minijail_run_as_init(self.jail);
300         }
301     }
namespace_pids(&mut self)302     pub fn namespace_pids(&mut self) {
303         unsafe {
304             libminijail::minijail_namespace_pids(self.jail);
305         }
306     }
namespace_user(&mut self)307     pub fn namespace_user(&mut self) {
308         unsafe {
309             libminijail::minijail_namespace_user(self.jail);
310         }
311     }
namespace_user_disable_setgroups(&mut self)312     pub fn namespace_user_disable_setgroups(&mut self) {
313         unsafe {
314             libminijail::minijail_namespace_user_disable_setgroups(self.jail);
315         }
316     }
namespace_vfs(&mut self)317     pub fn namespace_vfs(&mut self) {
318         unsafe {
319             libminijail::minijail_namespace_vfs(self.jail);
320         }
321     }
new_session_keyring(&mut self)322     pub fn new_session_keyring(&mut self) {
323         unsafe {
324             libminijail::minijail_new_session_keyring(self.jail);
325         }
326     }
skip_remount_private(&mut self)327     pub fn skip_remount_private(&mut self) {
328         unsafe {
329             libminijail::minijail_skip_remount_private(self.jail);
330         }
331     }
namespace_ipc(&mut self)332     pub fn namespace_ipc(&mut self) {
333         unsafe {
334             libminijail::minijail_namespace_ipc(self.jail);
335         }
336     }
namespace_net(&mut self)337     pub fn namespace_net(&mut self) {
338         unsafe {
339             libminijail::minijail_namespace_net(self.jail);
340         }
341     }
namespace_cgroups(&mut self)342     pub fn namespace_cgroups(&mut self) {
343         unsafe {
344             libminijail::minijail_namespace_cgroups(self.jail);
345         }
346     }
remount_proc_readonly(&mut self)347     pub fn remount_proc_readonly(&mut self) {
348         unsafe {
349             libminijail::minijail_remount_proc_readonly(self.jail);
350         }
351     }
uidmap(&mut self, uid_map: &str) -> Result<()>352     pub fn uidmap(&mut self, uid_map: &str) -> Result<()> {
353         let map_cstring =
354             CString::new(uid_map).map_err(|_| Error::StrToCString(uid_map.to_owned()))?;
355         unsafe {
356             libminijail::minijail_uidmap(self.jail, map_cstring.as_ptr());
357         }
358         Ok(())
359     }
gidmap(&mut self, gid_map: &str) -> Result<()>360     pub fn gidmap(&mut self, gid_map: &str) -> Result<()> {
361         let map_cstring =
362             CString::new(gid_map).map_err(|_| Error::StrToCString(gid_map.to_owned()))?;
363         unsafe {
364             libminijail::minijail_gidmap(self.jail, map_cstring.as_ptr());
365         }
366         Ok(())
367     }
inherit_usergroups(&mut self)368     pub fn inherit_usergroups(&mut self) {
369         unsafe {
370             libminijail::minijail_inherit_usergroups(self.jail);
371         }
372     }
use_alt_syscall(&mut self, table_name: &str) -> Result<()>373     pub fn use_alt_syscall(&mut self, table_name: &str) -> Result<()> {
374         let table_name_string =
375             CString::new(table_name).map_err(|_| Error::StrToCString(table_name.to_owned()))?;
376         let ret =
377             unsafe { libminijail::minijail_use_alt_syscall(self.jail, table_name_string.as_ptr()) };
378         if ret < 0 {
379             return Err(Error::SetAltSyscallTable {
380                 errno: ret,
381                 name: table_name.to_owned(),
382             });
383         }
384         Ok(())
385     }
enter_chroot(&mut self, dir: &Path) -> Result<()>386     pub fn enter_chroot(&mut self, dir: &Path) -> Result<()> {
387         let pathstring = dir
388             .as_os_str()
389             .to_str()
390             .ok_or(Error::PathToCString(dir.to_owned()))?;
391         let dirname = CString::new(pathstring).map_err(|_| Error::PathToCString(dir.to_owned()))?;
392         let ret = unsafe { libminijail::minijail_enter_chroot(self.jail, dirname.as_ptr()) };
393         if ret < 0 {
394             return Err(Error::SettingChrootDirectory(ret, dir.to_owned()));
395         }
396         Ok(())
397     }
enter_pivot_root(&mut self, dir: &Path) -> Result<()>398     pub fn enter_pivot_root(&mut self, dir: &Path) -> Result<()> {
399         let pathstring = dir
400             .as_os_str()
401             .to_str()
402             .ok_or(Error::PathToCString(dir.to_owned()))?;
403         let dirname = CString::new(pathstring).map_err(|_| Error::PathToCString(dir.to_owned()))?;
404         let ret = unsafe { libminijail::minijail_enter_pivot_root(self.jail, dirname.as_ptr()) };
405         if ret < 0 {
406             return Err(Error::SettingPivotRootDirectory(ret, dir.to_owned()));
407         }
408         Ok(())
409     }
mount(&mut self, src: &Path, dest: &Path, fstype: &str, flags: usize) -> Result<()>410     pub fn mount(&mut self, src: &Path, dest: &Path, fstype: &str, flags: usize) -> Result<()> {
411         self.mount_with_data(src, dest, fstype, flags, "")
412     }
mount_with_data( &mut self, src: &Path, dest: &Path, fstype: &str, flags: usize, data: &str, ) -> Result<()>413     pub fn mount_with_data(
414         &mut self,
415         src: &Path,
416         dest: &Path,
417         fstype: &str,
418         flags: usize,
419         data: &str,
420     ) -> Result<()> {
421         let src_os = src
422             .as_os_str()
423             .to_str()
424             .ok_or(Error::PathToCString(src.to_owned()))?;
425         let src_path = CString::new(src_os).map_err(|_| Error::StrToCString(src_os.to_owned()))?;
426         let dest_os = dest
427             .as_os_str()
428             .to_str()
429             .ok_or(Error::PathToCString(dest.to_owned()))?;
430         let dest_path =
431             CString::new(dest_os).map_err(|_| Error::StrToCString(dest_os.to_owned()))?;
432         let fstype_string =
433             CString::new(fstype).map_err(|_| Error::StrToCString(fstype.to_owned()))?;
434         let data_string = CString::new(data).map_err(|_| Error::StrToCString(data.to_owned()))?;
435         let ret = unsafe {
436             libminijail::minijail_mount_with_data(
437                 self.jail,
438                 src_path.as_ptr(),
439                 dest_path.as_ptr(),
440                 fstype_string.as_ptr(),
441                 flags as _,
442                 data_string.as_ptr(),
443             )
444         };
445         if ret < 0 {
446             return Err(Error::Mount {
447                 errno: ret,
448                 src: src.to_owned(),
449                 dest: dest.to_owned(),
450                 fstype: fstype.to_owned(),
451                 flags,
452                 data: data.to_owned(),
453             });
454         }
455         Ok(())
456     }
mount_dev(&mut self)457     pub fn mount_dev(&mut self) {
458         unsafe {
459             libminijail::minijail_mount_dev(self.jail);
460         }
461     }
mount_tmp(&mut self)462     pub fn mount_tmp(&mut self) {
463         unsafe {
464             libminijail::minijail_mount_tmp(self.jail);
465         }
466     }
mount_tmp_size(&mut self, size: usize)467     pub fn mount_tmp_size(&mut self, size: usize) {
468         unsafe {
469             libminijail::minijail_mount_tmp_size(self.jail, size);
470         }
471     }
mount_bind(&mut self, src: &Path, dest: &Path, writable: bool) -> Result<()>472     pub fn mount_bind(&mut self, src: &Path, dest: &Path, writable: bool) -> Result<()> {
473         let src_os = src
474             .as_os_str()
475             .to_str()
476             .ok_or(Error::PathToCString(src.to_owned()))?;
477         let src_path = CString::new(src_os).map_err(|_| Error::StrToCString(src_os.to_owned()))?;
478         let dest_os = dest
479             .as_os_str()
480             .to_str()
481             .ok_or(Error::PathToCString(dest.to_owned()))?;
482         let dest_path =
483             CString::new(dest_os).map_err(|_| Error::StrToCString(dest_os.to_owned()))?;
484         let ret = unsafe {
485             libminijail::minijail_bind(
486                 self.jail,
487                 src_path.as_ptr(),
488                 dest_path.as_ptr(),
489                 writable as _,
490             )
491         };
492         if ret < 0 {
493             return Err(Error::BindMount {
494                 errno: ret,
495                 src: src.to_owned(),
496                 dst: dest.to_owned(),
497             });
498         }
499         Ok(())
500     }
501 
502     /// Forks and execs a child and puts it in the previously configured minijail.
503     /// FDs 0, 1, and 2 are overwritten with /dev/null FDs unless they are included in the
504     /// inheritable_fds list. This function may abort in the child on error because a partially
505     /// entered jail isn't recoverable.
run(&self, cmd: &Path, inheritable_fds: &[RawFd], args: &[&str]) -> Result<pid_t>506     pub fn run(&self, cmd: &Path, inheritable_fds: &[RawFd], args: &[&str]) -> Result<pid_t> {
507         let cmd_os = cmd.to_str().ok_or(Error::PathToCString(cmd.to_owned()))?;
508         let cmd_cstr = CString::new(cmd_os).map_err(|_| Error::StrToCString(cmd_os.to_owned()))?;
509 
510         // Converts each incoming `args` string to a `CString`, and then puts each `CString` pointer
511         // into a null terminated array, suitable for use as an argv parameter to `execve`.
512         let mut args_cstr = Vec::with_capacity(args.len());
513         let mut args_array = Vec::with_capacity(args.len());
514         for &arg in args {
515             let arg_cstr = CString::new(arg).map_err(|_| Error::StrToCString(arg.to_owned()))?;
516             args_array.push(arg_cstr.as_ptr());
517             args_cstr.push(arg_cstr);
518         }
519         args_array.push(null());
520 
521         for fd in inheritable_fds {
522             let ret = unsafe { libminijail::minijail_preserve_fd(self.jail, *fd, *fd) };
523             if ret < 0 {
524                 return Err(Error::PreservingFd(ret));
525             }
526         }
527 
528         let dev_null = fs::OpenOptions::new()
529             .read(true)
530             .write(true)
531             .open("/dev/null")
532             .map_err(Error::OpenDevNull)?;
533         // Set stdin, stdout, and stderr to /dev/null unless they are in the inherit list.
534         // These will only be closed when this process exits.
535         for io_fd in &[libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO] {
536             if !inheritable_fds.contains(io_fd) {
537                 let ret = unsafe {
538                     libminijail::minijail_preserve_fd(self.jail, dev_null.as_raw_fd(), *io_fd)
539                 };
540                 if ret < 0 {
541                     return Err(Error::PreservingFd(ret));
542                 }
543             }
544         }
545 
546         unsafe {
547             libminijail::minijail_close_open_fds(self.jail);
548         }
549 
550         let mut pid = 0;
551         let ret = unsafe {
552             libminijail::minijail_run_pid_pipes(
553                 self.jail,
554                 cmd_cstr.as_ptr(),
555                 args_array.as_ptr(),
556                 &mut pid,
557                 null_mut(),
558                 null_mut(),
559                 null_mut(),
560             )
561         };
562         if ret < 0 {
563             return Err(Error::ForkingMinijail(ret));
564         }
565         Ok(pid)
566     }
567 
568     /// Forks a child and puts it in the previously configured minijail.
569     /// `fork` is unsafe because it closes all open FD for this process.  That
570     /// could cause a lot of trouble if not handled carefully.  FDs 0, 1, and 2
571     /// are overwritten with /dev/null FDs unless they are included in the
572     /// inheritable_fds list.
573     /// This Function may abort in the child on error because a partially
574     /// entered jail isn't recoverable.
fork(&self, inheritable_fds: Option<&[RawFd]>) -> Result<pid_t>575     pub unsafe fn fork(&self, inheritable_fds: Option<&[RawFd]>) -> Result<pid_t> {
576         if !is_single_threaded().map_err(Error::CheckingMultiThreaded)? {
577             // This test will fail during `cargo test` because the test harness always spawns a test
578             // thread. We will make an exception for that case because the tests for this module
579             // should always be run in a serial fashion using `--test-threads=1`.
580             #[cfg(not(test))]
581             return Err(Error::ForkingWhileMultiThreaded);
582         }
583 
584         if let Some(keep_fds) = inheritable_fds {
585             for fd in keep_fds {
586                 let ret = libminijail::minijail_preserve_fd(self.jail, *fd, *fd);
587                 if ret < 0 {
588                     return Err(Error::PreservingFd(ret));
589                 }
590             }
591         }
592 
593         let dev_null = fs::OpenOptions::new()
594             .read(true)
595             .write(true)
596             .open("/dev/null")
597             .map_err(Error::OpenDevNull)?;
598         // Set stdin, stdout, and stderr to /dev/null unless they are in the inherit list.
599         // These will only be closed when this process exits.
600         for io_fd in &[libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO] {
601             if inheritable_fds.is_none() || !inheritable_fds.unwrap().contains(io_fd) {
602                 let ret =
603                     libminijail::minijail_preserve_fd(self.jail, dev_null.as_raw_fd(), *io_fd);
604                 if ret < 0 {
605                     return Err(Error::PreservingFd(ret));
606                 }
607             }
608         }
609 
610         libminijail::minijail_close_open_fds(self.jail);
611 
612         let ret = libminijail::minijail_fork(self.jail);
613         if ret < 0 {
614             return Err(Error::ForkingMinijail(ret));
615         }
616         Ok(ret as pid_t)
617     }
618 }
619 
620 impl Drop for Minijail {
621     /// Frees the Minijail created in Minijail::new.
drop(&mut self)622     fn drop(&mut self) {
623         unsafe {
624             // Destroys the minijail's memory.  It is safe to do here because all references to
625             // this object have been dropped.
626             libminijail::minijail_destroy(self.jail);
627         }
628     }
629 }
630 
631 // Count the number of files in the directory specified by `path`.
count_dir_entries<P: AsRef<Path>>(path: P) -> io::Result<usize>632 fn count_dir_entries<P: AsRef<Path>>(path: P) -> io::Result<usize> {
633     Ok(fs::read_dir(path)?.count())
634 }
635 
636 // Return true if the current thread is the only thread in the process.
is_single_threaded() -> io::Result<bool>637 fn is_single_threaded() -> io::Result<bool> {
638     match count_dir_entries("/proc/self/task") {
639         Ok(1) => Ok(true),
640         Ok(_) => Ok(false),
641         Err(e) => Err(e),
642     }
643 }
644 
645 #[cfg(test)]
646 mod tests {
647     use super::*;
648 
649     #[test]
create_and_free()650     fn create_and_free() {
651         unsafe {
652             let j = libminijail::minijail_new();
653             assert_ne!(std::ptr::null_mut(), j);
654             libminijail::minijail_destroy(j);
655         }
656 
657         let j = Minijail::new().unwrap();
658         drop(j);
659     }
660 
661     #[test]
662     // Test that setting a seccomp filter with no-new-privs works as non-root.
663     // This is equivalent to minijail0 -n -S <seccomp_policy>
seccomp_no_new_privs()664     fn seccomp_no_new_privs() {
665         let mut j = Minijail::new().unwrap();
666         j.no_new_privs();
667         j.parse_seccomp_filters(Path::new("src/test_filter.policy"))
668             .unwrap();
669         j.use_seccomp_filter();
670         unsafe {
671             j.fork(None).unwrap();
672         }
673     }
674 
675     #[test]
676     // Test that open FDs get closed and that FDs in the inherit list are left open.
close_fds()677     fn close_fds() {
678         unsafe {
679             // Using libc to open/close FDs for testing.
680             const FILE_PATH: &[u8] = b"/dev/null\0";
681             let j = Minijail::new().unwrap();
682             let first = libc::open(FILE_PATH.as_ptr() as *const i8, libc::O_RDONLY);
683             assert!(first >= 0);
684             let second = libc::open(FILE_PATH.as_ptr() as *const i8, libc::O_RDONLY);
685             assert!(second >= 0);
686             let fds: Vec<RawFd> = vec![0, 1, 2, first];
687             if j.fork(Some(&fds)).unwrap() == 0 {
688                 assert!(libc::close(second) < 0); // Should fail as second should be closed already.
689                 assert_eq!(libc::close(first), 0); // Should succeed as first should be untouched.
690             }
691         }
692     }
693 
694     #[test]
695     #[ignore] // privileged operation.
chroot()696     fn chroot() {
697         let mut j = Minijail::new().unwrap();
698         j.enter_chroot(Path::new(".")).unwrap();
699         unsafe {
700             j.fork(None).unwrap();
701         }
702     }
703 
704     #[test]
705     #[ignore] // privileged operation.
namespace_vfs()706     fn namespace_vfs() {
707         let mut j = Minijail::new().unwrap();
708         j.namespace_vfs();
709         unsafe {
710             j.fork(None).unwrap();
711         }
712     }
713 
714     #[test]
run()715     fn run() {
716         let j = Minijail::new().unwrap();
717         j.run(Path::new("/bin/true"), &[], &[]).unwrap();
718     }
719 }
720