~apparmor-dev/apparmor/master

« back to all changes in this revision

Viewing changes to utils/aa-mergeprof

  • Committer: Steve Beattie
  • Date: 2019-02-19 09:38:13 UTC
  • Revision ID: sbeattie@ubuntu.com-20190219093813-ud526ee6hwn8nljz
The AppArmor project has been converted to git and is now hosted on
gitlab.

To get the converted repository, please do
  git clone https://gitlab.com/apparmor/apparmor

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#! /usr/bin/python3
2
 
# ----------------------------------------------------------------------
3
 
#    Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
4
 
#    Copyright (C) 2014-2017 Christian Boltz <apparmor@cboltz.de>
5
 
#
6
 
#    This program is free software; you can redistribute it and/or
7
 
#    modify it under the terms of version 2 of the GNU General Public
8
 
#    License as published by the Free Software Foundation.
9
 
#
10
 
#    This program is distributed in the hope that it will be useful,
11
 
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 
#    GNU General Public License for more details.
14
 
#
15
 
# ----------------------------------------------------------------------
16
 
import argparse
17
 
import os
18
 
 
19
 
import apparmor.aa
20
 
import apparmor.aamode
21
 
 
22
 
import apparmor.severity
23
 
import apparmor.cleanprofile as cleanprofile
24
 
import apparmor.ui as aaui
25
 
 
26
 
from apparmor.common import AppArmorException
27
 
from apparmor.regex import re_match_include
28
 
 
29
 
 
30
 
# setup exception handling
31
 
from apparmor.fail import enable_aa_exception_handler
32
 
enable_aa_exception_handler()
33
 
 
34
 
# setup module translations
35
 
from apparmor.translations import init_translation
36
 
_ = init_translation()
37
 
 
38
 
parser = argparse.ArgumentParser(description=_('Merge the given profiles into /etc/apparmor.d/ (or the directory specified with -d)'))
39
 
parser.add_argument('files', nargs='+', type=str, help=_('Profile(s) to merge'))
40
 
parser.add_argument('-d', '--dir', type=str, help=_('path to profiles'))
41
 
#parser.add_argument('-a', '--auto', action='store_true', help=_('Automatically merge profiles, exits incase of *x conflicts'))
42
 
args = parser.parse_args()
43
 
 
44
 
args.other = None
45
 
 
46
 
apparmor.aa.init_aa()
47
 
 
48
 
profiles = args.files
49
 
 
50
 
profiledir = args.dir
51
 
if profiledir:
52
 
    apparmor.aa.profile_dir = apparmor.aa.get_full_path(profiledir)
53
 
    if not os.path.isdir(apparmor.aa.profile_dir):
54
 
        raise AppArmorException(_("%s is not a directory.") %profiledir)
55
 
 
56
 
def reset_aa():
57
 
    apparmor.aa.aa = apparmor.aa.hasher()
58
 
    apparmor.aa.filelist = apparmor.aa.hasher()
59
 
    apparmor.aa.include = dict()
60
 
    apparmor.aa.existing_profiles = apparmor.aa.hasher()
61
 
    apparmor.aa.original_aa = apparmor.aa.hasher()
62
 
 
63
 
def find_profiles_from_files(files):
64
 
    profile_to_filename = dict()
65
 
    for file_name in files:
66
 
        apparmor.aa.read_profile(file_name, True)
67
 
        for profile_name in apparmor.aa.filelist[file_name]['profiles'].keys():
68
 
            profile_to_filename[profile_name] = file_name
69
 
        reset_aa()
70
 
 
71
 
    return profile_to_filename
72
 
 
73
 
def find_files_from_profiles(profiles):
74
 
    profile_to_filename = dict()
75
 
    apparmor.aa.read_profiles()
76
 
 
77
 
    for profile_name in profiles:
78
 
        profile_to_filename[profile_name] = apparmor.aa.get_profile_filename(profile_name)
79
 
 
80
 
    reset_aa()
81
 
 
82
 
    return profile_to_filename
83
 
 
84
 
def main():
85
 
    base_profile_to_file = find_profiles_from_files(profiles)
86
 
 
87
 
    profiles_to_merge = set(base_profile_to_file.keys())
88
 
 
89
 
    user_profile_to_file = find_files_from_profiles(profiles_to_merge)
90
 
 
91
 
    for profile_name in profiles_to_merge:
92
 
        aaui.UI_Info("\n\n" + _("Merging profile for %s" % profile_name))
93
 
        user_file = user_profile_to_file[profile_name]
94
 
        base_file = base_profile_to_file.get(profile_name, None)
95
 
 
96
 
        act([user_file, base_file], profile_name)
97
 
 
98
 
        reset_aa()
