Home | History | Annotate | Download | only in security_SessionManagerDbusEndpoints
      1 # Copyright 2017 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 dbus
      6 import logging
      7 import os.path
      8 import pwd
      9 import socket
     10 
     11 from autotest_lib.client.bin import test, utils
     12 from autotest_lib.client.common_lib import error
     13 from autotest_lib.client.cros import constants, login
     14 
     15 
     16 class security_SessionManagerDbusEndpoints(test.test):
     17     """Verifies SessionManager DBus endpoints are not exposed.
     18     """
     19     version = 1
     20 
     21     _FLAGFILE = '/tmp/security_SessionManagerDbusEndpoints_regression'
     22 
     23 
     24     def _set_user_environment(self, username):
     25         for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'):
     26             if name in os.environ:
     27                 os.environ[name] = username
     28 
     29 
     30     def _set_user(self, username):
     31         user_info = pwd.getpwnam(username)
     32         os.setegid(user_info[3])
     33         os.seteuid(user_info[2])
     34         self._set_user_environment(username)
     35 
     36 
     37     def _reset_user(self):
     38         uid = os.getuid()
     39         username = pwd.getpwuid(uid)[0]
     40         os.seteuid(uid)
     41         os.setegid(os.getgid())
     42         self._set_user_environment(username)
     43 
     44 
     45     def _ps(self, proc=constants.BROWSER):
     46         """Grab the oldest pid for process |proc|."""
     47         pscmd = 'ps -C %s -o pid --no-header | head -1' % proc
     48         return utils.system_output(pscmd)
     49 
     50 
     51     def run_once(self):
     52         """Main test code."""
     53         login.wait_for_browser()
     54         passed_enable_chrome_testing = self.test_enable_chrome_testing()
     55         passed_restart_job = self.test_restart_job()
     56 
     57         if not passed_enable_chrome_testing or not passed_restart_job:
     58             raise error.TestFail('SessionManager DBus endpoints can be abused, '
     59                                  'see error log')
     60 
     61 
     62     def test_restart_job(self):
     63         """Test SessionManager.RestartJob."""
     64         bus = dbus.SystemBus()
     65         proxy = bus.get_object('org.chromium.SessionManager',
     66                                '/org/chromium/SessionManager')
     67         session_manager = dbus.Interface(proxy,
     68                                          'org.chromium.SessionManagerInterface')
     69 
     70         # Craft a malicious replacement for the target process.
     71         cmd = ['touch', self._FLAGFILE]
     72 
     73         # Try to get our malicious replacement to run via RestartJob.
     74         try:
     75             remote, local = socket.socketpair(socket.AF_UNIX)
     76             logging.info('Calling RestartJob(<socket>, %r)', cmd)
     77             session_manager.RestartJob(dbus.types.UnixFd(remote), cmd)
     78             # Fails if the RestartJob call doesn't generate an error.
     79             logging.error(
     80                 'RestartJob did not fail when passed an arbitrary command')
     81             return False
     82         except dbus.DBusException as e:
     83             logging.info(e.get_dbus_message())
     84             pass
     85         except OSError as e:
     86             raise error.TestError('Could not create sockets for creds: %s', e)
     87         finally:
     88             try:
     89                 local.close()
     90             except OSError:
     91                 pass
     92 
     93         if os.path.exists(self._FLAGFILE):
     94             logging.error('RestartJob ran an arbitrary command')
     95             return False
     96 
     97         return True
     98 
     99 
    100     def test_enable_chrome_testing(self):
    101         """Test SessionManager.EnableChromeTesting."""
    102         self._set_user('chronos')
    103 
    104         bus = dbus.SystemBus()
    105         proxy = bus.get_object('org.chromium.SessionManager',
    106                                '/org/chromium/SessionManager')
    107         session_manager = dbus.Interface(proxy,
    108                                          'org.chromium.SessionManagerInterface')
    109 
    110         chrome_pid = self._ps()
    111 
    112         # Try DBus call and make sure it fails.
    113         try:
    114             # DBus cannot infer the type of an empty Python list.
    115             # Pass an empty dbus.Array with the correct signature, taken from
    116             # platform2/login_manager/dbus_bindings/org.chromium.SessionManagerInterface.xml.
    117             empty_string_array = dbus.Array(signature="as")
    118             path = session_manager.EnableChromeTesting(True, empty_string_array)
    119         except dbus.exceptions.DBusException as dbe:
    120             logging.info(dbe)
    121         else:
    122             logging.error('EnableChromeTesting '
    123                           'succeeded when it should have failed')
    124             return False
    125 
    126         # Make sure Chrome didn't restart.
    127         if chrome_pid != self._ps():
    128             logging.error('Chrome restarted during test.')
    129             return False
    130 
    131         self._reset_user()
    132         return True
    133