• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2011 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
5import logging
6import os
7import shutil
8
9from autotest_lib.client.bin import test, utils
10from autotest_lib.client.common_lib import error
11
12class security_AccountsBaseline(test.test):
13    version = 1
14
15
16    @staticmethod
17    def match_passwd(expected, actual):
18        """Match login shell (2nd field), uid (3rd field),
19           and gid (4th field)."""
20        if expected[1:4] != actual[1:4]:
21            logging.error(
22                "Expected shell/uid/gid %s for user '%s', got %s.",
23                tuple(expected[1:4]), expected[0], tuple(actual[1:4]))
24            return False
25        return True
26
27
28    @staticmethod
29    def match_group(expected, actual):
30        """Match login shell (2nd field), gid (3rd field),
31           and members (4th field, comma-separated)."""
32        matched = True
33        if expected[1:3] != actual[1:3]:
34            matched = False
35            logging.error(
36                "Expected shell/id %s for group '%s', got %s.",
37                tuple(expected[1:3]), expected[0], tuple(actual[1:3]))
38        if set(expected[3].split(',')) != set(actual[3].split(',')):
39            matched = False
40            logging.error(
41                "Expected members '%s' for group '%s', got '%s'.",
42                expected[3], expected[0], actual[3])
43        return matched
44
45
46    def load_path(self, path):
47        """Load the given passwd/group file."""
48        return [x.strip().split(':') for x in open(path).readlines()]
49
50
51    def capture_files(self):
52        for f in ['passwd','group']:
53            shutil.copyfile(os.path.join('/etc', f),
54                            os.path.join(self.resultsdir, f))
55
56
57    def check_file(self, basename):
58        match_func = getattr(self, 'match_%s' % basename)
59        success = True
60
61        expected_entries = self.load_path(
62            os.path.join(self.bindir, 'baseline.%s' % basename))
63
64        # TODO(spang): Remove this once per-board baselines are supported
65        # (crbug.com/406013).
66        if utils.is_freon():
67            extra_baseline = 'baseline.%s.freon' % basename
68        else:
69            extra_baseline = 'baseline.%s.x11' % basename
70
71        expected_entries += self.load_path(
72            os.path.join(self.bindir, extra_baseline))
73
74        actual_entries = self.load_path('/etc/%s' % basename)
75
76        if len(actual_entries) > len(expected_entries):
77            success = False
78            logging.error(
79                '%s baseline mismatch: expected %d entries, got %d.',
80                basename, len(expected_entries), len(actual_entries))
81
82        for actual in actual_entries:
83            expected = [x for x in expected_entries if x[0] == actual[0]]
84            if not expected:
85                success = False
86                logging.error("Unexpected %s entry for '%s'.",
87                              basename, actual[0])
88                continue
89            expected = expected[0]
90            match_res = match_func(expected, actual)
91            success = success and match_res
92
93        for expected in expected_entries:
94            actual = [x for x in actual_entries if x[0] == expected[0]]
95            if not actual:
96                logging.info("Ignoring missing %s entry for '%s'.",
97                             basename, expected[0])
98
99        return success
100
101
102    def run_once(self):
103        self.capture_files()
104
105        passwd_ok = self.check_file('passwd')
106        group_ok = self.check_file('group')
107
108        # Fail after all mismatches have been reported.
109        if not (passwd_ok and group_ok):
110            raise error.TestFail('Baseline mismatch.')
111