summary refs log tree commit diff
path: root/src/main.rs
diff options
context:
space:
mode:
authorStephen Barber <smbarber@chromium.org>2017-10-20 17:15:30 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-10-26 04:17:52 -0700
commit28a5a61616c09525206b6d9166832446adbc5314 (patch)
treec27932b92912038d864f53fe2c74af3f7488ef74 /src/main.rs
parentf70a9d46e7c90dc15fa62f343b6d424ec239a563 (diff)
downloadcrosvm-28a5a61616c09525206b6d9166832446adbc5314.tar
crosvm-28a5a61616c09525206b6d9166832446adbc5314.tar.gz
crosvm-28a5a61616c09525206b6d9166832446adbc5314.tar.bz2
crosvm-28a5a61616c09525206b6d9166832446adbc5314.tar.lz
crosvm-28a5a61616c09525206b6d9166832446adbc5314.tar.xz
crosvm-28a5a61616c09525206b6d9166832446adbc5314.tar.zst
crosvm-28a5a61616c09525206b6d9166832446adbc5314.zip
crosvm: allow specifying wayland path and group
Add a wayland-sock argument for specifying the path to the
wayland socket. Also add a wayland-group argument for specifying
the group that has access to that socket.

BUG=none
TEST=crosvm starts with wayland

Change-Id: I3e53eb697951200340613663dc3e4ed28d8ed7f8
Reviewed-on: https://chromium-review.googlesource.com/733732
Commit-Ready: Stephen Barber <smbarber@chromium.org>
Tested-by: Stephen Barber <smbarber@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs163
1 files changed, 101 insertions, 62 deletions
diff --git a/src/main.rs b/src/main.rs
index 75d4af8..571d27d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -21,8 +21,7 @@ pub mod argument;
 pub mod kernel_cmdline;
 pub mod device_manager;
 
-use std::env::var_os;
-use std::ffi::{OsString, CString, CStr};
+use std::ffi::{CString, CStr};
 use std::fmt;
 use std::fs::{File, OpenOptions, remove_file};
 use std::io::{stdin, stdout};
@@ -38,8 +37,8 @@ use std::time::Duration;
 use io_jail::Minijail;
 use kvm::*;
 use sys_util::{GuestAddress, GuestMemory, EventFd, TempDir, Terminal, Poller, Pollable, Scm,
-               register_signal_handler, Killable, SignalFd, geteuid, getegid, getpid,
-               kill_process_group, reap_child, syslog};
+               register_signal_handler, Killable, SignalFd, chown, getpid, geteuid, getegid,
+               get_user_id, get_group_id, kill_process_group, reap_child, syslog};
 
 
 use argument::{Argument, set_arguments, print_help};
