diff --git a/src/main.rs b/src/main.rs index 70dbf4de0c..dec98d1d75 100644 --- a/src/main.rs +++ b/src/main.rs @@ -72,6 +72,13 @@ struct BindMount { writable: bool, } +#[allow(dead_code)] +struct GidMap { + inner: libc::gid_t, + outer: libc::gid_t, + count: u32, +} + const DEFAULT_TRACKPAD_WIDTH: u32 = 800; const DEFAULT_TRACKPAD_HEIGHT: u32 = 1280; @@ -102,6 +109,7 @@ pub struct Config { plugin: Option, plugin_root: Option, plugin_mounts: Vec, + plugin_gid_maps: Vec, disks: Vec, host_ip: Option, netmask: Option, @@ -138,6 +146,7 @@ impl Default for Config { plugin: None, plugin_root: None, plugin_mounts: Vec::new(), + plugin_gid_maps: Vec::new(), disks: Vec::new(), host_ip: None, netmask: None, @@ -504,6 +513,45 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: cfg.plugin_mounts.push(BindMount { src, dst, writable }); } + "plugin-gid-map" => { + let components: Vec<&str> = value.unwrap().split(":").collect(); + if components.len() != 3 { + return Err(argument::Error::InvalidValue { + value: value.unwrap().to_owned(), + expected: + "`plugin-gid-map` must have exactly 3 components: ::", + }); + } + + let inner: libc::gid_t = + components[0] + .parse() + .map_err(|_| argument::Error::InvalidValue { + value: components[0].to_owned(), + expected: "the component for `plugin-gid-map` is not valid gid", + })?; + + let outer: libc::gid_t = + components[1] + .parse() + .map_err(|_| argument::Error::InvalidValue { + value: components[1].to_owned(), + expected: "the component for `plugin-gid-map` is not valid gid", + })?; + + let count: u32 = components[2] + .parse() + .map_err(|_| argument::Error::InvalidValue { + value: components[2].to_owned(), + expected: "the component for `plugin-gid-map` is not valid number", + })?; + + cfg.plugin_gid_maps.push(GidMap { + inner, + outer, + count, + }); + } "vhost-net" => cfg.vhost_net = true, "tap-fd" => { cfg.tap_fd.push( @@ -627,6 +675,8 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> { Argument::value("plugin-root", "PATH", "Absolute path to a directory that will become root filesystem for the plugin process."), #[cfg(feature = "plugin")] Argument::value("plugin-mount", "PATH:PATH:BOOL", "Path to be mounted into the plugin's root filesystem. Can be given more than once."), + #[cfg(feature = "plugin")] + Argument::value("plugin-gid-map", "GID:GID:INT", "Supplemental GIDs that should be mapped in plugin jail. Can be given more than once."), Argument::flag("vhost-net", "Use vhost for networking."), Argument::value("tap-fd", "fd", diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index 9ad0fd7afe..0736b1f858 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -481,6 +481,17 @@ pub fn run_config(cfg: Config) -> Result<()> { let policy_path = cfg.seccomp_policy_dir.join("plugin.policy"); let mut jail = create_plugin_jail(root_path, &policy_path)?; + // Update gid map of the jail if caller provided supplemental groups. + if !cfg.plugin_gid_maps.is_empty() { + let map = format!("0 {} 1", getegid()) + + &cfg + .plugin_gid_maps + .into_iter() + .map(|m| format!(",{} {} {}", m.inner, m.outer, m.count)) + .collect::(); + jail.gidmap(&map).map_err(Error::SetGidMap)?; + } + // Mount minimal set of devices (full, zero, urandom, etc). We can not use // jail.mount_dev() here because crosvm may not be running with CAP_SYS_ADMIN. let device_names = ["full", "null", "urandom", "zero"];