Home | History | Annotate | Download | only in security_SuidBinaries
      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 
      5 import logging
      6 import os
      7 
      8 from autotest_lib.client.bin import test, utils
      9 from autotest_lib.client.common_lib import error
     10 
     11 class 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