普通文本  |  108行  |  3.96 KB

# 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", "fuse-drivefs", "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 'cros-disks' can't setuid() to 'chronos'
        self._test_setuid("cros-disks", "chronos", 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')