diff --git a/utils/apparmor/tools.py b/utils/apparmor/tools.py index d4cd0b3b3..e8a99bbe6 100644 --- a/utils/apparmor/tools.py +++ b/utils/apparmor/tools.py @@ -1,6 +1,6 @@ # ---------------------------------------------------------------------- # Copyright (C) 2013 Kshitij Gupta -# Copyright (C) 2015-2022 Christian Boltz +# Copyright (C) 2015-2023 Christian Boltz # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public @@ -27,6 +27,7 @@ _ = init_translation() class aa_tools: def __init__(self, tool_name, args): apparmor.init_aa(profiledir=args.dir, confdir=args.configdir) + apparmor.read_profiles() if not user_perm(apparmor.profile_dir): raise AppArmorException("Cannot write to profile directory: %s" % (apparmor.profile_dir)) @@ -51,51 +52,55 @@ class aa_tools: if not p: continue - program = None - profile = None if os.path.exists(p) or p.startswith('/'): fq_path = apparmor.get_full_path(p).strip() if os.path.commonprefix([apparmor.profile_dir, fq_path]) == apparmor.profile_dir: program = None - profile = fq_path + prof_filename = fq_path + profile = None else: program = fq_path - if self.name == 'cleanprof': - profile = apparmor.active_profiles.profile_from_attachment(fq_path) - else: - profile = apparmor.get_profile_filename_from_attachment(fq_path, True) + profile = apparmor.active_profiles.profile_from_attachment(fq_path) + prof_filename = apparmor.get_profile_filename_from_attachment(fq_path, True) else: which_ = which(p) if self.name == 'cleanprof' and p in apparmor.aa: program = p # not really correct, but works profile = p + prof_filename = apparmor.get_profile_filename_from_profile_name(profile) elif which_ is not None: program = apparmor.get_full_path(which_) - if self.name == 'cleanprof': - profile = program - else: - profile = apparmor.get_profile_filename_from_attachment(program, True) + profile = program + prof_filename = apparmor.get_profile_filename_from_attachment(program, True) elif os.path.exists(os.path.join(apparmor.profile_dir, p)): program = None - if self.name == 'cleanprof': - profile = p - else: - profile = apparmor.get_full_path(os.path.join(apparmor.profile_dir, p)).strip() + profile = p + prof_filename = 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}) else: aaui.UI_Info(_("%s does not exist, please double-check the path.") % p) + continue - yield (program, profile) + yield (program, profile, prof_filename) + + def get_next_for_modechange(self): + """common code for mode/flags changes""" + + for (program, _, prof_filename) in self.get_next_to_profile(): + output_name = prof_filename if program is None else program + + if not os.path.isfile(prof_filename) or is_skippable_file(prof_filename): + aaui.UI_Info(_('Profile for %s not found, skipping') % output_name) + continue + + yield (program, prof_filename, output_name) def cleanprof_act(self): - # used by aa-cleanprof - apparmor.read_profiles() - - for (program, profile) in self.get_next_to_profile(): + for (program, profile, prof_filename) in self.get_next_to_profile(): if program is None: program = profile @@ -107,7 +112,7 @@ class aa_tools: sys.exit(1) if program and profile in apparmor.aa: - self.clean_profile(program, profile) + self.clean_profile(program, profile, prof_filename) else: if '/' not in program: @@ -118,83 +123,48 @@ class aa_tools: sys.exit(1) def cmd_disable(self): - apparmor.read_profiles() - - for (program, profile) in self.get_next_to_profile(): - - output_name = profile if program is None else program - - if not os.path.isfile(profile) or is_skippable_file(profile): - aaui.UI_Info(_('Profile for %s not found, skipping') % output_name) - continue - + for (program, prof_filename, output_name) in self.get_next_for_modechange(): aaui.UI_Info(_('Disabling %s.') % output_name) - self.disable_profile(profile) - self.unload_profile(profile) + apparmor.create_symlink('disable', prof_filename) + + self.unload_profile(prof_filename) def cmd_enforce(self): - apparmor.read_profiles() + for (program, prof_filename, output_name) in self.get_next_for_modechange(): + apparmor.set_enforce(prof_filename, program) - for (program, profile) in self.get_next_to_profile(): - - output_name = profile if program is None else program - - if not os.path.isfile(profile) or is_skippable_file(profile): - aaui.UI_Info(_('Profile for %s not found, skipping') % output_name) - continue - - apparmor.set_enforce(profile, program) - - self.reload_profile(profile) + self.reload_profile(prof_filename) def cmd_complain(self): - apparmor.read_profiles() + for (program, prof_filename, output_name) in self.get_next_for_modechange(): + apparmor.set_complain(prof_filename, program) - for (program, profile) in self.get_next_to_profile(): - - output_name = profile if program is None else program - - if not os.path.isfile(profile) or is_skippable_file(profile): - aaui.UI_Info(_('Profile for %s not found, skipping') % output_name) - continue - - apparmor.set_complain(profile, program) - - self.reload_profile(profile) + self.reload_profile(prof_filename) def cmd_audit(self): - apparmor.read_profiles() - - for (program, profile) in self.get_next_to_profile(): - - output_name = profile if program is None else program - - if not os.path.isfile(profile) or is_skippable_file(profile): - aaui.UI_Info(_('Profile for %s not found, skipping') % output_name) - continue + for (program, prof_filename, output_name) in self.get_next_for_modechange(): # keep this to allow toggling 'audit' flags if not self.remove: aaui.UI_Info(_('Setting %s to audit mode.') % output_name) else: aaui.UI_Info(_('Removing audit mode from %s.') % output_name) - apparmor.change_profile_flags(profile, program, 'audit', not self.remove) + apparmor.change_profile_flags(prof_filename, program, 'audit', not self.remove) - disable_link = '%s/disable/%s' % (apparmor.profile_dir, os.path.basename(profile)) + disable_link = '%s/disable/%s' % (apparmor.profile_dir, os.path.basename(prof_filename)) if os.path.exists(disable_link): - aaui.UI_Info(_('\nWarning: the profile %s is disabled. Use aa-enforce or aa-complain to enable it.') % os.path.basename(profile)) + aaui.UI_Info(_('\nWarning: the profile %s is disabled. Use aa-enforce or aa-complain to enable it.') % os.path.basename(prof_filename)) - self.reload_profile(profile) + self.reload_profile(prof_filename) def cmd_autodep(self): - apparmor.read_profiles() apparmor.loadincludes() - for (program, profile) in self.get_next_to_profile(): + for (program, _, prof_filename) in self.get_next_to_profile(): if not program: - aaui.UI_Info(_('Please pass an application to generate a profile for, not a profile itself - skipping %s.') % profile) + aaui.UI_Info(_('Please pass an application to generate a profile for, not a profile itself - skipping %s.') % prof_filename) continue apparmor.check_qualifiers(program) @@ -206,59 +176,53 @@ class aa_tools: if self.aa_mountpoint: apparmor.reload(program) - def clean_profile(self, program, profile): - filename = apparmor.get_profile_filename_from_profile_name(profile) + def clean_profile(self, program, profile, prof_filename): import apparmor.cleanprofile as cleanprofile - prof = cleanprofile.Prof(filename) + + prof = cleanprofile.Prof(prof_filename) cleanprof = cleanprofile.CleanProf(True, prof, prof) deleted = cleanprof.remove_duplicate_rules(profile) aaui.UI_Info(_("\nDeleted %s rules.") % deleted) apparmor.changed[profile] = True - if filename: - 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.functions = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT'] - q.default = 'CMD_VIEW_CHANGES' - q.options = [] - q.selected = 0 - ans = '' - arg = None - while ans != 'CMD_SAVE_CHANGES': - ans, arg = q.promptUser() - if ans == 'CMD_SAVE_CHANGES': - apparmor.write_profile_ui_feedback(profile) - self.reload_profile(filename) - elif ans == 'CMD_VIEW_CHANGES': - # oldprofile = apparmor.serialize_profile(apparmor.split_to_merged(apparmor.original_aa), profile, {}) - newprofile = apparmor.serialize_profile(apparmor.split_to_merged(apparmor.aa), profile, {}) # , {'is_attachment': True}) - aaui.UI_Changes(filename, newprofile, comments=True) - else: - apparmor.write_profile_ui_feedback(profile, True) - self.reload_profile(filename) - else: + if not prof_filename: raise AppArmorException(_('The profile for %s does not exists. Nothing to clean.') % program) - def enable_profile(self, filename): - apparmor.delete_symlink('disable', filename) + if self.silent: + apparmor.write_profile_ui_feedback(profile, True) + self.reload_profile(prof_filename) + else: + 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': prof_filename} + q.functions = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT'] + q.default = 'CMD_VIEW_CHANGES' + q.options = [] + q.selected = 0 + ans = '' + arg = None + while ans != 'CMD_SAVE_CHANGES': + ans, arg = q.promptUser() + if ans == 'CMD_SAVE_CHANGES': + apparmor.write_profile_ui_feedback(profile) + self.reload_profile(prof_filename) + elif ans == 'CMD_VIEW_CHANGES': + # oldprofile = apparmor.serialize_profile(apparmor.split_to_merged(apparmor.original_aa), profile, {}) + newprofile = apparmor.serialize_profile(apparmor.split_to_merged(apparmor.aa), profile, {}) # , {'is_attachment': True}) + aaui.UI_Changes(prof_filename, newprofile, comments=True) - def disable_profile(self, filename): - apparmor.create_symlink('disable', filename) - - def unload_profile(self, profile): + def unload_profile(self, prof_filename): if not self.do_reload: return # FIXME: should ensure profile is loaded before unloading - cmd_info = cmd([apparmor.parser, '-I%s' % apparmor.profile_dir, '--base', apparmor.profile_dir, '-R', profile]) + cmd_info = cmd([apparmor.parser, '-I%s' % apparmor.profile_dir, '--base', apparmor.profile_dir, '-R', prof_filename]) if cmd_info[0] != 0: raise AppArmorException(cmd_info[1]) - def reload_profile(self, profile): + def reload_profile(self, prof_filename): if not self.do_reload: return - apparmor.reload_profile(profile, raise_exc=True) + apparmor.reload_profile(prof_filename, raise_exc=True)