普通文本  |  95行  |  3.25 KB

# Copyright (c) 2010 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 os

from autotest_lib.client.bin import test, utils
from autotest_lib.client.common_lib import error

class security_SuidBinaries(test.test):
    """
    Make sure no surprise binaries become setuid, setgid, or gain filesystem
    capabilities without autotest noticing.
    """
    version = 1

    def _load_baseline_file(self, basename):
        """Load the list of expected files from a given file name.

        @param basename the basename of the file to load.
        @returns a set containing the names of the files listed in the baseline
        file.
        """
        path = os.path.join(self.bindir, basename)
        if os.path.exists(path):
            with open(path) as basefile:
                return set(l.strip() for l in basefile if l.strip()[0] != '#')
        return set()


    def _load_baseline(self, bltype):
        """Load the list of expected files for a given baseline type.

        @param bltype the baseline to load.
        @returns a set containing the names of the files in the board's
        baseline.
        """
        # Baseline common to all boards.
        blname = 'baseline.' + bltype
        blset = self._load_baseline_file(blname)
        # Board-specific baseline.
        board_blname = 'baseline.%s.%s' % (utils.get_current_board(), bltype)
        blset |= self._load_baseline_file(board_blname)
        return blset


    def run_once(self, baseline='suid'):
        """
        Do a find on the system for setuid binaries, compare against baseline.
        Fail if setuid binaries are found on the system but not on the baseline.
        """
        exclude = [ '/proc',
                    '/dev',
                    '/sys',
                    '/run',
                    '/usr/local',
                    '/mnt/stateful_partition',
                  ]
        cmd = 'find / '
        for item in exclude:
            cmd += '-wholename %s -prune -o ' % (item)
        cmd += '-type f '

        permmask = {'suid': '4000', 'sgid': '2000'}

        if baseline in permmask:
            cmd += '-a -perm /%s -print' % (permmask[baseline])
        elif baseline == 'fscap':
            cmd += '-exec getcap {} +'
        else:
            raise error.TestFail("Unknown baseline '%s'!" % (baseline))

        cmd_output = utils.system_output(cmd, ignore_status=True)
        observed_set = set(cmd_output.splitlines())
        baseline_set = self._load_baseline(baseline)

        # Report observed set for debugging.
        for line in observed_set:
            logging.debug('%s: %s', baseline, line)

        # Fail if we find new binaries.
        new = observed_set.difference(baseline_set)
        if len(new) > 0:
            message = 'New %s binaries: %s' % (baseline, ', '.join(new))
            raise error.TestFail(message)

        # Log but not fail if we find missing binaries.
        missing = baseline_set.difference(observed_set)
        if len(missing) > 0:
            for filepath in missing:
                logging.error('Missing %s binary: %s', baseline, filepath)
        else:
            logging.debug('OK: %s baseline matches system', baseline)