~ubuntu-core-dev/apparmor/master

1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
1
#! /usr/bin/env python
2
# ----------------------------------------------------------------------
3
#    Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
4
#
5
#    This program is free software; you can redistribute it and/or
6
#    modify it under the terms of version 2 of the GNU General Public
7
#    License as published by the Free Software Foundation.
8
#
9
#    This program is distributed in the hope that it will be useful,
10
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
11
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
#    GNU General Public License for more details.
13
#
14
# ----------------------------------------------------------------------
15
import argparse
16
import re
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
17
import os
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
18
19
import apparmor.aa
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
20
from apparmor.aa import available_buttons, combine_name, delete_duplicates, is_known_rule, match_includes
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
21
import apparmor.aamode
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
22
from apparmor.common import AppArmorException
23
from apparmor.regex import re_match_include
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
24
import apparmor.severity
25
import apparmor.cleanprofile as cleanprofile
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
26
import apparmor.ui as aaui
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
27
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
28
# setup exception handling
29
from apparmor.fail import enable_aa_exception_handler
30
enable_aa_exception_handler()
31
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
32
# setup module translations
33
from apparmor.translations import init_translation
34
_ = init_translation()
35
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
36
parser = argparse.ArgumentParser(description=_('Merge the given profiles into /etc/apparmor.d/ (or the directory specified with -d)'))
37
parser.add_argument('files', nargs='+', type=str, help=_('Profile(s) to merge'))
38
#parser.add_argument('other', nargs='?', type=str, help=_('other profile'))
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
39
parser.add_argument('-d', '--dir', type=str, help=_('path to profiles'))
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
40
#parser.add_argument('-a', '--auto', action='store_true', help=_('Automatically merge profiles, exits incase of *x conflicts'))
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
41
args = parser.parse_args()
42
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
43
args.other = None
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
44
# 2-way merge or 3-way merge based on number of params
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
45
merge_mode = 2 #if args.other == None else 3
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
46
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
47
profiles = [args.files, [args.other]]
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
48
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
49
profiledir = args.dir
50
if profiledir:
51
    apparmor.aa.profile_dir = apparmor.aa.get_full_path(profiledir)
52
    if not os.path.isdir(apparmor.aa.profile_dir):
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
53
        raise AppArmorException(_("%s is not a directory.") %profiledir)
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
54
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
55
def reset_aa():
56
    apparmor.aa.aa = apparmor.aa.hasher()
57
    apparmor.aa.filelist = apparmor.aa.hasher()
58
    apparmor.aa.include = dict()
59
    apparmor.aa.existing_profiles = apparmor.aa.hasher()
60
    apparmor.aa.original_aa = apparmor.aa.hasher()
61
62
def find_profiles_from_files(files):
63
    profile_to_filename = dict()
64
    for file_name in files:
65
        apparmor.aa.read_profile(file_name, True)
66
        for profile_name in apparmor.aa.filelist[file_name]['profiles'].keys():
67
            profile_to_filename[profile_name] = file_name
68
        reset_aa()
69
70
    return profile_to_filename
71
72
def find_files_from_profiles(profiles):
73
    profile_to_filename = dict()
74
    apparmor.aa.read_profiles()
75
76
    for profile_name in profiles:
77
        profile_to_filename[profile_name] = apparmor.aa.get_profile_filename(profile_name)
78
79
    reset_aa()
80
81
    return profile_to_filename
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
82
83
def main():
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
84
    profiles_to_merge = set()
85
86
    base_files, other_files = profiles
87
88
    base_profile_to_file = find_profiles_from_files(base_files)
89
90
    profiles_to_merge = profiles_to_merge.union(set(base_profile_to_file.keys()))
91
92
    other_profile_to_file = dict()
93
94
    if merge_mode == 3:
95
        other_profile_to_file = find_profiles_from_files(other_files)
96
        profiles_to_merge.add(other_profile_to_file.keys())
97
98
    user_profile_to_file = find_files_from_profiles(profiles_to_merge)
99
100
#    print(base_files,"\n",other_files)
101
#    print(base_profile_to_file,"\n",other_profile_to_file,"\n",user_profile_to_file)
102
#    print(profiles_to_merge)
103
104
    for profile_name in profiles_to_merge:
