summary refs log tree commit diff
path: root/src/plugin/mod.rs
diff options
context:
space:
mode:
authorZach Reizner <zachr@google.com>2018-01-23 21:16:42 -0800
committerchrome-bot <chrome-bot@chromium.org>2018-02-12 22:42:34 -0800
commitcc30d58c18353905154173bab850d3610c7d01bc (patch)
tree4da2ae3f20644d168309a681e825bdbaa0b9dbad /src/plugin/mod.rs
parent8864cb0f3a9184e2420bbad64c43fcddf161e427 (diff)
downloadcrosvm-cc30d58c18353905154173bab850d3610c7d01bc.tar
crosvm-cc30d58c18353905154173bab850d3610c7d01bc.tar.gz
crosvm-cc30d58c18353905154173bab850d3610c7d01bc.tar.bz2
crosvm-cc30d58c18353905154173bab850d3610c7d01bc.tar.lz
crosvm-cc30d58c18353905154173bab850d3610c7d01bc.tar.xz
crosvm-cc30d58c18353905154173bab850d3610c7d01bc.tar.zst
crosvm-cc30d58c18353905154173bab850d3610c7d01bc.zip
crosvm: run plugin process in a jail by default
The plugin process is similar to a virtual device from the perspective
of crosvm. Therefore, the plugin process should be run in a jail,
similar to the other devices in crosvm.

TEST=cargo build --features plugin; ./build_test
BUG=chromium:800626

Change-Id: I881d7b0f8a11e2626f69a5fa0eee0aa59bb6b6be
Reviewed-on: https://chromium-review.googlesource.com/882131
Commit-Ready: Zach Reizner <zachr@chromium.org>
Tested-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
Diffstat (limited to 'src/plugin/mod.rs')
-rw-r--r--src/plugin/mod.rs90
1 files changed, 86 insertions, 4 deletions
diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs
index 4d4805d..31ff8a0 100644
--- a/src/plugin/mod.rs
+++ b/src/plugin/mod.rs
@@ -10,6 +10,7 @@ use std::fs::File;
 use std::io;
 use std::os::unix::io::{IntoRawFd, FromRawFd};
 use std::os::unix::net::UnixDatagram;
+use std::path::Path;
 use std::result;
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::{Arc, Barrier};
@@ -17,13 +18,15 @@ use std::thread;
 use std::time::{Duration, Instant};
 
 use libc::{socketpair, ioctl, c_ulong, AF_UNIX, SOCK_SEQPACKET, FIOCLEX, EAGAIN, EINTR, EINVAL,
-           ENOENT, EPERM, EDEADLK, ENOTTY, EEXIST, EBADF, EOVERFLOW, SIGCHLD};
+           ENOENT, EPERM, EDEADLK, ENOTTY, EEXIST, EBADF, EOVERFLOW, SIGCHLD, MS_NOSUID, MS_NODEV};
 
 use protobuf::ProtobufError;
 
+use io_jail::{self, Minijail};
 use kvm::{Kvm, Vm, Vcpu, VcpuExit, IoeventAddress, NoDatamatch};
 use sys_util::{EventFd, MmapError, Killable, SignalFd, SignalFdError, Poller, Pollable,
-               GuestMemory, Result as SysResult, Error as SysError, register_signal_handler};
+               GuestMemory, Result as SysResult, Error as SysError, register_signal_handler,
+               geteuid, getegid};
 
 use Config;
 
