2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-28 12:58:07 +00:00

Merge Add PivotRootRule class

... and tests for it.

This replaces the old code that just stores the full rule as text.

We also get rid of the old ['allow'] and ['deny'] items in
ProfileStorage, the handling of old write functions, and the last usage
of _Raw_Rule (and therefore _Raw_Rule itsself).

Also delete the old test-pivot_root_parse.py which relied on the ancient
code, and even used a wrong syntax in its test rules.

Oh, and aa-logprof can now ask about pivot_root events.

See the individual commits for details.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1232
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Christian Boltz <apparmor@cboltz.de>
This commit is contained in:
Christian Boltz 2024-05-23 18:03:24 +00:00
commit 38dfa14c60
12 changed files with 801 additions and 149 deletions

View File

@ -26,7 +26,6 @@ from tempfile import NamedTemporaryFile
import apparmor.config import apparmor.config
import apparmor.logparser import apparmor.logparser
import apparmor.rules as aarules
import apparmor.severity import apparmor.severity
import apparmor.ui as aaui import apparmor.ui as aaui
from apparmor.aare import AARE from apparmor.aare import AARE
@ -38,7 +37,7 @@ from apparmor.profile_storage import ProfileStorage, add_or_remove_flag, ruletyp
from apparmor.regex import ( from apparmor.regex import (
RE_HAS_COMMENT_SPLIT, RE_PROFILE_CHANGE_HAT, RE_PROFILE_CONDITIONAL, RE_HAS_COMMENT_SPLIT, RE_PROFILE_CHANGE_HAT, RE_PROFILE_CONDITIONAL,
RE_PROFILE_CONDITIONAL_BOOLEAN, RE_PROFILE_CONDITIONAL_VARIABLE, RE_PROFILE_END, RE_PROFILE_CONDITIONAL_BOOLEAN, RE_PROFILE_CONDITIONAL_VARIABLE, RE_PROFILE_END,
RE_PROFILE_HAT_DEF, RE_PROFILE_PIVOT_ROOT, RE_PROFILE_START, RE_PROFILE_HAT_DEF, RE_PROFILE_START,
RE_RULE_HAS_COMMA, parse_profile_start_line, re_match_include) RE_RULE_HAS_COMMA, parse_profile_start_line, re_match_include)
from apparmor.rule.abi import AbiRule from apparmor.rule.abi import AbiRule
from apparmor.rule.capability import CapabilityRule from apparmor.rule.capability import CapabilityRule
@ -47,6 +46,7 @@ from apparmor.rule.dbus import DbusRule
from apparmor.rule.file import FileRule from apparmor.rule.file import FileRule
from apparmor.rule.include import IncludeRule from apparmor.rule.include import IncludeRule
from apparmor.rule.network import NetworkRule from apparmor.rule.network import NetworkRule
from apparmor.rule.pivot_root import PivotRootRule
from apparmor.rule.ptrace import PtraceRule from apparmor.rule.ptrace import PtraceRule
from apparmor.rule.signal import SignalRule from apparmor.rule.signal import SignalRule
from apparmor.rule.userns import UserNamespaceRule from apparmor.rule.userns import UserNamespaceRule
@ -1731,6 +1731,14 @@ def collapse_log(hashlog, ignore_null_profiles=True):
mount_event = MountRule(operation=operation, fstype=_fstype, options=_options, source=_source, dest=_dest) mount_event = MountRule(operation=operation, fstype=_fstype, options=_options, source=_source, dest=_dest)
if not hat_exists or not is_known_rule(aa[profile][hat], 'mount', mount_event): if not hat_exists or not is_known_rule(aa[profile][hat], 'mount', mount_event):
log_dict[aamode][final_name]['mount'].add(mount_event) log_dict[aamode][final_name]['mount'].add(mount_event)
pivot_root = hashlog[aamode][full_profile]['pivot_root']
for oldroot in pivot_root.keys():
for newroot in pivot_root[oldroot]:
pivot_root_event = PivotRootRule(oldroot, newroot, PivotRootRule.ALL, log_event=True)
if not hat_exists or not is_known_rule(aa[profile][hat], 'pivot_root', pivot_root_event):
log_dict[aamode][final_name]['pivot_root'].add(pivot_root_event)
return log_dict return log_dict
@ -1955,29 +1963,6 @@ def parse_profile_data(data, file, do_include, in_preamble):
# Conditional Boolean defined # Conditional Boolean defined
pass pass
elif RE_PROFILE_PIVOT_ROOT.search(line):
matches = RE_PROFILE_PIVOT_ROOT.search(line).groups()
if not profile:
raise AppArmorException(_('Syntax Error: Unexpected pivot_root entry found in file: %(file)s line: %(line)s')
% {'file': file, 'line': lineno + 1})
audit = False
if matches[0]:
audit = True
allow = 'allow'
if matches[1] and matches[1].strip() == 'deny':
allow = 'deny'
pivot_root = matches[2].strip()
pivot_root_rule = parse_pivot_root_rule(pivot_root)
pivot_root_rule.audit = audit
pivot_root_rule.deny = (allow == 'deny')
pivot_root_rules = profile_data[profname][allow].get('pivot_root', [])
pivot_root_rules.append(pivot_root_rule)
profile_data[profname][allow]['pivot_root'] = pivot_root_rules
elif RE_PROFILE_CHANGE_HAT.search(line): elif RE_PROFILE_CHANGE_HAT.search(line):
matches = RE_PROFILE_CHANGE_HAT.search(line).groups() matches = RE_PROFILE_CHANGE_HAT.search(line).groups()
@ -2065,6 +2050,7 @@ def match_line_against_rule_classes(line, profile, file, lineno, in_preamble):
'mqueue', 'mqueue',
'io_uring', 'io_uring',
'mount', 'mount',
'pivot_root',
'unix', 'unix',
): ):
@ -2117,11 +2103,6 @@ def split_to_merged(profile_data):
return merged return merged
def parse_pivot_root_rule(line):
# XXX Do real parsing here
return aarules.Raw_Pivot_Root_Rule(line)
def write_piece(profile_data, depth, name, nhat): def write_piece(profile_data, depth, name, nhat):
pre = ' ' * depth pre = ' ' * depth
data = [] data = []

View File

@ -20,8 +20,6 @@ import termios
import tty import tty
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
import apparmor.rules as rules
DEBUGGING = False DEBUGGING = False
@ -106,8 +104,6 @@ def recursive_print(src, dpth=0, key=''):
for litem in src: for litem in src:
recursive_print(litem, dpth + 1) recursive_print(litem, dpth + 1)
print(tabs + "]") print(tabs + "]")
elif isinstance(src, rules._Raw_Rule):
src.recursive_print(dpth)
else: else:
if key: if key:
print(tabs + '%s = %s' % (key, src)) print(tabs + '%s = %s' % (key, src))

