def run_once(self):
        """
        Checks a number of sensitive processes on Chrome OS for
        unexpected open file descriptors.
        """
        self.snapshot_system()

        passes = []
        filters = [r'0700 anon_inode:\[event.*\]',
                   r'0[35]00 pipe:.*',
                   r'0[57]00 socket:.*',
                   r'0500 /dev/null',
                   r'0[57]00 /dev/urandom',
                   r'0300 /var/log/chrome/chrome_.*',
                   r'0[37]00 /var/log/ui/ui.*',
                  ]

        # Whitelist fd-type check, suitable for Chrome processes.
        # Notably, this omits S_ISDIR.
        allowed_fd_type_check = lambda x: (stat.S_ISREG(x) or
                                           stat.S_ISCHR(x) or
                                           stat.S_ISSOCK(x) or
                                           stat.S_ISFIFO(x) or
                                           security_OpenFDs._S_ISANONFD(x))

        # TODO(jorgelo): revisit this and potentially remove.
        if asan.running_on_asan():
            # On ASan, allow all fd types and opening /proc
            logging.info("Running on ASan, allowing /proc")
            allowed_fd_type_check = lambda x: True
            filters.append(r'0500 /proc')

        passes.append(self.check_process('chrome', 'type=plugin', filters,
                                         allowed_fd_type_check))

        filters.extend([r'0[57]00 /dev/shm/..*',
                        r'0500 /opt/google/chrome/.*.pak',
                        r'0500 /opt/google/chrome/icudtl.dat',
                        # These used to be bundled with the Chrome binary.
                        # See crbug.com/475170.
                        r'0500 /opt/google/chrome/natives_blob.bin',
                        r'0500 /opt/google/chrome/snapshot_blob.bin',
                        # Font files can be kept open in renderers
                        # for performance reasons.
                        # See crbug.com/452227.
                        r'0500 /usr/share/fonts/.*',
                       ])
        try:
            # Renderers have access to DRM vgem device for graphics tile upload.
            # See crbug.com/537474.
            filters.append(r'0700 /dev/dri/%s' % os.readlink('/dev/dri/vgem'))
        except OSError:
            # /dev/dri/vgem doesn't exist.
            pass

        passes.append(self.check_process('chrome', 'type=renderer', filters,
                                         allowed_fd_type_check))

        if False in passes:
            raise error.TestFail("Unexpected open file descriptors.")
class login_LoginSuccess(test.test):
    """Sign in using Telemetry and validate system state."""
    version = 1

    _SESSION_START_TIMEOUT = 10
    _SESSION_STOP_TIMEOUT = 60
    # TODO(afakhry): Remove this timeout increase for asan bots once we figure
    # out why logging out is taking so long. See crbug.com/488291
    if asan.running_on_asan():
      _SESSION_STOP_TIMEOUT *= 2


    def initialize(self):
        super(login_LoginSuccess, self).initialize()

        bus_loop = DBusGMainLoop(set_as_default=True)
        self._session_manager = session_manager.connect(bus_loop)
        self._listener = session_manager.SessionSignalListener(
                gobject.MainLoop())


    def run_once(self, stress_run=False, arc_mode=None):
        """
        Runs the test.

        @param stress_run: True if we are doing a stress run and want to
                           double the timeout.
        @param arc_mode: This value is passed to Chrome and determines how
                         the ARC/Android instance should start. Possible values
                         are defined in common_lib/cros/arc_common.py.

        """
        if stress_run:
            self._SESSION_STOP_TIMEOUT *= 2
        self._listener.listen_for_session_state_change('started')
        with chrome.Chrome(arc_mode=arc_mode):
            self._listener.wait_for_signals(desc='Session started.',
                                            timeout=self._SESSION_START_TIMEOUT)
            # To enable use as a 'helper test'.
            self.job.set_state('client_success', True)

            # Start listening to stop signal before logging out.
            self._listener.listen_for_session_state_change('stopped')

        self._listener.wait_for_signals(desc='Session stopped.',
                                        timeout=self._SESSION_STOP_TIMEOUT)
