diff options
author | Dmitry Torokhov <dtor@chromium.org> | 2019-03-06 10:56:51 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2019-03-08 21:20:23 -0800 |
commit | 710060744866cde8cada39caa8461a7194e4869b (patch) | |
tree | 75e7590ddb729c2780f94f605f219e80c4220b5e | |
parent | 766f8108b39ab55fcb05bf8de249ea6170536599 (diff) | |
download | crosvm-710060744866cde8cada39caa8461a7194e4869b.tar crosvm-710060744866cde8cada39caa8461a7194e4869b.tar.gz crosvm-710060744866cde8cada39caa8461a7194e4869b.tar.bz2 crosvm-710060744866cde8cada39caa8461a7194e4869b.tar.lz crosvm-710060744866cde8cada39caa8461a7194e4869b.tar.xz crosvm-710060744866cde8cada39caa8461a7194e4869b.tar.zst crosvm-710060744866cde8cada39caa8461a7194e4869b.zip |
Drop capabilities before spawning any vcpu thread
In case crosvm starts with elevated capabilities (for example, we need to start with CAP_SETGID to be able to map additional gids into plugin jail), we should drop them before spawning VCPU threads. BUG=b:117989168 TEST=Start plugin via concierge_client and verify the process does not have any effective or permitted privileges. tast run [] 'vm.*' Change-Id: Ia1e80bfe19b296936d77fe9ffeda361211b41eed Reviewed-on: https://chromium-review.googlesource.com/1506296 Commit-Ready: Dmitry Torokhov <dtor@chromium.org> Tested-by: Dmitry Torokhov <dtor@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Chirantan Ekbote <chirantan@chromium.org>
-rw-r--r-- | src/linux.rs | 12 | ||||
-rw-r--r-- | src/plugin/mod.rs | 12 | ||||
-rw-r--r-- | sys_util/src/capabilities.rs | 42 | ||||
-rw-r--r-- | sys_util/src/lib.rs | 2 |
4 files changed, 62 insertions, 6 deletions
diff --git a/src/linux.rs b/src/linux.rs index 58bf673..6398270 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -36,9 +36,10 @@ use rand_ish::SimpleRng; use sync::{Condvar, Mutex}; use sys_util::net::{UnixSeqpacket, UnixSeqpacketListener, UnlinkUnixSeqpacketListener}; use sys_util::{ - self, block_signal, clear_signal, flock, get_blocked_signals, get_group_id, get_user_id, - getegid, geteuid, register_signal_handler, validate_raw_fd, EventFd, FlockOperation, - GuestMemory, Killable, PollContext, PollToken, SignalFd, Terminal, TimerFd, SIGRTMIN, + self, block_signal, clear_signal, drop_capabilities, flock, get_blocked_signals, get_group_id, + get_user_id, getegid, geteuid, register_signal_handler, validate_raw_fd, EventFd, + FlockOperation, GuestMemory, Killable, PollContext, PollToken, SignalFd, Terminal, TimerFd, + SIGRTMIN, }; use vhost; use vm_control::{VmRequest, VmResponse, VmRunMode}; @@ -73,6 +74,7 @@ pub enum Error { DevicePivotRoot(io_jail::Error), Disk(io::Error), DiskImageLock(sys_util::Error), + DropCapabilities(sys_util::Error), InvalidFdPath, InvalidWaylandPath, IoJail(io_jail::Error), @@ -139,6 +141,7 @@ impl Display for Error { DevicePivotRoot(e) => write!(f, "failed to pivot root device: {}", e), Disk(e) => write!(f, "failed to load disk image: {}", e), DiskImageLock(e) => write!(f, "failed to lock disk image: {}", e), + DropCapabilities(e) => write!(f, "failed to drop process capabilities: {}", e), InvalidFdPath => write!(f, "failed parsing a /proc/self/fd/*"), InvalidWaylandPath => write!(f, "wayland socket path has no parent or file name"), IoJail(e) => write!(f, "{}", e), @@ -1204,6 +1207,9 @@ fn run_control( .subsec_nanos() as u64, ); + // Before starting VCPUs, in case we started with some capabilities, drop them all. + drop_capabilities().map_err(Error::DropCapabilities)?; + let mut vcpu_handles = Vec::with_capacity(linux.vcpus.len()); let vcpu_thread_barrier = Arc::new(Barrier::new(linux.vcpus.len() + 1)); let run_mode_arc = Arc::new(VcpuRunMode::default()); diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index 787c73f..b79176e 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -28,9 +28,9 @@ use io_jail::{self, Minijail}; use kvm::{Datamatch, IoeventAddress, Kvm, Vcpu, VcpuExit, Vm}; use net_util::{Error as TapError, Tap, TapT}; use sys_util::{ - block_signal, clear_signal, getegid, geteuid, register_signal_handler, validate_raw_fd, - Error as SysError, EventFd, GuestMemory, Killable, MmapError, PollContext, PollToken, - Result as SysResult, SignalFd, SignalFdError, SIGRTMIN, + block_signal, clear_signal, drop_capabilities, getegid, geteuid, register_signal_handler, + validate_raw_fd, Error as SysError, EventFd, GuestMemory, Killable, MmapError, PollContext, + PollToken, Result as SysResult, SignalFd, SignalFdError, SIGRTMIN, }; use Config; @@ -59,6 +59,7 @@ pub enum Error { CreateVcpuSocket(SysError), CreateVm(SysError), DecodeRequest(ProtobufError), + DropCapabilities(SysError), EncodeResponse(ProtobufError), Mount(io_jail::Error), MountDev(io_jail::Error), @@ -124,6 +125,7 @@ impl Display for Error { CreateVcpuSocket(e) => write!(f, "error creating vcpu request socket: {}", e), CreateVm(e) => write!(f, "error creating vm: {}", e), DecodeRequest(e) => write!(f, "failed to decode plugin request: {}", e), + DropCapabilities(e) => write!(f, "failed to drop process capabilities: {}", e), EncodeResponse(e) => write!(f, "failed to encode plugin response: {}", e), Mount(e) | MountDev(e) | MountLib(e) | MountLib64(e) | MountPlugin(e) | MountPluginLib(e) | MountRoot(e) => write!(f, "failed to mount: {}", e), @@ -544,7 +546,11 @@ pub fn run_config(cfg: Config) -> Result<()> { let mut vm = Vm::new(&kvm, mem).map_err(Error::CreateVm)?; vm.create_irq_chip().map_err(Error::CreateIrqChip)?; vm.create_pit().map_err(Error::CreatePIT)?; + let mut plugin = Process::new(vcpu_count, plugin_path, &plugin_args, jail)?; + // Now that the jail for the plugin has been created and we had a chance to adjust gids there, + // we can drop all our capabilities in case we had any. + drop_capabilities().map_err(Error::DropCapabilities)?; let mut res = Ok(()); // If Some, we will exit after enough time is passed to shutdown cleanly. diff --git a/sys_util/src/capabilities.rs b/sys_util/src/capabilities.rs new file mode 100644 index 0000000..0117ce4 --- /dev/null +++ b/sys_util/src/capabilities.rs @@ -0,0 +1,42 @@ +// Copyright 2019 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +extern crate libc; + +use libc::{c_int, c_void}; +use {errno_result, Result}; + +#[allow(non_camel_case_types)] +type cap_t = *mut c_void; + +#[link(name = "cap")] +extern "C" { + fn cap_init() -> cap_t; + fn cap_free(ptr: *mut c_void) -> c_int; + fn cap_set_proc(cap: cap_t) -> c_int; +} + +/// Drops all capabilities (permitted, inheritable, and effective) from the current process. +pub fn drop_capabilities() -> Result<()> { + unsafe { + // Safe because we do not actually manipulate any memory handled by libcap + // and we check errors. + let caps = cap_init(); + if caps.is_null() { + return errno_result(); + } + + // Freshly initialized capabilities do not have any bits set, so applying them + // will drop all capabilities from the process. + // Safe because we will check the result and otherwise do not touch the memory. + let ret = cap_set_proc(caps); + // We need to free capabilities regardless of success of the operation above. + cap_free(caps); + // Now check if we managed to apply (drop) capabilities. + if ret < 0 { + return errno_result(); + } + } + Ok(()) +} diff --git a/sys_util/src/lib.rs b/sys_util/src/lib.rs index 29aa489..dcc7390 100644 --- a/sys_util/src/lib.rs +++ b/sys_util/src/lib.rs @@ -18,6 +18,7 @@ pub mod handle_eintr; pub mod ioctl; #[macro_use] pub mod syslog; +mod capabilities; mod clock; mod errno; mod eventfd; @@ -43,6 +44,7 @@ mod terminal; mod timerfd; mod write_zeroes; +pub use capabilities::drop_capabilities; pub use clock::{Clock, FakeClock}; use errno::errno_result; pub use errno::{Error, Result}; |