105
        aaui.UI_Info("\n\n" + _("Merging profile for %s" % profile_name))
106
        user_file = user_profile_to_file[profile_name]
107
        base_file = base_profile_to_file.get(profile_name, None)
108
        other_file =  None
109
110
        if merge_mode == 3:
111
            other_file = other_profile_to_file.get(profile_name, None)
112
113
        if base_file == None:
114
            if other_file == None:
115
                continue
116
117
            act([user_file, other_file, None], 2, profile_name)
118
        else:
119
            if other_file == None:
120
                act([user_file, base_file, None], 2, profile_name)
121
            else:
122
                act([user_file, base_file, other_file], 3, profile_name)
123
124
        reset_aa()
125
126
def act(files, merge_mode, merging_profile):
127
    mergeprofiles = Merge(files)
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
128
    #Get rid of common/superfluous stuff
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
129
#    mergeprofiles.clear_common()
130
# mergeprofiles.clear_common() temporarily disabled because it crashes,
131
# see https://bugs.launchpad.net/apparmor/+bug/1382236
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
132
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
133
#    if not args.auto:
134
    if 1 == 1:  # workaround to avoid lots of whitespace changes
135
        if merge_mode == 3:
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
136
            mergeprofiles.ask_the_questions('other', merging_profile)
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
137
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
138
            mergeprofiles.clear_common()
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
139
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
140
        mergeprofiles.ask_the_questions('base', merging_profile)
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
141
1503 by Jamie Strandboge
* Updated to apparmor 2.9.beta4 (aka apparmor 2.8.98)
142
        q = aaui.PromptQuestion()
143
        q.title = _('Changed Local Profiles')
144
        q.explanation = _('The following local profiles were changed. Would you like to save them?')
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
145
        q.functions = ['CMD_SAVE_CHANGES', 'CMD_VIEW_CHANGES', 'CMD_ABORT', 'CMD_IGNORE_ENTRY']
1503 by Jamie Strandboge
* Updated to apparmor 2.9.beta4 (aka apparmor 2.8.98)
146
        q.default = 'CMD_VIEW_CHANGES'
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
147
        q.options = [merging_profile]
1503 by Jamie Strandboge
* Updated to apparmor 2.9.beta4 (aka apparmor 2.8.98)
148
        q.selected = 0
149
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
150
        ans = ''
151
        arg = None
152
        programs = list(mergeprofiles.user.aa.keys())
153
        program = programs[0]
154
        while ans != 'CMD_SAVE_CHANGES':
1503 by Jamie Strandboge
* Updated to apparmor 2.9.beta4 (aka apparmor 2.8.98)
155
            ans, arg = q.promptUser()
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
156
            if ans == 'CMD_SAVE_CHANGES':
157
                apparmor.aa.write_profile_ui_feedback(program)
158
                apparmor.aa.reload_base(program)
159
            elif ans == 'CMD_VIEW_CHANGES':
160
                for program in programs:
161
                    apparmor.aa.original_aa[program] = apparmor.aa.deepcopy(apparmor.aa.aa[program])
162
                #oldprofile = apparmor.serialize_profile(apparmor.original_aa[program], program, '')
163
                newprofile = apparmor.aa.serialize_profile(mergeprofiles.user.aa[program], program, '')
164
                apparmor.aa.display_changes_with_comments(mergeprofiles.user.filename, newprofile)
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
165
            elif ans == 'CMD_IGNORE_ENTRY':
166
                break
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
167
168
169
class Merge(object):
170
    def __init__(self, profiles):
171
        user, base, other = profiles
172
173
        #Read and parse base profile and save profile data, include data from it and reset them
174
        apparmor.aa.read_profile(base, True)
175
        self.base = cleanprofile.Prof(base)
176
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
177
        reset_aa()
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
178
179
        #Read and parse other profile and save profile data, include data from it and reset them
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
180
        if merge_mode == 3:
181
            apparmor.aa.read_profile(other, True)