class login_LoginSuccess(test.test):
    """Sign in using Telemetry and validate system state."""
    version = 1

    _SESSION_START_TIMEOUT = 10
    _SESSION_STOP_TIMEOUT = 60
    # TODO(afakhry): Remove this timeout increase for asan bots once we figure
    # out why logging out is taking so long. See crbug.com/488291
    if asan.running_on_asan():
      _SESSION_STOP_TIMEOUT *= 2


    def initialize(self):
        super(login_LoginSuccess, self).initialize()

        bus_loop = DBusGMainLoop(set_as_default=True)
        self._session_manager = session_manager.connect(bus_loop)
        self._listener = session_manager.SessionSignalListener(
                gobject.MainLoop())


    def run_once(self, stress_run=False):
        # For stress runs, we are extending timeout to find other problems
        if stress_run:
            self._SESSION_STOP_TIMEOUT *= 2
        self._listener.listen_for_session_state_change('started')
        with chrome.Chrome():
            self._listener.wait_for_signals(desc='Session started.',
                                            timeout=self._SESSION_START_TIMEOUT)
            # To enable use as a 'helper test'.
            self.job.set_state('client_success', True)

            # Start listening to stop signal before logging out.
            self._listener.listen_for_session_state_change('stopped')

        self._listener.wait_for_signals(desc='Session stopped.',
                                        timeout=self._SESSION_STOP_TIMEOUT)
