1# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging
6import os
7
8from autotest_lib.client.bin import test, utils
9from autotest_lib.client.common_lib import error
10
11class security_SuidBinaries(test.test):
12    """
13    Make sure no surprise binaries become setuid, setgid, or gain filesystem
14    capabilities without autotest noticing.
15    """
16    version = 1
17
18    def load_baseline(self, bltype):
19        """
20        Load the list of expected files for a given baseline type.
21        @param bltype the baseline to load.
22        """
23        baseline_file = open(os.path.join(self.bindir, 'baseline.' + bltype))
24        return set(l.strip() for l in baseline_file)
25
26
27    def run_once(self, baseline='suid'):
28        """
29        Do a find on the system for setuid binaries, compare against baseline.
30        Fail if setuid binaries are found on the system but not on the baseline.
31        """
32        exclude = [ '/proc',
33                    '/dev',
34                    '/sys',
35                    '/run',
36                    '/usr/local',
37                    '/mnt/stateful_partition',
38                  ]
39        cmd = 'find / '
40        for item in exclude:
41            cmd += '-wholename %s -prune -o ' % (item)
42        cmd += '-type f '
43
44        permmask = {'suid': '4000', 'sgid': '2000'}
45
46        if baseline in permmask:
47            cmd += '-a -perm /%s -print' % (permmask[baseline])
48        elif baseline == 'fscap':
49            cmd += '-exec getcap {} +'
50        else:
51            raise error.TestFail("Unknown baseline '%s'!" % (baseline))
52
53        cmd_output = utils.system_output(cmd, ignore_status=True)
54        observed_set = set(cmd_output.splitlines())
55        baseline_set = self.load_baseline(baseline)
56
57        # Report observed set for debugging.
58        for line in observed_set:
59            logging.debug('%s: %s', baseline, line)
60
61        # Fail if we find new binaries.
62        new = observed_set.difference(baseline_set)
63        if len(new) > 0:
64            message = 'New %s binaries: %s' % (baseline, ', '.join(new))
65            raise error.TestFail(message)
66
67        # Log but not fail if we find missing binaries.
68        missing = baseline_set.difference(observed_set)
69        if len(missing) > 0:
70            for filepath in missing:
71                logging.error('Missing %s binary: %s', baseline, filepath)
72        else:
73            logging.debug('OK: %s baseline matches system', baseline)
74