@@ -39,6 +42,7 @@ pub enum Error {
     CloneVcpuSocket(io::Error),
     CreateEventFd(SysError),
     CreateIrqChip(SysError),
+    CreateJail(io_jail::Error),
     CreateKvm(SysError),
     CreateMainSocket(SysError),
     CreateSignalFd(SignalFdError),
@@ -48,9 +52,18 @@ pub enum Error {
     CreateVm(SysError),
     DecodeRequest(ProtobufError),
     EncodeResponse(ProtobufError),
+    MountLib(io_jail::Error),
+    MountLib64(io_jail::Error),
+    MountPlugin(io_jail::Error),
+    MountPluginLib(io_jail::Error),
+    MountRoot(io_jail::Error),
+    NoVarEmpty,
+    ParsePivotRoot(io_jail::Error),
+    ParseSeccomp(io_jail::Error),
     PluginFailed(i32),
     PluginKill(SysError),
     PluginKilled(i32),
+    PluginRunJail(io_jail::Error),
     PluginSocketHup,
     PluginSocketPoll(SysError),
     PluginSocketRecv(SysError),
@@ -59,6 +72,8 @@ pub enum Error {
     PluginTimeout,
     PluginWait(SysError),
     Poll(SysError),
+    SetGidMap(io_jail::Error),
+    SetUidMap(io_jail::Error),
     SigChild {
         pid: u32,
         signo: u32,
@@ -76,6 +91,7 @@ impl fmt::Display for Error {
             Error::CloneVcpuSocket(ref e) => write!(f, "failed to clone vcpu socket: {:?}", e),
             Error::CreateEventFd(ref e) => write!(f, "failed to create eventfd: {:?}", e),
             Error::CreateIrqChip(ref e) => write!(f, "failed to create kvm irqchip: {:?}", e),
+            Error::CreateJail(ref e) => write!(f, "failed to create jail: {}", e),
             Error::CreateKvm(ref e) => write!(f, "error creating Kvm: {:?}", e),
             Error::CreateMainSocket(ref e) => {
                 write!(f, "error creating main request socket: {:?}", e)
@@ -89,9 +105,18 @@ impl fmt::Display for Error {
             Error::CreateVm(ref e) => write!(f, "error creating vm: {:?}", e),
             Error::DecodeRequest(ref e) => write!(f, "failed to decode plugin request: {}", e),
             Error::EncodeResponse(ref e) => write!(f, "failed to encode plugin response: {}", e),
+            Error::MountLib(ref e) => write!(f, "failed to mount: {}", e),
+            Error::MountLib64(ref e) => write!(f, "failed to mount: {}", e),
+            Error::MountPlugin(ref e) => write!(f, "failed to mount: {}", e),
+            Error::MountPluginLib(ref e) => write!(f, "failed to mount: {}", e),
+            Error::MountRoot(ref e) => write!(f, "failed to mount: {}", e),
+            Error::NoVarEmpty => write!(f, "no /var/empty for jailed process to pivot root into"),
+            Error::ParsePivotRoot(ref e) => write!(f, "failed to set jail pivot root: {}", e),
+            Error::ParseSeccomp(ref e) => write!(f, "failed to parse jail seccomp filter: {}", e),
             Error::PluginFailed(ref e) => write!(f, "plugin exited with error: {}", e),
             Error::PluginKill(ref e) => write!(f, "error sending kill signal to plugin: {:?}", e),
             Error::PluginKilled(ref e) => write!(f, "plugin exited with signal {}", e),
+            Error::PluginRunJail(ref e) => write!(f, "failed to run jail: {}", e),
             Error::PluginSocketHup => write!(f, "plugin request socket has been hung up"),
             Error::PluginSocketPoll(ref e) => {
                 write!(f, "failed to poll plugin request sockets: {:?}", e)
@@ -106,6 +131,8 @@ impl fmt::Display for Error {
             Error::PluginTimeout => write!(f, "plugin did not exit within timeout"),
             Error::PluginWait(ref e) => write!(f, "error waiting for plugin to exit: {:?}", e),
             Error::Poll(ref e) => write!(f, "failed to poll all FDs: {:?}", e),
+            Error::SetGidMap(ref e) => write!(f, "failed to set gidmap for jail: {}", e),
+            Error::SetUidMap(ref e) => write!(f, "failed to set uidmap for jail: {}", e),
             Error::SigChild {
                 pid,
                 signo,
@@ -163,6 +190,46 @@ fn mmap_to_sys_err(e: MmapError) -> SysError {
     }
 }
 
+fn create_plugin_jail(root: &Path, seccomp_policy: &Path) -> Result<Minijail> {
+    // All child jails run in a new user namespace without any users mapped,
+    // they run as nobody unless otherwise configured.
+    let mut j = Minijail::new().map_err(Error::CreateJail)?;
+    j.namespace_pids();
+    j.namespace_user();
+    j.uidmap(&format!("{0} {0} 1", geteuid()))
+        .map_err(Error::SetUidMap)?;
+    j.gidmap(&format!("{0} {0} 1", getegid()))
+        .map_err(Error::SetGidMap)?;
+    j.namespace_user_disable_setgroups();
+    // Don't need any capabilities.
+    j.use_caps(0);
+    // Create a new mount namespace with an empty root FS.
+    j.namespace_vfs();
+    j.enter_pivot_root(root).map_err(Error::ParsePivotRoot)?;
+    // Run in an empty network namespace.
+    j.namespace_net();
+    j.no_new_privs();
+    // Use TSYNC only for the side effect of it using SECCOMP_RET_TRAP, which will correctly kill
+    // the entire plugin process if a worker thread commits a seccomp violation.
+    j.set_seccomp_filter_tsync();
+    j.parse_seccomp_filters(seccomp_policy)
+        .map_err(Error::ParseSeccomp)?;
+    j.use_seccomp_filter();
+    // Don't do init setup.
+    j.run_as_init();
+
+    // Create a tmpfs in the plugin's root directory so that we can bind mount it's executable
+    // file into it.  The size=67108864 is size=64*1024*1024 or size=64MB.
+    j.mount_with_data(Path::new("none"),
+                         Path::new("/"),
+                         "tmpfs",
+                         (MS_NOSUID | MS_NODEV) as usize,
+                         "size=67108864")
+        .map_err(Error::MountRoot)?;
+
+    Ok(j)
+}
+
 /// Each `PluginObject` represents one object that was instantiated by the guest using the `Create`
 /// request.
 ///
@@ -228,12 +295,27 @@ pub fn run_config(cfg: Config) -> Result<()> {
     // quickly.
     let sigchld_fd = SignalFd::new(SIGCHLD).map_err(Error::CreateSignalFd)?;
 
+    let jail = if cfg.multiprocess {
+        // An empty directory for jailed plugin pivot root.
+        let empty_root_path = Path::new("/var/empty");
+        if !empty_root_path.exists() {
+            return Err(Error::NoVarEmpty);
+        }
+
+        let policy_path = cfg.seccomp_policy_dir.join("plugin.policy");
+        let jail = create_plugin_jail(empty_root_path, &policy_path)?;
+        Some(jail)
+    } else {
+        None
+    };
+
+    let plugin_path = cfg.plugin.as_ref().unwrap().as_path();
     let vcpu_count = cfg.vcpu_count.unwrap_or(1);
     let mem = GuestMemory::new(&[]).unwrap();
     let kvm = Kvm::new().map_err(Error::CreateKvm)?;
     let mut vm = Vm::new(&kvm, mem).map_err(Error::CreateVm)?;
     vm.create_irq_chip().map_err(Error::CreateIrqChip)?;
-    let mut plugin = Process::new(vcpu_count, &mut vm, &cfg.plugin.unwrap())?;
+    let mut plugin = Process::new(vcpu_count, &mut vm, plugin_path, jail)?;
 
     let exit_evt = EventFd::new().map_err(Error::CreateEventFd)?;
     let kill_signaled = Arc::new(AtomicBool::new(false));
@@ -385,7 +467,7 @@ pub fn run_config(cfg: Config) -> Result<()> {
                             Ok(Some(siginfo)) => {
                                 // If the plugin process has ended, there is no need to continue
                                 // processing plugin connections, so we break early.
-                                if siginfo.ssi_pid == plugin.pid() {
+                                if siginfo.ssi_pid == plugin.pid() as u32 {
                                     break 'poll;
                                 }
                                 // Because SIGCHLD is not expected from anything other than the