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