182
            self.other = cleanprofile.Prof(other)
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
183
            reset_aa()
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
184
185
        #Read and parse user profile
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
186
        apparmor.aa.read_profile(user, True)
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
187
        self.user = cleanprofile.Prof(user)
188
189
    def clear_common(self):
190
        deleted = 0
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
191
192
        if merge_mode == 3:
193
            #Remove off the parts in other profile which are common/superfluous from user profile
194
            user_other = cleanprofile.CleanProf(False, self.user, self.other)
195
            deleted += user_other.compare_profiles()
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
196
197
        #Remove off the parts in base profile which are common/superfluous from user profile
198
        user_base = cleanprofile.CleanProf(False, self.user, self.base)
199
        deleted += user_base.compare_profiles()
200
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
201
        if merge_mode == 3:
202
            #Remove off the parts in other profile which are common/superfluous from base profile
203
            base_other = cleanprofile.CleanProf(False, self.base, self.other)
204
            deleted += base_other.compare_profiles()
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
205
206
    def conflict_mode(self, profile, hat, allow, path, mode, new_mode, old_mode):
207
        m = new_mode
208
        o = old_mode
209
        new_mode = apparmor.aa.flatten_mode(new_mode)
210
        old_mode = apparmor.aa.flatten_mode(old_mode)
211
        conflict_modes = set('uUpPcCiIxX')
212
        conflict_x= (old_mode | new_mode) & conflict_modes
213
        if conflict_x:
214
        #We may have conflicting x modes
215
            if conflict_x & set('x'):
216
                conflict_x.remove('x')
217
            if conflict_x & set('X'):
218
                conflict_x.remove('X')
219
            if len(conflict_x) > 1:
1503 by Jamie Strandboge
* Updated to apparmor 2.9.beta4 (aka apparmor 2.8.98)
220
                q = aaui.PromptQuestion()
221
                q.headers = [_('Path'), path]
222
                q.headers += [_('Select the appropriate mode'), '']
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
223
                options = []
224
                options.append('%s: %s' %(mode, apparmor.aa.mode_to_str_user(new_mode)))# - (old_mode & conflict_x))))
225
                options.append('%s: %s' %(mode, apparmor.aa.mode_to_str_user(old_mode)))#(old_mode | new_mode) - (new_mode & conflict_x))))
1503 by Jamie Strandboge
* Updated to apparmor 2.9.beta4 (aka apparmor 2.8.98)
226
                q.options = options
227
                q.functions = ['CMD_ALLOW', 'CMD_ABORT']
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
228
                done = False
229
                while not done:
1503 by Jamie Strandboge
* Updated to apparmor 2.9.beta4 (aka apparmor 2.8.98)
230
                    ans, selected = q.promptUser()
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
231
                    if ans == 'CMD_ALLOW':
232
                        if selected == 0:
233
                            self.user.aa[profile][hat][allow]['path'][path][mode] = m#apparmor.aa.owner_flatten_mode(new_mode)#(old_mode | new_mode) - (old_mode & conflict_x)
234
                            return m
235
                        elif selected == 1:
236
                            return o
237
                            pass#self.user.aa[profile][hat][allow][path][mode] = (old_mode | new_mode) - (new_mode & conflict_x)
238
                        else:
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
239
                            raise AppArmorException(_('Unknown selection'))
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
240
                        done = True
241
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
242
    def ask_the_questions(self, other, profile):
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
243
        aa = self.user.aa  # keep references so that the code in this function can use the short name
244
        changed = apparmor.aa.changed  # (and be more in sync with aa.py ask_the_questions())
245
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
246
        if other == 'other':
247
            other = self.other
248
        else:
249
            other = self.base
250
        #print(other.aa)
251
252
        #Add the file-wide includes from the other profile to the user profile
253
        done = False
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
254
255
        options = []
256
        for inc in other.filelist[other.filename]['include'].keys():
257
            if not inc in self.user.filelist[self.user.filename]['include'].keys():
258
                options.append('#include <%s>' %inc)
259
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
260
        default_option = 1
1503 by Jamie Strandboge
* Updated to apparmor 2.9.beta4 (aka apparmor 2.8.98)
261
262
        q = aaui.PromptQuestion()
263
        q.options = options