示例#4
0
class desktopui_ScreenLocker(test.test):
    """This is a client side test that exercises the screenlocker."""
    version = 1

    _SCREEN_IS_LOCKED_TIMEOUT = 30
    # TODO(jdufault): Remove this timeout increase for asan bots once we figure
    # out what's taking so long to lock the screen. See crbug.com/452599.
    if asan.running_on_asan():
        _SCREEN_IS_LOCKED_TIMEOUT *= 2
    """Timeout for password authentication."""
    _AUTHENTICATION_TIMEOUT = 30

    def initialize(self):
        """Init method"""
        super(desktopui_ScreenLocker, self).initialize()
        DBusGMainLoop(set_as_default=True)
        self.player = input_playback.InputPlayback()
        self.player.emulate(input_type='keyboard')
        self.player.find_connected_inputs()

    def cleanup(self):
        """Test cleanup."""
        self.player.close()

    @property
    def screen_locked(self):
        """True if the screen is locked."""
        return self._chrome.login_status['isScreenLocked']

    @property
    def screen_ready_for_password(self):
        """True if the screen is ready for password."""
        return self._chrome.login_status['isReadyForPassword']

    def lock_screen(self, perf_values):
        """Lock the screen.

        @param perf_values: Performance data will be stored inside of this dict.

        @raises: error.TestFail when screen already locked.
        @raises: error.TestFail when screen not locked.

        """
        logging.debug('lock_screen')
        if self.screen_locked:
            raise error.TestFail('Screen already locked')
        signal_listener = session_manager.ScreenIsLockedSignalListener(
            gobject.MainLoop())
        ext = self._chrome.autotest_ext

        start = datetime.now()
        ext.EvaluateJavaScript('chrome.autotestPrivate.lockScreen();')
        signal_listener.wait_for_signals(
            desc='Screen is locked.', timeout=self._SCREEN_IS_LOCKED_TIMEOUT)
        perf_values['lock_seconds'] = (datetime.now() - start).total_seconds()

        utils.poll_for_condition(lambda: self.screen_locked,
                                 exception=error.TestFail('Screen not locked'))

    def lock_screen_through_keyboard(self):
        """Lock the screen with keyboard(search+L) .

         @raises: error.TestFail when screen already locked.
         @raises: error.TestFail if screen not locked after using keyboard
                  shortcut.

         """
        logging.debug('Locking screen through the keyboard shortcut')
        if self.screen_locked:
            raise error.TestFail('Screen already locked')
        self.player.blocking_playback_of_default_file(
            input_type='keyboard', filename='keyboard_search+L')
        utils.poll_for_condition(
            lambda: self.screen_locked,
            exception=error.TestFail(
                'Screen not locked after using keyboard shortcut'))

    def attempt_unlock_bad_password(self):
        """Attempt unlock with a bad password.

         @raises: error.TestFail when successfully unlock with bad password.

         """
        logging.debug('attempt_unlock_bad_password')
        self.player.blocking_playback_of_default_file(
            input_type='keyboard', filename='keyboard_b+a+d+enter')

        # Wait for the authentication to complete.
        utils.poll_for_condition(
            lambda: self.screen_ready_for_password,
            exception=error.TestFail(
                'Authentication is not completed after %d seconds',
                self._AUTHENTICATION_TIMEOUT),
            timeout=self._AUTHENTICATION_TIMEOUT)
        if not self.screen_locked:
            raise error.TestFail('Screen unlocked with bad password')

    def unlock_screen(self):
        """Unlock the screen with the right password. The correct password is
           the empty string.
           TODO(crbug.com/792251): Use non-empty password.

         @raises: error.TestFail if failed to unlock screen.

        """
        logging.debug('unlock_screen')
        self.player.blocking_playback_of_default_file(
            input_type='keyboard', filename='keyboard_enter')
        utils.poll_for_condition(
            lambda: not self.screen_locked,
            exception=error.TestFail('Failed to unlock screen'),
            timeout=self._AUTHENTICATION_TIMEOUT)

    def run_once(self):
        """
        This test locks the screen, tries to unlock with a bad password,
        then unlocks with the right password.

        """
        with chrome.Chrome(autotest_ext=True) as self._chrome:
            try:
                # Give performance data some initial state that will be reported
                # if the test times out.
                perf_values = {'lock_seconds': self._SCREEN_IS_LOCKED_TIMEOUT}

                self.lock_screen(perf_values)
                self.attempt_unlock_bad_password()
                self.unlock_screen()
                self.lock_screen_through_keyboard()
                self.unlock_screen()
            finally:
                self.output_perf_value(description='time_to_lock_screen',
                                       value=perf_values['lock_seconds'],
                                       units='s',
                                       higher_is_better=False)
    def run_once(self):
        """Inspects the process list, looking for root and sandboxed processes
        (with some exclusions). If we have a baseline entry for a given process,
        confirms it's an exact match. Warns if we see root or sandboxed
        processes that we have no baseline for, and warns if we have
        baselines for processes not seen running.
        """

        fieldnames, baseline = self.load_baseline()
        exclusions = self.load_exclusions()
        running_processes = self.get_running_processes()
        is_asan = asan.running_on_asan()
        if is_asan:
            logging.info('ASAN image detected -> skipping seccomp checks')

        kthreadd_pid = -1

        init_process = None
        running_services = {}

        # Filter running processes list.
        for process in running_processes:
            exe = process.comm

            if exe == "kthreadd":
                kthreadd_pid = process.pid
                continue
            elif process.pid == "1":
                init_process = process
                continue

            # Don't worry about kernel threads.
            if process.ppid == kthreadd_pid:
                continue

            if exe in exclusions:
                continue

            running_services[exe] = process

        if not init_process:
            raise error.TestFail("Cannot find init process")

        # Find differences between running services and baseline.
        services_set = set(running_services.keys())
        baseline_set = set(baseline.keys())

        new_services = services_set.difference(baseline_set)
        stale_baselines = defaultdict(list)

        for exe in baseline_set.difference(services_set):
            stale_baselines[exe].append('unused')

        # Check baseline.
        sandbox_delta = defaultdict(list)
        for exe in services_set.intersection(baseline_set):
            process = running_services[exe]
            stale_flags = []
            errors = []

            # If the process is not running as the correct user.
            if process.euser != baseline[exe]["euser"]:
                errors.append('bad user: wanted "%s" but got "%s"' %
                              (baseline[exe]['euser'], process.euser))

            # If the process is not running as the correct group.
            if process.egroup != baseline[exe]['egroup']:
                errors.append('bad group: wanted "%s" but got "%s"' %
                              (baseline[exe]['egroup'], process.egroup))

            # Check the various sandbox settings.
            if process.pidns == init_process.pidns:
                if baseline[exe]['pidns'] == 'Yes':
                    errors.append('missing pid ns usage')
            elif baseline[exe]['pidns'] != 'Yes':
                stale_flags.append('pidns')

            if process.mntns == init_process.mntns:
                if baseline[exe]['mntns'] == 'Yes':
                    errors.append('missing mount ns usage')
            elif has_test_image_mounts(process.mountinfo):
                if baseline[exe]['mntns'] == 'Yes':
                    errors.append('did not call pivot_root(2)')
            elif baseline[exe]['mntns'] != 'Yes':
                stale_flags.append('mntns')

            if process.capeff == init_process.capeff:
                if baseline[exe]['caps'] == 'Yes':
                    errors.append('missing caps usage')
            elif baseline[exe]['caps'] != 'Yes':
                stale_flags.append('caps')

            if process.nonewprivs != '1':
                if baseline[exe]['nonewprivs'] == 'Yes':
                    errors.append('missing NoNewPrivs')
            elif baseline[exe]['nonewprivs'] != 'Yes':
                stale_flags.append('nonewprivs')

            if not is_asan:
                # Since Minijail disables seccomp at runtime when ASAN is
                # active, we can't enforce it on ASAN bots.  Just ignore
                # the test entirely.  (Comment applies to "is_asan" above.)
                if process.seccomp != SECCOMP_MODE_FILTER:
                    if baseline[exe]['filter'] == 'Yes':
                        errors.append(
                            'missing seccomp usage: '
                            'wanted %s (%s) but got %s (%s)' %
                            (SECCOMP_MODE_FILTER,
                             SECCOMP_MAP[SECCOMP_MODE_FILTER], process.seccomp,
                             SECCOMP_MAP.get(process.seccomp, '???')))
                elif baseline[exe]['filter'] != 'Yes':
                    stale_flags.append('filter')

            if stale_flags:
                stale_baselines[exe].append('potentially missing flags: %s' %
                                            ','.join(stale_flags))
            if errors:
                sandbox_delta[exe].extend(errors)

        # Save current run to results dir.
        running_services_properties = [get_properties(s, init_process)
                                       for s in running_services.values()]
        self.dump_services(fieldnames, running_services_properties)

        if len(stale_baselines) > 0:
            logging.warn('Stale baselines: %r', stale_baselines)

        if len(new_services) > 0:
            logging.warn('New services: %r', new_services)

            # We won't complain about new non-root services (on the assumption
            # that they've already somewhat sandboxed things), but we'll fail
            # with new root services (on the assumption they haven't done any
            # sandboxing work).  If they really need to run as root, they can
            # update the baseline to whitelist it.
            for exe in new_services:
                if running_services[exe].euser == 'root':
                    sandbox_delta[exe].append('missing euser')

        if len(sandbox_delta) > 0:
            for delta_entry in sandbox_delta:
                logging.error('Failed sandboxing: %s', delta_entry)
            raise error.TestFail('One or more processes failed sandboxing: %r' %
                                 sandbox_delta)