View File

@ -55,6 +55,7 @@ class ReadLog:
'exec': hasher(), 'exec': hasher(),
'network': hasher(), 'network': hasher(),
'path': hasher(), 'path': hasher(),
'pivot_root': hasher(),
'ptrace': hasher(), 'ptrace': hasher(),
'signal': hasher(), 'signal': hasher(),
'userns': hasher(), 'userns': hasher(),
@ -118,6 +119,8 @@ class ReadLog:
ev['peer'] = event.peer ev['peer'] = event.peer
elif ev['operation'] and ev['operation'] == 'ptrace': elif ev['operation'] and ev['operation'] == 'ptrace':
ev['peer'] = event.peer ev['peer'] = event.peer
elif ev['operation'] and ev['operation'] == 'pivotroot':
ev['src_name'] = event.src_name
elif ev['operation'] and ev['operation'] == 'mount': elif ev['operation'] and ev['operation'] == 'mount':
ev['flags'] = event.flags ev['flags'] = event.flags
ev['fs_type'] = event.fs_type ev['fs_type'] = event.fs_type
@ -238,6 +241,10 @@ class ReadLog:
self.hashlog[aamode][full_profile]['mount'][e['operation']][e['flags']][e['fs_type']][e['name']][None] = True self.hashlog[aamode][full_profile]['mount'][e['operation']][e['flags']][e['fs_type']][e['name']][None] = True
return return
elif e['operation'] and e['operation'] == 'pivotroot':
# TODO: can the log contain the target profile?
self.hashlog[aamode][full_profile]['pivot_root'][e['src_name']][e['name']] = True
elif e['class'] and e['class'] == 'net' and e['family'] and e['family'] == 'unix': elif e['class'] and e['class'] == 'net' and e['family'] and e['family'] == 'unix':
rule = (e['sock_type'], None) # Protocol is not supported yet. rule = (e['sock_type'], None) # Protocol is not supported yet.
local = (e['addr'], None, e['attr'], None) local = (e['addr'], None, e['attr'], None)

View File

