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'))
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)
def test_validate_log_mode_2(self): self.assertTrue(validate_log_mode('rw'))
def test_validate_log_mode_invalid_2(self): self.assertFalse(validate_log_mode('R')) # only lowercase 'r' is valid
def test_validate_log_mode_invalid_4(self): self.assertFalse(validate_log_mode(''))
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_4(self): self.assertTrue(validate_log_mode('rrrr'))
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
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)
def test_validate_log_mode_1(self): self.assertTrue(validate_log_mode('a'))