class desktopui_ScreenLocker(test.test):
    """This is a client side test that exercises the screenlocker."""
    version = 1

    _SCREEN_IS_LOCKED_TIMEOUT = 30
    # TODO(jdufault): Remove this timeout increase for asan bots once we figure
    # out what's taking so long to lock the screen. See crbug.com/452599.
    if asan.running_on_asan():
      _SCREEN_IS_LOCKED_TIMEOUT *= 2


    def initialize(self):
        super(desktopui_ScreenLocker, self).initialize()
        DBusGMainLoop(set_as_default=True)


    @property
    def screen_locked(self):
        """True if the screen is locked."""
        return self._chrome.login_status['isScreenLocked']


    @property
    def screenlocker_visible(self):
        """True if the screenlocker screen is visible."""
        oobe = self._chrome.browser.oobe
        return (oobe and
                oobe.EvaluateJavaScript(
                    "(typeof Oobe == 'function') && "
                    "(typeof Oobe.authenticateForTesting == 'function') && "
                    "($('account-picker') != null)"))

    @property
    def error_bubble_visible(self):
        """True if the error bubble for bad password is visible."""
        return self._chrome.browser.oobe.EvaluateJavaScript(
                "cr.ui.login.DisplayManager.errorMessageWasShownForTesting_;")


    def attempt_unlock(self, password=''):
        """Attempt to unlock a locked screen. The correct password is the empty
           string.

        @param password: password to use to attempt the unlock.

        """
        self._chrome.browser.oobe.ExecuteJavaScript(
                "Oobe.authenticateForTesting('%s', '%s');"
                % (self._chrome.username, password))


    def lock_screen(self):
        """Lock the screen."""
        logging.debug('lock_screen')
        if self.screen_locked:
            raise error.TestFail('Screen already locked')
        signal_listener = session_manager.ScreenIsLockedSignalListener(
                gobject.MainLoop())
        ext = self._chrome.autotest_ext
        ext.EvaluateJavaScript('chrome.autotestPrivate.lockScreen();')
        signal_listener.wait_for_signals(desc='Screen is locked.',
                                         timeout=self._SCREEN_IS_LOCKED_TIMEOUT)
        utils.poll_for_condition(lambda: self.screenlocker_visible,
                exception=error.TestFail('Screenlock screen not visible'))
        if not self.screen_locked:
            raise error.TestFail('Screen not locked')


    def attempt_unlock_bad_password(self):
        """Attempt unlock with a bad password."""
        logging.debug('attempt_unlock_bad_password')
        if self.error_bubble_visible:
            raise error.TestFail('Error bubble prematurely visible')
        self.attempt_unlock('bad')
        utils.poll_for_condition(lambda: self.error_bubble_visible,
                exception=error.TestFail('Bad password bubble did not show'))
        if not self.screen_locked:
            raise error.TestFail('Screen unlocked with bad password')


    def unlock_screen(self):
        """Unlock the screen with the right password."""
        logging.debug('unlock_screen')
        self.attempt_unlock()
        utils.poll_for_condition(
                lambda: not self._chrome.browser.oobe_exists,
                exception=error.TestFail('Failed to unlock screen'))
        if self.screen_locked:
            raise error.TestFail('Screen should be unlocked')


    def run_once(self):
        """
        This test locks the screen, tries to unlock with a bad password,
        then unlocks with the right password.

        """
        with chrome.Chrome(autotest_ext=True) as self._chrome:
            self.lock_screen()
            self.attempt_unlock_bad_password()
            self.unlock_screen()
