mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-22 10:07:12 +00:00
Merge tools.py: the big cleanup
tools.py contained quite some things that need a big cleanup. See the individual commits for details and more readable diffs. Note: This MR "only" does cleanups and some refactoring. It does not change the (user-visible) behaviour of the code. MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1114 Approved-by: John Johansen <john@jjmx.net> Merged-by: John Johansen <john@jjmx.net>
This commit is contained in:
commit
58a89284d5
@ -1,6 +1,6 @@
|
|||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
|
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
|
||||||
# Copyright (C) 2015-2022 Christian Boltz <apparmor@cboltz.de>
|
# Copyright (C) 2015-2023 Christian Boltz <apparmor@cboltz.de>
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of version 2 of the GNU General Public
|
# modify it under the terms of version 2 of the GNU General Public
|
||||||
@ -27,6 +27,7 @@ _ = init_translation()
|
|||||||
class aa_tools:
|
class aa_tools:
|
||||||
def __init__(self, tool_name, args):
|
def __init__(self, tool_name, args):
|
||||||
apparmor.init_aa(profiledir=args.dir, confdir=args.configdir)
|
apparmor.init_aa(profiledir=args.dir, confdir=args.configdir)
|
||||||
|
apparmor.read_profiles()
|
||||||
|
|
||||||
if not user_perm(apparmor.profile_dir):
|
if not user_perm(apparmor.profile_dir):
|
||||||
raise AppArmorException("Cannot write to profile directory: %s" % (apparmor.profile_dir))
|
raise AppArmorException("Cannot write to profile directory: %s" % (apparmor.profile_dir))
|
||||||
@ -51,51 +52,55 @@ class aa_tools:
|
|||||||
if not p:
|
if not p:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
program = None
|
|
||||||
profile = None
|
|
||||||
if os.path.exists(p) or p.startswith('/'):
|
if os.path.exists(p) or p.startswith('/'):
|
||||||
fq_path = apparmor.get_full_path(p).strip()
|
fq_path = apparmor.get_full_path(p).strip()
|
||||||
if os.path.commonprefix([apparmor.profile_dir, fq_path]) == apparmor.profile_dir:
|
if os.path.commonprefix([apparmor.profile_dir, fq_path]) == apparmor.profile_dir:
|
||||||
program = None
|
program = None
|
||||||
profile = fq_path
|
prof_filename = fq_path
|
||||||
|
profile = None
|
||||||
else:
|
else:
|
||||||
program = fq_path
|
program = fq_path
|
||||||
if self.name == 'cleanprof':
|
profile = apparmor.active_profiles.profile_from_attachment(fq_path)
|
||||||
profile = apparmor.active_profiles.profile_from_attachment(fq_path)
|
prof_filename = apparmor.get_profile_filename_from_attachment(fq_path, True)
|
||||||
else:
|
|
||||||
profile = apparmor.get_profile_filename_from_attachment(fq_path, True)
|
|
||||||
else:
|
else:
|
||||||
which_ = which(p)
|
which_ = which(p)
|
||||||
if self.name == 'cleanprof' and p in apparmor.aa:
|
if self.name == 'cleanprof' and p in apparmor.aa:
|
||||||
program = p # not really correct, but works
|
program = p # not really correct, but works
|
||||||
profile = p
|
profile = p
|
||||||
|
prof_filename = apparmor.get_profile_filename_from_profile_name(profile)
|
||||||
elif which_ is not None:
|
elif which_ is not None:
|
||||||
program = apparmor.get_full_path(which_)
|
program = apparmor.get_full_path(which_)
|
||||||
if self.name == 'cleanprof':
|
profile = program
|
||||||
profile = program
|
prof_filename = apparmor.get_profile_filename_from_attachment(program, True)
|
||||||
else:
|
|
||||||
profile = apparmor.get_profile_filename_from_attachment(program, True)
|
|
||||||
elif os.path.exists(os.path.join(apparmor.profile_dir, p)):
|
elif os.path.exists(os.path.join(apparmor.profile_dir, p)):
|
||||||
program = None
|
program = None
|
||||||
if self.name == 'cleanprof':
|
profile = p
|
||||||
profile = p
|
prof_filename = apparmor.get_full_path(os.path.join(apparmor.profile_dir, p)).strip()
|
||||||
else:
|
|
||||||
profile = apparmor.get_full_path(os.path.join(apparmor.profile_dir, p)).strip()
|
|
||||||
else:
|
else:
|
||||||
if '/' not in p:
|
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.")
|
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})
|
% {'program': p})
|
||||||
else:
|
else:
|
||||||
aaui.UI_Info(_("%s does not exist, please double-check the path.") % p)
|
aaui.UI_Info(_("%s does not exist, please double-check the path.") % p)
|
||||||
|
|
||||||
continue
|
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):
|
def cleanprof_act(self):
|
||||||
# used by aa-cleanprof
|
for (program, profile, prof_filename) in self.get_next_to_profile():
|
||||||
apparmor.read_profiles()
|
|
||||||
|
|
||||||
for (program, profile) in self.get_next_to_profile():
|
|
||||||
if program is None:
|
if program is None:
|
||||||
program = profile
|
program = profile
|
||||||
|
|
||||||
@ -107,7 +112,7 @@ class aa_tools:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if program and profile in apparmor.aa:
|
if program and profile in apparmor.aa:
|
||||||
self.clean_profile(program, profile)
|
self.clean_profile(program, profile, prof_filename)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if '/' not in program:
|
if '/' not in program:
|
||||||
@ -118,83 +123,48 @@ class aa_tools:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def cmd_disable(self):
|
def cmd_disable(self):
|
||||||
apparmor.read_profiles()
|
for (program, prof_filename, output_name) in self.get_next_for_modechange():
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
aaui.UI_Info(_('Disabling %s.') % output_name)
|
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):
|
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():
|
self.reload_profile(prof_filename)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def cmd_complain(self):
|
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():
|
self.reload_profile(prof_filename)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def cmd_audit(self):
|
def cmd_audit(self):
|
||||||
apparmor.read_profiles()
|
for (program, prof_filename, output_name) in self.get_next_for_modechange():
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
# keep this to allow toggling 'audit' flags
|
# keep this to allow toggling 'audit' flags
|
||||||
if not self.remove:
|
if not self.remove:
|
||||||
aaui.UI_Info(_('Setting %s to audit mode.') % output_name)
|
aaui.UI_Info(_('Setting %s to audit mode.') % output_name)
|
||||||
else:
|
else:
|
||||||
aaui.UI_Info(_('Removing audit mode from %s.') % output_name)
|
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):
|
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):
|
def cmd_autodep(self):
|
||||||
apparmor.read_profiles()
|
|
||||||
apparmor.loadincludes()
|
apparmor.loadincludes()
|
||||||
|
|
||||||
for (program, profile) in self.get_next_to_profile():
|
for (program, _, prof_filename) in self.get_next_to_profile():
|
||||||
if not program:
|
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
|
continue
|
||||||
|
|
||||||
apparmor.check_qualifiers(program)
|
apparmor.check_qualifiers(program)
|
||||||
@ -206,59 +176,53 @@ class aa_tools:
|
|||||||
if self.aa_mountpoint:
|
if self.aa_mountpoint:
|
||||||
apparmor.reload(program)
|
apparmor.reload(program)
|
||||||
|
|
||||||
def clean_profile(self, program, profile):
|
def clean_profile(self, program, profile, prof_filename):
|
||||||
filename = apparmor.get_profile_filename_from_profile_name(profile)
|
|
||||||
import apparmor.cleanprofile as cleanprofile
|
import apparmor.cleanprofile as cleanprofile
|
||||||
prof = cleanprofile.Prof(filename)
|
|
||||||
|
prof = cleanprofile.Prof(prof_filename)
|
||||||
cleanprof = cleanprofile.CleanProf(True, prof, prof)
|
cleanprof = cleanprofile.CleanProf(True, prof, prof)
|
||||||
deleted = cleanprof.remove_duplicate_rules(profile)
|
deleted = cleanprof.remove_duplicate_rules(profile)
|
||||||
aaui.UI_Info(_("\nDeleted %s rules.") % deleted)
|
aaui.UI_Info(_("\nDeleted %s rules.") % deleted)
|
||||||
apparmor.changed[profile] = True
|
apparmor.changed[profile] = True
|
||||||
|
|
||||||
if filename:
|
if not prof_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:
|
|
||||||
raise AppArmorException(_('The profile for %s does not exists. Nothing to clean.') % program)
|
raise AppArmorException(_('The profile for %s does not exists. Nothing to clean.') % program)
|
||||||
|
|
||||||
def enable_profile(self, filename):
|
if self.silent:
|
||||||
apparmor.delete_symlink('disable', filename)
|
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):
|
def unload_profile(self, prof_filename):
|
||||||
apparmor.create_symlink('disable', filename)
|
|
||||||
|
|
||||||
def unload_profile(self, profile):
|
|
||||||
if not self.do_reload:
|
if not self.do_reload:
|
||||||
return
|
return
|
||||||
|
|
||||||
# FIXME: should ensure profile is loaded before unloading
|
# 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:
|
if cmd_info[0] != 0:
|
||||||
raise AppArmorException(cmd_info[1])
|
raise AppArmorException(cmd_info[1])
|
||||||
|
|
||||||
def reload_profile(self, profile):
|
def reload_profile(self, prof_filename):
|
||||||
if not self.do_reload:
|
if not self.do_reload:
|
||||||
return
|
return
|
||||||
|
|
||||||
apparmor.reload_profile(profile, raise_exc=True)
|
apparmor.reload_profile(prof_filename, raise_exc=True)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user