• 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 use libc::pid_t;
6 use minijail_sys::*;
7 use std::ffi::CString;
8 use std::fmt::{self, Display};
9 use std::fs;
10 use std::io;
11 use std::os::raw::{c_char, c_ulong, c_ushort};
12 use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
13 use std::path::{Path, PathBuf};
14 use std::ptr::{null, null_mut};
15 
16 #[derive(Debug)]
17 pub enum Error {
18     // minijail failed to accept bind mount.
19     BindMount {
20         errno: i32,
21         src: PathBuf,
22         dst: PathBuf,
23     },
24     // minijail failed to accept mount.
25     Mount {
26         errno: i32,
27         src: PathBuf,
28         dest: PathBuf,
29         fstype: String,
30         flags: usize,
31         data: String,
32     },
33     /// Failure to count the number of threads in /proc/self/tasks.
34     CheckingMultiThreaded(io::Error),
35     /// minjail_new failed, this is an allocation failure.
36     CreatingMinijail,
37     /// minijail_fork failed with the given error code.
38     ForkingMinijail(i32),
39     /// Attempt to `fork` while already multithreaded.
40     ForkingWhileMultiThreaded,
41     /// The seccomp policy path doesn't exist.
42     SeccompPath(PathBuf),
43     /// The string passed in didn't parse to a valid CString.
44     StrToCString(String),
45     /// The path passed in didn't parse to a valid CString.
46     PathToCString(PathBuf),
47     /// Failed to call dup2 to set stdin, stdout, or stderr to /dev/null.
48     DupDevNull(i32),
49     /// Failed to set up /dev/null for FDs 0, 1, or 2.
50     OpenDevNull(io::Error),
51     /// Failed to read policy bpf from file.
52     ReadProgram(io::Error),
53     /// Setting the specified alt-syscall table failed with errno. Is the table in the kernel?
54     SetAltSyscallTable { errno: i32, name: String },
55     /// Setting the specified rlimit failed with errno.
56     SetRlimit { errno: i32, kind: libc::c_int },
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     /// Program size is too large
70     ProgramTooLarge,
71     /// Alignment of file should be divisible by the alignment of sock_filter.
72     WrongProgramAlignment,
73     /// File size should be non-zero and a multiple of sock_filter
74     WrongProgramSize,
75 
76     /// The command was not found.
77     NoCommand,
78     /// The command could not be run.
79     NoAccess,
80     /// Process was killed by SIGSYS indicating a seccomp violation.
81     SeccompViolation(i32),
82     /// Process was killed by a signal other than SIGSYS.
83     Killed(u8),
84     /// Process finished returning a non-zero code.
85     ReturnCode(u8),
86 }
87 
88 impl Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result89     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90         use self::Error::*;
91 
92         match self {
93             BindMount { src, dst, errno } => write!(
94                 f,
95                 "failed to accept bind mount {} -> {}: {}",
96                 src.display(),
97                 dst.display(),
98                 io::Error::from_raw_os_error(*errno),
99             ),
100             Mount {
101                 errno,
102                 src,
103                 dest,
104                 fstype,
105                 flags,
106                 data,
107             } => write!(
108                 f,
109                 "failed to accept mount {} -> {} of type {:?} with flags 0x{:x} \
110                  and data {:?}: {}",
111                 src.display(),
112                 dest.display(),
113                 fstype,
114                 flags,
115                 data,
116                 io::Error::from_raw_os_error(*errno),
117             ),
118             CheckingMultiThreaded(e) => write!(
119                 f,
120                 "Failed to count the number of threads from /proc/self/tasks {}",
121                 e
122             ),
123             CreatingMinijail => write!(f, "minjail_new failed due to an allocation failure"),
124             ForkingMinijail(e) => write!(f, "minijail_fork failed with error {}", e),
125             ForkingWhileMultiThreaded => write!(f, "Attempt to call fork() while multithreaded"),
126             SeccompPath(p) => write!(f, "missing seccomp policy path: {}", p.display()),
127             StrToCString(s) => write!(f, "failed to convert string into CString: {}", s),
128             PathToCString(s) => write!(f, "failed to convert path into CString: {}", s.display()),
129             DupDevNull(errno) => write!(
130                 f,
131                 "failed to call dup2 to set stdin, stdout, or stderr to /dev/null: {}",
132                 io::Error::from_raw_os_error(*errno),
133             ),
134             OpenDevNull(e) => write!(
135                 f,
136                 "fail to open /dev/null for setting FDs 0, 1, or 2: {}",
137                 e,
138             ),
139             ReadProgram(e) => write!(f, "failed to read from bpf file: {}", e),
140             SetAltSyscallTable { name, errno } => write!(
141                 f,
142                 "failed to set alt-syscall table {}: {}",
143                 name,
144                 io::Error::from_raw_os_error(*errno),
145             ),
146             SetRlimit { errno, kind } => write!(f, "failed to set rlimit {}: {}", kind, errno),
147             SettingChrootDirectory(errno, p) => write!(
148                 f,
149                 "failed to set chroot {}: {}",
150                 p.display(),
151                 io::Error::from_raw_os_error(*errno),
152             ),
153             SettingPivotRootDirectory(errno, p) => write!(
154                 f,
155                 "failed to set pivot root {}: {}",
156                 p.display(),
157                 io::Error::from_raw_os_error(*errno),
158             ),
159             ReadFdDirEntry(e) => write!(f, "failed to read an entry in /proc/self/fd: {}", e),
160             ReadFdDir(e) => write!(f, "failed to open /proc/self/fd: {}", e),
161             ProcFd(s) => write!(f, "an entry in /proc/self/fd is not an integer: {}", s),
162             PreservingFd(e) => write!(f, "fork failed in minijail_preserve_fd with error {}", e),
163             ProgramTooLarge => write!(f, "bpf program is too large (max 64K instructions)"),
164             WrongProgramAlignment => write!(
165                 f,
166                 "the alignment of bpf file was not a multiple of that of sock_filter"
167             ),
168             WrongProgramSize => write!(f, "bpf file was empty or not a multiple of sock_filter"),
169             NoCommand => write!(f, "command was not found"),
170             NoAccess => write!(f, "unable to execute command"),
171             SeccompViolation(s) => write!(f, "seccomp violation syscall #{}", s),
172             Killed(s) => write!(f, "killed with signal number {}", s),
173             ReturnCode(e) => write!(f, "exited with code {}", e),
174         }
175     }
176 }
177 
178 impl std::error::Error for Error {}
179 
180 pub type Result<T> = std::result::Result<T, Error>;
181 
182 /// Configuration to jail a process based on wrapping libminijail.
183 ///
184 /// Intentionally leave out everything related to `minijail_run`.  Forking is
185 /// hard to reason about w.r.t. memory and resource safety.  It is better to avoid
186 /// forking from rust code.  Leave forking to the library user, who can make
187 /// an informed decision about when to fork to minimize risk.
188 /// # Examples
189 /// * Load seccomp policy - like "minijail0 -n -S myfilter.policy"
190 ///
191 /// ```
192 /// # use minijail::Minijail;
193 /// # fn seccomp_filter_test() -> Result<(), ()> {
194 ///       let mut j = Minijail::new().map_err(|_| ())?;
195 ///       j.no_new_privs();
196 ///       j.parse_seccomp_filters("my_filter.policy").map_err(|_| ())?;
197 ///       j.use_seccomp_filter();
198 ///       unsafe { // `fork` will close all the programs FDs.
199 ///           j.fork(None).map_err(|_| ())?;
200 ///       }
201 /// #     Ok(())
202 /// # }
203 /// ```
204 ///
205 /// * Keep stdin, stdout, and stderr open after jailing.
206 ///
207 /// ```
208 /// # use minijail::Minijail;
209 /// # use std::os::unix::io::RawFd;
210 /// # fn seccomp_filter_test() -> Result<(), ()> {
211 ///       let j = Minijail::new().map_err(|_| ())?;
212 ///       let preserve_fds: Vec<RawFd> = vec![0, 1, 2];
213 ///       unsafe { // `fork` will close all the programs FDs.
214 ///           j.fork(Some(&preserve_fds)).map_err(|_| ())?;
215 ///       }
216 /// #     Ok(())
217 /// # }
218 /// ```
219 /// # Errors
220 /// The `fork` function might not return an error if it fails after forking. A
221 /// partial jail is not recoverable and will instead result in killing the
222 /// process.
223 pub struct Minijail {
224     jail: *mut minijail,
225 }
226 
227 #[link(name = "c")]
228 extern "C" {
__libc_current_sigrtmax() -> libc::c_int229     fn __libc_current_sigrtmax() -> libc::c_int;
230 }
231 
translate_wait_error(ret: libc::c_int) -> Result<()>232 fn translate_wait_error(ret: libc::c_int) -> Result<()> {
233     if ret == 0 {
234         return Ok(());
235     }
236     if ret == MINIJAIL_ERR_NO_COMMAND as libc::c_int {
237         return Err(Error::NoCommand);
238     }
239     if ret == MINIJAIL_ERR_NO_ACCESS as libc::c_int {
240         return Err(Error::NoAccess);
241     }
242     let sig_base: libc::c_int = MINIJAIL_ERR_SIG_BASE as libc::c_int;
243     let sig_max_code: libc::c_int = unsafe { __libc_current_sigrtmax() } + sig_base;
244     if ret > sig_base && ret <= sig_max_code {
245         return Err(Error::Killed(
246             (ret - MINIJAIL_ERR_SIG_BASE as libc::c_int) as u8,
247         ));
248     }
249     if ret > 0 && ret <= 0xff {
250         return Err(Error::ReturnCode(ret as u8));
251     }
252     unreachable!();
253 }
254 
255 impl Minijail {
256     /// Creates a new jail configuration.
new() -> Result<Minijail>257     pub fn new() -> Result<Minijail> {
258         let j = unsafe {
259             // libminijail actually owns the minijail structure. It will live until we call
260             // minijail_destroy.
261             minijail_new()
262         };
263         if j.is_null() {
264             return Err(Error::CreatingMinijail);
265         }
266         Ok(Minijail { jail: j })
267     }
268 
269     /// Clones self to a new `Minijail`. Useful because `fork` can only be called once on a
270     /// `Minijail`.
try_clone(&self) -> Result<Minijail>271     pub fn try_clone(&self) -> Result<Minijail> {
272         let jail_out = Minijail::new()?;
273         unsafe {
274             // Safe to clone one minijail to the other as minijail_clone doesn't modify the source
275             // jail(`self`) and leaves a valid minijail in the destination(`jail_out`).
276             let ret = minijail_copy_jail(self.jail, jail_out.jail);
277             if ret < 0 {
278                 return Err(Error::ReturnCode(ret as u8));
279             }
280         }
281 
282         Ok(jail_out)
283     }
284 
285     // The following functions are safe because they only set values in the
286     // struct already owned by minijail.  The struct's lifetime is tied to
287     // `struct Minijail` so it is guaranteed to be valid
288 
change_uid(&mut self, uid: libc::uid_t)289     pub fn change_uid(&mut self, uid: libc::uid_t) {
290         unsafe {
291             minijail_change_uid(self.jail, uid);
292         }
293     }
change_gid(&mut self, gid: libc::gid_t)294     pub fn change_gid(&mut self, gid: libc::gid_t) {
295         unsafe {
296             minijail_change_gid(self.jail, gid);
297         }
298     }
change_user(&mut self, user: &str) -> Result<()>299     pub fn change_user(&mut self, user: &str) -> Result<()> {
300         let user_cstring = CString::new(user).map_err(|_| Error::StrToCString(user.to_owned()))?;
301         unsafe {
302             minijail_change_user(self.jail, user_cstring.as_ptr());
303         }
304         Ok(())
305     }
change_group(&mut self, group: &str) -> Result<()>306     pub fn change_group(&mut self, group: &str) -> Result<()> {
307         let group_cstring =
308             CString::new(group).map_err(|_| Error::StrToCString(group.to_owned()))?;
309         unsafe {
310             minijail_change_group(self.jail, group_cstring.as_ptr());
311         }
312         Ok(())
313     }
set_supplementary_gids(&mut self, ids: &[libc::gid_t])314     pub fn set_supplementary_gids(&mut self, ids: &[libc::gid_t]) {
315         unsafe {
316             minijail_set_supplementary_gids(self.jail, ids.len(), ids.as_ptr());
317         }
318     }
keep_supplementary_gids(&mut self)319     pub fn keep_supplementary_gids(&mut self) {
320         unsafe {
321             minijail_keep_supplementary_gids(self.jail);
322         }
323     }
324     // rlim_t is defined in minijail-sys to be u64 on all platforms, to avoid
325     // issues on 32-bit platforms. It's also useful to us here to avoid
326     // libc::rlim64_t, which is not defined at all on Android.
set_rlimit(&mut self, kind: libc::c_int, cur: rlim_t, max: rlim_t) -> Result<()>327     pub fn set_rlimit(&mut self, kind: libc::c_int, cur: rlim_t, max: rlim_t) -> Result<()> {
328         let errno = unsafe { minijail_rlimit(self.jail, kind, cur, max) };
329         if errno == 0 {
330             Ok(())
331         } else {
332             Err(Error::SetRlimit { errno, kind })
333         }
334     }
use_seccomp(&mut self)335     pub fn use_seccomp(&mut self) {
336         unsafe {
337             minijail_use_seccomp(self.jail);
338         }
339     }
no_new_privs(&mut self)340     pub fn no_new_privs(&mut self) {
341         unsafe {
342             minijail_no_new_privs(self.jail);
343         }
344     }
use_seccomp_filter(&mut self)345     pub fn use_seccomp_filter(&mut self) {
346         unsafe {
347             minijail_use_seccomp_filter(self.jail);
348         }
349     }
set_seccomp_filter_tsync(&mut self)350     pub fn set_seccomp_filter_tsync(&mut self) {
351         unsafe {
352             minijail_set_seccomp_filter_tsync(self.jail);
353         }
354     }
parse_seccomp_program<P: AsRef<Path>>(&mut self, path: P) -> Result<()>355     pub fn parse_seccomp_program<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
356         if !path.as_ref().is_file() {
357             return Err(Error::SeccompPath(path.as_ref().to_owned()));
358         }
359 
360         let buffer = fs::read(path).map_err(Error::ReadProgram)?;
361         if buffer.len() % std::mem::size_of::<sock_filter>() != 0 {
362             return Err(Error::WrongProgramSize);
363         }
364         let count = buffer.len() / std::mem::size_of::<sock_filter>();
365         if count > (!0 as u16) as usize {
366             return Err(Error::ProgramTooLarge);
367         }
368         if buffer.as_ptr() as usize % std::mem::align_of::<sock_filter>() != 0 {
369             return Err(Error::WrongProgramAlignment);
370         }
371 
372         // Safe cast because we checked that the buffer address is divisible by the alignment of
373         // sock_filter.
374         #[allow(clippy::cast_ptr_alignment)]
375         let header = sock_fprog {
376             len: count as c_ushort,
377             filter: buffer.as_ptr() as *mut sock_filter,
378         };
379         unsafe {
380             minijail_set_seccomp_filters(self.jail, &header);
381         }
382         Ok(())
383     }
parse_seccomp_filters<P: AsRef<Path>>(&mut self, path: P) -> Result<()>384     pub fn parse_seccomp_filters<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
385         if !path.as_ref().is_file() {
386             return Err(Error::SeccompPath(path.as_ref().to_owned()));
387         }
388 
389         let pathstring = path
390             .as_ref()
391             .as_os_str()
392             .to_str()
393             .ok_or_else(|| Error::PathToCString(path.as_ref().to_owned()))?;
394         let filename =
395             CString::new(pathstring).map_err(|_| Error::PathToCString(path.as_ref().to_owned()))?;
396         unsafe {
397             minijail_parse_seccomp_filters(self.jail, filename.as_ptr());
398         }
399         Ok(())
400     }
log_seccomp_filter_failures(&mut self)401     pub fn log_seccomp_filter_failures(&mut self) {
402         unsafe {
403             minijail_log_seccomp_filter_failures(self.jail);
404         }
405     }
use_caps(&mut self, capmask: u64)406     pub fn use_caps(&mut self, capmask: u64) {
407         unsafe {
408             minijail_use_caps(self.jail, capmask);
409         }
410     }
capbset_drop(&mut self, capmask: u64)411     pub fn capbset_drop(&mut self, capmask: u64) {
412         unsafe {
413             minijail_capbset_drop(self.jail, capmask);
414         }
415     }
set_ambient_caps(&mut self)416     pub fn set_ambient_caps(&mut self) {
417         unsafe {
418             minijail_set_ambient_caps(self.jail);
419         }
420     }
reset_signal_mask(&mut self)421     pub fn reset_signal_mask(&mut self) {
422         unsafe {
423             minijail_reset_signal_mask(self.jail);
424         }
425     }
run_as_init(&mut self)426     pub fn run_as_init(&mut self) {
427         unsafe {
428             minijail_run_as_init(self.jail);
429         }
430     }
namespace_pids(&mut self)431     pub fn namespace_pids(&mut self) {
432         unsafe {
433             minijail_namespace_pids(self.jail);
434         }
435     }
namespace_user(&mut self)436     pub fn namespace_user(&mut self) {
437         unsafe {
438             minijail_namespace_user(self.jail);
439         }
440     }
namespace_user_disable_setgroups(&mut self)441     pub fn namespace_user_disable_setgroups(&mut self) {
442         unsafe {
443             minijail_namespace_user_disable_setgroups(self.jail);
444         }
445     }
namespace_vfs(&mut self)446     pub fn namespace_vfs(&mut self) {
447         unsafe {
448             minijail_namespace_vfs(self.jail);
449         }
450     }
new_session_keyring(&mut self)451     pub fn new_session_keyring(&mut self) {
452         unsafe {
453             minijail_new_session_keyring(self.jail);
454         }
455     }
skip_remount_private(&mut self)456     pub fn skip_remount_private(&mut self) {
457         unsafe {
458             minijail_skip_remount_private(self.jail);
459         }
460     }
namespace_ipc(&mut self)461     pub fn namespace_ipc(&mut self) {
462         unsafe {
463             minijail_namespace_ipc(self.jail);
464         }
465     }
namespace_net(&mut self)466     pub fn namespace_net(&mut self) {
467         unsafe {
468             minijail_namespace_net(self.jail);
469         }
470     }
namespace_cgroups(&mut self)471     pub fn namespace_cgroups(&mut self) {
472         unsafe {
473             minijail_namespace_cgroups(self.jail);
474         }
475     }
remount_proc_readonly(&mut self)476     pub fn remount_proc_readonly(&mut self) {
477         unsafe {
478             minijail_remount_proc_readonly(self.jail);
479         }
480     }
set_remount_mode(&mut self, mode: c_ulong)481     pub fn set_remount_mode(&mut self, mode: c_ulong) {
482         unsafe { minijail_remount_mode(self.jail, mode) }
483     }
uidmap(&mut self, uid_map: &str) -> Result<()>484     pub fn uidmap(&mut self, uid_map: &str) -> Result<()> {
485         let map_cstring =
486             CString::new(uid_map).map_err(|_| Error::StrToCString(uid_map.to_owned()))?;
487         unsafe {
488             minijail_uidmap(self.jail, map_cstring.as_ptr());
489         }
490         Ok(())
491     }
gidmap(&mut self, gid_map: &str) -> Result<()>492     pub fn gidmap(&mut self, gid_map: &str) -> Result<()> {
493         let map_cstring =
494             CString::new(gid_map).map_err(|_| Error::StrToCString(gid_map.to_owned()))?;
495         unsafe {
496             minijail_gidmap(self.jail, map_cstring.as_ptr());
497         }
498         Ok(())
499     }
inherit_usergroups(&mut self)500     pub fn inherit_usergroups(&mut self) {
501         unsafe {
502             minijail_inherit_usergroups(self.jail);
503         }
504     }
use_alt_syscall(&mut self, table_name: &str) -> Result<()>505     pub fn use_alt_syscall(&mut self, table_name: &str) -> Result<()> {
506         let table_name_string =
507             CString::new(table_name).map_err(|_| Error::StrToCString(table_name.to_owned()))?;
508         let ret = unsafe { minijail_use_alt_syscall(self.jail, table_name_string.as_ptr()) };
509         if ret < 0 {
510             return Err(Error::SetAltSyscallTable {
511                 errno: ret,
512                 name: table_name.to_owned(),
513             });
514         }
515         Ok(())
516     }
enter_chroot<P: AsRef<Path>>(&mut self, dir: P) -> Result<()>517     pub fn enter_chroot<P: AsRef<Path>>(&mut self, dir: P) -> Result<()> {
518         let pathstring = dir
519             .as_ref()
520             .as_os_str()
521             .to_str()
522             .ok_or_else(|| Error::PathToCString(dir.as_ref().to_owned()))?;
523         let dirname =
524             CString::new(pathstring).map_err(|_| Error::PathToCString(dir.as_ref().to_owned()))?;
525         let ret = unsafe { minijail_enter_chroot(self.jail, dirname.as_ptr()) };
526         if ret < 0 {
527             return Err(Error::SettingChrootDirectory(ret, dir.as_ref().to_owned()));
528         }
529         Ok(())
530     }
enter_pivot_root<P: AsRef<Path>>(&mut self, dir: P) -> Result<()>531     pub fn enter_pivot_root<P: AsRef<Path>>(&mut self, dir: P) -> Result<()> {
532         let pathstring = dir
533             .as_ref()
534             .as_os_str()
535             .to_str()
536             .ok_or_else(|| Error::PathToCString(dir.as_ref().to_owned()))?;
537         let dirname =
538             CString::new(pathstring).map_err(|_| Error::PathToCString(dir.as_ref().to_owned()))?;
539         let ret = unsafe { minijail_enter_pivot_root(self.jail, dirname.as_ptr()) };
540         if ret < 0 {
541             return Err(Error::SettingPivotRootDirectory(
542                 ret,
543                 dir.as_ref().to_owned(),
544             ));
545         }
546         Ok(())
547     }
mount<P1: AsRef<Path>, P2: AsRef<Path>>( &mut self, src: P1, dest: P2, fstype: &str, flags: usize, ) -> Result<()>548     pub fn mount<P1: AsRef<Path>, P2: AsRef<Path>>(
549         &mut self,
550         src: P1,
551         dest: P2,
552         fstype: &str,
553         flags: usize,
554     ) -> Result<()> {
555         self.mount_with_data(src, dest, fstype, flags, "")
556     }
mount_with_data<P1: AsRef<Path>, P2: AsRef<Path>>( &mut self, src: P1, dest: P2, fstype: &str, flags: usize, data: &str, ) -> Result<()>557     pub fn mount_with_data<P1: AsRef<Path>, P2: AsRef<Path>>(
558         &mut self,
559         src: P1,
560         dest: P2,
561         fstype: &str,
562         flags: usize,
563         data: &str,
564     ) -> Result<()> {
565         let src_os = src
566             .as_ref()
567             .as_os_str()
568             .to_str()
569             .ok_or_else(|| Error::PathToCString(src.as_ref().to_owned()))?;
570         let src_path = CString::new(src_os).map_err(|_| Error::StrToCString(src_os.to_owned()))?;
571         let dest_os = dest
572             .as_ref()
573             .as_os_str()
574             .to_str()
575             .ok_or_else(|| Error::PathToCString(dest.as_ref().to_owned()))?;
576         let dest_path =
577             CString::new(dest_os).map_err(|_| Error::StrToCString(dest_os.to_owned()))?;
578         let fstype_string =
579             CString::new(fstype).map_err(|_| Error::StrToCString(fstype.to_owned()))?;
580         let data_string = CString::new(data).map_err(|_| Error::StrToCString(data.to_owned()))?;
581         let ret = unsafe {
582             minijail_mount_with_data(
583                 self.jail,
584                 src_path.as_ptr(),
585                 dest_path.as_ptr(),
586                 fstype_string.as_ptr(),
587                 flags as _,
588                 data_string.as_ptr(),
589             )
590         };
591         if ret < 0 {
592             return Err(Error::Mount {
593                 errno: ret,
594                 src: src.as_ref().to_owned(),
595                 dest: dest.as_ref().to_owned(),
596                 fstype: fstype.to_owned(),
597                 flags,
598                 data: data.to_owned(),
599             });
600         }
601         Ok(())
602     }
mount_dev(&mut self)603     pub fn mount_dev(&mut self) {
604         unsafe {
605             minijail_mount_dev(self.jail);
606         }
607     }
mount_tmp(&mut self)608     pub fn mount_tmp(&mut self) {
609         unsafe {
610             minijail_mount_tmp(self.jail);
611         }
612     }
mount_tmp_size(&mut self, size: usize)613     pub fn mount_tmp_size(&mut self, size: usize) {
614         unsafe {
615             minijail_mount_tmp_size(self.jail, size);
616         }
617     }
mount_bind<P1: AsRef<Path>, P2: AsRef<Path>>( &mut self, src: P1, dest: P2, writable: bool, ) -> Result<()>618     pub fn mount_bind<P1: AsRef<Path>, P2: AsRef<Path>>(
619         &mut self,
620         src: P1,
621         dest: P2,
622         writable: bool,
623     ) -> Result<()> {
624         let src_os = src
625             .as_ref()
626             .as_os_str()
627             .to_str()
628             .ok_or_else(|| Error::PathToCString(src.as_ref().to_owned()))?;
629         let src_path = CString::new(src_os).map_err(|_| Error::StrToCString(src_os.to_owned()))?;
630         let dest_os = dest
631             .as_ref()
632             .as_os_str()
633             .to_str()
634             .ok_or_else(|| Error::PathToCString(dest.as_ref().to_owned()))?;
635         let dest_path =
636             CString::new(dest_os).map_err(|_| Error::StrToCString(dest_os.to_owned()))?;
637         let ret = unsafe {
638             minijail_bind(
639                 self.jail,
640                 src_path.as_ptr(),
641                 dest_path.as_ptr(),
642                 writable as _,
643             )
644         };
645         if ret < 0 {
646             return Err(Error::BindMount {
647                 errno: ret,
648                 src: src.as_ref().to_owned(),
649                 dst: dest.as_ref().to_owned(),
650             });
651         }
652         Ok(())
653     }
654 
655     /// Forks and execs a child and puts it in the previously configured minijail.
656     /// FDs 0, 1, and 2 are overwritten with /dev/null FDs unless they are included in the
657     /// inheritable_fds list. This function may abort in the child on error because a partially
658     /// entered jail isn't recoverable.
run<P: AsRef<Path>, S: AsRef<str>>( &self, cmd: P, inheritable_fds: &[RawFd], args: &[S], ) -> Result<pid_t>659     pub fn run<P: AsRef<Path>, S: AsRef<str>>(
660         &self,
661         cmd: P,
662         inheritable_fds: &[RawFd],
663         args: &[S],
664     ) -> Result<pid_t> {
665         self.run_remap(
666             cmd,
667             &inheritable_fds
668                 .iter()
669                 .map(|&a| (a, a))
670                 .collect::<Vec<(RawFd, RawFd)>>(),
671             args,
672         )
673     }
674 
675     /// Behaves the same as `run()` except `inheritable_fds` is a list of fd
676     /// mappings rather than just a list of fds to preserve.
run_remap<P: AsRef<Path>, S: AsRef<str>>( &self, cmd: P, inheritable_fds: &[(RawFd, RawFd)], args: &[S], ) -> Result<pid_t>677     pub fn run_remap<P: AsRef<Path>, S: AsRef<str>>(
678         &self,
679         cmd: P,
680         inheritable_fds: &[(RawFd, RawFd)],
681         args: &[S],
682     ) -> Result<pid_t> {
683         let cmd_os = cmd
684             .as_ref()
685             .to_str()
686             .ok_or_else(|| Error::PathToCString(cmd.as_ref().to_owned()))?;
687         let cmd_cstr = CString::new(cmd_os).map_err(|_| Error::StrToCString(cmd_os.to_owned()))?;
688 
689         // Converts each incoming `args` string to a `CString`, and then puts each `CString` pointer
690         // into a null terminated array, suitable for use as an argv parameter to `execve`.
691         let mut args_cstr = Vec::with_capacity(args.len());
692         let mut args_array = Vec::with_capacity(args.len());
693         for arg in args {
694             let arg_cstr = CString::new(arg.as_ref())
695                 .map_err(|_| Error::StrToCString(arg.as_ref().to_owned()))?;
696             args_array.push(arg_cstr.as_ptr());
697             args_cstr.push(arg_cstr);
698         }
699         args_array.push(null());
700 
701         for (src_fd, dst_fd) in inheritable_fds {
702             let ret = unsafe { minijail_preserve_fd(self.jail, *src_fd, *dst_fd) };
703             if ret < 0 {
704                 return Err(Error::PreservingFd(ret));
705             }
706         }
707 
708         let dev_null = fs::OpenOptions::new()
709             .read(true)
710             .write(true)
711             .open("/dev/null")
712             .map_err(Error::OpenDevNull)?;
713         // Set stdin, stdout, and stderr to /dev/null unless they are in the inherit list.
714         // These will only be closed when this process exits.
715         for io_fd in &[libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO] {
716             if !inheritable_fds.iter().any(|(_, fd)| *fd == *io_fd) {
717                 let ret = unsafe { minijail_preserve_fd(self.jail, dev_null.as_raw_fd(), *io_fd) };
718                 if ret < 0 {
719                     return Err(Error::PreservingFd(ret));
720                 }
721             }
722         }
723 
724         unsafe {
725             minijail_close_open_fds(self.jail);
726         }
727 
728         let mut pid = 0;
729         let ret = unsafe {
730             minijail_run_pid_pipes(
731                 self.jail,
732                 cmd_cstr.as_ptr(),
733                 args_array.as_ptr() as *const *mut c_char,
734                 &mut pid,
735                 null_mut(),
736                 null_mut(),
737                 null_mut(),
738             )
739         };
740         if ret < 0 {
741             return Err(Error::ForkingMinijail(ret));
742         }
743         Ok(pid)
744     }
745 
746     /// Forks a child and puts it in the previously configured minijail.
747     ///
748     /// # Safety
749     /// `fork` is unsafe because it closes all open FD for this process.  That
750     /// could cause a lot of trouble if not handled carefully.  FDs 0, 1, and 2
751     /// are overwritten with /dev/null FDs unless they are included in the
752     /// inheritable_fds list.
753     ///
754     /// Also, any Rust objects that own fds may try to close them after the fork. If they belong
755     /// to a fd number that was mapped to, the mapped fd will be closed instead.
756     ///
757     /// This Function may abort in the child on error because a partially
758     /// entered jail isn't recoverable.
fork(&self, inheritable_fds: Option<&[RawFd]>) -> Result<pid_t>759     pub unsafe fn fork(&self, inheritable_fds: Option<&[RawFd]>) -> Result<pid_t> {
760         let m: Vec<(RawFd, RawFd)> = inheritable_fds
761             .unwrap_or(&[])
762             .iter()
763             .map(|&a| (a, a))
764             .collect();
765         self.fork_remap(&m)
766     }
767 
768     /// Behaves the same as `fork()` except `inheritable_fds` is a list of fd
769     /// mappings rather than just a list of fds to preserve.
770     ///
771     /// # Safety
772     /// See `fork`.
fork_remap(&self, inheritable_fds: &[(RawFd, RawFd)]) -> Result<pid_t>773     pub unsafe fn fork_remap(&self, inheritable_fds: &[(RawFd, RawFd)]) -> Result<pid_t> {
774         if !is_single_threaded().map_err(Error::CheckingMultiThreaded)? {
775             // This test will fail during `cargo test` because the test harness always spawns a test
776             // thread. We will make an exception for that case because the tests for this module
777             // should always be run in a serial fashion using `--test-threads=1`.
778             #[cfg(not(test))]
779             return Err(Error::ForkingWhileMultiThreaded);
780         }
781 
782         for (src_fd, dst_fd) in inheritable_fds {
783             let ret = minijail_preserve_fd(self.jail, *src_fd, *dst_fd);
784             if ret < 0 {
785                 return Err(Error::PreservingFd(ret));
786             }
787         }
788 
789         let dev_null = fs::OpenOptions::new()
790             .read(true)
791             .write(true)
792             .open("/dev/null")
793             .map_err(Error::OpenDevNull)?;
794         // Set stdin, stdout, and stderr to /dev/null unless they are in the inherit list.
795         // These will only be closed when this process exits.
796         for io_fd in &[libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO] {
797             if !inheritable_fds.iter().any(|(_, fd)| *fd == *io_fd) {
798                 let ret = minijail_preserve_fd(self.jail, dev_null.as_raw_fd(), *io_fd);
799                 if ret < 0 {
800                     return Err(Error::PreservingFd(ret));
801                 }
802             }
803         }
804 
805         minijail_close_open_fds(self.jail);
806 
807         let ret = minijail_fork(self.jail);
808         if ret < 0 {
809             return Err(Error::ForkingMinijail(ret));
810         }
811         if ret == 0 {
812             // Safe because dev_null was remapped.
813             dev_null.into_raw_fd();
814         }
815         Ok(ret as pid_t)
816     }
817 
wait(&self) -> Result<()>818     pub fn wait(&self) -> Result<()> {
819         let ret: libc::c_int;
820         // This is safe because it does not modify the struct.
821         unsafe {
822             ret = minijail_wait(self.jail);
823         }
824         translate_wait_error(ret)
825     }
826 
827     /// Send a SIGTERM to the child process and wait for its return code.
kill(&self) -> Result<()>828     pub fn kill(&self) -> Result<()> {
829         let ret = unsafe {
830             // The kill does not change any internal state.
831             minijail_kill(self.jail)
832         };
833         // minijail_kill waits for the process, so also translate the returned wait error.
834         translate_wait_error(ret)
835     }
836 }
837 
838 impl Drop for Minijail {
839     /// Frees the Minijail created in Minijail::new.
drop(&mut self)840     fn drop(&mut self) {
841         unsafe {
842             // Destroys the minijail's memory.  It is safe to do here because all references to
843             // this object have been dropped.
844             minijail_destroy(self.jail);
845         }
846     }
847 }
848 
849 // Count the number of files in the directory specified by `path`.
count_dir_entries<P: AsRef<Path>>(path: P) -> io::Result<usize>850 fn count_dir_entries<P: AsRef<Path>>(path: P) -> io::Result<usize> {
851     Ok(fs::read_dir(path)?.count())
852 }
853 
854 // Return true if the current thread is the only thread in the process.
is_single_threaded() -> io::Result<bool>855 fn is_single_threaded() -> io::Result<bool> {
856     match count_dir_entries("/proc/self/task") {
857         Ok(1) => Ok(true),
858         Ok(_) => Ok(false),
859         Err(e) => Err(e),
860     }
861 }
862 
863 #[cfg(test)]
864 mod tests {
865     use std::process::exit;
866 
867     use super::*;
868 
869     const SHELL: &str = "/bin/sh";
870     const EMPTY_STRING_SLICE: &[&str] = &[];
871 
872     #[test]
create_and_free()873     fn create_and_free() {
874         unsafe {
875             let j = minijail_new();
876             assert_ne!(std::ptr::null_mut(), j);
877             minijail_destroy(j);
878         }
879 
880         let j = Minijail::new().unwrap();
881         drop(j);
882     }
883 
884     #[test]
885     // Test that setting a seccomp filter with no-new-privs works as non-root.
886     // This is equivalent to minijail0 -n -S <seccomp_policy>
seccomp_no_new_privs()887     fn seccomp_no_new_privs() {
888         let mut j = Minijail::new().unwrap();
889         j.no_new_privs();
890         j.parse_seccomp_filters("src/test_filter.policy").unwrap();
891         j.use_seccomp_filter();
892         if unsafe { j.fork(None).unwrap() } == 0 {
893             exit(0);
894         }
895     }
896 
897     #[test]
898     // Test that open FDs get closed and that FDs in the inherit list are left open.
close_fds()899     fn close_fds() {
900         unsafe {
901             // Using libc to open/close FDs for testing.
902             const FILE_PATH: &[u8] = b"/dev/null\0";
903             let j = Minijail::new().unwrap();
904             let first = libc::open(FILE_PATH.as_ptr() as *const c_char, libc::O_RDONLY);
905             assert!(first >= 0);
906             let second = libc::open(FILE_PATH.as_ptr() as *const c_char, libc::O_RDONLY);
907             assert!(second >= 0);
908             let fds: Vec<RawFd> = vec![0, 1, 2, first];
909             if j.fork(Some(&fds)).unwrap() == 0 {
910                 assert!(libc::close(second) < 0); // Should fail as second should be closed already.
911                 assert_eq!(libc::close(first), 0); // Should succeed as first should be untouched.
912                 exit(0);
913             }
914         }
915     }
916 
917     macro_rules! expect_result {
918         ($call:expr, $expected:pat) => {
919             let got = $call;
920             match got {
921                 $expected => {}
922                 _ => {
923                     panic!("got {:?} expected {:?}", got, stringify!($expected));
924                 }
925             }
926         };
927     }
928 
929     #[test]
wait_success()930     fn wait_success() {
931         let j = Minijail::new().unwrap();
932         j.run("/bin/true", &[1, 2], &EMPTY_STRING_SLICE).unwrap();
933         expect_result!(j.wait(), Ok(()));
934     }
935 
936     #[test]
wait_killed()937     fn wait_killed() {
938         let j = Minijail::new().unwrap();
939         j.run(
940             SHELL,
941             &[1, 2],
942             &[SHELL, "-c", "kill -9 $$ &\n/usr/bin/sleep 5"],
943         )
944         .unwrap();
945         expect_result!(j.wait(), Err(Error::Killed(9)));
946     }
947 
948     #[test]
wait_returncode()949     fn wait_returncode() {
950         let j = Minijail::new().unwrap();
951         j.run("/bin/false", &[1, 2], &EMPTY_STRING_SLICE).unwrap();
952         expect_result!(j.wait(), Err(Error::ReturnCode(1)));
953     }
954 
955     #[test]
wait_noaccess()956     fn wait_noaccess() {
957         let j = Minijail::new().unwrap();
958         j.run("/dev/null", &[1, 2], &EMPTY_STRING_SLICE).unwrap();
959         expect_result!(j.wait(), Err(Error::NoAccess));
960     }
961 
962     #[test]
wait_nocommand()963     fn wait_nocommand() {
964         let j = Minijail::new().unwrap();
965         j.run("/bin/does not exist", &[1, 2], &EMPTY_STRING_SLICE)
966             .unwrap();
967         expect_result!(j.wait(), Err(Error::NoCommand));
968     }
969 
970     #[test]
kill_success()971     fn kill_success() {
972         let j = Minijail::new().unwrap();
973         j.run(
974             Path::new("usr/bin/sleep"),
975             &[1, 2],
976             &["/usr/bin/sleep", "5"],
977         )
978         .unwrap();
979         const EXPECTED_SIGNAL: u8 = libc::SIGTERM as u8;
980         expect_result!(j.kill(), Err(Error::Killed(EXPECTED_SIGNAL)));
981     }
982 
983     #[test]
984     #[ignore] // privileged operation.
chroot()985     fn chroot() {
986         let mut j = Minijail::new().unwrap();
987         j.enter_chroot(".").unwrap();
988         if unsafe { j.fork(None).unwrap() } == 0 {
989             exit(0);
990         }
991     }
992 
993     #[test]
994     #[ignore] // privileged operation.
namespace_vfs()995     fn namespace_vfs() {
996         let mut j = Minijail::new().unwrap();
997         j.namespace_vfs();
998         if unsafe { j.fork(None).unwrap() } == 0 {
999             exit(0);
1000         }
1001     }
1002 
1003     #[test]
run()1004     fn run() {
1005         let j = Minijail::new().unwrap();
1006         j.run("/bin/true", &[], &EMPTY_STRING_SLICE).unwrap();
1007     }
1008 
1009     #[test]
run_clone()1010     fn run_clone() {
1011         let j = Minijail::new().unwrap();
1012         let b = j.try_clone().unwrap();
1013         // Pass the same FDs to both clones and make sure they don't conflict.
1014         j.run("/bin/true", &[1, 2], &EMPTY_STRING_SLICE).unwrap();
1015         b.run("/bin/true", &[1, 2], &EMPTY_STRING_SLICE).unwrap();
1016     }
1017 
1018     #[test]
run_string_vec()1019     fn run_string_vec() {
1020         let j = Minijail::new().unwrap();
1021         let args = vec!["ignored".to_string()];
1022         j.run(Path::new("/bin/true"), &[], &args).unwrap();
1023     }
1024 }
1025