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
5import logging
6import os
7import re
8from xml.dom import minidom
9
10from autotest_lib.client.bin import test
11from autotest_lib.client.common_lib import error
12
13class security_DbusOwners(test.test):
14    version = 1
15    _DBUS_CONFIG_DIR = '/etc/dbus-1/system.d/'
16
17
18    def load_baseline(self):
19        """Return a list of interface names to be owned by chronos."""
20        bfile = open(os.path.join(self.bindir, 'baseline'))
21        baseline_data = bfile.read()
22        baseline_set = set(baseline_data.splitlines())
23        bfile.close()
24        return baseline_set
25
26
27    def fetch_owners(self):
28        """
29        For every DBus interface XML, look for <policy user="chronos"> sections
30        containing <allow own="InterfaceName">. Return the list of interfaces
31        owned by chronos.
32        """
33        chronos_owned = []
34        for root, dirs, files in os.walk(self._DBUS_CONFIG_DIR):
35            for filename in files:
36                # Skip cruft like dotfiles
37                if not re.search('^[^.].*\.conf$', filename):
38                    logging.debug('Skipping %s', filename)
39                    continue
40
41                logging.debug('Parsing %s', filename)
42                xmldoc = minidom.parse(os.path.join(root,filename))
43                policies = xmldoc.getElementsByTagName('policy')
44
45                for policy in policies:
46                    if (policy.hasAttribute('user') and
47                        policy.getAttribute('user') == 'chronos'):
48                        allows = policy.getElementsByTagName('allow')
49
50                        for allow in allows:
51                            if allow.hasAttribute('own'):
52                                chronos_owned.append(allow.getAttribute('own'))
53        return set(chronos_owned)
54
55
56    def run_once(self):
57        """
58        Enumerate all the DBus interfaces owned by chronos.
59        Fail if it's not included in the expected set.
60        """
61        observed_set = self.fetch_owners()
62        baseline_set = self.load_baseline()
63
64        # We log but don't fail if we find missing interfaces.
65        missing_ifaces = baseline_set.difference(observed_set)
66        if len(missing_ifaces) > 0:
67            for iface in missing_ifaces:
68                logging.error('Missing chronos-owned interface %s', iface)
69
70        # We fail if we find new interfaces.
71        new_ifaces = observed_set.difference(baseline_set)
72        if len(new_ifaces) > 0:
73            message = 'New chronos-owned interface(s): ' + ', '.join(new_ifaces)
74            raise error.TestFail(message)
75