264
        q.selected = default_option - 1
265
        q.headers = [_('File includes'), _('Select the ones you wish to add')]
266
        q.functions = ['CMD_ALLOW', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
267
        q.default = 'CMD_ALLOW'
268
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
269
        while not done and options:
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
270
            ans, selected = q.promptUser()
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
271
            if ans == 'CMD_IGNORE_ENTRY':
272
                done = True
273
            elif ans == 'CMD_ALLOW':
274
                selection = options[selected]
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
275
                inc = re_match_include(selection)
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
276
                self.user.filelist[self.user.filename]['include'][inc] = True
277
                options.pop(selected)
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
278
                aaui.UI_Info(_('Adding %s to the file.') % selection)
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
279
            elif ans == 'CMD_FINISHED':
280
                return
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
281
282
        sev_db = apparmor.aa.sev_db
283
        if not sev_db:
284
            sev_db = apparmor.severity.Severity(apparmor.aa.CONFDIR + '/severity.db', _('unknown'))
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
285
286
        for hat in sorted(other.aa[profile].keys()):
287
            #Add the includes from the other profile to the user profile
288
            done = False
289
290
            options = []
291
            for inc in other.aa[profile][hat]['include'].keys():
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
292
                if not inc in aa[profile][hat]['include'].keys():
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
293
                    options.append('#include <%s>' %inc)
294
295
            default_option = 1
296
297
            q = aaui.PromptQuestion()
298
            q.options = options
299
            q.selected = default_option - 1
300
            q.headers = [_('File includes'), _('Select the ones you wish to add')]
301
            q.functions = ['CMD_ALLOW', 'CMD_IGNORE_ENTRY', 'CMD_ABORT', 'CMD_FINISHED']
302
            q.default = 'CMD_ALLOW'
303
304
            while not done and options:
305
                ans, selected = q.promptUser()
306
                if ans == 'CMD_IGNORE_ENTRY':
307
                    done = True
308
                elif ans == 'CMD_ALLOW':
309
                    selection = options[selected]
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
310
                    inc = re_match_include(selection)
311
                    deleted = apparmor.aa.delete_duplicates(aa[profile][hat], inc)
312
                    aa[profile][hat]['include'][inc] = True
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
313
                    options.pop(selected)
314
                    aaui.UI_Info(_('Adding %s to the file.') % selection)
315
                    if deleted:
316
                        aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
317
                elif ans == 'CMD_FINISHED':
318
                    return
319
320
            # Process all the path entries.
321
            for allow in ['allow', 'deny']:
322
                for path in sorted(other.aa[profile][hat][allow]['path'].keys()):
323
                    #print(path, other.aa[profile][hat][allow]['path'][path])
324
                    mode = other.aa[profile][hat][allow]['path'][path]['mode']
325
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
326
                    if aa[profile][hat][allow]['path'].get(path, False):
327
                        mode = self.conflict_mode(profile, hat, allow, path, 'mode', other.aa[profile][hat][allow]['path'][path]['mode'], aa[profile][hat][allow]['path'][path]['mode'])
328
                        self.conflict_mode(profile, hat, allow, path, 'audit', other.aa[profile][hat][allow]['path'][path]['audit'], aa[profile][hat][allow]['path'][path]['audit'])
329
                        changed[profile] = True
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
330
                        continue
331
                    # Lookup modes from profile
332
                    allow_mode = set()
333
                    allow_audit = set()
334
                    deny_mode = set()
335
                    deny_audit = set()
336
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
337
                    fmode, famode, fm = apparmor.aa.rematchfrag(aa[profile][hat], 'allow', path)
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
338
                    if fmode:
339
                        allow_mode |= fmode
340
                    if famode:
341
                        allow_audit |= famode
342
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
343
                    cm, cam, m = apparmor.aa.rematchfrag(aa[profile][hat], 'deny', path)
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
344
                    if cm:
345
                        deny_mode |= cm
346
                    if cam:
347
                        deny_audit |= cam
348
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
349
                    imode, iamode, im = apparmor.aa.match_prof_incs_to_path(aa[profile][hat], 'allow', path)
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
350
                    if imode:
351
                        allow_mode |= imode
352
                    if iamode:
353
                        allow_audit |= iamode
354
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
355
                    cm, cam, m = apparmor.aa.match_prof_incs_to_path(aa[profile][hat], 'deny', path)
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
356
                    if cm:
357
                        deny_mode |= cm
358
                    if cam:
359
                        deny_audit |= cam
360
361
                    if deny_mode & apparmor.aamode.AA_MAY_EXEC:
362
                        deny_mode |= apparmor.aamode.ALL_AA_EXEC_TYPE
363
364
                    # Mask off the denied modes
365
                    mode = mode - deny_mode
366
367
                    # If we get an exec request from some kindof event that generates 'PERMITTING X'
368
                    # check if its already in allow_mode
369
                    # if not add ix permission
370
                    if mode & apparmor.aamode.AA_MAY_EXEC:
371
                        # Remove all type access permission
372
                        mode = mode - apparmor.aamode.ALL_AA_EXEC_TYPE
373
                        if not allow_mode & apparmor.aamode.AA_MAY_EXEC:
374
                            mode |= apparmor.aa.str_to_mode('ix')
375
376
                    if not mode:
377
                        continue
378
379
                    matches = []
380
381
                    if fmode:
382
                        matches += fm
383
384
                    if imode:
385
                        matches += im
386
387
                    if not apparmor.aa.mode_contains(allow_mode, mode):
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
388
                        default_option = 1
389
                        options = []
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
390
                        newincludes = []
391
                        include_valid = False
392
393
                        for incname in apparmor.aa.include.keys():
394
                            include_valid = False
395
                            # If already present skip
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
396
                            if aa[profile][hat][incname]:
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
397
                                continue
398
                            if incname.startswith(apparmor.aa.profile_dir):
399
                                incname = incname.replace(apparmor.aa.profile_dir+'/', '', 1)
400
401
                            include_valid = apparmor.aa.valid_include('', incname)
402
403
                            if not include_valid:
404
                                continue
405
406
                            cm, am, m = apparmor.aa.match_include_to_path(incname, 'allow', path)
407
408
                            if cm and apparmor.aa.mode_contains(cm, mode):
409
                                dm = apparmor.aa.match_include_to_path(incname, 'deny', path)[0]
410
                                # If the mode is denied
411
                                if not mode & dm:
412
                                    if not list(filter(lambda s: '/**' == s, m)):
413
                                        newincludes.append(incname)
414
                        # Add new includes to the options
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
415
                        if newincludes:
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
416
                            options += list(map(lambda s: '#include <%s>' % s, sorted(set(newincludes))))
417
                        # We should have literal the path in options list too
418
                        options.append(path)
419
                        # Add any the globs matching path from logprof
420
                        globs = apparmor.aa.glob_common(path)
421
                        if globs:
422
                            matches += globs
423
                        # Add any user entered matching globs
424
                        for user_glob in apparmor.aa.user_globs:
425
                            if apparmor.aa.matchliteral(user_glob, path):
426
                                matches.append(user_glob)
427
428
                        matches = list(set(matches))
429
                        if path in matches:
430
                            matches.remove(path)
431
432
                        options += apparmor.aa.order_globs(matches, path)
433
                        default_option = len(options)
434
435
                        sev_db.unload_variables()
436
                        sev_db.load_variables(apparmor.aa.get_profile_filename(profile))
437
                        severity = sev_db.rank(path, apparmor.aa.mode_to_str(mode))
438
                        sev_db.unload_variables()
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
439
440
                        audit_toggle = 0
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
441
                        owner_toggle = 0
442
                        if apparmor.aa.cfg['settings']['default_owner_prompt']:
443
                            owner_toggle = apparmor.aa.cfg['settings']['default_owner_prompt']
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
444
                        done = False
445
                        while not done:
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
446
                            q = aaui.PromptQuestion()
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
447
                            q.headers = [_('Profile'), apparmor.aa.combine_name(profile, hat),
448
                                            _('Path'), path]
449
450
                            if allow_mode:
451
                                mode |= allow_mode
452
                                tail = ''
453
                                s = ''
454
                                prompt_mode = None
455
                                if owner_toggle == 0:
456
                                    prompt_mode = apparmor.aa.flatten_mode(mode)
457
                                    tail = '     ' + _('(owner permissions off)')
458
                                elif owner_toggle == 1:
459
                                    prompt_mode = mode
460
                                elif owner_toggle == 2:
461
                                    prompt_mode = allow_mode | apparmor.aa.owner_flatten_mode(mode - allow_mode)
462
                                    tail = '     ' + _('(force new perms to owner)')
463
                                else:
464
                                    prompt_mode = apparmor.aa.owner_flatten_mode(mode)
465
                                    tail = '     ' + _('(force all rule perms to owner)')
466
467
                                if audit_toggle == 1:
468
                                    s = apparmor.aa.mode_to_str_user(allow_mode)
469
                                    if allow_mode:
470
                                        s += ', '
471
                                    s += 'audit ' + apparmor.aa.mode_to_str_user(prompt_mode - allow_mode) + tail
472
                                elif audit_toggle == 2:
473
                                    s = 'audit ' + apparmor.aa.mode_to_str_user(prompt_mode) + tail
474
                                else:
475
                                    s = apparmor.aa.mode_to_str_user(prompt_mode) + tail
476
477
                                q.headers += [_('Old Mode'), apparmor.aa.mode_to_str_user(allow_mode),
478
                                                 _('New Mode'), s]
479
480
                            else:
481
                                s = ''
482
                                tail = ''
483
                                prompt_mode = None
484
                                if audit_toggle:
485
                                    s = 'audit'
486
                                if owner_toggle == 0:
487
                                    prompt_mode = apparmor.aa.flatten_mode(mode)
488
                                    tail = '     ' + _('(owner permissions off)')
489
                                elif owner_toggle == 1:
490
                                    prompt_mode = mode
491
                                else:
492
                                    prompt_mode = apparmor.aa.owner_flatten_mode(mode)
493
                                    tail = '     ' + _('(force perms to owner)')
494
495
                                s = apparmor.aa.mode_to_str_user(prompt_mode)
496
                                q.headers += [_('Mode'), s]
497
498
                            q.headers += [_('Severity'), severity]
499
                            q.options = options
500
                            q.selected = default_option - 1
501
                            q.functions = ['CMD_ALLOW', 'CMD_DENY', 'CMD_IGNORE_ENTRY', 'CMD_GLOB',
502
                                              'CMD_GLOBEXT', 'CMD_NEW', 'CMD_ABORT',
503
                                              'CMD_FINISHED', 'CMD_OTHER']
504
505
                            q.default = 'CMD_ALLOW'
506
507
1503 by Jamie Strandboge
* Updated to apparmor 2.9.beta4 (aka apparmor 2.8.98)
508
                            ans, selected = q.promptUser()
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
509
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
510
                            if ans == 'CMD_IGNORE_ENTRY':
511
                                done = True
512
                                break
513
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
514
                            elif ans == 'CMD_FINISHED':
515
                                return
516
517
                            if ans == 'CMD_OTHER':
518
                                aaui.UI_Important("Sorry, not implemented yet!")
519
                                # audit_toggle, owner_toggle = aaui.UI_ask_mode_toggles(audit_toggle, owner_toggle, allow_mode)
520
# crashes with
521
#    audit_toggle, owner_toggle = aaui.UI_ask_mode_toggles(audit_toggle, owner_toggle, allow_mode)
522
#    AttributeError: 'module' object has no attribute 'UI_ask_mode_toggles'
523
                            elif ans == 'CMD_USER_TOGGLE':
524
                                owner_toggle += 1
525
                                if not allow_mode and owner_toggle == 2:
526
                                    owner_toggle += 1
527
                                if owner_toggle > 3:
528
                                    owner_toggle = 0
529
                            elif ans == 'CMD_ALLOW':
530
                                path = options[selected]
531
                                done = True
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
532
                                match = re_match_include(path)
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
533
                                if match:
534
                                    inc = match
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
535
                                    deleted = apparmor.aa.delete_duplicates(aa[profile][hat], inc)
536
                                    aa[profile][hat]['include'][inc] = True
537
                                    changed[profile] = True
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
538
                                    aaui.UI_Info(_('Adding %s to profile.') % path)
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
539
                                    if deleted:
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
540
                                        aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
541
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
542
                                else:
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
543
                                    if aa[profile][hat]['allow']['path'][path].get('mode', False):
544
                                        mode |= aa[profile][hat]['allow']['path'][path]['mode']
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
545
                                    deleted = []
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
546
                                    for entry in aa[profile][hat]['allow']['path'].keys():
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
547
                                        if path == entry:
548
                                            continue
549
550
                                        if apparmor.aa.matchregexp(path, entry):
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
551
                                            if apparmor.aa.mode_contains(mode, aa[profile][hat]['allow']['path'][entry]['mode']):
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
552
                                                deleted.append(entry)
553
                                    for entry in deleted:
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
554
                                        aa[profile][hat]['allow']['path'].pop(entry)
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
555
                                    deleted = len(deleted)
556
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
557
                                    if owner_toggle == 0:
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
558
                                        mode = apparmor.aa.flatten_mode(mode)
559
                                    #elif owner_toggle == 1:
560
                                    #    mode = mode
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
561
                                    elif owner_toggle == 2:
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
562
                                        mode = allow_mode | apparmor.aa.owner_flatten_mode(mode - allow_mode)
563
                                    elif owner_toggle == 3:
564
                                        mode = apparmor.aa.owner_flatten_mode(mode)
565
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
566
                                    if not aa[profile][hat]['allow'].get(path, False):
567
                                        aa[profile][hat]['allow']['path'][path]['mode'] = aa[profile][hat]['allow']['path'][path].get('mode', set()) | mode
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
568
569
570
                                    tmpmode = set()
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
571
                                    if audit_toggle == 1:
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
572
                                        tmpmode = mode - allow_mode
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
573
                                    elif audit_toggle == 2:
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
574
                                        tmpmode = mode
575
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
576
                                    aa[profile][hat]['allow']['path'][path]['audit'] = aa[profile][hat]['allow']['path'][path].get('audit', set()) | tmpmode
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
577
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
578
                                    changed[profile] = True
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
579
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
580
                                    aaui.UI_Info(_('Adding %(path)s %(mode)s to profile') % { 'path': path, 'mode': apparmor.aa.mode_to_str_user(mode) })
581
                                    if deleted:
582
                                        aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
583
584
                            elif ans == 'CMD_DENY':
585
                                path = options[selected].strip()
586
                                # Add new entry?
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
587
                                aa[profile][hat]['deny']['path'][path]['mode'] = aa[profile][hat]['deny']['path'][path].get('mode', set()) | (mode - allow_mode)
588
589
                                aa[profile][hat]['deny']['path'][path]['audit'] = aa[profile][hat]['deny']['path'][path].get('audit', set())
590
591
                                changed[profile] = True
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
592
593
                                done = True
594
595
                            elif ans == 'CMD_NEW':
596
                                arg = options[selected]
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
597
                                if not re_match_include(arg):
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
598
                                    ans = aaui.UI_GetString(_('Enter new path: '), arg)
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
599
#                                         if ans:
600
#                                             if not matchliteral(ans, path):
601
#                                                 ynprompt = _('The specified path does not match this log entry:\n\n  Log Entry: %s\n  Entered Path:  %s\nDo you really want to use this path?') % (path,ans)
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
602
#                                                 key = aaui.UI_YesNo(ynprompt, 'n')
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
603
#                                                 if key == 'n':
1498 by Jamie Strandboge
Update to 2.8.96~2652-0ubuntu3
604
#                                                     continue
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
605
                                    apparmor.aa.user_globs.append(ans)
606
                                    options.append(ans)
607
                                    default_option = len(options)
608
609
                            elif ans == 'CMD_GLOB':
610
                                newpath = options[selected].strip()
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
611
                                if not re_match_include(newpath):
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
612
                                    newpath = apparmor.aa.glob_path(newpath)
613
614
                                    if newpath not in options:
615
                                        options.append(newpath)
616
                                        default_option = len(options)
617
                                    else:
618
                                        default_option = options.index(newpath) + 1
619
620
                            elif ans == 'CMD_GLOBEXT':
621
                                newpath = options[selected].strip()
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
622
                                if not re_match_include(newpath):
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
623
                                    newpath = apparmor.aa.glob_path_withext(newpath)
624
625
                                    if newpath not in options:
626
                                        options.append(newpath)
627
                                        default_option = len(options)
628
                                    else:
629
                                        default_option = options.index(newpath) + 1
630
631
                            elif re.search('\d', ans):
632
                                default_option = ans
633
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
634
            for ruletype in ['capability', 'network', 'change_profile']:
635
                if other.aa[profile][hat].get(ruletype, False): # needed until we have proper profile initialization
636
                    for rule_obj in other.aa[profile][hat][ruletype].rules:
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
637
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
638
                        if is_known_rule(aa[profile][hat], ruletype, rule_obj):
639
                            continue
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
640
641
                        default_option = 1
642
                        options = []
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
643
                        newincludes = match_includes(aa[profile][hat], ruletype, rule_obj)
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
644
                        q = aaui.PromptQuestion()
645
                        if newincludes:
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
646
                            options += list(map(lambda inc: '#include <%s>' % inc, sorted(set(newincludes))))
647
648
                        options.append(rule_obj.get_clean())
649
                        q.options = options
650
                        q.selected = default_option - 1
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
651
652
                        done = False
653
                        while not done:
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
654
                            q.headers = [_('Profile'), combine_name(profile, hat)]
655
                            q.headers += rule_obj.logprof_header()
656
657
                            # Load variables into sev_db? Not needed/used for capabilities and network rules.
658
                            severity = rule_obj.severity(sev_db)
659
                            if severity != sev_db.NOT_IMPLEMENTED:
660
                                q.headers += [_('Severity'), severity]
661
662
                            q.functions = available_buttons(rule_obj)
663
                            q.default = q.functions[0]
664
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
665
                            ans, selected = q.promptUser()
666
                            if ans == 'CMD_IGNORE_ENTRY':
667
                                done = True
668
                                break
669
670
                            elif ans == 'CMD_FINISHED':
671
                                return
672
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
673
                            elif ans.startswith('CMD_AUDIT'):
674
                                if ans == 'CMD_AUDIT_NEW':
675
                                    rule_obj.audit = True
676
                                    rule_obj.raw_rule = None
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
677
                                else:
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
678
                                    rule_obj.audit = False
679
                                    rule_obj.raw_rule = None
680
681
                                options[len(options) - 1] = rule_obj.get_clean()
682
                                q.options = options
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
683
684
                            elif ans == 'CMD_ALLOW':
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
685
                                done = True
686
                                changed[profile] = True
687
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
688
                                selection = options[selected]
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
689
690
                                inc = re_match_include(selection)
691
                                if inc:
692
                                    deleted = delete_duplicates(aa[profile][hat], inc)
693
694
                                    aa[profile][hat]['include'][inc] = True
695
696
                                    aaui.UI_Info(_('Adding %s to profile.') % selection)
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
697
                                    if deleted:
698
                                        aaui.UI_Info(_('Deleted %s previous matching profile entries.') % deleted)
699
700
                                else:
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
701
                                    aa[profile][hat][ruletype].add(rule_obj)
702
703
                                    aaui.UI_Info(_('Adding %s to profile.') % rule_obj.get_clean())
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
704
705
                            elif ans == 'CMD_DENY':
706
                                done = True
1509 by Jamie Strandboge
* debian/patches/lp1529074.patch: for systems using networkd, add read on
707
                                changed[profile] = True
708
709
                                rule_obj.deny = True
710
                                rule_obj.raw_rule = None  # reset raw rule after manually modifying rule_obj
711
                                aa[profile][hat][ruletype].add(rule_obj)
712
                                aaui.UI_Info(_('Adding %s to profile.') % rule_obj.get_clean())
1507 by Jamie Strandboge
sync with citrain branch. We should really not be maintaining two branches
713
714
                            else:
715
                                done = False
1495.1.1 by Seth Arnold
[ Jamie Strandboge ]
716
717
if __name__ == '__main__':
718
    main()