| # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import logging |
| import os |
| |
| from autotest_lib.client.bin import test, utils |
| from autotest_lib.client.common_lib import error |
| import os.path |
| |
| class security_ReservedPrivileges(test.test): |
| version = 1 |
| |
| def reserved_commands(self, command_list): |
| process_list = [] |
| for line in command_list: |
| items = line.split() |
| # We don't care about defunct processes for purposes of this test, |
| # so skip to the next process if we encounter one. |
| if '<defunct>' in items: |
| continue |
| |
| # There are n items in the list. The first is the command, all of |
| # the remaining are either the different users or groups. They |
| # must all match, if they don't we collect it. |
| matches = [i for i,owners in enumerate(items) if owners == items[1]] |
| # We do < because some processes have the same name as their owner. |
| # in that case the number of items will equal the number of matches |
| if (len(matches) < (len(items) - 1)): |
| process_list.append(items[0]) |
| return set(process_list) |
| |
| |
| def load_baseline(self, bltype): |
| # Figure out path to baseline file, by looking up our own path |
| path = os.path.abspath(__file__) |
| path = os.path.join(os.path.dirname(path), 'baseline.%s' % bltype) |
| if (os.path.isfile(path) == False): |
| return set([]) |
| baseline_file = open(path) |
| baseline_data = baseline_file.read() |
| baseline_set = set(baseline_data.splitlines()) |
| baseline_file.close() |
| return baseline_set |
| |
| |
| def run_once(self, owner_type='user'): |
| """ |
| Do a find on the system for commands with reserved privileges and |
| compare against baseline. Fail if these do not match. |
| """ |
| |
| # Find the max column width needed to represent user and group names |
| # in ps outoupt. |
| usermax = utils.system_output("cut -d: -f1 /etc/passwd | wc -L", |
| ignore_status=True) |
| usermax = max(int(usermax), 8) |
| |
| groupmax = utils.system_output("cut -d: -f1 /etc/group | wc -L", |
| ignore_status=True) |
| groupmax = max(int(groupmax), 8) |
| |
| if (owner_type == 'user'): |
| command = ('ps --no-headers -eo '\ |
| 'comm:16,euser:%d,ruser:%d,suser:%d,fuser:%d' % |
| (usermax, usermax, usermax, usermax)) |
| else: |
| command = ('ps --no-headers -eo comm:16,rgroup:%d,group:%d' % |
| (groupmax, groupmax)) |
| |
| command_output = utils.system_output(command, ignore_status=True) |
| output_lines = command_output.splitlines() |
| |
| dump_file = open(os.path.join(self.resultsdir, "ps_output"), 'w') |
| for line in output_lines: |
| dump_file.write(line.strip() + "\n") |
| |
| dump_file.close() |
| |
| observed_set = self.reserved_commands(output_lines) |
| baseline_set = self.load_baseline(owner_type) |
| |
| # If something in the observed set is not |
| # covered by the baseline... |
| diff = observed_set.difference(baseline_set) |
| if len(diff) > 0: |
| for command in diff: |
| logging.error('Unexpected command: %s' % command) |
| |
| # Or, things in baseline are missing from the system: |
| diff2 = baseline_set.difference(observed_set) |
| if len(diff2) > 0: |
| for command in diff2: |
| logging.error('Missing command: %s' % command) |
| |
| if (len(diff) + len(diff2)) > 0: |
| raise error.TestFail('Baseline mismatch') |