mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 08:24:42 +01:00
First set of tools in their alpha release, logprof and genprof are pre-bleeding edge so dont hurt yourself or worse your distro.
This commit is contained in:
parent
1fb521418d
commit
5490dddbda
14 changed files with 703 additions and 236 deletions
54
Tools/aa-audit.py
Normal file
54
Tools/aa-audit.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
#!/usr/bin/python
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
|
||||
import apparmor.aa as apparmor
|
||||
|
||||
parser = argparse.ArgumentParser(description='Switch the given programs to audit mode')
|
||||
parser.add_argument('-d', type=str, help='path to profiles')
|
||||
parser.add_argument('program', type=str, nargs='+', help='name of program to hswitch to audit mode')
|
||||
args = parser.parse_args()
|
||||
|
||||
profiling = args.program
|
||||
profiledir = args.d
|
||||
|
||||
if profiledir:
|
||||
apparmor.profile_dir = apparmor.get_full_path(profiledir)
|
||||
if not os.path.isdir(apparmor.profile_dir):
|
||||
raise apparmor.AppArmorException("Can't find AppArmor profiles in %s." %profiledir)
|
||||
|
||||
for p in profiling:
|
||||
if not p:
|
||||
continue
|
||||
|
||||
program = None
|
||||
if os.path.exists(p):
|
||||
program = apparmor.get_full_path(p).strip()
|
||||
else:
|
||||
which = apparmor.which(p)
|
||||
if which:
|
||||
program = apparmor.get_full_path(which)
|
||||
|
||||
if os.path.exists(program):
|
||||
apparmor.read_profiles()
|
||||
filename = apparmor.get_profile_filename(program)
|
||||
|
||||
if not os.path.isfile(filename) or apparmor.is_skippable_file(filename):
|
||||
continue
|
||||
|
||||
sys.stdout.write(_('Setting %s to audit mode.\n')%program)
|
||||
|
||||
apparmor.set_profile_flags(filename, 'audit')
|
||||
|
||||
cmd_info = apparmor.cmd(['cat', filename, '|', parser, '-I%s'%apparmor.profile_dir, '-R 2>&1', '1>/dev/null'])
|
||||
if cmd_info[0] != 0:
|
||||
raise apparmor.AppArmorException(cmd_info[1])
|
||||
else:
|
||||
if '/' not in p:
|
||||
apparmor.UI_Info(_("Can't find %s in the system path list. If the name of the application is correct, please run 'which %s' as a user with correct PATH environment set up in order to find the fully-qualified path.")%(p, p))
|
||||
else:
|
||||
apparmor.UI_Info(_("%s does not exist, please double-check the path.")%p)
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
53
Tools/aa-autodep.py
Normal file
53
Tools/aa-autodep.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/python
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
|
||||
import apparmor.aa as apparmor
|
||||
|
||||
parser = argparse.ArgumentParser(description='Disable the profile for the given programs')
|
||||
parser.add_argument('--force', type=str, help='path to profiles')
|
||||
parser.add_argument('-d', type=str, help='path to profiles')
|
||||
parser.add_argument('program', type=str, nargs='+', help='name of program to have profile disabled')
|
||||
args = parser.parse_args()
|
||||
|
||||
force = args.force
|
||||
profiledir = args.d
|
||||
profiling = args.program
|
||||
|
||||
aa_mountpoint = apparmor.check_for_apparmor()
|
||||
|
||||
if profiledir:
|
||||
apparmor.profile_dir = apparmor.get_full_path(profiledir)
|
||||
if not os.path.isdir(apparmor.profile_dir):
|
||||
raise apparmor.AppArmorException("Can't find AppArmor profiles in %s." %profiledir)
|
||||
|
||||
for p in profiling:
|
||||
if not p:
|
||||
continue
|
||||
|
||||
program = None
|
||||
if os.path.exists(p):
|
||||
program = apparmor.get_full_path(p).strip()
|
||||
else:
|
||||
which = apparmor.which(p)
|
||||
if which:
|
||||
program = apparmor.get_full_path(which)
|
||||
|
||||
apparmor.check_qualifiers(program)
|
||||
|
||||
if os.path.exists(program):
|
||||
if os.path.exists(apparmor.get_profile_filename(program) and not force):
|
||||
apparmor.UI_Info('Profile for %s already exists - skipping.'%program)
|
||||
else:
|
||||
apparmor.autodep(program)
|
||||
if aa_mountpoint:
|
||||
apparmor.reload(program)
|
||||
else:
|
||||
if '/' not in p:
|
||||
apparmor.UI_Info(_("Can't find %s in the system path list. If the name of the application is correct, please run 'which %s' as a user with correct PATH environment set up in order to find the fully-qualified path.")%(p, p))
|
||||
else:
|
||||
apparmor.UI_Info(_("%s does not exist, please double-check the path.")%p)
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
55
Tools/aa-complain.py
Normal file
55
Tools/aa-complain.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/python
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
|
||||
import apparmor.aa as apparmor
|
||||
|
||||
parser = argparse.ArgumentParser(description='Switch the given program to complain mode')
|
||||
parser.add_argument('-d', type=str, help='path to profiles')
|
||||
parser.add_argument('program', type=str, nargs='+', help='name of program to switch to complain mode')
|
||||
args = parser.parse_args()
|
||||
|
||||
profiling = args.program
|
||||
profiledir = args.d
|
||||
|
||||
if profiledir:
|
||||
apparmor.profile_dir = apparmor.get_full_path(profiledir)
|
||||
if not os.path.isdir(apparmor.profile_dir):
|
||||
raise apparmor.AppArmorException("Can't find AppArmor profiles in %s." %profiledir)
|
||||
|
||||
for p in profiling:
|
||||
if not p:
|
||||
continue
|
||||
|
||||
program = None
|
||||
if os.path.exists(p):
|
||||
program = apparmor.get_full_path(p).strip()
|
||||
else:
|
||||
which = apparmor.which(p)
|
||||
if which:
|
||||
program = apparmor.get_full_path(which)
|
||||
|
||||
if os.path.exists(program):
|
||||
apparmor.read_profiles()
|
||||
filename = apparmor.get_profile_filename(program)
|
||||
|
||||
if not os.path.isfile(filename) or apparmor.is_skippable_file(filename):
|
||||
continue
|
||||
|
||||
sys.stdout.write(_('Setting %s to complain mode.\n')%program)
|
||||
|
||||
apparmor.set_profile_flags(filename, 'complain')
|
||||
|
||||
cmd_info = apparmor.cmd(['cat', filename, '|', parser, '-I%s'%apparmor.profile_dir, '-R 2>&1', '1>/dev/null'])
|
||||
if cmd_info[0] != 0:
|
||||
raise apparmor.AppArmorException(cmd_info[1])
|
||||
else:
|
||||
if '/' not in p:
|
||||
apparmor.UI_Info(_("Can't find %s in the system path list. If the name of the application is correct, please run 'which %s' as a user with correct PATH environment set up in order to find the fully-qualified path.")%(p, p))
|
||||
else:
|
||||
apparmor.UI_Info(_("%s does not exist, please double-check the path.")%p)
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
||||
|
67
Tools/aa-disable.py
Normal file
67
Tools/aa-disable.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
#!/usr/bin/python
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
|
||||
import apparmor.aa as apparmor
|
||||
|
||||
parser = argparse.ArgumentParser(description='Disable the profile for the given programs')
|
||||
parser.add_argument('-d', type=str, help='path to profiles')
|
||||
parser.add_argument('program', type=str, nargs='+', help='name of program to have profile disabled')
|
||||
args = parser.parse_args()
|
||||
|
||||
profiledir = args.d
|
||||
profiling = args.program
|
||||
|
||||
if profiledir:
|
||||
apparmor.profile_dir = apparmor.get_full_path(profiledir)
|
||||
if not os.path.isdir(apparmor.profile_dir):
|
||||
raise apparmor.AppArmorException("Can't find AppArmor profiles in %s." %profiledir)
|
||||
|
||||
disabledir = apparmor.profile_dir+'/disable'
|
||||
if not os.path.isdir(disabledir):
|
||||
raise apparmor.AppArmorException("Can't find AppArmor disable directorys %s." %disabledir)
|
||||
|
||||
for p in profiling:
|
||||
if not p:
|
||||
continue
|
||||
|
||||
program = None
|
||||
if os.path.exists(p):
|
||||
program = apparmor.get_full_path(p).strip()
|
||||
else:
|
||||
which = apparmor.which(p)
|
||||
if which:
|
||||
program = apparmor.get_full_path(which)
|
||||
|
||||
if os.path.exists(program):
|
||||
apparmor.read_profiles()
|
||||
filename = apparmor.get_profile_filename(program)
|
||||
|
||||
if not os.path.isfile(filename) or apparmor.is_skippable_file(filename):
|
||||
continue
|
||||
|
||||
bname = os.path.basename(filename)
|
||||
if not bname:
|
||||
apparmor.AppArmorException(_('Unable to find basename for %s.')%filename)
|
||||
|
||||
sys.stdout.write(_('Disabling %s.\n')%program)
|
||||
|
||||
link = '%s/%s'%(disabledir, bname)
|
||||
if not os.path.exists(link):
|
||||
try:
|
||||
os.symlink(filename, link)
|
||||
except:
|
||||
raise apparmor.AppArmorException('Could not create %s symlink to %s.'%(link, filename))
|
||||
|
||||
cmd_info = apparmor.cmd(['cat', filename, '|', parser, '-I%s'%apparmor.profile_dir, '-R 2>&1', '1>/dev/null'])
|
||||
if cmd_info[0] != 0:
|
||||
raise apparmor.AppArmorException(cmd_info[1])
|
||||
else:
|
||||
if '/' not in p:
|
||||
apparmor.UI_Info(_("Can't find %s in the system path list. If the name of the application is correct, please run 'which %s' as a user with correct PATH environment set up in order to find the fully-qualified path.")%(p, p))
|
||||
else:
|
||||
apparmor.UI_Info(_("%s does not exist, please double-check the path.")%p)
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
67
Tools/aa-enforce.py
Normal file
67
Tools/aa-enforce.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
#!/usr/bin/python
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
|
||||
import apparmor.aa as apparmor
|
||||
|
||||
parser = argparse.ArgumentParser(description='Switch the given program to enforce mode')
|
||||
parser.add_argument('-d', type=str, help='path to profiles')
|
||||
parser.add_argument('program', type=str, nargs='+', help='name of program to switch to enforce mode')
|
||||
args = parser.parse_args()
|
||||
|
||||
profiledir = args.d
|
||||
profiling = args.program
|
||||
|
||||
if profiledir:
|
||||
apparmor.profile_dir = apparmor.get_full_path(profiledir)
|
||||
if not os.path.isdir(apparmor.profile_dir):
|
||||
raise apparmor.AppArmorException("Can't find AppArmor profiles in %s." %profiledir)
|
||||
|
||||
for p in profiling:
|
||||
if not p:
|
||||
continue
|
||||
|
||||
program = None
|
||||
if os.path.exists(p):
|
||||
program = apparmor.get_full_path(p).strip()
|
||||
else:
|
||||
which = apparmor.which(p)
|
||||
if which:
|
||||
program = apparmor.get_full_path(which)
|
||||
|
||||
if os.path.exists(program):
|
||||
apparmor.read_profiles()
|
||||
filename = apparmor.get_profile_filename(program)
|
||||
|
||||
if not os.path.isfile(filename) or apparmor.is_skippable_file(filename):
|
||||
continue
|
||||
|
||||
sys.stdout.write(_('Setting %s to enforce mode.\n')%program)
|
||||
|
||||
apparmor.set_profile_flags(filename, '')
|
||||
|
||||
# Remove symlink from profile_dir/force-complain
|
||||
complainlink = filename
|
||||
complainlink = re.sub('^%s'%apparmor.profile_dir, '%s/force-complain'%apparmor.profile_dir, complainlink)
|
||||
if os.path.exists(complainlink):
|
||||
os.remove(complainlink)
|
||||
|
||||
# remove symlink in profile_dir/disable
|
||||
disablelink = filename
|
||||
disablelink = re.sub('^%s'%apparmor.profile_dir, '%s/disable'%apparmor.profile_dir, disablelink)
|
||||
if os.path.exists(disablelink):
|
||||
os.remove(disablelink)
|
||||
|
||||
cmd_info = apparmor.cmd(['cat', filename, '|', parser, '-I%s'%apparmor.profile_dir, '-R 2>&1', '1>/dev/null'])
|
||||
if cmd_info[0] != 0:
|
||||
raise apparmor.AppArmorException(cmd_info[1])
|
||||
else:
|
||||
if '/' not in p:
|
||||
apparmor.UI_Info(_("Can't find %s in the system path list. If the name of the application is correct, please run 'which %s' as a user with correct PATH environment set up in order to find the fully-qualified path.")%(p, p))
|
||||
else:
|
||||
apparmor.UI_Info(_("%s does not exist, please double-check the path.")%p)
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
||||
|
148
Tools/aa-genprof.py
Normal file
148
Tools/aa-genprof.py
Normal file
|
@ -0,0 +1,148 @@
|
|||
#!/usr/bin/python
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
import re
|
||||
import atexit
|
||||
import argparse
|
||||
|
||||
import apparmor.aa as apparmor
|
||||
|
||||
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 not value:
|
||||
return
|
||||
with open(path, 'w') as f_out:
|
||||
f_out.write(str(value))
|
||||
|
||||
def last_audit_entry_time():
|
||||
out = subprocess.check_output(['tail', '-1', '/var/log/audit/audit.log'], shell=True)
|
||||
logmark = None
|
||||
if re.search('^*msg\=audit\((\d+\.\d+\:\d+).*\).*$', out):
|
||||
logmark = re.search('^*msg\=audit\((\d+\.\d+\:\d+).*\).*$', out).groups()[0]
|
||||
else:
|
||||
logmark = ''
|
||||
return logmark
|
||||
|
||||
def restore_ratelimit():
|
||||
sysctl_write(ratelimit_sysctl, ratelimit_saved)
|
||||
|
||||
parser = argparse.ArgumentParser(description='Generate profile for the given program')
|
||||
parser.add_argument('-d', type=str, help='path to profiles')
|
||||
parser.add_argument('-f', type=str, help='path to logfile')
|
||||
parser.add_argument('program', type=str, help='name of program to profile')
|
||||
args = parser.parse_args()
|
||||
|
||||
profiling = args.program
|
||||
profiledir = args.d
|
||||
filename = args.f
|
||||
|
||||
aa_mountpoint = apparmor.check_for_apparmor()
|
||||
if not aa_mountpoint:
|
||||
raise apparmor.AppArmorException(_('AppArmor seems to have not been started. Please enable AppArmor and try again.'))
|
||||
|
||||
if profiledir:
|
||||
apparmor.profile_dir = apparmor.get_full_path(profiledir)
|
||||
if not os.path.isdir(apparmor.profile_dir):
|
||||
raise apparmor.AppArmorException("Can't find AppArmor profiles in %s." %profiledir)
|
||||
|
||||
|
||||
# if not profiling:
|
||||
# profiling = apparmor.UI_GetString(_('Please enter the program to profile: '), '')
|
||||
# if profiling:
|
||||
# profiling = profiling.strip()
|
||||
# else:
|
||||
# sys.exit(0)
|
||||
|
||||
program = None
|
||||
#if os.path.exists(apparmor.which(profiling.strip())):
|
||||
if os.path.exists(profiling):
|
||||
program = apparmor.get_full_path(profiling)
|
||||
else:
|
||||
if '/' not in profiling:
|
||||
which = apparmor.which(profiling)
|
||||
if which:
|
||||
program = apparmor.get_full_path(which)
|
||||
|
||||
if not program or not os.path.exists(program):
|
||||
if '/' not in profiling:
|
||||
raise apparmor.AppArmorException(_("Can't find %s in the system path list. If the name of the application is correct, please run 'which %s' in another window in order to find the fully-qualified path.") %(profiling, profiling))
|
||||
else:
|
||||
raise apparmor.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)
|
||||
|
||||
apparmor.loadincludes()
|
||||
|
||||
profile_filename = apparmor.get_profile_filename(program)
|
||||
if os.path.exists(profile_filename):
|
||||
apparmor.helpers[program] = apparmor.get_profile_flags(profile_filename)
|
||||
else:
|
||||
apparmor.autodep(program)
|
||||
apparmor.helpers[program] = 'enforce'
|
||||
|
||||
if apparmor.helpers[program] == 'enforce':
|
||||
apparmor.complain(program)
|
||||
apparmor.reload(program)
|
||||
|
||||
# When reading from syslog, it is possible to hit the default kernel
|
||||
# printk ratelimit. This will result in audit entries getting skipped,
|
||||
# making profile generation inaccurate. When using genprof, disable
|
||||
# the printk ratelimit, and restore it on exit.
|
||||
ratelimit_sysctl = '/proc/sys/kernel/printk_ratelimit'
|
||||
ratelimit_saved = sysctl_read(ratelimit_sysctl)
|
||||
sysctl_write(ratelimit_sysctl, 0)
|
||||
|
||||
atexit.register(restore_ratelimit)
|
||||
|
||||
apparmor.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:\nhttp://wiki.apparmor.net/index.php/Profiles'))
|
||||
|
||||
apparmor.UI_Important(_('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.'))
|
||||
|
||||
syslog = True
|
||||
logmark = ''
|
||||
done_profiling = False
|
||||
|
||||
if os.path.exists('/var/log/audit/audit.log'):
|
||||
syslog = False
|
||||
|
||||
passno = 0
|
||||
while not done_profiling:
|
||||
if syslog:
|
||||
logmark = subprocess.check_output(['date | md5sum'], shell=True)
|
||||
logmark = logmark.decode('ascii').strip()
|
||||
logmark = re.search('^([0-9a-f]+)', logmark).groups()[0]
|
||||
t=subprocess.call("%s -p kern.warn 'GenProf: %s'"%(apparmor.logger, logmark), shell=True)
|
||||
|
||||
else:
|
||||
logmark = last_audit_entry_time()
|
||||
|
||||
q=apparmor.hasher()
|
||||
q['headers'] = [_('Profiling'), program]
|
||||
q['functions'] = ['CMD_SCAN', 'CMD_FINISHED']
|
||||
q['default'] = 'CMD_SCAN'
|
||||
ans, arg = apparmor.UI_PromptUser(q, 'noexit')
|
||||
|
||||
if ans == 'CMD_SCAN':
|
||||
lp_ret = apparmor.do_logprof_pass(logmark, passno)
|
||||
passno += 1
|
||||
if lp_ret == 'FINISHED':
|
||||
done_profiling = True
|
||||
else:
|
||||
done_profiling = True
|
||||
|
||||
for p in sorted(apparmor.helpers.keys()):
|
||||
if apparmor.helpers[p] == 'enforce':
|
||||
enforce(p)
|
||||
reload(p)
|
||||
|
||||
apparmor.UI_Info(_('\nReloaded AppArmor profiles in enforce mode.'))
|
||||
apparmor.UI_Info(_('\nPlease consider contributing your new profile!\nSee the following wiki page for more information:\nhttp://wiki.apparmor.net/index.php/Profiles\n'))
|
||||
apparmor.UI_Info(_('Finished generating profile for %s.')%program)
|
||||
sys.exit(0)
|
|
@ -1,15 +1,30 @@
|
|||
#!/usr/bin/python
|
||||
import sys
|
||||
|
||||
sys.path.append('../')
|
||||
import apparmor.aa
|
||||
import apparmor.aa as apparmor
|
||||
import os
|
||||
import argparse
|
||||
|
||||
logmark = ''
|
||||
parser = argparse.ArgumentParser(description='Process log entries to generate profiles')
|
||||
parser.add_argument('-d', type=str, help='path to profiles')
|
||||
parser.add_argument('-f', type=str, help='path to logfile')
|
||||
parser.add_argument('-m', type=str, help='mark in the log to start processing after')
|
||||
args = parser.parse_args()
|
||||
|
||||
apparmor.aa.loadincludes()
|
||||
profiledir = args.d
|
||||
filename = args.f
|
||||
logmark = args.m or ''
|
||||
|
||||
apparmor.aa.do_logprof_pass(logmark)
|
||||
aa_mountpoint = apparmor.check_for_apparmor()
|
||||
if not aa_mountpoint:
|
||||
raise apparmor.AppArmorException(_('AppArmor seems to have not been started. Please enable AppArmor and try again.'))
|
||||
|
||||
if profiledir:
|
||||
apparmor.profiledir = apparmor.get_full_path(profiledir)
|
||||
if not os.path.isdir(apparmor.profiledir):
|
||||
raise apparmor.AppArmorException("Can't find AppArmor profiles in %s." %profiledir)
|
||||
|
||||
apparmor.loadincludes()
|
||||
|
||||
apparmor.do_logprof_pass(logmark)
|
||||
|
||||
sys.exit(0)
|
||||
|
|
69
Tools/aa-unconfined.py
Normal file
69
Tools/aa-unconfined.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
#!/usr/bin/python
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import argparse
|
||||
|
||||
import apparmor.aa as apparmor
|
||||
|
||||
parser = argparse.ArgumentParser(description='')
|
||||
parser.add_argument('--paranoid', type=str)
|
||||
args = parser.parse_args()
|
||||
|
||||
paranoid = args.paranoid
|
||||
|
||||
aa_mountpoint = apparmor.check_for_apparmor()
|
||||
if not aa_mountpoint:
|
||||
raise apparmor.AppArmorException(_('AppArmor seems to have not been started. Please enable AppArmor and try again.'))
|
||||
|
||||
pids = []
|
||||
if paranoid:
|
||||
pids = list(filter(lambda x: re.search('^\d+$', x), apparmor.get_subdirectories('/proc')))
|
||||
else:
|
||||
regex_tcp_udp = re.compile('^(tcp|udp)\s+\d+\s+\d+\s+\S+\:(\d+)\s+\S+\:(\*|\d+)\s+(LISTEN|\s+)\s+(\d+)\/(\S+)')
|
||||
output = apparmor.cmd(['netstat','-nlp'])[1].split('\n')
|
||||
for line in output:
|
||||
match = regex_tcp_udp.search(line)
|
||||
if match:
|
||||
pids.append(match.groups()[4])
|
||||
# We can safely remove duplicate pid's?
|
||||
pids = list(map(lambda x: int(x), set(pids)))
|
||||
|
||||
for pid in sorted(pids):
|
||||
try:
|
||||
prog = os.readlink('/proc/%s/exe'%pid)
|
||||
except:
|
||||
continue
|
||||
attr = None
|
||||
if os.path.exists('/proc/%s/attr/current'%pid):
|
||||
with apparmor.open_file_read('/proc/%s/attr/current'%pid) as current:
|
||||
for line in current:
|
||||
if line.startswith('/') or line.startswith('null'):
|
||||
attr = line.strip()
|
||||
|
||||
cmdline = apparmor.cmd(['cat', '/proc/%s/cmdline'%pid])[1]
|
||||
pname = cmdline.split('\0')[0]
|
||||
if '/' in pname and pname != prog:
|
||||
pname = '(%s)'%pname
|
||||
else:
|
||||
pname = ''
|
||||
if not attr:
|
||||
if re.search('^(/usr)?/bin/(python|perl|bash)', prog):
|
||||
cmdline = re.sub('\0', ' ', cmdline)
|
||||
cmdline = re.sub('\s+$', '', cmdline).strip()
|
||||
sys.stdout.write(_('%s %s (%s) not confined\n')%(pid, prog, cmdline))
|
||||
else:
|
||||
if pname and pname[-1] == ')':
|
||||
pname += ' '
|
||||
sys.stdout.write(_('%s %s %snot confined\n')%(pid, prog, pname))
|
||||
else:
|
||||
if re.search('^(/usr)?/bin/(python|perl|bash)', prog):
|
||||
cmdline = re.sub('\0', ' ', cmdline)
|
||||
cmdline = re.sub('\s+$', '', cmdline).strip()
|
||||
sys.stdout.write(_("%s %s (%s) confined by '%s'\n")%(pid, prog, cmdline, attr))
|
||||
else:
|
||||
if pname and pname[-1] == ')':
|
||||
pname += ' '
|
||||
sys.stdout.write(_("%s %s %sconfined by '%s'\n")%(pid, prog, pname, attr))
|
||||
|
||||
sys.exit(0)
|
|
@ -18,7 +18,7 @@ import apparmor.logparser
|
|||
import apparmor.severity
|
||||
import LibAppArmor
|
||||
|
||||
from apparmor.common import (AppArmorException, error, debug, msg,
|
||||
from apparmor.common import (AppArmorException, error, debug, msg, cmd,
|
||||
open_file_read, valid_path,
|
||||
hasher, open_file_write, convert_regexp, DebugLogger)
|
||||
|
||||
|
@ -228,7 +228,7 @@ def complain(path):
|
|||
set_profile_flags(prof_filename, 'complain')
|
||||
|
||||
def enforce(path):
|
||||
"""Sets the profile to complain mode if it exists"""
|
||||
"""Sets the profile to enforce mode if it exists"""
|
||||
prof_filename, name = name_to_prof_filename(path)
|
||||
if not prof_filename :
|
||||
fatal_error("Can't find %s" % path)
|
||||
|
@ -241,7 +241,9 @@ def head(file):
|
|||
if os.path.isfile(file):
|
||||
with open_file_read(file) as f_in:
|
||||
first = f_in.readline().rstrip()
|
||||
return first
|
||||
return first
|
||||
else:
|
||||
raise AppArmorException('Unable to read first line from: %s : File Not Found' %file)
|
||||
|
||||
def get_output(params):
|
||||
"""Returns the return code output by running the program with the args given in the list"""
|
||||
|
@ -484,7 +486,7 @@ def autodep(bin_name, pname=''):
|
|||
if not bin_name and pname.startswith('/'):
|
||||
bin_name = pname
|
||||
if not repo_cfg and not cfg['repository'].get('url', False):
|
||||
repo_conf = apparmor.config.Config('shell')
|
||||
repo_conf = apparmor.config.Config('shell', CONFDIR)
|
||||
repo_cfg = repo_conf.read_config('repository.conf')
|
||||
if not repo_cfg.get('repository', False) or repo_cfg['repository']['enabled'] == 'later':
|
||||
UI_ask_to_enable_repo()
|
||||
|
@ -511,6 +513,16 @@ def autodep(bin_name, pname=''):
|
|||
filelist.file = hasher()
|
||||
filelist[file][include]['tunables/global'] = True
|
||||
write_profile_ui_feedback(pname)
|
||||
|
||||
def get_profile_flags(filename):
|
||||
flags = 'enforce'
|
||||
with open_file_read(filename) as f_in:
|
||||
for line in f_in:
|
||||
if RE_PROFILE_START.search(line):
|
||||
flags = RE_PROFILE_START.search(line).groups()[6]
|
||||
return flags
|
||||
return flags
|
||||
|
||||
|
||||
def set_profile_flags(prof_filename, newflags):
|
||||
"""Reads the old profile file and updates the flags accordingly"""
|
||||
|
@ -821,6 +833,7 @@ def handle_children(profile, hat, root):
|
|||
else:
|
||||
typ = entry.pop(0)
|
||||
if typ == 'fork':
|
||||
# If type is fork then we (should) have pid, profile and hat
|
||||
pid, p, h = entry[:3]
|
||||
if not regex_nullcomplain.search(p) and not regex_nullcomplain.search(h):
|
||||
profile = p
|
||||
|
@ -830,6 +843,7 @@ def handle_children(profile, hat, root):
|
|||
else:
|
||||
profile_changes[pid] = profile
|
||||
elif typ == 'unknown_hat':
|
||||
# If hat is not known then we (should) have pid, profile, hat, mode and unknown hat in entry
|
||||
pid, p, h, aamode, uhat = entry[:5]
|
||||
if not regex_nullcomplain.search(p):
|
||||
profile = p
|
||||
|
@ -882,9 +896,11 @@ def handle_children(profile, hat, root):
|
|||
elif ans == 'CMD_USEDEFAULT':
|
||||
hat = default_hat
|
||||
elif ans == 'CMD_DENY':
|
||||
# As unknown hat is denied no entry for it should be made
|
||||
return None
|
||||
|
||||
elif typ == 'capability':
|
||||
# If capability then we (should) have pid, profile, hat, program, mode, capability
|
||||
pid, p, h, prog, aamode, capability = entry[:6]
|
||||
if not regex_nullcomplain.search(p) and not regex_nullcomplain.search(h):
|
||||
profile = p
|
||||
|
@ -894,6 +910,7 @@ def handle_children(profile, hat, root):
|
|||
prelog[aamode][profile][hat]['capability'][capability] = True
|
||||
|
||||
elif typ == 'path' or typ == 'exec':
|
||||
# If path or exec then we (should) have pid, profile, hat, program, mode, details and to_name
|
||||
pid, p, h, prog, aamode, mode, detail, to_name = entry[:8]
|
||||
if not mode:
|
||||
mode = set()
|
||||
|
@ -1086,6 +1103,7 @@ def handle_children(profile, hat, root):
|
|||
default = None
|
||||
if 'p' in options and os.path.exists(get_profile_filename(exec_target)):
|
||||
default = 'CMD_px'
|
||||
sys.stdout.write('Target profile exists: %s\n' %get_profile_filename(exec_target))
|
||||
elif 'i' in options:
|
||||
default = 'CMD_ix'
|
||||
elif 'c' in options:
|
||||
|
@ -1309,6 +1327,7 @@ def handle_children(profile, hat, root):
|
|||
return None
|
||||
|
||||
elif typ == 'netdomain':
|
||||
# If netdomain we (should) have pid, profile, hat, program, mode, network family, socket type and protocol
|
||||
pid, p, h, prog, aamode, family, sock_type, protocol = entry[:8]
|
||||
|
||||
if not regex_nullcomplain.search(p) and not regex_nullcomplain.search(h):
|
||||
|
@ -1717,15 +1736,17 @@ def ask_the_questions():
|
|||
else:
|
||||
if aa[profile][hat]['allow']['path'][path].get('mode', False):
|
||||
mode |= aa[profile][hat]['allow']['path'][path]['mode']
|
||||
deleted = 0
|
||||
deleted = []
|
||||
for entry in aa[profile][hat]['allow']['path'].keys():
|
||||
if path == entry:
|
||||
continue
|
||||
|
||||
if matchregexp(path, entry):
|
||||
if mode_contains(mode, aa[profile][hat]['allow']['path'][entry]['mode']):
|
||||
aa[profile][hat]['allow']['path'].pop(entry)
|
||||
deleted += 1
|
||||
deleted.append(entry)
|
||||
for entry in deleted:
|
||||
aa[profile][hat]['allow']['path'].pop(entry)
|
||||
deleted = len(deleted)
|
||||
|
||||
if owner_toggle == 0:
|
||||
mode = flatten_mode(mode)
|
||||
|
@ -1948,13 +1969,15 @@ def delete_net_duplicates(netrules, incnetrules):
|
|||
return deleted
|
||||
|
||||
def delete_cap_duplicates(profilecaps, inccaps):
|
||||
deleted = 0
|
||||
deleted = []
|
||||
if profilecaps and inccaps:
|
||||
for capname in profilecaps.keys():
|
||||
if inccaps[capname].get('set', False) == 1:
|
||||
profilecaps.pop(capname)
|
||||
deleted += 1
|
||||
return deleted
|
||||
deleted.append(capname)
|
||||
for capname in deleted:
|
||||
profilecaps.pop(capname)
|
||||
|
||||
return len(deleted)
|
||||
|
||||
def delete_path_duplicates(profile, incname, allow):
|
||||
deleted = []
|
||||
|
@ -2047,7 +2070,7 @@ def match_net_includes(profile, family, nettype):
|
|||
|
||||
return newincludes
|
||||
|
||||
def do_logprof_pass(logmark='', pid=pid):
|
||||
def do_logprof_pass(logmark='', passno=0, pid=pid):
|
||||
# set up variables for this pass
|
||||
t = hasher()
|
||||
# transitions = hasher()
|
||||
|
@ -2066,9 +2089,10 @@ def do_logprof_pass(logmark='', pid=pid):
|
|||
# filelist = hasher()
|
||||
|
||||
UI_Info(_('Reading log entries from %s.') %filename)
|
||||
UI_Info(_('Updating AppArmor profiles in %s.') %profile_dir)
|
||||
|
||||
read_profiles()
|
||||
if not passno:
|
||||
UI_Info(_('Updating AppArmor profiles in %s.') %profile_dir)
|
||||
read_profiles()
|
||||
|
||||
if not sev_db:
|
||||
sev_db = apparmor.severity.Severity(CONFDIR + '/severity.db', _('unknown'))
|
||||
|
@ -2158,7 +2182,7 @@ def save_profiles():
|
|||
q['title'] = 'Changed Local Profiles'
|
||||
q['headers'] = []
|
||||
q['explanation'] = _('The following local profiles were changed. Would you like to save them?')
|
||||
q['functions'] = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_VIEW_CHANGES_CLEAN', 'CMD_ABORT']
|
||||
q['functions'] = ['CMD_SAVE_CHANGES', 'CMD_SAVE_SELECTED', 'CMD_VIEW_CHANGES', 'CMD_VIEW_CHANGES_CLEAN', 'CMD_ABORT']
|
||||
q['default'] = 'CMD_VIEW_CHANGES'
|
||||
q['options'] = changed
|
||||
q['selected'] = 0
|
||||
|
@ -2167,7 +2191,14 @@ def save_profiles():
|
|||
arg = None
|
||||
while ans != 'CMD_SAVE_CHANGES':
|
||||
ans, arg = UI_PromptUser(q)
|
||||
if ans == 'CMD_VIEW_CHANGES':
|
||||
if ans == 'CMD_SAVE_SELECTED':
|
||||
profile_name = list(changed.keys())[arg]
|
||||
write_profile_ui_feedback(profile_name)
|
||||
reload_base(profile_name)
|
||||
changed.pop(profile_name)
|
||||
#q['options'] = changed
|
||||
|
||||
elif ans == 'CMD_VIEW_CHANGES':
|
||||
which = list(changed.keys())[arg]
|
||||
oldprofile = None
|
||||
if aa[which][which].get('filename', False):
|
||||
|
@ -2485,9 +2516,8 @@ def parse_profile_data(data, file, do_include):
|
|||
profile_data[profile][hat]['external'] = True
|
||||
else:
|
||||
hat = profile
|
||||
# Profile stored
|
||||
existing_profiles[profile] = file
|
||||
|
||||
# Profile stored
|
||||
existing_profiles[profile] = file
|
||||
|
||||
flags = matches[6]
|
||||
|
||||
|
@ -2959,7 +2989,7 @@ def write_cap_rules(prof_data, depth, allow):
|
|||
for cap in sorted(prof_data[allow]['capability'].keys()):
|
||||
audit = ''
|
||||
if prof_data[allow]['capability'][cap].get('audit', False):
|
||||
audit = 'audit'
|
||||
audit = 'audit '
|
||||
if prof_data[allow]['capability'][cap].get('set', False):
|
||||
data.append('%s%s%scapability %s,' %(pre, audit, allowstr, cap))
|
||||
data.append('')
|
||||
|
@ -3837,7 +3867,7 @@ def reload_base(bin_path):
|
|||
|
||||
def reload(bin_path):
|
||||
bin_path = find_executable(bin_path)
|
||||
if not bin:
|
||||
if not bin_path:
|
||||
return None
|
||||
|
||||
return reload_base(bin_path)
|
||||
|
@ -3955,6 +3985,7 @@ def check_qualifiers(program):
|
|||
'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"""
|
||||
|
@ -4048,7 +4079,7 @@ def matchregexp(new, old):
|
|||
|
||||
######Initialisations######
|
||||
|
||||
conf = apparmor.config.Config('ini')
|
||||
conf = apparmor.config.Config('ini', CONFDIR)
|
||||
cfg = conf.read_config('logprof.conf')
|
||||
|
||||
#print(cfg['settings'])
|
||||
|
|
|
@ -28,8 +28,8 @@ from apparmor.common import AppArmorException, warn, msg, open_file_read
|
|||
# REPO_CFG = None
|
||||
# SHELL_FILES = ['easyprof.conf', 'notify.conf', 'parser.conf', 'subdomain.conf']
|
||||
class Config:
|
||||
def __init__(self, conf_type):
|
||||
self.CONF_DIR = '/etc/apparmor'
|
||||
def __init__(self, conf_type, conf_dir='/etc/apparmor'):
|
||||
self.CONF_DIR = conf_dir
|
||||
# The type of config file that'll be read and/or written
|
||||
if conf_type == 'shell' or conf_type == 'ini':
|
||||
self.conf_type = conf_type
|
||||
|
|
|
@ -50,7 +50,7 @@ class ReadLog:
|
|||
if self.next_log_entry:
|
||||
sys.stderr.out('A log entry already present: %s' % self.next_log_entry)
|
||||
self.next_log_entry = self.LOG.readline()
|
||||
while not (self.RE_LOG_v2_6_syslog.search(self.next_log_entry) or self.RE_LOG_v2_6_audit.search(self.next_log_entry)) or (self.logmark and re.search(self.logmark, self.next_log_entry)):
|
||||
while not self.RE_LOG_v2_6_syslog.search(self.next_log_entry) and not self.RE_LOG_v2_6_audit.search(self.next_log_entry) and not (self.logmark and self.logmark in self.next_log_entry):
|
||||
self.next_log_entry = self.LOG.readline()
|
||||
if not self.next_log_entry:
|
||||
break
|
||||
|
@ -328,8 +328,11 @@ class ReadLog:
|
|||
except IOError:
|
||||
raise AppArmorException('Can not read AppArmor logfile: ' + self.filename)
|
||||
#LOG = open_file_read(log_open)
|
||||
line = self.get_next_log_entry()
|
||||
line = True
|
||||
while line:
|
||||
line = self.get_next_log_entry()
|
||||
if not line:
|
||||
break
|
||||
line = line.strip()
|
||||
self.debug_logger.debug('read_log: %s' % line)
|
||||
if self.logmark in line:
|
||||
|
@ -337,13 +340,12 @@ class ReadLog:
|
|||
|
||||
self.debug_logger.debug('read_log: seenmark = %s' %seenmark)
|
||||
if not seenmark:
|
||||
line = self.get_next_log_entry()
|
||||
continue
|
||||
continue
|
||||
|
||||
event = self.parse_log_record(line)
|
||||
#print(event)
|
||||
if event:
|
||||
self.add_event_to_tree(event)
|
||||
line = self.get_next_log_entry()
|
||||
self.LOG.close()
|
||||
self.logmark = ''
|
||||
return self.log
|
||||
|
|
|
@ -17,53 +17,50 @@ class Severity:
|
|||
self.severity['VARIABLES'] = dict()
|
||||
if not dbname:
|
||||
return None
|
||||
try:
|
||||
database = open_file_read(dbname)#open(dbname, 'r')
|
||||
except IOError:
|
||||
raise AppArmorException("Could not open severity database: %s" % dbname)
|
||||
for lineno, line in enumerate(database, start=1):
|
||||
line = line.strip() # or only rstrip and lstrip?
|
||||
if line == '' or line.startswith('#') :
|
||||
continue
|
||||
if line.startswith('/'):
|
||||
try:
|
||||
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))
|
||||
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))
|
||||
path = path.lstrip('/')
|
||||
if '*' not in path:
|
||||
self.severity['FILES'][path] = {'r': read, 'w': write, 'x': execute}
|
||||
|
||||
with open_file_read(dbname) as database:#open(dbname, 'r')
|
||||
for lineno, line in enumerate(database, start=1):
|
||||
line = line.strip() # or only rstrip and lstrip?
|
||||
if line == '' or line.startswith('#') :
|
||||
continue
|
||||
if line.startswith('/'):
|
||||
try:
|
||||
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))
|
||||
else:
|
||||
ptr = self.severity['REGEXPS']
|
||||
pieces = path.split('/')
|
||||
for index, piece in enumerate(pieces):
|
||||
if '*' in piece:
|
||||
path = '/'.join(pieces[index:])
|
||||
regexp = convert_regexp(path)
|
||||
ptr[regexp] = {'AA_RANK': {'r': read, 'w': write, 'x': execute}}
|
||||
break
|
||||
else:
|
||||
ptr[piece] = ptr.get(piece, {})
|
||||
ptr = ptr[piece]
|
||||
elif line.startswith('CAP_'):
|
||||
try:
|
||||
resource, severity = line.split()
|
||||
severity = int(severity)
|
||||
except ValueError as e:
|
||||
error_message = 'No severity value present in file: %s\n\t[Line %s]: %s' % (dbname, lineno, line)
|
||||
#error(error_message)
|
||||
raise AppArmorException(error_message) # from None
|
||||
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))
|
||||
path = path.lstrip('/')
|
||||
if '*' not in path:
|
||||
self.severity['FILES'][path] = {'r': read, 'w': write, 'x': execute}
|
||||
else:
|
||||
ptr = self.severity['REGEXPS']
|
||||
pieces = path.split('/')
|
||||
for index, piece in enumerate(pieces):
|
||||
if '*' in piece:
|
||||
path = '/'.join(pieces[index:])
|
||||
regexp = convert_regexp(path)
|
||||
ptr[regexp] = {'AA_RANK': {'r': read, 'w': write, 'x': execute}}
|
||||
break
|
||||
else:
|
||||
ptr[piece] = ptr.get(piece, {})
|
||||
ptr = ptr[piece]
|
||||
elif line.startswith('CAP_'):
|
||||
try:
|
||||
resource, severity = line.split()
|
||||
severity = int(severity)
|
||||
except ValueError as e:
|
||||
error_message = 'No severity value present in file: %s\n\t[Line %s]: %s' % (dbname, lineno, line)
|
||||
#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))
|
||||
self.severity['CAPABILITIES'][resource] = severity
|
||||
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))
|
||||
self.severity['CAPABILITIES'][resource] = severity
|
||||
else:
|
||||
raise AppArmorException("Unexpected line in file: %s\n\t[Line %s]: %s" % (dbname, lineno, line))
|
||||
database.close()
|
||||
raise AppArmorException("Unexpected line in file: %s\n\t[Line %s]: %s" % (dbname, lineno, line))
|
||||
|
||||
def handle_capability(self, resource):
|
||||
"""Returns the severity of for the capability resource, default value if no match"""
|
||||
|
@ -187,11 +184,11 @@ class Severity:
|
|||
try:
|
||||
self.severity['VARIABLES'][line[0]] += [i.strip('"') for i in line[1].split()]
|
||||
except KeyError:
|
||||
raise AppArmorException("Variable %s was not previously declared, but is being assigned additional values" % line[0])
|
||||
raise AppArmorException("Variable %s was not previously declared, but is being assigned additional values in file: %s" % (line[0], prof_path))
|
||||
else:
|
||||
line = line.split('=')
|
||||
if line[0] in self.severity['VARIABLES'].keys():
|
||||
raise AppArmorException("Variable %s was previously declared" % line[0])
|
||||
raise AppArmorException("Variable %s was previously declared in file: %s" % (line[0], prof_path))
|
||||
self.severity['VARIABLES'][line[0]] = [i.strip('"') for i in line[1].split()]
|
||||
|
||||
def unload_variables(self):
|
||||
|
|
103
apparmor/ui.py
103
apparmor/ui.py
|
@ -42,25 +42,43 @@ def UI_Important(text):
|
|||
})
|
||||
path, yarg = GetDataFromYast()
|
||||
|
||||
def get_translated_hotkey(translated, cmsg=''):
|
||||
msg = 'PromptUser: '+_('Invalid hotkey for')
|
||||
if re.search('\((\S)\)', translated):
|
||||
return re.search('\((\S)\)', translated).groups()[0]
|
||||
else:
|
||||
if cmsg:
|
||||
raise AppArmorException(cmsg)
|
||||
else:
|
||||
raise AppArmorException('%s %s' %(msg, translated))
|
||||
|
||||
def UI_YesNo(text, default):
|
||||
debug_logger.debug('UI_YesNo: %s: %s %s' %(UI_mode, text, default))
|
||||
ans = default
|
||||
default = default.lower()
|
||||
ans = None
|
||||
if UI_mode == 'text':
|
||||
yes = '(Y)es'
|
||||
no = '(N)o'
|
||||
usrmsg = 'PromptUser: Invalid hotkey for'
|
||||
yeskey = 'y'
|
||||
nokey = 'n'
|
||||
sys.stdout.write('\n' + text + '\n')
|
||||
if default == 'y':
|
||||
sys.stdout.write('\n[%s] / %s\n' % (yes, no))
|
||||
else:
|
||||
sys.stdout.write('\n%s / [%s]\n' % (yes, no))
|
||||
ans = getkey()#readkey()
|
||||
if ans:
|
||||
ans = ans.lower()
|
||||
else:
|
||||
ans = default
|
||||
yes = _('(Y)es')
|
||||
no = _('(N)o')
|
||||
yeskey = get_translated_hotkey(yes).lower()
|
||||
nokey = get_translated_hotkey(no).lower()
|
||||
ans = 'XXXINVALIDXXX'
|
||||
while ans not in ['y', 'n']:
|
||||
sys.stdout.write('\n' + text + '\n')
|
||||
if default == 'y':
|
||||
sys.stdout.write('\n[%s] / %s\n' % (yes, no))
|
||||
else:
|
||||
sys.stdout.write('\n%s / [%s]\n' % (yes, no))
|
||||
ans = getkey()
|
||||
if ans:
|
||||
# Get back to english from localised answer
|
||||
ans = ans.lower()
|
||||
if ans == yeskey:
|
||||
ans = 'y'
|
||||
else:
|
||||
ans = 'n'
|
||||
else:
|
||||
ans = default
|
||||
|
||||
else:
|
||||
SendDataToYast({
|
||||
'type': 'dialog-yesno',
|
||||
|
@ -74,16 +92,19 @@ def UI_YesNo(text, default):
|
|||
|
||||
def UI_YesNoCancel(text, default):
|
||||
debug_logger.debug('UI_YesNoCancel: %s: %s %s' % (UI_mode, text, default))
|
||||
|
||||
default = default.lower()
|
||||
ans = None
|
||||
if UI_mode == 'text':
|
||||
yes = '(Y)es'
|
||||
no = '(N)o'
|
||||
cancel = '(C)ancel'
|
||||
yeskey = 'y'
|
||||
nokey = 'n'
|
||||
cancelkey = 'c'
|
||||
yes = _('(Y)es')
|
||||
no = _('(N)o')
|
||||
cancel = _('(C)ancel')
|
||||
|
||||
yeskey = get_translated_hotkey(yes).lower()
|
||||
nokey = get_translated_hotkey(no).lower()
|
||||
cancelkey = get_translated_hotkey(cancel).lower()
|
||||
|
||||
ans = 'XXXINVALIDXXX'
|
||||
while ans != 'c' and ans != 'n' and ans != 'y':
|
||||
while ans not in ['c', 'n', 'y']:
|
||||
sys.stdout.write('\n' + text + '\n')
|
||||
if default == 'y':
|
||||
sys.stdout.write('\n[%s] / %s / %s\n' % (yes, no, cancel))
|
||||
|
@ -91,9 +112,16 @@ def UI_YesNoCancel(text, default):
|
|||
sys.stdout.write('\n%s / [%s] / %s\n' % (yes, no, cancel))
|
||||
else:
|
||||
sys.stdout.write('\n%s / %s / [%s]\n' % (yes, no, cancel))
|
||||
ans = getkey()#readkey()
|
||||
ans = getkey()
|
||||
if ans:
|
||||
# Get back to english from localised answer
|
||||
ans = ans.lower()
|
||||
if ans == yeskey:
|
||||
ans = 'y'
|
||||
elif ans == nokey:
|
||||
ans = 'n'
|
||||
elif ans== cancelkey:
|
||||
ans= 'c'
|
||||
else:
|
||||
ans = default
|
||||
else:
|
||||
|
@ -111,7 +139,7 @@ def UI_GetString(text, default):
|
|||
debug_logger.debug('UI_GetString: %s: %s %s' % (UI_mode, text, default))
|
||||
string = default
|
||||
if UI_mode == 'text':
|
||||
sys.stdout.write('\n' + text + '\n')
|
||||
sys.stdout.write('\n' + text)
|
||||
string = sys.stdin.readline()
|
||||
else:
|
||||
SendDataToYast({
|
||||
|
@ -198,6 +226,7 @@ CMDS = {
|
|||
'CMD_UPDATE_PROFILE': '(U)pdate Profile',
|
||||
'CMD_IGNORE_UPDATE': '(I)gnore Update',
|
||||
'CMD_SAVE_CHANGES': '(S)ave Changes',
|
||||
'CMD_SAVE_SELECTED': 'Save Selec(t)ed Profile',
|
||||
'CMD_UPLOAD_CHANGES': '(U)pload Changes',
|
||||
'CMD_VIEW_CHANGES': '(V)iew Changes',
|
||||
'CMD_VIEW_CHANGES_CLEAN': 'View Changes b/w (C)lean profiles',
|
||||
|
@ -216,7 +245,7 @@ CMDS = {
|
|||
'CMD_IGNORE_ENTRY': '(I)gnore'
|
||||
}
|
||||
|
||||
def UI_PromptUser(q):
|
||||
def UI_PromptUser(q, params=''):
|
||||
cmd = None
|
||||
arg = None
|
||||
if UI_mode == 'text':
|
||||
|
@ -230,10 +259,11 @@ def UI_PromptUser(q):
|
|||
arg = yarg['selected']
|
||||
if cmd == 'CMD_ABORT':
|
||||
confirm_and_abort()
|
||||
cmd == 'XXXINVALIDXXX'
|
||||
cmd = 'XXXINVALIDXXX'
|
||||
elif cmd == 'CMD_FINISHED':
|
||||
confirm_and_finish()
|
||||
cmd == 'XXXINVALIDXXX'
|
||||
if not params:
|
||||
confirm_and_finish()
|
||||
cmd = 'XXXINVALIDXXX'
|
||||
return (cmd, arg)
|
||||
|
||||
def confirm_and_abort():
|
||||
|
@ -287,11 +317,7 @@ def Text_PromptUser(question):
|
|||
|
||||
menutext = _(CMDS[cmd])
|
||||
|
||||
menuhotkey = re.search('\((\S)\)', menutext)
|
||||
if not menuhotkey:
|
||||
raise AppArmorException('PromptUser: %s \'%s\'' %(_('Invalid hotkey in'), menutext))
|
||||
|
||||
key = menuhotkey.groups()[0].lower()
|
||||
key = get_translated_hotkey(menutext).lower()
|
||||
# Duplicate hotkey
|
||||
if keys.get(key, False):
|
||||
raise AppArmorException('PromptUser: %s %s: %s' %(_('Duplicate hotkey for'), cmd, menutext))
|
||||
|
@ -306,12 +332,9 @@ def Text_PromptUser(question):
|
|||
default_key = 0
|
||||
if default and CMDS[default]:
|
||||
defaulttext = _(CMDS[default])
|
||||
defmsg = 'PromptUser: ' + _('Invalid hotkey in default item')
|
||||
|
||||
defaulthotkey = re.search('\((\S)\)', defaulttext)
|
||||
if not menuhotkey:
|
||||
raise AppArmorException('PromptUser: %s \'%s\'' %(_('Invalid hotkey in default item'), defaulttext))
|
||||
|
||||
default_key = defaulthotkey.groups()[0].lower()
|
||||
default_key = get_translated_hotkey(defaulttext, defmsg).lower()
|
||||
|
||||
if not keys.get(default_key, False):
|
||||
raise AppArmorException('PromptUser: %s %s' %(_('Invalid default'), default))
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
def write_header(prof_data, depth, name, embedded_hat, write_flags):
|
||||
pre = ' ' * depth
|
||||
data = []
|
||||
name = quote_if_needed(name)
|
||||
|
||||
if (not embedded_hat and re.search('^[^/]|^"[^/]', name)) or (embedded_hat and re.search('^[^^]' ,name)):
|
||||
name = 'profile %s' % name
|
||||
|
||||
if write_flags and prof_data['flags']:
|
||||
data.append('%s%s flags=(%s) {' % (pre, name, prof_data['flags']))
|
||||
else:
|
||||
data.append('%s%s {' % (pre, name))
|
||||
|
||||
return data
|
||||
|
||||
def write_rules(prof_data, depth):
|
||||
data = write_alias(prof_data, depth)
|
||||
data += write_list_vars(prof_data, depth)
|
||||
data += write_includes(prof_data, depth)
|
||||
data += write_rlimits(prof_data, depth)
|
||||
data += write_capabilities(prof_data, depth)
|
||||
data += write_netdomain(prof_data, depth)
|
||||
data += write_links(prof_data, depth)
|
||||
data += write_paths(prof_data, depth)
|
||||
data += write_change_profile(prof_data, depth)
|
||||
|
||||
return data
|
||||
|
||||
def write_piece(profile_data, depth, name, nhat, write_flags):
|
||||
pre = ' ' * depth
|
||||
data = []
|
||||
wname = None
|
||||
inhat = False
|
||||
if name == nhat:
|
||||
wname = name
|
||||
else:
|
||||
wname = name + '//' + nhat
|
||||
name = nhat
|
||||
inhat = True
|
||||
data += ['begin header']
|
||||
data += write_header(profile_data[name], depth, wname, False, write_flags)
|
||||
data +=['end header']
|
||||
data += write_rules(profile_data[name], depth+1)
|
||||
|
||||
pre2 = ' ' * (depth+1)
|
||||
# External hat declarations
|
||||
for hat in list(filter(lambda x: x != name, sorted(profile_data.keys()))):
|
||||
if profile_data[hat].get('declared', False):
|
||||
data.append('%s^%s,' %(pre2, hat))
|
||||
|
||||
if not inhat:
|
||||
# Embedded hats
|
||||
for hat in list(filter(lambda x: x != name, sorted(profile_data.keys()))):
|
||||
if not profile_data[hat]['external'] and not profile_data[hat]['declared']:
|
||||
data.append('')
|
||||
if profile_data[hat]['profile']:
|
||||
data += list(map(str, write_header(profile_data[hat], depth+1, hat, True, write_flags)))
|
||||
else:
|
||||
data += list(map(str, write_header(profile_data[hat], depth+1, '^'+hat, True, write_flags)))
|
||||
|
||||
data += list(map(str, write_rules(profile_data[hat], depth+2)))
|
||||
|
||||
data.append('%s}' %pre2)
|
||||
|
||||
data.append('%s}' %pre)
|
||||
|
||||
# External hats
|
||||
for hat in list(filter(lambda x: x != name, sorted(profile_data.keys()))):
|
||||
if name == nhat and profile_data[hat].get('external', False):
|
||||
data.append('')
|
||||
data += list(map(lambda x: ' %s' %x, write_piece(profile_data, depth-1, name, nhat, write_flags)))
|
||||
data.append(' }')
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def serialize_profile(profile_data, name, options):
|
||||
string = ''
|
||||
include_metadata = False
|
||||
include_flags = True
|
||||
data= []
|
||||
|
||||
if options:# and type(options) == dict:
|
||||
if options.get('METADATA', False):
|
||||
include_metadata = True
|
||||
if options.get('NO_FLAGS', False):
|
||||
include_flags = False
|
||||
|
||||
if include_metadata:
|
||||
string = '# Last Modified: %s\n' %time.time()
|
||||
|
||||
if (profile_data[name].get('repo', False) and profile_data[name]['repo']['url']
|
||||
and profile_data[name]['repo']['user'] and profile_data[name]['repo']['id']):
|
||||
repo = profile_data[name]['repo']
|
||||
string += '# REPOSITORY: %s %s %s\n' %(repo['url'], repo['user'], repo['id'])
|
||||
elif profile_data[name]['repo']['neversubmit']:
|
||||
string += '# REPOSITORY: NEVERSUBMIT\n'
|
||||
|
||||
if profile_data[name].get('initial_comment', False):
|
||||
comment = profile_data[name]['initial_comment']
|
||||
comment.replace('\\n', '\n')
|
||||
string += comment + '\n'
|
||||
|
||||
prof_filename = get_profile_filename(name)
|
||||
if filelist.get(prof_filename, False):
|
||||
data += write_alias(filelist[prof_filename], 0)
|
||||
data += write_list_vars(filelist[prof_filename], 0)
|
||||
data += write_includes(filelist[prof_filename], 0)
|
||||
|
||||
data += write_piece(profile_data, 0, name, name, include_flags)
|
||||
|
||||
string += '\n'.join(data)
|
||||
|
||||
return string+'\n'
|
Loading…
Add table
Reference in a new issue