@@ -48,7 +47,6 @@ use vm_control::{VmRequest, VmResponse};
 
 enum Error {
     OpenKernel(PathBuf, std::io::Error),
-    EnvVar(&'static str),
     Socket(std::io::Error),
     Disk(std::io::Error),
     BlockDeviceNew(sys_util::Error),
@@ -64,10 +62,11 @@ enum Error {
     RegisterNet(device_manager::Error),
     RegisterWayland(device_manager::Error),
     RegisterVsock(device_manager::Error),
-    SettingGidMap(io_jail::Error),
-    SettingUidMap(io_jail::Error),
     Cmdline(kernel_cmdline::Error),
-    MissingWayland(PathBuf),
+    GetWaylandGroup(sys_util::Error),
+    SettingUidMap(io_jail::Error),
+    SettingGidMap(io_jail::Error),
+    ChownWaylandRoot(sys_util::Error),
     RegisterIrqfd(sys_util::Error),
     RegisterRng(device_manager::Error),
     RngDeviceNew(devices::virtio::RngError),
@@ -106,7 +105,6 @@ impl fmt::Display for Error {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
             &Error::OpenKernel(ref p, ref e) => write!(f, "failed to open kernel image {:?}: {}", p, e),
-            &Error::EnvVar(key) => write!(f, "missing enviroment variable: {}", key),
             &Error::Socket(ref e) => write!(f, "failed to create socket: {}", e),
             &Error::Disk(ref e) => write!(f, "failed to load disk image: {}", e),
             &Error::BlockDeviceNew(ref e) => write!(f, "failed to create block device: {:?}", e),
@@ -133,10 +131,11 @@ impl fmt::Display for Error {
                 write!(f, "failed to create root directory for a rng device: {:?}", e)
             }
             &Error::RegisterWayland(ref e) => write!(f, "error registering wayland device: {}", e),
-            &Error::SettingGidMap(ref e) => write!(f, "error setting GID map: {}", e),
             &Error::SettingUidMap(ref e) => write!(f, "error setting UID map: {}", e),
+            &Error::SettingGidMap(ref e) => write!(f, "error setting GID map: {}", e),
+            &Error::ChownWaylandRoot(ref e) => write!(f, "error chowning wayland root directory: {:?}", e),
             &Error::Cmdline(ref e) => write!(f, "the given kernel command line was invalid: {}", e),
-            &Error::MissingWayland(ref p) => write!(f, "wayland socket does not exist: {:?}", p),
+            &Error::GetWaylandGroup(ref e) => write!(f, "could not find gid for wayland group: {:?}", e),
             &Error::RegisterIrqfd(ref e) => write!(f, "error registering irqfd: {:?}", e),
             &Error::KernelLoader(ref e) => write!(f, "error loading kernel: {:?}", e),
             #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
@@ -171,13 +170,6 @@ impl Drop for UnlinkUnixDatagram {
     }
 }
 
-fn env_var(key: &'static str) -> Result<OsString> {
-    match var_os(key) {
-        Some(v) => Ok(v),
-        None => Err(Error::EnvVar(key))
-    }
-}
-
 struct DiskOption {
     path: PathBuf,
     writable: bool,
@@ -193,7 +185,8 @@ struct Config {
     netmask: Option<net::Ipv4Addr>,
     mac_address: Option<String>,
     vhost_net: bool,
-    disable_wayland: bool,
+    wayland_socket_path: Option<PathBuf>,
+    wayland_group: Option<String>,
     socket_path: Option<PathBuf>,
     multiprocess: bool,
     seccomp_policy_dir: PathBuf,
@@ -212,7 +205,8 @@ impl Default for Config {
             netmask: None,
             mac_address: None,
             vhost_net: false,
-            disable_wayland: false,
+            wayland_socket_path: None,
+            wayland_group: None,
             socket_path: None,
             multiprocess: true,
             seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR),
@@ -384,46 +378,70 @@ fn run_config(cfg: Config) -> Result<()> {
     }
 
     let wl_root = TempDir::new(&PathBuf::from("/tmp/wl_root"))?;
-    if !cfg.disable_wayland {
-        match env_var("XDG_RUNTIME_DIR") {
-            Ok(p) => {
-                let jailed_wayland_path = Path::new("/wayland-0");
-                let wayland_path = Path::new(&p).join("wayland-0");
-                if !wayland_path.exists() {
-                    return Err(Error::MissingWayland(wayland_path));
-                }
+    if let Some(wayland_socket_path) = cfg.wayland_socket_path {
+        let jailed_wayland_path = Path::new("/wayland-0");
 
-                let (host_socket, device_socket) = UnixDatagram::pair().map_err(Error::Socket)?;
-                control_sockets.push(UnlinkUnixDatagram(host_socket));
-                let wl_box = Box::new(devices::virtio::Wl::new(if cfg.multiprocess {
-                        &jailed_wayland_path
-                    } else {
-                        wayland_path.as_path()
-                    },
-                    device_socket)?);
-
-                let jail = if cfg.multiprocess {
-                    let wl_root_path = wl_root.as_path().unwrap(); // Won't fail if new succeeded.
-                    let policy_path: PathBuf = cfg.seccomp_policy_dir.join("wl_device.policy");
-                    let mut jail = create_base_minijail(wl_root_path, &policy_path)?;
-                    // Map the jail's root uid/gid to the main processes effective uid/gid so that
-                    // the jailed device can access the wayland-0 socket with the same credentials
-                    // as the main process.
-                    jail.uidmap(&format!("0 {} 1", geteuid()))
-                        .map_err(Error::SettingUidMap)?;
-                    jail.change_uid(geteuid());
-                    jail.gidmap(&format!("0 {} 1", getegid()))
-                        .map_err(Error::SettingGidMap)?;
-                    jail.change_gid(getegid());
-                    jail.mount_bind(wayland_path.as_path(), jailed_wayland_path, true).unwrap();
-                    Some(jail)
-                } else {
-                    None
-                };
-                device_manager.register_mmio(wl_box, jail, &mut cmdline).map_err(Error::RegisterWayland)?;
-            }
-            _ => warn!("missing environment variable \"XDG_RUNTIME_DIR\" required to activate virtio wayland device"),
-        }
+        let (host_socket, device_socket) = UnixDatagram::pair().map_err(Error::Socket)?;
+        control_sockets.push(UnlinkUnixDatagram(host_socket));
+        let wl_box = Box::new(devices::virtio::Wl::new(if cfg.multiprocess {
+            &jailed_wayland_path
+        } else {
+            wayland_socket_path.as_path()
+        },
+        device_socket)?);
+
+        let jail = if cfg.multiprocess {
+            let wl_root_path = wl_root.as_path().unwrap(); // Won't fail if new succeeded.
+            let policy_path: PathBuf = cfg.seccomp_policy_dir.join("wl_device.policy");
+            let mut jail = create_base_minijail(wl_root_path, &policy_path)?;
+
+            // Bind mount the wayland socket into jail's root. This is necessary since each
+            // new wayland context must open() the socket.
+            jail.mount_bind(wayland_socket_path.as_path(), jailed_wayland_path, true)
+                .unwrap();
+
+            // Set the uid/gid for the jailed process, and give a basic id map. This
+            // is required for the above bind mount to work.
+            let wayland_group = cfg.wayland_group.unwrap_or(String::from("wayland"));
+            let wayland_cstr = CString::new(wayland_group.into_bytes()).unwrap();
+            let wayland_gid = get_group_id(&wayland_cstr)
+                .map_err(Error::GetWaylandGroup)?;
+
+            let crosvm_user_group = CStr::from_bytes_with_nul(b"crosvm\0").unwrap();
+            let crosvm_uid = match get_user_id(&crosvm_user_group) {
+                Ok(u) => u,
+                Err(e) => {
+                    warn!("falling back to current user id for Wayland: {:?}", e);
+                    geteuid()
+                }
+            };
+            let crosvm_gid = match get_group_id(&crosvm_user_group) {
+                Ok(u) => u,
+                Err(e) => {
+                    warn!("falling back to current group id for Wayland: {:?}", e);
+                    getegid()
+                }
+            };
+            jail.change_uid(crosvm_uid);
+            jail.change_gid(wayland_gid);
+            jail.uidmap(&format!("{0} {0} 1", crosvm_uid))
+                .map_err(Error::SettingUidMap)?;
+            jail.gidmap(&format!("{0} {0} 1", wayland_gid))
+                .map_err(Error::SettingGidMap)?;
+
+            // chown the root directory for the jail so we can actually bind mount the socket.
+            let wayland_root_cstr = CString::new(wl_root_path.as_os_str().to_str().unwrap())
+                .unwrap();
+            chown(&wayland_root_cstr, crosvm_uid, crosvm_gid)
+                .map_err(Error::ChownWaylandRoot)?;
+
+            Some(jail)
+        } else {
+            None
+        };
+        device_manager
+            .register_mmio(wl_box, jail, &mut cmdline)
+            .map_err(Error::RegisterWayland)?;
     }
 
     let vsock_root = TempDir::new(&PathBuf::from("/tmp/vsock_root"))
@@ -874,8 +892,26 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             }
             cfg.mac_address = Some(value.unwrap().to_owned());
         }
-        "no-wl" => {
-            cfg.disable_wayland = true;
+        "wayland-sock" => {
+            if cfg.wayland_socket_path.is_some() {
+                return Err(argument::Error::TooManyArguments("`wayland-sock` already given"
+                                                                 .to_owned()));
+            }
+            let wayland_socket_path = PathBuf::from(value.unwrap());
+            if !wayland_socket_path.exists() {
+                return Err(argument::Error::InvalidValue {
+                               value: value.unwrap().to_string(),
+                               expected: "Wayland socket does not exist",
+                           });
+            }
+            cfg.wayland_socket_path = Some(wayland_socket_path);
+        }
+        "wayland-group" => {
+            if cfg.wayland_group.is_some() {
+                return Err(argument::Error::TooManyArguments("`wayland-group` already given"
+                                                                 .to_owned()));
+            }
+            cfg.wayland_group = Some(value.unwrap().to_owned());
         }
         "socket" => {
             if cfg.socket_path.is_some() {
@@ -944,7 +980,10 @@ fn run_vm(args: std::env::Args) {
                           "IP address to assign to host tap interface."),
           Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
           Argument::value("mac", "MAC", "MAC address for VM."),
-          Argument::flag("no-wl", "Disables the virtio wayland device."),
+          Argument::value("wayland-sock", "PATH", "Path to the Wayland socket to use."),
+          Argument::value("wayland-group",
+                          "GROUP",
+                          "Name of the group with access to the Wayland socket."),
           Argument::short_value('s',
                                 "socket",
                                 "PATH",