| # Copyright (c) 2011 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 |
| import shutil |
| |
| from autotest_lib.client.bin import test, utils |
| from autotest_lib.client.common_lib import error |
| |
| class security_AccountsBaseline(test.test): |
| version = 1 |
| |
| |
| @staticmethod |
| def match_passwd(expected, actual): |
| """Match login shell (2nd field), uid (3rd field), |
| and gid (4th field).""" |
| if expected[1:4] != actual[1:4]: |
| logging.error( |
| "Expected shell/uid/gid %s for user '%s', got %s.", |
| tuple(expected[1:4]), expected[0], tuple(actual[1:4])) |
| return False |
| return True |
| |
| |
| @staticmethod |
| def match_group(expected, actual): |
| """Match login shell (2nd field), gid (3rd field), |
| and members (4th field, comma-separated).""" |
| matched = True |
| if expected[1:3] != actual[1:3]: |
| matched = False |
| logging.error( |
| "Expected shell/id %s for group '%s', got %s.", |
| tuple(expected[1:3]), expected[0], tuple(actual[1:3])) |
| if set(expected[3].split(',')) != set(actual[3].split(',')): |
| matched = False |
| logging.error( |
| "Expected members '%s' for group '%s', got '%s'.", |
| expected[3], expected[0], actual[3]) |
| return matched |
| |
| |
| def load_path(self, path): |
| """Load the given passwd/group file.""" |
| return [x.strip().split(':') for x in open(path).readlines()] |
| |
| |
| def capture_files(self): |
| for f in ['passwd','group']: |
| shutil.copyfile(os.path.join('/etc', f), |
| os.path.join(self.resultsdir, f)) |
| |
| |
| def check_file(self, basename): |
| match_func = getattr(self, 'match_%s' % basename) |
| success = True |
| |
| expected_entries = self.load_path( |
| os.path.join(self.bindir, 'baseline.%s' % basename)) |
| |
| # TODO(spang): Remove this once per-board baselines are supported |
| # (crbug.com/406013). |
| if utils.is_freon(): |
| extra_baseline = 'baseline.%s.freon' % basename |
| else: |
| extra_baseline = 'baseline.%s.x11' % basename |
| |
| expected_entries += self.load_path( |
| os.path.join(self.bindir, extra_baseline)) |
| |
| actual_entries = self.load_path('/etc/%s' % basename) |
| |
| if len(actual_entries) > len(expected_entries): |
| success = False |
| logging.error( |
| '%s baseline mismatch: expected %d entries, got %d.', |
| basename, len(expected_entries), len(actual_entries)) |
| |
| for actual in actual_entries: |
| expected = [x for x in expected_entries if x[0] == actual[0]] |
| if not expected: |
| success = False |
| logging.error("Unexpected %s entry for '%s'.", |
| basename, actual[0]) |
| continue |
| expected = expected[0] |
| match_res = match_func(expected, actual) |
| success = success and match_res |
| |
| for expected in expected_entries: |
| actual = [x for x in actual_entries if x[0] == expected[0]] |
| if not actual: |
| logging.info("Ignoring missing %s entry for '%s'.", |
| basename, expected[0]) |
| |
| return success |
| |
| |
| def run_once(self): |
| self.capture_files() |
| |
| passwd_ok = self.check_file('passwd') |
| group_ok = self.check_file('group') |
| |
| # Fail after all mismatches have been reported. |
| if not (passwd_ok and group_ok): |
| raise error.TestFail('Baseline mismatch.') |