139a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
239a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert# Use of this source code is governed by a BSD-style license that can be
339a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert# found in the LICENSE file.
439a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert
539a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebertimport logging
639a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebertimport os
739a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebertimport re
839a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebertfrom xml.dom import minidom
939a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert
10cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obesfrom autotest_lib.client.bin import test
1139a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebertfrom autotest_lib.client.common_lib import error
1239a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert
1339a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebertclass security_DbusOwners(test.test):
1439a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert    version = 1
1539a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert    _DBUS_CONFIG_DIR = '/etc/dbus-1/system.d/'
1639a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert
1739a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert
1839a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert    def load_baseline(self):
19cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes        """Return a list of interface names to be owned by chronos."""
20cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes        bfile = open(os.path.join(self.bindir, 'baseline'))
2139a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert        baseline_data = bfile.read()
2239a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert        baseline_set = set(baseline_data.splitlines())
2339a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert        bfile.close()
2439a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert        return baseline_set
2539a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert
2639a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert
2739a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert    def fetch_owners(self):
2839a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert        """
29cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes        For every DBus interface XML, look for <policy user="chronos"> sections
30cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes        containing <allow own="InterfaceName">. Return the list of interfaces
31cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes        owned by chronos.
3239a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert        """
3339a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert        chronos_owned = []
3439a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert        for root, dirs, files in os.walk(self._DBUS_CONFIG_DIR):
3539a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert            for filename in files:
3639a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert                # Skip cruft like dotfiles
3739a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert                if not re.search('^[^.].*\.conf$', filename):
38cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes                    logging.debug('Skipping %s', filename)
3939a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert                    continue
4039a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert
41cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes                logging.debug('Parsing %s', filename)
4239a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert                xmldoc = minidom.parse(os.path.join(root,filename))
4339a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert                policies = xmldoc.getElementsByTagName('policy')
4439a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert
4539a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert                for policy in policies:
4639a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert                    if (policy.hasAttribute('user') and
4739a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert                        policy.getAttribute('user') == 'chronos'):
4839a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert                        allows = policy.getElementsByTagName('allow')
4939a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert
5039a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert                        for allow in allows:
5139a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert                            if allow.hasAttribute('own'):
5239a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert                                chronos_owned.append(allow.getAttribute('own'))
5339a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert        return set(chronos_owned)
5439a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert
5539a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert
5639a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert    def run_once(self):
5739a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert        """
58cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes        Enumerate all the DBus interfaces owned by chronos.
59cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes        Fail if it's not included in the expected set.
6039a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert        """
6139a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert        observed_set = self.fetch_owners()
6239a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert        baseline_set = self.load_baseline()
6339a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert
64cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes        # We log but don't fail if we find missing interfaces.
65cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes        missing_ifaces = baseline_set.difference(observed_set)
66cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes        if len(missing_ifaces) > 0:
67cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes            for iface in missing_ifaces:
68cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes                logging.error('Missing chronos-owned interface %s', iface)
6939a055a9f942f1f1a58bdaffd2c11a6442b00496Jim Hebert
70cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes        # We fail if we find new interfaces.
71cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes        new_ifaces = observed_set.difference(baseline_set)
72cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes        if len(new_ifaces) > 0:
73cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes            message = 'New chronos-owned interface(s): ' + ', '.join(new_ifaces)
74cf90b9af3ef6d3e851fd4ea355bc7aec4135f9adJorge Lucangeli Obes            raise error.TestFail(message)
75