class desktopui_ScreenLocker(test.test):
    """This is a client side test that exercises the screenlocker."""
    version = 1

    _SCREEN_IS_LOCKED_TIMEOUT = 30
    # TODO(jdufault): Remove this timeout increase for asan bots once we figure
    # out what's taking so long to lock the screen. See crbug.com/452599.
    if asan.running_on_asan():
        _SCREEN_IS_LOCKED_TIMEOUT *= 2

    def initialize(self):
        super(desktopui_ScreenLocker, self).initialize()
        DBusGMainLoop(set_as_default=True)
        self.player = input_playback.InputPlayback()
        self.player.emulate(input_type='keyboard')
        self.player.find_connected_inputs()

    def cleanup(self):
        """Test cleanup."""
        self.player.close()

    @property
    def screen_locked(self):
        """True if the screen is locked."""
        return self._chrome.login_status['isScreenLocked']

    @property
    def screenlocker_visible(self):
        """True if the screenlocker screen is visible."""
        oobe = self._chrome.browser.oobe
        return (oobe and oobe.EvaluateJavaScript(
            "(typeof Oobe == 'function') && "
            "(typeof Oobe.authenticateForTesting == 'function') && "
            "($('account-picker') != null)"))

    @property
    def error_bubble_visible(self):
        """True if the error bubble for bad password is visible."""
        return self._chrome.browser.oobe.EvaluateJavaScript(
            "cr.ui.login.DisplayManager.errorMessageWasShownForTesting_;")

    def attempt_unlock(self, password=''):
        """Attempt to unlock a locked screen. The correct password is the empty
           string.

        @param password: password to use to attempt the unlock.

        """
        self._chrome.browser.oobe.ExecuteJavaScript(
            "Oobe.authenticateForTesting('%s', '%s');" %
            (self._chrome.username, password))

    def lock_screen(self, perf_values):
        """Lock the screen.

        @param perf_values: Performance data will be stored inside of this dict.

        @raises: error.TestFail when screen already locked.
        @raises: error.TestFail if lockscreen screen not visible.
        @raises: error.TestFail when screen not locked.

        """
        logging.debug('lock_screen')
        if self.screen_locked:
            raise error.TestFail('Screen already locked')
        signal_listener = session_manager.ScreenIsLockedSignalListener(
            gobject.MainLoop())
        ext = self._chrome.autotest_ext

        start = datetime.now()
        ext.EvaluateJavaScript('chrome.autotestPrivate.lockScreen();')
        signal_listener.wait_for_signals(
            desc='Screen is locked.', timeout=self._SCREEN_IS_LOCKED_TIMEOUT)
        perf_values['lock_seconds'] = (datetime.now() - start).total_seconds()

        utils.poll_for_condition(
            lambda: self.screenlocker_visible,
            exception=error.TestFail('Screenlock screen not visible'))
        if not self.screen_locked:
            raise error.TestFail('Screen not locked')

    def lock_screen_through_keyboard(self):
        """Lock the screen with keyboard(search+L) .

         @raises: error.TestFail when screen already locked.
         @raises: error.TestFail if lockscreen screen not visible.
         @raises: error.TestFail when screen not locked after using keyboard shortcut.

         """
        logging.debug('Locking screen through the keyboard shortcut')
        if self.screen_locked:
            raise error.TestFail('Screen already locked')
        self.player.blocking_playback_of_default_file(
            input_type='keyboard', filename='keyboard_search+L')
        utils.poll_for_condition(
            lambda: self.screenlocker_visible,
            exception=error.TestFail('Screenlock screen not visible'))
        if not self.screen_locked:
            raise error.TestFail(
                'Screen not locked after using keyboard shortcut')

    def attempt_unlock_bad_password(self):
        """Attempt unlock with a bad password.

         @raises: error.TestFail when try to unlock with bad password.
         @raises: error.TestFail if bubble prematurely visible.
         @raises: error.TestFail when Bad password bubble did not show.

         """
        logging.debug('attempt_unlock_bad_password')
        if self.error_bubble_visible:
            raise error.TestFail('Error bubble prematurely visible')
        self.attempt_unlock('bad')
        utils.poll_for_condition(
            lambda: self.error_bubble_visible,
            exception=error.TestFail('Bad password bubble did not show'))
        if not self.screen_locked:
            raise error.TestFail('Screen unlocked with bad password')

    def unlock_screen(self):
        """Unlock the screen with the right password.

         @raises: error.TestFail if failed to unlock screen.
         @raises: error.TestFail if screen unlocked.

        """
        logging.debug('unlock_screen')
        self.attempt_unlock()
        utils.poll_for_condition(
            lambda: not self._chrome.browser.oobe_exists,
            exception=error.TestFail('Failed to unlock screen'))
        if self.screen_locked:
            raise error.TestFail('Screen should be unlocked')

    def run_once(self):
        """
        This test locks the screen, tries to unlock with a bad password,
        then unlocks with the right password.

        """
        with chrome.Chrome(autotest_ext=True) as self._chrome:
            perf_values = {}
            self.lock_screen(perf_values)
            self.attempt_unlock_bad_password()
            self.unlock_screen()
            self.lock_screen_through_keyboard()
            self.unlock_screen()
            self.output_perf_value(description='time_to_lock_screen',
                                   value=perf_values['lock_seconds'],
                                   units='s',
                                   higher_is_better=False)
    def run_once(self):
        """Inspects the process list, looking for root and sandboxed processes
        (with some exclusions). If we have a baseline entry for a given process,
        confirms it's an exact match. Warns if we see root or sandboxed
        processes that we have no baseline for, and warns if we have
        baselines for processes not seen running.
        """

        fieldnames, baseline = self.load_baseline()
        exclusions = self.load_exclusions()
        running_processes = self.get_running_processes()
        is_asan = asan.running_on_asan()
        if is_asan:
            logging.info('ASAN image detected -> skipping seccomp checks')

        kthreadd_pid = -1

        init_process = None
        running_services = {}

        # Filter running processes list
        for process in running_processes:
            exe = process.comm

            if exe == "kthreadd":
                kthreadd_pid = process.pid
                continue
            elif process.pid == "1":
                init_process = process
                continue

            # Don't worry about kernel threads
            if process.ppid == kthreadd_pid:
                continue

            if exe in exclusions:
                continue

            running_services[exe] = process

        if not init_process:
            raise error.TestFail("Cannot find init process")

        # Find differences between running services and baseline
        services_set = set(running_services.keys())
        baseline_set = set(baseline.keys())

        new_services = services_set.difference(baseline_set)
        stale_baselines = baseline_set.difference(services_set)

        # Check baseline
        sandbox_delta = []
        for exe in services_set.intersection(baseline_set):
            process = running_services[exe]

            # If the process is not running as the correct user
            if process.euser != baseline[exe]["euser"]:
                logging.error('%s: bad user: wanted "%s" but got "%s"', exe,
                              baseline[exe]['euser'], process.euser)
                sandbox_delta.append(exe)
                continue

            # If the process is not running as the correct group
            if process.egroup != baseline[exe]['egroup']:
                logging.error('%s: bad group: wanted "%s" but got "%s"', exe,
                              baseline[exe]['egroup'], process.egroup)
                sandbox_delta.append(exe)
                continue

            # Check the various sandbox settings.
            if (baseline[exe]['pidns'] == 'Yes'
                    and process.pidns == init_process.pidns):
                logging.error('%s: missing pid ns usage', exe)
                sandbox_delta.append(exe)
            elif (baseline[exe]['caps'] == 'Yes'
                  and process.capeff == init_process.capeff):
                logging.error('%s: missing caps usage', exe)
                sandbox_delta.append(exe)
            elif (baseline[exe]['filter'] == 'Yes'
                  and process.seccomp != SECCOMP_MODE_FILTER and not is_asan):
                # Since minijail disables seccomp at runtime when ASAN is
                # active, we can't enforce it on ASAN bots.  Just ignore
                # the test entirely.  (Comment applies to "is_asan" above.)
                logging.error(
                    '%s: missing seccomp usage: wanted %s (%s) but '
                    'got %s (%s)', exe, SECCOMP_MODE_FILTER,
                    SECCOMP_MAP[SECCOMP_MODE_FILTER], process.seccomp,
                    SECCOMP_MAP.get(process.seccomp, '???'))
                sandbox_delta.append(exe)

        # Save current run to results dir
        running_services_properties = [
            get_properties(s, init_process) for s in running_services.values()
        ]
        self.dump_services(fieldnames, running_services_properties)

        if len(stale_baselines) > 0:
            logging.warn('Stale baselines: %r', stale_baselines)

        if len(new_services) > 0:
            logging.warn('New services: %r', new_services)

            # We won't complain about new non-root services (on the assumption
            # that they've already somewhat sandboxed things), but we'll fail
            # with new root services (on the assumption they haven't done any
            # sandboxing work).  If they really need to run as root, they can
            # update the baseline to whitelist it.
            new_root_services = [
                x for x in new_services if running_services[x].euser == 'root'
            ]
            if new_root_services:
                logging.error(
                    'New services are not allowed to run as root, '
                    'but these are: %r', new_root_services)
                sandbox_delta.extend(new_root_services)

        if len(sandbox_delta) > 0:
            logging.error('Failed sandboxing: %r', sandbox_delta)
            raise error.TestFail("One or more processes failed sandboxing")