#! /usr/bin/python3 # ---------------------------------------------------------------------- # Copyright (C) 2013 Kshitij Gupta # Copyright (C) 2014-2024 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.cleanprofile as cleanprofile import apparmor.ui as aaui from apparmor.fail import enable_aa_exception_handler from apparmor.translations import init_translation enable_aa_exception_handler() # setup exception handling _ = init_translation() # setup module translations 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')) parser.add_argument('--configdir', type=str, help=argparse.SUPPRESS) args = parser.parse_args() args.other = None apparmor.aa.init_aa(confdir=args.configdir, profiledir=args.dir) profiles = args.files def find_profiles_from_files(files): profile_to_filename = dict() for file_name in files: try: apparmor.aa.read_profile(file_name, True, read_error_fatal=True) for profile_name in apparmor.aa.active_profiles.profiles_in_file(file_name): profile_to_filename[profile_name] = file_name except IOError: pass 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.active_profiles.get_all_profiles()} apparmor.aa.loadincludes() apparmor.aa.load_sev_db() # 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()