# Copyright 2018 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. import logging import subprocess from autotest_lib.client.bin import test, utils from autotest_lib.client.common_lib import error class security_ProcessManagementPolicy(test.test): """ Forks processes as non-root users and ensures the processes can change UID to a user that is explicitly allowed in the system-wide whitelist, but no other user. """ version = 1 _WHITELIST_DICT = { "cros-disks": set(("avfs", "chronos", "fuse-exfat", "fuse-sshfs", "nobody", "ntfs-3g")), "shill": set(("dhcp", "ipsec", "openvpn", "syslog", "nobody")), } def __init__(self, *args, **kwargs): version = utils.get_kernel_version() if version == "3.8.11": raise error.TestNAError('Test is n/a for kernels older than 3.10') super(security_ProcessManagementPolicy, self).__init__(*args, **kwargs) self._failure = False def cleanup(self): """ Clean up the test environment. """ super(security_ProcessManagementPolicy, self).cleanup() def _fail(self, msg): """ Log failure message and record failure. @param msg: String to log. """ logging.error(msg) self._failure = True def _test_setuid(self, parent, child, give_cap_setuid, expect_success): if give_cap_setuid: caps = "0xc0" else: caps = "0x0" try: subprocess.check_output(["/sbin/minijail0", "-u", parent, "-g", parent, "-c", caps, "--", "/sbin/capsh", "--user=" + child, "--", "-c", "/usr/bin/whoami"]) except subprocess.CalledProcessError, e: if expect_success: logging.error(" " + parent + " not able to setuid to " + child) self._failure = True return if not expect_success: logging.error(" " + parent + " able to setuid to " + child) self._failure = True def run_once(self): """ Runs the test, spawning processes as users and checking setuid() behavior. """ for parent in self._WHITELIST_DICT: for child in self._WHITELIST_DICT[parent]: # Expect the setuid() call to be permitted self._test_setuid(parent, child, True, True) # Expect the setuid() call to be denied self._test_setuid(parent, child, False, False) # Make sure 'cros-disks' can't setuid() to 'root' self._test_setuid("cros-disks", "root", True, False) # Make sure 'shill' can't setuid() to 'chronos' self._test_setuid("shill", "chronos", True, False) # Make sure 'openvpn' can't setuid() to 'root' self._test_setuid("openvpn", "root", True, False) # Make sure 'ipsec' can't setuid() to 'root' self._test_setuid("ipsec", "root", True, False) # Make the test fail if any unexpected behaviour got detected. Note # that the error log output that will be included in the failure # message mentions the failed location to aid debugging. if self._failure: raise error.TestFail('Unexpected setuid() behavior')