#! /usr/bin/python3 # ---------------------------------------------------------------------- # Copyright (C) 2013 Kshitij Gupta # Copyright (C) 2014-2018 Christian Boltz # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # 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 apparmor.aa import apparmor.severity import apparmor.cleanprofile as cleanprofile import apparmor.ui as aaui # setup exception handling from apparmor.fail import enable_aa_exception_handler enable_aa_exception_handler() # 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('-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 apparmor.aa.init_aa(profiledir=args.dir) profiles = args.files 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.active_profiles.profiles_in_file(file_name): profile_to_filename[profile_name] = file_name apparmor.aa.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_from_profile_name(profile_name, True) apparmor.aa.reset_aa() return profile_to_filename def main(): base_profile_to_file = find_profiles_from_files(profiles) profiles_to_merge = set(base_profile_to_file.keys()) user_profile_to_file = find_files_from_profiles(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) act(user_file, base_file, profile_name) apparmor.aa.reset_aa() def act(user_file, base_file, merging_profile): mergeprofiles = Merge(user_file, base_file) #Get rid of common/superfluous stuff mergeprofiles.clear_common() mergeprofiles.ask_merge_questions() apparmor.aa.changed[merging_profile] = True # force asking to save the profile apparmor.aa.save_profiles(True) class Merge(object): def __init__(self, user, base): #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) apparmor.aa.reset_aa() #Read and parse user profile apparmor.aa.read_profile(user, True) self.user = cleanprofile.Prof(user) def clear_common(self): deleted = 0 #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() def ask_merge_questions(self): other = self.base log_dict = {'merge': other.aa} apparmor.aa.loadincludes() if not apparmor.aa.sev_db: apparmor.aa.sev_db = apparmor.severity.Severity(apparmor.aa.CONFDIR + '/severity.db', _('unknown')) # ask about preamble rules apparmor.aa.ask_rule_questions( other.active_profiles.files[other.filename], # prof_events aka log_dict '[preamble]', # displayed profile name self.user.active_profiles.files[self.user.filename], # profile to update ['abi', 'inc_ie'] # rule types - TODO: don't hardcode ) apparmor.aa.ask_the_questions(log_dict) if __name__ == '__main__': main()