diff --git a/parser/tst/caching.py b/parser/tst/caching.py index 4a56aab41..bbcb710cd 100755 --- a/parser/tst/caching.py +++ b/parser/tst/caching.py @@ -567,6 +567,7 @@ def main(): return rc + if __name__ == "__main__": rc = main() exit(rc) diff --git a/parser/tst/errors.py b/parser/tst/errors.py index 85e7d0d91..389a32a0d 100755 --- a/parser/tst/errors.py +++ b/parser/tst/errors.py @@ -21,6 +21,7 @@ import testlib config = None + class AAErrorTests(testlib.AATestTemplate): def setUp(self): self.maxDiff = None diff --git a/parser/tst/gen-dbus.py b/parser/tst/gen-dbus.py index 90fa377f1..544d0f64a 100755 --- a/parser/tst/gen-dbus.py +++ b/parser/tst/gen-dbus.py @@ -18,7 +18,8 @@ from testlib import write_file -def get_rule (quantifier, perms, session, name, path, interface, member, peer): + +def get_rule(quantifier, perms, session, name, path, interface, member, peer): result = ' ' @@ -30,6 +31,7 @@ def get_rule (quantifier, perms, session, name, path, interface, member, peer): return result + def gen_file(test, xres, quantifier, perms, session, name, path, interface, member, peer): global count @@ -46,7 +48,8 @@ def gen_file(test, xres, quantifier, perms, session, name, path, interface, memb count += 1 -def gen_files (test, xres, quantifiers, perms, sessions, names, paths, interfaces, members, peers): + +def gen_files(test, xres, quantifiers, perms, sessions, names, paths, interfaces, members, peers): for quantifier in quantifiers: for perm in perms: for session in sessions: @@ -57,7 +60,8 @@ def gen_files (test, xres, quantifiers, perms, sessions, names, paths, interface for peer in peers: gen_file(test, xres, quantifier, perm, session, name, path, interface, member, peer) -count=0 + +count = 0 quantifier = ('', 'deny', 'audit') session = ('', 'bus=session', 'bus=system', 'bus=accessibility') diff --git a/parser/tst/gen-xtrans.py b/parser/tst/gen-xtrans.py index 0ff169f16..70b725052 100755 --- a/parser/tst/gen-xtrans.py +++ b/parser/tst/gen-xtrans.py @@ -60,6 +60,7 @@ qualifiers = ("", "owner") count = 0 + def gen_list(): output = [] for trans in trans_types: @@ -71,6 +72,7 @@ def gen_list(): return output + def test_gen_list(): ''' test if gen_list returns the expected output ''' @@ -80,6 +82,7 @@ def test_gen_list(): if actual != expected: raise Exception("gen_list produced unexpected result, expected %s, got %s" % (expected, actual)) + def build_rule(leading, qual, name, perm, target): rule = '' @@ -95,7 +98,8 @@ def build_rule(leading, qual, name, perm, target): return rule -def gen_file (name, xres, leading1, qual1, rule1, perm1, target1, leading2, qual2, rule2, perm2, target2): + +def gen_file(name, xres, leading1, qual1, rule1, perm1, target1, leading2, qual2, rule2, perm2, target2): global count count += 1 @@ -144,23 +148,27 @@ def gen_files(name, rule1, rule2, default): gen_file(file, xres, 0, q, rule1, i, t, 0, r, rule2, j, u) + def gen_conflicting_x(): gen_files("conflict", "/bin/cat", "/bin/cat", "FAIL") + def gen_overlap_re_exact(): gen_files("exact", "/bin/cat", "/bin/*", "PASS") + # we currently don't support this, once supported change to "PASS" def gen_dominate_re_re(): gen_files("dominate", "/bin/*", "/bin/**", "FAIL") + def gen_ambiguous_re_re(): gen_files("ambiguous", "/bin/a*", "/bin/*b", "FAIL") # test that rules that lead with permissions don't conflict with # the same rule using trailing permissions. -def gen_leading_perms (name, rule1, rule2): +def gen_leading_perms(name, rule1, rule2): perms = gen_list() for i in perms: @@ -171,6 +179,7 @@ def gen_leading_perms (name, rule1, rule2): file = prefix_leading + '/' + name + '-' + q + i + t + ".sd" gen_file(file, "PASS", 0, q, rule1, i, t, 1, q, rule2, i, t) + # test for rules with leading safe or unsafe keywords. # check they are equivalent to their counterpart, # or if $invert that they properly conflict with their counterpart @@ -216,4 +225,4 @@ gen_safe_perms("overlap", "PASS", "inv", "/*", "/bin/cat") gen_safe_perms("dominate", "FAIL", "inv", "/**", "/*") gen_safe_perms("ambiguous", "FAIL", "inv", "/a*", "/*b") -print ("Generated %s xtransition interaction tests" % count) +print("Generated %s xtransition interaction tests" % count) diff --git a/parser/tst/mk_features_file.py b/parser/tst/mk_features_file.py index f94bf1ce4..39ba31261 100755 --- a/parser/tst/mk_features_file.py +++ b/parser/tst/mk_features_file.py @@ -15,7 +15,8 @@ from argparse import ArgumentParser import os from sys import stderr, exit -DEFAULT_FEATURES_DIR='/sys/kernel/security/apparmor/features' +DEFAULT_FEATURES_DIR = '/sys/kernel/security/apparmor/features' + def main(): p = ArgumentParser() @@ -33,5 +34,6 @@ def main(): return 0 + if __name__ == "__main__": exit(main()) diff --git a/parser/tst/valgrind_simple.py b/parser/tst/valgrind_simple.py index f60e698ed..a4c9bbf92 100755 --- a/parser/tst/valgrind_simple.py +++ b/parser/tst/valgrind_simple.py @@ -50,8 +50,10 @@ class AAParserValgrindTests(testlib.AATestTemplate): command.extend(parser_args) command.append(testname) rc, output = self.run_cmd(command, timeout=120) - self.assertNotIn(rc, failure_rc, - "valgrind returned error code %d, gave the following output\n%s\ncommand run: %s" % (rc, output, " ".join(command))) + self.assertNotIn( + rc, failure_rc, + "valgrind returned error code %d, gave the following output\n%s\ncommand run: %s" + % (rc, output, " ".join(command))) def find_testcases(testdir): @@ -122,6 +124,7 @@ def main(): return rc + if __name__ == "__main__": rc = main() exit(rc) diff --git a/utils/aa-audit b/utils/aa-audit index 316befa9e..d3741a046 100755 --- a/utils/aa-audit +++ b/utils/aa-audit @@ -35,4 +35,3 @@ args = parser.parse_args() tool = apparmor.tools.aa_tools('audit', args) tool.cmd_audit() - diff --git a/utils/aa-complain b/utils/aa-complain index 11d9bf8d0..7dbcf93fc 100755 --- a/utils/aa-complain +++ b/utils/aa-complain @@ -32,5 +32,5 @@ parser.add_argument('--configdir', type=str, help=argparse.SUPPRESS) args = parser.parse_args() tool = apparmor.tools.aa_tools('complain', args) -#print(args) +# print(args) tool.cmd_complain() diff --git a/utils/aa-disable b/utils/aa-disable index ca18f4401..3023ed4fe 100755 --- a/utils/aa-disable +++ b/utils/aa-disable @@ -34,4 +34,3 @@ args = parser.parse_args() tool = apparmor.tools.aa_tools('disable', args) tool.cmd_disable() - diff --git a/utils/aa-easyprof b/utils/aa-easyprof index a58245470..7eec8e950 100755 --- a/utils/aa-easyprof +++ b/utils/aa-easyprof @@ -54,8 +54,8 @@ if __name__ == "__main__": os.strerror(e.errno), e.errno)) profiles = apparmor.easyprof.parse_manifest(manifest, opt) - else: # fake up a tuple list when processing command line args - profiles.append( (binary, opt) ) + else: # fake up a tuple list when processing command line args + profiles.append((binary, opt)) count = 0 for (binary, options) in profiles: diff --git a/utils/aa-genprof b/utils/aa-genprof index edc2a717c..7f7a3a9ec 100755 --- a/utils/aa-genprof +++ b/utils/aa-genprof @@ -32,19 +32,22 @@ enable_aa_exception_handler() from apparmor.translations import init_translation _ = init_translation() + def sysctl_read(path): value = None with open(path, 'r') as f_in: value = int(f_in.readline()) return value + def sysctl_write(path, value): if value is None: - warn('Not writing invalid value "None" to %s'%path) + warn('Not writing invalid value "None" to %s' % path) return with open(path, 'w') as f_out: f_out.write(str(value)) + def last_audit_entry_time(): out = subprocess.check_output(['tail', '-1', apparmor.logfile]) logmark = None @@ -55,6 +58,7 @@ def last_audit_entry_time(): logmark = '' return logmark + def restore_ratelimit(): try: sysctl_write(ratelimit_sysctl, ratelimit_saved) @@ -62,6 +66,7 @@ def restore_ratelimit(): if ratelimit_saved != sysctl_read(ratelimit_sysctl): raise # happens only if a) running under lxd and b) something changed the ratelimit since starting aa-genprof + parser = argparse.ArgumentParser(description=_('Generate profile for the given program')) parser.add_argument('-d', '--dir', type=str, help=_('path to profiles')) parser.add_argument('-f', '--file', type=str, help=_('path to logfile')) @@ -83,7 +88,7 @@ if not aa_mountpoint: raise AppArmorException(_('It seems AppArmor was not started. Please enable AppArmor and try again.')) program = None -#if os.path.exists(apparmor.which(profiling.strip())): +# if os.path.exists(apparmor.which(profiling.strip())): if os.path.exists(profiling): program = apparmor.get_full_path(profiling) else: @@ -94,9 +99,14 @@ else: if not program or not os.path.exists(program): if '/' not in profiling: - raise AppArmorException(_("Can't find %(profiling)s in the system path list. If the name of the application\nis correct, please run 'which %(profiling)s' as a user with correct PATH\nenvironment set up in order to find the fully-qualified path and\nuse the full path as parameter.") % { 'profiling': profiling }) + raise AppArmorException( + _("Can't find %(profiling)s in the system path list. If the name of the application\n" + "is correct, please run 'which %(profiling)s' as a user with correct PATH\n" + "environment set up in order to find the fully-qualified path and\n" + "use the full path as parameter.") + % {'profiling': profiling}) else: - raise AppArmorException(_('%s does not exists, please double-check the path.') %profiling) + raise AppArmorException(_('%s does not exists, please double-check the path.') % profiling) # Check if the program has been marked as not allowed to have a profile apparmor.check_qualifiers(program) @@ -130,7 +140,11 @@ except PermissionError: # will fail in lxd atexit.register(restore_ratelimit) -aaui.UI_Info(_('\nBefore you begin, you may wish to check if a\nprofile already exists for the application you\nwish to confine. See the following wiki page for\nmore information:')+'\nhttps://gitlab.com/apparmor/apparmor/wikis/Profiles') +aaui.UI_Info( + _('\nBefore you begin, you may wish to check if a\n' + 'profile already exists for the application you\n' + 'wish to confine. See the following wiki page for\n' + 'more information:') + '\nhttps://gitlab.com/apparmor/apparmor/wikis/Profiles') syslog = True logmark = '' @@ -142,7 +156,7 @@ if os.path.exists('/var/log/audit/audit.log'): while not done_profiling: if syslog: logmark = 'logmark-%s' % str(int(time.time())) # unix timestamp, seconds only - t=subprocess.call([apparmor.logger_path(), '-p', 'kern.warn', 'GenProf: %s'%logmark]) + t = subprocess.call([apparmor.logger_path(), '-p', 'kern.warn', 'GenProf: %s' % logmark]) else: logmark = last_audit_entry_time() @@ -151,7 +165,14 @@ while not done_profiling: q.headers = [_('Profiling'), program] q.functions = ['CMD_SCAN', 'CMD_FINISHED'] q.default = 'CMD_SCAN' - q.explanation = _('Please start the application to be profiled in\nanother window and exercise its functionality now.\n\nOnce completed, select the "Scan" option below in \norder to scan the system logs for AppArmor events. \n\nFor each AppArmor event, you will be given the \nopportunity to choose whether the access should be \nallowed or denied.') + q.explanation = \ + _('Please start the application to be profiled in\n' + 'another window and exercise its functionality now.\n\n' + 'Once completed, select the "Scan" option below in \n' + 'order to scan the system logs for AppArmor events. \n\n' + 'For each AppArmor event, you will be given the \n' + 'opportunity to choose whether the access should be \n' + 'allowed or denied.') ans, arg = q.promptUser('noexit') if ans == 'CMD_SCAN': @@ -165,6 +186,8 @@ for p in sorted(apparmor.helpers.keys()): apparmor.reload(p) aaui.UI_Info(_('\nReloaded AppArmor profiles in enforce mode.')) -aaui.UI_Info(_('\nPlease consider contributing your new profile!\nSee the following wiki page for more information:')+'\nhttps://gitlab.com/apparmor/apparmor/wikis/Profiles\n') -aaui.UI_Info(_('Finished generating profile for %s.')%program) +aaui.UI_Info(_('\nPlease consider contributing your new profile!\n' + 'See the following wiki page for more information:') + + '\nhttps://gitlab.com/apparmor/apparmor/wikis/Profiles\n') +aaui.UI_Info(_('Finished generating profile for %s.') % program) sys.exit(0) diff --git a/utils/aa-logprof b/utils/aa-logprof index 55093240c..f6059cc36 100755 --- a/utils/aa-logprof +++ b/utils/aa-logprof @@ -52,4 +52,3 @@ apparmor.loadincludes() apparmor.read_profiles(True) apparmor.do_logprof_pass(logmark) - diff --git a/utils/aa-mergeprof b/utils/aa-mergeprof index 7160f63f0..8c614f2b7 100755 --- a/utils/aa-mergeprof +++ b/utils/aa-mergeprof @@ -22,7 +22,6 @@ import apparmor.cleanprofile as cleanprofile import apparmor.ui as aaui - # setup exception handling from apparmor.fail import enable_aa_exception_handler enable_aa_exception_handler() @@ -34,7 +33,7 @@ _ = init_translation() parser = argparse.ArgumentParser(description=_('Merge the given profiles into /etc/apparmor.d/ (or the directory specified with -d)')) parser.add_argument('files', nargs='+', type=str, help=_('Profile(s) to merge')) parser.add_argument('-d', '--dir', type=str, help=_('path to profiles')) -#parser.add_argument('-a', '--auto', action='store_true', help=_('Automatically merge profiles, exits incase of *x conflicts')) +# parser.add_argument('-a', '--auto', action='store_true', help=_('Automatically merge profiles, exits incase of *x conflicts')) parser.add_argument('--configdir', type=str, help=argparse.SUPPRESS) args = parser.parse_args() @@ -44,6 +43,7 @@ apparmor.aa.init_aa(confdir=args.configdir, profiledir=args.dir) profiles = args.files + def find_profiles_from_files(files): profile_to_filename = dict() for file_name in files: @@ -54,6 +54,7 @@ def find_profiles_from_files(files): return profile_to_filename + def find_files_from_profiles(profiles): profile_to_filename = dict() apparmor.aa.read_profiles() @@ -65,6 +66,7 @@ def find_files_from_profiles(profiles): return profile_to_filename + def main(): base_profile_to_file = find_profiles_from_files(profiles) @@ -81,9 +83,10 @@ def main(): apparmor.aa.reset_aa() + def act(user_file, base_file, merging_profile): mergeprofiles = Merge(user_file, base_file) - #Get rid of common/superfluous stuff + # Get rid of common/superfluous stuff mergeprofiles.clear_common() mergeprofiles.ask_merge_questions() @@ -91,22 +94,23 @@ def act(user_file, base_file, merging_profile): apparmor.aa.changed[merging_profile] = True # force asking to save the profile apparmor.aa.save_profiles(True) + class Merge(object): def __init__(self, user, base): - #Read and parse base profile and save profile data, include data from it and reset them + # Read and parse base profile and save profile data, include data from it and reset them apparmor.aa.read_profile(base, True) self.base = cleanprofile.Prof(base) apparmor.aa.reset_aa() - #Read and parse user profile + # Read and parse user profile apparmor.aa.read_profile(user, True) self.user = cleanprofile.Prof(user) def clear_common(self): deleted = 0 - #Remove off the parts in base profile which are common/superfluous from user profile + # Remove off the parts in base profile which are common/superfluous from user profile user_base = cleanprofile.CleanProf(False, self.user, self.base) deleted += user_base.compare_profiles() @@ -129,5 +133,6 @@ class Merge(object): apparmor.aa.ask_the_questions(log_dict) + if __name__ == '__main__': main() diff --git a/utils/aa-notify b/utils/aa-notify index 650281682..438cf241f 100755 --- a/utils/aa-notify +++ b/utils/aa-notify @@ -49,6 +49,7 @@ from apparmor.translations import init_translation import LibAppArmor # C-library to parse one log line + def get_user_login(): '''Portable function to get username. Should not trigger any "OSError: [Errno 25] Inappropriate ioctl for device" errors in Giltab-CI''' @@ -61,7 +62,6 @@ def get_user_login(): return username - def format_event(event, logsource): output = [] @@ -143,7 +143,7 @@ def show_entries_since_last_login(logfile, username=get_user_login()): if args.verbose: print(_('Showing entries since {} logged in').format(username)) - print() # Newline + print() # Newline epoch_since = get_last_login_timestamp(username) if epoch_since == 0: print(_('ERROR: Could not find last login'), file=sys.stderr) @@ -313,6 +313,7 @@ def drop_privileges(): os.setegid(int(next_gid)) os.seteuid(int(next_uid)) + def raise_privileges(): '''If was running as user with saved user ID 0, raise back to root privileges''' @@ -323,6 +324,7 @@ def raise_privileges(): # os.setgid(int(next_gid)) os.seteuid(original_effective_user) + def read_notify_conf(path, shell_config): try: shell_config.CONF_DIR = path @@ -332,6 +334,7 @@ def read_notify_conf(path, shell_config): except FileNotFoundError: return {} + def main(): ''' Main function of aa-notify that parses command line diff --git a/utils/aa-unconfined b/utils/aa-unconfined index f7f67791a..43f0b7954 100755 --- a/utils/aa-unconfined +++ b/utils/aa-unconfined @@ -21,7 +21,7 @@ import sys import apparmor.aa as aa import apparmor.ui as ui -from apparmor.common import AppArmorException, open_file_read +from apparmor.common import AppArmorException, open_file_read # setup exception handling from apparmor.fail import enable_aa_exception_handler diff --git a/utils/apparmor/aa.py b/utils/apparmor/aa.py index 15111f50d..c9262f441 100644 --- a/utils/apparmor/aa.py +++ b/utils/apparmor/aa.py @@ -32,19 +32,18 @@ from copy import deepcopy from apparmor.aare import AARE -from apparmor.common import (AppArmorException, AppArmorBug, cmd, is_skippable_file, open_file_read, valid_path, hasher, - combine_profname, split_name, open_file_write, DebugLogger) +from apparmor.common import ( + AppArmorException, AppArmorBug, cmd, is_skippable_file, open_file_read, valid_path, hasher, + combine_profname, split_name, open_file_write, DebugLogger) import apparmor.ui as aaui -from apparmor.regex import (RE_PROFILE_START, RE_PROFILE_END, - RE_PROFILE_CONDITIONAL, - RE_PROFILE_CONDITIONAL_VARIABLE, RE_PROFILE_CONDITIONAL_BOOLEAN, - RE_PROFILE_CHANGE_HAT, - RE_PROFILE_HAT_DEF, RE_PROFILE_MOUNT, - RE_PROFILE_PIVOT_ROOT, - RE_PROFILE_UNIX, RE_RULE_HAS_COMMA, RE_HAS_COMMENT_SPLIT, - parse_profile_start_line, re_match_include ) +from apparmor.regex import ( + RE_PROFILE_START, RE_PROFILE_END, RE_PROFILE_CONDITIONAL, + RE_PROFILE_CONDITIONAL_VARIABLE, RE_PROFILE_CONDITIONAL_BOOLEAN, + RE_PROFILE_CHANGE_HAT, RE_PROFILE_HAT_DEF, RE_PROFILE_MOUNT, + RE_PROFILE_PIVOT_ROOT, RE_PROFILE_UNIX, RE_RULE_HAS_COMMA, RE_HAS_COMMENT_SPLIT, + parse_profile_start_line, re_match_include) from apparmor.profile_list import ProfileList, preamble_ruletypes @@ -52,15 +51,15 @@ from apparmor.profile_storage import ProfileStorage, add_or_remove_flag, ruletyp import apparmor.rules as aarules -from apparmor.rule.abi import AbiRule -from apparmor.rule.capability import CapabilityRule -from apparmor.rule.change_profile import ChangeProfileRule -from apparmor.rule.dbus import DbusRule -from apparmor.rule.file import FileRule -from apparmor.rule.include import IncludeRule -from apparmor.rule.network import NetworkRule -from apparmor.rule.ptrace import PtraceRule -from apparmor.rule.signal import SignalRule +from apparmor.rule.abi import AbiRule +from apparmor.rule.capability import CapabilityRule +from apparmor.rule.change_profile import ChangeProfileRule +from apparmor.rule.dbus import DbusRule +from apparmor.rule.file import FileRule +from apparmor.rule.include import IncludeRule +from apparmor.rule.network import NetworkRule +from apparmor.rule.ptrace import PtraceRule +from apparmor.rule.signal import SignalRule # setup module translations from apparmor.translations import init_translation @@ -105,6 +104,7 @@ created = [] helpers = dict() # Preserve this between passes # was our ### logprof ends + def reset_aa(): ''' Reset the most important global variables @@ -118,14 +118,17 @@ def reset_aa(): active_profiles = ProfileList() original_aa = hasher() + def on_exit(): """Shutdowns the logger and records exit if debugging enabled""" debug_logger.debug('Exiting..') debug_logger.shutdown() + # Register the on_exit method with atexit atexit.register(on_exit) + def check_for_LD_XXX(file): """Returns True if specified program contains references to LD_PRELOAD or LD_LIBRARY_PATH to give the Px/Ux code better suggestions""" @@ -141,6 +144,7 @@ def check_for_LD_XXX(file): return True return False + def fatal_error(message): # Get the traceback to the message tb_stack = traceback.format_list(traceback.extract_stack()) @@ -153,6 +157,7 @@ def fatal_error(message): aaui.UI_Important(message) sys.exit(1) + def check_for_apparmor(filesystem='/proc/filesystems', mounts='/proc/mounts'): """Finds and returns the mountpoint for apparmor None otherwise""" support_securityfs = False @@ -176,6 +181,7 @@ def check_for_apparmor(filesystem='/proc/filesystems', mounts='/proc/mounts'): break return aa_mountpoint + def get_full_path(original_path): """Return the full path after resolving any symlinks""" path = original_path @@ -196,6 +202,7 @@ def get_full_path(original_path): path = os.path.join(direc, link) return os.path.realpath(path) + def find_executable(bin_path): """Returns the full executable path for the given executable, None otherwise""" full_bin = None @@ -210,6 +217,7 @@ def find_executable(bin_path): return full_bin return None + def get_profile_filename_from_profile_name(profile, get_new=False): """Returns the full profile name for the given profile name""" @@ -220,6 +228,7 @@ def get_profile_filename_from_profile_name(profile, get_new=False): if get_new: return get_new_profile_filename(profile) + def get_profile_filename_from_attachment(profile, get_new=False): """Returns the full profile name for the given attachment""" @@ -230,6 +239,7 @@ def get_profile_filename_from_attachment(profile, get_new=False): if get_new: return get_new_profile_filename(profile) + def get_new_profile_filename(profile): '''Compose filename for a new profile''' if profile.startswith('/'): @@ -241,6 +251,7 @@ def get_new_profile_filename(profile): filename = os.path.join(profile_dir, filename) return filename + def name_to_prof_filename(prof_filename): """Returns the profile""" if prof_filename.startswith(profile_dir): @@ -255,6 +266,7 @@ def name_to_prof_filename(prof_filename): return None, None + def complain(path): """Sets the profile to complain mode if it exists""" prof_filename, name = name_to_prof_filename(path) @@ -262,6 +274,7 @@ def complain(path): fatal_error(_("Can't find %s") % path) set_complain(prof_filename, name) + def enforce(path): """Sets the profile to enforce mode if it exists""" prof_filename, name = name_to_prof_filename(path) @@ -269,6 +282,7 @@ def enforce(path): fatal_error(_("Can't find %s") % path) set_enforce(prof_filename, name) + def set_complain(filename, program): """Sets the profile to complain mode""" aaui.UI_Info(_('Setting %s to complain mode.') % (filename if program is None else program)) @@ -278,6 +292,7 @@ def set_complain(filename, program): change_profile_flags(filename, program, ['enforce', 'kill', 'unconfined', 'prompt'], False) # remove conflicting mode flags change_profile_flags(filename, program, 'complain', True) + def set_enforce(filename, program): """Sets the profile to enforce mode""" aaui.UI_Info(_('Setting %s to enforce mode.') % (filename if program is None else program)) @@ -285,22 +300,24 @@ def set_enforce(filename, program): delete_symlink('disable', filename) change_profile_flags(filename, program, ['complain', 'kill', 'unconfined', 'prompt'], False) # remove conflicting and complain mode flags + def delete_symlink(subdir, filename): path = filename link = re.sub('^%s' % profile_dir, '%s/%s' % (profile_dir, subdir), path) if link != path and os.path.islink(link): os.remove(link) + def create_symlink(subdir, filename): path = filename bname = os.path.basename(filename) if not bname: raise AppArmorException(_('Unable to find basename for %s.') % filename) - #print(filename) + # print(filename) link = re.sub('^%s' % profile_dir, '%s/%s' % (profile_dir, subdir), path) - #print(link) - #link = link + '/%s'%bname - #print(link) + # print(link) + # link = link + '/%s'%bname + # print(link) symlink_dir = os.path.dirname(link) if not os.path.exists(symlink_dir): # If the symlink directory does not exist create it @@ -310,7 +327,10 @@ def create_symlink(subdir, filename): try: os.symlink(filename, link) except: - raise AppArmorException(_('Could not create %(link)s symlink to %(file)s.') % { 'link': link, 'file': filename }) + raise AppArmorException( + _('Could not create %(link)s symlink to %(file)s.') + % {'link': link, 'file': filename}) + def head(file): """Returns the first/head line of the file""" @@ -325,6 +345,7 @@ def head(file): else: raise AppArmorException(_('Unable to read first line from %s: File Not Found') % file) + def get_output(params): '''Runs the program with the given args and returns the return code and stdout (as list of lines)''' try: @@ -332,7 +353,9 @@ def get_output(params): output = subprocess.check_output(params) # nosec ret = 0 except OSError as e: - raise AppArmorException(_("Unable to fork: %(program)s\n\t%(error)s") % { 'program': params[0], 'error': str(e) }) + raise AppArmorException( + _("Unable to fork: %(program)s\n\t%(error)s") + % {'program': params[0], 'error': str(e)}) except subprocess.CalledProcessError as e: # If exit code != 0 output = e.output ret = e.returncode @@ -345,6 +368,7 @@ def get_output(params): return (ret, output) + def get_reqs(file): """Returns a list of paths from ldd output""" pattern1 = re.compile('^\s*\S+ => (\/\S+)') @@ -373,6 +397,7 @@ def get_reqs(file): reqs.append(match.groups()[0]) return reqs + def handle_binfmt(profile, path): """Modifies the profile to add the requirements""" reqs_processed = dict() @@ -395,6 +420,7 @@ def handle_binfmt(profile, path): profile['file'].add(library_rule) + def get_interpreter_and_abstraction(exec_target): '''Check if exec_target is a script. If a hashbang is found, check if we have an abstraction for it. @@ -433,6 +459,7 @@ def get_interpreter_and_abstraction(exec_target): return interpreter_path, abstraction + def create_new_profile(localfile, is_stub=False): local_profile = {} local_profile[localfile] = ProfileStorage('NEW', localfile, 'create_new_profile()') @@ -478,6 +505,7 @@ def create_new_profile(localfile, is_stub=False): debug_logger.debug("Profile for %s:\n\t%s" % (localfile, local_profile.__str__())) return local_profile + def delete_profile(local_prof): """Deletes the specified file from the disk and remove it from our list""" profile_file = get_profile_filename_from_profile_name(local_prof, True) @@ -486,7 +514,8 @@ def delete_profile(local_prof): if aa.get(local_prof, False): aa.pop(local_prof) - #prof_unload(local_prof) + # prof_unload(local_prof) + def confirm_and_abort(): ans = aaui.UI_YesNo(_('Are you sure you want to abandon this set of profile changes and exit?'), 'n') @@ -496,6 +525,7 @@ def confirm_and_abort(): delete_profile(prof) sys.exit(0) + def get_profile(prof_name): '''search for inactive/extra profile, and ask if it should be used''' @@ -548,14 +578,15 @@ def get_profile(prof_name): return None # CMD_CREATE_PROFILE chosen + def autodep(bin_name, pname=''): bin_full = None if bin_name: bin_full = find_executable(bin_name) - #if not bin_full: + # if not bin_full: # bin_full = bin_name - #if not bin_full.startswith('/'): - #return None + # if not bin_full.startswith('/'): + # return None # Return if executable path not found if not bin_full: return None @@ -586,6 +617,7 @@ def autodep(bin_name, pname=''): active_profiles.add_inc_ie(file, IncludeRule('tunables/global', False, True)) write_profile_ui_feedback(pname) + def get_profile_flags(filename, program): # To-Do # XXX If more than one profile in a file then second one is being ignored XXX @@ -600,11 +632,13 @@ def get_profile_flags(filename, program): else: profile_glob = AARE(matches['profile'], True) flags = matches['flags'] - if (program is not None and profile_glob.match(program)) or program is None or program == matches['profile']: + if ((program is not None and profile_glob.match(program)) + or program is None or program == matches['profile']): return flags raise AppArmorException(_('%s contains no profile') % filename) + def change_profile_flags(prof_filename, program, flag, set_flag): """Reads the old profile file and updates the flags accordingly""" # TODO: count the number of matching lines (separated by profile and hat?) and return it @@ -664,6 +698,7 @@ def change_profile_flags(prof_filename, program, flag, set_flag): else: raise AppArmorException("%(file)s doesn't contain a valid profile for %(profile)s (syntax error?)" % {'file': prof_filename, 'profile': program}) + def profile_exists(program): """Returns True if profile exists, False otherwise""" # Check cache of profiles @@ -672,7 +707,7 @@ def profile_exists(program): return True # Check the disk for profile prof_path = get_profile_filename_from_attachment(program, True) - #print(prof_path) + # print(prof_path) if os.path.isfile(prof_path): # Add to cache of profile raise AppArmorBug('Reached strange condition in profile_exists(), please open a bugreport!') @@ -680,6 +715,7 @@ def profile_exists(program): # return True return False + def build_x_functions(default, options, exec_toggle): ret_list = [] fallback_toggle = False @@ -721,6 +757,7 @@ def build_x_functions(default, options, exec_toggle): ret_list.extend(('CMD_DENY', 'CMD_ABORT', 'CMD_FINISHED')) return ret_list + def ask_addhat(hashlog): '''ask the user about change_hat events (requests to add a hat)''' @@ -788,6 +825,7 @@ def ask_addhat(hashlog): hashlog[aamode][full_hat]['final_name'] = '' continue + def ask_exec(hashlog): '''ask the user about exec events (requests to execute another program) and which exec mode to use''' @@ -795,7 +833,9 @@ def ask_exec(hashlog): for full_profile in hashlog[aamode]: if '//' in hashlog[aamode][full_profile]['final_name'] and hashlog[aamode][full_profile]['exec'].keys(): # TODO: is this really needed? Or would removing Cx from the options be good enough? - aaui.UI_Important('WARNING: Ignoring exec event in %s, nested profiles are not supported yet.' % hashlog[aamode][full_profile]['final_name']) + aaui.UI_Important( + 'WARNING: Ignoring exec event in %s, nested profiles are not supported yet.' + % hashlog[aamode][full_profile]['final_name']) continue profile, hat = split_name(full_profile) # XXX temporary solution to avoid breaking the existing code @@ -805,7 +845,8 @@ def ask_exec(hashlog): to_name = '' if os.path.isdir(exec_target): - raise AppArmorBug('exec permissions requested for directory %s. This should not happen - please open a bugreport!' % exec_target) + raise AppArmorBug( + 'exec permissions requested for directory %s. This should not happen - please open a bugreport!' % exec_target) if not aa[profile].get(hat): continue # ignore log entries for non-existing profiles @@ -823,9 +864,9 @@ def ask_exec(hashlog): if True: options = cfg['qualifiers'].get(exec_target, 'ipcnu') - ### If profiled program executes itself only 'ix' option - ##if exec_target == profile: - ##options = 'i' + # If profiled program executes itself only 'ix' option + # if exec_target == profile: + # options = 'i' # Don't allow hats to cx? options.replace('c', '') @@ -850,9 +891,11 @@ def ask_exec(hashlog): prof_filename = get_profile_filename_from_profile_name(profile) if prof_filename and active_profiles.files.get(prof_filename): - sev_db.set_variables(active_profiles.get_all_merged_variables(prof_filename, include_list_recursive(active_profiles.files[prof_filename], True))) + sev_db.set_variables(active_profiles.get_all_merged_variables( + prof_filename, + include_list_recursive(active_profiles.files[prof_filename], True))) else: - sev_db.set_variables( {} ) + sev_db.set_variables({}) severity = sev_db.rank_path(exec_target, 'x') @@ -907,9 +950,22 @@ def ask_exec(hashlog): exec_mode = 'ix' elif ans in ('CMD_px', 'CMD_cx', 'CMD_pix', 'CMD_cix'): exec_mode = ans.replace('CMD_', '') - px_msg = _("Should AppArmor sanitise the environment when\nswitching profiles?\n\nSanitising environment is more secure,\nbut some applications depend on the presence\nof LD_PRELOAD or LD_LIBRARY_PATH.") + px_msg = _( + "Should AppArmor sanitise the environment when\n" + "switching profiles?\n" + "\n" + "Sanitising environment is more secure,\n" + "but some applications depend on the presence\n" + "of LD_PRELOAD or LD_LIBRARY_PATH.") if parent_uses_ld_xxx: - px_msg = _("Should AppArmor sanitise the environment when\nswitching profiles?\n\nSanitising environment is more secure,\nbut this application appears to be using LD_PRELOAD\nor LD_LIBRARY_PATH and sanitising the environment\ncould cause functionality problems.") + px_msg = _( + "Should AppArmor sanitise the environment when\n" + "switching profiles?\n" + "\n" + "Sanitising environment is more secure,\n" + "but this application appears to be using LD_PRELOAD\n" + "or LD_LIBRARY_PATH and sanitising the environment\n" + "could cause functionality problems.") ynans = aaui.UI_YesNo(px_msg, 'y') if ynans == 'y': @@ -917,9 +973,20 @@ def ask_exec(hashlog): exec_mode = exec_mode.capitalize() elif ans == 'CMD_ux': exec_mode = 'ux' - ynans = aaui.UI_YesNo(_("Launching processes in an unconfined state is a very\ndangerous operation and can cause serious security holes.\n\nAre you absolutely certain you wish to remove all\nAppArmor protection when executing %s ?") % exec_target, 'n') + ynans = aaui.UI_YesNo(_( + "Launching processes in an unconfined state is a very\n" + "dangerous operation and can cause serious security holes.\n" + "\n" + "Are you absolutely certain you wish to remove all\n" + "AppArmor protection when executing %s ?") % exec_target, 'n') if ynans == 'y': - ynans = aaui.UI_YesNo(_("Should AppArmor sanitise the environment when\nrunning this program unconfined?\n\nNot sanitising the environment when unconfining\na program opens up significant security holes\nand should be avoided if at all possible."), 'y') + ynans = aaui.UI_YesNo(_( + "Should AppArmor sanitise the environment when\n" + "running this program unconfined?\n" + "\n" + "Not sanitising the environment when unconfining\n" + "a program opens up significant security holes\n" + "and should be avoided if at all possible."), 'y') if ynans == 'y': # Disable the unsafe mode exec_mode = exec_mode.capitalize() @@ -1025,6 +1092,7 @@ def ask_exec(hashlog): elif ans.startswith('CMD_ux'): continue + def order_globs(globs, original_path): """Returns the globs in sorted order, more specific behind""" # To-Do @@ -1039,6 +1107,7 @@ def order_globs(globs, original_path): return globs + def ask_the_questions(log_dict): for aamode in sorted(log_dict.keys()): # Describe the type of changes @@ -1059,7 +1128,7 @@ def ask_the_questions(log_dict): if prof_filename and active_profiles.files.get(prof_filename): sev_db.set_variables(active_profiles.get_all_merged_variables(prof_filename, include_list_recursive(active_profiles.files[prof_filename], True))) else: - sev_db.set_variables( {} ) + sev_db.set_variables({}) if True: if not aa[profile].get(hat, {}).get('file'): @@ -1104,12 +1173,15 @@ def ask_the_questions(log_dict): # check for and ask about conflicting exec modes ask_conflict_mode(aa[profile][hat], log_dict[aamode][full_profile]) - prof_changed, end_profiling = ask_rule_questions(log_dict[aamode][full_profile], combine_name(profile, hat), aa[profile][hat], ruletypes) + prof_changed, end_profiling = ask_rule_questions( + log_dict[aamode][full_profile], combine_name(profile, hat), + aa[profile][hat], ruletypes) if prof_changed: changed[profile] = True if end_profiling: return # end profiling loop + def ask_rule_questions(prof_events, profile_name, the_profile, r_types): ''' ask questions about rules to add to a single profile/hat @@ -1129,166 +1201,176 @@ def ask_rule_questions(prof_events, profile_name, the_profile, r_types): for ruletype in r_types: for rule_obj in prof_events[ruletype].rules: - if is_known_rule(the_profile, ruletype, rule_obj): - continue + if is_known_rule(the_profile, ruletype, rule_obj): + continue - default_option = 1 - options = [] - newincludes = match_includes(the_profile, ruletype, rule_obj) - q = aaui.PromptQuestion() - if newincludes: - options.extend(map(lambda inc: 'include <%s>' % inc, sorted(set(newincludes)))) + default_option = 1 + options = [] + newincludes = match_includes(the_profile, ruletype, rule_obj) + q = aaui.PromptQuestion() + if newincludes: + options.extend(map(lambda inc: 'include <%s>' % inc, sorted(set(newincludes)))) - if ruletype == 'file' and rule_obj.path: - options += propose_file_rules(the_profile, rule_obj) - else: - options.append(rule_obj.get_clean()) + if ruletype == 'file' and rule_obj.path: + options += propose_file_rules(the_profile, rule_obj) + else: + options.append(rule_obj.get_clean()) - done = False - while not done: - q.options = options - q.selected = default_option - 1 - q.headers = [_('Profile'), profile_name] - q.headers.extend(rule_obj.logprof_header()) + done = False + while not done: + q.options = options + q.selected = default_option - 1 + q.headers = [_('Profile'), profile_name] + q.headers.extend(rule_obj.logprof_header()) - # Load variables into sev_db? Not needed/used for capabilities and network rules. - severity = rule_obj.severity(sev_db) - if severity != sev_db.NOT_IMPLEMENTED: - q.headers.extend((_('Severity'), severity)) + # Load variables into sev_db? Not needed/used for capabilities and network rules. + severity = rule_obj.severity(sev_db) + if severity != sev_db.NOT_IMPLEMENTED: + q.headers.extend((_('Severity'), severity)) - q.functions = available_buttons(rule_obj) + q.functions = available_buttons(rule_obj) - # In complain mode: events default to allow - # In enforce mode: events default to deny - # XXX does this behaviour really make sense, except for "historical reasons"[tm]? - q.default = 'CMD_DENY' - if rule_obj.log_event == 'PERMITTING': - q.default = 'CMD_ALLOW' + # In complain mode: events default to allow + # In enforce mode: events default to deny + # XXX does this behaviour really make sense, except for "historical reasons"[tm]? + q.default = 'CMD_DENY' + if rule_obj.log_event == 'PERMITTING': + q.default = 'CMD_ALLOW' - ans, selected = q.promptUser() - selection = options[selected] + ans, selected = q.promptUser() + selection = options[selected] - if ans == 'CMD_IGNORE_ENTRY': - done = True - break + if ans == 'CMD_IGNORE_ENTRY': + done = True + break - elif ans == 'CMD_FINISHED': - return changed, True + elif ans == 'CMD_FINISHED': + return changed, True - elif ans.startswith('CMD_AUDIT'): - if ans == 'CMD_AUDIT_NEW': - rule_obj.audit = True - rule_obj.raw_rule = None - else: - rule_obj.audit = False - rule_obj.raw_rule = None + elif ans.startswith('CMD_AUDIT'): + if ans == 'CMD_AUDIT_NEW': + rule_obj.audit = True + rule_obj.raw_rule = None + else: + rule_obj.audit = False + rule_obj.raw_rule = None - options = set_options_audit_mode(rule_obj, options) + options = set_options_audit_mode(rule_obj, options) - elif ans.startswith('CMD_USER_'): - if ans == 'CMD_USER_ON': - rule_obj.owner = True - rule_obj.raw_rule = None - else: - rule_obj.owner = False - rule_obj.raw_rule = None + elif ans.startswith('CMD_USER_'): + if ans == 'CMD_USER_ON': + rule_obj.owner = True + rule_obj.raw_rule = None + else: + rule_obj.owner = False + rule_obj.raw_rule = None - options = set_options_owner_mode(rule_obj, options) + options = set_options_owner_mode(rule_obj, options) - elif ans == 'CMD_ALLOW': - done = True - changed = True + elif ans == 'CMD_ALLOW': + done = True + changed = True - inc = re_match_include(selection) - if inc: - deleted = delete_all_duplicates(the_profile, inc, r_types) + inc = re_match_include(selection) + if inc: + deleted = delete_all_duplicates(the_profile, inc, r_types) - the_profile['inc_ie'].add(IncludeRule.parse(selection)) + the_profile['inc_ie'].add(IncludeRule.parse(selection)) - aaui.UI_Info(_('Adding %s to profile.') % selection) - if deleted: - aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted) + aaui.UI_Info(_('Adding %s to profile.') % selection) + if deleted: + aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted) - else: - rule_obj = selection_to_rule_obj(rule_obj, selection) - deleted = the_profile[ruletype].add(rule_obj, cleanup=True) + else: + rule_obj = selection_to_rule_obj(rule_obj, selection) + deleted = the_profile[ruletype].add(rule_obj, cleanup=True) - aaui.UI_Info(_('Adding %s to profile.') % rule_obj.get_clean()) - if deleted: - aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted) + aaui.UI_Info(_('Adding %s to profile.') % rule_obj.get_clean()) + if deleted: + aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted) - elif ans == 'CMD_DENY': - if re_match_include(selection): - aaui.UI_Important("Denying via an include file isn't supported by the AppArmor tools") + elif ans == 'CMD_DENY': + if re_match_include(selection): + aaui.UI_Important("Denying via an include file isn't supported by the AppArmor tools") - else: - done = True - changed = True + else: + done = True + changed = True - rule_obj = selection_to_rule_obj(rule_obj, selection) - rule_obj.deny = True - rule_obj.raw_rule = None # reset raw rule after manually modifying rule_obj - deleted = the_profile[ruletype].add(rule_obj, cleanup=True) - aaui.UI_Info(_('Adding %s to profile.') % rule_obj.get_clean()) - if deleted: - aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted) + rule_obj = selection_to_rule_obj(rule_obj, selection) + rule_obj.deny = True + rule_obj.raw_rule = None # reset raw rule after manually modifying rule_obj + deleted = the_profile[ruletype].add(rule_obj, cleanup=True) + aaui.UI_Info(_('Adding %s to profile.') % rule_obj.get_clean()) + if deleted: + aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted) - elif ans == 'CMD_GLOB': - if not re_match_include(selection): - globbed_rule_obj = selection_to_rule_obj(rule_obj, selection) - globbed_rule_obj.glob() - options, default_option = add_to_options(options, globbed_rule_obj.get_raw()) + elif ans == 'CMD_GLOB': + if not re_match_include(selection): + globbed_rule_obj = selection_to_rule_obj(rule_obj, selection) + globbed_rule_obj.glob() + options, default_option = add_to_options(options, globbed_rule_obj.get_raw()) - elif ans == 'CMD_GLOBEXT': - if not re_match_include(selection): - globbed_rule_obj = selection_to_rule_obj(rule_obj, selection) - globbed_rule_obj.glob_ext() - options, default_option = add_to_options(options, globbed_rule_obj.get_raw()) + elif ans == 'CMD_GLOBEXT': + if not re_match_include(selection): + globbed_rule_obj = selection_to_rule_obj(rule_obj, selection) + globbed_rule_obj.glob_ext() + options, default_option = add_to_options(options, globbed_rule_obj.get_raw()) - elif ans == 'CMD_NEW': - if not re_match_include(selection): - edit_rule_obj = selection_to_rule_obj(rule_obj, selection) - prompt, oldpath = edit_rule_obj.edit_header() + elif ans == 'CMD_NEW': + if not re_match_include(selection): + edit_rule_obj = selection_to_rule_obj(rule_obj, selection) + prompt, oldpath = edit_rule_obj.edit_header() - newpath = aaui.UI_GetString(prompt, oldpath) - if newpath: - try: - input_matches_path = rule_obj.validate_edit(newpath) # note that we check against the original rule_obj here, not edit_rule_obj (which might be based on a globbed path) - except AppArmorException: - aaui.UI_Important(_('The path you entered is invalid (not starting with / or a variable)!')) - continue + newpath = aaui.UI_GetString(prompt, oldpath) + if newpath: + try: + input_matches_path = rule_obj.validate_edit(newpath) # note that we check against the original rule_obj here, not edit_rule_obj (which might be based on a globbed path) + except AppArmorException: + aaui.UI_Important(_('The path you entered is invalid (not starting with / or a variable)!')) + continue - if not input_matches_path: - ynprompt = _('The specified path does not match this log entry:\n\n Log Entry: %(path)s\n Entered Path: %(ans)s\nDo you really want to use this path?') % { 'path': oldpath, 'ans': newpath } - key = aaui.UI_YesNo(ynprompt, 'n') - if key == 'n': - continue + if not input_matches_path: + ynprompt = ( + _('The specified path does not match this log entry:\n' + '\n' + ' Log Entry: %(path)s\n' + ' Entered Path: %(ans)s\n' + 'Do you really want to use this path?') + % {'path': oldpath, 'ans': newpath}) + key = aaui.UI_YesNo(ynprompt, 'n') + if key == 'n': + continue - edit_rule_obj.store_edit(newpath) - options, default_option = add_to_options(options, edit_rule_obj.get_raw()) - user_globs[newpath] = AARE(newpath, True) + edit_rule_obj.store_edit(newpath) + options, default_option = add_to_options(options, edit_rule_obj.get_raw()) + user_globs[newpath] = AARE(newpath, True) - else: - done = False + else: + done = False return changed, False + def selection_to_rule_obj(rule_obj, selection): rule_type = type(rule_obj) return rule_type.parse(selection) + def set_options_audit_mode(rule_obj, options): '''change audit state in options (proposed rules) to audit state in rule_obj. #include options will be kept unchanged ''' return set_options_mode(rule_obj, options, 'audit') + def set_options_owner_mode(rule_obj, options): '''change owner state in options (proposed rules) to owner state in rule_obj. #include options will be kept unchanged ''' return set_options_mode(rule_obj, options, 'owner') + def set_options_mode(rule_obj, options, what): ''' helper function for set_options_audit_mode() and set_options_owner_mode''' new_options = [] @@ -1310,6 +1392,7 @@ def set_options_mode(rule_obj, options, what): return new_options + def available_buttons(rule_obj): buttons = [] @@ -1342,6 +1425,7 @@ def available_buttons(rule_obj): return buttons + def add_to_options(options, newpath): if newpath not in options: options.append(newpath) @@ -1349,6 +1433,7 @@ def add_to_options(options, newpath): default_option = options.index(newpath) + 1 return (options, default_option) + def delete_all_duplicates(profile, incname, r_types): deleted = 0 # Allow rules covered by denied rules shouldn't be deleted @@ -1360,6 +1445,7 @@ def delete_all_duplicates(profile, incname, r_types): return deleted + def ask_conflict_mode(old_profile, merge_profile): '''ask user about conflicting exec rules''' for oldrule in old_profile['file'].rules: @@ -1394,6 +1480,7 @@ def ask_conflict_mode(old_profile, merge_profile): done = True + def match_includes(profile, rule_type, rule_obj): ''' propose abstractions that allow the given rule_obj @@ -1425,6 +1512,7 @@ def match_includes(profile, rule_type, rule_obj): return newincludes + def valid_include(incname): ''' check if the given include file exists or is whitelisted in custom_includes ''' if cfg['settings']['custom_includes']: @@ -1439,6 +1527,7 @@ def valid_include(incname): return False + def set_logfile(filename): ''' set logfile to a) the specified filename or b) if not given, the first existing logfile from logprof.conf''' @@ -1461,6 +1550,7 @@ def set_logfile(filename): elif os.path.isdir(logfile): raise AppArmorException(_('%s is a directory. Please specify a file as logfile') % logfile) + def do_logprof_pass(logmark=''): # set up variables for this pass global active_profiles @@ -1472,8 +1562,8 @@ def do_logprof_pass(logmark=''): if not sev_db: sev_db = apparmor.severity.Severity(CONFDIR + '/severity.db', _('unknown')) - #print(pid) - #print(active_profiles) + # print(pid) + # print(active_profiles) log_reader = apparmor.logparser.ReadLog(logfile, active_profiles, profile_dir) hashlog = log_reader.read_log(logmark) @@ -1487,6 +1577,7 @@ def do_logprof_pass(logmark=''): save_profiles() + def save_profiles(is_mergeprof=False): # Ensure the changed profiles are actual active profiles for prof_name in changed.keys(): @@ -1550,6 +1641,7 @@ def save_profiles(is_mergeprof=False): write_profile_ui_feedback(profile_name) reload_base(profile_name) + def collapse_log(hashlog, ignore_null_profiles=True): log_dict = {} @@ -1604,22 +1696,22 @@ def collapse_log(hashlog, ignore_null_profiles=True): log_dict[aamode][full_profile]['change_profile'].add(cp_event) dbus = hashlog[aamode][full_profile]['dbus'] - for access in dbus: - for bus in dbus[access]: - for path in dbus[access][bus]: - for name in dbus[access][bus][path]: - for interface in dbus[access][bus][path][name]: - for member in dbus[access][bus][path][name][interface]: + for access in dbus: # noqa: E271 + for bus in dbus[access]: # noqa: E271 + for path in dbus[access][bus]: # noqa: E271 + for name in dbus[access][bus][path]: # noqa: E271 + for interface in dbus[access][bus][path][name]: # noqa: E271 + for member in dbus[access][bus][path][name][interface]: # noqa: E271 for peer_profile in dbus[access][bus][path][name][interface][member]: # Depending on the access type, not all parameters are allowed. # Ignore them, even if some of them appear in the log. # Also, the log doesn't provide a peer name, therefore always use ALL. if access in ('send', 'receive'): - dbus_event = DbusRule(access, bus, path, DbusRule.ALL, interface, member, DbusRule.ALL, peer_profile, log_event=True) + dbus_event = DbusRule(access, bus, path, DbusRule.ALL, interface, member, DbusRule.ALL, peer_profile, log_event=True) elif access == 'bind': - dbus_event = DbusRule(access, bus, DbusRule.ALL, name, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, log_event=True) + dbus_event = DbusRule(access, bus, DbusRule.ALL, name, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, log_event=True) elif access == 'eavesdrop': - dbus_event = DbusRule(access, bus, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, log_event=True) + dbus_event = DbusRule(access, bus, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, log_event=True) else: raise AppArmorBug('unexpected dbus access: %s') @@ -1650,6 +1742,7 @@ def collapse_log(hashlog, ignore_null_profiles=True): return log_dict + def read_profiles(ui_msg=False, skip_profiles=[]): # we'll read all profiles from disk, so reset the storage first (autodep() might have created/stored # a profile already, which would cause a 'Conflicting profile' error in attach_profile_data()) @@ -1679,6 +1772,7 @@ def read_profiles(ui_msg=False, skip_profiles=[]): else: read_profile(full_file, True) + def read_inactive_profiles(skip_profiles=[]): # The skip_profiles parameter should only be specified by tests. @@ -1707,6 +1801,7 @@ def read_inactive_profiles(skip_profiles=[]): else: read_profile(full_file, False) + def read_profile(file, active_profile): data = None try: @@ -1748,6 +1843,7 @@ def read_profile(file, active_profile): extra_profiles.add_profile(filename, profile, attachment, profile_data[profile]) + def attach_profile_data(profiles, profile_data): profile_data = merged_to_split(profile_data) # Make deep copy of data to avoid changes to @@ -1756,8 +1852,9 @@ def attach_profile_data(profiles, profile_data): if profiles.get(p, False): for hat in profile_data[p].keys(): if profiles[p].get(hat, False): - raise AppArmorException(_("Conflicting profiles for %s defined in two files:\n- %s\n- %s") % - (combine_name(p, hat), profiles[p][hat]['filename'], profile_data[p][hat]['filename'])) + raise AppArmorException( + _("Conflicting profiles for %s defined in two files:\n- %s\n- %s") + % (combine_name(p, hat), profiles[p][hat]['filename'], profile_data[p][hat]['filename'])) profiles[p] = deepcopy(profile_data[p]) @@ -1824,8 +1921,9 @@ def parse_profile_data(data, file, do_include, in_preamble): profname = combine_profname([profile, hat]) if profile_data.get(profname, False): - raise AppArmorException('Profile %(profile)s defined twice in %(file)s, last found in line %(line)s' % - { 'file': file, 'line': lineno + 1, 'profile': combine_name(profile, hat) }) + raise AppArmorException( + 'Profile %(profile)s defined twice in %(file)s, last found in line %(line)s' % + {'file': file, 'line': lineno + 1, 'profile': combine_name(profile, hat)}) profile_data[profname] = prof_storage @@ -1838,7 +1936,9 @@ def parse_profile_data(data, file, do_include, in_preamble): elif RE_PROFILE_END.search(line): # If profile ends and we're not in one if not profile: - raise AppArmorException(_('Syntax Error: Unexpected End of Profile reached in file: %(file)s line: %(line)s') % { 'file': file, 'line': lineno + 1 }) + raise AppArmorException( + _('Syntax Error: Unexpected End of Profile reached in file: %(file)s line: %(line)s') + % {'file': file, 'line': lineno + 1}) if in_contained_hat: hat = None @@ -1868,7 +1968,8 @@ def parse_profile_data(data, file, do_include, in_preamble): matches = RE_PROFILE_MOUNT.search(line).groups() if not profile: - raise AppArmorException(_('Syntax Error: Unexpected mount entry found in file: %(file)s line: %(line)s') % { 'file': file, 'line': lineno + 1 }) + raise AppArmorException(_('Syntax Error: Unexpected mount entry found in file: %(file)s line: %(line)s') + % {'file': file, 'line': lineno + 1}) audit = False if matches[0]: @@ -1890,7 +1991,8 @@ def parse_profile_data(data, file, do_include, in_preamble): 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 }) + 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]: @@ -1912,7 +2014,8 @@ def parse_profile_data(data, file, do_include, in_preamble): matches = RE_PROFILE_UNIX.search(line).groups() if not profile: - raise AppArmorException(_('Syntax Error: Unexpected unix entry found in file: %(file)s line: %(line)s') % { 'file': file, 'line': lineno + 1 }) + raise AppArmorException(_('Syntax Error: Unexpected unix entry found in file: %(file)s line: %(line)s') + % {'file': file, 'line': lineno + 1}) audit = False if matches[0]: @@ -1934,10 +2037,11 @@ def parse_profile_data(data, file, do_include, in_preamble): matches = RE_PROFILE_CHANGE_HAT.search(line).groups() if not profile: - raise AppArmorException(_('Syntax Error: Unexpected change hat declaration found in file: %(file)s line: %(line)s') % { 'file': file, 'line': lineno + 1 }) + raise AppArmorException(_('Syntax Error: Unexpected change hat declaration found in file: %(file)s line: %(line)s') + % {'file': file, 'line': lineno + 1}) - aaui.UI_Important(_('Ignoring no longer supported change hat declaration "^%(hat)s," found in file: %(file)s line: %(line)s') % { - 'hat': matches[0], 'file': file, 'line': lineno + 1 }) + aaui.UI_Important(_('Ignoring no longer supported change hat declaration "^%(hat)s," found in file: %(file)s line: %(line)s') + % {'hat': matches[0], 'file': file, 'line': lineno + 1}) elif line[0] == '#': # Handle initial comments @@ -1947,7 +2051,7 @@ def parse_profile_data(data, file, do_include, in_preamble): else: initial_comment = initial_comment + line + '\n' - if line.startswith('# LOGPROF-SUGGEST:'): # TODO: allow any number of spaces/tabs after '#' + if line.startswith('# LOGPROF-SUGGEST:'): # TODO: allow any number of spaces/tabs after '#' parts = line.split() if len(parts) > 2: profile_data[profname]['logprof_suggest'] = parts[2] @@ -1963,12 +2067,16 @@ def parse_profile_data(data, file, do_include, in_preamble): else: lastline = line else: - raise AppArmorException(_('Syntax Error: Unknown line found in file %(file)s line %(lineno)s:\n %(line)s') % { 'file': file, 'lineno': lineno + 1, 'line': line }) + raise AppArmorException( + _('Syntax Error: Unknown line found in file %(file)s line %(lineno)s:\n %(line)s') + % {'file': file, 'lineno': lineno + 1, 'line': line}) if lastline: # lastline gets merged into line (and reset to None) when reading the next line. # If it isn't empty, this means there's something unparsable at the end of the profile - raise AppArmorException(_('Syntax Error: Unknown line found in file %(file)s line %(lineno)s:\n %(line)s') % { 'file': file, 'lineno': lineno + 1, 'line': lastline }) + raise AppArmorException( + _('Syntax Error: Unknown line found in file %(file)s line %(lineno)s:\n %(line)s') + % {'file': file, 'lineno': lineno + 1, 'line': lastline}) # Below is not required I'd say if not do_include: @@ -1983,10 +2091,13 @@ def parse_profile_data(data, file, do_include, in_preamble): # End of file reached but we're stuck in a profile if profile and not do_include: - raise AppArmorException(_("Syntax Error: Missing '}' or ','. Reached end of file %(file)s while inside profile %(profile)s") % { 'file': file, 'profile': profile }) + raise AppArmorException( + _("Syntax Error: Missing '}' or ','. Reached end of file %(file)s while inside profile %(profile)s") + % {'file': file, 'profile': profile}) return profile_data + def match_line_against_rule_classes(line, profile, file, lineno, in_preamble): ''' handle all lines handled by *Rule classes ''' @@ -2004,7 +2115,7 @@ def match_line_against_rule_classes(line, profile, file, lineno, in_preamble): 'ptrace', 'rlimit', 'signal', - ): + ): if rule_name in ruletypes: rule_class = ruletypes[rule_name]['rule'] @@ -2013,15 +2124,20 @@ def match_line_against_rule_classes(line, profile, file, lineno, in_preamble): if rule_class.match(line): if not in_preamble and rule_name not in ruletypes: - raise AppArmorException(_('Syntax Error: Unexpected %(rule)s definition found inside profile in file: %(file)s line: %(line)s') % { 'file': file, 'line': lineno + 1, 'rule': rule_name }) + raise AppArmorException( + _('Syntax Error: Unexpected %(rule)s definition found inside profile in file: %(file)s line: %(line)s') + % {'file': file, 'line': lineno + 1, 'rule': rule_name}) if in_preamble and rule_name not in preamble_ruletypes: - raise AppArmorException(_('Syntax Error: Unexpected %(rule)s entry found in file: %(file)s line: %(line)s') % { 'file': file, 'line': lineno + 1, 'rule': rule_name }) + raise AppArmorException( + _('Syntax Error: Unexpected %(rule)s entry found in file: %(file)s line: %(line)s') + % {'file': file, 'line': lineno + 1, 'rule': rule_name}) rule_obj = rule_class.parse(line) - return(rule_name, rule_obj) + return (rule_name, rule_obj) + + return (None, None) - return(None, None) def merged_to_split(profile_data): ''' (temporary) helper function to convert a list of profile['foo//bar'] profiles into compat['foo']['bar']''' @@ -2032,6 +2148,7 @@ def merged_to_split(profile_data): return compat + def split_to_merged(profile_data): ''' (temporary) helper function to convert a traditional compat['foo']['bar'] to a profile['foo//bar'] list ''' @@ -2048,18 +2165,22 @@ def split_to_merged(profile_data): return merged + def parse_mount_rule(line): # XXX Do real parsing here return aarules.Raw_Mount_Rule(line) + def parse_pivot_root_rule(line): # XXX Do real parsing here return aarules.Raw_Pivot_Root_Rule(line) + def parse_unix_rule(line): # XXX Do real parsing here return aarules.Raw_Unix_Rule(line) + def write_piece(profile_data, depth, name, nhat): pre = ' ' * depth data = [] @@ -2106,6 +2227,7 @@ def write_piece(profile_data, depth, name, nhat): return data + def serialize_profile(profile_data, name, options): string = '' data = [] @@ -2130,7 +2252,7 @@ def serialize_profile(profile_data, name, options): data.extend(active_profiles.get_clean(prof_filename, 0)) - #Here should be all the profiles from the files added write after global/common stuff + # Here should be all the profiles from the files added write after global/common stuff for prof in sorted(active_profiles.profiles_in_file(prof_filename)): if prof != name: if original_aa[prof][prof].get('initial_comment', False): @@ -2150,10 +2272,12 @@ def serialize_profile(profile_data, name, options): return string + '\n' + def write_profile_ui_feedback(profile, is_attachment=False): aaui.UI_Info(_('Writing updated profile for %s.') % profile) write_profile(profile, is_attachment) + def write_profile(profile, is_attachment=False): if aa[profile][profile].get('filename', False): prof_filename = aa[profile][profile]['filename'] @@ -2181,6 +2305,7 @@ def write_profile(profile, is_attachment=False): original_aa[profile] = deepcopy(aa[profile]) + def include_list_recursive(profile, in_preamble=False): ''' get a list of all includes in a profile and its included files ''' @@ -2206,6 +2331,7 @@ def include_list_recursive(profile, in_preamble=False): return full_list + def is_known_rule(profile, rule_type, rule_obj): # XXX get rid of get() checks after we have a proper function to initialize a profile if profile.get(rule_type, False): @@ -2220,6 +2346,7 @@ def is_known_rule(profile, rule_type, rule_obj): return False + def get_file_perms(profile, path, audit, deny): '''get the current permissions for the given path''' @@ -2243,6 +2370,7 @@ def get_file_perms(profile, path, audit, deny): return perms + def propose_file_rules(profile_obj, rule_obj): '''Propose merged file rules based on the existing profile and the log events - permissions get merged @@ -2284,6 +2412,7 @@ def propose_file_rules(profile_obj, rule_obj): return options + def reload_base(bin_path): if not check_for_apparmor(): return None @@ -2292,6 +2421,7 @@ def reload_base(bin_path): reload_profile(prof_filename) + def reload_profile(prof_filename, raise_exc=False): ''' run apparmor_parser to reload the given profile file ''' @@ -2303,6 +2433,7 @@ def reload_profile(prof_filename, raise_exc=False): else: print(out) + def reload(bin_path): bin_path = find_executable(bin_path) if not bin_path: @@ -2310,6 +2441,7 @@ def reload(bin_path): return reload_base(bin_path) + def get_include_data(filename): data = [] if not filename.startswith('/'): @@ -2321,6 +2453,7 @@ def get_include_data(filename): raise AppArmorException(_('File Not Found: %s') % filename) return data + def include_dir_filelist(include_name): '''returns a list of files in the given include_name directory, except skippable files. @@ -2340,6 +2473,7 @@ def include_dir_filelist(include_name): return files + def load_include(incname, in_preamble=False): load_includeslist = [incname] while load_includeslist: @@ -2353,7 +2487,7 @@ def load_include(incname, in_preamble=False): data = get_include_data(incfile) incdata = parse_profile_data(data, incfile, True, in_preamble) attach_profile_data(include, incdata) - #If the include is a directory means include all subfiles + # If the include is a directory means include all subfiles elif os.path.isdir(incfile): load_includeslist += include_dir_filelist(incfile) else: @@ -2361,20 +2495,30 @@ def load_include(incname, in_preamble=False): return 0 + def check_qualifiers(program): if cfg['qualifiers'].get(program, False): if cfg['qualifiers'][program] != 'p': - fatal_error(_("%s is currently marked as a program that should not have its own\nprofile. Usually, programs are marked this way if creating a profile for \nthem is likely to break the rest of the system. If you know what you\'re\ndoing and are certain you want to create a profile for this program, edit\nthe corresponding entry in the [qualifiers] section in /etc/apparmor/logprof.conf.") % program) + fatal_error( + _("%s is currently marked as a program that should not have its own\n" + "profile. Usually, programs are marked this way if creating a profile for \n" + "them is likely to break the rest of the system. If you know what you\'re\n" + "doing and are certain you want to create a profile for this program, edit\n" + "the corresponding entry in the [qualifiers] section in /etc/apparmor/logprof.conf.") + % program) return False + def get_subdirectories(current_dir): """Returns a list of all directories directly inside given directory""" return next(os.walk(current_dir))[1] + def loadincludes(): loadincludes_dir('tunables', True) loadincludes_dir('abstractions', False) + def loadincludes_dir(subdir, in_preamble): idir = os.path.join(profile_dir, subdir) @@ -2387,6 +2531,7 @@ def loadincludes_dir(subdir, in_preamble): fi = os.path.join(dirpath, fi) load_include(fi, in_preamble) + def glob_common(path): globs = [] @@ -2406,18 +2551,21 @@ def glob_common(path): return sorted(set(globs)) + def combine_name(name1, name2): if name1 == name2: return name1 else: return '%s^%s' % (name1, name2) + def logger_path(): logger = conf.find_first_file(cfg['settings']['logger']) or '/bin/logger' if not os.path.isfile(logger) or not os.access(logger, os.EX_OK): raise AppArmorException("Can't find logger!\nPlease make sure %s exists, or update the 'logger' path in logprof.conf." % logger) return logger + ######Initialisations###### def init_aa(confdir=None, profiledir=None): diff --git a/utils/apparmor/aare.py b/utils/apparmor/aare.py index 31b33074e..e31b378aa 100644 --- a/utils/apparmor/aare.py +++ b/utils/apparmor/aare.py @@ -16,6 +16,7 @@ import re from apparmor.common import convert_regexp, AppArmorBug, AppArmorException + class AARE: '''AARE (AppArmor Regular Expression) wrapper class''' @@ -101,17 +102,17 @@ class AARE: else: newpath = re.sub('/[^/]+/$', '/*/', self.regex) else: - if self.regex[-3:] == '/**' or self.regex[-2:] == '/*': - # /foo/** and /foo/* => /** - newpath = re.sub('/[^/]+/\*{1,2}$', '/**', self.regex) - elif re.search('/[^/]*\*\*[^/]+$', self.regex): - # /**foo and /foor**bar => /** - newpath = re.sub('/[^/]*\*\*[^/]+$', '/**', self.regex) - elif re.search('/[^/]+\*\*$', self.regex): - # /foo** => /** - newpath = re.sub('/[^/]+\*\*$', '/**', self.regex) - else: - newpath = re.sub('/[^/]+$', '/*', self.regex) + if self.regex[-3:] == '/**' or self.regex[-2:] == '/*': + # /foo/** and /foo/* => /** + newpath = re.sub('/[^/]+/\*{1,2}$', '/**', self.regex) + elif re.search('/[^/]*\*\*[^/]+$', self.regex): + # /**foo and /foor**bar => /** + newpath = re.sub('/[^/]*\*\*[^/]+$', '/**', self.regex) + elif re.search('/[^/]+\*\*$', self.regex): + # /foo** => /** + newpath = re.sub('/[^/]+\*\*$', '/**', self.regex) + else: + newpath = re.sub('/[^/]+$', '/*', self.regex) return AARE(newpath, False) def glob_path_withext(self): diff --git a/utils/apparmor/cleanprofile.py b/utils/apparmor/cleanprofile.py index a612568ef..c35acb0e1 100644 --- a/utils/apparmor/cleanprofile.py +++ b/utils/apparmor/cleanprofile.py @@ -14,6 +14,7 @@ # ---------------------------------------------------------------------- import apparmor.aa as apparmor + class Prof: def __init__(self, filename): apparmor.init_aa() @@ -22,9 +23,10 @@ class Prof: self.include = apparmor.include self.filename = filename + class CleanProf: def __init__(self, same_file, profile, other): - #If same_file we're basically comparing the file against itself to check superfluous rules + # If same_file we're basically comparing the file against itself to check superfluous rules self.same_file = same_file self.profile = profile self.other = other @@ -40,25 +42,25 @@ class CleanProf: return deleted def remove_duplicate_rules(self, program): - #Process the profile of the program + # Process the profile of the program deleted = 0 # remove duplicate rules from the preamble deleted += self.profile.active_profiles.delete_preamble_duplicates(self.profile.filename) - #Process every hat in the profile individually + # Process every hat in the profile individually for hat in sorted(self.profile.aa[program].keys()): includes = self.profile.aa[program][hat]['inc_ie'].get_all_full_paths(apparmor.profile_dir) - #Clean up superfluous rules from includes in the other profile + # Clean up superfluous rules from includes in the other profile for inc in includes: if not self.profile.include.get(inc, {}).get(inc, False): apparmor.load_include(inc) if self.other.aa[program].get(hat): # carefully avoid to accidentally initialize self.other.aa[program][hat] deleted += apparmor.delete_all_duplicates(self.other.aa[program][hat], inc, apparmor.ruletypes) - #Clean duplicate rules in other profile + # Clean duplicate rules in other profile for ruletype in apparmor.ruletypes: if not self.same_file: if self.other.aa[program].get(hat): # carefully avoid to accidentally initialize self.other.aa[program][hat] diff --git a/utils/apparmor/common.py b/utils/apparmor/common.py index 70f68ee17..71d1c737f 100644 --- a/utils/apparmor/common.py +++ b/utils/apparmor/common.py @@ -36,9 +36,10 @@ class AppArmorException(Exception): def __str__(self): return repr(self.value) + class AppArmorBug(Exception): '''This class represents AppArmor exceptions "that should never happen"''' - pass + # # Utility functions @@ -53,6 +54,7 @@ def error(out, exit_code=1, do_exit=True): if do_exit: sys.exit(exit_code) + def warn(out): '''Print warning message''' try: @@ -60,6 +62,7 @@ def warn(out): except IOError: pass + def msg(out, output=sys.stdout): '''Print message''' try: @@ -67,6 +70,7 @@ def msg(out, output=sys.stdout): except IOError: pass + def debug(out): '''Print debug message''' global DEBUGGING @@ -76,7 +80,8 @@ def debug(out): except IOError: pass -def recursive_print(src, dpth = 0, key = ''): + +def recursive_print(src, dpth=0, key=''): # print recursively in a nicely formatted way # useful for debugging, too verbose for production code ;-) @@ -88,26 +93,27 @@ def recursive_print(src, dpth = 0, key = ''): if isinstance(src, dict): empty = True for key in src.keys(): - print (tabs + '[%s]' % key) + print(tabs + '[%s]' % key) recursive_print(src[key], dpth + 1, key) empty = False if empty: - print (tabs + '[--- empty ---]') + print(tabs + '[--- empty ---]') elif isinstance(src, list) or isinstance(src, tuple): if len(src) == 0: - print (tabs + '[--- empty ---]') + print(tabs + '[--- empty ---]') else: - print (tabs + "[") + print(tabs + "[") for litem in src: recursive_print(litem, dpth + 1) - print (tabs + "]") + print(tabs + "]") elif isinstance(src, rules._Raw_Rule): src.recursive_print(dpth) else: if key: - print (tabs + '%s = %s' % (key, src)) + print(tabs + '%s = %s' % (key, src)) else: - print (tabs + '- %s' % src) + print(tabs + '- %s' % src) + def cmd(command): '''Try to execute the given command.''' @@ -135,6 +141,7 @@ def cmd_pipe(command1, command2): return [sp2.returncode, out] + def valid_path(path): '''Valid path''' # No relative paths @@ -154,6 +161,7 @@ def valid_path(path): return False return True + def get_directory_contents(path): '''Find contents of the given directory''' if not valid_path(path): @@ -166,6 +174,7 @@ def get_directory_contents(path): files.sort() return files + def is_skippable_file(path): """Returns True if filename matches something to be skipped (rpm or dpkg backup files, hidden files etc.) The list of skippable files needs to be synced with apparmor initscript and libapparmor _aa_is_blacklisted() @@ -176,20 +185,25 @@ def is_skippable_file(path): if not basename or basename[0] == '.' or basename == 'README': return True - skippable_suffix = ('.dpkg-new', '.dpkg-old', '.dpkg-dist', '.dpkg-bak', '.dpkg-remove', '.pacsave', '.pacnew', '.rpmnew', '.rpmsave', '.orig', '.rej', '~') + skippable_suffix = ( + '.dpkg-new', '.dpkg-old', '.dpkg-dist', '.dpkg-bak', '.dpkg-remove', + '.pacsave', '.pacnew', '.rpmnew', '.rpmsave', '.orig', '.rej', '~') if basename.endswith(skippable_suffix): return True return False + def open_file_read(path, encoding='UTF-8'): '''Open specified file read-only''' return open_file_anymode('r', path, encoding) + def open_file_write(path): '''Open specified file in write/overwrite mode''' return open_file_anymode('w', path, 'UTF-8') + def open_file_anymode(mode, path, encoding='UTF-8'): '''Crash-resistant wrapper to open a specified file in specified mode''' @@ -198,6 +212,7 @@ def open_file_anymode(mode, path, encoding='UTF-8'): # at several other places we don't know yet ;-) return open(path, mode, encoding=encoding, errors='surrogateescape') + def readkey(): '''Returns the pressed key''' fd = sys.stdin.fileno() @@ -210,6 +225,7 @@ def readkey(): return ch + def hasher(): '''A neat alternative to perl's hash reference''' # Creates a dictionary for any depth and returns empty dictionary otherwise @@ -217,6 +233,7 @@ def hasher(): # This might cause strange effects when using .keys() return collections.defaultdict(hasher) + def convert_regexp(regexp): regex_paren = re.compile('^(.*){([^}]*)}(.*)$') regexp = regexp.strip() @@ -236,7 +253,7 @@ def convert_regexp(regexp): multi_glob = '__KJHDKVZH_AAPROF_INTERNAL_GLOB_SVCUZDGZID__' new_reg = new_reg.replace('**', multi_glob) - #print(new_reg) + # print(new_reg) # Match at least one character if * or ** after / # ?< is the negative lookback operator @@ -248,6 +265,7 @@ def convert_regexp(regexp): new_reg = new_reg + '$' return new_reg + def user_perm(prof_dir): if not os.access(prof_dir, os.W_OK): sys.stdout.write("Cannot write to profile directory.\n" + @@ -266,6 +284,7 @@ def split_name(full_profile): return (profile, hat) + def combine_profname(name_parts): ''' combine name_parts (main profile, child) into a joint main//child profile name ''' @@ -346,4 +365,4 @@ class DebugLogger: def shutdown(self): logging.shutdown() - #logging.shutdown([self.logger]) + # logging.shutdown([self.logger]) diff --git a/utils/apparmor/easyprof.py b/utils/apparmor/easyprof.py index d26395143..5459315db 100644 --- a/utils/apparmor/easyprof.py +++ b/utils/apparmor/easyprof.py @@ -24,6 +24,7 @@ from apparmor.common import AppArmorException, open_file_read DEBUGGING = False + # # TODO: move this out to a utilities library # @@ -131,7 +132,7 @@ def valid_path(path, relative_ok=False): debug("%s (relative)" % (m)) return False - if '"' in path: # We double quote elsewhere + if '"' in path: # We double quote elsewhere debug("%s (quote)" % (m)) return False @@ -520,7 +521,6 @@ class AppArmorEasyProfile: return rule - def gen_policy( self, name, @@ -663,7 +663,7 @@ class AppArmorEasyProfile: out_fn = params['profile_name'] elif 'binary' in params: out_fn = params['binary'] - else: # should not ever reach this + else: # should not ever reach this raise AppArmorException("Could not determine output filename") # Generate an absolute path, converting any path delimiters to '.' @@ -715,9 +715,9 @@ class AppArmorEasyProfile: for key in params: if key == 'profile_name' or \ (key == 'binary' and not 'profile_name' in params): - continue # don't re-add the pkey + continue # don't re-add the pkey elif key == 'binary' and not params[key]: - continue # binary can by None when specifying --profile-name + continue # binary can by None when specifying --profile-name elif key == 'template_var': d['security']['profiles'][pkey]['template_variables'] = dict() for tvar in params[key]: @@ -731,22 +731,26 @@ class AppArmorEasyProfile: d['security']['profiles'][pkey][key].sort() else: d['security']['profiles'][pkey][key] = params[key] - json_str = json.dumps(d, - sort_keys=True, - indent=2, - separators=(',', ': ') - ) + json_str = json.dumps( + d, + sort_keys=True, + indent=2, + separators=(',', ': ') + ) return json_str + def print_basefilenames(files): for i in files: sys.stdout.write("%s\n" % (os.path.basename(i))) + def print_files(files): for i in files: with open(i) as f: sys.stdout.write(f.read()+"\n") + def check_manifest_conflict_args(option, opt_str, value, parser): '''Check for -m/--manifest with conflicting args''' conflict_args = ['abstractions', @@ -769,6 +773,7 @@ def check_manifest_conflict_args(option, opt_str, value, parser): "argument" % conflict) setattr(parser.values, option.dest, value) + def check_for_manifest_arg(option, opt_str, value, parser): '''Check for -m/--manifest with conflicting args''' if parser.values.manifest: @@ -776,6 +781,7 @@ def check_for_manifest_arg(option, opt_str, value, parser): "argument" % opt_str.lstrip('-')) setattr(parser.values, option.dest, value) + def check_for_manifest_arg_append(option, opt_str, value, parser): '''Check for -m/--manifest with conflicting args (with append)''' if parser.values.manifest: @@ -783,6 +789,7 @@ def check_for_manifest_arg_append(option, opt_str, value, parser): "argument" % opt_str.lstrip('-')) parser.values.ensure_value(option.dest, []).append(value) + def add_parser_policy_args(parser): '''Add parser arguments''' parser.add_option("--parser", @@ -867,6 +874,7 @@ def add_parser_policy_args(parser): help="AppArmor profile name", metavar="PROFILENAME") + def parse_args(args=None, parser=None): '''Parse arguments''' global DEBUGGING @@ -963,7 +971,6 @@ def parse_args(args=None, parser=None): dest="verify_manifest", help="Verify JSON manifest file") - # add policy args now add_parser_policy_args(parser) @@ -973,6 +980,7 @@ def parse_args(args=None, parser=None): DEBUGGING = True return (my_opt, my_args) + def gen_policy_params(binary, opt): '''Generate parameters for gen_policy''' params = dict(binary=binary) @@ -991,7 +999,7 @@ def gen_policy_params(binary, opt): elif binary: params['name'] = os.path.basename(binary) - if opt.template_var: # What about specified multiple times? + if opt.template_var: # What about specified multiple times? params['template_var'] = opt.template_var if opt.abstractions: params['abstractions'] = opt.abstractions @@ -1014,6 +1022,7 @@ def gen_policy_params(binary, opt): return params + def parse_manifest(manifest, opt_orig): '''Take a JSON manifest as a string and updates options, returning an updated binary. Note that a JSON file may contain multiple profiles.''' @@ -1033,21 +1042,22 @@ def parse_manifest(manifest, opt_orig): table = top_table['profiles'] # generally mirrors what is settable in gen_policy_params() - valid_keys = ['abstractions', - 'author', - 'binary', - 'comment', - 'copyright', - 'name', - 'policy_groups', - 'policy_version', - 'policy_vendor', - 'profile_name', - 'read_path', - 'template', - 'template_variables', - 'write_path', - ] + valid_keys = [ + 'abstractions', + 'author', + 'binary', + 'comment', + 'copyright', + 'name', + 'policy_groups', + 'policy_version', + 'policy_vendor', + 'profile_name', + 'read_path', + 'template', + 'template_variables', + 'write_path', + ] profiles = [] @@ -1082,7 +1092,7 @@ def parse_manifest(manifest, opt_orig): raise AppArmorException("Invalid key '%s'" % key) if key == 'binary': - continue # handled above + continue # handled above elif key == 'abstractions' or key == 'policy_groups': setattr(opt, key, ",".join(table[profile_name][key])) elif key == "template_variables": @@ -1095,7 +1105,7 @@ def parse_manifest(manifest, opt_orig): if hasattr(opt, key): setattr(opt, key, table[profile_name][key]) - profiles.append( (binary, opt) ) + profiles.append((binary, opt)) return profiles @@ -1180,4 +1190,3 @@ def verify_manifest(params, args=None): return False return True - diff --git a/utils/apparmor/fail.py b/utils/apparmor/fail.py index 6e538444e..ddd9cb42a 100644 --- a/utils/apparmor/fail.py +++ b/utils/apparmor/fail.py @@ -15,6 +15,7 @@ from tempfile import NamedTemporaryFile from apparmor.common import error + # # Exception handling # @@ -27,7 +28,7 @@ def handle_exception(*exc_info): ''' (ex_cls, ex, tb) = exc_info - if ex_cls.__name__ == 'AppArmorException': # I didn't find a way to get this working with isinstance() :-/ + if ex_cls.__name__ == 'AppArmorException': # I didn't find a way to get this working with isinstance() :-/ print('', file=sys.stderr) error(ex.value) else: @@ -46,6 +47,7 @@ def handle_exception(*exc_info): print('Please consider reporting a bug at https://gitlab.com/apparmor/apparmor/-/issues', file=sys.stderr) print('and attach this file.', file=sys.stderr) + def enable_aa_exception_handler(): '''Setup handle_exception() as exception handler''' sys.excepthook = handle_exception diff --git a/utils/apparmor/logparser.py b/utils/apparmor/logparser.py index 7e170395d..ae475ec27 100644 --- a/utils/apparmor/logparser.py +++ b/utils/apparmor/logparser.py @@ -23,6 +23,7 @@ from apparmor.common import AppArmorException, AppArmorBug, hasher, open_file_re from apparmor.translations import init_translation _ = init_translation() + class ReadLog: # used to pre-filter log lines so that we hand over only relevant lines to LibAppArmor parsing @@ -32,7 +33,7 @@ class ReadLog: self.filename = filename self.profile_dir = profile_dir self.active_profiles = active_profiles - self.hashlog = { 'PERMITTING': {}, 'REJECTING': {} } # structure inside {}: {'profilename': init_hashlog(aamode, profilename), 'profilename2': init_hashlog(...), ...} + self.hashlog = {'PERMITTING': {}, 'REJECTING': {}} # structure inside {}: {'profilename': init_hashlog(aamode, profilename), 'profilename2': init_hashlog(...), ...} self.debug_logger = DebugLogger('ReadLog') self.LOG = None self.logmark = '' @@ -244,7 +245,7 @@ class ReadLog: return None elif e['operation'] == 'signal': - self.hashlog[aamode][full_profile]['signal'][e['peer']][e['denied_mask']][e['signal']]= True + self.hashlog[aamode][full_profile]['signal'][e['peer']][e['denied_mask']][e['signal']] = True return None elif e['operation'].startswith('dbus_'): @@ -285,7 +286,7 @@ class ReadLog: except AppArmorException as e: ex_msg = ('%(msg)s\n\nThis error was caused by the log line:\n%(logline)s' % - {'msg': e.value, 'logline': line}) + {'msg': e.value, 'logline': line}) raise AppArmorBug(ex_msg) from None self.logmark = '' @@ -296,43 +297,43 @@ class ReadLog: # (used by op_type() which checks some event details to decide) OP_TYPE_FILE_OR_NET = { # Note: op_type() also uses some startswith() checks which are not listed here! - 'create', - 'post_create', - 'bind', - 'connect', - 'listen', - 'accept', - 'sendmsg', - 'recvmsg', - 'getsockname', - 'getpeername', - 'getsockopt', - 'setsockopt', - 'socket_create', - 'sock_shutdown', - 'open', - 'truncate', - 'mkdir', - 'mknod', - 'chmod', - 'chown', - 'rename_src', - 'rename_dest', - 'unlink', - 'rmdir', - 'symlink', - 'symlink_create', - 'link', - 'sysctl', - 'getattr', - 'setattr', - 'xattr', + 'create', + 'post_create', + 'bind', + 'connect', + 'listen', + 'accept', + 'sendmsg', + 'recvmsg', + 'getsockname', + 'getpeername', + 'getsockopt', + 'setsockopt', + 'socket_create', + 'sock_shutdown', + 'open', + 'truncate', + 'mkdir', + 'mknod', + 'chmod', + 'chown', + 'rename_src', + 'rename_dest', + 'unlink', + 'rmdir', + 'symlink', + 'symlink_create', + 'link', + 'sysctl', + 'getattr', + 'setattr', + 'xattr', } def op_type(self, event): """Returns the operation type if known, unknown otherwise""" - if ( event['operation'].startswith('file_') or event['operation'].startswith('inode_') or event['operation'] in self.OP_TYPE_FILE_OR_NET ): + if (event['operation'].startswith('file_') or event['operation'].startswith('inode_') or event['operation'] in self.OP_TYPE_FILE_OR_NET): # file or network event? if event['family'] and event['protocol'] and event['sock_type']: # 'unix' events also use keywords like 'connect', but protocol is 0 and should therefore be filtered out diff --git a/utils/apparmor/notify.py b/utils/apparmor/notify.py index 3043b4737..5cce6959d 100644 --- a/utils/apparmor/notify.py +++ b/utils/apparmor/notify.py @@ -31,6 +31,7 @@ def sane_timestamp(timestamp): return True + def get_last_login_timestamp(username, filename='/var/log/wtmp'): '''Directly read wtmp and get last login for user as epoch timestamp''' timestamp = 0 @@ -48,10 +49,11 @@ def get_last_login_timestamp(username, filename='/var/log/wtmp'): # detect architecture based on utmp format differences wtmp_file.seek(340) # first possible timestamp position - timestamp_x86_64 = struct.unpack("L", wtmp_file.read(4))[0] - debug_logger.debug('WTMP timestamps: x86_64 %s, aarch64 %s, s390x %s' % (timestamp_x86_64, timestamp_aarch64, timestamp_s390x)) + timestamp_x86_64 = struct.unpack("L", wtmp_file.read(4))[0] # noqa: E221 + debug_logger.debug('WTMP timestamps: x86_64 %s, aarch64 %s, s390x %s' + % (timestamp_x86_64, timestamp_aarch64, timestamp_s390x)) if sane_timestamp(timestamp_x86_64): endianness = '<' # little endian @@ -66,7 +68,9 @@ def get_last_login_timestamp(username, filename='/var/log/wtmp'): extra_offset_before = 8 extra_offset_after = 8 else: - raise AppArmorBug('Your /var/log/wtmp is broken or has an unknown format. Please open a bugreport with /var/log/wtmp and the output of "last" attached!') + raise AppArmorBug( + 'Your /var/log/wtmp is broken or has an unknown format. ' + 'Please open a bugreport with /var/log/wtmp and the output of "last" attached!') while offset < wtmp_filesize: wtmp_file.seek(offset) diff --git a/utils/apparmor/profile_list.py b/utils/apparmor/profile_list.py index 274efb28d..0e6884e95 100644 --- a/utils/apparmor/profile_list.py +++ b/utils/apparmor/profile_list.py @@ -26,11 +26,11 @@ from apparmor.translations import init_translation _ = init_translation() preamble_ruletypes = { - 'abi': {'rule': AbiRule, 'ruleset': AbiRuleset }, - 'alias': {'rule': AliasRule, 'ruleset': AliasRuleset }, - 'inc_ie': {'rule': IncludeRule, 'ruleset': IncludeRuleset }, - 'variable': {'rule': VariableRule, 'ruleset': VariableRuleset }, - 'boolean': {'rule': BooleanRule, 'ruleset': BooleanRuleset }, + 'abi': {'rule': AbiRule, 'ruleset': AbiRuleset}, + 'alias': {'rule': AliasRule, 'ruleset': AliasRuleset}, + 'inc_ie': {'rule': IncludeRule, 'ruleset': IncludeRuleset}, + 'variable': {'rule': VariableRule, 'ruleset': VariableRuleset}, + 'boolean': {'rule': BooleanRule, 'ruleset': BooleanRuleset}, } header_rule_write_order = ('abi', 'alias', 'inc_ie', 'variable', 'boolean') # TODO: Dicts are ordered in Python 3.7+; use above dict's keys instead @@ -51,7 +51,7 @@ class ProfileList: self.profiles = {} # profile_name -> ProfileStorage def __repr__(self): - return('\n\n%s\n\n' % '\n'.join(self.files)) + return ('\n\n%s\n\n' % '\n'.join(self.files)) def init_file(self, filename): if self.files.get(filename): @@ -77,10 +77,14 @@ class ProfileList: raise AppArmorBug('Invalid profile type: %s' % type(prof_storage)) if profile_name in self.profile_names: - raise AppArmorException(_('Profile %(profile_name)s exists in %(filename)s and %(filename2)s' % {'profile_name': profile_name, 'filename': filename, 'filename2': self.profile_names[profile_name]})) + raise AppArmorException( + _('Profile %(profile_name)s exists in %(filename)s and %(filename2)s' + % {'profile_name': profile_name, 'filename': filename, 'filename2': self.profile_names[profile_name]})) if attachment in self.attachments: - raise AppArmorException(_('Profile for %(profile_name)s exists in %(filename)s and %(filename2)s' % {'profile_name': attachment, 'filename': filename, 'filename2': self.attachments[attachment]})) + raise AppArmorException( + _('Profile for %(profile_name)s exists in %(filename)s and %(filename2)s' + % {'profile_name': attachment, 'filename': filename, 'filename2': self.attachments[attachment]})) if profile_name: self.profile_names[profile_name] = filename @@ -98,7 +102,6 @@ class ProfileList: self.files[filename]['profiles'].append(attachment) self.profiles[attachment] = prof_storage - def add_rule(self, filename, ruletype, rule): ''' Store the given rule for the given profile filename preamble ''' @@ -202,7 +205,7 @@ class ProfileList: def filename_from_attachment(self, attachment): ''' Return profile filename for the given attachment/executable path, or None ''' - if not attachment.startswith( ('/', '@', '{') ): + if not attachment.startswith(('/', '@', '{')): raise AppArmorBug('Called filename_from_attachment with non-path attachment: %s' % attachment) # plain path @@ -248,8 +251,9 @@ class ProfileList: for var in inc_vars['=']: if merged_variables.get(var): - raise AppArmorException('While parsing %(profile)s: Conflicting variable definitions for variable %(var)s found in %(file1)s and %(file2)s.' % { - 'var': var, 'profile': filename, 'file1': set_in[var], 'file2': incname}) + raise AppArmorException( + 'While parsing %(profile)s: Conflicting variable definitions for variable %(var)s found in %(file1)s and %(file2)s.' + % {'var': var, 'profile': filename, 'file1': set_in[var], 'file2': incname}) else: merged_variables[var] = inc_vars['='][var] set_in[var] = incname @@ -264,8 +268,9 @@ class ProfileList: if merged_variables.get(var): merged_variables[var] |= inc_add[incname][var] else: - raise AppArmorException('While parsing %(profile)s: Variable %(var)s was not previously declared, but is being assigned additional value in file %(file)s.' % { - 'var': var, 'profile': filename, 'file': incname}) + raise AppArmorException( + 'While parsing %(profile)s: Variable %(var)s was not previously declared, but is being assigned additional value in file %(file)s.' + % {'var': var, 'profile': filename, 'file': incname}) return merged_variables diff --git a/utils/apparmor/profile_storage.py b/utils/apparmor/profile_storage.py index 481126538..5b5001331 100644 --- a/utils/apparmor/profile_storage.py +++ b/utils/apparmor/profile_storage.py @@ -16,16 +16,16 @@ from apparmor.common import AppArmorBug, AppArmorException -from apparmor.rule.abi import AbiRule, AbiRuleset -from apparmor.rule.capability import CapabilityRule, CapabilityRuleset -from apparmor.rule.change_profile import ChangeProfileRule, ChangeProfileRuleset -from apparmor.rule.dbus import DbusRule, DbusRuleset -from apparmor.rule.file import FileRule, FileRuleset -from apparmor.rule.include import IncludeRule, IncludeRuleset -from apparmor.rule.network import NetworkRule, NetworkRuleset -from apparmor.rule.ptrace import PtraceRule, PtraceRuleset -from apparmor.rule.rlimit import RlimitRule, RlimitRuleset -from apparmor.rule.signal import SignalRule, SignalRuleset +from apparmor.rule.abi import AbiRule, AbiRuleset +from apparmor.rule.capability import CapabilityRule, CapabilityRuleset +from apparmor.rule.change_profile import ChangeProfileRule, ChangeProfileRuleset +from apparmor.rule.dbus import DbusRule, DbusRuleset +from apparmor.rule.file import FileRule, FileRuleset +from apparmor.rule.include import IncludeRule, IncludeRuleset +from apparmor.rule.network import NetworkRule, NetworkRuleset +from apparmor.rule.ptrace import PtraceRule, PtraceRuleset +from apparmor.rule.rlimit import RlimitRule, RlimitRuleset +from apparmor.rule.signal import SignalRule, SignalRuleset from apparmor.rule import quote_if_needed @@ -36,18 +36,19 @@ from apparmor.translations import init_translation _ = init_translation() ruletypes = { - 'abi': {'rule': AbiRule, 'ruleset': AbiRuleset, }, - 'inc_ie': {'rule': IncludeRule, 'ruleset': IncludeRuleset, }, - 'capability': {'rule': CapabilityRule, 'ruleset': CapabilityRuleset, }, - 'change_profile': {'rule': ChangeProfileRule, 'ruleset': ChangeProfileRuleset, }, - 'dbus': {'rule': DbusRule, 'ruleset': DbusRuleset, }, - 'file': {'rule': FileRule, 'ruleset': FileRuleset, }, - 'network': {'rule': NetworkRule, 'ruleset': NetworkRuleset, }, - 'ptrace': {'rule': PtraceRule, 'ruleset': PtraceRuleset, }, - 'rlimit': {'rule': RlimitRule, 'ruleset': RlimitRuleset, }, - 'signal': {'rule': SignalRule, 'ruleset': SignalRuleset, }, + 'abi': {'rule': AbiRule, 'ruleset': AbiRuleset}, + 'inc_ie': {'rule': IncludeRule, 'ruleset': IncludeRuleset}, + 'capability': {'rule': CapabilityRule, 'ruleset': CapabilityRuleset}, + 'change_profile': {'rule': ChangeProfileRule, 'ruleset': ChangeProfileRuleset}, + 'dbus': {'rule': DbusRule, 'ruleset': DbusRuleset}, + 'file': {'rule': FileRule, 'ruleset': FileRuleset}, + 'network': {'rule': NetworkRule, 'ruleset': NetworkRuleset}, + 'ptrace': {'rule': PtraceRule, 'ruleset': PtraceRuleset}, + 'rlimit': {'rule': RlimitRule, 'ruleset': RlimitRuleset}, + 'signal': {'rule': SignalRule, 'ruleset': SignalRuleset}, } + class ProfileStorage: '''class to store the content (header, rules, comments) of a profilename @@ -63,18 +64,18 @@ class ProfileStorage: for rule in ruletypes: data[rule] = ruletypes[rule]['ruleset']() - data['filename'] = '' - data['logprof_suggest'] = '' # set in abstractions that should be suggested by aa-logprof - data['name'] = '' - data['attachment'] = '' - data['xattrs'] = '' - data['flags'] = '' - data['external'] = False - data['header_comment'] = '' # comment in the profile/hat start line - data['initial_comment'] = '' - data['profile_keyword'] = False - data['is_hat'] = False # profile or hat? - data['hat_keyword'] = False # True for 'hat foo', False for '^foo' + data['filename'] = '' + data['logprof_suggest'] = '' # set in abstractions that should be suggested by aa-logprof + data['name'] = '' + data['attachment'] = '' + data['xattrs'] = '' + data['flags'] = '' + data['external'] = False + data['header_comment'] = '' # comment in the profile/hat start line + data['initial_comment'] = '' + data['profile_keyword'] = False + data['is_hat'] = False # profile or hat? + data['hat_keyword'] = False # True for 'hat foo', False for '^foo' data['allow'] = dict() data['deny'] = dict() @@ -125,7 +126,7 @@ class ProfileStorage: raise AppArmorBug('Attempt to overwrite "%s" with %s, type %s' % (key, value, type(value))) def __repr__(self): - return('\n\n%s\n\n' % '\n'.join(self.get_rules_clean(1))) + return ('\n\n%s\n\n' % '\n'.join(self.get_rules_clean(1))) def get(self, key, fallback=None): if key in self.data: @@ -214,12 +215,14 @@ class ProfileStorage: if profile: # we are inside a profile, so we expect a child profile if not matches['profile_keyword']: - raise AppArmorException(_('%(profile)s profile in %(file)s contains syntax errors in line %(line)s: missing "profile" keyword.') % { - 'profile': profile, 'file': file, 'line': lineno + 1 }) + raise AppArmorException( + _('%(profile)s profile in %(file)s contains syntax errors in line %(line)s: missing "profile" keyword.') + % {'profile': profile, 'file': file, 'line': lineno + 1}) if hat is not None: # nesting limit reached - a child profile can't contain another child profile - raise AppArmorException(_('%(profile)s profile in %(file)s contains syntax errors in line %(line)s: a child profile inside another child profile is not allowed.') % { - 'profile': profile, 'file': file, 'line': lineno + 1 }) + raise AppArmorException( + _('%(profile)s profile in %(file)s contains syntax errors in line %(line)s: a child profile inside another child profile is not allowed.') + % {'profile': profile, 'file': file, 'line': lineno + 1}) hat = matches['profile'] pps_set_hat_external = False @@ -227,7 +230,9 @@ class ProfileStorage: else: # stand-alone profile profile = matches['profile'] if len(profile.split('//')) > 2: - raise AppArmorException("Nested child profiles ('%(profile)s', found in %(file)s) are not supported by the AppArmor tools yet." % {'profile': profile, 'file': file}) + raise AppArmorException( + "Nested child profiles ('%(profile)s', found in %(file)s) are not supported by the AppArmor tools yet." + % {'profile': profile, 'file': file}) elif len(profile.split('//')) == 2: profile, hat = profile.split('//') pps_set_hat_external = True @@ -265,6 +270,7 @@ def split_flags(flags): # sort and remove duplicates return sorted(set(flags_list)) + def add_or_remove_flag(flags, flags_to_change, set_flag): '''add (if set_flag is True) or remove the given flags_to_change to flags''' @@ -294,6 +300,7 @@ def var_transform(ref): data.append(quote_if_needed(value)) return ' '.join(data) + def write_mount_rules(prof_data, depth, allow): pre = ' ' * depth data = [] @@ -307,11 +314,13 @@ def write_mount_rules(prof_data, depth, allow): data.append('') return data + def write_mount(prof_data, depth): data = write_mount_rules(prof_data, depth, 'deny') data.extend(write_mount_rules(prof_data, depth, 'allow')) return data + def write_pivot_root_rules(prof_data, depth, allow): pre = ' ' * depth data = [] @@ -325,16 +334,19 @@ def write_pivot_root_rules(prof_data, depth, allow): 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 + def write_unix(prof_data, depth): data = write_unix_rules(prof_data, depth, 'deny') data.extend(write_unix_rules(prof_data, depth, 'allow')) return data + def write_unix_rules(prof_data, depth, allow): pre = ' ' * depth data = [] diff --git a/utils/apparmor/regex.py b/utils/apparmor/regex.py index 326877350..f1e544b46 100644 --- a/utils/apparmor/regex.py +++ b/utils/apparmor/regex.py @@ -20,54 +20,55 @@ from apparmor.common import AppArmorBug, AppArmorException from apparmor.translations import init_translation _ = init_translation() -## Profile parsing Regex -RE_AUDIT_DENY = '^\s*(?Paudit\s+)?(?Pallow\s+|deny\s+)?' # line start, optionally: leading whitespace, and /deny -RE_EOL = '\s*(?P#.*?)?\s*$' # optional whitespace, optional , optional whitespace, end of the line -RE_COMMA_EOL = '\s*,' + RE_EOL # optional whitespace, comma + RE_EOL +# Profile parsing Regex +RE_AUDIT_DENY = '^\s*(?Paudit\s+)?(?Pallow\s+|deny\s+)?' # line start, optionally: leading whitespace, and /deny +RE_EOL = '\s*(?P#.*?)?\s*$' # optional whitespace, optional , optional whitespace, end of the line +RE_COMMA_EOL = '\s*,' + RE_EOL # optional whitespace, comma + RE_EOL -RE_PROFILE_NAME = '(?P<%s>(\S+|"[^"]+"))' # string without spaces, or quoted string. %s is the match group name -RE_PATH = '/\S*|"/[^"]*"' # filename (starting with '/') without spaces, or quoted filename. -RE_PROFILE_PATH = '(?P<%s>(' + RE_PATH + '))' # quoted or unquoted filename. %s is the match group name -RE_PROFILE_PATH_OR_VAR = '(?P<%s>(' + RE_PATH + '|@{\S+}\S*|"@{\S+}[^"]*"))' # quoted or unquoted filename or variable. %s is the match group name -RE_SAFE_OR_UNSAFE = '(?P(safe|unsafe))' -RE_XATTRS = '(\s+xattrs\s*=\s*\((?P([^)=]+(=[^)=]+)?\s?)+)\)\s*)?' -RE_FLAGS = '(\s+(flags\s*=\s*)?\((?P[^)]+)\))?' +RE_PROFILE_NAME = '(?P<%s>(\S+|"[^"]+"))' # string without spaces, or quoted string. %s is the match group name +RE_PATH = '/\S*|"/[^"]*"' # filename (starting with '/') without spaces, or quoted filename. +RE_PROFILE_PATH = '(?P<%s>(' + RE_PATH + '))' # quoted or unquoted filename. %s is the match group name +RE_PROFILE_PATH_OR_VAR = '(?P<%s>(' + RE_PATH + '|@{\S+}\S*|"@{\S+}[^"]*"))' # quoted or unquoted filename or variable. %s is the match group name +RE_SAFE_OR_UNSAFE = '(?P(safe|unsafe))' +RE_XATTRS = '(\s+xattrs\s*=\s*\((?P([^)=]+(=[^)=]+)?\s?)+)\)\s*)?' +RE_FLAGS = '(\s+(flags\s*=\s*)?\((?P[^)]+)\))?' -RE_PROFILE_END = re.compile('^\s*\}' + RE_EOL) -RE_PROFILE_CAP = re.compile(RE_AUDIT_DENY + 'capability(?P(\s+\S+)+)?' + RE_COMMA_EOL) -RE_PROFILE_ALIAS = re.compile('^\s*alias\s+(?P"??.+?"??)\s+->\s*(?P"??.+?"??)' + RE_COMMA_EOL) -RE_PROFILE_RLIMIT = re.compile('^\s*set\s+rlimit\s+(?P[a-z]+)\s*<=\s*(?P[^ ]+(\s+[a-zA-Z]+)?)' + RE_COMMA_EOL) -RE_PROFILE_BOOLEAN = re.compile('^\s*(?P\$\{?\w*\}?)\s*=\s*(?Ptrue|false)\s*,?' + RE_EOL, flags=re.IGNORECASE) -RE_PROFILE_VARIABLE = re.compile('^\s*(?P@\{?\w+\}?)\s*(?P\+?=)\s*(?P@*.+?)' + RE_EOL) -RE_PROFILE_CONDITIONAL = re.compile('^\s*if\s+(not\s+)?(\$\{?\w*\}?)\s*\{' + RE_EOL) +RE_PROFILE_END = re.compile('^\s*\}' + RE_EOL) +RE_PROFILE_CAP = re.compile(RE_AUDIT_DENY + 'capability(?P(\s+\S+)+)?' + RE_COMMA_EOL) +RE_PROFILE_ALIAS = re.compile('^\s*alias\s+(?P"??.+?"??)\s+->\s*(?P"??.+?"??)' + RE_COMMA_EOL) +RE_PROFILE_RLIMIT = re.compile('^\s*set\s+rlimit\s+(?P[a-z]+)\s*<=\s*(?P[^ ]+(\s+[a-zA-Z]+)?)' + RE_COMMA_EOL) +RE_PROFILE_BOOLEAN = re.compile('^\s*(?P\$\{?\w*\}?)\s*=\s*(?Ptrue|false)\s*,?' + RE_EOL, flags=re.IGNORECASE) +RE_PROFILE_VARIABLE = re.compile('^\s*(?P@\{?\w+\}?)\s*(?P\+?=)\s*(?P@*.+?)' + RE_EOL) +RE_PROFILE_CONDITIONAL = re.compile('^\s*if\s+(not\s+)?(\$\{?\w*\}?)\s*\{' + RE_EOL) RE_PROFILE_CONDITIONAL_VARIABLE = re.compile('^\s*if\s+(not\s+)?defined\s+(@\{?\w+\}?)\s*\{\s*(#.*)?$') RE_PROFILE_CONDITIONAL_BOOLEAN = re.compile('^\s*if\s+(not\s+)?defined\s+(\$\{?\w+\}?)\s*\{\s*(#.*)?$') -RE_PROFILE_NETWORK = re.compile(RE_AUDIT_DENY + 'network(?P
\s+.*)?' + RE_COMMA_EOL) -RE_PROFILE_CHANGE_HAT = re.compile('^\s*\^(\"??.+?\"??)' + RE_COMMA_EOL) -RE_PROFILE_HAT_DEF = re.compile('^(?P\s*)(?P\^|hat\s+)(?P\"??[^)]+?\"??)' + RE_FLAGS + '\s*\{' + RE_EOL) -RE_PROFILE_DBUS = re.compile(RE_AUDIT_DENY + '(dbus\s*,|dbus(?P
\s+[^#]*)\s*,)' + RE_EOL) -RE_PROFILE_MOUNT = re.compile(RE_AUDIT_DENY + '((mount|remount|umount|unmount)(\s+[^#]*)?\s*,)' + RE_EOL) -RE_PROFILE_SIGNAL = re.compile(RE_AUDIT_DENY + '(signal\s*,|signal(?P
\s+[^#]*)\s*,)' + RE_EOL) -RE_PROFILE_PTRACE = re.compile(RE_AUDIT_DENY + '(ptrace\s*,|ptrace(?P
\s+[^#]*)\s*,)' + RE_EOL) -RE_PROFILE_PIVOT_ROOT = re.compile(RE_AUDIT_DENY + '(pivot_root\s*,|pivot_root\s+[^#]*\s*,)' + RE_EOL) -RE_PROFILE_UNIX = re.compile(RE_AUDIT_DENY + '(unix\s*,|unix\s+[^#]*\s*,)' + RE_EOL) +RE_PROFILE_NETWORK = re.compile(RE_AUDIT_DENY + 'network(?P
\s+.*)?' + RE_COMMA_EOL) +RE_PROFILE_CHANGE_HAT = re.compile('^\s*\^(\"??.+?\"??)' + RE_COMMA_EOL) +RE_PROFILE_HAT_DEF = re.compile('^(?P\s*)(?P\^|hat\s+)(?P\"??[^)]+?\"??)' + RE_FLAGS + '\s*\{' + RE_EOL) +RE_PROFILE_DBUS = re.compile(RE_AUDIT_DENY + '(dbus\s*,|dbus(?P
\s+[^#]*)\s*,)' + RE_EOL) +RE_PROFILE_MOUNT = re.compile(RE_AUDIT_DENY + '((mount|remount|umount|unmount)(\s+[^#]*)?\s*,)' + RE_EOL) +RE_PROFILE_SIGNAL = re.compile(RE_AUDIT_DENY + '(signal\s*,|signal(?P
\s+[^#]*)\s*,)' + RE_EOL) +RE_PROFILE_PTRACE = re.compile(RE_AUDIT_DENY + '(ptrace\s*,|ptrace(?P
\s+[^#]*)\s*,)' + RE_EOL) +RE_PROFILE_PIVOT_ROOT = re.compile(RE_AUDIT_DENY + '(pivot_root\s*,|pivot_root\s+[^#]*\s*,)' + RE_EOL) +RE_PROFILE_UNIX = re.compile(RE_AUDIT_DENY + '(unix\s*,|unix\s+[^#]*\s*,)' + RE_EOL) # match anything that's not " or #, or matching quotes with anything except quotes inside __re_no_or_quoted_hash = '([^#"]|"[^"]*")*' -RE_RULE_HAS_COMMA = re.compile('^' + __re_no_or_quoted_hash + +RE_RULE_HAS_COMMA = re.compile( + '^' + __re_no_or_quoted_hash + ',\s*(#.*)?$') # match comma plus any trailing comment -RE_HAS_COMMENT_SPLIT = re.compile('^(?P' + __re_no_or_quoted_hash + ')' + # store in 'not_comment' group +RE_HAS_COMMENT_SPLIT = re.compile( + '^(?P' + __re_no_or_quoted_hash + ')' + # store in 'not_comment' group '(?P#.*)$') # match trailing comment and store in 'comment' group - -RE_PROFILE_START = re.compile( +RE_PROFILE_START = re.compile( '^(?P\s*)' + '(' + - RE_PROFILE_PATH_OR_VAR % 'plainprofile' + # just a path - '|' + # or - '(' + 'profile' + '\s+' + RE_PROFILE_NAME % 'namedprofile' + '(\s+' + RE_PROFILE_PATH_OR_VAR % 'attachment' + ')?' + ')' + # 'profile', profile name, optionally attachment + RE_PROFILE_PATH_OR_VAR % 'plainprofile' + # just a path + '|' + # or + '(' + 'profile' + '\s+' + RE_PROFILE_NAME % 'namedprofile' + '(\s+' + RE_PROFILE_PATH_OR_VAR % 'attachment' + ')?' + ')' + # 'profile', profile name, optionally attachment ')' + RE_XATTRS + RE_FLAGS + @@ -93,7 +94,7 @@ RE_PROFILE_FILE_ENTRY = re.compile( '(?Powner\s+)?' + # optionally: '(' + '(?Pfile)' + # bare 'file,' - '|' + # or + '|' + # or '(?Pfile\s+)?' + # optional 'file' keyword '(' + RE_PROFILE_PATH_OR_VAR % 'path' + '\s+' + RE_PATH_PERMS % 'perms' + # path and perms @@ -101,7 +102,7 @@ RE_PROFILE_FILE_ENTRY = re.compile( RE_PATH_PERMS % 'perms2' + '\s+' + RE_PROFILE_PATH_OR_VAR % 'path2' + # perms and path ')' + '(\s+->\s*' + RE_PROFILE_NAME % 'target' + ')?' + - '|' + # or + '|' + # or '(?Plink\s+)' + # 'link' keyword '(?Psubset\s+)?' + # optional 'subset' keyword RE_PROFILE_PATH_OR_VAR % 'link_path' + # path @@ -112,9 +113,9 @@ RE_PROFILE_FILE_ENTRY = re.compile( def parse_profile_start_line(line, filename): - common_sections = [ 'leadingspace', 'flags', 'comment'] + common_sections = ['leadingspace', 'flags', 'comment'] - sections = [ 'plainprofile', 'namedprofile', 'attachment', 'xattrs'] + common_sections + sections = ['plainprofile', 'namedprofile', 'attachment', 'xattrs'] + common_sections matches = RE_PROFILE_START.search(line) if not matches: @@ -122,7 +123,8 @@ def parse_profile_start_line(line, filename): matches = RE_PROFILE_HAT_DEF.search(line) if not matches: - raise AppArmorBug('The given line from file %(filename)s is not the start of a profile: %(line)s' % { 'filename': filename, 'line': line } ) + raise AppArmorBug('The given line from file %(filename)s is not the start of a profile: %(line)s' + % {'filename': filename, 'line': line}) result = {} @@ -137,7 +139,9 @@ def parse_profile_start_line(line, filename): result[section] = None if result['flags'] and result['flags'].strip() == '': - raise AppArmorException(_('Invalid syntax in %(filename)s: Empty set of flags in line %(line)s.' % { 'filename': filename, 'line': line } )) + raise AppArmorException( + _('Invalid syntax in %(filename)s: Empty set of flags in line %(line)s.' + % {'filename': filename, 'line': line})) result['is_hat'] = False if result.get('hat'): @@ -157,10 +161,12 @@ def parse_profile_start_line(line, filename): return result + RE_MAGIC_OR_QUOTED_PATH = '(<(?P.*)>|"(?P.*)"|(?P[^<>"]*))' RE_ABI = re.compile('^\s*#?abi\s*' + RE_MAGIC_OR_QUOTED_PATH + RE_COMMA_EOL) RE_INCLUDE = re.compile('^\s*#?include(?P\s+if\s+exists)?\s*' + RE_MAGIC_OR_QUOTED_PATH + RE_EOL) + def re_match_include_parse(line, rule_name): '''Matches the path for include, include if exists and abi rules @@ -215,6 +221,7 @@ def re_match_include_parse(line, rule_name): return path, ifexists, ismagic + def re_match_include(line): ''' return path of a 'include' rule ''' (path, ifexists, ismagic) = re_match_include_parse(line, 'include') @@ -224,6 +231,7 @@ def re_match_include(line): return None + def strip_parenthesis(data): '''strips parenthesis from the given string and returns the strip()ped result. The parenthesis must be the first and last char, otherwise they won't be removed. @@ -234,6 +242,7 @@ def strip_parenthesis(data): else: return data.strip() + def strip_quotes(data): if len(data) > 1 and data[0] + data[-1] == '""': return data[1:-1] diff --git a/utils/apparmor/rule/__init__.py b/utils/apparmor/rule/__init__.py index 8aa0ae398..8b5656fee 100644 --- a/utils/apparmor/rule/__init__.py +++ b/utils/apparmor/rule/__init__.py @@ -517,12 +517,13 @@ def check_and_split_list(lst, allowed_keywords, all_obj, classname, keyword_name if not item.strip(): raise AppArmorBug( 'Passed empty %(keyword_name)s to %(classname)s' % - {'keyword_name': keyword_name, 'classname': classname}) + {'keyword_name': keyword_name, 'classname': classname}) if item not in allowed_keywords: unknown_items.add(item) return result_list, False, unknown_items + def logprof_value_or_all(value, all_values): '''helper for logprof_header() to return 'all' (if all_values is True) or the specified value. For some types, the value is made more readable.''' @@ -537,6 +538,7 @@ def logprof_value_or_all(value, all_values): else: return value + def parse_comment(matches): '''returns the comment (with a leading space) from the matches object''' comment = '' @@ -545,6 +547,7 @@ def parse_comment(matches): comment = ' %s' % matches.group('comment') return comment + def parse_modifiers(matches): '''returns audit, deny, allow_keyword and comment from the matches object - audit, deny and allow_keyword are True/False @@ -569,9 +572,9 @@ def parse_modifiers(matches): return (audit, deny, allow_keyword, comment) + def quote_if_needed(data): '''quote data if it contains whitespace''' if ' ' in data: data = '"' + data + '"' return data - diff --git a/utils/apparmor/rule/abi.py b/utils/apparmor/rule/abi.py index 60370cf2d..06f145bc1 100644 --- a/utils/apparmor/rule/abi.py +++ b/utils/apparmor/rule/abi.py @@ -20,6 +20,7 @@ from apparmor.rule.include import IncludeRule, IncludeRuleset from apparmor.translations import init_translation _ = init_translation() + # abi and include rules have a very similar syntax # base AbiRule on IncludeRule to inherit most of its behaviour class AbiRule(IncludeRule): @@ -48,14 +49,12 @@ class AbiRule(IncludeRule): space = ' ' * depth if self.ismagic: - return('%s%s <%s>,%s' % (space, self.rule_name, self.path, self.comment)) + return ('%s%s <%s>,%s' % (space, self.rule_name, self.path, self.comment)) else: - return('%s%s "%s",%s' % (space, self.rule_name, self.path, self.comment)) + return ('%s%s "%s",%s' % (space, self.rule_name, self.path, self.comment)) def logprof_header_localvars(self): - return [ - _('Abi'), self.get_clean(), - ] + return [_('Abi'), self.get_clean()] class AbiRuleset(IncludeRuleset): diff --git a/utils/apparmor/rule/alias.py b/utils/apparmor/rule/alias.py index 781385428..3cbe3fc66 100644 --- a/utils/apparmor/rule/alias.py +++ b/utils/apparmor/rule/alias.py @@ -72,8 +72,8 @@ class AliasRule(BaseRule): orig_path = strip_quotes(matches.group('orig_path').strip()) target = strip_quotes(matches.group('target').strip()) - return AliasRule( - orig_path, target, audit=False, deny=False, allow_keyword=False, comment=comment) + return AliasRule(orig_path, target, + audit=False, deny=False, allow_keyword=False, comment=comment) def get_clean(self, depth=0): '''return rule (in clean/default formatting)''' @@ -109,6 +109,7 @@ class AliasRule(BaseRule): _('Alias'), '%s -> %s' % (self.orig_path, self.target), ] + class AliasRuleset(BaseRuleset): '''Class to handle and store a collection of alias rules''' pass diff --git a/utils/apparmor/rule/boolean.py b/utils/apparmor/rule/boolean.py index a0d4dc44d..1aee98454 100644 --- a/utils/apparmor/rule/boolean.py +++ b/utils/apparmor/rule/boolean.py @@ -116,6 +116,7 @@ class BooleanRule(BaseRule): _('Boolean Variable'), self.get_clean(), ] + class BooleanRuleset(BaseRuleset): '''Class to handle and store a collection of variable rules''' @@ -127,6 +128,7 @@ class BooleanRuleset(BaseRuleset): for knownrule in self.rules: if rule.varname == knownrule.varname: - raise AppArmorException(_('Redefining existing variable %(variable)s: %(value)s') % { 'variable': rule.varname, 'value': rule.value }) + raise AppArmorException(_('Redefining existing variable %(variable)s: %(value)s') + % {'variable': rule.varname, 'value': rule.value}) super().add(rule, cleanup) diff --git a/utils/apparmor/rule/capability.py b/utils/apparmor/rule/capability.py index 904ab9839..6280458a8 100644 --- a/utils/apparmor/rule/capability.py +++ b/utils/apparmor/rule/capability.py @@ -91,11 +91,11 @@ class CapabilityRule(BaseRule): space = ' ' * depth if self.all_caps: - return('%s%scapability,%s' % (space, self.modifiers_str(), self.comment)) + return ('%s%scapability,%s' % (space, self.modifiers_str(), self.comment)) else: caps = ' '.join(self.capability).strip() # XXX return multiple lines, one for each capability, instead? if caps: - return('%s%scapability %s,%s' % (space, self.modifiers_str(), ' '.join(sorted(self.capability)), self.comment)) + return ('%s%scapability %s,%s' % (space, self.modifiers_str(), ' '.join(sorted(self.capability)), self.comment)) else: raise AppArmorBug("Empty capability rule") diff --git a/utils/apparmor/rule/change_profile.py b/utils/apparmor/rule/change_profile.py index 60c0ad598..7f0cffac5 100644 --- a/utils/apparmor/rule/change_profile.py +++ b/utils/apparmor/rule/change_profile.py @@ -34,7 +34,7 @@ class ChangeProfileRule(BaseRule): rule_name = 'change_profile' - equiv_execmodes = [ 'safe', '', None ] + equiv_execmodes = ['safe', '', None] def __init__(self, execmode, execcond, targetprofile, audit=False, deny=False, allow_keyword=False, comment='', log_event=None): @@ -106,7 +106,7 @@ class ChangeProfileRule(BaseRule): targetprofile = ChangeProfileRule.ALL return ChangeProfileRule(execmode, execcond, targetprofile, - audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment) + audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment) def get_clean(self, depth=0): '''return rule (in clean/default formatting)''' @@ -132,14 +132,14 @@ class ChangeProfileRule(BaseRule): else: raise AppArmorBug('Empty target profile in change_profile rule') - return('%s%schange_profile%s%s%s,%s' % (space, self.modifiers_str(), execmode, execcond, targetprofile, self.comment)) + return ('%s%schange_profile%s%s%s,%s' % (space, self.modifiers_str(), execmode, execcond, targetprofile, self.comment)) def is_covered_localvars(self, other_rule): '''check if other_rule is covered by this rule object''' if self.execmode != other_rule.execmode and \ - (self.execmode not in ChangeProfileRule.equiv_execmodes or \ - other_rule.execmode not in ChangeProfileRule.equiv_execmodes): + (self.execmode not in ChangeProfileRule.equiv_execmodes or \ + other_rule.execmode not in ChangeProfileRule.equiv_execmodes): return False if not self._is_covered_plain(self.execcond, self.all_execconds, other_rule.execcond, other_rule.all_execconds, 'exec condition'): @@ -159,8 +159,8 @@ class ChangeProfileRule(BaseRule): raise AppArmorBug('Passed non-change_profile rule: %s' % str(rule_obj)) if self.execmode != rule_obj.execmode and \ - (self.execmode not in ChangeProfileRule.equiv_execmodes or \ - rule_obj.execmode not in ChangeProfileRule.equiv_execmodes): + (self.execmode not in ChangeProfileRule.equiv_execmodes or \ + rule_obj.execmode not in ChangeProfileRule.equiv_execmodes): return False if (self.execcond != rule_obj.execcond @@ -179,8 +179,8 @@ class ChangeProfileRule(BaseRule): if self.execmode: headers.extend((_('Exec Mode'), self.execmode)) - execcond_txt = logprof_value_or_all(self.execcond, self.all_execconds) - targetprofiles_txt = logprof_value_or_all(self.targetprofile, self.all_targetprofiles) + execcond_txt = logprof_value_or_all(self.execcond, self.all_execconds) # noqa: E221 + targetprofiles_txt = logprof_value_or_all(self.targetprofile, self.all_targetprofiles) headers.extend(( _('Exec Condition'), execcond_txt, @@ -188,6 +188,7 @@ class ChangeProfileRule(BaseRule): )) return headers + class ChangeProfileRuleset(BaseRuleset): '''Class to handle and store a collection of change_profile rules''' diff --git a/utils/apparmor/rule/dbus.py b/utils/apparmor/rule/dbus.py index 6273e313b..4ff0dd81e 100644 --- a/utils/apparmor/rule/dbus.py +++ b/utils/apparmor/rule/dbus.py @@ -24,22 +24,23 @@ _ = init_translation() message_keywords = ['send', 'receive', 'r', 'read', 'w', 'write', 'rw'] -access_keywords = [ 'bind', 'eavesdrop' ] + message_keywords +access_keywords = ['bind', 'eavesdrop'] + message_keywords # XXX joint_access_keyword and RE_ACCESS_KEYWORDS exactly as in SignalRule - move to function? joint_access_keyword = '(' + '(\s|,)*' + '(' + '|'.join(access_keywords) + ')(\s|,)*' + ')' -RE_ACCESS_KEYWORDS = ( joint_access_keyword + # one of the access_keyword or - '|' + # or - '\(' + '(\s|,)*' + joint_access_keyword + '?' + '(' + '(\s|,)+' + joint_access_keyword + ')*' + '\)' # one or more access_keyword in (...) - ) +RE_ACCESS_KEYWORDS = ( + joint_access_keyword # one of the access_keyword or + + '|' # or + + '\(' + '(\s|,)*' + joint_access_keyword + '?' + '(' + '(\s|,)+' + joint_access_keyword + ')*' + '\)' # one or more access_keyword in (...) +) -RE_FLAG = '(?P<%s>(\S+|"[^"]+"|\(\s*\S+\s*\)|\(\s*"[^"]+"\)\s*))' # string without spaces, or quoted string, optionally wrapped in (...). %s is the match group name +RE_FLAG = '(?P<%s>(\S+|"[^"]+"|\(\s*\S+\s*\)|\(\s*"[^"]+"\)\s*))' # string without spaces, or quoted string, optionally wrapped in (...). %s is the match group name # plaintext version: | * | "* " | ( * ) | ( " * " ) | # XXX this regex will allow repeated parameters, last one wins # XXX (the parser will reject such rules) -RE_DBUS_DETAILS = re.compile( +RE_DBUS_DETAILS = re.compile( '^' + '(\s+(?P' + RE_ACCESS_KEYWORDS + '))?' + # optional access keyword(s) '((\s+(bus\s*=\s*' + RE_FLAG % 'bus' + '))?|' + # optional bus= system | session | AARE, (...) optional @@ -47,7 +48,7 @@ RE_DBUS_DETAILS = re.compile( '(\s+(name\s*=\s*' + RE_FLAG % 'name' + '))?|' + # optional name=AARE, (...) optional '(\s+(interface\s*=\s*' + RE_FLAG % 'interface' + '))?|' + # optional interface=AARE, (...) optional '(\s+(member\s*=\s*' + RE_FLAG % 'member' + '))?|' + # optional member=AARE, (...) optional - '(\s+(peer\s*=\s*\((,|\s)*' + # optional peer=( name=AARE and/or label=AARE ), (...) required + '(\s+(peer\s*=\s*\((,|\s)*' + # optional peer=(name=AARE and/or label=AARE), (...) required '(' + '(' + '(,|\s)*' + ')' + # empty peer=() '|' # or @@ -85,14 +86,14 @@ class DbusRule(BaseRule): if unknown_items: raise AppArmorException(_('Passed unknown access keyword to DbusRule: %s') % ' '.join(unknown_items)) - # rulepart partname is_path log_event - self.bus, self.all_buses = self._aare_or_all(bus, 'bus', False, log_event) - self.path, self.all_paths = self._aare_or_all(path, 'path', True, log_event) - self.name, self.all_names = self._aare_or_all(name, 'name', False, log_event) - self.interface, self.all_interfaces = self._aare_or_all(interface, 'interface', False, log_event) - self.member, self.all_members = self._aare_or_all(member, 'member', False, log_event) - self.peername, self.all_peernames = self._aare_or_all(peername, 'peer name', False, log_event) - self.peerlabel, self.all_peerlabels = self._aare_or_all(peerlabel, 'peer label', False, log_event) + # rulepart partname is_path log_event + self.bus, self.all_buses = self._aare_or_all(bus, 'bus', False, log_event) + self.path, self.all_paths = self._aare_or_all(path, 'path', True, log_event) + self.name, self.all_names = self._aare_or_all(name, 'name', False, log_event) + self.interface, self.all_interfaces = self._aare_or_all(interface, 'interface', False, log_event) + self.member, self.all_members = self._aare_or_all(member, 'member', False, log_event) + self.peername, self.all_peernames = self._aare_or_all(peername, 'peer name', False, log_event) + self.peerlabel, self.all_peerlabels = self._aare_or_all(peerlabel, 'peer label', False, log_event) # not all combinations are allowed if self.access and 'bind' in self.access and (self.path or self.interface or self.member or self.peername or self.peerlabel): @@ -219,7 +220,7 @@ class DbusRule(BaseRule): if peer: peer = ' peer=(%s)' % peer.strip() - return('%s%sdbus%s%s%s%s%s%s%s,%s' % (space, self.modifiers_str(), access, bus, path, name, interface, member, peer, self.comment)) + return ('%s%sdbus%s%s%s%s%s%s%s,%s' % (space, self.modifiers_str(), access, bus, path, name, interface, member, peer, self.comment)) def _get_aare_rule_part(self, prefix, value, all_values): '''helper function to write a rule part @@ -231,7 +232,6 @@ class DbusRule(BaseRule): else: raise AppArmorBug('Empty %(prefix_name)s in %(rule_name)s rule' % {'prefix_name': prefix, 'rule_name': self.rule_name}) - def is_covered_localvars(self, other_rule): '''check if other_rule is covered by this rule object''' @@ -262,7 +262,6 @@ class DbusRule(BaseRule): # still here? -> then it is covered return True - def is_equal_localvars(self, rule_obj, strict): '''compare if rule-specific variables are equal''' diff --git a/utils/apparmor/rule/file.py b/utils/apparmor/rule/file.py index 2dbc7ae1c..e0a1976c5 100644 --- a/utils/apparmor/rule/file.py +++ b/utils/apparmor/rule/file.py @@ -22,11 +22,10 @@ from apparmor.translations import init_translation _ = init_translation() -allow_exec_transitions = ('ix', 'ux', 'Ux', 'px', 'Px', 'cx', 'Cx') # 2 chars - len relevant for split_perms() +allow_exec_transitions = ('ix', 'ux', 'Ux', 'px', 'Px', 'cx', 'Cx') # 2 chars - len relevant for split_perms() allow_exec_fallback_transitions = ('pix', 'Pix', 'cix', 'Cix', 'pux', 'PUx', 'cux', 'CUx') # 3 chars - len relevant for split_perms() -deny_exec_transitions = ('x') -file_permissions = ('m', 'r', 'w', 'a', 'l', 'k', 'link', 'subset') # also defines the write order - +deny_exec_transitions = ('x') +file_permissions = ('m', 'r', 'w', 'a', 'l', 'k', 'link', 'subset') # also defines the write order class FileRule(BaseRule): @@ -36,6 +35,7 @@ class FileRule(BaseRule): # should reference the class field FileRule.ALL class __FileAll: pass + class __FileAnyExec: pass @@ -45,7 +45,7 @@ class FileRule(BaseRule): rule_name = 'file' def __init__(self, path, perms, exec_perms, target, owner, file_keyword=False, leading_perms=False, - audit=False, deny=False, allow_keyword=False, comment='', log_event=None): + audit=False, deny=False, allow_keyword=False, comment='', log_event=None): '''Initialize FileRule Parameters: @@ -61,9 +61,9 @@ class FileRule(BaseRule): super().__init__(audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment, log_event=log_event) - # rulepart partperms is_path log_event - self.path, self.all_paths = self._aare_or_all(path, 'path', True, log_event) - self.target, self.all_targets, = self._aare_or_all(target, 'target', False, log_event) + # rulepart partperms is_path log_event + self.path, self.all_paths = self._aare_or_all(path, 'path', True, log_event) + self.target, self.all_targets = self._aare_or_all(target, 'target', False, log_event) self.can_glob = not self.all_paths self.can_glob_ext = not self.all_paths @@ -82,7 +82,8 @@ class FileRule(BaseRule): self.perms = perms self.all_perms = False else: - self.perms, self.all_perms, unknown_items = check_and_split_list(perms, file_permissions, FileRule.ALL, 'FileRule', 'permissions', allow_empty_list=True) + self.perms, self.all_perms, unknown_items = check_and_split_list( + perms, file_permissions, FileRule.ALL, 'FileRule', 'permissions', allow_empty_list=True) if unknown_items: raise AppArmorBug('Passed unknown perms to FileRule: %s' % str(unknown_items)) if self.perms and 'a' in self.perms and 'w' in self.perms: @@ -232,9 +233,9 @@ class FileRule(BaseRule): file_keyword = '' if self.all_paths and self.all_perms and not path and not perms and not target: - return('%s%s%sfile,%s' % (space, self.modifiers_str(), owner, self.comment)) # plain 'file,' rule + return ('%s%s%sfile,%s' % (space, self.modifiers_str(), owner, self.comment)) # plain 'file,' rule elif not self.all_paths and not self.all_perms and path and perms: - return('%s%s%s%s%s%s,%s' % (space, self.modifiers_str(), file_keyword, owner, path_and_perms, target, self.comment)) + return ('%s%s%s%s%s%s,%s' % (space, self.modifiers_str(), file_keyword, owner, path_and_perms, target, self.comment)) else: raise AppArmorBug('Invalid combination of path and perms in file rule - either specify path and perms, or none of them') @@ -261,7 +262,7 @@ class FileRule(BaseRule): def is_covered_localvars(self, other_rule): '''check if other_rule is covered by this rule object''' - if not self._is_covered_aare(self.path, self.all_paths, other_rule.path, other_rule.all_paths, 'path'): + if not self._is_covered_aare(self.path, self.all_paths, other_rule.path, other_rule.all_paths, 'path'): return False if self.perms and 'subset' in self.perms and other_rule.perms and 'subset' not in other_rule.perms: @@ -293,7 +294,7 @@ class FileRule(BaseRule): if (other_rule.exec_perms and other_rule.exec_perms != self.ANY_EXEC) or \ (other_rule.perms and 'l' in other_rule.perms) or \ (other_rule.perms and 'link' in other_rule.perms): - if not self._is_covered_aare(self.target, self.all_targets, other_rule.target, other_rule.all_targets, 'target'): + if not self._is_covered_aare(self.target, self.all_targets, other_rule.target, other_rule.all_targets, 'target'): return False # a different target means running with a different profile, therefore we have to be more strict than _is_covered_aare() @@ -309,7 +310,6 @@ class FileRule(BaseRule): # still here? -> then it is covered return True - def is_equal_localvars(self, rule_obj, strict): '''compare if rule-specific variables are equal''' @@ -319,7 +319,7 @@ class FileRule(BaseRule): if self.owner != rule_obj.owner: return False - if not self._is_equal_aare(self.path, self.all_paths, rule_obj.path, rule_obj.all_paths, 'path'): + if not self._is_equal_aare(self.path, self.all_paths, rule_obj.path, rule_obj.all_paths, 'path'): return False if self.perms != rule_obj.perms: @@ -331,7 +331,7 @@ class FileRule(BaseRule): if self.exec_perms != rule_obj.exec_perms: return False - if not self._is_equal_aare(self.target, self.all_targets, rule_obj.target, rule_obj.all_targets, 'target'): + if not self._is_equal_aare(self.target, self.all_targets, rule_obj.target, rule_obj.all_targets, 'target'): return False if strict: # file_keyword and leading_perms are only cosmetics, but still a difference @@ -367,7 +367,8 @@ class FileRule(BaseRule): old_mode = '' if self.original_perms: original_perms_all = self._join_given_perms(self.original_perms['allow']['all'], None) - original_perms_owner = self._join_given_perms(self.original_perms['allow']['owner'] - self.original_perms['allow']['all'], None) # only list owner perms that are not covered by other perms + original_perms_owner = self._join_given_perms( + self.original_perms['allow']['owner'] - self.original_perms['allow']['all'], None) # only list owner perms that are not covered by other perms if original_perms_all and original_perms_owner: old_mode = '%s + owner %s' % (original_perms_all, original_perms_owner) @@ -418,7 +419,7 @@ class FileRule(BaseRule): if self.all_paths: raise AppArmorBug('Attemp to edit bare file rule') - return(_('Enter new path: '), self.path.regex) + return (_('Enter new path: '), self.path.regex) def validate_edit(self, newpath): if self.all_paths: @@ -461,17 +462,17 @@ class FileRuleset(BaseRuleset): 'path': involved_paths} Note: exec rules and exec/link target are not honored! ''' - # XXX do we need to honor the link target? + # XXX do we need to honor the link target? perms = { - 'allow': {'owner': set(), 'all': set() }, - 'deny': {'owner': set(), 'all': set() }, + 'allow': {'owner': set(), 'all': set()}, + 'deny': {'owner': set(), 'all': set()}, } all_perms = { - 'allow': {'owner': False, 'all': False }, - 'deny': {'owner': False, 'all': False }, + 'allow': {'owner': False, 'all': False}, + 'deny': {'owner': False, 'all': False}, } - paths = set() + paths = set() matching_rules = self.get_rules_for_path(path, audit, deny) @@ -535,7 +536,6 @@ class FileRuleset(BaseRuleset): return conflictingrules - def split_perms(perm_string, deny): '''parse permission string - perm_string: the permission string to parse @@ -568,6 +568,7 @@ def split_perms(perm_string, deny): return perms, exec_mode + def perms_with_a(perms): '''if perms includes 'w', add 'a' perms - perms: the original permissions diff --git a/utils/apparmor/rule/include.py b/utils/apparmor/rule/include.py index d697baeb7..a3654d04b 100644 --- a/utils/apparmor/rule/include.py +++ b/utils/apparmor/rule/include.py @@ -69,7 +69,8 @@ class IncludeRule(BaseRule): # TODO: move re_match_include_parse() from regex.py to this class after converting all code to use IncludeRule path, ifexists, ismagic = re_match_include_parse(raw_rule, cls.rule_name) - return cls(path, ifexists, ismagic, audit=False, deny=False, allow_keyword=False, comment=comment) + return cls(path, ifexists, ismagic, + audit=False, deny=False, allow_keyword=False, comment=comment) def get_clean(self, depth=0): '''return rule (in clean/default formatting)''' @@ -81,9 +82,9 @@ class IncludeRule(BaseRule): ifexists_txt = ' if exists' if self.ismagic: - return('%s%s%s <%s>%s' % (space, self.rule_name, ifexists_txt, self.path, self.comment)) + return ('%s%s%s <%s>%s' % (space, self.rule_name, ifexists_txt, self.path, self.comment)) else: - return('%s%s%s "%s"%s' % (space, self.rule_name, ifexists_txt, self.path, self.comment)) + return ('%s%s%s "%s"%s' % (space, self.rule_name, ifexists_txt, self.path, self.comment)) def is_covered_localvars(self, other_rule): '''check if other_rule is covered by this rule object''' @@ -118,9 +119,7 @@ class IncludeRule(BaseRule): return True def logprof_header_localvars(self): - return [ - _('Include'), self.get_clean(), - ] + return [_('Include'), self.get_clean()] def get_full_paths(self, profile_dir): ''' get list of full paths of an include (can contain multiple paths if self.path is a directory) ''' @@ -150,6 +149,7 @@ class IncludeRule(BaseRule): return files + class IncludeRuleset(BaseRuleset): '''Class to handle and store a collection of include rules''' diff --git a/utils/apparmor/rule/network.py b/utils/apparmor/rule/network.py index 9eb81374c..eddfc31f3 100644 --- a/utils/apparmor/rule/network.py +++ b/utils/apparmor/rule/network.py @@ -24,20 +24,21 @@ from apparmor.translations import init_translation _ = init_translation() -network_domain_keywords = [ 'unspec', 'unix', 'inet', 'ax25', 'ipx', 'appletalk', 'netrom', 'bridge', 'atmpvc', 'x25', 'inet6', - 'rose', 'netbeui', 'security', 'key', 'netlink', 'packet', 'ash', 'econet', 'atmsvc', 'rds', 'sna', - 'irda', 'pppox', 'wanpipe', 'llc', 'ib', 'mpls', 'can', 'tipc', 'bluetooth', 'iucv', 'rxrpc', 'isdn', - 'phonet', 'ieee802154', 'caif', 'alg', 'nfc', 'vsock', 'kcm', 'qipcrtr', 'smc', 'xdp', 'mctp' ] +network_domain_keywords = [ + 'unspec', 'unix', 'inet', 'ax25', 'ipx', 'appletalk', 'netrom', 'bridge', 'atmpvc', 'x25', 'inet6', + 'rose', 'netbeui', 'security', 'key', 'netlink', 'packet', 'ash', 'econet', 'atmsvc', 'rds', 'sna', + 'irda', 'pppox', 'wanpipe', 'llc', 'ib', 'mpls', 'can', 'tipc', 'bluetooth', 'iucv', 'rxrpc', 'isdn', + 'phonet', 'ieee802154', 'caif', 'alg', 'nfc', 'vsock', 'kcm', 'qipcrtr', 'smc', 'xdp', 'mctp'] -network_type_keywords = ['stream', 'dgram', 'seqpacket', 'rdm', 'raw', 'packet'] +network_type_keywords = ['stream', 'dgram', 'seqpacket', 'rdm', 'raw', 'packet'] network_protocol_keywords = ['tcp', 'udp', 'icmp'] -RE_NETWORK_DOMAIN = '(' + '|'.join(network_domain_keywords) + ')' -RE_NETWORK_TYPE = '(' + '|'.join(network_type_keywords) + ')' +RE_NETWORK_DOMAIN = '(' + '|'.join(network_domain_keywords) + ')' +RE_NETWORK_TYPE = '(' + '|'.join(network_type_keywords) + ')' RE_NETWORK_PROTOCOL = '(' + '|'.join(network_protocol_keywords) + ')' -RE_NETWORK_DETAILS = re.compile( +RE_NETWORK_DETAILS = re.compile( '^\s*' + '(?P' + RE_NETWORK_DOMAIN + ')?' + # optional domain '(\s+(?P' + RE_NETWORK_TYPE + '|' + RE_NETWORK_PROTOCOL + '))?' + # optional type or protocol @@ -146,7 +147,7 @@ class NetworkRule(BaseRule): else: raise AppArmorBug('Empty type or protocol in network rule') - return('%s%snetwork%s%s,%s' % (space, self.modifiers_str(), domain, type_or_protocol, self.comment)) + return ('%s%snetwork%s%s,%s' % (space, self.modifiers_str(), domain, type_or_protocol, self.comment)) def is_covered_localvars(self, other_rule): '''check if other_rule is covered by this rule object''' @@ -177,12 +178,12 @@ class NetworkRule(BaseRule): return True def logprof_header_localvars(self): - family = logprof_value_or_all(self.domain, self.all_domains) - sock_type = logprof_value_or_all(self.type_or_protocol, self.all_type_or_protocols) + family = logprof_value_or_all(self.domain, self.all_domains) # noqa: E221 + sock_type = logprof_value_or_all(self.type_or_protocol, self.all_type_or_protocols) return [ _('Network Family'), family, - _('Socket Type'), sock_type, + _('Socket Type'), sock_type, ] diff --git a/utils/apparmor/rule/ptrace.py b/utils/apparmor/rule/ptrace.py index b091cd024..779bf9888 100644 --- a/utils/apparmor/rule/ptrace.py +++ b/utils/apparmor/rule/ptrace.py @@ -23,17 +23,17 @@ from apparmor.translations import init_translation _ = init_translation() -access_keywords = ['r', 'w', 'rw', 'wr', 'read', 'write', 'readby', 'trace', 'tracedby'] # XXX 'wr' and 'write' accepted by the parser, but not documented in apparmor.d.pod +access_keywords = ['r', 'w', 'rw', 'wr', 'read', 'write', 'readby', 'trace', 'tracedby'] # XXX 'wr' and 'write' accepted by the parser, but not documented in apparmor.d.pod # XXX joint_access_keyword and RE_ACCESS_KEYWORDS exactly as in PtraceRule - move to function! joint_access_keyword = '\s*(' + '|'.join(access_keywords) + ')\s*' -RE_ACCESS_KEYWORDS = ( joint_access_keyword + # one of the access_keyword or - '|' + # or - '\(' + joint_access_keyword + '(' + '(\s|,)+' + joint_access_keyword + ')*' + '\)' # one or more access_keyword in (...) - ) +RE_ACCESS_KEYWORDS = (joint_access_keyword + # one of the access_keyword or + '|' + # or + '\(' + joint_access_keyword + '(' + '(\s|,)+' + joint_access_keyword + ')*' + '\)' # one or more access_keyword in (...) + ) -RE_PTRACE_DETAILS = re.compile( +RE_PTRACE_DETAILS = re.compile( '^' + '(\s+(?P' + RE_ACCESS_KEYWORDS + '))?' + # optional access keyword(s) '(\s+(peer=' + RE_PROFILE_NAME % 'peer' + '))?' + # optional peer @@ -58,7 +58,8 @@ class PtraceRule(BaseRule): super().__init__(audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment, log_event=log_event) - self.access, self.all_access, unknown_items = check_and_split_list(access, access_keywords, PtraceRule.ALL, 'PtraceRule', 'access') + self.access, self.all_access, unknown_items = check_and_split_list( + access, access_keywords, PtraceRule.ALL, 'PtraceRule', 'access') if unknown_items: raise AppArmorException(_('Passed unknown access keyword to PtraceRule: %s') % ' '.join(unknown_items)) @@ -104,8 +105,8 @@ class PtraceRule(BaseRule): access = PtraceRule.ALL peer = PtraceRule.ALL - return PtraceRule( - access, peer, audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment) + return PtraceRule(access, peer, + audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment) def get_clean(self, depth=0): '''return rule (in clean/default formatting)''' @@ -128,7 +129,7 @@ class PtraceRule(BaseRule): else: raise AppArmorBug('Empty peer in ptrace rule') - return('%s%sptrace%s%s,%s' % (space, self.modifiers_str(), access, peer, self.comment)) + return ('%s%sptrace%s%s,%s' % (space, self.modifiers_str(), access, peer, self.comment)) def is_covered_localvars(self, other_rule): '''check if other_rule is covered by this rule object''' @@ -158,12 +159,12 @@ class PtraceRule(BaseRule): return True def logprof_header_localvars(self): - access = logprof_value_or_all(self.access,self.all_access) - peer = logprof_value_or_all(self.peer, self.all_peers) + access = logprof_value_or_all(self.access, self.all_access) + peer = logprof_value_or_all(self.peer, self.all_peers) # noqa: E221 return [ _('Access mode'), access, - _('Peer'), peer + _('Peer'), peer, ] diff --git a/utils/apparmor/rule/rlimit.py b/utils/apparmor/rule/rlimit.py index 1afa0fb5f..fa5099793 100644 --- a/utils/apparmor/rule/rlimit.py +++ b/utils/apparmor/rule/rlimit.py @@ -23,17 +23,17 @@ from apparmor.rule import BaseRule, BaseRuleset, parse_comment, quote_if_needed from apparmor.translations import init_translation _ = init_translation() -rlimit_size = ['fsize', 'data', 'stack', 'core', 'rss', 'as', 'memlock', 'msgqueue'] # NUMBER ( 'K' | 'M' | 'G' ) -rlimit_number = ['ofile', 'nofile', 'locks', 'sigpending', 'nproc', 'rtprio'] -rlimit_time = ['cpu', 'rttime'] # number + time unit (cpu in seconds+, rttime in us+) -rlimit_nice = ['nice'] # a number between -20 and 19. +rlimit_size = ['fsize', 'data', 'stack', 'core', 'rss', 'as', 'memlock', 'msgqueue'] # NUMBER ( 'K' | 'M' | 'G' ) +rlimit_number = ['ofile', 'nofile', 'locks', 'sigpending', 'nproc', 'rtprio'] +rlimit_time = ['cpu', 'rttime'] # number + time unit (cpu in seconds+, rttime in us+) +rlimit_nice = ['nice'] # a number between -20 and 19. -rlimit_all = rlimit_size + rlimit_number + rlimit_time + rlimit_nice +rlimit_all = rlimit_size + rlimit_number + rlimit_time + rlimit_nice -RE_NUMBER_UNIT = re.compile('^(?P[0-9]+)\s*(?P[a-zA-Z]*)$') -RE_NUMBER = re.compile('^[0-9]+$') -RE_UNIT_SIZE = re.compile('^[0-9]+\s*([KMG]B?)?$') -RE_NICE = re.compile('^(-20|-[01]?[0-9]|[01]?[0-9])$') +RE_NUMBER_UNIT = re.compile('^(?P[0-9]+)\s*(?P[a-zA-Z]*)$') +RE_NUMBER = re.compile('^[0-9]+$') +RE_UNIT_SIZE = re.compile('^[0-9]+\s*([KMG]B?)?$') +RE_NICE = re.compile('^(-20|-[01]?[0-9]|[01]?[0-9])$') class RlimitRule(BaseRule): @@ -150,7 +150,7 @@ class RlimitRule(BaseRule): else: raise AppArmorBug('Empty value in rlimit rule') - return('%s%sset rlimit%s%s,%s' % (space, self.modifiers_str(), rlimit, value, self.comment)) + return ('%s%sset rlimit%s%s,%s' % (space, self.modifiers_str(), rlimit, value, self.comment)) def size_to_int(self, value): number, unit = split_unit(value) @@ -182,13 +182,13 @@ class RlimitRule(BaseRule): number = number / 1000.0 if default_unit == 'seconds': raise AppArmorException(_('Invalid unit in rlimit cpu %s rule') % value) - elif unit in ('s', 'sec', 'second', 'seconds'): # manpage doesn't list sec + elif unit in ('s', 'sec', 'second', 'seconds'): # manpage doesn't list sec pass elif unit in ('min', 'minute', 'minutes'): number = number * 60 elif unit in ('h', 'hour', 'hours'): number = number * 60 * 60 - elif unit in ('d', 'day', 'days'): # manpage doesn't list 'd' + elif unit in ('d', 'day', 'days'): # manpage doesn't list 'd' number = number * 60 * 60 * 24 elif unit in ('week', 'weeks'): number = number * 60 * 60 * 24 * 7 @@ -243,6 +243,7 @@ class RlimitRule(BaseRule): _('Value'), values_txt, ] + class RlimitRuleset(BaseRuleset): '''Class to handle and store a collection of rlimit rules''' @@ -261,5 +262,3 @@ def split_unit(value): unit = matches.group('unit') or '' return number, unit - - diff --git a/utils/apparmor/rule/signal.py b/utils/apparmor/rule/signal.py index 708104ae3..11cf029c0 100644 --- a/utils/apparmor/rule/signal.py +++ b/utils/apparmor/rule/signal.py @@ -16,39 +16,43 @@ import re from apparmor.regex import RE_PROFILE_SIGNAL, RE_PROFILE_NAME from apparmor.common import AppArmorBug, AppArmorException -from apparmor.rule import BaseRule, BaseRuleset, check_and_split_list, logprof_value_or_all, parse_modifiers, quote_if_needed +from apparmor.rule import ( + BaseRule, BaseRuleset, check_and_split_list, logprof_value_or_all, + parse_modifiers, quote_if_needed) # setup module translations from apparmor.translations import init_translation _ = init_translation() -access_keywords_read = ['receive', 'r', 'read'] -access_keywords_write = ['send', 'w', 'write'] -access_keywords_rw = ['rw', 'wr'] -access_keywords = access_keywords_read + access_keywords_write + access_keywords_rw +access_keywords_read = ['receive', 'r', 'read'] +access_keywords_write = ['send', 'w', 'write'] +access_keywords_rw = ['rw', 'wr'] +access_keywords = access_keywords_read + access_keywords_write + access_keywords_rw -signal_keywords = ['hup', 'int', 'quit', 'ill', 'trap', 'abrt', 'bus', 'fpe', 'kill', 'usr1', - 'segv', 'usr2', 'pipe', 'alrm', 'term', 'stkflt', 'chld', 'cont', 'stop', - 'stp', 'ttin', 'ttou', 'urg', 'xcpu', 'xfsz', 'vtalrm', 'prof', 'winch', - 'io', 'pwr', 'sys', 'emt', 'exists'] +signal_keywords = [ + 'hup', 'int', 'quit', 'ill', 'trap', 'abrt', 'bus', 'fpe', 'kill', 'usr1', + 'segv', 'usr2', 'pipe', 'alrm', 'term', 'stkflt', 'chld', 'cont', 'stop', + 'stp', 'ttin', 'ttou', 'urg', 'xcpu', 'xfsz', 'vtalrm', 'prof', 'winch', + 'io', 'pwr', 'sys', 'emt', 'exists'] RE_SIGNAL_REALTIME = re.compile('^rtmin\+0*([0-9]|[12][0-9]|3[0-2])$') # rtmin+0..rtmin+32, number may have leading zeros joint_access_keyword = '\s*(' + '|'.join(access_keywords) + ')\s*' -RE_ACCESS_KEYWORDS = ( joint_access_keyword + # one of the access_keyword or - '|' + # or - '\(' + joint_access_keyword + '(' + '(\s|,)+' + joint_access_keyword + ')*' + '\)' # one or more access_keyword in (...) - ) +RE_ACCESS_KEYWORDS = ( + joint_access_keyword + # one of the access_keyword or + '|' + # or + '\(' + joint_access_keyword + '(' + '(\s|,)+' + joint_access_keyword + ')*' + '\)' # one or more access_keyword in (...) +) signal_keyword = '\s*([a-z0-9+]+|"[a-z0-9+]+")\s*' # don't check against the signal keyword list in the regex to allow a more helpful error message RE_SIGNAL_KEYWORDS = ( - 'set\s*=\s*' + signal_keyword + # one of the signal_keyword or - '|' + # or - 'set\s*=\s*\(' + signal_keyword + '(' + '(\s|,)+' + signal_keyword + ')*' + '\)' # one or more signal_keyword in (...) - ) + 'set\s*=\s*' + signal_keyword + # one of the signal_keyword or + '|' + # or + 'set\s*=\s*\(' + signal_keyword + '(' + '(\s|,)+' + signal_keyword + ')*' + '\)' # one or more signal_keyword in (...) +) -RE_SIGNAL_DETAILS = re.compile( +RE_SIGNAL_DETAILS = re.compile( '^' + '(\s+(?P' + RE_ACCESS_KEYWORDS + '))?' + # optional access keyword(s) '(?P' + '(\s+(' + RE_SIGNAL_KEYWORDS + '))+' + ')?' + # optional signal set(s) @@ -61,6 +65,7 @@ RE_FILTER_SET_2 = re.compile('set\s*=') RE_FILTER_PARENTHESIS = re.compile('\((.*)\)') RE_FILTER_QUOTES = re.compile('"([a-z0-9]+)"') # used to strip quotes around signal keywords - don't use for peer! + class SignalRule(BaseRule): '''Class to handle and store a single signal rule''' @@ -79,11 +84,13 @@ class SignalRule(BaseRule): super().__init__(audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment, log_event=log_event) - self.access, self.all_access, unknown_items = check_and_split_list(access, access_keywords, SignalRule.ALL, 'SignalRule', 'access') + self.access, self.all_access, unknown_items = check_and_split_list( + access, access_keywords, SignalRule.ALL, 'SignalRule', 'access') if unknown_items: raise AppArmorException(_('Passed unknown access keyword to SignalRule: %s') % ' '.join(unknown_items)) - self.signal, self.all_signals, unknown_items = check_and_split_list(signal, signal_keywords, SignalRule.ALL, 'SignalRule', 'signal') + self.signal, self.all_signals, unknown_items = check_and_split_list( + signal, signal_keywords, SignalRule.ALL, 'SignalRule', 'signal') if unknown_items: for item in unknown_items: if RE_SIGNAL_REALTIME.match(item): @@ -175,7 +182,7 @@ class SignalRule(BaseRule): else: raise AppArmorBug('Empty peer in signal rule') - return('%s%ssignal%s%s%s,%s' % (space, self.modifiers_str(), access, signal, peer, self.comment)) + return ('%s%ssignal%s%s%s,%s' % (space, self.modifiers_str(), access, signal, peer, self.comment)) def is_covered_localvars(self, other_rule): '''check if other_rule is covered by this rule object''' @@ -212,14 +219,14 @@ class SignalRule(BaseRule): return True def logprof_header_localvars(self): - access = logprof_value_or_all(self.access, self.all_access) - signal = logprof_value_or_all(self.signal, self.all_signals) - peer = logprof_value_or_all(self.peer, self.all_peers) + access = logprof_value_or_all(self.access, self.all_access) + signal = logprof_value_or_all(self.signal, self.all_signals) + peer = logprof_value_or_all(self.peer, self.all_peers) # noqa: E221 return [ _('Access mode'), access, - _('Signal'), signal, - _('Peer'), peer + _('Signal'), signal, + _('Peer'), peer ] @@ -230,4 +237,3 @@ class SignalRuleset(BaseRuleset): '''Return the next possible glob. For signal rules, that means removing access, signal or peer''' # XXX only remove one part, not all return 'signal,' - diff --git a/utils/apparmor/rule/variable.py b/utils/apparmor/rule/variable.py index a2ac3fe01..b4f38115c 100644 --- a/utils/apparmor/rule/variable.py +++ b/utils/apparmor/rule/variable.py @@ -81,7 +81,7 @@ class VariableRule(BaseRule): values = separate_vars(matches.group('values')) return VariableRule(varname, mode, values, - audit=False, deny=False, allow_keyword=False, comment=comment) + audit=False, deny=False, allow_keyword=False, comment=comment) def get_clean(self, depth=0): '''return rule (in clean/default formatting)''' @@ -135,6 +135,7 @@ class VariableRule(BaseRule): _('Variable'), self.get_clean(), ] + class VariableRuleset(BaseRuleset): '''Class to handle and store a collection of variable rules''' @@ -147,7 +148,9 @@ class VariableRuleset(BaseRuleset): if rule.mode == '=': for knownrule in self.rules: if rule.varname == knownrule.varname: - raise AppArmorException(_('Redefining existing variable %(variable)s: %(value)s') % { 'variable': rule.varname, 'value': rule.values }) + raise AppArmorException( + _('Redefining existing variable %(variable)s: %(value)s') + % {'variable': rule.varname, 'value': rule.values}) super().add(rule, cleanup) @@ -171,6 +174,7 @@ class VariableRuleset(BaseRuleset): return {'=': var_set, '+=': var_add} + def separate_vars(vs): """Returns a list of all the values for a variable""" data = set() diff --git a/utils/apparmor/rules.py b/utils/apparmor/rules.py index b165f414b..b17a10321 100644 --- a/utils/apparmor/rules.py +++ b/utils/apparmor/rules.py @@ -17,7 +17,7 @@ class _Raw_Rule: def serialize(self): return "%s%s%s" % ('audit ' if self.audit else '', - 'deny ' if self.deny else '', + 'deny ' if self.deny else '', self.rule) def recursive_print(self, depth): @@ -32,8 +32,10 @@ class _Raw_Rule: class Raw_Mount_Rule(_Raw_Rule): pass + class Raw_Pivot_Root_Rule(_Raw_Rule): pass + class Raw_Unix_Rule(_Raw_Rule): pass diff --git a/utils/apparmor/sandbox.py b/utils/apparmor/sandbox.py index 78fa27dab..71c1074b8 100644 --- a/utils/apparmor/sandbox.py +++ b/utils/apparmor/sandbox.py @@ -24,12 +24,14 @@ from tempfile import NamedTemporaryFile def check_requirements(binary): '''Verify necessary software is installed''' - exes = ['xset', # for detecting free X display - 'aa-easyprof', # for templates - 'aa-exec', # for changing profile - 'sudo', # eventually get rid of this - 'pkexec', # eventually get rid of this - binary] + exes = [ + 'xset', # for detecting free X display + 'aa-easyprof', # for templates + 'aa-exec', # for changing profile + 'sudo', # eventually get rid of this + 'pkexec', # eventually get rid of this + binary + ] for e in exes: debug("Searching for '%s'" % e) @@ -39,6 +41,7 @@ def check_requirements(binary): return True + def parse_args(args=None, parser=None): '''Parse arguments''' if parser is None: @@ -82,8 +85,8 @@ def parse_args(args=None, parser=None): valid_xservers = ['xpra', 'xpra3d', 'xephyr'] if my_opt.withx and my_opt.xserver.lower() not in valid_xservers: - error("Invalid server '%s'. Use one of: %s" % (my_opt.xserver, \ - ", ".join(valid_xservers))) + error("Invalid server '%s'. Use one of: %s" + % (my_opt.xserver, ", ".join(valid_xservers))) if my_opt.withx: if my_opt.xephyr_geometry and my_opt.xserver.lower() != "xephyr": @@ -99,10 +102,12 @@ def parse_args(args=None, parser=None): return (my_opt, my_args) + def gen_policy_name(binary): '''Generate a temporary policy based on the binary name''' return "sandbox-%s%s" % (pwd.getpwuid(os.geteuid())[0], re.sub(r'/', '_', binary)) + def set_environ(env): keys = env.keys() keys.sort() @@ -110,6 +115,7 @@ def set_environ(env): msg("Using: %s=%s" % (k, env[k])) os.environ[k] = env[k] + def aa_exec(command, opt, environ={}, verify_rules=[]): '''Execute binary under specified policy''' if opt.profile is not None: @@ -161,12 +167,14 @@ def aa_exec(command, opt, environ={}, verify_rules=[]): rc, report = cmd(args) return rc, report + def run_sandbox(command, opt): '''Run application''' # aa-exec rc, report = aa_exec(command, opt) return rc, report + class SandboxXserver(): def __init__(self, title, geometry=None, driver=None, xauth=None, clipboard=False): self.geometry = geometry @@ -175,7 +183,7 @@ class SandboxXserver(): self.driver = driver self.clipboard = clipboard self.tempfiles = [] - self.timeout = 5 # used by xauth and for server starts + self.timeout = 5 # used by xauth and for server starts # preserve our environment self.old_environ = dict() @@ -233,8 +241,7 @@ class SandboxXserver(): display = "" current = self.old_environ["DISPLAY"] - for i in range(1,257): # TODO: this puts an artificial limit of 256 - # sandboxed applications + for i in range(1, 257): # TODO: this puts an artificial limit of 256 sandboxed applications tmp = ":%d" % i os.environ["DISPLAY"] = tmp rc, report = cmd(('xset', '-q')) @@ -310,27 +317,30 @@ class SandboxXephyr(SandboxXserver): listener_x = os.fork() if listener_x == 0: # TODO: break into config file? Which are needed? - x_exts = ['-extension', 'GLX', - '-extension', 'MIT-SHM', - '-extension', 'RENDER', - '-extension', 'SECURITY', - '-extension', 'DAMAGE' - ] + x_exts = [ + '-extension', 'GLX', + '-extension', 'MIT-SHM', + '-extension', 'RENDER', + '-extension', 'SECURITY', + '-extension', 'DAMAGE' + ] # verify_these - x_extra_args = ['-host-cursor', # less secure? - '-fakexa', # for games? seems not needed - '-nodri', # more secure? - ] + x_extra_args = [ + '-host-cursor', # less secure? + '-fakexa', # for games? seems not needed + '-nodri', # more secure? + ] if not self.geometry: self.geometry = "640x480" - x_args = ['-nolisten', 'tcp', - '-screen', self.geometry, - '-br', # black background - '-reset', # reset after last client exists - '-terminate', # terminate at server reset - '-title', self.generate_title(), - ] + x_exts + x_extra_args + x_args = [ + '-nolisten', 'tcp', + '-screen', self.geometry, + '-br', # black background + '-reset', # reset after last client exists + '-terminate', # terminate at server reset + '-title', self.generate_title(), + ] + x_exts + x_extra_args args = ['/usr/bin/Xephyr'] + x_args + [self.display] debug(" ".join(args)) @@ -338,7 +348,7 @@ class SandboxXephyr(SandboxXserver): sys.exit(0) self.pids.append(listener_x) - time.sleep(1) # FIXME: detect if running + time.sleep(1) # FIXME: detect if running # Next, start the window manager sys.stdout.flush() @@ -354,7 +364,7 @@ class SandboxXephyr(SandboxXserver): sys.exit(0) self.pids.append(listener_wm) - time.sleep(1) # FIXME: detect if running + time.sleep(1) # FIXME: detect if running class SandboxXpra(SandboxXserver): @@ -539,7 +549,7 @@ EndSection tmp.write(conf.encode('utf-8')) xvfb_args.append('--xvfb=Xorg') - xvfb_args.append('-dpi 96') # https://www.xpra.org/trac/ticket/163 + xvfb_args.append('-dpi 96') # https://www.xpra.org/trac/ticket/163 xvfb_args.append('-nolisten tcp') xvfb_args.append('-noreset') xvfb_args.append('-logfile %s' % os.path.expanduser('~/.xpra/%s.log' % self.display)) @@ -577,7 +587,7 @@ EndSection cmd(('xpra', 'list')) x_args = ['--no-daemon', - #'--no-mmap', # for security? + # '--no-mmap', # for security? '--no-pulseaudio'] if not self.clipboard: x_args.append('--no-clipboard') @@ -597,7 +607,7 @@ EndSection # We need to wait for the xpra socket to exist before attaching fn = os.path.join(os.environ['HOME'], '.xpra', '%s-%s' % \ (socket.gethostname(), self.display.split(':')[1])) - for i in range(self.timeout * 2): # up to self.timeout seconds to start + for i in range(self.timeout * 2): # up to self.timeout seconds to start if os.path.exists(fn): debug("Found '%s'! Proceeding to attach" % fn) break @@ -609,7 +619,7 @@ EndSection self.cleanup() raise AppArmorException("Could not start xpra (try again with -d)") - for i in range(self.timeout): # Up to self.timeout seconds to start + for i in range(self.timeout): # Up to self.timeout seconds to start rc, out = cmd(('xpra', 'list')) if 'DEAD session at %s' % self.display in out: @@ -636,7 +646,7 @@ EndSection if listener_attach == 0: args = ['/usr/bin/xpra', 'attach', self.display, '--title=%s' % self.generate_title(), - #'--no-mmap', # for security? + # '--no-mmap', # for security? '--no-tray', '--no-pulseaudio'] if not self.clipboard: @@ -650,9 +660,9 @@ EndSection self.pids.append(listener_attach) # Make sure that a client has attached - for i in range(self.timeout): # up to self.timeout seconds to attach + for i in range(self.timeout): # up to self.timeout seconds to attach time.sleep(1) - rc, out = cmd (('xpra', 'info', self.display)) + rc, out = cmd(('xpra', 'info', self.display)) search = 'clients=1' if search in out: debug("Client successfully attached!") @@ -684,7 +694,7 @@ def run_xsandbox(command, opt): keys = x.old_environ.keys() keys.sort() for k in keys: - debug ("Old: %s=%s" % (k, x.old_environ[k])) + debug("Old: %s=%s" % (k, x.old_environ[k])) try: x.start() diff --git a/utils/apparmor/severity.py b/utils/apparmor/severity.py index ac4cab3e8..a6ecec294 100644 --- a/utils/apparmor/severity.py +++ b/utils/apparmor/severity.py @@ -14,6 +14,7 @@ import re from apparmor.common import AppArmorException, open_file_read, warn, convert_regexp # , msg, error, debug + class Severity: def __init__(self, dbname=None, default_rank=10): """Initialises the class object""" @@ -40,10 +41,14 @@ class Severity: path, read, write, execute = line.split() read, write, execute = int(read), int(write), int(execute) except ValueError: - raise AppArmorException("Insufficient values for permissions in file: %s\n\t[Line %s]: %s" % (dbname, lineno, line)) + raise AppArmorException( + "Insufficient values for permissions in file: %s\n\t[Line %s]: %s" + % (dbname, lineno, line)) else: if read not in range(0, 11) or write not in range(0, 11) or execute not in range(0, 11): - raise AppArmorException("Inappropriate values for permissions in file: %s\n\t[Line %s]: %s" % (dbname, lineno, line)) + raise AppArmorException( + "Inappropriate values for permissions in file: %s\n\t[Line %s]: %s" + % (dbname, lineno, line)) path = path.lstrip('/') if '*' not in path: self.severity['FILES'][path] = {'r': read, 'w': write, 'x': execute} @@ -65,11 +70,13 @@ class Severity: severity = int(severity) except ValueError: error_message = 'No severity value present in file: %s\n\t[Line %s]: %s' % (dbname, lineno, line) - #error(error_message) + # error(error_message) raise AppArmorException(error_message) # from None else: if severity not in range(0, 11): - raise AppArmorException("Inappropriate severity value present in file: %s\n\t[Line %s]: %s" % (dbname, lineno, line)) + raise AppArmorException( + "Inappropriate severity value present in file: %s\n\t[Line %s]: %s" + % (dbname, lineno, line)) self.severity['CAPABILITIES'][resource] = severity else: raise AppArmorException("Unexpected line in file: %s\n\t[Line %s]: %s" % (dbname, lineno, line)) @@ -146,7 +153,7 @@ class Severity: if matches: rank = self.severity['DEFAULT_RANK'] variable = '@{%s}' % matches.groups()[0] - #variables = regex_variable.findall(resource) + # variables = regex_variable.findall(resource) for replacement in self.severity['VARIABLES'][variable]: resource_replaced = self.variable_replace(variable, replacement, resource) rank_new = self.handle_variable_rank(resource_replaced, mode) diff --git a/utils/apparmor/tools.py b/utils/apparmor/tools.py index 0c92a8719..4033c87cf 100644 --- a/utils/apparmor/tools.py +++ b/utils/apparmor/tools.py @@ -24,6 +24,7 @@ from apparmor.common import AppArmorException, cmd, is_skippable_file, user_perm from apparmor.translations import init_translation _ = init_translation() + class aa_tools: def __init__(self, tool_name, args): apparmor.init_aa(profiledir=args.dir, confdir=args.configdir) @@ -71,7 +72,8 @@ class aa_tools: profile = apparmor.get_full_path(os.path.join(apparmor.profile_dir, p)).strip() else: if '/' not in p: - aaui.UI_Info(_("Can't find %(program)s in the system path list. If the name of the application\nis correct, please run 'which %(program)s' as a user with correct PATH\nenvironment set up in order to find the fully-qualified path and\nuse the full path as parameter.") % { 'program': p }) + aaui.UI_Info(_("Can't find %(program)s in the system path list. If the name of the application\nis correct, please run 'which %(program)s' as a user with correct PATH\nenvironment set up in order to find the fully-qualified path and\nuse the full path as parameter.") + % {'program': p}) else: aaui.UI_Info(_("%s does not exist, please double-check the path.") % p) continue @@ -98,7 +100,8 @@ class aa_tools: else: if '/' not in program: - aaui.UI_Info(_("Can't find %(program)s in the system path list. If the name of the application\nis correct, please run 'which %(program)s' as a user with correct PATH\nenvironment set up in order to find the fully-qualified path and\nuse the full path as parameter.") % { 'program': program }) + aaui.UI_Info(_("Can't find %(program)s in the system path list. If the name of the application\nis correct, please run 'which %(program)s' as a user with correct PATH\nenvironment set up in order to find the fully-qualified path and\nuse the full path as parameter.") + % {'program': program}) else: aaui.UI_Info(_("%s does not exist, please double-check the path.") % program) sys.exit(1) @@ -205,7 +208,7 @@ class aa_tools: if not self.silent: q = aaui.PromptQuestion() q.title = 'Changed Local Profiles' - q.explanation = _('The local profile for %(program)s in file %(file)s was changed. Would you like to save it?') % { 'program': program, 'file': filename } + q.explanation = _('The local profile for %(program)s in file %(file)s was changed. Would you like to save it?') % {'program': program, 'file': filename} q.functions = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT'] q.default = 'CMD_VIEW_CHANGES' q.options = [] @@ -218,7 +221,7 @@ class aa_tools: apparmor.write_profile_ui_feedback(program, True) self.reload_profile(filename) elif ans == 'CMD_VIEW_CHANGES': - #oldprofile = apparmor.serialize_profile(apparmor.split_to_merged(apparmor.original_aa), program, {}) + # oldprofile = apparmor.serialize_profile(apparmor.split_to_merged(apparmor.original_aa), program, {}) newprofile = apparmor.serialize_profile(apparmor.split_to_merged(apparmor.aa), program, {'is_attachment': True}) aaui.UI_Changes(filename, newprofile, comments=True) else: diff --git a/utils/apparmor/translations.py b/utils/apparmor/translations.py index fb6d5c40d..949c6c1b3 100644 --- a/utils/apparmor/translations.py +++ b/utils/apparmor/translations.py @@ -13,6 +13,7 @@ TRANSLATION_DOMAIN = 'apparmor-utils' __apparmor_gettext__ = None + def init_translation(): global __apparmor_gettext__ if __apparmor_gettext__ is None: diff --git a/utils/apparmor/ui.py b/utils/apparmor/ui.py index 930ec24f6..0132804b7 100644 --- a/utils/apparmor/ui.py +++ b/utils/apparmor/ui.py @@ -63,6 +63,7 @@ def set_text_mode(): global UI_mode UI_mode = 'text' + # reads the response on command line for json and verifies the response # for the dialog type def json_response(dialog_type): @@ -284,6 +285,7 @@ def UI_Changes(oldprofile, newprofile, comments=False): with difftemp: UI_ShowFile(header, difftemp.name) + def UI_ShowFile(header, filename): if UI_mode == 'json': jsonout = {'dialog': 'changes', 'header': header, 'filename': filename} @@ -298,7 +300,7 @@ CMDS = {'CMD_ALLOW': _('(A)llow'), 'CMD_AUDIT_NEW': _('Audi(t)'), 'CMD_AUDIT_OFF': _('Audi(t) off'), 'CMD_AUDIT_FULL': _('Audit (A)ll'), - #'CMD_OTHER': '(O)pts', + # 'CMD_OTHER': '(O)pts', 'CMD_USER_ON': _('(O)wner permissions on'), 'CMD_USER_OFF': _('(O)wner permissions off'), 'CMD_DENY': _('(D)eny'), @@ -403,7 +405,9 @@ class PromptQuestion: key = get_translated_hotkey(menutext).lower() # Duplicate hotkey if keys.get(key, False): - raise AppArmorException(_('PromptUser: Duplicate hotkey for %(command)s: %(menutext)s ') % { 'command': cmd, 'menutext': menutext }) + raise AppArmorException( + _('PromptUser: Duplicate hotkey for %(command)s: %(menutext)s ') + % {'command': cmd, 'menutext': menutext}) keys[key] = cmd diff --git a/utils/python-tools-setup.py b/utils/python-tools-setup.py index 775cec8d8..61d65c081 100644 --- a/utils/python-tools-setup.py +++ b/utils/python-tools-setup.py @@ -26,6 +26,7 @@ import os import shutil import sys + class Install(_install): '''Override setuptools to install the files where we want them.''' def run(self): @@ -61,21 +62,22 @@ shutil.copytree('apparmor', 'staging') # Support the --version=... since this will be part of a Makefile version = "unknown-version" if "--version=" in sys.argv[-1]: - version=sys.argv[-1].split('=')[1] + version = sys.argv[-1].split('=')[1] sys.argv = sys.argv[0:-1] -setup (name='apparmor', - version=version, - description='Python libraries for AppArmor utilities', - long_description='Python libraries for AppArmor utilities', - author='AppArmor Developers', - author_email='apparmor@lists.ubuntu.com', - url='https://gitlab.com/apparmor/apparmor', - license='GPL-2', - cmdclass={'install': Install}, - package_dir={'apparmor': 'staging'}, - packages=['apparmor', 'apparmor.rule'], - py_modules=['apparmor.easyprof'] +setup( + name='apparmor', + version=version, + description='Python libraries for AppArmor utilities', + long_description='Python libraries for AppArmor utilities', + author='AppArmor Developers', + author_email='apparmor@lists.ubuntu.com', + url='https://gitlab.com/apparmor/apparmor', + license='GPL-2', + cmdclass={'install': Install}, + package_dir={'apparmor': 'staging'}, + packages=['apparmor', 'apparmor.rule'], + py_modules=['apparmor.easyprof'] ) shutil.rmtree('staging') diff --git a/utils/test/common_test.py b/utils/test/common_test.py index 3833575e5..713e3aaa0 100755 --- a/utils/test/common_test.py +++ b/utils/test/common_test.py @@ -20,9 +20,9 @@ import sys import tempfile - #def test_readkey(self): - # print("Please press the Y button on the keyboard.") - # self.assertEqual(apparmor.common.readkey().lower(), 'y', 'Error reading key from shell!') +# def test_readkey(self): +# print("Please press the Y button on the keyboard.") +# self.assertEqual(apparmor.common.readkey().lower(), 'y', 'Error reading key from shell!') class AATest(unittest.TestCase): @@ -55,15 +55,18 @@ class AATest(unittest.TestCase): tests = () tmpdir = None + class AAParseTest(unittest.TestCase): parse_function = None def _test_parse_rule(self, rule): self.assertIsNot(self.parse_function, 'Test class did not set a parse_function') parsed = self.parse_function(rule) - self.assertEqual(rule, parsed.serialize(), + self.assertEqual( + rule, parsed.serialize(), 'parse object %s returned "%s", expected "%s"' \ - %(self.parse_function.__doc__, parsed.serialize(), rule)) + % (self.parse_function.__doc__, parsed.serialize(), rule)) + def setup_all_loops(module_name): '''call setup_tests_loop() for each class in module_name''' @@ -72,6 +75,7 @@ def setup_all_loops(module_name): if issubclass(obj, unittest.TestCase): setup_tests_loop(obj) + def setup_tests_loop(test_class): '''Create tests in test_class using test_class.tests and self._run_test() @@ -103,6 +107,7 @@ def setup_regex_tests(test_class): stub_test.__doc__ = "test '%s': %s" % (line, desc) setattr(test_class, 'test_%d' % (i), stub_test) + def setup_aa(aa): confdir = os.getenv('__AA_CONFDIR') try: @@ -114,6 +119,7 @@ def setup_aa(aa): # apparmor.aa module versions <= 2.11 do not have the init_aa() method pass + def write_file(directory, file, contents): '''construct path, write contents to it, and return the constructed path''' path = os.path.join(directory, file) @@ -121,13 +127,13 @@ def write_file(directory, file, contents): f.write(contents) return path + def read_file(path): '''read and return file contents''' with open(path, 'r') as f: return f.read() - if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.test_RegexParser'] + # import sys;sys.argv = ['', 'Test.test_RegexParser'] unittest.main() diff --git a/utils/test/test-aa-cli-bootstrap.py b/utils/test/test-aa-cli-bootstrap.py index 8b8883d0f..9a23b7dde 100644 --- a/utils/test/test-aa-cli-bootstrap.py +++ b/utils/test/test-aa-cli-bootstrap.py @@ -67,7 +67,9 @@ class AACliBootstrapTest(AATest): aaui.set_json_mode() sys.stdout.getvalue() aaui.UI_Info('Test string') - self.assertEqual(sys.stdout.getvalue(), '{"dialog": "apparmor-json-version","data": "2.12"}\n{"dialog": "info","data": "Test string"}\n') + self.assertEqual( + sys.stdout.getvalue(), + '{"dialog": "apparmor-json-version","data": "2.12"}\n{"dialog": "info","data": "Test string"}\n') aaui.set_text_mode() diff --git a/utils/test/test-aa-decode.py b/utils/test/test-aa-decode.py index 6c111ad69..11d378025 100755 --- a/utils/test/test-aa-decode.py +++ b/utils/test/test-aa-decode.py @@ -20,6 +20,7 @@ from tempfile import NamedTemporaryFile # these tests in an installed environment aadecode_bin = "../aa-decode" + # http://www.chiark.greenend.org.uk/ucgi/~cjwatson/blosxom/2009-07-02-python-sigpipe.html # This is needed so that the subprocesses that produce endless output # actually quit when the reader goes away. @@ -28,6 +29,7 @@ def subprocess_setup(): # non-Python subprocesses expect. signal.signal(signal.SIGPIPE, signal.SIG_DFL) + # Define only arguments that are actually ever used: command and stdin def cmd(command, stdin=None): '''Try to execute given command (array) and return its stdout, or return @@ -112,9 +114,11 @@ class AADecodeTest(unittest.TestCase): def test_simple_multiline(self): '''test simple multiline decoding of the name argument''' - expected_strings = ('ses=4294967295 new ses=2762', - 'name="/tmp/foo bar"', - 'name="/home/steve/tmp/my test file"') + expected_strings = ( + 'ses=4294967295 new ses=2762', + 'name="/tmp/foo bar"', + 'name="/home/steve/tmp/my test file"', + ) content = \ ''' type=LOGIN msg=audit(1348980001.155:2925): login pid=17875 uid=0 old auid=4294967295 new auid=0 old ses=4294967295 new ses=2762 type=AVC msg=audit(1348982151.183:2934): apparmor="DENIED" operation="open" parent=30751 profile="/usr/lib/firefox/firefox{,*[^s] [^h]}" name=2F746D702F666F6F20626172 pid=30833 comm="plugin-containe" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0 @@ -127,8 +131,8 @@ type=AVC msg=audit(1348982148.195:2933): apparmor="DENIED" operation="file_lock" '''test simple decoding of the profile argument''' '''Example take from LP: #897957''' - expected_strings = ('name="/lib/x86_64-linux-gnu/libdl-2.13.so"', - 'profile="/test space"') + expected_strings = ( + 'name="/lib/x86_64-linux-gnu/libdl-2.13.so"', 'profile="/test space"') content = \ '''[289763.843292] type=1400 audit(1322614912.304:857): apparmor="ALLOWED" operation="getattr" parent=16001 profile=2F74657374207370616365 name="/lib/x86_64-linux-gnu/libdl-2.13.so" pid=17011 comm="bash" requested_mask="r" denied_mask="r" fsuid=0 ouid=0 ''' @@ -180,14 +184,14 @@ type=AVC msg=audit(1348982148.195:2933): apparmor="DENIED" operation="file_lock" def test_simple_encoded_nonpath_profiles(self): '''test simple decoding of nonpath profiles''' - expected_strings = ('name="/lib/x86_64-linux-gnu/libdl-2.13.so"', - 'profile="test space"') + expected_strings = ('name="/lib/x86_64-linux-gnu/libdl-2.13.so"', 'profile="test space"') content = \ '''[289763.843292] type=1400 audit(1322614912.304:857): apparmor="ALLOWED" operation="getattr" parent=16001 profile=74657374207370616365 name="/lib/x86_64-linux-gnu/libdl-2.13.so" pid=17011 comm="bash" requested_mask="r" denied_mask="r" fsuid=0 ouid=0 ''' self._run_file_test(content, expected_strings) + # # Main # diff --git a/utils/test/test-aa-easyprof.py b/utils/test/test-aa-easyprof.py index dc99bc2d9..e7052df5d 100755 --- a/utils/test/test-aa-easyprof.py +++ b/utils/test/test-aa-easyprof.py @@ -24,6 +24,7 @@ from apparmor.common import AppArmorException topdir = None debugging = False + def recursive_rm(dirPath, contents_only=False): '''recursively remove directory''' names = os.listdir(dirPath) @@ -36,6 +37,7 @@ def recursive_rm(dirPath, contents_only=False): if not contents_only: os.rmdir(dirPath) + # From Lib/test/test_optparse.py from python 2.7.4 class InterceptedError(Exception): def __init__(self, @@ -99,6 +101,7 @@ class Manifest: return json.dumps(dumpee, indent=2) + # # Our test class # @@ -229,7 +232,7 @@ TEMPLATES_DIR="%s/templates" except Exception: raise - raise Exception ("File should have been invalid") + raise Exception("File should have been invalid") def test_configuration_file_p_empty(self): '''Test config parsing (empty POLICYGROUPS_DIR)''' @@ -247,7 +250,7 @@ TEMPLATES_DIR="%s/templates" except Exception: raise - raise Exception ("File should have been invalid") + raise Exception("File should have been invalid") def test_configuration_file_p_nonexistent(self): '''Test config parsing (nonexistent POLICYGROUPS_DIR)''' @@ -265,7 +268,7 @@ TEMPLATES_DIR="%s/templates" except Exception: raise - raise Exception ("File should have been invalid") + raise Exception("File should have been invalid") def test_policygroups_dir_relative(self): '''Test --policy-groups-dir (relative DIR)''' @@ -280,8 +283,9 @@ TEMPLATES_DIR="%s/templates" easyp = easyprof.AppArmorEasyProfile(self.binary, self.options) # no fallback - self.assertTrue(easyp.dirs['policygroups'] == rel, "Not using specified --policy-groups-dir\n" + - "Specified dir: %s\nActual dir: %s" % (rel, str(easyp.dirs['policygroups']))) + self.assertTrue(easyp.dirs['policygroups'] == rel, + "Not using specified --policy-groups-dir\n" + "Specified dir: %s\nActual dir: %s" % (rel, str(easyp.dirs['policygroups']))) self.assertFalse(easyp.get_policy_groups() is None, "Could not find policy-groups") def test_policygroups_dir_nonexistent(self): @@ -321,7 +325,8 @@ TEMPLATES_DIR="%s/templates" os.chdir(self.tmpdir) valid = os.path.join(self.tmpdir, 'valid') os.mkdir(valid) - shutil.copy(os.path.join(self.tmpdir, 'policygroups', self.test_policygroup), os.path.join(valid, self.test_policygroup)) + shutil.copy(os.path.join(self.tmpdir, 'policygroups', self.test_policygroup), + os.path.join(valid, self.test_policygroup)) vendor = "ubuntu" version = "1.0" @@ -356,7 +361,7 @@ POLICYGROUPS_DIR="%s/templates" except Exception: raise - raise Exception ("File should have been invalid") + raise Exception("File should have been invalid") def test_configuration_file_t_empty(self): '''Test config parsing (empty TEMPLATES_DIR)''' @@ -374,7 +379,7 @@ POLICYGROUPS_DIR="%s/templates" except Exception: raise - raise Exception ("File should have been invalid") + raise Exception("File should have been invalid") def test_configuration_file_t_nonexistent(self): '''Test config parsing (nonexistent TEMPLATES_DIR)''' @@ -392,7 +397,7 @@ POLICYGROUPS_DIR="%s/templates" except Exception: raise - raise Exception ("File should have been invalid") + raise Exception("File should have been invalid") def test_templates_dir_relative(self): '''Test --templates-dir (relative DIR)''' @@ -407,8 +412,9 @@ POLICYGROUPS_DIR="%s/templates" easyp = easyprof.AppArmorEasyProfile(self.binary, self.options) # no fallback - self.assertTrue(easyp.dirs['templates'] == rel, "Not using specified --template-dir\n" + - "Specified dir: %s\nActual dir: %s" % (rel, str(easyp.dirs['templates']))) + self.assertTrue(easyp.dirs['templates'] == rel, + "Not using specified --template-dir\n" + "Specified dir: %s\nActual dir: %s" % (rel, str(easyp.dirs['templates']))) self.assertFalse(easyp.get_templates() is None, "Could not find templates") def test_templates_dir_nonexistent(self): @@ -500,7 +506,7 @@ POLICYGROUPS_DIR="%s/templates" return except Exception: raise - raise Exception ("Binary should have been invalid") + raise Exception("Binary should have been invalid") def test_binary_symlink(self): '''Test binary (symlink)''' @@ -515,7 +521,7 @@ POLICYGROUPS_DIR="%s/templates" return except Exception: raise - raise Exception ("Binary should have been invalid") + raise Exception("Binary should have been invalid") # # Templates tests @@ -762,11 +768,9 @@ POLICYGROUPS_DIR="%s/templates" '''Test manifest arg conflicts with policy-vendor arg''' self._manifest_conflicts("--policy-vendor", "somevendor") - # # Test genpolicy # - def _gen_policy(self, name=None, template=None, extra_args=[]): '''Generate a policy''' # Build up our args @@ -834,16 +838,16 @@ POLICYGROUPS_DIR="%s/templates" def test__is_safe(self): '''Test _is_safe()''' bad = ( - "/../../../../etc/passwd", - "abstraction with spaces", - "semicolon;bad", - "bad\x00baz", - "foo/bar", - "foo'bar", - 'foo"bar', - ) + "/../../../../etc/passwd", + "abstraction with spaces", + "semicolon;bad", + "bad\x00baz", + "foo/bar", + "foo'bar", + 'foo"bar', + ) for s in bad: - self.assertFalse(easyprof._is_safe(s), "'%s' should be bad" %s) + self.assertFalse(easyprof._is_safe(s), "'%s' should be bad" % s) def test_genpolicy_templates_abspath(self): '''Test genpolicy (abspath to template)''' @@ -873,7 +877,7 @@ POLICYGROUPS_DIR="%s/templates" return except Exception: raise - raise Exception ("template should be invalid") + raise Exception("template should be invalid") def test_genpolicy_name(self): '''Test genpolicy (name)''' @@ -936,7 +940,7 @@ POLICYGROUPS_DIR="%s/templates" continue except Exception: raise - raise Exception ("abstraction '%s' should be invalid" % s) + raise Exception("abstraction '%s' should be invalid" % s) def _create_tmp_base_dir(self, prefix='', abstractions=[], tunables=[]): '''Create a temporary base dir layout''' @@ -986,7 +990,7 @@ POLICYGROUPS_DIR="%s/templates" def test_genpolicy_abstractions_custom_base_bad(self): '''Test genpolicy (custom base dir - bad base dirs)''' abstraction = "custom-base-dir-test-abstraction" - bad = [ None, '/etc/apparmor.d', '/' ] + bad = [None, '/etc/apparmor.d', '/'] for base in bad: try: args = ['--abstractions=%s' % abstraction] @@ -997,7 +1001,7 @@ POLICYGROUPS_DIR="%s/templates" continue except Exception: raise - raise Exception ("abstraction '%s' should be invalid" % abstraction) + raise Exception("abstraction '%s' should be invalid" % abstraction) def test_genpolicy_abstractions_custom_include(self): '''Test genpolicy (custom include dir)''' @@ -1015,7 +1019,7 @@ POLICYGROUPS_DIR="%s/templates" def test_genpolicy_abstractions_custom_include_bad(self): '''Test genpolicy (custom include dir - bad include dirs)''' abstraction = "custom-include-dir-test-abstraction" - bad = [ None, '/etc/apparmor.d', '/' ] + bad = [None, '/etc/apparmor.d', '/'] for include in bad: try: args = ['--abstractions=%s' % abstraction] @@ -1026,15 +1030,15 @@ POLICYGROUPS_DIR="%s/templates" continue except Exception: raise - raise Exception ("abstraction '%s' should be invalid" % abstraction) + raise Exception("abstraction '%s' should be invalid" % abstraction) def test_genpolicy_profile_name_bad(self): '''Test genpolicy (profile name - bad values)''' bad = [ - "/../../../../etc/passwd", - "../../../../etc/passwd", - "profile name with spaces", - ] + "/../../../../etc/passwd", + "../../../../etc/passwd", + "profile name with spaces", + ] for s in bad: try: self._gen_policy(extra_args=['--profile-name=%s' % s]) @@ -1042,15 +1046,15 @@ POLICYGROUPS_DIR="%s/templates" continue except Exception: raise - raise Exception ("profile_name '%s' should be invalid" % s) + raise Exception("profile_name '%s' should be invalid" % s) def test_genpolicy_policy_group_bad(self): '''Test genpolicy (policy group - bad values)''' bad = [ - "/../../../../etc/passwd", - "../../../../etc/passwd", - "profile name with spaces", - ] + "/../../../../etc/passwd", + "../../../../etc/passwd", + "profile name with spaces", + ] for s in bad: try: self._gen_policy(extra_args=['--policy-groups=%s' % s]) @@ -1058,7 +1062,7 @@ POLICYGROUPS_DIR="%s/templates" continue except Exception: raise - raise Exception ("policy group '%s' should be invalid" % s) + raise Exception("policy group '%s' should be invalid" % s) def test_genpolicy_policygroups(self): '''Test genpolicy (single policygroup)''' @@ -1100,7 +1104,7 @@ POLICYGROUPS_DIR="%s/templates" return except Exception: raise - raise Exception ("policygroup should be invalid") + raise Exception("policygroup should be invalid") def test_genpolicy_readpath_file(self): '''Test genpolicy (read-path file)''' @@ -1170,13 +1174,15 @@ POLICYGROUPS_DIR="%s/templates" def test_genpolicy_readpath_multiple(self): '''Test genpolicy (read-path multiple)''' - paths = ["/opt/test-foo", - "/home/*/test-foo", - "@{HOME}/test-foo", - "@{HOMEDIRS}/test-foo", - "/opt/test-foo-dir/", - "/opt/test-foo-dir/*", - "/opt/test-foo-dir/**"] + paths = [ + "/opt/test-foo", + "/home/*/test-foo", + "@{HOME}/test-foo", + "@{HOMEDIRS}/test-foo", + "/opt/test-foo-dir/", + "/opt/test-foo-dir/*", + "/opt/test-foo-dir/**", + ] args = [] search_terms = [] for s in paths: @@ -1209,7 +1215,7 @@ POLICYGROUPS_DIR="%s/templates" return except Exception: raise - raise Exception ("read-path should be invalid") + raise Exception("read-path should be invalid") def test_genpolicy_writepath_file(self): '''Test genpolicy (write-path file)''' @@ -1279,13 +1285,15 @@ POLICYGROUPS_DIR="%s/templates" def test_genpolicy_writepath_multiple(self): '''Test genpolicy (write-path multiple)''' - paths = ["/opt/test-foo", - "/home/*/test-foo", - "@{HOME}/test-foo", - "@{HOMEDIRS}/test-foo", - "/opt/test-foo-dir/", - "/opt/test-foo-dir/*", - "/opt/test-foo-dir/**"] + paths = [ + "/opt/test-foo", + "/home/*/test-foo", + "@{HOME}/test-foo", + "@{HOMEDIRS}/test-foo", + "/opt/test-foo-dir/", + "/opt/test-foo-dir/*", + "/opt/test-foo-dir/**", + ] args = [] search_terms = [] for s in paths: @@ -1318,7 +1326,7 @@ POLICYGROUPS_DIR="%s/templates" return except Exception: raise - raise Exception ("write-path should be invalid") + raise Exception("write-path should be invalid") def test_genpolicy_templatevar(self): '''Test genpolicy (template-var single)''' @@ -1365,7 +1373,7 @@ POLICYGROUPS_DIR="%s/templates" continue except Exception: raise - raise Exception ("template-var should be invalid") + raise Exception("template-var should be invalid") def test_genpolicy_invalid_template_policy(self): '''Test genpolicy (invalid template policy)''' @@ -1390,7 +1398,7 @@ POLICYGROUPS_DIR="%s/templates" return except Exception: raise - raise Exception ("policy should be invalid") + raise Exception("policy should be invalid") def test_genpolicy_no_binary_without_profile_name(self): '''Test genpolicy (no binary with no profile name)''' @@ -1400,7 +1408,7 @@ POLICYGROUPS_DIR="%s/templates" return except Exception: raise - raise Exception ("No binary or profile name should have been invalid") + raise Exception("No binary or profile name should have been invalid") def test_genpolicy_with_binary_with_profile_name(self): '''Test genpolicy (binary with profile name)''' @@ -1468,7 +1476,7 @@ POLICYGROUPS_DIR="%s/templates" return except Exception: raise - raise Exception ("abs path template name should be invalid") + raise Exception("abs path template name should be invalid") def test_gen_manifest_escape_path_templates(self): '''Test gen_manifest_policy (esc path template)''' @@ -1480,7 +1488,7 @@ POLICYGROUPS_DIR="%s/templates" return except Exception: raise - raise Exception ("../ template name should be invalid") + raise Exception("../ template name should be invalid") def test_gen_manifest_policy_templates_nonexistent(self): '''Test gen manifest policy (nonexistent template)''' @@ -1492,7 +1500,7 @@ POLICYGROUPS_DIR="%s/templates" return except Exception: raise - raise Exception ("template should be invalid") + raise Exception("template should be invalid") def test_gen_manifest_policy_comment(self): '''Test gen manifest policy (comment)''' @@ -1571,7 +1579,7 @@ POLICYGROUPS_DIR="%s/templates" return except Exception: raise - raise Exception ("policygroup should be invalid") + raise Exception("policygroup should be invalid") def test_gen_manifest_policy_templatevar(self): '''Test gen manifest policy (template-var single)''' @@ -1599,24 +1607,25 @@ POLICYGROUPS_DIR="%s/templates" def test_gen_manifest_policy_invalid_keys(self): '''Test gen manifest policy (invalid keys)''' - keys = ['config_file', - 'debug', - 'help', - 'list-templates', - 'list_templates', - 'show-template', - 'show_template', - 'list-policy-groups', - 'list_policy_groups', - 'show-policy-group', - 'show_policy_group', - 'templates-dir', - 'templates_dir', - 'policy-groups-dir', - 'policy_groups_dir', - 'nonexistent', - 'no_verify', - ] + keys = [ + 'config_file', + 'debug', + 'help', + 'list-templates', + 'list_templates', + 'show-template', + 'show_template', + 'list-policy-groups', + 'list_policy_groups', + 'show-policy-group', + 'show_policy_group', + 'templates-dir', + 'templates_dir', + 'policy-groups-dir', + 'policy_groups_dir', + 'nonexistent', + 'no_verify', + ] args = self.full_args args.append("--manifest=/dev/null") @@ -1630,7 +1639,7 @@ POLICYGROUPS_DIR="%s/templates" easyprof.parse_manifest(j, self.options) except AppArmorException: continue - raise Exception ("'%s' should be invalid" % k) + raise Exception("'%s' should be invalid" % k) def test_gen_manifest(self): '''Test gen_manifest''' @@ -1758,7 +1767,7 @@ POLICYGROUPS_DIR="%s/templates" easyprof.AppArmorEasyProfile(binary, self.options) except AppArmorException: return - raise Exception ("Should have failed on missing version") + raise Exception("Should have failed on missing version") def test_parse_manifest_no_vendor(self): '''Test parse_manifest (version with no vendor)''' @@ -1790,7 +1799,7 @@ POLICYGROUPS_DIR="%s/templates" easyprof.AppArmorEasyProfile(binary, self.options) except AppArmorException: return - raise Exception ("Should have failed on missing vendor") + raise Exception("Should have failed on missing vendor") def test_parse_manifest_multiple(self): '''Test parse_manifest_multiple''' @@ -1856,7 +1865,6 @@ POLICYGROUPS_DIR="%s/templates" easyp.gen_manifest(params) easyp.gen_policy(**params) - # verify manifest tests def _verify_manifest(self, m, expected, invalid=False): args = self.full_args @@ -1870,9 +1878,9 @@ POLICYGROUPS_DIR="%s/templates" raise params = easyprof.gen_policy_params(binary, options) if expected: - self.assertTrue(easyprof.verify_manifest(params, args), "params=%s\nmanifest=%s" % (params,m)) + self.assertTrue(easyprof.verify_manifest(params, args), "params=%s\nmanifest=%s" % (params, m)) else: - self.assertFalse(easyprof.verify_manifest(params, args), "params=%s\nmanifest=%s" % (params,m)) + self.assertFalse(easyprof.verify_manifest(params, args), "params=%s\nmanifest=%s" % (params, m)) def test_verify_manifest_full(self): '''Test verify_manifest (full)''' @@ -2066,7 +2074,7 @@ POLICYGROUPS_DIR="%s/templates" '"VAR6": "b}ar"', '"VAR7": "bar[0-9]"', '"VAR8": "b{ar"', - '"VAR9": "foo/bar"' # this is valid, but potentially unsafe + '"VAR9": "foo/bar"' # this is valid, but potentially unsafe ): m = '''{ "security": { @@ -2153,8 +2161,7 @@ POLICYGROUPS_DIR="%s/templates" except AppArmorException: return - raise Exception ("Should have failed with invalid variable declaration") - + raise Exception("Should have failed with invalid variable declaration") # policy version tests def test_policy_vendor_manifest_nonexistent(self): @@ -2182,7 +2189,7 @@ POLICYGROUPS_DIR="%s/templates" except AppArmorException: return - raise Exception ("Should have failed with non-existent directory") + raise Exception("Should have failed with non-existent directory") def test_policy_version_manifest(self): '''Test policy version via manifest (good)''' @@ -2272,16 +2279,16 @@ POLICYGROUPS_DIR="%s/templates" except AppArmorException: return - raise Exception ("Should have failed with non-existent directory") + raise Exception("Should have failed with non-existent directory") def test_policy_version_args_bad(self): '''Test policy version via command line args (bad)''' bad = [ - "../../../../../../etc", - "notanumber", - "v1.0a", - "-1", - ] + "../../../../../../etc", + "notanumber", + "v1.0a", + "-1", + ] for policy_version in bad: args = self.full_args args.append("--policy-version=%s" % policy_version) @@ -2294,15 +2301,15 @@ POLICYGROUPS_DIR="%s/templates" except AppArmorException: continue - raise Exception ("Should have failed with bad version") + raise Exception("Should have failed with bad version") def test_policy_vendor_args_bad(self): '''Test policy vendor via command line args (bad)''' bad = [ - "../../../../../../etc", - "vendor with space", - "semicolon;isbad", - ] + "../../../../../../etc", + "vendor with space", + "semicolon;isbad", + ] for policy_vendor in bad: args = self.full_args args.append("--policy-vendor=%s" % policy_vendor) @@ -2315,7 +2322,7 @@ POLICYGROUPS_DIR="%s/templates" except AppArmorException: continue - raise Exception ("Should have failed with bad vendor") + raise Exception("Should have failed with bad vendor") # output_directory tests def test_output_directory_multiple(self): @@ -2454,9 +2461,6 @@ POLICYGROUPS_DIR="%s/templates" f = os.path.join(out_dir, fn) self.assertTrue(os.path.exists(f), "Could not find '%s'" % f) - - - def test_output_directory_invalid(self): '''Test output_directory (output directory exists as file)''' files = dict() @@ -2479,7 +2483,6 @@ POLICYGROUPS_DIR="%s/templates" } }''' % files["usr.bin.baz"] - out_dir = os.path.join(self.tmpdir, "output") open(out_dir, 'w').close() @@ -2493,7 +2496,7 @@ POLICYGROUPS_DIR="%s/templates" easyp.output_policy(params, dir=out_dir) except AppArmorException: return - raise Exception ("Should have failed with 'is not a directory'") + raise Exception("Should have failed with 'is not a directory'") def test_output_directory_invalid_params(self): '''Test output_directory (no binary or profile_name)''' @@ -2530,7 +2533,7 @@ POLICYGROUPS_DIR="%s/templates" easyp.output_policy(params, dir=out_dir) except AppArmorException: return - raise Exception ("Should have failed with 'Must specify binary and/or profile name'") + raise Exception("Should have failed with 'Must specify binary and/or profile name'") def test_output_directory_invalid2(self): '''Test output_directory (profile exists)''' @@ -2568,7 +2571,7 @@ POLICYGROUPS_DIR="%s/templates" easyp.output_policy(params, dir=out_dir) except AppArmorException: return - raise Exception ("Should have failed with 'already exists'") + raise Exception("Should have failed with 'already exists'") def test_output_directory_args(self): '''Test output_directory (args)''' @@ -2598,64 +2601,68 @@ POLICYGROUPS_DIR="%s/templates" # def test_valid_profile_name(self): '''Test valid_profile_name''' - names = ['foo', - 'com.example.foo', - '/usr/bin/foo', - 'com.example.app_myapp_1:2.3+ab12~foo', - ] + names = [ + 'foo', + 'com.example.foo', + '/usr/bin/foo', + 'com.example.app_myapp_1:2.3+ab12~foo', + ] for n in names: self.assertTrue(easyprof.valid_profile_name(n), "'%s' should be valid" % n) def test_valid_profile_name_invalid(self): '''Test valid_profile_name (invalid)''' - names = ['fo/o', - '/../../etc/passwd', - '../../etc/passwd', - './../etc/passwd', - './etc/passwd', - '/usr/bin//foo', - '/usr/bin/./foo', - 'foo`', - 'foo!', - 'foo@', - 'foo$', - 'foo#', - 'foo%', - 'foo^', - 'foo&', - 'foo*', - 'foo(', - 'foo)', - 'foo=', - 'foo{', - 'foo}', - 'foo[', - 'foo]', - 'foo|', - 'foo/', - 'foo\\', - 'foo;', - 'foo\'', - 'foo"', - 'foo<', - 'foo>', - 'foo?', - 'foo\/', - 'foo,', - '_foo', - ] + names = [ + 'fo/o', + '/../../etc/passwd', + '../../etc/passwd', + './../etc/passwd', + './etc/passwd', + '/usr/bin//foo', + '/usr/bin/./foo', + 'foo`', + 'foo!', + 'foo@', + 'foo$', + 'foo#', + 'foo%', + 'foo^', + 'foo&', + 'foo*', + 'foo(', + 'foo)', + 'foo=', + 'foo{', + 'foo}', + 'foo[', + 'foo]', + 'foo|', + 'foo/', + 'foo\\', + 'foo;', + 'foo\'', + 'foo"', + 'foo<', + 'foo>', + 'foo?', + 'foo\/', + 'foo,', + '_foo', + ] for n in names: self.assertFalse(easyprof.valid_profile_name(n), "'%s' should be invalid" % n) def test_valid_path(self): '''Test valid_path''' - names = ['/bin/bar', - '/etc/apparmor.d/com.example.app_myapp_1:2.3+ab12~foo', - ] - names_rel = ['bin/bar', - 'apparmor.d/com.example.app_myapp_1:2.3+ab12~foo', - 'com.example.app_myapp_1:2.3+ab12~foo', - ] + names = [ + '/bin/bar', + '/etc/apparmor.d/com.example.app_myapp_1:2.3+ab12~foo', + ] + names_rel = [ + 'bin/bar', + 'apparmor.d/com.example.app_myapp_1:2.3+ab12~foo', + 'com.example.app_myapp_1:2.3+ab12~foo', + ] for n in names: self.assertTrue(easyprof.valid_path(n), "'%s' should be valid" % n) for n in names_rel: @@ -2663,16 +2670,18 @@ POLICYGROUPS_DIR="%s/templates" def test_zz_valid_path_invalid(self): '''Test valid_path (invalid)''' - names = ['/bin//bar', - 'bin/bar', - '/../etc/passwd', - './bin/bar', - './', - ] - names_rel = ['bin/../bar', - 'apparmor.d/../passwd', - 'com.example.app_"myapp_1:2.3+ab12~foo', - ] + names = [ + '/bin//bar', + 'bin/bar', + '/../etc/passwd', + './bin/bar', + './', + ] + names_rel = [ + 'bin/../bar', + 'apparmor.d/../passwd', + 'com.example.app_"myapp_1:2.3+ab12~foo', + ] for n in names: self.assertFalse(easyprof.valid_path(n, relative_ok=False), "'%s' should be invalid" % n) for n in names_rel: diff --git a/utils/test/test-aa-notify.py b/utils/test/test-aa-notify.py index 3ea085371..6facd05a6 100644 --- a/utils/test/test-aa-notify.py +++ b/utils/test/test-aa-notify.py @@ -25,6 +25,7 @@ import apparmor.aa as aa # these tests in an installed environment aanotify_bin = ["../aa-notify"] + # http://www.chiark.greenend.org.uk/ucgi/~cjwatson/blosxom/2009-07-02-python-sigpipe.html # This is needed so that the subprocesses that produce endless output # actually quit when the reader goes away. diff --git a/utils/test/test-aa.py b/utils/test/test-aa.py index b60e0b4b1..fd56595a4 100644 --- a/utils/test/test-aa.py +++ b/utils/test/test-aa.py @@ -17,15 +17,17 @@ import os import shutil import apparmor.aa # needed to set global vars in some tests -from apparmor.aa import (check_for_apparmor, get_output, get_reqs, get_interpreter_and_abstraction, create_new_profile, - get_profile_flags, change_profile_flags, set_options_audit_mode, set_options_owner_mode, - parse_profile_data, - get_file_perms, propose_file_rules, merged_to_split, split_to_merged) +from apparmor.aa import ( + check_for_apparmor, get_output, get_reqs, get_interpreter_and_abstraction, create_new_profile, + get_profile_flags, change_profile_flags, set_options_audit_mode, set_options_owner_mode, + parse_profile_data, + get_file_perms, propose_file_rules, merged_to_split, split_to_merged) from apparmor.aare import AARE from apparmor.common import AppArmorException, AppArmorBug, is_skippable_file from apparmor.rule.file import FileRule from apparmor.rule.include import IncludeRule + class AaTestWithTempdir(AATest): def AASetup(self): self.createTmpdir() @@ -35,12 +37,14 @@ class AaTest_check_for_apparmor(AaTestWithTempdir): FILESYSTEMS_WITH_SECURITYFS = 'nodev\tdevtmpfs\nnodev\tsecurityfs\nnodev\tsockfs\n\text3\n\text2\n\text4' FILESYSTEMS_WITHOUT_SECURITYFS = 'nodev\tdevtmpfs\nnodev\tsockfs\n\text3\n\text2\n\text4' - MOUNTS_WITH_SECURITYFS = ( 'proc /proc proc rw,relatime 0 0\n' + MOUNTS_WITH_SECURITYFS = ( + 'proc /proc proc rw,relatime 0 0\n' 'securityfs %s/security securityfs rw,nosuid,nodev,noexec,relatime 0 0\n' - '/dev/sda1 / ext3 rw,noatime,data=ordered 0 0' ) + '/dev/sda1 / ext3 rw,noatime,data=ordered 0 0') - MOUNTS_WITHOUT_SECURITYFS = ( 'proc /proc proc rw,relatime 0 0\n' - '/dev/sda1 / ext3 rw,noatime,data=ordered 0 0' ) + MOUNTS_WITHOUT_SECURITYFS = ( + 'proc /proc proc rw,relatime 0 0\n' + '/dev/sda1 / ext3 rw,noatime,data=ordered 0 0') def test_check_for_apparmor_None_1(self): filesystems = write_file(self.tmpdir, 'filesystems', self.FILESYSTEMS_WITHOUT_SECURITYFS) @@ -77,12 +81,14 @@ class AaTest_check_for_apparmor(AaTestWithTempdir): mounts = write_file(self.tmpdir, 'mounts', self.MOUNTS_WITH_SECURITYFS % self.tmpdir) self.assertEqual('%s/security/apparmor' % self.tmpdir, check_for_apparmor(filesystems, mounts)) + class AATest_get_output(AATest): tests = ( - (('./fake_ldd', '/AATest/lib64/libc-2.22.so'), (0, [' /AATest/lib64/ld-linux-x86-64.so.2 (0x0000556858473000)', ' linux-vdso.so.1 (0x00007ffe98912000)'] )), - (('./fake_ldd', '/tmp/aa-test-foo'), (0, [' not a dynamic executable'] )), - (('./fake_ldd', 'invalid'), (1, [] )), # stderr is not part of output + (('./fake_ldd', '/AATest/lib64/libc-2.22.so'), (0, [' /AATest/lib64/ld-linux-x86-64.so.2 (0x0000556858473000)', ' linux-vdso.so.1 (0x00007ffe98912000)'])), + (('./fake_ldd', '/tmp/aa-test-foo'), (0, [' not a dynamic executable'])), + (('./fake_ldd', 'invalid'), (1, [])), # stderr is not part of output ) + def _run_test(self, params, expected): self.assertEqual(get_output(params), expected) @@ -90,6 +96,7 @@ class AATest_get_output(AATest): with self.assertRaises(AppArmorException): ret, output = get_output(('./_file_/_not_/_found_',)) + class AATest_get_reqs(AATest): tests = ( ('/AATest/bin/bash', ['/AATest/lib64/libreadline.so.6', '/AATest/lib64/libtinfo.so.6', '/AATest/lib64/libdl.so.2', '/AATest/lib64/libc.so.6', '/AATest/lib64/ld-linux-x86-64.so.2']), @@ -101,19 +108,21 @@ class AATest_get_reqs(AATest): apparmor.aa.cfg['settings']['ldd'] = './fake_ldd' self.assertEqual(get_reqs(params), expected) + class AaTest_create_new_profile(AATest): tests = ( - # file content filename expected interpreter expected abstraction (besides 'base') expected profiles - (('#!/bin/bash\ntrue', 'script'), (u'/bin/bash', 'abstractions/bash', ['script'])), - (('foo bar', 'fake_binary'), (None, None, ['fake_binary'])), - (('hats expected', 'apache2'), (None, None, ['apache2', 'apache2//DEFAULT_URI', 'apache2//HANDLING_UNTRUSTED_INPUT'])), + # file content filename expected interpreter expected abstraction (besides 'base') expected profiles + (('#!/bin/bash\ntrue', 'script'), (u'/bin/bash', 'abstractions/bash', ['script'])), + (('foo bar', 'fake_binary'), (None, None, ['fake_binary'])), + (('hats expected', 'apache2'), (None, None, ['apache2', 'apache2//DEFAULT_URI', 'apache2//HANDLING_UNTRUSTED_INPUT'])), ) + def _run_test(self, params, expected): apparmor.aa.cfg['settings']['ldd'] = './fake_ldd' self.createTmpdir() - #copy the local profiles to the test directory + # copy the local profiles to the test directory self.profile_dir = '%s/profiles' % self.tmpdir shutil.copytree('../../profiles/apparmor.d/', self.profile_dir, symlinks=True) @@ -139,8 +148,12 @@ class AaTest_create_new_profile(AATest): self.assertEqual(list(profile.keys()), expected_profiles) if exp_interpreter_path: - self.assertEqual(set(profile[program]['file'].get_clean()), {'%s ix,' % exp_interpreter_path, '%s r,' % program, '', - '/AATest/lib64/libtinfo.so.* mr,', '/AATest/lib64/libc.so.* mr,', '/AATest/lib64/libdl.so.* mr,', '/AATest/lib64/libreadline.so.* mr,', '/AATest/lib64/ld-linux-x86-64.so.* mr,' }) + self.assertEqual( + set(profile[program]['file'].get_clean()), + {'%s ix,' % exp_interpreter_path, '%s r,' % program, '', + '/AATest/lib64/libtinfo.so.* mr,', '/AATest/lib64/libc.so.* mr,', + '/AATest/lib64/libdl.so.* mr,', '/AATest/lib64/libreadline.so.* mr,', + '/AATest/lib64/ld-linux-x86-64.so.* mr,'}) else: self.assertEqual(set(profile[program]['file'].get_clean()), {'%s mr,' % program, ''}) @@ -149,25 +162,26 @@ class AaTest_create_new_profile(AATest): else: self.assertEqual(profile[program]['inc_ie'].get_clean(), ['include ', '']) + class AaTest_get_interpreter_and_abstraction(AATest): tests = ( - ('#!/bin/bash', ('/bin/bash', 'abstractions/bash')), - ('#!/bin/dash', ('/bin/dash', 'abstractions/bash')), - ('#!/bin/sh', ('/bin/sh', 'abstractions/bash')), - ('#! /bin/sh ', ('/bin/sh', 'abstractions/bash')), - ('#! /bin/sh -x ', ('/bin/sh', 'abstractions/bash')), # '-x' is not part of the interpreter path - ('#!/usr/bin/perl', ('/usr/bin/perl', 'abstractions/perl')), - ('#!/usr/bin/perl -w', ('/usr/bin/perl', 'abstractions/perl')), # '-w' is not part of the interpreter path - ('#!/usr/bin/python', ('/usr/bin/python', 'abstractions/python')), - ('#!/usr/bin/python2', ('/usr/bin/python2', 'abstractions/python')), - ('#!/usr/bin/python2.7', ('/usr/bin/python2.7', 'abstractions/python')), - ('#!/usr/bin/python3', ('/usr/bin/python3', 'abstractions/python')), - ('#!/usr/bin/python4', ('/usr/bin/python4', None)), # python abstraction is only applied to py2 and py3 - ('#!/usr/bin/ruby', ('/usr/bin/ruby', 'abstractions/ruby')), - ('#!/usr/bin/ruby2.2', ('/usr/bin/ruby2.2', 'abstractions/ruby')), - ('#!/usr/bin/ruby1.9.1', ('/usr/bin/ruby1.9.1', 'abstractions/ruby')), - ('#!/usr/bin/foobarbaz', ('/usr/bin/foobarbaz', None)), # we don't have an abstraction for "foobarbaz" - ('foo', (None, None)), # no hashbang - not a script + ('#!/bin/bash', ('/bin/bash', 'abstractions/bash')), + ('#!/bin/dash', ('/bin/dash', 'abstractions/bash')), + ('#!/bin/sh', ('/bin/sh', 'abstractions/bash')), + ('#! /bin/sh ', ('/bin/sh', 'abstractions/bash')), + ('#! /bin/sh -x ', ('/bin/sh', 'abstractions/bash')), # '-x' is not part of the interpreter path + ('#!/usr/bin/perl', ('/usr/bin/perl', 'abstractions/perl')), + ('#!/usr/bin/perl -w', ('/usr/bin/perl', 'abstractions/perl')), # '-w' is not part of the interpreter path + ('#!/usr/bin/python', ('/usr/bin/python', 'abstractions/python')), + ('#!/usr/bin/python2', ('/usr/bin/python2', 'abstractions/python')), + ('#!/usr/bin/python2.7', ('/usr/bin/python2.7', 'abstractions/python')), + ('#!/usr/bin/python3', ('/usr/bin/python3', 'abstractions/python')), + ('#!/usr/bin/python4', ('/usr/bin/python4', None)), # python abstraction is only applied to py2 and py3 + ('#!/usr/bin/ruby', ('/usr/bin/ruby', 'abstractions/ruby')), + ('#!/usr/bin/ruby2.2', ('/usr/bin/ruby2.2', 'abstractions/ruby')), + ('#!/usr/bin/ruby1.9.1', ('/usr/bin/ruby1.9.1', 'abstractions/ruby')), + ('#!/usr/bin/foobarbaz', ('/usr/bin/foobarbaz', None)), # we don't have an abstraction for "foobarbaz" + ('foo', (None, None)), # no hashbang - not a script ) def _run_test(self, params, expected): @@ -222,10 +236,11 @@ class AaTest_get_profile_flags(AaTestWithTempdir): with self.assertRaises(AppArmorException): self._test_get_flags('/no-such-profile flags=(complain)', 'complain') + class AaTest_change_profile_flags(AaTestWithTempdir): - def _test_change_profile_flags(self, profile, old_flags, flags_to_change, set_flag, expected_flags, whitespace='', comment='', - more_rules='', expected_more_rules='@-@-@', - check_new_flags=True, profile_name='/foo'): + def _test_change_profile_flags( + self, profile, old_flags, flags_to_change, set_flag, expected_flags, whitespace='', + comment='', more_rules='', expected_more_rules='@-@-@', check_new_flags=True, profile_name='/foo'): if old_flags: old_flags = ' %s' % old_flags @@ -304,39 +319,45 @@ class AaTest_change_profile_flags(AaTestWithTempdir): # test handling of hat flags def test_set_flags_with_hat_01(self): - self._test_change_profile_flags('/foo', 'flags=(complain)', 'audit', True, 'audit, complain', + self._test_change_profile_flags( + '/foo', 'flags=(complain)', 'audit', True, 'audit, complain', more_rules='\n ^foobar {\n}\n', expected_more_rules='\n ^foobar flags=(audit) {\n}\n' ) def test_change_profile_flags_with_hat_02(self): - self._test_change_profile_flags('/foo', 'flags=(complain)', 'audit', False, 'complain', + self._test_change_profile_flags( + '/foo', 'flags=(complain)', 'audit', False, 'complain', profile_name=None, more_rules='\n ^foobar flags=(audit) {\n}\n', expected_more_rules='\n ^foobar {\n}\n' ) def test_change_profile_flags_with_hat_03(self): - self._test_change_profile_flags('/foo', 'flags=(complain)', 'audit', True, 'audit, complain', + self._test_change_profile_flags( + '/foo', 'flags=(complain)', 'audit', True, 'audit, complain', more_rules='\n^foobar (attach_disconnected) { # comment\n}\n', expected_more_rules='\n ^foobar flags=(attach_disconnected, audit) { # comment\n}\n' ) def test_change_profile_flags_with_hat_04(self): - self._test_change_profile_flags('/foo', '', 'audit', True, 'audit', + self._test_change_profile_flags( + '/foo', '', 'audit', True, 'audit', more_rules='\n hat foobar (attach_disconnected) { # comment\n}\n', expected_more_rules='\n hat foobar flags=(attach_disconnected, audit) { # comment\n}\n' ) def test_change_profile_flags_with_hat_05(self): - self._test_change_profile_flags('/foo', '(audit)', 'audit', False, '', + self._test_change_profile_flags( + '/foo', '(audit)', 'audit', False, '', more_rules='\n hat foobar (attach_disconnected) { # comment\n}\n', expected_more_rules='\n hat foobar flags=(attach_disconnected) { # comment\n}\n' ) # test handling of child profiles def test_change_profile_flags_with_child_01(self): - self._test_change_profile_flags('/foo', 'flags=(complain)', 'audit', True, 'audit, complain', + self._test_change_profile_flags( + '/foo', 'flags=(complain)', 'audit', True, 'audit, complain', profile_name=None, more_rules='\n profile /bin/bar {\n}\n', expected_more_rules='\n profile /bin/bar flags=(audit) {\n}\n' @@ -344,12 +365,12 @@ class AaTest_change_profile_flags(AaTestWithTempdir): def test_change_profile_flags_with_child_02(self): # XXX child profile flags aren't changed if profile parameter is not None - self._test_change_profile_flags('/foo', 'flags=(complain)', 'audit', True, 'audit, complain', + self._test_change_profile_flags( + '/foo', 'flags=(complain)', 'audit', True, 'audit, complain', more_rules='\n profile /bin/bar {\n}\n', expected_more_rules='\n profile /bin/bar {\n}\n' # flags(audit) should be added ) - def test_change_profile_flags_invalid_01(self): with self.assertRaises(AppArmorBug): self._test_change_profile_flags('/foo', '()', None, False, '', check_new_flags=False) @@ -361,7 +382,7 @@ class AaTest_change_profile_flags(AaTestWithTempdir): self._test_change_profile_flags('/foo', '( )', '', True, '', check_new_flags=False) def test_change_profile_flags_invalid_04(self): with self.assertRaises(AppArmorBug): - self._test_change_profile_flags('/foo', 'flags=(complain, audit)', ' ', True, 'audit, complain', check_new_flags=False) # whitespace-only newflags + self._test_change_profile_flags('/foo', 'flags=(complain, audit)', ' ', True, 'audit, complain', check_new_flags=False) # whitespace-only newflags def test_change_profile_flags_other_profile(self): # test behaviour if the file doesn't contain the specified /foo profile @@ -391,13 +412,14 @@ class AaTest_change_profile_flags(AaTestWithTempdir): with self.assertRaises(IOError): change_profile_flags('%s/file-not-found' % self.tmpdir, '/foo', 'audit', True) + class AaTest_set_options_audit_mode(AATest): tests = ( - ((FileRule.parse('audit /foo/bar r,'), ['/foo/bar r,', '/foo/* r,', '/** r,'] ), ['audit /foo/bar r,', 'audit /foo/* r,', 'audit /** r,']), - ((FileRule.parse('audit /foo/bar r,'), ['/foo/bar r,', 'audit /foo/* r,', 'audit /** r,'] ), ['audit /foo/bar r,', 'audit /foo/* r,', 'audit /** r,']), - ((FileRule.parse('/foo/bar r,'), ['/foo/bar r,', '/foo/* r,', '/** r,'] ), ['/foo/bar r,', '/foo/* r,', '/** r,']), - ((FileRule.parse('/foo/bar r,'), ['audit /foo/bar r,', 'audit /foo/* r,', 'audit /** r,'] ), ['/foo/bar r,', '/foo/* r,', '/** r,']), - ((FileRule.parse('audit /foo/bar r,'), ['/foo/bar r,', '/foo/* r,', '#include ']), ['audit /foo/bar r,', 'audit /foo/* r,', '#include ']), + ((FileRule.parse('audit /foo/bar r,'), ['/foo/bar r,', '/foo/* r,', '/** r,']), ['audit /foo/bar r,', 'audit /foo/* r,', 'audit /** r,']), + ((FileRule.parse('audit /foo/bar r,'), ['/foo/bar r,', 'audit /foo/* r,', 'audit /** r,']), ['audit /foo/bar r,', 'audit /foo/* r,', 'audit /** r,']), + ((FileRule.parse('/foo/bar r,'), ['/foo/bar r,', '/foo/* r,', '/** r,']), ['/foo/bar r,', '/foo/* r,', '/** r,']), + ((FileRule.parse('/foo/bar r,'), ['audit /foo/bar r,', 'audit /foo/* r,', 'audit /** r,']), ['/foo/bar r,', '/foo/* r,', '/** r,']), + ((FileRule.parse('audit /foo/bar r,'), ['/foo/bar r,', '/foo/* r,', '#include ']), ['audit /foo/bar r,', 'audit /foo/* r,', '#include ']), ) def _run_test(self, params, expected): @@ -405,13 +427,14 @@ class AaTest_set_options_audit_mode(AATest): new_options = set_options_audit_mode(rule_obj, options) self.assertEqual(new_options, expected) + class AaTest_set_options_owner_mode(AATest): tests = ( - ((FileRule.parse('owner /foo/bar r,'), ['/foo/bar r,', '/foo/* r,', '/** r,'] ), ['owner /foo/bar r,', 'owner /foo/* r,', 'owner /** r,']), - ((FileRule.parse('owner /foo/bar r,'), ['/foo/bar r,', 'owner /foo/* r,', 'owner /** r,'] ), ['owner /foo/bar r,', 'owner /foo/* r,', 'owner /** r,']), - ((FileRule.parse('/foo/bar r,'), ['/foo/bar r,', '/foo/* r,', '/** r,'] ), ['/foo/bar r,', '/foo/* r,', '/** r,']), - ((FileRule.parse('/foo/bar r,'), ['owner /foo/bar r,', 'owner /foo/* r,', 'owner /** r,'] ), ['/foo/bar r,', '/foo/* r,', '/** r,']), - ((FileRule.parse('audit owner /foo/bar r,'),['audit /foo/bar r,', 'audit /foo/* r,', '#include ']), ['audit owner /foo/bar r,', 'audit owner /foo/* r,', '#include ']), + ((FileRule.parse('owner /foo/bar r,'), ['/foo/bar r,', '/foo/* r,', '/** r,']), ['owner /foo/bar r,', 'owner /foo/* r,', 'owner /** r,']), + ((FileRule.parse('owner /foo/bar r,'), ['/foo/bar r,', 'owner /foo/* r,', 'owner /** r,']), ['owner /foo/bar r,', 'owner /foo/* r,', 'owner /** r,']), + ((FileRule.parse('/foo/bar r,'), ['/foo/bar r,', '/foo/* r,', '/** r,']), ['/foo/bar r,', '/foo/* r,', '/** r,']), + ((FileRule.parse('/foo/bar r,'), ['owner /foo/bar r,', 'owner /foo/* r,', 'owner /** r,']), ['/foo/bar r,', '/foo/* r,', '/** r,']), + ((FileRule.parse('audit owner /foo/bar r,'), ['audit /foo/bar r,', 'audit /foo/* r,', '#include ']), ['audit owner /foo/bar r,', 'audit owner /foo/* r,', '#include ']), ) def _run_test(self, params, expected): @@ -419,6 +442,7 @@ class AaTest_set_options_owner_mode(AATest): new_options = set_options_owner_mode(rule_obj, options) self.assertEqual(new_options, expected) + class AaTest_is_skippable_file(AATest): def test_not_skippable_01(self): self.assertFalse(is_skippable_file('bin.ping')) @@ -467,6 +491,7 @@ class AaTest_is_skippable_file(AATest): def test_skippable_16(self): self.assertTrue(is_skippable_file('README')) + class AaTest_parse_profile_data(AATest): def test_parse_empty_profile_01(self): prof = parse_profile_data('/foo {\n}\n'.split(), 'somefile', False, False) @@ -525,20 +550,21 @@ class AaTest_parse_profile_data(AATest): d = '/foo flags=(complain) xattrs=(user.bar=bar) {\n}\n' parse_profile_data(d.split(), 'somefile', False, False) + class AaTest_get_file_perms_1(AATest): tests = ( - ('/usr/share/common-licenses/foo/bar', {'allow': {'all': set(), 'owner': {'w'} }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/usr/share/common-licenses/**'} }), - ('/dev/null', {'allow': {'all': {'r', 'w', 'k'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/dev/null'} }), - ('/foo/bar', {'allow': {'all': {'r', 'w'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/foo/bar'} }), # exec perms not included - ('/no/thing', {'allow': {'all': set(), 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': set() }), - ('/usr/lib/ispell/', {'allow': {'all': set(), 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': set() }), - ('/usr/lib/aspell/*.so', {'allow': {'all': set(), 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': set() }), + ('/usr/share/common-licenses/foo/bar', {'allow': {'all': set(), 'owner': {'w'}}, 'deny': {'all': set(), 'owner': set()}, 'paths': {'/usr/share/common-licenses/**'}}), + ('/dev/null', {'allow': {'all': {'r', 'w', 'k'}, 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': {'/dev/null'}}), + ('/foo/bar', {'allow': {'all': {'r', 'w'}, 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': {'/foo/bar'}}), # exec perms not included + ('/no/thing', {'allow': {'all': set(), 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': set()}), + ('/usr/lib/ispell/', {'allow': {'all': set(), 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': set()}), + ('/usr/lib/aspell/*.so', {'allow': {'all': set(), 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': set()}), ) def _run_test(self, params, expected): self.createTmpdir() - #copy the local profiles to the test directory + # copy the local profiles to the test directory self.profile_dir = '%s/profiles' % self.tmpdir shutil.copytree('../../profiles/apparmor.d/', self.profile_dir, symlinks=True) @@ -552,21 +578,22 @@ class AaTest_get_file_perms_1(AATest): perms = get_file_perms(profile, params, False, False) # only testing with audit and deny = False self.assertEqual(perms, expected) + class AaTest_get_file_perms_2(AATest): tests = ( - ('/usr/share/common-licenses/foo/bar', {'allow': {'all': {'r'}, 'owner': {'w'} }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/usr/share/common-licenses/**'} }), - ('/usr/share/common-licenses/what/ever', {'allow': {'all': {'r'}, 'owner': {'w'} }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/usr/share/common-licenses/**', '/usr/share/common-licenses/what/ever'} }), - ('/dev/null', {'allow': {'all': {'r', 'w', 'k'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/dev/null'} }), - ('/foo/bar', {'allow': {'all': {'r', 'w'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/foo/bar'} }), # exec perms not included - ('/no/thing', {'allow': {'all': set(), 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': set() }), - ('/usr/lib/ispell/', {'allow': {'all': {'r'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/usr/lib/ispell/', '/{usr/,}lib{,32,64}/**'} }), # from abstractions/enchant - ('/usr/lib/aspell/*.so', {'allow': {'all': {'m', 'r'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/usr/lib/aspell/*', '/usr/lib/aspell/*.so', '/{usr/,}lib{,32,64}/**', '/{usr/,}lib{,32,64}/**.so*'} }), # from abstractions/aspell via abstractions/enchant and from abstractions/base + ('/usr/share/common-licenses/foo/bar', {'allow': {'all': {'r'}, 'owner': {'w'}}, 'deny': {'all': set(), 'owner': set()}, 'paths': {'/usr/share/common-licenses/**'}}), + ('/usr/share/common-licenses/what/ever', {'allow': {'all': {'r'}, 'owner': {'w'}}, 'deny': {'all': set(), 'owner': set()}, 'paths': {'/usr/share/common-licenses/**', '/usr/share/common-licenses/what/ever'}}), + ('/dev/null', {'allow': {'all': {'r', 'w', 'k'}, 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': {'/dev/null'}}), + ('/foo/bar', {'allow': {'all': {'r', 'w'}, 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': {'/foo/bar'}}), # exec perms not included + ('/no/thing', {'allow': {'all': set(), 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': set()}), + ('/usr/lib/ispell/', {'allow': {'all': {'r'}, 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': {'/usr/lib/ispell/', '/{usr/,}lib{,32,64}/**'}}), # from abstractions/enchant + ('/usr/lib/aspell/*.so', {'allow': {'all': {'m', 'r'}, 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': {'/usr/lib/aspell/*', '/usr/lib/aspell/*.so', '/{usr/,}lib{,32,64}/**', '/{usr/,}lib{,32,64}/**.so*'}}), # from abstractions/aspell via abstractions/enchant and from abstractions/base ) def _run_test(self, params, expected): self.createTmpdir() - #copy the local profiles to the test directory + # copy the local profiles to the test directory self.profile_dir = '%s/profiles' % self.tmpdir shutil.copytree('../../profiles/apparmor.d/', self.profile_dir, symlinks=True) @@ -590,21 +617,22 @@ class AaTest_get_file_perms_2(AATest): perms = get_file_perms(profile, params, False, False) # only testing with audit and deny = False self.assertEqual(perms, expected) + class AaTest_propose_file_rules(AATest): tests = ( - # log event path and perms expected proposals - (('/usr/share/common-licenses/foo/bar', 'w'), ['/usr/share/common*/foo/* rw,', '/usr/share/common-licenses/** rw,', '/usr/share/common-licenses/foo/bar rw,'] ), - (('/dev/null', 'wk'), ['/dev/null rwk,'] ), - (('/foo/bar', 'rw'), ['/foo/bar rw,'] ), - (('/usr/lib/ispell/', 'w'), ['/{usr/,}lib{,32,64}/** rw,', '/usr/lib/ispell/ rw,'] ), - (('/usr/lib/aspell/some.so', 'k'), ['/usr/lib/aspell/* mrk,', '/usr/lib/aspell/*.so mrk,', '/{usr/,}lib{,32,64}/** mrk,', '/{usr/,}lib{,32,64}/**.so* mrk,', '/usr/lib/aspell/some.so mrk,'] ), - (('/foo/log', 'w'), ['/foo/log w,'] ), + # log event path and perms expected proposals + (('/usr/share/common-licenses/foo/bar', 'w'), ['/usr/share/common*/foo/* rw,', '/usr/share/common-licenses/** rw,', '/usr/share/common-licenses/foo/bar rw,']), + (('/dev/null', 'wk'), ['/dev/null rwk,']), + (('/foo/bar', 'rw'), ['/foo/bar rw,']), + (('/usr/lib/ispell/', 'w'), ['/{usr/,}lib{,32,64}/** rw,', '/usr/lib/ispell/ rw,']), + (('/usr/lib/aspell/some.so', 'k'), ['/usr/lib/aspell/* mrk,', '/usr/lib/aspell/*.so mrk,', '/{usr/,}lib{,32,64}/** mrk,', '/{usr/,}lib{,32,64}/**.so* mrk,', '/usr/lib/aspell/some.so mrk,']), + (('/foo/log', 'w'), ['/foo/log w,']), ) def _run_test(self, params, expected): self.createTmpdir() - #copy the local profiles to the test directory + # copy the local profiles to the test directory self.profile_dir = '%s/profiles' % self.tmpdir shutil.copytree('../../profiles/apparmor.d/', self.profile_dir, symlinks=True) @@ -624,7 +652,6 @@ class AaTest_propose_file_rules(AATest): profile['inc_ie'].add(IncludeRule.parse('include ')) profile['inc_ie'].add(IncludeRule.parse('include ')) - profile['file'].add(FileRule.parse('owner /usr/share/common-licenses/** w,')) profile['file'].add(FileRule.parse('/dev/null rwk,')) profile['file'].add(FileRule.parse('/foo/bar rwix,')) @@ -637,17 +664,17 @@ class AaTest_propose_file_rules(AATest): class AaTest_propose_file_rules_with_absolute_includes(AATest): tests = ( - # log event path and perms expected proposals - (('/not/found/anywhere', 'r'), ['/not/found/anywhere r,']), - (('/dev/null', 'w'), ['/dev/null rw,']), - (('/some/random/include', 'r'), ['/some/random/include rw,']), - (('/some/other/include', 'w'), ['/some/other/* rw,', '/some/other/inc* rw,', '/some/other/include rw,']), + # log event path and perms expected proposals + (('/not/found/anywhere', 'r'), ['/not/found/anywhere r,']), + (('/dev/null', 'w'), ['/dev/null rw,']), + (('/some/random/include', 'r'), ['/some/random/include rw,']), + (('/some/other/include', 'w'), ['/some/other/* rw,', '/some/other/inc* rw,', '/some/other/include rw,']), ) def _run_test(self, params, expected): self.createTmpdir() - #copy the local profiles to the test directory + # copy the local profiles to the test directory self.profile_dir = '%s/profiles' % self.tmpdir shutil.copytree('../../profiles/apparmor.d/', self.profile_dir, symlinks=True) @@ -677,19 +704,20 @@ class AaTest_propose_file_rules_with_absolute_includes(AATest): class AaTest_nonexistent_includes(AATest): tests = ( - ("/nonexistent/absolute/path", AppArmorException), - ("nonexistent/relative/path", AppArmorBug), # load_include() only accepts absolute paths + ("/nonexistent/absolute/path", AppArmorException), + ("nonexistent/relative/path", AppArmorBug), # load_include() only accepts absolute paths ) def _run_test(self, params, expected): with self.assertRaises(expected): apparmor.aa.load_include(params) + class AaTest_merged_to_split(AATest): tests = ( - ("foo", ("foo", "foo")), - ("foo//bar", ("foo", "bar")), - ("foo//bar//baz", ("foo", "bar")), # XXX known limitation + ("foo", ("foo", "foo")), + ("foo//bar", ("foo", "bar")), + ("foo//bar//baz", ("foo", "bar")), # XXX known limitation ) def _run_test(self, params, expected): @@ -703,10 +731,11 @@ class AaTest_merged_to_split(AATest): self.assertEqual(list(result[profile].keys()), [hat]) self.assertTrue(result[profile][hat]) + class AaTest_split_to_merged(AATest): tests = ( - (("foo", "foo"), "foo"), - (("foo", "bar"), "foo//bar"), + (("foo", "foo"), "foo"), + (("foo", "bar"), "foo//bar"), ) def _run_test(self, params, expected): @@ -721,6 +750,7 @@ class AaTest_split_to_merged(AATest): self.assertEqual(list(result.keys()), [expected]) self.assertTrue(result[expected]) + setup_aa(apparmor.aa) setup_all_loops(__name__) if __name__ == '__main__': diff --git a/utils/test/test-aare.py b/utils/test/test-aare.py index 96af49822..11af0e2ab 100644 --- a/utils/test/test-aare.py +++ b/utils/test/test-aare.py @@ -18,131 +18,133 @@ import re from apparmor.common import convert_regexp, AppArmorBug, AppArmorException from apparmor.aare import AARE, convert_expression_to_aare + class TestConvert_regexp(AATest): tests = ( - ('/foo', '^/foo$'), - ('/{foo,bar}', '^/(foo|bar)$'), - # ('/\{foo,bar}', '^/\{foo,bar}$'), # XXX gets converted to ^/\(foo|bar)$ - ('/fo[abc]', '^/fo[abc]$'), - ('/foo bar', '^/foo bar$'), - ('/x\y', '^/x\y$'), - ('/x\[y', '^/x\[y$'), - ('/x\\y', '^/x\\y$'), - ('/fo?', '^/fo[^/\000]$'), - ('/foo/*', '^/foo/(((?<=/)[^/\000]+)|((?,', exp('', 'abstractions/base', False, True )), # magic path - ('abi , # comment', exp(' # comment', 'abstractions/base', False, True )), - ('abi,#comment', exp(' #comment', 'abstractions/base', False, True )), - (' abi , ', exp('', 'abstractions/base', False, True )), - ('abi "/foo/bar",', exp('', '/foo/bar', False, False)), # absolute path - ('abi "/foo/bar", # comment', exp(' # comment', '/foo/bar', False, False)), - ('abi "/foo/bar",#comment', exp(' #comment', '/foo/bar', False, False)), - (' abi "/foo/bar" , ', exp('', '/foo/bar', False, False)), + # AbiRule object comment path if exists ismagic + ('abi ,', exp('', 'abstractions/base', False, True)), # magic path + ('abi , # comment', exp(' # comment', 'abstractions/base', False, True)), + ('abi,#comment', exp(' #comment', 'abstractions/base', False, True)), + (' abi , ', exp('', 'abstractions/base', False, True)), + ('abi "/foo/bar",', exp('', '/foo/bar', False, False)), # absolute path + ('abi "/foo/bar", # comment', exp(' # comment', '/foo/bar', False, False)), + ('abi "/foo/bar",#comment', exp(' #comment', '/foo/bar', False, False)), + (' abi "/foo/bar" , ', exp('', '/foo/bar', False, False)), ) def _run_test(self, rawrule, expected): @@ -61,13 +63,14 @@ class AbiTestParse(AbiTest): self.assertEqual(rawrule.strip(), obj.raw_rule) self._compare_obj(obj, expected) + class AbiTestParseInvalid(AbiTest): - tests = ( -# (' some abi ', AppArmorException), -# (' /etc/fstab r,', AppArmorException), -# ('/usr/abi r,', AppArmorException), -# ('/abi r,', AppArmorException), - ) + # tests = ( + # (' some abi ', AppArmorException), + # (' /etc/fstab r,', AppArmorException), + # ('/usr/abi r,', AppArmorException), + # ('/abi r,', AppArmorException), + # ) def _run_test(self, rawrule, expected): self.assertTrue(AbiRule.match(rawrule)) # the above invalid rules still match the main regex! @@ -76,30 +79,32 @@ class AbiTestParseInvalid(AbiTest): # class AbiTestParseFromLog(AbiTest): # we'll never have log events for abi + class AbiFromInit(AbiTest): tests = ( - # AbiRule object ifexists ismagic comment path ifexists ismagic - (AbiRule('abi/4.19', False, False) , exp('', 'abi/4.19', False, False )), - (AbiRule('foo', False, False) , exp('', 'foo', False, False )), - (AbiRule('bar', False, True) , exp('', 'bar', False, True )), - (AbiRule('comment', False, False, comment='# cmt') , exp('# cmt', 'comment', False, False )), + # AbiRule object ifexists ismagic comment path ifexists ismagic + (AbiRule('abi/4.19', False, False), exp('', 'abi/4.19', False, False)), + (AbiRule('foo', False, False), exp('', 'foo', False, False)), + (AbiRule('bar', False, True), exp('', 'bar', False, True)), + (AbiRule('comment', False, False, comment='# cmt'), exp('# cmt', 'comment', False, False)), ) def _run_test(self, obj, expected): self._compare_obj(obj, expected) + class InvalidAbiInit(AATest): tests = ( - # init params expected exception - ((False, False, False ) , AppArmorBug), # wrong type for path - (('', False, False ) , AppArmorBug), # empty path - ((None, False, False ) , AppArmorBug), # wrong type for path -# ((' ', False, False ) , AppArmorBug), # whitespace-only path - (('foo', None, False ) , AppArmorBug), # wrong type for ifexists - (('foo', '', False ) , AppArmorBug), # wrong type for ifexists - (('foo', False, None ) , AppArmorBug), # wrong type for ismagic - (('foo', False, '' ) , AppArmorBug), # wrong type for ismagic - (('', True, False ) , AppArmorBug), # ifexists set + # init params expected exception + ((False, False, False), AppArmorBug), # wrong type for path + (('', False, False), AppArmorBug), # empty path + ((None, False, False), AppArmorBug), # wrong type for path + # ((' ', False, False), AppArmorBug), # whitespace-only path + (('foo', None, False), AppArmorBug), # wrong type for ifexists + (('foo', '', False), AppArmorBug), # wrong type for ifexists + (('foo', False, None), AppArmorBug), # wrong type for ismagic + (('foo', False, ''), AppArmorBug), # wrong type for ismagic + (('', True, False), AppArmorBug), # ifexists set ) def _run_test(self, params, expected): @@ -130,8 +135,9 @@ class InvalidAbiInit(AATest): with self.assertRaises(AppArmorBug): AbiRule('foo', True, False) + class InvalidAbiTest(AATest): - def _check_invalid_rawrule(self, rawrule, matches_regex = False): + def _check_invalid_rawrule(self, rawrule, matches_regex=False): obj = None self.assertEqual(AbiRule.match(rawrule), matches_regex) with self.assertRaises(AppArmorException): @@ -152,6 +158,7 @@ class InvalidAbiTest(AATest): # with self.assertRaises(AppArmorBug): # obj.get_clean(1) + class WriteAbiTestAATest(AATest): def _run_test(self, rawrule, expected): self.assertTrue(AbiRule.match(rawrule)) @@ -163,18 +170,18 @@ class WriteAbiTestAATest(AATest): self.assertEqual(rawrule.strip(), raw, 'unexpected raw rule') tests = ( - # raw rule clean rule - (' abi , ', 'abi ,' ), - (' abi foo , ', 'abi "foo",' ), - (' abi "foo" , ', 'abi "foo",' ), - (' abi /foo , ', 'abi "/foo",' ), - (' abi "/foo" , ', 'abi "/foo",' ), + # raw rule clean rule + (' abi , ', 'abi ,'), + (' abi foo , ', 'abi "foo",'), + (' abi "foo" , ', 'abi "foo",'), + (' abi /foo , ', 'abi "/foo",'), + (' abi "/foo" , ', 'abi "/foo",'), - (' abi , # bar ', 'abi , # bar' ), - (' abi foo , # bar ', 'abi "foo", # bar' ), - (' abi "foo", # bar ', 'abi "foo", # bar' ), - (' abi /foo, # bar ', 'abi "/foo", # bar' ), - (' abi "/foo", # bar ', 'abi "/foo", # bar' ), + (' abi , # bar ', 'abi , # bar'), + (' abi foo , # bar ', 'abi "foo", # bar'), + (' abi "foo", # bar ', 'abi "foo", # bar'), + (' abi /foo, # bar ', 'abi "/foo", # bar'), + (' abi "/foo", # bar ', 'abi "/foo", # bar'), ) def test_write_manually(self): @@ -199,82 +206,87 @@ class AbiCoveredTest(AATest): self.assertEqual(obj.is_covered(check_obj), expected[2], 'Mismatch in is_covered, expected %s' % expected[2]) self.assertEqual(obj.is_covered(check_obj, True, True), expected[3], 'Mismatch in is_covered/exact, expected %s' % expected[3]) + class AbiCoveredTest_01(AbiCoveredTest): rule = 'abi ,' tests = ( - # rule equal strict equal covered covered exact - ('abi ,' , ( True , True , True , True )), - ('abi "foo",' , ( False , False , False , False )), - ('abi ,' , ( False , False , False , False )), - ('abi "foo",' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('abi ,', (True, True, True, True)), + ('abi "foo",', (False, False, False, False)), + ('abi ,', (False, False, False, False)), + ('abi "foo",', (False, False, False, False)), ) + class AbiCoveredTest_02(AbiCoveredTest): rule = 'abi "foo",' tests = ( - # rule equal strict equal covered covered exact - ('abi ,' , ( False , False , False , False )), - ('abi "foo",' , ( True , True , True , True )), - ('abi "foobar",' , ( False , False , False , False )), - ('abi foo,' , ( True , False , True , True )), + # rule equal strict equal covered covered exact + ('abi ,', (False, False, False, False)), + ('abi "foo",', (True, True, True, True)), + ('abi "foobar",', (False, False, False, False)), + ('abi foo,', (True, False, True, True)), ) -#class AbiCoveredTest_Invalid(AATest): -# def test_borked_obj_is_covered_1(self): -# obj = AbiRule.parse('abi ') -# testobj = AbiRule('foo', True, True) -# testobj.path = '' +# class AbiCoveredTest_Invalid(AATest): +# def test_borked_obj_is_covered_1(self): +# obj = AbiRule.parse('abi ') +# +# testobj = AbiRule('foo', True, True) +# testobj.path = '' +# +# with self.assertRaises(AppArmorBug): +# obj.is_covered(testobj) +# +# def test_borked_obj_is_covered_2(self): +# obj = AbiRule.parse('abi send set=quit peer=/foo,') +# +# testobj = AbiRule('send', 'quit', '/foo') +# testobj.abi = '' +# +# with self.assertRaises(AppArmorBug): +# obj.is_covered(testobj) +# +# def test_borked_obj_is_covered_3(self): +# obj = AbiRule.parse('abi send set=quit peer=/foo,') +# +# testobj = AbiRule('send', 'quit', '/foo') +# testobj.peer = '' +# +# with self.assertRaises(AppArmorBug): +# obj.is_covered(testobj) +# +# def test_invalid_is_covered(self): +# obj = AbiRule.parse('abi send,') +# +# testobj = BaseRule() # different type +# +# with self.assertRaises(AppArmorBug): +# obj.is_covered(testobj) +# +# def test_invalid_is_equal(self): +# obj = AbiRule.parse('abi send,') +# +# testobj = BaseRule() # different type +# +# with self.assertRaises(AppArmorBug): +# obj.is_equal(testobj) -# with self.assertRaises(AppArmorBug): -# obj.is_covered(testobj) - -# def test_borked_obj_is_covered_2(self): -# obj = AbiRule.parse('abi send set=quit peer=/foo,') - -# testobj = AbiRule('send', 'quit', '/foo') -# testobj.abi = '' - -# with self.assertRaises(AppArmorBug): -# obj.is_covered(testobj) - -# def test_borked_obj_is_covered_3(self): -# obj = AbiRule.parse('abi send set=quit peer=/foo,') - -# testobj = AbiRule('send', 'quit', '/foo') -# testobj.peer = '' - -# with self.assertRaises(AppArmorBug): -# obj.is_covered(testobj) - -# def test_invalid_is_covered(self): -# obj = AbiRule.parse('abi send,') - -# testobj = BaseRule() # different type - -# with self.assertRaises(AppArmorBug): -# obj.is_covered(testobj) - -# def test_invalid_is_equal(self): -# obj = AbiRule.parse('abi send,') - -# testobj = BaseRule() # different type - -# with self.assertRaises(AppArmorBug): -# obj.is_equal(testobj) class AbiLogprofHeaderTest(AATest): tests = ( - ('abi ,', [_('Abi'), 'abi ,', ]), - ('abi "/foo/bar",', [_('Abi'), 'abi "/foo/bar",', ]), + ('abi ,', [_('Abi'), 'abi ,']), + ('abi "/foo/bar",', [_('Abi'), 'abi "/foo/bar",']), ) def _run_test(self, params, expected): obj = AbiRule.parse(params) self.assertEqual(obj.logprof_header(), expected) + ## --- tests for AbiRuleset --- # class AbiRulesTest(AATest): @@ -319,23 +331,26 @@ class AbiRulesTest(AATest): self.assertEqual(expected_clean, ruleset.get_clean()) self.assertEqual(expected_clean_unsorted, ruleset.get_clean_unsorted()) + class AbiGlobTestAATest(AATest): def setUp(self): self.maxDiff = None self.ruleset = AbiRuleset() -# def test_glob(self): -# with self.assertRaises(NotImplementedError): -# # get_glob_ext is not available for include rules -# self.ruleset.get_glob('include send set=int,') + # def test_glob(self): + # with self.assertRaises(NotImplementedError): + # # get_glob_ext is not available for include rules + # self.ruleset.get_glob('include send set=int,') def test_glob_ext(self): with self.assertRaises(NotImplementedError): # get_glob_ext is not available for include rules self.ruleset.get_glob_ext('include send set=int,') -#class AbiDeleteTestAATest(AATest): -# pass + +# class AbiDeleteTestAATest(AATest): +# pass + setup_all_loops(__name__) if __name__ == '__main__': diff --git a/utils/test/test-alias.py b/utils/test/test-alias.py index 75786d14a..89b731b20 100644 --- a/utils/test/test-alias.py +++ b/utils/test/test-alias.py @@ -27,6 +27,7 @@ exp = namedtuple('exp', ('comment', 'orig_path', 'target')) # --- tests for single AliasRule --- # + class AliasTest(AATest): def _compare_obj(self, obj, expected): # aliases don't support the allow, audit or deny keyword @@ -38,12 +39,13 @@ class AliasTest(AATest): self.assertEqual(expected.target, obj.target) self.assertEqual(expected.comment, obj.comment) + class AliasTestParse(AliasTest): tests = ( - # rawrule comment orig_path target - ('alias /foo -> /bar,', exp('', '/foo', '/bar' )), - (' alias /foo -> /bar , # comment', exp(' # comment', '/foo', '/bar' )), - ('alias "/foo 2" -> "/bar 2" ,', exp('', '/foo 2', '/bar 2' )), + # rawrule comment orig_path target + ('alias /foo -> /bar,', exp('', '/foo', '/bar')), + (' alias /foo -> /bar , # comment', exp(' # comment', '/foo', '/bar')), + ('alias "/foo 2" -> "/bar 2" ,', exp('', '/foo 2', '/bar 2')), ) def _run_test(self, rawrule, expected): @@ -52,14 +54,15 @@ class AliasTestParse(AliasTest): self.assertEqual(rawrule.strip(), obj.raw_rule) self._compare_obj(obj, expected) + class AliasTestParseInvalid(AliasTest): tests = ( - # rawrule matches regex exception - ('alias ,' , (False, AppArmorException)), - ('alias /foo ,' , (False, AppArmorException)), - ('alias /foo -> ,' , (True, AppArmorException)), - ('alias -> /bar ,' , (True, AppArmorException)), - ('/foo -> bar ,' , (False, AppArmorException)), + # rawrule matches regex exception + ('alias ,', (False, AppArmorException)), + ('alias /foo ,', (False, AppArmorException)), + ('alias /foo -> ,', (True, AppArmorException)), + ('alias -> /bar ,', (True, AppArmorException)), + ('/foo -> bar ,', (False, AppArmorException)), ) def _run_test(self, rawrule, expected): @@ -67,11 +70,12 @@ class AliasTestParseInvalid(AliasTest): with self.assertRaises(expected[1]): AliasRule.parse(rawrule) + class AliasFromInit(AliasTest): tests = ( - # AliasRule object comment orig_path target - (AliasRule('/foo', '/bar'), exp('', '/foo', '/bar' )), - (AliasRule('/foo', '/bar', comment='# cmt'), exp('# cmt', '/foo', '/bar' )), + # AliasRule object comment orig_path target + (AliasRule('/foo', '/bar'), exp('', '/foo', '/bar')), + (AliasRule('/foo', '/bar', comment='# cmt'), exp('# cmt', '/foo', '/bar')), ) def _run_test(self, obj, expected): @@ -80,14 +84,14 @@ class AliasFromInit(AliasTest): class InvalidAliasInit(AATest): tests = ( - # init params expected exception - ((None, '/bar' ), AppArmorBug), # orig_path not a str - (('', '/bar' ), AppArmorException), # empty orig_path - (('foo', '/bar' ), AppArmorException), # orig_path not starting with / + # init params expected exception + ((None, '/bar'), AppArmorBug), # orig_path not a str + (('', '/bar'), AppArmorException), # empty orig_path + (('foo', '/bar'), AppArmorException), # orig_path not starting with / - (('/foo', None ), AppArmorBug), # target not a str - (('/foo', '' ), AppArmorException), # empty target - (('/foo', 'bar' ), AppArmorException), # target not starting with / + (('/foo', None), AppArmorBug), # target not a str + (('/foo', ''), AppArmorException), # empty target + (('/foo', 'bar'), AppArmorException), # target not starting with / ) def _run_test(self, params, expected): @@ -132,11 +136,11 @@ class InvalidAliasTest(AATest): class WriteAliasTestAATest(AATest): tests = ( - # raw rule clean rule - (' alias /foo -> /bar, ', 'alias /foo -> /bar,'), - (' alias /foo -> /bar, # comment', 'alias /foo -> /bar,'), - (' alias "/foo" -> "/bar", ', 'alias /foo -> /bar,'), - (' alias "/foo 2" -> "/bar 2", ', 'alias "/foo 2" -> "/bar 2",'), + # raw rule clean rule + (' alias /foo -> /bar, ', 'alias /foo -> /bar,'), + (' alias /foo -> /bar, # comment', 'alias /foo -> /bar,'), + (' alias "/foo" -> "/bar", ', 'alias /foo -> /bar,'), + (' alias "/foo 2" -> "/bar 2", ', 'alias "/foo 2" -> "/bar 2",'), ) def _run_test(self, rawrule, expected): @@ -178,37 +182,39 @@ class AliasCoveredTest(AATest): self.assertEqual(obj.is_covered(check_obj), expected[2], 'Mismatch in is_covered, expected %s' % expected[2]) self.assertEqual(obj.is_covered(check_obj, True, True), expected[3], 'Mismatch in is_covered/exact, expected %s' % expected[3]) + class AliasCoveredTest_01(AliasCoveredTest): rule = 'alias /foo -> /bar,' tests = ( - # rule equal strict equal covered covered exact - (' alias /foo -> /bar,' , ( True , True , True , True )), - (' alias /foo -> /bar , ' , ( True , False , True , True )), - (' alias /foo -> /bar, # comment' , ( True , False , True , True )), - (' alias /foo -> /bar, # comment' , ( True , False , True , True )), - (' alias /foo -> /asdf,' , ( False , False , False , False )), - (' alias /whatever -> /bar,' , ( False , False , False , False )), - (' alias /whatever -> /asdf,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + (' alias /foo -> /bar,', (True, True, True, True)), + (' alias /foo -> /bar , ', (True, False, True, True)), + (' alias /foo -> /bar, # comment', (True, False, True, True)), + (' alias /foo -> /bar, # comment', (True, False, True, True)), + (' alias /foo -> /asdf,', (False, False, False, False)), + (' alias /whatever -> /bar,', (False, False, False, False)), + (' alias /whatever -> /asdf,', (False, False, False, False)), ) + class AliasCoveredTest_Invalid(AATest): -# def test_borked_obj_is_covered_1(self): -# obj = AliasRule.parse('alias /foo -> /bar,') - -# testobj = AliasRule('/foo', '/bar') - -# with self.assertRaises(AppArmorBug): -# obj.is_covered(testobj) - -# def test_borked_obj_is_covered_2(self): -# obj = AliasRule.parse('alias /foo -> /bar,') - -# testobj = AliasRule('/foo', '/bar') -# testobj.target = '' - -# with self.assertRaises(AppArmorBug): -# obj.is_covered(testobj) + # def test_borked_obj_is_covered_1(self): + # obj = AliasRule.parse('alias /foo -> /bar,') + # + # testobj = AliasRule('/foo', '/bar') + # + # with self.assertRaises(AppArmorBug): + # obj.is_covered(testobj) + # + # def test_borked_obj_is_covered_2(self): + # obj = AliasRule.parse('alias /foo -> /bar,') + # + # testobj = AliasRule('/foo', '/bar') + # testobj.target = '' + # + # with self.assertRaises(AppArmorBug): + # obj.is_covered(testobj) def test_invalid_is_covered_3(self): obj = AliasRule.parse('alias /foo -> /bar,') @@ -226,15 +232,17 @@ class AliasCoveredTest_Invalid(AATest): with self.assertRaises(AppArmorBug): obj.is_equal(testobj) + class AliasLogprofHeaderTest(AATest): tests = ( - ('alias /foo -> /bar,', [_('Alias'), '/foo -> /bar' ]), + ('alias /foo -> /bar,', [_('Alias'), '/foo -> /bar']), ) def _run_test(self, params, expected): obj = AliasRule.parse(params) self.assertEqual(obj.logprof_header(), expected) + # --- tests for AliasRuleset --- # class AliasRulesTest(AATest): @@ -286,6 +294,7 @@ class AliasRulesTest(AATest): self.assertEqual(expected_clean, ruleset.get_clean()) self.assertEqual(expected_clean_unsorted, ruleset.get_clean_unsorted()) + class AliasGlobTestAATest(AATest): def setUp(self): self.ruleset = AliasRuleset() @@ -299,9 +308,11 @@ class AliasGlobTestAATest(AATest): # get_glob_ext is not available for change_profile rules self.ruleset.get_glob_ext('@{foo} = /bar') + class AliasDeleteTestAATest(AATest): pass + setup_all_loops(__name__) if __name__ == '__main__': unittest.main(verbosity=1) diff --git a/utils/test/test-baserule.py b/utils/test/test-baserule.py index 1e7b55dab..58b8770cc 100644 --- a/utils/test/test-baserule.py +++ b/utils/test/test-baserule.py @@ -18,6 +18,7 @@ import apparmor.severity as severity import re + class TestBaserule(AATest): def test_abstract__parse(self): with self.assertRaises(NotImplementedError): diff --git a/utils/test/test-boolean.py b/utils/test/test-boolean.py index 6b095cfdb..03016cf5a 100644 --- a/utils/test/test-boolean.py +++ b/utils/test/test-boolean.py @@ -27,6 +27,7 @@ exp = namedtuple('exp', ('comment', 'varname', 'value')) # --- tests for single BooleanRule --- # + class BooleanTest(AATest): def _compare_obj(self, obj, expected): # boolean variables don't support the allow, audit or deny keyword @@ -38,15 +39,16 @@ class BooleanTest(AATest): self.assertEqual(expected.value, obj.value) self.assertEqual(expected.comment, obj.comment) + class BooleanTestParse(BooleanTest): tests = ( - # rawrule comment varname value - ('$foo=true', exp('', '$foo', 'true' )), - ('$foo = false', exp('', '$foo', 'false' )), - ('$foo=TrUe', exp('', '$foo', 'true' )), - ('$foo = FaLsE', exp('', '$foo', 'false' )), - (' $foo = true ', exp('', '$foo', 'true' )), - (' $foo = true # comment', exp(' # comment', '$foo', 'true' )), + # rawrule comment varname value + ('$foo=true', exp('', '$foo', 'true')), + ('$foo = false', exp('', '$foo', 'false')), + ('$foo=TrUe', exp('', '$foo', 'true')), + ('$foo = FaLsE', exp('', '$foo', 'false')), + (' $foo = true ', exp('', '$foo', 'true')), + (' $foo = true # comment', exp(' # comment', '$foo', 'true')), ) def _run_test(self, rawrule, expected): @@ -55,16 +57,17 @@ class BooleanTestParse(BooleanTest): self.assertEqual(rawrule.strip(), obj.raw_rule) self._compare_obj(obj, expected) + class BooleanTestParseInvalid(BooleanTest): tests = ( - # rawrule matches regex exception - ('$foo =', (False, AppArmorException)), - ('$ foo = # comment', (False, AppArmorException)), - ('${foo = ', (False, AppArmorException)), + # rawrule matches regex exception + ('$foo =', (False, AppArmorException)), + ('$ foo = # comment', (False, AppArmorException)), + ('${foo = ', (False, AppArmorException)), # XXX RE_PROFILE_BOOLEAN allows a trailing comma even if the parser disallows it - # ('$foo = true,', (True, AppArmorException)), # trailing comma - # ('$foo = false , ', (True, AppArmorException)), # trailing comma - # ('$foo = true, # comment', (True, AppArmorException)), # trailing comma + # ('$foo = true,', (True, AppArmorException)), # trailing comma + # ('$foo = false , ', (True, AppArmorException)), # trailing comma + # ('$foo = true, # comment', (True, AppArmorException)), # trailing comma ) def _run_test(self, rawrule, expected): @@ -72,14 +75,15 @@ class BooleanTestParseInvalid(BooleanTest): with self.assertRaises(expected[1]): BooleanRule.parse(rawrule) + class BooleanFromInit(BooleanTest): -# tests = ( -# # BooleanRule object comment varname value -# (BooleanRule('$foo', True, exp('', '$foo', True ))), -# (BooleanRule('$foo', False, exp('', '$foo', False ))), -# (BooleanRule('$foo', True, comment='# cmt'), exp('# cmt', '$foo', True ))), -# (BooleanRule('$foo', False, comment='# cmt'), exp('# cmt', '$foo', False ))), -# ) + # tests = ( + # # BooleanRule object comment varname value + # (BooleanRule('$foo', True), exp('', '$foo', True)), + # (BooleanRule('$foo', False), exp('', '$foo', False)), + # (BooleanRule('$foo', True, comment='# cmt'), exp('# cmt', '$foo', True)), + # (BooleanRule('$foo', False, comment='# cmt'), exp('# cmt', '$foo', False)), + # ) def _run_test(self, obj, expected): self._compare_obj(obj, expected) @@ -87,15 +91,15 @@ class BooleanFromInit(BooleanTest): class InvalidBooleanInit(AATest): tests = ( - # init params expected exception - ((None, True ), AppArmorBug), # varname not a str - (('', True ), AppArmorException), # empty varname - (('foo', True ), AppArmorException), # varname not starting with '$' - (('foo', True ), AppArmorException), # varname not starting with '$' + # init params expected exception + ((None, True), AppArmorBug), # varname not a str + (('', True), AppArmorException), # empty varname + (('foo', True), AppArmorException), # varname not starting with '$' + (('foo', True), AppArmorException), # varname not starting with '$' - (('$foo', None ), AppArmorBug), # value not a string - (('$foo', '' ), AppArmorException), # empty value - (('$foo', 'maybe' ), AppArmorException), # invalid value + (('$foo', None), AppArmorBug), # value not a string + (('$foo', ''), AppArmorException), # empty value + (('$foo', 'maybe'), AppArmorException), # invalid value ) def _run_test(self, params, expected): @@ -137,11 +141,11 @@ class InvalidBooleanTest(AATest): class WriteBooleanTestAATest(AATest): tests = ( - # raw rule clean rule - (' $foo = true ', '$foo = true'), - (' $foo = true # comment', '$foo = true'), - (' $foo = false ', '$foo = false'), - (' $foo = false # comment', '$foo = false'), + # raw rule clean rule + (' $foo = true ', '$foo = true'), + (' $foo = true # comment', '$foo = true'), + (' $foo = false ', '$foo = false'), + (' $foo = false # comment', '$foo = false'), ) def _run_test(self, rawrule, expected): @@ -183,31 +187,34 @@ class BooleanCoveredTest(AATest): self.assertEqual(obj.is_covered(check_obj), expected[2], 'Mismatch in is_covered, expected %s' % expected[2]) self.assertEqual(obj.is_covered(check_obj, True, True), expected[3], 'Mismatch in is_covered/exact, expected %s' % expected[3]) + class BooleanCoveredTest_01(BooleanCoveredTest): rule = '$foo = true' tests = ( - # rule equal strict equal covered covered exact - (' $foo = true' , ( True , True , True , True )), - (' $foo = TRUE' , ( True , False , True , True )), # upper vs. lower case - (' $foo = true # comment' , ( True , False , True , True )), - (' $foo = false' , ( False , False , False , False )), - (' $foo = false # cmt' , ( False , False , False , False )), - (' $bar = true' , ( False , False , False , False )), # different variable name + # rule equal strict equal covered covered exact + (' $foo = true', (True, True, True, True)), + (' $foo = TRUE', (True, False, True, True)), # upper vs. lower case + (' $foo = true # comment', (True, False, True, True)), + (' $foo = false', (False, False, False, False)), + (' $foo = false # cmt', (False, False, False, False)), + (' $bar = true', (False, False, False, False)), # different variable name ) + class BooleanCoveredTest_02(BooleanCoveredTest): rule = '$foo = false' tests = ( - # rule equal strict equal covered covered exact - (' $foo = false' , ( True , True , True , True )), - (' $foo = false # comment' , ( True , False , True , True )), - (' $foo = true' , ( False , False , False , False )), - (' $foo = true # cmt' , ( False , False , False , False )), - (' $bar = false' , ( False , False , False , False )), # different variable name + # rule equal strict equal covered covered exact + (' $foo = false', (True, True, True, True)), + (' $foo = false # comment', (True, False, True, True)), + (' $foo = true', (False, False, False, False)), + (' $foo = true # cmt', (False, False, False, False)), + (' $bar = false', (False, False, False, False)), # different variable name ) + class BooleanCoveredTest_Invalid(AATest): def test_borked_obj_is_covered_2(self): obj = BooleanRule.parse('$foo = true') @@ -234,15 +241,17 @@ class BooleanCoveredTest_Invalid(AATest): with self.assertRaises(AppArmorBug): obj.is_equal(testobj) + class BooleanLogprofHeaderTest(AATest): tests = ( - ('$foo = true', [_('Boolean Variable'), '$foo = true' ]), + ('$foo = true', [_('Boolean Variable'), '$foo = true']), ) def _run_test(self, params, expected): obj = BooleanRule.parse(params) self.assertEqual(obj.logprof_header(), expected) + # --- tests for BooleanRuleset --- # class BooleanRulesTest(AATest): @@ -293,6 +302,7 @@ class BooleanRulesTest(AATest): with self.assertRaises(AppArmorException): ruleset.add(BooleanRule.parse('$foo = false')) # attempt to redefine @{foo} + class BooleanGlobTestAATest(AATest): def setUp(self): self.ruleset = BooleanRuleset() @@ -306,9 +316,11 @@ class BooleanGlobTestAATest(AATest): # get_glob_ext is not available for boolean rules self.ruleset.get_glob_ext('$foo = true') + class BooleanDeleteTestAATest(AATest): pass + setup_all_loops(__name__) if __name__ == '__main__': unittest.main(verbosity=1) diff --git a/utils/test/test-capability.py b/utils/test/test-capability.py index e3ab64286..f7d885865 100644 --- a/utils/test/test-capability.py +++ b/utils/test/test-capability.py @@ -24,6 +24,7 @@ from apparmor.logparser import ReadLog from apparmor.translations import init_translation _ = init_translation() + # --- tests for single CapabilityRule --- # class CapabilityTest(AATest): @@ -46,54 +47,54 @@ class CapabilityTest(AATest): def test_cap_allow_all(self): self._compare_obj_with_rawrule("capability,", { - 'allow_keyword': False, - 'deny': False, - 'audit': False, - 'capability': set(), - 'all_caps': True, - 'comment': "", + 'allow_keyword': False, + 'deny': False, + 'audit': False, + 'capability': set(), + 'all_caps': True, + 'comment': "", }) def test_cap_allow_sys_admin(self): self._compare_obj_with_rawrule("capability sys_admin,", { - 'allow_keyword': False, - 'deny': False, - 'audit': False, - 'capability': {'sys_admin'}, - 'all_caps': False, - 'comment': "", + 'allow_keyword': False, + 'deny': False, + 'audit': False, + 'capability': {'sys_admin'}, + 'all_caps': False, + 'comment': "", }) def test_cap_deny_sys_admin(self): self._compare_obj_with_rawrule(" deny capability sys_admin, # some comment", { - 'allow_keyword': False, - 'deny': True, - 'audit': False, - 'capability': {'sys_admin'}, - 'all_caps': False, - 'comment': " # some comment", + 'allow_keyword': False, + 'deny': True, + 'audit': False, + 'capability': {'sys_admin'}, + 'all_caps': False, + 'comment': " # some comment", }) def test_cap_multi(self): self._compare_obj_with_rawrule("capability sys_admin dac_override,", { - 'allow_keyword': False, - 'deny': False, - 'audit': False, - 'capability': {'sys_admin', 'dac_override'}, - 'all_caps': False, - 'comment': "", + 'allow_keyword': False, + 'deny': False, + 'audit': False, + 'capability': {'sys_admin', 'dac_override'}, + 'all_caps': False, + 'comment': "", }) # Template for test_cap_* functions - # def test_cap_(self): - # self._compare_obj_with_rawrule("capability,", { - # 'allow_keyword': False, - # 'deny': False, - # 'audit': False, - # 'capability': set(), # (or {'foo'} if not empty) - # 'all_caps': False, - # 'comment': "", - # }) + # def test_cap_(self): + # self._compare_obj_with_rawrule("capability,", { + # 'allow_keyword': False, + # 'deny': False, + # 'audit': False, + # 'capability': set(), # (or {'foo'} if not empty) + # 'all_caps': False, + # 'comment': "", + # }) def test_cap_from_log(self): parser = ReadLog('', '', '') @@ -127,92 +128,92 @@ class CapabilityTest(AATest): obj = CapabilityRule(parsed_event['name'], log_event=parsed_event) self._compare_obj(obj, { - 'allow_keyword': False, - 'deny': False, - 'audit': False, - 'capability': {'net_raw'}, - 'all_caps': False, - 'comment': "", + 'allow_keyword': False, + 'deny': False, + 'audit': False, + 'capability': {'net_raw'}, + 'all_caps': False, + 'comment': "", }) self.assertEqual(obj.get_raw(1), ' capability net_raw,') -# def test_cap_from_invalid_log(self): -# parser = ReadLog('', '', '') -# # invalid log entry, name= should contain the capability name -# event = 'type=AVC msg=audit(1415403814.628:662): apparmor="ALLOWED" operation="capable" profile="/bin/ping" pid=15454 comm="ping" capability=13 capname=""' -# -# parsed_event = parser.parse_event(event) -# -# obj = CapabilityRule() -# -# with self.assertRaises(AppArmorBug): -# obj.set_log(parsed_event) -# -# with self.assertRaises(AppArmorBug): -# obj.get_raw(1) -# -# def test_cap_from_non_cap_log(self): -# parser = ReadLog('', '', '') -# # log entry for different rule type -# event = 'type=AVC msg=audit(1415403814.973:667): apparmor="ALLOWED" operation="setsockopt" profile="/home/sys-tmp/ping" pid=15454 comm="ping" lport=1 family="inet" sock_type="raw" protocol=1' -# -# parsed_event = parser.parse_event(event) -# -# obj = CapabilityRule() -# -# with self.assertRaises(AppArmorBug): -# obj.set_log(parsed_event) -# -# with self.assertRaises(AppArmorBug): -# obj.get_raw(1) + # def test_cap_from_invalid_log(self): + # parser = ReadLog('', '', '') + # # invalid log entry, name= should contain the capability name + # event = 'type=AVC msg=audit(1415403814.628:662): apparmor="ALLOWED" operation="capable" profile="/bin/ping" pid=15454 comm="ping" capability=13 capname=""' + # + # parsed_event = parser.parse_event(event) + # + # obj = CapabilityRule() + # + # with self.assertRaises(AppArmorBug): + # obj.set_log(parsed_event) + # + # with self.assertRaises(AppArmorBug): + # obj.get_raw(1) + # + # def test_cap_from_non_cap_log(self): + # parser = ReadLog('', '', '') + # # log entry for different rule type + # event = 'type=AVC msg=audit(1415403814.973:667): apparmor="ALLOWED" operation="setsockopt" profile="/home/sys-tmp/ping" pid=15454 comm="ping" lport=1 family="inet" sock_type="raw" protocol=1' + # + # parsed_event = parser.parse_event(event) + # + # obj = CapabilityRule() + # + # with self.assertRaises(AppArmorBug): + # obj.set_log(parsed_event) + # + # with self.assertRaises(AppArmorBug): + # obj.get_raw(1) def test_cap_from_init_01(self): obj = CapabilityRule('chown') self._compare_obj(obj, { - 'allow_keyword': False, - 'deny': False, - 'audit': False, - 'capability': {'chown'}, - 'all_caps': False, - 'comment': "", + 'allow_keyword': False, + 'deny': False, + 'audit': False, + 'capability': {'chown'}, + 'all_caps': False, + 'comment': "", }) def test_cap_from_init_02(self): obj = CapabilityRule(['chown']) self._compare_obj(obj, { - 'allow_keyword': False, - 'deny': False, - 'audit': False, - 'capability': {'chown'}, - 'all_caps': False, - 'comment': "", + 'allow_keyword': False, + 'deny': False, + 'audit': False, + 'capability': {'chown'}, + 'all_caps': False, + 'comment': "", }) def test_cap_from_init_03(self): obj = CapabilityRule('chown', audit=True, deny=True) self._compare_obj(obj, { - 'allow_keyword': False, - 'deny': True, - 'audit': True, - 'capability': {'chown'}, - 'all_caps': False, - 'comment': "", + 'allow_keyword': False, + 'deny': True, + 'audit': True, + 'capability': {'chown'}, + 'all_caps': False, + 'comment': "", }) def test_cap_from_init_04(self): obj = CapabilityRule(['chown', 'fsetid'], deny=True) self._compare_obj(obj, { - 'allow_keyword': False, - 'deny': True, - 'audit': False, - 'capability': {'chown', 'fsetid'}, - 'all_caps': False, - 'comment': "", + 'allow_keyword': False, + 'deny': True, + 'audit': False, + 'capability': {'chown', 'fsetid'}, + 'all_caps': False, + 'comment': "", }) @@ -290,6 +291,7 @@ class WriteCapabilityTest(AATest): self.assertEqual(expected, obj.get_clean(2), 'unexpected clean rule') self.assertEqual(expected, obj.get_raw(2), 'unexpected raw rule') + class CapabilityCoveredTest(AATest): def _is_covered(self, obj, rule_to_test): self.assertTrue(CapabilityRule.match(rule_to_test)) @@ -425,6 +427,7 @@ class CapabilityCoveredTest(AATest): self.assertFalse(self._is_covered(obj2, 'capability sys_admin,')) self.assertTrue(self._is_covered(obj2, 'capability ptrace,')) + class CapabiliySeverityTest(AATest): tests = ( ('fsetid', 9), @@ -433,27 +436,30 @@ class CapabiliySeverityTest(AATest): (CapabilityRule.ALL, 10), ('foo', 'unknown'), ) + def _run_test(self, params, expected): sev_db = severity.Severity('../severity.db', 'unknown') obj = CapabilityRule(params) rank = obj.severity(sev_db) self.assertEqual(rank, expected) + class CapabilityLogprofHeaderTest(AATest): tests = ( - ('capability,', [ _('Capability'), _('ALL'), ]), - ('capability chown,', [ _('Capability'), 'chown', ]), - ('capability chown fsetid,', [ _('Capability'), 'chown fsetid', ]), - ('audit capability,', [_('Qualifier'), 'audit', _('Capability'), _('ALL'), ]), - ('deny capability chown,', [_('Qualifier'), 'deny', _('Capability'), 'chown', ]), - ('allow capability chown fsetid,', [_('Qualifier'), 'allow', _('Capability'), 'chown fsetid', ]), - ('audit deny capability,', [_('Qualifier'), 'audit deny', _('Capability'), _('ALL'), ]), + ('capability,', [ _('Capability'), _('ALL')]), + ('capability chown,', [ _('Capability'), 'chown']), + ('capability chown fsetid,', [ _('Capability'), 'chown fsetid']), + ('audit capability,', [_('Qualifier'), 'audit', _('Capability'), _('ALL')]), + ('deny capability chown,', [_('Qualifier'), 'deny', _('Capability'), 'chown']), + ('allow capability chown fsetid,', [_('Qualifier'), 'allow', _('Capability'), 'chown fsetid']), + ('audit deny capability,', [_('Qualifier'), 'audit deny', _('Capability'), _('ALL')]), ) def _run_test(self, params, expected): obj = CapabilityRule.parse(params) self.assertEqual(obj.logprof_header(), expected) + # --- tests for CapabilityRuleset --- # class CapabilityRulesTest(AATest): @@ -630,6 +636,7 @@ class CapabilityRulesCoveredTest(AATest): # parser = ReadLog('', '', '') # self.assertEqual(True, self.ruleset.is_log_covered(parser.parse_event(event_base%'chgrp'), False)) # ignores allow/deny + class CapabilityGlobTest(AATest): def AASetup(self): self.ruleset = CapabilityRuleset() @@ -641,6 +648,7 @@ class CapabilityGlobTest(AATest): with self.assertRaises(NotImplementedError): self.ruleset.get_glob_ext('capability net_raw,') + class CapabilityDeleteTest(AATest): def AASetup(self): self.ruleset = CapabilityRuleset() @@ -812,9 +820,7 @@ class CapabilityDeleteTest(AATest): def test_delete_duplicates_4(self): inc = CapabilityRuleset() - rules = [ - 'capability,', - ] + rules = ['capability,'] for rule in rules: inc.add(CapabilityRule.parse(rule)) @@ -873,7 +879,6 @@ class CapabilityDeleteTest(AATest): self.assertEqual(expected_raw, self.ruleset.get_raw(1)) self.assertEqual(expected_clean, self.ruleset.get_clean(1)) - def _check_test_delete_duplicates_in_profile(self, rules, expected_raw, expected_clean, expected_deleted): obj = CapabilityRuleset() @@ -886,7 +891,6 @@ class CapabilityDeleteTest(AATest): self.assertEqual(expected_clean, obj.get_clean(1)) self.assertEqual(deleted, expected_deleted) - def test_delete_duplicates_in_profile_01(self): rules = [ 'audit capability chown,', diff --git a/utils/test/test-change_profile.py b/utils/test/test-change_profile.py index 646eea525..b0789e848 100644 --- a/utils/test/test-change_profile.py +++ b/utils/test/test-change_profile.py @@ -24,11 +24,13 @@ from apparmor.logparser import ReadLog from apparmor.translations import init_translation _ = init_translation() -exp = namedtuple('exp', ('audit', 'allow_keyword', 'deny', 'comment', - 'execmode', 'execcond', 'all_execconds', 'targetprofile', 'all_targetprofiles')) +exp = namedtuple( + 'exp', ('audit', 'allow_keyword', 'deny', 'comment', + 'execmode', 'execcond', 'all_execconds', 'targetprofile', 'all_targetprofiles')) # --- tests for single ChangeProfileRule --- # + class ChangeProfileTest(AATest): def _compare_obj(self, obj, expected): self.assertEqual(expected.allow_keyword, obj.allow_keyword) @@ -41,35 +43,36 @@ class ChangeProfileTest(AATest): self.assertEqual(expected.deny, obj.deny) self.assertEqual(expected.comment, obj.comment) + class ChangeProfileTestParse(ChangeProfileTest): tests = ( - # rawrule audit allow deny comment execmode execcond all? targetprof all? - ('change_profile,' , exp(False, False, False, '' , None , None , True , None , True )), - ('change_profile /foo,' , exp(False, False, False, '' , None , '/foo', False, None , True )), - ('change_profile safe /foo,' , exp(False, False, False, '' , 'safe' , '/foo', False, None , True )), - ('change_profile unsafe /foo,' , exp(False, False, False, '' , 'unsafe' , '/foo', False, None , True )), - ('change_profile /foo -> /bar,' , exp(False, False, False, '' , None , '/foo', False, '/bar' , False)), - ('change_profile safe /foo -> /bar,' , exp(False, False, False, '' , 'safe' , '/foo', False, '/bar' , False)), - ('change_profile unsafe /foo -> /bar,' , exp(False, False, False, '' , 'unsafe' , '/foo', False, '/bar' , False)), - ('deny change_profile /foo -> /bar, # comment' , exp(False, False, True , ' # comment' , None , '/foo', False, '/bar' , False)), - ('audit allow change_profile safe /foo,' , exp(True , True , False, '' , 'safe' , '/foo', False, None , True )), - ('change_profile -> /bar,' , exp(False, False, False, '' , None , None , True , '/bar' , False)), - ('audit allow change_profile -> /bar,' , exp(True , True , False, '' , None , None , True , '/bar' , False)), + # rawrule audit allow deny comment execmode execcond all? targetprof all? + ('change_profile,', exp(False, False, False, '', None, None, True, None, True)), + ('change_profile /foo,', exp(False, False, False, '', None, '/foo', False, None, True)), + ('change_profile safe /foo,', exp(False, False, False, '', 'safe', '/foo', False, None, True)), + ('change_profile unsafe /foo,', exp(False, False, False, '', 'unsafe', '/foo', False, None, True)), + ('change_profile /foo -> /bar,', exp(False, False, False, '', None, '/foo', False, '/bar', False)), + ('change_profile safe /foo -> /bar,', exp(False, False, False, '', 'safe', '/foo', False, '/bar', False)), + ('change_profile unsafe /foo -> /bar,', exp(False, False, False, '', 'unsafe', '/foo', False, '/bar', False)), + ('deny change_profile /foo -> /bar, # comment', exp(False, False, True, ' # comment', None, '/foo', False, '/bar', False)), + ('audit allow change_profile safe /foo,', exp(True, True, False, '', 'safe', '/foo', False, None, True)), + ('change_profile -> /bar,', exp(False, False, False, '', None, None, True, '/bar', False)), + ('audit allow change_profile -> /bar,', exp(True, True, False, '', None, None, True, '/bar', False)), # quoted versions - ('change_profile "/foo",' , exp(False, False, False, '' , None , '/foo', False, None , True )), - ('change_profile "/foo" -> "/bar",' , exp(False, False, False, '' , None , '/foo', False, '/bar' , False)), - ('deny change_profile "/foo" -> "/bar", # cmt' , exp(False, False, True, ' # cmt' , None , '/foo', False, '/bar' , False)), - ('audit allow change_profile "/foo",' , exp(True , True , False, '' , None , '/foo', False, None , True )), - ('change_profile -> "/bar",' , exp(False, False, False, '' , None , None , True , '/bar' , False)), - ('audit allow change_profile -> "/bar",' , exp(True , True , False, '' , None , None , True , '/bar' , False)), + ('change_profile "/foo",', exp(False, False, False, '', None, '/foo', False, None, True)), + ('change_profile "/foo" -> "/bar",', exp(False, False, False, '', None, '/foo', False, '/bar', False)), + ('deny change_profile "/foo" -> "/bar", # cmt', exp(False, False, True, ' # cmt', None, '/foo', False, '/bar', False)), + ('audit allow change_profile "/foo",', exp(True, True, False, '', None, '/foo', False, None, True)), + ('change_profile -> "/bar",', exp(False, False, False, '', None, None, True, '/bar', False)), + ('audit allow change_profile -> "/bar",', exp(True, True, False, '', None, None, True, '/bar', False)), # with globbing and/or named profiles - ('change_profile,' , exp(False, False, False, '' , None , None , True , None , True )), - ('change_profile /*,' , exp(False, False, False, '' , None , '/*' , False, None , True )), - ('change_profile /* -> bar,' , exp(False, False, False, '' , None , '/*' , False, 'bar' , False)), - ('deny change_profile /** -> bar, # comment' , exp(False, False, True , ' # comment' , None , '/**' , False, 'bar' , False)), - ('audit allow change_profile /**,' , exp(True , True , False, '' , None , '/**' , False, None , True )), - ('change_profile -> "ba r",' , exp(False, False, False, '' , None , None , True , 'ba r' , False)), - ('audit allow change_profile -> "ba r",' , exp(True , True , False, '' , None , None , True , 'ba r' , False)), + ('change_profile,', exp(False, False, False, '', None, None, True, None, True)), + ('change_profile /*,', exp(False, False, False, '', None, '/*', False, None, True)), + ('change_profile /* -> bar,', exp(False, False, False, '', None, '/*', False, 'bar', False)), + ('deny change_profile /** -> bar, # comment', exp(False, False, True, ' # comment', None, '/**', False, 'bar', False)), + ('audit allow change_profile /**,', exp(True, True, False, '', None, '/**', False, None, True)), + ('change_profile -> "ba r",', exp(False, False, False, '', None, None, True, 'ba r', False)), + ('audit allow change_profile -> "ba r",', exp(True, True, False, '', None, None, True, 'ba r', False)), ) def _run_test(self, rawrule, expected): @@ -78,12 +81,13 @@ class ChangeProfileTestParse(ChangeProfileTest): self.assertEqual(rawrule.strip(), obj.raw_rule) self._compare_obj(obj, expected) + class ChangeProfileTestParseInvalid(ChangeProfileTest): tests = ( - ('change_profile -> ,' , AppArmorException), - ('change_profile foo -> ,' , AppArmorException), - ('change_profile notsafe,' , AppArmorException), - ('change_profile safety -> /bar,' , AppArmorException), + ('change_profile -> ,', AppArmorException), + ('change_profile foo -> ,', AppArmorException), + ('change_profile notsafe,', AppArmorException), + ('change_profile safety -> /bar,', AppArmorException), ) def _run_test(self, rawrule, expected): @@ -91,6 +95,7 @@ class ChangeProfileTestParseInvalid(ChangeProfileTest): with self.assertRaises(expected): ChangeProfileRule.parse(rawrule) + class ChangeProfileTestParseFromLog(ChangeProfileTest): def test_change_profile_from_log(self): parser = ReadLog('', '', '') @@ -118,7 +123,7 @@ class ChangeProfileTestParseFromLog(ChangeProfileTest): 'pid': 3459, 'task': 0, 'attr': None, - 'name2': '/foo/rename', # target + 'name2': '/foo/rename', # target 'name': None, 'family': None, 'protocol': None, @@ -127,8 +132,8 @@ class ChangeProfileTestParseFromLog(ChangeProfileTest): obj = ChangeProfileRule(None, ChangeProfileRule.ALL, parsed_event['name2'], log_event=parsed_event) - # audit allow deny comment execmode execcond all? targetprof all? - expected = exp(False, False, False, '' , None, None, True, '/foo/rename', False) + # audit allow deny comment execmode execcond all? targetprof all? + expected = exp(False, False, False, '', None, None, True, '/foo/rename', False) self._compare_obj(obj, expected) @@ -137,14 +142,14 @@ class ChangeProfileTestParseFromLog(ChangeProfileTest): class ChangeProfileFromInit(ChangeProfileTest): tests = ( - # ChangeProfileRule object audit allow deny comment execmode execcond all? targetprof all? - (ChangeProfileRule(None , '/foo', '/bar', deny=True) , exp(False, False, True , '' , None , '/foo', False, '/bar' , False)), - (ChangeProfileRule(None , '/foo', '/bar') , exp(False, False, False, '' , None , '/foo', False, '/bar' , False)), - (ChangeProfileRule('safe' , '/foo', '/bar') , exp(False, False, False, '' , 'safe' , '/foo', False, '/bar' , False)), - (ChangeProfileRule('unsafe', '/foo', '/bar') , exp(False, False, False, '' , 'unsafe', '/foo', False, '/bar' , False)), - (ChangeProfileRule(None , '/foo', ChangeProfileRule.ALL) , exp(False, False, False, '' , None , '/foo', False, None , True )), - (ChangeProfileRule(None , ChangeProfileRule.ALL, '/bar') , exp(False, False, False, '' , None , None , True , '/bar' , False)), - (ChangeProfileRule(None , ChangeProfileRule.ALL, ChangeProfileRule.ALL), exp(False, False, False, '' , None, None , True , None , True )), + # ChangeProfileRule object audit allow deny comment execmode execcond all? targetprof all? + (ChangeProfileRule(None, '/foo', '/bar', deny=True), exp(False, False, True, '', None, '/foo', False, '/bar', False)), + (ChangeProfileRule(None, '/foo', '/bar'), exp(False, False, False, '', None, '/foo', False, '/bar', False)), + (ChangeProfileRule('safe', '/foo', '/bar'), exp(False, False, False, '', 'safe', '/foo', False, '/bar', False)), + (ChangeProfileRule('unsafe', '/foo', '/bar'), exp(False, False, False, '', 'unsafe', '/foo', False, '/bar', False)), + (ChangeProfileRule(None, '/foo', ChangeProfileRule.ALL), exp(False, False, False, '', None, '/foo', False, None, True)), + (ChangeProfileRule(None, ChangeProfileRule.ALL, '/bar'), exp(False, False, False, '', None, None, True, '/bar', False)), + (ChangeProfileRule(None, ChangeProfileRule.ALL, ChangeProfileRule.ALL), exp(False, False, False, '', None, None, True, None, True)), ) def _run_test(self, obj, expected): @@ -153,17 +158,17 @@ class ChangeProfileFromInit(ChangeProfileTest): class InvalidChangeProfileInit(AATest): tests = ( - # init params expected exception - ((None , '/foo', '' ) , AppArmorBug), # empty targetprofile - ((None , '' , '/bar' ) , AppArmorBug), # empty execcond - ((None , ' ', '/bar' ) , AppArmorBug), # whitespace execcond - ((None , '/foo', ' ' ) , AppArmorBug), # whitespace targetprofile - ((None , 'xyxy', '/bar' ) , AppArmorException), # invalid execcond - ((None , dict(), '/bar' ) , AppArmorBug), # wrong type for execcond - ((None , None , '/bar' ) , AppArmorBug), # wrong type for execcond - ((None , '/foo', dict() ) , AppArmorBug), # wrong type for targetprofile - ((None , '/foo', None ) , AppArmorBug), # wrong type for targetprofile - (('maybe' , '/foo', '/bar' ) , AppArmorBug), # invalid keyword for execmode + # init params expected exception + ((None, '/foo', ''), AppArmorBug), # empty targetprofile + ((None, '', '/bar'), AppArmorBug), # empty execcond + ((None, ' ', '/bar'), AppArmorBug), # whitespace execcond + ((None, '/foo', ' '), AppArmorBug), # whitespace targetprofile + ((None, 'xyxy', '/bar'), AppArmorException), # invalid execcond + ((None, dict(), '/bar'), AppArmorBug), # wrong type for execcond + ((None, None, '/bar'), AppArmorBug), # wrong type for execcond + ((None, '/foo', dict()), AppArmorBug), # wrong type for targetprofile + ((None, '/foo', None), AppArmorBug), # wrong type for targetprofile + (('maybe', '/foo', '/bar'), AppArmorBug), # invalid keyword for execmode ) def _run_test(self, params, expected): @@ -211,14 +216,14 @@ class InvalidChangeProfileTest(AATest): class WriteChangeProfileTestAATest(AATest): tests = ( - # raw rule clean rule - (' change_profile , # foo ' , 'change_profile, # foo'), - (' audit change_profile /foo,' , 'audit change_profile /foo,'), - (' deny change_profile /foo -> bar,# foo bar' , 'deny change_profile /foo -> bar, # foo bar'), - (' deny change_profile /foo ,# foo bar' , 'deny change_profile /foo, # foo bar'), - (' allow change_profile -> /bar ,# foo bar' , 'allow change_profile -> /bar, # foo bar'), - (' allow change_profile unsafe /** -> /bar ,# foo bar' , 'allow change_profile unsafe /** -> /bar, # foo bar'), - (' allow change_profile "/fo o" -> "/b ar",' , 'allow change_profile "/fo o" -> "/b ar",'), + # raw rule clean rule + (' change_profile , # foo ', 'change_profile, # foo'), + (' audit change_profile /foo,', 'audit change_profile /foo,'), + (' deny change_profile /foo -> bar,# foo bar', 'deny change_profile /foo -> bar, # foo bar'), + (' deny change_profile /foo ,# foo bar', 'deny change_profile /foo, # foo bar'), + (' allow change_profile -> /bar ,# foo bar', 'allow change_profile -> /bar, # foo bar'), + (' allow change_profile unsafe /** -> /bar ,# foo bar', 'allow change_profile unsafe /** -> /bar, # foo bar'), + (' allow change_profile "/fo o" -> "/b ar",', 'allow change_profile "/fo o" -> "/b ar",'), ) def _run_test(self, rawrule, expected): @@ -252,41 +257,43 @@ class ChangeProfileCoveredTest(AATest): self.assertEqual(obj.is_covered(check_obj), expected[2], 'Mismatch in is_covered, expected %s' % expected[2]) self.assertEqual(obj.is_covered(check_obj, True, True), expected[3], 'Mismatch in is_covered/exact, expected %s' % expected[3]) + class ChangeProfileCoveredTest_01(ChangeProfileCoveredTest): rule = 'change_profile /foo,' tests = ( - # rule equal strict equal covered covered exact - (' change_profile,' , ( False , False , False , False )), - (' change_profile /foo,' , ( True , True , True , True )), - (' change_profile safe /foo,' , ( True , False , True , True )), - (' change_profile unsafe /foo,' , ( False , False , False , False )), - (' change_profile /foo, # comment', ( True , False , True , True )), - (' allow change_profile /foo,' , ( True , False , True , True )), - (' change_profile /foo,' , ( True , False , True , True )), - (' change_profile /foo -> /bar,' , ( False , False , True , True )), - (' change_profile /foo -> bar,' , ( False , False , True , True )), - ('audit change_profile /foo,' , ( False , False , False , False )), - ('audit change_profile,' , ( False , False , False , False )), - (' change_profile /asdf,' , ( False , False , False , False )), - (' change_profile -> /bar,' , ( False , False , False , False )), - ('audit deny change_profile /foo,' , ( False , False , False , False )), - (' deny change_profile /foo,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + (' change_profile,', (False, False, False, False)), + (' change_profile /foo,', (True, True, True, True)), + (' change_profile safe /foo,', (True, False, True, True)), + (' change_profile unsafe /foo,', (False, False, False, False)), + (' change_profile /foo, # comment', (True, False, True, True)), + (' allow change_profile /foo,', (True, False, True, True)), + (' change_profile /foo,', (True, False, True, True)), + (' change_profile /foo -> /bar,', (False, False, True, True)), + (' change_profile /foo -> bar,', (False, False, True, True)), + ('audit change_profile /foo,', (False, False, False, False)), + ('audit change_profile,', (False, False, False, False)), + (' change_profile /asdf,', (False, False, False, False)), + (' change_profile -> /bar,', (False, False, False, False)), + ('audit deny change_profile /foo,', (False, False, False, False)), + (' deny change_profile /foo,', (False, False, False, False)), ) + class ChangeProfileCoveredTest_02(ChangeProfileCoveredTest): rule = 'audit change_profile /foo,' tests = ( - # rule equal strict equal covered covered exact - ( 'change_profile /foo,' , ( False , False , True , False )), - ('audit change_profile /foo,' , ( True , True , True , True )), - ( 'change_profile /foo -> /bar,' , ( False , False , True , False )), - ( 'change_profile safe /foo -> /bar,' , ( False , False , True , False )), - ('audit change_profile /foo -> /bar,' , ( False , False , True , True )), # XXX is "covered exact" correct here? - ( 'change_profile,' , ( False , False , False , False )), - ('audit change_profile,' , ( False , False , False , False )), - (' change_profile -> /bar,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'change_profile /foo,', (False, False, True, False)), + ('audit change_profile /foo,', (True, True, True, True)), + ( 'change_profile /foo -> /bar,', (False, False, True, False)), + ( 'change_profile safe /foo -> /bar,', (False, False, True, False)), + ('audit change_profile /foo -> /bar,', (False, False, True, True)), # XXX is "covered exact" correct here? + ( 'change_profile,', (False, False, False, False)), + ('audit change_profile,', (False, False, False, False)), + (' change_profile -> /bar,', (False, False, False, False)), ) @@ -294,57 +301,61 @@ class ChangeProfileCoveredTest_03(ChangeProfileCoveredTest): rule = 'change_profile /foo -> /bar,' tests = ( - # rule equal strict equal covered covered exact - ( 'change_profile /foo -> /bar,' , ( True , True , True , True )), - ('allow change_profile /foo -> /bar,' , ( True , False , True , True )), - ( 'change_profile /foo,' , ( False , False , False , False )), - ( 'change_profile,' , ( False , False , False , False )), - ( 'change_profile /foo -> /xyz,' , ( False , False , False , False )), - ('audit change_profile,' , ( False , False , False , False )), - ('audit change_profile /foo -> /bar,' , ( False , False , False , False )), - ( 'change_profile -> /bar,' , ( False , False , False , False )), - ( 'change_profile,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'change_profile /foo -> /bar,', (True, True, True, True)), + ('allow change_profile /foo -> /bar,', (True, False, True, True)), + ( 'change_profile /foo,', (False, False, False, False)), + ( 'change_profile,', (False, False, False, False)), + ( 'change_profile /foo -> /xyz,', (False, False, False, False)), + ('audit change_profile,', (False, False, False, False)), + ('audit change_profile /foo -> /bar,', (False, False, False, False)), + ( 'change_profile -> /bar,', (False, False, False, False)), + ( 'change_profile,', (False, False, False, False)), ) + class ChangeProfileCoveredTest_04(ChangeProfileCoveredTest): rule = 'change_profile,' tests = ( - # rule equal strict equal covered covered exact - ( 'change_profile,' , ( True , True , True , True )), - ('allow change_profile,' , ( True , False , True , True )), - ( 'change_profile /foo,' , ( False , False , True , True )), - ( 'change_profile /xyz -> bar,' , ( False , False , True , True )), - ( 'change_profile -> /bar,' , ( False , False , True , True )), - ( 'change_profile /foo -> /bar,' , ( False , False , True , True )), - ('audit change_profile,' , ( False , False , False , False )), - ('deny change_profile,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'change_profile,', (True, True, True, True)), + ('allow change_profile,', (True, False, True, True)), + ( 'change_profile /foo,', (False, False, True, True)), + ( 'change_profile /xyz -> bar,', (False, False, True, True)), + ( 'change_profile -> /bar,', (False, False, True, True)), + ( 'change_profile /foo -> /bar,', (False, False, True, True)), + ('audit change_profile,', (False, False, False, False)), + ('deny change_profile,', (False, False, False, False)), ) + class ChangeProfileCoveredTest_05(ChangeProfileCoveredTest): rule = 'deny change_profile /foo,' tests = ( - # rule equal strict equal covered covered exact - ( 'deny change_profile /foo,' , ( True , True , True , True )), - ('audit deny change_profile /foo,' , ( False , False , False , False )), - ( 'change_profile /foo,' , ( False , False , False , False )), # XXX should covered be true here? - ( 'deny change_profile /bar,' , ( False , False , False , False )), - ( 'deny change_profile,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'deny change_profile /foo,', (True, True, True, True)), + ('audit deny change_profile /foo,', (False, False, False, False)), + ( 'change_profile /foo,', (False, False, False, False)), # XXX should covered be true here? + ( 'deny change_profile /bar,', (False, False, False, False)), + ( 'deny change_profile,', (False, False, False, False)), ) + class ChangeProfileCoveredTest_06(ChangeProfileCoveredTest): rule = 'change_profile safe /foo,' tests = ( - # rule equal strict equal covered covered exact - ( 'deny change_profile /foo,' , ( False , False , False , False )), - ('audit deny change_profile /foo,' , ( False , False , False , False )), - ( 'change_profile /foo,' , ( True , False , True , True )), - ( 'deny change_profile /bar,' , ( False , False , False , False )), - ( 'deny change_profile,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'deny change_profile /foo,', (False, False, False, False)), + ('audit deny change_profile /foo,', (False, False, False, False)), + ( 'change_profile /foo,', (True, False, True, True)), + ( 'deny change_profile /bar,', (False, False, False, False)), + ( 'deny change_profile,', (False, False, False, False)), ) + class ChangeProfileCoveredTest_Invalid(AATest): def test_borked_obj_is_covered_1(self): obj = ChangeProfileRule.parse('change_profile /foo,') @@ -380,22 +391,24 @@ class ChangeProfileCoveredTest_Invalid(AATest): with self.assertRaises(AppArmorBug): obj.is_equal(testobj) + class ChangeProfileLogprofHeaderTest(AATest): tests = ( - ('change_profile,', [ _('Exec Condition'), _('ALL'), _('Target Profile'), _('ALL'), ]), - ('change_profile -> /bin/ping,', [ _('Exec Condition'), _('ALL'), _('Target Profile'), '/bin/ping',]), - ('change_profile /bar -> /bin/bar,', [ _('Exec Condition'), '/bar', _('Target Profile'), '/bin/bar', ]), - ('change_profile safe /foo,', [ _('Exec Mode'), 'safe', _('Exec Condition'), '/foo', _('Target Profile'), _('ALL'), ]), - ('audit change_profile -> /bin/ping,', [_('Qualifier'), 'audit', _('Exec Condition'), _('ALL'), _('Target Profile'), '/bin/ping',]), - ('deny change_profile /bar -> /bin/bar,', [_('Qualifier'), 'deny', _('Exec Condition'), '/bar', _('Target Profile'), '/bin/bar', ]), - ('allow change_profile unsafe /foo,', [_('Qualifier'), 'allow', _('Exec Mode'), 'unsafe', _('Exec Condition'), '/foo', _('Target Profile'), _('ALL'), ]), - ('audit deny change_profile,', [_('Qualifier'), 'audit deny', _('Exec Condition'), _('ALL'), _('Target Profile'), _('ALL'), ]), + ('change_profile,', [ _('Exec Condition'), _('ALL'), _('Target Profile'), _('ALL')]), + ('change_profile -> /bin/ping,', [ _('Exec Condition'), _('ALL'), _('Target Profile'), '/bin/ping']), + ('change_profile /bar -> /bin/bar,', [ _('Exec Condition'), '/bar', _('Target Profile'), '/bin/bar']), + ('change_profile safe /foo,', [ _('Exec Mode'), 'safe', _('Exec Condition'), '/foo', _('Target Profile'), _('ALL')]), + ('audit change_profile -> /bin/ping,', [_('Qualifier'), 'audit', _('Exec Condition'), _('ALL'), _('Target Profile'), '/bin/ping']), + ('deny change_profile /bar -> /bin/bar,', [_('Qualifier'), 'deny', _('Exec Condition'), '/bar', _('Target Profile'), '/bin/bar']), + ('allow change_profile unsafe /foo,', [_('Qualifier'), 'allow', _('Exec Mode'), 'unsafe', _('Exec Condition'), '/foo', _('Target Profile'), _('ALL')]), + ('audit deny change_profile,', [_('Qualifier'), 'audit deny', _('Exec Condition'), _('ALL'), _('Target Profile'), _('ALL')]), ) def _run_test(self, params, expected): obj = ChangeProfileRule.parse(params) self.assertEqual(obj.logprof_header(), expected) + # --- tests for ChangeProfileRuleset --- # class ChangeProfileRulesTest(AATest): @@ -478,9 +491,11 @@ class ChangeProfileGlobTestAATest(AATest): # get_glob_ext is not available for change_profile rules self.ruleset.get_glob_ext('change_profile /foo -> /bar,') + class ChangeProfileDeleteTestAATest(AATest): pass + setup_all_loops(__name__) if __name__ == '__main__': unittest.main(verbosity=1) diff --git a/utils/test/test-common.py b/utils/test/test-common.py index a17578414..88d18687a 100644 --- a/utils/test/test-common.py +++ b/utils/test/test-common.py @@ -15,25 +15,27 @@ from apparmor.common import AppArmorBug from apparmor.common import split_name, combine_profname + class AaTest_split_name(AATest): tests = ( - # full profile name expected parts - ('foo', ('foo', 'foo')), - ('foo//bar', ('foo', 'bar')), - ('foo//bar//baz', ('foo', 'bar')), # XXX nested child profiles get cut off + # full profile name expected parts + ('foo', ('foo', 'foo')), + ('foo//bar', ('foo', 'bar')), + ('foo//bar//baz', ('foo', 'bar')), # XXX nested child profiles get cut off ) def _run_test(self, params, expected): self.assertEqual(split_name(params), expected) + class AaTest_combine_profname(AATest): tests = ( - # name parts expected full profile name - (['foo'], 'foo'), - (['foo', 'bar'], 'foo//bar'), - (['foo', 'bar', 'baz'], 'foo//bar//baz'), - (['foo', 'bar', None], 'foo//bar'), - (['foo', 'bar', 'baz', None], 'foo//bar//baz'), + # name parts expected full profile name + (['foo'], 'foo'), + (['foo', 'bar'], 'foo//bar'), + (['foo', 'bar', 'baz'], 'foo//bar//baz'), + (['foo', 'bar', None], 'foo//bar'), + (['foo', 'bar', 'baz', None], 'foo//bar//baz'), ) def _run_test(self, params, expected): diff --git a/utils/test/test-config.py b/utils/test/test-config.py index e83fff3ce..ff5ece5e8 100755 --- a/utils/test/test-config.py +++ b/utils/test/test-config.py @@ -15,15 +15,17 @@ import unittest import apparmor.config as config -class Test(unittest.TestCase): +class Test(unittest.TestCase): def test_IniConfig(self): ini_config = config.Config('ini') ini_config.CONF_DIR = '.' conf = ini_config.read_config('logprof.conf') logprof_sections = ['settings', 'qualifiers', 'required_hats', 'defaulthat', 'globs'] - logprof_sections_options = ['profiledir', 'inactive_profiledir', 'logfiles', 'parser', 'ldd', 'logger', 'default_owner_prompt', 'custom_includes'] + logprof_sections_options = [ + 'profiledir', 'inactive_profiledir', 'logfiles', 'parser', 'ldd', + 'logger', 'default_owner_prompt', 'custom_includes'] logprof_settings_parser = '../../parser/apparmor_parser ../parser/apparmor_parser' self.assertEqual(conf.sections(), logprof_sections) @@ -43,8 +45,6 @@ class Test(unittest.TestCase): self.assertEqual(conf['']['TEMPLATES_DIR'], easyprof_Templates) - - if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testConfig'] + # import sys;sys.argv = ['', 'Test.testConfig'] unittest.main() diff --git a/utils/test/test-dbus.py b/utils/test/test-dbus.py index cf577d2d1..4716676fb 100644 --- a/utils/test/test-dbus.py +++ b/utils/test/test-dbus.py @@ -24,8 +24,12 @@ from apparmor.logparser import ReadLog from apparmor.translations import init_translation _ = init_translation() -exp = namedtuple('exp', ('audit', 'allow_keyword', 'deny', 'comment', - 'access', 'all_access', 'bus', 'all_buses', 'path', 'all_paths', 'name', 'all_names', 'interface', 'all_interfaces', 'member', 'all_members', 'peername', 'all_peernames', 'peerlabel', 'all_peerlabels')) +exp = namedtuple( + 'exp', ('audit', 'allow_keyword', 'deny', 'comment', 'access', 'all_access', + 'bus', 'all_buses', 'path', 'all_paths', 'name', 'all_names', + 'interface', 'all_interfaces', 'member', 'all_members', + 'peername', 'all_peernames', 'peerlabel', 'all_peerlabels')) + # --- tests for single DbusRule --- # @@ -60,39 +64,40 @@ class DbusTest(AATest): else: self.assertEqual(obj, expected) + class DbusTestParse(DbusTest): tests = ( - # DbusRule object audit allow deny comment access all? bus all? path all? name all? interface all? member all? peername all? peerlabel all? - ('dbus,' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, None, True, None, True)), - ('dbus ( ),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, None, True, None, True)), - ('dbus ( , ),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, None, True, None, True)), - ('dbus send,' , exp(False, False, False, '', {'send'}, False, None, True, None, True, None, True, None, True, None, True, None, True, None, True)), - ('dbus (send, receive),' , exp(False, False, False, '', {'send', 'receive'}, False, None, True, None, True, None, True, None, True, None, True, None, True, None, True)), - ('dbus send bus=session,' , exp(False, False, False, '', {'send'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, None, True)), - ('deny dbus send bus="session", # cmt' , exp(False, False, True , ' # cmt', {'send'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, None, True)), - ('audit allow dbus peer=(label=foo),' , exp(True , True , False, '', None , True , None, True, None, True, None, True, None, True, None, True, None, True, 'foo', False)), - ('dbus bus=session path=/foo/bar,' , exp(False, False, False, '', None , True , 'session', False, '/foo/bar', False, None, True, None, True, None, True, None, True, None, True)), - ('dbus send bus=(session),' , exp(False, False, False, '', {'send'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, None, True)), - ('dbus name=(SomeService),' , exp(False, False, False, '', None, True, None, True, None, True, 'SomeService',False, None, True, None, True, None, True, None, True)), - ('dbus send bus=session peer=(label="foo"),' , exp(False, False, False, '', {'send'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, 'foo', False)), - ('dbus send bus = ( session ) , ' , exp(False, False, False, '', {'send'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, None, True)), - ('dbus path=/foo,' , exp(False, False, False, '', None , True , None, True, '/foo', False, None, True, None, True, None, True, None, True, None, True)), - ('dbus eavesdrop bus=session,' , exp(False, False, False, '', {'eavesdrop'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, None, True)), - ('dbus peer=(name=foo label=bar),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), - ('dbus peer=( name = foo label = bar ),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), - ('dbus peer=( name = foo , label = bar ),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), - ('dbus peer=(, name = foo , label = bar ,),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), - ('dbus peer=( name = foo, label = bar ),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, 'foo,', False, 'bar', False)), # XXX peername includes the comma - ('dbus peer=(label=bar name=foo),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), - ('dbus peer=( label = bar name = foo ),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), - ('dbus peer=(, label = bar , name = foo ,),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), - ('dbus peer=(, label = bar, name = foo ),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar,', False)), # XXX peerlabel includes the comma - ('dbus peer=( label = bar , name = foo ),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), - ('dbus peer=( label = "bar" name = "foo" ),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), - ('dbus path=/foo/bar bus=session,' , exp(False, False, False, '', None , True , 'session', False, '/foo/bar', False, None, True, None, True, None, True, None, True, None, True)), - ('dbus bus=system path=/foo/bar bus=session,' , exp(False, False, False, '', None , True , 'session', False, '/foo/bar', False, None, True, None, True, None, True, None, True, None, True)), # XXX bus= specified twice, last one wins - ('dbus send peer=(label="foo") bus=session,' , exp(False, False, False, '', {'send'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, 'foo', False)), - ('dbus bus=1 bus=2 bus=3 bus=4 bus=5 bus=6,' , exp(False, False, False, '', None , True , '6', False, None, True, None, True, None, True, None, True, None, True, None, True)), # XXX bus= specified multiple times, last one wins + # DbusRule object audit allow deny comment access all? bus all? path all? name all? interface all? member all? peername all? peerlabel all? + ('dbus,', exp(False, False, False, '', None, True, None, True, None, True, None, True, None, True, None, True, None, True, None, True)), + ('dbus ( ),', exp(False, False, False, '', None, True, None, True, None, True, None, True, None, True, None, True, None, True, None, True)), + ('dbus ( , ),', exp(False, False, False, '', None, True, None, True, None, True, None, True, None, True, None, True, None, True, None, True)), + ('dbus send,', exp(False, False, False, '', {'send'}, False, None, True, None, True, None, True, None, True, None, True, None, True, None, True)), + ('dbus (send, receive),', exp(False, False, False, '', {'send', 'receive'}, False, None, True, None, True, None, True, None, True, None, True, None, True, None, True)), + ('dbus send bus=session,', exp(False, False, False, '', {'send'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, None, True)), + ('deny dbus send bus="session", # cmt', exp(False, False, True, ' # cmt', {'send'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, None, True)), + ('audit allow dbus peer=(label=foo),', exp(True, True, False, '', None, True, None, True, None, True, None, True, None, True, None, True, None, True, 'foo', False)), + ('dbus bus=session path=/foo/bar,', exp(False, False, False, '', None, True, 'session', False, '/foo/bar', False, None, True, None, True, None, True, None, True, None, True)), + ('dbus send bus=(session),', exp(False, False, False, '', {'send'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, None, True)), + ('dbus name=(SomeService),', exp(False, False, False, '', None, True, None, True, None, True, 'SomeService', False, None, True, None, True, None, True, None, True)), + ('dbus send bus=session peer=(label="foo"),', exp(False, False, False, '', {'send'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, 'foo', False)), + ('dbus send bus = ( session ) , ', exp(False, False, False, '', {'send'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, None, True)), + ('dbus path=/foo,', exp(False, False, False, '', None, True, None, True, '/foo', False, None, True, None, True, None, True, None, True, None, True)), + ('dbus eavesdrop bus=session,', exp(False, False, False, '', {'eavesdrop'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, None, True)), + ('dbus peer=(name=foo label=bar),', exp(False, False, False, '', None, True, None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), + ('dbus peer=( name = foo label = bar ),', exp(False, False, False, '', None, True, None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), + ('dbus peer=( name = foo , label = bar ),', exp(False, False, False, '', None, True, None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), + ('dbus peer=(, name = foo , label = bar ,),', exp(False, False, False, '', None, True, None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), + ('dbus peer=( name = foo, label = bar ),', exp(False, False, False, '', None, True, None, True, None, True, None, True, None, True, None, True, 'foo,', False, 'bar', False)), # XXX peername includes the comma + ('dbus peer=(label=bar name=foo),', exp(False, False, False, '', None, True, None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), + ('dbus peer=( label = bar name = foo ),', exp(False, False, False, '', None, True, None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), + ('dbus peer=(, label = bar , name = foo ,),', exp(False, False, False, '', None, True, None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), + ('dbus peer=(, label = bar, name = foo ),', exp(False, False, False, '', None, True, None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar,', False)), # XXX peerlabel includes the comma + ('dbus peer=( label = bar , name = foo ),', exp(False, False, False, '', None, True, None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), + ('dbus peer=( label = "bar" name = "foo" ),', exp(False, False, False, '', None, True, None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), + ('dbus path=/foo/bar bus=session,', exp(False, False, False, '', None, True, 'session', False, '/foo/bar', False, None, True, None, True, None, True, None, True, None, True)), + ('dbus bus=system path=/foo/bar bus=session,', exp(False, False, False, '', None, True, 'session', False, '/foo/bar', False, None, True, None, True, None, True, None, True, None, True)), # XXX bus= specified twice, last one wins + ('dbus send peer=(label="foo") bus=session,', exp(False, False, False, '', {'send'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, 'foo', False)), + ('dbus bus=1 bus=2 bus=3 bus=4 bus=5 bus=6,', exp(False, False, False, '', None, True, '6', False, None, True, None, True, None, True, None, True, None, True, None, True)), # XXX bus= specified multiple times, last one wins ) def _run_test(self, rawrule, expected): @@ -101,18 +106,19 @@ class DbusTestParse(DbusTest): self.assertEqual(rawrule.strip(), obj.raw_rule) self._compare_obj(obj, expected) + class DbusTestParseInvalid(DbusTest): tests = ( - ('dbus foo,' , AppArmorException), - ('dbus foo bar,' , AppArmorException), - ('dbus foo int,' , AppArmorException), - ('dbus send bar,' , AppArmorException), - ('dbus send receive,' , AppArmorException), - ('dbus peer=,' , AppArmorException), - ('dbus peer=(label=foo) path=,' , AppArmorException), - ('dbus (invalid),' , AppArmorException), - ('dbus peer=,' , AppArmorException), - ('dbus bus=session bind bus=system,', AppArmorException), + ('dbus foo,', AppArmorException), + ('dbus foo bar,', AppArmorException), + ('dbus foo int,', AppArmorException), + ('dbus send bar,', AppArmorException), + ('dbus send receive,', AppArmorException), + ('dbus peer=,', AppArmorException), + ('dbus peer=(label=foo) path=,', AppArmorException), + ('dbus (invalid),', AppArmorException), + ('dbus peer=,', AppArmorException), + ('dbus bus=session bind bus=system,', AppArmorException), ('dbus bus=1 bus=2 bus=3 bus=4 bus=5 bus=6 bus=7,', AppArmorException), ) @@ -121,6 +127,7 @@ class DbusTestParseInvalid(DbusTest): with self.assertRaises(expected): DbusRule.parse(rawrule) + class DbusTestParseFromLog(DbusTest): def test_dbus_from_log(self): parser = ReadLog('', '', '') @@ -156,110 +163,111 @@ class DbusTestParseFromLog(DbusTest): 'sock_type': None, }) -# XXX send rules must not contain name conditional, but the log event includes it - how should we handle this in logparser.py? + # # XXX send rules must not contain name conditional, but the log event includes it - how should we handle this in logparser.py? + # + # # access bus path name interface member peername peerlabel + # obj = DbusRule(parsed_event['denied_mask'], parsed_event['bus'], parsed_event['path'], parsed_event['name'], parsed_event['interface'], parsed_event['member'], parsed_event['peer_profile'], DbusRule.ALL, log_event=parsed_event) + # + # # DbusRule audit allow deny comment access all? bus all? path all? name all? interface all? member all? peername all? peerlabel all? + # expected = exp(False, False, False, '', {'send'}, False, 'system', False, '/org/freedesktop/DBus', False, 'org.freedesktop.DBus', False, 'org.freedesktop.DBus', False, 'Hello', False, 'unconfined', False, None, True) + # + # self._compare_obj(obj, expected) + # + # self.assertEqual(obj.get_raw(1), ' dbus send bus=system path=/org/freedesktop/DBus name=org.freedesktop.DBus member=Hello peer=(name=unconfined),') -# # access bus path name interface member peername peerlabel -# obj = DbusRule(parsed_event['denied_mask'], parsed_event['bus'], parsed_event['path'], parsed_event['name'], parsed_event['interface'], parsed_event['member'], parsed_event['peer_profile'], DbusRule.ALL, log_event=parsed_event) - -# # DbusRule audit allow deny comment access all? bus all? path all? name all? interface all? member all? peername all? peerlabel all? -# expected = exp( False, False, False, '', {'send'}, False, 'system', False, '/org/freedesktop/DBus', False, 'org.freedesktop.DBus', False, 'org.freedesktop.DBus', False, -# 'Hello', False, 'unconfined', False, None, True) - -# self._compare_obj(obj, expected) - -# self.assertEqual(obj.get_raw(1), ' dbus send bus=system path=/org/freedesktop/DBus name=org.freedesktop.DBus member=Hello peer=(name=unconfined),') class DbusFromInit(DbusTest): tests = ( - #DbusRule# access bus path name interface member peername peerlabel audit=, deny=, allow_keyword, comment=, log_event) - (DbusRule( 'send' , 'session', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL), - #exp# audit allow deny comment access all? bus all? path all? name all? interface all? member all? peername all? peerlabel all? - exp( False, False, False, '', {'send'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, None, True)), + # access bus path name interface member peername peerlabel audit=, deny=, allow_keyword, comment=, log_event) + (DbusRule('send', 'session', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL), + # audit allow deny comment access all? bus all? path all? name all? interface all? member all? peername all? peerlabel all? + exp(False, False, False, '', {'send'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, None, True)), - #DbusRule# access bus path name interface member peername peerlabel audit=, deny=, allow_keyword, comment=, log_event) - (DbusRule(('send', 'receive'), 'session', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL), - #exp# audit allow deny comment access all? bus all? path all? name all? interface all? member all? peername all? peerlabel all? - exp( False, False, False, '', {'send', 'receive'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, None, True)), + # access bus path name interface member peername peerlabel audit=, deny=, allow_keyword, comment=, log_event) + (DbusRule(('send', 'receive'), 'session', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL), + # audit allow deny comment access all? bus all? path all? name all? interface all? member all? peername all? peerlabel all? + exp(False, False, False, '', {'send', 'receive'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, None, True)), - #DbusRule# access bus path name interface member peername peerlabel audit=, deny=, allow_keyword, comment=, log_event) - (DbusRule(DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label'), - #exp# audit allow deny comment access all? bus all? path all? name all? interface all? member all? peername all? peerlabel all? - exp( False, False, False, '', None , True , None, True, None, True, None, True, '/int/face',False, '/mem/ber', False, '/peer/name', False, '/peer/label', False)), + # access bus path name interface member peername peerlabel audit=, deny=, allow_keyword, comment=, log_event) + (DbusRule(DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label'), + # audit allow deny comment access all? bus all? path all? name all? interface all? member all? peername all? peerlabel all? + exp(False, False, False, '', None, True, None, True, None, True, None, True, '/int/face', False, '/mem/ber', False, '/peer/name', False, '/peer/label', False)), ) def _run_test(self, obj, expected): self._compare_obj(obj, expected) + class InvalidDbusInit(AATest): tests = ( - # access bus path name interface member peername peerlabel expected exception + # access bus path name interface member peername peerlabel expected exception # empty fields - ( ('', 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( ((), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (DbusRule.ALL, '', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (DbusRule.ALL, 'session', '', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (DbusRule.ALL, 'session', '/org/test', '', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '/int/face', '', '/peer/name', '/peer/label'), AppArmorBug), - ( (DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '', '/peer/label'), AppArmorBug), - ( (DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '' ), AppArmorBug), + (('', 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + (((), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((DbusRule.ALL, '', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((DbusRule.ALL, 'session', '', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((DbusRule.ALL, 'session', '/org/test', '', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '/int/face', '', '/peer/name', '/peer/label'), AppArmorBug), + ((DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '', '/peer/label'), AppArmorBug), + ((DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', ''), AppArmorBug), # whitespace fields - ( (' ', 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (DbusRule.ALL, ' ', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (DbusRule.ALL, 'session', ' ', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (DbusRule.ALL, 'session', '/org/test', ' ', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (DbusRule.ALL, 'session', '/org/test', 'com.aa.test', ' ', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '/int/face', ' ', '/peer/name', '/peer/label'), AppArmorBug), - ( (DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', ' ', '/peer/label'), AppArmorBug), - ( (DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', ' ' ), AppArmorBug), + ((' ', 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((DbusRule.ALL, ' ', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((DbusRule.ALL, 'session', ' ', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((DbusRule.ALL, 'session', '/org/test', ' ', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((DbusRule.ALL, 'session', '/org/test', 'com.aa.test', ' ', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '/int/face', ' ', '/peer/name', '/peer/label'), AppArmorBug), + ((DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', ' ', '/peer/label'), AppArmorBug), + ((DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', ' '), AppArmorBug), # wrong type - dict() - ( (dict(), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (('send'), dict(), '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (('send'), 'session', dict(), 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (('send'), 'session', '/org/test', dict(), '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (('send'), 'session', '/org/test', 'com.aa.test', dict(), '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (('send'), 'session', '/org/test', 'com.aa.test', '/int/face', dict(), '/peer/name', '/peer/label'), AppArmorBug), - ( (('send'), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', dict(), '/peer/label'), AppArmorBug), - ( (('send'), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', dict() ), AppArmorBug), + ((dict(), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((('send'), dict(), '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((('send'), 'session', dict(), 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((('send'), 'session', '/org/test', dict(), '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((('send'), 'session', '/org/test', 'com.aa.test', dict(), '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((('send'), 'session', '/org/test', 'com.aa.test', '/int/face', dict(), '/peer/name', '/peer/label'), AppArmorBug), + ((('send'), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', dict(), '/peer/label'), AppArmorBug), + ((('send'), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', dict()), AppArmorBug), # wrong type - None - ( (None, 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( ((None), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (('send'), None, '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (('send'), 'session', None, 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (('send'), 'session', '/org/test', None, '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (('send'), 'session', '/org/test', 'com.aa.test', None, '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), - ( (('send'), 'session', '/org/test', 'com.aa.test', '/int/face', None, '/peer/name', '/peer/label'), AppArmorBug), - ( (('send'), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', None, '/peer/label'), AppArmorBug), - ( (('send'), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', None ), AppArmorBug), + ((None, 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + (((None), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((('send'), None, '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((('send'), 'session', None, 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((('send'), 'session', '/org/test', None, '/int/face', '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((('send'), 'session', '/org/test', 'com.aa.test', None, '/mem/ber', '/peer/name', '/peer/label'), AppArmorBug), + ((('send'), 'session', '/org/test', 'com.aa.test', '/int/face', None, '/peer/name', '/peer/label'), AppArmorBug), + ((('send'), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', None, '/peer/label'), AppArmorBug), + ((('send'), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', None), AppArmorBug), # bind conflicts with path, interface, member, peer name and peer label - ( (('bind'), DbusRule.ALL, '/org/test', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL ), AppArmorException), - ( (('bind'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/int/face', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL ), AppArmorException), - ( (('bind'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/mem/ber', DbusRule.ALL, DbusRule.ALL ), AppArmorException), - ( (('bind'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/peer/name', DbusRule.ALL ), AppArmorException), - ( (('bind'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/peer/label'), AppArmorException), + ((('bind'), DbusRule.ALL, '/org/test', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL), AppArmorException), + ((('bind'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/int/face', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL), AppArmorException), + ((('bind'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/mem/ber', DbusRule.ALL, DbusRule.ALL), AppArmorException), + ((('bind'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/peer/name', DbusRule.ALL), AppArmorException), + ((('bind'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/peer/label'), AppArmorException), # eavesdrop conflcts with path, name, interface, member, peer name and peer label - ( (('eavesdrop'),DbusRule.ALL, '/org/test', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL ), AppArmorException), - ( (('eavesdrop'),DbusRule.ALL, DbusRule.ALL, 'com.aa.test', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL ), AppArmorException), - ( (('eavesdrop'),DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/int/face', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL ), AppArmorException), - ( (('eavesdrop'),DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/mem/ber', DbusRule.ALL, DbusRule.ALL ), AppArmorException), - ( (('eavesdrop'),DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/peer/name', DbusRule.ALL ), AppArmorException), - ( (('eavesdrop'),DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/peer/label'), AppArmorException), + ((('eavesdrop'), DbusRule.ALL, '/org/test', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL), AppArmorException), + ((('eavesdrop'), DbusRule.ALL, DbusRule.ALL, 'com.aa.test', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL), AppArmorException), + ((('eavesdrop'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/int/face', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL), AppArmorException), + ((('eavesdrop'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/mem/ber', DbusRule.ALL, DbusRule.ALL), AppArmorException), + ((('eavesdrop'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/peer/name', DbusRule.ALL), AppArmorException), + ((('eavesdrop'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, '/peer/label'), AppArmorException), # send and receive conflict with name - ( (('send'), DbusRule.ALL, DbusRule.ALL, 'com.aa.test', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL ), AppArmorException), - ( (('receive'), DbusRule.ALL, DbusRule.ALL, 'com.aa.test', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL ), AppArmorException), + ((('send'), DbusRule.ALL, DbusRule.ALL, 'com.aa.test', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL), AppArmorException), + ((('receive'), DbusRule.ALL, DbusRule.ALL, 'com.aa.test', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL), AppArmorException), # misc - ( (DbusRule.ALL, DbusRule.ALL, 'foo/bar', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL ), AppArmorException), # path doesn't start with / - ( (('foo'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL ), AppArmorException), # invalid access keyword - ( (('foo', 'send'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL ), AppArmorException), # valid + invalid access keyword + ((DbusRule.ALL, DbusRule.ALL, 'foo/bar', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL), AppArmorException), # path doesn't start with / + ((('foo'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL), AppArmorException), # invalid access keyword + ((('foo', 'send'), DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL), AppArmorException), # valid + invalid access keyword ) def _run_test(self, params, expected): @@ -272,27 +280,28 @@ class InvalidDbusInit(AATest): def test_missing_params_2(self): with self.assertRaises(TypeError): - DbusRule(('send'), 'session') + DbusRule(('send'), 'session') def test_missing_params_3(self): with self.assertRaises(TypeError): - DbusRule(('send'), 'session', '/org/test') + DbusRule(('send'), 'session', '/org/test') def test_missing_params_4(self): with self.assertRaises(TypeError): - DbusRule(('send'), 'session', '/org/test', 'com.aa.test') + DbusRule(('send'), 'session', '/org/test', 'com.aa.test') def test_missing_params_5(self): with self.assertRaises(TypeError): - DbusRule(('send'), 'session', '/org/test', 'com.aa.test', '/int/face') + DbusRule(('send'), 'session', '/org/test', 'com.aa.test', '/int/face') def test_missing_params_6(self): with self.assertRaises(TypeError): - DbusRule(('send'), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber') + DbusRule(('send'), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber') def test_missing_params_7(self): with self.assertRaises(TypeError): - DbusRule(('send'), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name') + DbusRule(('send'), 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name') + class InvalidDbusTest(AATest): def _check_invalid_rawrule(self, rawrule): @@ -310,57 +319,57 @@ class InvalidDbusTest(AATest): self._check_invalid_rawrule('signal,') # not a dbus rule def test_empty_data_1(self): - # access bus path name interface member peername peerlabel expected exception - obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label') + # access bus path name interface member peername peerlabel + obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label') obj.access = '' # no access set, and ALL not set with self.assertRaises(AppArmorBug): obj.get_clean(1) def test_empty_data_2(self): - obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label') + obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label') obj.bus = '' # no bus set, and ALL not set with self.assertRaises(AppArmorBug): obj.get_clean(1) def test_empty_data_3(self): - obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label') + obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label') obj.path = '' # no path set, and ALL not set with self.assertRaises(AppArmorBug): obj.get_clean(1) def test_empty_data_4(self): - obj = DbusRule(DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label') + obj = DbusRule(DbusRule.ALL, 'session', '/org/test', 'com.aa.test', '/int/face', '/mem/ber', '/peer/name', '/peer/label') obj.name = '' # no name set, and ALL not set with self.assertRaises(AppArmorBug): obj.get_clean(1) def test_empty_data_5(self): - obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label') + obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label') obj.interface = '' # no interface set, and ALL not set with self.assertRaises(AppArmorBug): obj.get_clean(1) def test_empty_data_6(self): - obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label') + obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label') obj.member = '' # no member set, and ALL not set with self.assertRaises(AppArmorBug): obj.get_clean(1) def test_empty_data_7(self): - obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label') + obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label') obj.peername = '' # no peername set, and ALL not set with self.assertRaises(AppArmorBug): obj.get_clean(1) def test_empty_data_8(self): - obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label') + obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label') obj.peerlabel = '' # no peerlabel set, and ALL not set with self.assertRaises(AppArmorBug): @@ -378,36 +387,36 @@ class WriteDbusTest(AATest): self.assertEqual(rawrule.strip(), raw, 'unexpected raw rule') tests = ( - # raw rule clean rule - (' dbus , # foo ' , 'dbus, # foo'), - (' audit dbus send,' , 'audit dbus send,'), - (' audit dbus (send ),' , 'audit dbus send,'), - (' audit dbus (send , receive ),' , 'audit dbus (receive send),'), - (' deny dbus send bus=session,# foo bar' , 'deny dbus send bus=session, # foo bar'), - (' deny dbus send bus=(session), ' , 'deny dbus send bus=session,'), - (' deny dbus send peer=(name=unconfined label=foo),' , 'deny dbus send peer=(name=unconfined label=foo),'), - (' deny dbus send interface = ( foo ),' , 'deny dbus send interface=foo,'), - (' deny dbus send ,# foo bar' , 'deny dbus send, # foo bar'), - (' allow dbus peer=(label=foo) ,# foo bar' , 'allow dbus peer=(label=foo), # foo bar'), - ('dbus,' , 'dbus,'), - ('dbus (receive),' , 'dbus receive,'), - ('dbus (send),' , 'dbus send,'), - ('dbus (send receive),' , 'dbus (receive send),'), - ('dbus receive,' , 'dbus receive,'), - ('dbus eavesdrop,' , 'dbus eavesdrop,'), - ('dbus bind bus = foo name = bar,' , 'dbus bind bus=foo name=bar,'), - ('dbus send peer=( label = /foo ) ,' , 'dbus send peer=(label=/foo),'), - ('dbus (receive) member=baz,' , 'dbus receive member=baz,'), - ('dbus send path = /foo,' , 'dbus send path=/foo,'), - ('dbus receive peer=(label=foo),' , 'dbus receive peer=(label=foo),'), - ('dbus (send receive) peer=(name=/usr/bin/bar),' , 'dbus (receive send) peer=(name=/usr/bin/bar),'), - ('dbus (, receive ,,, send ,) interface=/sbin/baz,' , 'dbus (receive send) interface=/sbin/baz,'), # XXX leading and trailing ',' inside (...) causes error + # raw rule clean rule + (' dbus , # foo ', 'dbus, # foo'), + (' audit dbus send,', 'audit dbus send,'), + (' audit dbus (send ),', 'audit dbus send,'), + (' audit dbus (send , receive ),', 'audit dbus (receive send),'), + (' deny dbus send bus=session,# foo bar', 'deny dbus send bus=session, # foo bar'), + (' deny dbus send bus=(session), ', 'deny dbus send bus=session,'), + (' deny dbus send peer=(name=unconfined label=foo),', 'deny dbus send peer=(name=unconfined label=foo),'), + (' deny dbus send interface = ( foo ),', 'deny dbus send interface=foo,'), + (' deny dbus send ,# foo bar', 'deny dbus send, # foo bar'), + (' allow dbus peer=(label=foo) ,# foo bar', 'allow dbus peer=(label=foo), # foo bar'), + ('dbus,', 'dbus,'), + ('dbus (receive),', 'dbus receive,'), + ('dbus (send),', 'dbus send,'), + ('dbus (send receive),', 'dbus (receive send),'), + ('dbus receive,', 'dbus receive,'), + ('dbus eavesdrop,', 'dbus eavesdrop,'), + ('dbus bind bus = foo name = bar,', 'dbus bind bus=foo name=bar,'), + ('dbus send peer=( label = /foo ) ,', 'dbus send peer=(label=/foo),'), + ('dbus (receive) member=baz,', 'dbus receive member=baz,'), + ('dbus send path = /foo,', 'dbus send path=/foo,'), + ('dbus receive peer=(label=foo),', 'dbus receive peer=(label=foo),'), + ('dbus (send receive) peer=(name=/usr/bin/bar),', 'dbus (receive send) peer=(name=/usr/bin/bar),'), + ('dbus (, receive ,,, send ,) interface=/sbin/baz,', 'dbus (receive send) interface=/sbin/baz,'), # XXX leading and trailing ',' inside (...) causes error # XXX add more complex rules ) def test_write_manually_1(self): - # access bus path name interface member peername peerlabel expected exception - obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label', allow_keyword=True) + # access bus path name interface member peername peerlabel + obj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label', allow_keyword=True) expected = ' allow dbus send bus=session path=/org/test interface=/int/face member=/mem/ber peer=(name=/peer/name label=/peer/label),' @@ -415,8 +424,8 @@ class WriteDbusTest(AATest): self.assertEqual(expected, obj.get_raw(2), 'unexpected raw rule') def test_write_manually_2(self): - # access bus path name interface member peername peerlabel expected exception - obj = DbusRule(('send', 'receive'), DbusRule.ALL, '/org/test', DbusRule.ALL, DbusRule.ALL, '/mem/ber', '/peer/name', DbusRule.ALL, allow_keyword=True) + # access bus path name interface member peername peerlabel + obj = DbusRule(('send', 'receive'), DbusRule.ALL, '/org/test', DbusRule.ALL, DbusRule.ALL, '/mem/ber', '/peer/name', DbusRule.ALL, allow_keyword=True) expected = ' allow dbus (receive send) path=/org/test member=/mem/ber peer=(name=/peer/name),' @@ -437,263 +446,272 @@ class DbusCoveredTest(AATest): self.assertEqual(obj.is_covered(check_obj), expected[2], 'Mismatch in is_covered, expected %s' % expected[2]) self.assertEqual(obj.is_covered(check_obj, True, True), expected[3], 'Mismatch in is_covered/exact, expected %s' % expected[3]) + class DbusCoveredTest_01(DbusCoveredTest): rule = 'dbus send,' tests = ( - # rule equal strict equal covered covered exact - ('dbus,' , ( False , False , False , False )), - ('dbus send,' , ( True , True , True , True )), - ('dbus send member=unconfined,' , ( False , False , True , True )), - ('dbus send, # comment' , ( True , False , True , True )), - ('allow dbus send,' , ( True , False , True , True )), - ('dbus send,' , ( True , False , True , True )), - ('dbus send bus=session,' , ( False , False , True , True )), - ('dbus send member=(label=foo),' , ( False , False , True , True )), - ('audit dbus send,' , ( False , False , False , False )), - ('audit dbus,' , ( False , False , False , False )), - ('dbus receive,' , ( False , False , False , False )), - ('dbus member=(label=foo),' , ( False , False , False , False )), - ('audit deny dbus send,' , ( False , False , False , False )), - ('deny dbus send,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('dbus,', (False, False, False, False)), + ('dbus send,', (True, True, True, True)), + ('dbus send member=unconfined,', (False, False, True, True)), + ('dbus send, # comment', (True, False, True, True)), + ('allow dbus send,', (True, False, True, True)), + ('dbus send,', (True, False, True, True)), + ('dbus send bus=session,', (False, False, True, True)), + ('dbus send member=(label=foo),', (False, False, True, True)), + ('audit dbus send,', (False, False, False, False)), + ('audit dbus,', (False, False, False, False)), + ('dbus receive,', (False, False, False, False)), + ('dbus member=(label=foo),', (False, False, False, False)), + ('audit deny dbus send,', (False, False, False, False)), + ('deny dbus send,', (False, False, False, False)), ) + class DbusCoveredTest_02(DbusCoveredTest): rule = 'audit dbus send,' tests = ( - # rule equal strict equal covered covered exact - ( 'dbus send,' , ( False , False , True , False )), - ('audit dbus send,' , ( True , True , True , True )), - ( 'dbus send bus=session,' , ( False , False , True , False )), - ('audit dbus send bus=session,' , ( False , False , True , True )), - ( 'dbus,' , ( False , False , False , False )), - ('audit dbus,' , ( False , False , False , False )), - ('dbus receive,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'dbus send,', (False, False, True, False)), + ('audit dbus send,', (True, True, True, True)), + ( 'dbus send bus=session,', (False, False, True, False)), + ('audit dbus send bus=session,', (False, False, True, True)), + ( 'dbus,', (False, False, False, False)), + ('audit dbus,', (False, False, False, False)), + ('dbus receive,', (False, False, False, False)), ) + class DbusCoveredTest_03(DbusCoveredTest): rule = 'dbus send bus=session,' tests = ( - # rule equal strict equal covered covered exact - ( 'dbus send bus=session,' , ( True , True , True , True )), - ('allow dbus send bus=session,' , ( True , False , True , True )), - ( 'dbus send,' , ( False , False , False , False )), - ( 'dbus,' , ( False , False , False , False )), - ( 'dbus send member=(label=foo),' , ( False , False , False , False )), - ('audit dbus,' , ( False , False , False , False )), - ('audit dbus send bus=session,' , ( False , False , False , False )), - ('audit dbus bus=session,' , ( False , False , False , False )), - ( 'dbus send,' , ( False , False , False , False )), - ( 'dbus,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'dbus send bus=session,', (True, True, True, True)), + ('allow dbus send bus=session,', (True, False, True, True)), + ( 'dbus send,', (False, False, False, False)), + ( 'dbus,', (False, False, False, False)), + ( 'dbus send member=(label=foo),', (False, False, False, False)), + ('audit dbus,', (False, False, False, False)), + ('audit dbus send bus=session,', (False, False, False, False)), + ('audit dbus bus=session,', (False, False, False, False)), + ( 'dbus send,', (False, False, False, False)), + ( 'dbus,', (False, False, False, False)), ) + class DbusCoveredTest_04(DbusCoveredTest): rule = 'dbus,' tests = ( - # rule equal strict equal covered covered exact - ( 'dbus,' , ( True , True , True , True )), - ('allow dbus,' , ( True , False , True , True )), - ( 'dbus send,' , ( False , False , True , True )), - ( 'dbus receive bus=session,' , ( False , False , True , True )), - ( 'dbus member=(label=foo),' , ( False , False , True , True )), - ( 'dbus send bus=session,' , ( False , False , True , True )), - ('audit dbus,' , ( False , False , False , False )), - ('deny dbus,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'dbus,', (True, True, True, True)), + ('allow dbus,', (True, False, True, True)), + ( 'dbus send,', (False, False, True, True)), + ( 'dbus receive bus=session,', (False, False, True, True)), + ( 'dbus member=(label=foo),', (False, False, True, True)), + ( 'dbus send bus=session,', (False, False, True, True)), + ('audit dbus,', (False, False, False, False)), + ('deny dbus,', (False, False, False, False)), ) + class DbusCoveredTest_05(DbusCoveredTest): rule = 'deny dbus send,' tests = ( - # rule equal strict equal covered covered exact - ( 'deny dbus send,' , ( True , True , True , True )), - ('audit deny dbus send,' , ( False , False , False , False )), - ( 'dbus send,' , ( False , False , False , False )), # XXX should covered be true here? - ( 'deny dbus receive,' , ( False , False , False , False )), - ( 'deny dbus,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'deny dbus send,', (True, True, True, True)), + ('audit deny dbus send,', (False, False, False, False)), + ( 'dbus send,', (False, False, False, False)), # XXX should covered be true here? + ( 'deny dbus receive,', (False, False, False, False)), + ( 'deny dbus,', (False, False, False, False)), ) + class DbusCoveredTest_06(DbusCoveredTest): rule = 'dbus send peer=(name=unconfined),' tests = ( - # rule equal strict equal covered covered exact - ('dbus,' , ( False , False , False , False )), - ('dbus send,' , ( False , False , False , False )), - ('dbus send peer=(name=unconfined),' , ( True , True , True , True )), - ('dbus peer=(name=unconfined),' , ( False , False , False , False )), - ('dbus send, # comment' , ( False , False , False , False )), - ('allow dbus send,' , ( False , False , False , False )), - ('allow dbus send peer=(name=unconfined),' , ( True , False , True , True )), - ('allow dbus send peer=(name=/foo/bar),' , ( False , False , False , False )), - ('allow dbus send peer=(name=/**),' , ( False , False , False , False )), - ('allow dbus send peer=(name=**),' , ( False , False , False , False )), - ('dbus send,' , ( False , False , False , False )), - ('dbus send peer=(name=unconfined),' , ( True , False , True , True )), - ('dbus send bus=session,' , ( False , False , False , False )), - ('dbus send peer=(name=unconfined label=foo),' , ( False , False , True , True )), - ('audit dbus send peer=(name=unconfined),' , ( False , False , False , False )), - ('audit dbus,' , ( False , False , False , False )), - ('dbus receive,' , ( False , False , False , False )), - ('dbus peer=(label=foo),' , ( False , False , False , False )), - ('audit deny dbus send,' , ( False , False , False , False )), - ('deny dbus send,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('dbus,', (False, False, False, False)), + ('dbus send,', (False, False, False, False)), + ('dbus send peer=(name=unconfined),', (True, True, True, True)), + ('dbus peer=(name=unconfined),', (False, False, False, False)), + ('dbus send, # comment', (False, False, False, False)), + ('allow dbus send,', (False, False, False, False)), + ('allow dbus send peer=(name=unconfined),', (True, False, True, True)), + ('allow dbus send peer=(name=/foo/bar),', (False, False, False, False)), + ('allow dbus send peer=(name=/**),', (False, False, False, False)), + ('allow dbus send peer=(name=**),', (False, False, False, False)), + ('dbus send,', (False, False, False, False)), + ('dbus send peer=(name=unconfined),', (True, False, True, True)), + ('dbus send bus=session,', (False, False, False, False)), + ('dbus send peer=(name=unconfined label=foo),', (False, False, True, True)), + ('audit dbus send peer=(name=unconfined),', (False, False, False, False)), + ('audit dbus,', (False, False, False, False)), + ('dbus receive,', (False, False, False, False)), + ('dbus peer=(label=foo),', (False, False, False, False)), + ('audit deny dbus send,', (False, False, False, False)), + ('deny dbus send,', (False, False, False, False)), ) + class DbusCoveredTest_07(DbusCoveredTest): rule = 'dbus send peer=(label=unconfined),' tests = ( - # rule equal strict equal covered covered exact - ('dbus,' , ( False , False , False , False )), - ('dbus send,' , ( False , False , False , False )), - ('dbus send peer=(label=unconfined),' , ( True , True , True , True )), - ('dbus peer=(label=unconfined),' , ( False , False , False , False )), - ('dbus send, # comment' , ( False , False , False , False )), - ('allow dbus send,' , ( False , False , False , False )), - ('allow dbus send peer=(label=unconfined),' , ( True , False , True , True )), - ('allow dbus send peer=(label=/foo/bar),' , ( False , False , False , False )), - ('allow dbus send peer=(label=/**),' , ( False , False , False , False )), - ('allow dbus send peer=(label=**),' , ( False , False , False , False )), - ('dbus send,' , ( False , False , False , False )), - ('dbus send peer=(label=unconfined),' , ( True , False , True , True )), - ('dbus send bus=session,' , ( False , False , False , False )), - ('dbus send peer=(label=unconfined name=foo),' , ( False , False , True , True )), - ('audit dbus send peer=(label=unconfined),' , ( False , False , False , False )), - ('audit dbus,' , ( False , False , False , False )), - ('dbus receive,' , ( False , False , False , False )), - ('dbus peer=(label=foo),' , ( False , False , False , False )), - ('audit deny dbus send,' , ( False , False , False , False )), - ('deny dbus send,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('dbus,', (False, False, False, False)), + ('dbus send,', (False, False, False, False)), + ('dbus send peer=(label=unconfined),', (True, True, True, True)), + ('dbus peer=(label=unconfined),', (False, False, False, False)), + ('dbus send, # comment', (False, False, False, False)), + ('allow dbus send,', (False, False, False, False)), + ('allow dbus send peer=(label=unconfined),', (True, False, True, True)), + ('allow dbus send peer=(label=/foo/bar),', (False, False, False, False)), + ('allow dbus send peer=(label=/**),', (False, False, False, False)), + ('allow dbus send peer=(label=**),', (False, False, False, False)), + ('dbus send,', (False, False, False, False)), + ('dbus send peer=(label=unconfined),', (True, False, True, True)), + ('dbus send bus=session,', (False, False, False, False)), + ('dbus send peer=(label=unconfined name=foo),', (False, False, True, True)), + ('audit dbus send peer=(label=unconfined),', (False, False, False, False)), + ('audit dbus,', (False, False, False, False)), + ('dbus receive,', (False, False, False, False)), + ('dbus peer=(label=foo),', (False, False, False, False)), + ('audit deny dbus send,', (False, False, False, False)), + ('deny dbus send,', (False, False, False, False)), ) + class DbusCoveredTest_08(DbusCoveredTest): rule = 'dbus send path=/foo/bar,' tests = ( - # rule equal strict equal covered covered exact - ('dbus,' , ( False , False , False , False )), - ('dbus send,' , ( False , False , False , False )), - ('dbus send path=/foo/bar,' , ( True , True , True , True )), - ('dbus send path=/foo/*,' , ( False , False , False , False )), - ('dbus send path=/**,' , ( False , False , False , False )), - ('dbus send path=/what/*,' , ( False , False , False , False )), - ('dbus path=/foo/bar,' , ( False , False , False , False )), - ('dbus send, # comment' , ( False , False , False , False )), - ('allow dbus send,' , ( False , False , False , False )), - ('allow dbus send path=/foo/bar,' , ( True , False , True , True )), - ('dbus send,' , ( False , False , False , False )), - ('dbus send path=/foo/bar,' , ( True , False , True , True )), - ('dbus send path=/what/ever,' , ( False , False , False , False )), - ('dbus send bus=session,' , ( False , False , False , False )), - ('dbus send path=/foo/bar peer=(label=foo),' , ( False , False , True , True )), - ('audit dbus send path=/foo/bar,' , ( False , False , False , False )), - ('audit dbus,' , ( False , False , False , False )), - ('dbus receive,' , ( False , False , False , False )), - ('dbus peer=(label=foo),' , ( False , False , False , False )), - ('audit deny dbus send,' , ( False , False , False , False )), - ('deny dbus send,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('dbus,', (False, False, False, False)), + ('dbus send,', (False, False, False, False)), + ('dbus send path=/foo/bar,', (True, True, True, True)), + ('dbus send path=/foo/*,', (False, False, False, False)), + ('dbus send path=/**,', (False, False, False, False)), + ('dbus send path=/what/*,', (False, False, False, False)), + ('dbus path=/foo/bar,', (False, False, False, False)), + ('dbus send, # comment', (False, False, False, False)), + ('allow dbus send,', (False, False, False, False)), + ('allow dbus send path=/foo/bar,', (True, False, True, True)), + ('dbus send,', (False, False, False, False)), + ('dbus send path=/foo/bar,', (True, False, True, True)), + ('dbus send path=/what/ever,', (False, False, False, False)), + ('dbus send bus=session,', (False, False, False, False)), + ('dbus send path=/foo/bar peer=(label=foo),', (False, False, True, True)), + ('audit dbus send path=/foo/bar,', (False, False, False, False)), + ('audit dbus,', (False, False, False, False)), + ('dbus receive,', (False, False, False, False)), + ('dbus peer=(label=foo),', (False, False, False, False)), + ('audit deny dbus send,', (False, False, False, False)), + ('deny dbus send,', (False, False, False, False)), ) + class DbusCoveredTest_09(DbusCoveredTest): rule = 'dbus send member=**,' tests = ( - # rule equal strict equal covered covered exact - ('dbus,' , ( False , False , False , False )), - ('dbus send,' , ( False , False , False , False )), - ('dbus send member=/foo/bar,' , ( False , False , True , True )), - ('dbus send member=/foo/*,' , ( False , False , False , False )), # TODO: wildcard vs. wildcard never matches in is_covered_aare() - ('dbus send member=/**,' , ( False , False , False , False )), # TODO: wildcard vs. wildcard never matches in is_covered_aare() - ('dbus send member=/what/*,' , ( False , False , False , False )), # TODO: wildcard vs. wildcard never matches in is_covered_aare() - ('dbus member=/foo/bar,' , ( False , False , False , False )), - ('dbus send, # comment' , ( False , False , False , False )), - ('allow dbus send,' , ( False , False , False , False )), - ('allow dbus send member=/foo/bar,' , ( False , False , True , True )), - ('dbus send,' , ( False , False , False , False )), - ('dbus send member=/foo/bar,' , ( False , False , True , True )), - ('dbus send member=/what/ever,' , ( False , False , True , True )), - ('dbus send bus=session,' , ( False , False , False , False )), - ('dbus send member=/foo/bar peer=(label=foo),' , ( False , False , True , True )), - ('audit dbus send member=/foo/bar,' , ( False , False , False , False )), - ('audit dbus,' , ( False , False , False , False )), - ('dbus receive,' , ( False , False , False , False )), - ('dbus member=(label=foo),' , ( False , False , False , False )), - ('audit deny dbus send,' , ( False , False , False , False )), - ('deny dbus send,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('dbus,', (False, False, False, False)), + ('dbus send,', (False, False, False, False)), + ('dbus send member=/foo/bar,', (False, False, True, True)), + ('dbus send member=/foo/*,', (False, False, False, False)), # TODO: wildcard vs. wildcard never matches in is_covered_aare() + ('dbus send member=/**,', (False, False, False, False)), # TODO: wildcard vs. wildcard never matches in is_covered_aare() + ('dbus send member=/what/*,', (False, False, False, False)), # TODO: wildcard vs. wildcard never matches in is_covered_aare() + ('dbus member=/foo/bar,', (False, False, False, False)), + ('dbus send, # comment', (False, False, False, False)), + ('allow dbus send,', (False, False, False, False)), + ('allow dbus send member=/foo/bar,', (False, False, True, True)), + ('dbus send,', (False, False, False, False)), + ('dbus send member=/foo/bar,', (False, False, True, True)), + ('dbus send member=/what/ever,', (False, False, True, True)), + ('dbus send bus=session,', (False, False, False, False)), + ('dbus send member=/foo/bar peer=(label=foo),', (False, False, True, True)), + ('audit dbus send member=/foo/bar,', (False, False, False, False)), + ('audit dbus,', (False, False, False, False)), + ('dbus receive,', (False, False, False, False)), + ('dbus member=(label=foo),', (False, False, False, False)), + ('audit deny dbus send,', (False, False, False, False)), + ('deny dbus send,', (False, False, False, False)), ) + class DbusCoveredTest_10(DbusCoveredTest): rule = 'dbus (send, receive) interface=foo,' tests = ( - # rule equal strict equal covered covered exact - ('dbus,' , ( False , False , False , False )), - ('dbus send,' , ( False , False , False , False )), - ('dbus send interface=foo,' , ( False , False , True , True )), - ('dbus receive bus=session interface=foo,' , ( False , False , True , True )), - ('dbus (receive,send) interface=foo,' , ( True , False , True , True )), - ('dbus (receive,send),' , ( False , False , False , False )), - ('dbus send bus=session,' , ( False , False , False , False )), - ('dbus send member=/foo/bar,' , ( False , False , False , False )), - ('dbus send member=/foo/*,' , ( False , False , False , False )), - ('dbus send member=/**,' , ( False , False , False , False )), - ('dbus send member=/what/*,' , ( False , False , False , False )), - ('dbus member=/foo/bar,' , ( False , False , False , False )), - ('dbus send, # comment' , ( False , False , False , False )), - ('allow dbus send,' , ( False , False , False , False )), - ('allow dbus send member=/foo/bar,' , ( False , False , False , False )), - ('dbus send,' , ( False , False , False , False )), - ('dbus send member=/foo/bar,' , ( False , False , False , False )), - ('dbus send member=/what/ever,' , ( False , False , False , False )), - ('dbus send bus=session,' , ( False , False , False , False )), - ('dbus send bus=session interface=foo,' , ( False , False , True , True )), - ('dbus send member=/foo/bar peer=(label=foo),' , ( False , False , False , False )), - ('audit dbus send member=/foo/bar,' , ( False , False , False , False )), - ('audit dbus,' , ( False , False , False , False )), - ('dbus receive,' , ( False , False , False , False )), - ('dbus peer=(label=foo),' , ( False , False , False , False )), - ('audit deny dbus send,' , ( False , False , False , False )), - ('deny dbus send,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('dbus,', (False, False, False, False)), + ('dbus send,', (False, False, False, False)), + ('dbus send interface=foo,', (False, False, True, True)), + ('dbus receive bus=session interface=foo,', (False, False, True, True)), + ('dbus (receive,send) interface=foo,', (True, False, True, True)), + ('dbus (receive,send),', (False, False, False, False)), + ('dbus send bus=session,', (False, False, False, False)), + ('dbus send member=/foo/bar,', (False, False, False, False)), + ('dbus send member=/foo/*,', (False, False, False, False)), + ('dbus send member=/**,', (False, False, False, False)), + ('dbus send member=/what/*,', (False, False, False, False)), + ('dbus member=/foo/bar,', (False, False, False, False)), + ('dbus send, # comment', (False, False, False, False)), + ('allow dbus send,', (False, False, False, False)), + ('allow dbus send member=/foo/bar,', (False, False, False, False)), + ('dbus send,', (False, False, False, False)), + ('dbus send member=/foo/bar,', (False, False, False, False)), + ('dbus send member=/what/ever,', (False, False, False, False)), + ('dbus send bus=session,', (False, False, False, False)), + ('dbus send bus=session interface=foo,', (False, False, True, True)), + ('dbus send member=/foo/bar peer=(label=foo),', (False, False, False, False)), + ('audit dbus send member=/foo/bar,', (False, False, False, False)), + ('audit dbus,', (False, False, False, False)), + ('dbus receive,', (False, False, False, False)), + ('dbus peer=(label=foo),', (False, False, False, False)), + ('audit deny dbus send,', (False, False, False, False)), + ('deny dbus send,', (False, False, False, False)), ) + class DbusCoveredTest_11(DbusCoveredTest): rule = 'dbus name=/foo/bar,' tests = ( - # rule equal strict equal covered covered exact - ('dbus,' , ( False , False , False , False )), - ('dbus name=/foo/bar,' , ( True , True , True , True )), - ('dbus name=/foo/*,' , ( False , False , False , False )), - ('dbus name=/**,' , ( False , False , False , False )), - ('dbus name=/what/*,' , ( False , False , False , False )), - ('dbus, # comment' , ( False , False , False , False )), - ('allow dbus,' , ( False , False , False , False )), - ('allow dbus name=/foo/bar,' , ( True , False , True , True )), - ('dbus ,' , ( False , False , False , False )), - ('dbus name=/foo/bar,' , ( True , False , True , True )), - ('dbus name=/what/ever,' , ( False , False , False , False )), - ('dbus bus=session,' , ( False , False , False , False )), - ('dbus name=/foo/bar peer=(label=foo),' , ( False , False , True , True )), - ('audit dbus name=/foo/bar,' , ( False , False , False , False )), - ('audit dbus,' , ( False , False , False , False )), - ('dbus receive,' , ( False , False , False , False )), - ('dbus peer=(label=foo),' , ( False , False , False , False )), - ('audit deny dbus,' , ( False , False , False , False )), - ('deny dbus,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('dbus,', (False, False, False, False)), + ('dbus name=/foo/bar,', (True, True, True, True)), + ('dbus name=/foo/*,', (False, False, False, False)), + ('dbus name=/**,', (False, False, False, False)), + ('dbus name=/what/*,', (False, False, False, False)), + ('dbus, # comment', (False, False, False, False)), + ('allow dbus,', (False, False, False, False)), + ('allow dbus name=/foo/bar,', (True, False, True, True)), + ('dbus ,', (False, False, False, False)), + ('dbus name=/foo/bar,', (True, False, True, True)), + ('dbus name=/what/ever,', (False, False, False, False)), + ('dbus bus=session,', (False, False, False, False)), + ('dbus name=/foo/bar peer=(label=foo),', (False, False, True, True)), + ('audit dbus name=/foo/bar,', (False, False, False, False)), + ('audit dbus,', (False, False, False, False)), + ('dbus receive,', (False, False, False, False)), + ('dbus peer=(label=foo),', (False, False, False, False)), + ('audit deny dbus,', (False, False, False, False)), + ('deny dbus,', (False, False, False, False)), ) - class DbusCoveredTest_Invalid(AATest): def AASetup(self): - # access bus path name interface member peername peerlabel expected exception - self.obj = DbusRule(('send', 'receive'), 'session', '/org/test', DbusRule.ALL, '/int/face', DbusRule.ALL, '/peer/name', '/peer/label', allow_keyword=True) - self.testobj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label', allow_keyword=True) - + # access bus path name interface member peername peerlabel + self.obj = DbusRule(('send', 'receive'), 'session', '/org/test', DbusRule.ALL, '/int/face', DbusRule.ALL, '/peer/name', '/peer/label', allow_keyword=True) + self.testobj = DbusRule(('send'), 'session', '/org/test', DbusRule.ALL, '/int/face', '/mem/ber', '/peer/name', '/peer/label', allow_keyword=True) def test_borked_obj_is_covered_1(self): self.testobj.access = '' @@ -715,8 +733,8 @@ class DbusCoveredTest_Invalid(AATest): def test_borked_obj_is_covered_4(self): # we need a different 'victim' because dbus send doesn't allow the name conditional we want to test here - self.obj = DbusRule(('bind'), 'session', DbusRule.ALL, '/name', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, allow_keyword=True) - self.testobj = DbusRule(('bind'), 'session', DbusRule.ALL, '/name', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, allow_keyword=True) + self.obj = DbusRule( ('bind'), 'session', DbusRule.ALL, '/name', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, allow_keyword=True) + self.testobj = DbusRule(('bind'), 'session', DbusRule.ALL, '/name', DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, DbusRule.ALL, allow_keyword=True) self.testobj.name = '' with self.assertRaises(AppArmorBug): @@ -746,7 +764,6 @@ class DbusCoveredTest_Invalid(AATest): with self.assertRaises(AppArmorBug): self.obj.is_covered(self.testobj) - def test_invalid_is_covered(self): obj = DbusRule.parse('dbus send,') @@ -763,25 +780,27 @@ class DbusCoveredTest_Invalid(AATest): with self.assertRaises(AppArmorBug): obj.is_equal(testobj) + class DbusLogprofHeaderTest(AATest): tests = ( - ('dbus,', [ _('Access mode'), _('ALL'), _('Bus'), _('ALL'), _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), - ('dbus (send receive),', [ _('Access mode'), 'receive send', _('Bus'), _('ALL'), _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), - ('dbus send bus=session,', [ _('Access mode'), 'send', _('Bus'), 'session', _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), - ('deny dbus,', [_('Qualifier'), 'deny', _('Access mode'), _('ALL'), _('Bus'), _('ALL'), _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), - ('allow dbus send,', [_('Qualifier'), 'allow', _('Access mode'), 'send', _('Bus'), _('ALL'), _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), - ('audit dbus send bus=session,', [_('Qualifier'), 'audit', _('Access mode'), 'send', _('Bus'), 'session', _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), - ('audit deny dbus send,', [_('Qualifier'), 'audit deny', _('Access mode'), 'send', _('Bus'), _('ALL'), _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), - ('dbus bind name=bind.name,', [ _('Access mode'), 'bind', _('Bus'), _('ALL'), _('Path'), _('ALL'), _('Name'), 'bind.name', _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), + ('dbus,', [ _('Access mode'), _('ALL'), _('Bus'), _('ALL'), _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), + ('dbus (send receive),', [ _('Access mode'), 'receive send', _('Bus'), _('ALL'), _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), + ('dbus send bus=session,', [ _('Access mode'), 'send', _('Bus'), 'session', _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), + ('deny dbus,', [_('Qualifier'), 'deny', _('Access mode'), _('ALL'), _('Bus'), _('ALL'), _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), + ('allow dbus send,', [_('Qualifier'), 'allow', _('Access mode'), 'send', _('Bus'), _('ALL'), _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), + ('audit dbus send bus=session,', [_('Qualifier'), 'audit', _('Access mode'), 'send', _('Bus'), 'session', _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), + ('audit deny dbus send,', [_('Qualifier'), 'audit deny', _('Access mode'), 'send', _('Bus'), _('ALL'), _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), + ('dbus bind name=bind.name,', [ _('Access mode'), 'bind', _('Bus'), _('ALL'), _('Path'), _('ALL'), _('Name'), 'bind.name', _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), _('ALL')]), ('dbus send bus=session path=/path interface=aa.test member=ExMbr peer=(name=(peer.name)),', - [ _('Access mode'), 'send', _('Bus'), 'session', _('Path'), '/path', _('Name'), _('ALL'), _('Interface'), 'aa.test', _('Member'), 'ExMbr', _('Peer name'), 'peer.name',_('Peer label'), _('ALL')]), - ('dbus send peer=(label=foo),', [ _('Access mode'), 'send', _('Bus'), _('ALL'), _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), 'foo' ]), + [ _('Access mode'), 'send', _('Bus'), 'session', _('Path'), '/path', _('Name'), _('ALL'), _('Interface'), 'aa.test', _('Member'), 'ExMbr', _('Peer name'), 'peer.name', _('Peer label'), _('ALL')]), + ('dbus send peer=(label=foo),', [ _('Access mode'), 'send', _('Bus'), _('ALL'), _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer name'), _('ALL'), _('Peer label'), 'foo']), ) def _run_test(self, params, expected): obj = DbusRule.parse(params) self.assertEqual(obj.logprof_header(), expected) + ## --- tests for DbusRuleset --- # class DbusRulesTest(AATest): @@ -865,8 +884,10 @@ class DbusGlobTest(AATest): # get_glob_ext is not available for dbus rules self.ruleset.get_glob_ext('dbus send peer=(label=foo),') -#class DbusDeleteTest(AATest): -# pass + +# class DbusDeleteTest(AATest): +# pass + setup_all_loops(__name__) if __name__ == '__main__': diff --git a/utils/test/test-example.py b/utils/test/test-example.py index 997ed993d..cf415fece 100644 --- a/utils/test/test-example.py +++ b/utils/test/test-example.py @@ -10,18 +10,20 @@ # ------------------------------------------------------------------ import unittest -from common_test import AATest, setup_all_loops # , setup_aa +from common_test import AATest, setup_all_loops # , setup_aa # import apparmor.aa as aa # see the setup_aa() call for details + class TestFoo(AATest): tests = ( - (0, 0 ), + (0, 0), (42, 42), ) def _run_test(self, params, expected): self.assertEqual(params, expected) + class TestBar(AATest): tests = ( ('a', 'foo'), @@ -35,6 +37,7 @@ class TestBar(AATest): def testAdditionalBarTest(self): self.assertEqual(1, 1) + class TestBaz(AATest): def AASetup(self): # called by setUp() - use AASetup() to avoid the need for using super(...) @@ -47,6 +50,7 @@ class TestBaz(AATest): def test_Baz_only_one_test(self): self.assertEqual("baz", "baz") + # if you import apparmor.aa and call init_aa() in your tests, uncomment this # setup_aa(aa) setup_all_loops(__name__) diff --git a/utils/test/test-file.py b/utils/test/test-file.py index 74494aacf..dfe17be1b 100644 --- a/utils/test/test-file.py +++ b/utils/test/test-file.py @@ -25,8 +25,11 @@ from apparmor.logparser import ReadLog from apparmor.translations import init_translation _ = init_translation() -exp = namedtuple('exp', ('audit', 'allow_keyword', 'deny', 'comment', - 'path', 'all_paths', 'perms', 'all_perms', 'exec_perms', 'target', 'all_targets', 'owner', 'file_keyword', 'leading_perms')) +exp = namedtuple( + 'exp', ('audit', 'allow_keyword', 'deny', 'comment', 'path', 'all_paths', + 'perms', 'all_perms', 'exec_perms', 'target', 'all_targets', + 'owner', 'file_keyword', 'leading_perms')) + # --- tests for single FileRule --- # @@ -56,46 +59,47 @@ class FileTest(AATest): else: self.assertEqual(obj, expected) + class FileTestParse(FileTest): tests = ( - # FileRule object audit allow deny comment path all_paths perms all? exec_perms target all? owner file keyword leading perms + # FileRule object audit allow deny comment path all_paths perms all? exec_perms target all? owner file keyword leading perms # bare file rules - ('file,' , exp(False, False, False, '', None, True , None, True, None, None, True, False, False, False )), - ('allow file,' , exp(False, True, False, '', None, True , None, True, None, None, True, False, False, False )), - ('audit deny owner file, # cmt' , exp(True, False, True, ' # cmt', None, True , None, True, None, None, True, True, False, False )), + ('file,', exp(False, False, False, '', None, True, None, True, None, None, True, False, False, False)), + ('allow file,', exp(False, True, False, '', None, True, None, True, None, None, True, False, False, False)), + ('audit deny owner file, # cmt', exp(True, False, True, ' # cmt', None, True, None, True, None, None, True, True, False, False)), # "normal" file rules - ('/foo r,' , exp(False, False, False, '', '/foo', False, {'r'}, False, None, None, True, False, False, False )), - ('file /foo rwix,' , exp(False, False, False, '', '/foo', False, {'r', 'w'}, False, 'ix', None, True, False, True, False )), - ('/foo Px -> bar,' , exp(False, False, False, '', '/foo', False, set(), False, 'Px', 'bar', False, False, False, False )), - ('@{PROC}/[a-z]** mr,' , exp(False, False, False, '', '@{PROC}/[a-z]**',False, {'r', 'm'}, False, None, None, True, False, False, False )), + ('/foo r,', exp(False, False, False, '', '/foo', False, {'r'}, False, None, None, True, False, False, False)), + ('file /foo rwix,', exp(False, False, False, '', '/foo', False, {'r', 'w'}, False, 'ix', None, True, False, True, False)), + ('/foo Px -> bar,', exp(False, False, False, '', '/foo', False, set(), False, 'Px', 'bar', False, False, False, False)), + ('@{PROC}/[a-z]** mr,', exp(False, False, False, '', '@{PROC}/[a-z]**', False, {'r', 'm'}, False, None, None, True, False, False, False)), - ('audit /tmp/foo r,' , exp(True, False, False, '', '/tmp/foo', False, {'r'}, False, None, None, True, False, False, False )), - ('audit deny /tmp/foo r,' , exp(True, False, True, '', '/tmp/foo', False, {'r'}, False, None, None, True, False, False, False )), - ('audit deny /tmp/foo rx,' , exp(True, False, True, '', '/tmp/foo', False, {'r'}, False, 'x', None, True, False, False, False )), - ('allow /tmp/foo ra,' , exp(False, True, False, '', '/tmp/foo', False, {'r', 'a'}, False, None, None, True, False, False, False )), - ('audit allow /tmp/foo ra,' , exp(True, True, False, '', '/tmp/foo', False, {'r', 'a'}, False, None, None, True, False, False, False )), + ('audit /tmp/foo r,', exp(True, False, False, '', '/tmp/foo', False, {'r'}, False, None, None, True, False, False, False)), + ('audit deny /tmp/foo r,', exp(True, False, True, '', '/tmp/foo', False, {'r'}, False, None, None, True, False, False, False)), + ('audit deny /tmp/foo rx,', exp(True, False, True, '', '/tmp/foo', False, {'r'}, False, 'x', None, True, False, False, False)), + ('allow /tmp/foo ra,', exp(False, True, False, '', '/tmp/foo', False, {'r', 'a'}, False, None, None, True, False, False, False)), + ('audit allow /tmp/foo ra,', exp(True, True, False, '', '/tmp/foo', False, {'r', 'a'}, False, None, None, True, False, False, False)), # file rules with leading permission - ('r /foo,' , exp(False, False, False, '', '/foo', False, {'r'}, False, None, None, True, False, False, True )), - ('file rwix /foo,' , exp(False, False, False, '', '/foo', False, {'r', 'w'}, False, 'ix', None, True, False, True, True )), - ('Px /foo -> bar,' , exp(False, False, False, '', '/foo', False, set(), False, 'Px', 'bar', False, False, False, True )), - ('mr @{PROC}/[a-z]**,' , exp(False, False, False, '', '@{PROC}/[a-z]**',False, {'r', 'm'}, False, None, None, True, False, False, True )), + ('r /foo,', exp(False, False, False, '', '/foo', False, {'r'}, False, None, None, True, False, False, True)), + ('file rwix /foo,', exp(False, False, False, '', '/foo', False, {'r', 'w'}, False, 'ix', None, True, False, True, True)), + ('Px /foo -> bar,', exp(False, False, False, '', '/foo', False, set(), False, 'Px', 'bar', False, False, False, True)), + ('mr @{PROC}/[a-z]**,', exp(False, False, False, '', '@{PROC}/[a-z]**', False, {'r', 'm'}, False, None, None, True, False, False, True)), - ('audit r /tmp/foo,' , exp(True, False, False, '', '/tmp/foo', False, {'r'}, False, None, None, True, False, False, True )), - ('audit deny r /tmp/foo,' , exp(True, False, True, '', '/tmp/foo', False, {'r'}, False, None, None, True, False, False, True )), - ('allow ra /tmp/foo,' , exp(False, True, False, '', '/tmp/foo', False, {'r', 'a'}, False, None, None, True, False, False, True )), - ('audit allow ra /tmp/foo,' , exp(True, True, False, '', '/tmp/foo', False, {'r', 'a'}, False, None, None, True, False, False, True )), + ('audit r /tmp/foo,', exp(True, False, False, '', '/tmp/foo', False, {'r'}, False, None, None, True, False, False, True)), + ('audit deny r /tmp/foo,', exp(True, False, True, '', '/tmp/foo', False, {'r'}, False, None, None, True, False, False, True)), + ('allow ra /tmp/foo,', exp(False, True, False, '', '/tmp/foo', False, {'r', 'a'}, False, None, None, True, False, False, True)), + ('audit allow ra /tmp/foo,', exp(True, True, False, '', '/tmp/foo', False, {'r', 'a'}, False, None, None, True, False, False, True)), # duplicated (but not conflicting) permissions - ('/foo PxPxPxPxrwPx -> bar,' , exp(False, False, False, '', '/foo', False, {'r', 'w'}, False, 'Px', 'bar', False, False, False, False )), - ('/foo CixCixrwCix -> bar, ' , exp(False, False, False, '', '/foo', False, {'r', 'w'}, False, 'Cix', 'bar', False, False, False, False )), + ('/foo PxPxPxPxrwPx -> bar,', exp(False, False, False, '', '/foo', False, {'r', 'w'}, False, 'Px', 'bar', False, False, False, False)), + ('/foo CixCixrwCix -> bar, ', exp(False, False, False, '', '/foo', False, {'r', 'w'}, False, 'Cix', 'bar', False, False, False, False)), # link rules - ('link /foo -> /bar,' , exp(False, False, False, '', '/foo', False, {'link'}, False, None, '/bar', False, False, False, True )), - ('link subset /foo -> /bar,' , exp(False, False, False, '', '/foo', False, {'link', 'subset'}, False, None, '/bar', False, False, False, True )), + ('link /foo -> /bar,', exp(False, False, False, '', '/foo', False, {'link'}, False, None, '/bar', False, False, False, True)), + ('link subset /foo -> /bar,', exp(False, False, False, '', '/foo', False, {'link', 'subset'}, False, None, '/bar', False, False, False, True)), ) def _run_test(self, rawrule, expected): @@ -104,19 +108,20 @@ class FileTestParse(FileTest): self.assertEqual(rawrule.strip(), obj.raw_rule) self._compare_obj(obj, expected) + class FileTestParseInvalid(FileTest): tests = ( - ('/foo x,' , AppArmorException), # should be *x - ('/foo raw,' , AppArmorException), # r and a conflict - ('deny /foo ix,' , AppArmorException), # endy only allows x, but not *x - ('deny /foo Px,' , AppArmorException), # deny only allows x, but not *x - ('deny /foo Pi,' , AppArmorException), # missing 'x', and P not allowed - ('allow /foo x,' , AppArmorException), # should be *x - ('/foo Pxrix,' , AppArmorException), # exec mode conflict - ('/foo PixUx,' , AppArmorException), # exec mode conflict - ('/foo PxUx,' , AppArmorException), # exec mode conflict - ('/foo PUxPix,' , AppArmorException), # exec mode conflict - ('/foo Pi,' , AppArmorException), # missing 'x' + ('/foo x,', AppArmorException), # should be *x + ('/foo raw,', AppArmorException), # r and a conflict + ('deny /foo ix,', AppArmorException), # endy only allows x, but not *x + ('deny /foo Px,', AppArmorException), # deny only allows x, but not *x + ('deny /foo Pi,', AppArmorException), # missing 'x', and P not allowed + ('allow /foo x,', AppArmorException), # should be *x + ('/foo Pxrix,', AppArmorException), # exec mode conflict + ('/foo PixUx,', AppArmorException), # exec mode conflict + ('/foo PxUx,', AppArmorException), # exec mode conflict + ('/foo PUxPix,', AppArmorException), # exec mode conflict + ('/foo Pi,', AppArmorException), # missing 'x' ) def _run_test(self, rawrule, expected): @@ -124,23 +129,25 @@ class FileTestParseInvalid(FileTest): with self.assertRaises(expected): FileRule.parse(rawrule) + class FileTestNonMatch(AATest): tests = ( - ('file /foo,' , False ), - ('file rw,' , False ), - ('file -> bar,' , False ), - ('file Px -> bar,' , False ), - ('/foo bar,' , False ), - ('dbus /foo,' , False ), - ('link /foo,' , False ), # missing '-> /target' - ('link -> /bar,' , False ), # missing path - ('/foo -> bar link,', False ), # link has to be leading keyword - ('link,' , False ), # link isn't available as bare keyword + ('file /foo,', False), + ('file rw,', False), + ('file -> bar,', False), + ('file Px -> bar,', False), + ('/foo bar,', False), + ('dbus /foo,', False), + ('link /foo,', False), # missing '-> /target' + ('link -> /bar,', False), # missing path + ('/foo -> bar link,', False), # link has to be leading keyword + ('link,', False), # link isn't available as bare keyword ) def _run_test(self, rawrule, expected): self.assertFalse(FileRule.match(rawrule)) + class FileTestParseFromLog(FileTest): def test_file_from_log(self): parser = ReadLog('', '', '') @@ -171,98 +178,100 @@ class FileTestParseFromLog(FileTest): 'sock_type': None, }) - #FileRule# path, perms, exec_perms, target, owner, file_keyword, leading_perms - #obj = FileRule(parsed_event['name'], parsed_event['denied_mask'], None, FileRule.ALL, False, False, False, ) - obj = FileRule(parsed_event['name'], 'r', None, FileRule.ALL, False, False, False, ) + # FileRule path, perms, exec_perms, target, owner, file_keyword, leading_perms + # obj = FileRule(parsed_event['name'], parsed_event['denied_mask'], None, FileRule.ALL, False, False, False,) + obj = FileRule(parsed_event['name'], 'r', None, FileRule.ALL, False, False, False,) # XXX handle things like '::r' # XXX split off exec perms? - # audit allow deny comment path all_paths perms all? exec_perms target all? owner file keyword leading perms - expected = exp(False, False, False, '', '/bin/dash', False, {'r'}, False, None, None, True, False, False, False ) + # audit allow deny comment path all_paths perms all? exec_perms target all? owner file keyword leading perms + expected = exp(False, False, False, '', '/bin/dash', False, {'r'}, False, None, None, True, False, False, False) self._compare_obj(obj, expected) self.assertEqual(obj.get_raw(1), ' /bin/dash r,') + # TODO: add logparser example for link event class FileFromInit(FileTest): tests = ( - #FileRule# path, perms, exec_perms, target, owner, file_keyword, leading_perms - (FileRule( '/foo', 'rw', None, FileRule.ALL, False, False, False, audit=True, deny=True ), - #exp# audit allow deny comment path all_paths perms all? exec_perms target all? owner file keyword leading perms - exp( True, False, True, '', '/foo', False, {'r', 'w'}, False, None, None, True, False, False, False )), + # path, perms, exec_perms, target, owner, file_keyword, leading_perms + (FileRule('/foo', 'rw', None, FileRule.ALL, False, False, False, audit=True, deny=True), + # audit allow deny comment path all_paths perms all? exec_perms target all? owner file keyword leading perms + exp(True, False, True, '', '/foo', False, {'r', 'w'}, False, None, None, True, False, False, False)), - #FileRule# path, perms, exec_perms, target, owner, file_keyword, leading_perms - (FileRule( '/foo', None, 'Pix', 'bar_prof', True, True, True, allow_keyword=True ), - #exp# audit allow deny comment path all_paths perms all? exec_perms target all? owner file keyword leading perms - exp( False, True, False, '', '/foo', False, set(), False, 'Pix', 'bar_prof', False, True, True, True )), + # path, perms, exec_perms, target, owner, file_keyword, leading_perms + (FileRule('/foo', None, 'Pix', 'bar_prof', True, True, True, allow_keyword=True), + # audit allow deny comment path all_paths perms all? exec_perms target all? owner file keyword leading perms + exp(False, True, False, '', '/foo', False, set(), False, 'Pix', 'bar_prof', False, True, True, True)), - #FileRule# path, perms, exec_perms, target, owner, file_keyword, leading_perms - (FileRule( '/foo', {'link', 'subset'}, None, '/bar', False, False, True, audit=True, deny=True ), - #exp# audit allow deny comment path all_paths perms all? exec_perms target all? owner file keyword leading perms - exp( True, False, True, '', '/foo', False, {'link', 'subset'}, False, None, '/bar', False, False, False, True )), + # path, perms, exec_perms, target, owner, file_keyword, leading_perms + (FileRule('/foo', {'link', 'subset'}, None, '/bar', False, False, True, audit=True, deny=True), + # audit allow deny comment path all_paths perms all? exec_perms target all? owner file keyword leading perms + exp(True, False, True, '', '/foo', False, {'link', 'subset'}, False, None, '/bar', False, False, False, True)), ) def _run_test(self, obj, expected): self._compare_obj(obj, expected) + class InvalidFileInit(AATest): tests = ( - #FileRule# path, perms, exec_perms, target, owner, file_keyword, leading_perms + # path, perms, exec_perms, target, owner, file_keyword, leading_perms expected exception # empty fields - ( ( '', 'rw', 'ix', '/bar', False, False, False ), AppArmorBug), - # OK ( '/foo', '', 'ix', '/bar', False, False, False ), AppArmorBug), - ( ( '/foo', 'rw', '', '/bar', False, False, False ), AppArmorBug), - ( ( '/foo', 'rw', 'ix', '', False, False, False ), AppArmorBug), + ( ('', 'rw', 'ix', '/bar', False, False, False), AppArmorBug), + # OK ('/foo', '', 'ix', '/bar', False, False, False), AppArmorBug), + ( ('/foo', 'rw', '', '/bar', False, False, False), AppArmorBug), + ( ('/foo', 'rw', 'ix', '', False, False, False), AppArmorBug), # whitespace fields - ( ( ' ', 'rw', 'ix', '/bar', False, False, False ), AppArmorBug), - ( ( '/foo', ' ', 'ix', '/bar', False, False, False ), AppArmorException), - ( ( '/foo', 'rw', ' ', '/bar', False, False, False ), AppArmorBug), - ( ( '/foo', 'rw', 'ix', ' ', False, False, False ), AppArmorBug), + ( (' ', 'rw', 'ix', '/bar', False, False, False), AppArmorBug), + ( ('/foo', ' ', 'ix', '/bar', False, False, False), AppArmorException), + ( ('/foo', 'rw', ' ', '/bar', False, False, False), AppArmorBug), + ( ('/foo', 'rw', 'ix', ' ', False, False, False), AppArmorBug), # wrong type - dict() - ( ( dict(), 'rw', 'ix', '/bar', False, False, False ), AppArmorBug), - ( ( '/foo', dict(), 'ix', '/bar', False, False, False ), AppArmorBug), - ( ( '/foo', 'rw', dict(), '/bar', False, False, False ), AppArmorBug), - ( ( '/foo', 'rw', 'ix', dict(), False, False, False ), AppArmorBug), - ( ( '/foo', 'rw', 'ix', '/bar', dict(), False, False ), AppArmorBug), - ( ( '/foo', 'rw', 'ix', '/bar', False, dict(), False ), AppArmorBug), - ( ( '/foo', 'rw', 'ix', '/bar', False, False, dict() ), AppArmorBug), + ( (dict(), 'rw', 'ix', '/bar', False, False, False), AppArmorBug), + ( ('/foo', dict(), 'ix', '/bar', False, False, False), AppArmorBug), + ( ('/foo', 'rw', dict(), '/bar', False, False, False), AppArmorBug), + ( ('/foo', 'rw', 'ix', dict(), False, False, False), AppArmorBug), + ( ('/foo', 'rw', 'ix', '/bar', dict(), False, False), AppArmorBug), + ( ('/foo', 'rw', 'ix', '/bar', False, dict(), False), AppArmorBug), + ( ('/foo', 'rw', 'ix', '/bar', False, False, dict()), AppArmorBug), # wrong type - None - ( ( None, 'rw', 'ix', '/bar', False, False, False ), AppArmorBug), - # OK ( '/foo', None, 'ix', '/bar', False, False, False ), AppArmorBug), - # OK ( '/foo', 'rw', None, '/bar', False, False, False ), AppArmorBug), - ( ( '/foo', 'rw', 'ix', None, False, False, False ), AppArmorBug), - ( ( '/foo', 'rw', 'ix', '/bar', None, False, False ), AppArmorBug), - ( ( '/foo', 'rw', 'ix', '/bar', False, None, False ), AppArmorBug), - ( ( '/foo', 'rw', 'ix', '/bar', False, False, None ), AppArmorBug), + ( (None, 'rw', 'ix', '/bar', False, False, False), AppArmorBug), + # OK ('/foo', None, 'ix', '/bar', False, False, False), AppArmorBug), + # OK ('/foo', 'rw', None, '/bar', False, False, False), AppArmorBug), + ( ('/foo', 'rw', 'ix', None, False, False, False), AppArmorBug), + ( ('/foo', 'rw', 'ix', '/bar', None, False, False), AppArmorBug), + ( ('/foo', 'rw', 'ix', '/bar', False, None, False), AppArmorBug), + ( ('/foo', 'rw', 'ix', '/bar', False, False, None), AppArmorBug), # misc - ( ( '/foo', 'rwa', 'ix', '/bar', False, False, False ), AppArmorException), # 'r' and 'a' conflict - ( ( '/foo', None, 'rw', '/bar', False, False, False ), AppArmorBug), # file perms in exec perms parameter - ( ( '/foo', 'ix', None, '/bar', False, False, False ), AppArmorBug), # exec perms in file perms parameter - ( ( 'foo', 'rw', 'ix', '/bar', False, False, False ), AppArmorException), # path doesn't start with / - ( ( '/foo', 'rb', 'ix', '/bar', False, False, False ), AppArmorException), # invalid file mode 'b' (str) - ( ( '/foo', {'b'}, 'ix', '/bar', False, False, False ), AppArmorBug), # invalid file mode 'b' (str) - ( ( '/foo', 'rw', 'ax', '/bar', False, False, False ), AppArmorBug), # invalid exec mode 'ax' - ( ( '/foo', 'rw', 'x', '/bar', False, False, False ), AppArmorException), # plain 'x' is only allowed in deny rules - ( ( FileRule.ALL, FileRule.ALL, None, '/bar', False, False, False ), AppArmorBug), # plain 'file,' doesn't allow exec target + ( ('/foo', 'rwa', 'ix', '/bar', False, False, False), AppArmorException), # 'r' and 'a' conflict + ( ('/foo', None, 'rw', '/bar', False, False, False), AppArmorBug), # file perms in exec perms parameter + ( ('/foo', 'ix', None, '/bar', False, False, False), AppArmorBug), # exec perms in file perms parameter + ( ('foo', 'rw', 'ix', '/bar', False, False, False), AppArmorException), # path doesn't start with / + ( ('/foo', 'rb', 'ix', '/bar', False, False, False), AppArmorException), # invalid file mode 'b' (str) + ( ('/foo', {'b'}, 'ix', '/bar', False, False, False), AppArmorBug), # invalid file mode 'b' (str) + ( ('/foo', 'rw', 'ax', '/bar', False, False, False), AppArmorBug), # invalid exec mode 'ax' + ( ('/foo', 'rw', 'x', '/bar', False, False, False), AppArmorException), # plain 'x' is only allowed in deny rules + ( (FileRule.ALL, FileRule.ALL, None, '/bar', False, False, False), AppArmorBug), # plain 'file,' doesn't allow exec target # link rules - ( ( None, {'link'}, None, None, False, False, False, ), AppArmorBug), # missing path and target - ( ( '/foo', {'link'}, None, None, False, False, False, ), AppArmorBug), # missing target - ( ( None, {'link'}, None, '/bar', False, False, False, ), AppArmorBug), # missing path - ( ( '/foo', {'subset'}, None, '/bar', False, False, False, ), AppArmorBug), # subset without link - ( ( '/foo', {'link'}, 'ix', '/bar', False, False, False, ), AppArmorBug), # link rule with exec perms - ( ( '/foo', {'link', 'subset'}, 'ix', '/bar', False, False, False, ), AppArmorBug), # link subset rule with exec perms + ( (None, {'link'}, None, None, False, False, False), AppArmorBug), # missing path and target + ( ('/foo', {'link'}, None, None, False, False, False), AppArmorBug), # missing target + ( ( None, {'link'}, None, '/bar', False, False, False), AppArmorBug), # missing path + ( ('/foo', {'subset'}, None, '/bar', False, False, False), AppArmorBug), # subset without link + ( ('/foo', {'link'}, 'ix', '/bar', False, False, False), AppArmorBug), # link rule with exec perms + ( ('/foo', {'link', 'subset'}, 'ix', '/bar', False, False, False), AppArmorBug), # link subset rule with exec perms ) def _run_test(self, params, expected): @@ -271,23 +280,24 @@ class InvalidFileInit(AATest): def test_missing_params_1(self): with self.assertRaises(TypeError): - FileRule( '/foo') + FileRule('/foo') def test_missing_params_2(self): with self.assertRaises(TypeError): - FileRule( '/foo', 'rw') + FileRule('/foo', 'rw') def test_missing_params_3(self): with self.assertRaises(TypeError): - FileRule( '/foo', 'rw', 'ix') + FileRule('/foo', 'rw', 'ix') def test_missing_params_4(self): with self.assertRaises(TypeError): - FileRule( '/foo', 'rw', 'ix', '/bar') + FileRule('/foo', 'rw', 'ix', '/bar') def test_deny_ix(self): with self.assertRaises(AppArmorException): - FileRule( '/foo', 'rw', 'ix', '/bar', False, False, False, deny=True) + FileRule('/foo', 'rw', 'ix', '/bar', False, False, False, deny=True) + class InvalidFileTest(AATest): def _check_invalid_rawrule(self, rawrule): @@ -304,10 +314,11 @@ class InvalidFileTest(AATest): def test_invalid_non_FileRule(self): self._check_invalid_rawrule('signal,') # not a file rule + class BrokenFileTest(AATest): def AASetup(self): - #FileRule# path, perms, exec_perms, target, owner, file_keyword, leading_perms - self.obj = FileRule('/foo', 'rw', 'ix', '/bar', False, False, False) + # path, perms, exec_perms, target, owner, file_keyword, leading_perms + self.obj = FileRule('/foo', 'rw', 'ix', '/bar', False, False, False) def test_empty_data_1(self): self.obj.path = '' @@ -340,6 +351,7 @@ class BrokenFileTest(AATest): with self.assertRaises(AppArmorBug): self.obj.get_clean(1) + class FileGlobTest(AATest): def _run_test(self, params, expected): exp_can_glob, exp_can_glob_ext, exp_rule_glob, exp_rule_glob_ext = expected @@ -363,85 +375,87 @@ class FileGlobTest(AATest): # These tests are meant to ensure AARE integration in FileRule works as expected. # test-aare.py has more comprehensive globbing tests. tests = ( - # rule can glob can glob_ext globbed rule globbed_ext rule - ('/foo/bar r,', (True, True, '/foo/* r,', '/foo/bar r,')), - ('/foo/* r,', (True, True, '/** r,', '/foo/* r,')), - ('/foo/bar.xy r,', (True, True, '/foo/* r,', '/foo/*.xy r,')), - ('/foo/*.xy r,', (True, True, '/foo/* r,', '/**.xy r,')), - ('file,', (False, False, 'file,', 'file,')), # bare 'file,' rules can't be globbed - ('link /a/b -> /c,', (True, True, 'link /a/* -> /c,', 'link /a/b -> /c,')), + # rule can glob can glob_ext globbed rule globbed_ext rule + ('/foo/bar r,', (True, True, '/foo/* r,', '/foo/bar r,')), + ('/foo/* r,', (True, True, '/** r,', '/foo/* r,')), + ('/foo/bar.xy r,', (True, True, '/foo/* r,', '/foo/*.xy r,')), + ('/foo/*.xy r,', (True, True, '/foo/* r,', '/**.xy r,')), + ('file,', (False, False, 'file,', 'file,')), # bare 'file,' rules can't be globbed + ('link /a/b -> /c,', (True, True, 'link /a/* -> /c,', 'link /a/b -> /c,')), ) + class WriteFileTest(AATest): def _run_test(self, rawrule, expected): - self.assertTrue(FileRule.match(rawrule), 'FileRule.match() failed') - obj = FileRule.parse(rawrule) - clean = obj.get_clean() - raw = obj.get_raw() + self.assertTrue(FileRule.match(rawrule), 'FileRule.match() failed') + obj = FileRule.parse(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') + self.assertEqual(expected.strip(), clean, 'unexpected clean rule') + self.assertEqual(rawrule.strip(), raw, 'unexpected raw rule') tests = ( - # raw rule clean rule - ('file,' , 'file,'), - (' file , # foo ' , 'file, # foo'), - (' audit file /foo r,' , 'audit file /foo r,'), - (' audit file /foo lwr,' , 'audit file /foo rwl,'), - (' audit file /foo Pxrm -> bar,' , 'audit file /foo mrPx -> bar,'), - (' deny file /foo r,' , 'deny file /foo r,'), - (' deny file /foo wr,' , 'deny file /foo rw,'), - (' allow file /foo Pxrm -> bar,' , 'allow file /foo mrPx -> bar,'), - (' deny owner /foo r,' , 'deny owner /foo r,'), - (' deny owner /foo wr,' , 'deny owner /foo rw,'), - (' allow owner /foo Pxrm -> bar,' , 'allow owner /foo mrPx -> bar,'), - (' /foo r,' , '/foo r,'), - (' /foo lwr,' , '/foo rwl,'), - (' /foo Pxrm -> bar,' , '/foo mrPx -> bar,'), + # raw rule clean rule + ('file,', 'file,'), + (' file , # foo ', 'file, # foo'), + (' audit file /foo r,', 'audit file /foo r,'), + (' audit file /foo lwr,', 'audit file /foo rwl,'), + (' audit file /foo Pxrm -> bar,', 'audit file /foo mrPx -> bar,'), + (' deny file /foo r,', 'deny file /foo r,'), + (' deny file /foo wr,', 'deny file /foo rw,'), + (' allow file /foo Pxrm -> bar,', 'allow file /foo mrPx -> bar,'), + (' deny owner /foo r,', 'deny owner /foo r,'), + (' deny owner /foo wr,', 'deny owner /foo rw,'), + (' allow owner /foo Pxrm -> bar,', 'allow owner /foo mrPx -> bar,'), + (' /foo r,', '/foo r,'), + (' /foo lwr,', '/foo rwl,'), + (' /foo Pxrm -> bar,', '/foo mrPx -> bar,'), # with leading permissions - (' audit file r /foo,' , 'audit file r /foo,'), - (' audit file lwr /foo,' , 'audit file rwl /foo,'), - (' audit file Pxrm /foo -> bar,' , 'audit file mrPx /foo -> bar,'), - (' deny file r /foo,' , 'deny file r /foo,'), - (' deny file wr /foo ,' , 'deny file rw /foo,'), - (' allow file Pxmr /foo -> bar,' , 'allow file mrPx /foo -> bar,'), - (' deny owner r /foo ,' , 'deny owner r /foo,'), - (' deny owner wr /foo ,' , 'deny owner rw /foo,'), - (' allow owner Pxrm /foo -> bar,' , 'allow owner mrPx /foo -> bar,'), - (' r /foo ,' , 'r /foo,'), - (' klwr /foo ,' , 'rwlk /foo,'), - (' Pxrm /foo -> bar,' , 'mrPx /foo -> bar,'), + (' audit file r /foo,', 'audit file r /foo,'), + (' audit file lwr /foo,', 'audit file rwl /foo,'), + (' audit file Pxrm /foo -> bar,', 'audit file mrPx /foo -> bar,'), + (' deny file r /foo,', 'deny file r /foo,'), + (' deny file wr /foo ,', 'deny file rw /foo,'), + (' allow file Pxmr /foo -> bar,', 'allow file mrPx /foo -> bar,'), + (' deny owner r /foo ,', 'deny owner r /foo,'), + (' deny owner wr /foo ,', 'deny owner rw /foo,'), + (' allow owner Pxrm /foo -> bar,', 'allow owner mrPx /foo -> bar,'), + (' r /foo ,', 'r /foo,'), + (' klwr /foo ,', 'rwlk /foo,'), + (' Pxrm /foo -> bar,', 'mrPx /foo -> bar,'), # link rules - (' link /foo -> /bar,' , 'link /foo -> /bar,'), - (' audit deny owner link subset /foo -> /bar,' , 'audit deny owner link subset /foo -> /bar,'), - (' link subset /foo -> /bar,' , 'link subset /foo -> /bar,') + (' link /foo -> /bar,', 'link /foo -> /bar,'), + (' audit deny owner link subset /foo -> /bar,', 'audit deny owner link subset /foo -> /bar,'), + (' link subset /foo -> /bar,', 'link subset /foo -> /bar,') ) def test_write_manually_1(self): - #FileRule# path, perms, exec_perms, target, owner, file_keyword, leading_perms - obj = FileRule( '/foo', 'rw', 'Px', '/bar', False, True, False, allow_keyword=True) + # path, perms, exec_perms, target, owner, file_keyword, leading_perms + obj = FileRule('/foo', 'rw', 'Px', '/bar', False, True, False, allow_keyword=True) - expected = ' allow file /foo rwPx -> /bar,' + expected = ' allow file /foo rwPx -> /bar,' - self.assertEqual(expected, obj.get_clean(2), 'unexpected clean rule') - self.assertEqual(expected, obj.get_raw(2), 'unexpected raw rule') + self.assertEqual(expected, obj.get_clean(2), 'unexpected clean rule') + self.assertEqual(expected, obj.get_raw(2), 'unexpected raw rule') def test_write_manually_2(self): - #FileRule# path, perms, exec_perms, target, owner, file_keyword, leading_perms - obj = FileRule( '/foo', 'rw', 'x', FileRule.ALL, True, False, True, deny=True) + # path, perms, exec_perms, target, owner, file_keyword, leading_perms + obj = FileRule('/foo', 'rw', 'x', FileRule.ALL, True, False, True, deny=True) - expected = ' deny owner rwx /foo,' + expected = ' deny owner rwx /foo,' - self.assertEqual(expected, obj.get_clean(2), 'unexpected clean rule') - self.assertEqual(expected, obj.get_raw(2), 'unexpected raw rule') + self.assertEqual(expected, obj.get_clean(2), 'unexpected clean rule') + self.assertEqual(expected, obj.get_raw(2), 'unexpected raw rule') def test_write_any_exec(self): - obj = FileRule( '/foo', 'rw', FileRule.ANY_EXEC,'/bar', False, False, False) + obj = FileRule('/foo', 'rw', FileRule.ANY_EXEC, '/bar', False, False, False) with self.assertRaises(AppArmorBug): obj.get_clean() + class FileCoveredTest(AATest): def _run_test(self, param, expected): obj = FileRule.parse(self.rule) @@ -455,230 +469,239 @@ class FileCoveredTest(AATest): self.assertEqual(obj.is_covered(check_obj), expected[2], 'Mismatch in is_covered, expected %s' % expected[2]) self.assertEqual(obj.is_covered(check_obj, True, True), expected[3], 'Mismatch in is_covered/exact, expected %s' % expected[3]) + class FileCoveredTest_01(FileCoveredTest): rule = 'file /foo r,' +tests = ( + # rule equal strict equal covered covered exact + ('file /foo r,', (True, True, True, True)), + ('file /foo r ,', (True, False, True, True)), + ('allow file /foo r,', (True, False, True, True)), + ('allow /foo r, # comment', (True, False, True, True)), + ('allow owner /foo r,', (False, False, True, True)), + ('/foo r -> bar,', (False, False, True, True)), + ('file r /foo,', (True, False, True, True)), + ('allow file r /foo,', (True, False, True, True)), + ('allow r /foo, # comment', (True, False, True, True)), + ('allow owner r /foo,', (False, False, True, True)), + ('r /foo -> bar,', (False, False, True, True)), + ('file,', (False, False, False, False)), + ('file /foo w,', (False, False, False, False)), + ('file /foo rw,', (False, False, False, False)), + ('file /bar r,', (False, False, False, False)), + ('audit /foo r,', (False, False, False, False)), + ('audit file,', (False, False, False, False)), + ('audit deny /foo r,', (False, False, False, False)), + ('deny file /foo r,', (False, False, False, False)), + ('/foo rPx,', (False, False, False, False)), + ('/foo Pxr,', (False, False, False, False)), + ('/foo Px,', (False, False, False, False)), + ('/foo ix,', (False, False, False, False)), + ('/foo ix -> bar,', (False, False, False, False)), + ('/foo rPx -> bar,', (False, False, False, False)), +) - tests = ( - # rule equal strict equal covered covered exact - ('file /foo r,' , ( True , True , True , True )), - ('file /foo r ,' , ( True , False , True , True )), - ('allow file /foo r,' , ( True , False , True , True )), - ('allow /foo r, # comment' , ( True , False , True , True )), - ('allow owner /foo r,' , ( False , False , True , True )), - ('/foo r -> bar,' , ( False , False , True , True )), - ('file r /foo,' , ( True , False , True , True )), - ('allow file r /foo,' , ( True , False , True , True )), - ('allow r /foo, # comment' , ( True , False , True , True )), - ('allow owner r /foo,' , ( False , False , True , True )), - ('r /foo -> bar,' , ( False , False , True , True )), - ('file,' , ( False , False , False , False )), - ('file /foo w,' , ( False , False , False , False )), - ('file /foo rw,' , ( False , False , False , False )), - ('file /bar r,' , ( False , False , False , False )), - ('audit /foo r,' , ( False , False , False , False )), - ('audit file,' , ( False , False , False , False )), - ('audit deny /foo r,' , ( False , False , False , False )), - ('deny file /foo r,' , ( False , False , False , False )), - ('/foo rPx,' , ( False , False , False , False )), - ('/foo Pxr,' , ( False , False , False , False )), - ('/foo Px,' , ( False , False , False , False )), - ('/foo ix,' , ( False , False , False , False )), - ('/foo ix -> bar,' , ( False , False , False , False )), - ('/foo rPx -> bar,' , ( False , False , False , False )), - ) class FileCoveredTest_02(FileCoveredTest): rule = 'audit /foo r,' tests = ( - # rule equal strict equal covered covered exact - ('file /foo r,' , ( False , False , True , False )), - ('allow file /foo r,' , ( False , False , True , False )), - ('allow /foo r, # comment' , ( False , False , True , False )), - ('allow owner /foo r,' , ( False , False , True , False )), - ('/foo r -> bar,' , ( False , False , True , False )), - ('file r /foo,' , ( False , False , True , False )), - ('allow file r /foo,' , ( False , False , True , False )), - ('allow r /foo, # comment' , ( False , False , True , False )), - ('allow owner r /foo,' , ( False , False , True , False )), - ('r /foo -> bar,' , ( False , False , True , False )), # XXX exact - ('file,' , ( False , False , False , False )), - ('file /foo w,' , ( False , False , False , False )), - ('file /foo rw,' , ( False , False , False , False )), - ('file /bar r,' , ( False , False , False , False )), - ('audit /foo r,' , ( True , True , True , True )), - ('audit file,' , ( False , False , False , False )), - ('audit deny /foo r,' , ( False , False , False , False )), - ('deny file /foo r,' , ( False , False , False , False )), - ('/foo rPx,' , ( False , False , False , False )), - ('/foo Pxr,' , ( False , False , False , False )), - ('/foo Px,' , ( False , False , False , False )), - ('/foo ix,' , ( False , False , False , False )), - ('/foo ix -> bar,' , ( False , False , False , False )), - ('/foo rPx -> bar,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('file /foo r,', (False, False, True, False)), + ('allow file /foo r,', (False, False, True, False)), + ('allow /foo r, # comment', (False, False, True, False)), + ('allow owner /foo r,', (False, False, True, False)), + ('/foo r -> bar,', (False, False, True, False)), + ('file r /foo,', (False, False, True, False)), + ('allow file r /foo,', (False, False, True, False)), + ('allow r /foo, # comment', (False, False, True, False)), + ('allow owner r /foo,', (False, False, True, False)), + ('r /foo -> bar,', (False, False, True, False)), # XXX exact + ('file,', (False, False, False, False)), + ('file /foo w,', (False, False, False, False)), + ('file /foo rw,', (False, False, False, False)), + ('file /bar r,', (False, False, False, False)), + ('audit /foo r,', (True, True, True, True)), + ('audit file,', (False, False, False, False)), + ('audit deny /foo r,', (False, False, False, False)), + ('deny file /foo r,', (False, False, False, False)), + ('/foo rPx,', (False, False, False, False)), + ('/foo Pxr,', (False, False, False, False)), + ('/foo Px,', (False, False, False, False)), + ('/foo ix,', (False, False, False, False)), + ('/foo ix -> bar,', (False, False, False, False)), + ('/foo rPx -> bar,', (False, False, False, False)), ) + class FileCoveredTest_03(FileCoveredTest): rule = '/foo mrwPx,' tests = ( - # rule equal strict equal covered covered exact - ('file /foo r,' , ( False , False , True , True )), - ('allow file /foo r,' , ( False , False , True , True )), - ('allow /foo r, # comment' , ( False , False , True , True )), - ('allow owner /foo r,' , ( False , False , True , True )), - ('/foo r -> bar,' , ( False , False , True , True )), - ('file r /foo,' , ( False , False , True , True )), - ('allow file r /foo,' , ( False , False , True , True )), - ('allow r /foo, # comment' , ( False , False , True , True )), - ('allow owner r /foo,' , ( False , False , True , True )), - ('r /foo -> bar,' , ( False , False , True , True )), - ('file,' , ( False , False , False , False )), - ('file /foo w,' , ( False , False , True , True )), - ('file /foo rw,' , ( False , False , True , True )), - ('file /bar r,' , ( False , False , False , False )), - ('audit /foo r,' , ( False , False , False , False )), - ('audit file,' , ( False , False , False , False )), - ('audit deny /foo r,' , ( False , False , False , False )), - ('deny file /foo r,' , ( False , False , False , False )), - ('/foo mrwPx,' , ( True , True , True , True )), - ('/foo wPxrm,' , ( True , False , True , True )), - ('/foo rm,' , ( False , False , True , True )), - ('/foo Px,' , ( False , False , True , True )), - ('/foo ix,' , ( False , False , False , False )), - ('/foo ix -> bar,' , ( False , False , False , False )), - ('/foo mrwPx -> bar,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('file /foo r,', (False, False, True, True)), + ('allow file /foo r,', (False, False, True, True)), + ('allow /foo r, # comment', (False, False, True, True)), + ('allow owner /foo r,', (False, False, True, True)), + ('/foo r -> bar,', (False, False, True, True)), + ('file r /foo,', (False, False, True, True)), + ('allow file r /foo,', (False, False, True, True)), + ('allow r /foo, # comment', (False, False, True, True)), + ('allow owner r /foo,', (False, False, True, True)), + ('r /foo -> bar,', (False, False, True, True)), + ('file,', (False, False, False, False)), + ('file /foo w,', (False, False, True, True)), + ('file /foo rw,', (False, False, True, True)), + ('file /bar r,', (False, False, False, False)), + ('audit /foo r,', (False, False, False, False)), + ('audit file,', (False, False, False, False)), + ('audit deny /foo r,', (False, False, False, False)), + ('deny file /foo r,', (False, False, False, False)), + ('/foo mrwPx,', (True, True, True, True)), + ('/foo wPxrm,', (True, False, True, True)), + ('/foo rm,', (False, False, True, True)), + ('/foo Px,', (False, False, True, True)), + ('/foo ix,', (False, False, False, False)), + ('/foo ix -> bar,', (False, False, False, False)), + ('/foo mrwPx -> bar,', (False, False, False, False)), ) + class FileCoveredTest_04(FileCoveredTest): rule = '/foo mrwPx -> bar,' tests = ( - # rule equal strict equal covered covered exact - ('file /foo r,' , ( False , False , True , True )), - ('allow file /foo r,' , ( False , False , True , True )), - ('allow /foo r, # comment' , ( False , False , True , True )), - ('allow owner /foo r,' , ( False , False , True , True )), - ('/foo r -> bar,' , ( False , False , True , True )), - ('file r /foo,' , ( False , False , True , True )), - ('allow file r /foo,' , ( False , False , True , True )), - ('allow r /foo, # comment' , ( False , False , True , True )), - ('allow owner r /foo,' , ( False , False , True , True )), - ('r /foo -> bar,' , ( False , False , True , True )), - ('file,' , ( False , False , False , False )), - ('file /foo w,' , ( False , False , True , True )), - ('file /foo rw,' , ( False , False , True , True )), - ('file /bar r,' , ( False , False , False , False )), - ('audit /foo r,' , ( False , False , False , False )), - ('audit file,' , ( False , False , False , False )), - ('audit deny /foo r,' , ( False , False , False , False )), - ('deny file /foo r,' , ( False , False , False , False )), - ('/foo mrwPx,' , ( False , False , False , False )), - ('/foo wPxrm,' , ( False , False , False , False )), - ('/foo rm,' , ( False , False , True , True )), - ('/foo Px,' , ( False , False , False , False )), - ('/foo ix,' , ( False , False , False , False )), - ('/foo ix -> bar,' , ( False , False , False , False )), - ('/foo mrwPx -> bar,' , ( True , True , True , True )), + # rule equal strict equal covered covered exact + ('file /foo r,', (False, False, True, True)), + ('allow file /foo r,', (False, False, True, True)), + ('allow /foo r, # comment', (False, False, True, True)), + ('allow owner /foo r,', (False, False, True, True)), + ('/foo r -> bar,', (False, False, True, True)), + ('file r /foo,', (False, False, True, True)), + ('allow file r /foo,', (False, False, True, True)), + ('allow r /foo, # comment', (False, False, True, True)), + ('allow owner r /foo,', (False, False, True, True)), + ('r /foo -> bar,', (False, False, True, True)), + ('file,', (False, False, False, False)), + ('file /foo w,', (False, False, True, True)), + ('file /foo rw,', (False, False, True, True)), + ('file /bar r,', (False, False, False, False)), + ('audit /foo r,', (False, False, False, False)), + ('audit file,', (False, False, False, False)), + ('audit deny /foo r,', (False, False, False, False)), + ('deny file /foo r,', (False, False, False, False)), + ('/foo mrwPx,', (False, False, False, False)), + ('/foo wPxrm,', (False, False, False, False)), + ('/foo rm,', (False, False, True, True)), + ('/foo Px,', (False, False, False, False)), + ('/foo ix,', (False, False, False, False)), + ('/foo ix -> bar,', (False, False, False, False)), + ('/foo mrwPx -> bar,', (True, True, True, True)), ) + class FileCoveredTest_05(FileCoveredTest): rule = 'file,' tests = ( - # rule equal strict equal covered covered exact - ('file /foo r,' , ( False , False , True , True )), - ('allow file /foo r,' , ( False , False , True , True )), - ('allow /foo r, # comment' , ( False , False , True , True )), - ('allow owner /foo r,' , ( False , False , True , True )), - ('/foo r -> bar,' , ( False , False , True , True )), - ('file r /foo,' , ( False , False , True , True )), - ('allow file r /foo,' , ( False , False , True , True )), - ('allow r /foo, # comment' , ( False , False , True , True )), - ('allow owner r /foo,' , ( False , False , True , True )), - ('r /foo -> bar,' , ( False , False , True , True )), - ('file,' , ( True , True , True , True )), - ('file /foo w,' , ( False , False , True , True )), - ('file /foo rw,' , ( False , False , True , True )), - ('file /bar r,' , ( False , False , True , True )), - ('audit /foo r,' , ( False , False , False , False )), - ('audit file,' , ( False , False , False , False )), - ('audit deny /foo r,' , ( False , False , False , False )), - ('deny file /foo r,' , ( False , False , False , False )), - ('/foo mrwPx,' , ( False , False , False , False )), - ('/foo wPxrm,' , ( False , False , False , False )), - ('/foo rm,' , ( False , False , True , True )), - ('/foo Px,' , ( False , False , False , False )), - ('/foo ix,' , ( False , False , False , False )), - ('/foo ix -> bar,' , ( False , False , False , False )), - ('/foo mrwPx -> bar,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('file /foo r,', (False, False, True, True)), + ('allow file /foo r,', (False, False, True, True)), + ('allow /foo r, # comment', (False, False, True, True)), + ('allow owner /foo r,', (False, False, True, True)), + ('/foo r -> bar,', (False, False, True, True)), + ('file r /foo,', (False, False, True, True)), + ('allow file r /foo,', (False, False, True, True)), + ('allow r /foo, # comment', (False, False, True, True)), + ('allow owner r /foo,', (False, False, True, True)), + ('r /foo -> bar,', (False, False, True, True)), + ('file,', (True, True, True, True)), + ('file /foo w,', (False, False, True, True)), + ('file /foo rw,', (False, False, True, True)), + ('file /bar r,', (False, False, True, True)), + ('audit /foo r,', (False, False, False, False)), + ('audit file,', (False, False, False, False)), + ('audit deny /foo r,', (False, False, False, False)), + ('deny file /foo r,', (False, False, False, False)), + ('/foo mrwPx,', (False, False, False, False)), + ('/foo wPxrm,', (False, False, False, False)), + ('/foo rm,', (False, False, True, True)), + ('/foo Px,', (False, False, False, False)), + ('/foo ix,', (False, False, False, False)), + ('/foo ix -> bar,', (False, False, False, False)), + ('/foo mrwPx -> bar,', (False, False, False, False)), ) + class FileCoveredTest_06(FileCoveredTest): rule = 'deny /foo w,' tests = ( - # rule equal strict equal covered covered exact - ('/foo w,' , ( False , False , False , False )), - ('/foo a,' , ( False , False , False , False )), - ('deny /foo w,' , ( True , True , True , True )), - ('deny /foo a,' , ( False , False , True , True )), + # rule equal strict equal covered covered exact + ('/foo w,', (False, False, False, False)), + ('/foo a,', (False, False, False, False)), + ('deny /foo w,', (True, True, True, True)), + ('deny /foo a,', (False, False, True, True)), ) + class FileCoveredTest_07(FileCoveredTest): rule = '/foo w,' tests = ( - # rule equal strict equal covered covered exact - ('/foo w,' , ( True , True , True , True )), - ('/foo a,' , ( False , False , True , True )), - ('deny /foo w,' , ( False , False , False , False )), - ('deny /foo a,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('/foo w,', (True, True, True, True)), + ('/foo a,', (False, False, True, True)), + ('deny /foo w,', (False, False, False, False)), + ('deny /foo a,', (False, False, False, False)), ) + class FileCoveredTest_08(FileCoveredTest): rule = 'link /foo -> /bar,' tests = ( - # rule equal strict equal covered covered exact - ('link /foo -> /bar,' , ( True , True , True , True )), - ('link /asdf -> /bar,' , ( False , False , False , False )), - ('link /foo -> /asdf,' , ( False , False , False , False )), - ('deny link /foo -> /bar,' , ( False , False , False , False )), - ('deny link /foo -> /bar,' , ( False , False , False , False )), - ('link subset /foo -> /bar,' , ( False , False , True , True )), # subset makes the rule more strict - # ('/foo l -> /bar,' , ( ? , ? , ? , ? )), # TODO - # ('l /foo -> /bar,' , ( ? , ? , ? , ? )), # TODO + # rule equal strict equal covered covered exact + ('link /foo -> /bar,', (True, True, True, True)), + ('link /asdf -> /bar,', (False, False, False, False)), + ('link /foo -> /asdf,', (False, False, False, False)), + ('deny link /foo -> /bar,', (False, False, False, False)), + ('deny link /foo -> /bar,', (False, False, False, False)), + ('link subset /foo -> /bar,', (False, False, True, True)), # subset makes the rule more strict + # ('/foo l -> /bar,', (?, ?, ?, ?)), # TODO + # ('l /foo -> /bar,', (?, ?, ?, ?)), # TODO ) + class FileCoveredTest_09(FileCoveredTest): rule = 'link subset /foo -> /bar,' tests = ( - # rule equal strict equal covered covered exact - ('link subset /foo -> /bar,' , ( True , True , True , True )), - ('link subset /asdf -> /bar,' , ( False , False , False , False )), - ('link subset /foo -> /asdf,' , ( False , False , False , False )), - ('deny link subset /foo -> /bar,' , ( False , False , False , False )), - ('deny link subset /foo -> /bar,' , ( False , False , False , False )), - ('link /foo -> /bar,' , ( False , False , False , False )), # no subset means more permissions - # ('/foo l -> /bar,' , ( ? , ? , ? , ? )), # TODO - # ('l /foo -> /bar,' , ( ? , ? , ? , ? )), # TODO + # rule equal strict equal covered covered exact + ('link subset /foo -> /bar,', (True, True, True, True)), + ('link subset /asdf -> /bar,', (False, False, False, False)), + ('link subset /foo -> /asdf,', (False, False, False, False)), + ('deny link subset /foo -> /bar,', (False, False, False, False)), + ('deny link subset /foo -> /bar,', (False, False, False, False)), + ('link /foo -> /bar,', (False, False, False, False)), # no subset means more permissions + # ('/foo l -> /bar,', (?, ?, ?, ?)), # TODO + # ('l /foo -> /bar,', (?, ?, ?, ?)), # TODO ) + class FileCoveredTest_ManualOrInvalid(AATest): def AASetup(self): - #FileRule# path, perms, exec_perms, target, owner, file_keyword, leading_perms - self.obj = FileRule( '/foo', 'rw', 'ix', '/bar', False, False, False) - self.testobj = FileRule( '/foo', 'rw', 'ix', '/bar', False, False, False) + # path, perms, exec_perms, target, owner, file_keyword, leading_perms + self.obj = FileRule('/foo', 'rw', 'ix', '/bar', False, False, False) + self.testobj = FileRule('/foo', 'rw', 'ix', '/bar', False, False, False) def test_covered_owner_1(self): # testobj with 'owner' - self.testobj = FileRule( '/foo', 'rw', 'ix', '/bar', True, False, False) + self.testobj = FileRule('/foo', 'rw', 'ix', '/bar', True, False, False) self.assertTrue(self.obj.is_covered(self.testobj)) def test_covered_owner_2(self): # obj with 'owner' - self.obj = FileRule( '/foo', 'rw', 'ix', '/bar', True, False, False) + self.obj = FileRule('/foo', 'rw', 'ix', '/bar', True, False, False) self.assertFalse(self.obj.is_covered(self.testobj)) def test_equal_all_perms(self): @@ -687,60 +710,60 @@ class FileCoveredTest_ManualOrInvalid(AATest): def test_equal_file_keyword(self): # testobj with file_keyword - self.testobj = FileRule( '/foo', 'rw', 'ix', '/bar', False, True, False) + self.testobj = FileRule('/foo', 'rw', 'ix', '/bar', False, True, False) self.assertTrue(self.obj.is_equal(self.testobj, strict=False)) self.assertFalse(self.obj.is_equal(self.testobj, strict=True)) def test_equal_file_leading_perms(self): # testobj with leading_perms - self.testobj = FileRule( '/foo', 'rw', 'ix', '/bar', False, False, True) + self.testobj = FileRule('/foo', 'rw', 'ix', '/bar', False, False, True) self.assertTrue(self.obj.is_equal(self.testobj, strict=False)) self.assertFalse(self.obj.is_equal(self.testobj, strict=True)) def test_covered_anyperm_1(self): - self.obj = FileRule( '/foo', 'rw', None, '/bar', False, False, False) - self.testobj = FileRule( '/foo', 'rw', FileRule.ANY_EXEC, '/bar', False, False, False) + self.obj = FileRule('/foo', 'rw', None, '/bar', False, False, False) + self.testobj = FileRule('/foo', 'rw', FileRule.ANY_EXEC, '/bar', False, False, False) self.assertFalse(self.obj.is_covered(self.testobj)) self.assertFalse(self.obj.is_equal(self.testobj, strict=False)) self.assertFalse(self.obj.is_equal(self.testobj, strict=True)) def test_covered_anyperm_2(self): - self.testobj = FileRule( '/foo', 'rw', FileRule.ANY_EXEC,'/bar', False, False, False) + self.testobj = FileRule('/foo', 'rw', FileRule.ANY_EXEC, '/bar', False, False, False) self.assertTrue(self.obj.is_covered(self.testobj)) self.assertFalse(self.obj.is_equal(self.testobj, strict=False)) self.assertFalse(self.obj.is_equal(self.testobj, strict=True)) def test_covered_anyperm_3(self): # make sure a different exec target gets ignored with ANY_EXEC - self.testobj = FileRule( '/foo', 'rw', FileRule.ANY_EXEC, '/xyz', False, False, False) + self.testobj = FileRule('/foo', 'rw', FileRule.ANY_EXEC, '/xyz', False, False, False) self.assertTrue(self.obj.is_covered(self.testobj)) self.assertFalse(self.obj.is_equal(self.testobj, strict=False)) self.assertFalse(self.obj.is_equal(self.testobj, strict=True)) def test_covered_anyperm_4(self): # make sure a different exec target gets ignored with ANY_EXEC - self.testobj = FileRule( '/foo', 'rw', FileRule.ANY_EXEC, FileRule.ALL, False, False, False) + self.testobj = FileRule('/foo', 'rw', FileRule.ANY_EXEC, FileRule.ALL, False, False, False) self.assertTrue(self.obj.is_covered(self.testobj)) self.assertFalse(self.obj.is_equal(self.testobj, strict=False)) self.assertFalse(self.obj.is_equal(self.testobj, strict=True)) def test_covered_anyperm_5(self): # even with ANY_EXEC, a different link target causes a mismatch - self.testobj = FileRule( '/foo', 'rwl', FileRule.ANY_EXEC, '/xyz', False, False, False) + self.testobj = FileRule('/foo', 'rwl', FileRule.ANY_EXEC, '/xyz', False, False, False) self.assertFalse(self.obj.is_covered(self.testobj)) self.assertFalse(self.obj.is_equal(self.testobj, strict=False)) self.assertFalse(self.obj.is_equal(self.testobj, strict=True)) def test_covered_anyperm_6(self): # even with ANY_EXEC, a different link target causes a mismatch - self.testobj = FileRule( '/foo', 'rwl', FileRule.ANY_EXEC, FileRule.ALL, False, False, False) + self.testobj = FileRule('/foo', 'rwl', FileRule.ANY_EXEC, FileRule.ALL, False, False, False) self.assertFalse(self.obj.is_covered(self.testobj)) self.assertFalse(self.obj.is_equal(self.testobj, strict=False)) self.assertFalse(self.obj.is_equal(self.testobj, strict=True)) def test_covered_anyperm_7(self): - self.obj = FileRule( '/foo', 'rw', 'x', '/bar', False, False, False, deny=True) - self.testobj = FileRule( '/foo', 'rw', FileRule.ANY_EXEC,'/bar', False, False, False) + self.obj = FileRule('/foo', 'rw', 'x', '/bar', False, False, False, deny=True) + self.testobj = FileRule('/foo', 'rw', FileRule.ANY_EXEC, '/bar', False, False, False) self.assertFalse(self.obj.is_covered(self.testobj)) self.assertTrue(self.obj.is_covered(self.testobj, check_allow_deny=False)) self.assertFalse(self.obj.is_equal(self.testobj, strict=False)) @@ -781,20 +804,21 @@ class FileCoveredTest_ManualOrInvalid(AATest): with self.assertRaises(AppArmorBug): obj.is_equal(testobj) + class FileSeverityTest(AATest): tests = ( - ('/usr/bin/whatis ix,', 5), - ('/etc ix,', 'unknown'), - ('/dev/doublehit ix,', 0), - ('/dev/doublehit rix,', 4), - ('/dev/doublehit rwix,', 8), - ('/dev/tty10 rwix,', 9), - ('/var/adm/foo/** rix,', 3), - ('/etc/apparmor/** r,', 6), - ('/etc/** r,', 'unknown'), - ('/usr/foo@bar r,', 'unknown'), # filename containing @ - ('/home/foo@bar rw,', 6), # filename containing @ - ('file,', 'unknown'), # bare file rule XXX should return maximum severity + ('/usr/bin/whatis ix,', 5), + ('/etc ix,', 'unknown'), + ('/dev/doublehit ix,', 0), + ('/dev/doublehit rix,', 4), + ('/dev/doublehit rwix,', 8), + ('/dev/tty10 rwix,', 9), + ('/var/adm/foo/** rix,', 3), + ('/etc/apparmor/** r,', 6), + ('/etc/** r,', 'unknown'), + ('/usr/foo@bar r,', 'unknown'), # filename containing @ + ('/home/foo@bar rw,', 6), # filename containing @ + ('file,', 'unknown'), # bare file rule XXX should return maximum severity ) def _run_test(self, params, expected): @@ -803,37 +827,39 @@ class FileSeverityTest(AATest): rank = obj.severity(sev_db) self.assertEqual(rank, expected) + class FileLogprofHeaderTest(AATest): tests = ( # log event old perms ALL / owner - (('file,', set(), set() ), [ _('Path'), _('ALL'), _('New Mode'), _('ALL') ]), - (('/foo r,', set(), set() ), [ _('Path'), '/foo', _('New Mode'), 'r' ]), - (('file /bar Px -> foo,', set(), set() ), [ _('Path'), '/bar', _('New Mode'), 'Px -> foo' ]), - (('deny file,', set(), set() ), [_('Qualifier'), 'deny', _('Path'), _('ALL'), _('New Mode'), _('ALL') ]), - (('allow file /baz rwk,', set(), set() ), [_('Qualifier'), 'allow', _('Path'), '/baz', _('New Mode'), 'rwk' ]), - (('audit file /foo mr,', set(), set() ), [_('Qualifier'), 'audit', _('Path'), '/foo', _('New Mode'), 'mr' ]), - (('audit deny /foo wk,', set(), set() ), [_('Qualifier'), 'audit deny', _('Path'), '/foo', _('New Mode'), 'wk' ]), - (('owner file /foo ix,', set(), set() ), [ _('Path'), '/foo', _('New Mode'), 'owner ix' ]), - (('audit deny file /foo rlx -> /baz,', set(), set() ), [_('Qualifier'), 'audit deny', _('Path'), '/foo', _('New Mode'), 'rlx -> /baz' ]), - (('/foo rw,', set('r'), set() ), [ _('Path'), '/foo', _('Old Mode'), _('r'), _('New Mode'), _('rw') ]), - (('/foo rw,', set(), set('rw') ), [ _('Path'), '/foo', _('Old Mode'), _('owner rw'), _('New Mode'), _('rw') ]), - (('/foo mrw,', set('r'), set('k') ), [ _('Path'), '/foo', _('Old Mode'), _('r + owner k'), _('New Mode'), _('mrw') ]), - (('/foo mrw,', set('r'), set('rk') ), [ _('Path'), '/foo', _('Old Mode'), _('r + owner k'), _('New Mode'), _('mrw') ]), - (('link /foo -> /bar,', set(), set() ), [ _('Path'), '/foo', _('New Mode'), 'link -> /bar' ]), - (('link subset /foo -> /bar,', set(), set() ), [ _('Path'), '/foo', _('New Mode'), 'link subset -> /bar' ]), + (('file,', set(), set()), [ _('Path'), _('ALL'), _('New Mode'), _('ALL')]), + (('/foo r,', set(), set()), [ _('Path'), '/foo', _('New Mode'), 'r']), + (('file /bar Px -> foo,', set(), set()), [ _('Path'), '/bar', _('New Mode'), 'Px -> foo']), + (('deny file,', set(), set()), [_('Qualifier'), 'deny', _('Path'), _('ALL'), _('New Mode'), _('ALL')]), + (('allow file /baz rwk,', set(), set()), [_('Qualifier'), 'allow', _('Path'), '/baz', _('New Mode'), 'rwk']), + (('audit file /foo mr,', set(), set()), [_('Qualifier'), 'audit', _('Path'), '/foo', _('New Mode'), 'mr']), + (('audit deny /foo wk,', set(), set()), [_('Qualifier'), 'audit deny', _('Path'), '/foo', _('New Mode'), 'wk']), + (('owner file /foo ix,', set(), set()), [ _('Path'), '/foo', _('New Mode'), 'owner ix']), + (('audit deny file /foo rlx -> /baz,', set(), set()), [_('Qualifier'), 'audit deny', _('Path'), '/foo', _('New Mode'), 'rlx -> /baz']), + (('/foo rw,', set('r'), set()), [ _('Path'), '/foo', _('Old Mode'), _('r'), _('New Mode'), _('rw')]), + (('/foo rw,', set(), set('rw')), [ _('Path'), '/foo', _('Old Mode'), _('owner rw'), _('New Mode'), _('rw')]), + (('/foo mrw,', set('r'), set('k')), [ _('Path'), '/foo', _('Old Mode'), _('r + owner k'), _('New Mode'), _('mrw')]), + (('/foo mrw,', set('r'), set('rk')), [ _('Path'), '/foo', _('Old Mode'), _('r + owner k'), _('New Mode'), _('mrw')]), + (('link /foo -> /bar,', set(), set()), [ _('Path'), '/foo', _('New Mode'), 'link -> /bar']), + (('link subset /foo -> /bar,', set(), set()), [ _('Path'), '/foo', _('New Mode'), 'link subset -> /bar']), ) def _run_test(self, params, expected): obj = FileRule.parse(params[0]) if params[1] or params[2]: - obj.original_perms = {'allow': { 'all': params[1], 'owner': params[2]}} + obj.original_perms = {'allow': {'all': params[1], 'owner': params[2]}} self.assertEqual(obj.logprof_header(), expected) def test_empty_original_perms(self): obj = FileRule.parse('/foo rw,') - obj.original_perms = {'allow': { 'all': set(), 'owner': set()}} + obj.original_perms = {'allow': {'all': set(), 'owner': set()}} self.assertEqual(obj.logprof_header(), [_('Path'), '/foo', _('New Mode'), _('rw')]) + class FileEditHeaderTest(AATest): def _run_test(self, params, expected): rule_obj = FileRule.parse(params) @@ -842,9 +868,9 @@ class FileEditHeaderTest(AATest): self.assertEqual(path_to_edit, expected) tests = ( - ('/foo/bar/baz r,', '/foo/bar/baz'), - ('/foo/**/baz r,', '/foo/**/baz'), - ('link /foo/** -> /bar,', '/foo/**'), + ('/foo/bar/baz r,', '/foo/bar/baz'), + ('/foo/**/baz r,', '/foo/**/baz'), + ('link /foo/** -> /bar,', '/foo/**'), ) def test_edit_header_bare_file(self): @@ -853,6 +879,7 @@ class FileEditHeaderTest(AATest): with self.assertRaises(AppArmorBug): rule_obj.edit_header() + class FileValidateAndStoreEditTest(AATest): def _run_test(self, params, expected): rule_obj = FileRule('/foo/bar/baz', 'r', None, FileRule.ALL, False, False, False, log_event=True) @@ -863,12 +890,12 @@ class FileValidateAndStoreEditTest(AATest): self.assertEqual(rule_obj.get_raw(), '%s r,' % params) tests = ( - # edited path match - ('/foo/bar/baz', True), - ('/foo/bar/*', True), - ('/foo/bar/???', True), - ('/foo/xy**', False), - ('/foo/bar/baz/', False), + # 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): @@ -891,7 +918,6 @@ class FileValidateAndStoreEditTest(AATest): rule_obj.store_edit('/foo/bar') - ## --- tests for FileRuleset --- # class FileRulesTest(AATest): @@ -945,7 +971,7 @@ class FileRulesTest(AATest): ' /foo Px,', ' /bar Cx -> bar_child ,', ' deny /asdf w,', - '', + '', ] expected_clean = [ @@ -953,7 +979,7 @@ class FileRulesTest(AATest): '', ' /bar Cx -> bar_child,', ' /foo Px,', - '', + '', ] deleted = 0 @@ -980,14 +1006,14 @@ class FileRulesTest(AATest): ' /foo/baz rw,', ' /foo/baz rwk,', ' /foo/* r,', - '', + '', ] expected_clean = [ ' /foo/* r,', ' /foo/baz rw,', ' /foo/baz rwk,', - '', + '', ] deleted = 0 @@ -1004,21 +1030,22 @@ class FileRulesTest(AATest): self.assertEqual(expected_clean, ruleset.get_clean(1)) -#class FileDeleteTest(AATest): -# pass +# class FileDeleteTest(AATest): +# pass + class FileGetRulesForPath(AATest): tests = ( - # path audit deny expected - (('/etc/foo/dovecot.conf', False, False), ['/etc/foo/* r,', '/etc/foo/dovecot.conf rw,', '']), - (('/etc/foo/foo.conf', False, False), ['/etc/foo/* r,', '']), - (('/etc/foo/dovecot-database.conf.ext', False, False), ['/etc/foo/* r,', '/etc/foo/dovecot-database.conf.ext w,', '']), - (('/etc/foo/auth.d/authfoo.conf', False, False), ['/etc/foo/{auth,conf}.d/*.conf r,','/etc/foo/{auth,conf}.d/authfoo.conf w,', '']), - (('/etc/foo/dovecot-deny.conf', False, False), ['deny /etc/foo/dovecot-deny.conf r,', '', '/etc/foo/* r,', '']), - (('/foo/bar', False, True ), [ ]), - (('/etc/foo/dovecot-deny.conf', False, True ), ['deny /etc/foo/dovecot-deny.conf r,', '']), - (('/etc/foo/foo.conf', False, True ), [ ]), - (('/etc/foo/owner.conf', False, False), ['/etc/foo/* r,', 'owner /etc/foo/owner.conf w,', '']), + # path audit deny expected + (('/etc/foo/dovecot.conf', False, False), ['/etc/foo/* r,', '/etc/foo/dovecot.conf rw,', '']), + (('/etc/foo/foo.conf', False, False), ['/etc/foo/* r,', '']), + (('/etc/foo/dovecot-database.conf.ext', False, False), ['/etc/foo/* r,', '/etc/foo/dovecot-database.conf.ext w,', '']), + (('/etc/foo/auth.d/authfoo.conf', False, False), ['/etc/foo/{auth,conf}.d/*.conf r,', '/etc/foo/{auth,conf}.d/authfoo.conf w,', '']), + (('/etc/foo/dovecot-deny.conf', False, False), ['deny /etc/foo/dovecot-deny.conf r,', '', '/etc/foo/* r,', '']), + (('/foo/bar', False, True), [ ]), + (('/etc/foo/dovecot-deny.conf', False, True), ['deny /etc/foo/dovecot-deny.conf r,', '']), + (('/etc/foo/foo.conf', False, True), [ ]), + (('/etc/foo/owner.conf', False, False), ['/etc/foo/* r,', 'owner /etc/foo/owner.conf w,', '']), ) def _run_test(self, params, expected): @@ -1043,15 +1070,15 @@ class FileGetRulesForPath(AATest): class FileGetPermsForPath_1(AATest): tests = ( # path audit deny expected - (('/etc/foo/dovecot.conf', False, False), {'allow': {'all': {'r', 'w'}, 'owner': set() }, 'deny': {'all': set(), 'owner': set() }, 'paths': {'/etc/foo/*', '/etc/foo/dovecot.conf' } }), - (('/etc/foo/foo.conf', False, False), {'allow': {'all': {'r' }, 'owner': set() }, 'deny': {'all': set(), 'owner': set() }, 'paths': {'/etc/foo/*' } }), - (('/etc/foo/dovecot-database.conf.ext', False, False), {'allow': {'all': {'r', 'w'}, 'owner': set() }, 'deny': {'all': set(), 'owner': set() }, 'paths': {'/etc/foo/*', '/etc/foo/dovecot-database.conf.ext' } }), - (('/etc/foo/auth.d/authfoo.conf', False, False), {'allow': {'all': {'r', 'w'}, 'owner': set() }, 'deny': {'all': set(), 'owner': set() }, 'paths': {'/etc/foo/{auth,conf}.d/*.conf', '/etc/foo/{auth,conf}.d/authfoo.conf' } }), - (('/etc/foo/dovecot-deny.conf', False, False), {'allow': {'all': {'r' }, 'owner': set() }, 'deny': {'all': {'r' }, 'owner': set() }, 'paths': {'/etc/foo/*', '/etc/foo/dovecot-deny.conf' } }), - (('/foo/bar', False, True ), {'allow': {'all': set(), 'owner': set() }, 'deny': {'all': set(), 'owner': set() }, 'paths': set() }), - (('/etc/foo/dovecot-deny.conf', False, True ), {'allow': {'all': set(), 'owner': set() }, 'deny': {'all': {'r' }, 'owner': set() }, 'paths': {'/etc/foo/dovecot-deny.conf' } }), - (('/etc/foo/foo.conf', False, True ), {'allow': {'all': set(), 'owner': set() }, 'deny': {'all': set(), 'owner': set() }, 'paths': set() }), - (('/usr/lib/dovecot/config', False, False), {'allow': {'all': set(), 'owner': set() }, 'deny': {'all': set(), 'owner': set() }, 'paths': set() }), # exec perms are not honored by get_perms_for_path() + (('/etc/foo/dovecot.conf', False, False), {'allow': {'all': {'r', 'w'}, 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': {'/etc/foo/*', '/etc/foo/dovecot.conf'}}), + (('/etc/foo/foo.conf', False, False), {'allow': {'all': {'r'}, 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': {'/etc/foo/*'}}), + (('/etc/foo/dovecot-database.conf.ext', False, False), {'allow': {'all': {'r', 'w'}, 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': {'/etc/foo/*', '/etc/foo/dovecot-database.conf.ext'}}), + (('/etc/foo/auth.d/authfoo.conf', False, False), {'allow': {'all': {'r', 'w'}, 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': {'/etc/foo/{auth,conf}.d/*.conf', '/etc/foo/{auth,conf}.d/authfoo.conf'}}), + (('/etc/foo/dovecot-deny.conf', False, False), {'allow': {'all': {'r'}, 'owner': set()}, 'deny': {'all': {'r'}, 'owner': set()}, 'paths': {'/etc/foo/*', '/etc/foo/dovecot-deny.conf'}}), + (('/foo/bar', False, True), {'allow': {'all': set(), 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': set()}), + (('/etc/foo/dovecot-deny.conf', False, True), {'allow': {'all': set(), 'owner': set()}, 'deny': {'all': {'r'}, 'owner': set()}, 'paths': {'/etc/foo/dovecot-deny.conf'}}), + (('/etc/foo/foo.conf', False, True), {'allow': {'all': set(), 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': set()}), + (('/usr/lib/dovecot/config', False, False), {'allow': {'all': set(), 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': set()}), # exec perms are not honored by get_perms_for_path() ) def _run_test(self, params, expected): @@ -1072,20 +1099,21 @@ class FileGetPermsForPath_1(AATest): perms = ruleset.get_perms_for_path(*params) self. assertEqual(perms, expected) + class FileGetPermsForPath_2(AATest): tests = ( # path audit deny expected - (('/etc/foo/dovecot.conf', False, False), {'allow': {'all': FileRule.ALL, 'owner': set() }, 'deny': {'all': FileRule.ALL, 'owner': set() }, 'paths': {'/etc/foo/*', '/etc/foo/dovecot.conf' } }), - (('/etc/foo/dovecot.conf', True, False), {'allow': {'all': {'r', 'w'}, 'owner': set() }, 'deny': {'all': set(), 'owner': set() }, 'paths': {'/etc/foo/dovecot.conf' } }), - (('/etc/foo/foo.conf', False, False), {'allow': {'all': FileRule.ALL, 'owner': set() }, 'deny': {'all': FileRule.ALL, 'owner': set() }, 'paths': {'/etc/foo/*' } }), - (('/etc/foo/dovecot-database.conf.ext', False, False), {'allow': {'all': FileRule.ALL, 'owner': set() }, 'deny': {'all': FileRule.ALL, 'owner': set() }, 'paths': {'/etc/foo/*', '/etc/foo/dovecot-database.conf.ext' } }), - (('/etc/foo/auth.d/authfoo.conf', False, False), {'allow': {'all': FileRule.ALL, 'owner': set() }, 'deny': {'all': FileRule.ALL, 'owner': set() }, 'paths': {'/etc/foo/{auth,conf}.d/*.conf', '/etc/foo/{auth,conf}.d/authfoo.conf' } }), - (('/etc/foo/auth.d/authfoo.conf', True, False), {'allow': {'all': {'w' }, 'owner': set() }, 'deny': {'all': set(), 'owner': set() }, 'paths': {'/etc/foo/{auth,conf}.d/authfoo.conf' } }), - (('/etc/foo/dovecot-deny.conf', False, False), {'allow': {'all': FileRule.ALL, 'owner': set() }, 'deny': {'all': FileRule.ALL, 'owner': set() }, 'paths': {'/etc/foo/*', '/etc/foo/dovecot-deny.conf' } }), - (('/foo/bar', False, True ), {'allow': {'all': set(), 'owner': set() }, 'deny': {'all': FileRule.ALL, 'owner': set() }, 'paths': set() }), - (('/etc/foo/dovecot-deny.conf', False, True ), {'allow': {'all': set(), 'owner': set() }, 'deny': {'all': FileRule.ALL, 'owner': set() }, 'paths': {'/etc/foo/dovecot-deny.conf' } }), - (('/etc/foo/foo.conf', False, True ), {'allow': {'all': set(), 'owner': set() }, 'deny': {'all': FileRule.ALL, 'owner': set() }, 'paths': set() }), - # (('/etc/foo/owner.conf', False, True ), {'allow': {'all': set(), 'owner': {'w'} }, 'deny': {'all': FileRule.ALL, 'owner': set() }, 'paths': {'/etc/foo/owner.conf' } }), # XXX doesn't work yet + (('/etc/foo/dovecot.conf', False, False), {'allow': {'all': FileRule.ALL, 'owner': set()}, 'deny': {'all': FileRule.ALL, 'owner': set()}, 'paths': {'/etc/foo/*', '/etc/foo/dovecot.conf'}}), + (('/etc/foo/dovecot.conf', True, False), {'allow': {'all': {'r', 'w'}, 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': {'/etc/foo/dovecot.conf'}}), + (('/etc/foo/foo.conf', False, False), {'allow': {'all': FileRule.ALL, 'owner': set()}, 'deny': {'all': FileRule.ALL, 'owner': set()}, 'paths': {'/etc/foo/*'}}), + (('/etc/foo/dovecot-database.conf.ext', False, False), {'allow': {'all': FileRule.ALL, 'owner': set()}, 'deny': {'all': FileRule.ALL, 'owner': set()}, 'paths': {'/etc/foo/*', '/etc/foo/dovecot-database.conf.ext'}}), + (('/etc/foo/auth.d/authfoo.conf', False, False), {'allow': {'all': FileRule.ALL, 'owner': set()}, 'deny': {'all': FileRule.ALL, 'owner': set()}, 'paths': {'/etc/foo/{auth,conf}.d/*.conf', '/etc/foo/{auth,conf}.d/authfoo.conf'}}), + (('/etc/foo/auth.d/authfoo.conf', True, False), {'allow': {'all': {'w'}, 'owner': set()}, 'deny': {'all': set(), 'owner': set()}, 'paths': {'/etc/foo/{auth,conf}.d/authfoo.conf'}}), + (('/etc/foo/dovecot-deny.conf', False, False), {'allow': {'all': FileRule.ALL, 'owner': set()}, 'deny': {'all': FileRule.ALL, 'owner': set()}, 'paths': {'/etc/foo/*', '/etc/foo/dovecot-deny.conf'}}), + (('/foo/bar', False, True), {'allow': {'all': set(), 'owner': set()}, 'deny': {'all': FileRule.ALL, 'owner': set()}, 'paths': set()}), + (('/etc/foo/dovecot-deny.conf', False, True), {'allow': {'all': set(), 'owner': set()}, 'deny': {'all': FileRule.ALL, 'owner': set()}, 'paths': {'/etc/foo/dovecot-deny.conf'}}), + (('/etc/foo/foo.conf', False, True), {'allow': {'all': set(), 'owner': set()}, 'deny': {'all': FileRule.ALL, 'owner': set()}, 'paths': set()}), + # (('/etc/foo/owner.conf', False, True), {'allow': {'all': set(), 'owner': {'w'}}, 'deny': {'all': FileRule.ALL, 'owner': set()}, 'paths': {'/etc/foo/owner.conf'}}), # XXX doesn't work yet ) def _run_test(self, params, expected): @@ -1108,11 +1136,12 @@ class FileGetPermsForPath_2(AATest): perms = ruleset.get_perms_for_path(*params) self. assertEqual(perms, expected) + class FileGetExecRulesForPath_1(AATest): tests = ( - ('/bin/foo', ['audit /bin/foo ix,', ''] ), - ('/bin/bar', ['deny /bin/bar x,', ''] ), - ('/foo', [] ), + ('/bin/foo', ['audit /bin/foo ix,', '']), + ('/bin/bar', ['deny /bin/bar x,', '']), + ('/foo', []), ) def _run_test(self, params, expected): @@ -1131,11 +1160,12 @@ class FileGetExecRulesForPath_1(AATest): matches = perms.get_clean() self. assertEqual(matches, expected) + class FileGetExecRulesForPath_2(AATest): tests = ( - ('/bin/foo', ['audit /bin/foo ix,', ''] ), - ('/bin/bar', ['deny /bin/bar x,', '', '/bin/b* Px,', ''] ), - ('/foo', [] ), + ('/bin/foo', ['audit /bin/foo ix,', '']), + ('/bin/bar', ['deny /bin/bar x,', '', '/bin/b* Px,', '']), + ('/foo', []), ) def _run_test(self, params, expected): @@ -1154,12 +1184,13 @@ class FileGetExecRulesForPath_2(AATest): matches = perms.get_clean() self. assertEqual(matches, expected) + class FileGetExecConflictRules_1(AATest): tests = ( - ('/bin/foo ix,', ['/bin/foo Px,', ''] ), - ('/bin/bar Px,', ['deny /bin/bar x,', '', '/bin/bar cx,', ''] ), - ('/bin/bar cx,', ['deny /bin/bar x,','',] ), - ('/bin/foo r,', [] ), + ('/bin/foo ix,', ['/bin/foo Px,', '']), + ('/bin/bar Px,', ['deny /bin/bar x,', '', '/bin/bar cx,', '']), + ('/bin/bar cx,', ['deny /bin/bar x,', '']), + ('/bin/foo r,', []), ) def _run_test(self, params, expected): @@ -1181,7 +1212,6 @@ class FileGetExecConflictRules_1(AATest): self. assertEqual(conflicts.get_clean(), expected) - setup_all_loops(__name__) if __name__ == '__main__': unittest.main(verbosity=1) diff --git a/utils/test/test-include.py b/utils/test/test-include.py index d705a37ab..82217c0e4 100644 --- a/utils/test/test-include.py +++ b/utils/test/test-include.py @@ -23,16 +23,17 @@ import shutil from apparmor.rule.include import IncludeRule, IncludeRuleset from apparmor.rule import BaseRule from apparmor.common import AppArmorException, AppArmorBug -#from apparmor.logparser import ReadLog +# from apparmor.logparser import ReadLog from apparmor.translations import init_translation _ = init_translation() -exp = namedtuple('exp', ( # 'audit', 'allow_keyword', 'deny', - 'comment', - 'path', 'ifexists', 'ismagic')) +exp = namedtuple( + 'exp', ( # 'audit', 'allow_keyword', 'deny', + 'comment', 'path', 'ifexists', 'ismagic')) # --- tests for single IncludeRule --- # + class IncludeTest(AATest): def _compare_obj(self, obj, expected): self.assertEqual(False, obj.allow_keyword) # not supported in include rules, expected to be always False @@ -44,45 +45,46 @@ class IncludeTest(AATest): self.assertEqual(expected.ifexists, obj.ifexists) self.assertEqual(expected.ismagic, obj.ismagic) + class IncludeTestParse(IncludeTest): tests = ( - # IncludeRule object comment path if exists ismagic + # IncludeRule object comment path if exists ismagic # #include - ('#include ', exp('', 'abstractions/base', False, True )), # magic path - ('#include # comment', exp(' # comment', 'abstractions/base', False, True )), - ('#include#comment', exp(' #comment', 'abstractions/base', False, True )), - (' #include ', exp('', 'abstractions/base', False, True )), - ('#include "/foo/bar"', exp('', '/foo/bar', False, False)), # absolute path - ('#include "/foo/bar" # comment', exp(' # comment', '/foo/bar', False, False)), - ('#include "/foo/bar"#comment', exp(' #comment', '/foo/bar', False, False)), - (' #include "/foo/bar" ', exp('', '/foo/bar', False, False)), + ('#include ', exp('', 'abstractions/base', False, True)), # magic path + ('#include # comment', exp(' # comment', 'abstractions/base', False, True)), + ('#include#comment', exp(' #comment', 'abstractions/base', False, True)), + (' #include ', exp('', 'abstractions/base', False, True)), + ('#include "/foo/bar"', exp('', '/foo/bar', False, False)), # absolute path + ('#include "/foo/bar" # comment', exp(' # comment', '/foo/bar', False, False)), + ('#include "/foo/bar"#comment', exp(' #comment', '/foo/bar', False, False)), + (' #include "/foo/bar" ', exp('', '/foo/bar', False, False)), # include (without #) - ('include ', exp('', 'abstractions/base', False, True )), # magic path - ('include # comment', exp(' # comment', 'abstractions/base', False, True )), - ('include#comment', exp(' #comment', 'abstractions/base', False, True )), - (' include ', exp('', 'abstractions/base', False, True )), - ('include "/foo/bar"', exp('', '/foo/bar', False, False)), # absolute path - ('include "/foo/bar" # comment', exp(' # comment', '/foo/bar', False, False)), - ('include "/foo/bar"#comment', exp(' #comment', '/foo/bar', False, False)), - (' include "/foo/bar" ', exp('', '/foo/bar', False, False)), + ('include ', exp('', 'abstractions/base', False, True)), # magic path + ('include # comment', exp(' # comment', 'abstractions/base', False, True)), + ('include#comment', exp(' #comment', 'abstractions/base', False, True)), + (' include ', exp('', 'abstractions/base', False, True)), + ('include "/foo/bar"', exp('', '/foo/bar', False, False)), # absolute path + ('include "/foo/bar" # comment', exp(' # comment', '/foo/bar', False, False)), + ('include "/foo/bar"#comment', exp(' #comment', '/foo/bar', False, False)), + (' include "/foo/bar" ', exp('', '/foo/bar', False, False)), # #include if exists - ('#include if exists ', exp('', 'abstractions/base', True, True )), # magic path - ('#include if exists # comment', exp(' # comment', 'abstractions/base', True, True )), - ('#include if exists#comment', exp(' #comment', 'abstractions/base', True, True )), - (' #include if exists ', exp('', 'abstractions/base', True, True )), - ('#include if exists "/foo/bar"', exp('', '/foo/bar', True, False)), # absolute path - ('#include if exists "/foo/bar" # comment', exp(' # comment', '/foo/bar', True, False)), - ('#include if exists "/foo/bar"#comment', exp(' #comment', '/foo/bar', True, False)), - (' #include if exists "/foo/bar" ', exp('', '/foo/bar', True, False)), + ('#include if exists ', exp('', 'abstractions/base', True, True)), # magic path + ('#include if exists # comment', exp(' # comment', 'abstractions/base', True, True)), + ('#include if exists#comment', exp(' #comment', 'abstractions/base', True, True)), + (' #include if exists ', exp('', 'abstractions/base', True, True)), + ('#include if exists "/foo/bar"', exp('', '/foo/bar', True, False)), # absolute path + ('#include if exists "/foo/bar" # comment', exp(' # comment', '/foo/bar', True, False)), + ('#include if exists "/foo/bar"#comment', exp(' #comment', '/foo/bar', True, False)), + (' #include if exists "/foo/bar" ', exp('', '/foo/bar', True, False)), # include if exists (without #) - ('include if exists ', exp('', 'abstractions/base', True, True )), # magic path - ('include if exists # comment', exp(' # comment', 'abstractions/base', True, True )), - ('include if exists#comment', exp(' #comment', 'abstractions/base', True, True )), - (' include if exists ', exp('', 'abstractions/base', True, True )), - ('include if exists "/foo/bar"', exp('', '/foo/bar', True, False)), # absolute path - ('include if exists "/foo/bar" # comment', exp(' # comment', '/foo/bar', True, False)), - ('include if exists "/foo/bar"#comment', exp(' #comment', '/foo/bar', True, False)), - (' include if exists "/foo/bar" ', exp('', '/foo/bar', True, False)), + ('include if exists ', exp('', 'abstractions/base', True, True)), # magic path + ('include if exists # comment', exp(' # comment', 'abstractions/base', True, True)), + ('include if exists#comment', exp(' #comment', 'abstractions/base', True, True)), + (' include if exists ', exp('', 'abstractions/base', True, True)), + ('include if exists "/foo/bar"', exp('', '/foo/bar', True, False)), # absolute path + ('include if exists "/foo/bar" # comment', exp(' # comment', '/foo/bar', True, False)), + ('include if exists "/foo/bar"#comment', exp(' #comment', '/foo/bar', True, False)), + (' include if exists "/foo/bar" ', exp('', '/foo/bar', True, False)), ) def _run_test(self, rawrule, expected): @@ -91,12 +93,13 @@ class IncludeTestParse(IncludeTest): self.assertEqual(rawrule.strip(), obj.raw_rule) self._compare_obj(obj, expected) + class IncludeTestParseInvalid(IncludeTest): tests = ( -# (' some #include if exists ', AppArmorException), -# (' /etc/fstab r,', AppArmorException), -# ('/usr/include r,', AppArmorException), -# ('/include r,', AppArmorException), + # (' some #include if exists ', AppArmorException), + # (' /etc/fstab r,', AppArmorException), + # ('/usr/include r,', AppArmorException), + # ('/include r,', AppArmorException), ) def _run_test(self, rawrule, expected): @@ -106,30 +109,32 @@ class IncludeTestParseInvalid(IncludeTest): # class IncludeTestParseFromLog(IncludeTest): # we'll never have log events for includes + class IncludeFromInit(IncludeTest): tests = ( - # IncludeRule object ifexists ismagic comment path ifexists ismagic - (IncludeRule('abstractions/base', False, False) , exp('', 'abstractions/base', False, False )), - (IncludeRule('foo', True, False) , exp('', 'foo', True, False )), - (IncludeRule('bar', False, True) , exp('', 'bar', False, True )), - (IncludeRule('baz', True, True) , exp('', 'baz', True, True )), - (IncludeRule('comment', False, False, comment='# cmt') , exp('# cmt', 'comment', False, False )), + # IncludeRule object ifexists ismagic comment path ifexists ismagic + (IncludeRule('abstractions/base', False, False), exp('', 'abstractions/base', False, False)), + (IncludeRule('foo', True, False), exp('', 'foo', True, False)), + (IncludeRule('bar', False, True), exp('', 'bar', False, True)), + (IncludeRule('baz', True, True), exp('', 'baz', True, True)), + (IncludeRule('comment', False, False, comment='# cmt'), exp('# cmt', 'comment', False, False)), ) def _run_test(self, obj, expected): self._compare_obj(obj, expected) + class InvalidIncludeInit(AATest): tests = ( - # init params expected exception - ((False, False, False ) , AppArmorBug), # wrong type for path - (('', False, False ) , AppArmorBug), # empty path - ((None, False, False ) , AppArmorBug), # wrong type for path -# ((' ', False, False ) , AppArmorBug), # whitespace-only path - (('foo', None, False ) , AppArmorBug), # wrong type for ifexists - (('foo', '', False ) , AppArmorBug), # wrong type for ifexists - (('foo', False, None ) , AppArmorBug), # wrong type for ismagic - (('foo', False, '' ) , AppArmorBug), # wrong type for ismagic + # init params expected exception + ((False, False, False), AppArmorBug), # wrong type for path + (('', False, False), AppArmorBug), # empty path + ((None, False, False), AppArmorBug), # wrong type for path + # ((' ', False, False), AppArmorBug), # whitespace-only path + (('foo', None, False), AppArmorBug), # wrong type for ifexists + (('foo', '', False), AppArmorBug), # wrong type for ifexists + (('foo', False, None), AppArmorBug), # wrong type for ismagic + (('foo', False, ''), AppArmorBug), # wrong type for ismagic ) def _run_test(self, params, expected): @@ -156,8 +161,9 @@ class InvalidIncludeInit(AATest): with self.assertRaises(AppArmorBug): IncludeRule('foo', False, False, deny=True) + class InvalidIncludeTest(AATest): - def _check_invalid_rawrule(self, rawrule, matches_regex = False): + def _check_invalid_rawrule(self, rawrule, matches_regex=False): obj = None self.assertEqual(IncludeRule.match(rawrule), matches_regex) with self.assertRaises(AppArmorException): @@ -171,12 +177,13 @@ class InvalidIncludeTest(AATest): def test_invalid_non_IncludeRule(self): self._check_invalid_rawrule('dbus,') # not a include rule -# def test_empty_data_1(self): -# obj = IncludeRule('foo', False, False) -# obj.path = '' -# # no path set -# with self.assertRaises(AppArmorBug): -# obj.get_clean(1) + # def test_empty_data_1(self): + # obj = IncludeRule('foo', False, False) + # obj.path = '' + # # no path set + # with self.assertRaises(AppArmorBug): + # obj.get_clean(1) + class WriteIncludeTestAATest(AATest): def _run_test(self, rawrule, expected): @@ -189,43 +196,43 @@ class WriteIncludeTestAATest(AATest): self.assertEqual(rawrule.strip(), raw, 'unexpected raw rule') tests = ( - # raw rule clean rule - (' include ', 'include ' ), -# (' include foo ', 'include "foo"' ), # several test cases disabled due to implementation restrictions, see re_match_include_parse() -# (' include "foo" ', 'include "foo"' ), -# (' include /foo ', 'include "/foo"' ), - (' include "/foo" ', 'include "/foo"' ), + # (raw rule, clean rule) + (' include ', 'include '), + # (' include foo ', 'include "foo"'), # several test cases disabled due to implementation restrictions, see re_match_include_parse() + # (' include "foo" ', 'include "foo"'), + # (' include /foo ', 'include "/foo"'), + (' include "/foo" ', 'include "/foo"'), - (' include # bar ', 'include # bar' ), -# (' include foo # bar ', 'include "foo" # bar' ), -# (' include "foo" # bar ', 'include "foo" # bar' ), -# (' include /foo # bar ', 'include "/foo" # bar' ), - (' include "/foo" # bar ', 'include "/foo" # bar' ), + (' include # bar ', 'include # bar'), + # (' include foo # bar ', 'include "foo" # bar'), + # (' include "foo" # bar ', 'include "foo" # bar'), + # (' include /foo # bar ', 'include "/foo" # bar'), + (' include "/foo" # bar ', 'include "/foo" # bar'), - (' include if exists ', 'include if exists ' ), -# (' include if exists foo ', 'include if exists "foo"' ), -# (' include if exists "foo" ', 'include if exists "foo"' ), -# (' include if exists /foo ', 'include if exists "/foo"' ), - (' include if exists "/foo" ', 'include if exists "/foo"' ), + (' include if exists ', 'include if exists '), + # (' include if exists foo ', 'include if exists "foo"'), + # (' include if exists "foo" ', 'include if exists "foo"'), + # (' include if exists /foo ', 'include if exists "/foo"'), + (' include if exists "/foo" ', 'include if exists "/foo"'), # and the same again with #include... - (' #include ', 'include ' ), -# (' #include foo ', 'include "foo"' ), -# (' #include "foo" ', 'include "foo"' ), -# (' #include /foo ', 'include "/foo"' ), - (' #include "/foo" ', 'include "/foo"' ), + (' #include ', 'include '), + # (' #include foo ', 'include "foo"'), + # (' #include "foo" ', 'include "foo"'), + # (' #include /foo ', 'include "/foo"'), + (' #include "/foo" ', 'include "/foo"'), - (' #include # bar ', 'include # bar' ), -# (' #include foo # bar ', 'include "foo" # bar' ), -# (' #include "foo" # bar ', 'include "foo" # bar' ), -# (' #include /foo # bar ', 'include "/foo" # bar' ), - (' #include "/foo" # bar ', 'include "/foo" # bar' ), + (' #include # bar ', 'include # bar'), + # (' #include foo # bar ', 'include "foo" # bar'), + # (' #include "foo" # bar ', 'include "foo" # bar'), + # (' #include /foo # bar ', 'include "/foo" # bar'), + (' #include "/foo" # bar ', 'include "/foo" # bar'), - (' #include if exists ', 'include if exists ' ), -# (' #include if exists foo ', 'include if exists "foo"' ), -# (' #include if exists "foo" ', 'include if exists "foo"' ), -# (' #include if exists /foo ', 'include if exists "/foo"' ), - (' #include if exists "/foo" ', 'include if exists "/foo"' ), + (' #include if exists ', 'include if exists '), + # (' #include if exists foo ', 'include if exists "foo"'), + # (' #include if exists "foo" ', 'include if exists "foo"'), + # (' #include if exists /foo ', 'include if exists "/foo"'), + (' #include if exists "/foo" ', 'include if exists "/foo"'), ) def test_write_manually(self): @@ -250,42 +257,45 @@ class IncludeCoveredTest(AATest): self.assertEqual(obj.is_covered(check_obj), expected[2], 'Mismatch in is_covered, expected %s' % expected[2]) self.assertEqual(obj.is_covered(check_obj, True, True), expected[3], 'Mismatch in is_covered/exact, expected %s' % expected[3]) + class IncludeCoveredTest_01(IncludeCoveredTest): rule = 'include ' tests = ( - # rule equal strict equal covered covered exact - ('include ' , ( True , True , True , True )), - ('#include ' , ( True , False , True , True )), - ('include if exists ' , ( False , False , True , True )), - ('#include if exists ' , ( False , False , True , True )), - ('include ' , ( False , False , False , False )), -# ('include "foo"' , ( False , False , False , False )), # disabled due to implementation restrictions, see re_match_include_parse() -# ('include if exists "foo"' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('include ', (True, True, True, True)), + ('#include ', (True, False, True, True)), + ('include if exists ', (False, False, True, True)), + ('#include if exists ', (False, False, True, True)), + ('include ', (False, False, False, False)), + # ('include "foo"', (False, False, False, False)), # disabled due to implementation restrictions, see re_match_include_parse() + # ('include if exists "foo"', (False, False, False, False)), ) + class IncludeCoveredTest_02(IncludeCoveredTest): rule = 'include if exists ' tests = ( - # rule equal strict equal covered covered exact - ('include ' , ( False , False , False , False )), - ('#include ' , ( False , False , False , False )), - ('#include if exists ' , ( True , False , True , True )), - ('include ' , ( False , False , False , False )), -# ('include "foo"' , ( False , False , False , False )), # disabled due to implementation restrictions, see re_match_include_parse() -# ('include if exists "foo"' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('include ', (False, False, False, False)), + ('#include ', (False, False, False, False)), + ('#include if exists ', (True, False, True, True)), + ('include ', (False, False, False, False)), + # ('include "foo"', (False, False, False, False)), # disabled due to implementation restrictions, see re_match_include_parse() + # ('include if exists "foo"', (False, False, False, False)), ) + class IncludeCoveredTest_Invalid(AATest): -# def test_borked_obj_is_covered_1(self): -# obj = IncludeRule.parse('include ') - -# testobj = IncludeRule('foo', True, True) -# testobj.path = '' - -# with self.assertRaises(AppArmorBug): -# obj.is_covered(testobj) + # def test_borked_obj_is_covered_1(self): + # obj = IncludeRule.parse('include ') + # + # testobj = IncludeRule('foo', True, True) + # testobj.path = '' + # + # with self.assertRaises(AppArmorBug): + # obj.is_covered(testobj) def test_invalid_is_covered(self): obj = IncludeRule.parse('include ') @@ -303,21 +313,23 @@ class IncludeCoveredTest_Invalid(AATest): with self.assertRaises(AppArmorBug): obj.is_equal(testobj) + class IncludeLogprofHeaderTest(AATest): tests = ( - ('include ', [_('Include'), 'include ' ]), - ('include "/what/ever"', [_('Include'), 'include "/what/ever"', ]), + ('include ', [_('Include'), 'include ']), + ('include "/what/ever"', [_('Include'), 'include "/what/ever"']), ) def _run_test(self, params, expected): obj = IncludeRule.parse(params) self.assertEqual(obj.logprof_header(), expected) + class IncludeFullPathsTest(AATest): def AASetup(self): self.createTmpdir() - #copy the local profiles to the test directory + # copy the local profiles to the test directory self.profile_dir = '%s/profiles' % self.tmpdir shutil.copytree('../../profiles/apparmor.d/', self.profile_dir, symlinks=True) @@ -334,14 +346,14 @@ class IncludeFullPathsTest(AATest): os.mkdir(empty_dir, 0o755) tests = ( - # @@ will be replaced with self.profile_dir - ('include ', ('@@/abstractions/base',) ), -# ('include "foo"', ('@@/foo',) ), # TODO: adjust logic to honor quoted vs. magic paths (and allow quoted relative paths in re_match_include_parse()) - ('include "/foo/bar"', ('/foo/bar',) ), - ('include ', ('@@/abstractions/inc.d/incbar', '@@/abstractions/inc.d/incfoo') ), - ('include ', () ), - ('include ', ('@@/abstractions/not_found',) ), - ('include if exists ', () ), + # @@ will be replaced with self.profile_dir + ('include ', ('@@/abstractions/base',)), + # ('include "foo"', ('@@/foo',)), # TODO: adjust logic to honor quoted vs. magic paths (and allow quoted relative paths in re_match_include_parse()) + ('include "/foo/bar"', ('/foo/bar',)), + ('include ', ('@@/abstractions/inc.d/incbar', '@@/abstractions/inc.d/incfoo')), + ('include ', ()), + ('include ', ('@@/abstractions/not_found',)), + ('include if exists ', ()), ) def _run_test(self, params, expected): @@ -352,13 +364,14 @@ class IncludeFullPathsTest(AATest): obj = IncludeRule.parse(params) self.assertEqual(obj.get_full_paths(self.profile_dir), exp2) + ## --- tests for IncludeRuleset --- # class IncludeRulesTest(AATest): def AASetup(self): self.createTmpdir() - #copy the local profiles to the test directory + # copy the local profiles to the test directory self.profile_dir = '%s/profiles' % self.tmpdir shutil.copytree('../../profiles/apparmor.d/', self.profile_dir, symlinks=True) @@ -459,23 +472,26 @@ class IncludeRulesTest(AATest): self.assertEqual(expected_clean_unsorted, ruleset.get_clean_unsorted()) self.assertEqual(expected_fullpaths, ruleset.get_all_full_paths(self.profile_dir)) + class IncludeGlobTestAATest(AATest): def setUp(self): self.maxDiff = None self.ruleset = IncludeRuleset() -# def test_glob(self): -# with self.assertRaises(NotImplementedError): -# # get_glob_ext is not available for include rules -# self.ruleset.get_glob('include send set=int,') + # def test_glob(self): + # with self.assertRaises(NotImplementedError): + # # get_glob_ext is not available for include rules + # self.ruleset.get_glob('include send set=int,') def test_glob_ext(self): with self.assertRaises(NotImplementedError): # get_glob_ext is not available for include rules self.ruleset.get_glob_ext('include send set=int,') -#class IncludeDeleteTestAATest(AATest): -# pass + +# class IncludeDeleteTestAATest(AATest): +# pass + setup_all_loops(__name__) if __name__ == '__main__': diff --git a/utils/test/test-libapparmor-test_multi.py b/utils/test/test-libapparmor-test_multi.py index 2a46845d4..1161832e2 100644 --- a/utils/test/test-libapparmor-test_multi.py +++ b/utils/test/test-libapparmor-test_multi.py @@ -66,7 +66,7 @@ class TestLibapparmorTestMulti(AATest): 'src_name', # pivotroot 'dbus_bus', 'dbus_interface', 'dbus_member', 'dbus_path', # dbus 'peer_pid', 'peer_profile', # dbus - ): + ): pass elif parsed_items['operation'] == 'exec' and label in ('sock_type', 'family', 'protocol'): pass # XXX 'exec' + network? really? @@ -92,19 +92,19 @@ class TestLibapparmorTestMulti(AATest): # list of labels that use a different name in logparser.py than in the test_multi *.out files # (additionally, .lower() is applied to all labels) label_map = { - 'Mask': 'request_mask', - 'Command': 'comm', - 'Token': 'magic_token', - 'ErrorCode': 'error_code', - 'Network family': 'family', - 'Socket type': 'sock_type', - 'Local addr': 'net_local_addr', - 'Foreign addr': 'net_foreign_addr', - 'Local port': 'net_local_port', - 'Foreign port': 'net_foreign_port', - 'Audit subid': 'audit_sub_id', - 'Attribute': 'attr', - 'Epoch': 'time', + 'Mask': 'request_mask', + 'Command': 'comm', + 'Token': 'magic_token', + 'ErrorCode': 'error_code', + 'Network family': 'family', + 'Socket type': 'sock_type', + 'Local addr': 'net_local_addr', + 'Foreign addr': 'net_foreign_addr', + 'Local port': 'net_local_port', + 'Foreign port': 'net_foreign_port', + 'Audit subid': 'audit_sub_id', + 'Attribute': 'attr', + 'Epoch': 'time', } def _parse_libapparmor_test_multi(self, file_with_path): @@ -114,7 +114,8 @@ class TestLibapparmorTestMulti(AATest): expected = f_in.readlines() if expected[0].rstrip('\n') != 'START': - raise Exception("%s.out doesn't have 'START' in its first line! (%s)" % ( file_with_path, expected[0])) + raise Exception("%s.out doesn't have 'START' in its first line! (%s)" + % (file_with_path, expected[0])) expected.pop(0) @@ -129,7 +130,8 @@ class TestLibapparmorTestMulti(AATest): exresult[label] = value.strip() if not exresult['event_type'].startswith('AA_RECORD_'): - raise Exception("event_type doesn't start with AA_RECORD_: %s in file %s" % (exresult['event_type'], file_with_path)) + raise Exception("event_type doesn't start with AA_RECORD_: %s in file %s" + % (exresult['event_type'], file_with_path)) exresult['aamode'] = exresult['event_type'].replace('AA_RECORD_', '') if exresult['aamode'] == 'ALLOWED': @@ -142,6 +144,7 @@ class TestLibapparmorTestMulti(AATest): return exresult + # tests that cause crashes or need user interaction (will be skipped) log_to_skip = [ 'testcase_dbus_09', # multiline log not currently supported @@ -180,6 +183,7 @@ log_to_profile_known_empty_log = [ 'unconfined-change_hat', # unconfined trying to change_hat, which isn't allowed ] + class TestLogToProfile(AATest): '''Check if the libraries/libapparmor/testsuite/test_multi tests result in the expected profile''' @@ -215,7 +219,7 @@ def logfile_to_profile(logfile): aamode = parsed_event['aamode'] - if aamode in ('AUDIT', 'STATUS', 'HINT'): # ignore some event types # XXX maybe we shouldn't ignore AUDIT events? + if aamode in ('AUDIT', 'STATUS', 'HINT'): # ignore some event types # XXX maybe we shouldn't ignore AUDIT events? return None, aamode if aamode not in ('PERMITTING', 'REJECTING'): @@ -278,6 +282,7 @@ def logfile_to_profile(logfile): return profile, new_profile + def find_test_multi(log_dir): '''find all log sniplets in the given log_dir''' @@ -297,6 +302,7 @@ def find_test_multi(log_dir): return tests + # if a logfile is given as parameter, print the resulting profile and exit (with $? = 42 to make sure tests break if the caller accidentally hands over a parameter) if __name__ == '__main__' and len(sys.argv) == 2: print(logfile_to_profile(sys.argv[1])[1]) diff --git a/utils/test/test-logparser.py b/utils/test/test-logparser.py index e351cf0e3..e8e3dea30 100644 --- a/utils/test/test-logparser.py +++ b/utils/test/test-logparser.py @@ -16,9 +16,10 @@ import unittest from apparmor.logparser import ReadLog -from common_test import AATest, setup_all_loops # , setup_aa +from common_test import AATest, setup_all_loops # , setup_aa from apparmor.common import AppArmorException + class TestParseEvent(AATest): tests = () @@ -99,10 +100,11 @@ class TestParseEvent(AATest): self.assertIsNotNone(ReadLog.RE_LOG_ALL.search(event)) + class TestParseEventForTreeInvalid(AATest): tests = ( - ('type=AVC msg=audit(1556742870.707:3614): apparmor="ALLOWED" operation="open" profile="/bin/hello" name="/dev/tty" pid=12856 comm="hello" requested_mask="wr" denied_mask="foo" fsuid=1000 ouid=0', AppArmorException), # invalid file permissions "foo" - ('type=AVC msg=audit(1556742870.707:3614): apparmor="ALLOWED" operation="open" profile="/bin/hello" name="/dev/tty" pid=12856 comm="hello" requested_mask="wr" denied_mask="wr::w" fsuid=1000 ouid=0', AppArmorException), # "wr::w" mixes owner and other + ('type=AVC msg=audit(1556742870.707:3614): apparmor="ALLOWED" operation="open" profile="/bin/hello" name="/dev/tty" pid=12856 comm="hello" requested_mask="wr" denied_mask="foo" fsuid=1000 ouid=0', AppArmorException), # invalid file permissions "foo" + ('type=AVC msg=audit(1556742870.707:3614): apparmor="ALLOWED" operation="open" profile="/bin/hello" name="/dev/tty" pid=12856 comm="hello" requested_mask="wr" denied_mask="wr::w" fsuid=1000 ouid=0', AppArmorException), # "wr::w" mixes owner and other ) def _fake_profile_exists(self, program): @@ -115,6 +117,7 @@ class TestParseEventForTreeInvalid(AATest): with self.assertRaises(expected): self.parser.parse_event_for_tree(parsed_event) + setup_all_loops(__name__) if __name__ == "__main__": unittest.main(verbosity=1) diff --git a/utils/test/test-minitools.py b/utils/test/test-minitools.py index 3d4c516a5..62729547f 100755 --- a/utils/test/test-minitools.py +++ b/utils/test/test-minitools.py @@ -22,13 +22,14 @@ from common_test import read_file python_interpreter = 'python3' + class MinitoolsTest(AATest): def AASetup(self): self.createTmpdir() - #copy the local profiles to the test directory - #Should be the set of cleanprofile + # copy the local profiles to the test directory + # Should be the set of cleanprofile self.profile_dir = '%s/profiles' % self.tmpdir shutil.copytree('../../profiles/apparmor.d/', self.profile_dir, symlinks=True) @@ -41,111 +42,168 @@ class MinitoolsTest(AATest): def test_audit(self): # Set test profile to audit mode and check if it was correctly set - str(subprocess.check_output('%s ./../aa-audit --no-reload -d %s %s --configdir ./' % (python_interpreter, self.profile_dir, self.test_path), shell=True)) + str(subprocess.check_output( + '%s ./../aa-audit --no-reload -d %s %s --configdir ./' + % (python_interpreter, self.profile_dir, self.test_path), shell=True)) - self.assertEqual(apparmor.get_profile_flags(self.local_profilename, self.test_path), 'audit', - 'Audit flag could not be set in profile %s' % self.local_profilename) + self.assertEqual( + apparmor.get_profile_flags(self.local_profilename, self.test_path), + 'audit', + 'Audit flag could not be set in profile %s' % self.local_profilename) # Remove audit mode from test profile and check if it was correctly removed - subprocess.check_output('%s ./../aa-audit --no-reload -d %s -r %s --configdir ./' % (python_interpreter, self.profile_dir, self.test_path), shell=True) - - self.assertEqual(apparmor.get_profile_flags(self.local_profilename, self.test_path), None, - 'Audit flag could not be removed in profile %s' % self.local_profilename) + subprocess.check_output( + '%s ./../aa-audit --no-reload -d %s -r %s --configdir ./' + % (python_interpreter, self.profile_dir, self.test_path), shell=True) + self.assertEqual( + apparmor.get_profile_flags(self.local_profilename, self.test_path), + None, + 'Audit flag could not be removed in profile %s' % self.local_profilename) def test_complain(self): # Set test profile to complain mode and check if it was correctly set - subprocess.check_output('%s ./../aa-complain --no-reload -d %s %s --configdir ./' % (python_interpreter, self.profile_dir, self.test_path), shell=True) + subprocess.check_output( + '%s ./../aa-complain --no-reload -d %s %s --configdir ./' + % (python_interpreter, self.profile_dir, self.test_path), shell=True) # "manually" create a force-complain symlink (will be deleted by aa-enforce later) force_complain_dir = '%s/force-complain' % self.profile_dir if not os.path.isdir(force_complain_dir): os.mkdir(force_complain_dir) - os.symlink(self.local_profilename, '%s/%s' % (force_complain_dir, os.path.basename(self.local_profilename))) + os.symlink( + self.local_profilename, + '%s/%s' % (force_complain_dir, os.path.basename(self.local_profilename))) - self.assertEqual(os.path.islink('%s/%s' % (force_complain_dir, os.path.basename(self.local_profilename))), True, - 'Failed to create a symlink for %s in force-complain' % self.local_profilename) - self.assertEqual(apparmor.get_profile_flags(self.local_profilename, self.test_path), 'complain', - 'Complain flag could not be set in profile %s'%self.local_profilename) + self.assertEqual( + os.path.islink('%s/%s' % (force_complain_dir, os.path.basename(self.local_profilename))), + True, + 'Failed to create a symlink for %s in force-complain' % self.local_profilename) + self.assertEqual( + apparmor.get_profile_flags(self.local_profilename, self.test_path), + 'complain', + 'Complain flag could not be set in profile %s' % self.local_profilename) # Set test profile to enforce mode and check if it was correctly set - subprocess.check_output('%s ./../aa-enforce --no-reload -d %s %s --configdir ./'%(python_interpreter, self.profile_dir, self.test_path), shell=True) + subprocess.check_output( + '%s ./../aa-enforce --no-reload -d %s %s --configdir ./' + % (python_interpreter, self.profile_dir, self.test_path), shell=True) - self.assertEqual(os.path.islink('%s/%s' % (force_complain_dir, os.path.basename(self.local_profilename))), False, - 'Failed to remove symlink for %s from force-complain'%self.local_profilename) - self.assertEqual(os.path.islink('%s/disable/%s' % (self.profile_dir, os.path.basename(self.local_profilename))), False, - 'Failed to remove symlink for %s from disable'%self.local_profilename) - self.assertEqual(apparmor.get_profile_flags(self.local_profilename, self.test_path), None, - 'Complain flag could not be removed in profile %s'%self.local_profilename) + self.assertEqual( + os.path.islink('%s/%s' % (force_complain_dir, os.path.basename(self.local_profilename))), + False, + 'Failed to remove symlink for %s from force-complain' % self.local_profilename) + self.assertEqual( + os.path.islink('%s/disable/%s' % (self.profile_dir, os.path.basename(self.local_profilename))), + False, + 'Failed to remove symlink for %s from disable' % self.local_profilename) + self.assertEqual( + apparmor.get_profile_flags(self.local_profilename, self.test_path), + None, + 'Complain flag could not be removed in profile %s' % self.local_profilename) # Set audit flag and then complain flag in a profile - subprocess.check_output('%s ./../aa-audit --no-reload -d %s %s --configdir ./'%(python_interpreter, self.profile_dir, self.test_path), shell=True) - subprocess.check_output('%s ./../aa-complain --no-reload -d %s %s --configdir ./'%(python_interpreter, self.profile_dir, self.test_path), shell=True) + subprocess.check_output( + '%s ./../aa-audit --no-reload -d %s %s --configdir ./' + % (python_interpreter, self.profile_dir, self.test_path), shell=True) + subprocess.check_output( + '%s ./../aa-complain --no-reload -d %s %s --configdir ./' + % (python_interpreter, self.profile_dir, self.test_path), shell=True) # "manually" create a force-complain symlink (will be deleted by aa-enforce later) - os.symlink(self.local_profilename, '%s/%s'% (force_complain_dir, os.path.basename(self.local_profilename))) + os.symlink( + self.local_profilename, + '%s/%s' % (force_complain_dir, os.path.basename(self.local_profilename))) - self.assertEqual(os.path.islink('%s/%s' % (force_complain_dir, os.path.basename(self.local_profilename))), True, - 'Failed to create a symlink for %s in force-complain'%self.local_profilename) - self.assertEqual(apparmor.get_profile_flags(self.local_profilename, self.test_path), 'audit, complain', - 'Complain flag could not be set in profile %s'%self.local_profilename) + self.assertEqual( + os.path.islink('%s/%s' % (force_complain_dir, os.path.basename(self.local_profilename))), + True, + 'Failed to create a symlink for %s in force-complain' % self.local_profilename) + self.assertEqual( + apparmor.get_profile_flags(self.local_profilename, self.test_path), + 'audit, complain', + 'Complain flag could not be set in profile %s' % self.local_profilename) - #Remove complain flag first i.e. set to enforce mode - subprocess.check_output('%s ./../aa-enforce --no-reload -d %s %s --configdir ./'%(python_interpreter, self.profile_dir, self.test_path), shell=True) + # Remove complain flag first i.e. set to enforce mode + subprocess.check_output( + '%s ./../aa-enforce --no-reload -d %s %s --configdir ./' + % (python_interpreter, self.profile_dir, self.test_path), shell=True) - self.assertEqual(os.path.islink('%s/%s' % (force_complain_dir, os.path.basename(self.local_profilename))), False, - 'Failed to remove symlink for %s from force-complain'%self.local_profilename) - self.assertEqual(os.path.islink('%s/disable/%s' % (self.profile_dir, os.path.basename(self.local_profilename))), False, - 'Failed to remove symlink for %s from disable'%self.local_profilename) - self.assertEqual(apparmor.get_profile_flags(self.local_profilename, self.test_path), 'audit', - 'Complain flag could not be removed in profile %s'%self.local_profilename) + self.assertEqual( + os.path.islink('%s/%s' % (force_complain_dir, os.path.basename(self.local_profilename))), + False, + 'Failed to remove symlink for %s from force-complain' % self.local_profilename) + self.assertEqual( + os.path.islink('%s/disable/%s' % (self.profile_dir, os.path.basename(self.local_profilename))), + False, + 'Failed to remove symlink for %s from disable' % self.local_profilename) + self.assertEqual( + apparmor.get_profile_flags(self.local_profilename, self.test_path), + 'audit', + 'Complain flag could not be removed in profile %s' % self.local_profilename) - #Remove audit flag - subprocess.check_output('%s ./../aa-audit --no-reload -d %s -r %s --configdir ./'%(python_interpreter, self.profile_dir, self.test_path), shell=True) + # Remove audit flag + subprocess.check_output( + '%s ./../aa-audit --no-reload -d %s -r %s --configdir ./' + % (python_interpreter, self.profile_dir, self.test_path), shell=True) def test_enforce(self): # Set test profile to enforce mode and check if it was correctly set - subprocess.check_output('%s ./../aa-enforce --no-reload -d %s %s --configdir ./'%(python_interpreter, self.profile_dir, self.test_path), shell=True) - - self.assertEqual(os.path.islink('%s/force-complain/%s' % (self.profile_dir, os.path.basename(self.local_profilename))), False, - 'Failed to remove symlink for %s from force-complain'%self.local_profilename) - self.assertEqual(os.path.islink('%s/disable/%s' % (self.profile_dir, os.path.basename(self.local_profilename))), False, - 'Failed to remove symlink for %s from disable'%self.local_profilename) - self.assertEqual(apparmor.get_profile_flags(self.local_profilename, self.test_path), None, - 'Complain flag could not be removed in profile %s'%self.local_profilename) + subprocess.check_output( + '%s ./../aa-enforce --no-reload -d %s %s --configdir ./' + % (python_interpreter, self.profile_dir, self.test_path), shell=True) + self.assertEqual( + os.path.islink('%s/force-complain/%s' % (self.profile_dir, os.path.basename(self.local_profilename))), + False, + 'Failed to remove symlink for %s from force-complain' % self.local_profilename) + self.assertEqual( + os.path.islink('%s/disable/%s' % (self.profile_dir, os.path.basename(self.local_profilename))), + False, + 'Failed to remove symlink for %s from disable' % self.local_profilename) + self.assertEqual( + apparmor.get_profile_flags(self.local_profilename, self.test_path), + None, + 'Complain flag could not be removed in profile %s' % self.local_profilename) def test_disable(self): # Disable the test profile and check if it was correctly disabled - subprocess.check_output('%s ./../aa-disable --no-reload -d %s %s --configdir ./'%(python_interpreter, self.profile_dir, self.test_path), shell=True) + subprocess.check_output( + '%s ./../aa-disable --no-reload -d %s %s --configdir ./' + % (python_interpreter, self.profile_dir, self.test_path), shell=True) - self.assertEqual(os.path.islink('%s/disable/%s' % (self.profile_dir, os.path.basename(self.local_profilename))), True, - 'Failed to create a symlink for %s in disable' % self.local_profilename) + self.assertEqual( + os.path.islink('%s/disable/%s' % (self.profile_dir, os.path.basename(self.local_profilename))), + True, + 'Failed to create a symlink for %s in disable' % self.local_profilename) def test_autodep(self): pass @unittest.skipIf(apparmor.check_for_apparmor() is None, "Securityfs not mounted or doesn't have the apparmor directory.") def test_unconfined(self): - output = subprocess.check_output('%s ./../aa-unconfined --configdir ./'%python_interpreter, shell=True) + output = subprocess.check_output( + '%s ./../aa-unconfined --configdir ./' % python_interpreter, shell=True) - output_force = subprocess.check_output('%s ./../aa-unconfined --paranoid --configdir ./'%python_interpreter, shell=True) + output_force = subprocess.check_output( + '%s ./../aa-unconfined --paranoid --configdir ./' % python_interpreter, shell=True) self.assertIsNot(output, '', 'Failed to run aa-unconfined') self.assertIsNot(output_force, '', 'Failed to run aa-unconfined in paranoid mode') - def test_cleanprof(self): input_file = 'cleanprof_test.in' output_file = 'cleanprof_test.out' - #We position the local testfile - shutil.copy('./%s'%input_file, self.profile_dir) - #Our silly test program whose profile we wish to clean + # We position the local testfile + shutil.copy('./%s' % input_file, self.profile_dir) + # Our silly test program whose profile we wish to clean cleanprof_test = '/usr/bin/a/simple/cleanprof/test/profile' - subprocess.check_output('%s ./../aa-cleanprof --no-reload -d %s -s %s --configdir ./' % (python_interpreter, self.profile_dir, cleanprof_test), shell=True) + subprocess.check_output( + '%s ./../aa-cleanprof --no-reload -d %s -s %s --configdir ./' + % (python_interpreter, self.profile_dir, cleanprof_test), shell=True) - #Strip off the first line (#modified line) + # Strip off the first line (#modified line) subprocess.check_output('sed -i 1d %s/%s' % (self.profile_dir, input_file), shell=True) exp_content = read_file('./%s' % output_file) diff --git a/utils/test/test-mount_parse.py b/utils/test/test-mount_parse.py index bdb68f821..775a8bea3 100644 --- a/utils/test/test-mount_parse.py +++ b/utils/test/test-mount_parse.py @@ -13,10 +13,12 @@ import apparmor.aa as aa import unittest from common_test import AAParseTest, setup_regex_tests, setup_aa + class BaseAAParseMountTest(AAParseTest): def setUp(self): self.parse_function = aa.parse_mount_rule + class AAParseMountTest(BaseAAParseMountTest): tests = ( ('mount,', 'mount base keyword rule'), @@ -24,6 +26,7 @@ class AAParseMountTest(BaseAAParseMountTest): ('mount -o rw /dev/sdb1 -> /mnt/external,', 'mount rw with mount point'), ) + class AAParseRemountTest(BaseAAParseMountTest): tests = ( ('remount,', 'remount base keyword rule'), @@ -31,6 +34,7 @@ class AAParseRemountTest(BaseAAParseMountTest): ('remount -o ro /,', 'remount ro with mountpoint'), ) + class AAParseUmountTest(BaseAAParseMountTest): tests = ( ('umount,', 'umount base keyword rule'), @@ -39,6 +43,7 @@ class AAParseUmountTest(BaseAAParseMountTest): ('unmount /mnt/external,', 'unmount with mount point'), ) + setup_aa(aa) if __name__ == '__main__': setup_regex_tests(AAParseMountTest) diff --git a/utils/test/test-network.py b/utils/test/test-network.py index 360a07568..4c144b43c 100644 --- a/utils/test/test-network.py +++ b/utils/test/test-network.py @@ -24,11 +24,13 @@ from apparmor.logparser import ReadLog from apparmor.translations import init_translation _ = init_translation() -exp = namedtuple('exp', ('audit', 'allow_keyword', 'deny', 'comment', - 'domain', 'all_domains', 'type_or_protocol', 'all_type_or_protocols')) +exp = namedtuple( + 'exp', ('audit', 'allow_keyword', 'deny', 'comment', + 'domain', 'all_domains', 'type_or_protocol', 'all_type_or_protocols')) # --- check if the keyword list is up to date --- # + class NetworkKeywordsTest(AATest): def test_network_keyword_list(self): rc, output = cmd('../../common/list_af_names.sh') @@ -48,11 +50,15 @@ class NetworkKeywordsTest(AATest): # keywords missing in the system are ok (= older kernel), but network_domain_keywords needs to have the full list missing_af_names.append(keyword) - self.assertEqual(missing_af_names, [], 'Missing af_names in NetworkRule network_domain_keywords. This test is likely running ' - 'on an newer kernel and will require updating the list of network domain keywords in utils/apparmor/rule/network.py') + self.assertEqual( + missing_af_names, [], + 'Missing af_names in NetworkRule network_domain_keywords. This test is likely running ' + 'on an newer kernel and will require updating the list of network domain keywords in ' + 'utils/apparmor/rule/network.py') # --- tests for single NetworkRule --- # + class NetworkTest(AATest): def _compare_obj(self, obj, expected): self.assertEqual(expected.allow_keyword, obj.allow_keyword) @@ -64,15 +70,16 @@ class NetworkTest(AATest): self.assertEqual(expected.deny, obj.deny) self.assertEqual(expected.comment, obj.comment) + class NetworkTestParse(NetworkTest): tests = ( - # rawrule audit allow deny comment domain all? type/proto all? - ('network,' , exp(False, False, False, '' , None , True , None , True )), - ('network inet,' , exp(False, False, False, '' , 'inet', False, None , True )), - ('network inet stream,' , exp(False, False, False, '' , 'inet', False, 'stream' , False)), - ('deny network inet stream, # comment' , exp(False, False, True , ' # comment' , 'inet', False, 'stream' , False)), - ('audit allow network tcp,' , exp(True , True , False, '' , None , True , 'tcp' , False)), - ('network stream,' , exp(False, False, False, '' , None , True , 'stream' , False)), + # rawrule audit allow deny comment domain all? type/proto all? + ('network,', exp(False, False, False, '', None, True, None, True)), + ('network inet,', exp(False, False, False, '', 'inet', False, None, True)), + ('network inet stream,', exp(False, False, False, '', 'inet', False, 'stream', False)), + ('deny network inet stream, # comment', exp(False, False, True, ' # comment', 'inet', False, 'stream', False)), + ('audit allow network tcp,', exp(True, True, False, '', None, True, 'tcp', False)), + ('network stream,', exp(False, False, False, '', None, True, 'stream', False)), ) def _run_test(self, rawrule, expected): @@ -81,12 +88,13 @@ class NetworkTestParse(NetworkTest): self.assertEqual(rawrule.strip(), obj.raw_rule) self._compare_obj(obj, expected) + class NetworkTestParseInvalid(NetworkTest): tests = ( - ('network foo,' , AppArmorException), - ('network foo bar,' , AppArmorException), - ('network foo tcp,' , AppArmorException), - ('network inet bar,' , AppArmorException), + ('network foo,', AppArmorException), + ('network foo bar,', AppArmorException), + ('network foo tcp,', AppArmorException), + ('network inet bar,', AppArmorException), ) def _run_test(self, rawrule, expected): @@ -94,6 +102,7 @@ class NetworkTestParseInvalid(NetworkTest): with self.assertRaises(expected): NetworkRule.parse(rawrule) + class NetworkTestParseFromLog(NetworkTest): def test_net_from_log(self): parser = ReadLog('', '', '') @@ -126,8 +135,8 @@ class NetworkTestParseFromLog(NetworkTest): obj = NetworkRule(parsed_event['family'], parsed_event['sock_type'], log_event=parsed_event) - # audit allow deny comment domain all? type/proto all? - expected = exp(False, False, False, '' , 'inet', False, 'raw' , False) + # audit allow deny comment domain all? type/proto all? + expected = exp(False, False, False, '', 'inet', False, 'raw', False) self._compare_obj(obj, expected) @@ -136,13 +145,13 @@ class NetworkTestParseFromLog(NetworkTest): class NetworkFromInit(NetworkTest): tests = ( - # NetworkRule object audit allow deny comment domain all? type/proto all? - (NetworkRule('inet', 'raw', deny=True) , exp(False, False, True , '' , 'inet', False, 'raw' , False)), - (NetworkRule('inet', 'raw') , exp(False, False, False, '' , 'inet', False, 'raw' , False)), - (NetworkRule('inet', NetworkRule.ALL) , exp(False, False, False, '' , 'inet', False, None , True )), - (NetworkRule(NetworkRule.ALL, NetworkRule.ALL) , exp(False, False, False, '' , None , True , None , True )), - (NetworkRule(NetworkRule.ALL, 'tcp') , exp(False, False, False, '' , None , True , 'tcp' , False)), - (NetworkRule(NetworkRule.ALL, 'stream') , exp(False, False, False, '' , None , True , 'stream' , False)), + # NetworkRule object audit allow deny comment domain all? type/proto all? + (NetworkRule('inet', 'raw', deny=True), exp(False, False, True, '', 'inet', False, 'raw', False)), + (NetworkRule('inet', 'raw'), exp(False, False, False, '', 'inet', False, 'raw', False)), + (NetworkRule('inet', NetworkRule.ALL), exp(False, False, False, '', 'inet', False, None, True)), + (NetworkRule(NetworkRule.ALL, NetworkRule.ALL), exp(False, False, False, '', None, True, None, True)), + (NetworkRule(NetworkRule.ALL, 'tcp'), exp(False, False, False, '', None, True, 'tcp', False)), + (NetworkRule(NetworkRule.ALL, 'stream'), exp(False, False, False, '', None, True, 'stream', False)), ) def _run_test(self, obj, expected): @@ -151,17 +160,17 @@ class NetworkFromInit(NetworkTest): class InvalidNetworkInit(AATest): tests = ( - # init params expected exception - (('inet', '' ) , AppArmorBug), # empty type_or_protocol - (('' , 'tcp' ) , AppArmorBug), # empty domain - ((' ', 'tcp' ) , AppArmorBug), # whitespace domain - (('inet', ' ' ) , AppArmorBug), # whitespace type_or_protocol - (('xyxy', 'tcp' ) , AppArmorBug), # invalid domain - (('inet', 'xyxy' ) , AppArmorBug), # invalid type_or_protocol - ((dict(), 'tcp' ) , AppArmorBug), # wrong type for domain - ((None , 'tcp' ) , AppArmorBug), # wrong type for domain - (('inet', dict() ) , AppArmorBug), # wrong type for type_or_protocol - (('inet', None ) , AppArmorBug), # wrong type for type_or_protocol + # init params expected exception + (('inet', ''), AppArmorBug), # empty type_or_protocol + (('', 'tcp'), AppArmorBug), # empty domain + ((' ', 'tcp'), AppArmorBug), # whitespace domain + (('inet', ' '), AppArmorBug), # whitespace type_or_protocol + (('xyxy', 'tcp'), AppArmorBug), # invalid domain + (('inet', 'xyxy'), AppArmorBug), # invalid type_or_protocol + ((dict(), 'tcp'), AppArmorBug), # wrong type for domain + ((None, 'tcp'), AppArmorBug), # wrong type for domain + (('inet', dict()), AppArmorBug), # wrong type for type_or_protocol + (('inet', None), AppArmorBug), # wrong type for type_or_protocol ) def _run_test(self, params, expected): @@ -218,12 +227,12 @@ class WriteNetworkTestAATest(AATest): self.assertEqual(rawrule.strip(), raw, 'unexpected raw rule') tests = ( - # raw rule clean rule - (' network , # foo ' , 'network, # foo'), - (' audit network inet,' , 'audit network inet,'), - (' deny network inet stream,# foo bar' , 'deny network inet stream, # foo bar'), - (' deny network inet ,# foo bar' , 'deny network inet, # foo bar'), - (' allow network tcp ,# foo bar' , 'allow network tcp, # foo bar'), + # raw rule clean rule + (' network , # foo ', 'network, # foo'), + (' audit network inet,', 'audit network inet,'), + (' deny network inet stream,# foo bar', 'deny network inet stream, # foo bar'), + (' deny network inet ,# foo bar', 'deny network inet, # foo bar'), + (' allow network tcp ,# foo bar', 'allow network tcp, # foo bar'), ) def test_write_manually(self): @@ -242,44 +251,54 @@ class NetworkCoveredTest(AATest): self.assertTrue(NetworkRule.match(param)) - self.assertEqual(obj.is_equal(check_obj), expected[0], 'Mismatch in is_equal, expected %s' % expected[0]) - self.assertEqual(obj.is_equal(check_obj, True), expected[1], 'Mismatch in is_equal/strict, expected %s' % expected[1]) + self.assertEqual( + obj.is_equal(check_obj), expected[0], + 'Mismatch in is_equal, expected %s' % expected[0]) + self.assertEqual( + obj.is_equal(check_obj, True), expected[1], + 'Mismatch in is_equal/strict, expected %s' % expected[1]) + + self.assertEqual( + obj.is_covered(check_obj), expected[2], + 'Mismatch in is_covered, expected %s' % expected[2]) + self.assertEqual( + obj.is_covered(check_obj, True, True), expected[3], + 'Mismatch in is_covered/exact, expected %s' % expected[3]) - self.assertEqual(obj.is_covered(check_obj), expected[2], 'Mismatch in is_covered, expected %s' % expected[2]) - self.assertEqual(obj.is_covered(check_obj, True, True), expected[3], 'Mismatch in is_covered/exact, expected %s' % expected[3]) class NetworkCoveredTest_01(NetworkCoveredTest): rule = 'network inet,' tests = ( - # rule equal strict equal covered covered exact - ('network,' , ( False , False , False , False )), - ('network inet,' , ( True , True , True , True )), - ('network inet, # comment' , ( True , False , True , True )), - ('allow network inet,' , ( True , False , True , True )), - ('network inet,' , ( True , False , True , True )), - ('network inet stream,' , ( False , False , True , True )), - ('network inet tcp,' , ( False , False , True , True )), - ('audit network inet,' , ( False , False , False , False )), - ('audit network,' , ( False , False , False , False )), - ('network unix,' , ( False , False , False , False )), - ('network tcp,' , ( False , False , False , False )), - ('audit deny network inet,' , ( False , False , False , False )), - ('deny network inet,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('network,', (False, False, False, False)), + ('network inet,', (True, True, True, True)), + ('network inet, # comment', (True, False, True, True)), + ('allow network inet,', (True, False, True, True)), + ('network inet,', (True, False, True, True)), + ('network inet stream,', (False, False, True, True)), + ('network inet tcp,', (False, False, True, True)), + ('audit network inet,', (False, False, False, False)), + ('audit network,', (False, False, False, False)), + ('network unix,', (False, False, False, False)), + ('network tcp,', (False, False, False, False)), + ('audit deny network inet,', (False, False, False, False)), + ('deny network inet,', (False, False, False, False)), ) + class NetworkCoveredTest_02(NetworkCoveredTest): rule = 'audit network inet,' tests = ( - # rule equal strict equal covered covered exact - ( 'network inet,' , ( False , False , True , False )), - ('audit network inet,' , ( True , True , True , True )), - ( 'network inet stream,' , ( False , False , True , False )), - ('audit network inet stream,' , ( False , False , True , True )), - ( 'network,' , ( False , False , False , False )), - ('audit network,' , ( False , False , False , False )), - ('network unix,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'network inet,', (False, False, True, False)), + ('audit network inet,', (True, True, True, True)), + ( 'network inet stream,', (False, False, True, False)), + ('audit network inet stream,', (False, False, True, True)), + ( 'network,', (False, False, False, False)), + ('audit network,', (False, False, False, False)), + ('network unix,', (False, False, False, False)), ) @@ -287,43 +306,45 @@ class NetworkCoveredTest_03(NetworkCoveredTest): rule = 'network inet stream,' tests = ( - # rule equal strict equal covered covered exact - ( 'network inet stream,' , ( True , True , True , True )), - ('allow network inet stream,' , ( True , False , True , True )), - ( 'network inet,' , ( False , False , False , False )), - ( 'network,' , ( False , False , False , False )), - ( 'network inet tcp,' , ( False , False , False , False )), - ('audit network,' , ( False , False , False , False )), - ('audit network inet stream,' , ( False , False , False , False )), - ( 'network unix,' , ( False , False , False , False )), - ( 'network,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'network inet stream,', (True, True, True, True)), + ('allow network inet stream,', (True, False, True, True)), + ( 'network inet,', (False, False, False, False)), + ( 'network,', (False, False, False, False)), + ( 'network inet tcp,', (False, False, False, False)), + ('audit network,', (False, False, False, False)), + ('audit network inet stream,', (False, False, False, False)), + ( 'network unix,', (False, False, False, False)), + ( 'network,', (False, False, False, False)), ) + class NetworkCoveredTest_04(NetworkCoveredTest): rule = 'network,' tests = ( - # rule equal strict equal covered covered exact - ( 'network,' , ( True , True , True , True )), - ('allow network,' , ( True , False , True , True )), - ( 'network inet,' , ( False , False , True , True )), - ( 'network inet6 stream,' , ( False , False , True , True )), - ( 'network tcp,' , ( False , False , True , True )), - ( 'network inet raw,' , ( False , False , True , True )), - ('audit network,' , ( False , False , False , False )), - ('deny network,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'network,', (True, True, True, True)), + ('allow network,', (True, False, True, True)), + ( 'network inet,', (False, False, True, True)), + ( 'network inet6 stream,', (False, False, True, True)), + ( 'network tcp,', (False, False, True, True)), + ( 'network inet raw,', (False, False, True, True)), + ('audit network,', (False, False, False, False)), + ('deny network,', (False, False, False, False)), ) + class NetworkCoveredTest_05(NetworkCoveredTest): rule = 'deny network inet,' tests = ( - # rule equal strict equal covered covered exact - ( 'deny network inet,' , ( True , True , True , True )), - ('audit deny network inet,' , ( False , False , False , False )), - ( 'network inet,' , ( False , False , False , False )), # XXX should covered be true here? - ( 'deny network unix,' , ( False , False , False , False )), - ( 'deny network,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'deny network inet,', (True, True, True, True)), + ('audit deny network inet,', (False, False, False, False)), + ( 'network inet,', (False, False, False, False)), # XXX should covered be true here? + ( 'deny network unix,', (False, False, False, False)), + ( 'deny network,', (False, False, False, False)), ) @@ -362,26 +383,29 @@ class NetworkCoveredTest_Invalid(AATest): with self.assertRaises(AppArmorBug): obj.is_equal(testobj) + class NetworkLogprofHeaderTest(AATest): tests = ( - ('network,', [ _('Network Family'), _('ALL'), _('Socket Type'), _('ALL'), ]), - ('network inet,', [ _('Network Family'), 'inet', _('Socket Type'), _('ALL'), ]), - ('network inet stream,', [ _('Network Family'), 'inet', _('Socket Type'), 'stream', ]), - ('deny network,', [_('Qualifier'), 'deny', _('Network Family'), _('ALL'), _('Socket Type'), _('ALL'), ]), - ('allow network inet,', [_('Qualifier'), 'allow', _('Network Family'), 'inet', _('Socket Type'), _('ALL'), ]), - ('audit network inet stream,', [_('Qualifier'), 'audit', _('Network Family'), 'inet', _('Socket Type'), 'stream', ]), - ('audit deny network inet,', [_('Qualifier'), 'audit deny', _('Network Family'), 'inet', _('Socket Type'), _('ALL'), ]), + ('network,', [ _('Network Family'), _('ALL'), _('Socket Type'), _('ALL')]), + ('network inet,', [ _('Network Family'), 'inet', _('Socket Type'), _('ALL')]), + ('network inet stream,', [ _('Network Family'), 'inet', _('Socket Type'), 'stream']), + ('deny network,', [_('Qualifier'), 'deny', _('Network Family'), _('ALL'), _('Socket Type'), _('ALL')]), + ('allow network inet,', [_('Qualifier'), 'allow', _('Network Family'), 'inet', _('Socket Type'), _('ALL')]), + ('audit network inet stream,', [_('Qualifier'), 'audit', _('Network Family'), 'inet', _('Socket Type'), 'stream']), + ('audit deny network inet,', [_('Qualifier'), 'audit deny', _('Network Family'), 'inet', _('Socket Type'), _('ALL')]), ) def _run_test(self, params, expected): obj = NetworkRule.parse(params) self.assertEqual(obj.logprof_header(), expected) + class NetworkRuleReprTest(AATest): tests = ( (NetworkRule('inet', 'stream'), ' network inet stream,'), (NetworkRule.parse(' allow network inet stream, # foo'), ' allow network inet stream, # foo'), ) + def _run_test(self, params, expected): self.assertEqual(str(params), expected) @@ -469,9 +493,11 @@ class NetworkGlobTestAATest(AATest): # get_glob_ext is not available for network rules self.ruleset.get_glob_ext('network inet raw,') + class NetworkDeleteTestAATest(AATest): pass + class NetworkRulesetReprTest(AATest): def test_network_ruleset_repr(self): obj = NetworkRuleset() @@ -482,7 +508,6 @@ class NetworkRulesetReprTest(AATest): self.assertEqual(str(obj), expected) - setup_all_loops(__name__) if __name__ == '__main__': unittest.main(verbosity=1) diff --git a/utils/test/test-notify.py b/utils/test/test-notify.py index b00b40686..69f7ed322 100644 --- a/utils/test/test-notify.py +++ b/utils/test/test-notify.py @@ -15,27 +15,29 @@ from common_test import AATest, setup_all_loops from apparmor.common import AppArmorBug from apparmor.notify import get_last_login_timestamp, sane_timestamp + class TestSane_timestamp(AATest): tests = ( - (2524704400, False), # Sun Jan 2 03:46:40 CET 2050 - ( 944780400, False), # Fri Dec 10 00:00:00 CET 1999 - (1635026400, True ), # Sun Oct 24 00:00:00 CEST 2021 + (2524704400, False), # Sun Jan 2 03:46:40 CET 2050 + (944780400, False), # Fri Dec 10 00:00:00 CET 1999 + (1635026400, True), # Sun Oct 24 00:00:00 CEST 2021 ) def _run_test(self, params, expected): self.assertEqual(sane_timestamp(params), expected) + class TestGet_last_login_timestamp(AATest): tests = ( - (('wtmp-x86_64', 'root' ), 1635070346), # Sun Oct 24 12:12:26 CEST 2021 - (('wtmp-x86_64', 'whoever' ), 0), - (('wtmp-s390x', 'root' ), 1626368763), # Thu Jul 15 19:06:03 CEST 2021 - (('wtmp-s390x', 'linux1' ), 1626368772), # Thu Jul 15 19:06:12 CEST 2021 - (('wtmp-s390x', 'whoever' ), 0), - (('wtmp-aarch64', 'guillaume' ), 1611562789), # Mon Jan 25 09:19:49 CET 2021 - (('wtmp-aarch64', 'whoever' ), 0), - (('wtmp-truncated', 'root' ), 0), - (('wtmp-truncated', 'whoever' ), 0), + (('wtmp-x86_64', 'root'), 1635070346), # Sun Oct 24 12:12:26 CEST 2021 + (('wtmp-x86_64', 'whoever'), 0), + (('wtmp-s390x', 'root'), 1626368763), # Thu Jul 15 19:06:03 CEST 2021 + (('wtmp-s390x', 'linux1'), 1626368772), # Thu Jul 15 19:06:12 CEST 2021 + (('wtmp-s390x', 'whoever'), 0), + (('wtmp-aarch64', 'guillaume'), 1611562789), # Mon Jan 25 09:19:49 CET 2021 + (('wtmp-aarch64', 'whoever'), 0), + (('wtmp-truncated', 'root'), 0), + (('wtmp-truncated', 'whoever'), 0), ) def _run_test(self, params, expected): diff --git a/utils/test/test-parser-simple-tests.py b/utils/test/test-parser-simple-tests.py index 16ed7a7e1..1e9414d50 100644 --- a/utils/test/test-parser-simple-tests.py +++ b/utils/test/test-parser-simple-tests.py @@ -406,6 +406,7 @@ syntax_failure = ( 'bare_include_tests/ok_2.sd', # two #include<...> in one line ) + class TestParseParserTests(AATest): tests = [] # filled by parse_test_profiles() @@ -434,6 +435,7 @@ class TestParseParserTests(AATest): apparmor.parse_profile_data(data, params['file'], 0, True) apparmor.active_profiles.get_all_merged_variables(params['file'], apparmor.include_list_recursive(apparmor.active_profiles.files[params['file']])) + def parse_test_profiles(file_with_path): '''parse the test-related headers of a profile (for example EXRESULT) and add the profile to the set of tests''' exresult = None diff --git a/utils/test/test-pivot_root_parse.py b/utils/test/test-pivot_root_parse.py index 14f75e624..f9d2dcf20 100644 --- a/utils/test/test-pivot_root_parse.py +++ b/utils/test/test-pivot_root_parse.py @@ -13,17 +13,19 @@ import apparmor.aa as aa import unittest from common_test import AAParseTest, setup_regex_tests, setup_aa + 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,', '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) diff --git a/utils/test/test-profile-list.py b/utils/test/test-profile-list.py index 79c7ca148..6d68710c6 100644 --- a/utils/test/test-profile-list.py +++ b/utils/test/test-profile-list.py @@ -25,6 +25,7 @@ from apparmor.rule.boolean import BooleanRule from apparmor.rule.include import IncludeRule from apparmor.rule.variable import VariableRule + class TestAdd_profile(AATest): def AASetup(self): self.pl = ProfileList() @@ -98,24 +99,30 @@ class TestAdd_profile(AATest): with self.assertRaises(AppArmorBug): self.pl.add_profile('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo', 'wrong_type') + class TestFilename_from_profile_name(AATest): tests = ( ('foo', '/etc/apparmor.d/bin.foo'), ('/bin/foo', None), ('bar', None), - ('/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}', '/etc/apparmor.d/usr.bin.wine'), - ('/usr/lib/wine/bin/wine-preloader-staging-foo', None), # no AARE matching for profile names + ('/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}', '/etc/apparmor.d/usr.bin.wine'), + ('/usr/lib/wine/bin/wine-preloader-staging-foo', None), # no AARE matching for profile names ) def AASetup(self): self.pl = ProfileList() self.dummy_profile = ProfileStorage('TEST DUMMY', 'AATest_no_file', 'TEST') self.pl.add_profile('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo', self.dummy_profile) - self.pl.add_profile('/etc/apparmor.d/usr.bin.wine', '/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}', '/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}', self.dummy_profile) + self.pl.add_profile( + '/etc/apparmor.d/usr.bin.wine', + '/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}', + '/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}', + self.dummy_profile) def _run_test(self, params, expected): self.assertEqual(self.pl.filename_from_profile_name(params), expected) + class TestFilename_from_attachment(AATest): tests = ( ('/bin/foo', '/etc/apparmor.d/bin.foo'), @@ -123,8 +130,8 @@ class TestFilename_from_attachment(AATest): ('/bin/foobar', '/etc/apparmor.d/bin.foobar'), ('@{foo}', None), # XXX variables not supported yet (and @{foo} isn't defined in this test) ('/bin/404', None), - ('/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}', '/etc/apparmor.d/usr.bin.wine'), # XXX should this really match, or should attachment matching only use AARE? - ('/usr/lib/wine/bin/wine-preloader-staging-foo', '/etc/apparmor.d/usr.bin.wine'), # AARE match + ('/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}', '/etc/apparmor.d/usr.bin.wine'), # XXX should this really match, or should attachment matching only use AARE? + ('/usr/lib/wine/bin/wine-preloader-staging-foo', '/etc/apparmor.d/usr.bin.wine'), # AARE match ) def AASetup(self): @@ -133,7 +140,11 @@ class TestFilename_from_attachment(AATest): self.pl.add_profile('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo', self.dummy_profile) self.pl.add_profile('/etc/apparmor.d/bin.baz', 'baz', '/bin/ba*', self.dummy_profile) self.pl.add_profile('/etc/apparmor.d/bin.foobar', 'foobar', '/bin/foo{bar,baz}', self.dummy_profile) - self.pl.add_profile('/etc/apparmor.d/usr.bin.wine', '/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}', '/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}', self.dummy_profile) + self.pl.add_profile( + '/etc/apparmor.d/usr.bin.wine', + '/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}', + '/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}', + self.dummy_profile) def _run_test(self, params, expected): self.assertEqual(self.pl.filename_from_attachment(params), expected) @@ -142,6 +153,7 @@ class TestFilename_from_attachment(AATest): with self.assertRaises(AppArmorBug): self.pl.filename_from_attachment('foo') + class TestAdd_inc_ie(AATest): def AASetup(self): self.pl = ProfileList() @@ -179,6 +191,7 @@ class TestAdd_inc_ie(AATest): self.pl.delete_preamble_duplicates('/file/not/found') self.assertEqual(list(self.pl.files.keys()), []) + class TestAdd_abi(AATest): def AASetup(self): self.pl = ProfileList() @@ -210,6 +223,7 @@ class TestAdd_abi(AATest): self.assertEqual(self.pl.get_clean('/etc/apparmor.d/bin.foo'), ['abi ,', '']) self.assertEqual(self.pl.get_raw('/etc/apparmor.d/bin.foo'), ['abi ,', '']) + class TestAdd_alias(AATest): def AASetup(self): self.pl = ProfileList() @@ -259,6 +273,7 @@ class TestAdd_alias(AATest): self.assertEqual(self.pl.get_clean('/etc/apparmor.d/bin.foo'), ['alias /foo -> /bar,', 'alias /foo -> /another_target,', '']) self.assertEqual(self.pl.get_raw('/etc/apparmor.d/bin.foo'), ['alias /foo -> /bar,', 'alias /foo -> /another_target,', '']) + class TestAdd_variable(AATest): def AASetup(self): self.pl = ProfileList() @@ -296,6 +311,7 @@ class TestAdd_variable(AATest): self.pl.delete_preamble_duplicates('/file/not/found') self.assertEqual(list(self.pl.files.keys()), []) + class TestAdd_boolean(AATest): def AASetup(self): self.pl = ProfileList() @@ -318,6 +334,7 @@ class TestAdd_boolean(AATest): self.pl.add_boolean('/etc/apparmor.d/bin.foo', '$foo') # str insteadd of IncludeRule self.assertEqual(list(self.pl.files.keys()), []) + class TestGet(AATest): def AASetup(self): self.pl = ProfileList() @@ -330,6 +347,7 @@ class TestGet(AATest): with self.assertRaises(AppArmorBug): self.pl.get_raw('/etc/apparmor.d/not.found') + class AaTest_get_all_merged_variables(AATest): tests = () @@ -352,7 +370,9 @@ class AaTest_get_all_merged_variables(AATest): def test_unchanged(self): self._load_profiles() prof_filename = os.path.join(self.profile_dir, 'usr.sbin.dnsmasq') - vars = apparmor.aa.active_profiles.get_all_merged_variables(os.path.join(self.profile_dir, 'usr.sbin.dnsmasq'), apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename], True)) + vars = apparmor.aa.active_profiles.get_all_merged_variables( + os.path.join(self.profile_dir, 'usr.sbin.dnsmasq'), + apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename], True)) self.assertEqual(vars['@{TFTP_DIR}'], {'/var/tftp', '/srv/tftp', '/srv/tftpboot'}) self.assertEqual(vars['@{HOME}'], {'@{HOMEDIRS}/*/', '/root/'}) @@ -360,7 +380,9 @@ class AaTest_get_all_merged_variables(AATest): write_file(self.profile_dir, 'tunables/home.d/extend_home', '@{HOME} += /my/castle/') self._load_profiles() prof_filename = os.path.join(self.profile_dir, 'usr.sbin.dnsmasq') - vars = apparmor.aa.active_profiles.get_all_merged_variables(os.path.join(self.profile_dir, 'usr.sbin.dnsmasq'), apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename], True)) + vars = apparmor.aa.active_profiles.get_all_merged_variables( + os.path.join(self.profile_dir, 'usr.sbin.dnsmasq'), + apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename], True)) self.assertEqual(vars['@{TFTP_DIR}'], {'/var/tftp', '/srv/tftp', '/srv/tftpboot'}) self.assertEqual(vars['@{HOME}'], {'@{HOMEDIRS}/*/', '/root/', '/my/castle/'}) @@ -369,7 +391,9 @@ class AaTest_get_all_merged_variables(AATest): write_file(self.profile_dir, 'tunables/home.d/moving_around', '@{HOME} += /on/the/road/') self._load_profiles() prof_filename = os.path.join(self.profile_dir, 'usr.sbin.dnsmasq') - vars = apparmor.aa.active_profiles.get_all_merged_variables(os.path.join(self.profile_dir, 'usr.sbin.dnsmasq'), apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename], True)) + vars = apparmor.aa.active_profiles.get_all_merged_variables( + os.path.join(self.profile_dir, 'usr.sbin.dnsmasq'), + apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename], True)) self.assertEqual(vars['@{TFTP_DIR}'], {'/var/tftp', '/srv/tftp', '/srv/tftpboot'}) self.assertEqual(vars['@{HOME}'], {'@{HOMEDIRS}/*/', '/root/', '/my/castle/', '/on/the/road/'}) @@ -378,7 +402,9 @@ class AaTest_get_all_merged_variables(AATest): write_file(self.profile_dir, 'dummy_profile', 'include \n@{HOME} += /in/the/profile/') self._load_profiles() prof_filename = os.path.join(self.profile_dir, 'dummy_profile') - vars = apparmor.aa.active_profiles.get_all_merged_variables(os.path.join(self.profile_dir, 'dummy_profile'), apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename], True)) + vars = apparmor.aa.active_profiles.get_all_merged_variables( + os.path.join(self.profile_dir, 'dummy_profile'), + apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename], True)) self.assertEqual(vars.get('@{TFTP_DIR}', None), None) self.assertEqual(vars['@{HOME}'], {'@{HOMEDIRS}/*/', '/root/', '/my/castle/', '/in/the/profile/'}) @@ -387,18 +413,24 @@ class AaTest_get_all_merged_variables(AATest): self._load_profiles() prof_filename = os.path.join(self.profile_dir, 'usr.sbin.dnsmasq') with self.assertRaises(AppArmorException): - apparmor.aa.active_profiles.get_all_merged_variables(os.path.join(self.profile_dir, 'usr.sbin.dnsmasq'), apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename], True)) + apparmor.aa.active_profiles.get_all_merged_variables( + os.path.join(self.profile_dir, 'usr.sbin.dnsmasq'), + apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename], True)) def test_add_to_nonexisting(self): write_file(self.profile_dir, 'tunables/home.d/no_such_var', '@{NO_SUCH_HOME} += /my/castle/') # add to non-existing variable self._load_profiles() prof_filename = os.path.join(self.profile_dir, 'usr.sbin.dnsmasq') with self.assertRaises(AppArmorException): - apparmor.aa.active_profiles.get_all_merged_variables(os.path.join(self.profile_dir, 'usr.sbin.dnsmasq'), apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename], True)) + apparmor.aa.active_profiles.get_all_merged_variables( + os.path.join(self.profile_dir, 'usr.sbin.dnsmasq'), + apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename], True)) def test_vars_from_nonexisting_profile(self): with self.assertRaises(AppArmorBug): - apparmor.aa.active_profiles.get_all_merged_variables(os.path.join(self.profile_dir, 'file.not.found'), list()) + apparmor.aa.active_profiles.get_all_merged_variables( + os.path.join(self.profile_dir, 'file.not.found'), list()) + class TestGet_profile_and_childs(AATest): def AASetup(self): @@ -406,11 +438,11 @@ class TestGet_profile_and_childs(AATest): self.dummy_profile = ProfileStorage('TEST DUMMY', 'AATest_no_file', 'TEST') def testGet_profile_and_childs1(self): - self.pl.add_profile('/etc/apparmor.d/bin.foo', 'bafoo', '/bin/bafoo', self.dummy_profile) - self.pl.add_profile('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo', self.dummy_profile) - self.pl.add_profile('/etc/apparmor.d/bin.foo', 'foobar', '/bin/foobar', self.dummy_profile) - self.pl.add_profile('/etc/apparmor.d/bin.foo', 'foo//bar', '/bin/foo//bar', self.dummy_profile) - self.pl.add_profile('/etc/apparmor.d/bin.foo', 'foo//xy', '/bin/foo//xy', self.dummy_profile) + self.pl.add_profile('/etc/apparmor.d/bin.foo', 'bafoo', '/bin/bafoo', self.dummy_profile) + self.pl.add_profile('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo', self.dummy_profile) + self.pl.add_profile('/etc/apparmor.d/bin.foo', 'foobar', '/bin/foobar', self.dummy_profile) + self.pl.add_profile('/etc/apparmor.d/bin.foo', 'foo//bar', '/bin/foo//bar', self.dummy_profile) + self.pl.add_profile('/etc/apparmor.d/bin.foo', 'foo//xy', '/bin/foo//xy', self.dummy_profile) expected = ['foo', 'foo//bar', 'foo//xy'] diff --git a/utils/test/test-profile-storage.py b/utils/test/test-profile-storage.py index 6906b001e..357f3d378 100644 --- a/utils/test/test-profile-storage.py +++ b/utils/test/test-profile-storage.py @@ -15,6 +15,7 @@ from common_test import AATest, setup_all_loops from apparmor.common import AppArmorBug, AppArmorException from apparmor.profile_storage import ProfileStorage, add_or_remove_flag, split_flags, var_transform + class TestUnknownKey(AATest): def AASetup(self): self.storage = ProfileStorage('/test/foo', 'hat', 'TEST') @@ -35,33 +36,34 @@ class TestUnknownKey(AATest): with self.assertRaises(AppArmorBug): self.storage['foo'] = 'bar' + class AaTest_get_header(AATest): tests = ( - # name embedded_hat depth flags attachment xattrs prof.keyw. comment expected - (('/foo', False, 1, 'complain', '', '', False, '' ), ' /foo flags=(complain) {'), - (('/foo', True, 1, 'complain', '', '', False, '' ), ' profile /foo flags=(complain) {'), - (('/foo sp', False, 2, 'complain', '', '', False, '' ), ' "/foo sp" flags=(complain) {'), - (('/foo', True, 2, 'complain', '', '', False, '' ), ' profile /foo flags=(complain) {'), - (('/foo', False, 0, None, '', '', False, '' ), '/foo {'), - (('/foo', False, 0, None, '', 'user.foo=bar', False, '' ), '/foo xattrs=(user.foo=bar) {'), - (('/foo', True, 0, None, '', '', False, '' ), 'profile /foo {'), - (('bar', False, 1, 'complain', '', '', False, '' ), ' profile bar flags=(complain) {'), - (('bar', False, 1, 'complain', '/foo', '', False, '' ), ' profile bar /foo flags=(complain) {'), - (('bar', True, 1, 'complain', '/foo', '', False, '' ), ' profile bar /foo flags=(complain) {'), - (('bar baz', False, 1, None, '/foo', '', False, '' ), ' profile "bar baz" /foo {'), - (('bar', True, 1, None, '/foo', '', False, '' ), ' profile bar /foo {'), - (('bar baz', False, 1, 'complain', '/foo sp', '', False, '' ), ' profile "bar baz" "/foo sp" flags=(complain) {'), - (('bar baz', False, 1, 'complain', '/foo sp', 'user.foo=bar', False, '' ), ' profile "bar baz" "/foo sp" xattrs=(user.foo=bar) flags=(complain) {'), - (('^foo', False, 1, 'complain', '', '', False, '' ), ' profile ^foo flags=(complain) {'), - (('^foo', True, 1, 'complain', '', '', False, '' ), ' ^foo flags=(complain) {'), - (('^foo', True, 1.5, 'complain', '', '', False, '' ), ' ^foo flags=(complain) {'), - (('^foo', True, 1.3, 'complain', '', '', False, '' ), ' ^foo flags=(complain) {'), - (('/foo', False, 1, 'complain', '', '', True, '' ), ' profile /foo flags=(complain) {'), - (('/foo', True, 1, 'complain', '', '', True, '' ), ' profile /foo flags=(complain) {'), - (('/foo', False, 1, 'complain', '', '', False, '# x' ), ' /foo flags=(complain) { # x'), - (('/foo', True, 1, None, '', '', False, '# x' ), ' profile /foo { # x'), - (('/foo', False, 1, None, '', '', True, '# x' ), ' profile /foo { # x'), - (('/foo', True, 1, 'complain', '', '', True, '# x' ), ' profile /foo flags=(complain) { # x'), + # name embedded_hat depth flags attachment xattrs prof.keyw. comment expected + (('/foo', False, 1, 'complain', '', '', False, ''), ' /foo flags=(complain) {'), + (('/foo', True, 1, 'complain', '', '', False, ''), ' profile /foo flags=(complain) {'), + (('/foo sp', False, 2, 'complain', '', '', False, ''), ' "/foo sp" flags=(complain) {'), + (('/foo', True, 2, 'complain', '', '', False, ''), ' profile /foo flags=(complain) {'), + (('/foo', False, 0, None, '', '', False, ''), '/foo {'), + (('/foo', False, 0, None, '', 'user.foo=bar', False, ''), '/foo xattrs=(user.foo=bar) {'), + (('/foo', True, 0, None, '', '', False, ''), 'profile /foo {'), + (('bar', False, 1, 'complain', '', '', False, ''), ' profile bar flags=(complain) {'), + (('bar', False, 1, 'complain', '/foo', '', False, ''), ' profile bar /foo flags=(complain) {'), + (('bar', True, 1, 'complain', '/foo', '', False, ''), ' profile bar /foo flags=(complain) {'), + (('bar baz', False, 1, None, '/foo', '', False, ''), ' profile "bar baz" /foo {'), + (('bar', True, 1, None, '/foo', '', False, ''), ' profile bar /foo {'), + (('bar baz', False, 1, 'complain', '/foo sp', '', False, ''), ' profile "bar baz" "/foo sp" flags=(complain) {'), + (('bar baz', False, 1, 'complain', '/foo sp', 'user.foo=bar', False, ''), ' profile "bar baz" "/foo sp" xattrs=(user.foo=bar) flags=(complain) {'), + (('^foo', False, 1, 'complain', '', '', False, ''), ' profile ^foo flags=(complain) {'), + (('^foo', True, 1, 'complain', '', '', False, ''), ' ^foo flags=(complain) {'), + (('^foo', True, 1.5, 'complain', '', '', False, ''), ' ^foo flags=(complain) {'), + (('^foo', True, 1.3, 'complain', '', '', False, ''), ' ^foo flags=(complain) {'), + (('/foo', False, 1, 'complain', '', '', True, ''), ' profile /foo flags=(complain) {'), + (('/foo', True, 1, 'complain', '', '', True, ''), ' profile /foo flags=(complain) {'), + (('/foo', False, 1, 'complain', '', '', False, '# x'), ' /foo flags=(complain) { # x'), + (('/foo', True, 1, None, '', '', False, '# x'), ' profile /foo { # x'), + (('/foo', False, 1, None, '', '', True, '# x'), ' profile /foo { # x'), + (('/foo', True, 1, 'complain', '', '', True, '# x'), ' profile /foo flags=(complain) { # x'), ) def _run_test(self, params, expected): @@ -79,13 +81,14 @@ class AaTest_get_header(AATest): result = prof_storage.get_header(depth, name, embedded_hat) self.assertEqual(result, [expected]) + class AaTest_get_header_01(AATest): tests = ( - ({'name': '/foo', 'depth': 1, 'flags': 'complain' }, ' /foo flags=(complain) {', ), - ({'name': '/foo', 'depth': 1, 'flags': 'complain', 'profile_keyword': True }, ' profile /foo flags=(complain) {', ), - ({'name': '/foo', 'flags': 'complain' }, '/foo flags=(complain) {', ), - ({'name': '/foo', 'xattrs': 'user.foo=bar', 'flags': 'complain' }, '/foo xattrs=(user.foo=bar) flags=(complain) {', ), - ({'name': '/foo', 'xattrs': 'user.foo=bar', 'embedded_hat': True }, 'profile /foo xattrs=(user.foo=bar) {', ), + ({'name': '/foo', 'depth': 1, 'flags': 'complain' }, ' /foo flags=(complain) {'), + ({'name': '/foo', 'depth': 1, 'flags': 'complain', 'profile_keyword': True }, ' profile /foo flags=(complain) {'), + ({'name': '/foo', 'flags': 'complain' }, '/foo flags=(complain) {'), + ({'name': '/foo', 'xattrs': 'user.foo=bar', 'flags': 'complain' }, '/foo xattrs=(user.foo=bar) flags=(complain) {'), + ({'name': '/foo', 'xattrs': 'user.foo=bar', 'embedded_hat': True}, 'profile /foo xattrs=(user.foo=bar) {'), ) def _run_test(self, params, expected): @@ -93,7 +96,6 @@ class AaTest_get_header_01(AATest): embedded_hat = params.get('embedded_hat', False) depth = params.get('depth', 0) - prof_storage = ProfileStorage(name, '', 'test') for param in ('flags', 'attachment', 'profile_keyword', 'header_comment', 'xattrs'): @@ -106,12 +108,12 @@ class AaTest_get_header_01(AATest): class TestSetInvalid(AATest): tests = ( - (('profile_keyword', None), AppArmorBug), # expects bool - (('profile_keyword', 'foo'), AppArmorBug), - (('attachment', False), AppArmorBug), # expects string - (('attachment', None), AppArmorBug), - (('filename', True), AppArmorBug), # expects string or None - (('allow', None), AppArmorBug), # doesn't allow overwriting at all + (('profile_keyword', None), AppArmorBug), # expects bool + (('profile_keyword', 'foo'), AppArmorBug), + (('attachment', False), AppArmorBug), # expects string + (('attachment', None), AppArmorBug), + (('filename', True), AppArmorBug), # expects string or None + (('allow', None), AppArmorBug), # doesn't allow overwriting at all ) def _run_test(self, params, expected): @@ -119,39 +121,41 @@ class TestSetInvalid(AATest): with self.assertRaises(expected): self.storage[params[0]] = params[1] + class AaTest_parse_profile_start(AATest): tests = ( - # profile start line profile hat profile hat attachment xattrs flags pps_set_hat_external - (('/foo {', None, None), ('/foo', '/foo', '', '', None, False)), - (('/foo (complain) {', None, None), ('/foo', '/foo', '', '', 'complain', False)), - (('profile foo /foo {', None, None), ('foo', 'foo', '/foo', '', None, False)), # named profile - (('profile /foo {', '/bar', None), ('/bar', '/foo', '', '', None, False)), # child profile - (('/foo//bar {', None, None), ('/foo', 'bar', '', '', None, True )), # external hat - (('profile "/foo" (complain) {', None, None), ('/foo', '/foo', '', '', 'complain', False)), - (('profile "/foo" xattrs=(user.bar=bar) {', None, None), ('/foo', '/foo', '', 'user.bar=bar', None, False)), - (('profile "/foo" xattrs=(user.bar=bar user.foo=*) {', None, None), ('/foo', '/foo', '', 'user.bar=bar user.foo=*', None, False)), - (('/usr/bin/xattrs-test xattrs=(myvalue="foo.bar") {', None, None), ('/usr/bin/xattrs-test', '/usr/bin/xattrs-test', '', 'myvalue="foo.bar"', None, False)), + # profile start line profile hat profile hat attachment xattrs flags pps_set_hat_external + (('/foo {', None, None), ('/foo', '/foo', '', '', None, False)), + (('/foo (complain) {', None, None), ('/foo', '/foo', '', '', 'complain', False)), + (('profile foo /foo {', None, None), ('foo', 'foo', '/foo', '', None, False)), # named profile + (('profile /foo {', '/bar', None), ('/bar', '/foo', '', '', None, False)), # child profile + (('/foo//bar {', None, None), ('/foo', 'bar', '', '', None, True)), # external hat + (('profile "/foo" (complain) {', None, None), ('/foo', '/foo', '', '', 'complain', False)), + (('profile "/foo" xattrs=(user.bar=bar) {', None, None), ('/foo', '/foo', '', 'user.bar=bar', None, False)), + (('profile "/foo" xattrs=(user.bar=bar user.foo=*) {', None, None), ('/foo', '/foo', '', 'user.bar=bar user.foo=*', None, False)), + (('/usr/bin/xattrs-test xattrs=(myvalue="foo.bar") {', None, None), ('/usr/bin/xattrs-test', '/usr/bin/xattrs-test', '', 'myvalue="foo.bar"', None, False)), ) def _run_test(self, params, expected): (profile, hat, prof_storage) = ProfileStorage.parse(params[0], 'somefile', 1, params[1], params[2]) - self.assertEqual(profile, expected[0]) - self.assertEqual(hat, expected[1]) - self.assertEqual(prof_storage['attachment'], expected[2]) - self.assertEqual(prof_storage['xattrs'], expected[3]) - self.assertEqual(prof_storage['flags'], expected[4]) - self.assertEqual(prof_storage['is_hat'], False) - self.assertEqual(prof_storage['external'], expected[5]) + self.assertEqual(profile, expected[0]) + self.assertEqual(hat, expected[1]) + self.assertEqual(prof_storage['attachment'], expected[2]) + self.assertEqual(prof_storage['xattrs'], expected[3]) + self.assertEqual(prof_storage['flags'], expected[4]) + self.assertEqual(prof_storage['is_hat'], False) + self.assertEqual(prof_storage['external'], expected[5]) + class AaTest_parse_profile_start_errors(AATest): tests = ( - (('/foo///bar///baz {', None, None), AppArmorException), # XXX deeply nested external hat - (('profile asdf {', '/foo', '/bar'), AppArmorException), # nested child profile - (('/foo {', '/bar', None), AppArmorException), # child profile without profile keyword - (('/foo {', '/bar', '/bar'), AppArmorException), # child profile without profile keyword - (('xy', '/bar', None), AppArmorBug), # not a profile start - (('xy', '/bar', '/bar'), AppArmorBug), # not a profile start + (('/foo///bar///baz {', None, None), AppArmorException), # XXX deeply nested external hat + (('profile asdf {', '/foo', '/bar'), AppArmorException), # nested child profile + (('/foo {', '/bar', None), AppArmorException), # child profile without profile keyword + (('/foo {', '/bar', '/bar'), AppArmorException), # child profile without profile keyword + (('xy', '/bar', None), AppArmorBug), # not a profile start + (('xy', '/bar', '/bar'), AppArmorBug), # not a profile start ) def _run_test(self, params, expected): @@ -161,55 +165,57 @@ class AaTest_parse_profile_start_errors(AATest): class AaTest_add_or_remove_flag(AATest): tests = ( - # existing flag(s) flag to change add or remove? expected flags - (( [], 'complain', True ), ['complain'] ), - (( [], 'complain', False ), [] ), - (( ['complain'], 'complain', True ), ['complain'] ), - (( ['complain'], 'complain', False ), [] ), - (( [], 'audit', True ), ['audit'] ), - (( [], 'audit', False ), [] ), - (( ['complain'], 'audit', True ), ['audit', 'complain'] ), - (( ['complain'], 'audit', False ), ['complain'] ), - (( '', 'audit', True ), ['audit'] ), - (( None, 'audit', False ), [] ), - (( 'complain', 'audit', True ), ['audit', 'complain'] ), - (( ' complain ', 'audit', False ), ['complain'] ), - (( 'audit complain', ('audit', 'complain'), False ), [] ), - (( 'audit complain', 'audit complain', False ), [] ), - (( 'audit complain', ('audit', 'enforce'), False ), ['complain'] ), - (( 'audit complain', 'audit enforce', False ), ['complain'] ), - (( '', ('audit', 'complain'), True ), ['audit', 'complain'] ), - (( '', 'audit complain', True ), ['audit', 'complain'] ), - (( 'audit', ('audit', 'enforce'), True ), ['audit', 'enforce'] ), - (( 'audit', 'audit enforce', True ), ['audit', 'enforce'] ), + # existing flag(s) flag to change add or remove? expected flags + (([], 'complain', True), ['complain']), + (([], 'complain', False), []), + ((['complain'], 'complain', True), ['complain']), + ((['complain'], 'complain', False), []), + (([], 'audit', True), ['audit']), + (([], 'audit', False), []), + ((['complain'], 'audit', True), ['audit', 'complain']), + ((['complain'], 'audit', False), ['complain']), + (('', 'audit', True), ['audit']), + ((None, 'audit', False), []), + (('complain', 'audit', True), ['audit', 'complain']), + ((' complain ', 'audit', False), ['complain']), + (('audit complain', ('audit', 'complain'), False), []), + (('audit complain', 'audit complain', False), []), + (('audit complain', ('audit', 'enforce'), False), ['complain']), + (('audit complain', 'audit enforce', False), ['complain']), + (('', ('audit', 'complain'), True), ['audit', 'complain']), + (('', 'audit complain', True), ['audit', 'complain']), + (('audit', ('audit', 'enforce'), True), ['audit', 'enforce']), + (('audit', 'audit enforce', True), ['audit', 'enforce']), ) def _run_test(self, params, expected): new_flags = add_or_remove_flag(*params) self.assertEqual(new_flags, expected) + class AaTest_split_flags(AATest): tests = ( - (None , [] ), - ('' , [] ), - (' ' , [] ), - (' , ' , [] ), - ('complain' , ['complain'] ), - (' complain attach_disconnected' , ['attach_disconnected', 'complain'] ), - (' complain , attach_disconnected' , ['attach_disconnected', 'complain'] ), - (' complain , , audit , , ' , ['audit', 'complain'] ), + (None, []), + ('', []), + (' ', []), + (' , ', []), + ('complain', ['complain']), + (' complain attach_disconnected', ['attach_disconnected', 'complain']), + (' complain , attach_disconnected', ['attach_disconnected', 'complain']), + (' complain , , audit , , ', ['audit', 'complain']), ) def _run_test(self, params, expected): split = split_flags(params) self.assertEqual(split, expected) + class AaTest_var_transform(AATest): tests = ( - (('foo', ''), '"" foo' ), - (('foo', 'bar'), 'bar foo' ), - (('',), '""' ), - (('bar baz', 'foo'), '"bar baz" foo' ), + (('foo', ''), '"" foo'), + (('foo', 'bar'), 'bar foo'), + (('',), '""'), + (('bar baz', 'foo'), '"bar baz" foo'), ) def _run_test(self, params, expected): diff --git a/utils/test/test-profiles.py b/utils/test/test-profiles.py index d4198d9be..0606157ea 100644 --- a/utils/test/test-profiles.py +++ b/utils/test/test-profiles.py @@ -16,11 +16,10 @@ import apparmor.aa as aa # If a profile can't be parsed by the tools, add it to skip_active_profiles or skip_extra_profiles. # Add only the filename (without path), for example 'usr.bin.foo'. # These skip lists are meant as a temporary solution, and should be empty on release. -skip_active_profiles = [ -] +skip_active_profiles = [] + +skip_extra_profiles = [] -skip_extra_profiles = [ -] class TestFoo(AATest): # Make sure the python code can parse all profiles shipped with AppArmor. @@ -39,6 +38,7 @@ class TestFoo(AATest): self.assertGreaterEqual(len(aa.extra_profiles.profile_names), 100) + setup_aa(aa) setup_all_loops(__name__) if __name__ == '__main__': diff --git a/utils/test/test-ptrace.py b/utils/test/test-ptrace.py index c3f5b3d4e..9a0fbcf86 100644 --- a/utils/test/test-ptrace.py +++ b/utils/test/test-ptrace.py @@ -24,8 +24,11 @@ from apparmor.logparser import ReadLog from apparmor.translations import init_translation _ = init_translation() -exp = namedtuple('exp', ('audit', 'allow_keyword', 'deny', 'comment', - 'access', 'all_access', 'peer', 'all_peers')) +exp = namedtuple( + 'exp', ('audit', 'allow_keyword', 'deny', 'comment', 'access', 'all_access', 'peer', + 'all_peers'), +) + # # --- tests for single PtraceRule --- # @@ -43,22 +46,23 @@ class PtraceTest(AATest): self.assertEqual(expected.deny, obj.deny) self.assertEqual(expected.comment, obj.comment) + class PtraceTestParse(PtraceTest): tests = ( - # PtraceRule object audit allow deny comment access all? peer all? - ('ptrace,' , exp(False, False, False, '', None , True , None, True )), -# ('ptrace (),' , exp(False, False, False, '', None , True , None, True )), # XXX also broken in SignalRule? - ('ptrace read,' , exp(False, False, False, '', {'read'}, False, None, True )), - ('ptrace (read, tracedby),' , exp(False, False, False, '', {'read', 'tracedby'}, False, None, True )), - ('ptrace read,' , exp(False, False, False, '', {'read'}, False, None, True )), - ('deny ptrace read, # cmt' , exp(False, False, True , ' # cmt', {'read'}, False, None, True )), - ('audit allow ptrace,' , exp(True , True , False, '', None , True , None, True )), - ('ptrace peer=unconfined,' , exp(False, False, False, '', None , True , 'unconfined', False )), - ('ptrace peer="unconfined",' , exp(False, False, False, '', None , True , 'unconfined', False )), - ('ptrace read,' , exp(False, False, False, '', {'read'}, False, None, True )), - ('ptrace peer=/foo,' , exp(False, False, False, '', None , True , '/foo', False )), - ('ptrace r peer=/foo,' , exp(False, False, False, '', {'r'}, False, '/foo', False )), - ('ptrace r peer="/foo bar",' , exp(False, False, False, '', {'r'}, False, '/foo bar', False )), + # PtraceRule object audit allow deny comment access all? peer all? + ('ptrace,', exp(False, False, False, '', None, True, None, True)), + # ('ptrace (),', exp(False, False, False, '', None, True, None, True)), # XXX also broken in SignalRule? + ('ptrace read,', exp(False, False, False, '', {'read'}, False, None, True)), + ('ptrace (read, tracedby),', exp(False, False, False, '', {'read', 'tracedby'}, False, None, True)), + ('ptrace read,', exp(False, False, False, '', {'read'}, False, None, True)), + ('deny ptrace read, # cmt', exp(False, False, True, ' # cmt', {'read'}, False, None, True)), + ('audit allow ptrace,', exp(True, True, False, '', None, True, None, True)), + ('ptrace peer=unconfined,', exp(False, False, False, '', None, True, 'unconfined', False)), + ('ptrace peer="unconfined",', exp(False, False, False, '', None, True, 'unconfined', False)), + ('ptrace read,', exp(False, False, False, '', {'read'}, False, None, True)), + ('ptrace peer=/foo,', exp(False, False, False, '', None, True, '/foo', False)), + ('ptrace r peer=/foo,', exp(False, False, False, '', {'r'}, False, '/foo', False)), + ('ptrace r peer="/foo bar",', exp(False, False, False, '', {'r'}, False, '/foo bar', False)), ) def _run_test(self, rawrule, expected): @@ -67,14 +71,15 @@ class PtraceTestParse(PtraceTest): self.assertEqual(rawrule.strip(), obj.raw_rule) self._compare_obj(obj, expected) + class PtraceTestParseInvalid(PtraceTest): tests = ( - ('ptrace foo,' , AppArmorException), - ('ptrace foo bar,' , AppArmorException), - ('ptrace foo int,' , AppArmorException), - ('ptrace read bar,' , AppArmorException), - ('ptrace read tracedby,' , AppArmorException), - ('ptrace peer=,' , AppArmorException), + ('ptrace foo,', AppArmorException), + ('ptrace foo bar,', AppArmorException), + ('ptrace foo int,', AppArmorException), + ('ptrace read bar,', AppArmorException), + ('ptrace read tracedby,', AppArmorException), + ('ptrace peer=,', AppArmorException), ) def _run_test(self, rawrule, expected): @@ -82,12 +87,12 @@ class PtraceTestParseInvalid(PtraceTest): with self.assertRaises(expected): PtraceRule.parse(rawrule) + class PtraceTestParseFromLog(PtraceTest): def test_ptrace_from_log(self): parser = ReadLog('', '', '') event = 'type=AVC msg=audit(1409700683.304:547661): apparmor="DENIED" operation="ptrace" profile="/home/ubuntu/bzr/apparmor/tests/regression/apparmor/ptrace" pid=22465 comm="ptrace" requested_mask="tracedby" denied_mask="tracedby" peer="/home/ubuntu/bzr/apparmor/tests/regression/apparmor/ptrace"' - parsed_event = parser.parse_event(event) self.assertEqual(parsed_event, { @@ -116,40 +121,44 @@ class PtraceTestParseFromLog(PtraceTest): obj = PtraceRule(parsed_event['denied_mask'], parsed_event['peer'], log_event=parsed_event) - # audit allow deny comment access all? peer all? - expected = exp(False, False, False, '', {'tracedby'}, False, '/home/ubuntu/bzr/apparmor/tests/regression/apparmor/ptrace', False) + # audit allow deny comment access all? peer all? + expected = exp(False, False, False, '', {'tracedby'}, False, '/home/ubuntu/bzr/apparmor/tests/regression/apparmor/ptrace', False) self._compare_obj(obj, expected) - self.assertEqual(obj.get_raw(1), ' ptrace tracedby peer=/home/ubuntu/bzr/apparmor/tests/regression/apparmor/ptrace,') + self.assertEqual( + obj.get_raw(1), + ' ptrace tracedby peer=/home/ubuntu/bzr/apparmor/tests/regression/apparmor/ptrace,') + class PtraceFromInit(PtraceTest): tests = ( - # PtraceRule object audit allow deny comment access all? peer all? - (PtraceRule('r', 'unconfined', deny=True) , exp(False, False, True , '' , {'r'}, False, 'unconfined', False)), - (PtraceRule(('r', 'read'), '/bin/foo') , exp(False, False, False, '' , {'r', 'read'},False, '/bin/foo', False)), - (PtraceRule(PtraceRule.ALL, '/bin/foo') , exp(False, False, False, '' , None, True, '/bin/foo', False )), - (PtraceRule('rw', '/bin/foo') , exp(False, False, False, '' , {'rw'}, False, '/bin/foo', False )), - (PtraceRule('rw', PtraceRule.ALL) , exp(False, False, False, '' , {'rw'}, False, None, True )), - (PtraceRule(PtraceRule.ALL, PtraceRule.ALL) , exp(False, False, False, '' , None , True, None, True )), + # PtraceRule object audit allow deny comment access all? peer all? + (PtraceRule('r', 'unconfined', deny=True), exp(False, False, True, '', {'r'}, False, 'unconfined', False)), + (PtraceRule(('r', 'read'), '/bin/foo'), exp(False, False, False, '', {'r', 'read'}, False, '/bin/foo', False)), + (PtraceRule(PtraceRule.ALL, '/bin/foo'), exp(False, False, False, '', None, True, '/bin/foo', False)), + (PtraceRule('rw', '/bin/foo'), exp(False, False, False, '', {'rw'}, False, '/bin/foo', False)), + (PtraceRule('rw', PtraceRule.ALL), exp(False, False, False, '', {'rw'}, False, None, True)), + (PtraceRule(PtraceRule.ALL, PtraceRule.ALL), exp(False, False, False, '', None, True, None, True)), ) def _run_test(self, obj, expected): self._compare_obj(obj, expected) + class InvalidPtraceInit(AATest): tests = ( - # init params expected exception - (('' , '/foo' ) , AppArmorBug), # empty access - (('read', '' ) , AppArmorBug), # empty peer - ((' ', '/foo' ) , AppArmorBug), # whitespace access - (('read', ' ' ) , AppArmorBug), # whitespace peer - (('xyxy', '/foo' ) , AppArmorException), # invalid access + # (init params, expected exception) + (('', '/foo'), AppArmorBug), # empty access + (('read', ''), AppArmorBug), # empty peer + ((' ', '/foo'), AppArmorBug), # whitespace access + (('read', ' '), AppArmorBug), # whitespace peer + (('xyxy', '/foo'), AppArmorException), # invalid access # XXX is 'invalid peer' possible at all? - ((dict(), '/foo' ) , AppArmorBug), # wrong type for access - ((None , '/foo' ) , AppArmorBug), # wrong type for access - (('read', dict() ) , AppArmorBug), # wrong type for peer - (('read', None ) , AppArmorBug), # wrong type for peer + ((dict(), '/foo'), AppArmorBug), # wrong type for access + ((None, '/foo'), AppArmorBug), # wrong type for access + (('read', dict()), AppArmorBug), # wrong type for peer + (('read', None), AppArmorBug), # wrong type for peer ) def _run_test(self, params, expected): @@ -164,6 +173,7 @@ class InvalidPtraceInit(AATest): with self.assertRaises(TypeError): PtraceRule('r') + class InvalidPtraceTest(AATest): def _check_invalid_rawrule(self, rawrule): obj = None @@ -205,33 +215,33 @@ class WritePtraceTestAATest(AATest): self.assertEqual(rawrule.strip(), raw, 'unexpected raw rule') tests = ( - # raw rule clean rule - ('ptrace,' , 'ptrace,'), - (' ptrace , # foo ' , 'ptrace, # foo'), - (' audit ptrace read,' , 'audit ptrace read,'), - (' audit ptrace (read ),' , 'audit ptrace read,'), - (' audit ptrace (read , tracedby ),' , 'audit ptrace (read tracedby),'), - (' deny ptrace read ,# foo bar' , 'deny ptrace read, # foo bar'), - (' deny ptrace ( read ), ' , 'deny ptrace read,'), - (' allow ptrace ,# foo bar' , 'allow ptrace, # foo bar'), - ('ptrace,' , 'ptrace,'), - ('ptrace (trace),' , 'ptrace trace,'), - ('ptrace (tracedby),' , 'ptrace tracedby,'), - ('ptrace (read),' , 'ptrace read,'), - ('ptrace (readby),' , 'ptrace readby,'), - ('ptrace (trace read),' , 'ptrace (read trace),'), - ('ptrace (read tracedby),' , 'ptrace (read tracedby),'), - ('ptrace r,' , 'ptrace r,'), - ('ptrace w,' , 'ptrace w,'), - ('ptrace rw,' , 'ptrace rw,'), - ('ptrace read,' , 'ptrace read,'), - ('ptrace (tracedby),' , 'ptrace tracedby,'), - ('ptrace w,' , 'ptrace w,'), - ('ptrace read peer=foo,' , 'ptrace read peer=foo,'), - ('ptrace tracedby peer=foo,' , 'ptrace tracedby peer=foo,'), - ('ptrace (read tracedby) peer=/usr/bin/bar,' , 'ptrace (read tracedby) peer=/usr/bin/bar,'), - ('ptrace (trace read) peer=/usr/bin/bar,' , 'ptrace (read trace) peer=/usr/bin/bar,'), - ('ptrace wr peer=/sbin/baz,' , 'ptrace wr peer=/sbin/baz,'), + # raw rule clean rule + ('ptrace,', 'ptrace,'), + (' ptrace , # foo ', 'ptrace, # foo'), + (' audit ptrace read,', 'audit ptrace read,'), + (' audit ptrace (read ),', 'audit ptrace read,'), + (' audit ptrace (read , tracedby ),', 'audit ptrace (read tracedby),'), + (' deny ptrace read ,# foo bar', 'deny ptrace read, # foo bar'), + (' deny ptrace ( read ), ', 'deny ptrace read,'), + (' allow ptrace ,# foo bar', 'allow ptrace, # foo bar'), + ('ptrace,', 'ptrace,'), + ('ptrace (trace),', 'ptrace trace,'), + ('ptrace (tracedby),', 'ptrace tracedby,'), + ('ptrace (read),', 'ptrace read,'), + ('ptrace (readby),', 'ptrace readby,'), + ('ptrace (trace read),', 'ptrace (read trace),'), + ('ptrace (read tracedby),', 'ptrace (read tracedby),'), + ('ptrace r,', 'ptrace r,'), + ('ptrace w,', 'ptrace w,'), + ('ptrace rw,', 'ptrace rw,'), + ('ptrace read,', 'ptrace read,'), + ('ptrace (tracedby),', 'ptrace tracedby,'), + ('ptrace w,', 'ptrace w,'), + ('ptrace read peer=foo,', 'ptrace read peer=foo,'), + ('ptrace tracedby peer=foo,', 'ptrace tracedby peer=foo,'), + ('ptrace (read tracedby) peer=/usr/bin/bar,', 'ptrace (read tracedby) peer=/usr/bin/bar,'), + ('ptrace (trace read) peer=/usr/bin/bar,', 'ptrace (read trace) peer=/usr/bin/bar,'), + ('ptrace wr peer=/sbin/baz,', 'ptrace wr peer=/sbin/baz,'), ) def test_write_manually(self): @@ -256,166 +266,173 @@ class PtraceCoveredTest(AATest): self.assertEqual(obj.is_covered(check_obj), expected[2], 'Mismatch in is_covered, expected %s' % expected[2]) self.assertEqual(obj.is_covered(check_obj, True, True), expected[3], 'Mismatch in is_covered/exact, expected %s' % expected[3]) + class PtraceCoveredTest_01(PtraceCoveredTest): rule = 'ptrace read,' tests = ( - # rule equal strict equal covered covered exact - ('ptrace,' , ( False , False , False , False )), - ('ptrace read,' , ( True , True , True , True )), - ('ptrace read peer=unconfined,' , ( False , False , True , True )), - ('ptrace read, # comment' , ( True , False , True , True )), - ('allow ptrace read,' , ( True , False , True , True )), - ('ptrace read,' , ( True , False , True , True )), - ('audit ptrace read,' , ( False , False , False , False )), - ('audit ptrace,' , ( False , False , False , False )), - ('ptrace tracedby,' , ( False , False , False , False )), - ('audit deny ptrace read,' , ( False , False , False , False )), - ('deny ptrace read,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('ptrace,', (False, False, False, False)), + ('ptrace read,', (True, True, True, True)), + ('ptrace read peer=unconfined,', (False, False, True, True)), + ('ptrace read, # comment', (True, False, True, True)), + ('allow ptrace read,', (True, False, True, True)), + ('ptrace read,', (True, False, True, True)), + ('audit ptrace read,', (False, False, False, False)), + ('audit ptrace,', (False, False, False, False)), + ('ptrace tracedby,', (False, False, False, False)), + ('audit deny ptrace read,', (False, False, False, False)), + ('deny ptrace read,', (False, False, False, False)), ) + class PtraceCoveredTest_02(PtraceCoveredTest): rule = 'audit ptrace read,' tests = ( - # rule equal strict equal covered covered exact - ( 'ptrace read,' , ( False , False , True , False )), - ('audit ptrace read,' , ( True , True , True , True )), - ( 'ptrace,' , ( False , False , False , False )), - ('audit ptrace,' , ( False , False , False , False )), - ('ptrace tracedby,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'ptrace read,', (False, False, True, False)), + ('audit ptrace read,', (True, True, True, True)), + ( 'ptrace,', (False, False, False, False)), + ('audit ptrace,', (False, False, False, False)), + ('ptrace tracedby,', (False, False, False, False)), ) + class PtraceCoveredTest_03(PtraceCoveredTest): rule = 'ptrace,' tests = ( - # rule equal strict equal covered covered exact - ( 'ptrace,' , ( True , True , True , True )), - ('allow ptrace,' , ( True , False , True , True )), - ( 'ptrace read,' , ( False , False , True , True )), - ( 'ptrace w,' , ( False , False , True , True )), - ('audit ptrace,' , ( False , False , False , False )), - ('deny ptrace,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'ptrace,', (True, True, True, True)), + ('allow ptrace,', (True, False, True, True)), + ( 'ptrace read,', (False, False, True, True)), + ( 'ptrace w,', (False, False, True, True)), + ('audit ptrace,', (False, False, False, False)), + ('deny ptrace,', (False, False, False, False)), ) + class PtraceCoveredTest_04(PtraceCoveredTest): rule = 'deny ptrace read,' tests = ( - # rule equal strict equal covered covered exact - ( 'deny ptrace read,' , ( True , True , True , True )), - ('audit deny ptrace read,' , ( False , False , False , False )), - ( 'ptrace read,' , ( False , False , False , False )), # XXX should covered be true here? - ( 'deny ptrace tracedby,' , ( False , False , False , False )), - ( 'deny ptrace,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'deny ptrace read,', (True, True, True, True)), + ('audit deny ptrace read,', (False, False, False, False)), + ( 'ptrace read,', (False, False, False, False)), # XXX should covered be true here? + ( 'deny ptrace tracedby,', (False, False, False, False)), + ( 'deny ptrace,', (False, False, False, False)), ) + class PtraceCoveredTest_05(PtraceCoveredTest): rule = 'ptrace read peer=unconfined,' tests = ( - # rule equal strict equal covered covered exact - ('ptrace,' , ( False , False , False , False )), - ('ptrace read,' , ( False , False , False , False )), - ('ptrace read peer=unconfined,' , ( True , True , True , True )), - ('ptrace peer=unconfined,' , ( False , False , False , False )), - ('ptrace read, # comment' , ( False , False , False , False )), - ('allow ptrace read,' , ( False , False , False , False )), - ('allow ptrace read peer=unconfined,' , ( True , False , True , True )), - ('allow ptrace read peer=/foo/bar,' , ( False , False , False , False )), - ('allow ptrace read peer=/**,' , ( False , False , False , False )), - ('allow ptrace read peer=**,' , ( False , False , False , False )), - ('ptrace read,' , ( False , False , False , False )), - ('ptrace read peer=unconfined,' , ( True , False , True , True )), - ('audit ptrace read peer=unconfined,' , ( False , False , False , False )), - ('audit ptrace,' , ( False , False , False , False )), - ('ptrace tracedby,' , ( False , False , False , False )), - ('audit deny ptrace read,' , ( False , False , False , False )), - ('deny ptrace read,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('ptrace,', (False, False, False, False)), + ('ptrace read,', (False, False, False, False)), + ('ptrace read peer=unconfined,', (True, True, True, True)), + ('ptrace peer=unconfined,', (False, False, False, False)), + ('ptrace read, # comment', (False, False, False, False)), + ('allow ptrace read,', (False, False, False, False)), + ('allow ptrace read peer=unconfined,', (True, False, True, True)), + ('allow ptrace read peer=/foo/bar,', (False, False, False, False)), + ('allow ptrace read peer=/**,', (False, False, False, False)), + ('allow ptrace read peer=**,', (False, False, False, False)), + ('ptrace read,', (False, False, False, False)), + ('ptrace read peer=unconfined,', (True, False, True, True)), + ('audit ptrace read peer=unconfined,', (False, False, False, False)), + ('audit ptrace,', (False, False, False, False)), + ('ptrace tracedby,', (False, False, False, False)), + ('audit deny ptrace read,', (False, False, False, False)), + ('deny ptrace read,', (False, False, False, False)), ) + class PtraceCoveredTest_06(PtraceCoveredTest): rule = 'ptrace read peer=/foo/bar,' tests = ( - # rule equal strict equal covered covered exact - ('ptrace,' , ( False , False , False , False )), - ('ptrace read,' , ( False , False , False , False )), - ('ptrace read peer=/foo/bar,' , ( True , True , True , True )), - ('ptrace read peer=/foo/*,' , ( False , False , False , False )), - ('ptrace read peer=/**,' , ( False , False , False , False )), - ('ptrace read peer=/what/*,' , ( False , False , False , False )), - ('ptrace peer=/foo/bar,' , ( False , False , False , False )), - ('ptrace read, # comment' , ( False , False , False , False )), - ('allow ptrace read,' , ( False , False , False , False )), - ('allow ptrace read peer=/foo/bar,' , ( True , False , True , True )), - ('ptrace read,' , ( False , False , False , False )), - ('ptrace read peer=/foo/bar,' , ( True , False , True , True )), - ('ptrace read peer=/what/ever,' , ( False , False , False , False )), - ('audit ptrace read peer=/foo/bar,' , ( False , False , False , False )), - ('audit ptrace,' , ( False , False , False , False )), - ('ptrace tracedby,' , ( False , False , False , False )), - ('audit deny ptrace read,' , ( False , False , False , False )), - ('deny ptrace read,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('ptrace,', (False, False, False, False)), + ('ptrace read,', (False, False, False, False)), + ('ptrace read peer=/foo/bar,', (True, True, True, True)), + ('ptrace read peer=/foo/*,', (False, False, False, False)), + ('ptrace read peer=/**,', (False, False, False, False)), + ('ptrace read peer=/what/*,', (False, False, False, False)), + ('ptrace peer=/foo/bar,', (False, False, False, False)), + ('ptrace read, # comment', (False, False, False, False)), + ('allow ptrace read,', (False, False, False, False)), + ('allow ptrace read peer=/foo/bar,', (True, False, True, True)), + ('ptrace read,', (False, False, False, False)), + ('ptrace read peer=/foo/bar,', (True, False, True, True)), + ('ptrace read peer=/what/ever,', (False, False, False, False)), + ('audit ptrace read peer=/foo/bar,', (False, False, False, False)), + ('audit ptrace,', (False, False, False, False)), + ('ptrace tracedby,', (False, False, False, False)), + ('audit deny ptrace read,', (False, False, False, False)), + ('deny ptrace read,', (False, False, False, False)), ) + class PtraceCoveredTest_07(PtraceCoveredTest): rule = 'ptrace read peer=**,' tests = ( - # rule equal strict equal covered covered exact - ('ptrace,' , ( False , False , False , False )), - ('ptrace read,' , ( False , False , False , False )), - ('ptrace read peer=/foo/bar,' , ( False , False , True , True )), - ('ptrace read peer=/foo/*,' , ( False , False , False , False )), # TODO: wildcard vs. wildcard never matches in is_covered_aare() - ('ptrace read peer=/**,' , ( False , False , False , False )), # TODO: wildcard vs. wildcard never matches in is_covered_aare() - ('ptrace read peer=/what/*,' , ( False , False , False , False )), # TODO: wildcard vs. wildcard never matches in is_covered_aare() - ('ptrace peer=/foo/bar,' , ( False , False , False , False )), - ('ptrace read, # comment' , ( False , False , False , False )), - ('allow ptrace read,' , ( False , False , False , False )), - ('allow ptrace read peer=/foo/bar,' , ( False , False , True , True )), - ('ptrace read,' , ( False , False , False , False )), - ('ptrace read peer=/foo/bar,' , ( False , False , True , True )), - ('ptrace read peer=/what/ever,' , ( False , False , True , True )), - ('audit ptrace read peer=/foo/bar,' , ( False , False , False , False )), - ('audit ptrace,' , ( False , False , False , False )), - ('ptrace tracedby,' , ( False , False , False , False )), - ('audit deny ptrace read,' , ( False , False , False , False )), - ('deny ptrace read,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('ptrace,', (False, False, False, False)), + ('ptrace read,', (False, False, False, False)), + ('ptrace read peer=/foo/bar,', (False, False, True, True)), + ('ptrace read peer=/foo/*,', (False, False, False, False)), # TODO: wildcard vs. wildcard never matches in is_covered_aare() + ('ptrace read peer=/**,', (False, False, False, False)), # TODO: wildcard vs. wildcard never matches in is_covered_aare() + ('ptrace read peer=/what/*,', (False, False, False, False)), # TODO: wildcard vs. wildcard never matches in is_covered_aare() + ('ptrace peer=/foo/bar,', (False, False, False, False)), + ('ptrace read, # comment', (False, False, False, False)), + ('allow ptrace read,', (False, False, False, False)), + ('allow ptrace read peer=/foo/bar,', (False, False, True, True)), + ('ptrace read,', (False, False, False, False)), + ('ptrace read peer=/foo/bar,', (False, False, True, True)), + ('ptrace read peer=/what/ever,', (False, False, True, True)), + ('audit ptrace read peer=/foo/bar,', (False, False, False, False)), + ('audit ptrace,', (False, False, False, False)), + ('ptrace tracedby,', (False, False, False, False)), + ('audit deny ptrace read,', (False, False, False, False)), + ('deny ptrace read,', (False, False, False, False)), ) + class PtraceCoveredTest_08(PtraceCoveredTest): rule = 'ptrace (trace, tracedby) peer=/foo/*,' tests = ( - # rule equal strict equal covered covered exact - ('ptrace,' , ( False , False , False , False )), - ('ptrace trace,' , ( False , False , False , False )), - ('ptrace (tracedby, trace),' , ( False , False , False , False )), - ('ptrace trace peer=/foo/bar,' , ( False , False , True , True )), - ('ptrace (tracedby trace) peer=/foo/bar,',( False , False , True , True )), - ('ptrace (tracedby, trace) peer=/foo/*,', ( True , False , True , True )), - ('ptrace tracedby peer=/foo/bar,' , ( False , False , True , True )), - ('ptrace trace peer=/foo/*,' , ( False , False , True , True )), - ('ptrace trace peer=/**,' , ( False , False , False , False )), - ('ptrace trace peer=/what/*,' , ( False , False , False , False )), - ('ptrace peer=/foo/bar,' , ( False , False , False , False )), - ('ptrace trace, # comment' , ( False , False , False , False )), - ('allow ptrace trace,' , ( False , False , False , False )), - ('allow ptrace trace peer=/foo/bar,' , ( False , False , True , True )), - ('ptrace trace,' , ( False , False , False , False )), - ('ptrace trace peer=/foo/bar,' , ( False , False , True , True )), - ('ptrace trace peer=/what/ever,' , ( False , False , False , False )), - ('audit ptrace trace peer=/foo/bar,' , ( False , False , False , False )), - ('audit ptrace,' , ( False , False , False , False )), - ('ptrace tracedby,' , ( False , False , False , False )), - ('audit deny ptrace trace,' , ( False , False , False , False )), - ('deny ptrace trace,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('ptrace,', (False, False, False, False)), + ('ptrace trace,', (False, False, False, False)), + ('ptrace (tracedby, trace),', (False, False, False, False)), + ('ptrace trace peer=/foo/bar,', (False, False, True, True)), + ('ptrace (tracedby trace) peer=/foo/bar,', (False, False, True, True)), + ('ptrace (tracedby, trace) peer=/foo/*,', (True, False, True, True)), + ('ptrace tracedby peer=/foo/bar,', (False, False, True, True)), + ('ptrace trace peer=/foo/*,', (False, False, True, True)), + ('ptrace trace peer=/**,', (False, False, False, False)), + ('ptrace trace peer=/what/*,', (False, False, False, False)), + ('ptrace peer=/foo/bar,', (False, False, False, False)), + ('ptrace trace, # comment', (False, False, False, False)), + ('allow ptrace trace,', (False, False, False, False)), + ('allow ptrace trace peer=/foo/bar,', (False, False, True, True)), + ('ptrace trace,', (False, False, False, False)), + ('ptrace trace peer=/foo/bar,', (False, False, True, True)), + ('ptrace trace peer=/what/ever,', (False, False, False, False)), + ('audit ptrace trace peer=/foo/bar,', (False, False, False, False)), + ('audit ptrace,', (False, False, False, False)), + ('ptrace tracedby,', (False, False, False, False)), + ('audit deny ptrace trace,', (False, False, False, False)), + ('deny ptrace trace,', (False, False, False, False)), ) - class PtraceCoveredTest_Invalid(AATest): def test_borked_obj_is_covered_1(self): obj = PtraceRule.parse('ptrace read peer=/foo,') @@ -463,19 +480,20 @@ class PtraceCoveredTest_Invalid(AATest): class PtraceLogprofHeaderTest(AATest): tests = ( - ('ptrace,', [ _('Access mode'), _('ALL'), _('Peer'), _('ALL'), ]), - ('ptrace read,', [ _('Access mode'), 'read', _('Peer'), _('ALL'), ]), - ('deny ptrace,', [_('Qualifier'), 'deny', _('Access mode'), _('ALL'), _('Peer'), _('ALL'), ]), - ('allow ptrace read,', [_('Qualifier'), 'allow', _('Access mode'), 'read', _('Peer'), _('ALL'), ]), - ('audit ptrace read,', [_('Qualifier'), 'audit', _('Access mode'), 'read', _('Peer'), _('ALL'), ]), - ('audit deny ptrace read,', [_('Qualifier'), 'audit deny', _('Access mode'), 'read', _('Peer'), _('ALL'), ]), - ('ptrace (read, tracedby) peer=/foo,', [ _('Access mode'), 'read tracedby', _('Peer'), '/foo', ]), + ('ptrace,', [ _('Access mode'), _('ALL'), _('Peer'), _('ALL')]), + ('ptrace read,', [ _('Access mode'), 'read', _('Peer'), _('ALL')]), + ('deny ptrace,', [_('Qualifier'), 'deny', _('Access mode'), _('ALL'), _('Peer'), _('ALL')]), + ('allow ptrace read,', [_('Qualifier'), 'allow', _('Access mode'), 'read', _('Peer'), _('ALL')]), + ('audit ptrace read,', [_('Qualifier'), 'audit', _('Access mode'), 'read', _('Peer'), _('ALL')]), + ('audit deny ptrace read,', [_('Qualifier'), 'audit deny', _('Access mode'), 'read', _('Peer'), _('ALL')]), + ('ptrace (read, tracedby) peer=/foo,', [ _('Access mode'), 'read tracedby', _('Peer'), '/foo']), ) def _run_test(self, params, expected): obj = PtraceRule.parse(params) self.assertEqual(obj.logprof_header(), expected) + ## --- tests for PtraceRuleset --- # class PtraceRulesTest(AATest): @@ -518,7 +536,8 @@ class PtraceRulesTest(AATest): # test __repr__() for non-empty ruleset as_string = '%s' % ruleset - self.assertEqual(as_string, '\n ptrace peer=/foo,\n ptrace read,\n') + self.assertEqual( + as_string, '\n ptrace peer=/foo,\n ptrace read,\n') def test_ruleset_2(self): ruleset = PtraceRuleset() @@ -567,8 +586,10 @@ class PtraceGlobTestAATest(AATest): # get_glob_ext is not available for ptrace rules self.ruleset.get_glob_ext('ptrace read peer=/foo,') -#class PtraceDeleteTestAATest(AATest): -# pass + +# class PtraceDeleteTestAATest(AATest): +# pass + setup_all_loops(__name__) if __name__ == '__main__': diff --git a/utils/test/test-regex_matches.py b/utils/test/test-regex_matches.py index f6cd39bc1..828c210b3 100644 --- a/utils/test/test-regex_matches.py +++ b/utils/test/test-regex_matches.py @@ -14,15 +14,17 @@ import unittest from common_test import AATest, setup_all_loops, setup_aa from apparmor.common import AppArmorBug, AppArmorException -from apparmor.regex import ( strip_parenthesis, strip_quotes, parse_profile_start_line, re_match_include, - re_match_include_parse, - RE_PROFILE_START, RE_PROFILE_DBUS, RE_PROFILE_CAP, RE_PROFILE_PTRACE, RE_PROFILE_SIGNAL ) +from apparmor.regex import ( + strip_parenthesis, strip_quotes, parse_profile_start_line, re_match_include, + re_match_include_parse, + RE_PROFILE_START, RE_PROFILE_DBUS, RE_PROFILE_CAP, RE_PROFILE_PTRACE, RE_PROFILE_SIGNAL) class AARegexTest(AATest): def _run_test(self, params, expected): return _regex_test(self, params, expected) + class AANamedRegexTest(AATest): def _run_test(self, line, expected): '''Run a line through self.regex.search() and verify the result @@ -43,8 +45,7 @@ class AANamedRegexTest(AATest): match = matches.group(exp) if match: match = match - self.assertEqual(match, expected[exp], 'Group %s mismatch in rule %s' % (exp,line)) - + self.assertEqual(match, expected[exp], 'Group %s mismatch in rule %s' % (exp, line)) class AARegexHasComma(AATest): @@ -57,6 +58,7 @@ class AARegexHasComma(AATest): else: self.assertEqual(None, result, 'Found an unexpected comma in "%s"' % line) + regex_has_comma_testcases = ( ('dbus send%s', 'simple'), ('dbus (r, w, bind, eavesdrop)%s', 'embedded parens 01'), @@ -114,19 +116,24 @@ regex_has_comma_testcases = ( # ('@{BAR}="{bar,baz,blort%s" ', 'tricksy variable declaration') ) + def setup_has_comma_testcases(): i = 0 for (test_string, description) in regex_has_comma_testcases: i += 1 + def stub_test_comma(self, test_string=test_string): self._check(test_string % ',') + def stub_test_no_comma(self, test_string=test_string): self._check(test_string % ' ', False) + stub_test_comma.__doc__ = "test %s (w/comma)" % (description) stub_test_no_comma.__doc__ = "test %s (no comma)" % (description) setattr(AARegexHasComma, 'test_comma_%d' % (i), stub_test_comma) setattr(AARegexHasComma, 'test_no_comma_%d' % (i), stub_test_no_comma) + class AARegexSplitComment(AATest): '''Tests for RE_HAS_COMMENT_SPLIT''' @@ -134,13 +141,16 @@ class AARegexSplitComment(AATest): result = aa.RE_HAS_COMMENT_SPLIT.search(line) if expected: self.assertTrue(result, 'Couldn\'t find a comment in "%s"' % line) - self.assertEqual(result.group('comment'), comment, 'Expected comment "%s", got "%s"' - % (comment, result.group('comment'))) - self.assertEqual(result.group('not_comment'), not_comment, 'Expected not comment "%s", got "%s"' - % (not_comment, result.group('not_comment'))) + self.assertEqual( + result.group('comment'), comment, + 'Expected comment "%s", got "%s"' % (comment, result.group('comment'))) + self.assertEqual( + result.group('not_comment'), not_comment, + 'Expected not comment "%s", got "%s"' % (not_comment, result.group('not_comment'))) else: self.assertEqual(None, result, 'Found an unexpected comment "%s" in "%s"' - % ("" if result is None else result.group('comment'), line )) + % ("" if result is None else result.group('comment'), line)) + # Tuples of (string, expected result), where expected result is False if # the string should not be considered as having a comment, or a second @@ -162,15 +172,18 @@ regex_split_comment_testcases = ( ('pivot_root /old /new -> child,', False), ) + def setup_split_comment_testcases(): i = 0 for (test_string, result) in regex_split_comment_testcases: i += 1 + def stub_test(self, test_string=test_string, result=result): if result is False: self._check(test_string, False) else: self._check(test_string, True, not_comment=result[0], comment=result[1]) + stub_test.__doc__ = "test '%s'" % (test_string) setattr(AARegexSplitComment, 'test_split_comment_%d' % (i), stub_test) @@ -196,10 +209,7 @@ def _regex_test(self, line, expected): for (i, group) in enumerate(groups): if group: group = group.strip() - self.assertEqual(group, expected[i], 'Group %d mismatch in rule %s' % (i,line)) - - - + self.assertEqual(group, expected[i], 'Group %d mismatch in rule %s' % (i, line)) class AARegexCapability(AARegexTest): @@ -216,6 +226,7 @@ class AARegexCapability(AARegexTest): (' capabilitynet_raw,', False) ) + class AARegexDbus(AARegexTest): '''Tests for RE_PROFILE_DBUS''' @@ -223,15 +234,16 @@ class AARegexDbus(AARegexTest): self.regex = RE_PROFILE_DBUS tests = ( - (' dbus,', (None, None, 'dbus,', None, None)), - (' audit dbus,', ('audit', None, 'dbus,', None, None)), - (' dbus send member=no_comment,', (None, None, 'dbus send member=no_comment,', 'send member=no_comment', None)), - (' dbus send member=no_comment, # comment', (None, None, 'dbus send member=no_comment,', 'send member=no_comment', '# comment')), + (' dbus,', (None, None, 'dbus,', None, None)), + (' audit dbus,', ('audit', None, 'dbus,', None, None)), + (' dbus send member=no_comment,', (None, None, 'dbus send member=no_comment,', 'send member=no_comment', None)), + (' dbus send member=no_comment, # comment', (None, None, 'dbus send member=no_comment,', 'send member=no_comment', '# comment')), (' dbusdriver,', False), (' audit dbusdriver,', False), ) + class AARegexMount(AARegexTest): '''Tests for RE_PROFILE_MOUNT''' @@ -239,23 +251,22 @@ class AARegexMount(AARegexTest): self.regex = aa.RE_PROFILE_MOUNT tests = ( - (' mount,', (None, None, 'mount,', 'mount', None, None)), - (' audit mount,', ('audit', None, 'mount,', 'mount', None, None)), - (' umount,', (None, None, 'umount,', 'umount', None, None)), - (' audit umount,', ('audit', None, 'umount,', 'umount', None, None)), - (' unmount,', (None, None, 'unmount,', 'unmount', None, None)), - (' audit unmount,', ('audit', None, 'unmount,', 'unmount', None, None)), - (' remount,', (None, None, 'remount,', 'remount', None, None)), - (' deny remount,', (None, 'deny', 'remount,', 'remount', None, None)), + (' mount,', (None, None, 'mount,', 'mount', None, None)), + (' audit mount,', ('audit', None, 'mount,', 'mount', None, None)), + (' umount,', (None, None, 'umount,', 'umount', None, None)), + (' audit umount,', ('audit', None, 'umount,', 'umount', None, None)), + (' unmount,', (None, None, 'unmount,', 'unmount', None, None)), + (' audit unmount,', ('audit', None, 'unmount,', 'unmount', None, None)), + (' remount,', (None, None, 'remount,', 'remount', None, None)), + (' deny remount,', (None, 'deny', 'remount,', 'remount', None, None)), - (' mount, # comment', (None, None, 'mount,', 'mount', None, '# comment')), + (' mount, # comment', (None, None, 'mount,', 'mount', None, '# comment')), (' mountain,', False), (' audit mountain,', False), ) - class AARegexSignal(AARegexTest): '''Tests for RE_PROFILE_SIGNAL''' @@ -263,13 +274,13 @@ class AARegexSignal(AARegexTest): self.regex = RE_PROFILE_SIGNAL tests = ( - (' signal,', (None, None, 'signal,', None, None)), - (' audit signal,', ('audit', None, 'signal,', None, None)), - (' signal receive,', (None, None, 'signal receive,', 'receive', None)), - (' signal (send, receive),', (None, None, 'signal (send, receive),', '(send, receive)', None)), - (' audit signal (receive),', ('audit', None, 'signal (receive),', '(receive)', None)), - (' signal (send, receive) set=(usr1 usr2),', (None, None, 'signal (send, receive) set=(usr1 usr2),', '(send, receive) set=(usr1 usr2)', None)), - (' signal send set=(hup, quit) peer=/usr/sbin/daemon,', (None, None, 'signal send set=(hup, quit) peer=/usr/sbin/daemon,', 'send set=(hup, quit) peer=/usr/sbin/daemon', None)), + (' signal,', (None, None, 'signal,', None, None)), + (' audit signal,', ('audit', None, 'signal,', None, None)), + (' signal receive,', (None, None, 'signal receive,', 'receive', None)), + (' signal (send, receive),', (None, None, 'signal (send, receive),', '(send, receive)', None)), + (' audit signal (receive),', ('audit', None, 'signal (receive),', '(receive)', None)), + (' signal (send, receive) set=(usr1 usr2),', (None, None, 'signal (send, receive) set=(usr1 usr2),', '(send, receive) set=(usr1 usr2)', None)), + (' signal send set=(hup, quit) peer=/usr/sbin/daemon,', (None, None, 'signal send set=(hup, quit) peer=/usr/sbin/daemon,', 'send set=(hup, quit) peer=/usr/sbin/daemon', None)), (' signalling,', False), (' audit signalling,', False), @@ -284,13 +295,13 @@ class AARegexPtrace(AARegexTest): self.regex = RE_PROFILE_PTRACE tests = ( - # audit allow rule rule details comment - (' ptrace,', (None, None, 'ptrace,', None, None)), - (' audit ptrace,', ('audit', None, 'ptrace,', None, None)), - (' ptrace trace,', (None, None, 'ptrace trace,', 'trace', None)), - (' ptrace (tracedby, readby),', (None, None, 'ptrace (tracedby, readby),', '(tracedby, readby)', None)), - (' audit ptrace (read),', ('audit', None, 'ptrace (read),', '(read)', None)), - (' ptrace trace peer=/usr/sbin/daemon,', (None, None, 'ptrace trace peer=/usr/sbin/daemon,', 'trace peer=/usr/sbin/daemon', None)), + # audit allow rule rule details comment + (' ptrace,', (None, None, 'ptrace,', None, None)), + (' audit ptrace,', ('audit', None, 'ptrace,', None, None)), + (' ptrace trace,', (None, None, 'ptrace trace,', 'trace', None)), + (' ptrace (tracedby, readby),', (None, None, 'ptrace (tracedby, readby),', '(tracedby, readby)', None)), + (' audit ptrace (read),', ('audit', None, 'ptrace (read),', '(read)', None)), + (' ptrace trace peer=/usr/sbin/daemon,', (None, None, 'ptrace trace peer=/usr/sbin/daemon,', 'trace peer=/usr/sbin/daemon', None)), (' ptraceback,', False), (' audit ptraceback,', False), @@ -305,16 +316,12 @@ class AARegexPivotRoot(AARegexTest): self.regex = aa.RE_PROFILE_PIVOT_ROOT tests = ( - (' pivot_root,', (None, None, 'pivot_root,', None)), - (' audit pivot_root,', ('audit', None, 'pivot_root,', None)), - (' pivot_root oldroot=/new/old,', - (None, None, 'pivot_root 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 -> child,', - (None, 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,', None)), + (' pivot_root,', (None, None, 'pivot_root,', None)), + (' audit pivot_root,', ('audit', None, 'pivot_root,', None)), + (' pivot_root oldroot=/new/old,', (None, None, 'pivot_root 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 -> child,', (None, 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,', None)), ('pivot_root', False), # comma missing @@ -325,6 +332,7 @@ class AARegexPivotRoot(AARegexTest): ('pivot_rootbeer /new, # comment', False), ) + class AARegexUnix(AARegexTest): '''Tests for RE_PROFILE_UNIX''' @@ -332,24 +340,21 @@ class AARegexUnix(AARegexTest): self.regex = aa.RE_PROFILE_UNIX tests = ( - (' unix,', (None, None, 'unix,', None)), - (' audit unix,', ('audit', None, 'unix,', None)), - (' unix accept,', (None, None, 'unix accept,', None)), - (' allow unix connect,', (None, 'allow', 'unix connect,', None)), - (' audit allow unix bind,', ('audit', 'allow', 'unix bind,', None)), - (' deny unix bind,', (None, 'deny', 'unix bind,', None)), - ('unix peer=(label=@{profile_name}),', - (None, None, 'unix peer=(label=@{profile_name}),', None)), - ('unix (receive) peer=(label=unconfined),', - (None, None, 'unix (receive) peer=(label=unconfined),', None)), - (' unix (getattr, shutdown) peer=(addr=none),', - (None, None, 'unix (getattr, shutdown) peer=(addr=none),', None)), - ('unix (connect, receive, send) type=stream peer=(label=unconfined,addr="@/tmp/dbus-*"),', - (None, None, 'unix (connect, receive, send) type=stream peer=(label=unconfined,addr="@/tmp/dbus-*"),', None)), + (' unix,', (None, None, 'unix,', None)), + (' audit unix,', ('audit', None, 'unix,', None)), + (' unix accept,', (None, None, 'unix accept,', None)), + (' allow unix connect,', (None, 'allow', 'unix connect,', None)), + (' audit allow unix bind,', ('audit', 'allow', 'unix bind,', None)), + (' deny unix bind,', (None, 'deny', 'unix bind,', None)), + ('unix peer=(label=@{profile_name}),', (None, None, 'unix peer=(label=@{profile_name}),', None)), + ('unix (receive) peer=(label=unconfined),', (None, None, 'unix (receive) peer=(label=unconfined),', None)), + (' unix (getattr, shutdown) peer=(addr=none),', (None, None, 'unix (getattr, shutdown) peer=(addr=none),', None)), + ('unix (connect, receive, send) type=stream peer=(label=unconfined,addr="@/tmp/dbus-*"),', (None, None, 'unix (connect, receive, send) type=stream peer=(label=unconfined,addr="@/tmp/dbus-*"),', None)), ('unixlike', False), ('deny unixlike,', False), ) + class AANamedRegexProfileStart_2(AANamedRegexTest): '''Tests for RE_PROFILE_START''' @@ -357,62 +362,62 @@ class AANamedRegexProfileStart_2(AANamedRegexTest): self.regex = RE_PROFILE_START tests = ( - ('/bin/foo ', False), # no '{' - ('/bin/foo /bin/bar', False), # missing 'profile' keyword - ('profile {', False), # no attachment - (' profile foo bar /foo {', False), # missing quotes around "foo bar" - ('bin/foo {', False), # not starting with '/' - ('"bin/foo" {', False), # not starting with '/', quoted version + ('/bin/foo ', False), # no '{' + ('/bin/foo /bin/bar', False), # missing 'profile' keyword + ('profile {', False), # no attachment + (' profile foo bar /foo {', False), # missing quotes around "foo bar" + ('bin/foo {', False), # not starting with '/' + ('"bin/foo" {', False), # not starting with '/', quoted version - (' /foo {', { 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': None, 'comment': None }), - (' "/foo" {', { 'plainprofile': '"/foo"', 'namedprofile': None, 'attachment': None, 'flags': None, 'comment': None }), - (' profile /foo {', { 'plainprofile': None, 'namedprofile': '/foo', 'attachment': None, 'flags': None, 'comment': None }), - (' profile "/foo" {', { 'plainprofile': None, 'namedprofile': '"/foo"', 'attachment': None, 'flags': None, 'comment': None }), - (' profile foo /foo {', { 'plainprofile': None, 'namedprofile': 'foo', 'attachment': '/foo', 'flags': None, 'comment': None }), - (' profile foo /foo (audit) {', { 'plainprofile': None, 'namedprofile': 'foo', 'attachment': '/foo', 'flags': 'audit', 'comment': None }), - (' profile "foo" "/foo" {', { 'plainprofile': None, 'namedprofile': '"foo"', 'attachment': '"/foo"', 'flags': None, 'comment': None }), - (' profile "foo bar" /foo {', { 'plainprofile': None, 'namedprofile': '"foo bar"', 'attachment': '/foo', 'flags': None, 'comment': None }), - (' /foo (complain) {', { 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': 'complain', 'comment': None }), - (' /foo flags=(complain) {', { 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': 'complain', 'comment': None }), - (' /foo (complain) { # x', { 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': 'complain', 'comment': '# x'}), - (' /foo flags = ( complain ){#',{ 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': ' complain ', 'comment': '#'}), - (' @{foo} {', { 'plainprofile': '@{foo}', 'namedprofile': None, 'attachment': None, 'flags': None, 'comment': None }), - (' profile @{foo} {', { 'plainprofile': None, 'namedprofile': '@{foo}', 'attachment': None, 'flags': None, 'comment': None }), - (' profile @{foo} /bar {', { 'plainprofile': None, 'namedprofile': '@{foo}', 'attachment': '/bar', 'flags': None, 'comment': None }), - (' profile foo @{bar} {', { 'plainprofile': None, 'namedprofile': 'foo', 'attachment': '@{bar}', 'flags': None, 'comment': None }), - (' profile @{foo} @{bar} {', { 'plainprofile': None, 'namedprofile': '@{foo}', 'attachment': '@{bar}', 'flags': None, 'comment': None }), + (' /foo {', {'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': None, 'comment': None}), + (' "/foo" {', {'plainprofile': '"/foo"', 'namedprofile': None, 'attachment': None, 'flags': None, 'comment': None}), + (' profile /foo {', {'plainprofile': None, 'namedprofile': '/foo', 'attachment': None, 'flags': None, 'comment': None}), + (' profile "/foo" {', {'plainprofile': None, 'namedprofile': '"/foo"', 'attachment': None, 'flags': None, 'comment': None}), + (' profile foo /foo {', {'plainprofile': None, 'namedprofile': 'foo', 'attachment': '/foo', 'flags': None, 'comment': None}), + (' profile foo /foo (audit) {', {'plainprofile': None, 'namedprofile': 'foo', 'attachment': '/foo', 'flags': 'audit', 'comment': None}), + (' profile "foo" "/foo" {', {'plainprofile': None, 'namedprofile': '"foo"', 'attachment': '"/foo"', 'flags': None, 'comment': None}), + (' profile "foo bar" /foo {', {'plainprofile': None, 'namedprofile': '"foo bar"', 'attachment': '/foo', 'flags': None, 'comment': None}), + (' /foo (complain) {', {'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': 'complain', 'comment': None}), + (' /foo flags=(complain) {', {'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': 'complain', 'comment': None}), + (' /foo (complain) { # x', {'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': 'complain', 'comment': '# x'}), + (' /foo flags = ( complain ){#', {'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': ' complain ', 'comment': '#'}), + (' @{foo} {', {'plainprofile': '@{foo}', 'namedprofile': None, 'attachment': None, 'flags': None, 'comment': None}), + (' profile @{foo} {', {'plainprofile': None, 'namedprofile': '@{foo}', 'attachment': None, 'flags': None, 'comment': None}), + (' profile @{foo} /bar {', {'plainprofile': None, 'namedprofile': '@{foo}', 'attachment': '/bar', 'flags': None, 'comment': None}), + (' profile foo @{bar} {', {'plainprofile': None, 'namedprofile': 'foo', 'attachment': '@{bar}', 'flags': None, 'comment': None}), + (' profile @{foo} @{bar} {', {'plainprofile': None, 'namedprofile': '@{foo}', 'attachment': '@{bar}', 'flags': None, 'comment': None}), - (' /foo {', { 'plainprofile': '/foo', 'namedprofile': None, 'leadingspace': ' ' }), - ('/foo {', { 'plainprofile': '/foo', 'namedprofile': None, 'leadingspace': '' }), - (' profile foo {', { 'plainprofile': None, 'namedprofile': 'foo', 'leadingspace': ' ' }), - ('profile foo {', { 'plainprofile': None, 'namedprofile': 'foo', 'leadingspace': '' }), + (' /foo {', {'plainprofile': '/foo', 'namedprofile': None, 'leadingspace': ' '}), + ('/foo {', {'plainprofile': '/foo', 'namedprofile': None, 'leadingspace': ''}), + (' profile foo {', {'plainprofile': None, 'namedprofile': 'foo', 'leadingspace': ' '}), + ('profile foo {', {'plainprofile': None, 'namedprofile': 'foo', 'leadingspace': ''}), ) class Test_parse_profile_start_line(AATest): tests = ( - (' /foo {', { 'profile': '/foo', 'profile_keyword': False, 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': None, 'comment': None }), - (' "/foo" {', { 'profile': '/foo', 'profile_keyword': False, 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': None, 'comment': None }), - (' profile /foo {', { 'profile': '/foo', 'profile_keyword': True, 'plainprofile': None, 'namedprofile': '/foo', 'attachment': None, 'flags': None, 'comment': None }), - (' profile "/foo" {', { 'profile': '/foo', 'profile_keyword': True, 'plainprofile': None, 'namedprofile': '/foo', 'attachment': None, 'flags': None, 'comment': None }), - (' profile foo /foo {', { 'profile': 'foo', 'profile_keyword': True, 'plainprofile': None, 'namedprofile': 'foo', 'attachment': '/foo', 'flags': None, 'comment': None }), - (' profile foo /foo (audit) {', { 'profile': 'foo', 'profile_keyword': True, 'plainprofile': None, 'namedprofile': 'foo', 'attachment': '/foo', 'flags': 'audit', 'comment': None }), - (' profile "foo" "/foo" {', { 'profile': 'foo', 'profile_keyword': True, 'plainprofile': None, 'namedprofile': 'foo', 'attachment': '/foo', 'flags': None, 'comment': None }), - (' profile "foo bar" /foo {', { 'profile': 'foo bar', 'profile_keyword': True, 'plainprofile': None, 'namedprofile': 'foo bar','attachment': '/foo', 'flags': None, 'comment': None }), - (' /foo (complain) {', { 'profile': '/foo', 'profile_keyword': False, 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': 'complain', 'comment': None }), - (' /foo flags=(complain) {', { 'profile': '/foo', 'profile_keyword': False, 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': 'complain', 'comment': None }), - (' /foo flags = ( complain ){', { 'profile': '/foo', 'profile_keyword': False, 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': ' complain ', 'comment': None }), - (' /foo (complain) { # x', { 'profile': '/foo', 'profile_keyword': False, 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': 'complain', 'comment': '# x'}), + (' /foo {', {'profile': '/foo', 'profile_keyword': False, 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': None, 'comment': None}), + (' "/foo" {', {'profile': '/foo', 'profile_keyword': False, 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': None, 'comment': None}), + (' profile /foo {', {'profile': '/foo', 'profile_keyword': True, 'plainprofile': None, 'namedprofile': '/foo', 'attachment': None, 'flags': None, 'comment': None}), + (' profile "/foo" {', {'profile': '/foo', 'profile_keyword': True, 'plainprofile': None, 'namedprofile': '/foo', 'attachment': None, 'flags': None, 'comment': None}), + (' profile foo /foo {', {'profile': 'foo', 'profile_keyword': True, 'plainprofile': None, 'namedprofile': 'foo', 'attachment': '/foo', 'flags': None, 'comment': None}), + (' profile foo /foo (audit) {', {'profile': 'foo', 'profile_keyword': True, 'plainprofile': None, 'namedprofile': 'foo', 'attachment': '/foo', 'flags': 'audit', 'comment': None}), + (' profile "foo" "/foo" {', {'profile': 'foo', 'profile_keyword': True, 'plainprofile': None, 'namedprofile': 'foo', 'attachment': '/foo', 'flags': None, 'comment': None}), + (' profile "foo bar" /foo {', {'profile': 'foo bar', 'profile_keyword': True, 'plainprofile': None, 'namedprofile': 'foo bar', 'attachment': '/foo', 'flags': None, 'comment': None}), + (' /foo (complain) {', {'profile': '/foo', 'profile_keyword': False, 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': 'complain', 'comment': None}), + (' /foo flags=(complain) {', {'profile': '/foo', 'profile_keyword': False, 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': 'complain', 'comment': None}), + (' /foo flags = ( complain ){', {'profile': '/foo', 'profile_keyword': False, 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': ' complain ', 'comment': None}), + (' /foo (complain) { # x', {'profile': '/foo', 'profile_keyword': False, 'plainprofile': '/foo', 'namedprofile': None, 'attachment': None, 'flags': 'complain', 'comment': '# x'}), - (' /foo {', { 'profile': '/foo', 'plainprofile': '/foo', 'namedprofile': None, 'leadingspace': ' ' }), - ('/foo {', { 'profile': '/foo', 'plainprofile': '/foo', 'namedprofile': None, 'leadingspace': None }), - (' profile foo {', { 'profile': 'foo', 'plainprofile': None, 'namedprofile': 'foo', 'leadingspace': ' ' }), - ('profile foo {', { 'profile': 'foo', 'plainprofile': None, 'namedprofile': 'foo', 'leadingspace': None }), - (' @{foo} {', { 'profile': '@{foo}', 'plainprofile': '@{foo}', 'namedprofile': None, 'attachment': None, 'flags': None, 'comment': None }), - (' profile @{foo} {', { 'profile': '@{foo}', 'plainprofile': None, 'namedprofile': '@{foo}', 'attachment': None, 'flags': None, 'comment': None }), - (' profile @{foo} /bar {', { 'profile': '@{foo}', 'plainprofile': None, 'namedprofile': '@{foo}', 'attachment': '/bar', 'flags': None, 'comment': None }), - (' profile foo @{bar} {', { 'profile': 'foo', 'plainprofile': None, 'namedprofile': 'foo', 'attachment': '@{bar}', 'flags': None, 'comment': None }), - (' profile @{foo} @{bar} {', { 'profile': '@{foo}', 'plainprofile': None, 'namedprofile': '@{foo}', 'attachment': '@{bar}', 'flags': None, 'comment': None }), + (' /foo {', {'profile': '/foo', 'leadingspace': ' ', 'plainprofile': '/foo', 'namedprofile': None}), + ('/foo {', {'profile': '/foo', 'leadingspace': None, 'plainprofile': '/foo', 'namedprofile': None}), + (' profile foo {', {'profile': 'foo', 'leadingspace': ' ', 'plainprofile': None, 'namedprofile': 'foo'}), + ('profile foo {', {'profile': 'foo', 'leadingspace': None, 'plainprofile': None, 'namedprofile': 'foo'}), + (' @{foo} {', {'profile': '@{foo}', 'plainprofile': '@{foo}', 'namedprofile': None, 'attachment': None, 'flags': None, 'comment': None}), + (' profile @{foo} {', {'profile': '@{foo}', 'plainprofile': None, 'namedprofile': '@{foo}', 'attachment': None, 'flags': None, 'comment': None}), + (' profile @{foo} /bar {', {'profile': '@{foo}', 'plainprofile': None, 'namedprofile': '@{foo}', 'attachment': '/bar', 'flags': None, 'comment': None}), + (' profile foo @{bar} {', {'profile': 'foo', 'plainprofile': None, 'namedprofile': 'foo', 'attachment': '@{bar}', 'flags': None, 'comment': None}), + (' profile @{foo} @{bar} {', {'profile': '@{foo}', 'plainprofile': None, 'namedprofile': '@{foo}', 'attachment': '@{bar}', 'flags': None, 'comment': None}), ) def _run_test(self, line, expected): @@ -421,187 +426,195 @@ class Test_parse_profile_start_line(AATest): self.assertTrue(matches) for exp in expected: - self.assertEqual(matches[exp], expected[exp], 'Group %s mismatch in rule %s' % (exp,line)) + self.assertEqual( + matches[exp], expected[exp], + 'Group %s mismatch in rule %s' % (exp, line)) + class TestInvalid_parse_profile_start_line(AATest): tests = ( - ('/bin/foo ', False), # no '{' - ('/bin/foo /bin/bar', False), # missing 'profile' keyword - ('profile {', False), # no attachment - (' profile foo bar /foo {', False), # missing quotes around "foo bar" + ('/bin/foo ', False), # no '{' + ('/bin/foo /bin/bar', False), # missing 'profile' keyword + ('profile {', False), # no attachment + (' profile foo bar /foo {', False), # missing quotes around "foo bar" ) def _run_test(self, line, expected): with self.assertRaises(AppArmorBug): parse_profile_start_line(line, 'somefile') + class Test_re_match_include(AATest): tests = ( # #include - ('#include ', 'abstractions/base' ), # magic path - ('#include # comment', 'abstractions/base' ), - ('#include#comment', 'abstractions/base' ), - (' #include ', 'abstractions/base' ), - ('#include "/foo/bar"', '/foo/bar' ), # absolute path - ('#include "/foo/bar" # comment', '/foo/bar' ), - ('#include "/foo/bar"#comment', '/foo/bar' ), - (' #include "/foo/bar" ', '/foo/bar' ), + ('#include ', 'abstractions/base'), # magic path + ('#include # comment', 'abstractions/base'), + ('#include#comment', 'abstractions/base'), + (' #include ', 'abstractions/base'), + ('#include "/foo/bar"', '/foo/bar'), # absolute path + ('#include "/foo/bar" # comment', '/foo/bar'), + ('#include "/foo/bar"#comment', '/foo/bar'), + (' #include "/foo/bar" ', '/foo/bar'), # include (without #) - ('include ', 'abstractions/base' ), # magic path - ('include # comment', 'abstractions/base' ), - ('include#comment', 'abstractions/base' ), - (' include ', 'abstractions/base' ), - ('include "/foo/bar"', '/foo/bar' ), # absolute path - ('include "/foo/bar" # comment', '/foo/bar' ), - ('include "/foo/bar"#comment', '/foo/bar' ), - (' include "/foo/bar" ', '/foo/bar' ), + ('include ', 'abstractions/base'), # magic path + ('include # comment', 'abstractions/base'), + ('include#comment', 'abstractions/base'), + (' include ', 'abstractions/base'), + ('include "/foo/bar"', '/foo/bar'), # absolute path + ('include "/foo/bar" # comment', '/foo/bar'), + ('include "/foo/bar"#comment', '/foo/bar'), + (' include "/foo/bar" ', '/foo/bar'), - (' some #include ', None, ), # non-matching - (' /etc/fstab r,', None, ), - ('/usr/include r,', None, ), - ('/include r,', None, ), - (' #include if exists ', None, ), # include if exists - (' #include if exists "/foo/bar"', None, ), + (' some #include ', None), # non-matching + (' /etc/fstab r,', None), + ('/usr/include r,', None), + ('/include r,', None), + (' #include if exists ', None), # include if exists + (' #include if exists "/foo/bar"', None), ) def _run_test(self, params, expected): self.assertEqual(re_match_include(params), expected) + class TestInvalid_re_match_include(AATest): tests = ( - ('#include <>', AppArmorException ), # '#include' - ('#include < >', AppArmorException ), - ('#include ""', AppArmorException ), - ('#include " "', AppArmorException ), - ('#include', AppArmorException ), - ('#include ', AppArmorException ), - ('#include "foo"', AppArmorException ), # LP: 1738880 (relative) - ('#include "foo" # comment', AppArmorException ), - ('#include "foo"#comment', AppArmorException ), - (' #include "foo" ', AppArmorException ), - ('#include "foo/bar"', AppArmorException ), - ('#include "foo/bar" # comment', AppArmorException ), - ('#include "foo/bar"#comment', AppArmorException ), - (' #include "foo/bar" ', AppArmorException ), - ('#include foo', AppArmorException ), # LP: 1738879 (no quotes) - ('#include foo/bar', AppArmorException ), - ('#include /foo/bar', AppArmorException ), - ('#include foo bar', AppArmorException ), # LP: 1738877 (space in name) - ('#include foo bar/baz', AppArmorException ), - ('#include "foo bar"', AppArmorException ), - ('#include /foo bar', AppArmorException ), - ('#include "/foo bar"', AppArmorException ), - ('#include "foo bar/baz"', AppArmorException ), + ('#include <>', AppArmorException), # '#include' + ('#include < >', AppArmorException), + ('#include ""', AppArmorException), + ('#include " "', AppArmorException), + ('#include', AppArmorException), + ('#include ', AppArmorException), + ('#include "foo"', AppArmorException), # LP: 1738880 (relative) + ('#include "foo" # comment', AppArmorException), + ('#include "foo"#comment', AppArmorException), + (' #include "foo" ', AppArmorException), + ('#include "foo/bar"', AppArmorException), + ('#include "foo/bar" # comment', AppArmorException), + ('#include "foo/bar"#comment', AppArmorException), + (' #include "foo/bar" ', AppArmorException), + ('#include foo', AppArmorException), # LP: 1738879 (no quotes) + ('#include foo/bar', AppArmorException), + ('#include /foo/bar', AppArmorException), + ('#include foo bar', AppArmorException), # LP: 1738877 (space in name) + ('#include foo bar/baz', AppArmorException), + ('#include "foo bar"', AppArmorException), + ('#include /foo bar', AppArmorException), + ('#include "/foo bar"', AppArmorException), + ('#include "foo bar/baz"', AppArmorException), - ('include <>', AppArmorException ), # 'include' - ('include < >', AppArmorException ), - ('include ""', AppArmorException ), - ('include " "', AppArmorException ), - ('include', AppArmorException ), - ('include ', AppArmorException ), - ('include "foo"', AppArmorException ), # LP: 1738880 (relative) - ('include "foo" # comment', AppArmorException ), - ('include "foo"#comment', AppArmorException ), - (' include "foo" ', AppArmorException ), - ('include "foo/bar"', AppArmorException ), - ('include "foo/bar" # comment', AppArmorException ), - ('include "foo/bar"#comment', AppArmorException ), - (' include "foo/bar" ', AppArmorException ), - ('include foo', AppArmorException ), # LP: 1738879 (no quotes) - ('include foo/bar', AppArmorException ), - ('include /foo/bar', AppArmorException ), - ('include foo bar', AppArmorException ), # LP: 1738877 (space in name) - ('include foo bar/baz', AppArmorException ), - ('include "foo bar"', AppArmorException ), - ('include /foo bar', AppArmorException ), - ('include "/foo bar"', AppArmorException ), - ('include "foo bar/baz"', AppArmorException ), + ('include <>', AppArmorException), # 'include' + ('include < >', AppArmorException), + ('include ""', AppArmorException), + ('include " "', AppArmorException), + ('include', AppArmorException), + ('include ', AppArmorException), + ('include "foo"', AppArmorException), # LP: 1738880 (relative) + ('include "foo" # comment', AppArmorException), + ('include "foo"#comment', AppArmorException), + (' include "foo" ', AppArmorException), + ('include "foo/bar"', AppArmorException), + ('include "foo/bar" # comment', AppArmorException), + ('include "foo/bar"#comment', AppArmorException), + (' include "foo/bar" ', AppArmorException), + ('include foo', AppArmorException), # LP: 1738879 (no quotes) + ('include foo/bar', AppArmorException), + ('include /foo/bar', AppArmorException), + ('include foo bar', AppArmorException), # LP: 1738877 (space in name) + ('include foo bar/baz', AppArmorException), + ('include "foo bar"', AppArmorException), + ('include /foo bar', AppArmorException), + ('include "/foo bar"', AppArmorException), + ('include "foo bar/baz"', AppArmorException), ) def _run_test(self, params, expected): with self.assertRaises(expected): re_match_include(params) + class Test_re_match_include_parse(AATest): tests = ( - # path if exists magic path + # path if exists magic path # #include - ('#include ', ('abstractions/base', False, True ) ), # magic path - ('#include # comment', ('abstractions/base', False, True ) ), - ('#include#comment', ('abstractions/base', False, True ) ), - (' #include ', ('abstractions/base', False, True ) ), - ('#include "/foo/bar"', ('/foo/bar', False, False) ), # absolute path - ('#include "/foo/bar" # comment', ('/foo/bar', False, False) ), - ('#include "/foo/bar"#comment', ('/foo/bar', False, False) ), - (' #include "/foo/bar" ', ('/foo/bar', False, False) ), + ('#include ', ('abstractions/base', False, True)), # magic path + ('#include # comment', ('abstractions/base', False, True)), + ('#include#comment', ('abstractions/base', False, True)), + (' #include ', ('abstractions/base', False, True)), + ('#include "/foo/bar"', ('/foo/bar', False, False)), # absolute path + ('#include "/foo/bar" # comment', ('/foo/bar', False, False)), + ('#include "/foo/bar"#comment', ('/foo/bar', False, False)), + (' #include "/foo/bar" ', ('/foo/bar', False, False)), # include (without #) - ('include ', ('abstractions/base', False, True ) ), # magic path - ('include # comment', ('abstractions/base', False, True ) ), - ('include#comment', ('abstractions/base', False, True ) ), - (' include ', ('abstractions/base', False, True ) ), - ('include "/foo/bar"', ('/foo/bar', False, False) ), # absolute path - ('include "/foo/bar" # comment', ('/foo/bar', False, False) ), - ('include "/foo/bar"#comment', ('/foo/bar', False, False) ), - (' include "/foo/bar" ', ('/foo/bar', False, False) ), + ('include ', ('abstractions/base', False, True)), # magic path + ('include # comment', ('abstractions/base', False, True)), + ('include#comment', ('abstractions/base', False, True)), + (' include ', ('abstractions/base', False, True)), + ('include "/foo/bar"', ('/foo/bar', False, False)), # absolute path + ('include "/foo/bar" # comment', ('/foo/bar', False, False)), + ('include "/foo/bar"#comment', ('/foo/bar', False, False)), + (' include "/foo/bar" ', ('/foo/bar', False, False)), # #include if exists - ('#include if exists ', ('abstractions/base', True, True ) ), # magic path - ('#include if exists # comment', ('abstractions/base', True, True ) ), - ('#include if exists#comment', ('abstractions/base', True, True ) ), - (' #include if exists ', ('abstractions/base', True, True ) ), - ('#include if exists "/foo/bar"', ('/foo/bar', True, False) ), # absolute path - ('#include if exists "/foo/bar" # comment', ('/foo/bar', True, False) ), - ('#include if exists "/foo/bar"#comment', ('/foo/bar', True, False) ), - (' #include if exists "/foo/bar" ', ('/foo/bar', True, False) ), + ('#include if exists ', ('abstractions/base', True, True)), # magic path + ('#include if exists # comment', ('abstractions/base', True, True)), + ('#include if exists#comment', ('abstractions/base', True, True)), + (' #include if exists ', ('abstractions/base', True, True)), + ('#include if exists "/foo/bar"', ('/foo/bar', True, False)), # absolute path + ('#include if exists "/foo/bar" # comment', ('/foo/bar', True, False)), + ('#include if exists "/foo/bar"#comment', ('/foo/bar', True, False)), + (' #include if exists "/foo/bar" ', ('/foo/bar', True, False)), # include if exists (without #) - ('include if exists ', ('abstractions/base', True, True ) ), # magic path - ('include if exists # comment', ('abstractions/base', True, True ) ), - ('include if exists#comment', ('abstractions/base', True, True ) ), - (' include if exists ', ('abstractions/base', True, True ) ), - ('include if exists "/foo/bar"', ('/foo/bar', True, False) ), # absolute path - ('include if exists "/foo/bar" # comment', ('/foo/bar', True, False) ), - ('include if exists "/foo/bar"#comment', ('/foo/bar', True, False) ), - (' include if exists "/foo/bar" ', ('/foo/bar', True, False) ), + ('include if exists ', ('abstractions/base', True, True)), # magic path + ('include if exists # comment', ('abstractions/base', True, True)), + ('include if exists#comment', ('abstractions/base', True, True)), + (' include if exists ', ('abstractions/base', True, True)), + ('include if exists "/foo/bar"', ('/foo/bar', True, False)), # absolute path + ('include if exists "/foo/bar" # comment', ('/foo/bar', True, False)), + ('include if exists "/foo/bar"#comment', ('/foo/bar', True, False)), + (' include if exists "/foo/bar" ', ('/foo/bar', True, False)), - (' some #include if exists ', (None, None, None ) ), # non-matching - (' /etc/fstab r,', (None, None, None ) ), - ('/usr/include r,', (None, None, None ) ), - ('/include r,', (None, None, None ) ), - ('abi ,', (None, None, None ) ), # abi rule + (' some #include if exists ', (None, None, None)), # non-matching + (' /etc/fstab r,', (None, None, None)), + ('/usr/include r,', (None, None, None)), + ('/include r,', (None, None, None)), + ('abi ,', (None, None, None)), # abi rule ) def _run_test(self, params, expected): self.assertEqual(re_match_include_parse(params, 'include'), expected) + class Test_re_match_include_parse_abi(AATest): tests = ( - # path if exists magic path - ('abi ,', ('abi/4.19', False, True ) ), # magic path - ('abi , # comment', ('abi/4.19', False, True ) ), - (' abi , # comment', ('abi/4.19', False, True ) ), - ('abi "/abi/4.19" ,', ('/abi/4.19', False, False) ), # quoted path starting with / - ('abi "/abi/4.19", # comment', ('/abi/4.19', False, False) ), - (' abi "/abi/4.19" , # comment ', ('/abi/4.19', False, False) ), - (' abi "abi/4.19" , # comment ', ('abi/4.19', False, False) ), # quoted path, no leading / - ('abi abi/4.19,', ('abi/4.19', False, False) ), # without quotes - ('some abi ,', (None, None, None ) ), # non-matching - (' /etc/fstab r,', (None, None, None ) ), - ('/usr/abi r,', (None, None, None ) ), - ('/abi r,', (None, None, None ) ), - ('#include ', (None, None, None ) ), # include rule path + # path if exists magic path + ('abi ,', ('abi/4.19', False, True)), # magic path + ('abi , # comment', ('abi/4.19', False, True)), + (' abi , # comment', ('abi/4.19', False, True)), + ('abi "/abi/4.19" ,', ('/abi/4.19', False, False)), # quoted path starting with / + ('abi "/abi/4.19", # comment', ('/abi/4.19', False, False)), + (' abi "/abi/4.19" , # comment ', ('/abi/4.19', False, False)), + (' abi "abi/4.19" , # comment ', ('abi/4.19', False, False)), # quoted path, no leading / + ('abi abi/4.19,', ('abi/4.19', False, False)), # without quotes + ('some abi ,', (None, None, None)), # non-matching + (' /etc/fstab r,', (None, None, None)), + ('/usr/abi r,', (None, None, None)), + ('/abi r,', (None, None, None)), + ('#include ', (None, None, None)), # include rule path ) def _run_test(self, params, expected): self.assertEqual(re_match_include_parse(params, 'abi'), expected) + class Test_re_match_include_parse_errors(AATest): tests = ( - (('include <>', 'include'), AppArmorException), # various rules with empty filename - (('include ""', 'include'), AppArmorException), - (('include ', 'include'), AppArmorException), - (('abi <>,', 'abi'), AppArmorException), - (('abi "",', 'abi'), AppArmorException), - (('abi ,', 'abi'), AppArmorException), - (('abi ,', 'invalid'), AppArmorBug), # invalid rule name + (('include <>', 'include'), AppArmorException), # various rules with empty filename + (('include ""', 'include'), AppArmorException), + (('include ', 'include'), AppArmorException), + (('abi <>,', 'abi'), AppArmorException), + (('abi "",', 'abi'), AppArmorException), + (('abi ,', 'abi'), AppArmorException), + (('abi ,', 'invalid'), AppArmorBug), # invalid rule name ) def _run_test(self, params, expected): @@ -609,37 +622,39 @@ class Test_re_match_include_parse_errors(AATest): rule, rule_name = params re_match_include_parse(rule, rule_name) + class TestStripParenthesis(AATest): tests = ( - ('foo', 'foo' ), - ('(foo)', 'foo' ), - ('( foo )', 'foo' ), - ('(foo', '(foo' ), - ('foo )', 'foo )' ), - ('foo ()', 'foo ()' ), - ('()', '' ), - ('( )', '' ), - ('(())', '()' ), - (' (foo)', '(foo)' ), # parenthesis not first char, whitespace stripped nevertheless - ('(foo) ', '(foo)' ), # parenthesis not last char, whitespace stripped nevertheless + ('foo', 'foo'), + ('(foo)', 'foo'), + ('( foo )', 'foo'), + ('(foo', '(foo'), + ('foo )', 'foo )'), + ('foo ()', 'foo ()'), + ('()', ''), + ('( )', ''), + ('(())', '()'), + (' (foo)', '(foo)'), # parenthesis not first char, whitespace stripped nevertheless + ('(foo) ', '(foo)'), # parenthesis not last char, whitespace stripped nevertheless ) def _run_test(self, params, expected): self.assertEqual(strip_parenthesis(params), expected) + class TestStripQuotes(AATest): tests = ( - ('foo', 'foo'), - ('"foo"', 'foo'), - ('"foo', '"foo'), - ('foo"', 'foo"'), - ('""', ''), - ('foo"bar', 'foo"bar'), - ('"foo"bar"', 'foo"bar'), - ('""""foo"bar""""', '"""foo"bar"""'), - ('', ''), - ('/', '/'), - ('"', '"'), + ('foo', 'foo'), + ('"foo"', 'foo'), + ('"foo', '"foo'), + ('foo"', 'foo"'), + ('""', ''), + ('foo"bar', 'foo"bar'), + ('"foo"bar"', 'foo"bar'), + ('""""foo"bar""""', '"""foo"bar"""'), + ('', ''), + ('/', '/'), + ('"', '"'), ) def _run_test(self, params, expected): diff --git a/utils/test/test-rlimit.py b/utils/test/test-rlimit.py index 6e46daf72..b6f624567 100644 --- a/utils/test/test-rlimit.py +++ b/utils/test/test-rlimit.py @@ -20,15 +20,16 @@ from common_test import AATest, setup_all_loops from apparmor.rule.rlimit import RlimitRule, RlimitRuleset, split_unit from apparmor.rule import BaseRule from apparmor.common import AppArmorException, AppArmorBug -#from apparmor.logparser import ReadLog +# from apparmor.logparser import ReadLog from apparmor.translations import init_translation _ = init_translation() -exp = namedtuple('exp', ('audit', 'allow_keyword', 'deny', 'comment', - 'rlimit', 'value', 'all_values')) +exp = namedtuple( + 'exp', ('audit', 'allow_keyword', 'deny', 'comment', 'rlimit', 'value', 'all_values')) # --- tests for single RlimitRule --- # + class RlimitTest(AATest): def _compare_obj(self, obj, expected): self.assertEqual(expected.allow_keyword, obj.allow_keyword) @@ -39,31 +40,32 @@ class RlimitTest(AATest): self.assertEqual(expected.deny, obj.deny) self.assertEqual(expected.comment, obj.comment) + class RlimitTestParse(RlimitTest): tests = ( - # rawrule audit allow deny comment rlimit value all/infinity? - ('set rlimit as <= 2047MB,' , exp(False, False, False, '' , 'as' , '2047MB' , False)), - ('set rlimit as <= 2047 MB,' , exp(False, False, False, '' , 'as' , '2047 MB' , False)), - ('set rlimit cpu <= 1024,' , exp(False, False, False, '' , 'cpu' , '1024' , False)), - ('set rlimit stack <= 1024GB,' , exp(False, False, False, '' , 'stack' , '1024GB' , False)), - ('set rlimit stack <= 1024 GB,' , exp(False, False, False, '' , 'stack' , '1024 GB' , False)), - ('set rlimit rtprio <= 10, # comment' , exp(False, False, False, ' # comment' , 'rtprio' , '10' , False)), - ('set rlimit core <= 44444KB,' , exp(False, False, False, '' , 'core' , '44444KB' , False)), - ('set rlimit core <= 44444 KB,' , exp(False, False, False, '' , 'core' , '44444 KB' , False)), - ('set rlimit rttime <= 60ms,' , exp(False, False, False, '' , 'rttime' , '60ms' , False)), - ('set rlimit cpu <= infinity,' , exp(False, False, False, '' , 'cpu' , None , True )), - ('set rlimit nofile <= 256,' , exp(False, False, False, '' , 'nofile' , '256' , False)), - ('set rlimit data <= 4095KB,' , exp(False, False, False, '' , 'data' , '4095KB' , False)), - ('set rlimit cpu <= 12, # cmt' , exp(False, False, False, ' # cmt' , 'cpu' , '12' , False)), - ('set rlimit ofile <= 1234,' , exp(False, False, False, '' , 'ofile' , '1234' , False)), - ('set rlimit msgqueue <= 4444,' , exp(False, False, False, '' , 'msgqueue' , '4444' , False)), - ('set rlimit nice <= -10,' , exp(False, False, False, '' , 'nice' , '-10' , False)), - ('set rlimit rttime <= 60minutes,' , exp(False, False, False, '' , 'rttime' , '60minutes' , False)), - ('set rlimit fsize <= 1023MB,' , exp(False, False, False, '' , 'fsize' , '1023MB' , False)), - ('set rlimit nproc <= 1,' , exp(False, False, False, '' , 'nproc' , '1' , False)), - ('set rlimit rss <= infinity, # cmt' , exp(False, False, False, ' # cmt' , 'rss' , None , True )), - ('set rlimit memlock <= 10240,' , exp(False, False, False, '' , 'memlock' , '10240' , False)), - ('set rlimit sigpending <= 42,' , exp(False, False, False, '' , 'sigpending' , '42' , False)), + # rawrule audit allow deny comment rlimit value all/infinity? + ('set rlimit as <= 2047MB,', exp(False, False, False, '', 'as', '2047MB', False)), + ('set rlimit as <= 2047 MB,', exp(False, False, False, '', 'as', '2047 MB', False)), + ('set rlimit cpu <= 1024,', exp(False, False, False, '', 'cpu', '1024', False)), + ('set rlimit stack <= 1024GB,', exp(False, False, False, '', 'stack', '1024GB', False)), + ('set rlimit stack <= 1024 GB,', exp(False, False, False, '', 'stack', '1024 GB', False)), + ('set rlimit rtprio <= 10, # comment', exp(False, False, False, ' # comment', 'rtprio', '10', False)), + ('set rlimit core <= 44444KB,', exp(False, False, False, '', 'core', '44444KB', False)), + ('set rlimit core <= 44444 KB,', exp(False, False, False, '', 'core', '44444 KB', False)), + ('set rlimit rttime <= 60ms,', exp(False, False, False, '', 'rttime', '60ms', False)), + ('set rlimit cpu <= infinity,', exp(False, False, False, '', 'cpu', None, True)), + ('set rlimit nofile <= 256,', exp(False, False, False, '', 'nofile', '256', False)), + ('set rlimit data <= 4095KB,', exp(False, False, False, '', 'data', '4095KB', False)), + ('set rlimit cpu <= 12, # cmt', exp(False, False, False, ' # cmt', 'cpu', '12', False)), + ('set rlimit ofile <= 1234,', exp(False, False, False, '', 'ofile', '1234', False)), + ('set rlimit msgqueue <= 4444,', exp(False, False, False, '', 'msgqueue', '4444', False)), + ('set rlimit nice <= -10,', exp(False, False, False, '', 'nice', '-10', False)), + ('set rlimit rttime <= 60minutes,', exp(False, False, False, '', 'rttime', '60minutes', False)), + ('set rlimit fsize <= 1023MB,', exp(False, False, False, '', 'fsize', '1023MB', False)), + ('set rlimit nproc <= 1,', exp(False, False, False, '', 'nproc', '1', False)), + ('set rlimit rss <= infinity, # cmt', exp(False, False, False, ' # cmt', 'rss', None, True)), + ('set rlimit memlock <= 10240,', exp(False, False, False, '', 'memlock', '10240', False)), + ('set rlimit sigpending <= 42,', exp(False, False, False, '', 'sigpending', '42', False)), ) def _run_test(self, rawrule, expected): @@ -72,59 +74,61 @@ class RlimitTestParse(RlimitTest): self.assertEqual(rawrule.strip(), obj.raw_rule) self._compare_obj(obj, expected) + class RlimitTestParseInvalid(RlimitTest): tests = ( - ('set rlimit,' , AppArmorException), # missing parts - ('set rlimit <= 5,' , AppArmorException), - ('set rlimit cpu <= ,' , AppArmorException), - ('set rlimit cpu <= "",' , AppArmorBug), # evil quoting trick - ('set rlimit foo <= 5,' , AppArmorException), # unknown rlimit - ('set rlimit rttime <= 60m,' , AppArmorException), # 'm' could mean 'ms' or 'minutes' - ('set rlimit cpu <= 20ms,' , AppArmorException), # cpu doesn't support 'ms'... - ('set rlimit cpu <= 20us,' , AppArmorException), # ... or 'us' - ('set rlimit nice <= 20MB,' , AppArmorException), # invalid unit for this rlimit type - ('set rlimit cpu <= 20MB,' , AppArmorException), - ('set rlimit data <= 20seconds,' , AppArmorException), - ('set rlimit locks <= 20seconds,' , AppArmorException), + ('set rlimit,', AppArmorException), # missing parts + ('set rlimit <= 5,', AppArmorException), + ('set rlimit cpu <= ,', AppArmorException), + ('set rlimit cpu <= "",', AppArmorBug), # evil quoting trick + ('set rlimit foo <= 5,', AppArmorException), # unknown rlimit + ('set rlimit rttime <= 60m,', AppArmorException), # 'm' could mean 'ms' or 'minutes' + ('set rlimit cpu <= 20ms,', AppArmorException), # cpu doesn't support 'ms'... + ('set rlimit cpu <= 20us,', AppArmorException), # ... or 'us' + ('set rlimit nice <= 20MB,', AppArmorException), # invalid unit for this rlimit type + ('set rlimit cpu <= 20MB,', AppArmorException), + ('set rlimit data <= 20seconds,', AppArmorException), + ('set rlimit locks <= 20seconds,', AppArmorException), ) def _run_test(self, rawrule, expected): - #self.assertFalse(RlimitRule.match(rawrule)) # the main regex isn't very strict + # self.assertFalse(RlimitRule.match(rawrule)) # the main regex isn't very strict with self.assertRaises(expected): RlimitRule.parse(rawrule) + class RlimitTestParseFromLog(RlimitTest): pass # def test_net_from_log(self): - # parser = ReadLog('', '', '') - - # event = 'type=AVC ...' - - # parsed_event = parser.parse_event(event) - - # self.assertEqual(parsed_event, { - # ... - # }) - - # obj = RlimitRule(RlimitRule.ALL, parsed_event['name2'], log_event=parsed_event) - - # # audit allow deny comment rlimit value all? - # expected = exp(False, False, False, '' , None, '/foo/rename', False) - - # self._compare_obj(obj, expected) - - # self.assertEqual(obj.get_raw(1), ' rlimit -> /foo/rename,') + # parser = ReadLog('', '', '') + # + # event = 'type=AVC ...' + # + # parsed_event = parser.parse_event(event) + # + # self.assertEqual(parsed_event, { + # ... + # }) + # + # obj = RlimitRule(RlimitRule.ALL, parsed_event['name2'], log_event=parsed_event) + # + # # audit allow deny comment rlimit value all? + # expected = exp(False, False, False, '', None, '/foo/rename', False) + # + # self._compare_obj(obj, expected) + # + # self.assertEqual(obj.get_raw(1), ' rlimit -> /foo/rename,') class RlimitFromInit(RlimitTest): tests = ( - # RlimitRule object audit allow deny comment rlimit value all/infinity? - (RlimitRule('as', '2047MB') , exp(False, False, False, '' , 'as' , '2047MB' , False)), - (RlimitRule('as', '2047 MB') , exp(False, False, False, '' , 'as' , '2047 MB' , False)), - (RlimitRule('cpu', '1024') , exp(False, False, False, '' , 'cpu' , '1024' , False)), - (RlimitRule('rttime', '60minutes') , exp(False, False, False, '' , 'rttime' , '60minutes', False)), - (RlimitRule('nice', '-10') , exp(False, False, False, '' , 'nice' , '-10' , False)), - (RlimitRule('rss', RlimitRule.ALL) , exp(False, False, False, '' , 'rss' , None , True )), + # RlimitRule object audit allow deny comment rlimit value all/infinity? + (RlimitRule('as', '2047MB'), exp(False, False, False, '', 'as', '2047MB', False)), + (RlimitRule('as', '2047 MB'), exp(False, False, False, '', 'as', '2047 MB', False)), + (RlimitRule('cpu', '1024'), exp(False, False, False, '', 'cpu', '1024', False)), + (RlimitRule('rttime', '60minutes'), exp(False, False, False, '', 'rttime', '60minutes', False)), + (RlimitRule('nice', '-10'), exp(False, False, False, '', 'nice', '-10', False)), + (RlimitRule('rss', RlimitRule.ALL), exp(False, False, False, '', 'rss', None, True)), ) def _run_test(self, obj, expected): @@ -133,17 +137,17 @@ class RlimitFromInit(RlimitTest): class InvalidRlimitInit(AATest): tests = ( - # init params expected exception - (('as' , '' ) , AppArmorBug), # empty value - (('' , '1024' ) , AppArmorException), # empty rlimit - ((' ', '1024' ) , AppArmorException), # whitespace rlimit - (('as' , ' ' ) , AppArmorBug), # whitespace value - (('xyxy', '1024' ) , AppArmorException), # invalid rlimit - ((dict(), '1024' ) , AppArmorBug), # wrong type for rlimit - ((None , '1024' ) , AppArmorBug), # wrong type for rlimit - (('as' , dict() ) , AppArmorBug), # wrong type for value - (('as' , None ) , AppArmorBug), # wrong type for value - (('cpu' , '100xy2' ) , AppArmorException), # invalid unit + # init params expected exception + (('as', ''), AppArmorBug), # empty value + (('', '1024'), AppArmorException), # empty rlimit + ((' ', '1024'), AppArmorException), # whitespace rlimit + (('as', ' '), AppArmorBug), # whitespace value + (('xyxy', '1024'), AppArmorException), # invalid rlimit + ((dict(), '1024'), AppArmorBug), # wrong type for rlimit + ((None, '1024'), AppArmorBug), # wrong type for rlimit + (('as', dict()), AppArmorBug), # wrong type for value + (('as', None), AppArmorBug), # wrong type for value + (('cpu', '100xy2'), AppArmorException), # invalid unit ) def _run_test(self, params, expected): @@ -203,14 +207,14 @@ class InvalidRlimitTest(AATest): class WriteRlimitTest(AATest): tests = ( - # raw rule clean rule - (' set rlimit cpu <= 1024 , # foo ' , 'set rlimit cpu <= 1024, # foo'), - (' set rlimit stack <= 1024GB ,' , 'set rlimit stack <= 1024GB,'), - (' set rlimit rttime <= 100ms , # foo bar' , 'set rlimit rttime <= 100ms, # foo bar'), - (' set rlimit cpu <= infinity , ' , 'set rlimit cpu <= infinity,'), - (' set rlimit msgqueue <= 4444 , ' , 'set rlimit msgqueue <= 4444,'), - (' set rlimit nice <= 5 , # foo bar' , 'set rlimit nice <= 5, # foo bar'), - (' set rlimit nice <= -5 , # cmt' , 'set rlimit nice <= -5, # cmt'), + # raw rule clean rule + (' set rlimit cpu <= 1024 , # foo ', 'set rlimit cpu <= 1024, # foo'), + (' set rlimit stack <= 1024GB ,', 'set rlimit stack <= 1024GB,'), + (' set rlimit rttime <= 100ms , # foo bar', 'set rlimit rttime <= 100ms, # foo bar'), + (' set rlimit cpu <= infinity , ', 'set rlimit cpu <= infinity,'), + (' set rlimit msgqueue <= 4444 , ', 'set rlimit msgqueue <= 4444,'), + (' set rlimit nice <= 5 , # foo bar', 'set rlimit nice <= 5, # foo bar'), + (' set rlimit nice <= -5 , # cmt', 'set rlimit nice <= -5, # cmt'), ) def _run_test(self, rawrule, expected): @@ -238,83 +242,97 @@ class RlimitCoveredTest(AATest): self.assertTrue(RlimitRule.match(param)) - self.assertEqual(obj.is_equal(check_obj), expected[0], 'Mismatch in is_equal, expected %s' % expected[0]) - self.assertEqual(obj.is_equal(check_obj, True), expected[1], 'Mismatch in is_equal/strict, expected %s' % expected[1]) + self.assertEqual( + obj.is_equal(check_obj), expected[0], + 'Mismatch in is_equal, expected %s' % expected[0]) + self.assertEqual( + obj.is_equal(check_obj, True), expected[1], + 'Mismatch in is_equal/strict, expected %s' % expected[1]) + + self.assertEqual( + obj.is_covered(check_obj), expected[2], + 'Mismatch in is_covered, expected %s' % expected[2]) + self.assertEqual( + obj.is_covered(check_obj, True, True), expected[3], + 'Mismatch in is_covered/exact, expected %s' % expected[3]) - self.assertEqual(obj.is_covered(check_obj), expected[2], 'Mismatch in is_covered, expected %s' % expected[2]) - self.assertEqual(obj.is_covered(check_obj, True, True), expected[3], 'Mismatch in is_covered/exact, expected %s' % expected[3]) class RlimitCoveredTest_01(RlimitCoveredTest): rule = 'set rlimit cpu <= 150,' tests = ( - # rule equal strict equal covered covered exact - ('set rlimit as <= 100MB,' , ( False , False , False , False )), - ('set rlimit rttime <= 150,' , ( False , False , False , False )), - ('set rlimit cpu <= 100,' , ( False , False , True , True )), - ('set rlimit cpu <= 150,' , ( True , True , True , True )), - ('set rlimit cpu <= 300,' , ( False , False , False , False )), - ('set rlimit cpu <= 10seconds,' , ( False , False , True , True )), - ('set rlimit cpu <= 150seconds,', ( True , False , True , True )), - ('set rlimit cpu <= 300seconds,', ( False , False , False , False )), - ('set rlimit cpu <= 1minutes,' , ( False , False , True , True )), - ('set rlimit cpu <= 1min,' , ( False , False , True , True )), - ('set rlimit cpu <= 3minutes,' , ( False , False , False , False )), - ('set rlimit cpu <= 1hour,' , ( False , False , False , False )), - ('set rlimit cpu <= 2 days,' , ( False , False , False , False )), - ('set rlimit cpu <= 1 week,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('set rlimit as <= 100MB,', (False, False, False, False)), + ('set rlimit rttime <= 150,', (False, False, False, False)), + ('set rlimit cpu <= 100,', (False, False, True, True)), + ('set rlimit cpu <= 150,', (True, True, True, True)), + ('set rlimit cpu <= 300,', (False, False, False, False)), + ('set rlimit cpu <= 10seconds,', (False, False, True, True)), + ('set rlimit cpu <= 150seconds,', (True, False, True, True)), + ('set rlimit cpu <= 300seconds,', (False, False, False, False)), + ('set rlimit cpu <= 1minutes,', (False, False, True, True)), + ('set rlimit cpu <= 1min,', (False, False, True, True)), + ('set rlimit cpu <= 3minutes,', (False, False, False, False)), + ('set rlimit cpu <= 1hour,', (False, False, False, False)), + ('set rlimit cpu <= 2 days,', (False, False, False, False)), + ('set rlimit cpu <= 1 week,', (False, False, False, False)), ) + class RlimitCoveredTest_02(RlimitCoveredTest): rule = 'set rlimit data <= 4MB,' tests = ( - # rule equal strict equal covered covered exact - ('set rlimit data <= 100,' , ( False , False , True , True )), - ('set rlimit data <= 2KB,' , ( False , False , True , True )), - ('set rlimit data <= 2MB,' , ( False , False , True , True )), - ('set rlimit data <= 4194304,' , ( True , False , True , True )), - ('set rlimit data <= 4096KB,' , ( True , False , True , True )), - ('set rlimit data <= 4MB,' , ( True , True , True , True )), - ('set rlimit data <= 4 MB,' , ( True , False , True , True )), - ('set rlimit data <= 6MB,' , ( False , False , False , False )), - ('set rlimit data <= 6 MB,' , ( False , False , False , False )), - ('set rlimit data <= 1GB,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('set rlimit data <= 100,', (False, False, True, True)), + ('set rlimit data <= 2KB,', (False, False, True, True)), + ('set rlimit data <= 2MB,', (False, False, True, True)), + ('set rlimit data <= 4194304,', (True, False, True, True)), + ('set rlimit data <= 4096KB,', (True, False, True, True)), + ('set rlimit data <= 4MB,', (True, True, True, True)), + ('set rlimit data <= 4 MB,', (True, False, True, True)), + ('set rlimit data <= 6MB,', (False, False, False, False)), + ('set rlimit data <= 6 MB,', (False, False, False, False)), + ('set rlimit data <= 1GB,', (False, False, False, False)), ) + class RlimitCoveredTest_03(RlimitCoveredTest): rule = 'set rlimit nice <= -1,' tests = ( - # rule equal strict equal covered covered exact - ('set rlimit nice <= 5,' , ( False , False , True , True )), - ('set rlimit nice <= 0,' , ( False , False , True , True )), - ('set rlimit nice <= -1,' , ( True , True , True , True )), - ('set rlimit nice <= -3,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('set rlimit nice <= 5,', (False, False, True, True)), + ('set rlimit nice <= 0,', (False, False, True, True)), + ('set rlimit nice <= -1,', (True, True, True, True)), + ('set rlimit nice <= -3,', (False, False, False, False)), ) + class RlimitCoveredTest_04(RlimitCoveredTest): rule = 'set rlimit locks <= 42,' tests = ( - # rule equal strict equal covered covered exact - ('set rlimit locks <= 20,' , ( False , False , True , True )), - ('set rlimit locks <= 40,' , ( False , False , True , True )), - ('set rlimit locks <= 42,' , ( True , True , True , True )), - ('set rlimit locks <= 60,' , ( False , False , False , False )), - ('set rlimit locks <= infinity,', ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('set rlimit locks <= 20,', (False, False, True, True)), + ('set rlimit locks <= 40,', (False, False, True, True)), + ('set rlimit locks <= 42,', (True, True, True, True)), + ('set rlimit locks <= 60,', (False, False, False, False)), + ('set rlimit locks <= infinity,', (False, False, False, False)), ) + class RlimitCoveredTest_05(RlimitCoveredTest): rule = 'set rlimit locks <= infinity,' tests = ( - # rule equal strict equal covered covered exact - ('set rlimit locks <= 20,' , ( False , False , True , True )), - ('set rlimit cpu <= 40,' , ( False , False , False , False )), - ('set rlimit locks <= infinity,', ( True , True , True , True )), + # rule equal strict equal covered covered exact + ('set rlimit locks <= 20,', (False, False, True, True)), + ('set rlimit cpu <= 40,', (False, False, False, False)), + ('set rlimit locks <= infinity,', (True, True, True, True)), ) + class RlimitCoveredTest_Invalid(AATest): def test_borked_obj_is_covered_1(self): obj = RlimitRule.parse('set rlimit cpu <= 1024,') @@ -350,18 +368,20 @@ class RlimitCoveredTest_Invalid(AATest): with self.assertRaises(AppArmorBug): obj.is_equal(testobj) + class RlimitLogprofHeaderTest(AATest): tests = ( - ('set rlimit cpu <= infinity,', [_('Rlimit'), 'cpu', _('Value'), 'infinity', ]), - ('set rlimit as <= 200MB,', [_('Rlimit'), 'as', _('Value'), '200MB', ]), - ('set rlimit rttime <= 200ms,', [_('Rlimit'), 'rttime', _('Value'), '200ms', ]), - ('set rlimit nproc <= 1,', [_('Rlimit'), 'nproc', _('Value'), '1', ]), + ('set rlimit cpu <= infinity,', [_('Rlimit'), 'cpu', _('Value'), 'infinity']), + ('set rlimit as <= 200MB,', [_('Rlimit'), 'as', _('Value'), '200MB']), + ('set rlimit rttime <= 200ms,', [_('Rlimit'), 'rttime', _('Value'), '200ms']), + ('set rlimit nproc <= 1,', [_('Rlimit'), 'nproc', _('Value'), '1']), ) def _run_test(self, params, expected): obj = RlimitRule.parse(params) self.assertEqual(obj.logprof_header(), expected) + # --- tests for RlimitRuleset --- # class RlimitRulesTest(AATest): @@ -398,6 +418,7 @@ class RlimitRulesTest(AATest): self.assertEqual(expected_raw, ruleset.get_raw()) self.assertEqual(expected_clean, ruleset.get_clean()) + class RlimitGlobTestAATest(AATest): def setUp(self): self.ruleset = RlimitRuleset() @@ -415,6 +436,7 @@ class RlimitGlobTestAATest(AATest): # get_glob_ext is not available for rlimit rules self.ruleset.get_glob_ext('set rlimit cpu <= 100,') + class RlimitDeleteTestAATest(AATest): # no de-duplication tests for rlimit - de-duplication consists of is_covered() (which we already test) and # code from BaseRuleset (which is already tested in the capability, network and change_profile tests). @@ -425,9 +447,9 @@ class RlimitDeleteTestAATest(AATest): # --- other tests --- # class RlimitSplit_unitTest(AATest): tests = ( - ('40MB' , ( 40, 'MB',)), - ('40 MB' , ( 40, 'MB',)), - ('40' , ( 40, '', )), + ('40MB', (40, 'MB')), + ('40 MB', (40, 'MB')), + ('40', (40, '')), ) def _run_test(self, params, expected): @@ -437,15 +459,16 @@ class RlimitSplit_unitTest(AATest): with self.assertRaises(AppArmorBug): split_unit('MB') + class RlimitSize_to_intTest(AATest): def AASetup(self): self.obj = RlimitRule('cpu', '1') tests = ( - ('40GB' , 40 * 1024 * 1024 * 1024), - ('40MB' , 41943040), - ('40KB' , 40960), - ('40' , 40), + ('40GB', 40 * 1024 * 1024 * 1024), + ('40MB', 41943040), + ('40KB', 40960), + ('40', 40), ) def _run_test(self, params, expected): @@ -455,19 +478,20 @@ class RlimitSize_to_intTest(AATest): with self.assertRaises(AppArmorException): self.obj.size_to_int('20mice') + class RlimitTime_to_intTest(AATest): def AASetup(self): self.obj = RlimitRule('cpu', '1') tests = ( - ('40' , 0.00004), - ('30us' , 0.00003), - ('40ms' , 0.04), - ('40seconds', 40), - ('2minutes' , 2*60), - ('2hours' , 2*60*60), - ('1 day' , 1*60*60*24), - ('2 weeks' , 2*60*60*24*7), + ('40', 0.00004), + ('30us', 0.00003), + ('40ms', 0.04), + ('40seconds', 40), + ('2minutes', 2*60), + ('2hours', 2*60*60), + ('1 day', 1*60*60*24), + ('2 weeks', 2*60*60*24*7), ) def _run_test(self, params, expected): @@ -487,7 +511,6 @@ class RlimitTime_to_intTest(AATest): self.obj.time_to_int('20mice', 'seconds') - setup_all_loops(__name__) if __name__ == '__main__': unittest.main(verbosity=1) diff --git a/utils/test/test-severity.py b/utils/test/test-severity.py index 812b5ee2f..997fd0acc 100755 --- a/utils/test/test-severity.py +++ b/utils/test/test-severity.py @@ -20,6 +20,7 @@ from common_test import AATest, setup_all_loops import apparmor.severity as severity from apparmor.common import AppArmorException + class SeverityBaseTest(AATest): def AASetup(self): @@ -35,31 +36,33 @@ class SeverityBaseTest(AATest): self.assertEqual(rank, expected_rank, 'expected rank %s, got %s' % (expected_rank, rank)) + class SeverityTest(SeverityBaseTest): tests = ( - (('/usr/bin/whatis', 'x' ), 5), - (('/etc', 'x' ), 'unknown'), - (('/dev/doublehit', 'x' ), 0), - (('/dev/doublehit', 'rx' ), 4), + (('/usr/bin/whatis', 'x'), 5), + (('/etc', 'x'), 'unknown'), + (('/dev/doublehit', 'x'), 0), + (('/dev/doublehit', 'rx'), 4), (('/dev/doublehit', 'rwx'), 8), (('/dev/tty10', 'rwx'), 9), - (('/var/adm/foo/**', 'rx' ), 3), - (('/etc/apparmor/**', 'r' ), 6), - (('/etc/**', 'r' ), 'unknown'), - (('/usr/foo@bar', 'r' ), 'unknown'), ## filename containing @ - (('/home/foo@bar', 'rw' ), 6), ## filename containing @ - (('/etc/apache2/ssl.key/bar', 'r' ), 7), # /etc/apache2/** (3) vs. /etc/apache2/**ssl** (7) - (('/etc/apache2/foo/ssl/bar', 'r' ), 7), # additional path level triggers otherwise untested branch + (('/var/adm/foo/**', 'rx'), 3), + (('/etc/apparmor/**', 'r'), 6), + (('/etc/**', 'r'), 'unknown'), + (('/usr/foo@bar', 'r'), 'unknown'), # filename containing @ + (('/home/foo@bar', 'rw'), 6), # filename containing @ + (('/etc/apache2/ssl.key/bar', 'r'), 7), # /etc/apache2/** (3) vs. /etc/apache2/**ssl** (7) + (('/etc/apache2/foo/ssl/bar', 'r'), 7), # additional path level triggers otherwise untested branch (('/proc/sys/kernel/hotplug', 'rwx'), 10), # non-glob filename, severity depends on mode ) def _run_test(self, params, expected): - self._simple_severity_w_perm(params[0], params[1], expected) ## filename containing @ + self._simple_severity_w_perm(params[0], params[1], expected) # filename containing @ def test_invalid_rank(self): with self.assertRaises(AppArmorException): self._simple_severity_w_perm('unexpected_unput', 'rw', 6) + class SeverityTestCap(SeverityBaseTest): tests = ( ('KILL', 8), @@ -79,28 +82,29 @@ class SeverityTestCap(SeverityBaseTest): class SeverityVarsTest(SeverityBaseTest): tests = ( - (('@{PROC}/sys/vm/overcommit_memory', 'r'), 6), - (('@{HOME}/sys/@{PROC}/overcommit_memory', 'r'), 4), - (('/overco@{multiarch}mmit_memory', 'r'), 'unknown'), - (('@{PROC}/sys/@{TFTP_DIR}/overcommit_memory', 'r'), 6), - (('@{somepaths}/somefile', 'r'), 7), - (('@{strangevar}/somefile', 'r'), 6), + (('@{PROC}/sys/vm/overcommit_memory', 'r'), 6), + (('@{HOME}/sys/@{PROC}/overcommit_memory', 'r'), 4), + (('/overco@{multiarch}mmit_memory', 'r'), 'unknown'), + (('@{PROC}/sys/@{TFTP_DIR}/overcommit_memory', 'r'), 6), + (('@{somepaths}/somefile', 'r'), 7), + (('@{strangevar}/somefile', 'r'), 6), ) def _run_test(self, params, expected): vars = { - # Note: using [] instead of {} is intentional to keep order of checking (and therefore code coverage) constant - '@{HOME}': ['@{HOMEDIRS}/*/', '/root/'], - '@{HOMEDIRS}': ['/home/', '/storage/'], - '@{multiarch}': ['*-linux-gnu*'], - '@{TFTP_DIR}': ['/var/tftp /srv/tftpboot'], - '@{PROC}': ['/proc/'], - '@{somepaths}': ['/home/foo/downloads', '@{HOMEDIRS}/foo/.ssh/'], + # Note: using [] instead of {} is intentional to keep order of checking (and therefore code coverage) constant + '@{HOME}': ['@{HOMEDIRS}/*/', '/root/'], + '@{HOMEDIRS}': ['/home/', '/storage/'], + '@{multiarch}': ['*-linux-gnu*'], + '@{TFTP_DIR}': ['/var/tftp /srv/tftpboot'], + '@{PROC}': ['/proc/'], + '@{somepaths}': ['/home/foo/downloads', '@{HOMEDIRS}/foo/.ssh/'], '@{strangevar}': ['/srv/', '/proc/'], } self.sev_db.set_variables(vars) self._simple_severity_w_perm(params[0], params[1], expected) + class SeverityDBTest(AATest): def _test_db(self, contents): self.db_file = self.writeTmpfile('severity.db', contents) @@ -108,17 +112,17 @@ class SeverityDBTest(AATest): return self.sev_db tests = ( - ("CAP_LEASE 18\n" , AppArmorException), # out of range - ("CAP_LEASE -1\n" , AppArmorException), # out of range - ("/etc/passwd* 0 4\n" , AppArmorException), # insufficient vals - ("/etc/passwd* 0 4 5 6\n" , AppArmorException), # too many vals - ("/etc/passwd* -2 4 6\n" , AppArmorException), # out of range - ("/etc/passwd* 12 4 6\n" , AppArmorException), # out of range - ("/etc/passwd* 2 -4 6\n" , AppArmorException), # out of range - ("/etc/passwd 2 14 6\n" , AppArmorException), # out of range - ("/etc/passwd 2 4 -12\n" , AppArmorException), # out of range - ("/etc/passwd 2 4 4294967297\n" , AppArmorException), # out of range - ("garbage line\n" , AppArmorException), + ("CAP_LEASE 18\n", AppArmorException), # out of range + ("CAP_LEASE -1\n", AppArmorException), # out of range + ("/etc/passwd* 0 4\n", AppArmorException), # insufficient vals + ("/etc/passwd* 0 4 5 6\n", AppArmorException), # too many vals + ("/etc/passwd* -2 4 6\n", AppArmorException), # out of range + ("/etc/passwd* 12 4 6\n", AppArmorException), # out of range + ("/etc/passwd* 2 -4 6\n", AppArmorException), # out of range + ("/etc/passwd 2 14 6\n", AppArmorException), # out of range + ("/etc/passwd 2 4 -12\n", AppArmorException), # out of range + ("/etc/passwd 2 4 4294967297\n", AppArmorException), # out of range + ("garbage line\n", AppArmorException), ) def _run_test(self, params, expected): @@ -147,6 +151,7 @@ class SeverityDBTest(AATest): with self.assertRaises(AppArmorException): severity.Severity() + setup_all_loops(__name__) if __name__ == '__main__': unittest.main(verbosity=1) diff --git a/utils/test/test-signal.py b/utils/test/test-signal.py index 95a858c7c..d9584b79c 100644 --- a/utils/test/test-signal.py +++ b/utils/test/test-signal.py @@ -24,11 +24,13 @@ from apparmor.logparser import ReadLog from apparmor.translations import init_translation _ = init_translation() -exp = namedtuple('exp', ('audit', 'allow_keyword', 'deny', 'comment', - 'access', 'all_access', 'signal', 'all_signals', 'peer', 'all_peers')) +exp = namedtuple( + 'exp', ('audit', 'allow_keyword', 'deny', 'comment', + 'access', 'all_access', 'signal', 'all_signals', 'peer', 'all_peers')) # --- tests for single SignalRule --- # + class SignalTest(AATest): def _compare_obj(self, obj, expected): self.assertEqual(expected.allow_keyword, obj.allow_keyword) @@ -45,22 +47,23 @@ class SignalTest(AATest): self.assertEqual(expected.deny, obj.deny) self.assertEqual(expected.comment, obj.comment) + class SignalTestParse(SignalTest): tests = ( - # SignalRule object audit allow deny comment access all? signal all? peer all? - ('signal,' , exp(False, False, False, '', None , True , None, True, None, True )), - ('signal send,' , exp(False, False, False, '', {'send'}, False, None, True, None, True )), - ('signal (send, receive),' , exp(False, False, False, '', {'send', 'receive'}, False, None, True, None, True )), - ('signal send set=quit,' , exp(False, False, False, '', {'send'}, False, {'quit'}, False, None, True )), - ('deny signal send set=quit, # cmt' , exp(False, False, True , ' # cmt', {'send'}, False, {'quit'}, False, None, True )), - ('audit allow signal set=int,' , exp(True , True , False, '', None , True , {'int'}, False, None, True )), - ('signal set=quit peer=unconfined,' , exp(False, False, False, '', None , True , {'quit'}, False, 'unconfined', False )), - ('signal send set=(quit),' , exp(False, False, False, '', {'send'}, False, {'quit'}, False, None, True )), - ('signal send set=(quit, int),' , exp(False, False, False, '', {'send'}, False, {'quit', 'int'}, False, None, True )), - ('signal set=(quit, int),' , exp(False, False, False, '', None, True, {'quit', 'int'}, False, None, True )), - ('signal send set = ( quit , int ) ,' , exp(False, False, False, '', {'send'}, False, {'quit', 'int'}, False, None, True )), - ('signal peer=/foo,' , exp(False, False, False, '', None , True , None, True, '/foo', False )), - ('signal r set=quit set=int peer=/foo,' , exp(False, False, False, '', {'r'}, False, {'quit', 'int'}, False, '/foo', False )), + # SignalRule object audit allow deny comment access all? signal all? peer all? + ('signal,', exp(False, False, False, '', None, True, None, True, None, True)), + ('signal send,', exp(False, False, False, '', {'send'}, False, None, True, None, True)), + ('signal (send, receive),', exp(False, False, False, '', {'send', 'receive'}, False, None, True, None, True)), + ('signal send set=quit,', exp(False, False, False, '', {'send'}, False, {'quit'}, False, None, True)), + ('deny signal send set=quit, # cmt', exp(False, False, True, ' # cmt', {'send'}, False, {'quit'}, False, None, True)), + ('audit allow signal set=int,', exp(True, True, False, '', None, True, {'int'}, False, None, True)), + ('signal set=quit peer=unconfined,', exp(False, False, False, '', None, True, {'quit'}, False, 'unconfined', False)), + ('signal send set=(quit),', exp(False, False, False, '', {'send'}, False, {'quit'}, False, None, True)), + ('signal send set=(quit, int),', exp(False, False, False, '', {'send'}, False, {'quit', 'int'}, False, None, True)), + ('signal set=(quit, int),', exp(False, False, False, '', None, True, {'quit', 'int'}, False, None, True)), + ('signal send set = ( quit , int ) ,', exp(False, False, False, '', {'send'}, False, {'quit', 'int'}, False, None, True)), + ('signal peer=/foo,', exp(False, False, False, '', None, True, None, True, '/foo', False)), + ('signal r set=quit set=int peer=/foo,', exp(False, False, False, '', {'r'}, False, {'quit', 'int'}, False, '/foo', False)), ) def _run_test(self, rawrule, expected): @@ -69,17 +72,18 @@ class SignalTestParse(SignalTest): self.assertEqual(rawrule.strip(), obj.raw_rule) self._compare_obj(obj, expected) + class SignalTestParseInvalid(SignalTest): tests = ( - ('signal foo,' , AppArmorException), - ('signal foo bar,' , AppArmorException), - ('signal foo int,' , AppArmorException), - ('signal send bar,' , AppArmorException), - ('signal send receive,' , AppArmorException), - ('signal set=,' , AppArmorException), - ('signal set=int set=,' , AppArmorException), - ('signal set=invalid,' , AppArmorException), - ('signal peer=,' , AppArmorException), + ('signal foo,', AppArmorException), + ('signal foo bar,', AppArmorException), + ('signal foo int,', AppArmorException), + ('signal send bar,', AppArmorException), + ('signal send receive,', AppArmorException), + ('signal set=,', AppArmorException), + ('signal set=int set=,', AppArmorException), + ('signal set=invalid,', AppArmorException), + ('signal peer=,', AppArmorException), ) def _run_test(self, rawrule, expected): @@ -87,6 +91,7 @@ class SignalTestParseInvalid(SignalTest): with self.assertRaises(expected): SignalRule.parse(rawrule) + class SignalTestParseFromLog(SignalTest): def test_signal_from_log(self): parser = ReadLog('', '', '') @@ -121,45 +126,47 @@ class SignalTestParseFromLog(SignalTest): obj = SignalRule(parsed_event['denied_mask'], parsed_event['signal'], parsed_event['peer'], log_event=parsed_event) - # audit allow deny comment access all? signal all? peer all? - expected = exp(False, False, False, '', {'send'}, False, {'term'}, False, '/usr/bin/pulseaudio///usr/lib/pulseaudio/pulse/gconf-helper', False) + # audit allow deny comment access all? signal all? peer all? + expected = exp(False, False, False, '', {'send'}, False, {'term'}, False, '/usr/bin/pulseaudio///usr/lib/pulseaudio/pulse/gconf-helper', False) self._compare_obj(obj, expected) self.assertEqual(obj.get_raw(1), ' signal send set=term peer=/usr/bin/pulseaudio///usr/lib/pulseaudio/pulse/gconf-helper,') + class SignalFromInit(SignalTest): tests = ( - # SignalRule object audit allow deny comment access all? signal all? peer all? - (SignalRule('r', 'hup', 'unconfined', deny=True) , exp(False, False, True , '' , {'r'}, False, {'hup'}, False, 'unconfined', False)), - (SignalRule(('r', 'send'), ('hup', 'int'), '/bin/foo') , exp(False, False, False, '' , {'r', 'send'},False, {'hup', 'int'}, False, '/bin/foo', False)), - (SignalRule(SignalRule.ALL, 'int', '/bin/foo') , exp(False, False, False, '' , None, True, {'int'}, False, '/bin/foo', False )), - (SignalRule('rw', SignalRule.ALL, '/bin/foo') , exp(False, False, False, '' , {'rw'}, False, None, True, '/bin/foo', False )), - (SignalRule('rw', ('int'), SignalRule.ALL) , exp(False, False, False, '' , {'rw'}, False, {'int'}, False, None, True )), - (SignalRule(SignalRule.ALL, SignalRule.ALL, SignalRule.ALL) , exp(False, False, False, '' , None , True, None, True, None, True )), + # SignalRule object audit allow deny comment access all? signal all? peer all? + (SignalRule('r', 'hup', 'unconfined', deny=True), exp(False, False, True, '', {'r'}, False, {'hup'}, False, 'unconfined', False)), + (SignalRule(('r', 'send'), ('hup', 'int'), '/bin/foo'), exp(False, False, False, '', {'r', 'send'}, False, {'hup', 'int'}, False, '/bin/foo', False)), + (SignalRule(SignalRule.ALL, 'int', '/bin/foo'), exp(False, False, False, '', None, True, {'int'}, False, '/bin/foo', False)), + (SignalRule('rw', SignalRule.ALL, '/bin/foo'), exp(False, False, False, '', {'rw'}, False, None, True, '/bin/foo', False)), + (SignalRule('rw', ('int'), SignalRule.ALL), exp(False, False, False, '', {'rw'}, False, {'int'}, False, None, True)), + (SignalRule(SignalRule.ALL, SignalRule.ALL, SignalRule.ALL), exp(False, False, False, '', None, True, None, True, None, True)), ) def _run_test(self, obj, expected): self._compare_obj(obj, expected) + class InvalidSignalInit(AATest): tests = ( # init params expected exception - (('send', '' , '/foo' ) , AppArmorBug), # empty signal - (('' , 'int' , '/foo' ) , AppArmorBug), # empty access - (('send', 'int' , '' ) , AppArmorBug), # empty peer - ((' ', 'int' , '/foo' ) , AppArmorBug), # whitespace access - (('send', ' ' , '/foo' ) , AppArmorBug), # whitespace signal - (('send', 'int' , ' ' ) , AppArmorBug), # whitespace peer - (('xyxy', 'int' , '/foo' ) , AppArmorException), # invalid access - (('send', 'xyxy', '/foo' ) , AppArmorException), # invalid signal + (('send', '', '/foo'), AppArmorBug), # empty signal + (('', 'int', '/foo'), AppArmorBug), # empty access + (('send', 'int', ''), AppArmorBug), # empty peer + ((' ', 'int', '/foo'), AppArmorBug), # whitespace access + (('send', ' ', '/foo'), AppArmorBug), # whitespace signal + (('send', 'int', ' '), AppArmorBug), # whitespace peer + (('xyxy', 'int', '/foo'), AppArmorException), # invalid access + (('send', 'xyxy', '/foo'), AppArmorException), # invalid signal # XXX is 'invalid peer' possible at all? - ((dict(), 'int' , '/foo' ) , AppArmorBug), # wrong type for access - ((None , 'int' , '/foo' ) , AppArmorBug), # wrong type for access - (('send', dict(), '/foo' ) , AppArmorBug), # wrong type for signal - (('send', None , '/foo' ) , AppArmorBug), # wrong type for signal - (('send', 'int' , dict() ) , AppArmorBug), # wrong type for peer - (('send', 'int' , None ) , AppArmorBug), # wrong type for peer + ((dict(), 'int', '/foo'), AppArmorBug), # wrong type for access + ((None, 'int', '/foo'), AppArmorBug), # wrong type for access + (('send', dict(), '/foo'), AppArmorBug), # wrong type for signal + (('send', None, '/foo'), AppArmorBug), # wrong type for signal + (('send', 'int', dict()), AppArmorBug), # wrong type for peer + (('send', 'int', None), AppArmorBug), # wrong type for peer ) def _run_test(self, params, expected): @@ -178,6 +185,7 @@ class InvalidSignalInit(AATest): with self.assertRaises(TypeError): SignalRule('r', 'int') + class InvalidSignalTest(AATest): def _check_invalid_rawrule(self, rawrule): obj = None @@ -226,30 +234,30 @@ class WriteSignalTestAATest(AATest): self.assertEqual(rawrule.strip(), raw, 'unexpected raw rule') tests = ( - # raw rule clean rule - (' signal , # foo ' , 'signal, # foo'), - (' audit signal send,' , 'audit signal send,'), - (' audit signal (send ),' , 'audit signal send,'), - (' audit signal (send , receive ),' , 'audit signal (receive send),'), - (' deny signal send set=quit,# foo bar' , 'deny signal send set=quit, # foo bar'), - (' deny signal send set=(quit), ' , 'deny signal send set=quit,'), - (' deny signal send set=(int , quit),' , 'deny signal send set=(int quit),'), - (' deny signal send set=(quit, int ),' , 'deny signal send set=(int quit),'), - (' deny signal send ,# foo bar' , 'deny signal send, # foo bar'), - (' allow signal set=int ,# foo bar' , 'allow signal set=int, # foo bar'), - ('signal,' , 'signal,'), - ('signal (receive),' , 'signal receive,'), - ('signal (send),' , 'signal send,'), - ('signal (send receive),' , 'signal (receive send),'), - ('signal r,' , 'signal r,'), - ('signal w,' , 'signal w,'), - ('signal rw,' , 'signal rw,'), - ('signal send set=("hup"),' , 'signal send set=hup,'), - ('signal (receive) set=kill,' , 'signal receive set=kill,'), - ('signal w set=(quit int),' , 'signal w set=(int quit),'), - ('signal receive peer=foo,' , 'signal receive peer=foo,'), - ('signal (send receive) peer=/usr/bin/bar,' , 'signal (receive send) peer=/usr/bin/bar,'), - ('signal wr set=(pipe, usr1) peer=/sbin/baz,' , 'signal wr set=(pipe usr1) peer=/sbin/baz,'), + # raw rule clean rule + (' signal , # foo ', 'signal, # foo'), + (' audit signal send,', 'audit signal send,'), + (' audit signal (send ),', 'audit signal send,'), + (' audit signal (send , receive ),', 'audit signal (receive send),'), + (' deny signal send set=quit,# foo bar', 'deny signal send set=quit, # foo bar'), + (' deny signal send set=(quit), ', 'deny signal send set=quit,'), + (' deny signal send set=(int , quit),', 'deny signal send set=(int quit),'), + (' deny signal send set=(quit, int ),', 'deny signal send set=(int quit),'), + (' deny signal send ,# foo bar', 'deny signal send, # foo bar'), + (' allow signal set=int ,# foo bar', 'allow signal set=int, # foo bar'), + ('signal,', 'signal,'), + ('signal (receive),', 'signal receive,'), + ('signal (send),', 'signal send,'), + ('signal (send receive),', 'signal (receive send),'), + ('signal r,', 'signal r,'), + ('signal w,', 'signal w,'), + ('signal rw,', 'signal rw,'), + ('signal send set=("hup"),', 'signal send set=hup,'), + ('signal (receive) set=kill,', 'signal receive set=kill,'), + ('signal w set=(quit int),', 'signal w set=(int quit),'), + ('signal receive peer=foo,', 'signal receive peer=foo,'), + ('signal (send receive) peer=/usr/bin/bar,', 'signal (receive send) peer=/usr/bin/bar,'), + ('signal wr set=(pipe, usr1) peer=/sbin/baz,', 'signal wr set=(pipe usr1) peer=/sbin/baz,'), ) def test_write_manually(self): @@ -268,209 +276,225 @@ class SignalCoveredTest(AATest): self.assertTrue(SignalRule.match(param)) - self.assertEqual(obj.is_equal(check_obj), expected[0], 'Mismatch in is_equal, expected %s' % expected[0]) - self.assertEqual(obj.is_equal(check_obj, True), expected[1], 'Mismatch in is_equal/strict, expected %s' % expected[1]) + self.assertEqual( + obj.is_equal(check_obj), expected[0], + 'Mismatch in is_equal, expected %s' % expected[0]) + self.assertEqual( + obj.is_equal(check_obj, True), expected[1], + 'Mismatch in is_equal/strict, expected %s' % expected[1]) + + self.assertEqual( + obj.is_covered(check_obj), expected[2], + 'Mismatch in is_covered, expected %s' % expected[2]) + self.assertEqual( + obj.is_covered(check_obj, True, True), expected[3], + 'Mismatch in is_covered/exact, expected %s' % expected[3]) - self.assertEqual(obj.is_covered(check_obj), expected[2], 'Mismatch in is_covered, expected %s' % expected[2]) - self.assertEqual(obj.is_covered(check_obj, True, True), expected[3], 'Mismatch in is_covered/exact, expected %s' % expected[3]) class SignalCoveredTest_01(SignalCoveredTest): rule = 'signal send,' tests = ( - # rule equal strict equal covered covered exact - ('signal,' , ( False , False , False , False )), - ('signal send,' , ( True , True , True , True )), - ('signal send peer=unconfined,' , ( False , False , True , True )), - ('signal send, # comment' , ( True , False , True , True )), - ('allow signal send,' , ( True , False , True , True )), - ('signal send,' , ( True , False , True , True )), - ('signal send set=quit,' , ( False , False , True , True )), - ('signal send set=int,' , ( False , False , True , True )), - ('audit signal send,' , ( False , False , False , False )), - ('audit signal,' , ( False , False , False , False )), - ('signal receive,' , ( False , False , False , False )), - ('signal set=int,' , ( False , False , False , False )), - ('audit deny signal send,' , ( False , False , False , False )), - ('deny signal send,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('signal,', (False, False, False, False)), + ('signal send,', (True, True, True, True)), + ('signal send peer=unconfined,', (False, False, True, True)), + ('signal send, # comment', (True, False, True, True)), + ('allow signal send,', (True, False, True, True)), + ('signal send,', (True, False, True, True)), + ('signal send set=quit,', (False, False, True, True)), + ('signal send set=int,', (False, False, True, True)), + ('audit signal send,', (False, False, False, False)), + ('audit signal,', (False, False, False, False)), + ('signal receive,', (False, False, False, False)), + ('signal set=int,', (False, False, False, False)), + ('audit deny signal send,', (False, False, False, False)), + ('deny signal send,', (False, False, False, False)), ) + class SignalCoveredTest_02(SignalCoveredTest): rule = 'audit signal send,' tests = ( - # rule equal strict equal covered covered exact - ( 'signal send,' , ( False , False , True , False )), - ('audit signal send,' , ( True , True , True , True )), - ( 'signal send set=quit,' , ( False , False , True , False )), - ('audit signal send set=quit,' , ( False , False , True , True )), - ( 'signal,' , ( False , False , False , False )), - ('audit signal,' , ( False , False , False , False )), - ('signal receive,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'signal send,', (False, False, True, False)), + ('audit signal send,', (True, True, True, True)), + ( 'signal send set=quit,', (False, False, True, False)), + ('audit signal send set=quit,', (False, False, True, True)), + ( 'signal,', (False, False, False, False)), + ('audit signal,', (False, False, False, False)), + ('signal receive,', (False, False, False, False)), ) + class SignalCoveredTest_03(SignalCoveredTest): rule = 'signal send set=quit,' tests = ( - # rule equal strict equal covered covered exact - ( 'signal send set=quit,' , ( True , True , True , True )), - ('allow signal send set=quit,' , ( True , False , True , True )), - ( 'signal send,' , ( False , False , False , False )), - ( 'signal,' , ( False , False , False , False )), - ( 'signal send set=int,' , ( False , False , False , False )), - ('audit signal,' , ( False , False , False , False )), - ('audit signal send set=quit,' , ( False , False , False , False )), - ('audit signal set=quit,' , ( False , False , False , False )), - ( 'signal send,' , ( False , False , False , False )), - ( 'signal,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'signal send set=quit,', (True, True, True, True)), + ('allow signal send set=quit,', (True, False, True, True)), + ( 'signal send,', (False, False, False, False)), + ( 'signal,', (False, False, False, False)), + ( 'signal send set=int,', (False, False, False, False)), + ('audit signal,', (False, False, False, False)), + ('audit signal send set=quit,', (False, False, False, False)), + ('audit signal set=quit,', (False, False, False, False)), + ( 'signal send,', (False, False, False, False)), + ( 'signal,', (False, False, False, False)), ) + class SignalCoveredTest_04(SignalCoveredTest): rule = 'signal,' tests = ( - # rule equal strict equal covered covered exact - ( 'signal,' , ( True , True , True , True )), - ('allow signal,' , ( True , False , True , True )), - ( 'signal send,' , ( False , False , True , True )), - ( 'signal w set=quit,' , ( False , False , True , True )), - ( 'signal set=int,' , ( False , False , True , True )), - ( 'signal send set=quit,' , ( False , False , True , True )), - ('audit signal,' , ( False , False , False , False )), - ('deny signal,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'signal,', (True, True, True, True)), + ('allow signal,', (True, False, True, True)), + ( 'signal send,', (False, False, True, True)), + ( 'signal w set=quit,', (False, False, True, True)), + ( 'signal set=int,', (False, False, True, True)), + ( 'signal send set=quit,', (False, False, True, True)), + ('audit signal,', (False, False, False, False)), + ('deny signal,', (False, False, False, False)), ) + class SignalCoveredTest_05(SignalCoveredTest): rule = 'deny signal send,' tests = ( - # rule equal strict equal covered covered exact - ( 'deny signal send,' , ( True , True , True , True )), - ('audit deny signal send,' , ( False , False , False , False )), - ( 'signal send,' , ( False , False , False , False )), # XXX should covered be true here? - ( 'deny signal receive,' , ( False , False , False , False )), - ( 'deny signal,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ( 'deny signal send,', (True, True, True, True)), + ('audit deny signal send,', (False, False, False, False)), + ( 'signal send,', (False, False, False, False)), # XXX should covered be true here? + ( 'deny signal receive,', (False, False, False, False)), + ( 'deny signal,', (False, False, False, False)), ) + class SignalCoveredTest_06(SignalCoveredTest): rule = 'signal send peer=unconfined,' tests = ( - # rule equal strict equal covered covered exact - ('signal,' , ( False , False , False , False )), - ('signal send,' , ( False , False , False , False )), - ('signal send peer=unconfined,' , ( True , True , True , True )), - ('signal peer=unconfined,' , ( False , False , False , False )), - ('signal send, # comment' , ( False , False , False , False )), - ('allow signal send,' , ( False , False , False , False )), - ('allow signal send peer=unconfined,' , ( True , False , True , True )), - ('allow signal send peer=/foo/bar,' , ( False , False , False , False )), - ('allow signal send peer=/**,' , ( False , False , False , False )), - ('allow signal send peer=**,' , ( False , False , False , False )), - ('signal send,' , ( False , False , False , False )), - ('signal send peer=unconfined,' , ( True , False , True , True )), - ('signal send set=quit,' , ( False , False , False , False )), - ('signal send set=int peer=unconfined,',( False , False , True , True )), - ('audit signal send peer=unconfined,' , ( False , False , False , False )), - ('audit signal,' , ( False , False , False , False )), - ('signal receive,' , ( False , False , False , False )), - ('signal set=int,' , ( False , False , False , False )), - ('audit deny signal send,' , ( False , False , False , False )), - ('deny signal send,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('signal,', (False, False, False, False)), + ('signal send,', (False, False, False, False)), + ('signal send peer=unconfined,', (True, True, True, True)), + ('signal peer=unconfined,', (False, False, False, False)), + ('signal send, # comment', (False, False, False, False)), + ('allow signal send,', (False, False, False, False)), + ('allow signal send peer=unconfined,', (True, False, True, True)), + ('allow signal send peer=/foo/bar,', (False, False, False, False)), + ('allow signal send peer=/**,', (False, False, False, False)), + ('allow signal send peer=**,', (False, False, False, False)), + ('signal send,', (False, False, False, False)), + ('signal send peer=unconfined,', (True, False, True, True)), + ('signal send set=quit,', (False, False, False, False)), + ('signal send set=int peer=unconfined,', (False, False, True, True)), + ('audit signal send peer=unconfined,', (False, False, False, False)), + ('audit signal,', (False, False, False, False)), + ('signal receive,', (False, False, False, False)), + ('signal set=int,', (False, False, False, False)), + ('audit deny signal send,', (False, False, False, False)), + ('deny signal send,', (False, False, False, False)), ) + class SignalCoveredTest_07(SignalCoveredTest): rule = 'signal send peer=/foo/bar,' tests = ( - # rule equal strict equal covered covered exact - ('signal,' , ( False , False , False , False )), - ('signal send,' , ( False , False , False , False )), - ('signal send peer=/foo/bar,' , ( True , True , True , True )), - ('signal send peer=/foo/*,' , ( False , False , False , False )), - ('signal send peer=/**,' , ( False , False , False , False )), - ('signal send peer=/what/*,' , ( False , False , False , False )), - ('signal peer=/foo/bar,' , ( False , False , False , False )), - ('signal send, # comment' , ( False , False , False , False )), - ('allow signal send,' , ( False , False , False , False )), - ('allow signal send peer=/foo/bar,' , ( True , False , True , True )), - ('signal send,' , ( False , False , False , False )), - ('signal send peer=/foo/bar,' , ( True , False , True , True )), - ('signal send peer=/what/ever,' , ( False , False , False , False )), - ('signal send set=quit,' , ( False , False , False , False )), - ('signal send set=int peer=/foo/bar,' , ( False , False , True , True )), - ('audit signal send peer=/foo/bar,' , ( False , False , False , False )), - ('audit signal,' , ( False , False , False , False )), - ('signal receive,' , ( False , False , False , False )), - ('signal set=int,' , ( False , False , False , False )), - ('audit deny signal send,' , ( False , False , False , False )), - ('deny signal send,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('signal,', (False, False, False, False)), + ('signal send,', (False, False, False, False)), + ('signal send peer=/foo/bar,', (True, True, True, True)), + ('signal send peer=/foo/*,', (False, False, False, False)), + ('signal send peer=/**,', (False, False, False, False)), + ('signal send peer=/what/*,', (False, False, False, False)), + ('signal peer=/foo/bar,', (False, False, False, False)), + ('signal send, # comment', (False, False, False, False)), + ('allow signal send,', (False, False, False, False)), + ('allow signal send peer=/foo/bar,', (True, False, True, True)), + ('signal send,', (False, False, False, False)), + ('signal send peer=/foo/bar,', (True, False, True, True)), + ('signal send peer=/what/ever,', (False, False, False, False)), + ('signal send set=quit,', (False, False, False, False)), + ('signal send set=int peer=/foo/bar,', (False, False, True, True)), + ('audit signal send peer=/foo/bar,', (False, False, False, False)), + ('audit signal,', (False, False, False, False)), + ('signal receive,', (False, False, False, False)), + ('signal set=int,', (False, False, False, False)), + ('audit deny signal send,', (False, False, False, False)), + ('deny signal send,', (False, False, False, False)), ) + class SignalCoveredTest_08(SignalCoveredTest): rule = 'signal send peer=**,' tests = ( - # rule equal strict equal covered covered exact - ('signal,' , ( False , False , False , False )), - ('signal send,' , ( False , False , False , False )), - ('signal send peer=/foo/bar,' , ( False , False , True , True )), - ('signal send peer=/foo/*,' , ( False , False , False , False )), # TODO: wildcard vs. wildcard never matches in is_covered_aare() - ('signal send peer=/**,' , ( False , False , False , False )), # TODO: wildcard vs. wildcard never matches in is_covered_aare() - ('signal send peer=/what/*,' , ( False , False , False , False )), # TODO: wildcard vs. wildcard never matches in is_covered_aare() - ('signal peer=/foo/bar,' , ( False , False , False , False )), - ('signal send, # comment' , ( False , False , False , False )), - ('allow signal send,' , ( False , False , False , False )), - ('allow signal send peer=/foo/bar,' , ( False , False , True , True )), - ('signal send,' , ( False , False , False , False )), - ('signal send peer=/foo/bar,' , ( False , False , True , True )), - ('signal send peer=/what/ever,' , ( False , False , True , True )), - ('signal send set=quit,' , ( False , False , False , False )), - ('signal send set=int peer=/foo/bar,' , ( False , False , True , True )), - ('audit signal send peer=/foo/bar,' , ( False , False , False , False )), - ('audit signal,' , ( False , False , False , False )), - ('signal receive,' , ( False , False , False , False )), - ('signal set=int,' , ( False , False , False , False )), - ('audit deny signal send,' , ( False , False , False , False )), - ('deny signal send,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('signal,', (False, False, False, False)), + ('signal send,', (False, False, False, False)), + ('signal send peer=/foo/bar,', (False, False, True, True)), + ('signal send peer=/foo/*,', (False, False, False, False)), # TODO: wildcard vs. wildcard never matches in is_covered_aare() + ('signal send peer=/**,', (False, False, False, False)), # TODO: wildcard vs. wildcard never matches in is_covered_aare() + ('signal send peer=/what/*,', (False, False, False, False)), # TODO: wildcard vs. wildcard never matches in is_covered_aare() + ('signal peer=/foo/bar,', (False, False, False, False)), + ('signal send, # comment', (False, False, False, False)), + ('allow signal send,', (False, False, False, False)), + ('allow signal send peer=/foo/bar,', (False, False, True, True)), + ('signal send,', (False, False, False, False)), + ('signal send peer=/foo/bar,', (False, False, True, True)), + ('signal send peer=/what/ever,', (False, False, True, True)), + ('signal send set=quit,', (False, False, False, False)), + ('signal send set=int peer=/foo/bar,', (False, False, True, True)), + ('audit signal send peer=/foo/bar,', (False, False, False, False)), + ('audit signal,', (False, False, False, False)), + ('signal receive,', (False, False, False, False)), + ('signal set=int,', (False, False, False, False)), + ('audit deny signal send,', (False, False, False, False)), + ('deny signal send,', (False, False, False, False)), ) + class SignalCoveredTest_09(SignalCoveredTest): rule = 'signal (send, receive) set=(int, quit),' tests = ( - # rule equal strict equal covered covered exact - ('signal,' , ( False , False , False , False )), - ('signal send,' , ( False , False , False , False )), - ('signal send set=int,' , ( False , False , True , True )), - ('signal receive set=quit,' , ( False , False , True , True )), - ('signal (receive,send) set=int,' , ( False , False , True , True )), - ('signal (receive,send) set=(int quit),',(True , False , True , True )), - ('signal send set=(quit int),' , ( False , False , True , True )), - ('signal send peer=/foo/bar,' , ( False , False , False , False )), - ('signal send peer=/foo/*,' , ( False , False , False , False )), - ('signal send peer=/**,' , ( False , False , False , False )), - ('signal send peer=/what/*,' , ( False , False , False , False )), - ('signal peer=/foo/bar,' , ( False , False , False , False )), - ('signal send, # comment' , ( False , False , False , False )), - ('allow signal send,' , ( False , False , False , False )), - ('allow signal send peer=/foo/bar,' , ( False , False , False , False )), - ('signal send,' , ( False , False , False , False )), - ('signal send peer=/foo/bar,' , ( False , False , False , False )), - ('signal send peer=/what/ever,' , ( False , False , False , False )), - ('signal send set=quit,' , ( False , False , True , True )), - ('signal send set=int peer=/foo/bar,' , ( False , False , True , True )), - ('audit signal send peer=/foo/bar,' , ( False , False , False , False )), - ('audit signal,' , ( False , False , False , False )), - ('signal receive,' , ( False , False , False , False )), - ('signal set=int,' , ( False , False , False , False )), - ('audit deny signal send,' , ( False , False , False , False )), - ('deny signal send,' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + ('signal,', (False, False, False, False)), + ('signal send,', (False, False, False, False)), + ('signal send set=int,', (False, False, True, True)), + ('signal receive set=quit,', (False, False, True, True)), + ('signal (receive,send) set=int,', (False, False, True, True)), + ('signal (receive,send) set=(int quit),', (True, False, True, True)), + ('signal send set=(quit int),', (False, False, True, True)), + ('signal send peer=/foo/bar,', (False, False, False, False)), + ('signal send peer=/foo/*,', (False, False, False, False)), + ('signal send peer=/**,', (False, False, False, False)), + ('signal send peer=/what/*,', (False, False, False, False)), + ('signal peer=/foo/bar,', (False, False, False, False)), + ('signal send, # comment', (False, False, False, False)), + ('allow signal send,', (False, False, False, False)), + ('allow signal send peer=/foo/bar,', (False, False, False, False)), + ('signal send,', (False, False, False, False)), + ('signal send peer=/foo/bar,', (False, False, False, False)), + ('signal send peer=/what/ever,', (False, False, False, False)), + ('signal send set=quit,', (False, False, True, True)), + ('signal send set=int peer=/foo/bar,', (False, False, True, True)), + ('audit signal send peer=/foo/bar,', (False, False, False, False)), + ('audit signal,', (False, False, False, False)), + ('signal receive,', (False, False, False, False)), + ('signal set=int,', (False, False, False, False)), + ('audit deny signal send,', (False, False, False, False)), + ('deny signal send,', (False, False, False, False)), ) - class SignalCoveredTest_Invalid(AATest): def test_borked_obj_is_covered_1(self): obj = SignalRule.parse('signal send peer=/foo,') @@ -515,24 +539,26 @@ class SignalCoveredTest_Invalid(AATest): with self.assertRaises(AppArmorBug): obj.is_equal(testobj) + class SignalLogprofHeaderTest(AATest): tests = ( - ('signal,', [ _('Access mode'), _('ALL'), _('Signal'), _('ALL'), _('Peer'), _('ALL'), ]), - ('signal send,', [ _('Access mode'), 'send', _('Signal'), _('ALL'), _('Peer'), _('ALL'), ]), - ('signal send set=quit,', [ _('Access mode'), 'send', _('Signal'), 'quit', _('Peer'), _('ALL'), ]), - ('deny signal,', [_('Qualifier'), 'deny', _('Access mode'), _('ALL'), _('Signal'), _('ALL'), _('Peer'), _('ALL'), ]), - ('allow signal send,', [_('Qualifier'), 'allow', _('Access mode'), 'send', _('Signal'), _('ALL'), _('Peer'), _('ALL'), ]), - ('audit signal send set=quit,', [_('Qualifier'), 'audit', _('Access mode'), 'send', _('Signal'), 'quit', _('Peer'), _('ALL'), ]), - ('audit deny signal send,', [_('Qualifier'), 'audit deny', _('Access mode'), 'send', _('Signal'), _('ALL'), _('Peer'), _('ALL'), ]), - ('signal set=(int, quit),', [ _('Access mode'), _('ALL'), _('Signal'), 'int quit', _('Peer'), _('ALL'), ]), - ('signal set=( quit, int),', [ _('Access mode'), _('ALL'), _('Signal'), 'int quit', _('Peer'), _('ALL'), ]), - ('signal (send, receive) set=( quit, int) peer=/foo,', [ _('Access mode'), 'receive send', _('Signal'), 'int quit', _('Peer'), '/foo', ]), + ('signal,', [ _('Access mode'), _('ALL'), _('Signal'), _('ALL'), _('Peer'), _('ALL')]), + ('signal send,', [ _('Access mode'), 'send', _('Signal'), _('ALL'), _('Peer'), _('ALL')]), + ('signal send set=quit,', [ _('Access mode'), 'send', _('Signal'), 'quit', _('Peer'), _('ALL')]), + ('deny signal,', [_('Qualifier'), 'deny', _('Access mode'), _('ALL'), _('Signal'), _('ALL'), _('Peer'), _('ALL')]), + ('allow signal send,', [_('Qualifier'), 'allow', _('Access mode'), 'send', _('Signal'), _('ALL'), _('Peer'), _('ALL')]), + ('audit signal send set=quit,', [_('Qualifier'), 'audit', _('Access mode'), 'send', _('Signal'), 'quit', _('Peer'), _('ALL')]), + ('audit deny signal send,', [_('Qualifier'), 'audit deny', _('Access mode'), 'send', _('Signal'), _('ALL'), _('Peer'), _('ALL')]), + ('signal set=(int, quit),', [ _('Access mode'), _('ALL'), _('Signal'), 'int quit', _('Peer'), _('ALL')]), + ('signal set=( quit, int),', [ _('Access mode'), _('ALL'), _('Signal'), 'int quit', _('Peer'), _('ALL')]), + ('signal (send, receive) set=( quit, int) peer=/foo,', [ _('Access mode'), 'receive send', _('Signal'), 'int quit', _('Peer'), '/foo']), ) def _run_test(self, params, expected): obj = SignalRule.parse(params) self.assertEqual(obj.logprof_header(), expected) + ## --- tests for SignalRuleset --- # class SignalRulesTest(AATest): @@ -616,8 +642,10 @@ class SignalGlobTestAATest(AATest): # get_glob_ext is not available for signal rules self.ruleset.get_glob_ext('signal send set=int,') -#class SignalDeleteTestAATest(AATest): -# pass + +# class SignalDeleteTestAATest(AATest): +# pass + setup_all_loops(__name__) if __name__ == '__main__': diff --git a/utils/test/test-translations.py b/utils/test/test-translations.py index 6f7eae454..4591dd292 100644 --- a/utils/test/test-translations.py +++ b/utils/test/test-translations.py @@ -18,25 +18,26 @@ import subprocess from apparmor.ui import CMDS, get_translated_hotkey + class TestHotkeyConflicts(AATest): # check if there are any hotkey conflicts in one of the apparmor-utils translations tests = ( - (('CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB', 'CMD_GLOBEXT', 'CMD_NEW', 'CMD_AUDIT_OFF', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa.py available_buttons() with CMD_AUDIT_OFF - (('CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB', 'CMD_GLOBEXT', 'CMD_NEW', 'CMD_AUDIT_NEW', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa.py available_buttons() with CMD_AUDIT_NEW + (('CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB', 'CMD_GLOBEXT', 'CMD_NEW', 'CMD_AUDIT_OFF', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa.py available_buttons() with CMD_AUDIT_OFF + (('CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB', 'CMD_GLOBEXT', 'CMD_NEW', 'CMD_AUDIT_NEW', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa.py available_buttons() with CMD_AUDIT_NEW (('CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB', 'CMD_GLOBEXT', 'CMD_NEW', 'CMD_AUDIT_OFF', 'CMD_USER_ON', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa.py available_buttons() with CMD_AUDIT_OFF and CMD_USER_ON (('CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB', 'CMD_GLOBEXT', 'CMD_NEW', 'CMD_AUDIT_OFF', 'CMD_USER_OFF', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa.py available_buttons() with CMD_AUDIT_OFF and CMD_USER_OFF (('CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB', 'CMD_GLOBEXT', 'CMD_NEW', 'CMD_AUDIT_NEW', 'CMD_USER_ON', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa.py available_buttons() with CMD_AUDIT_NEW and CMD_USER_ON (('CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB', 'CMD_GLOBEXT', 'CMD_NEW', 'CMD_AUDIT_NEW', 'CMD_USER_OFF', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa.py available_buttons() with CMD_AUDIT_NEW and CMD_USER_OFF - (('CMD_SAVE_CHANGES', 'CMD_SAVE_SELECTED', 'CMD_VIEW_CHANGES', 'CMD_VIEW_CHANGES_CLEAN', 'CMD_ABORT'), True), # aa.py save_profiles() - (('CMD_VIEW_PROFILE', 'CMD_USE_PROFILE', 'CMD_CREATE_PROFILE', 'CMD_ABORT'), True), # aa.py get_profile() - (('CMD_ix', 'CMD_pix', 'CMD_cix', 'CMD_nix', 'CMD_EXEC_IX_OFF', 'CMD_ux', 'CMD_DENY', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa.py build_x_functions() with exec_toggle - (('CMD_ix', 'CMD_cx', 'CMD_px', 'CMD_nx', 'CMD_ux', 'CMD_EXEC_IX_ON', 'CMD_DENY', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa.py build_x_functions() without exec_toggle - (('CMD_ADDHAT', 'CMD_USEDEFAULT', 'CMD_DENY', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa.py ask_addhat() - (('CMD_YES', 'CMD_NO', 'CMD_CANCEL'), True), # ui.py UI_YesNo() and UI_YesNoCancel - (('CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT', 'CMD_IGNORE_ENTRY'), True), # aa-mergeprof act() - (('CMD_ALLOW', 'CMD_ABORT'), True), # aa-mergeprof conflict_mode() - (('CMD_ADDSUBPROFILE', 'CMD_DENY', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa-mergeprof ask_the_questions() - new subprofile - (('CMD_ADDHAT', 'CMD_DENY', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa-mergeprof ask_the_questions() - new hat + (('CMD_SAVE_CHANGES', 'CMD_SAVE_SELECTED', 'CMD_VIEW_CHANGES', 'CMD_VIEW_CHANGES_CLEAN', 'CMD_ABORT'), True), # aa.py save_profiles() + (('CMD_VIEW_PROFILE', 'CMD_USE_PROFILE', 'CMD_CREATE_PROFILE', 'CMD_ABORT'), True), # aa.py get_profile() + (('CMD_ix', 'CMD_pix', 'CMD_cix', 'CMD_nix', 'CMD_EXEC_IX_OFF', 'CMD_ux', 'CMD_DENY', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa.py build_x_functions() with exec_toggle + (('CMD_ix', 'CMD_cx', 'CMD_px', 'CMD_nx', 'CMD_ux', 'CMD_EXEC_IX_ON', 'CMD_DENY', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa.py build_x_functions() without exec_toggle + (('CMD_ADDHAT', 'CMD_USEDEFAULT', 'CMD_DENY', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa.py ask_addhat() + (('CMD_YES', 'CMD_NO', 'CMD_CANCEL'), True), # ui.py UI_YesNo() and UI_YesNoCancel + (('CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT', 'CMD_IGNORE_ENTRY'), True), # aa-mergeprof act() + (('CMD_ALLOW', 'CMD_ABORT'), True), # aa-mergeprof conflict_mode() + (('CMD_ADDSUBPROFILE', 'CMD_DENY', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa-mergeprof ask_the_questions() - new subprofile + (('CMD_ADDHAT', 'CMD_DENY', 'CMD_ABORT', 'CMD_FINISHED'), True), # aa-mergeprof ask_the_questions() - new hat ) def _run_test(self, params, expected): diff --git a/utils/test/test-unix_parse.py b/utils/test/test-unix_parse.py index 7a82a8387..3e5274a4c 100644 --- a/utils/test/test-unix_parse.py +++ b/utils/test/test-unix_parse.py @@ -13,6 +13,7 @@ import apparmor.aa as aa import unittest from common_test import AAParseTest, setup_regex_tests, setup_aa + class AAParseUnixTest(AAParseTest): def setUp(self): @@ -30,10 +31,10 @@ class AAParseUnixTest(AAParseTest): ('unix (rw),', 'unix (rw) rule'), ('unix (send),', 'unix (send) rule'), ('unix (receive),', 'unix (receive) rule'), - ('unix (connect, receive, send) type=stream peer=(label=unconfined,addr="@/tmp/.X11-unix/X[0-9]*"),', - 'complex unix rule'), + ('unix (connect, receive, send) type=stream peer=(label=unconfined,addr="@/tmp/.X11-unix/X[0-9]*"),', 'complex unix rule'), ) + setup_aa(aa) if __name__ == '__main__': setup_regex_tests(AAParseUnixTest) diff --git a/utils/test/test-variable.py b/utils/test/test-variable.py index 54d3fd7f2..fcca1f629 100644 --- a/utils/test/test-variable.py +++ b/utils/test/test-variable.py @@ -23,11 +23,11 @@ from apparmor.common import AppArmorException, AppArmorBug from apparmor.translations import init_translation _ = init_translation() -exp = namedtuple('exp', ('comment', - 'varname', 'mode', 'values')) +exp = namedtuple('exp', ('comment', 'varname', 'mode', 'values')) # --- tests for single VariableRule --- # + class VariableTest(AATest): def _compare_obj(self, obj, expected): # variables don't support the allow, audit or deny keyword @@ -40,25 +40,26 @@ class VariableTest(AATest): self.assertEqual(expected.values, obj.values) self.assertEqual(expected.comment, obj.comment) + class AaTest_separate_vars(AATest): tests = ( - ('' , set() ), - (' ' , set() ), - (' foo bar' , {'foo', 'bar' }), - ('foo " ' , AppArmorException ), - (' " foo ' , AppArmorException ), # half-quoted - (' foo bar ' , {'foo', 'bar' }), - (' foo bar # comment' , {'foo', 'bar', '#', 'comment'}), # XXX should comments be stripped? - ('foo' , {'foo' }), - ('"foo" "bar baz"' , {'foo', 'bar baz' }), - ('foo "bar baz" xy' , {'foo', 'bar baz', 'xy' }), - ('foo "bar baz ' , AppArmorException ), # half-quoted - (' " foo" bar' , {' foo', 'bar' }), - (' " foo" bar x' , {' foo', 'bar', 'x' }), - ('""' , {'' }), # empty value - ('"" foo' , {'', 'foo' }), # empty value + 'foo' - ('"" foo "bar"' , {'', 'foo', 'bar' }), # empty value + 'foo' + 'bar' (bar has superfluous quotes) - ('"bar"' , {'bar' }), # 'bar' with superfluous quotes + ('', set()), + (' ', set()), + (' foo bar', {'foo', 'bar'}), + ('foo " ', AppArmorException), + (' " foo ', AppArmorException), # half-quoted + (' foo bar ', {'foo', 'bar'}), + (' foo bar # comment', {'foo', 'bar', '#', 'comment'}), # XXX should comments be stripped? + ('foo', {'foo'}), + ('"foo" "bar baz"', {'foo', 'bar baz'}), + ('foo "bar baz" xy', {'foo', 'bar baz', 'xy'}), + ('foo "bar baz ', AppArmorException), # half-quoted + (' " foo" bar', {' foo', 'bar'}), + (' " foo" bar x', {' foo', 'bar', 'x'}), + ('""', {''}), # empty value + ('"" foo', {'', 'foo'}), # empty value + 'foo' + ('"" foo "bar"', {'', 'foo', 'bar'}), # empty value + 'foo' + 'bar' (bar has superfluous quotes) + ('"bar"', {'bar'}), # 'bar' with superfluous quotes ) def _run_test(self, params, expected): @@ -69,17 +70,18 @@ class AaTest_separate_vars(AATest): result = separate_vars(params) self.assertEqual(result, expected) + class VariableTestParse(VariableTest): tests = ( - # rawrule comment varname mode values - ('@{foo}=/bar', exp('', '@{foo}', '=', {'/bar'} )), - ('@{foo}+=/bar', exp('', '@{foo}', '+=', {'/bar'} )), - (' @{foo} = /bar ', exp('', '@{foo}', '=', {'/bar'} )), - (' @{foo} += /bar', exp('', '@{foo}', '+=', {'/bar'} )), - (' @{foo} = /bar # comment', exp(' # comment', '@{foo}', '=', {'/bar'} )), - (' @{foo} += /bar # comment', exp(' # comment', '@{foo}', '+=', {'/bar'} )), - ('@{foo}=/bar /baz', exp('', '@{foo}', '=', {'/bar', '/baz'} )), - ('@{foo} = "/bar," # comment', exp(' # comment', '@{foo}', '=', {'/bar,'} )), # value with trailing comma, needs to be quoted + # rawrule comment varname mode values + ('@{foo}=/bar', exp('', '@{foo}', '=', {'/bar'})), + ('@{foo}+=/bar', exp('', '@{foo}', '+=', {'/bar'})), + (' @{foo} = /bar ', exp('', '@{foo}', '=', {'/bar'})), + (' @{foo} += /bar', exp('', '@{foo}', '+=', {'/bar'})), + (' @{foo} = /bar # comment', exp(' # comment', '@{foo}', '=', {'/bar'})), + (' @{foo} += /bar # comment', exp(' # comment', '@{foo}', '+=', {'/bar'})), + ('@{foo}=/bar /baz', exp('', '@{foo}', '=', {'/bar', '/baz'})), + ('@{foo} = "/bar," # comment', exp(' # comment', '@{foo}', '=', {'/bar,'})), # value with trailing comma, needs to be quoted ) def _run_test(self, rawrule, expected): @@ -88,17 +90,18 @@ class VariableTestParse(VariableTest): self.assertEqual(rawrule.strip(), obj.raw_rule) self._compare_obj(obj, expected) + class VariableTestParseInvalid(VariableTest): tests = ( - # rawrule matches regex exception - ('@{foo} =', (False, AppArmorException)), - ('@ {foo} = # comment', (False, AppArmorException)), - ('@ {foo} = ', (False, AppArmorException)), - ('@{foo} = /foo,', (True, AppArmorException)), # trailing comma - ('@{foo} = /foo, ', (True, AppArmorException)), # trailing comma - ('@{foo} = /foo, # comment', (True, AppArmorException)), # trailing comma - ('@{foo} = /foo, /bar', (True, AppArmorException)), # trailing comma in first value - ('@{foo = /foo f', (True, AppArmorException)), # variable name broken, missing } + # rawrule matches regex exception + ('@{foo} =', (False, AppArmorException)), + ('@ {foo} = # comment', (False, AppArmorException)), + ('@ {foo} = ', (False, AppArmorException)), + ('@{foo} = /foo,', (True, AppArmorException)), # trailing comma + ('@{foo} = /foo, ', (True, AppArmorException)), # trailing comma + ('@{foo} = /foo, # comment', (True, AppArmorException)), # trailing comma + ('@{foo} = /foo, /bar', (True, AppArmorException)), # trailing comma in first value + ('@{foo = /foo f', (True, AppArmorException)), # variable name broken, missing } ) def _run_test(self, rawrule, expected): @@ -106,15 +109,16 @@ class VariableTestParseInvalid(VariableTest): with self.assertRaises(expected[1]): VariableRule.parse(rawrule) + class VariableFromInit(VariableTest): tests = ( - # VariableRule object comment varname mode values - (VariableRule('@{foo}', '=', {'/bar'}), exp('', '@{foo}', '=', {'/bar'} )), - (VariableRule('@{foo}', '+=', {'/bar'}), exp('', '@{foo}', '+=', {'/bar'} )), - (VariableRule('@{foo}', '=', {'/bar', '/baz'}), exp('', '@{foo}', '=', {'/bar', '/baz'} )), - (VariableRule('@{foo}', '+=', {'/bar', '/baz'}), exp('', '@{foo}', '+=', {'/bar', '/baz'} )), - (VariableRule('@{foo}', '=', {'/bar'}, comment='# cmt'), exp('# cmt', '@{foo}', '=', {'/bar'} )), - (VariableRule('@{foo}', '+=', {'/bar'}, comment='# cmt'), exp('# cmt', '@{foo}', '+=', {'/bar'} )), + # VariableRule object comment varname mode values + (VariableRule('@{foo}', '=', {'/bar'}), exp('', '@{foo}', '=', {'/bar'})), + (VariableRule('@{foo}', '+=', {'/bar'}), exp('', '@{foo}', '+=', {'/bar'})), + (VariableRule('@{foo}', '=', {'/bar', '/baz'}), exp('', '@{foo}', '=', {'/bar', '/baz'})), + (VariableRule('@{foo}', '+=', {'/bar', '/baz'}), exp('', '@{foo}', '+=', {'/bar', '/baz'})), + (VariableRule('@{foo}', '=', {'/bar'}, comment='# cmt'), exp('# cmt', '@{foo}', '=', {'/bar'})), + (VariableRule('@{foo}', '+=', {'/bar'}, comment='# cmt'), exp('# cmt', '@{foo}', '+=', {'/bar'})), ) def _run_test(self, obj, expected): @@ -123,19 +127,19 @@ class VariableFromInit(VariableTest): class InvalidVariableInit(AATest): tests = ( - # init params expected exception - ((None, '=', ['/bar']), AppArmorBug), # varname not a str - (('', '=', ['/bar']), AppArmorException), # empty varname - (('foo', '=', ['/bar']), AppArmorException), # varname not starting with '@{' - (('foo', '=', ['/bar']), AppArmorException), # varname not starting with '@{' + # init params expected exception + ((None, '=', ['/bar']), AppArmorBug), # varname not a str + (('', '=', ['/bar']), AppArmorException), # empty varname + (('foo', '=', ['/bar']), AppArmorException), # varname not starting with '@{' + (('foo', '=', ['/bar']), AppArmorException), # varname not starting with '@{' - (('@{foo}', '', ['/bar']), AppArmorBug), # mode not '=' or '+=' - (('@{foo}', '-=', ['/bar']), AppArmorBug), # mode not '=' or '+=' - (('@{foo}', ' ', ['/bar']), AppArmorBug), # mode not '=' or '+=' - (('@{foo}', None, ['/bar']), AppArmorBug), # mode not '=' or '+=' + (('@{foo}', '', ['/bar']), AppArmorBug), # mode not '=' or '+=' + (('@{foo}', '-=', ['/bar']), AppArmorBug), # mode not '=' or '+=' + (('@{foo}', ' ', ['/bar']), AppArmorBug), # mode not '=' or '+=' + (('@{foo}', None, ['/bar']), AppArmorBug), # mode not '=' or '+=' - (('@{foo}', '=', None ), AppArmorBug), # values not a set - (('@{foo}', '=', set() ), AppArmorException), # empty values + (('@{foo}', '=', None), AppArmorBug), # values not a set + (('@{foo}', '=', set()), AppArmorException), # empty values ) def _run_test(self, params, expected): @@ -181,16 +185,16 @@ class InvalidVariableTest(AATest): class WriteVariableTestAATest(AATest): tests = ( - # raw rule clean rule - (' @{foo} = /bar ', '@{foo} = /bar'), - (' @{foo} = /bar # comment', '@{foo} = /bar'), - (' @{foo} = /bar ""', '@{foo} = "" /bar'), - (' @{foo} += /bar ', '@{foo} += /bar'), - (' @{foo} += /bar # comment', '@{foo} += /bar'), - (' @{foo} += /bar /baz', '@{foo} += /bar /baz'), - (' @{foo} += /bar /baz', '@{foo} += /bar /baz'), - (' @{foo} += /bar @{baz}', '@{foo} += /bar @{baz}'), - (' @{foo} += /bar @{baz}', '@{foo} += /bar @{baz}'), + # raw rule clean rule + (' @{foo} = /bar ', '@{foo} = /bar'), + (' @{foo} = /bar # comment', '@{foo} = /bar'), + (' @{foo} = /bar ""', '@{foo} = "" /bar'), + (' @{foo} += /bar ', '@{foo} += /bar'), + (' @{foo} += /bar # comment', '@{foo} += /bar'), + (' @{foo} += /bar /baz', '@{foo} += /bar /baz'), + (' @{foo} += /bar /baz', '@{foo} += /bar /baz'), + (' @{foo} += /bar @{baz}', '@{foo} += /bar @{baz}'), + (' @{foo} += /bar @{baz}', '@{foo} += /bar @{baz}'), ) def _run_test(self, rawrule, expected): @@ -232,53 +236,56 @@ class VariableCoveredTest(AATest): self.assertEqual(obj.is_covered(check_obj), expected[2], 'Mismatch in is_covered, expected %s' % expected[2]) self.assertEqual(obj.is_covered(check_obj, True, True), expected[3], 'Mismatch in is_covered/exact, expected %s' % expected[3]) + class VariableCoveredTest_01(VariableCoveredTest): rule = '@{foo} = /bar' tests = ( - # rule equal strict equal covered covered exact - (' @{foo} = /bar' , ( True , True , True , True )), - (' @{foo} += /bar' , ( False , False , False , False )), - (' @{foo} = /bar # comment' , ( True , False , True , True )), - (' @{foo} += /bar # comment' , ( False , False , False , False )), - (' @{foo} = /baz /bar' , ( False , False , False , False )), - (' @{foo} += /baz /bar' , ( False , False , False , False )), - (' @{foo} = /baz /bar # cmt' , ( False , False , False , False )), - (' @{foo} += /baz /bar # cmt' , ( False , False , False , False )), - (' @{bar} = /bar' , ( False , False , False , False )), # different variable name + # rule equal strict equal covered covered exact + (' @{foo} = /bar', (True, True, True, True)), + (' @{foo} += /bar', (False, False, False, False)), + (' @{foo} = /bar # comment', (True, False, True, True)), + (' @{foo} += /bar # comment', (False, False, False, False)), + (' @{foo} = /baz /bar', (False, False, False, False)), + (' @{foo} += /baz /bar', (False, False, False, False)), + (' @{foo} = /baz /bar # cmt', (False, False, False, False)), + (' @{foo} += /baz /bar # cmt', (False, False, False, False)), + (' @{bar} = /bar', (False, False, False, False)), # different variable name ) + class VariableCoveredTest_02(VariableCoveredTest): rule = '@{foo} = /bar /baz' tests = ( - # rule equal strict equal covered covered exact - (' @{foo} = /bar /baz' , ( True , True , True , True )), - (' @{foo} += /bar /baz' , ( False , False , False , False )), - (' @{foo} = /bar /baz # cmt' , ( True , False , True , True )), - (' @{foo} += /bar /baz # cmt' , ( False , False , False , False )), + # rule equal strict equal covered covered exact + (' @{foo} = /bar /baz', (True, True, True, True)), + (' @{foo} += /bar /baz', (False, False, False, False)), + (' @{foo} = /bar /baz # cmt', (True, False, True, True)), + (' @{foo} += /bar /baz # cmt', (False, False, False, False)), # changed order of values - (' @{foo} = /baz /bar' , ( True , False , True , True )), - (' @{foo} += /baz /bar' , ( False , False , False , False )), - (' @{foo} = /baz /bar # cmt' , ( True , False , True , True )), - (' @{foo} += /baz /bar # cmt' , ( False , False , False , False )), + (' @{foo} = /baz /bar', (True, False, True, True)), + (' @{foo} += /baz /bar', (False, False, False, False)), + (' @{foo} = /baz /bar # cmt', (True, False, True, True)), + (' @{foo} += /baz /bar # cmt', (False, False, False, False)), # only one value - (' @{foo} = /bar' , ( False , False , True , True )), - (' @{foo} += /bar' , ( False , False , False , False )), - (' @{foo} = /bar # comment' , ( False , False , True , True )), - (' @{foo} += /bar # comment' , ( False , False , False , False )), - (' @{bar} = /bar' , ( False , False , False , False )), # different variable name + (' @{foo} = /bar', (False, False, True, True)), + (' @{foo} += /bar', (False, False, False, False)), + (' @{foo} = /bar # comment', (False, False, True, True)), + (' @{foo} += /bar # comment', (False, False, False, False)), + (' @{bar} = /bar', (False, False, False, False)), # different variable name ) + class VariableCoveredTest_Invalid(AATest): -# def test_borked_obj_is_covered_1(self): -# obj = VariableRule.parse('@{foo} = /bar') - -# testobj = VariableRule('@{foo}', '=', '/bar') -# testobj.mode = '' - -# with self.assertRaises(AppArmorBug): -# obj.is_covered(testobj) + # def test_borked_obj_is_covered_1(self): + # obj = VariableRule.parse('@{foo} = /bar') + # + # testobj = VariableRule('@{foo}', '=', '/bar') + # testobj.mode = '' + # + # with self.assertRaises(AppArmorBug): + # obj.is_covered(testobj) def test_borked_obj_is_covered_2(self): obj = VariableRule.parse('@{foo} = /bar') @@ -305,9 +312,10 @@ class VariableCoveredTest_Invalid(AATest): with self.assertRaises(AppArmorBug): obj.is_equal(testobj) + class VariableLogprofHeaderTest(AATest): tests = ( - ('@{foo} = /bar', [_('Variable'), '@{foo} = /bar' ]), + ('@{foo} = /bar', [_('Variable'), '@{foo} = /bar']), ) def _run_test(self, params, expected): @@ -316,6 +324,7 @@ class VariableLogprofHeaderTest(AATest): # --- tests for VariableRuleset --- # + class VariableRulesTest(AATest): def test_empty_ruleset(self): ruleset = VariableRuleset() @@ -383,7 +392,8 @@ class VariableRulesTest(AATest): ruleset.add(VariableRule.parse('@{foo} = /bar')) with self.assertRaises(AppArmorException): ruleset.add(VariableRule.parse('@{foo} = /asdf')) # attempt to redefine @{foo} - self.assertEqual({'=': {'@{foo}': {'/bar'} }, '+=': {}}, ruleset.get_merged_variables()) + self.assertEqual({'=': {'@{foo}': {'/bar'}}, '+=': {}}, ruleset.get_merged_variables()) + class VariableGlobTestAATest(AATest): def setUp(self): @@ -398,9 +408,11 @@ class VariableGlobTestAATest(AATest): # get_glob_ext is not available for change_profile rules self.ruleset.get_glob_ext('@{foo} = /bar') + class VariableDeleteTestAATest(AATest): pass + setup_all_loops(__name__) if __name__ == '__main__': unittest.main(verbosity=1) diff --git a/utils/vim/create-apparmor.vim.py b/utils/vim/create-apparmor.vim.py index 2bf3b108e..e0b363c8e 100644 --- a/utils/vim/create-apparmor.vim.py +++ b/utils/vim/create-apparmor.vim.py @@ -43,6 +43,7 @@ def cmd(command, input=None, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, s outerr = '' return sp.returncode, out, outerr + # get capabilities list (rc, output, outerr) = cmd(('../../common/list_capabilities.sh',)) if rc != 0: @@ -149,7 +150,7 @@ filerule = filerule + create_file_rule('sdEntryIX', r'(r|m|k|ix)+', 'ix(mr) - filerule = filerule + create_file_rule('sdEntryM', r'(r|m|k)+', 'mr - mmap with PROT_EXEC') filerule = filerule + create_file_rule('sdEntryM', r'(r|m|k|x)+', 'special case: deny x is allowed (does not need to be ix, px, ux or cx)', 1) -#syn match sdEntryM /@@DENYFILE@@(r|m|k|x)+@@EOL@@/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude +# syn match sdEntryM /@@DENYFILE@@(r|m|k|x)+@@EOL@@/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude filerule = filerule + create_file_rule('sdError', r'\S*(w\S*a|a\S*w)\S*', 'write + append is an error')