106 lines
3.9 KiB
Python
106 lines
3.9 KiB
Python
# 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(("chronos", "fuse-exfat", "fuse-sshfs", "nobody",
|
|
"ntfs-3g", "fuse-rar2fs", "fuse-smbfs", "fuse-zip")),
|
|
"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')
|