2016-10-01 20:57:09 +02:00
|
|
|
#! /usr/bin/python3
|
2013-09-28 20:43:06 +05:30
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
|
2017-01-19 16:46:23 +01:00
|
|
|
# Copyright (C) 2014-2017 Christian Boltz <apparmor@cboltz.de>
|
2013-09-28 20:43:06 +05:30
|
|
|
#
|
|
|
|
# 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.
|
|
|
|
#
|
|
|
|
# ----------------------------------------------------------------------
|
2013-09-12 14:42:15 +05:30
|
|
|
import argparse
|
2014-08-04 20:19:08 +02:00
|
|
|
import os
|
2013-09-12 14:42:15 +05:30
|
|
|
|
2013-09-23 19:32:25 +05:30
|
|
|
import apparmor.aa
|
|
|
|
import apparmor.aamode
|
2016-06-10 01:18:32 +05:30
|
|
|
|
2013-09-23 19:32:25 +05:30
|
|
|
import apparmor.severity
|
2013-09-17 22:30:48 +05:30
|
|
|
import apparmor.cleanprofile as cleanprofile
|
2014-07-28 00:24:26 +02:00
|
|
|
import apparmor.ui as aaui
|
2013-09-19 10:32:19 +05:30
|
|
|
|
2016-06-10 01:18:32 +05:30
|
|
|
from apparmor.common import AppArmorException
|
|
|
|
from apparmor.regex import re_match_include
|
|
|
|
|
|
|
|
|
2015-07-06 22:02:34 +02:00
|
|
|
# setup exception handling
|
|
|
|
from apparmor.fail import enable_aa_exception_handler
|
|
|
|
enable_aa_exception_handler()
|
|
|
|
|
2014-02-10 22:15:05 -08:00
|
|
|
# setup module translations
|
2014-02-11 16:23:21 -08:00
|
|
|
from apparmor.translations import init_translation
|
|
|
|
_ = init_translation()
|
2014-02-10 22:15:05 -08:00
|
|
|
|
2014-10-16 23:25:33 +02:00
|
|
|
parser = argparse.ArgumentParser(description=_('Merge the given profiles into /etc/apparmor.d/ (or the directory specified with -d)'))
|
2014-10-16 23:35:06 +02:00
|
|
|
parser.add_argument('files', nargs='+', type=str, help=_('Profile(s) to merge'))
|
2013-09-22 15:25:20 +05:30
|
|
|
parser.add_argument('-d', '--dir', type=str, help=_('path to profiles'))
|
2014-09-04 01:49:47 +02:00
|
|
|
#parser.add_argument('-a', '--auto', action='store_true', help=_('Automatically merge profiles, exits incase of *x conflicts'))
|
2013-09-12 14:42:15 +05:30
|
|
|
args = parser.parse_args()
|
|
|
|
|
2014-10-16 20:06:45 +02:00
|
|
|
args.other = None
|
2013-09-12 14:42:15 +05:30
|
|
|
|
2017-03-02 21:21:53 +00:00
|
|
|
apparmor.aa.init_aa()
|
|
|
|
|
2017-01-19 16:45:29 +01:00
|
|
|
profiles = args.files
|
2014-02-13 08:31:59 -08:00
|
|
|
|
2014-08-04 20:19:08 +02:00
|
|
|
profiledir = args.dir
|
|
|
|
if profiledir:
|
|
|
|
apparmor.aa.profile_dir = apparmor.aa.get_full_path(profiledir)
|
|
|
|
if not os.path.isdir(apparmor.aa.profile_dir):
|
2015-05-18 01:35:51 +02:00
|
|
|
raise AppArmorException(_("%s is not a directory.") %profiledir)
|
2014-08-04 20:19:08 +02:00
|
|
|
|
2014-10-16 20:06:45 +02:00
|
|
|
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
|
2014-08-04 20:19:08 +02:00
|
|
|
|
2013-09-12 14:42:15 +05:30
|
|
|
def main():
|
2017-01-19 16:45:29 +01:00
|
|
|
base_profile_to_file = find_profiles_from_files(profiles)
|
2014-10-16 20:06:45 +02:00
|
|
|
|
2017-01-19 16:45:29 +01:00
|
|
|
profiles_to_merge = set(base_profile_to_file.keys())
|
2014-10-16 20:06:45 +02:00
|
|
|
|
|
|
|
user_profile_to_file = find_files_from_profiles(profiles_to_merge)
|
|
|
|
|
|
|
|
for profile_name in profiles_to_merge:
|
2014-10-16 23:25:33 +02:00
|
|
|
aaui.UI_Info("\n\n" + _("Merging profile for %s" % profile_name))
|
2014-10-16 20:06:45 +02:00
|
|
|
user_file = user_profile_to_file[profile_name]
|
|
|
|
base_file = base_profile_to_file.get(profile_name, None)
|
|
|
|
|
2017-01-19 16:45:29 +01:00
|
|
|
act([user_file, base_file], profile_name)
|
2014-10-16 20:06:45 +02:00
|
|
|
|
|
|
|
reset_aa()
|
|
|
|
|
2017-01-19 16:45:29 +01:00
|
|
|
def act(files, merging_profile):
|
2014-10-16 20:06:45 +02:00
|
|
|
mergeprofiles = Merge(files)
|
2013-09-23 02:14:11 +05:30
|
|
|
#Get rid of common/superfluous stuff
|
2016-10-01 20:09:36 +02:00
|
|
|
mergeprofiles.clear_common()
|
2014-02-13 08:31:59 -08:00
|
|
|
|
2014-09-04 01:49:47 +02:00
|
|
|
# if not args.auto:
|
|
|
|
if 1 == 1: # workaround to avoid lots of whitespace changes
|
2017-01-19 16:54:47 +01:00
|
|
|
mergeprofiles.ask_merge_questions()
|
2014-02-13 08:31:59 -08:00
|
|
|
|
2014-10-07 18:36:01 +05:30
|
|
|
q = aaui.PromptQuestion()
|
|
|
|
q.title = _('Changed Local Profiles')
|
|
|
|
q.explanation = _('The following local profiles were changed. Would you like to save them?')
|
2014-10-16 20:06:45 +02:00
|
|
|
q.functions = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT', 'CMD_IGNORE_ENTRY']
|
2014-10-07 18:36:01 +05:30
|
|
|
q.default = 'CMD_VIEW_CHANGES'
|
2014-10-16 23:35:06 +02:00
|
|
|
q.options = [merging_profile]
|
2014-10-07 18:36:01 +05:30
|
|
|
q.selected = 0
|
|
|
|
|
2013-09-23 19:32:25 +05:30
|
|
|
ans = ''
|
|
|
|
arg = None
|
|
|
|
programs = list(mergeprofiles.user.aa.keys())
|
|
|
|
program = programs[0]
|
|
|
|
while ans != 'CMD_SAVE_CHANGES':
|
2014-10-07 18:36:01 +05:30
|
|
|
ans, arg = q.promptUser()
|
2013-09-23 19:32:25 +05:30
|
|
|
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, '')
|
2017-10-26 00:34:40 +02:00
|
|
|
aaui.UI_Changes(mergeprofiles.user.filename, newprofile, comments=True)
|
2014-10-16 20:06:45 +02:00
|
|
|
elif ans == 'CMD_IGNORE_ENTRY':
|
|
|
|
break
|
2014-02-13 08:31:59 -08:00
|
|
|
|
2013-09-22 22:51:30 +05:30
|
|
|
|
2013-09-12 14:42:15 +05:30
|
|
|
class Merge(object):
|
|
|
|
def __init__(self, profiles):
|
2017-01-19 16:45:29 +01:00
|
|
|
user, base = profiles
|
2013-09-22 22:51:30 +05:30
|
|
|
|
2013-09-12 14:42:15 +05:30
|
|
|
#Read and parse base profile and save profile data, include data from it and reset them
|
2013-09-23 19:32:25 +05:30
|
|
|
apparmor.aa.read_profile(base, True)
|
2013-09-23 03:47:15 +05:30
|
|
|
self.base = cleanprofile.Prof(base)
|
2013-09-23 02:14:11 +05:30
|
|
|
|
2014-10-16 20:06:45 +02:00
|
|
|
reset_aa()
|
2013-09-22 22:51:30 +05:30
|
|
|
|
2013-09-12 14:42:15 +05:30
|
|
|
#Read and parse user profile
|
2014-07-29 12:39:12 +02:00
|
|
|
apparmor.aa.read_profile(user, True)
|
2013-09-23 03:47:15 +05:30
|
|
|
self.user = cleanprofile.Prof(user)
|
2013-09-22 22:51:30 +05:30
|
|
|
|
2013-09-12 14:42:15 +05:30
|
|
|
def clear_common(self):
|
2013-09-23 02:14:11 +05:30
|
|
|
deleted = 0
|
2014-07-29 12:39:12 +02:00
|
|
|
|
2013-09-23 02:14:11 +05:30
|
|
|
#Remove off the parts in base profile which are common/superfluous from user profile
|
2013-09-23 03:47:15 +05:30
|
|
|
user_base = cleanprofile.CleanProf(False, self.user, self.base)
|
2013-09-23 02:14:11 +05:30
|
|
|
deleted += user_base.compare_profiles()
|
2014-02-13 08:31:59 -08:00
|
|
|
|
2017-01-19 16:54:47 +01:00
|
|
|
def ask_merge_questions(self):
|
2017-01-19 16:45:29 +01:00
|
|
|
other = self.base
|
2017-01-19 16:46:23 +01:00
|
|
|
log_dict = {'merge': other.aa}
|
2014-02-13 08:31:59 -08:00
|
|
|
|
2016-10-01 20:20:27 +02:00
|
|
|
apparmor.aa.loadincludes()
|
2013-09-23 19:32:25 +05:30
|
|
|
done = False
|
2014-10-16 20:22:52 +02:00
|
|
|
|
2017-01-19 16:46:23 +01:00
|
|
|
#Add the file-wide includes from the other profile to the user profile
|
2014-10-16 20:22:52 +02:00
|
|
|
options = []
|
|
|
|
for inc in other.filelist[other.filename]['include'].keys():
|
|
|
|
if not inc in self.user.filelist[self.user.filename]['include'].keys():
|
2017-12-18 19:37:35 +00:00
|
|
|
if inc.startswith('/'):
|
|
|
|
options.append('#include "%s"' %inc)
|
|
|
|
else:
|
|
|
|
options.append('#include <%s>' %inc)
|
2014-10-16 20:22:52 +02:00
|
|
|
|
2013-09-23 19:32:25 +05:30
|
|
|
default_option = 1
|
2014-10-07 18:36:01 +05:30
|
|
|
|
|
|
|
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'
|
|
|
|
|
2013-09-23 19:32:25 +05:30
|
|
|
while not done and options:
|
2014-10-16 23:25:33 +02:00
|
|
|
ans, selected = q.promptUser()
|
2013-09-23 19:32:25 +05:30
|
|
|
if ans == 'CMD_IGNORE_ENTRY':
|
|
|
|
done = True
|
|
|
|
elif ans == 'CMD_ALLOW':
|
|
|
|
selection = options[selected]
|
2015-06-19 21:41:41 +02:00
|
|
|
inc = re_match_include(selection)
|
2013-09-23 19:32:25 +05:30
|
|
|
self.user.filelist[self.user.filename]['include'][inc] = True
|
|
|
|
options.pop(selected)
|
2014-07-28 00:24:26 +02:00
|
|
|
aaui.UI_Info(_('Adding %s to the file.') % selection)
|
2014-10-16 23:25:33 +02:00
|
|
|
elif ans == 'CMD_FINISHED':
|
|
|
|
return
|
2014-02-13 08:31:59 -08:00
|
|
|
|
2017-01-19 16:54:47 +01:00
|
|
|
if not apparmor.aa.sev_db:
|
|
|
|
apparmor.aa.sev_db = apparmor.severity.Severity(apparmor.aa.CONFDIR + '/severity.db', _('unknown'))
|
|
|
|
|
|
|
|
apparmor.aa.ask_the_questions(log_dict)
|
2013-09-12 14:42:15 +05:30
|
|
|
|
2013-09-23 02:14:11 +05:30
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|