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_file(self, basename): 19 """Load the list of expected files from a given file name. 20 21 @param basename the basename of the file to load. 22 @returns a set containing the names of the files listed in the baseline 23 file. 24 """ 25 path = os.path.join(self.bindir, basename) 26 if os.path.exists(path): 27 with open(path) as basefile: 28 return set(l.strip() for l in basefile if l.strip()[0] != '#') 29 return set() 30 31 32 def _load_baseline(self, bltype): 33 """Load the list of expected files for a given baseline type. 34 35 @param bltype the baseline to load. 36 @returns a set containing the names of the files in the board's 37 baseline. 38 """ 39 # Baseline common to all boards. 40 blname = 'baseline.' + bltype 41 blset = self._load_baseline_file(blname) 42 # Board-specific baseline. 43 board_blname = 'baseline.%s.%s' % (utils.get_current_board(), bltype) 44 blset |= self._load_baseline_file(board_blname) 45 return blset 46 47 48 def run_once(self, baseline='suid'): 49 """ 50 Do a find on the system for setuid binaries, compare against baseline. 51 Fail if setuid binaries are found on the system but not on the baseline. 52 """ 53 exclude = [ '/proc', 54 '/dev', 55 '/sys', 56 '/run', 57 '/usr/local', 58 '/mnt/stateful_partition', 59 ] 60 cmd = 'find / ' 61 for item in exclude: 62 cmd += '-wholename %s -prune -o ' % (item) 63 cmd += '-type f ' 64 65 permmask = {'suid': '4000', 'sgid': '2000'} 66 67 if baseline in permmask: 68 cmd += '-a -perm /%s -print' % (permmask[baseline]) 69 elif baseline == 'fscap': 70 cmd += '-exec getcap {} +' 71 else: 72 raise error.TestFail("Unknown baseline '%s'!" % (baseline)) 73 74 cmd_output = utils.system_output(cmd, ignore_status=True) 75 observed_set = set(cmd_output.splitlines()) 76 baseline_set = self._load_baseline(baseline) 77 78 # Report observed set for debugging. 79 for line in observed_set: 80 logging.debug('%s: %s', baseline, line) 81 82 # Fail if we find new binaries. 83 new = observed_set.difference(baseline_set) 84 if len(new) > 0: 85 message = 'New %s binaries: %s' % (baseline, ', '.join(new)) 86 raise error.TestFail(message) 87 88 # Log but not fail if we find missing binaries. 89 missing = baseline_set.difference(observed_set) 90 if len(missing) > 0: 91 for filepath in missing: 92 logging.error('Missing %s binary: %s', baseline, filepath) 93 else: 94 logging.debug('OK: %s baseline matches system', baseline) 95