@ -32,6 +32,7 @@ from apparmor.rule.userns import UserNamespaceRule, UserNamespaceRuleset
from apparmor.rule.mqueue import MessageQueueRule, MessageQueueRuleset from apparmor.rule.mqueue import MessageQueueRule, MessageQueueRuleset
from apparmor.rule.io_uring import IOUringRule, IOUringRuleset from apparmor.rule.io_uring import IOUringRule, IOUringRuleset
from apparmor.rule.mount import MountRule, MountRuleset from apparmor.rule.mount import MountRule, MountRuleset
from apparmor.rule.pivot_root import PivotRootRule, PivotRootRuleset
from apparmor.rule.unix import UnixRule, UnixRuleset from apparmor.rule.unix import UnixRule, UnixRuleset
from apparmor.translations import init_translation from apparmor.translations import init_translation
@ -54,8 +55,8 @@ ruletypes = {
'mqueue': {'rule': MessageQueueRule, 'ruleset': MessageQueueRuleset}, 'mqueue': {'rule': MessageQueueRule, 'ruleset': MessageQueueRuleset},
'io_uring': {'rule': IOUringRule, 'ruleset': IOUringRuleset}, 'io_uring': {'rule': IOUringRule, 'ruleset': IOUringRuleset},
'mount': {'rule': MountRule, 'ruleset': MountRuleset}, 'mount': {'rule': MountRule, 'ruleset': MountRuleset},
'pivot_root': {'rule': PivotRootRule, 'ruleset': PivotRootRuleset},
'unix': {'rule': UnixRule, 'ruleset': UnixRuleset}, 'unix': {'rule': UnixRule, 'ruleset': UnixRuleset},
} }
@ -87,13 +88,6 @@ class ProfileStorage:
data['is_hat'] = False # profile or hat? data['is_hat'] = False # profile or hat?
data['hat_keyword'] = False # True for 'hat foo', False for '^foo' data['hat_keyword'] = False # True for 'hat foo', False for '^foo'
data['allow'] = dict()
data['deny'] = dict()
# pivot_root has a .get() fallback to list() - initialize it nevertheless
data['allow']['pivot_root'] = []
data['deny']['pivot_root'] = []
self.data = data self.data = data
def __getitem__(self, key): def __getitem__(self, key):
@ -184,11 +178,6 @@ class ProfileStorage:
Note that the profile header and the closing "}" are _not_ included. Note that the profile header and the closing "}" are _not_ included.
""" """
# "old" write functions for rule types not implemented as *Rule class yet
write_functions = {
'pivot_root': write_pivot_root,
}
write_order = [ write_order = [
'abi', 'abi',
'inc_ie', 'inc_ie',
@ -211,10 +200,7 @@ class ProfileStorage:
data = [] data = []
for ruletype in write_order: for ruletype in write_order:
if write_functions.get(ruletype): data.extend(self.data[ruletype].get_clean(depth))
data.extend(write_functions[ruletype](self.data, depth))
else:
data.extend(self.data[ruletype].get_clean(depth))
return data return data
@ -310,23 +296,3 @@ def var_transform(ref):
value = '""' value = '""'
data.append(quote_if_needed(value)) data.append(quote_if_needed(value))
return ' '.join(data) return ' '.join(data)
def write_pivot_root_rules(prof_data, depth, allow):
pre = ' ' * depth
data = []
# no pivot_root rules, so return
if not prof_data[allow].get('pivot_root', False):
return data
for pivot_root_rule in prof_data[allow]['pivot_root']:
data.append('%s%s' % (pre, pivot_root_rule.serialize()))
data.append('')
return data
def write_pivot_root(prof_data, depth):
data = write_pivot_root_rules(prof_data, depth, 'deny')
data.extend(write_pivot_root_rules(prof_data, depth, 'allow'))
return data

View File

@ -50,7 +50,7 @@ RE_PROFILE_DBUS = re.compile(RE_AUDIT_DENY + r'(dbus\s*,|dbus(?P<details>\s+[^#]
RE_PROFILE_MOUNT = re.compile(RE_AUDIT_DENY + r'((?P<operation>mount|remount|umount|unmount)(?P<details>\s+[^#]*)?\s*,)' + RE_EOL) RE_PROFILE_MOUNT = re.compile(RE_AUDIT_DENY + r'((?P<operation>mount|remount|umount|unmount)(?P<details>\s+[^#]*)?\s*,)' + RE_EOL)
RE_PROFILE_SIGNAL = re.compile(RE_AUDIT_DENY + r'(signal\s*,|signal(?P<details>\s+[^#]*)\s*,)' + RE_EOL) RE_PROFILE_SIGNAL = re.compile(RE_AUDIT_DENY + r'(signal\s*,|signal(?P<details>\s+[^#]*)\s*,)' + RE_EOL)
RE_PROFILE_PTRACE = re.compile(RE_AUDIT_DENY + r'(ptrace\s*,|ptrace(?P<details>\s+[^#]*)\s*,)' + RE_EOL) RE_PROFILE_PTRACE = re.compile(RE_AUDIT_DENY + r'(ptrace\s*,|ptrace(?P<details>\s+[^#]*)\s*,)' + RE_EOL)
RE_PROFILE_PIVOT_ROOT = re.compile(RE_AUDIT_DENY + r'(pivot_root\s*,|pivot_root\s+[^#]*\s*,)' + RE_EOL) RE_PROFILE_PIVOT_ROOT = re.compile(RE_AUDIT_DENY + r'(pivot_root\s*,|pivot_root(?P<details>\s+[^#]*),)' + RE_EOL)
RE_PROFILE_UNIX = re.compile(RE_AUDIT_DENY + r'(unix\s*,|unix(?P<details>\s+[^#]*)\s*,)' + RE_EOL) RE_PROFILE_UNIX = re.compile(RE_AUDIT_DENY + r'(unix\s*,|unix(?P<details>\s+[^#]*)\s*,)' + RE_EOL)
RE_PROFILE_USERNS = re.compile(RE_AUDIT_DENY + r'(userns\s*,|userns(?P<details>\s+[^#]*)\s*,)' + RE_EOL) RE_PROFILE_USERNS = re.compile(RE_AUDIT_DENY + r'(userns\s*,|userns(?P<details>\s+[^#]*)\s*,)' + RE_EOL)
RE_PROFILE_MQUEUE = re.compile(RE_AUDIT_DENY + r'(mqueue\s*,|mqueue(?P<details>\s+[^#]*)\s*,)' + RE_EOL) RE_PROFILE_MQUEUE = re.compile(RE_AUDIT_DENY + r'(mqueue\s*,|mqueue(?P<details>\s+[^#]*)\s*,)' + RE_EOL)

View File

@ -0,0 +1,198 @@
# ----------------------------------------------------------------------
# Copyright (C) 2024 Christian Boltz
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# ----------------------------------------------------------------------
import re
from apparmor.common import AppArmorBug, AppArmorException
from apparmor.aare import AARE
from apparmor.regex import RE_PROFILE_PIVOT_ROOT, RE_PROFILE_NAME, RE_PROFILE_PATH_OR_VAR, strip_quotes
from apparmor.rule import BaseRule, BaseRuleset, parse_modifiers, logprof_value_or_all, quote_if_needed
from apparmor.translations import init_translation
_ = init_translation()
RE_PIVOT_ROOT_DETAILS = re.compile(
r'^\s*'
+ r'(\s+oldroot=' + RE_PROFILE_PATH_OR_VAR % 'oldroot' + r')?' # noqa: E221
+ r'(\s+' + RE_PROFILE_PATH_OR_VAR % 'newroot' + r')?' # noqa: E221
+ r'(\s+->\s+' + RE_PROFILE_NAME % 'profile_name' + r')?' # noqa: E221
+ r'\s*$'
)
class PivotRootRule(BaseRule):
'''Class to handle and store a single pivot_root rule'''
# Nothing external should reference this class, all external users
# should reference the class field PivotRootRule.ALL
class __PivotRootAll(object):
pass
ALL = __PivotRootAll
rule_name = 'pivot_root'
_match_re = RE_PROFILE_PIVOT_ROOT
# PIVOT ROOT RULE = [ QUALIFIERS ] pivot_root [ oldroot=OLD PUT FILEGLOB ] [ NEW ROOT FILEGLOB ] [ -> PROFILE NAME ]
def __init__(self, oldroot, newroot, profile_name, audit=False, deny=False, allow_keyword=False, comment='', log_event=None):
super().__init__(audit=audit, deny=deny,
allow_keyword=allow_keyword,
comment=comment,
log_event=log_event)
self.oldroot, self.all_oldroots = self._aare_or_all(oldroot, 'oldroot', True, log_event) # noqa: E221
self.newroot, self.all_newroots = self._aare_or_all(newroot, 'newroot', True, log_event) # noqa: E221
self.profile_name, self.all_profile_names = self._aare_or_all(profile_name, 'profile_name', False, log_event) # noqa: E221
self.can_glob = not self.all_newroots
self.can_glob_ext = False
self.can_edit = not self.all_newroots
@classmethod
def _create_instance(cls, raw_rule, matches):
'''parse raw_rule and return instance of this class'''
audit, deny, allow_keyword, comment = parse_modifiers(matches)
rule_details = ''
if matches.group('details'):
rule_details = matches.group('details')
parsed = RE_PIVOT_ROOT_DETAILS.search(rule_details)
if not parsed:
raise AppArmorException('Cannot parse pivot_root rule ' + raw_rule)
r = parsed.groupdict()
if r['oldroot']:
oldroot = strip_quotes(r['oldroot'])
else:
oldroot = cls.ALL
if r['newroot']:
newroot = strip_quotes(r['newroot'])
else:
newroot = cls.ALL
if r['profile_name']:
profile_name = strip_quotes(r['profile_name'])
else:
profile_name = cls.ALL
else:
oldroot = cls.ALL
newroot = cls.ALL
profile_name = cls.ALL
return cls(oldroot=oldroot, newroot=newroot, profile_name=profile_name,
audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment)
def get_clean(self, depth=0):
space = ' ' * depth
if self.all_oldroots:
oldroot = ''
elif self.oldroot:
oldroot = ' oldroot=' + quote_if_needed(self.oldroot.regex)
else:
raise AppArmorBug('Empty oldroot in pivot_root rule')
if self.all_newroots:
newroot = ''
elif self.newroot:
newroot = ' ' + quote_if_needed(self.newroot.regex)
else:
raise AppArmorBug('Empty newroot in pivot_root rule')
if self.all_profile_names:
profile_name = ''
elif self.profile_name:
profile_name = ' -> ' + quote_if_needed(self.profile_name.regex)
else:
raise AppArmorBug('Empty profile_name in pivot_root rule')
return f'{space}{self.modifiers_str()}pivot_root{oldroot}{newroot}{profile_name},{self.comment}'
def _is_covered_localvars(self, other_rule):
if not self._is_covered_aare(self.oldroot, self.all_oldroots, other_rule.oldroot, other_rule.all_oldroots, 'oldroot'):
return False
if not self._is_covered_aare(self.newroot, self.all_newroots, other_rule.newroot, other_rule.all_newroots, 'newroot'):
return False
if not self._is_covered_aare(self.profile_name, self.all_profile_names, other_rule.profile_name, other_rule.all_profile_names, 'profile_name'):
return False
# still here? -> then it is covered
return True
def _is_equal_localvars(self, rule_obj, strict):
if not self._is_equal_aare(self.oldroot, self.all_oldroots, rule_obj.oldroot, rule_obj.all_oldroots, 'oldroot'):
return False
if not self._is_equal_aare(self.newroot, self.all_newroots, rule_obj.newroot, rule_obj.all_newroots, 'newroot'):
return False
if not self._is_equal_aare(self.profile_name, self.all_profile_names, rule_obj.profile_name, rule_obj.all_profile_names, 'profile_name'):
return False
return True
def glob(self):
'''Change newroot path to next possible glob'''
if self.all_newroots:
return
self.newroot = self.newroot.glob_path()
self.raw_rule = None
def edit_header(self):
if self.all_newroots:
raise AppArmorBug('Attemp to edit pivot_root rule without newroot limitations')
return (_('Enter new newroot: '), self.newroot.regex)
def validate_edit(self, newpath):
if self.all_newroots:
raise AppArmorBug('Attemp to edit pivot_root rule without newroot limitations')
newpath = AARE(newpath, True) # might raise AppArmorException if the new path doesn't start with / or a variable
return newpath.match(self.newroot)
def store_edit(self, newpath):
if self.all_newroots:
raise AppArmorBug('Attemp to edit pivot_root rule without newroot limitations')
self.newroot = AARE(newpath, True) # might raise AppArmorException if the new path doesn't start with / or a variable
self.raw_rule = None
def _logprof_header_localvars(self):
oldroot = logprof_value_or_all(self.oldroot, self.all_oldroots)
newroot = logprof_value_or_all(self.newroot, self.all_newroots)
profile_name = logprof_value_or_all(self.profile_name, self.all_profile_names)
return (
_('Old root'), oldroot,
_('New root'), newroot,
_('Target profile'), profile_name,
)
class PivotRootRuleset(BaseRuleset):
'''Class to handle and store a collection of pivot_root rules'''

View File

@ -1,33 +0,0 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2014 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License published by the Free Software Foundation.
#
# ------------------------------------------------------------------
class _Raw_Rule:
audit = False
deny = False
def __init__(self, rule):
self.rule = rule
def serialize(self):
return "%s%s%s" % ('audit ' if self.audit else '',
'deny ' if self.deny else '',
self.rule)
def recursive_print(self, depth):
tabs = ' ' * depth * 4
print('%s[%s]' % (tabs, type(self).__name__))
tabs += ' ' * 4
print('%saudit = %s' % (tabs, self.audit))
print('%sdeny = %s' % (tabs, self.deny))
print('%sraw rule = %s' % (tabs, self.rule))
class Raw_Pivot_Root_Rule(_Raw_Rule):
pass

View File

@ -21,7 +21,7 @@ COMMONDIR=../../common/
include $(COMMONDIR)/Make.rules include $(COMMONDIR)/Make.rules
# files that don't have 100% test coverage # files that don't have 100% test coverage
INCOMPLETE_COVERAGE=libraries/libapparmor/swig/python/.*/LibAppArmor/LibAppArmor.py|utils/aa-logprof|utils/apparmor/aa.py|utils/apparmor/common.py|utils/apparmor/config.py|utils/apparmor/easyprof.py|utils/apparmor/fail.py|utils/apparmor/logparser.py|utils/apparmor/profile_storage.py|utils/apparmor/rules.py|utils/apparmor/ui.py|minitools_test.py INCOMPLETE_COVERAGE=libraries/libapparmor/swig/python/.*/LibAppArmor/LibAppArmor.py|utils/aa-logprof|utils/apparmor/aa.py|utils/apparmor/common.py|utils/apparmor/config.py|utils/apparmor/easyprof.py|utils/apparmor/fail.py|utils/apparmor/logparser.py|utils/apparmor/ui.py|minitools_test.py
ifdef USE_SYSTEM ifdef USE_SYSTEM

View File

@ -153,8 +153,6 @@ log_to_skip = [
# tests that do not produce the expected profile (checked with assertNotEqual) # tests that do not produce the expected profile (checked with assertNotEqual)
log_to_profile_known_failures = [ log_to_profile_known_failures = [
'testcase_pivotroot_01', # pivot_rot not yet supported in logparser
# exec events # exec events
'testcase01', 'testcase01',
'testcase12', 'testcase12',
@ -175,7 +173,6 @@ log_to_profile_skip = [
# tests that cause an empty log # tests that cause an empty log
log_to_profile_known_empty_log = [ log_to_profile_known_empty_log = [
'change_onexec_lp1648143', # change_onexec not supported in logparser.py yet (and the log is about "no new privs" error) 'change_onexec_lp1648143', # change_onexec not supported in logparser.py yet (and the log is about "no new privs" error)
'testcase_pivotroot_01', # pivotroot not yet supported in logparser
'ptrace_garbage_lp1689667_1', # no denied= in log 'ptrace_garbage_lp1689667_1', # no denied= in log
'ptrace_no_denied_mask', # no denied= in log 'ptrace_no_denied_mask', # no denied= in log
'unconfined-change_hat', # unconfined trying to change_hat, which isn't allowed 'unconfined-change_hat', # unconfined trying to change_hat, which isn't allowed

View File

@ -0,0 +1,572 @@
#!/usr/bin/python3
# ----------------------------------------------------------------------
# Copyright (C) 2015 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# ----------------------------------------------------------------------
import unittest
from collections import namedtuple
from apparmor.common import AppArmorBug, AppArmorException
from apparmor.logparser import ReadLog
from apparmor.aare import AARE
from apparmor.rule.pivot_root import PivotRootRule, PivotRootRuleset
from apparmor.translations import init_translation
from common_test import AATest, setup_all_loops
_ = init_translation()
exp = namedtuple(
'exp', ('audit', 'allow_keyword', 'deny', 'comment', 'oldroot', 'all_oldroots', 'newroot',
'all_newroots', 'profile_name', 'all_profile_names'),
)
# # --- tests for single PivotRootRule --- #
class PivotRootTest(AATest):
def _compare_obj(self, obj, expected):
self.assertEqual(expected.audit, obj.audit)
self.assertEqual(expected.allow_keyword, obj.allow_keyword)
self.assertEqual(expected.deny, obj.deny)
self.assertEqual(expected.comment, obj.comment)
if type(obj.oldroot) is AARE:
self.assertEqual(expected.oldroot, obj.oldroot.regex)
else:
self.assertEqual(expected.oldroot, obj.oldroot)
self.assertEqual(expected.all_oldroots, obj.all_oldroots)
if type(obj.newroot) is AARE:
self.assertEqual(expected.newroot, obj.newroot.regex)
else:
self.assertEqual(expected.newroot, obj.newroot)
self.assertEqual(expected.all_newroots, obj.all_newroots)
if type(obj.profile_name) is AARE:
self.assertEqual(expected.profile_name, obj.profile_name.regex)
else:
self.assertEqual(expected.profile_name, obj.profile_name)
self.assertEqual(expected.all_profile_names, obj.all_profile_names)
class PivotRootTestParse(PivotRootTest):
tests = (
# PivotRootRule object audit allow deny comment oldroot all? newroot all? profile_name all?
('pivot_root,', exp(False, False, False, '', None, True, None, True, None, True)),
('pivot_root oldroot=/oldroot, # cmt', exp(False, False, False, ' # cmt', '/oldroot', False, None, True, None, True)),
('pivot_root oldroot=/oldroot /new/root, # cmt', exp(False, False, False, ' # cmt', '/oldroot', False, '/new/root', False, None, True)),
('pivot_root oldroot=/oldroot /new/root -> targetprof, # cmt', exp(False, False, False, ' # cmt', '/oldroot', False, '/new/root', False, 'targetprof', False)),
('pivot_root oldroot=/oldroot -> targetprof, # cmt', exp(False, False, False, ' # cmt', '/oldroot', False, None, True, 'targetprof', False)),
('pivot_root /new/root, # cmt', exp(False, False, False, ' # cmt', None, True, '/new/root', False, None, True)),
('pivot_root /new/root -> targetprof, # cmt', exp(False, False, False, ' # cmt', None, True, '/new/root', False, 'targetprof', False)),
('pivot_root -> targetprof, # cmt', exp(False, False, False, ' # cmt', None, True, None, True, 'targetprof', False)),
('pivot_root oldroot="/oldroot", # cmt', exp(False, False, False, ' # cmt', '/oldroot', False, None, True, None, True)),
('pivot_root "/new/root", # cmt', exp(False, False, False, ' # cmt', None, True, '/new/root', False, None, True)),
('pivot_root -> "targetprof", # cmt', exp(False, False, False, ' # cmt', None, True, None, True, 'targetprof', False)),
)
def _run_test(self, rawrule, expected):
self.assertTrue(PivotRootRule.match(rawrule))
obj = PivotRootRule.create_instance(rawrule)
self.assertEqual(rawrule.strip(), obj.raw_rule)
self._compare_obj(obj, expected)
class PivotRootTestParseInvalid(PivotRootTest):
tests = (
('pivot_root foo,', AppArmorException),
('pivot_root foo bar,', AppArmorException),
('pivot_root oldroot= ,', AppArmorException),
('pivot_root -> ,', AppArmorException),
)
def _run_test(self, rawrule, expected):
self.assertTrue(PivotRootRule.match(rawrule)) # the above invalid rules still match the main regex!
with self.assertRaises(expected):
PivotRootRule.create_instance(rawrule)
def test_invalid_rule_name(self):
self.assertFalse(PivotRootRule.match('pivot_rootbeer,'))
with self.assertRaises(AppArmorException):
PivotRootRule.create_instance('pivot_rootbeer,')
class PivotRootTestParseFromLog(PivotRootTest):
def test_pivot_root_from_log(self):
parser = ReadLog('', '', '')
event = 'type=AVC msg=audit(1409700678.384:547594): apparmor="DENIED" operation="pivotroot" profile="/home/ubuntu/bzr/apparmor/tests/regression/apparmor/pivot_root" name="/tmp/sdtest.21082-7446-EeefO6/new_root/" pid=21162 comm="pivot_root" srcname="/tmp/sdtest.21082-7446-EeefO6/new_root/put_old/"'
parsed_event = parser.parse_event(event)
self.assertEqual(parsed_event, {
'request_mask': None,
'denied_mask': None,
'error_code': 0,
'magic_token': 0,
'parent': 0,
'profile': '/home/ubuntu/bzr/apparmor/tests/regression/apparmor/pivot_root',
'operation': 'pivotroot',
'resource': None,
'info': None,
'aamode': 'REJECTING',
'time': 1409700678,
'active_hat': None,
'pid': 21162,
'task': 0,
'attr': None,
'name2': None,
'src_name': '/tmp/sdtest.21082-7446-EeefO6/new_root/put_old/',
'name': '/tmp/sdtest.21082-7446-EeefO6/new_root/',
'family': None,
'protocol': None,
'sock_type': None,
'class': None,
})
obj = PivotRootRule(parsed_event['src_name'], parsed_event['name'], PivotRootRule.ALL, log_event=parsed_event)
# audit allow deny comment oldroot all? newroot all? target all?
expected = exp(False, False, False, '', '/tmp/sdtest.21082-7446-EeefO6/new_root/put_old/', False, '/tmp/sdtest.21082-7446-EeefO6/new_root/', False, None, True)
self._compare_obj(obj, expected)
self.assertEqual(
obj.get_raw(1),
' pivot_root oldroot=/tmp/sdtest.21082-7446-EeefO6/new_root/put_old/ /tmp/sdtest.21082-7446-EeefO6/new_root/,')
class PivotRootFromInit(PivotRootTest):
tests = (
# PivotRootRule object audit allow deny comment oldroot all? newroot all? profile_name all?
(PivotRootRule('/oldroot', '/new/root', 'some_profile', deny=True), exp(False, False, True, '', '/oldroot', False, '/new/root', False, 'some_profile', False)),
(PivotRootRule('/oldroot', '/new/root', PivotRootRule.ALL, deny=True), exp(False, False, True, '', '/oldroot', False, '/new/root', False, None, True)),
(PivotRootRule('/oldroot', PivotRootRule.ALL, '/someprofile', deny=True), exp(False, False, True, '', '/oldroot', False, None, True, '/someprofile', False)),
(PivotRootRule(PivotRootRule.ALL, '/new/root', '/someprofile', deny=True), exp(False, False, True, '', None, True, '/new/root', False, '/someprofile', False)),
(PivotRootRule('/oldroot', PivotRootRule.ALL, PivotRootRule.ALL, deny=True), exp(False, False, True, '', '/oldroot', False, None, True, None, True)),
(PivotRootRule(PivotRootRule.ALL, '/new/root', PivotRootRule.ALL, deny=True), exp(False, False, True, '', None, True, '/new/root', False, None, True)),
(PivotRootRule(PivotRootRule.ALL, PivotRootRule.ALL, 'some_profile', deny=True), exp(False, False, True, '', None, True, None, True, 'some_profile', False)),
(PivotRootRule(PivotRootRule.ALL, PivotRootRule.ALL, PivotRootRule.ALL, deny=True), exp(False, False, True, '', None, True, None, True, None, True)),
)
def _run_test(self, obj, expected):
self._compare_obj(obj, expected)
class InvalidPivotRootInit(AATest):
tests = (
# (init params, expected exception)
(('', '/foo', 'bar'), AppArmorBug), # empty oldroot
(('/old', '', 'bar'), AppArmorBug), # empty newroot
(('/old', '/foo', '' ), AppArmorBug), # empty targetprof # noqa: E202
(('old', '/foo', 'bar'), AppArmorException), # oldroot is not a path
(('/old', 'foo', 'bar'), AppArmorException), # newroot is not a path
((None, '/foo', 'bar'), AppArmorBug), # wrong type
(('/old', None, 'bar'), AppArmorBug), #
(('/old', '/foo', None ), AppArmorBug), # noqa: E202
((dict(), '/foo', 'bar'), AppArmorBug), # wrong type
(('/old', dict(), 'bar'), AppArmorBug), #
(('/old', '/foo', dict()), AppArmorBug), #
)
def _run_test(self, params, expected):
with self.assertRaises(expected):
PivotRootRule(*params)
def test_missing_params_1(self):
with self.assertRaises(TypeError):
PivotRootRule()
def test_missing_params_2(self):
with self.assertRaises(TypeError):
PivotRootRule('/foo')
def test_missing_params_3(self):
with self.assertRaises(TypeError):
PivotRootRule('/foo', '/bar')
class InvalidPivotRootTest(AATest):
def _check_invalid_rawrule(self, rawrule):
obj = None
self.assertFalse(PivotRootRule.match(rawrule))
with self.assertRaises(AppArmorException):
obj = PivotRootRule.create_instance(rawrule)
self.assertIsNone(obj, 'PivotRootRule handed back an object unexpectedly')
def test_invalid_pivot_root_missing_comma(self):
self._check_invalid_rawrule('pivot_root') # missing comma
def test_invalid_non_PivotRootRule(self):
self._check_invalid_rawrule('dbus,') # not a pivot_root rule
def test_empty_data_1(self):
obj = PivotRootRule('/foo', '/bar', 'prof')
obj.oldroot = ''
# no oldroot set, and ALL not set
with self.assertRaises(AppArmorBug):
obj.get_clean(1)
def test_empty_data_2(self):
obj = PivotRootRule('/foo', '/bar', 'prof')
obj.newroot = ''
# no newroot set, and ALL not set
with self.assertRaises(AppArmorBug):
obj.get_clean(1)
def test_empty_data_3(self):
obj = PivotRootRule('/foo', '/bar', 'prof')
obj.profile_name = ''
# no profile_name set, and ALL not set
with self.assertRaises(AppArmorBug):
obj.get_clean(1)
class WritePivotRootTestAATest(AATest):
def _run_test(self, rawrule, expected):
self.assertTrue(PivotRootRule.match(rawrule))
obj = PivotRootRule.create_instance(rawrule)
clean = obj.get_clean()
raw = obj.get_raw()
self.assertEqual(expected.strip(), clean, 'unexpected clean rule')
self.assertEqual(rawrule.strip(), raw, 'unexpected raw rule')
tests = (
# raw rule clean rule
('pivot_root,', 'pivot_root,'),
(' pivot_root , # foo ', 'pivot_root, # foo'),
(' audit pivot_root /foo,', 'audit pivot_root /foo,'),
(' deny pivot_root /foo ,# foo bar', 'deny pivot_root /foo, # foo bar'),
(' deny pivot_root "/foo" ,# foo bar', 'deny pivot_root /foo, # foo bar'),
(' allow pivot_root ,# foo bar', 'allow pivot_root, # foo bar'),
(' pivot_root oldroot=/old , # foo ', 'pivot_root oldroot=/old, # foo'),
(' pivot_root oldroot="/old" , # foo ', 'pivot_root oldroot=/old, # foo'),
(' pivot_root oldroot=/old -> some_profile , ', 'pivot_root oldroot=/old -> some_profile,'),
(' pivot_root oldroot=/old /new -> some_profile , ', 'pivot_root oldroot=/old /new -> some_profile,'),
)
def test_write_manually(self):
obj = PivotRootRule('/old', '/new', 'target', allow_keyword=True)
expected = ' allow pivot_root oldroot=/old /new -> target,'
self.assertEqual(expected, obj.get_clean(2), 'unexpected clean rule')
self.assertEqual(expected, obj.get_raw(2), 'unexpected raw rule')
class PivotRootCoveredTest(AATest):
def _run_test(self, param, expected):
obj = PivotRootRule.create_instance(self.rule)
check_obj = PivotRootRule.create_instance(param)
self.assertTrue(PivotRootRule.match(param))
self.assertEqual(obj.is_equal(check_obj), expected[0], 'Mismatch in is_equal, expected {}'.format(expected[0]))
self.assertEqual(obj.is_equal(check_obj, True), expected[1], 'Mismatch in is_equal/strict, expected {}'.format(expected[1]))
self.assertEqual(obj.is_covered(check_obj), expected[2], 'Mismatch in is_covered, expected {}'.format(expected[2]))
self.assertEqual(obj.is_covered(check_obj, True, True), expected[3], 'Mismatch in is_covered/exact, expected {}'.format(expected[3]))
class PivotRootCoveredTest_01(PivotRootCoveredTest):
rule = 'pivot_root /new,'
tests = (
# rule equal strict equal covered covered exact
('pivot_root,', (False, False, False, False)),
('pivot_root /n*,', (False, False, False, False)),
('pivot_root oldroot=/old,', (False, False, False, False)),
('pivot_root /new,', (True, False, True, True)),
('pivot_root -> target,', (False, False, False, False)),
('pivot_root oldroot=/old /new,', (False, False, True, True)),
('pivot_root /new -> target,', (False, False, True, True)),
('pivot_root oldroot=/old -> target,', (False, False, False, False)),
('pivot_root oldroot=/old /new -> target,', (False, False, True, True)),
)
class PivotRootCoveredTest_02(PivotRootCoveredTest):
rule = 'audit pivot_root oldroot=/ol*,'
tests = (
# rule equal strict equal covered covered exact
('audit pivot_root,', (False, False, False, False)),
('audit pivot_root oldroot=/ol*,', (True, True, True, True)),
('audit pivot_root oldroot=/old,', (False, False, True, True)),
('audit pivot_root /new,', (False, False, False, False)),
('audit pivot_root -> target,', (False, False, False, False)),
('audit pivot_root oldroot=/old /new,', (False, False, True, True)),
('audit pivot_root /new -> target,', (False, False, False, False)),
('audit pivot_root oldroot=/old -> target,', (False, False, True, True)), # covered exact - really?
('audit pivot_root oldroot=/old /new -> target,', (False, False, True, True)), # covered exact - really?
)
class PivotRootCoveredTest_03(PivotRootCoveredTest):
rule = 'pivot_root -> target,'
tests = (
# rule equal strict equal covered covered exact
('pivot_root,', (False, False, False, False)),
('pivot_root oldroot=/ol*,', (False, False, False, False)),
('pivot_root oldroot=/old,', (False, False, False, False)),
('pivot_root /new,', (False, False, False, False)),
('pivot_root -> target,', (True, False, True, True)),
('pivot_root oldroot=/old /new,', (False, False, False, False)),
('pivot_root /new -> target,', (False, False, True, True)),
('pivot_root oldroot=/old -> target,', (False, False, True, True)),
('pivot_root oldroot=/old /new -> target,', (False, False, True, True)),
)
class PivotRootCoveredTest_04(PivotRootCoveredTest):
rule = 'deny pivot_root /foo,'
tests = (
# rule equal strict equal covered covered exact
(' deny pivot_root /foo,', (True, True, True, True)),
('audit deny pivot_root /foo,', (False, False, False, False)),
(' pivot_root /foo,', (False, False, False, False)), # XXX should covered be true here?
(' deny pivot_root /bar,', (False, False, False, False)),
(' deny pivot_root,', (False, False, False, False)),
)
class PivotRootCoveredTest_Invalid(AATest):
# TODO: should this be detected?
# def test_borked_obj_is_covered_1(self):
# obj = PivotRootRule.create_instance('pivot_root oldroot=/old /new -> target,')
# testobj = PivotRootRule('/old', '/foo', 'targetprof')
# testobj.oldrooot = None
# with self.assertRaises(AppArmorBug):
# obj.is_covered(testobj)
def test_borked_obj_is_covered_2(self):
obj = PivotRootRule.create_instance('pivot_root oldroot=/old /new -> target,')
testobj = PivotRootRule('/old', '/foo', 'targetprof')
testobj.newroot = ''
with self.assertRaises(AppArmorBug):
obj.is_covered(testobj)
# def test_borked_obj_is_covered_3(self):
# TODO: should this be detected?
# obj = PivotRootRule.create_instance('pivot_root oldroot=/old /new -> target,')
# testobj = PivotRootRule('/old', '/foo', 'targetprof')
# testobj.profile_name = ''
# with self.assertRaises(AppArmorBug):
# obj.is_covered(testobj)
def test_invalid_is_covered(self):
raw_rule = 'pivot_root oldroot=/old /new -> target,'
class SomeOtherClass(PivotRootRule):
pass
obj = PivotRootRule.create_instance(raw_rule)
testobj = SomeOtherClass.create_instance(raw_rule) # different type
with self.assertRaises(AppArmorBug):
obj.is_covered(testobj)
def test_invalid_is_equal_1(self):
raw_rule = 'pivot_root oldroot=/old /new -> target,'
class SomeOtherClass(PivotRootRule):
pass
obj = PivotRootRule.create_instance(raw_rule)
testobj = SomeOtherClass.create_instance(raw_rule) # different type
with self.assertRaises(AppArmorBug):
obj.is_equal(testobj)
# def test_invalid_is_equal_2(self):
# TODO: should this be detected?
# obj = PivotRootRule.create_instance('pivot_root oldroot=/old /new -> target,')
# testobj = PivotRootRule.create_instance('pivot_root oldroot=/old /new -> target,')
# testobj.all_oldroots = False # make testobj invalid (should trigger exception in _is_equal_aare())
# with self.assertRaises(AppArmorBug):
# obj.is_equal(testobj)
class PivotRootLogprofHeaderTest(AATest):
tests = (
('pivot_root,', [ _('Old root'), _('ALL'), _('New root'), _('ALL'), _('Target profile'), _('ALL')]), # noqa: E201
('pivot_root oldroot=/old,', [ _('Old root'), '/old', _('New root'), _('ALL'), _('Target profile'), _('ALL')]), # noqa: E201
('deny pivot_root,', [_('Qualifier'), 'deny', _('Old root'), _('ALL'), _('New root'), _('ALL'), _('Target profile'), _('ALL')]),
('allow pivot_root oldroot=/old,', [_('Qualifier'), 'allow', _('Old root'), '/old', _('New root'), _('ALL'), _('Target profile'), _('ALL')]),
('audit pivot_root /new,', [_('Qualifier'), 'audit', _('Old root'), _('ALL'), _('New root'), '/new', _('Target profile'), _('ALL')]),
('audit deny pivot_root /new -> target,', [_('Qualifier'), 'audit deny', _('Old root'), _('ALL'), _('New root'), '/new', _('Target profile'), 'target']),
('pivot_root oldroot=/old /new -> target,', [ _('Old root'), '/old', _('New root'), '/new', _('Target profile'), 'target']), # noqa: E201
)
def _run_test(self, params, expected):
obj = PivotRootRule.create_instance(params)
self.assertEqual(obj.logprof_header(), expected)
class PivotRootEditHeaderTest(AATest):
def _run_test(self, params, expected):
rule_obj = PivotRootRule.create_instance(params)
self.assertEqual(rule_obj.can_edit, True)
prompt, path_to_edit = rule_obj.edit_header()
self.assertEqual(path_to_edit, expected)
tests = (
('pivot_root oldroot=/old /foo/bar/baz -> target,', '/foo/bar/baz'),
('pivot_root /foo/**/baz,', '/foo/**/baz'),
('pivot_root /foo/** -> /bar,', '/foo/**'),
)
def test_edit_header_bare_pivot_root(self):
rule_obj = PivotRootRule.create_instance('pivot_root,')
self.assertEqual(rule_obj.can_edit, False)
with self.assertRaises(AppArmorBug):
rule_obj.edit_header()
class PivotRootValidateAndStoreEditTest(AATest):
def _run_test(self, params, expected):
rule_obj = PivotRootRule('/old/', '/foo/bar/baz', 'target', log_event=True)
self.assertEqual(rule_obj.validate_edit(params), expected)
rule_obj.store_edit(params)
self.assertEqual(rule_obj.get_raw(), 'pivot_root oldroot=/old/ ' + params + ' -> target,')
tests = (
# edited path match
('/foo/bar/baz', True),
('/foo/bar/*', True),
('/foo/bar/???', True),
('/foo/xy**', False),
('/foo/bar/baz/', False),
)
def test_validate_not_a_path(self):
rule_obj = PivotRootRule.create_instance('pivot_root /foo/bar/baz,')
with self.assertRaises(AppArmorException):
rule_obj.validate_edit('foo/bar/baz')
with self.assertRaises(AppArmorException):
rule_obj.store_edit('foo/bar/baz')
def test_validate_edit_bare_pivot_root(self):
rule_obj = PivotRootRule.create_instance('pivot_root,')
self.assertEqual(rule_obj.can_edit, False)
with self.assertRaises(AppArmorBug):
rule_obj.validate_edit('/foo/bar')
with self.assertRaises(AppArmorBug):
rule_obj.store_edit('/foo/bar')
# --- tests for PivotRootRuleset --- #
class PivotRootRulesTest(AATest):
def test_empty_ruleset(self):
ruleset = PivotRootRuleset()
ruleset_2 = PivotRootRuleset()
self.assertEqual([], ruleset.get_raw(2))
self.assertEqual([], ruleset.get_clean(2))
self.assertEqual([], ruleset_2.get_raw(2))
self.assertEqual([], ruleset_2.get_clean(2))
# test __repr__() for empty ruleset
self.assertEqual(str(ruleset), '<PivotRootRuleset (empty) />')
def test_ruleset_1(self):
ruleset = PivotRootRuleset()
rules = (
'pivot_root oldroot=/foo,',
'pivot_root /new,',
)
expected_raw = [
'pivot_root oldroot=/foo,',
'pivot_root /new,',
'',
]
expected_clean = [
'pivot_root /new,',
'pivot_root oldroot=/foo,',
'',
]
for rule in rules:
ruleset.add(PivotRootRule.create_instance(rule))
self.assertEqual(expected_raw, ruleset.get_raw())
self.assertEqual(expected_clean, ruleset.get_clean())
# test __repr__() for non-empty ruleset
self.assertEqual(
str(ruleset), '<PivotRootRuleset>\n pivot_root oldroot=/foo,\n pivot_root /new,\n</PivotRootRuleset>')
class PivotRootGlobTestAATest(AATest):
def test_glob(self):
glob_list = [(
'pivot_root /foo/bar,',
'pivot_root /foo/*,',
'pivot_root /**,',
)]
for globs in glob_list:
for i in range(len(globs) - 1):
rule = PivotRootRule.create_instance(globs[i])
rule.glob()
self.assertEqual(rule.get_clean(), globs[i + 1])
def test_glob_all(self):
glob_list = [(
'pivot_root,',
'pivot_root,',
)]
for globs in glob_list:
for i in range(len(globs) - 1):
rule = PivotRootRule.create_instance(globs[i])
rule.glob()
self.assertEqual(rule.get_clean(), globs[i + 1])
# def test_glob_ext(self):
# # rule = PivotRootRule.create_instance('pivot_root /foo/bar,')
# with self.assertRaises(NotImplementedError):
# # get_glob_ext is not available for pivot_root rules
# self.ruleset.get_glob_ext('pivot_root /foo,')
# class PivotRootDeleteTestAATest(AATest):
# pass
setup_all_loops(__name__)
if __name__ == '__main__':
unittest.main(verbosity=1)

View File

@ -1,33 +0,0 @@
#! /usr/bin/python3
# ------------------------------------------------------------------
#
# Copyright (C) 2014 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License published by the Free Software Foundation.
#
# ------------------------------------------------------------------
import unittest
import apparmor.aa as aa
from common_test import AAParseTest, setup_aa, setup_regex_tests
class AAParsePivotRootTest(AAParseTest):
def setUp(self):
self.parse_function = aa.parse_pivot_root_rule
tests = (
('pivot_root,', 'pivot_root base keyword'),
('pivot_root /old,', 'pivot_root oldroot rule'),
('pivot_root /old /new,', 'pivot_root old and new root rule'),
('pivot_root /old /new -> /usr/bin/child,', 'pivot_root child rule'),
)
setup_aa(aa)
if __name__ == '__main__':
setup_regex_tests(AAParsePivotRootTest)
unittest.main(verbosity=1)

View File

@ -16,6 +16,7 @@ from apparmor.common import AppArmorBug, AppArmorException
from apparmor.regex import ( from apparmor.regex import (
RE_PROFILE_CAP, RE_PROFILE_DBUS, RE_PROFILE_MOUNT, RE_PROFILE_PTRACE, RE_PROFILE_SIGNAL, RE_PROFILE_CAP, RE_PROFILE_DBUS, RE_PROFILE_MOUNT, RE_PROFILE_PTRACE, RE_PROFILE_SIGNAL,
RE_PROFILE_START, parse_profile_start_line, re_match_include, RE_PROFILE_UNIX, RE_PROFILE_START, parse_profile_start_line, re_match_include, RE_PROFILE_UNIX,
RE_PROFILE_PIVOT_ROOT,
re_match_include_parse, strip_parenthesis, strip_quotes) re_match_include_parse, strip_parenthesis, strip_quotes)
from common_test import AATest, setup_aa, setup_all_loops from common_test import AATest, setup_aa, setup_all_loops
@ -313,15 +314,15 @@ class AARegexPivotRoot(AARegexTest):
"""Tests for RE_PROFILE_PIVOT_ROOT""" """Tests for RE_PROFILE_PIVOT_ROOT"""
def AASetup(self): def AASetup(self):
self.regex = aa.RE_PROFILE_PIVOT_ROOT self.regex = RE_PROFILE_PIVOT_ROOT
tests = ( tests = (
(' pivot_root,', (None, None, 'pivot_root,', None)), (' pivot_root,', (None, None, 'pivot_root,', None, None)),
(' audit pivot_root,', ('audit', None, 'pivot_root,', None)), (' audit pivot_root,', ('audit', None, 'pivot_root,', None, None)),
(' pivot_root oldroot=/new/old,', (None, None, 'pivot_root oldroot=/new/old,', None)), (' pivot_root oldroot=/new/old,', (None, None, 'pivot_root oldroot=/new/old,', 'oldroot=/new/old', None)),
(' pivot_root oldroot=/new/old /new,', (None, None, 'pivot_root oldroot=/new/old /new,', None)), (' pivot_root oldroot=/new/old /new,', (None, None, 'pivot_root oldroot=/new/old /new,', 'oldroot=/new/old /new', None)),
(' pivot_root oldroot=/new/old /new -> child,', (None, None, 'pivot_root oldroot=/new/old /new -> child,', None)), (' pivot_root oldroot=/new/old /new -> child,', (None, None, 'pivot_root oldroot=/new/old /new -> child,', 'oldroot=/new/old /new -> child', None)),
(' audit pivot_root oldroot=/new/old /new -> child,', ('audit', None, 'pivot_root oldroot=/new/old /new -> child,', None)), (' audit pivot_root oldroot=/new/old /new -> child,', ('audit', None, 'pivot_root oldroot=/new/old /new -> child,', 'oldroot=/new/old /new -> child', None)),
('pivot_root', False), # comma missing ('pivot_root', False), # comma missing