cros_vm: Don't use sudo for --disable-kvm.
* Software emulation doesn't need sudo access.
* Switch kvm directory to /tmp from /var/run.
* Add some security tests for kvm directory.
BUG=chromium:655280
TEST=manual
Change-Id: Ibc93933e0892fe6129310d3aa13e656f1cc2784c
Reviewed-on: https://chromium-review.googlesource.com/401384
Commit-Ready: Achuith Bhandarkar <achuith@chromium.org>
Tested-by: Achuith Bhandarkar <achuith@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
diff --git a/scripts/cros_vm.py b/scripts/cros_vm.py
index 8178ea8..2adeaac 100644
--- a/scripts/cros_vm.py
+++ b/scripts/cros_vm.py
@@ -28,7 +28,6 @@
"""Class for managing a VM."""
SSH_PORT = 9222
- VM_DIR = '/var/run/cros_vm'
def __init__(self, image_path=None, qemu_path=None, enable_kvm=True,
@@ -45,12 +44,24 @@
self.qemu_path = qemu_path
self.enable_kvm = enable_kvm
+ # Software emulation doesn't need sudo access.
+ self.use_sudo = enable_kvm
self.image_path = image_path
self.ssh_port = ssh_port
self.dry_run = dry_run
- self.pidfile = os.path.join(self.VM_DIR, 'kvm.pid')
- self.kvm_monitor = os.path.join(self.VM_DIR, 'kvm.monitor')
+ self.vm_dir = os.path.join(osutils.GetGlobalTempDir(), 'cros_vm')
+ if os.path.exists(self.vm_dir):
+ # For security, ensure that vm_dir is not a symlink, and is owned by us or
+ # by root.
+ assert not os.path.islink(self.vm_dir), \
+ 'VM state dir is misconfigured; please recreate: %s' % self.vm_dir
+ st_uid = os.stat(self.vm_dir).st_uid
+ assert st_uid == 0 or st_uid == os.getuid(), \
+ 'VM state dir is misconfigured; please recreate: %s' % self.vm_dir
+
+ self.pidfile = os.path.join(self.vm_dir, 'kvm.pid')
+ self.kvm_monitor = os.path.join(self.vm_dir, 'kvm.monitor')
self.kvm_pipe_in = '%s.in' % self.kvm_monitor # to KVM
self.kvm_pipe_out = '%s.out' % self.kvm_monitor # from KVM
self.kvm_serial = '%s.serial' % self.kvm_monitor
@@ -58,17 +69,24 @@
# TODO(achuith): support nographics, snapshot, mem_path, usb_passthrough,
# moblab, etc.
- @staticmethod
- def _CleanupFiles(recreate):
- """Cleanup VM_DIR.
+
+ def _RunCommand(self, *args, **kwargs):
+ """Use SudoRunCommand or RunCommand as necessary."""
+ if self.use_sudo:
+ return cros_build_lib.SudoRunCommand(*args, **kwargs)
+ else:
+ return cros_build_lib.RunCommand(*args, **kwargs)
+
+ def _CleanupFiles(self, recreate):
+ """Cleanup vm_dir.
Args:
- recreate: recreate VM_DIR.
+ recreate: recreate vm_dir.
"""
- cros_build_lib.SudoRunCommand(['rm', '-rf', VM.VM_DIR])
+ self._RunCommand(['rm', '-rf', self.vm_dir])
if recreate:
- cros_build_lib.SudoRunCommand(['mkdir', VM.VM_DIR])
- cros_build_lib.SudoRunCommand(['chmod', '777', VM.VM_DIR])
+ self._RunCommand(['mkdir', self.vm_dir])
+ self._RunCommand(['chmod', '777', self.vm_dir])
def PerformAction(self, start=False, stop=False, cmd=None):
"""Performs an action, one of start, stop, or run a command in the VM.
@@ -129,7 +147,7 @@
logging.info(' '.join(args))
logging.info('Pid file: %s', self.pidfile)
if not self.dry_run:
- cros_build_lib.SudoRunCommand(args)
+ self._RunCommand(args)
def _GetVMPid(self):
"""Get the pid of the VM.
@@ -137,16 +155,16 @@
Returns:
pid of the VM.
"""
- if not os.path.exists(self.VM_DIR):
- logging.debug('%s not present.', self.VM_DIR)
+ if not os.path.exists(self.vm_dir):
+ logging.debug('%s not present.', self.vm_dir)
return 0
if not os.path.exists(self.pidfile):
logging.info('%s does not exist.', self.pidfile)
return 0
- pid = cros_build_lib.SudoRunCommand(['cat', self.pidfile],
- redirect_stdout=True).output.rstrip()
+ pid = self._RunCommand(['cat', self.pidfile],
+ redirect_stdout=True).output.rstrip()
if not pid.isdigit():
logging.error('%s in %s is not a pid.', pid, self.pidfile)
return 0
@@ -164,8 +182,7 @@
return False
# Make sure the process actually exists.
- res = cros_build_lib.SudoRunCommand(['kill', '-0', str(pid)],
- error_code_ok=True)
+ res = self._RunCommand(['kill', '-0', str(pid)], error_code_ok=True)
return res.returncode == 0
def Stop(self):
@@ -176,8 +193,7 @@
if pid:
logging.info('Killing %d.', pid)
if not self.dry_run:
- cros_build_lib.SudoRunCommand(['kill', '-9', str(pid)],
- error_code_ok=True)
+ self._RunCommand(['kill', '-9', str(pid)], error_code_ok=True)
self._CleanupFiles(recreate=False)
@@ -190,7 +206,7 @@
timeout: maxiumum time to wait before raising an exception.
poll_interval: interval between checks.
"""
- if not os.path.exists(self.VM_DIR):
+ if not os.path.exists(self.vm_dir):
self.Start()
start_time = time.time()