def test_validate_log_mode_4(self):
     self.assertTrue(validate_log_mode('rrrr'))
 def test_validate_log_mode_invalid_1(self):
     self.assertFalse(
         validate_log_mode('c')
     )  # 'c' (create) must be converted to 'a' before calling validate_log_mode()
 def test_validate_log_mode_2(self):
     self.assertTrue(validate_log_mode('rw'))
 def test_validate_log_mode_3(self):
     self.assertTrue(validate_log_mode('Pixrw'))
 def test_validate_log_mode_invalid_4(self):
     self.assertFalse(validate_log_mode(''))
 def test_validate_log_mode_1(self):
     self.assertTrue(validate_log_mode('a'))
Пример #7
0
    def add_event_to_tree(self, e):
        aamode = e.get('aamode', 'UNKNOWN')
        if e.get('type', False):
            if re.search('(UNKNOWN\[1501\]|APPARMOR_AUDIT|1501)', e['type']):
                aamode = 'AUDIT'
            elif re.search('(UNKNOWN\[1502\]|APPARMOR_ALLOWED|1502)', e['type']):
                aamode = 'PERMITTING'
            elif re.search('(UNKNOWN\[1503\]|APPARMOR_DENIED|1503)', e['type']):
                aamode = 'REJECTING'
            elif re.search('(UNKNOWN\[1504\]|APPARMOR_HINT|1504)', e['type']):
                aamode = 'HINT'
            elif re.search('(UNKNOWN\[1505\]|APPARMOR_STATUS|1505)', e['type']):
                aamode = 'STATUS'
            elif re.search('(UNKNOWN\[1506\]|APPARMOR_ERROR|1506)', e['type']):
                aamode = 'ERROR'
            else:
                aamode = 'UNKNOWN'

        if aamode in ['UNKNOWN', 'AUDIT', 'STATUS', 'ERROR']:
            return None

        if 'profile_set' in e['operation']:
            return None

        # Skip if AUDIT event was issued due to a change_hat in unconfined mode
        if not e.get('profile', False):
            return None

        # Convert new null profiles to old single level null profile
        if '//null-' in e['profile']:
            e['profile'] = 'null-complain-profile'

        profile = e['profile']
        hat = None

        if '//' in e['profile']:
            profile, hat = e['profile'].split('//')[:2]

        # Filter out change_hat events that aren't from learning
        if e['operation'] == 'change_hat':
            if aamode != 'HINT' and aamode != 'PERMITTING':
                return None
            profile = e['name']
            #hat = None
            if '//' in e['name']:
                profile, hat = e['name'].split('//')[:2]

        if not hat:
            hat = profile

        # prog is no longer passed around consistently
        prog = 'HINT'

        if profile != 'null-complain-profile' and not self.profile_exists(profile):
            return None
        if e['operation'] == 'exec':
            # convert rmask and dmask to mode arrays
            e['denied_mask'],  e['name2'] = log_str_to_mode(e['profile'], e['denied_mask'], e['name2'])
            e['request_mask'], e['name2'] = log_str_to_mode(e['profile'], e['request_mask'], e['name2'])

            if e.get('info', False) and e['info'] == 'mandatory profile missing':
                self.add_to_tree(e['pid'], e['parent'], 'exec',
                                 [profile, hat, aamode, 'PERMITTING', e['denied_mask'], e['name'], e['name2']])
            elif (e.get('name2', False) and '//null-' in e['name2']) or e.get('name', False):
                self.add_to_tree(e['pid'], e['parent'], 'exec',
                                 [profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
            else:
                self.debug_logger.debug('add_event_to_tree: dropped exec event in %s' % e['profile'])

        elif ( e['operation'].startswith('file_') or e['operation'].startswith('inode_') or
            e['operation'] in ['open', 'truncate', 'mkdir', 'mknod', 'chmod', 'rename_src',
                                'rename_dest', 'unlink', 'rmdir', 'symlink_create', 'link',
                                'sysctl', 'getattr', 'setattr', 'xattr'] ):

            # for some reason, we get file_perm log events without request_mask, see https://bugs.launchpad.net/apparmor/+bug/1466812/
            if e['operation'] == 'file_perm' and e['request_mask'] is None:
                self.debug_logger.debug('UNHANDLED (missing request_mask): %s' % e)
                return None

            # Map c (create) to a and d (delete) to w (logging is more detailed than the profile language)
            rmask = e['request_mask']
            rmask = rmask.replace('c', 'a')
            rmask = rmask.replace('d', 'w')
            if not validate_log_mode(hide_log_mode(rmask)):
                raise AppArmorException(_('Log contains unknown mode %s') % rmask)

            dmask = e['denied_mask']
            dmask = dmask.replace('c', 'a')
            dmask = dmask.replace('d', 'w')
            if not validate_log_mode(hide_log_mode(dmask)):
                raise AppArmorException(_('Log contains unknown mode %s') % dmask)

            # convert rmask and dmask to mode arrays
            e['denied_mask'],  e['name2'] = log_str_to_mode(e['profile'], dmask, e['name2'])
            e['request_mask'], e['name2'] = log_str_to_mode(e['profile'], rmask, e['name2'])

            # check if this is an exec event
            is_domain_change = False
            if e['operation'] == 'inode_permission' and (e['denied_mask'] & AA_MAY_EXEC) and aamode == 'PERMITTING':
                following = self.peek_at_next_log_entry()
                if following:
                    entry = self.parse_log_record(following)
                    if entry and entry.get('info', False) == 'set profile':
                        is_domain_change = True
                        self.throw_away_next_log_entry()

            if is_domain_change:
                self.add_to_tree(e['pid'], e['parent'], 'exec',
                                 [profile, hat, prog, aamode, e['denied_mask'], e['name'], e['name2']])
            else:
                self.add_to_tree(e['pid'], e['parent'], 'path',
                                 [profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])

        elif e['operation'] == 'capable':
            self.add_to_tree(e['pid'], e['parent'], 'capability',
                             [profile, hat, prog, aamode, e['name'], ''])

        elif e['operation'] == 'clone':
            parent, child = e['pid'], e['task']
            if not parent:
                parent = 'null-complain-profile'
            if not hat:
                hat = 'null-complain-profile'
            arrayref = []
            if self.pid.get(parent, False):
                self.pid[parent].append(arrayref)
            else:
                self.log.append(arrayref)
            self.pid[child].append(arrayref)
            for ia in ['fork', child, profile, hat]:
                arrayref.append(ia)
#             if self.pid.get(parent, False):
#                 self.pid[parent] += [arrayref]
#             else:
#                 self.log += [arrayref]
#             self.pid[child] = arrayref

        elif self.op_type(e['operation']) == 'net':
            self.add_to_tree(e['pid'], e['parent'], 'netdomain',
                             [profile, hat, prog, aamode, e['family'], e['sock_type'], e['protocol']])
        elif e['operation'] == 'change_hat':
            self.add_to_tree(e['pid'], e['parent'], 'unknown_hat',
                             [profile, hat, aamode, hat])
        else:
            self.debug_logger.debug('UNHANDLED: %s' % e)
Пример #8
0
 def test_validate_log_mode_2(self):
     self.assertTrue(validate_log_mode('rw'))
Пример #9
0
 def test_validate_log_mode_invalid_2(self):
     self.assertFalse(validate_log_mode('R'))  # only lowercase 'r' is valid
Пример #10
0
 def test_validate_log_mode_invalid_4(self):
     self.assertFalse(validate_log_mode(''))
Пример #11
0
 def test_validate_log_mode_invalid_1(self):
     self.assertFalse(validate_log_mode('c'))  # 'c' (create) must be converted to 'a' before calling validate_log_mode()
Пример #12
0
 def test_validate_log_mode_4(self):
     self.assertTrue(validate_log_mode('rrrr'))
Пример #13
0
 def test_validate_log_mode_3(self):
     self.assertTrue(validate_log_mode('Pixrw'))
 def test_validate_log_mode_invalid_2(self):
     self.assertFalse(validate_log_mode('R'))  # only lowercase 'r' is valid
Пример #15
0
    def parse_event_for_tree(self, e):
        aamode = e.get('aamode', 'UNKNOWN')

        if e.get('type', False):
            aamode = self.map_log_type(e['type'])

        if aamode in ['UNKNOWN', 'AUDIT', 'STATUS', 'ERROR']:
            return None

        if 'profile_set' in e['operation']:
            return None

        # Skip if AUDIT event was issued due to a change_hat in unconfined mode
        if not e.get('profile', False):
            return None

        # Convert new null profiles to old single level null profile
        if '//null-' in e['profile']:
            e['profile'] = 'null-complain-profile'

        profile = e['profile']
        hat = None

        if '//' in e['profile']:
            profile, hat = e['profile'].split('//')[:2]

        # Filter out change_hat events that aren't from learning
        if e['operation'] == 'change_hat':
            if aamode != 'HINT' and aamode != 'PERMITTING':
                return None
            profile = e['name2']
            #hat = None
            if '//' in e['name2']:
                profile, hat = e['name2'].split('//')[:2]

        if not hat:
            hat = profile

        # prog is no longer passed around consistently
        prog = 'HINT'

        if profile != 'null-complain-profile' and not self.profile_exists(profile):
            return None
        if e['operation'] == 'exec':
            # convert rmask and dmask to mode arrays
            e['denied_mask'],  e['name2'] = log_str_to_mode(e['profile'], e['denied_mask'], e['name2'])
            e['request_mask'], e['name2'] = log_str_to_mode(e['profile'], e['request_mask'], e['name2'])

            if e.get('info', False) and e['info'] == 'mandatory profile missing':
                return(e['pid'], e['parent'], 'exec',
                                 [profile, hat, aamode, 'PERMITTING', e['denied_mask'], e['name'], e['name2']])
            elif (e.get('name2', False) and '//null-' in e['name2']) or e.get('name', False):
                return(e['pid'], e['parent'], 'exec',
                                 [profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
            else:
                self.debug_logger.debug('parse_event_for_tree: dropped exec event in %s' % e['profile'])

        elif ( e['operation'].startswith('file_') or e['operation'].startswith('inode_') or
            e['operation'] in ['open', 'truncate', 'mkdir', 'mknod', 'chmod', 'rename_src',
                                'rename_dest', 'unlink', 'rmdir', 'symlink_create', 'link',
                                'sysctl', 'getattr', 'setattr', 'xattr'] ):

            # for some kernel-side reason, we get file-related log events without request_mask, see
            # https://bugs.launchpad.net/apparmor/+bug/1466812/, https://bugs.launchpad.net/apparmor/+bug/1509030 and https://bugs.launchpad.net/apparmor/+bug/1540562
            # request_mask can also be '', see https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/1525119
            if not e['request_mask']:
                self.debug_logger.debug('UNHANDLED (missing request_mask): %s' % e)
                return None

            # sometimes network events come with an e['operation'] that matches the list of file operations
            # see https://bugs.launchpad.net/apparmor/+bug/1577051 and https://bugs.launchpad.net/apparmor/+bug/1582374
            # XXX these events are network events, so we should map them as such
            if 'send' in e['request_mask'] or 'receive' in e['request_mask']:
                self.debug_logger.debug('UNHANDLED (request_mask is send or receive): %s' % e)
                return None

            # Map c (create) and d (delete) to w (logging is more detailed than the profile language)
            rmask = e['request_mask']
            rmask = rmask.replace('c', 'w')
            rmask = rmask.replace('d', 'w')
            if not validate_log_mode(hide_log_mode(rmask)):
                raise AppArmorException(_('Log contains unknown mode %s') % rmask)

            dmask = e['denied_mask']
            dmask = dmask.replace('c', 'w')
            dmask = dmask.replace('d', 'w')
            if not validate_log_mode(hide_log_mode(dmask)):
                raise AppArmorException(_('Log contains unknown mode %s') % dmask)

            # convert rmask and dmask to mode arrays
            e['denied_mask'],  e['name2'] = log_str_to_mode(e['profile'], dmask, e['name2'])
            e['request_mask'], e['name2'] = log_str_to_mode(e['profile'], rmask, e['name2'])

            # check if this is an exec event
            is_domain_change = False
            if e['operation'] == 'inode_permission' and (e['denied_mask'] & AA_MAY_EXEC) and aamode == 'PERMITTING':
                following = self.peek_at_next_log_entry()
                if following:
                    entry = self.parse_log_record(following)
                    if entry and entry.get('info', False) == 'set profile':
                        is_domain_change = True
                        self.throw_away_next_log_entry()

            if is_domain_change:
                return(e['pid'], e['parent'], 'exec',
                                 [profile, hat, prog, aamode, e['denied_mask'], e['name'], e['name2']])
            else:
                return(e['pid'], e['parent'], 'path',
                                 [profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])

        elif e['operation'] == 'capable':
            return(e['pid'], e['parent'], 'capability',
                             [profile, hat, prog, aamode, e['name'], ''])

        elif e['operation'] == 'clone':
            parent, child = e['pid'], e['task']
            if not parent:
                parent = 'null-complain-profile'
            if not hat:
                hat = 'null-complain-profile'
            arrayref = []
            if self.pid.get(parent, False):
                self.pid[parent].append(arrayref)
            else:
                self.log.append(arrayref)
            self.pid[child].append(arrayref)
            for ia in ['fork', child, profile, hat]:
                arrayref.append(ia)
#             if self.pid.get(parent, False):
#                 self.pid[parent] += [arrayref]
#             else:
#                 self.log += [arrayref]
#             self.pid[child] = arrayref

        elif self.op_type(e['operation']) == 'net':
            return(e['pid'], e['parent'], 'netdomain',
                             [profile, hat, prog, aamode, e['family'], e['sock_type'], e['protocol']])
        elif e['operation'] == 'change_hat':
            return(e['pid'], e['parent'], 'unknown_hat',
                             [profile, hat, aamode, hat])
        elif e['operation'] == 'ptrace':
            return(e['pid'], e['parent'], 'ptrace',
                             [profile, hat, prog, aamode, e['denied_mask'], e['peer']])
        elif e['operation'] == 'signal':
            return(e['pid'], e['parent'], 'signal',
                             [profile, hat, prog, aamode, e['denied_mask'], e['signal'], e['peer']])
        else:
            self.debug_logger.debug('UNHANDLED: %s' % e)
    def parse_event_for_tree(self, e):
        aamode = e.get('aamode', 'UNKNOWN')

        if aamode == 'UNKNOWN':
            raise AppArmorBug('aamode is UNKNOWN - %s' %
                              e['type'])  # should never happen

        if aamode in ['AUDIT', 'STATUS', 'ERROR']:
            return None

        if 'profile_set' in e['operation']:
            return None

        # Skip if AUDIT event was issued due to a change_hat in unconfined mode
        if not e.get('profile', False):
            return None

        # Convert new null profiles to old single level null profile
        if '//null-' in e['profile']:
            e['profile'] = 'null-complain-profile'

        profile = e['profile']
        hat = None

        if '//' in e['profile']:
            profile, hat = e['profile'].split('//')[:2]

        # Filter out change_hat events that aren't from learning
        if e['operation'] == 'change_hat':
            if aamode != 'HINT' and aamode != 'PERMITTING':
                return None
            if e['error_code'] == 1 and e[
                    'info'] == 'unconfined can not change_hat':
                return None
            profile = e['name2']
            #hat = None
            if '//' in e['name2']:
                profile, hat = e['name2'].split('//')[:2]

        if not hat:
            hat = profile

        # prog is no longer passed around consistently
        prog = 'HINT'

        if profile != 'null-complain-profile' and not self.profile_exists(
                profile):
            return None
        if e['operation'] == 'exec':
            # convert rmask and dmask to mode arrays
            e['denied_mask'], e['name2'] = log_str_to_mode(
                e['profile'], e['denied_mask'], e['name2'])
            e['request_mask'], e['name2'] = log_str_to_mode(
                e['profile'], e['request_mask'], e['name2'])

            if e.get('info',
                     False) and e['info'] == 'mandatory profile missing':
                return (e['pid'], e['parent'], 'exec', [
                    profile, hat, aamode, 'PERMITTING', e['denied_mask'],
                    e['name'], e['name2']
                ])
            elif (e.get('name2', False) and '//null-' in e['name2']) or e.get(
                    'name', False):
                return (e['pid'], e['parent'], 'exec', [
                    profile, hat, prog, aamode, e['denied_mask'], e['name'], ''
                ])
            else:
                self.debug_logger.debug(
                    'parse_event_for_tree: dropped exec event in %s' %
                    e['profile'])

        elif self.op_type(e) == 'file':
            # Map c (create) and d (delete) to w (logging is more detailed than the profile language)
            rmask = e['request_mask']
            rmask = rmask.replace('c', 'w')
            rmask = rmask.replace('d', 'w')
            if not validate_log_mode(hide_log_mode(rmask)):
                raise AppArmorException(
                    _('Log contains unknown mode %s') % rmask)

            dmask = e['denied_mask']
            dmask = dmask.replace('c', 'w')
            dmask = dmask.replace('d', 'w')
            if not validate_log_mode(hide_log_mode(dmask)):
                raise AppArmorException(
                    _('Log contains unknown mode %s') % dmask)

            if e.get('ouid') is not None and e['fsuid'] == e['ouid']:
                # mark as "owner" event
                if '::' not in rmask:
                    rmask = '%s::' % rmask
                if '::' not in dmask:
                    dmask = '%s::' % dmask

            # convert rmask and dmask to mode arrays
            e['denied_mask'], e['name2'] = log_str_to_mode(
                e['profile'], dmask, e['name2'])
            e['request_mask'], e['name2'] = log_str_to_mode(
                e['profile'], rmask, e['name2'])

            # check if this is an exec event
            is_domain_change = False
            if e['operation'] == 'inode_permission' and (
                    e['denied_mask'] & AA_MAY_EXEC) and aamode == 'PERMITTING':
                following = self.peek_at_next_log_entry()
                if following:
                    entry = self.parse_log_record(following)
                    if entry and entry.get('info', False) == 'set profile':
                        is_domain_change = True
                        self.throw_away_next_log_entry()

            if is_domain_change:
                return (e['pid'], e['parent'], 'exec', [
                    profile, hat, prog, aamode, e['denied_mask'], e['name'],
                    e['name2']
                ])
            else:
                return (e['pid'], e['parent'], 'path', [
                    profile, hat, prog, aamode, e['denied_mask'], e['name'], ''
                ])

        elif e['operation'] == 'capable':
            return (e['pid'], e['parent'], 'capability',
                    [profile, hat, prog, aamode, e['name'], ''])

        elif e['operation'] == 'clone':
            parent, child = e['pid'], e['task']
            if not parent:
                parent = 'null-complain-profile'
            if not hat:
                hat = 'null-complain-profile'
            arrayref = []
            if self.pid.get(parent, False):
                self.pid[parent].append(arrayref)
            else:
                self.log.append(arrayref)
            self.pid[child].append(arrayref)
            for ia in ['fork', child, profile, hat]:
                arrayref.append(ia)
#             if self.pid.get(parent, False):
#                 self.pid[parent] += [arrayref]
#             else:
#                 self.log += [arrayref]
#             self.pid[child] = arrayref

        elif self.op_type(e) == 'net':
            return (e['pid'], e['parent'], 'netdomain', [
                profile, hat, prog, aamode, e['family'], e['sock_type'],
                e['protocol']
            ])
        elif e['operation'] == 'change_hat':
            return (e['pid'], e['parent'], 'unknown_hat',
                    [profile, hat, aamode, hat])
        elif e['operation'] == 'ptrace':
            if not e['peer']:
                self.debug_logger.debug(
                    'ignored garbage ptrace event with empty peer')
                return None
            if not e['denied_mask']:
                self.debug_logger.debug(
                    'ignored garbage ptrace event with empty denied_mask')
                return None

            return (e['pid'], e['parent'], 'ptrace',
                    [profile, hat, prog, aamode, e['denied_mask'], e['peer']])
        elif e['operation'] == 'signal':
            return (e['pid'], e['parent'], 'signal', [
                profile, hat, prog, aamode, e['denied_mask'], e['signal'],
                e['peer']
            ])
        elif e['operation'].startswith('dbus_'):
            return (e['pid'], e['parent'], 'dbus', [
                profile, hat, prog, aamode, e['denied_mask'], e['bus'],
                e['path'], e['name'], e['interface'], e['member'],
                e['peer_profile']
            ])
        else:
            self.debug_logger.debug('UNHANDLED: %s' % e)
Пример #17
0
 def test_validate_log_mode_1(self):
     self.assertTrue(validate_log_mode('a'))