99
 
 
100
 
def act(files, merging_profile):
101
 
    mergeprofiles = Merge(files)
102
 
    #Get rid of common/superfluous stuff
103
 
    mergeprofiles.clear_common()
104
 
 
105
 
#    if not args.auto:
106
 
    if 1 == 1:  # workaround to avoid lots of whitespace changes
107
 
        mergeprofiles.ask_merge_questions()
108
 
 
109
 
        q = aaui.PromptQuestion()
110
 
        q.title = _('Changed Local Profiles')
111
 
        q.explanation = _('The following local profiles were changed. Would you like to save them?')
112
 
        q.functions = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT', 'CMD_IGNORE_ENTRY']
113
 
        q.default = 'CMD_VIEW_CHANGES'
114
 
        q.options = [merging_profile]
115
 
        q.selected = 0
116
 
 
117
 
        ans = ''
118
 
        arg = None
119
 
        programs = list(mergeprofiles.user.aa.keys())
120
 
        program = programs[0]
121
 
        while ans != 'CMD_SAVE_CHANGES':
122
 
            ans, arg = q.promptUser()
123
 
            if ans == 'CMD_SAVE_CHANGES':
124
 
                apparmor.aa.write_profile_ui_feedback(program)
125
 
                apparmor.aa.reload_base(program)
126
 
            elif ans == 'CMD_VIEW_CHANGES':
127
 
                for program in programs:
128
 
                    apparmor.aa.original_aa[program] = apparmor.aa.deepcopy(apparmor.aa.aa[program])
129
 
                #oldprofile = apparmor.serialize_profile(apparmor.original_aa[program], program, '')
130
 
                newprofile = apparmor.aa.serialize_profile(mergeprofiles.user.aa[program], program, '')
131
 
                aaui.UI_Changes(mergeprofiles.user.filename, newprofile, comments=True)
132
 
            elif ans == 'CMD_IGNORE_ENTRY':
133
 
                break
134
 
 
135
 
 
136
 
class Merge(object):
137
 
    def __init__(self, profiles):
138
 
        user, base = profiles
139
 
 
140
 
        #Read and parse base profile and save profile data, include data from it and reset them
141
 
        apparmor.aa.read_profile(base, True)
142
 
        self.base = cleanprofile.Prof(base)
143
 
 
144
 
        reset_aa()
145
 
 
146
 
        #Read and parse user profile
147
 
        apparmor.aa.read_profile(user, True)
148
 
        self.user = cleanprofile.Prof(user)
149
 
 
150
 
    def clear_common(self):
151
 
        deleted = 0
152
 
 
153
 
        #Remove off the parts in base profile which are common/superfluous from user profile
154
 
        user_base = cleanprofile.CleanProf(False, self.user, self.base)
155
 
        deleted += user_base.compare_profiles()
156
 
 
157
 
    def ask_merge_questions(self):
158
 
        other = self.base
159
 
        log_dict = {'merge': other.aa}
160
 
 
161
 
        apparmor.aa.loadincludes()
162
 
        done = False
163
 
 
164
 
        #Add the file-wide includes from the other profile to the user profile
165
 
        options = []
166
 
        for inc in other.filelist[other.filename]['include'].keys():
167
 
            if not inc in self.user.filelist[self.user.filename]['include'].keys():
168
 
                options.append('#include <%s>' %inc)
169
 
 
170
 
        default_option = 1
171
 
 
172
 
        q = aaui.PromptQuestion()
173
 
        q.options = options
174
 
        q.selected = default_option - 1
175
 
        q.headers = [_('File includes'), _('Select the ones you wish to add')]
176
 
        q.functions = ['CMD_ALLOW', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
177
 
        q.default = 'CMD_ALLOW'
178
 
 
179
 
        while not done and options:
180
 
            ans, selected = q.promptUser()
181
 
            if ans == 'CMD_IGNORE_ENTRY':
182
 
                done = True
183
 
            elif ans == 'CMD_ALLOW':
184
 
                selection = options[selected]
185
 
                inc = re_match_include(selection)
186
 
                self.user.filelist[self.user.filename]['include'][inc] = True
187
 
                options.pop(selected)
188
 
                aaui.UI_Info(_('Adding %s to the file.') % selection)
189
 
            elif ans == 'CMD_FINISHED':
190
 
                return
191
 
 
192
 
        if not apparmor.aa.sev_db:
193
 
            apparmor.aa.sev_db = apparmor.severity.Severity(apparmor.aa.CONFDIR + '/severity.db', _('unknown'))
194
 
 
195
 
        apparmor.aa.ask_the_questions(log_dict)
196
 
 
197
 
if __name__ == '__main__':
198
 
    main()