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