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