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