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 5import dbus 6import logging 7import os.path 8import pwd 9import socket 10 11from autotest_lib.client.bin import test, utils 12from autotest_lib.client.common_lib import error 13from autotest_lib.client.cros import constants, login 14 15 16class 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 empty_string_array) 120 except dbus.exceptions.DBusException as dbe: 121 logging.info(dbe) 122 else: 123 logging.error('EnableChromeTesting ' 124 'succeeded when it should have failed') 125 return False 126 127 # Make sure Chrome didn't restart. 128 if chrome_pid != self._ps(): 129 logging.error('Chrome restarted during test.') 130 return False 131 132 self._reset_user() 133 return True 134