Home | History | Annotate | Download | only in security_ReservedPrivileges
      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 import os.path
     11 
     12 class security_ReservedPrivileges(test.test):
     13     version = 1
     14 
     15     def reserved_commands(self, command_list):
     16         process_list = []
     17         for line in command_list:
     18             items = line.split()
     19             # We don't care about defunct processes for purposes of this test,
     20             # so skip to the next process if we encounter one.
     21             if '<defunct>' in items:
     22                 continue
     23 
     24             # There are n items in the list.  The first is the command, all of
     25             # the remaining are either the different users or groups.  They
     26             # must all match, if they don't we collect it.
     27             matches = [i for i,owners in enumerate(items) if owners == items[1]]
     28             # We do < because some processes have the same name as their owner.
     29             # in that case the number of items will equal the number of matches
     30             if (len(matches) < (len(items) - 1)):
     31                 process_list.append(items[0])
     32         return set(process_list)
     33 
     34 
     35     def load_baseline(self, bltype):
     36         # Figure out path to baseline file, by looking up our own path
     37         path = os.path.abspath(__file__)
     38         path = os.path.join(os.path.dirname(path), 'baseline.%s' % bltype)
     39         if (os.path.isfile(path) == False):
     40             return set([])
     41         baseline_file = open(path)
     42         baseline_data = baseline_file.read()
     43         baseline_set = set(baseline_data.splitlines())
     44         baseline_file.close()
     45         return baseline_set
     46 
     47 
     48     def run_once(self, owner_type='user'):
     49         """
     50         Do a find on the system for commands with reserved privileges and
     51         compare against baseline.  Fail if these do not match.
     52         """
     53 
     54         # Find the max column width needed to represent user and group names
     55         # in ps outoupt.
     56         usermax = utils.system_output("cut -d: -f1 /etc/passwd | wc -L",
     57                                       ignore_status=True)
     58         usermax = max(int(usermax), 8)
     59 
     60         groupmax = utils.system_output("cut -d: -f1 /etc/group | wc -L",
     61                                        ignore_status=True)
     62         groupmax = max(int(groupmax), 8)
     63 
     64         if (owner_type == 'user'):
     65             command = ('ps --no-headers -eo '\
     66                        'comm:16,euser:%d,ruser:%d,suser:%d,fuser:%d' %
     67                        (usermax, usermax, usermax, usermax))
     68         else:
     69             command = ('ps --no-headers -eo comm:16,rgroup:%d,group:%d' %
     70                        (groupmax, groupmax))
     71 
     72         command_output = utils.system_output(command, ignore_status=True)
     73         output_lines = command_output.splitlines()
     74 
     75         dump_file = open(os.path.join(self.resultsdir, "ps_output"), 'w')
     76         for line in output_lines:
     77             dump_file.write(line.strip() + "\n")
     78 
     79         dump_file.close()
     80 
     81         observed_set = self.reserved_commands(output_lines)
     82         baseline_set = self.load_baseline(owner_type)
     83 
     84         # If something in the observed set is not
     85         # covered by the baseline...
     86         diff = observed_set.difference(baseline_set)
     87         if len(diff) > 0:
     88             for command in diff:
     89                 logging.error('Unexpected command: %s' % command)
     90 
     91         # Or, things in baseline are missing from the system:
     92         diff2 = baseline_set.difference(observed_set)
     93         if len(diff2) > 0:
     94             for command in diff2:
     95                 logging.error('Missing command: %s' % command)
     96 
     97         if (len(diff) + len(diff2)) > 0:
     98             raise error.TestFail('Baseline mismatch')
     99