apparmor/utils/aa-mergeprof
Christian Boltz c795a1f228 Update aa-mergeprof to use the NetworkRule(set) class layout
aa-mergeprof still used the old aa[profile][hat][allow]['netdomain']
which no longer gets populated. This resulted in not asking for merging
any network rules.

This patch changes ask_the_question() to the NetworkRule(set) layout.
Besides that,
- don't ask for network rules that are already covered.
  Using is_known_rule() also fixes
  https://bugs.launchpad.net/apparmor/+bug/1382241
- include the audit keyword in the "Network Family" headline
  (I'd prefer to just use the get_clean() rule, but that's another topic)
- hide "(A)llow" when merging a deny rule
- as a side effect of using NetworkRule, fix crashes for 'network,' and
  'network foo,' rules

To avoid having to repeat the list of available "buttons" and the logic
to update that list, add a available_buttons() function that returns the
list of available buttons depending on rule_obj.deny and rule_obj.audit
to aa.py, and import it into mergeprof.

I tested all changes manually.


Acked-by: Steve Beattie <steve@nxnw.org>
2015-05-29 01:12:38 +02:00

822 lines
38 KiB
Python
Executable file

#! /usr/bin/env python
# ----------------------------------------------------------------------
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# ----------------------------------------------------------------------
import argparse
import re
import os
import apparmor.aa
from apparmor.aa import available_buttons
import apparmor.aamode
from apparmor.common import AppArmorException
import apparmor.severity
import apparmor.cleanprofile as cleanprofile
import apparmor.ui as aaui
# setup module translations
from apparmor.translations import init_translation
_ = 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('other', nargs='?', type=str, help=_('other profile'))
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'))
args = parser.parse_args()
args.other = None
# 2-way merge or 3-way merge based on number of params
merge_mode = 2 #if args.other == None else 3
profiles = [args.files, [args.other]]
profiledir = args.dir
if profiledir:
apparmor.aa.profile_dir = apparmor.aa.get_full_path(profiledir)
if not os.path.isdir(apparmor.aa.profile_dir):
raise AppArmorException(_("%s is not a directory.") %profiledir)
def reset_aa():
apparmor.aa.aa = apparmor.aa.hasher()
apparmor.aa.filelist = apparmor.aa.hasher()
apparmor.aa.include = dict()
apparmor.aa.existing_profiles = apparmor.aa.hasher()
apparmor.aa.original_aa = apparmor.aa.hasher()
def find_profiles_from_files(files):
profile_to_filename = dict()
for file_name in files:
apparmor.aa.read_profile(file_name, True)
for profile_name in apparmor.aa.filelist[file_name]['profiles'].keys():
profile_to_filename[profile_name] = file_name
reset_aa()
return profile_to_filename
def find_files_from_profiles(profiles):
profile_to_filename = dict()
apparmor.aa.read_profiles()
for profile_name in profiles:
profile_to_filename[profile_name] = apparmor.aa.get_profile_filename(profile_name)
reset_aa()
return profile_to_filename
def main():
profiles_to_merge = set()
base_files, other_files = profiles
base_profile_to_file = find_profiles_from_files(base_files)
profiles_to_merge = profiles_to_merge.union(set(base_profile_to_file.keys()))
other_profile_to_file = dict()
if merge_mode == 3:
other_profile_to_file = find_profiles_from_files(other_files)
profiles_to_merge.add(other_profile_to_file.keys())
user_profile_to_file = find_files_from_profiles(profiles_to_merge)
# print(base_files,"\n",other_files)
# print(base_profile_to_file,"\n",other_profile_to_file,"\n",user_profile_to_file)
# print(profiles_to_merge)
for profile_name in profiles_to_merge:
aaui.UI_Info("\n\n" + _("Merging profile for %s" % profile_name))
user_file = user_profile_to_file[profile_name]
base_file = base_profile_to_file.get(profile_name, None)
other_file = None
if merge_mode == 3:
other_file = other_profile_to_file.get(profile_name, None)
if base_file == None:
if other_file == None:
continue
act([user_file, other_file, None], 2, profile_name)
else:
if other_file == None:
act([user_file, base_file, None], 2, profile_name)
else:
act([user_file, base_file, other_file], 3, profile_name)
reset_aa()
def act(files, merge_mode, merging_profile):
mergeprofiles = Merge(files)
#Get rid of common/superfluous stuff
# mergeprofiles.clear_common()
# mergeprofiles.clear_common() temporarily disabled because it crashes,
# see https://bugs.launchpad.net/apparmor/+bug/1382236
# if not args.auto:
if 1 == 1: # workaround to avoid lots of whitespace changes
if merge_mode == 3:
mergeprofiles.ask_the_questions('other', merging_profile)
mergeprofiles.clear_common()
mergeprofiles.ask_the_questions('base', merging_profile)
q = aaui.PromptQuestion()
q.title = _('Changed Local Profiles')
q.explanation = _('The following local profiles were changed. Would you like to save them?')
q.functions = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT', 'CMD_IGNORE_ENTRY']
q.default = 'CMD_VIEW_CHANGES'
q.options = [merging_profile]
q.selected = 0
ans = ''
arg = None
programs = list(mergeprofiles.user.aa.keys())
program = programs[0]
while ans != 'CMD_SAVE_CHANGES':
ans, arg = q.promptUser()
if ans == 'CMD_SAVE_CHANGES':
apparmor.aa.write_profile_ui_feedback(program)
apparmor.aa.reload_base(program)
elif ans == 'CMD_VIEW_CHANGES':
for program in programs:
apparmor.aa.original_aa[program] = apparmor.aa.deepcopy(apparmor.aa.aa[program])
#oldprofile = apparmor.serialize_profile(apparmor.original_aa[program], program, '')
newprofile = apparmor.aa.serialize_profile(mergeprofiles.user.aa[program], program, '')
apparmor.aa.display_changes_with_comments(mergeprofiles.user.filename, newprofile)
elif ans == 'CMD_IGNORE_ENTRY':
break
class Merge(object):
def __init__(self, profiles):
user, base, other = profiles
#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)
reset_aa()
#Read and parse other profile and save profile data, include data from it and reset them
if merge_mode == 3:
apparmor.aa.read_profile(other, True)
self.other = cleanprofile.Prof(other)
reset_aa()
#Read and parse user profile
apparmor.aa.read_profile(user, True)
self.user = cleanprofile.Prof(user)
def clear_common(self):
deleted = 0
if merge_mode == 3:
#Remove off the parts in other profile which are common/superfluous from user profile
user_other = cleanprofile.CleanProf(False, self.user, self.other)
deleted += user_other.compare_profiles()
#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()
if merge_mode == 3:
#Remove off the parts in other profile which are common/superfluous from base profile
base_other = cleanprofile.CleanProf(False, self.base, self.other)
deleted += base_other.compare_profiles()
def conflict_mode(self, profile, hat, allow, path, mode, new_mode, old_mode):
m = new_mode
o = old_mode
new_mode = apparmor.aa.flatten_mode(new_mode)
old_mode = apparmor.aa.flatten_mode(old_mode)
conflict_modes = set('uUpPcCiIxX')
conflict_x= (old_mode | new_mode) & conflict_modes
if conflict_x:
#We may have conflicting x modes
if conflict_x & set('x'):
conflict_x.remove('x')
if conflict_x & set('X'):
conflict_x.remove('X')
if len(conflict_x) > 1:
q = aaui.PromptQuestion()
q.headers = [_('Path'), path]
q.headers += [_('Select the appropriate mode'), '']
options = []
options.append('%s: %s' %(mode, apparmor.aa.mode_to_str_user(new_mode)))# - (old_mode & conflict_x))))
options.append('%s: %s' %(mode, apparmor.aa.mode_to_str_user(old_mode)))#(old_mode | new_mode) - (new_mode & conflict_x))))
q.options = options
q.functions = ['CMD_ALLOW', 'CMD_ABORT']
done = False
while not done:
ans, selected = q.promptUser()
if ans == 'CMD_ALLOW':
if selected == 0:
self.user.aa[profile][hat][allow]['path'][path][mode] = m#apparmor.aa.owner_flatten_mode(new_mode)#(old_mode | new_mode) - (old_mode & conflict_x)
return m
elif selected == 1:
return o
pass#self.user.aa[profile][hat][allow][path][mode] = (old_mode | new_mode) - (new_mode & conflict_x)
else:
raise AppArmorException(_('Unknown selection'))
done = True
def ask_the_questions(self, other, profile):
if other == 'other':
other = self.other
else:
other = self.base
#print(other.aa)
#Add the file-wide includes from the other profile to the user profile
done = False
options = []
for inc in other.filelist[other.filename]['include'].keys():
if not inc in self.user.filelist[self.user.filename]['include'].keys():
options.append('#include <%s>' %inc)
default_option = 1
q = aaui.PromptQuestion()
q.options = options
q.selected = default_option - 1
q.headers = [_('File includes'), _('Select the ones you wish to add')]
q.functions = ['CMD_ALLOW', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
q.default = 'CMD_ALLOW'
while not done and options:
ans, selected = q.promptUser()
if ans == 'CMD_IGNORE_ENTRY':
done = True
elif ans == 'CMD_ALLOW':
selection = options[selected]
inc = apparmor.aa.re_match_include(selection)
self.user.filelist[self.user.filename]['include'][inc] = True
options.pop(selected)
aaui.UI_Info(_('Adding %s to the file.') % selection)
elif ans == 'CMD_FINISHED':
return
sev_db = apparmor.aa.sev_db
if not sev_db:
sev_db = apparmor.severity.Severity(apparmor.aa.CONFDIR + '/severity.db', _('unknown'))
for hat in sorted(other.aa[profile].keys()):
#Add the includes from the other profile to the user profile
done = False
options = []
for inc in other.aa[profile][hat]['include'].keys():
if not inc in self.user.aa[profile][hat]['include'].keys():
options.append('#include <%s>' %inc)
default_option = 1
q = aaui.PromptQuestion()
q.options = options
q.selected = default_option - 1
q.headers = [_('File includes'), _('Select the ones you wish to add')]
q.functions = ['CMD_ALLOW', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
q.default = 'CMD_ALLOW'
while not done and options:
ans, selected = q.promptUser()
if ans == 'CMD_IGNORE_ENTRY':
done = True
elif ans == 'CMD_ALLOW':
selection = options[selected]
inc = apparmor.aa.re_match_include(selection)
deleted = apparmor.aa.delete_duplicates(self.user.aa[profile][hat], inc)
self.user.aa[profile][hat]['include'][inc] = True
options.pop(selected)
aaui.UI_Info(_('Adding %s to the file.') % selection)
if deleted:
aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
elif ans == 'CMD_FINISHED':
return
#Add the capabilities
if other.aa[profile][hat].get('capability', False): # needed until we have proper profile initialization
for cap_obj in other.aa[profile][hat]['capability'].rules:
if apparmor.aa.is_known_rule(self.user.aa[profile][hat], 'capability', cap_obj):
continue
if cap_obj.all_caps:
severity = 10
cap_txt = 'ALL'
else:
cap_txt = ' '.join(cap_obj.capability)
severity = 0
for cap in cap_obj.capability:
severity = max(severity, sev_db.rank('CAP_%s' % cap))
if cap_obj.deny:
cap_txt = 'deny %s' % cap_txt
if cap_obj.audit:
cap_txt = 'audit %s' % cap_txt
default_option = 1
options = []
newincludes = apparmor.aa.match_includes(self.user.aa[profile][hat], 'capability', cap_obj)
q = aaui.PromptQuestion()
if newincludes:
options += list(map(lambda inc: '#include <%s>' %inc, sorted(set(newincludes))))
if options:
options.append(cap_obj.get_clean())
q.options = options
q.selected = default_option - 1
q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
q.headers += [_('Capability'), cap_txt]
q.headers += [_('Severity'), severity]
audit_toggle = 0
q.functions = []
if not cap_obj.deny:
q.functions += ['CMD_ALLOW']
q.functions += ['CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
q.default = q.functions[0]
done = False
while not done:
ans, selected = q.promptUser()
# Ignore the log entry
if ans == 'CMD_IGNORE_ENTRY':
done = True
break
elif ans == 'CMD_FINISHED':
return
if ans == 'CMD_ALLOW':
selection = ''
if options:
selection = options[selected]
match = apparmor.aa.re_match_include(selection)
if match:
deleted = False
inc = match
deleted = apparmor.aa.delete_duplicates(self.user.aa[profile][hat], inc)
self.user.aa[profile][hat]['include'][inc] = True
aaui.UI_Info(_('Adding %s to profile.') % selection)
if deleted:
aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
self.user.aa[profile][hat]['capability'].add(cap_obj)
apparmor.aa.changed[profile] = True
aaui.UI_Info(_('Adding %s to profile.') % cap_obj.get_clean())
done = True
elif ans == 'CMD_DENY':
cap_obj.deny = True
cap_obj.raw_rule = None # reset raw rule after manually modifying cap_obj
self.user.aa[profile][hat]['capability'].add(cap_obj)
apparmor.aa.changed[profile] = True
aaui.UI_Info(_('Adding %s to profile.') % cap_obj.get_clean())
done = True
else:
done = False
# Process all the path entries.
for allow in ['allow', 'deny']:
for path in sorted(other.aa[profile][hat][allow]['path'].keys()):
#print(path, other.aa[profile][hat][allow]['path'][path])
mode = other.aa[profile][hat][allow]['path'][path]['mode']
if self.user.aa[profile][hat][allow]['path'].get(path, False):
mode = self.conflict_mode(profile, hat, allow, path, 'mode', other.aa[profile][hat][allow]['path'][path]['mode'], self.user.aa[profile][hat][allow]['path'][path]['mode'])
self.conflict_mode(profile, hat, allow, path, 'audit', other.aa[profile][hat][allow]['path'][path]['audit'], self.user.aa[profile][hat][allow]['path'][path]['audit'])
apparmor.aa.changed[profile] = True
continue
# Lookup modes from profile
allow_mode = set()
allow_audit = set()
deny_mode = set()
deny_audit = set()
fmode, famode, fm = apparmor.aa.rematchfrag(self.user.aa[profile][hat], 'allow', path)
if fmode:
allow_mode |= fmode
if famode:
allow_audit |= famode
cm, cam, m = apparmor.aa.rematchfrag(self.user.aa[profile][hat], 'deny', path)
if cm:
deny_mode |= cm
if cam:
deny_audit |= cam
imode, iamode, im = apparmor.aa.match_prof_incs_to_path(self.user.aa[profile][hat], 'allow', path)
if imode:
allow_mode |= imode
if iamode:
allow_audit |= iamode
cm, cam, m = apparmor.aa.match_prof_incs_to_path(self.user.aa[profile][hat], 'deny', path)
if cm:
deny_mode |= cm
if cam:
deny_audit |= cam
if deny_mode & apparmor.aamode.AA_MAY_EXEC:
deny_mode |= apparmor.aamode.ALL_AA_EXEC_TYPE
# Mask off the denied modes
mode = mode - deny_mode
# If we get an exec request from some kindof event that generates 'PERMITTING X'
# check if its already in allow_mode
# if not add ix permission
if mode & apparmor.aamode.AA_MAY_EXEC:
# Remove all type access permission
mode = mode - apparmor.aamode.ALL_AA_EXEC_TYPE
if not allow_mode & apparmor.aamode.AA_MAY_EXEC:
mode |= apparmor.aa.str_to_mode('ix')
if not mode:
continue
matches = []
if fmode:
matches += fm
if imode:
matches += im
if not apparmor.aa.mode_contains(allow_mode, mode):
default_option = 1
options = []
newincludes = []
include_valid = False
for incname in apparmor.aa.include.keys():
include_valid = False
# If already present skip
if self.user.aa[profile][hat][incname]:
continue
if incname.startswith(apparmor.aa.profile_dir):
incname = incname.replace(apparmor.aa.profile_dir+'/', '', 1)
include_valid = apparmor.aa.valid_include('', incname)
if not include_valid:
continue
cm, am, m = apparmor.aa.match_include_to_path(incname, 'allow', path)
if cm and apparmor.aa.mode_contains(cm, mode):
dm = apparmor.aa.match_include_to_path(incname, 'deny', path)[0]
# If the mode is denied
if not mode & dm:
if not list(filter(lambda s: '/**' == s, m)):
newincludes.append(incname)
# Add new includes to the options
if newincludes:
options += list(map(lambda s: '#include <%s>' % s, sorted(set(newincludes))))
# We should have literal the path in options list too
options.append(path)
# Add any the globs matching path from logprof
globs = apparmor.aa.glob_common(path)
if globs:
matches += globs
# Add any user entered matching globs
for user_glob in apparmor.aa.user_globs:
if apparmor.aa.matchliteral(user_glob, path):
matches.append(user_glob)
matches = list(set(matches))
if path in matches:
matches.remove(path)
options += apparmor.aa.order_globs(matches, path)
default_option = len(options)
sev_db.unload_variables()
sev_db.load_variables(apparmor.aa.get_profile_filename(profile))
severity = sev_db.rank(path, apparmor.aa.mode_to_str(mode))
sev_db.unload_variables()
audit_toggle = 0
owner_toggle = 0
if apparmor.aa.cfg['settings']['default_owner_prompt']:
owner_toggle = apparmor.aa.cfg['settings']['default_owner_prompt']
done = False
while not done:
q = aaui.PromptQuestion()
q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat),
_('Path'), path]
if allow_mode:
mode |= allow_mode
tail = ''
s = ''
prompt_mode = None
if owner_toggle == 0:
prompt_mode = apparmor.aa.flatten_mode(mode)
tail = ' ' + _('(owner permissions off)')
elif owner_toggle == 1:
prompt_mode = mode
elif owner_toggle == 2:
prompt_mode = allow_mode | apparmor.aa.owner_flatten_mode(mode - allow_mode)
tail = ' ' + _('(force new perms to owner)')
else:
prompt_mode = apparmor.aa.owner_flatten_mode(mode)
tail = ' ' + _('(force all rule perms to owner)')
if audit_toggle == 1:
s = apparmor.aa.mode_to_str_user(allow_mode)
if allow_mode:
s += ', '
s += 'audit ' + apparmor.aa.mode_to_str_user(prompt_mode - allow_mode) + tail
elif audit_toggle == 2:
s = 'audit ' + apparmor.aa.mode_to_str_user(prompt_mode) + tail
else:
s = apparmor.aa.mode_to_str_user(prompt_mode) + tail
q.headers += [_('Old Mode'), apparmor.aa.mode_to_str_user(allow_mode),
_('New Mode'), s]
else:
s = ''
tail = ''
prompt_mode = None
if audit_toggle:
s = 'audit'
if owner_toggle == 0:
prompt_mode = apparmor.aa.flatten_mode(mode)
tail = ' ' + _('(owner permissions off)')
elif owner_toggle == 1:
prompt_mode = mode
else:
prompt_mode = apparmor.aa.owner_flatten_mode(mode)
tail = ' ' + _('(force perms to owner)')
s = apparmor.aa.mode_to_str_user(prompt_mode)
q.headers += [_('Mode'), s]
q.headers += [_('Severity'), severity]
q.options = options
q.selected = default_option - 1
q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB',
'CMD_GLOBEXT', 'CMD_NEW', 'CMD_ABORT',
'CMD_FINISHED', 'CMD_OTHER']
q.default = 'CMD_ALLOW'
ans, selected = q.promptUser()
if ans == 'CMD_IGNORE_ENTRY':
done = True
break
elif ans == 'CMD_FINISHED':
return
if ans == 'CMD_OTHER':
aaui.UI_Important("Sorry, not implemented yet!")
# audit_toggle, owner_toggle = aaui.UI_ask_mode_toggles(audit_toggle, owner_toggle, allow_mode)
# crashes with
# audit_toggle, owner_toggle = aaui.UI_ask_mode_toggles(audit_toggle, owner_toggle, allow_mode)
# AttributeError: 'module' object has no attribute 'UI_ask_mode_toggles'
elif ans == 'CMD_USER_TOGGLE':
owner_toggle += 1
if not allow_mode and owner_toggle == 2:
owner_toggle += 1
if owner_toggle > 3:
owner_toggle = 0
elif ans == 'CMD_ALLOW':
path = options[selected]
done = True
match = apparmor.aa.re_match_include(path)
if match:
inc = match
deleted = 0
deleted = apparmor.aa.delete_duplicates(self.user.aa[profile][hat], inc)
self.user.aa[profile][hat]['include'][inc] = True
apparmor.aa.changed[profile] = True
aaui.UI_Info(_('Adding %s to profile.') % path)
if deleted:
aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
else:
if self.user.aa[profile][hat]['allow']['path'][path].get('mode', False):
mode |= self.user.aa[profile][hat]['allow']['path'][path]['mode']
deleted = []
for entry in self.user.aa[profile][hat]['allow']['path'].keys():
if path == entry:
continue
if apparmor.aa.matchregexp(path, entry):
if apparmor.aa.mode_contains(mode, self.user.aa[profile][hat]['allow']['path'][entry]['mode']):
deleted.append(entry)
for entry in deleted:
self.user.aa[profile][hat]['allow']['path'].pop(entry)
deleted = len(deleted)
if owner_toggle == 0:
mode = apparmor.aa.flatten_mode(mode)
#elif owner_toggle == 1:
# mode = mode
elif owner_toggle == 2:
mode = allow_mode | apparmor.aa.owner_flatten_mode(mode - allow_mode)
elif owner_toggle == 3:
mode = apparmor.aa.owner_flatten_mode(mode)
if not self.user.aa[profile][hat]['allow'].get(path, False):
self.user.aa[profile][hat]['allow']['path'][path]['mode'] = self.user.aa[profile][hat]['allow']['path'][path].get('mode', set()) | mode
tmpmode = set()
if audit_toggle == 1:
tmpmode = mode- allow_mode
elif audit_toggle == 2:
tmpmode = mode
self.user.aa[profile][hat]['allow']['path'][path]['audit'] = self.user.aa[profile][hat]['allow']['path'][path].get('audit', set()) | tmpmode
apparmor.aa.changed[profile] = True
aaui.UI_Info(_('Adding %(path)s %(mode)s to profile') % { 'path': path, 'mode': apparmor.aa.mode_to_str_user(mode) })
if deleted:
aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
elif ans == 'CMD_DENY':
path = options[selected].strip()
# Add new entry?
self.user.aa[profile][hat]['deny']['path'][path]['mode'] = self.user.aa[profile][hat]['deny']['path'][path].get('mode', set()) | (mode - allow_mode)
self.user.aa[profile][hat]['deny']['path'][path]['audit'] = self.user.aa[profile][hat]['deny']['path'][path].get('audit', set())
apparmor.aa.changed[profile] = True
done = True
elif ans == 'CMD_NEW':
arg = options[selected]
if not apparmor.aa.re_match_include(arg):
ans = aaui.UI_GetString(_('Enter new path: '), arg)
# if ans:
# if not matchliteral(ans, path):
# ynprompt = _('The specified path does not match this log entry:\n\n Log Entry: %s\n Entered Path: %s\nDo you really want to use this path?') % (path,ans)
# key = aaui.UI_YesNo(ynprompt, 'n')
# if key == 'n':
# continue
apparmor.aa.user_globs.append(ans)
options.append(ans)
default_option = len(options)
elif ans == 'CMD_GLOB':
newpath = options[selected].strip()
if not apparmor.aa.re_match_include(newpath):
newpath = apparmor.aa.glob_path(newpath)
if newpath not in options:
options.append(newpath)
default_option = len(options)
else:
default_option = options.index(newpath) + 1
elif ans == 'CMD_GLOBEXT':
newpath = options[selected].strip()
if not apparmor.aa.re_match_include(newpath):
newpath = apparmor.aa.glob_path_withext(newpath)
if newpath not in options:
options.append(newpath)
default_option = len(options)
else:
default_option = options.index(newpath) + 1
elif re.search('\d', ans):
default_option = ans
if 1 == 1: # avoid whitespace change
if other.aa[profile][hat].get('network', False): # needed until we have proper profile initialization
for net_obj in other.aa[profile][hat]['network'].rules:
# severity handling for net toggles goes here
if apparmor.aa.is_known_rule(self.user.aa[profile][hat], 'network', net_obj):
continue
if net_obj.all_domains:
family = 'ALL'
else:
family = net_obj.domain
if net_obj.all_type_or_protocols:
sock_type = 'ALL'
else:
sock_type = net_obj.type_or_protocol
default_option = 1
options = []
newincludes = apparmor.aa.match_includes(self.user.aa[profile][hat], 'network', net_obj)
q = aaui.PromptQuestion()
if newincludes:
options += list(map(lambda s: '#include <%s>'%s, sorted(set(newincludes))))
if True:#options:
options.append(net_obj.get_clean())
q.options = options
q.selected = default_option - 1
audit = ''
if net_obj.audit:
audit = 'audit '
q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
q.headers += [_('Network Family'), audit + family]
q.headers += [_('Socket Type'), sock_type]
q.functions = available_buttons(net_obj)
q.default = q.functions[0]
done = False
while not done:
ans, selected = q.promptUser()
if ans == 'CMD_IGNORE_ENTRY':
done = True
break
elif ans == 'CMD_FINISHED':
return
if ans.startswith('CMD_AUDIT'):
if ans == 'CMD_AUDIT_NEW':
net_obj.audit = True
net_obj.raw_rule = None
audit = 'audit '
else:
net_obj.audit = False
net_obj.raw_rule = None
audit = ''
q.functions = available_buttons(net_obj)
options[len(options) - 1] = net_obj.get_clean()
q.options = options
q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat)]
q.headers += [_('Network Family'), audit + family]
q.headers += [_('Socket Type'), sock_type]
elif ans == 'CMD_ALLOW':
#print(options, selected)
selection = options[selected]
done = True
if apparmor.aa.re_match_include(selection): #re.search('#include\s+<.+>$', selection):
inc = apparmor.aa.re_match_include(selection) #re.search('#include\s+<(.+)>$', selection).groups()[0]
deleted = 0
deleted = apparmor.aa.delete_duplicates(self.user.aa[profile][hat], inc)
self.user.aa[profile][hat]['include'][inc] = True
apparmor.aa.changed[profile] = True
aaui.UI_Info(_('Adding %s to profile') % selection)
if deleted:
aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
else:
self.user.aa[profile][hat]['network'].add(net_obj)
apparmor.aa.changed[profile] = True
aaui.UI_Info(_('Adding network access %(family)s %(type)s to profile.') % { 'family': family, 'type': sock_type })
elif ans == 'CMD_DENY':
done = True
net_obj.deny = True
net_obj.raw_rule = None
self.user.aa[profile][hat]['network'].add(net_obj)
apparmor.aa.changed[profile] = True
aaui.UI_Info(_('Denying network access %(family)s %(type)s to profile') % { 'family': family, 'type': sock_type })
else:
done = False
if __name__ == '__main__':
main()