Home | History | Annotate | Download | only in security_DbusOwners
      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